LCOV - code coverage report
Current view: top level - gcore - gdalorienteddataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 121 142 85.2 %
Date: 2024-11-21 22:18:42 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Dataset that modifies the orientation of an underlying dataset
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys dot com>
       6             :  *
       7             :  **********************************************************************
       8             :  * Copyright (c) 2022, Even Rouault, <even dot rouault at spatialys dot com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalorienteddataset.h"
      14             : 
      15             : #include "gdal_utils.h"
      16             : 
      17             : #include <algorithm>
      18             : 
      19             : //! @cond Doxygen_Suppress
      20             : 
      21             : /************************************************************************/
      22             : /*                      GDALOrientedRasterBand                          */
      23             : /************************************************************************/
      24             : 
      25             : class GDALOrientedRasterBand : public GDALRasterBand
      26             : {
      27             :     GDALRasterBand *m_poSrcBand;
      28             :     std::unique_ptr<GDALDataset> m_poCacheDS{};
      29             : 
      30             :     GDALOrientedRasterBand(const GDALOrientedRasterBand &) = delete;
      31             :     GDALOrientedRasterBand &operator=(const GDALOrientedRasterBand &) = delete;
      32             : 
      33             :   protected:
      34             :     CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
      35             : 
      36             :   public:
      37             :     GDALOrientedRasterBand(GDALOrientedDataset *poDSIn, int nBandIn);
      38             : 
      39           7 :     GDALColorInterp GetColorInterpretation() override
      40             :     {
      41           7 :         return m_poSrcBand->GetColorInterpretation();
      42             :     }
      43             : };
      44             : 
      45             : /************************************************************************/
      46             : /*                      GDALOrientedRasterBand()                        */
      47             : /************************************************************************/
      48             : 
      49          14 : GDALOrientedRasterBand::GDALOrientedRasterBand(GDALOrientedDataset *poDSIn,
      50          14 :                                                int nBandIn)
      51          14 :     : m_poSrcBand(poDSIn->m_poSrcDS->GetRasterBand(nBandIn))
      52             : {
      53          14 :     poDS = poDSIn;
      54          14 :     eDataType = m_poSrcBand->GetRasterDataType();
      55          14 :     if (poDSIn->m_eOrigin == GDALOrientedDataset::Origin::TOP_LEFT)
      56             :     {
      57           0 :         m_poSrcBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
      58             :     }
      59             :     else
      60             :     {
      61          14 :         nBlockXSize = poDS->GetRasterXSize();
      62          14 :         nBlockYSize = 1;
      63             :     }
      64          14 : }
      65             : 
      66             : /************************************************************************/
      67             : /*                  FlipLineHorizontally()                              */
      68             : /************************************************************************/
      69             : 
      70          40 : static void FlipLineHorizontally(void *pLine, int nDTSize, int nBlockXSize)
      71             : {
      72          40 :     switch (nDTSize)
      73             :     {
      74          40 :         case 1:
      75             :         {
      76          40 :             GByte *pabyLine = static_cast<GByte *>(pLine);
      77          80 :             for (int iX = 0; iX < nBlockXSize / 2; ++iX)
      78             :             {
      79          40 :                 std::swap(pabyLine[iX], pabyLine[nBlockXSize - 1 - iX]);
      80             :             }
      81          40 :             break;
      82             :         }
      83             : 
      84           0 :         default:
      85             :         {
      86           0 :             GByte *pabyLine = static_cast<GByte *>(pLine);
      87           0 :             std::vector<GByte> abyTemp(nDTSize);
      88           0 :             for (int iX = 0; iX < nBlockXSize / 2; ++iX)
      89             :             {
      90           0 :                 memcpy(&abyTemp[0], pabyLine + iX * nDTSize, nDTSize);
      91           0 :                 memcpy(pabyLine + iX * nDTSize,
      92           0 :                        pabyLine + (nBlockXSize - 1 - iX) * nDTSize, nDTSize);
      93           0 :                 memcpy(pabyLine + (nBlockXSize - 1 - iX) * nDTSize, &abyTemp[0],
      94             :                        nDTSize);
      95             :             }
      96           0 :             break;
      97             :         }
      98             :     }
      99          40 : }
     100             : 
     101             : /************************************************************************/
     102             : /*                            IReadBlock()                              */
     103             : /************************************************************************/
     104             : 
     105          70 : CPLErr GDALOrientedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     106             :                                           void *pImage)
     107             : {
     108          70 :     auto l_poDS = cpl::down_cast<GDALOrientedDataset *>(poDS);
     109             : 
     110          70 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
     111          70 :     if (m_poCacheDS == nullptr &&
     112          92 :         l_poDS->m_eOrigin != GDALOrientedDataset::Origin::TOP_LEFT &&
     113          22 :         l_poDS->m_eOrigin != GDALOrientedDataset::Origin::TOP_RIGHT)
     114             :     {
     115          12 :         auto poGTiffDrv = GetGDALDriverManager()->GetDriverByName("GTiff");
     116          12 :         CPLStringList aosOptions;
     117          12 :         aosOptions.AddString("-f");
     118          12 :         aosOptions.AddString(poGTiffDrv ? "GTiff" : "MEM");
     119          12 :         aosOptions.AddString("-b");
     120          12 :         aosOptions.AddString(CPLSPrintf("%d", nBand));
     121          12 :         if (poGTiffDrv)
     122             :         {
     123          12 :             aosOptions.AddString("-co");
     124          12 :             aosOptions.AddString("TILED=YES");
     125             :         }
     126          12 :         std::string osTmpName;
     127          12 :         if (poGTiffDrv)
     128             :         {
     129          12 :             if (static_cast<GIntBig>(nRasterXSize) * nRasterYSize * nDTSize >
     130             :                 10 * 1024 * 1024)
     131             :             {
     132           0 :                 osTmpName = CPLGenerateTempFilename(nullptr);
     133             :             }
     134             :             else
     135             :             {
     136          12 :                 osTmpName = VSIMemGenerateHiddenFilename(nullptr);
     137             :             }
     138             :         }
     139             :         GDALTranslateOptions *psOptions =
     140          12 :             GDALTranslateOptionsNew(aosOptions.List(), nullptr);
     141          12 :         if (psOptions == nullptr)
     142           0 :             return CE_Failure;
     143          12 :         GDALDatasetH hOutDS = GDALTranslate(
     144             :             osTmpName.c_str(), GDALDataset::ToHandle(l_poDS->m_poSrcDS),
     145             :             psOptions, nullptr);
     146          12 :         GDALTranslateOptionsFree(psOptions);
     147          12 :         if (hOutDS == nullptr)
     148           0 :             return CE_Failure;
     149          12 :         m_poCacheDS.reset(GDALDataset::FromHandle(hOutDS));
     150          12 :         m_poCacheDS->MarkSuppressOnClose();
     151             :     }
     152             : 
     153          70 :     CPLErr eErr = CE_None;
     154          70 :     switch (l_poDS->m_eOrigin)
     155             :     {
     156           0 :         case GDALOrientedDataset::Origin::TOP_LEFT:
     157             :         {
     158           0 :             eErr = m_poSrcBand->ReadBlock(nBlockXOff, nBlockYOff, pImage);
     159           0 :             break;
     160             :         }
     161             : 
     162          10 :         case GDALOrientedDataset::Origin::TOP_RIGHT:
     163             :         {
     164          10 :             CPLAssert(nBlockXSize == nRasterXSize);
     165          10 :             CPLAssert(nBlockYSize == 1);
     166          10 :             if (m_poSrcBand->RasterIO(GF_Read, 0, nBlockYOff, nRasterXSize, 1,
     167             :                                       pImage, nRasterXSize, 1, eDataType, 0, 0,
     168          10 :                                       nullptr) != CE_None)
     169             :             {
     170           0 :                 return CE_Failure;
     171             :             }
     172          10 :             FlipLineHorizontally(pImage, nDTSize, nBlockXSize);
     173          10 :             break;
     174             :         }
     175             : 
     176          20 :         case GDALOrientedDataset::Origin::BOT_RIGHT:
     177             :         case GDALOrientedDataset::Origin::BOT_LEFT:
     178             :         {
     179          20 :             CPLAssert(nBlockXSize == nRasterXSize);
     180          20 :             CPLAssert(nBlockYSize == 1);
     181          20 :             if (m_poCacheDS->GetRasterBand(1)->RasterIO(
     182          20 :                     GF_Read, 0, nRasterYSize - 1 - nBlockYOff, nRasterXSize, 1,
     183             :                     pImage, nRasterXSize, 1, eDataType, 0, 0,
     184          20 :                     nullptr) != CE_None)
     185             :             {
     186           0 :                 return CE_Failure;
     187             :             }
     188          20 :             if (l_poDS->m_eOrigin == GDALOrientedDataset::Origin::BOT_RIGHT)
     189          10 :                 FlipLineHorizontally(pImage, nDTSize, nBlockXSize);
     190          20 :             break;
     191             :         }
     192             : 
     193          20 :         case GDALOrientedDataset::Origin::LEFT_TOP:
     194             :         case GDALOrientedDataset::Origin::RIGHT_TOP:
     195             :         {
     196          20 :             CPLAssert(nBlockXSize == nRasterXSize);
     197          20 :             CPLAssert(nBlockYSize == 1);
     198          20 :             if (m_poCacheDS->GetRasterBand(1)->RasterIO(
     199             :                     GF_Read, nBlockYOff, 0, 1, nRasterXSize, pImage, 1,
     200          20 :                     nRasterXSize, eDataType, 0, 0, nullptr) != CE_None)
     201             :             {
     202           0 :                 return CE_Failure;
     203             :             }
     204          20 :             if (l_poDS->m_eOrigin == GDALOrientedDataset::Origin::RIGHT_TOP)
     205          10 :                 FlipLineHorizontally(pImage, nDTSize, nBlockXSize);
     206          20 :             break;
     207             :         }
     208             : 
     209          20 :         case GDALOrientedDataset::Origin::RIGHT_BOT:
     210             :         case GDALOrientedDataset::Origin::LEFT_BOT:
     211             :         {
     212          20 :             CPLAssert(nBlockXSize == nRasterXSize);
     213          20 :             CPLAssert(nBlockYSize == 1);
     214          20 :             if (m_poCacheDS->GetRasterBand(1)->RasterIO(
     215          20 :                     GF_Read, nRasterYSize - 1 - nBlockYOff, 0, 1, nRasterXSize,
     216             :                     pImage, 1, nRasterXSize, eDataType, 0, 0,
     217          20 :                     nullptr) != CE_None)
     218             :             {
     219           0 :                 return CE_Failure;
     220             :             }
     221          20 :             if (l_poDS->m_eOrigin == GDALOrientedDataset::Origin::RIGHT_BOT)
     222          10 :                 FlipLineHorizontally(pImage, nDTSize, nBlockXSize);
     223          20 :             break;
     224             :         }
     225             :     }
     226             : 
     227          70 :     return eErr;
     228             : }
     229             : 
     230             : /************************************************************************/
     231             : /*                         GDALOrientedDataset()                        */
     232             : /************************************************************************/
     233             : 
     234          14 : GDALOrientedDataset::GDALOrientedDataset(GDALDataset *poSrcDataset,
     235          14 :                                          Origin eOrigin)
     236          14 :     : m_poSrcDS(poSrcDataset), m_eOrigin(eOrigin)
     237             : {
     238          14 :     switch (eOrigin)
     239             :     {
     240           6 :         case GDALOrientedDataset::Origin::TOP_LEFT:
     241             :         case GDALOrientedDataset::Origin::TOP_RIGHT:
     242             :         case GDALOrientedDataset::Origin::BOT_RIGHT:
     243             :         case GDALOrientedDataset::Origin::BOT_LEFT:
     244             :         {
     245           6 :             nRasterXSize = poSrcDataset->GetRasterXSize();
     246           6 :             nRasterYSize = poSrcDataset->GetRasterYSize();
     247           6 :             break;
     248             :         }
     249             : 
     250           8 :         case GDALOrientedDataset::Origin::LEFT_TOP:
     251             :         case GDALOrientedDataset::Origin::RIGHT_TOP:
     252             :         case GDALOrientedDataset::Origin::RIGHT_BOT:
     253             :         case GDALOrientedDataset::Origin::LEFT_BOT:
     254             :         {
     255             :             // Permute (x, y)
     256           8 :             nRasterXSize = poSrcDataset->GetRasterYSize();
     257           8 :             nRasterYSize = poSrcDataset->GetRasterXSize();
     258           8 :             break;
     259             :         }
     260             :     }
     261             : 
     262          14 :     const int nSrcBands = poSrcDataset->GetRasterCount();
     263          28 :     for (int i = 1; i <= nSrcBands; ++i)
     264             :     {
     265          14 :         SetBand(i, new GDALOrientedRasterBand(this, i));
     266             :     }
     267          14 : }
     268             : 
     269             : /************************************************************************/
     270             : /*                         GDALOrientedDataset()                        */
     271             : /************************************************************************/
     272             : 
     273          14 : GDALOrientedDataset::GDALOrientedDataset(
     274          14 :     std::unique_ptr<GDALDataset> &&poSrcDataset, Origin eOrigin)
     275          14 :     : GDALOrientedDataset(poSrcDataset.get(), eOrigin)
     276             : {
     277             :     // cppcheck-suppress useInitializationList
     278          14 :     m_poSrcDSHolder = std::move(poSrcDataset);
     279          14 : }
     280             : 
     281             : /************************************************************************/
     282             : /*                           GetMetadata()                              */
     283             : /************************************************************************/
     284             : 
     285          28 : char **GDALOrientedDataset::GetMetadata(const char *pszDomain)
     286             : {
     287          28 :     if (pszDomain == nullptr || pszDomain[0] == '\0')
     288             :     {
     289          14 :         if (m_aosSrcMD.empty())
     290             :         {
     291           7 :             m_aosSrcMD.Assign(CSLDuplicate(m_poSrcDS->GetMetadata(pszDomain)));
     292             :             const char *pszOrientation =
     293           7 :                 m_aosSrcMD.FetchNameValue("EXIF_Orientation");
     294           7 :             if (pszOrientation)
     295             :             {
     296             :                 m_aosSrcMD.SetNameValue("original_EXIF_Orientation",
     297           7 :                                         pszOrientation);
     298           7 :                 m_aosSrcMD.SetNameValue("EXIF_Orientation", nullptr);
     299             :             }
     300             :         }
     301          14 :         return m_aosSrcMD.List();
     302             :     }
     303          14 :     if (EQUAL(pszDomain, "EXIF"))
     304             :     {
     305          14 :         if (m_aosSrcMD_EXIF.empty())
     306             :         {
     307             :             m_aosSrcMD_EXIF.Assign(
     308           7 :                 CSLDuplicate(m_poSrcDS->GetMetadata(pszDomain)));
     309             :             const char *pszOrientation =
     310           7 :                 m_aosSrcMD_EXIF.FetchNameValue("EXIF_Orientation");
     311           7 :             if (pszOrientation)
     312             :             {
     313             :                 m_aosSrcMD_EXIF.SetNameValue("original_EXIF_Orientation",
     314           7 :                                              pszOrientation);
     315           7 :                 m_aosSrcMD_EXIF.SetNameValue("EXIF_Orientation", nullptr);
     316             :             }
     317             :         }
     318          14 :         return m_aosSrcMD_EXIF.List();
     319             :     }
     320           0 :     return m_poSrcDS->GetMetadata(pszDomain);
     321             : }
     322             : 
     323             : /************************************************************************/
     324             : /*                       GetMetadataItem()                              */
     325             : /************************************************************************/
     326             : 
     327          28 : const char *GDALOrientedDataset::GetMetadataItem(const char *pszName,
     328             :                                                  const char *pszDomain)
     329             : {
     330          28 :     return CSLFetchNameValue(GetMetadata(pszDomain), pszName);
     331             : }
     332             : 
     333             : //! @endcond

Generated by: LCOV version 1.14