LCOV - code coverage report
Current view: top level - gcore - gdaljp2box.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 167 214 78.0 %
Date: 2026-01-07 17:35:05 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        7757 : GDALJP2Box::GDALJP2Box(VSILFILE *fpIn) : fpVSIL(fpIn)
      36             : {
      37        7757 : }
      38             : 
      39             : /************************************************************************/
      40             : /*                            ~GDALJP2Box()                             */
      41             : /************************************************************************/
      42             : 
      43        7757 : GDALJP2Box::~GDALJP2Box()
      44             : 
      45             : {
      46             :     // Do not close fpVSIL. Ownership remains to the caller of GDALJP2Box
      47             :     // constructor
      48        7757 : }
      49             : 
      50             : /************************************************************************/
      51             : /*                             SetOffset()                              */
      52             : /************************************************************************/
      53             : 
      54       20137 : int GDALJP2Box::SetOffset(vsi_l_offset nNewOffset)
      55             : 
      56             : {
      57       20137 :     szBoxType[0] = '\0';
      58       20137 :     return VSIFSeekL(fpVSIL, nNewOffset, SEEK_SET) == 0;
      59             : }
      60             : 
      61             : /************************************************************************/
      62             : /*                             ReadFirst()                              */
      63             : /************************************************************************/
      64             : 
      65        2610 : int GDALJP2Box::ReadFirst()
      66             : 
      67             : {
      68        2610 :     return SetOffset(0) && ReadBox();
      69             : }
      70             : 
      71             : /************************************************************************/
      72             : /*                              ReadNext()                              */
      73             : /************************************************************************/
      74             : 
      75       15230 : int GDALJP2Box::ReadNext()
      76             : 
      77             : {
      78       15230 :     return SetOffset(nBoxOffset + nBoxLength) && ReadBox();
      79             : }
      80             : 
      81             : /************************************************************************/
      82             : /*                           ReadFirstChild()                           */
      83             : /************************************************************************/
      84             : 
      85        2338 : int GDALJP2Box::ReadFirstChild(GDALJP2Box *poSuperBox)
      86             : 
      87             : {
      88        2338 :     if (poSuperBox == nullptr)
      89          41 :         return ReadFirst();
      90             : 
      91        2297 :     szBoxType[0] = '\0';
      92        2297 :     if (!poSuperBox->IsSuperBox())
      93           0 :         return FALSE;
      94             : 
      95        2297 :     return SetOffset(poSuperBox->nDataOffset) && ReadBox();
      96             : }
      97             : 
      98             : /************************************************************************/
      99             : /*                           ReadNextChild()                            */
     100             : /************************************************************************/
     101             : 
     102        4648 : int GDALJP2Box::ReadNextChild(GDALJP2Box *poSuperBox)
     103             : 
     104             : {
     105        4648 :     if (poSuperBox == nullptr)
     106         210 :         return ReadNext();
     107             : 
     108        4438 :     if (!ReadNext())
     109           0 :         return FALSE;
     110             : 
     111        4438 :     if (nBoxOffset >= poSuperBox->nBoxOffset + poSuperBox->nBoxLength)
     112             :     {
     113        2040 :         szBoxType[0] = '\0';
     114        2040 :         return FALSE;
     115             :     }
     116             : 
     117        2398 :     return TRUE;
     118             : }
     119             : 
     120             : /************************************************************************/
     121             : /*                              ReadBox()                               */
     122             : /************************************************************************/
     123             : 
     124       20137 : int GDALJP2Box::ReadBox()
     125             : 
     126             : {
     127       20137 :     GUInt32 nLBox = 0;
     128       20137 :     GUInt32 nTBox = 0;
     129             : 
     130       20137 :     nBoxOffset = VSIFTellL(fpVSIL);
     131             : 
     132       38513 :     if (VSIFReadL(&nLBox, 4, 1, fpVSIL) != 1 ||
     133       18376 :         VSIFReadL(&nTBox, 4, 1, fpVSIL) != 1)
     134             :     {
     135        1762 :         return FALSE;
     136             :     }
     137             : 
     138       18375 :     memcpy(szBoxType, &nTBox, 4);
     139       18375 :     szBoxType[4] = '\0';
     140             : 
     141       18375 :     nLBox = CPL_MSBWORD32(nLBox);
     142             : 
     143       18375 :     if (nLBox != 1)
     144             :     {
     145       18370 :         nBoxLength = nLBox;
     146       18370 :         nDataOffset = nBoxOffset + 8;
     147             :     }
     148             :     else
     149             :     {
     150           5 :         GByte abyXLBox[8] = {0};
     151           5 :         if (VSIFReadL(abyXLBox, 8, 1, fpVSIL) != 1)
     152           0 :             return FALSE;
     153             : 
     154           5 :         CPL_MSBPTR64(abyXLBox);
     155           5 :         memcpy(&nBoxLength, abyXLBox, 8);
     156             : 
     157           5 :         if (nBoxLength < 0)
     158             :         {
     159           0 :             CPLDebug("GDALJP2", "Invalid length for box %s", szBoxType);
     160           0 :             return FALSE;
     161             :         }
     162           5 :         nDataOffset = nBoxOffset + 16;
     163             :     }
     164             : 
     165       18375 :     if (nBoxLength == 0 && m_bAllowGetFileSize)
     166             :     {
     167         258 :         if (VSIFSeekL(fpVSIL, 0, SEEK_END) != 0)
     168           0 :             return FALSE;
     169         258 :         nBoxLength = VSIFTellL(fpVSIL) - nBoxOffset;
     170         258 :         if (VSIFSeekL(fpVSIL, static_cast<vsi_l_offset>(nDataOffset),
     171         258 :                       SEEK_SET) != 0)
     172           0 :             return FALSE;
     173             :     }
     174             : 
     175       18375 :     if (EQUAL(szBoxType, "uuid"))
     176             :     {
     177        2774 :         if (VSIFReadL(abyUUID, 16, 1, fpVSIL) != 1)
     178           0 :             return FALSE;
     179        2774 :         nDataOffset += 16;
     180             :     }
     181             : 
     182       18375 :     if (m_bAllowGetFileSize && GetDataLength() < 0)
     183             :     {
     184           0 :         CPLDebug("GDALJP2", "Invalid length for box %s", szBoxType);
     185           0 :         return FALSE;
     186             :     }
     187             : 
     188       18375 :     return TRUE;
     189             : }
     190             : 
     191             : /************************************************************************/
     192             : /*                             IsSuperBox()                             */
     193             : /************************************************************************/
     194             : 
     195        2698 : int GDALJP2Box::IsSuperBox()
     196             : 
     197             : {
     198        4589 :     if (EQUAL(GetType(), "asoc") || EQUAL(GetType(), "jp2h") ||
     199        4589 :         EQUAL(GetType(), "res ") || EQUAL(GetType(), "jumb"))
     200        2377 :         return TRUE;
     201             : 
     202         321 :     return FALSE;
     203             : }
     204             : 
     205             : /************************************************************************/
     206             : /*                            ReadBoxData()                             */
     207             : /************************************************************************/
     208             : 
     209        2805 : GByte *GDALJP2Box::ReadBoxData()
     210             : 
     211             : {
     212        2805 :     GIntBig nDataLength = GetDataLength();
     213        2805 :     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        2805 :     if (VSIFSeekL(fpVSIL, static_cast<vsi_l_offset>(nDataOffset), SEEK_SET) !=
     221             :         0)
     222           0 :         return nullptr;
     223             : 
     224             :     char *pszData = static_cast<char *>(
     225        2805 :         VSI_MALLOC_VERBOSE(static_cast<int>(nDataLength) + 1));
     226        2805 :     if (pszData == nullptr)
     227           0 :         return nullptr;
     228             : 
     229        2805 :     if (static_cast<GIntBig>(VSIFReadL(
     230        2805 :             pszData, 1, static_cast<int>(nDataLength), fpVSIL)) != nDataLength)
     231             :     {
     232           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot read box content");
     233           0 :         CPLFree(pszData);
     234           0 :         return nullptr;
     235             :     }
     236             : 
     237        2805 :     pszData[nDataLength] = '\0';
     238             : 
     239        2805 :     return reinterpret_cast<GByte *>(pszData);
     240             : }
     241             : 
     242             : /************************************************************************/
     243             : /*                           GetDataLength()                            */
     244             : /************************************************************************/
     245             : 
     246       29466 : GIntBig GDALJP2Box::GetDataLength() const
     247             : {
     248       29466 :     return nBoxLength - (nDataOffset - nBoxOffset);
     249             : }
     250             : 
     251             : /************************************************************************/
     252             : /*                            DumpReadable()                            */
     253             : /************************************************************************/
     254             : 
     255           0 : int GDALJP2Box::DumpReadable(FILE *fpOut, int nIndentLevel)
     256             : 
     257             : {
     258           0 :     if (fpOut == nullptr)
     259           0 :         fpOut = stdout;
     260             : 
     261           0 :     for (int i = 0; i < nIndentLevel; ++i)
     262           0 :         fprintf(fpOut, "  ");
     263             : 
     264             :     char szBuffer[128];
     265           0 :     CPLsnprintf(szBuffer, sizeof(szBuffer),
     266             :                 "  Type=%s, Offset=" CPL_FRMT_GIB "/" CPL_FRMT_GIB
     267             :                 ", Data Size=" CPL_FRMT_GIB,
     268           0 :                 szBoxType, nBoxOffset, nDataOffset, GetDataLength());
     269           0 :     fprintf(fpOut, "%s", szBuffer);
     270             : 
     271           0 :     if (IsSuperBox())
     272             :     {
     273           0 :         fprintf(fpOut, " (super)");
     274             :     }
     275             : 
     276           0 :     fprintf(fpOut, "\n");
     277             : 
     278           0 :     if (IsSuperBox())
     279             :     {
     280           0 :         GDALJP2Box oSubBox(GetFILE());
     281             : 
     282           0 :         for (oSubBox.ReadFirstChild(this); strlen(oSubBox.GetType()) > 0;
     283           0 :              oSubBox.ReadNextChild(this))
     284             :         {
     285           0 :             oSubBox.DumpReadable(fpOut, nIndentLevel + 1);
     286             :         }
     287             :     }
     288             : 
     289           0 :     if (EQUAL(GetType(), "uuid"))
     290             :     {
     291           0 :         char *pszHex = CPLBinaryToHex(16, GetUUID());
     292           0 :         for (int i = 0; i < nIndentLevel; ++i)
     293           0 :             fprintf(fpOut, "  ");
     294             : 
     295           0 :         fprintf(fpOut, "    UUID=%s", pszHex);
     296             : 
     297           0 :         if (EQUAL(pszHex, "B14BF8BD083D4B43A5AE8CD7D5A6CE03"))
     298           0 :             fprintf(fpOut, " (GeoTIFF)");
     299           0 :         if (EQUAL(pszHex, "96A9F1F1DC98402DA7AED68E34451809"))
     300           0 :             fprintf(fpOut, " (MSI Worldfile)");
     301           0 :         if (EQUAL(pszHex, "BE7ACFCB97A942E89C71999491E3AFAC"))
     302           0 :             fprintf(fpOut, " (XMP)");
     303           0 :         CPLFree(pszHex);
     304             : 
     305           0 :         fprintf(fpOut, "\n");
     306             :     }
     307             : 
     308           0 :     return 0;
     309             : }
     310             : 
     311             : /************************************************************************/
     312             : /*                              SetType()                               */
     313             : /************************************************************************/
     314             : 
     315        2009 : void GDALJP2Box::SetType(const char *pszType)
     316             : 
     317             : {
     318        2009 :     CPLAssert(strlen(pszType) == 4);
     319             : 
     320        2009 :     memcpy(szBoxType, pszType, 4);
     321        2009 :     szBoxType[4] = '\0';
     322        2009 : }
     323             : 
     324             : /************************************************************************/
     325             : /*                          GetWritableBoxData()                        */
     326             : /************************************************************************/
     327             : 
     328          32 : GByte *GDALJP2Box::GetWritableBoxData() const
     329             : {
     330          32 :     CPLAssert(static_cast<GUInt32>(nBoxLength) == 8 + abyData.size());
     331             :     GByte *pabyRet =
     332          32 :         static_cast<GByte *>(CPLMalloc(static_cast<GUInt32>(nBoxLength)));
     333          32 :     const GUInt32 nLBox = CPL_MSBWORD32(static_cast<GUInt32>(nBoxLength));
     334          32 :     memcpy(pabyRet, &nLBox, sizeof(GUInt32));
     335          32 :     memcpy(pabyRet + 4, szBoxType, 4);
     336          32 :     memcpy(pabyRet + 8, abyData.data(), abyData.size());
     337          32 :     return pabyRet;
     338             : }
     339             : 
     340             : /************************************************************************/
     341             : /*                          SetWritableData()                           */
     342             : /************************************************************************/
     343             : 
     344         775 : void GDALJP2Box::SetWritableData(int nLength, const GByte *pabyDataIn)
     345             : 
     346             : {
     347         775 :     abyData.assign(pabyDataIn, pabyDataIn + nLength);
     348             : 
     349         775 :     nBoxOffset = -9;  // Virtual offsets for data length computation.
     350         775 :     nDataOffset = -1;
     351             : 
     352         775 :     nBoxLength = 8 + nLength;
     353         775 : }
     354             : 
     355             : /************************************************************************/
     356             : /*                          AppendWritableData()                        */
     357             : /************************************************************************/
     358             : 
     359        4750 : void GDALJP2Box::AppendWritableData(int nLength, const void *pabyDataIn)
     360             : 
     361             : {
     362        4750 :     if (abyData.empty())
     363             :     {
     364        1229 :         nBoxOffset = -9;  // Virtual offsets for data length computation.
     365        1229 :         nDataOffset = -1;
     366        1229 :         nBoxLength = 8;
     367             :     }
     368             : 
     369        9500 :     abyData.insert(abyData.end(), static_cast<const GByte *>(pabyDataIn),
     370        4750 :                    static_cast<const GByte *>(pabyDataIn) + nLength);
     371             : 
     372        4750 :     nBoxLength += nLength;
     373        4750 : }
     374             : 
     375             : /************************************************************************/
     376             : /*                              AppendUInt32()                          */
     377             : /************************************************************************/
     378             : 
     379         872 : void GDALJP2Box::AppendUInt32(GUInt32 nVal)
     380             : {
     381         872 :     CPL_MSBPTR32(&nVal);
     382         872 :     AppendWritableData(4, &nVal);
     383         872 : }
     384             : 
     385             : /************************************************************************/
     386             : /*                              AppendUInt16()                          */
     387             : /************************************************************************/
     388             : 
     389         619 : void GDALJP2Box::AppendUInt16(GUInt16 nVal)
     390             : {
     391         619 :     CPL_MSBPTR16(&nVal);
     392         619 :     AppendWritableData(2, &nVal);
     393         619 : }
     394             : 
     395             : /************************************************************************/
     396             : /*                              AppendUInt8()                           */
     397             : /************************************************************************/
     398             : 
     399        2010 : void GDALJP2Box::AppendUInt8(GByte nVal)
     400             : {
     401        2010 :     AppendWritableData(1, &nVal);
     402        2010 : }
     403             : 
     404             : /************************************************************************/
     405             : /*                           CreateUUIDBox()                            */
     406             : /************************************************************************/
     407             : 
     408         229 : GDALJP2Box *GDALJP2Box::CreateUUIDBox(const GByte *pabyUUID, int nDataSize,
     409             :                                       const GByte *pabyDataIn)
     410             : 
     411             : {
     412         229 :     GDALJP2Box *const poBox = new GDALJP2Box();
     413         229 :     poBox->SetType("uuid");
     414             : 
     415         229 :     poBox->AppendWritableData(16, pabyUUID);
     416         229 :     poBox->AppendWritableData(nDataSize, pabyDataIn);
     417             : 
     418         229 :     return poBox;
     419             : }
     420             : 
     421             : /************************************************************************/
     422             : /*                           CreateAsocBox()                            */
     423             : /************************************************************************/
     424             : 
     425         200 : GDALJP2Box *GDALJP2Box::CreateAsocBox(int nCount,
     426             :                                       const GDALJP2Box *const *papoBoxes)
     427             : {
     428         200 :     return CreateSuperBox("asoc", nCount, papoBoxes);
     429             : }
     430             : 
     431             : /************************************************************************/
     432             : /*                           CreateAsocBox()                            */
     433             : /************************************************************************/
     434             : 
     435         458 : GDALJP2Box *GDALJP2Box::CreateSuperBox(const char *pszType, int nCount,
     436             :                                        const GDALJP2Box *const *papoBoxes)
     437             : {
     438         458 :     int nDataSize = 0;
     439             : 
     440             :     /* -------------------------------------------------------------------- */
     441             :     /*      Compute size of data area of asoc box.                          */
     442             :     /* -------------------------------------------------------------------- */
     443        1421 :     for (int iBox = 0; iBox < nCount; ++iBox)
     444         963 :         nDataSize += 8 + static_cast<int>(papoBoxes[iBox]->GetDataLength());
     445             : 
     446         458 :     GByte *pabyNext = static_cast<GByte *>(CPLMalloc(nDataSize));
     447         458 :     GByte *pabyCompositeData = pabyNext;
     448             : 
     449             :     /* -------------------------------------------------------------------- */
     450             :     /*      Copy subboxes headers and data into buffer.                     */
     451             :     /* -------------------------------------------------------------------- */
     452        1421 :     for (int iBox = 0; iBox < nCount; ++iBox)
     453             :     {
     454         963 :         GUInt32 nLBox =
     455         963 :             CPL_MSBWORD32(static_cast<GUInt32>(papoBoxes[iBox]->nBoxLength));
     456         963 :         memcpy(pabyNext, &nLBox, 4);
     457         963 :         pabyNext += 4;
     458             : 
     459         963 :         memcpy(pabyNext, papoBoxes[iBox]->szBoxType, 4);
     460         963 :         pabyNext += 4;
     461             : 
     462         963 :         memcpy(pabyNext, papoBoxes[iBox]->abyData.data(),
     463         963 :                papoBoxes[iBox]->abyData.size());
     464         963 :         pabyNext += papoBoxes[iBox]->GetDataLength();
     465             :     }
     466             : 
     467             :     /* -------------------------------------------------------------------- */
     468             :     /*      Create asoc box.                                                */
     469             :     /* -------------------------------------------------------------------- */
     470         458 :     GDALJP2Box *const poAsoc = new GDALJP2Box();
     471             : 
     472         458 :     poAsoc->SetType(pszType);
     473         458 :     poAsoc->SetWritableData(nDataSize, pabyCompositeData);
     474             : 
     475         458 :     CPLFree(pabyCompositeData);
     476             : 
     477         458 :     return poAsoc;
     478             : }
     479             : 
     480             : /************************************************************************/
     481             : /*                            CreateLblBox()                            */
     482             : /************************************************************************/
     483             : 
     484          87 : GDALJP2Box *GDALJP2Box::CreateLblBox(const char *pszLabel)
     485             : 
     486             : {
     487          87 :     GDALJP2Box *const poBox = new GDALJP2Box();
     488          87 :     poBox->SetType("lbl ");
     489          87 :     poBox->SetWritableData(static_cast<int>(strlen(pszLabel)),
     490             :                            reinterpret_cast<const GByte *>(pszLabel));
     491             : 
     492          87 :     return poBox;
     493             : }
     494             : 
     495             : /************************************************************************/
     496             : /*                       CreateLabelledXMLAssoc()                       */
     497             : /************************************************************************/
     498             : 
     499         108 : GDALJP2Box *GDALJP2Box::CreateLabelledXMLAssoc(const char *pszLabel,
     500             :                                                const char *pszXML)
     501             : 
     502             : {
     503         216 :     GDALJP2Box oLabel;
     504         108 :     oLabel.SetType("lbl ");
     505         108 :     oLabel.SetWritableData(static_cast<int>(strlen(pszLabel)),
     506             :                            reinterpret_cast<const GByte *>(pszLabel));
     507             : 
     508         216 :     GDALJP2Box oXML;
     509         108 :     oXML.SetType("xml ");
     510         108 :     oXML.SetWritableData(static_cast<int>(strlen(pszXML)),
     511             :                          reinterpret_cast<const GByte *>(pszXML));
     512             : 
     513         108 :     GDALJP2Box *aoList[2] = {&oLabel, &oXML};
     514             : 
     515         216 :     return CreateAsocBox(2, aoList);
     516             : }
     517             : 
     518             : /************************************************************************/
     519             : /*                    CreateJUMBFDescriptionBox()                       */
     520             : /************************************************************************/
     521             : 
     522          40 : GDALJP2Box *GDALJP2Box::CreateJUMBFDescriptionBox(const GByte *pabyUUIDType,
     523             :                                                   const char *pszLabel)
     524             : 
     525             : {
     526          40 :     GDALJP2Box *const poBox = new GDALJP2Box();
     527          40 :     poBox->SetType("jumd");
     528             : 
     529          40 :     poBox->AppendWritableData(16, pabyUUIDType);
     530          40 :     poBox->AppendUInt8(3);  // requestable field
     531             :     // +1 since NUL terminated byte required in the JUMBF spec
     532             :     // cf other implementation at https://gitlab.com/wg1/jpeg-systems/reference-software/jumbf-reference-implementation-2/-/blame/main/dbench_jumbf/src/db_jumbf_desc_box.cpp?ref_type=heads#L169
     533          40 :     const size_t nLabelLen = strlen(pszLabel) + 1;
     534          40 :     poBox->AppendWritableData(static_cast<int>(nLabelLen), pszLabel);
     535             : 
     536          40 :     return poBox;
     537             : }
     538             : 
     539             : /************************************************************************/
     540             : /*                         CreateJUMBFBox()                             */
     541             : /************************************************************************/
     542             : 
     543          40 : GDALJP2Box *GDALJP2Box::CreateJUMBFBox(const GDALJP2Box *poJUMBFDescriptionBox,
     544             :                                        int nCount,
     545             :                                        const GDALJP2Box *const *papoBoxes)
     546             : {
     547          80 :     std::vector<const GDALJP2Box *> apoBoxes;
     548          40 :     apoBoxes.push_back(poJUMBFDescriptionBox);
     549          40 :     apoBoxes.insert(apoBoxes.end(), papoBoxes, papoBoxes + nCount);
     550          40 :     return CreateSuperBox("jumb", static_cast<int>(apoBoxes.size()),
     551         120 :                           apoBoxes.data());
     552             : }
     553             : 
     554             : /*! @endcond */

Generated by: LCOV version 1.14