LCOV - code coverage report
Current view: top level - frmts/gtiff - gt_jpeg_copy.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 311 359 86.6 %
Date: 2024-11-21 22:18:42 Functions: 6 7 85.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GeoTIFF Driver
       4             :  * Purpose:  Specialized copy of JPEG content into TIFF.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2012, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : 
      15             : #include "gt_jpeg_copy.h"
      16             : 
      17             : #include "cpl_vsi.h"
      18             : 
      19             : #if defined(JPEG_DIRECT_COPY) || defined(HAVE_LIBJPEG)
      20             : #include "vrt/vrtdataset.h"
      21             : #endif
      22             : 
      23             : #include <algorithm>
      24             : 
      25             : // Note: JPEG_DIRECT_COPY is not defined by default, because it is mainly
      26             : // useful for debugging purposes.
      27             : 
      28             : #if defined(JPEG_DIRECT_COPY) || defined(HAVE_LIBJPEG)
      29             : 
      30             : /************************************************************************/
      31             : /*                      GetUnderlyingDataset()                          */
      32             : /************************************************************************/
      33             : 
      34        1899 : static GDALDataset *GetUnderlyingDataset(GDALDataset *poSrcDS)
      35             : {
      36             :     // Test if we can directly copy original JPEG content if available.
      37        1899 :     if (auto poVRTDS = dynamic_cast<VRTDataset *>(poSrcDS))
      38             :     {
      39        1144 :         poSrcDS = poVRTDS->GetSingleSimpleSource();
      40             :     }
      41             : 
      42        1899 :     return poSrcDS;
      43             : }
      44             : 
      45             : #endif  // defined(JPEG_DIRECT_COPY) || defined(HAVE_LIBJPEG)
      46             : 
      47             : #ifdef JPEG_DIRECT_COPY
      48             : 
      49             : /************************************************************************/
      50             : /*                        IsBaselineDCTJPEG()                           */
      51             : /************************************************************************/
      52             : 
      53             : static bool IsBaselineDCTJPEG(VSILFILE *fp)
      54             : {
      55             :     GByte abyBuf[4] = {0};
      56             : 
      57             :     if (VSIFReadL(abyBuf, 1, 2, fp) != 2 || abyBuf[0] != 0xff ||
      58             :         abyBuf[1] != 0xd8)
      59             :     {
      60             :         CPLError(CE_Failure, CPLE_AppDefined, "Not a valid JPEG file");
      61             :         return false;
      62             :     }
      63             : 
      64             :     int nOffset = 2;
      65             :     while (true)
      66             :     {
      67             :         VSIFSeekL(fp, nOffset, SEEK_SET);
      68             :         if (VSIFReadL(abyBuf, 1, 4, fp) != 4 || abyBuf[0] != 0xFF)
      69             :         {
      70             :             CPLError(CE_Failure, CPLE_AppDefined, "Not a valid JPEG file");
      71             :             return false;
      72             :         }
      73             : 
      74             :         const int nMarker = abyBuf[1];
      75             : 
      76             :         // Start of Frame 0 = Baseline DCT.
      77             :         if (nMarker == 0xC0)
      78             :             return true;
      79             : 
      80             :         if (nMarker == 0xD9)
      81             :             return false;
      82             : 
      83             :         if (nMarker == 0xF7 ||  // JPEG Extension 7, JPEG-LS
      84             :             nMarker == 0xF8 ||  // JPEG Extension 8, JPEG-LS Extension.
      85             :             // Other Start of Frames that we don't want to support.
      86             :             (nMarker >= 0xC1 && nMarker <= 0xCF))
      87             :         {
      88             :             CPLError(CE_Failure, CPLE_AppDefined,
      89             :                      "Unsupported type of JPEG file for JPEG_DIRECT_COPY mode");
      90             :             return false;
      91             :         }
      92             : 
      93             :         nOffset += 2 + abyBuf[2] * 256 + abyBuf[3];
      94             :     }
      95             : }
      96             : 
      97             : /************************************************************************/
      98             : /*                    GTIFF_CanDirectCopyFromJPEG()                     */
      99             : /************************************************************************/
     100             : 
     101             : int GTIFF_CanDirectCopyFromJPEG(GDALDataset *poSrcDS,
     102             :                                 char **&papszCreateOptions)
     103             : {
     104             :     poSrcDS = GetUnderlyingDataset(poSrcDS);
     105             :     if (poSrcDS == NULL)
     106             :         return FALSE;
     107             :     if (poSrcDS->GetDriver() == NULL)
     108             :         return FALSE;
     109             :     if (!EQUAL(GDALGetDriverShortName(poSrcDS->GetDriver()), "JPEG"))
     110             :         return FALSE;
     111             : 
     112             :     const char *pszCompress = CSLFetchNameValue(papszCreateOptions, "COMPRESS");
     113             :     if (pszCompress != NULL && !EQUAL(pszCompress, "JPEG"))
     114             :         return FALSE;
     115             : 
     116             :     const char *pszSrcColorSpace =
     117             :         poSrcDS->GetMetadataItem("SOURCE_COLOR_SPACE", "IMAGE_STRUCTURE");
     118             :     if (pszSrcColorSpace != NULL &&
     119             :         (EQUAL(pszSrcColorSpace, "CMYK") || EQUAL(pszSrcColorSpace, "YCbCrK")))
     120             :         return FALSE;
     121             : 
     122             :     bool bJPEGDirectCopy = false;
     123             : 
     124             :     VSILFILE *fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
     125             :     if (fpJPEG && IsBaselineDCTJPEG(fpJPEG))
     126             :     {
     127             :         bJPEGDirectCopy = true;
     128             : 
     129             :         if (pszCompress == NULL)
     130             :             papszCreateOptions =
     131             :                 CSLSetNameValue(papszCreateOptions, "COMPRESS", "JPEG");
     132             : 
     133             :         papszCreateOptions =
     134             :             CSLSetNameValue(papszCreateOptions, "BLOCKXSIZE", NULL);
     135             :         papszCreateOptions =
     136             :             CSLSetNameValue(papszCreateOptions, "BLOCKYSIZE",
     137             :                             CPLSPrintf("%d", poSrcDS->GetRasterYSize()));
     138             : 
     139             :         if (pszSrcColorSpace != NULL && EQUAL(pszSrcColorSpace, "YCbCr"))
     140             :             papszCreateOptions =
     141             :                 CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "YCBCR");
     142             :         else
     143             :             papszCreateOptions =
     144             :                 CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", NULL);
     145             : 
     146             :         if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte)
     147             :             papszCreateOptions =
     148             :                 CSLSetNameValue(papszCreateOptions, "NBITS", "12");
     149             :         else
     150             :             papszCreateOptions =
     151             :                 CSLSetNameValue(papszCreateOptions, "NBITS", NULL);
     152             : 
     153             :         papszCreateOptions = CSLSetNameValue(papszCreateOptions, "TILED", NULL);
     154             :         papszCreateOptions =
     155             :             CSLSetNameValue(papszCreateOptions, "JPEG_QUALITY", NULL);
     156             :     }
     157             :     if (fpJPEG)
     158             :     {
     159             :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpJPEG));
     160             :     }
     161             : 
     162             :     return bJPEGDirectCopy;
     163             : }
     164             : 
     165             : /************************************************************************/
     166             : /*                     GTIFF_DirectCopyFromJPEG()                       */
     167             : /************************************************************************/
     168             : 
     169             : CPLErr GTIFF_DirectCopyFromJPEG(GDALDataset *poDS, GDALDataset *poSrcDS,
     170             :                                 GDALProgressFunc pfnProgress,
     171             :                                 void *pProgressData,
     172             :                                 bool &bShouldFallbackToNormalCopyIfFail)
     173             : {
     174             :     bShouldFallbackToNormalCopyIfFail = true;
     175             : 
     176             :     poSrcDS = GetUnderlyingDataset(poSrcDS);
     177             :     if (poSrcDS == NULL)
     178             :         return CE_Failure;
     179             : 
     180             :     VSILFILE *fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
     181             :     if (fpJPEG == NULL)
     182             :         return CE_Failure;
     183             : 
     184             :     CPLErr eErr = CE_None;
     185             : 
     186             :     VSIFSeekL(fpJPEG, 0, SEEK_END);
     187             :     tmsize_t nSize = static_cast<tmsize_t>(VSIFTellL(fpJPEG));
     188             :     VSIFSeekL(fpJPEG, 0, SEEK_SET);
     189             : 
     190             :     void *pabyJPEGData = VSIMalloc(nSize);
     191             :     if (pabyJPEGData == NULL)
     192             :     {
     193             :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpJPEG));
     194             :         return CE_Failure;
     195             :     }
     196             : 
     197             :     if (pabyJPEGData != NULL && static_cast<tmsize_t>(VSIFReadL(
     198             :                                     pabyJPEGData, 1, nSize, fpJPEG)) == nSize)
     199             :     {
     200             :         bShouldFallbackToNormalCopyIfFail = false;
     201             : 
     202             :         TIFF *hTIFF = (TIFF *)poDS->GetInternalHandle(NULL);
     203             :         if (TIFFWriteRawStrip(hTIFF, 0, pabyJPEGData, nSize) != nSize)
     204             :             eErr = CE_Failure;
     205             : 
     206             :         if (!pfnProgress(1.0, NULL, pProgressData))
     207             :             eErr = CE_Failure;
     208             :     }
     209             :     else
     210             :     {
     211             :         eErr = CE_Failure;
     212             :     }
     213             : 
     214             :     VSIFree(pabyJPEGData);
     215             :     if (VSIFCloseL(fpJPEG) != 0)
     216             :         eErr = CE_Failure;
     217             : 
     218             :     return eErr;
     219             : }
     220             : 
     221             : #endif  // JPEG_DIRECT_COPY
     222             : 
     223             : #ifdef HAVE_LIBJPEG
     224             : 
     225             : #define jpeg_vsiio_src GTIFF_jpeg_vsiio_src
     226             : #define jpeg_vsiio_dest GTIFF_jpeg_vsiio_dest
     227             : #include "../jpeg/vsidataio.h"
     228             : #include "../jpeg/vsidataio.cpp"
     229             : 
     230             : #include <setjmp.h>
     231             : 
     232             : /*
     233             :  * We are using width_in_blocks which is supposed to be private to
     234             :  * libjpeg. Unfortunately, the libjpeg delivered with Cygwin has
     235             :  * renamed this member to width_in_data_units.  Since the header has
     236             :  * also renamed a define, use that unique define name in order to
     237             :  * detect the problem header and adjust to suit.
     238             :  */
     239             : #if defined(D_MAX_DATA_UNITS_IN_MCU)
     240             : #define width_in_blocks width_in_data_units
     241             : #endif
     242             : 
     243             : #ifdef EXPECTED_JPEG_LIB_VERSION
     244             : #if EXPECTED_JPEG_LIB_VERSION != JPEG_LIB_VERSION
     245             : #error EXPECTED_JPEG_LIB_VERSION != JPEG_LIB_VERSION
     246             : #endif
     247             : #endif
     248             : 
     249             : /************************************************************************/
     250             : /*                      GTIFF_CanCopyFromJPEG()                         */
     251             : /************************************************************************/
     252             : 
     253        1875 : int GTIFF_CanCopyFromJPEG(GDALDataset *poSrcDS, char **&papszCreateOptions)
     254             : {
     255        1875 :     poSrcDS = GetUnderlyingDataset(poSrcDS);
     256        1875 :     if (poSrcDS == nullptr)
     257        1044 :         return FALSE;
     258         831 :     if (poSrcDS->GetDriver() == nullptr)
     259          67 :         return FALSE;
     260         764 :     if (!EQUAL(GDALGetDriverShortName(poSrcDS->GetDriver()), "JPEG"))
     261         750 :         return FALSE;
     262             : 
     263          14 :     const char *pszCompress = CSLFetchNameValue(papszCreateOptions, "COMPRESS");
     264          14 :     if (pszCompress == nullptr || !EQUAL(pszCompress, "JPEG"))
     265           1 :         return FALSE;
     266             : 
     267             :     const int nBlockXSize =
     268          13 :         atoi(CSLFetchNameValueDef(papszCreateOptions, "BLOCKXSIZE", "0"));
     269             :     const int nBlockYSize =
     270          13 :         atoi(CSLFetchNameValueDef(papszCreateOptions, "BLOCKYSIZE", "0"));
     271          13 :     int nMCUSize = 8;
     272             :     const char *pszSrcColorSpace =
     273          13 :         poSrcDS->GetMetadataItem("SOURCE_COLOR_SPACE", "IMAGE_STRUCTURE");
     274          13 :     if (pszSrcColorSpace != nullptr && EQUAL(pszSrcColorSpace, "YCbCr"))
     275           5 :         nMCUSize = 16;
     276             : 
     277          13 :     const int nXSize = poSrcDS->GetRasterXSize();
     278          13 :     const int nYSize = poSrcDS->GetRasterYSize();
     279          13 :     const int nBands = poSrcDS->GetRasterCount();
     280             : 
     281             :     const char *pszPhotometric =
     282          13 :         CSLFetchNameValue(papszCreateOptions, "PHOTOMETRIC");
     283             : 
     284             :     const bool bCompatiblePhotometric =
     285           2 :         pszPhotometric == nullptr ||
     286           2 :         (nMCUSize == 16 && EQUAL(pszPhotometric, "YCbCr")) ||
     287           2 :         (nMCUSize == 8 && nBands == 4 &&
     288           2 :          poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_CyanBand &&
     289           2 :          poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
     290           2 :              GCI_MagentaBand &&
     291           2 :          poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
     292           2 :              GCI_YellowBand &&
     293           2 :          poSrcDS->GetRasterBand(4)->GetColorInterpretation() ==
     294           0 :              GCI_BlackBand) ||
     295          15 :         (nMCUSize == 8 && EQUAL(pszPhotometric, "RGB") && nBands == 3) ||
     296           0 :         (nMCUSize == 8 && EQUAL(pszPhotometric, "MINISBLACK") && nBands == 1);
     297          13 :     if (!bCompatiblePhotometric)
     298           0 :         return FALSE;
     299             : 
     300           3 :     if (nBands == 4 && pszPhotometric == nullptr &&
     301           1 :         poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_CyanBand &&
     302           1 :         poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
     303           1 :             GCI_MagentaBand &&
     304          17 :         poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_YellowBand &&
     305           1 :         poSrcDS->GetRasterBand(4)->GetColorInterpretation() == GCI_BlackBand)
     306             :     {
     307           1 :         papszCreateOptions =
     308           1 :             CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "CMYK");
     309             :     }
     310             : 
     311             :     const char *pszInterleave =
     312          13 :         CSLFetchNameValue(papszCreateOptions, "INTERLEAVE");
     313             : 
     314          13 :     const bool bCompatibleInterleave =
     315           2 :         pszInterleave == nullptr ||
     316          15 :         (nBands > 1 && EQUAL(pszInterleave, "PIXEL")) || nBands == 1;
     317          13 :     if (!bCompatibleInterleave)
     318           1 :         return FALSE;
     319             : 
     320             :     // We don't want to apply lossy JPEG on a source using lossless JPEG !
     321          24 :     const char *pszReversibility = poSrcDS->GetMetadataItem(
     322          12 :         "COMPRESSION_REVERSIBILITY", "IMAGE_STRUCTURE");
     323          12 :     if (pszReversibility && EQUAL(pszReversibility, "LOSSLESS"))
     324           0 :         return FALSE;
     325             : 
     326          12 :     if ((nBlockXSize == nXSize || (nBlockXSize % nMCUSize) == 0) &&
     327           9 :         (nBlockYSize == nYSize || (nBlockYSize % nMCUSize) == 0) &&
     328          12 :         poSrcDS->GetRasterBand(1)->GetRasterDataType() == GDT_Byte &&
     329          36 :         CSLFetchNameValue(papszCreateOptions, "NBITS") == nullptr &&
     330          12 :         CSLFetchNameValue(papszCreateOptions, "JPEG_QUALITY") == nullptr)
     331             :     {
     332          12 :         if (nMCUSize == 16 && pszPhotometric == nullptr)
     333           4 :             papszCreateOptions =
     334           4 :                 CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "YCBCR");
     335          12 :         return TRUE;
     336             :     }
     337             : 
     338           0 :     return FALSE;
     339             : }
     340             : 
     341             : /************************************************************************/
     342             : /*                      GTIFF_ErrorExitJPEG()                           */
     343             : /************************************************************************/
     344             : 
     345           0 : static void GTIFF_ErrorExitJPEG(j_common_ptr cinfo)
     346             : {
     347           0 :     jmp_buf *setjmp_buffer = static_cast<jmp_buf *>(cinfo->client_data);
     348           0 :     char buffer[JMSG_LENGTH_MAX] = {'\0'};
     349             : 
     350             :     // Create the message.
     351           0 :     (*cinfo->err->format_message)(cinfo, buffer);
     352             : 
     353           0 :     CPLError(CE_Failure, CPLE_AppDefined, "libjpeg: %s", buffer);
     354             : 
     355             :     // Return control to the setjmp point.
     356           0 :     longjmp(*setjmp_buffer, 1);
     357             : }
     358             : 
     359             : /************************************************************************/
     360             : /*                      GTIFF_Set_TIFFTAG_JPEGTABLES()                  */
     361             : /************************************************************************/
     362             : 
     363          12 : static void GTIFF_Set_TIFFTAG_JPEGTABLES(TIFF *hTIFF,
     364             :                                          jpeg_compress_struct &sCInfo)
     365             : {
     366          24 :     const std::string osTmpFilename(VSIMemGenerateHiddenFilename("tables"));
     367          12 :     VSILFILE *fpTABLES = VSIFOpenL(osTmpFilename.c_str(), "wb+");
     368             : 
     369          12 :     uint16_t nPhotometric = 0;
     370          12 :     TIFFGetField(hTIFF, TIFFTAG_PHOTOMETRIC, &nPhotometric);
     371             : 
     372          12 :     jpeg_vsiio_dest(&sCInfo, fpTABLES);
     373             : 
     374             :     // Avoid unnecessary tables to be emitted.
     375          12 :     if (nPhotometric != PHOTOMETRIC_YCBCR)
     376             :     {
     377           8 :         JQUANT_TBL *qtbl = sCInfo.quant_tbl_ptrs[1];
     378           8 :         if (qtbl != nullptr)
     379           8 :             qtbl->sent_table = TRUE;
     380           8 :         JHUFF_TBL *htbl = sCInfo.dc_huff_tbl_ptrs[1];
     381           8 :         if (htbl != nullptr)
     382           8 :             htbl->sent_table = TRUE;
     383           8 :         htbl = sCInfo.ac_huff_tbl_ptrs[1];
     384           8 :         if (htbl != nullptr)
     385           8 :             htbl->sent_table = TRUE;
     386             :     }
     387          12 :     jpeg_write_tables(&sCInfo);
     388             : 
     389          12 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fpTABLES));
     390             : 
     391          12 :     vsi_l_offset nSizeTables = 0;
     392             :     GByte *pabyJPEGTablesData =
     393          12 :         VSIGetMemFileBuffer(osTmpFilename.c_str(), &nSizeTables, FALSE);
     394          12 :     TIFFSetField(hTIFF, TIFFTAG_JPEGTABLES, static_cast<int>(nSizeTables),
     395             :                  pabyJPEGTablesData);
     396             : 
     397          12 :     VSIUnlink(osTmpFilename.c_str());
     398          12 : }
     399             : 
     400             : /************************************************************************/
     401             : /*             GTIFF_CopyFromJPEG_WriteAdditionalTags()                 */
     402             : /************************************************************************/
     403             : 
     404          12 : CPLErr GTIFF_CopyFromJPEG_WriteAdditionalTags(TIFF *hTIFF, GDALDataset *poSrcDS)
     405             : {
     406          12 :     poSrcDS = GetUnderlyingDataset(poSrcDS);
     407          12 :     if (poSrcDS == nullptr)
     408           0 :         return CE_Failure;
     409             : 
     410             :     /* -------------------------------------------------------------------- */
     411             :     /*      Write TIFFTAG_JPEGTABLES                                        */
     412             :     /* -------------------------------------------------------------------- */
     413             : 
     414          12 :     VSILFILE *fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
     415          12 :     if (fpJPEG == nullptr)
     416           0 :         return CE_Failure;
     417             : 
     418             :     struct jpeg_error_mgr sJErr;
     419             :     struct jpeg_decompress_struct sDInfo;
     420             :     jmp_buf setjmp_buffer;
     421             : 
     422          12 :     volatile bool bCallDestroyDecompress = false;
     423          12 :     volatile bool bCallDestroyCompress = false;
     424             : 
     425             :     struct jpeg_compress_struct sCInfo;
     426             : 
     427          12 :     if (setjmp(setjmp_buffer))
     428             :     {
     429           0 :         if (bCallDestroyCompress)
     430             :         {
     431           0 :             jpeg_abort_compress(&sCInfo);
     432           0 :             jpeg_destroy_compress(&sCInfo);
     433             :         }
     434           0 :         if (bCallDestroyDecompress)
     435             :         {
     436           0 :             jpeg_abort_decompress(&sDInfo);
     437           0 :             jpeg_destroy_decompress(&sDInfo);
     438             :         }
     439           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpJPEG));
     440           0 :         return CE_Failure;
     441             :     }
     442             : 
     443          12 :     sDInfo.err = jpeg_std_error(&sJErr);
     444          12 :     sJErr.error_exit = GTIFF_ErrorExitJPEG;
     445          12 :     sDInfo.client_data = &setjmp_buffer;
     446             : 
     447          12 :     bCallDestroyDecompress = true;
     448          12 :     jpeg_CreateDecompress(&sDInfo, JPEG_LIB_VERSION, sizeof(sDInfo));
     449             : 
     450          12 :     jpeg_vsiio_src(&sDInfo, fpJPEG);
     451          12 :     jpeg_read_header(&sDInfo, TRUE);
     452             : 
     453          12 :     sCInfo.err = jpeg_std_error(&sJErr);
     454          12 :     sJErr.error_exit = GTIFF_ErrorExitJPEG;
     455          12 :     sCInfo.client_data = &setjmp_buffer;
     456             : 
     457          12 :     jpeg_CreateCompress(&sCInfo, JPEG_LIB_VERSION, sizeof(sCInfo));
     458          12 :     bCallDestroyCompress = true;
     459          12 :     jpeg_copy_critical_parameters(&sDInfo, &sCInfo);
     460          12 :     GTIFF_Set_TIFFTAG_JPEGTABLES(hTIFF, sCInfo);
     461          12 :     bCallDestroyCompress = false;
     462          12 :     jpeg_abort_compress(&sCInfo);
     463          12 :     jpeg_destroy_compress(&sCInfo);
     464          12 :     CPL_IGNORE_RET_VAL(bCallDestroyCompress);
     465             : 
     466             :     /* -------------------------------------------------------------------- */
     467             :     /*      Write TIFFTAG_REFERENCEBLACKWHITE if needed.                    */
     468             :     /* -------------------------------------------------------------------- */
     469             : 
     470          12 :     uint16_t nPhotometric = 0;
     471          12 :     if (!TIFFGetField(hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric)))
     472           0 :         nPhotometric = PHOTOMETRIC_MINISBLACK;
     473             : 
     474          12 :     uint16_t nBitsPerSample = 0;
     475          12 :     if (!TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &(nBitsPerSample)))
     476           0 :         nBitsPerSample = 1;
     477             : 
     478          12 :     if (nPhotometric == PHOTOMETRIC_YCBCR)
     479             :     {
     480             :         /*
     481             :          * A ReferenceBlackWhite field *must* be present since the
     482             :          * default value is inappropriate for YCbCr.  Fill in the
     483             :          * proper value if application didn't set it.
     484             :          */
     485           4 :         float *ref = nullptr;
     486           4 :         if (!TIFFGetField(hTIFF, TIFFTAG_REFERENCEBLACKWHITE, &ref))
     487             :         {
     488           0 :             long top = 1L << nBitsPerSample;
     489           0 :             float refbw[6] = {0.0};
     490           0 :             refbw[1] = static_cast<float>(top - 1L);
     491           0 :             refbw[2] = static_cast<float>(top >> 1);
     492           0 :             refbw[3] = refbw[1];
     493           0 :             refbw[4] = refbw[2];
     494           0 :             refbw[5] = refbw[1];
     495           0 :             TIFFSetField(hTIFF, TIFFTAG_REFERENCEBLACKWHITE, refbw);
     496             :         }
     497             :     }
     498             : 
     499             :     /* -------------------------------------------------------------------- */
     500             :     /*      Write TIFFTAG_YCBCRSUBSAMPLING if needed.                       */
     501             :     /* -------------------------------------------------------------------- */
     502             : 
     503          12 :     if (nPhotometric == PHOTOMETRIC_YCBCR && sDInfo.num_components == 3)
     504             :     {
     505           4 :         if ((sDInfo.comp_info[0].h_samp_factor == 1 ||
     506           4 :              sDInfo.comp_info[0].h_samp_factor == 2) &&
     507           4 :             (sDInfo.comp_info[0].v_samp_factor == 1 ||
     508           4 :              sDInfo.comp_info[0].v_samp_factor == 2) &&
     509           4 :             sDInfo.comp_info[1].h_samp_factor == 1 &&
     510           4 :             sDInfo.comp_info[1].v_samp_factor == 1 &&
     511           4 :             sDInfo.comp_info[2].h_samp_factor == 1 &&
     512           4 :             sDInfo.comp_info[2].v_samp_factor == 1)
     513             :         {
     514           4 :             TIFFSetField(hTIFF, TIFFTAG_YCBCRSUBSAMPLING,
     515           4 :                          sDInfo.comp_info[0].h_samp_factor,
     516           4 :                          sDInfo.comp_info[0].v_samp_factor);
     517             :         }
     518             :         else
     519             :         {
     520           0 :             CPLDebug("GTiff", "Unusual sampling factors. "
     521             :                               "TIFFTAG_YCBCRSUBSAMPLING not written.");
     522             :         }
     523             :     }
     524             : 
     525             :     /* -------------------------------------------------------------------- */
     526             :     /*      Cleanup.                                                        */
     527             :     /* -------------------------------------------------------------------- */
     528             : 
     529          12 :     bCallDestroyDecompress = false;
     530          12 :     jpeg_abort_decompress(&sDInfo);
     531          12 :     jpeg_destroy_decompress(&sDInfo);
     532          12 :     CPL_IGNORE_RET_VAL(bCallDestroyDecompress);
     533             : 
     534          12 :     if (VSIFCloseL(fpJPEG) != 0)
     535           0 :         return CE_Failure;
     536             : 
     537          12 :     return CE_None;
     538             : }
     539             : 
     540             : /************************************************************************/
     541             : /*                    GTIFF_CopyBlockFromJPEG()                         */
     542             : /************************************************************************/
     543             : 
     544             : typedef struct
     545             : {
     546             :     TIFF *hTIFF;
     547             :     jpeg_decompress_struct *psDInfo;
     548             :     int iX;
     549             :     int iY;
     550             :     int nXBlocks;
     551             :     int nXSize;
     552             :     int nYSize;
     553             :     int nBlockXSize;
     554             :     int nBlockYSize;
     555             :     int iMCU_sample_width;
     556             :     int iMCU_sample_height;
     557             :     jvirt_barray_ptr *pSrcCoeffs;
     558             : } GTIFF_CopyBlockFromJPEGArgs;
     559             : 
     560          45 : static CPLErr GTIFF_CopyBlockFromJPEG(GTIFF_CopyBlockFromJPEGArgs *psArgs)
     561             : {
     562             :     const CPLString osTmpFilename(
     563          90 :         VSIMemGenerateHiddenFilename("GTIFF_CopyBlockFromJPEG.tif"));
     564          45 :     VSILFILE *fpMEM = VSIFOpenL(osTmpFilename.c_str(), "wb+");
     565             : 
     566             :     /* -------------------------------------------------------------------- */
     567             :     /*      Initialization of the compressor                                */
     568             :     /* -------------------------------------------------------------------- */
     569             :     jmp_buf setjmp_buffer;
     570          45 :     if (setjmp(setjmp_buffer))
     571             :     {
     572           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpMEM));
     573           0 :         VSIUnlink(osTmpFilename.c_str());
     574           0 :         return CE_Failure;
     575             :     }
     576             : 
     577          45 :     TIFF *hTIFF = psArgs->hTIFF;
     578          45 :     jpeg_decompress_struct *psDInfo = psArgs->psDInfo;
     579          45 :     const int iX = psArgs->iX;
     580          45 :     const int iY = psArgs->iY;
     581          45 :     const int nXBlocks = psArgs->nXBlocks;
     582          45 :     const int nXSize = psArgs->nXSize;
     583          45 :     const int nYSize = psArgs->nYSize;
     584          45 :     const int nBlockXSize = psArgs->nBlockXSize;
     585          45 :     const int nBlockYSize = psArgs->nBlockYSize;
     586          45 :     const int iMCU_sample_width = psArgs->iMCU_sample_width;
     587          45 :     const int iMCU_sample_height = psArgs->iMCU_sample_height;
     588          45 :     jvirt_barray_ptr *pSrcCoeffs = psArgs->pSrcCoeffs;
     589             : 
     590             :     struct jpeg_error_mgr sJErr;
     591             :     struct jpeg_compress_struct sCInfo;
     592          45 :     sCInfo.err = jpeg_std_error(&sJErr);
     593          45 :     sJErr.error_exit = GTIFF_ErrorExitJPEG;
     594          45 :     sCInfo.client_data = &setjmp_buffer;
     595             : 
     596             :     // Initialize destination compression parameters from source values.
     597          45 :     jpeg_CreateCompress(&sCInfo, JPEG_LIB_VERSION, sizeof(sCInfo));
     598          45 :     jpeg_copy_critical_parameters(psDInfo, &sCInfo);
     599             : 
     600             :     // Ensure libjpeg won't write any extraneous markers.
     601          45 :     sCInfo.write_JFIF_header = FALSE;
     602          45 :     sCInfo.write_Adobe_marker = FALSE;
     603             : 
     604             :     /* -------------------------------------------------------------------- */
     605             :     /*      Allocated destination coefficient array                         */
     606             :     /* -------------------------------------------------------------------- */
     607          45 :     const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(hTIFF));
     608             : 
     609          45 :     int nJPEGWidth = nBlockXSize;
     610          45 :     int nJPEGHeight = nBlockYSize;
     611          45 :     if (!bIsTiled)
     612             :     {
     613          37 :         nJPEGWidth = std::min(nBlockXSize, nXSize - iX * nBlockXSize);
     614          37 :         nJPEGHeight = std::min(nBlockYSize, nYSize - iY * nBlockYSize);
     615             :     }
     616             : 
     617             : // Code partially derived from libjpeg transupp.c.
     618             : 
     619             : // Correct the destination's image dimensions as necessary.
     620             : #if JPEG_LIB_VERSION >= 70
     621          45 :     sCInfo.jpeg_width = nJPEGWidth;
     622          45 :     sCInfo.jpeg_height = nJPEGHeight;
     623             : #else
     624             :     sCInfo.image_width = nJPEGWidth;
     625             :     sCInfo.image_height = nJPEGHeight;
     626             : #endif
     627             : 
     628             :     // Save x/y offsets measured in iMCUs.
     629          45 :     const int x_crop_offset = (iX * nBlockXSize) / iMCU_sample_width;
     630          45 :     const int y_crop_offset = (iY * nBlockYSize) / iMCU_sample_height;
     631             : 
     632             :     jvirt_barray_ptr *pDstCoeffs =
     633          90 :         static_cast<jvirt_barray_ptr *>((*sCInfo.mem->alloc_small)(
     634             :             reinterpret_cast<j_common_ptr>(&sCInfo), JPOOL_IMAGE,
     635          45 :             sizeof(jvirt_barray_ptr) * sCInfo.num_components));
     636             : 
     637         168 :     for (int ci = 0; ci < sCInfo.num_components; ci++)
     638             :     {
     639         123 :         jpeg_component_info *compptr = sCInfo.comp_info + ci;
     640             :         int h_samp_factor, v_samp_factor;
     641         123 :         if (sCInfo.num_components == 1)
     642             :         {
     643             :             // Force samp factors to 1x1 in this case.
     644           9 :             h_samp_factor = 1;
     645           9 :             v_samp_factor = 1;
     646             :         }
     647             :         else
     648             :         {
     649         114 :             h_samp_factor = compptr->h_samp_factor;
     650         114 :             v_samp_factor = compptr->v_samp_factor;
     651             :         }
     652         123 :         int width_in_iMCUs =
     653         123 :             (nJPEGWidth + iMCU_sample_width - 1) / iMCU_sample_width;
     654         123 :         int height_in_iMCUs =
     655         123 :             (nJPEGHeight + iMCU_sample_height - 1) / iMCU_sample_height;
     656         123 :         int nWidth_in_blocks = width_in_iMCUs * h_samp_factor;
     657         123 :         int nHeight_in_blocks = height_in_iMCUs * v_samp_factor;
     658         123 :         pDstCoeffs[ci] = (*sCInfo.mem->request_virt_barray)(
     659             :             reinterpret_cast<j_common_ptr>(&sCInfo), JPOOL_IMAGE, FALSE,
     660             :             nWidth_in_blocks, nHeight_in_blocks,
     661             :             static_cast<JDIMENSION>(v_samp_factor));
     662             :     }
     663             : 
     664          45 :     jpeg_vsiio_dest(&sCInfo, fpMEM);
     665             : 
     666             :     // Start compressor (note no image data is actually written here).
     667          45 :     jpeg_write_coefficients(&sCInfo, pDstCoeffs);
     668             : 
     669          45 :     jpeg_suppress_tables(&sCInfo, TRUE);
     670             : 
     671             :     // Must copy the right amount of data (the destination's image size)
     672             :     // starting at the given X and Y offsets in the source.
     673         168 :     for (int ci = 0; ci < sCInfo.num_components; ci++)
     674             :     {
     675         123 :         jpeg_component_info *compptr = sCInfo.comp_info + ci;
     676         123 :         const int x_crop_blocks = x_crop_offset * compptr->h_samp_factor;
     677         123 :         const int y_crop_blocks = y_crop_offset * compptr->v_samp_factor;
     678         123 :         const JDIMENSION nSrcWidthInBlocks =
     679         123 :             psDInfo->comp_info[ci].width_in_blocks;
     680         123 :         const JDIMENSION nSrcHeightInBlocks =
     681         123 :             psDInfo->comp_info[ci].height_in_blocks;
     682             : 
     683         123 :         JDIMENSION nXBlocksToCopy = compptr->width_in_blocks;
     684         123 :         if (x_crop_blocks + compptr->width_in_blocks > nSrcWidthInBlocks)
     685           8 :             nXBlocksToCopy = nSrcWidthInBlocks - x_crop_blocks;
     686             : 
     687         590 :         for (JDIMENSION dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
     688         467 :              dst_blk_y += compptr->v_samp_factor)
     689             :         {
     690         934 :             JBLOCKARRAY dst_buffer = (*psDInfo->mem->access_virt_barray)(
     691         467 :                 reinterpret_cast<j_common_ptr>(psDInfo), pDstCoeffs[ci],
     692         467 :                 dst_blk_y, static_cast<JDIMENSION>(compptr->v_samp_factor),
     693             :                 TRUE);
     694             : 
     695         467 :             int offset_y = 0;
     696         467 :             if (bIsTiled && dst_blk_y + y_crop_blocks + compptr->v_samp_factor >
     697             :                                 nSrcHeightInBlocks)
     698             :             {
     699          94 :                 const int nYBlocks =
     700          94 :                     static_cast<int>(nSrcHeightInBlocks) -
     701          94 :                     static_cast<int>(dst_blk_y + y_crop_blocks);
     702          94 :                 if (nYBlocks > 0)
     703             :                 {
     704             :                     JBLOCKARRAY src_buffer =
     705           2 :                         (*psDInfo->mem->access_virt_barray)(
     706             :                             reinterpret_cast<j_common_ptr>(psDInfo),
     707           2 :                             pSrcCoeffs[ci], dst_blk_y + y_crop_blocks,
     708             :                             static_cast<JDIMENSION>(1), FALSE);
     709           4 :                     for (; offset_y < nYBlocks; offset_y++)
     710             :                     {
     711           2 :                         memcpy(dst_buffer[offset_y],
     712           2 :                                src_buffer[offset_y] + x_crop_blocks,
     713           2 :                                nXBlocksToCopy * (DCTSIZE2 * sizeof(JCOEF)));
     714           2 :                         if (nXBlocksToCopy < compptr->width_in_blocks)
     715             :                         {
     716           1 :                             memset(dst_buffer[offset_y] + nXBlocksToCopy, 0,
     717           1 :                                    (compptr->width_in_blocks - nXBlocksToCopy) *
     718             :                                        (DCTSIZE2 * sizeof(JCOEF)));
     719             :                         }
     720             :                     }
     721             :                 }
     722             : 
     723         218 :                 for (; offset_y < compptr->v_samp_factor; offset_y++)
     724             :                 {
     725         124 :                     memset(dst_buffer[offset_y], 0,
     726         124 :                            compptr->width_in_blocks * DCTSIZE2 * sizeof(JCOEF));
     727          94 :                 }
     728             :             }
     729             :             else
     730             :             {
     731         373 :                 JBLOCKARRAY src_buffer = (*psDInfo->mem->access_virt_barray)(
     732         373 :                     reinterpret_cast<j_common_ptr>(psDInfo), pSrcCoeffs[ci],
     733         373 :                     dst_blk_y + y_crop_blocks,
     734         373 :                     static_cast<JDIMENSION>(compptr->v_samp_factor), FALSE);
     735         829 :                 for (; offset_y < compptr->v_samp_factor; offset_y++)
     736             :                 {
     737         456 :                     memcpy(dst_buffer[offset_y],
     738         456 :                            src_buffer[offset_y] + x_crop_blocks,
     739         456 :                            nXBlocksToCopy * (DCTSIZE2 * sizeof(JCOEF)));
     740         456 :                     if (nXBlocksToCopy < compptr->width_in_blocks)
     741             :                     {
     742          69 :                         memset(dst_buffer[offset_y] + nXBlocksToCopy, 0,
     743          69 :                                (compptr->width_in_blocks - nXBlocksToCopy) *
     744             :                                    (DCTSIZE2 * sizeof(JCOEF)));
     745             :                     }
     746             :                 }
     747             :             }
     748             :         }
     749             :     }
     750             : 
     751          45 :     jpeg_finish_compress(&sCInfo);
     752          45 :     jpeg_destroy_compress(&sCInfo);
     753             : 
     754          45 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fpMEM));
     755             : 
     756             :     /* -------------------------------------------------------------------- */
     757             :     /*      Write the JPEG content with libtiff raw API                     */
     758             :     /* -------------------------------------------------------------------- */
     759          45 :     vsi_l_offset nSize = 0;
     760             :     GByte *pabyJPEGData =
     761          45 :         VSIGetMemFileBuffer(osTmpFilename.c_str(), &nSize, FALSE);
     762             : 
     763          45 :     CPLErr eErr = CE_None;
     764             : 
     765          45 :     if (bIsTiled)
     766             :     {
     767           8 :         if (static_cast<vsi_l_offset>(
     768           8 :                 TIFFWriteRawTile(hTIFF, iX + iY * nXBlocks, pabyJPEGData,
     769           8 :                                  static_cast<tmsize_t>(nSize))) != nSize)
     770           0 :             eErr = CE_Failure;
     771             :     }
     772             :     else
     773             :     {
     774          37 :         if (static_cast<vsi_l_offset>(
     775          37 :                 TIFFWriteRawStrip(hTIFF, iX + iY * nXBlocks, pabyJPEGData,
     776          37 :                                   static_cast<tmsize_t>(nSize))) != nSize)
     777           0 :             eErr = CE_Failure;
     778             :     }
     779             : 
     780          45 :     VSIUnlink(osTmpFilename.c_str());
     781             : 
     782          45 :     return eErr;
     783             : }
     784             : 
     785             : /************************************************************************/
     786             : /*                      GTIFF_CopyFromJPEG()                            */
     787             : /************************************************************************/
     788             : 
     789          12 : CPLErr GTIFF_CopyFromJPEG(GDALDataset *poDS, GDALDataset *poSrcDS,
     790             :                           GDALProgressFunc pfnProgress, void *pProgressData,
     791             :                           bool &bShouldFallbackToNormalCopyIfFail)
     792             : {
     793          12 :     bShouldFallbackToNormalCopyIfFail = true;
     794             : 
     795          12 :     poSrcDS = GetUnderlyingDataset(poSrcDS);
     796          12 :     if (poSrcDS == nullptr)
     797           0 :         return CE_Failure;
     798             : 
     799          12 :     VSILFILE *fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
     800          12 :     if (fpJPEG == nullptr)
     801           0 :         return CE_Failure;
     802             : 
     803          12 :     CPLErr eErr = CE_None;
     804             : 
     805             :     /* -------------------------------------------------------------------- */
     806             :     /*      Initialization of the decompressor                              */
     807             :     /* -------------------------------------------------------------------- */
     808             :     struct jpeg_error_mgr sJErr;
     809             :     struct jpeg_decompress_struct sDInfo;
     810          12 :     memset(&sDInfo, 0, sizeof(sDInfo));
     811             :     jmp_buf setjmp_buffer;
     812          12 :     if (setjmp(setjmp_buffer))
     813             :     {
     814           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpJPEG));
     815           0 :         jpeg_destroy_decompress(&sDInfo);
     816           0 :         return CE_Failure;
     817             :     }
     818             : 
     819          12 :     sDInfo.err = jpeg_std_error(&sJErr);
     820          12 :     sJErr.error_exit = GTIFF_ErrorExitJPEG;
     821          12 :     sDInfo.client_data = &setjmp_buffer;
     822             : 
     823          12 :     jpeg_CreateDecompress(&sDInfo, JPEG_LIB_VERSION, sizeof(sDInfo));
     824             : 
     825             :     // This is to address bug related in ticket #1795.
     826          12 :     if (CPLGetConfigOption("JPEGMEM", nullptr) == nullptr)
     827             :     {
     828             :         // If the user doesn't provide a value for JPEGMEM, be sure that at
     829             :         // least 500 MB will be used before creating the temporary file.
     830          12 :         const long nMinMemory = 500 * 1024 * 1024;
     831          12 :         sDInfo.mem->max_memory_to_use =
     832          12 :             std::max(sDInfo.mem->max_memory_to_use, nMinMemory);
     833             :     }
     834             : 
     835          12 :     jpeg_vsiio_src(&sDInfo, fpJPEG);
     836          12 :     jpeg_read_header(&sDInfo, TRUE);
     837             : 
     838          12 :     jvirt_barray_ptr *pSrcCoeffs = jpeg_read_coefficients(&sDInfo);
     839             : 
     840             :     /* -------------------------------------------------------------------- */
     841             :     /*      Compute MCU dimensions                                          */
     842             :     /* -------------------------------------------------------------------- */
     843          12 :     int iMCU_sample_width = 8;
     844          12 :     int iMCU_sample_height = 8;
     845          12 :     if (sDInfo.num_components != 1)
     846             :     {
     847           8 :         iMCU_sample_width = sDInfo.max_h_samp_factor * 8;
     848           8 :         iMCU_sample_height = sDInfo.max_v_samp_factor * 8;
     849             :     }
     850             : 
     851             :     /* -------------------------------------------------------------------- */
     852             :     /*      Get raster and block dimensions                                 */
     853             :     /* -------------------------------------------------------------------- */
     854          12 :     int nBlockXSize = 0;
     855          12 :     int nBlockYSize = 0;
     856             : 
     857          12 :     const int nXSize = poDS->GetRasterXSize();
     858          12 :     const int nYSize = poDS->GetRasterYSize();
     859             :     // nBands = poDS->GetRasterCount();
     860             : 
     861             :     // Don't use the GDAL block dimensions because of the split-band
     862             :     // mechanism that can expose a pseudo one-line-strip whereas the
     863             :     // real layout is a single big strip.
     864             : 
     865          12 :     TIFF *hTIFF = static_cast<TIFF *>(poDS->GetInternalHandle(nullptr));
     866          12 :     if (TIFFIsTiled(hTIFF))
     867             :     {
     868           2 :         TIFFGetField(hTIFF, TIFFTAG_TILEWIDTH, &(nBlockXSize));
     869           2 :         TIFFGetField(hTIFF, TIFFTAG_TILELENGTH, &(nBlockYSize));
     870             :     }
     871             :     else
     872             :     {
     873          10 :         uint32_t nRowsPerStrip = 0;
     874          10 :         if (!TIFFGetField(hTIFF, TIFFTAG_ROWSPERSTRIP, &(nRowsPerStrip)))
     875             :         {
     876           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     877             :                      "RowsPerStrip not defined ... assuming all one strip.");
     878           0 :             nRowsPerStrip = nYSize;  // Dummy value.
     879             :         }
     880             : 
     881             :         // If the rows per strip is larger than the file we will get
     882             :         // confused.  libtiff internally will treat the rowsperstrip as
     883             :         // the image height and it is best if we do too. (#4468)
     884          10 :         if (nRowsPerStrip > static_cast<uint32_t>(nYSize))
     885           0 :             nRowsPerStrip = nYSize;
     886             : 
     887          10 :         nBlockXSize = nXSize;
     888          10 :         nBlockYSize = nRowsPerStrip;
     889             :     }
     890             : 
     891          12 :     const int nXBlocks = (nXSize + nBlockXSize - 1) / nBlockXSize;
     892          12 :     const int nYBlocks = (nYSize + nBlockYSize - 1) / nBlockYSize;
     893             : 
     894             :     /* -------------------------------------------------------------------- */
     895             :     /*      Copy blocks.                                                    */
     896             :     /* -------------------------------------------------------------------- */
     897             : 
     898          12 :     bShouldFallbackToNormalCopyIfFail = false;
     899             : 
     900          53 :     for (int iY = 0; iY < nYBlocks && eErr == CE_None; iY++)
     901             :     {
     902          86 :         for (int iX = 0; iX < nXBlocks && eErr == CE_None; iX++)
     903             :         {
     904             :             GTIFF_CopyBlockFromJPEGArgs sArgs;
     905          45 :             sArgs.hTIFF = hTIFF;
     906          45 :             sArgs.psDInfo = &sDInfo;
     907          45 :             sArgs.iX = iX;
     908          45 :             sArgs.iY = iY;
     909          45 :             sArgs.nXBlocks = nXBlocks;
     910          45 :             sArgs.nXSize = nXSize;
     911          45 :             sArgs.nYSize = nYSize;
     912          45 :             sArgs.nBlockXSize = nBlockXSize;
     913          45 :             sArgs.nBlockYSize = nBlockYSize;
     914          45 :             sArgs.iMCU_sample_width = iMCU_sample_width;
     915          45 :             sArgs.iMCU_sample_height = iMCU_sample_height;
     916          45 :             sArgs.pSrcCoeffs = pSrcCoeffs;
     917             : 
     918          45 :             eErr = GTIFF_CopyBlockFromJPEG(&sArgs);
     919             : 
     920          45 :             if (!pfnProgress((iY * nXBlocks + iX + 1) * 1.0 /
     921          45 :                                  (nXBlocks * nYBlocks),
     922             :                              nullptr, pProgressData))
     923           0 :                 eErr = CE_Failure;
     924             :         }
     925             :     }
     926             : 
     927             :     /* -------------------------------------------------------------------- */
     928             :     /*      Cleanup.                                                        */
     929             :     /* -------------------------------------------------------------------- */
     930             : 
     931          12 :     jpeg_finish_decompress(&sDInfo);
     932          12 :     jpeg_destroy_decompress(&sDInfo);
     933             : 
     934          12 :     if (VSIFCloseL(fpJPEG) != 0)
     935           0 :         eErr = CE_Failure;
     936             : 
     937          12 :     return eErr;
     938             : }
     939             : 
     940             : #endif  // HAVE_LIBJPEG

Generated by: LCOV version 1.14