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

Generated by: LCOV version 1.14