LCOV - code coverage report
Current view: top level - gcore - gdaljp2box.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 166 213 77.9 %
Date: 2025-12-05 02:43:06 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(GIntBig 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, nDataOffset, SEEK_SET) != 0)
     171           0 :             return FALSE;
     172             :     }
     173             : 
     174       18375 :     if (EQUAL(szBoxType, "uuid"))
     175             :     {
     176        2774 :         if (VSIFReadL(abyUUID, 16, 1, fpVSIL) != 1)
     177           0 :             return FALSE;
     178        2774 :         nDataOffset += 16;
     179             :     }
     180             : 
     181       18375 :     if (m_bAllowGetFileSize && GetDataLength() < 0)
     182             :     {
     183           0 :         CPLDebug("GDALJP2", "Invalid length for box %s", szBoxType);
     184           0 :         return FALSE;
     185             :     }
     186             : 
     187       18375 :     return TRUE;
     188             : }
     189             : 
     190             : /************************************************************************/
     191             : /*                             IsSuperBox()                             */
     192             : /************************************************************************/
     193             : 
     194        2698 : int GDALJP2Box::IsSuperBox()
     195             : 
     196             : {
     197        4589 :     if (EQUAL(GetType(), "asoc") || EQUAL(GetType(), "jp2h") ||
     198        4589 :         EQUAL(GetType(), "res ") || EQUAL(GetType(), "jumb"))
     199        2377 :         return TRUE;
     200             : 
     201         321 :     return FALSE;
     202             : }
     203             : 
     204             : /************************************************************************/
     205             : /*                            ReadBoxData()                             */
     206             : /************************************************************************/
     207             : 
     208        2805 : GByte *GDALJP2Box::ReadBoxData()
     209             : 
     210             : {
     211        2805 :     GIntBig nDataLength = GetDataLength();
     212        2805 :     if (nDataLength > 100 * 1024 * 1024)
     213             :     {
     214           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     215             :                  "Too big box : " CPL_FRMT_GIB " bytes", nDataLength);
     216           0 :         return nullptr;
     217             :     }
     218             : 
     219        2805 :     if (VSIFSeekL(fpVSIL, nDataOffset, SEEK_SET) != 0)
     220           0 :         return nullptr;
     221             : 
     222             :     char *pszData = static_cast<char *>(
     223        2805 :         VSI_MALLOC_VERBOSE(static_cast<int>(nDataLength) + 1));
     224        2805 :     if (pszData == nullptr)
     225           0 :         return nullptr;
     226             : 
     227        2805 :     if (static_cast<GIntBig>(VSIFReadL(
     228        2805 :             pszData, 1, static_cast<int>(nDataLength), fpVSIL)) != nDataLength)
     229             :     {
     230           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot read box content");
     231           0 :         CPLFree(pszData);
     232           0 :         return nullptr;
     233             :     }
     234             : 
     235        2805 :     pszData[nDataLength] = '\0';
     236             : 
     237        2805 :     return reinterpret_cast<GByte *>(pszData);
     238             : }
     239             : 
     240             : /************************************************************************/
     241             : /*                           GetDataLength()                            */
     242             : /************************************************************************/
     243             : 
     244       29466 : GIntBig GDALJP2Box::GetDataLength() const
     245             : {
     246       29466 :     return nBoxLength - (nDataOffset - nBoxOffset);
     247             : }
     248             : 
     249             : /************************************************************************/
     250             : /*                            DumpReadable()                            */
     251             : /************************************************************************/
     252             : 
     253           0 : int GDALJP2Box::DumpReadable(FILE *fpOut, int nIndentLevel)
     254             : 
     255             : {
     256           0 :     if (fpOut == nullptr)
     257           0 :         fpOut = stdout;
     258             : 
     259           0 :     for (int i = 0; i < nIndentLevel; ++i)
     260           0 :         fprintf(fpOut, "  ");
     261             : 
     262             :     char szBuffer[128];
     263           0 :     CPLsnprintf(szBuffer, sizeof(szBuffer),
     264             :                 "  Type=%s, Offset=" CPL_FRMT_GIB "/" CPL_FRMT_GIB
     265             :                 ", Data Size=" CPL_FRMT_GIB,
     266           0 :                 szBoxType, nBoxOffset, nDataOffset, GetDataLength());
     267           0 :     fprintf(fpOut, "%s", szBuffer);
     268             : 
     269           0 :     if (IsSuperBox())
     270             :     {
     271           0 :         fprintf(fpOut, " (super)");
     272             :     }
     273             : 
     274           0 :     fprintf(fpOut, "\n");
     275             : 
     276           0 :     if (IsSuperBox())
     277             :     {
     278           0 :         GDALJP2Box oSubBox(GetFILE());
     279             : 
     280           0 :         for (oSubBox.ReadFirstChild(this); strlen(oSubBox.GetType()) > 0;
     281           0 :              oSubBox.ReadNextChild(this))
     282             :         {
     283           0 :             oSubBox.DumpReadable(fpOut, nIndentLevel + 1);
     284             :         }
     285             :     }
     286             : 
     287           0 :     if (EQUAL(GetType(), "uuid"))
     288             :     {
     289           0 :         char *pszHex = CPLBinaryToHex(16, GetUUID());
     290           0 :         for (int i = 0; i < nIndentLevel; ++i)
     291           0 :             fprintf(fpOut, "  ");
     292             : 
     293           0 :         fprintf(fpOut, "    UUID=%s", pszHex);
     294             : 
     295           0 :         if (EQUAL(pszHex, "B14BF8BD083D4B43A5AE8CD7D5A6CE03"))
     296           0 :             fprintf(fpOut, " (GeoTIFF)");
     297           0 :         if (EQUAL(pszHex, "96A9F1F1DC98402DA7AED68E34451809"))
     298           0 :             fprintf(fpOut, " (MSI Worldfile)");
     299           0 :         if (EQUAL(pszHex, "BE7ACFCB97A942E89C71999491E3AFAC"))
     300           0 :             fprintf(fpOut, " (XMP)");
     301           0 :         CPLFree(pszHex);
     302             : 
     303           0 :         fprintf(fpOut, "\n");
     304             :     }
     305             : 
     306           0 :     return 0;
     307             : }
     308             : 
     309             : /************************************************************************/
     310             : /*                              SetType()                               */
     311             : /************************************************************************/
     312             : 
     313        2009 : void GDALJP2Box::SetType(const char *pszType)
     314             : 
     315             : {
     316        2009 :     CPLAssert(strlen(pszType) == 4);
     317             : 
     318        2009 :     memcpy(szBoxType, pszType, 4);
     319        2009 :     szBoxType[4] = '\0';
     320        2009 : }
     321             : 
     322             : /************************************************************************/
     323             : /*                          GetWritableBoxData()                        */
     324             : /************************************************************************/
     325             : 
     326          32 : GByte *GDALJP2Box::GetWritableBoxData() const
     327             : {
     328          32 :     CPLAssert(static_cast<GUInt32>(nBoxLength) == 8 + abyData.size());
     329             :     GByte *pabyRet =
     330          32 :         static_cast<GByte *>(CPLMalloc(static_cast<GUInt32>(nBoxLength)));
     331          32 :     const GUInt32 nLBox = CPL_MSBWORD32(static_cast<GUInt32>(nBoxLength));
     332          32 :     memcpy(pabyRet, &nLBox, sizeof(GUInt32));
     333          32 :     memcpy(pabyRet + 4, szBoxType, 4);
     334          32 :     memcpy(pabyRet + 8, abyData.data(), abyData.size());
     335          32 :     return pabyRet;
     336             : }
     337             : 
     338             : /************************************************************************/
     339             : /*                          SetWritableData()                           */
     340             : /************************************************************************/
     341             : 
     342         775 : void GDALJP2Box::SetWritableData(int nLength, const GByte *pabyDataIn)
     343             : 
     344             : {
     345         775 :     abyData.assign(pabyDataIn, pabyDataIn + nLength);
     346             : 
     347         775 :     nBoxOffset = -9;  // Virtual offsets for data length computation.
     348         775 :     nDataOffset = -1;
     349             : 
     350         775 :     nBoxLength = 8 + nLength;
     351         775 : }
     352             : 
     353             : /************************************************************************/
     354             : /*                          AppendWritableData()                        */
     355             : /************************************************************************/
     356             : 
     357        4750 : void GDALJP2Box::AppendWritableData(int nLength, const void *pabyDataIn)
     358             : 
     359             : {
     360        4750 :     if (abyData.empty())
     361             :     {
     362        1229 :         nBoxOffset = -9;  // Virtual offsets for data length computation.
     363        1229 :         nDataOffset = -1;
     364        1229 :         nBoxLength = 8;
     365             :     }
     366             : 
     367        9500 :     abyData.insert(abyData.end(), static_cast<const GByte *>(pabyDataIn),
     368        4750 :                    static_cast<const GByte *>(pabyDataIn) + nLength);
     369             : 
     370        4750 :     nBoxLength += nLength;
     371        4750 : }
     372             : 
     373             : /************************************************************************/
     374             : /*                              AppendUInt32()                          */
     375             : /************************************************************************/
     376             : 
     377         872 : void GDALJP2Box::AppendUInt32(GUInt32 nVal)
     378             : {
     379         872 :     CPL_MSBPTR32(&nVal);
     380         872 :     AppendWritableData(4, &nVal);
     381         872 : }
     382             : 
     383             : /************************************************************************/
     384             : /*                              AppendUInt16()                          */
     385             : /************************************************************************/
     386             : 
     387         619 : void GDALJP2Box::AppendUInt16(GUInt16 nVal)
     388             : {
     389         619 :     CPL_MSBPTR16(&nVal);
     390         619 :     AppendWritableData(2, &nVal);
     391         619 : }
     392             : 
     393             : /************************************************************************/
     394             : /*                              AppendUInt8()                           */
     395             : /************************************************************************/
     396             : 
     397        2010 : void GDALJP2Box::AppendUInt8(GByte nVal)
     398             : {
     399        2010 :     AppendWritableData(1, &nVal);
     400        2010 : }
     401             : 
     402             : /************************************************************************/
     403             : /*                           CreateUUIDBox()                            */
     404             : /************************************************************************/
     405             : 
     406         229 : GDALJP2Box *GDALJP2Box::CreateUUIDBox(const GByte *pabyUUID, int nDataSize,
     407             :                                       const GByte *pabyDataIn)
     408             : 
     409             : {
     410         229 :     GDALJP2Box *const poBox = new GDALJP2Box();
     411         229 :     poBox->SetType("uuid");
     412             : 
     413         229 :     poBox->AppendWritableData(16, pabyUUID);
     414         229 :     poBox->AppendWritableData(nDataSize, pabyDataIn);
     415             : 
     416         229 :     return poBox;
     417             : }
     418             : 
     419             : /************************************************************************/
     420             : /*                           CreateAsocBox()                            */
     421             : /************************************************************************/
     422             : 
     423         200 : GDALJP2Box *GDALJP2Box::CreateAsocBox(int nCount,
     424             :                                       const GDALJP2Box *const *papoBoxes)
     425             : {
     426         200 :     return CreateSuperBox("asoc", nCount, papoBoxes);
     427             : }
     428             : 
     429             : /************************************************************************/
     430             : /*                           CreateAsocBox()                            */
     431             : /************************************************************************/
     432             : 
     433         458 : GDALJP2Box *GDALJP2Box::CreateSuperBox(const char *pszType, int nCount,
     434             :                                        const GDALJP2Box *const *papoBoxes)
     435             : {
     436         458 :     int nDataSize = 0;
     437             : 
     438             :     /* -------------------------------------------------------------------- */
     439             :     /*      Compute size of data area of asoc box.                          */
     440             :     /* -------------------------------------------------------------------- */
     441        1421 :     for (int iBox = 0; iBox < nCount; ++iBox)
     442         963 :         nDataSize += 8 + static_cast<int>(papoBoxes[iBox]->GetDataLength());
     443             : 
     444         458 :     GByte *pabyNext = static_cast<GByte *>(CPLMalloc(nDataSize));
     445         458 :     GByte *pabyCompositeData = pabyNext;
     446             : 
     447             :     /* -------------------------------------------------------------------- */
     448             :     /*      Copy subboxes headers and data into buffer.                     */
     449             :     /* -------------------------------------------------------------------- */
     450        1421 :     for (int iBox = 0; iBox < nCount; ++iBox)
     451             :     {
     452         963 :         GUInt32 nLBox =
     453         963 :             CPL_MSBWORD32(static_cast<GUInt32>(papoBoxes[iBox]->nBoxLength));
     454         963 :         memcpy(pabyNext, &nLBox, 4);
     455         963 :         pabyNext += 4;
     456             : 
     457         963 :         memcpy(pabyNext, papoBoxes[iBox]->szBoxType, 4);
     458         963 :         pabyNext += 4;
     459             : 
     460         963 :         memcpy(pabyNext, papoBoxes[iBox]->abyData.data(),
     461         963 :                papoBoxes[iBox]->abyData.size());
     462         963 :         pabyNext += papoBoxes[iBox]->GetDataLength();
     463             :     }
     464             : 
     465             :     /* -------------------------------------------------------------------- */
     466             :     /*      Create asoc box.                                                */
     467             :     /* -------------------------------------------------------------------- */
     468         458 :     GDALJP2Box *const poAsoc = new GDALJP2Box();
     469             : 
     470         458 :     poAsoc->SetType(pszType);
     471         458 :     poAsoc->SetWritableData(nDataSize, pabyCompositeData);
     472             : 
     473         458 :     CPLFree(pabyCompositeData);
     474             : 
     475         458 :     return poAsoc;
     476             : }
     477             : 
     478             : /************************************************************************/
     479             : /*                            CreateLblBox()                            */
     480             : /************************************************************************/
     481             : 
     482          87 : GDALJP2Box *GDALJP2Box::CreateLblBox(const char *pszLabel)
     483             : 
     484             : {
     485          87 :     GDALJP2Box *const poBox = new GDALJP2Box();
     486          87 :     poBox->SetType("lbl ");
     487          87 :     poBox->SetWritableData(static_cast<int>(strlen(pszLabel)),
     488             :                            reinterpret_cast<const GByte *>(pszLabel));
     489             : 
     490          87 :     return poBox;
     491             : }
     492             : 
     493             : /************************************************************************/
     494             : /*                       CreateLabelledXMLAssoc()                       */
     495             : /************************************************************************/
     496             : 
     497         108 : GDALJP2Box *GDALJP2Box::CreateLabelledXMLAssoc(const char *pszLabel,
     498             :                                                const char *pszXML)
     499             : 
     500             : {
     501         216 :     GDALJP2Box oLabel;
     502         108 :     oLabel.SetType("lbl ");
     503         108 :     oLabel.SetWritableData(static_cast<int>(strlen(pszLabel)),
     504             :                            reinterpret_cast<const GByte *>(pszLabel));
     505             : 
     506         216 :     GDALJP2Box oXML;
     507         108 :     oXML.SetType("xml ");
     508         108 :     oXML.SetWritableData(static_cast<int>(strlen(pszXML)),
     509             :                          reinterpret_cast<const GByte *>(pszXML));
     510             : 
     511         108 :     GDALJP2Box *aoList[2] = {&oLabel, &oXML};
     512             : 
     513         216 :     return CreateAsocBox(2, aoList);
     514             : }
     515             : 
     516             : /************************************************************************/
     517             : /*                    CreateJUMBFDescriptionBox()                       */
     518             : /************************************************************************/
     519             : 
     520          40 : GDALJP2Box *GDALJP2Box::CreateJUMBFDescriptionBox(const GByte *pabyUUIDType,
     521             :                                                   const char *pszLabel)
     522             : 
     523             : {
     524          40 :     GDALJP2Box *const poBox = new GDALJP2Box();
     525          40 :     poBox->SetType("jumd");
     526             : 
     527          40 :     poBox->AppendWritableData(16, pabyUUIDType);
     528          40 :     poBox->AppendUInt8(3);  // requestable field
     529             :     // +1 since NUL terminated byte required in the JUMBF spec
     530             :     // 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
     531          40 :     const size_t nLabelLen = strlen(pszLabel) + 1;
     532          40 :     poBox->AppendWritableData(static_cast<int>(nLabelLen), pszLabel);
     533             : 
     534          40 :     return poBox;
     535             : }
     536             : 
     537             : /************************************************************************/
     538             : /*                         CreateJUMBFBox()                             */
     539             : /************************************************************************/
     540             : 
     541          40 : GDALJP2Box *GDALJP2Box::CreateJUMBFBox(const GDALJP2Box *poJUMBFDescriptionBox,
     542             :                                        int nCount,
     543             :                                        const GDALJP2Box *const *papoBoxes)
     544             : {
     545          80 :     std::vector<const GDALJP2Box *> apoBoxes;
     546          40 :     apoBoxes.push_back(poJUMBFDescriptionBox);
     547          40 :     apoBoxes.insert(apoBoxes.end(), papoBoxes, papoBoxes + nCount);
     548          40 :     return CreateSuperBox("jumb", static_cast<int>(apoBoxes.size()),
     549         120 :                           apoBoxes.data());
     550             : }
     551             : 
     552             : /*! @endcond */

Generated by: LCOV version 1.14