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

Generated by: LCOV version 1.14