LCOV - code coverage report
Current view: top level - frmts/gtiff - gtiffjpegoverviewds.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 165 177 93.2 %
Date: 2026-06-27 16:33:51 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GeoTIFF Driver
       4             :  * Purpose:  GDAL GeoTIFF support.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1998, 2002, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2007-2015, Even Rouault <even dot rouault at spatialys dot com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gtiffjpegoverviewds.h"
      15             : 
      16             : #include "gtiffdataset.h"
      17             : #include "gdal_priv.h"
      18             : 
      19             : #include "tifvsi.h"
      20             : 
      21             : /************************************************************************/
      22             : /* ==================================================================== */
      23             : /*                     GTiffJPEGOverviewBand                            */
      24             : /* ==================================================================== */
      25             : /************************************************************************/
      26             : 
      27             : class GTiffJPEGOverviewBand final : public GDALRasterBand
      28             : {
      29             :   public:
      30             :     GTiffJPEGOverviewBand(GTiffJPEGOverviewDS *poDS, int nBand);
      31             : 
      32             :     CPLErr IReadBlock(int, int, void *) override;
      33             : 
      34           9 :     GDALColorInterp GetColorInterpretation() override
      35             :     {
      36           9 :         return cpl::down_cast<GTiffJPEGOverviewDS *>(poDS)
      37           9 :             ->m_poParentDS->GetRasterBand(nBand)
      38           9 :             ->GetColorInterpretation();
      39             :     }
      40             : };
      41             : 
      42             : /************************************************************************/
      43             : /*                        GTiffJPEGOverviewDS()                         */
      44             : /************************************************************************/
      45             : 
      46          60 : GTiffJPEGOverviewDS::GTiffJPEGOverviewDS(GTiffDataset *poParentDSIn,
      47             :                                          int nOverviewLevelIn,
      48             :                                          const void *pJPEGTable,
      49          60 :                                          int nJPEGTableSizeIn)
      50             :     : m_poParentDS(poParentDSIn), m_nOverviewLevel(nOverviewLevelIn),
      51          60 :       m_nJPEGTableSize(nJPEGTableSizeIn)
      52             : {
      53          60 :     ShareLockWithParentDataset(poParentDSIn);
      54             : 
      55          60 :     m_osTmpFilenameJPEGTable = VSIMemGenerateHiddenFilename("jpegtable");
      56             : 
      57          60 :     const GByte abyAdobeAPP14RGB[] = {0xFF, 0xEE, 0x00, 0x0E, 0x41, 0x64,
      58             :                                       0x6F, 0x62, 0x65, 0x00, 0x64, 0x00,
      59             :                                       0x00, 0x00, 0x00, 0x00};
      60          60 :     const bool bAddAdobe =
      61         105 :         m_poParentDS->m_nPlanarConfig == PLANARCONFIG_CONTIG &&
      62          90 :         m_poParentDS->m_nPhotometric != PHOTOMETRIC_YCBCR &&
      63          30 :         m_poParentDS->nBands == 3;
      64          60 :     m_pabyJPEGTable = static_cast<GByte *>(CPLMalloc(
      65          60 :         m_nJPEGTableSize + (bAddAdobe ? sizeof(abyAdobeAPP14RGB) : 0)));
      66          60 :     memcpy(m_pabyJPEGTable, pJPEGTable, m_nJPEGTableSize);
      67          60 :     if (bAddAdobe)
      68             :     {
      69           6 :         memcpy(m_pabyJPEGTable + m_nJPEGTableSize, abyAdobeAPP14RGB,
      70             :                sizeof(abyAdobeAPP14RGB));
      71           6 :         m_nJPEGTableSize += sizeof(abyAdobeAPP14RGB);
      72             :     }
      73          60 :     CPL_IGNORE_RET_VAL(VSIFCloseL(VSIFileFromMemBuffer(
      74          60 :         m_osTmpFilenameJPEGTable, m_pabyJPEGTable, m_nJPEGTableSize, TRUE)));
      75             : 
      76          60 :     const int nScaleFactor = 1 << m_nOverviewLevel;
      77          60 :     nRasterXSize = DIV_ROUND_UP(m_poParentDS->nRasterXSize, nScaleFactor);
      78          60 :     nRasterYSize = DIV_ROUND_UP(m_poParentDS->nRasterYSize, nScaleFactor);
      79             : 
      80         204 :     for (int i = 1; i <= m_poParentDS->nBands; ++i)
      81         144 :         SetBand(i, new GTiffJPEGOverviewBand(this, i));
      82             : 
      83          60 :     SetMetadataItem(GDALMD_INTERLEAVE, "PIXEL", GDAL_MDD_IMAGE_STRUCTURE);
      84          60 :     if (m_poParentDS->m_nPhotometric == PHOTOMETRIC_YCBCR)
      85          15 :         SetMetadataItem(GDALMD_COMPRESSION, "YCbCr JPEG",
      86             :                         GDAL_MDD_IMAGE_STRUCTURE);
      87             :     else
      88          45 :         SetMetadataItem(GDALMD_COMPRESSION, "JPEG", GDAL_MDD_IMAGE_STRUCTURE);
      89          60 : }
      90             : 
      91             : /************************************************************************/
      92             : /*                        ~GTiffJPEGOverviewDS()                        */
      93             : /************************************************************************/
      94             : 
      95         120 : GTiffJPEGOverviewDS::~GTiffJPEGOverviewDS()
      96             : {
      97          60 :     m_poJPEGDS.reset();
      98          60 :     VSIUnlink(m_osTmpFilenameJPEGTable);
      99          60 :     if (!m_osTmpFilename.empty())
     100          50 :         VSIUnlink(m_osTmpFilename);
     101         120 : }
     102             : 
     103             : /************************************************************************/
     104             : /*                             IRasterIO()                              */
     105             : /************************************************************************/
     106             : 
     107          32 : CPLErr GTiffJPEGOverviewDS::IRasterIO(
     108             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
     109             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
     110             :     int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
     111             :     GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
     112             : 
     113             : {
     114             :     // For non-single strip JPEG-IN-TIFF, the block based strategy will
     115             :     // be the most efficient one, to avoid decompressing the JPEG content
     116             :     // for each requested band.
     117          32 :     if (nBandCount > 1 &&
     118          22 :         m_poParentDS->m_nPlanarConfig == PLANARCONFIG_CONTIG &&
     119          14 :         (m_poParentDS->m_nBlockXSize < m_poParentDS->nRasterXSize ||
     120          12 :          m_poParentDS->m_nBlockYSize > 1))
     121             :     {
     122          14 :         return BlockBasedRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
     123             :                                   nBufXSize, nBufYSize, eBufType, nBandCount,
     124             :                                   panBandMap, nPixelSpace, nLineSpace,
     125          14 :                                   nBandSpace, psExtraArg);
     126             :     }
     127             : 
     128          18 :     return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
     129             :                                   nBufXSize, nBufYSize, eBufType, nBandCount,
     130             :                                   panBandMap, nPixelSpace, nLineSpace,
     131          18 :                                   nBandSpace, psExtraArg);
     132             : }
     133             : 
     134             : /************************************************************************/
     135             : /*                       GTiffJPEGOverviewBand()                        */
     136             : /************************************************************************/
     137             : 
     138         144 : GTiffJPEGOverviewBand::GTiffJPEGOverviewBand(GTiffJPEGOverviewDS *poDSIn,
     139         144 :                                              int nBandIn)
     140             : {
     141         144 :     poDS = poDSIn;
     142         144 :     nBand = nBandIn;
     143         144 :     eDataType =
     144         144 :         poDSIn->m_poParentDS->GetRasterBand(nBandIn)->GetRasterDataType();
     145         144 :     poDSIn->m_poParentDS->GetRasterBand(nBandIn)->GetBlockSize(&nBlockXSize,
     146             :                                                                &nBlockYSize);
     147         144 :     const int nScaleFactor = 1 << poDSIn->m_nOverviewLevel;
     148         144 :     nBlockXSize = DIV_ROUND_UP(nBlockXSize, nScaleFactor);
     149         144 :     nBlockYSize = DIV_ROUND_UP(nBlockYSize, nScaleFactor);
     150         144 : }
     151             : 
     152             : /************************************************************************/
     153             : /*                             IReadBlock()                             */
     154             : /************************************************************************/
     155             : 
     156        2828 : CPLErr GTiffJPEGOverviewBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     157             :                                          void *pImage)
     158             : {
     159        2828 :     GTiffJPEGOverviewDS *m_poGDS = cpl::down_cast<GTiffJPEGOverviewDS *>(poDS);
     160             : 
     161             :     // Compute the source block ID.
     162        2828 :     int nBlockId = 0;
     163             :     int nParentBlockXSize, nParentBlockYSize;
     164        2828 :     m_poGDS->m_poParentDS->GetRasterBand(1)->GetBlockSize(&nParentBlockXSize,
     165             :                                                           &nParentBlockYSize);
     166        2828 :     const bool bIsSingleStripAsSplit =
     167        2830 :         (nParentBlockYSize == 1 &&
     168           2 :          m_poGDS->m_poParentDS->m_nBlockYSize != nParentBlockYSize);
     169        2828 :     if (!bIsSingleStripAsSplit)
     170             :     {
     171        2826 :         nBlockId =
     172        2826 :             nBlockYOff * m_poGDS->m_poParentDS->m_nBlocksPerRow + nBlockXOff;
     173             :     }
     174        2828 :     if (m_poGDS->m_poParentDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE)
     175             :     {
     176         688 :         nBlockId += (nBand - 1) * m_poGDS->m_poParentDS->m_nBlocksPerBand;
     177             :     }
     178             : 
     179             :     // Make sure it is available.
     180        2828 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
     181        2828 :     vsi_l_offset nOffset = 0;
     182        2828 :     vsi_l_offset nByteCount = 0;
     183        2828 :     bool bErrOccurred = false;
     184        2828 :     if (!m_poGDS->m_poParentDS->IsBlockAvailable(nBlockId, &nOffset,
     185             :                                                  &nByteCount, &bErrOccurred))
     186             :     {
     187          64 :         memset(pImage, 0,
     188          64 :                static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize *
     189          64 :                    nDataTypeSize);
     190          64 :         if (bErrOccurred)
     191           0 :             return CE_Failure;
     192          64 :         return CE_None;
     193             :     }
     194             : 
     195        2764 :     const int nScaleFactor = 1 << m_poGDS->m_nOverviewLevel;
     196        2764 :     if (m_poGDS->m_poJPEGDS == nullptr || nBlockId != m_poGDS->m_nBlockId)
     197             :     {
     198        2459 :         if (nByteCount < 2)
     199           0 :             return CE_Failure;
     200        2459 :         nOffset += 2;  // Skip leading 0xFF 0xF8.
     201        2459 :         nByteCount -= 2;
     202             : 
     203        2459 :         CPLString osFileToOpen;
     204        2459 :         m_poGDS->m_osTmpFilename = VSIMemGenerateHiddenFilename("sparse");
     205        2459 :         VSILFILE *fp = VSIFOpenL(m_poGDS->m_osTmpFilename, "wb+");
     206             : 
     207             :         // If the size of the JPEG strip/tile is small enough, we will
     208             :         // read it from the TIFF file and forge a in-memory JPEG file with
     209             :         // the JPEG table followed by the JPEG data.
     210        2459 :         const bool bInMemoryJPEGFile = nByteCount < 256 * 256;
     211        2459 :         if (bInMemoryJPEGFile)
     212             :         {
     213        2448 :             osFileToOpen = m_poGDS->m_osTmpFilename;
     214             : 
     215        2448 :             bool bError = false;
     216        2448 :             if (VSIFSeekL(fp, m_poGDS->m_nJPEGTableSize + nByteCount - 1,
     217        2448 :                           SEEK_SET) != 0)
     218           0 :                 bError = true;
     219        2448 :             char ch = 0;
     220        2448 :             if (!bError && VSIFWriteL(&ch, 1, 1, fp) != 1)
     221           0 :                 bError = true;
     222             :             GByte *pabyBuffer =
     223        2448 :                 VSIGetMemFileBuffer(m_poGDS->m_osTmpFilename, nullptr, FALSE);
     224        2448 :             memcpy(pabyBuffer, m_poGDS->m_pabyJPEGTable,
     225        2448 :                    m_poGDS->m_nJPEGTableSize);
     226        2448 :             TIFF *hTIFF = m_poGDS->m_poParentDS->m_hTIFF;
     227        2448 :             VSILFILE *fpTIF = VSI_TIFFGetVSILFile(TIFFClientdata(hTIFF));
     228        2448 :             if (!bError && VSIFSeekL(fpTIF, nOffset, SEEK_SET) != 0)
     229           0 :                 bError = true;
     230        2448 :             if (VSIFReadL(pabyBuffer + m_poGDS->m_nJPEGTableSize,
     231        2448 :                           static_cast<size_t>(nByteCount), 1, fpTIF) != 1)
     232           0 :                 bError = true;
     233        2448 :             if (bError)
     234             :             {
     235           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     236           0 :                 return CE_Failure;
     237             :             }
     238             :         }
     239             :         else
     240             :         {
     241             :             // If the JPEG strip/tile is too big (e.g. a single-strip
     242             :             // JPEG-in-TIFF), we will use /vsisparse mechanism to make a
     243             :             // fake JPEG file.
     244             : 
     245             :             osFileToOpen =
     246          11 :                 CPLSPrintf("/vsisparse/%s", m_poGDS->m_osTmpFilename.c_str());
     247             : 
     248          11 :             if (VSIFPrintfL(fp,
     249             :                             "<VSISparseFile><SubfileRegion>"
     250             :                             "<Filename relative='0'>%s</Filename>"
     251             :                             "<DestinationOffset>0</DestinationOffset>"
     252             :                             "<SourceOffset>0</SourceOffset>"
     253             :                             "<RegionLength>%d</RegionLength>"
     254             :                             "</SubfileRegion>"
     255             :                             "<SubfileRegion>"
     256             :                             "<Filename relative='0'>%s</Filename>"
     257             :                             "<DestinationOffset>%d</DestinationOffset>"
     258             :                             "<SourceOffset>" CPL_FRMT_GUIB "</SourceOffset>"
     259             :                             "<RegionLength>" CPL_FRMT_GUIB "</RegionLength>"
     260             :                             "</SubfileRegion></VSISparseFile>",
     261             :                             m_poGDS->m_osTmpFilenameJPEGTable.c_str(),
     262          11 :                             static_cast<int>(m_poGDS->m_nJPEGTableSize),
     263          11 :                             m_poGDS->m_poParentDS->GetDescription(),
     264          11 :                             static_cast<int>(m_poGDS->m_nJPEGTableSize),
     265          11 :                             nOffset, nByteCount) < 0)
     266             :             {
     267           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     268           0 :                 return CE_Failure;
     269             :             }
     270             :         }
     271        2459 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     272             : 
     273        2459 :         const char *const apszDrivers[] = {"JPEG", nullptr};
     274             : 
     275             :         CPLConfigOptionSetter oJPEGtoRGBSetter(
     276             :             "GDAL_JPEG_TO_RGB",
     277        4294 :             m_poGDS->m_poParentDS->m_nPlanarConfig == PLANARCONFIG_CONTIG &&
     278        1835 :                     m_poGDS->nBands == 4
     279             :                 ? "NO"
     280             :                 : "YES",
     281        6753 :             false);
     282             : 
     283        2459 :         m_poGDS->m_poJPEGDS.reset(
     284             :             GDALDataset::Open(osFileToOpen, GDAL_OF_RASTER | GDAL_OF_INTERNAL,
     285             :                               apszDrivers, nullptr, nullptr));
     286             : 
     287        2459 :         if (m_poGDS->m_poJPEGDS != nullptr)
     288             :         {
     289             :             // Force all implicit overviews to be available, even for
     290             :             // small tiles.
     291             :             CPLConfigOptionSetter oInternalOverviewsSetter(
     292        2459 :                 "JPEG_FORCE_INTERNAL_OVERVIEWS", "YES", false);
     293        2459 :             GDALGetOverviewCount(
     294        2459 :                 GDALGetRasterBand(m_poGDS->m_poJPEGDS.get(), 1));
     295             : 
     296        2459 :             m_poGDS->m_nBlockId = nBlockId;
     297             :         }
     298             :     }
     299             : 
     300        2764 :     CPLErr eErr = CE_Failure;
     301        2764 :     if (m_poGDS->m_poJPEGDS)
     302             :     {
     303        2764 :         GDALDataset *l_poDS = m_poGDS->m_poJPEGDS.get();
     304             : 
     305        2764 :         int nReqXOff = 0;
     306        2764 :         int nReqYOff = 0;
     307        2764 :         int nReqXSize = 0;
     308        2764 :         int nReqYSize = 0;
     309        2764 :         if (bIsSingleStripAsSplit)
     310             :         {
     311           2 :             nReqYOff = nBlockYOff * nScaleFactor;
     312           2 :             nReqXSize = l_poDS->GetRasterXSize();
     313           2 :             nReqYSize = nScaleFactor;
     314             :         }
     315             :         else
     316             :         {
     317        2762 :             if (nBlockXSize == m_poGDS->GetRasterXSize())
     318             :             {
     319        2340 :                 nReqXSize = l_poDS->GetRasterXSize();
     320             :             }
     321             :             else
     322             :             {
     323         422 :                 nReqXSize = nBlockXSize * nScaleFactor;
     324             :             }
     325        2762 :             nReqYSize = nBlockYSize * nScaleFactor;
     326             :         }
     327        2764 :         int nBufXSize = nBlockXSize;
     328        2764 :         int nBufYSize = nBlockYSize;
     329        2764 :         if (nBlockXOff == m_poGDS->m_poParentDS->m_nBlocksPerRow - 1)
     330             :         {
     331        2403 :             nReqXSize = m_poGDS->m_poParentDS->nRasterXSize -
     332        2403 :                         nBlockXOff * m_poGDS->m_poParentDS->m_nBlockXSize;
     333             :         }
     334        2764 :         if (nReqXOff + nReqXSize > l_poDS->GetRasterXSize())
     335             :         {
     336           0 :             nReqXSize = l_poDS->GetRasterXSize() - nReqXOff;
     337             :         }
     338        2764 :         if (!bIsSingleStripAsSplit &&
     339        2762 :             nBlockYOff == m_poGDS->m_poParentDS->m_nBlocksPerColumn - 1)
     340             :         {
     341         170 :             nReqYSize = m_poGDS->m_poParentDS->nRasterYSize -
     342         170 :                         nBlockYOff * m_poGDS->m_poParentDS->m_nBlockYSize;
     343             :         }
     344        2764 :         if (nReqYOff + nReqYSize > l_poDS->GetRasterYSize())
     345             :         {
     346           0 :             nReqYSize = l_poDS->GetRasterYSize() - nReqYOff;
     347             :         }
     348        2764 :         if (nBlockXOff * nBlockXSize > m_poGDS->GetRasterXSize() - nBufXSize)
     349             :         {
     350          60 :             memset(pImage, 0,
     351          60 :                    static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize *
     352          60 :                        nDataTypeSize);
     353          60 :             nBufXSize = m_poGDS->GetRasterXSize() - nBlockXOff * nBlockXSize;
     354             :         }
     355        2764 :         if (nBlockYOff * nBlockYSize > m_poGDS->GetRasterYSize() - nBufYSize)
     356             :         {
     357         111 :             memset(pImage, 0,
     358         111 :                    static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize *
     359         111 :                        nDataTypeSize);
     360         111 :             nBufYSize = m_poGDS->GetRasterYSize() - nBlockYOff * nBlockYSize;
     361             :         }
     362             : 
     363        2764 :         const int nSrcBand =
     364        2764 :             m_poGDS->m_poParentDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE
     365        2764 :                 ? 1
     366             :                 : nBand;
     367        2764 :         if (nSrcBand <= l_poDS->GetRasterCount())
     368             :         {
     369        5528 :             eErr = l_poDS->GetRasterBand(nSrcBand)->RasterIO(
     370             :                 GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize, pImage,
     371             :                 nBufXSize, nBufYSize, eDataType, 0,
     372        2764 :                 static_cast<GPtrDiff_t>(nBlockXSize) * nDataTypeSize, nullptr);
     373             :         }
     374             :     }
     375             : 
     376        2764 :     return eErr;
     377             : }

Generated by: LCOV version 1.14