LCOV - code coverage report
Current view: top level - frmts/gtiff - gtiffjpegoverviewds.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 170 182 93.4 %
Date: 2024-05-18 15:15:27 Functions: 9 9 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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "gtiffjpegoverviewds.h"
      31             : 
      32             : #include "gtiffdataset.h"
      33             : 
      34             : #include "tifvsi.h"
      35             : 
      36             : /************************************************************************/
      37             : /* ==================================================================== */
      38             : /*                     GTiffJPEGOverviewBand                            */
      39             : /* ==================================================================== */
      40             : /************************************************************************/
      41             : 
      42             : class GTiffJPEGOverviewBand final : public GDALRasterBand
      43             : {
      44             :   public:
      45             :     GTiffJPEGOverviewBand(GTiffJPEGOverviewDS *poDS, int nBand);
      46             : 
      47         288 :     virtual ~GTiffJPEGOverviewBand()
      48         144 :     {
      49         288 :     }
      50             : 
      51             :     virtual CPLErr IReadBlock(int, int, void *) override;
      52             : 
      53           9 :     GDALColorInterp GetColorInterpretation() override
      54             :     {
      55           9 :         return cpl::down_cast<GTiffJPEGOverviewDS *>(poDS)
      56           9 :             ->m_poParentDS->GetRasterBand(nBand)
      57           9 :             ->GetColorInterpretation();
      58             :     }
      59             : };
      60             : 
      61             : /************************************************************************/
      62             : /*                        GTiffJPEGOverviewDS()                         */
      63             : /************************************************************************/
      64             : 
      65          60 : GTiffJPEGOverviewDS::GTiffJPEGOverviewDS(GTiffDataset *poParentDSIn,
      66             :                                          int nOverviewLevelIn,
      67             :                                          const void *pJPEGTable,
      68          60 :                                          int nJPEGTableSizeIn)
      69             :     : m_poParentDS(poParentDSIn), m_nOverviewLevel(nOverviewLevelIn),
      70          60 :       m_nJPEGTableSize(nJPEGTableSizeIn)
      71             : {
      72          60 :     ShareLockWithParentDataset(poParentDSIn);
      73             : 
      74          60 :     m_osTmpFilenameJPEGTable.Printf("/vsimem/jpegtable_%p", this);
      75             : 
      76          60 :     const GByte abyAdobeAPP14RGB[] = {0xFF, 0xEE, 0x00, 0x0E, 0x41, 0x64,
      77             :                                       0x6F, 0x62, 0x65, 0x00, 0x64, 0x00,
      78             :                                       0x00, 0x00, 0x00, 0x00};
      79          60 :     const bool bAddAdobe =
      80         105 :         m_poParentDS->m_nPlanarConfig == PLANARCONFIG_CONTIG &&
      81          90 :         m_poParentDS->m_nPhotometric != PHOTOMETRIC_YCBCR &&
      82          30 :         m_poParentDS->nBands == 3;
      83          60 :     m_pabyJPEGTable = static_cast<GByte *>(CPLMalloc(
      84          60 :         m_nJPEGTableSize + (bAddAdobe ? sizeof(abyAdobeAPP14RGB) : 0)));
      85          60 :     memcpy(m_pabyJPEGTable, pJPEGTable, m_nJPEGTableSize);
      86          60 :     if (bAddAdobe)
      87             :     {
      88           6 :         memcpy(m_pabyJPEGTable + m_nJPEGTableSize, abyAdobeAPP14RGB,
      89             :                sizeof(abyAdobeAPP14RGB));
      90           6 :         m_nJPEGTableSize += sizeof(abyAdobeAPP14RGB);
      91             :     }
      92          60 :     CPL_IGNORE_RET_VAL(VSIFCloseL(VSIFileFromMemBuffer(
      93          60 :         m_osTmpFilenameJPEGTable, m_pabyJPEGTable, m_nJPEGTableSize, TRUE)));
      94             : 
      95          60 :     const int nScaleFactor = 1 << m_nOverviewLevel;
      96          60 :     nRasterXSize =
      97          60 :         (m_poParentDS->nRasterXSize + nScaleFactor - 1) / nScaleFactor;
      98          60 :     nRasterYSize =
      99          60 :         (m_poParentDS->nRasterYSize + nScaleFactor - 1) / nScaleFactor;
     100             : 
     101         204 :     for (int i = 1; i <= m_poParentDS->nBands; ++i)
     102         144 :         SetBand(i, new GTiffJPEGOverviewBand(this, i));
     103             : 
     104          60 :     SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
     105          60 :     if (m_poParentDS->m_nPhotometric == PHOTOMETRIC_YCBCR)
     106          15 :         SetMetadataItem("COMPRESSION", "YCbCr JPEG", "IMAGE_STRUCTURE");
     107             :     else
     108          45 :         SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
     109          60 : }
     110             : 
     111             : /************************************************************************/
     112             : /*                       ~GTiffJPEGOverviewDS()                         */
     113             : /************************************************************************/
     114             : 
     115         120 : GTiffJPEGOverviewDS::~GTiffJPEGOverviewDS()
     116             : {
     117          60 :     m_poJPEGDS.reset();
     118          60 :     VSIUnlink(m_osTmpFilenameJPEGTable);
     119          60 :     if (!m_osTmpFilename.empty())
     120          50 :         VSIUnlink(m_osTmpFilename);
     121         120 : }
     122             : 
     123             : /************************************************************************/
     124             : /*                            IRasterIO()                               */
     125             : /************************************************************************/
     126             : 
     127          32 : CPLErr GTiffJPEGOverviewDS::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     128             :                                       int nXSize, int nYSize, void *pData,
     129             :                                       int nBufXSize, int nBufYSize,
     130             :                                       GDALDataType eBufType, int nBandCount,
     131             :                                       int *panBandMap, GSpacing nPixelSpace,
     132             :                                       GSpacing nLineSpace, GSpacing nBandSpace,
     133             :                                       GDALRasterIOExtraArg *psExtraArg)
     134             : 
     135             : {
     136             :     // For non-single strip JPEG-IN-TIFF, the block based strategy will
     137             :     // be the most efficient one, to avoid decompressing the JPEG content
     138             :     // for each requested band.
     139          32 :     if (nBandCount > 1 &&
     140          22 :         m_poParentDS->m_nPlanarConfig == PLANARCONFIG_CONTIG &&
     141          14 :         (m_poParentDS->m_nBlockXSize < m_poParentDS->nRasterXSize ||
     142          12 :          m_poParentDS->m_nBlockYSize > 1))
     143             :     {
     144          14 :         return BlockBasedRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
     145             :                                   nBufXSize, nBufYSize, eBufType, nBandCount,
     146             :                                   panBandMap, nPixelSpace, nLineSpace,
     147          14 :                                   nBandSpace, psExtraArg);
     148             :     }
     149             : 
     150          18 :     return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
     151             :                                   nBufXSize, nBufYSize, eBufType, nBandCount,
     152             :                                   panBandMap, nPixelSpace, nLineSpace,
     153          18 :                                   nBandSpace, psExtraArg);
     154             : }
     155             : 
     156             : /************************************************************************/
     157             : /*                        GTiffJPEGOverviewBand()                       */
     158             : /************************************************************************/
     159             : 
     160         144 : GTiffJPEGOverviewBand::GTiffJPEGOverviewBand(GTiffJPEGOverviewDS *poDSIn,
     161         144 :                                              int nBandIn)
     162             : {
     163         144 :     poDS = poDSIn;
     164         144 :     nBand = nBandIn;
     165         144 :     eDataType =
     166         144 :         poDSIn->m_poParentDS->GetRasterBand(nBandIn)->GetRasterDataType();
     167         144 :     poDSIn->m_poParentDS->GetRasterBand(nBandIn)->GetBlockSize(&nBlockXSize,
     168             :                                                                &nBlockYSize);
     169         144 :     const int nScaleFactor = 1 << poDSIn->m_nOverviewLevel;
     170         144 :     nBlockXSize = (nBlockXSize + nScaleFactor - 1) / nScaleFactor;
     171         144 :     nBlockYSize = (nBlockYSize + nScaleFactor - 1) / nScaleFactor;
     172         144 : }
     173             : 
     174             : /************************************************************************/
     175             : /*                          IReadBlock()                                */
     176             : /************************************************************************/
     177             : 
     178        2828 : CPLErr GTiffJPEGOverviewBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     179             :                                          void *pImage)
     180             : {
     181        2828 :     GTiffJPEGOverviewDS *m_poGDS = cpl::down_cast<GTiffJPEGOverviewDS *>(poDS);
     182             : 
     183             :     // Compute the source block ID.
     184        2828 :     int nBlockId = 0;
     185             :     int nParentBlockXSize, nParentBlockYSize;
     186        2828 :     m_poGDS->m_poParentDS->GetRasterBand(1)->GetBlockSize(&nParentBlockXSize,
     187             :                                                           &nParentBlockYSize);
     188        2828 :     const bool bIsSingleStripAsSplit =
     189        2830 :         (nParentBlockYSize == 1 &&
     190           2 :          m_poGDS->m_poParentDS->m_nBlockYSize != nParentBlockYSize);
     191        2828 :     if (!bIsSingleStripAsSplit)
     192             :     {
     193        2826 :         nBlockId =
     194        2826 :             nBlockYOff * m_poGDS->m_poParentDS->m_nBlocksPerRow + nBlockXOff;
     195             :     }
     196        2828 :     if (m_poGDS->m_poParentDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE)
     197             :     {
     198         688 :         nBlockId += (nBand - 1) * m_poGDS->m_poParentDS->m_nBlocksPerBand;
     199             :     }
     200             : 
     201             :     // Make sure it is available.
     202        2828 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
     203        2828 :     vsi_l_offset nOffset = 0;
     204        2828 :     vsi_l_offset nByteCount = 0;
     205        2828 :     bool bErrOccurred = false;
     206        2828 :     if (!m_poGDS->m_poParentDS->IsBlockAvailable(nBlockId, &nOffset,
     207             :                                                  &nByteCount, &bErrOccurred))
     208             :     {
     209          64 :         memset(pImage, 0,
     210          64 :                static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize *
     211          64 :                    nDataTypeSize);
     212          64 :         if (bErrOccurred)
     213           0 :             return CE_Failure;
     214          64 :         return CE_None;
     215             :     }
     216             : 
     217        2764 :     const int nScaleFactor = 1 << m_poGDS->m_nOverviewLevel;
     218        2764 :     if (m_poGDS->m_poJPEGDS == nullptr || nBlockId != m_poGDS->m_nBlockId)
     219             :     {
     220        2459 :         if (nByteCount < 2)
     221           0 :             return CE_Failure;
     222        2459 :         nOffset += 2;  // Skip leading 0xFF 0xF8.
     223        2459 :         nByteCount -= 2;
     224             : 
     225        2459 :         CPLString osFileToOpen;
     226        2459 :         m_poGDS->m_osTmpFilename.Printf("/vsimem/sparse_%p", m_poGDS);
     227        2459 :         VSILFILE *fp = VSIFOpenL(m_poGDS->m_osTmpFilename, "wb+");
     228             : 
     229             :         // If the size of the JPEG strip/tile is small enough, we will
     230             :         // read it from the TIFF file and forge a in-memory JPEG file with
     231             :         // the JPEG table followed by the JPEG data.
     232        2459 :         const bool bInMemoryJPEGFile = nByteCount < 256 * 256;
     233        2459 :         if (bInMemoryJPEGFile)
     234             :         {
     235        2448 :             osFileToOpen = m_poGDS->m_osTmpFilename;
     236             : 
     237        2448 :             bool bError = false;
     238        2448 :             if (VSIFSeekL(fp, m_poGDS->m_nJPEGTableSize + nByteCount - 1,
     239        2448 :                           SEEK_SET) != 0)
     240           0 :                 bError = true;
     241        2448 :             char ch = 0;
     242        2448 :             if (!bError && VSIFWriteL(&ch, 1, 1, fp) != 1)
     243           0 :                 bError = true;
     244             :             GByte *pabyBuffer =
     245        2448 :                 VSIGetMemFileBuffer(m_poGDS->m_osTmpFilename, nullptr, FALSE);
     246        2448 :             memcpy(pabyBuffer, m_poGDS->m_pabyJPEGTable,
     247        2448 :                    m_poGDS->m_nJPEGTableSize);
     248        2448 :             TIFF *hTIFF = m_poGDS->m_poParentDS->m_hTIFF;
     249        2448 :             VSILFILE *fpTIF = VSI_TIFFGetVSILFile(TIFFClientdata(hTIFF));
     250        2448 :             if (!bError && VSIFSeekL(fpTIF, nOffset, SEEK_SET) != 0)
     251           0 :                 bError = true;
     252        2448 :             if (VSIFReadL(pabyBuffer + m_poGDS->m_nJPEGTableSize,
     253        2448 :                           static_cast<size_t>(nByteCount), 1, fpTIF) != 1)
     254           0 :                 bError = true;
     255        2448 :             if (bError)
     256             :             {
     257           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     258           0 :                 return CE_Failure;
     259             :             }
     260             :         }
     261             :         else
     262             :         {
     263             :             // If the JPEG strip/tile is too big (e.g. a single-strip
     264             :             // JPEG-in-TIFF), we will use /vsisparse mechanism to make a
     265             :             // fake JPEG file.
     266             : 
     267             :             osFileToOpen =
     268          11 :                 CPLSPrintf("/vsisparse/%s", m_poGDS->m_osTmpFilename.c_str());
     269             : 
     270          11 :             if (VSIFPrintfL(fp,
     271             :                             "<VSISparseFile><SubfileRegion>"
     272             :                             "<Filename relative='0'>%s</Filename>"
     273             :                             "<DestinationOffset>0</DestinationOffset>"
     274             :                             "<SourceOffset>0</SourceOffset>"
     275             :                             "<RegionLength>%d</RegionLength>"
     276             :                             "</SubfileRegion>"
     277             :                             "<SubfileRegion>"
     278             :                             "<Filename relative='0'>%s</Filename>"
     279             :                             "<DestinationOffset>%d</DestinationOffset>"
     280             :                             "<SourceOffset>" CPL_FRMT_GUIB "</SourceOffset>"
     281             :                             "<RegionLength>" CPL_FRMT_GUIB "</RegionLength>"
     282             :                             "</SubfileRegion></VSISparseFile>",
     283             :                             m_poGDS->m_osTmpFilenameJPEGTable.c_str(),
     284          11 :                             static_cast<int>(m_poGDS->m_nJPEGTableSize),
     285          11 :                             m_poGDS->m_poParentDS->GetDescription(),
     286          11 :                             static_cast<int>(m_poGDS->m_nJPEGTableSize),
     287          11 :                             nOffset, nByteCount) < 0)
     288             :             {
     289           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     290           0 :                 return CE_Failure;
     291             :             }
     292             :         }
     293        2459 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     294             : 
     295        2459 :         const char *const apszDrivers[] = {"JPEG", nullptr};
     296             : 
     297             :         CPLConfigOptionSetter oJPEGtoRGBSetter(
     298             :             "GDAL_JPEG_TO_RGB",
     299        4294 :             m_poGDS->m_poParentDS->m_nPlanarConfig == PLANARCONFIG_CONTIG &&
     300        1835 :                     m_poGDS->nBands == 4
     301             :                 ? "NO"
     302             :                 : "YES",
     303        6753 :             false);
     304             : 
     305        2459 :         m_poGDS->m_poJPEGDS.reset(
     306             :             GDALDataset::Open(osFileToOpen, GDAL_OF_RASTER | GDAL_OF_INTERNAL,
     307             :                               apszDrivers, nullptr, nullptr));
     308             : 
     309        2459 :         if (m_poGDS->m_poJPEGDS != nullptr)
     310             :         {
     311             :             // Force all implicit overviews to be available, even for
     312             :             // small tiles.
     313             :             CPLConfigOptionSetter oInternalOverviewsSetter(
     314        2459 :                 "JPEG_FORCE_INTERNAL_OVERVIEWS", "YES", false);
     315        2459 :             GDALGetOverviewCount(
     316        2459 :                 GDALGetRasterBand(m_poGDS->m_poJPEGDS.get(), 1));
     317             : 
     318        2459 :             m_poGDS->m_nBlockId = nBlockId;
     319             :         }
     320             :     }
     321             : 
     322        2764 :     CPLErr eErr = CE_Failure;
     323        2764 :     if (m_poGDS->m_poJPEGDS)
     324             :     {
     325        2764 :         GDALDataset *l_poDS = m_poGDS->m_poJPEGDS.get();
     326             : 
     327        2764 :         int nReqXOff = 0;
     328        2764 :         int nReqYOff = 0;
     329        2764 :         int nReqXSize = 0;
     330        2764 :         int nReqYSize = 0;
     331        2764 :         if (bIsSingleStripAsSplit)
     332             :         {
     333           2 :             nReqYOff = nBlockYOff * nScaleFactor;
     334           2 :             nReqXSize = l_poDS->GetRasterXSize();
     335           2 :             nReqYSize = nScaleFactor;
     336             :         }
     337             :         else
     338             :         {
     339        2762 :             if (nBlockXSize == m_poGDS->GetRasterXSize())
     340             :             {
     341        2340 :                 nReqXSize = l_poDS->GetRasterXSize();
     342             :             }
     343             :             else
     344             :             {
     345         422 :                 nReqXSize = nBlockXSize * nScaleFactor;
     346             :             }
     347        2762 :             nReqYSize = nBlockYSize * nScaleFactor;
     348             :         }
     349        2764 :         int nBufXSize = nBlockXSize;
     350        2764 :         int nBufYSize = nBlockYSize;
     351        2764 :         if (nBlockXOff == m_poGDS->m_poParentDS->m_nBlocksPerRow - 1)
     352             :         {
     353        2403 :             nReqXSize = m_poGDS->m_poParentDS->nRasterXSize -
     354        2403 :                         nBlockXOff * m_poGDS->m_poParentDS->m_nBlockXSize;
     355             :         }
     356        2764 :         if (nReqXOff + nReqXSize > l_poDS->GetRasterXSize())
     357             :         {
     358           0 :             nReqXSize = l_poDS->GetRasterXSize() - nReqXOff;
     359             :         }
     360        2764 :         if (!bIsSingleStripAsSplit &&
     361        2762 :             nBlockYOff == m_poGDS->m_poParentDS->m_nBlocksPerColumn - 1)
     362             :         {
     363         170 :             nReqYSize = m_poGDS->m_poParentDS->nRasterYSize -
     364         170 :                         nBlockYOff * m_poGDS->m_poParentDS->m_nBlockYSize;
     365             :         }
     366        2764 :         if (nReqYOff + nReqYSize > l_poDS->GetRasterYSize())
     367             :         {
     368           0 :             nReqYSize = l_poDS->GetRasterYSize() - nReqYOff;
     369             :         }
     370        2764 :         if (nBlockXOff * nBlockXSize > m_poGDS->GetRasterXSize() - nBufXSize)
     371             :         {
     372          60 :             memset(pImage, 0,
     373          60 :                    static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize *
     374          60 :                        nDataTypeSize);
     375          60 :             nBufXSize = m_poGDS->GetRasterXSize() - nBlockXOff * nBlockXSize;
     376             :         }
     377        2764 :         if (nBlockYOff * nBlockYSize > m_poGDS->GetRasterYSize() - nBufYSize)
     378             :         {
     379         111 :             memset(pImage, 0,
     380         111 :                    static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize *
     381         111 :                        nDataTypeSize);
     382         111 :             nBufYSize = m_poGDS->GetRasterYSize() - nBlockYOff * nBlockYSize;
     383             :         }
     384             : 
     385        2764 :         const int nSrcBand =
     386        2764 :             m_poGDS->m_poParentDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE
     387        2764 :                 ? 1
     388             :                 : nBand;
     389        2764 :         if (nSrcBand <= l_poDS->GetRasterCount())
     390             :         {
     391        5528 :             eErr = l_poDS->GetRasterBand(nSrcBand)->RasterIO(
     392             :                 GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize, pImage,
     393             :                 nBufXSize, nBufYSize, eDataType, 0,
     394        2764 :                 static_cast<GPtrDiff_t>(nBlockXSize) * nDataTypeSize, nullptr);
     395             :         }
     396             :     }
     397             : 
     398        2764 :     return eErr;
     399             : }

Generated by: LCOV version 1.14