LCOV - code coverage report
Current view: top level - frmts/gtiff - gtiffdataset_write.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3883 4256 91.2 %
Date: 2025-09-10 17:48:50 Functions: 109 138 79.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GeoTIFF Driver
       4             :  * Purpose:  Write/set operations on GTiffDataset
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1998, 2002, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2007-2015, Even Rouault <even dot rouault at spatialys dot com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gtiffdataset.h"
      15             : #include "gtiffrasterband.h"
      16             : #include "gtiffoddbitsband.h"
      17             : 
      18             : #include <cassert>
      19             : #include <cerrno>
      20             : 
      21             : #include <algorithm>
      22             : #include <cmath>
      23             : #include <limits>
      24             : #include <memory>
      25             : #include <mutex>
      26             : #include <set>
      27             : #include <string>
      28             : #include <tuple>
      29             : #include <utility>
      30             : 
      31             : #include "cpl_error.h"
      32             : #include "cpl_error_internal.h"  // CPLErrorHandlerAccumulatorStruct
      33             : #include "cpl_float.h"
      34             : #include "cpl_md5.h"
      35             : #include "cpl_vsi.h"
      36             : #include "cpl_vsi_virtual.h"
      37             : #include "cpl_worker_thread_pool.h"
      38             : #include "fetchbufferdirectio.h"
      39             : #include "gdal_mdreader.h"          // GDALWriteRPCTXTFile()
      40             : #include "gdal_priv_templates.hpp"  // GDALIsValueInRange<>
      41             : #include "gdal_thread_pool.h"       // GDALGetGlobalThreadPool()
      42             : #include "geovalues.h"              // RasterPixelIsPoint
      43             : #include "gt_jpeg_copy.h"
      44             : #include "gt_overview.h"  // GTIFFBuildOverviewMetadata()
      45             : #include "quant_table_md5sum.h"
      46             : #include "quant_table_md5sum_jpeg9e.h"
      47             : #include "tif_jxl.h"
      48             : #include "tifvsi.h"
      49             : #include "xtiffio.h"
      50             : 
      51             : #if LIFFLIB_VERSION > 20230908 || defined(INTERNAL_LIBTIFF)
      52             : /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
      53             : #define LIBTIFF_MULTIBAND_LERC_NAN_OK
      54             : #endif
      55             : 
      56             : static const int knGTIFFJpegTablesModeDefault = JPEGTABLESMODE_QUANT;
      57             : 
      58             : static constexpr const char szPROFILE_BASELINE[] = "BASELINE";
      59             : static constexpr const char szPROFILE_GeoTIFF[] = "GeoTIFF";
      60             : static constexpr const char szPROFILE_GDALGeoTIFF[] = "GDALGeoTIFF";
      61             : 
      62             : // Due to libgeotiff/xtiff.c declaring TIFFTAG_GEOTIEPOINTS with field_readcount
      63             : // and field_writecount == -1 == TIFF_VARIABLE, we are limited to writing
      64             : // 65535 values in that tag. That could potentially be overcome by changing the tag
      65             : // declaration to using TIFF_VARIABLE2 where the count is a uint32_t.
      66             : constexpr int knMAX_GCP_COUNT =
      67             :     static_cast<int>(std::numeric_limits<uint16_t>::max() / 6);
      68             : 
      69             : enum
      70             : {
      71             :     ENDIANNESS_NATIVE,
      72             :     ENDIANNESS_LITTLE,
      73             :     ENDIANNESS_BIG
      74             : };
      75             : 
      76       17066 : static signed char GTiffGetWebPLevel(CSLConstList papszOptions)
      77             : {
      78       17066 :     int nWebPLevel = DEFAULT_WEBP_LEVEL;
      79       17066 :     const char *pszValue = CSLFetchNameValue(papszOptions, "WEBP_LEVEL");
      80       17066 :     if (pszValue != nullptr)
      81             :     {
      82          51 :         nWebPLevel = atoi(pszValue);
      83          51 :         if (!(nWebPLevel >= 1 && nWebPLevel <= 100))
      84             :         {
      85           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
      86             :                      "WEBP_LEVEL=%s value not recognised, ignoring.", pszValue);
      87           0 :             nWebPLevel = DEFAULT_WEBP_LEVEL;
      88             :         }
      89             :     }
      90       17066 :     return static_cast<signed char>(nWebPLevel);
      91             : }
      92             : 
      93       17071 : static bool GTiffGetWebPLossless(CSLConstList papszOptions)
      94             : {
      95       17071 :     return CPLFetchBool(papszOptions, "WEBP_LOSSLESS", false);
      96             : }
      97             : 
      98       17138 : static double GTiffGetLERCMaxZError(CSLConstList papszOptions)
      99             : {
     100       17138 :     return CPLAtof(CSLFetchNameValueDef(papszOptions, "MAX_Z_ERROR", "0.0"));
     101             : }
     102             : 
     103        7604 : static double GTiffGetLERCMaxZErrorOverview(CSLConstList papszOptions)
     104             : {
     105        7604 :     return CPLAtof(CSLFetchNameValueDef(
     106             :         papszOptions, "MAX_Z_ERROR_OVERVIEW",
     107        7604 :         CSLFetchNameValueDef(papszOptions, "MAX_Z_ERROR", "0.0")));
     108             : }
     109             : 
     110             : #if HAVE_JXL
     111       17142 : static bool GTiffGetJXLLossless(CSLConstList papszOptions,
     112             :                                 bool *pbIsSpecified = nullptr)
     113             : {
     114       17142 :     const char *pszVal = CSLFetchNameValue(papszOptions, "JXL_LOSSLESS");
     115       17142 :     if (pbIsSpecified)
     116        9534 :         *pbIsSpecified = pszVal != nullptr;
     117       17142 :     return pszVal == nullptr || CPLTestBool(pszVal);
     118             : }
     119             : 
     120       17142 : static uint32_t GTiffGetJXLEffort(CSLConstList papszOptions)
     121             : {
     122       17142 :     return atoi(CSLFetchNameValueDef(papszOptions, "JXL_EFFORT", "5"));
     123             : }
     124             : 
     125       17059 : static float GTiffGetJXLDistance(CSLConstList papszOptions,
     126             :                                  bool *pbIsSpecified = nullptr)
     127             : {
     128       17059 :     const char *pszVal = CSLFetchNameValue(papszOptions, "JXL_DISTANCE");
     129       17060 :     if (pbIsSpecified)
     130        9533 :         *pbIsSpecified = pszVal != nullptr;
     131       17060 :     return pszVal == nullptr ? 1.0f : static_cast<float>(CPLAtof(pszVal));
     132             : }
     133             : 
     134       17142 : static float GTiffGetJXLAlphaDistance(CSLConstList papszOptions,
     135             :                                       bool *pbIsSpecified = nullptr)
     136             : {
     137       17142 :     const char *pszVal = CSLFetchNameValue(papszOptions, "JXL_ALPHA_DISTANCE");
     138       17142 :     if (pbIsSpecified)
     139        9533 :         *pbIsSpecified = pszVal != nullptr;
     140       17142 :     return pszVal == nullptr ? -1.0f : static_cast<float>(CPLAtof(pszVal));
     141             : }
     142             : 
     143             : #endif
     144             : 
     145             : /************************************************************************/
     146             : /*                           FillEmptyTiles()                           */
     147             : /************************************************************************/
     148             : 
     149        7780 : CPLErr GTiffDataset::FillEmptyTiles()
     150             : 
     151             : {
     152             :     /* -------------------------------------------------------------------- */
     153             :     /*      How many blocks are there in this file?                         */
     154             :     /* -------------------------------------------------------------------- */
     155       15560 :     const int nBlockCount = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     156        7780 :                                 ? m_nBlocksPerBand * nBands
     157             :                                 : m_nBlocksPerBand;
     158             : 
     159             :     /* -------------------------------------------------------------------- */
     160             :     /*      Fetch block maps.                                               */
     161             :     /* -------------------------------------------------------------------- */
     162        7780 :     toff_t *panByteCounts = nullptr;
     163             : 
     164        7780 :     if (TIFFIsTiled(m_hTIFF))
     165        1018 :         TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts);
     166             :     else
     167        6762 :         TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
     168             : 
     169        7780 :     if (panByteCounts == nullptr)
     170             :     {
     171             :         // Got here with libtiff 3.9.3 and tiff_write_8 test.
     172           0 :         ReportError(CE_Failure, CPLE_AppDefined,
     173             :                     "FillEmptyTiles() failed because panByteCounts == NULL");
     174           0 :         return CE_Failure;
     175             :     }
     176             : 
     177             :     /* -------------------------------------------------------------------- */
     178             :     /*      Prepare a blank data buffer to write for uninitialized blocks.  */
     179             :     /* -------------------------------------------------------------------- */
     180             :     const GPtrDiff_t nBlockBytes =
     181        7780 :         TIFFIsTiled(m_hTIFF) ? static_cast<GPtrDiff_t>(TIFFTileSize(m_hTIFF))
     182        6762 :                              : static_cast<GPtrDiff_t>(TIFFStripSize(m_hTIFF));
     183             : 
     184        7780 :     GByte *pabyData = static_cast<GByte *>(VSI_CALLOC_VERBOSE(nBlockBytes, 1));
     185        7780 :     if (pabyData == nullptr)
     186             :     {
     187           0 :         return CE_Failure;
     188             :     }
     189             : 
     190             :     // Force tiles completely filled with the nodata value to be written.
     191        7780 :     m_bWriteEmptyTiles = true;
     192             : 
     193             :     /* -------------------------------------------------------------------- */
     194             :     /*      If set, fill data buffer with no data value.                    */
     195             :     /* -------------------------------------------------------------------- */
     196        7780 :     if ((m_bNoDataSet && m_dfNoDataValue != 0.0) ||
     197        7539 :         (m_bNoDataSetAsInt64 && m_nNoDataValueInt64 != 0) ||
     198        7536 :         (m_bNoDataSetAsUInt64 && m_nNoDataValueUInt64 != 0))
     199             :     {
     200         247 :         const GDALDataType eDataType = GetRasterBand(1)->GetRasterDataType();
     201         247 :         const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
     202         247 :         if (nDataTypeSize &&
     203         247 :             nDataTypeSize * 8 == static_cast<int>(m_nBitsPerSample))
     204             :         {
     205         236 :             if (m_bNoDataSetAsInt64)
     206             :             {
     207           4 :                 GDALCopyWords64(&m_nNoDataValueInt64, GDT_Int64, 0, pabyData,
     208             :                                 eDataType, nDataTypeSize,
     209           4 :                                 nBlockBytes / nDataTypeSize);
     210             :             }
     211         232 :             else if (m_bNoDataSetAsUInt64)
     212             :             {
     213           3 :                 GDALCopyWords64(&m_nNoDataValueUInt64, GDT_UInt64, 0, pabyData,
     214             :                                 eDataType, nDataTypeSize,
     215           3 :                                 nBlockBytes / nDataTypeSize);
     216             :             }
     217             :             else
     218             :             {
     219         229 :                 double dfNoData = m_dfNoDataValue;
     220         229 :                 GDALCopyWords64(&dfNoData, GDT_Float64, 0, pabyData, eDataType,
     221         229 :                                 nDataTypeSize, nBlockBytes / nDataTypeSize);
     222         236 :             }
     223             :         }
     224          11 :         else if (nDataTypeSize)
     225             :         {
     226             :             // Handle non power-of-two depths.
     227             :             // Ideally make a packed buffer, but that is a bit tedious,
     228             :             // so use the normal I/O interfaces.
     229             : 
     230          11 :             CPLFree(pabyData);
     231             : 
     232          11 :             pabyData = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
     233             :                 m_nBlockXSize, m_nBlockYSize, nDataTypeSize));
     234          11 :             if (pabyData == nullptr)
     235           0 :                 return CE_Failure;
     236          11 :             if (m_bNoDataSetAsInt64)
     237             :             {
     238           0 :                 GDALCopyWords64(&m_nNoDataValueInt64, GDT_Int64, 0, pabyData,
     239             :                                 eDataType, nDataTypeSize,
     240           0 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     241           0 :                                     m_nBlockYSize);
     242             :             }
     243          11 :             else if (m_bNoDataSetAsUInt64)
     244             :             {
     245           0 :                 GDALCopyWords64(&m_nNoDataValueUInt64, GDT_UInt64, 0, pabyData,
     246             :                                 eDataType, nDataTypeSize,
     247           0 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     248           0 :                                     m_nBlockYSize);
     249             :             }
     250             :             else
     251             :             {
     252          11 :                 GDALCopyWords64(&m_dfNoDataValue, GDT_Float64, 0, pabyData,
     253             :                                 eDataType, nDataTypeSize,
     254          11 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     255          11 :                                     m_nBlockYSize);
     256             :             }
     257          11 :             CPLErr eErr = CE_None;
     258          46 :             for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     259             :             {
     260          35 :                 if (panByteCounts[iBlock] == 0)
     261             :                 {
     262          18 :                     if (m_nPlanarConfig == PLANARCONFIG_SEPARATE || nBands == 1)
     263             :                     {
     264          24 :                         if (GetRasterBand(1 + iBlock / m_nBlocksPerBand)
     265          12 :                                 ->WriteBlock((iBlock % m_nBlocksPerBand) %
     266          12 :                                                  m_nBlocksPerRow,
     267          12 :                                              (iBlock % m_nBlocksPerBand) /
     268          12 :                                                  m_nBlocksPerRow,
     269          12 :                                              pabyData) != CE_None)
     270             :                         {
     271           0 :                             eErr = CE_Failure;
     272             :                         }
     273             :                     }
     274             :                     else
     275             :                     {
     276             :                         // In contig case, don't directly call WriteBlock(), as
     277             :                         // it could cause useless decompression-recompression.
     278           6 :                         const int nXOff =
     279           6 :                             (iBlock % m_nBlocksPerRow) * m_nBlockXSize;
     280           6 :                         const int nYOff =
     281           6 :                             (iBlock / m_nBlocksPerRow) * m_nBlockYSize;
     282           6 :                         const int nXSize =
     283           6 :                             (nXOff + m_nBlockXSize <= nRasterXSize)
     284           6 :                                 ? m_nBlockXSize
     285           2 :                                 : nRasterXSize - nXOff;
     286           6 :                         const int nYSize =
     287           6 :                             (nYOff + m_nBlockYSize <= nRasterYSize)
     288           6 :                                 ? m_nBlockYSize
     289           3 :                                 : nRasterYSize - nYOff;
     290          18 :                         for (int iBand = 1; iBand <= nBands; ++iBand)
     291             :                         {
     292          12 :                             if (GetRasterBand(iBand)->RasterIO(
     293             :                                     GF_Write, nXOff, nYOff, nXSize, nYSize,
     294             :                                     pabyData, nXSize, nYSize, eDataType, 0, 0,
     295          12 :                                     nullptr) != CE_None)
     296             :                             {
     297           0 :                                 eErr = CE_Failure;
     298             :                             }
     299             :                         }
     300             :                     }
     301             :                 }
     302             :             }
     303          11 :             CPLFree(pabyData);
     304          11 :             return eErr;
     305         236 :         }
     306             :     }
     307             : 
     308             :     /* -------------------------------------------------------------------- */
     309             :     /*      When we must fill with zeroes, try to create non-sparse file    */
     310             :     /*      w.r.t TIFF spec ... as a sparse file w.r.t filesystem, ie by    */
     311             :     /*      seeking to end of file instead of writing zero blocks.          */
     312             :     /* -------------------------------------------------------------------- */
     313        7533 :     else if (m_nCompression == COMPRESSION_NONE && (m_nBitsPerSample % 8) == 0)
     314             :     {
     315        6083 :         CPLErr eErr = CE_None;
     316             :         // Only use libtiff to write the first sparse block to ensure that it
     317             :         // will serialize offset and count arrays back to disk.
     318        6083 :         int nCountBlocksToZero = 0;
     319     2317850 :         for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     320             :         {
     321     2311770 :             if (panByteCounts[iBlock] == 0)
     322             :             {
     323     2218770 :                 if (nCountBlocksToZero == 0)
     324             :                 {
     325        1070 :                     const bool bWriteEmptyTilesBak = m_bWriteEmptyTiles;
     326        1070 :                     m_bWriteEmptyTiles = true;
     327        1070 :                     const bool bOK = WriteEncodedTileOrStrip(iBlock, pabyData,
     328        1070 :                                                              FALSE) == CE_None;
     329        1070 :                     m_bWriteEmptyTiles = bWriteEmptyTilesBak;
     330        1070 :                     if (!bOK)
     331             :                     {
     332           2 :                         eErr = CE_Failure;
     333           2 :                         break;
     334             :                     }
     335             :                 }
     336     2218760 :                 nCountBlocksToZero++;
     337             :             }
     338             :         }
     339        6083 :         CPLFree(pabyData);
     340             : 
     341        6083 :         --nCountBlocksToZero;
     342             : 
     343             :         // And then seek to end of file for other ones.
     344        6083 :         if (nCountBlocksToZero > 0)
     345             :         {
     346         322 :             toff_t *panByteOffsets = nullptr;
     347             : 
     348         322 :             if (TIFFIsTiled(m_hTIFF))
     349          87 :                 TIFFGetField(m_hTIFF, TIFFTAG_TILEOFFSETS, &panByteOffsets);
     350             :             else
     351         235 :                 TIFFGetField(m_hTIFF, TIFFTAG_STRIPOFFSETS, &panByteOffsets);
     352             : 
     353         322 :             if (panByteOffsets == nullptr)
     354             :             {
     355           0 :                 ReportError(
     356             :                     CE_Failure, CPLE_AppDefined,
     357             :                     "FillEmptyTiles() failed because panByteOffsets == NULL");
     358           0 :                 return CE_Failure;
     359             :             }
     360             : 
     361         322 :             VSILFILE *fpTIF = VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
     362         322 :             VSIFSeekL(fpTIF, 0, SEEK_END);
     363         322 :             const vsi_l_offset nOffset = VSIFTellL(fpTIF);
     364             : 
     365         322 :             vsi_l_offset iBlockToZero = 0;
     366     2226870 :             for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     367             :             {
     368     2226550 :                 if (panByteCounts[iBlock] == 0)
     369             :                 {
     370     2217700 :                     panByteOffsets[iBlock] = static_cast<toff_t>(
     371     2217700 :                         nOffset + iBlockToZero * nBlockBytes);
     372     2217700 :                     panByteCounts[iBlock] = nBlockBytes;
     373     2217700 :                     iBlockToZero++;
     374             :                 }
     375             :             }
     376         322 :             CPLAssert(iBlockToZero ==
     377             :                       static_cast<vsi_l_offset>(nCountBlocksToZero));
     378             : 
     379         322 :             if (VSIFTruncateL(fpTIF, nOffset + iBlockToZero * nBlockBytes) != 0)
     380             :             {
     381           0 :                 eErr = CE_Failure;
     382           0 :                 ReportError(CE_Failure, CPLE_FileIO,
     383             :                             "Cannot initialize empty blocks");
     384             :             }
     385             :         }
     386             : 
     387        6083 :         return eErr;
     388             :     }
     389             : 
     390             :     /* -------------------------------------------------------------------- */
     391             :     /*      Check all blocks, writing out data for uninitialized blocks.    */
     392             :     /* -------------------------------------------------------------------- */
     393             : 
     394        1686 :     GByte *pabyRaw = nullptr;
     395        1686 :     vsi_l_offset nRawSize = 0;
     396        1686 :     CPLErr eErr = CE_None;
     397       45501 :     for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     398             :     {
     399       43822 :         if (panByteCounts[iBlock] == 0)
     400             :         {
     401        8941 :             if (pabyRaw == nullptr)
     402             :             {
     403        1638 :                 if (WriteEncodedTileOrStrip(iBlock, pabyData, FALSE) != CE_None)
     404             :                 {
     405           7 :                     eErr = CE_Failure;
     406           7 :                     break;
     407             :                 }
     408             : 
     409        1631 :                 vsi_l_offset nOffset = 0;
     410        1631 :                 if (!IsBlockAvailable(iBlock, &nOffset, &nRawSize, nullptr))
     411           0 :                     break;
     412             : 
     413             :                 // When using compression, get back the compressed block
     414             :                 // so we can use the raw API to write it faster.
     415        1631 :                 if (m_nCompression != COMPRESSION_NONE)
     416             :                 {
     417             :                     pabyRaw = static_cast<GByte *>(
     418         427 :                         VSI_MALLOC_VERBOSE(static_cast<size_t>(nRawSize)));
     419         427 :                     if (pabyRaw)
     420             :                     {
     421             :                         VSILFILE *fp =
     422         427 :                             VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
     423         427 :                         const vsi_l_offset nCurOffset = VSIFTellL(fp);
     424         427 :                         VSIFSeekL(fp, nOffset, SEEK_SET);
     425         427 :                         VSIFReadL(pabyRaw, 1, static_cast<size_t>(nRawSize),
     426             :                                   fp);
     427         427 :                         VSIFSeekL(fp, nCurOffset, SEEK_SET);
     428             :                     }
     429             :                 }
     430             :             }
     431             :             else
     432             :             {
     433        7303 :                 WriteRawStripOrTile(iBlock, pabyRaw,
     434             :                                     static_cast<GPtrDiff_t>(nRawSize));
     435             :             }
     436             :         }
     437             :     }
     438             : 
     439        1686 :     CPLFree(pabyData);
     440        1686 :     VSIFree(pabyRaw);
     441        1686 :     return eErr;
     442             : }
     443             : 
     444             : /************************************************************************/
     445             : /*                         HasOnlyNoData()                              */
     446             : /************************************************************************/
     447             : 
     448       42270 : bool GTiffDataset::HasOnlyNoData(const void *pBuffer, int nWidth, int nHeight,
     449             :                                  int nLineStride, int nComponents)
     450             : {
     451       42270 :     if (m_nSampleFormat == SAMPLEFORMAT_COMPLEXINT ||
     452       42270 :         m_nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP)
     453           0 :         return false;
     454       42270 :     if (m_bNoDataSetAsInt64 || m_bNoDataSetAsUInt64)
     455           2 :         return false;  // FIXME: over pessimistic
     456       84536 :     return GDALBufferHasOnlyNoData(
     457       42268 :         pBuffer, m_bNoDataSet ? m_dfNoDataValue : 0.0, nWidth, nHeight,
     458       42268 :         nLineStride, nComponents, m_nBitsPerSample,
     459       42268 :         m_nSampleFormat == SAMPLEFORMAT_UINT  ? GSF_UNSIGNED_INT
     460        4737 :         : m_nSampleFormat == SAMPLEFORMAT_INT ? GSF_SIGNED_INT
     461       42268 :                                               : GSF_FLOATING_POINT);
     462             : }
     463             : 
     464             : /************************************************************************/
     465             : /*                     IsFirstPixelEqualToNoData()                      */
     466             : /************************************************************************/
     467             : 
     468      166763 : inline bool GTiffDataset::IsFirstPixelEqualToNoData(const void *pBuffer)
     469             : {
     470      166763 :     const GDALDataType eDT = GetRasterBand(1)->GetRasterDataType();
     471      166769 :     const double dfEffectiveNoData = (m_bNoDataSet) ? m_dfNoDataValue : 0.0;
     472      166769 :     if (m_bNoDataSetAsInt64 || m_bNoDataSetAsUInt64)
     473           3 :         return true;  // FIXME: over pessimistic
     474      166766 :     if (m_nBitsPerSample == 8 ||
     475       58287 :         (m_nBitsPerSample < 8 && dfEffectiveNoData == 0))
     476             :     {
     477      111932 :         if (eDT == GDT_Int8)
     478             :         {
     479         270 :             return GDALIsValueInRange<signed char>(dfEffectiveNoData) &&
     480         135 :                    *(static_cast<const signed char *>(pBuffer)) ==
     481         270 :                        static_cast<signed char>(dfEffectiveNoData);
     482             :         }
     483      223563 :         return GDALIsValueInRange<GByte>(dfEffectiveNoData) &&
     484      111766 :                *(static_cast<const GByte *>(pBuffer)) ==
     485      223563 :                    static_cast<GByte>(dfEffectiveNoData);
     486             :     }
     487       54834 :     if (m_nBitsPerSample == 16 && eDT == GDT_UInt16)
     488             :     {
     489        3742 :         return GDALIsValueInRange<GUInt16>(dfEffectiveNoData) &&
     490        1871 :                *(static_cast<const GUInt16 *>(pBuffer)) ==
     491        3742 :                    static_cast<GUInt16>(dfEffectiveNoData);
     492             :     }
     493       52963 :     if (m_nBitsPerSample == 16 && eDT == GDT_Int16)
     494             :     {
     495        8430 :         return GDALIsValueInRange<GInt16>(dfEffectiveNoData) &&
     496        4215 :                *(static_cast<const GInt16 *>(pBuffer)) ==
     497        8430 :                    static_cast<GInt16>(dfEffectiveNoData);
     498             :     }
     499       48748 :     if (m_nBitsPerSample == 32 && eDT == GDT_UInt32)
     500             :     {
     501         370 :         return GDALIsValueInRange<GUInt32>(dfEffectiveNoData) &&
     502         185 :                *(static_cast<const GUInt32 *>(pBuffer)) ==
     503         370 :                    static_cast<GUInt32>(dfEffectiveNoData);
     504             :     }
     505       48563 :     if (m_nBitsPerSample == 32 && eDT == GDT_Int32)
     506             :     {
     507         498 :         return GDALIsValueInRange<GInt32>(dfEffectiveNoData) &&
     508         249 :                *(static_cast<const GInt32 *>(pBuffer)) ==
     509         498 :                    static_cast<GInt32>(dfEffectiveNoData);
     510             :     }
     511       48314 :     if (m_nBitsPerSample == 64 && eDT == GDT_UInt64)
     512             :     {
     513         234 :         return GDALIsValueInRange<std::uint64_t>(dfEffectiveNoData) &&
     514         117 :                *(static_cast<const std::uint64_t *>(pBuffer)) ==
     515         234 :                    static_cast<std::uint64_t>(dfEffectiveNoData);
     516             :     }
     517       48197 :     if (m_nBitsPerSample == 64 && eDT == GDT_Int64)
     518             :     {
     519         236 :         return GDALIsValueInRange<std::int64_t>(dfEffectiveNoData) &&
     520         118 :                *(static_cast<const std::int64_t *>(pBuffer)) ==
     521         236 :                    static_cast<std::int64_t>(dfEffectiveNoData);
     522             :     }
     523       48079 :     if (m_nBitsPerSample == 32 && eDT == GDT_Float32)
     524             :     {
     525       41206 :         if (std::isnan(m_dfNoDataValue))
     526           3 :             return CPL_TO_BOOL(
     527           6 :                 std::isnan(*(static_cast<const float *>(pBuffer))));
     528       82380 :         return GDALIsValueInRange<float>(dfEffectiveNoData) &&
     529       41190 :                *(static_cast<const float *>(pBuffer)) ==
     530       82379 :                    static_cast<float>(dfEffectiveNoData);
     531             :     }
     532        6873 :     if (m_nBitsPerSample == 64 && eDT == GDT_Float64)
     533             :     {
     534        4227 :         if (std::isnan(dfEffectiveNoData))
     535           0 :             return CPL_TO_BOOL(
     536           0 :                 std::isnan(*(static_cast<const double *>(pBuffer))));
     537        4227 :         return *(static_cast<const double *>(pBuffer)) == dfEffectiveNoData;
     538             :     }
     539        2646 :     return false;
     540             : }
     541             : 
     542             : /************************************************************************/
     543             : /*                      WriteDealWithLercAndNan()                       */
     544             : /************************************************************************/
     545             : 
     546             : template <typename T>
     547           0 : void GTiffDataset::WriteDealWithLercAndNan(T *pBuffer, int nActualBlockWidth,
     548             :                                            int nActualBlockHeight,
     549             :                                            int nStrileHeight)
     550             : {
     551             :     // This method does 2 things:
     552             :     // - warn the user if he tries to write NaN values with libtiff < 4.6.1
     553             :     //   and multi-band PlanarConfig=Contig configuration
     554             :     // - and in right-most and bottom-most tiles, replace non accessible
     555             :     //   pixel values by a safe one.
     556             : 
     557           0 :     const auto fPaddingValue =
     558             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     559             :         m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1
     560             :             ? 0
     561             :             :
     562             : #endif
     563             :             std::numeric_limits<T>::quiet_NaN();
     564             : 
     565           0 :     const int nBandsPerStrile =
     566           0 :         m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     567           0 :     for (int j = 0; j < nActualBlockHeight; ++j)
     568             :     {
     569             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     570             :         static bool bHasWarned = false;
     571             :         if (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1 && !bHasWarned)
     572             :         {
     573             :             for (int i = 0; i < nActualBlockWidth * nBandsPerStrile; ++i)
     574             :             {
     575             :                 if (std::isnan(
     576             :                         pBuffer[j * m_nBlockXSize * nBandsPerStrile + i]))
     577             :                 {
     578             :                     bHasWarned = true;
     579             :                     CPLError(CE_Warning, CPLE_AppDefined,
     580             :                              "libtiff < 4.6.1 does not handle properly NaN "
     581             :                              "values for multi-band PlanarConfig=Contig "
     582             :                              "configuration. As a workaround, you can set the "
     583             :                              "INTERLEAVE=BAND creation option.");
     584             :                     break;
     585             :                 }
     586             :             }
     587             :         }
     588             : #endif
     589           0 :         for (int i = nActualBlockWidth * nBandsPerStrile;
     590           0 :              i < m_nBlockXSize * nBandsPerStrile; ++i)
     591             :         {
     592           0 :             pBuffer[j * m_nBlockXSize * nBandsPerStrile + i] = fPaddingValue;
     593             :         }
     594             :     }
     595           0 :     for (int j = nActualBlockHeight; j < nStrileHeight; ++j)
     596             :     {
     597           0 :         for (int i = 0; i < m_nBlockXSize * nBandsPerStrile; ++i)
     598             :         {
     599           0 :             pBuffer[j * m_nBlockXSize * nBandsPerStrile + i] = fPaddingValue;
     600             :         }
     601             :     }
     602           0 : }
     603             : 
     604             : /************************************************************************/
     605             : /*                        WriteEncodedTile()                            */
     606             : /************************************************************************/
     607             : 
     608       49867 : bool GTiffDataset::WriteEncodedTile(uint32_t tile, GByte *pabyData,
     609             :                                     int bPreserveDataBuffer)
     610             : {
     611       49867 :     const int iColumn = (tile % m_nBlocksPerBand) % m_nBlocksPerRow;
     612       49867 :     const int iRow = (tile % m_nBlocksPerBand) / m_nBlocksPerRow;
     613             : 
     614       99734 :     const int nActualBlockWidth = (iColumn == m_nBlocksPerRow - 1)
     615       49867 :                                       ? nRasterXSize - iColumn * m_nBlockXSize
     616             :                                       : m_nBlockXSize;
     617       99734 :     const int nActualBlockHeight = (iRow == m_nBlocksPerColumn - 1)
     618       49867 :                                        ? nRasterYSize - iRow * m_nBlockYSize
     619             :                                        : m_nBlockYSize;
     620             : 
     621             :     /* -------------------------------------------------------------------- */
     622             :     /*      Don't write empty blocks in some cases.                         */
     623             :     /* -------------------------------------------------------------------- */
     624       49867 :     if (!m_bWriteEmptyTiles && IsFirstPixelEqualToNoData(pabyData))
     625             :     {
     626        1938 :         if (!IsBlockAvailable(tile, nullptr, nullptr, nullptr))
     627             :         {
     628        1938 :             const int nComponents =
     629        1938 :                 m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     630             : 
     631        1938 :             if (HasOnlyNoData(pabyData, nActualBlockWidth, nActualBlockHeight,
     632             :                               m_nBlockXSize, nComponents))
     633             :             {
     634        1188 :                 return true;
     635             :             }
     636             :         }
     637             :     }
     638             : 
     639             :     // Is this a partial right edge or bottom edge tile?
     640       94531 :     const bool bPartialTile = (nActualBlockWidth < m_nBlockXSize) ||
     641       45852 :                               (nActualBlockHeight < m_nBlockYSize);
     642             : 
     643             :     const bool bIsLercFloatingPoint =
     644       48744 :         m_nCompression == COMPRESSION_LERC &&
     645          66 :         (GetRasterBand(1)->GetRasterDataType() == GDT_Float32 ||
     646          64 :          GetRasterBand(1)->GetRasterDataType() == GDT_Float64);
     647             : 
     648             :     // Do we need to spread edge values right or down for a partial
     649             :     // JPEG encoded tile?  We do this to avoid edge artifacts.
     650             :     // We also need to be careful with LERC and NaN values
     651       48678 :     const bool bNeedTempBuffer =
     652       53094 :         bPartialTile &&
     653        4416 :         (m_nCompression == COMPRESSION_JPEG || bIsLercFloatingPoint);
     654             : 
     655             :     // If we need to fill out the tile, or if we want to prevent
     656             :     // TIFFWriteEncodedTile from altering the buffer as part of
     657             :     // byte swapping the data on write then we will need a temporary
     658             :     // working buffer.  If not, we can just do a direct write.
     659       48678 :     const GPtrDiff_t cc = static_cast<GPtrDiff_t>(TIFFTileSize(m_hTIFF));
     660             : 
     661       62945 :     if (bPreserveDataBuffer &&
     662       14275 :         (TIFFIsByteSwapped(m_hTIFF) || bNeedTempBuffer || m_panMaskOffsetLsb))
     663             :     {
     664         158 :         if (m_pabyTempWriteBuffer == nullptr)
     665             :         {
     666          35 :             m_pabyTempWriteBuffer = CPLMalloc(cc);
     667             :         }
     668         158 :         memcpy(m_pabyTempWriteBuffer, pabyData, cc);
     669             : 
     670         158 :         pabyData = static_cast<GByte *>(m_pabyTempWriteBuffer);
     671             :     }
     672             : 
     673             :     // Perform tile fill if needed.
     674             :     // TODO: we should also handle the case of nBitsPerSample == 12
     675             :     // but this is more involved.
     676       48670 :     if (bPartialTile && m_nCompression == COMPRESSION_JPEG &&
     677         134 :         m_nBitsPerSample == 8)
     678             :     {
     679         132 :         const int nComponents =
     680         132 :             m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     681             : 
     682         132 :         CPLDebug("GTiff", "Filling out jpeg edge tile on write.");
     683             : 
     684         132 :         const int nRightPixelsToFill =
     685         132 :             iColumn == m_nBlocksPerRow - 1
     686         132 :                 ? m_nBlockXSize * (iColumn + 1) - nRasterXSize
     687             :                 : 0;
     688         132 :         const int nBottomPixelsToFill =
     689         132 :             iRow == m_nBlocksPerColumn - 1
     690         132 :                 ? m_nBlockYSize * (iRow + 1) - nRasterYSize
     691             :                 : 0;
     692             : 
     693             :         // Fill out to the right.
     694         132 :         const int iSrcX = m_nBlockXSize - nRightPixelsToFill - 1;
     695             : 
     696       12461 :         for (int iX = iSrcX + 1; iX < m_nBlockXSize; ++iX)
     697             :         {
     698     3955880 :             for (int iY = 0; iY < m_nBlockYSize; ++iY)
     699             :             {
     700     3943550 :                 memcpy(pabyData +
     701     3943550 :                            (static_cast<GPtrDiff_t>(m_nBlockXSize) * iY + iX) *
     702     3943550 :                                nComponents,
     703     3943550 :                        pabyData + (static_cast<GPtrDiff_t>(m_nBlockXSize) * iY +
     704     3943550 :                                    iSrcX) *
     705     3943550 :                                       nComponents,
     706             :                        nComponents);
     707             :             }
     708             :         }
     709             : 
     710             :         // Now fill out the bottom.
     711         132 :         const int iSrcY = m_nBlockYSize - nBottomPixelsToFill - 1;
     712       17682 :         for (int iY = iSrcY + 1; iY < m_nBlockYSize; ++iY)
     713             :         {
     714       17550 :             memcpy(pabyData + static_cast<GPtrDiff_t>(m_nBlockXSize) *
     715       17550 :                                   nComponents * iY,
     716       17550 :                    pabyData + static_cast<GPtrDiff_t>(m_nBlockXSize) *
     717       17550 :                                   nComponents * iSrcY,
     718       17550 :                    static_cast<GPtrDiff_t>(m_nBlockXSize) * nComponents);
     719             :         }
     720             :     }
     721             : 
     722       48670 :     if (bIsLercFloatingPoint &&
     723             :         (bPartialTile
     724             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     725             :          /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
     726             :          || (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1)
     727             : #endif
     728             :              ))
     729             :     {
     730           0 :         if (GetRasterBand(1)->GetRasterDataType() == GDT_Float32)
     731           0 :             WriteDealWithLercAndNan(reinterpret_cast<float *>(pabyData),
     732             :                                     nActualBlockWidth, nActualBlockHeight,
     733             :                                     m_nBlockYSize);
     734             :         else
     735           0 :             WriteDealWithLercAndNan(reinterpret_cast<double *>(pabyData),
     736             :                                     nActualBlockWidth, nActualBlockHeight,
     737             :                                     m_nBlockYSize);
     738             :     }
     739             : 
     740       48670 :     if (m_panMaskOffsetLsb)
     741             :     {
     742           0 :         const int iBand = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     743           0 :                               ? static_cast<int>(tile) / m_nBlocksPerBand
     744             :                               : -1;
     745           0 :         DiscardLsb(pabyData, cc, iBand);
     746             :     }
     747             : 
     748       48672 :     if (m_bStreamingOut)
     749             :     {
     750          17 :         if (tile != static_cast<uint32_t>(m_nLastWrittenBlockId + 1))
     751             :         {
     752           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     753             :                         "Attempt to write block %d whereas %d was expected",
     754           1 :                         tile, m_nLastWrittenBlockId + 1);
     755           1 :             return false;
     756             :         }
     757          16 :         if (static_cast<GPtrDiff_t>(VSIFWriteL(pabyData, 1, cc, m_fpToWrite)) !=
     758             :             cc)
     759             :         {
     760           0 :             ReportError(CE_Failure, CPLE_FileIO,
     761             :                         "Could not write " CPL_FRMT_GUIB " bytes",
     762             :                         static_cast<GUIntBig>(cc));
     763           0 :             return false;
     764             :         }
     765          16 :         m_nLastWrittenBlockId = tile;
     766          16 :         return true;
     767             :     }
     768             : 
     769             :     /* -------------------------------------------------------------------- */
     770             :     /*      Should we do compression in a worker thread ?                   */
     771             :     /* -------------------------------------------------------------------- */
     772       48655 :     if (SubmitCompressionJob(tile, pabyData, cc, m_nBlockYSize))
     773       19789 :         return true;
     774             : 
     775       28852 :     return TIFFWriteEncodedTile(m_hTIFF, tile, pabyData, cc) == cc;
     776             : }
     777             : 
     778             : /************************************************************************/
     779             : /*                        WriteEncodedStrip()                           */
     780             : /************************************************************************/
     781             : 
     782      165600 : bool GTiffDataset::WriteEncodedStrip(uint32_t strip, GByte *pabyData,
     783             :                                      int bPreserveDataBuffer)
     784             : {
     785      165600 :     GPtrDiff_t cc = static_cast<GPtrDiff_t>(TIFFStripSize(m_hTIFF));
     786      165591 :     const auto ccFull = cc;
     787             : 
     788             :     /* -------------------------------------------------------------------- */
     789             :     /*      If this is the last strip in the image, and is partial, then    */
     790             :     /*      we need to trim the number of scanlines written to the          */
     791             :     /*      amount of valid data we have. (#2748)                           */
     792             :     /* -------------------------------------------------------------------- */
     793      165591 :     const int nStripWithinBand = strip % m_nBlocksPerBand;
     794      165591 :     int nStripHeight = m_nRowsPerStrip;
     795             : 
     796      165591 :     if (nStripWithinBand * nStripHeight > GetRasterYSize() - nStripHeight)
     797             :     {
     798         379 :         nStripHeight = GetRasterYSize() - nStripWithinBand * m_nRowsPerStrip;
     799         379 :         cc = (cc / m_nRowsPerStrip) * nStripHeight;
     800         758 :         CPLDebug("GTiff",
     801             :                  "Adjusted bytes to write from " CPL_FRMT_GUIB
     802             :                  " to " CPL_FRMT_GUIB ".",
     803         379 :                  static_cast<GUIntBig>(TIFFStripSize(m_hTIFF)),
     804             :                  static_cast<GUIntBig>(cc));
     805             :     }
     806             : 
     807             :     /* -------------------------------------------------------------------- */
     808             :     /*      Don't write empty blocks in some cases.                         */
     809             :     /* -------------------------------------------------------------------- */
     810      165595 :     if (!m_bWriteEmptyTiles && IsFirstPixelEqualToNoData(pabyData))
     811             :     {
     812       40502 :         if (!IsBlockAvailable(strip, nullptr, nullptr, nullptr))
     813             :         {
     814       40332 :             const int nComponents =
     815       40332 :                 m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     816             : 
     817       40332 :             if (HasOnlyNoData(pabyData, m_nBlockXSize, nStripHeight,
     818             :                               m_nBlockXSize, nComponents))
     819             :             {
     820       28297 :                 return true;
     821             :             }
     822             :         }
     823             :     }
     824             : 
     825             :     /* -------------------------------------------------------------------- */
     826             :     /*      TIFFWriteEncodedStrip can alter the passed buffer if            */
     827             :     /*      byte-swapping is necessary so we use a temporary buffer         */
     828             :     /*      before calling it.                                              */
     829             :     /* -------------------------------------------------------------------- */
     830      226535 :     if (bPreserveDataBuffer &&
     831       89233 :         (TIFFIsByteSwapped(m_hTIFF) || m_panMaskOffsetLsb))
     832             :     {
     833         294 :         if (m_pabyTempWriteBuffer == nullptr)
     834             :         {
     835         126 :             m_pabyTempWriteBuffer = CPLMalloc(ccFull);
     836             :         }
     837         294 :         memcpy(m_pabyTempWriteBuffer, pabyData, cc);
     838         294 :         pabyData = static_cast<GByte *>(m_pabyTempWriteBuffer);
     839             :     }
     840             : 
     841             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     842             :     const bool bIsLercFloatingPoint =
     843             :         m_nCompression == COMPRESSION_LERC &&
     844             :         (GetRasterBand(1)->GetRasterDataType() == GDT_Float32 ||
     845             :          GetRasterBand(1)->GetRasterDataType() == GDT_Float64);
     846             :     if (bIsLercFloatingPoint &&
     847             :         /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
     848             :         m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1)
     849             :     {
     850             :         if (GetRasterBand(1)->GetRasterDataType() == GDT_Float32)
     851             :             WriteDealWithLercAndNan(reinterpret_cast<float *>(pabyData),
     852             :                                     m_nBlockXSize, nStripHeight, nStripHeight);
     853             :         else
     854             :             WriteDealWithLercAndNan(reinterpret_cast<double *>(pabyData),
     855             :                                     m_nBlockXSize, nStripHeight, nStripHeight);
     856             :     }
     857             : #endif
     858             : 
     859      137302 :     if (m_panMaskOffsetLsb)
     860             :     {
     861         366 :         int iBand = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     862         183 :                         ? static_cast<int>(strip) / m_nBlocksPerBand
     863             :                         : -1;
     864         183 :         DiscardLsb(pabyData, cc, iBand);
     865             :     }
     866             : 
     867      137307 :     if (m_bStreamingOut)
     868             :     {
     869        1408 :         if (strip != static_cast<uint32_t>(m_nLastWrittenBlockId + 1))
     870             :         {
     871           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     872             :                         "Attempt to write block %d whereas %d was expected",
     873           1 :                         strip, m_nLastWrittenBlockId + 1);
     874           1 :             return false;
     875             :         }
     876        1407 :         if (static_cast<GPtrDiff_t>(VSIFWriteL(pabyData, 1, cc, m_fpToWrite)) !=
     877             :             cc)
     878             :         {
     879           0 :             ReportError(CE_Failure, CPLE_FileIO,
     880             :                         "Could not write " CPL_FRMT_GUIB " bytes",
     881             :                         static_cast<GUIntBig>(cc));
     882           0 :             return false;
     883             :         }
     884        1407 :         m_nLastWrittenBlockId = strip;
     885        1407 :         return true;
     886             :     }
     887             : 
     888             :     /* -------------------------------------------------------------------- */
     889             :     /*      Should we do compression in a worker thread ?                   */
     890             :     /* -------------------------------------------------------------------- */
     891      135899 :     if (SubmitCompressionJob(strip, pabyData, cc, nStripHeight))
     892        6727 :         return true;
     893             : 
     894      129165 :     return TIFFWriteEncodedStrip(m_hTIFF, strip, pabyData, cc) == cc;
     895             : }
     896             : 
     897             : /************************************************************************/
     898             : /*                        InitCompressionThreads()                      */
     899             : /************************************************************************/
     900             : 
     901       30849 : void GTiffDataset::InitCompressionThreads(bool bUpdateMode,
     902             :                                           CSLConstList papszOptions)
     903             : {
     904             :     // Raster == tile, then no need for threads
     905       30849 :     if (m_nBlockXSize == nRasterXSize && m_nBlockYSize == nRasterYSize)
     906       22587 :         return;
     907             : 
     908        8262 :     const char *pszValue = CSLFetchNameValue(papszOptions, "NUM_THREADS");
     909        8256 :     if (pszValue == nullptr)
     910        8191 :         pszValue = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
     911        8259 :     if (pszValue)
     912             :     {
     913             :         int nThreads =
     914         132 :             EQUAL(pszValue, "ALL_CPUS") ? CPLGetNumCPUs() : atoi(pszValue);
     915         132 :         if (nThreads > 1024)
     916           0 :             nThreads = 1024;  // to please Coverity
     917         132 :         if (nThreads > 1)
     918             :         {
     919          92 :             if ((bUpdateMode && m_nCompression != COMPRESSION_NONE) ||
     920          17 :                 (nBands >= 1 && IsMultiThreadedReadCompatible()))
     921             :             {
     922          70 :                 CPLDebug("GTiff",
     923             :                          "Using up to %d threads for compression/decompression",
     924             :                          nThreads);
     925             : 
     926          70 :                 m_poThreadPool = GDALGetGlobalThreadPool(nThreads);
     927          70 :                 if (bUpdateMode && m_poThreadPool)
     928          58 :                     m_poCompressQueue = m_poThreadPool->CreateJobQueue();
     929             : 
     930          70 :                 if (m_poCompressQueue != nullptr)
     931             :                 {
     932             :                     // Add a margin of an extra job w.r.t thread number
     933             :                     // so as to optimize compression time (enables the main
     934             :                     // thread to do boring I/O while all CPUs are working).
     935          58 :                     m_asCompressionJobs.resize(nThreads + 1);
     936          58 :                     memset(&m_asCompressionJobs[0], 0,
     937          58 :                            m_asCompressionJobs.size() *
     938             :                                sizeof(GTiffCompressionJob));
     939          58 :                     for (int i = 0;
     940         280 :                          i < static_cast<int>(m_asCompressionJobs.size()); ++i)
     941             :                     {
     942         444 :                         m_asCompressionJobs[i].pszTmpFilename =
     943         222 :                             CPLStrdup(VSIMemGenerateHiddenFilename(
     944             :                                 CPLSPrintf("thread_job_%d.tif", i)));
     945         222 :                         m_asCompressionJobs[i].nStripOrTile = -1;
     946             :                     }
     947             : 
     948             :                     // This is kind of a hack, but basically using
     949             :                     // TIFFWriteRawStrip/Tile and then TIFFReadEncodedStrip/Tile
     950             :                     // does not work on a newly created file, because
     951             :                     // TIFF_MYBUFFER is not set in tif_flags
     952             :                     // (if using TIFFWriteEncodedStrip/Tile first,
     953             :                     // TIFFWriteBufferSetup() is automatically called).
     954             :                     // This should likely rather fixed in libtiff itself.
     955          58 :                     CPL_IGNORE_RET_VAL(
     956          58 :                         TIFFWriteBufferSetup(m_hTIFF, nullptr, -1));
     957             :                 }
     958             :             }
     959             :         }
     960          57 :         else if (nThreads < 0 ||
     961          57 :                  (!EQUAL(pszValue, "0") && !EQUAL(pszValue, "1") &&
     962           3 :                   !EQUAL(pszValue, "ALL_CPUS")))
     963             :         {
     964           3 :             ReportError(CE_Warning, CPLE_AppDefined,
     965             :                         "Invalid value for NUM_THREADS: %s", pszValue);
     966             :         }
     967             :     }
     968             : }
     969             : 
     970             : /************************************************************************/
     971             : /*                      ThreadCompressionFunc()                         */
     972             : /************************************************************************/
     973             : 
     974       26528 : void GTiffDataset::ThreadCompressionFunc(void *pData)
     975             : {
     976       26528 :     GTiffCompressionJob *psJob = static_cast<GTiffCompressionJob *>(pData);
     977       26528 :     GTiffDataset *poDS = psJob->poDS;
     978             : 
     979       26528 :     VSILFILE *fpTmp = VSIFOpenL(psJob->pszTmpFilename, "wb+");
     980       26528 :     TIFF *hTIFFTmp = VSI_TIFFOpen(
     981       53056 :         psJob->pszTmpFilename, psJob->bTIFFIsBigEndian ? "wb+" : "wl+", fpTmp);
     982       26528 :     CPLAssert(hTIFFTmp != nullptr);
     983       26528 :     TIFFSetField(hTIFFTmp, TIFFTAG_IMAGEWIDTH, poDS->m_nBlockXSize);
     984       26528 :     TIFFSetField(hTIFFTmp, TIFFTAG_IMAGELENGTH, psJob->nHeight);
     985       26528 :     TIFFSetField(hTIFFTmp, TIFFTAG_BITSPERSAMPLE, poDS->m_nBitsPerSample);
     986       26528 :     TIFFSetField(hTIFFTmp, TIFFTAG_COMPRESSION, poDS->m_nCompression);
     987       26528 :     TIFFSetField(hTIFFTmp, TIFFTAG_PHOTOMETRIC, poDS->m_nPhotometric);
     988       26528 :     TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLEFORMAT, poDS->m_nSampleFormat);
     989       26528 :     TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLESPERPIXEL, poDS->m_nSamplesPerPixel);
     990       26528 :     TIFFSetField(hTIFFTmp, TIFFTAG_ROWSPERSTRIP, poDS->m_nBlockYSize);
     991       26528 :     TIFFSetField(hTIFFTmp, TIFFTAG_PLANARCONFIG, poDS->m_nPlanarConfig);
     992       26528 :     if (psJob->nPredictor != PREDICTOR_NONE)
     993         262 :         TIFFSetField(hTIFFTmp, TIFFTAG_PREDICTOR, psJob->nPredictor);
     994       26528 :     if (poDS->m_nCompression == COMPRESSION_LERC)
     995             :     {
     996          24 :         TIFFSetField(hTIFFTmp, TIFFTAG_LERC_PARAMETERS, 2,
     997          24 :                      poDS->m_anLercAddCompressionAndVersion);
     998             :     }
     999       26528 :     if (psJob->nExtraSampleCount)
    1000             :     {
    1001         336 :         TIFFSetField(hTIFFTmp, TIFFTAG_EXTRASAMPLES, psJob->nExtraSampleCount,
    1002             :                      psJob->pExtraSamples);
    1003             :     }
    1004             : 
    1005       26528 :     poDS->RestoreVolatileParameters(hTIFFTmp);
    1006             : 
    1007       53052 :     bool bOK = TIFFWriteEncodedStrip(hTIFFTmp, 0, psJob->pabyBuffer,
    1008       26526 :                                      psJob->nBufferSize) == psJob->nBufferSize;
    1009             : 
    1010       26526 :     toff_t nOffset = 0;
    1011       26526 :     if (bOK)
    1012             :     {
    1013       26526 :         toff_t *panOffsets = nullptr;
    1014       26526 :         toff_t *panByteCounts = nullptr;
    1015       26526 :         TIFFGetField(hTIFFTmp, TIFFTAG_STRIPOFFSETS, &panOffsets);
    1016       26527 :         TIFFGetField(hTIFFTmp, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
    1017             : 
    1018       26525 :         nOffset = panOffsets[0];
    1019       26525 :         psJob->nCompressedBufferSize =
    1020       26525 :             static_cast<GPtrDiff_t>(panByteCounts[0]);
    1021             :     }
    1022             :     else
    1023             :     {
    1024           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1025             :                  "Error when compressing strip/tile %d", psJob->nStripOrTile);
    1026             :     }
    1027             : 
    1028       26525 :     XTIFFClose(hTIFFTmp);
    1029       26525 :     if (VSIFCloseL(fpTmp) != 0)
    1030             :     {
    1031           0 :         if (bOK)
    1032             :         {
    1033           0 :             bOK = false;
    1034           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1035             :                      "Error when compressing strip/tile %d",
    1036             :                      psJob->nStripOrTile);
    1037             :         }
    1038             :     }
    1039             : 
    1040       26524 :     if (bOK)
    1041             :     {
    1042       26524 :         vsi_l_offset nFileSize = 0;
    1043             :         GByte *pabyCompressedBuffer =
    1044       26524 :             VSIGetMemFileBuffer(psJob->pszTmpFilename, &nFileSize, FALSE);
    1045       26528 :         CPLAssert(static_cast<vsi_l_offset>(
    1046             :                       nOffset + psJob->nCompressedBufferSize) <= nFileSize);
    1047       26528 :         psJob->pabyCompressedBuffer = pabyCompressedBuffer + nOffset;
    1048             :     }
    1049             :     else
    1050             :     {
    1051           0 :         psJob->pabyCompressedBuffer = nullptr;
    1052           0 :         psJob->nCompressedBufferSize = 0;
    1053             :     }
    1054             : 
    1055       26528 :     auto poMainDS = poDS->m_poBaseDS ? poDS->m_poBaseDS : poDS;
    1056       26528 :     if (poMainDS->m_poCompressQueue)
    1057             :     {
    1058        1576 :         std::lock_guard oLock(poMainDS->m_oCompressThreadPoolMutex);
    1059        1576 :         psJob->bReady = true;
    1060             :     }
    1061       26528 : }
    1062             : 
    1063             : /************************************************************************/
    1064             : /*                        WriteRawStripOrTile()                         */
    1065             : /************************************************************************/
    1066             : 
    1067       33831 : void GTiffDataset::WriteRawStripOrTile(int nStripOrTile,
    1068             :                                        GByte *pabyCompressedBuffer,
    1069             :                                        GPtrDiff_t nCompressedBufferSize)
    1070             : {
    1071             : #ifdef DEBUG_VERBOSE
    1072             :     CPLDebug("GTIFF", "Writing raw strip/tile %d, size " CPL_FRMT_GUIB,
    1073             :              nStripOrTile, static_cast<GUIntBig>(nCompressedBufferSize));
    1074             : #endif
    1075       33831 :     toff_t *panOffsets = nullptr;
    1076       33831 :     toff_t *panByteCounts = nullptr;
    1077       33831 :     bool bWriteAtEnd = true;
    1078       33831 :     bool bWriteLeader = m_bLeaderSizeAsUInt4;
    1079       33831 :     bool bWriteTrailer = m_bTrailerRepeatedLast4BytesRepeated;
    1080       33831 :     if (TIFFGetField(m_hTIFF,
    1081       33831 :                      TIFFIsTiled(m_hTIFF) ? TIFFTAG_TILEOFFSETS
    1082             :                                           : TIFFTAG_STRIPOFFSETS,
    1083       33831 :                      &panOffsets) &&
    1084       33831 :         panOffsets != nullptr && panOffsets[nStripOrTile] != 0)
    1085             :     {
    1086             :         // Forces TIFFAppendStrip() to consider if the location of the
    1087             :         // tile/strip can be reused or if the strile should be written at end of
    1088             :         // file.
    1089         360 :         TIFFSetWriteOffset(m_hTIFF, 0);
    1090             : 
    1091         360 :         if (m_bBlockOrderRowMajor)
    1092             :         {
    1093         264 :             if (TIFFGetField(m_hTIFF,
    1094         264 :                              TIFFIsTiled(m_hTIFF) ? TIFFTAG_TILEBYTECOUNTS
    1095             :                                                   : TIFFTAG_STRIPBYTECOUNTS,
    1096         528 :                              &panByteCounts) &&
    1097         264 :                 panByteCounts != nullptr)
    1098             :             {
    1099         264 :                 if (static_cast<GUIntBig>(nCompressedBufferSize) >
    1100         264 :                     panByteCounts[nStripOrTile])
    1101             :                 {
    1102           8 :                     GTiffDataset *poRootDS = m_poBaseDS ? m_poBaseDS : this;
    1103           8 :                     if (!poRootDS->m_bKnownIncompatibleEdition &&
    1104           8 :                         !poRootDS->m_bWriteKnownIncompatibleEdition)
    1105             :                     {
    1106           8 :                         ReportError(
    1107             :                             CE_Warning, CPLE_AppDefined,
    1108             :                             "A strile cannot be rewritten in place, which "
    1109             :                             "invalidates the BLOCK_ORDER optimization.");
    1110           8 :                         poRootDS->m_bKnownIncompatibleEdition = true;
    1111           8 :                         poRootDS->m_bWriteKnownIncompatibleEdition = true;
    1112             :                     }
    1113             :                 }
    1114             :                 // For mask interleaving, if the size is not exactly the same,
    1115             :                 // completely give up (we could potentially move the mask in
    1116             :                 // case the imagery is smaller)
    1117         256 :                 else if (m_poMaskDS && m_bMaskInterleavedWithImagery &&
    1118           0 :                          static_cast<GUIntBig>(nCompressedBufferSize) !=
    1119           0 :                              panByteCounts[nStripOrTile])
    1120             :                 {
    1121           0 :                     GTiffDataset *poRootDS = m_poBaseDS ? m_poBaseDS : this;
    1122           0 :                     if (!poRootDS->m_bKnownIncompatibleEdition &&
    1123           0 :                         !poRootDS->m_bWriteKnownIncompatibleEdition)
    1124             :                     {
    1125           0 :                         ReportError(
    1126             :                             CE_Warning, CPLE_AppDefined,
    1127             :                             "A strile cannot be rewritten in place, which "
    1128             :                             "invalidates the MASK_INTERLEAVED_WITH_IMAGERY "
    1129             :                             "optimization.");
    1130           0 :                         poRootDS->m_bKnownIncompatibleEdition = true;
    1131           0 :                         poRootDS->m_bWriteKnownIncompatibleEdition = true;
    1132             :                     }
    1133           0 :                     bWriteLeader = false;
    1134           0 :                     bWriteTrailer = false;
    1135           0 :                     if (m_bLeaderSizeAsUInt4)
    1136             :                     {
    1137             :                         // If there was a valid leader, invalidat it
    1138           0 :                         VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4,
    1139             :                                      SEEK_SET);
    1140             :                         uint32_t nOldSize;
    1141           0 :                         VSIFReadL(&nOldSize, 1, 4,
    1142             :                                   VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF)));
    1143           0 :                         CPL_LSBPTR32(&nOldSize);
    1144           0 :                         if (nOldSize == panByteCounts[nStripOrTile])
    1145             :                         {
    1146           0 :                             uint32_t nInvalidatedSize = 0;
    1147           0 :                             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4,
    1148             :                                          SEEK_SET);
    1149           0 :                             VSI_TIFFWrite(m_hTIFF, &nInvalidatedSize,
    1150             :                                           sizeof(nInvalidatedSize));
    1151             :                         }
    1152           0 :                     }
    1153             :                 }
    1154             :                 else
    1155             :                 {
    1156         256 :                     bWriteAtEnd = false;
    1157             :                 }
    1158             :             }
    1159             :         }
    1160             :     }
    1161       33831 :     if (bWriteLeader &&
    1162       24957 :         static_cast<GUIntBig>(nCompressedBufferSize) <= 0xFFFFFFFFU)
    1163             :     {
    1164             :         // cppcheck-suppress knownConditionTrueFalse
    1165       24957 :         if (bWriteAtEnd)
    1166             :         {
    1167       24701 :             VSI_TIFFSeek(m_hTIFF, 0, SEEK_END);
    1168             :         }
    1169             :         else
    1170             :         {
    1171             :             // If we rewrite an existing strile in place with an existing
    1172             :             // leader, check that the leader is valid, before rewriting it. And
    1173             :             // if it is not valid, then do not write the trailer, as we could
    1174             :             // corrupt other data.
    1175         256 :             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4, SEEK_SET);
    1176             :             uint32_t nOldSize;
    1177         256 :             VSIFReadL(&nOldSize, 1, 4,
    1178             :                       VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF)));
    1179         256 :             CPL_LSBPTR32(&nOldSize);
    1180         256 :             bWriteLeader =
    1181         256 :                 panByteCounts && nOldSize == panByteCounts[nStripOrTile];
    1182         256 :             bWriteTrailer = bWriteLeader;
    1183         256 :             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4, SEEK_SET);
    1184             :         }
    1185             :         // cppcheck-suppress knownConditionTrueFalse
    1186       24957 :         if (bWriteLeader)
    1187             :         {
    1188       24957 :             uint32_t nSize = static_cast<uint32_t>(nCompressedBufferSize);
    1189       24957 :             CPL_LSBPTR32(&nSize);
    1190       24957 :             if (!VSI_TIFFWrite(m_hTIFF, &nSize, sizeof(nSize)))
    1191           0 :                 m_bWriteError = true;
    1192             :         }
    1193             :     }
    1194             :     tmsize_t written;
    1195       33831 :     if (TIFFIsTiled(m_hTIFF))
    1196       26178 :         written = TIFFWriteRawTile(m_hTIFF, nStripOrTile, pabyCompressedBuffer,
    1197             :                                    nCompressedBufferSize);
    1198             :     else
    1199        7653 :         written = TIFFWriteRawStrip(m_hTIFF, nStripOrTile, pabyCompressedBuffer,
    1200             :                                     nCompressedBufferSize);
    1201       33831 :     if (written != nCompressedBufferSize)
    1202          12 :         m_bWriteError = true;
    1203       33831 :     if (bWriteTrailer &&
    1204       24957 :         static_cast<GUIntBig>(nCompressedBufferSize) <= 0xFFFFFFFFU)
    1205             :     {
    1206       24957 :         GByte abyLastBytes[4] = {};
    1207       24957 :         if (nCompressedBufferSize >= 4)
    1208       24957 :             memcpy(abyLastBytes,
    1209       24957 :                    pabyCompressedBuffer + nCompressedBufferSize - 4, 4);
    1210             :         else
    1211           0 :             memcpy(abyLastBytes, pabyCompressedBuffer, nCompressedBufferSize);
    1212       24957 :         if (!VSI_TIFFWrite(m_hTIFF, abyLastBytes, 4))
    1213           0 :             m_bWriteError = true;
    1214             :     }
    1215       33831 : }
    1216             : 
    1217             : /************************************************************************/
    1218             : /*                        WaitCompletionForJobIdx()                     */
    1219             : /************************************************************************/
    1220             : 
    1221        1576 : void GTiffDataset::WaitCompletionForJobIdx(int i)
    1222             : {
    1223        1576 :     auto poMainDS = m_poBaseDS ? m_poBaseDS : this;
    1224        1576 :     auto poQueue = poMainDS->m_poCompressQueue.get();
    1225        1576 :     auto &oQueue = poMainDS->m_asQueueJobIdx;
    1226        1576 :     auto &asJobs = poMainDS->m_asCompressionJobs;
    1227        1576 :     auto &mutex = poMainDS->m_oCompressThreadPoolMutex;
    1228             : 
    1229        1576 :     CPLAssert(i >= 0 && static_cast<size_t>(i) < asJobs.size());
    1230        1576 :     CPLAssert(asJobs[i].nStripOrTile >= 0);
    1231        1576 :     CPLAssert(!oQueue.empty());
    1232             : 
    1233        1576 :     bool bHasWarned = false;
    1234             :     while (true)
    1235             :     {
    1236             :         bool bReady;
    1237             :         {
    1238        2324 :             std::lock_guard oLock(mutex);
    1239        2324 :             bReady = asJobs[i].bReady;
    1240             :         }
    1241        2324 :         if (!bReady)
    1242             :         {
    1243         748 :             if (!bHasWarned)
    1244             :             {
    1245         462 :                 CPLDebug("GTIFF",
    1246             :                          "Waiting for worker job to finish handling block %d",
    1247         462 :                          asJobs[i].nStripOrTile);
    1248         462 :                 bHasWarned = true;
    1249             :             }
    1250         748 :             poQueue->GetPool()->WaitEvent();
    1251             :         }
    1252             :         else
    1253             :         {
    1254        1576 :             break;
    1255             :         }
    1256         748 :     }
    1257             : 
    1258        1576 :     if (asJobs[i].nCompressedBufferSize)
    1259             :     {
    1260        3152 :         asJobs[i].poDS->WriteRawStripOrTile(asJobs[i].nStripOrTile,
    1261        1576 :                                             asJobs[i].pabyCompressedBuffer,
    1262        1576 :                                             asJobs[i].nCompressedBufferSize);
    1263             :     }
    1264        1576 :     asJobs[i].pabyCompressedBuffer = nullptr;
    1265        1576 :     asJobs[i].nBufferSize = 0;
    1266             :     {
    1267             :         // Likely useless, but makes Coverity happy
    1268        1576 :         std::lock_guard oLock(mutex);
    1269        1576 :         asJobs[i].bReady = false;
    1270             :     }
    1271        1576 :     asJobs[i].nStripOrTile = -1;
    1272        1576 :     oQueue.pop();
    1273        1576 : }
    1274             : 
    1275             : /************************************************************************/
    1276             : /*                        WaitCompletionForBlock()                      */
    1277             : /************************************************************************/
    1278             : 
    1279     2300680 : void GTiffDataset::WaitCompletionForBlock(int nBlockId)
    1280             : {
    1281     2300680 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    1282     2281530 :                               : m_poCompressQueue.get();
    1283             :     // cppcheck-suppress constVariableReference
    1284     2300660 :     auto &oQueue = m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    1285             :     // cppcheck-suppress constVariableReference
    1286     2281520 :     auto &asJobs =
    1287     2300660 :         m_poBaseDS ? m_poBaseDS->m_asCompressionJobs : m_asCompressionJobs;
    1288             : 
    1289     2300660 :     if (poQueue != nullptr && !oQueue.empty())
    1290             :     {
    1291        1066 :         for (int i = 0; i < static_cast<int>(asJobs.size()); ++i)
    1292             :         {
    1293         888 :             if (asJobs[i].poDS == this && asJobs[i].nStripOrTile == nBlockId)
    1294             :             {
    1295         128 :                 while (!oQueue.empty() &&
    1296          64 :                        !(asJobs[oQueue.front()].poDS == this &&
    1297          64 :                          asJobs[oQueue.front()].nStripOrTile == nBlockId))
    1298             :                 {
    1299           0 :                     WaitCompletionForJobIdx(oQueue.front());
    1300             :                 }
    1301          64 :                 CPLAssert(!oQueue.empty() &&
    1302             :                           asJobs[oQueue.front()].poDS == this &&
    1303             :                           asJobs[oQueue.front()].nStripOrTile == nBlockId);
    1304          64 :                 WaitCompletionForJobIdx(oQueue.front());
    1305             :             }
    1306             :         }
    1307             :     }
    1308     2300660 : }
    1309             : 
    1310             : /************************************************************************/
    1311             : /*                      SubmitCompressionJob()                          */
    1312             : /************************************************************************/
    1313             : 
    1314      184558 : bool GTiffDataset::SubmitCompressionJob(int nStripOrTile, GByte *pabyData,
    1315             :                                         GPtrDiff_t cc, int nHeight)
    1316             : {
    1317             :     /* -------------------------------------------------------------------- */
    1318             :     /*      Should we do compression in a worker thread ?                   */
    1319             :     /* -------------------------------------------------------------------- */
    1320      184558 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    1321      170586 :                               : m_poCompressQueue.get();
    1322             : 
    1323      184529 :     if (poQueue && m_nCompression == COMPRESSION_NONE)
    1324             :     {
    1325             :         // We don't do multi-threaded compression for uncompressed...
    1326             :         // but we must wait for other related compression tasks (e.g mask)
    1327             :         // to be completed
    1328           0 :         poQueue->WaitCompletion();
    1329             : 
    1330             :         // Flush remaining data
    1331             :         // cppcheck-suppress constVariableReference
    1332           0 :         auto &oQueue =
    1333           0 :             m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    1334           0 :         while (!oQueue.empty())
    1335             :         {
    1336           0 :             WaitCompletionForJobIdx(oQueue.front());
    1337             :         }
    1338             :     }
    1339             : 
    1340             :     const auto SetupJob =
    1341      122805 :         [this, pabyData, cc, nHeight, nStripOrTile](GTiffCompressionJob &sJob)
    1342             :     {
    1343       26528 :         sJob.poDS = this;
    1344       26528 :         sJob.bTIFFIsBigEndian = CPL_TO_BOOL(TIFFIsBigEndian(m_hTIFF));
    1345             :         GByte *pabyBuffer =
    1346       26528 :             static_cast<GByte *>(VSI_REALLOC_VERBOSE(sJob.pabyBuffer, cc));
    1347       26528 :         if (!pabyBuffer)
    1348           0 :             return false;
    1349       26528 :         sJob.pabyBuffer = pabyBuffer;
    1350       26528 :         memcpy(sJob.pabyBuffer, pabyData, cc);
    1351       26528 :         sJob.nBufferSize = cc;
    1352       26528 :         sJob.nHeight = nHeight;
    1353       26528 :         sJob.nStripOrTile = nStripOrTile;
    1354       26528 :         sJob.nPredictor = PREDICTOR_NONE;
    1355       26528 :         if (GTIFFSupportsPredictor(m_nCompression))
    1356             :         {
    1357       16693 :             TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &sJob.nPredictor);
    1358             :         }
    1359             : 
    1360       26528 :         sJob.pExtraSamples = nullptr;
    1361       26528 :         sJob.nExtraSampleCount = 0;
    1362       26528 :         TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &sJob.nExtraSampleCount,
    1363             :                      &sJob.pExtraSamples);
    1364       26528 :         return true;
    1365      184529 :     };
    1366             : 
    1367      184529 :     if (poQueue == nullptr || !(m_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    1368         806 :                                 m_nCompression == COMPRESSION_LZW ||
    1369          78 :                                 m_nCompression == COMPRESSION_PACKBITS ||
    1370          72 :                                 m_nCompression == COMPRESSION_LZMA ||
    1371          62 :                                 m_nCompression == COMPRESSION_ZSTD ||
    1372          52 :                                 m_nCompression == COMPRESSION_LERC ||
    1373          46 :                                 m_nCompression == COMPRESSION_JXL ||
    1374          46 :                                 m_nCompression == COMPRESSION_JXL_DNG_1_7 ||
    1375          28 :                                 m_nCompression == COMPRESSION_WEBP ||
    1376          18 :                                 m_nCompression == COMPRESSION_JPEG))
    1377             :     {
    1378      182953 :         if (m_bBlockOrderRowMajor || m_bLeaderSizeAsUInt4 ||
    1379      158020 :             m_bTrailerRepeatedLast4BytesRepeated)
    1380             :         {
    1381             :             GTiffCompressionJob sJob;
    1382       24936 :             memset(&sJob, 0, sizeof(sJob));
    1383       24936 :             if (SetupJob(sJob))
    1384             :             {
    1385       24952 :                 sJob.pszTmpFilename =
    1386       24952 :                     CPLStrdup(VSIMemGenerateHiddenFilename("temp.tif"));
    1387             : 
    1388       24952 :                 ThreadCompressionFunc(&sJob);
    1389             : 
    1390       24952 :                 if (sJob.nCompressedBufferSize)
    1391             :                 {
    1392       24952 :                     sJob.poDS->WriteRawStripOrTile(sJob.nStripOrTile,
    1393             :                                                    sJob.pabyCompressedBuffer,
    1394             :                                                    sJob.nCompressedBufferSize);
    1395             :                 }
    1396             : 
    1397       24952 :                 CPLFree(sJob.pabyBuffer);
    1398       24952 :                 VSIUnlink(sJob.pszTmpFilename);
    1399       24952 :                 CPLFree(sJob.pszTmpFilename);
    1400       24952 :                 return sJob.nCompressedBufferSize > 0 && !m_bWriteError;
    1401             :             }
    1402             :         }
    1403             : 
    1404      158017 :         return false;
    1405             :     }
    1406             : 
    1407        1576 :     auto poMainDS = m_poBaseDS ? m_poBaseDS : this;
    1408        1576 :     auto &oQueue = poMainDS->m_asQueueJobIdx;
    1409        1576 :     auto &asJobs = poMainDS->m_asCompressionJobs;
    1410             : 
    1411        1576 :     int nNextCompressionJobAvail = -1;
    1412             : 
    1413        1576 :     if (oQueue.size() == asJobs.size())
    1414             :     {
    1415        1443 :         CPLAssert(!oQueue.empty());
    1416        1443 :         nNextCompressionJobAvail = oQueue.front();
    1417        1443 :         WaitCompletionForJobIdx(nNextCompressionJobAvail);
    1418             :     }
    1419             :     else
    1420             :     {
    1421         133 :         const int nJobs = static_cast<int>(asJobs.size());
    1422         324 :         for (int i = 0; i < nJobs; ++i)
    1423             :         {
    1424         324 :             if (asJobs[i].nBufferSize == 0)
    1425             :             {
    1426         133 :                 nNextCompressionJobAvail = i;
    1427         133 :                 break;
    1428             :             }
    1429             :         }
    1430             :     }
    1431        1576 :     CPLAssert(nNextCompressionJobAvail >= 0);
    1432             : 
    1433        1576 :     GTiffCompressionJob *psJob = &asJobs[nNextCompressionJobAvail];
    1434        1576 :     bool bOK = SetupJob(*psJob);
    1435        1576 :     if (bOK)
    1436             :     {
    1437        1576 :         poQueue->SubmitJob(ThreadCompressionFunc, psJob);
    1438        1576 :         oQueue.push(nNextCompressionJobAvail);
    1439             :     }
    1440             : 
    1441        1576 :     return bOK;
    1442             : }
    1443             : 
    1444             : /************************************************************************/
    1445             : /*                          DiscardLsb()                                */
    1446             : /************************************************************************/
    1447             : 
    1448         272 : template <class T> bool MustNotDiscardLsb(T value, bool bHasNoData, T nodata)
    1449             : {
    1450         272 :     return bHasNoData && value == nodata;
    1451             : }
    1452             : 
    1453             : template <>
    1454          44 : bool MustNotDiscardLsb<float>(float value, bool bHasNoData, float nodata)
    1455             : {
    1456          44 :     return (bHasNoData && value == nodata) || !std::isfinite(value);
    1457             : }
    1458             : 
    1459             : template <>
    1460          44 : bool MustNotDiscardLsb<double>(double value, bool bHasNoData, double nodata)
    1461             : {
    1462          44 :     return (bHasNoData && value == nodata) || !std::isfinite(value);
    1463             : }
    1464             : 
    1465             : template <class T> T AdjustValue(T value, uint64_t nRoundUpBitTest);
    1466             : 
    1467          10 : template <class T> T AdjustValueInt(T value, uint64_t nRoundUpBitTest)
    1468             : {
    1469          10 :     if (value >=
    1470          10 :         static_cast<T>(std::numeric_limits<T>::max() - (nRoundUpBitTest << 1)))
    1471           0 :         return static_cast<T>(value - (nRoundUpBitTest << 1));
    1472          10 :     return static_cast<T>(value + (nRoundUpBitTest << 1));
    1473             : }
    1474             : 
    1475           0 : template <> int8_t AdjustValue<int8_t>(int8_t value, uint64_t nRoundUpBitTest)
    1476             : {
    1477           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1478             : }
    1479             : 
    1480             : template <>
    1481           2 : uint8_t AdjustValue<uint8_t>(uint8_t value, uint64_t nRoundUpBitTest)
    1482             : {
    1483           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1484             : }
    1485             : 
    1486             : template <>
    1487           2 : int16_t AdjustValue<int16_t>(int16_t value, uint64_t nRoundUpBitTest)
    1488             : {
    1489           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1490             : }
    1491             : 
    1492             : template <>
    1493           2 : uint16_t AdjustValue<uint16_t>(uint16_t value, uint64_t nRoundUpBitTest)
    1494             : {
    1495           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1496             : }
    1497             : 
    1498             : template <>
    1499           2 : int32_t AdjustValue<int32_t>(int32_t value, uint64_t nRoundUpBitTest)
    1500             : {
    1501           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1502             : }
    1503             : 
    1504             : template <>
    1505           2 : uint32_t AdjustValue<uint32_t>(uint32_t value, uint64_t nRoundUpBitTest)
    1506             : {
    1507           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1508             : }
    1509             : 
    1510             : template <>
    1511           0 : int64_t AdjustValue<int64_t>(int64_t value, uint64_t nRoundUpBitTest)
    1512             : {
    1513           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1514             : }
    1515             : 
    1516             : template <>
    1517           0 : uint64_t AdjustValue<uint64_t>(uint64_t value, uint64_t nRoundUpBitTest)
    1518             : {
    1519           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1520             : }
    1521             : 
    1522           0 : template <> GFloat16 AdjustValue<GFloat16>(GFloat16 value, uint64_t)
    1523             : {
    1524             :     using std::nextafter;
    1525           0 :     return nextafter(value, cpl::NumericLimits<GFloat16>::max());
    1526             : }
    1527             : 
    1528           0 : template <> float AdjustValue<float>(float value, uint64_t)
    1529             : {
    1530           0 :     return std::nextafter(value, std::numeric_limits<float>::max());
    1531             : }
    1532             : 
    1533           0 : template <> double AdjustValue<double>(double value, uint64_t)
    1534             : {
    1535           0 :     return std::nextafter(value, std::numeric_limits<double>::max());
    1536             : }
    1537             : 
    1538             : template <class Teffective, class T>
    1539             : T RoundValueDiscardLsb(const void *ptr, uint64_t nMask,
    1540             :                        uint64_t nRoundUpBitTest);
    1541             : 
    1542             : template <class T>
    1543          16 : T RoundValueDiscardLsbUnsigned(const void *ptr, uint64_t nMask,
    1544             :                                uint64_t nRoundUpBitTest)
    1545             : {
    1546          32 :     if ((*reinterpret_cast<const T *>(ptr) & nMask) >
    1547          16 :         static_cast<uint64_t>(std::numeric_limits<T>::max()) -
    1548          16 :             (nRoundUpBitTest << 1U))
    1549             :     {
    1550           4 :         return static_cast<T>(std::numeric_limits<T>::max() & nMask);
    1551             :     }
    1552          12 :     const uint64_t newval =
    1553          12 :         (*reinterpret_cast<const T *>(ptr) & nMask) + (nRoundUpBitTest << 1U);
    1554          12 :     return static_cast<T>(newval);
    1555             : }
    1556             : 
    1557             : template <class T>
    1558          18 : T RoundValueDiscardLsbSigned(const void *ptr, uint64_t nMask,
    1559             :                              uint64_t nRoundUpBitTest)
    1560             : {
    1561          18 :     T oldval = *reinterpret_cast<const T *>(ptr);
    1562          18 :     if (oldval < 0)
    1563             :     {
    1564           4 :         return static_cast<T>(oldval & nMask);
    1565             :     }
    1566          14 :     const uint64_t newval =
    1567          14 :         (*reinterpret_cast<const T *>(ptr) & nMask) + (nRoundUpBitTest << 1U);
    1568          14 :     if (newval > static_cast<uint64_t>(std::numeric_limits<T>::max()))
    1569           4 :         return static_cast<T>(std::numeric_limits<T>::max() & nMask);
    1570          10 :     return static_cast<T>(newval);
    1571             : }
    1572             : 
    1573             : template <>
    1574          11 : uint16_t RoundValueDiscardLsb<uint16_t, uint16_t>(const void *ptr,
    1575             :                                                   uint64_t nMask,
    1576             :                                                   uint64_t nRoundUpBitTest)
    1577             : {
    1578          11 :     return RoundValueDiscardLsbUnsigned<uint16_t>(ptr, nMask, nRoundUpBitTest);
    1579             : }
    1580             : 
    1581             : template <>
    1582           5 : uint32_t RoundValueDiscardLsb<uint32_t, uint32_t>(const void *ptr,
    1583             :                                                   uint64_t nMask,
    1584             :                                                   uint64_t nRoundUpBitTest)
    1585             : {
    1586           5 :     return RoundValueDiscardLsbUnsigned<uint32_t>(ptr, nMask, nRoundUpBitTest);
    1587             : }
    1588             : 
    1589             : template <>
    1590           0 : uint64_t RoundValueDiscardLsb<uint64_t, uint64_t>(const void *ptr,
    1591             :                                                   uint64_t nMask,
    1592             :                                                   uint64_t nRoundUpBitTest)
    1593             : {
    1594           0 :     return RoundValueDiscardLsbUnsigned<uint64_t>(ptr, nMask, nRoundUpBitTest);
    1595             : }
    1596             : 
    1597             : template <>
    1598           0 : int8_t RoundValueDiscardLsb<int8_t, int8_t>(const void *ptr, uint64_t nMask,
    1599             :                                             uint64_t nRoundUpBitTest)
    1600             : {
    1601           0 :     return RoundValueDiscardLsbSigned<int8_t>(ptr, nMask, nRoundUpBitTest);
    1602             : }
    1603             : 
    1604             : template <>
    1605          13 : int16_t RoundValueDiscardLsb<int16_t, int16_t>(const void *ptr, uint64_t nMask,
    1606             :                                                uint64_t nRoundUpBitTest)
    1607             : {
    1608          13 :     return RoundValueDiscardLsbSigned<int16_t>(ptr, nMask, nRoundUpBitTest);
    1609             : }
    1610             : 
    1611             : template <>
    1612           5 : int32_t RoundValueDiscardLsb<int32_t, int32_t>(const void *ptr, uint64_t nMask,
    1613             :                                                uint64_t nRoundUpBitTest)
    1614             : {
    1615           5 :     return RoundValueDiscardLsbSigned<int32_t>(ptr, nMask, nRoundUpBitTest);
    1616             : }
    1617             : 
    1618             : template <>
    1619           0 : int64_t RoundValueDiscardLsb<int64_t, int64_t>(const void *ptr, uint64_t nMask,
    1620             :                                                uint64_t nRoundUpBitTest)
    1621             : {
    1622           0 :     return RoundValueDiscardLsbSigned<int64_t>(ptr, nMask, nRoundUpBitTest);
    1623             : }
    1624             : 
    1625             : template <>
    1626           0 : uint16_t RoundValueDiscardLsb<GFloat16, uint16_t>(const void *ptr,
    1627             :                                                   uint64_t nMask,
    1628             :                                                   uint64_t nRoundUpBitTest)
    1629             : {
    1630           0 :     return RoundValueDiscardLsbUnsigned<uint16_t>(ptr, nMask, nRoundUpBitTest);
    1631             : }
    1632             : 
    1633             : template <>
    1634           0 : uint32_t RoundValueDiscardLsb<float, uint32_t>(const void *ptr, uint64_t nMask,
    1635             :                                                uint64_t nRoundUpBitTest)
    1636             : {
    1637           0 :     return RoundValueDiscardLsbUnsigned<uint32_t>(ptr, nMask, nRoundUpBitTest);
    1638             : }
    1639             : 
    1640             : template <>
    1641           0 : uint64_t RoundValueDiscardLsb<double, uint64_t>(const void *ptr, uint64_t nMask,
    1642             :                                                 uint64_t nRoundUpBitTest)
    1643             : {
    1644           0 :     return RoundValueDiscardLsbUnsigned<uint64_t>(ptr, nMask, nRoundUpBitTest);
    1645             : }
    1646             : 
    1647             : template <class Teffective, class T>
    1648         145 : static void DiscardLsbT(GByte *pabyBuffer, size_t nBytes, int iBand, int nBands,
    1649             :                         uint16_t nPlanarConfig,
    1650             :                         const GTiffDataset::MaskOffset *panMaskOffsetLsb,
    1651             :                         bool bHasNoData, Teffective nNoDataValue)
    1652             : {
    1653             :     static_assert(sizeof(Teffective) == sizeof(T),
    1654             :                   "sizeof(Teffective) == sizeof(T)");
    1655         145 :     if (nPlanarConfig == PLANARCONFIG_SEPARATE)
    1656             :     {
    1657          98 :         const auto nMask = panMaskOffsetLsb[iBand].nMask;
    1658          98 :         const auto nRoundUpBitTest = panMaskOffsetLsb[iBand].nRoundUpBitTest;
    1659         196 :         for (size_t i = 0; i < nBytes / sizeof(T); ++i)
    1660             :         {
    1661          98 :             if (MustNotDiscardLsb(reinterpret_cast<Teffective *>(pabyBuffer)[i],
    1662             :                                   bHasNoData, nNoDataValue))
    1663             :             {
    1664          22 :                 continue;
    1665             :             }
    1666             : 
    1667          76 :             if (reinterpret_cast<T *>(pabyBuffer)[i] & nRoundUpBitTest)
    1668             :             {
    1669          30 :                 reinterpret_cast<T *>(pabyBuffer)[i] =
    1670          15 :                     RoundValueDiscardLsb<Teffective, T>(
    1671          15 :                         &(reinterpret_cast<T *>(pabyBuffer)[i]), nMask,
    1672             :                         nRoundUpBitTest);
    1673             :             }
    1674             :             else
    1675             :             {
    1676          61 :                 reinterpret_cast<T *>(pabyBuffer)[i] = static_cast<T>(
    1677          61 :                     reinterpret_cast<T *>(pabyBuffer)[i] & nMask);
    1678             :             }
    1679             : 
    1680             :             // Make sure that by discarding LSB we don't end up to a value
    1681             :             // that is no the nodata value
    1682          76 :             if (MustNotDiscardLsb(reinterpret_cast<Teffective *>(pabyBuffer)[i],
    1683             :                                   bHasNoData, nNoDataValue))
    1684             :             {
    1685           8 :                 reinterpret_cast<Teffective *>(pabyBuffer)[i] =
    1686           4 :                     AdjustValue(nNoDataValue, nRoundUpBitTest);
    1687             :             }
    1688             :         }
    1689             :     }
    1690             :     else
    1691             :     {
    1692          94 :         for (size_t i = 0; i < nBytes / sizeof(T); i += nBands)
    1693             :         {
    1694         147 :             for (int j = 0; j < nBands; ++j)
    1695             :             {
    1696         100 :                 if (MustNotDiscardLsb(
    1697         100 :                         reinterpret_cast<Teffective *>(pabyBuffer)[i + j],
    1698             :                         bHasNoData, nNoDataValue))
    1699             :                 {
    1700          14 :                     continue;
    1701             :                 }
    1702             : 
    1703          86 :                 if (reinterpret_cast<T *>(pabyBuffer)[i + j] &
    1704          86 :                     panMaskOffsetLsb[j].nRoundUpBitTest)
    1705             :                 {
    1706          38 :                     reinterpret_cast<T *>(pabyBuffer)[i + j] =
    1707          19 :                         RoundValueDiscardLsb<Teffective, T>(
    1708          19 :                             &(reinterpret_cast<T *>(pabyBuffer)[i + j]),
    1709          19 :                             panMaskOffsetLsb[j].nMask,
    1710          19 :                             panMaskOffsetLsb[j].nRoundUpBitTest);
    1711             :                 }
    1712             :                 else
    1713             :                 {
    1714          67 :                     reinterpret_cast<T *>(pabyBuffer)[i + j] = static_cast<T>(
    1715          67 :                         (reinterpret_cast<T *>(pabyBuffer)[i + j] &
    1716          67 :                          panMaskOffsetLsb[j].nMask));
    1717             :                 }
    1718             : 
    1719             :                 // Make sure that by discarding LSB we don't end up to a value
    1720             :                 // that is no the nodata value
    1721          86 :                 if (MustNotDiscardLsb(
    1722          86 :                         reinterpret_cast<Teffective *>(pabyBuffer)[i + j],
    1723             :                         bHasNoData, nNoDataValue))
    1724             :                 {
    1725           8 :                     reinterpret_cast<Teffective *>(pabyBuffer)[i + j] =
    1726           4 :                         AdjustValue(nNoDataValue,
    1727           4 :                                     panMaskOffsetLsb[j].nRoundUpBitTest);
    1728             :                 }
    1729             :             }
    1730             :         }
    1731             :     }
    1732         145 : }
    1733             : 
    1734         183 : static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand,
    1735             :                        int nBands, uint16_t nSampleFormat,
    1736             :                        uint16_t nBitsPerSample, uint16_t nPlanarConfig,
    1737             :                        const GTiffDataset::MaskOffset *panMaskOffsetLsb,
    1738             :                        bool bHasNoData, double dfNoDataValue)
    1739             : {
    1740         183 :     if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_UINT)
    1741             :     {
    1742          38 :         uint8_t nNoDataValue = 0;
    1743          38 :         if (bHasNoData && GDALIsValueExactAs<uint8_t>(dfNoDataValue))
    1744             :         {
    1745           6 :             nNoDataValue = static_cast<uint8_t>(dfNoDataValue);
    1746             :         }
    1747             :         else
    1748             :         {
    1749          32 :             bHasNoData = false;
    1750             :         }
    1751          38 :         if (nPlanarConfig == PLANARCONFIG_SEPARATE)
    1752             :         {
    1753          25 :             const auto nMask =
    1754          25 :                 static_cast<unsigned>(panMaskOffsetLsb[iBand].nMask);
    1755          25 :             const auto nRoundUpBitTest =
    1756          25 :                 static_cast<unsigned>(panMaskOffsetLsb[iBand].nRoundUpBitTest);
    1757          50 :             for (decltype(nBytes) i = 0; i < nBytes; ++i)
    1758             :             {
    1759          25 :                 if (bHasNoData && pabyBuffer[i] == nNoDataValue)
    1760           3 :                     continue;
    1761             : 
    1762             :                 // Keep 255 in case it is alpha.
    1763          22 :                 if (pabyBuffer[i] != 255)
    1764             :                 {
    1765          21 :                     if (pabyBuffer[i] & nRoundUpBitTest)
    1766           5 :                         pabyBuffer[i] = static_cast<GByte>(
    1767           5 :                             std::min(255U, (pabyBuffer[i] & nMask) +
    1768           5 :                                                (nRoundUpBitTest << 1U)));
    1769             :                     else
    1770          16 :                         pabyBuffer[i] =
    1771          16 :                             static_cast<GByte>(pabyBuffer[i] & nMask);
    1772             : 
    1773             :                     // Make sure that by discarding LSB we don't end up to a
    1774             :                     // value that is no the nodata value
    1775          21 :                     if (bHasNoData && pabyBuffer[i] == nNoDataValue)
    1776           2 :                         pabyBuffer[i] =
    1777           1 :                             AdjustValue(nNoDataValue, nRoundUpBitTest);
    1778             :                 }
    1779             :             }
    1780             :         }
    1781             :         else
    1782             :         {
    1783          26 :             for (decltype(nBytes) i = 0; i < nBytes; i += nBands)
    1784             :             {
    1785          42 :                 for (int j = 0; j < nBands; ++j)
    1786             :                 {
    1787          29 :                     if (bHasNoData && pabyBuffer[i + j] == nNoDataValue)
    1788           2 :                         continue;
    1789             : 
    1790             :                     // Keep 255 in case it is alpha.
    1791          27 :                     if (pabyBuffer[i + j] != 255)
    1792             :                     {
    1793          25 :                         if (pabyBuffer[i + j] &
    1794          25 :                             panMaskOffsetLsb[j].nRoundUpBitTest)
    1795             :                         {
    1796           6 :                             pabyBuffer[i + j] = static_cast<GByte>(std::min(
    1797          12 :                                 255U,
    1798           6 :                                 (pabyBuffer[i + j] &
    1799             :                                  static_cast<unsigned>(
    1800           6 :                                      panMaskOffsetLsb[j].nMask)) +
    1801             :                                     (static_cast<unsigned>(
    1802           6 :                                          panMaskOffsetLsb[j].nRoundUpBitTest)
    1803           6 :                                      << 1U)));
    1804             :                         }
    1805             :                         else
    1806             :                         {
    1807          19 :                             pabyBuffer[i + j] = static_cast<GByte>(
    1808          19 :                                 pabyBuffer[i + j] & panMaskOffsetLsb[j].nMask);
    1809             :                         }
    1810             : 
    1811             :                         // Make sure that by discarding LSB we don't end up to a
    1812             :                         // value that is no the nodata value
    1813          25 :                         if (bHasNoData && pabyBuffer[i + j] == nNoDataValue)
    1814           1 :                             pabyBuffer[i + j] = AdjustValue(
    1815             :                                 nNoDataValue,
    1816           1 :                                 panMaskOffsetLsb[j].nRoundUpBitTest);
    1817             :                     }
    1818             :                 }
    1819             :             }
    1820          38 :         }
    1821             :     }
    1822         145 :     else if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_INT)
    1823             :     {
    1824           0 :         int8_t nNoDataValue = 0;
    1825           0 :         if (bHasNoData && GDALIsValueExactAs<int8_t>(dfNoDataValue))
    1826             :         {
    1827           0 :             nNoDataValue = static_cast<int8_t>(dfNoDataValue);
    1828             :         }
    1829             :         else
    1830             :         {
    1831           0 :             bHasNoData = false;
    1832             :         }
    1833           0 :         DiscardLsbT<int8_t, int8_t>(pabyBuffer, nBytes, iBand, nBands,
    1834             :                                     nPlanarConfig, panMaskOffsetLsb, bHasNoData,
    1835           0 :                                     nNoDataValue);
    1836             :     }
    1837         145 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_INT)
    1838             :     {
    1839          48 :         int16_t nNoDataValue = 0;
    1840          48 :         if (bHasNoData && GDALIsValueExactAs<int16_t>(dfNoDataValue))
    1841             :         {
    1842           6 :             nNoDataValue = static_cast<int16_t>(dfNoDataValue);
    1843             :         }
    1844             :         else
    1845             :         {
    1846          42 :             bHasNoData = false;
    1847             :         }
    1848          48 :         DiscardLsbT<int16_t, int16_t>(pabyBuffer, nBytes, iBand, nBands,
    1849             :                                       nPlanarConfig, panMaskOffsetLsb,
    1850          48 :                                       bHasNoData, nNoDataValue);
    1851             :     }
    1852          97 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_UINT)
    1853             :     {
    1854          33 :         uint16_t nNoDataValue = 0;
    1855          33 :         if (bHasNoData && GDALIsValueExactAs<uint16_t>(dfNoDataValue))
    1856             :         {
    1857           6 :             nNoDataValue = static_cast<uint16_t>(dfNoDataValue);
    1858             :         }
    1859             :         else
    1860             :         {
    1861          27 :             bHasNoData = false;
    1862             :         }
    1863          33 :         DiscardLsbT<uint16_t, uint16_t>(pabyBuffer, nBytes, iBand, nBands,
    1864             :                                         nPlanarConfig, panMaskOffsetLsb,
    1865          33 :                                         bHasNoData, nNoDataValue);
    1866             :     }
    1867          64 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_INT)
    1868             :     {
    1869          13 :         int32_t nNoDataValue = 0;
    1870          13 :         if (bHasNoData && GDALIsValueExactAs<int32_t>(dfNoDataValue))
    1871             :         {
    1872           6 :             nNoDataValue = static_cast<int32_t>(dfNoDataValue);
    1873             :         }
    1874             :         else
    1875             :         {
    1876           7 :             bHasNoData = false;
    1877             :         }
    1878          13 :         DiscardLsbT<int32_t, int32_t>(pabyBuffer, nBytes, iBand, nBands,
    1879             :                                       nPlanarConfig, panMaskOffsetLsb,
    1880          13 :                                       bHasNoData, nNoDataValue);
    1881             :     }
    1882          51 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_UINT)
    1883             :     {
    1884          13 :         uint32_t nNoDataValue = 0;
    1885          13 :         if (bHasNoData && GDALIsValueExactAs<uint32_t>(dfNoDataValue))
    1886             :         {
    1887           6 :             nNoDataValue = static_cast<uint32_t>(dfNoDataValue);
    1888             :         }
    1889             :         else
    1890             :         {
    1891           7 :             bHasNoData = false;
    1892             :         }
    1893          13 :         DiscardLsbT<uint32_t, uint32_t>(pabyBuffer, nBytes, iBand, nBands,
    1894             :                                         nPlanarConfig, panMaskOffsetLsb,
    1895          13 :                                         bHasNoData, nNoDataValue);
    1896             :     }
    1897          38 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_INT)
    1898             :     {
    1899             :         // FIXME: we should not rely on dfNoDataValue when we support native
    1900             :         // data type for nodata
    1901           0 :         int64_t nNoDataValue = 0;
    1902           0 :         if (bHasNoData && GDALIsValueExactAs<int64_t>(dfNoDataValue))
    1903             :         {
    1904           0 :             nNoDataValue = static_cast<int64_t>(dfNoDataValue);
    1905             :         }
    1906             :         else
    1907             :         {
    1908           0 :             bHasNoData = false;
    1909             :         }
    1910           0 :         DiscardLsbT<int64_t, int64_t>(pabyBuffer, nBytes, iBand, nBands,
    1911             :                                       nPlanarConfig, panMaskOffsetLsb,
    1912           0 :                                       bHasNoData, nNoDataValue);
    1913             :     }
    1914          38 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_UINT)
    1915             :     {
    1916             :         // FIXME: we should not rely on dfNoDataValue when we support native
    1917             :         // data type for nodata
    1918           0 :         uint64_t nNoDataValue = 0;
    1919           0 :         if (bHasNoData && GDALIsValueExactAs<uint64_t>(dfNoDataValue))
    1920             :         {
    1921           0 :             nNoDataValue = static_cast<uint64_t>(dfNoDataValue);
    1922             :         }
    1923             :         else
    1924             :         {
    1925           0 :             bHasNoData = false;
    1926             :         }
    1927           0 :         DiscardLsbT<uint64_t, uint64_t>(pabyBuffer, nBytes, iBand, nBands,
    1928             :                                         nPlanarConfig, panMaskOffsetLsb,
    1929           0 :                                         bHasNoData, nNoDataValue);
    1930             :     }
    1931          38 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1932             :     {
    1933           0 :         const GFloat16 fNoDataValue = static_cast<GFloat16>(dfNoDataValue);
    1934           0 :         DiscardLsbT<GFloat16, uint16_t>(pabyBuffer, nBytes, iBand, nBands,
    1935             :                                         nPlanarConfig, panMaskOffsetLsb,
    1936           0 :                                         bHasNoData, fNoDataValue);
    1937             :     }
    1938          38 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1939             :     {
    1940          19 :         const float fNoDataValue = static_cast<float>(dfNoDataValue);
    1941          19 :         DiscardLsbT<float, uint32_t>(pabyBuffer, nBytes, iBand, nBands,
    1942             :                                      nPlanarConfig, panMaskOffsetLsb,
    1943          19 :                                      bHasNoData, fNoDataValue);
    1944             :     }
    1945          19 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1946             :     {
    1947          19 :         DiscardLsbT<double, uint64_t>(pabyBuffer, nBytes, iBand, nBands,
    1948             :                                       nPlanarConfig, panMaskOffsetLsb,
    1949             :                                       bHasNoData, dfNoDataValue);
    1950             :     }
    1951         183 : }
    1952             : 
    1953         183 : void GTiffDataset::DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes,
    1954             :                               int iBand) const
    1955             : {
    1956         183 :     ::DiscardLsb(pabyBuffer, nBytes, iBand, nBands, m_nSampleFormat,
    1957         183 :                  m_nBitsPerSample, m_nPlanarConfig, m_panMaskOffsetLsb,
    1958         183 :                  m_bNoDataSet, m_dfNoDataValue);
    1959         183 : }
    1960             : 
    1961             : /************************************************************************/
    1962             : /*                  WriteEncodedTileOrStrip()                           */
    1963             : /************************************************************************/
    1964             : 
    1965      215465 : CPLErr GTiffDataset::WriteEncodedTileOrStrip(uint32_t tile_or_strip, void *data,
    1966             :                                              int bPreserveDataBuffer)
    1967             : {
    1968      215465 :     CPLErr eErr = CE_None;
    1969             : 
    1970      215465 :     if (TIFFIsTiled(m_hTIFF))
    1971             :     {
    1972       49866 :         if (!(WriteEncodedTile(tile_or_strip, static_cast<GByte *>(data),
    1973             :                                bPreserveDataBuffer)))
    1974             :         {
    1975          14 :             eErr = CE_Failure;
    1976             :         }
    1977             :     }
    1978             :     else
    1979             :     {
    1980      165594 :         if (!(WriteEncodedStrip(tile_or_strip, static_cast<GByte *>(data),
    1981             :                                 bPreserveDataBuffer)))
    1982             :         {
    1983           8 :             eErr = CE_Failure;
    1984             :         }
    1985             :     }
    1986             : 
    1987      215458 :     return eErr;
    1988             : }
    1989             : 
    1990             : /************************************************************************/
    1991             : /*                           FlushBlockBuf()                            */
    1992             : /************************************************************************/
    1993             : 
    1994        9602 : CPLErr GTiffDataset::FlushBlockBuf()
    1995             : 
    1996             : {
    1997        9602 :     if (m_nLoadedBlock < 0 || !m_bLoadedBlockDirty)
    1998           0 :         return CE_None;
    1999             : 
    2000        9602 :     m_bLoadedBlockDirty = false;
    2001             : 
    2002             :     const CPLErr eErr =
    2003        9602 :         WriteEncodedTileOrStrip(m_nLoadedBlock, m_pabyBlockBuf, true);
    2004        9602 :     if (eErr != CE_None)
    2005             :     {
    2006           0 :         ReportError(CE_Failure, CPLE_AppDefined,
    2007             :                     "WriteEncodedTile/Strip() failed.");
    2008           0 :         m_bWriteError = true;
    2009             :     }
    2010             : 
    2011        9602 :     return eErr;
    2012             : }
    2013             : 
    2014             : /************************************************************************/
    2015             : /*                   GTiffFillStreamableOffsetAndCount()                */
    2016             : /************************************************************************/
    2017             : 
    2018           8 : static void GTiffFillStreamableOffsetAndCount(TIFF *hTIFF, int nSize)
    2019             : {
    2020           8 :     uint32_t nXSize = 0;
    2021           8 :     uint32_t nYSize = 0;
    2022           8 :     TIFFGetField(hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize);
    2023           8 :     TIFFGetField(hTIFF, TIFFTAG_IMAGELENGTH, &nYSize);
    2024           8 :     const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(hTIFF));
    2025             :     const int nBlockCount =
    2026           8 :         bIsTiled ? TIFFNumberOfTiles(hTIFF) : TIFFNumberOfStrips(hTIFF);
    2027             : 
    2028           8 :     toff_t *panOffset = nullptr;
    2029           8 :     TIFFGetField(hTIFF, bIsTiled ? TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS,
    2030             :                  &panOffset);
    2031           8 :     toff_t *panSize = nullptr;
    2032           8 :     TIFFGetField(hTIFF,
    2033             :                  bIsTiled ? TIFFTAG_TILEBYTECOUNTS : TIFFTAG_STRIPBYTECOUNTS,
    2034             :                  &panSize);
    2035           8 :     toff_t nOffset = nSize;
    2036             :     // Trick to avoid clang static analyzer raising false positive about
    2037             :     // divide by zero later.
    2038           8 :     int nBlocksPerBand = 1;
    2039           8 :     uint32_t nRowsPerStrip = 0;
    2040           8 :     if (!bIsTiled)
    2041             :     {
    2042           6 :         TIFFGetField(hTIFF, TIFFTAG_ROWSPERSTRIP, &nRowsPerStrip);
    2043           6 :         if (nRowsPerStrip > static_cast<uint32_t>(nYSize))
    2044           0 :             nRowsPerStrip = nYSize;
    2045           6 :         nBlocksPerBand = DIV_ROUND_UP(nYSize, nRowsPerStrip);
    2046             :     }
    2047        2947 :     for (int i = 0; i < nBlockCount; ++i)
    2048             :     {
    2049             :         GPtrDiff_t cc = bIsTiled
    2050        2939 :                             ? static_cast<GPtrDiff_t>(TIFFTileSize(hTIFF))
    2051        2907 :                             : static_cast<GPtrDiff_t>(TIFFStripSize(hTIFF));
    2052        2939 :         if (!bIsTiled)
    2053             :         {
    2054             :             /* --------------------------------------------------------------------
    2055             :              */
    2056             :             /*      If this is the last strip in the image, and is partial, then
    2057             :              */
    2058             :             /*      we need to trim the number of scanlines written to the */
    2059             :             /*      amount of valid data we have. (#2748) */
    2060             :             /* --------------------------------------------------------------------
    2061             :              */
    2062        2907 :             int nStripWithinBand = i % nBlocksPerBand;
    2063        2907 :             if (nStripWithinBand * nRowsPerStrip > nYSize - nRowsPerStrip)
    2064             :             {
    2065           1 :                 cc = (cc / nRowsPerStrip) *
    2066           1 :                      (nYSize - nStripWithinBand * nRowsPerStrip);
    2067             :             }
    2068             :         }
    2069        2939 :         panOffset[i] = nOffset;
    2070        2939 :         panSize[i] = cc;
    2071        2939 :         nOffset += cc;
    2072             :     }
    2073           8 : }
    2074             : 
    2075             : /************************************************************************/
    2076             : /*                             Crystalize()                             */
    2077             : /*                                                                      */
    2078             : /*      Make sure that the directory information is written out for     */
    2079             : /*      a new file, require before writing any imagery data.            */
    2080             : /************************************************************************/
    2081             : 
    2082     2640120 : void GTiffDataset::Crystalize()
    2083             : 
    2084             : {
    2085     2640120 :     if (m_bCrystalized)
    2086     2634650 :         return;
    2087             : 
    2088             :     // TODO: libtiff writes extended tags in the order they are specified
    2089             :     // and not in increasing order.
    2090        5468 :     WriteMetadata(this, m_hTIFF, true, m_eProfile, m_pszFilename,
    2091        5468 :                   m_papszCreationOptions);
    2092        5476 :     WriteGeoTIFFInfo();
    2093        5476 :     if (m_bNoDataSet)
    2094         298 :         WriteNoDataValue(m_hTIFF, m_dfNoDataValue);
    2095        5178 :     else if (m_bNoDataSetAsInt64)
    2096           2 :         WriteNoDataValue(m_hTIFF, m_nNoDataValueInt64);
    2097        5176 :     else if (m_bNoDataSetAsUInt64)
    2098           2 :         WriteNoDataValue(m_hTIFF, m_nNoDataValueUInt64);
    2099             : 
    2100        5476 :     m_bMetadataChanged = false;
    2101        5476 :     m_bGeoTIFFInfoChanged = false;
    2102        5476 :     m_bNoDataChanged = false;
    2103        5476 :     m_bNeedsRewrite = false;
    2104             : 
    2105        5476 :     m_bCrystalized = true;
    2106             : 
    2107        5476 :     TIFFWriteCheck(m_hTIFF, TIFFIsTiled(m_hTIFF), "GTiffDataset::Crystalize");
    2108             : 
    2109        5476 :     TIFFWriteDirectory(m_hTIFF);
    2110        5476 :     if (m_bStreamingOut)
    2111             :     {
    2112             :         // We need to write twice the directory to be sure that custom
    2113             :         // TIFF tags are correctly sorted and that padding bytes have been
    2114             :         // added.
    2115           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2116           3 :         TIFFWriteDirectory(m_hTIFF);
    2117             : 
    2118           3 :         if (VSIFSeekL(m_fpL, 0, SEEK_END) != 0)
    2119             :         {
    2120           0 :             ReportError(CE_Failure, CPLE_FileIO, "Could not seek");
    2121             :         }
    2122           3 :         const int nSize = static_cast<int>(VSIFTellL(m_fpL));
    2123             : 
    2124           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2125           3 :         GTiffFillStreamableOffsetAndCount(m_hTIFF, nSize);
    2126           3 :         TIFFWriteDirectory(m_hTIFF);
    2127             : 
    2128           3 :         vsi_l_offset nDataLength = 0;
    2129             :         void *pabyBuffer =
    2130           3 :             VSIGetMemFileBuffer(m_pszTmpFilename, &nDataLength, FALSE);
    2131           3 :         if (static_cast<int>(VSIFWriteL(
    2132           3 :                 pabyBuffer, 1, static_cast<int>(nDataLength), m_fpToWrite)) !=
    2133             :             static_cast<int>(nDataLength))
    2134             :         {
    2135           0 :             ReportError(CE_Failure, CPLE_FileIO, "Could not write %d bytes",
    2136             :                         static_cast<int>(nDataLength));
    2137             :         }
    2138             :         // In case of single strip file, there's a libtiff check that would
    2139             :         // issue a warning since the file hasn't the required size.
    2140           3 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2141           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2142           3 :         CPLPopErrorHandler();
    2143             :     }
    2144             :     else
    2145             :     {
    2146        5473 :         const tdir_t nNumberOfDirs = TIFFNumberOfDirectories(m_hTIFF);
    2147        5473 :         if (nNumberOfDirs > 0)
    2148             :         {
    2149        5473 :             TIFFSetDirectory(m_hTIFF, static_cast<tdir_t>(nNumberOfDirs - 1));
    2150             :         }
    2151             :     }
    2152             : 
    2153        5476 :     RestoreVolatileParameters(m_hTIFF);
    2154             : 
    2155        5476 :     m_nDirOffset = TIFFCurrentDirOffset(m_hTIFF);
    2156             : }
    2157             : 
    2158             : /************************************************************************/
    2159             : /*                             FlushCache()                             */
    2160             : /*                                                                      */
    2161             : /*      We override this so we can also flush out local tiff strip      */
    2162             : /*      cache if need be.                                               */
    2163             : /************************************************************************/
    2164             : 
    2165        4417 : CPLErr GTiffDataset::FlushCache(bool bAtClosing)
    2166             : 
    2167             : {
    2168        4417 :     return FlushCacheInternal(bAtClosing, true);
    2169             : }
    2170             : 
    2171       44990 : CPLErr GTiffDataset::FlushCacheInternal(bool bAtClosing, bool bFlushDirectory)
    2172             : {
    2173       44990 :     if (m_bIsFinalized)
    2174           2 :         return CE_None;
    2175             : 
    2176       44988 :     CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
    2177             : 
    2178       44987 :     if (m_bLoadedBlockDirty && m_nLoadedBlock != -1)
    2179             :     {
    2180         258 :         if (FlushBlockBuf() != CE_None)
    2181           0 :             eErr = CE_Failure;
    2182             :     }
    2183             : 
    2184       44987 :     CPLFree(m_pabyBlockBuf);
    2185       44988 :     m_pabyBlockBuf = nullptr;
    2186       44988 :     m_nLoadedBlock = -1;
    2187       44988 :     m_bLoadedBlockDirty = false;
    2188             : 
    2189             :     // Finish compression
    2190       44988 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    2191       42649 :                               : m_poCompressQueue.get();
    2192       44987 :     if (poQueue)
    2193             :     {
    2194         161 :         poQueue->WaitCompletion();
    2195             : 
    2196             :         // Flush remaining data
    2197             :         // cppcheck-suppress constVariableReference
    2198             : 
    2199         161 :         auto &oQueue =
    2200         161 :             m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    2201         230 :         while (!oQueue.empty())
    2202             :         {
    2203          69 :             WaitCompletionForJobIdx(oQueue.front());
    2204             :         }
    2205             :     }
    2206             : 
    2207       44987 :     if (bFlushDirectory && GetAccess() == GA_Update)
    2208             :     {
    2209       13401 :         if (FlushDirectory() != CE_None)
    2210          12 :             eErr = CE_Failure;
    2211             :     }
    2212       44987 :     return eErr;
    2213             : }
    2214             : 
    2215             : /************************************************************************/
    2216             : /*                           FlushDirectory()                           */
    2217             : /************************************************************************/
    2218             : 
    2219       20702 : CPLErr GTiffDataset::FlushDirectory()
    2220             : 
    2221             : {
    2222       20702 :     CPLErr eErr = CE_None;
    2223             : 
    2224         557 :     const auto ReloadAllOtherDirectories = [this]()
    2225             :     {
    2226         274 :         const auto poBaseDS = m_poBaseDS ? m_poBaseDS : this;
    2227         274 :         if (poBaseDS->m_papoOverviewDS)
    2228             :         {
    2229          12 :             for (int i = 0; i < poBaseDS->m_nOverviewCount; ++i)
    2230             :             {
    2231           3 :                 if (poBaseDS->m_papoOverviewDS[i]->m_bCrystalized &&
    2232           3 :                     poBaseDS->m_papoOverviewDS[i] != this)
    2233             :                 {
    2234           3 :                     poBaseDS->m_papoOverviewDS[i]->ReloadDirectory(true);
    2235             :                 }
    2236             : 
    2237           3 :                 if (poBaseDS->m_papoOverviewDS[i]->m_poMaskDS &&
    2238           0 :                     poBaseDS->m_papoOverviewDS[i]->m_poMaskDS != this &&
    2239           0 :                     poBaseDS->m_papoOverviewDS[i]->m_poMaskDS->m_bCrystalized)
    2240             :                 {
    2241           0 :                     poBaseDS->m_papoOverviewDS[i]->m_poMaskDS->ReloadDirectory(
    2242             :                         true);
    2243             :                 }
    2244             :             }
    2245             :         }
    2246         274 :         if (poBaseDS->m_poMaskDS && poBaseDS->m_poMaskDS != this &&
    2247           0 :             poBaseDS->m_poMaskDS->m_bCrystalized)
    2248             :         {
    2249           0 :             poBaseDS->m_poMaskDS->ReloadDirectory(true);
    2250             :         }
    2251         274 :         if (poBaseDS->m_bCrystalized && poBaseDS != this)
    2252             :         {
    2253           6 :             poBaseDS->ReloadDirectory(true);
    2254             :         }
    2255       20976 :     };
    2256             : 
    2257       20702 :     if (eAccess == GA_Update)
    2258             :     {
    2259       14987 :         if (m_bMetadataChanged)
    2260             :         {
    2261         140 :             m_bNeedsRewrite =
    2262         280 :                 WriteMetadata(this, m_hTIFF, true, m_eProfile, m_pszFilename,
    2263         140 :                               m_papszCreationOptions);
    2264         140 :             m_bMetadataChanged = false;
    2265             : 
    2266         140 :             if (m_bForceUnsetRPC)
    2267             :             {
    2268           5 :                 double *padfRPCTag = nullptr;
    2269             :                 uint16_t nCount;
    2270           5 :                 if (TIFFGetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT, &nCount,
    2271           5 :                                  &padfRPCTag))
    2272             :                 {
    2273           3 :                     std::vector<double> zeroes(92);
    2274           3 :                     TIFFSetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT, 92,
    2275             :                                  zeroes.data());
    2276           3 :                     TIFFUnsetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT);
    2277           3 :                     m_bNeedsRewrite = true;
    2278             :                 }
    2279             : 
    2280           5 :                 GDALWriteRPCTXTFile(m_pszFilename, nullptr);
    2281           5 :                 GDALWriteRPBFile(m_pszFilename, nullptr);
    2282             :             }
    2283             :         }
    2284             : 
    2285       14987 :         if (m_bGeoTIFFInfoChanged)
    2286             :         {
    2287         140 :             WriteGeoTIFFInfo();
    2288         140 :             m_bGeoTIFFInfoChanged = false;
    2289             :         }
    2290             : 
    2291       14987 :         if (m_bNoDataChanged)
    2292             :         {
    2293          46 :             if (m_bNoDataSet)
    2294             :             {
    2295          32 :                 WriteNoDataValue(m_hTIFF, m_dfNoDataValue);
    2296             :             }
    2297          14 :             else if (m_bNoDataSetAsInt64)
    2298             :             {
    2299           0 :                 WriteNoDataValue(m_hTIFF, m_nNoDataValueInt64);
    2300             :             }
    2301          14 :             else if (m_bNoDataSetAsUInt64)
    2302             :             {
    2303           0 :                 WriteNoDataValue(m_hTIFF, m_nNoDataValueUInt64);
    2304             :             }
    2305             :             else
    2306             :             {
    2307          14 :                 UnsetNoDataValue(m_hTIFF);
    2308             :             }
    2309          46 :             m_bNeedsRewrite = true;
    2310          46 :             m_bNoDataChanged = false;
    2311             :         }
    2312             : 
    2313       14987 :         if (m_bNeedsRewrite)
    2314             :         {
    2315         298 :             if (!m_bCrystalized)
    2316             :             {
    2317          27 :                 Crystalize();
    2318             :             }
    2319             :             else
    2320             :             {
    2321         271 :                 const TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(m_hTIFF);
    2322             : 
    2323         271 :                 m_nDirOffset = pfnSizeProc(TIFFClientdata(m_hTIFF));
    2324         271 :                 if ((m_nDirOffset % 2) == 1)
    2325          63 :                     ++m_nDirOffset;
    2326             : 
    2327         271 :                 if (TIFFRewriteDirectory(m_hTIFF) == 0)
    2328           0 :                     eErr = CE_Failure;
    2329             : 
    2330         271 :                 TIFFSetSubDirectory(m_hTIFF, m_nDirOffset);
    2331             : 
    2332         271 :                 ReloadAllOtherDirectories();
    2333             : 
    2334         271 :                 if (m_bLayoutIFDSBeforeData && m_bBlockOrderRowMajor &&
    2335           1 :                     m_bLeaderSizeAsUInt4 &&
    2336           1 :                     m_bTrailerRepeatedLast4BytesRepeated &&
    2337           1 :                     !m_bKnownIncompatibleEdition &&
    2338           1 :                     !m_bWriteKnownIncompatibleEdition)
    2339             :                 {
    2340           1 :                     ReportError(CE_Warning, CPLE_AppDefined,
    2341             :                                 "The IFD has been rewritten at the end of "
    2342             :                                 "the file, which breaks COG layout.");
    2343           1 :                     m_bKnownIncompatibleEdition = true;
    2344           1 :                     m_bWriteKnownIncompatibleEdition = true;
    2345             :                 }
    2346             :             }
    2347             : 
    2348         298 :             m_bNeedsRewrite = false;
    2349             :         }
    2350             :     }
    2351             : 
    2352             :     // There are some circumstances in which we can reach this point
    2353             :     // without having made this our directory (SetDirectory()) in which
    2354             :     // case we should not risk a flush.
    2355       35689 :     if (GetAccess() == GA_Update &&
    2356       14987 :         TIFFCurrentDirOffset(m_hTIFF) == m_nDirOffset)
    2357             :     {
    2358       14987 :         const TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(m_hTIFF);
    2359             : 
    2360       14987 :         toff_t nNewDirOffset = pfnSizeProc(TIFFClientdata(m_hTIFF));
    2361       14987 :         if ((nNewDirOffset % 2) == 1)
    2362        3294 :             ++nNewDirOffset;
    2363             : 
    2364       14987 :         if (TIFFFlush(m_hTIFF) == 0)
    2365          12 :             eErr = CE_Failure;
    2366             : 
    2367       14987 :         if (m_nDirOffset != TIFFCurrentDirOffset(m_hTIFF))
    2368             :         {
    2369           3 :             m_nDirOffset = nNewDirOffset;
    2370           3 :             ReloadAllOtherDirectories();
    2371           3 :             CPLDebug("GTiff",
    2372             :                      "directory moved during flush in FlushDirectory()");
    2373             :         }
    2374             :     }
    2375             : 
    2376       20702 :     SetDirectory();
    2377       20702 :     return eErr;
    2378             : }
    2379             : 
    2380             : /************************************************************************/
    2381             : /*                           CleanOverviews()                           */
    2382             : /************************************************************************/
    2383             : 
    2384           5 : CPLErr GTiffDataset::CleanOverviews()
    2385             : 
    2386             : {
    2387           5 :     CPLAssert(!m_poBaseDS);
    2388             : 
    2389           5 :     ScanDirectories();
    2390             : 
    2391           5 :     FlushDirectory();
    2392             : 
    2393             :     /* -------------------------------------------------------------------- */
    2394             :     /*      Cleanup overviews objects, and get offsets to all overview      */
    2395             :     /*      directories.                                                    */
    2396             :     /* -------------------------------------------------------------------- */
    2397          10 :     std::vector<toff_t> anOvDirOffsets;
    2398             : 
    2399          10 :     for (int i = 0; i < m_nOverviewCount; ++i)
    2400             :     {
    2401           5 :         anOvDirOffsets.push_back(m_papoOverviewDS[i]->m_nDirOffset);
    2402           5 :         if (m_papoOverviewDS[i]->m_poMaskDS)
    2403           1 :             anOvDirOffsets.push_back(
    2404           1 :                 m_papoOverviewDS[i]->m_poMaskDS->m_nDirOffset);
    2405           5 :         delete m_papoOverviewDS[i];
    2406             :     }
    2407             : 
    2408             :     /* -------------------------------------------------------------------- */
    2409             :     /*      Loop through all the directories, translating the offsets       */
    2410             :     /*      into indexes we can use with TIFFUnlinkDirectory().             */
    2411             :     /* -------------------------------------------------------------------- */
    2412          10 :     std::vector<uint16_t> anOvDirIndexes;
    2413           5 :     int iThisOffset = 1;
    2414             : 
    2415           5 :     TIFFSetDirectory(m_hTIFF, 0);
    2416             : 
    2417             :     while (true)
    2418             :     {
    2419          28 :         for (toff_t nOffset : anOvDirOffsets)
    2420             :         {
    2421          16 :             if (nOffset == TIFFCurrentDirOffset(m_hTIFF))
    2422             :             {
    2423           6 :                 anOvDirIndexes.push_back(static_cast<uint16_t>(iThisOffset));
    2424             :             }
    2425             :         }
    2426             : 
    2427          12 :         if (TIFFLastDirectory(m_hTIFF))
    2428           5 :             break;
    2429             : 
    2430           7 :         TIFFReadDirectory(m_hTIFF);
    2431           7 :         ++iThisOffset;
    2432           7 :     }
    2433             : 
    2434             :     /* -------------------------------------------------------------------- */
    2435             :     /*      Actually unlink the target directories.  Note that we do        */
    2436             :     /*      this from last to first so as to avoid renumbering any of       */
    2437             :     /*      the earlier directories we need to remove.                      */
    2438             :     /* -------------------------------------------------------------------- */
    2439          11 :     while (!anOvDirIndexes.empty())
    2440             :     {
    2441           6 :         TIFFUnlinkDirectory(m_hTIFF, anOvDirIndexes.back());
    2442           6 :         anOvDirIndexes.pop_back();
    2443             :     }
    2444             : 
    2445           5 :     CPLFree(m_papoOverviewDS);
    2446           5 :     m_nOverviewCount = 0;
    2447           5 :     m_papoOverviewDS = nullptr;
    2448             : 
    2449           5 :     if (m_poMaskDS)
    2450             :     {
    2451           1 :         CPLFree(m_poMaskDS->m_papoOverviewDS);
    2452           1 :         m_poMaskDS->m_nOverviewCount = 0;
    2453           1 :         m_poMaskDS->m_papoOverviewDS = nullptr;
    2454             :     }
    2455             : 
    2456           5 :     if (!SetDirectory())
    2457           0 :         return CE_Failure;
    2458             : 
    2459           5 :     return CE_None;
    2460             : }
    2461             : 
    2462             : /************************************************************************/
    2463             : /*                   RegisterNewOverviewDataset()                       */
    2464             : /************************************************************************/
    2465             : 
    2466         507 : CPLErr GTiffDataset::RegisterNewOverviewDataset(toff_t nOverviewOffset,
    2467             :                                                 int l_nJpegQuality,
    2468             :                                                 CSLConstList papszOptions)
    2469             : {
    2470         507 :     if (m_nOverviewCount == 127)
    2471           0 :         return CE_Failure;
    2472             : 
    2473             :     const auto GetOptionValue =
    2474        5577 :         [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
    2475       11153 :                        const char **ppszKeyUsed = nullptr)
    2476             :     {
    2477        5577 :         const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
    2478        5577 :         if (pszVal)
    2479             :         {
    2480           1 :             if (ppszKeyUsed)
    2481           1 :                 *ppszKeyUsed = pszOptionKey;
    2482           1 :             return pszVal;
    2483             :         }
    2484        5576 :         pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey);
    2485        5576 :         if (pszVal)
    2486             :         {
    2487           0 :             if (ppszKeyUsed)
    2488           0 :                 *ppszKeyUsed = pszConfigOptionKey;
    2489           0 :             return pszVal;
    2490             :         }
    2491        5576 :         if (pszConfigOptionKey)
    2492             :         {
    2493        5576 :             pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
    2494        5576 :             if (pszVal && ppszKeyUsed)
    2495          13 :                 *ppszKeyUsed = pszConfigOptionKey;
    2496             :         }
    2497        5576 :         return pszVal;
    2498         507 :     };
    2499             : 
    2500         507 :     int nZLevel = m_nZLevel;
    2501         507 :     if (const char *opt = GetOptionValue("ZLEVEL", "ZLEVEL_OVERVIEW"))
    2502             :     {
    2503           4 :         nZLevel = atoi(opt);
    2504             :     }
    2505             : 
    2506         507 :     int nZSTDLevel = m_nZSTDLevel;
    2507         507 :     if (const char *opt = GetOptionValue("ZSTD_LEVEL", "ZSTD_LEVEL_OVERVIEW"))
    2508             :     {
    2509           4 :         nZSTDLevel = atoi(opt);
    2510             :     }
    2511             : 
    2512         507 :     bool bWebpLossless = m_bWebPLossless;
    2513             :     const char *pszWebPLosslessOverview =
    2514         507 :         GetOptionValue("WEBP_LOSSLESS", "WEBP_LOSSLESS_OVERVIEW");
    2515         507 :     if (pszWebPLosslessOverview)
    2516             :     {
    2517           2 :         bWebpLossless = CPLTestBool(pszWebPLosslessOverview);
    2518             :     }
    2519             : 
    2520         507 :     int nWebpLevel = m_nWebPLevel;
    2521         507 :     const char *pszKeyWebpLevel = "";
    2522         507 :     if (const char *opt = GetOptionValue("WEBP_LEVEL", "WEBP_LEVEL_OVERVIEW",
    2523             :                                          &pszKeyWebpLevel))
    2524             :     {
    2525          14 :         if (pszWebPLosslessOverview == nullptr && m_bWebPLossless)
    2526             :         {
    2527           1 :             CPLDebug("GTiff",
    2528             :                      "%s specified, but not WEBP_LOSSLESS_OVERVIEW. "
    2529             :                      "Assuming WEBP_LOSSLESS_OVERVIEW=NO",
    2530             :                      pszKeyWebpLevel);
    2531           1 :             bWebpLossless = false;
    2532             :         }
    2533          13 :         else if (bWebpLossless)
    2534             :         {
    2535           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2536             :                      "%s is specified, but WEBP_LOSSLESS_OVERVIEW=YES. "
    2537             :                      "%s will be ignored.",
    2538             :                      pszKeyWebpLevel, pszKeyWebpLevel);
    2539             :         }
    2540          14 :         nWebpLevel = atoi(opt);
    2541             :     }
    2542             : 
    2543         507 :     double dfMaxZError = m_dfMaxZErrorOverview;
    2544         507 :     if (const char *opt = GetOptionValue("MAX_Z_ERROR", "MAX_Z_ERROR_OVERVIEW"))
    2545             :     {
    2546          20 :         dfMaxZError = CPLAtof(opt);
    2547             :     }
    2548             : 
    2549         507 :     signed char nJpegTablesMode = m_nJpegTablesMode;
    2550         507 :     if (const char *opt =
    2551         507 :             GetOptionValue("JPEG_TABLESMODE", "JPEG_TABLESMODE_OVERVIEW"))
    2552             :     {
    2553           0 :         nJpegTablesMode = static_cast<signed char>(atoi(opt));
    2554             :     }
    2555             : 
    2556             : #ifdef HAVE_JXL
    2557         507 :     bool bJXLLossless = m_bJXLLossless;
    2558         507 :     if (const char *opt =
    2559         507 :             GetOptionValue("JXL_LOSSLESS", "JXL_LOSSLESS_OVERVIEW"))
    2560             :     {
    2561           0 :         bJXLLossless = CPLTestBool(opt);
    2562             :     }
    2563             : 
    2564         507 :     float fJXLDistance = m_fJXLDistance;
    2565         507 :     if (const char *opt =
    2566         507 :             GetOptionValue("JXL_DISTANCE", "JXL_DISTANCE_OVERVIEW"))
    2567             :     {
    2568           0 :         fJXLDistance = static_cast<float>(CPLAtof(opt));
    2569             :     }
    2570             : 
    2571         507 :     float fJXLAlphaDistance = m_fJXLAlphaDistance;
    2572         507 :     if (const char *opt =
    2573         507 :             GetOptionValue("JXL_ALPHA_DISTANCE", "JXL_ALPHA_DISTANCE_OVERVIEW"))
    2574             :     {
    2575           0 :         fJXLAlphaDistance = static_cast<float>(CPLAtof(opt));
    2576             :     }
    2577             : 
    2578         507 :     int nJXLEffort = m_nJXLEffort;
    2579         507 :     if (const char *opt = GetOptionValue("JXL_EFFORT", "JXL_EFFORT_OVERVIEW"))
    2580             :     {
    2581           0 :         nJXLEffort = atoi(opt);
    2582             :     }
    2583             : #endif
    2584             : 
    2585         507 :     GTiffDataset *poODS = new GTiffDataset();
    2586         507 :     poODS->ShareLockWithParentDataset(this);
    2587         507 :     poODS->m_pszFilename = CPLStrdup(m_pszFilename);
    2588         507 :     const char *pszSparseOK = GetOptionValue("SPARSE_OK", "SPARSE_OK_OVERVIEW");
    2589         507 :     if (pszSparseOK && CPLTestBool(pszSparseOK))
    2590             :     {
    2591           1 :         poODS->m_bWriteEmptyTiles = false;
    2592           1 :         poODS->m_bFillEmptyTilesAtClosing = false;
    2593             :     }
    2594             :     else
    2595             :     {
    2596         506 :         poODS->m_bWriteEmptyTiles = m_bWriteEmptyTiles;
    2597         506 :         poODS->m_bFillEmptyTilesAtClosing = m_bFillEmptyTilesAtClosing;
    2598             :     }
    2599         507 :     poODS->m_nJpegQuality = static_cast<signed char>(l_nJpegQuality);
    2600         507 :     poODS->m_nWebPLevel = static_cast<signed char>(nWebpLevel);
    2601         507 :     poODS->m_nZLevel = static_cast<signed char>(nZLevel);
    2602         507 :     poODS->m_nLZMAPreset = m_nLZMAPreset;
    2603         507 :     poODS->m_nZSTDLevel = static_cast<signed char>(nZSTDLevel);
    2604         507 :     poODS->m_bWebPLossless = bWebpLossless;
    2605         507 :     poODS->m_nJpegTablesMode = nJpegTablesMode;
    2606         507 :     poODS->m_dfMaxZError = dfMaxZError;
    2607         507 :     poODS->m_dfMaxZErrorOverview = dfMaxZError;
    2608         507 :     memcpy(poODS->m_anLercAddCompressionAndVersion,
    2609         507 :            m_anLercAddCompressionAndVersion,
    2610             :            sizeof(m_anLercAddCompressionAndVersion));
    2611             : #ifdef HAVE_JXL
    2612         507 :     poODS->m_bJXLLossless = bJXLLossless;
    2613         507 :     poODS->m_fJXLDistance = fJXLDistance;
    2614         507 :     poODS->m_fJXLAlphaDistance = fJXLAlphaDistance;
    2615         507 :     poODS->m_nJXLEffort = nJXLEffort;
    2616             : #endif
    2617             : 
    2618         507 :     if (poODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nOverviewOffset,
    2619         507 :                           GA_Update) != CE_None)
    2620             :     {
    2621           0 :         delete poODS;
    2622           0 :         return CE_Failure;
    2623             :     }
    2624             : 
    2625             :     // Assign color interpretation from main dataset
    2626         507 :     const int l_nBands = GetRasterCount();
    2627        1522 :     for (int i = 1; i <= l_nBands; i++)
    2628             :     {
    2629        1015 :         auto poBand = dynamic_cast<GTiffRasterBand *>(poODS->GetRasterBand(i));
    2630        1015 :         if (poBand)
    2631        1015 :             poBand->m_eBandInterp = GetRasterBand(i)->GetColorInterpretation();
    2632             :     }
    2633             : 
    2634             :     // Do that now that m_nCompression is set
    2635         507 :     poODS->RestoreVolatileParameters(poODS->m_hTIFF);
    2636             : 
    2637         507 :     ++m_nOverviewCount;
    2638         507 :     m_papoOverviewDS = static_cast<GTiffDataset **>(
    2639         507 :         CPLRealloc(m_papoOverviewDS, m_nOverviewCount * (sizeof(void *))));
    2640         507 :     m_papoOverviewDS[m_nOverviewCount - 1] = poODS;
    2641         507 :     poODS->m_poBaseDS = this;
    2642         507 :     poODS->m_bIsOverview = true;
    2643         507 :     return CE_None;
    2644             : }
    2645             : 
    2646             : /************************************************************************/
    2647             : /*                     CreateTIFFColorTable()                           */
    2648             : /************************************************************************/
    2649             : 
    2650          12 : static void CreateTIFFColorTable(
    2651             :     GDALColorTable *poColorTable, int nBits, int nColorTableMultiplier,
    2652             :     std::vector<unsigned short> &anTRed, std::vector<unsigned short> &anTGreen,
    2653             :     std::vector<unsigned short> &anTBlue, unsigned short *&panRed,
    2654             :     unsigned short *&panGreen, unsigned short *&panBlue)
    2655             : {
    2656             :     int nColors;
    2657             : 
    2658          12 :     if (nBits == 8)
    2659          12 :         nColors = 256;
    2660           0 :     else if (nBits < 8)
    2661           0 :         nColors = 1 << nBits;
    2662             :     else
    2663           0 :         nColors = 65536;
    2664             : 
    2665          12 :     anTRed.resize(nColors, 0);
    2666          12 :     anTGreen.resize(nColors, 0);
    2667          12 :     anTBlue.resize(nColors, 0);
    2668             : 
    2669        3084 :     for (int iColor = 0; iColor < nColors; ++iColor)
    2670             :     {
    2671        3072 :         if (iColor < poColorTable->GetColorEntryCount())
    2672             :         {
    2673             :             GDALColorEntry sRGB;
    2674             : 
    2675        3072 :             poColorTable->GetColorEntryAsRGB(iColor, &sRGB);
    2676             : 
    2677        3072 :             anTRed[iColor] = GTiffDataset::ClampCTEntry(iColor, 1, sRGB.c1,
    2678             :                                                         nColorTableMultiplier);
    2679        3072 :             anTGreen[iColor] = GTiffDataset::ClampCTEntry(
    2680        3072 :                 iColor, 2, sRGB.c2, nColorTableMultiplier);
    2681        3072 :             anTBlue[iColor] = GTiffDataset::ClampCTEntry(iColor, 3, sRGB.c3,
    2682             :                                                          nColorTableMultiplier);
    2683             :         }
    2684             :         else
    2685             :         {
    2686           0 :             anTRed[iColor] = 0;
    2687           0 :             anTGreen[iColor] = 0;
    2688           0 :             anTBlue[iColor] = 0;
    2689             :         }
    2690             :     }
    2691             : 
    2692          12 :     panRed = &(anTRed[0]);
    2693          12 :     panGreen = &(anTGreen[0]);
    2694          12 :     panBlue = &(anTBlue[0]);
    2695          12 : }
    2696             : 
    2697             : /************************************************************************/
    2698             : /*                        GetOverviewParameters()                       */
    2699             : /************************************************************************/
    2700             : 
    2701         321 : bool GTiffDataset::GetOverviewParameters(
    2702             :     int &nCompression, uint16_t &nPlanarConfig, uint16_t &nPredictor,
    2703             :     uint16_t &nPhotometric, int &nOvrJpegQuality, std::string &osNoData,
    2704             :     uint16_t *&panExtraSampleValues, uint16_t &nExtraSamples,
    2705             :     CSLConstList papszOptions) const
    2706             : {
    2707             :     const auto GetOptionValue =
    2708        1065 :         [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
    2709        2122 :                        const char **ppszKeyUsed = nullptr)
    2710             :     {
    2711        1065 :         const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
    2712        1065 :         if (pszVal)
    2713             :         {
    2714           8 :             if (ppszKeyUsed)
    2715           8 :                 *ppszKeyUsed = pszOptionKey;
    2716           8 :             return pszVal;
    2717             :         }
    2718        1057 :         pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey);
    2719        1057 :         if (pszVal)
    2720             :         {
    2721           0 :             if (ppszKeyUsed)
    2722           0 :                 *ppszKeyUsed = pszConfigOptionKey;
    2723           0 :             return pszVal;
    2724             :         }
    2725        1057 :         pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
    2726        1057 :         if (pszVal && ppszKeyUsed)
    2727          58 :             *ppszKeyUsed = pszConfigOptionKey;
    2728        1057 :         return pszVal;
    2729         321 :     };
    2730             : 
    2731             :     /* -------------------------------------------------------------------- */
    2732             :     /*      Determine compression method.                                   */
    2733             :     /* -------------------------------------------------------------------- */
    2734         321 :     nCompression = m_nCompression;
    2735         321 :     const char *pszOptionKey = "";
    2736             :     const char *pszCompressValue =
    2737         321 :         GetOptionValue("COMPRESS", "COMPRESS_OVERVIEW", &pszOptionKey);
    2738         321 :     if (pszCompressValue != nullptr)
    2739             :     {
    2740          56 :         nCompression =
    2741          56 :             GTIFFGetCompressionMethod(pszCompressValue, pszOptionKey);
    2742          56 :         if (nCompression < 0)
    2743             :         {
    2744           0 :             nCompression = m_nCompression;
    2745             :         }
    2746             :     }
    2747             : 
    2748             :     /* -------------------------------------------------------------------- */
    2749             :     /*      Determine planar configuration.                                 */
    2750             :     /* -------------------------------------------------------------------- */
    2751         321 :     nPlanarConfig = m_nPlanarConfig;
    2752         321 :     if (nCompression == COMPRESSION_WEBP)
    2753             :     {
    2754          11 :         nPlanarConfig = PLANARCONFIG_CONTIG;
    2755             :     }
    2756             :     const char *pszInterleave =
    2757         321 :         GetOptionValue("INTERLEAVE", "INTERLEAVE_OVERVIEW", &pszOptionKey);
    2758         321 :     if (pszInterleave != nullptr && pszInterleave[0] != '\0')
    2759             :     {
    2760           2 :         if (EQUAL(pszInterleave, "PIXEL"))
    2761           1 :             nPlanarConfig = PLANARCONFIG_CONTIG;
    2762           1 :         else if (EQUAL(pszInterleave, "BAND"))
    2763           1 :             nPlanarConfig = PLANARCONFIG_SEPARATE;
    2764             :         else
    2765             :         {
    2766           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2767             :                      "%s=%s unsupported, "
    2768             :                      "value must be PIXEL or BAND. ignoring",
    2769             :                      pszOptionKey, pszInterleave);
    2770             :         }
    2771             :     }
    2772             : 
    2773             :     /* -------------------------------------------------------------------- */
    2774             :     /*      Determine predictor tag                                         */
    2775             :     /* -------------------------------------------------------------------- */
    2776         321 :     nPredictor = PREDICTOR_NONE;
    2777         321 :     if (GTIFFSupportsPredictor(nCompression))
    2778             :     {
    2779             :         const char *pszPredictor =
    2780          75 :             GetOptionValue("PREDICTOR", "PREDICTOR_OVERVIEW");
    2781          75 :         if (pszPredictor != nullptr)
    2782             :         {
    2783           1 :             nPredictor = static_cast<uint16_t>(atoi(pszPredictor));
    2784             :         }
    2785          74 :         else if (GTIFFSupportsPredictor(m_nCompression))
    2786          73 :             TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &nPredictor);
    2787             :     }
    2788             : 
    2789             :     /* -------------------------------------------------------------------- */
    2790             :     /*      Determine photometric tag                                       */
    2791             :     /* -------------------------------------------------------------------- */
    2792         321 :     if (m_nPhotometric == PHOTOMETRIC_YCBCR && nCompression != COMPRESSION_JPEG)
    2793           1 :         nPhotometric = PHOTOMETRIC_RGB;
    2794             :     else
    2795         320 :         nPhotometric = m_nPhotometric;
    2796             :     const char *pszPhotometric =
    2797         321 :         GetOptionValue("PHOTOMETRIC", "PHOTOMETRIC_OVERVIEW", &pszOptionKey);
    2798         321 :     if (!GTIFFUpdatePhotometric(pszPhotometric, pszOptionKey, nCompression,
    2799         321 :                                 pszInterleave, nBands, nPhotometric,
    2800             :                                 nPlanarConfig))
    2801             :     {
    2802           0 :         return false;
    2803             :     }
    2804             : 
    2805             :     /* -------------------------------------------------------------------- */
    2806             :     /*      Determine JPEG quality                                          */
    2807             :     /* -------------------------------------------------------------------- */
    2808         321 :     nOvrJpegQuality = m_nJpegQuality;
    2809         321 :     if (nCompression == COMPRESSION_JPEG)
    2810             :     {
    2811             :         const char *pszJPEGQuality =
    2812          27 :             GetOptionValue("JPEG_QUALITY", "JPEG_QUALITY_OVERVIEW");
    2813          27 :         if (pszJPEGQuality != nullptr)
    2814             :         {
    2815           9 :             nOvrJpegQuality = atoi(pszJPEGQuality);
    2816             :         }
    2817             :     }
    2818             : 
    2819             :     /* -------------------------------------------------------------------- */
    2820             :     /*      Set nodata.                                                     */
    2821             :     /* -------------------------------------------------------------------- */
    2822         321 :     if (m_bNoDataSet)
    2823             :     {
    2824          17 :         osNoData = GTiffFormatGDALNoDataTagValue(m_dfNoDataValue);
    2825             :     }
    2826             : 
    2827             :     /* -------------------------------------------------------------------- */
    2828             :     /*      Fetch extra sample tag                                          */
    2829             :     /* -------------------------------------------------------------------- */
    2830         321 :     panExtraSampleValues = nullptr;
    2831         321 :     nExtraSamples = 0;
    2832         321 :     if (TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples,
    2833         321 :                      &panExtraSampleValues))
    2834             :     {
    2835             :         uint16_t *panExtraSampleValuesNew = static_cast<uint16_t *>(
    2836          40 :             CPLMalloc(nExtraSamples * sizeof(uint16_t)));
    2837          40 :         memcpy(panExtraSampleValuesNew, panExtraSampleValues,
    2838          40 :                nExtraSamples * sizeof(uint16_t));
    2839          40 :         panExtraSampleValues = panExtraSampleValuesNew;
    2840             :     }
    2841             :     else
    2842             :     {
    2843         281 :         panExtraSampleValues = nullptr;
    2844         281 :         nExtraSamples = 0;
    2845             :     }
    2846             : 
    2847         321 :     return true;
    2848             : }
    2849             : 
    2850             : /************************************************************************/
    2851             : /*                  CreateOverviewsFromSrcOverviews()                   */
    2852             : /************************************************************************/
    2853             : 
    2854             : // If poOvrDS is not null, it is used and poSrcDS is ignored.
    2855             : 
    2856          68 : CPLErr GTiffDataset::CreateOverviewsFromSrcOverviews(GDALDataset *poSrcDS,
    2857             :                                                      GDALDataset *poOvrDS,
    2858             :                                                      int nOverviews)
    2859             : {
    2860          68 :     CPLAssert(poSrcDS->GetRasterCount() != 0);
    2861          68 :     CPLAssert(m_nOverviewCount == 0);
    2862             : 
    2863          68 :     ScanDirectories();
    2864             : 
    2865          68 :     FlushDirectory();
    2866             : 
    2867          68 :     int nOvBitsPerSample = m_nBitsPerSample;
    2868             : 
    2869             :     /* -------------------------------------------------------------------- */
    2870             :     /*      Do we need some metadata for the overviews?                     */
    2871             :     /* -------------------------------------------------------------------- */
    2872         136 :     CPLString osMetadata;
    2873             : 
    2874          68 :     GTIFFBuildOverviewMetadata("NONE", this, false, osMetadata);
    2875             : 
    2876             :     int nCompression;
    2877             :     uint16_t nPlanarConfig;
    2878             :     uint16_t nPredictor;
    2879             :     uint16_t nPhotometric;
    2880             :     int nOvrJpegQuality;
    2881         136 :     std::string osNoData;
    2882          68 :     uint16_t *panExtraSampleValues = nullptr;
    2883          68 :     uint16_t nExtraSamples = 0;
    2884          68 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    2885             :                                nPhotometric, nOvrJpegQuality, osNoData,
    2886             :                                panExtraSampleValues, nExtraSamples,
    2887             :                                /*papszOptions=*/nullptr))
    2888             :     {
    2889           0 :         return CE_Failure;
    2890             :     }
    2891             : 
    2892             :     /* -------------------------------------------------------------------- */
    2893             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    2894             :     /* -------------------------------------------------------------------- */
    2895         136 :     std::vector<unsigned short> anTRed;
    2896         136 :     std::vector<unsigned short> anTGreen;
    2897          68 :     std::vector<unsigned short> anTBlue;
    2898          68 :     unsigned short *panRed = nullptr;
    2899          68 :     unsigned short *panGreen = nullptr;
    2900          68 :     unsigned short *panBlue = nullptr;
    2901             : 
    2902          68 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    2903             :     {
    2904           0 :         if (m_nColorTableMultiplier == 0)
    2905           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    2906             : 
    2907           0 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    2908             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    2909             :                              panRed, panGreen, panBlue);
    2910             :     }
    2911             : 
    2912          68 :     int nOvrBlockXSize = 0;
    2913          68 :     int nOvrBlockYSize = 0;
    2914          68 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    2915             :                               &nOvrBlockXSize, &nOvrBlockYSize, nullptr,
    2916             :                               nullptr);
    2917             : 
    2918          68 :     CPLErr eErr = CE_None;
    2919             : 
    2920         196 :     for (int i = 0; i < nOverviews && eErr == CE_None; ++i)
    2921             :     {
    2922             :         GDALRasterBand *poOvrBand =
    2923         169 :             poOvrDS ? ((i == 0) ? poOvrDS->GetRasterBand(1)
    2924          41 :                                 : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    2925          49 :                     : poSrcDS->GetRasterBand(1)->GetOverview(i);
    2926             : 
    2927         128 :         int nOXSize = poOvrBand->GetXSize();
    2928         128 :         int nOYSize = poOvrBand->GetYSize();
    2929             : 
    2930         256 :         toff_t nOverviewOffset = GTIFFWriteDirectory(
    2931             :             m_hTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize, nOvBitsPerSample,
    2932         128 :             nPlanarConfig, m_nSamplesPerPixel, nOvrBlockXSize, nOvrBlockYSize,
    2933         128 :             TRUE, nCompression, nPhotometric, m_nSampleFormat, nPredictor,
    2934             :             panRed, panGreen, panBlue, nExtraSamples, panExtraSampleValues,
    2935             :             osMetadata,
    2936         128 :             nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality) : nullptr,
    2937         128 :             CPLSPrintf("%d", m_nJpegTablesMode),
    2938           2 :             osNoData.empty() ? nullptr : osNoData.c_str(),
    2939         128 :             m_anLercAddCompressionAndVersion, m_bWriteCOGLayout);
    2940             : 
    2941         128 :         if (nOverviewOffset == 0)
    2942           0 :             eErr = CE_Failure;
    2943             :         else
    2944         128 :             eErr = RegisterNewOverviewDataset(nOverviewOffset, nOvrJpegQuality,
    2945             :                                               nullptr);
    2946             :     }
    2947             : 
    2948             :     // For directory reloading, so that the chaining to the next directory is
    2949             :     // reloaded, as well as compression parameters.
    2950          68 :     ReloadDirectory();
    2951             : 
    2952          68 :     CPLFree(panExtraSampleValues);
    2953          68 :     panExtraSampleValues = nullptr;
    2954             : 
    2955          68 :     return eErr;
    2956             : }
    2957             : 
    2958             : /************************************************************************/
    2959             : /*                       CreateInternalMaskOverviews()                  */
    2960             : /************************************************************************/
    2961             : 
    2962         268 : CPLErr GTiffDataset::CreateInternalMaskOverviews(int nOvrBlockXSize,
    2963             :                                                  int nOvrBlockYSize)
    2964             : {
    2965         268 :     ScanDirectories();
    2966             : 
    2967             :     /* -------------------------------------------------------------------- */
    2968             :     /*      Create overviews for the mask.                                  */
    2969             :     /* -------------------------------------------------------------------- */
    2970         268 :     CPLErr eErr = CE_None;
    2971             : 
    2972         268 :     if (m_poMaskDS != nullptr && m_poMaskDS->GetRasterCount() == 1)
    2973             :     {
    2974             :         int nMaskOvrCompression;
    2975          42 :         if (strstr(GDALGetMetadataItem(GDALGetDriverByName("GTiff"),
    2976             :                                        GDAL_DMD_CREATIONOPTIONLIST, nullptr),
    2977          42 :                    "<Value>DEFLATE</Value>") != nullptr)
    2978          42 :             nMaskOvrCompression = COMPRESSION_ADOBE_DEFLATE;
    2979             :         else
    2980           0 :             nMaskOvrCompression = COMPRESSION_PACKBITS;
    2981             : 
    2982         117 :         for (int i = 0; i < m_nOverviewCount; ++i)
    2983             :         {
    2984          75 :             if (m_papoOverviewDS[i]->m_poMaskDS == nullptr)
    2985             :             {
    2986         126 :                 const toff_t nOverviewOffset = GTIFFWriteDirectory(
    2987             :                     m_hTIFF, FILETYPE_REDUCEDIMAGE | FILETYPE_MASK,
    2988          63 :                     m_papoOverviewDS[i]->nRasterXSize,
    2989          63 :                     m_papoOverviewDS[i]->nRasterYSize, 1, PLANARCONFIG_CONTIG,
    2990             :                     1, nOvrBlockXSize, nOvrBlockYSize, TRUE,
    2991             :                     nMaskOvrCompression, PHOTOMETRIC_MASK, SAMPLEFORMAT_UINT,
    2992             :                     PREDICTOR_NONE, nullptr, nullptr, nullptr, 0, nullptr, "",
    2993          63 :                     nullptr, nullptr, nullptr, nullptr, m_bWriteCOGLayout);
    2994             : 
    2995          63 :                 if (nOverviewOffset == 0)
    2996             :                 {
    2997           0 :                     eErr = CE_Failure;
    2998           0 :                     continue;
    2999             :                 }
    3000             : 
    3001          63 :                 GTiffDataset *poODS = new GTiffDataset();
    3002          63 :                 poODS->ShareLockWithParentDataset(this);
    3003          63 :                 poODS->m_pszFilename = CPLStrdup(m_pszFilename);
    3004          63 :                 if (poODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF),
    3005          63 :                                       nOverviewOffset, GA_Update) != CE_None)
    3006             :                 {
    3007           0 :                     delete poODS;
    3008           0 :                     eErr = CE_Failure;
    3009             :                 }
    3010             :                 else
    3011             :                 {
    3012          63 :                     poODS->m_bPromoteTo8Bits = CPLTestBool(CPLGetConfigOption(
    3013             :                         "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
    3014          63 :                     poODS->m_poBaseDS = this;
    3015          63 :                     poODS->m_poImageryDS = m_papoOverviewDS[i];
    3016          63 :                     m_papoOverviewDS[i]->m_poMaskDS = poODS;
    3017          63 :                     ++m_poMaskDS->m_nOverviewCount;
    3018         126 :                     m_poMaskDS->m_papoOverviewDS =
    3019         126 :                         static_cast<GTiffDataset **>(CPLRealloc(
    3020          63 :                             m_poMaskDS->m_papoOverviewDS,
    3021          63 :                             m_poMaskDS->m_nOverviewCount * (sizeof(void *))));
    3022          63 :                     m_poMaskDS
    3023          63 :                         ->m_papoOverviewDS[m_poMaskDS->m_nOverviewCount - 1] =
    3024             :                         poODS;
    3025             :                 }
    3026             :             }
    3027             :         }
    3028             :     }
    3029             : 
    3030         268 :     ReloadDirectory();
    3031             : 
    3032         268 :     return eErr;
    3033             : }
    3034             : 
    3035             : /************************************************************************/
    3036             : /*                            AddOverviews()                            */
    3037             : /************************************************************************/
    3038             : 
    3039             : CPLErr
    3040          13 : GTiffDataset::AddOverviews(const std::vector<GDALDataset *> &apoSrcOvrDSIn,
    3041             :                            GDALProgressFunc pfnProgress, void *pProgressData,
    3042             :                            CSLConstList papszOptions)
    3043             : {
    3044             :     /* -------------------------------------------------------------------- */
    3045             :     /*      If we don't have read access, then create the overviews         */
    3046             :     /*      externally.                                                     */
    3047             :     /* -------------------------------------------------------------------- */
    3048          13 :     if (GetAccess() != GA_Update)
    3049             :     {
    3050           4 :         CPLDebug("GTiff", "File open for read-only accessing, "
    3051             :                           "creating overviews externally.");
    3052             : 
    3053           4 :         CPLErr eErr = GDALDataset::AddOverviews(apoSrcOvrDSIn, pfnProgress,
    3054             :                                                 pProgressData, papszOptions);
    3055           4 :         if (eErr == CE_None && m_poMaskDS)
    3056             :         {
    3057           0 :             ReportError(
    3058             :                 CE_Warning, CPLE_NotSupported,
    3059             :                 "Building external overviews whereas there is an internal "
    3060             :                 "mask is not fully supported. "
    3061             :                 "The overviews of the non-mask bands will be created, "
    3062             :                 "but not the overviews of the mask band.");
    3063             :         }
    3064           4 :         return eErr;
    3065             :     }
    3066             : 
    3067          18 :     std::vector<GDALDataset *> apoSrcOvrDS = apoSrcOvrDSIn;
    3068             :     // Sort overviews by descending size
    3069           9 :     std::sort(apoSrcOvrDS.begin(), apoSrcOvrDS.end(),
    3070           0 :               [](const GDALDataset *poDS1, const GDALDataset *poDS2)
    3071           0 :               { return poDS1->GetRasterXSize() > poDS2->GetRasterXSize(); });
    3072             : 
    3073           9 :     if (!GDALDefaultOverviews::CheckSrcOverviewsConsistencyWithBase(
    3074             :             this, apoSrcOvrDS))
    3075           5 :         return CE_Failure;
    3076             : 
    3077           4 :     ScanDirectories();
    3078             : 
    3079             :     // Make implicit JPEG overviews invisible, but do not destroy
    3080             :     // them in case they are already used (not sure that the client
    3081             :     // has the right to do that). Behavior maybe undefined in GDAL API.
    3082           4 :     m_nJPEGOverviewCount = 0;
    3083             : 
    3084           4 :     FlushDirectory();
    3085             : 
    3086             :     /* -------------------------------------------------------------------- */
    3087             :     /*      If we are averaging bit data to grayscale we need to create     */
    3088             :     /*      8bit overviews.                                                 */
    3089             :     /* -------------------------------------------------------------------- */
    3090           4 :     int nOvBitsPerSample = m_nBitsPerSample;
    3091             : 
    3092             :     /* -------------------------------------------------------------------- */
    3093             :     /*      Do we need some metadata for the overviews?                     */
    3094             :     /* -------------------------------------------------------------------- */
    3095           8 :     CPLString osMetadata;
    3096             : 
    3097           4 :     const bool bIsForMaskBand = nBands == 1 && GetRasterBand(1)->IsMaskBand();
    3098           4 :     GTIFFBuildOverviewMetadata(/* resampling = */ "", this, bIsForMaskBand,
    3099             :                                osMetadata);
    3100             : 
    3101             :     int nCompression;
    3102             :     uint16_t nPlanarConfig;
    3103             :     uint16_t nPredictor;
    3104             :     uint16_t nPhotometric;
    3105             :     int nOvrJpegQuality;
    3106           8 :     std::string osNoData;
    3107           4 :     uint16_t *panExtraSampleValues = nullptr;
    3108           4 :     uint16_t nExtraSamples = 0;
    3109           4 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    3110             :                                nPhotometric, nOvrJpegQuality, osNoData,
    3111             :                                panExtraSampleValues, nExtraSamples,
    3112             :                                papszOptions))
    3113             :     {
    3114           0 :         return CE_Failure;
    3115             :     }
    3116             : 
    3117             :     /* -------------------------------------------------------------------- */
    3118             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    3119             :     /* -------------------------------------------------------------------- */
    3120           8 :     std::vector<unsigned short> anTRed;
    3121           8 :     std::vector<unsigned short> anTGreen;
    3122           4 :     std::vector<unsigned short> anTBlue;
    3123           4 :     unsigned short *panRed = nullptr;
    3124           4 :     unsigned short *panGreen = nullptr;
    3125           4 :     unsigned short *panBlue = nullptr;
    3126             : 
    3127           4 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    3128             :     {
    3129           0 :         if (m_nColorTableMultiplier == 0)
    3130           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    3131             : 
    3132           0 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    3133             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    3134             :                              panRed, panGreen, panBlue);
    3135             :     }
    3136             : 
    3137             :     /* -------------------------------------------------------------------- */
    3138             :     /*      Establish which of the overview levels we already have, and     */
    3139             :     /*      which are new.  We assume that band 1 of the file is            */
    3140             :     /*      representative.                                                 */
    3141             :     /* -------------------------------------------------------------------- */
    3142           4 :     int nOvrBlockXSize = 0;
    3143           4 :     int nOvrBlockYSize = 0;
    3144           4 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    3145             :                               &nOvrBlockXSize, &nOvrBlockYSize, papszOptions,
    3146             :                               "BLOCKSIZE");
    3147             : 
    3148           4 :     CPLErr eErr = CE_None;
    3149           8 :     for (const auto *poSrcOvrDS : apoSrcOvrDS)
    3150             :     {
    3151           4 :         bool bFound = false;
    3152           4 :         for (int i = 0; i < m_nOverviewCount && eErr == CE_None; ++i)
    3153             :         {
    3154           2 :             const GTiffDataset *poExistingODS = m_papoOverviewDS[i];
    3155           2 :             if (poExistingODS->GetRasterXSize() ==
    3156           4 :                     poSrcOvrDS->GetRasterXSize() &&
    3157           2 :                 poExistingODS->GetRasterYSize() == poSrcOvrDS->GetRasterYSize())
    3158             :             {
    3159           2 :                 bFound = true;
    3160           2 :                 break;
    3161             :             }
    3162             :         }
    3163           4 :         if (!bFound && eErr == CE_None)
    3164             :         {
    3165           2 :             if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    3166           0 :                 !m_bWriteKnownIncompatibleEdition)
    3167             :             {
    3168           0 :                 ReportError(CE_Warning, CPLE_AppDefined,
    3169             :                             "Adding new overviews invalidates the "
    3170             :                             "LAYOUT=IFDS_BEFORE_DATA property");
    3171           0 :                 m_bKnownIncompatibleEdition = true;
    3172           0 :                 m_bWriteKnownIncompatibleEdition = true;
    3173             :             }
    3174             : 
    3175           6 :             const toff_t nOverviewOffset = GTIFFWriteDirectory(
    3176             :                 m_hTIFF, FILETYPE_REDUCEDIMAGE, poSrcOvrDS->GetRasterXSize(),
    3177             :                 poSrcOvrDS->GetRasterYSize(), nOvBitsPerSample, nPlanarConfig,
    3178           2 :                 m_nSamplesPerPixel, nOvrBlockXSize, nOvrBlockYSize, TRUE,
    3179           2 :                 nCompression, nPhotometric, m_nSampleFormat, nPredictor, panRed,
    3180             :                 panGreen, panBlue, nExtraSamples, panExtraSampleValues,
    3181             :                 osMetadata,
    3182           2 :                 nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality)
    3183             :                                      : nullptr,
    3184           2 :                 CPLSPrintf("%d", m_nJpegTablesMode),
    3185           0 :                 osNoData.empty() ? nullptr : osNoData.c_str(),
    3186           2 :                 m_anLercAddCompressionAndVersion, false);
    3187             : 
    3188           2 :             if (nOverviewOffset == 0)
    3189           0 :                 eErr = CE_Failure;
    3190             :             else
    3191           2 :                 eErr = RegisterNewOverviewDataset(
    3192             :                     nOverviewOffset, nOvrJpegQuality, papszOptions);
    3193             :         }
    3194             :     }
    3195             : 
    3196           4 :     CPLFree(panExtraSampleValues);
    3197           4 :     panExtraSampleValues = nullptr;
    3198             : 
    3199           4 :     ReloadDirectory();
    3200             : 
    3201           4 :     if (!pfnProgress)
    3202           2 :         pfnProgress = GDALDummyProgress;
    3203             : 
    3204             :     // almost 0, but not 0 to please Coverity Scan
    3205           4 :     double dfTotalPixels = std::numeric_limits<double>::min();
    3206           8 :     for (const auto *poSrcOvrDS : apoSrcOvrDS)
    3207             :     {
    3208           4 :         dfTotalPixels += static_cast<double>(poSrcOvrDS->GetRasterXSize()) *
    3209           4 :                          poSrcOvrDS->GetRasterYSize();
    3210             :     }
    3211             : 
    3212             :     // Copy source datasets into target overview datasets
    3213           4 :     double dfCurPixels = 0;
    3214           8 :     for (auto *poSrcOvrDS : apoSrcOvrDS)
    3215             :     {
    3216           4 :         GDALDataset *poDstOvrDS = nullptr;
    3217           4 :         for (int i = 0; i < m_nOverviewCount && eErr == CE_None; ++i)
    3218             :         {
    3219           4 :             GTiffDataset *poExistingODS = m_papoOverviewDS[i];
    3220           4 :             if (poExistingODS->GetRasterXSize() ==
    3221           8 :                     poSrcOvrDS->GetRasterXSize() &&
    3222           4 :                 poExistingODS->GetRasterYSize() == poSrcOvrDS->GetRasterYSize())
    3223             :             {
    3224           4 :                 poDstOvrDS = poExistingODS;
    3225           4 :                 break;
    3226             :             }
    3227             :         }
    3228           4 :         if (poDstOvrDS)
    3229             :         {
    3230             :             const double dfThisPixels =
    3231           4 :                 static_cast<double>(poSrcOvrDS->GetRasterXSize()) *
    3232           4 :                 poSrcOvrDS->GetRasterYSize();
    3233           8 :             void *pScaledProgressData = GDALCreateScaledProgress(
    3234             :                 dfCurPixels / dfTotalPixels,
    3235           4 :                 (dfCurPixels + dfThisPixels) / dfTotalPixels, pfnProgress,
    3236             :                 pProgressData);
    3237           4 :             dfCurPixels += dfThisPixels;
    3238           4 :             eErr = GDALDatasetCopyWholeRaster(GDALDataset::ToHandle(poSrcOvrDS),
    3239             :                                               GDALDataset::ToHandle(poDstOvrDS),
    3240             :                                               nullptr, GDALScaledProgress,
    3241             :                                               pScaledProgressData);
    3242           4 :             GDALDestroyScaledProgress(pScaledProgressData);
    3243             :         }
    3244             :     }
    3245             : 
    3246           4 :     return eErr;
    3247             : }
    3248             : 
    3249             : /************************************************************************/
    3250             : /*                          IBuildOverviews()                           */
    3251             : /************************************************************************/
    3252             : 
    3253         403 : CPLErr GTiffDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
    3254             :                                      const int *panOverviewList, int nBandsIn,
    3255             :                                      const int *panBandList,
    3256             :                                      GDALProgressFunc pfnProgress,
    3257             :                                      void *pProgressData,
    3258             :                                      CSLConstList papszOptions)
    3259             : 
    3260             : {
    3261         403 :     ScanDirectories();
    3262             : 
    3263             :     // Make implicit JPEG overviews invisible, but do not destroy
    3264             :     // them in case they are already used (not sure that the client
    3265             :     // has the right to do that.  Behavior maybe undefined in GDAL API.
    3266         403 :     m_nJPEGOverviewCount = 0;
    3267             : 
    3268             :     /* -------------------------------------------------------------------- */
    3269             :     /*      If RRD or external OVR overviews requested, then invoke         */
    3270             :     /*      generic handling.                                               */
    3271             :     /* -------------------------------------------------------------------- */
    3272         403 :     bool bUseGenericHandling = false;
    3273         806 :     CPLStringList aosOptions(papszOptions);
    3274             : 
    3275         403 :     const char *pszLocation = CSLFetchNameValue(papszOptions, "LOCATION");
    3276         403 :     if (pszLocation && EQUAL(pszLocation, "EXTERNAL"))
    3277             :     {
    3278           1 :         bUseGenericHandling = true;
    3279             :     }
    3280         402 :     else if (pszLocation && EQUAL(pszLocation, "INTERNAL"))
    3281             :     {
    3282           0 :         if (GetAccess() != GA_Update)
    3283             :         {
    3284           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3285             :                      "Cannot create internal overviews on file opened in "
    3286             :                      "read-only mode");
    3287           0 :             return CE_Failure;
    3288             :         }
    3289             :     }
    3290         402 :     else if (pszLocation && EQUAL(pszLocation, "RRD"))
    3291             :     {
    3292           3 :         bUseGenericHandling = true;
    3293           3 :         aosOptions.SetNameValue("USE_RRD", "YES");
    3294             :     }
    3295             :     // Legacy
    3296         399 :     else if (CPLTestBool(
    3297             :                  CSLFetchNameValueDef(papszOptions, "USE_RRD",
    3298         798 :                                       CPLGetConfigOption("USE_RRD", "NO"))) ||
    3299         399 :              CPLTestBool(CSLFetchNameValueDef(
    3300             :                  papszOptions, "TIFF_USE_OVR",
    3301             :                  CPLGetConfigOption("TIFF_USE_OVR", "NO"))))
    3302             :     {
    3303           0 :         bUseGenericHandling = true;
    3304             :     }
    3305             : 
    3306             :     /* -------------------------------------------------------------------- */
    3307             :     /*      If we don't have read access, then create the overviews         */
    3308             :     /*      externally.                                                     */
    3309             :     /* -------------------------------------------------------------------- */
    3310         403 :     if (GetAccess() != GA_Update)
    3311             :     {
    3312         143 :         CPLDebug("GTiff", "File open for read-only accessing, "
    3313             :                           "creating overviews externally.");
    3314             : 
    3315         143 :         bUseGenericHandling = true;
    3316             :     }
    3317             : 
    3318         403 :     if (bUseGenericHandling)
    3319             :     {
    3320         146 :         if (m_nOverviewCount != 0)
    3321             :         {
    3322           0 :             ReportError(CE_Failure, CPLE_NotSupported,
    3323             :                         "Cannot add external overviews when there are already "
    3324             :                         "internal overviews");
    3325           0 :             return CE_Failure;
    3326             :         }
    3327             : 
    3328         146 :         if (!m_bWriteEmptyTiles)
    3329             :         {
    3330           1 :             aosOptions.SetNameValue("SPARSE_OK", "YES");
    3331             :         }
    3332             : 
    3333         146 :         CPLErr eErr = GDALDataset::IBuildOverviews(
    3334             :             pszResampling, nOverviews, panOverviewList, nBandsIn, panBandList,
    3335         146 :             pfnProgress, pProgressData, aosOptions);
    3336         146 :         if (eErr == CE_None && m_poMaskDS)
    3337             :         {
    3338           1 :             ReportError(
    3339             :                 CE_Warning, CPLE_NotSupported,
    3340             :                 "Building external overviews whereas there is an internal "
    3341             :                 "mask is not fully supported. "
    3342             :                 "The overviews of the non-mask bands will be created, "
    3343             :                 "but not the overviews of the mask band.");
    3344             :         }
    3345         146 :         return eErr;
    3346             :     }
    3347             : 
    3348             :     /* -------------------------------------------------------------------- */
    3349             :     /*      Our TIFF overview support currently only works safely if all    */
    3350             :     /*      bands are handled at the same time.                             */
    3351             :     /* -------------------------------------------------------------------- */
    3352         257 :     if (nBandsIn != GetRasterCount())
    3353             :     {
    3354           0 :         ReportError(CE_Failure, CPLE_NotSupported,
    3355             :                     "Generation of overviews in TIFF currently only "
    3356             :                     "supported when operating on all bands.  "
    3357             :                     "Operation failed.");
    3358           0 :         return CE_Failure;
    3359             :     }
    3360             : 
    3361             :     /* -------------------------------------------------------------------- */
    3362             :     /*      If zero overviews were requested, we need to clear all          */
    3363             :     /*      existing overviews.                                             */
    3364             :     /* -------------------------------------------------------------------- */
    3365         257 :     if (nOverviews == 0)
    3366             :     {
    3367           8 :         if (m_nOverviewCount == 0)
    3368           3 :             return GDALDataset::IBuildOverviews(
    3369             :                 pszResampling, nOverviews, panOverviewList, nBandsIn,
    3370           3 :                 panBandList, pfnProgress, pProgressData, papszOptions);
    3371             : 
    3372           5 :         return CleanOverviews();
    3373             :     }
    3374             : 
    3375         249 :     CPLErr eErr = CE_None;
    3376             : 
    3377             :     /* -------------------------------------------------------------------- */
    3378             :     /*      Initialize progress counter.                                    */
    3379             :     /* -------------------------------------------------------------------- */
    3380         249 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    3381             :     {
    3382           0 :         ReportError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    3383           0 :         return CE_Failure;
    3384             :     }
    3385             : 
    3386         249 :     FlushDirectory();
    3387             : 
    3388             :     /* -------------------------------------------------------------------- */
    3389             :     /*      If we are averaging bit data to grayscale we need to create     */
    3390             :     /*      8bit overviews.                                                 */
    3391             :     /* -------------------------------------------------------------------- */
    3392         249 :     int nOvBitsPerSample = m_nBitsPerSample;
    3393             : 
    3394         249 :     if (STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
    3395           2 :         nOvBitsPerSample = 8;
    3396             : 
    3397             :     /* -------------------------------------------------------------------- */
    3398             :     /*      Do we need some metadata for the overviews?                     */
    3399             :     /* -------------------------------------------------------------------- */
    3400         498 :     CPLString osMetadata;
    3401             : 
    3402         249 :     const bool bIsForMaskBand = nBands == 1 && GetRasterBand(1)->IsMaskBand();
    3403         249 :     GTIFFBuildOverviewMetadata(pszResampling, this, bIsForMaskBand, osMetadata);
    3404             : 
    3405             :     int nCompression;
    3406             :     uint16_t nPlanarConfig;
    3407             :     uint16_t nPredictor;
    3408             :     uint16_t nPhotometric;
    3409             :     int nOvrJpegQuality;
    3410         498 :     std::string osNoData;
    3411         249 :     uint16_t *panExtraSampleValues = nullptr;
    3412         249 :     uint16_t nExtraSamples = 0;
    3413         249 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    3414             :                                nPhotometric, nOvrJpegQuality, osNoData,
    3415             :                                panExtraSampleValues, nExtraSamples,
    3416             :                                papszOptions))
    3417             :     {
    3418           0 :         return CE_Failure;
    3419             :     }
    3420             : 
    3421             :     /* -------------------------------------------------------------------- */
    3422             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    3423             :     /* -------------------------------------------------------------------- */
    3424         498 :     std::vector<unsigned short> anTRed;
    3425         498 :     std::vector<unsigned short> anTGreen;
    3426         498 :     std::vector<unsigned short> anTBlue;
    3427         249 :     unsigned short *panRed = nullptr;
    3428         249 :     unsigned short *panGreen = nullptr;
    3429         249 :     unsigned short *panBlue = nullptr;
    3430             : 
    3431         249 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    3432             :     {
    3433          12 :         if (m_nColorTableMultiplier == 0)
    3434           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    3435             : 
    3436          12 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    3437             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    3438             :                              panRed, panGreen, panBlue);
    3439             :     }
    3440             : 
    3441             :     /* -------------------------------------------------------------------- */
    3442             :     /*      Establish which of the overview levels we already have, and     */
    3443             :     /*      which are new.  We assume that band 1 of the file is            */
    3444             :     /*      representative.                                                 */
    3445             :     /* -------------------------------------------------------------------- */
    3446         249 :     int nOvrBlockXSize = 0;
    3447         249 :     int nOvrBlockYSize = 0;
    3448         249 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    3449             :                               &nOvrBlockXSize, &nOvrBlockYSize, papszOptions,
    3450             :                               "BLOCKSIZE");
    3451         498 :     std::vector<bool> abRequireNewOverview(nOverviews, true);
    3452         682 :     for (int i = 0; i < nOverviews && eErr == CE_None; ++i)
    3453             :     {
    3454         766 :         for (int j = 0; j < m_nOverviewCount && eErr == CE_None; ++j)
    3455             :         {
    3456         389 :             GTiffDataset *poODS = m_papoOverviewDS[j];
    3457             : 
    3458             :             const int nOvFactor =
    3459         389 :                 GDALComputeOvFactor(poODS->GetRasterXSize(), GetRasterXSize(),
    3460             :                                     poODS->GetRasterYSize(), GetRasterYSize());
    3461             : 
    3462             :             // If we already have a 1x1 overview and this new one would result
    3463             :             // in it too, then don't create it.
    3464         449 :             if (poODS->GetRasterXSize() == 1 && poODS->GetRasterYSize() == 1 &&
    3465         449 :                 DIV_ROUND_UP(GetRasterXSize(), panOverviewList[i]) == 1 &&
    3466          21 :                 DIV_ROUND_UP(GetRasterYSize(), panOverviewList[i]) == 1)
    3467             :             {
    3468          21 :                 abRequireNewOverview[i] = false;
    3469          21 :                 break;
    3470             :             }
    3471             : 
    3472         701 :             if (nOvFactor == panOverviewList[i] ||
    3473         333 :                 nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3474             :                                                 GetRasterXSize(),
    3475             :                                                 GetRasterYSize()))
    3476             :             {
    3477          35 :                 abRequireNewOverview[i] = false;
    3478          35 :                 break;
    3479             :             }
    3480             :         }
    3481             : 
    3482         433 :         if (abRequireNewOverview[i])
    3483             :         {
    3484         377 :             if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    3485           2 :                 !m_bWriteKnownIncompatibleEdition)
    3486             :             {
    3487           2 :                 ReportError(CE_Warning, CPLE_AppDefined,
    3488             :                             "Adding new overviews invalidates the "
    3489             :                             "LAYOUT=IFDS_BEFORE_DATA property");
    3490           2 :                 m_bKnownIncompatibleEdition = true;
    3491           2 :                 m_bWriteKnownIncompatibleEdition = true;
    3492             :             }
    3493             : 
    3494             :             const int nOXSize =
    3495         377 :                 DIV_ROUND_UP(GetRasterXSize(), panOverviewList[i]);
    3496             :             const int nOYSize =
    3497         377 :                 DIV_ROUND_UP(GetRasterYSize(), panOverviewList[i]);
    3498             : 
    3499         754 :             const toff_t nOverviewOffset = GTIFFWriteDirectory(
    3500             :                 m_hTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize,
    3501         377 :                 nOvBitsPerSample, nPlanarConfig, m_nSamplesPerPixel,
    3502             :                 nOvrBlockXSize, nOvrBlockYSize, TRUE, nCompression,
    3503         377 :                 nPhotometric, m_nSampleFormat, nPredictor, panRed, panGreen,
    3504             :                 panBlue, nExtraSamples, panExtraSampleValues, osMetadata,
    3505         377 :                 nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality)
    3506             :                                      : nullptr,
    3507         377 :                 CPLSPrintf("%d", m_nJpegTablesMode),
    3508          25 :                 osNoData.empty() ? nullptr : osNoData.c_str(),
    3509         377 :                 m_anLercAddCompressionAndVersion, false);
    3510             : 
    3511         377 :             if (nOverviewOffset == 0)
    3512           0 :                 eErr = CE_Failure;
    3513             :             else
    3514         377 :                 eErr = RegisterNewOverviewDataset(
    3515             :                     nOverviewOffset, nOvrJpegQuality, papszOptions);
    3516             :         }
    3517             :     }
    3518             : 
    3519         249 :     CPLFree(panExtraSampleValues);
    3520         249 :     panExtraSampleValues = nullptr;
    3521             : 
    3522         249 :     ReloadDirectory();
    3523             : 
    3524             :     /* -------------------------------------------------------------------- */
    3525             :     /*      Create overviews for the mask.                                  */
    3526             :     /* -------------------------------------------------------------------- */
    3527         249 :     if (eErr != CE_None)
    3528           0 :         return eErr;
    3529             : 
    3530         249 :     eErr = CreateInternalMaskOverviews(nOvrBlockXSize, nOvrBlockYSize);
    3531             : 
    3532             :     /* -------------------------------------------------------------------- */
    3533             :     /*      Refresh overviews for the mask                                  */
    3534             :     /* -------------------------------------------------------------------- */
    3535             :     const bool bHasInternalMask =
    3536         249 :         m_poMaskDS != nullptr && m_poMaskDS->GetRasterCount() == 1;
    3537             :     const bool bHasExternalMask =
    3538         249 :         !bHasInternalMask && oOvManager.HaveMaskFile();
    3539         249 :     const bool bHasMask = bHasInternalMask || bHasExternalMask;
    3540             : 
    3541         249 :     if (bHasInternalMask)
    3542             :     {
    3543          23 :         int nMaskOverviews = 0;
    3544             : 
    3545             :         GDALRasterBand **papoOverviewBands = static_cast<GDALRasterBand **>(
    3546          23 :             CPLCalloc(sizeof(void *), m_nOverviewCount));
    3547          62 :         for (int i = 0; i < m_nOverviewCount; ++i)
    3548             :         {
    3549          39 :             if (m_papoOverviewDS[i]->m_poMaskDS != nullptr)
    3550             :             {
    3551          39 :                 papoOverviewBands[nMaskOverviews++] =
    3552          39 :                     m_papoOverviewDS[i]->m_poMaskDS->GetRasterBand(1);
    3553             :             }
    3554             :         }
    3555             : 
    3556          46 :         void *pScaledProgressData = GDALCreateScaledProgress(
    3557          23 :             0, 1.0 / (nBands + 1), pfnProgress, pProgressData);
    3558          46 :         eErr = GDALRegenerateOverviewsEx(
    3559          23 :             m_poMaskDS->GetRasterBand(1), nMaskOverviews,
    3560             :             reinterpret_cast<GDALRasterBandH *>(papoOverviewBands),
    3561             :             pszResampling, GDALScaledProgress, pScaledProgressData,
    3562             :             papszOptions);
    3563          23 :         GDALDestroyScaledProgress(pScaledProgressData);
    3564          23 :         CPLFree(papoOverviewBands);
    3565             :     }
    3566         226 :     else if (bHasExternalMask)
    3567             :     {
    3568           4 :         void *pScaledProgressData = GDALCreateScaledProgress(
    3569           2 :             0, 1.0 / (nBands + 1), pfnProgress, pProgressData);
    3570           2 :         eErr = oOvManager.BuildOverviewsMask(
    3571             :             pszResampling, nOverviews, panOverviewList, GDALScaledProgress,
    3572             :             pScaledProgressData, papszOptions);
    3573           2 :         GDALDestroyScaledProgress(pScaledProgressData);
    3574             :     }
    3575             : 
    3576             :     // If we have an alpha band, we want it to be generated before downsampling
    3577             :     // other bands
    3578         249 :     bool bHasAlphaBand = false;
    3579       66241 :     for (int iBand = 0; iBand < nBands; iBand++)
    3580             :     {
    3581       65992 :         if (papoBands[iBand]->GetColorInterpretation() == GCI_AlphaBand)
    3582          18 :             bHasAlphaBand = true;
    3583             :     }
    3584             : 
    3585             :     /* -------------------------------------------------------------------- */
    3586             :     /*      Refresh old overviews that were listed.                         */
    3587             :     /* -------------------------------------------------------------------- */
    3588         249 :     const auto poColorTable = GetRasterBand(panBandList[0])->GetColorTable();
    3589          20 :     if ((m_nPlanarConfig == PLANARCONFIG_CONTIG || bHasAlphaBand) &&
    3590         231 :         GDALDataTypeIsComplex(
    3591         231 :             GetRasterBand(panBandList[0])->GetRasterDataType()) == FALSE &&
    3592          12 :         (poColorTable == nullptr || STARTS_WITH_CI(pszResampling, "NEAR") ||
    3593         499 :          poColorTable->IsIdentity()) &&
    3594         223 :         (STARTS_WITH_CI(pszResampling, "NEAR") ||
    3595         117 :          EQUAL(pszResampling, "AVERAGE") || EQUAL(pszResampling, "RMS") ||
    3596          47 :          EQUAL(pszResampling, "GAUSS") || EQUAL(pszResampling, "CUBIC") ||
    3597          29 :          EQUAL(pszResampling, "CUBICSPLINE") ||
    3598          28 :          EQUAL(pszResampling, "LANCZOS") || EQUAL(pszResampling, "BILINEAR") ||
    3599          24 :          EQUAL(pszResampling, "MODE")))
    3600             :     {
    3601             :         // In the case of pixel interleaved compressed overviews, we want to
    3602             :         // generate the overviews for all the bands block by block, and not
    3603             :         // band after band, in order to write the block once and not loose
    3604             :         // space in the TIFF file.  We also use that logic for uncompressed
    3605             :         // overviews, since GDALRegenerateOverviewsMultiBand() will be able to
    3606             :         // trigger cascading overview regeneration even in the presence
    3607             :         // of an alpha band.
    3608             : 
    3609         202 :         int nNewOverviews = 0;
    3610             : 
    3611             :         GDALRasterBand ***papapoOverviewBands = static_cast<GDALRasterBand ***>(
    3612         202 :             CPLCalloc(sizeof(void *), nBandsIn));
    3613             :         GDALRasterBand **papoBandList =
    3614         202 :             static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nBandsIn));
    3615       66101 :         for (int iBand = 0; iBand < nBandsIn; ++iBand)
    3616             :         {
    3617       65899 :             GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
    3618             : 
    3619       65899 :             papoBandList[iBand] = poBand;
    3620      131798 :             papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
    3621       65899 :                 CPLCalloc(sizeof(void *), poBand->GetOverviewCount()));
    3622             : 
    3623       65899 :             int iCurOverview = 0;
    3624             :             std::vector<bool> abAlreadyUsedOverviewBand(
    3625       65899 :                 poBand->GetOverviewCount(), false);
    3626             : 
    3627      132084 :             for (int i = 0; i < nOverviews; ++i)
    3628             :             {
    3629       66645 :                 for (int j = 0; j < poBand->GetOverviewCount(); ++j)
    3630             :                 {
    3631       66630 :                     if (abAlreadyUsedOverviewBand[j])
    3632         459 :                         continue;
    3633             : 
    3634             :                     int nOvFactor;
    3635       66171 :                     GDALRasterBand *poOverview = poBand->GetOverview(j);
    3636             : 
    3637       66171 :                     nOvFactor = GDALComputeOvFactor(
    3638             :                         poOverview->GetXSize(), poBand->GetXSize(),
    3639             :                         poOverview->GetYSize(), poBand->GetYSize());
    3640             : 
    3641       66171 :                     GDALCopyNoDataValue(poOverview, poBand);
    3642             : 
    3643       66172 :                     if (nOvFactor == panOverviewList[i] ||
    3644           1 :                         nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3645             :                                                         poBand->GetXSize(),
    3646             :                                                         poBand->GetYSize()))
    3647             :                     {
    3648       66170 :                         if (iBand == 0)
    3649             :                         {
    3650             :                             const auto osNewResampling =
    3651         658 :                                 GDALGetNormalizedOvrResampling(pszResampling);
    3652             :                             const char *pszExistingResampling =
    3653         329 :                                 poOverview->GetMetadataItem("RESAMPLING");
    3654         658 :                             if (pszExistingResampling &&
    3655         329 :                                 pszExistingResampling != osNewResampling)
    3656             :                             {
    3657           2 :                                 poOverview->SetMetadataItem(
    3658           2 :                                     "RESAMPLING", osNewResampling.c_str());
    3659             :                             }
    3660             :                         }
    3661             : 
    3662       66170 :                         abAlreadyUsedOverviewBand[j] = true;
    3663       66170 :                         CPLAssert(iCurOverview < poBand->GetOverviewCount());
    3664       66170 :                         papapoOverviewBands[iBand][iCurOverview] = poOverview;
    3665       66170 :                         ++iCurOverview;
    3666       66170 :                         break;
    3667             :                     }
    3668             :                 }
    3669             :             }
    3670             : 
    3671       65899 :             if (nNewOverviews == 0)
    3672             :             {
    3673         202 :                 nNewOverviews = iCurOverview;
    3674             :             }
    3675       65697 :             else if (nNewOverviews != iCurOverview)
    3676             :             {
    3677           0 :                 CPLAssert(false);
    3678             :                 return CE_Failure;
    3679             :             }
    3680             :         }
    3681             : 
    3682             :         void *pScaledProgressData =
    3683         202 :             bHasMask ? GDALCreateScaledProgress(1.0 / (nBands + 1), 1.0,
    3684             :                                                 pfnProgress, pProgressData)
    3685         177 :                      : GDALCreateScaledProgress(0.0, 1.0, pfnProgress,
    3686         202 :                                                 pProgressData);
    3687         202 :         GDALRegenerateOverviewsMultiBand(nBandsIn, papoBandList, nNewOverviews,
    3688             :                                          papapoOverviewBands, pszResampling,
    3689             :                                          GDALScaledProgress,
    3690             :                                          pScaledProgressData, papszOptions);
    3691         202 :         GDALDestroyScaledProgress(pScaledProgressData);
    3692             : 
    3693       66101 :         for (int iBand = 0; iBand < nBandsIn; ++iBand)
    3694             :         {
    3695       65899 :             CPLFree(papapoOverviewBands[iBand]);
    3696             :         }
    3697         202 :         CPLFree(papapoOverviewBands);
    3698         202 :         CPLFree(papoBandList);
    3699             :     }
    3700             :     else
    3701             :     {
    3702             :         GDALRasterBand **papoOverviewBands = static_cast<GDALRasterBand **>(
    3703          47 :             CPLCalloc(sizeof(void *), nOverviews));
    3704             : 
    3705          47 :         const int iBandOffset = bHasMask ? 1 : 0;
    3706             : 
    3707         140 :         for (int iBand = 0; iBand < nBandsIn && eErr == CE_None; ++iBand)
    3708             :         {
    3709          93 :             GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
    3710          93 :             if (poBand == nullptr)
    3711             :             {
    3712           0 :                 eErr = CE_Failure;
    3713           0 :                 break;
    3714             :             }
    3715             : 
    3716             :             std::vector<bool> abAlreadyUsedOverviewBand(
    3717         186 :                 poBand->GetOverviewCount(), false);
    3718             : 
    3719          93 :             int nNewOverviews = 0;
    3720         282 :             for (int i = 0; i < nOverviews; ++i)
    3721             :             {
    3722         447 :                 for (int j = 0; j < poBand->GetOverviewCount(); ++j)
    3723             :                 {
    3724         429 :                     if (abAlreadyUsedOverviewBand[j])
    3725         257 :                         continue;
    3726             : 
    3727         172 :                     GDALRasterBand *poOverview = poBand->GetOverview(j);
    3728             : 
    3729         172 :                     GDALCopyNoDataValue(poOverview, poBand);
    3730             : 
    3731         172 :                     const int nOvFactor = GDALComputeOvFactor(
    3732             :                         poOverview->GetXSize(), poBand->GetXSize(),
    3733             :                         poOverview->GetYSize(), poBand->GetYSize());
    3734             : 
    3735         173 :                     if (nOvFactor == panOverviewList[i] ||
    3736           1 :                         nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3737             :                                                         poBand->GetXSize(),
    3738             :                                                         poBand->GetYSize()))
    3739             :                     {
    3740         171 :                         if (iBand == 0)
    3741             :                         {
    3742             :                             const auto osNewResampling =
    3743         166 :                                 GDALGetNormalizedOvrResampling(pszResampling);
    3744             :                             const char *pszExistingResampling =
    3745          83 :                                 poOverview->GetMetadataItem("RESAMPLING");
    3746         134 :                             if (pszExistingResampling &&
    3747          51 :                                 pszExistingResampling != osNewResampling)
    3748             :                             {
    3749           1 :                                 poOverview->SetMetadataItem(
    3750           1 :                                     "RESAMPLING", osNewResampling.c_str());
    3751             :                             }
    3752             :                         }
    3753             : 
    3754         171 :                         abAlreadyUsedOverviewBand[j] = true;
    3755         171 :                         CPLAssert(nNewOverviews < poBand->GetOverviewCount());
    3756         171 :                         papoOverviewBands[nNewOverviews++] = poOverview;
    3757         171 :                         break;
    3758             :                     }
    3759             :                 }
    3760             :             }
    3761             : 
    3762         186 :             void *pScaledProgressData = GDALCreateScaledProgress(
    3763          93 :                 (iBand + iBandOffset) /
    3764          93 :                     static_cast<double>(nBandsIn + iBandOffset),
    3765          93 :                 (iBand + iBandOffset + 1) /
    3766          93 :                     static_cast<double>(nBandsIn + iBandOffset),
    3767             :                 pfnProgress, pProgressData);
    3768             : 
    3769          93 :             eErr = GDALRegenerateOverviewsEx(
    3770             :                 poBand, nNewOverviews,
    3771             :                 reinterpret_cast<GDALRasterBandH *>(papoOverviewBands),
    3772             :                 pszResampling, GDALScaledProgress, pScaledProgressData,
    3773             :                 papszOptions);
    3774             : 
    3775          93 :             GDALDestroyScaledProgress(pScaledProgressData);
    3776             :         }
    3777             : 
    3778             :         /* --------------------------------------------------------------------
    3779             :          */
    3780             :         /*      Cleanup */
    3781             :         /* --------------------------------------------------------------------
    3782             :          */
    3783          47 :         CPLFree(papoOverviewBands);
    3784             :     }
    3785             : 
    3786         249 :     pfnProgress(1.0, nullptr, pProgressData);
    3787             : 
    3788         249 :     return eErr;
    3789             : }
    3790             : 
    3791             : /************************************************************************/
    3792             : /*                      GTiffWriteDummyGeokeyDirectory()                */
    3793             : /************************************************************************/
    3794             : 
    3795        1401 : static void GTiffWriteDummyGeokeyDirectory(TIFF *hTIFF)
    3796             : {
    3797             :     // If we have existing geokeys, try to wipe them
    3798             :     // by writing a dummy geokey directory. (#2546)
    3799        1401 :     uint16_t *panVI = nullptr;
    3800        1401 :     uint16_t nKeyCount = 0;
    3801             : 
    3802        1401 :     if (TIFFGetField(hTIFF, TIFFTAG_GEOKEYDIRECTORY, &nKeyCount, &panVI))
    3803             :     {
    3804          23 :         GUInt16 anGKVersionInfo[4] = {1, 1, 0, 0};
    3805          23 :         double adfDummyDoubleParams[1] = {0.0};
    3806          23 :         TIFFSetField(hTIFF, TIFFTAG_GEOKEYDIRECTORY, 4, anGKVersionInfo);
    3807          23 :         TIFFSetField(hTIFF, TIFFTAG_GEODOUBLEPARAMS, 1, adfDummyDoubleParams);
    3808          23 :         TIFFSetField(hTIFF, TIFFTAG_GEOASCIIPARAMS, "");
    3809             :     }
    3810        1401 : }
    3811             : 
    3812             : /************************************************************************/
    3813             : /*                    IsSRSCompatibleOfGeoTIFF()                        */
    3814             : /************************************************************************/
    3815             : 
    3816        2986 : static bool IsSRSCompatibleOfGeoTIFF(const OGRSpatialReference *poSRS,
    3817             :                                      GTIFFKeysFlavorEnum eGeoTIFFKeysFlavor)
    3818             : {
    3819        2986 :     char *pszWKT = nullptr;
    3820        2986 :     if ((poSRS->IsGeographic() || poSRS->IsProjected()) && !poSRS->IsCompound())
    3821             :     {
    3822        2968 :         const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
    3823        2968 :         const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
    3824        2968 :         if (pszAuthName && pszAuthCode && EQUAL(pszAuthName, "EPSG"))
    3825        2465 :             return true;
    3826             :     }
    3827             :     OGRErr eErr;
    3828             :     {
    3829        1042 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    3830        1042 :         if (poSRS->IsDerivedGeographic() ||
    3831         521 :             (poSRS->IsProjected() && !poSRS->IsCompound() &&
    3832          70 :              poSRS->GetAxesCount() == 3))
    3833             :         {
    3834           0 :             eErr = OGRERR_FAILURE;
    3835             :         }
    3836             :         else
    3837             :         {
    3838             :             // Geographic3D CRS can't be exported to WKT1, but are
    3839             :             // valid GeoTIFF 1.1
    3840         521 :             const char *const apszOptions[] = {
    3841         521 :                 poSRS->IsGeographic() ? nullptr : "FORMAT=WKT1", nullptr};
    3842         521 :             eErr = poSRS->exportToWkt(&pszWKT, apszOptions);
    3843         521 :             if (eErr == OGRERR_FAILURE && poSRS->IsProjected() &&
    3844             :                 eGeoTIFFKeysFlavor == GEOTIFF_KEYS_ESRI_PE)
    3845             :             {
    3846           0 :                 CPLFree(pszWKT);
    3847           0 :                 const char *const apszOptionsESRIWKT[] = {"FORMAT=WKT1_ESRI",
    3848             :                                                           nullptr};
    3849           0 :                 eErr = poSRS->exportToWkt(&pszWKT, apszOptionsESRIWKT);
    3850             :             }
    3851             :         }
    3852             :     }
    3853         521 :     const bool bCompatibleOfGeoTIFF =
    3854        1041 :         (eErr == OGRERR_NONE && pszWKT != nullptr &&
    3855         520 :          strstr(pszWKT, "custom_proj4") == nullptr);
    3856         521 :     CPLFree(pszWKT);
    3857         521 :     return bCompatibleOfGeoTIFF;
    3858             : }
    3859             : 
    3860             : /************************************************************************/
    3861             : /*                          WriteGeoTIFFInfo()                          */
    3862             : /************************************************************************/
    3863             : 
    3864        5616 : void GTiffDataset::WriteGeoTIFFInfo()
    3865             : 
    3866             : {
    3867        5616 :     bool bPixelIsPoint = false;
    3868        5616 :     bool bPointGeoIgnore = false;
    3869             : 
    3870             :     const char *pszAreaOrPoint =
    3871        5616 :         GTiffDataset::GetMetadataItem(GDALMD_AREA_OR_POINT);
    3872        5616 :     if (pszAreaOrPoint && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT))
    3873             :     {
    3874          17 :         bPixelIsPoint = true;
    3875             :         bPointGeoIgnore =
    3876          17 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    3877             :     }
    3878             : 
    3879        5616 :     if (m_bForceUnsetGTOrGCPs)
    3880             :     {
    3881          11 :         m_bNeedsRewrite = true;
    3882          11 :         m_bForceUnsetGTOrGCPs = false;
    3883             : 
    3884          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE);
    3885          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS);
    3886          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX);
    3887             :     }
    3888             : 
    3889        5616 :     if (m_bForceUnsetProjection)
    3890             :     {
    3891           8 :         m_bNeedsRewrite = true;
    3892           8 :         m_bForceUnsetProjection = false;
    3893             : 
    3894           8 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOKEYDIRECTORY);
    3895           8 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEODOUBLEPARAMS);
    3896           8 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOASCIIPARAMS);
    3897             :     }
    3898             : 
    3899             :     /* -------------------------------------------------------------------- */
    3900             :     /*      Write geotransform if valid.                                    */
    3901             :     /* -------------------------------------------------------------------- */
    3902        5616 :     if (m_bGeoTransformValid)
    3903             :     {
    3904        1689 :         m_bNeedsRewrite = true;
    3905             : 
    3906             :         /* --------------------------------------------------------------------
    3907             :          */
    3908             :         /*      Clear old tags to ensure we don't end up with conflicting */
    3909             :         /*      information. (#2625) */
    3910             :         /* --------------------------------------------------------------------
    3911             :          */
    3912        1689 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE);
    3913        1689 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS);
    3914        1689 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX);
    3915             : 
    3916             :         /* --------------------------------------------------------------------
    3917             :          */
    3918             :         /*      Write the transform.  If we have a normal north-up image we */
    3919             :         /*      use the tiepoint plus pixelscale otherwise we use a matrix. */
    3920             :         /* --------------------------------------------------------------------
    3921             :          */
    3922        1689 :         if (m_gt[2] == 0.0 && m_gt[4] == 0.0 && m_gt[5] < 0.0)
    3923             :         {
    3924        1623 :             double dfOffset = 0.0;
    3925        1623 :             if (m_eProfile != GTiffProfile::BASELINE)
    3926             :             {
    3927             :                 // In the case the SRS has a vertical component and we have
    3928             :                 // a single band, encode its scale/offset in the GeoTIFF tags
    3929        1617 :                 int bHasScale = FALSE;
    3930        1617 :                 double dfScale = GetRasterBand(1)->GetScale(&bHasScale);
    3931        1617 :                 int bHasOffset = FALSE;
    3932        1617 :                 dfOffset = GetRasterBand(1)->GetOffset(&bHasOffset);
    3933             :                 const bool bApplyScaleOffset =
    3934        1617 :                     m_oSRS.IsVertical() && GetRasterCount() == 1;
    3935        1617 :                 if (bApplyScaleOffset && !bHasScale)
    3936           0 :                     dfScale = 1.0;
    3937        1617 :                 if (!bApplyScaleOffset || !bHasOffset)
    3938        1614 :                     dfOffset = 0.0;
    3939             :                 const double adfPixelScale[3] = {
    3940        1617 :                     m_gt[1], fabs(m_gt[5]), bApplyScaleOffset ? dfScale : 0.0};
    3941        1617 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale);
    3942             :             }
    3943             : 
    3944        1623 :             double adfTiePoints[6] = {0.0,     0.0,     0.0,
    3945        1623 :                                       m_gt[0], m_gt[3], dfOffset};
    3946             : 
    3947        1623 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3948             :             {
    3949          13 :                 adfTiePoints[3] += m_gt[1] * 0.5 + m_gt[2] * 0.5;
    3950          13 :                 adfTiePoints[4] += m_gt[4] * 0.5 + m_gt[5] * 0.5;
    3951             :             }
    3952             : 
    3953        1623 :             if (m_eProfile != GTiffProfile::BASELINE)
    3954        1617 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
    3955             :         }
    3956             :         else
    3957             :         {
    3958          66 :             double adfMatrix[16] = {};
    3959             : 
    3960          66 :             adfMatrix[0] = m_gt[1];
    3961          66 :             adfMatrix[1] = m_gt[2];
    3962          66 :             adfMatrix[3] = m_gt[0];
    3963          66 :             adfMatrix[4] = m_gt[4];
    3964          66 :             adfMatrix[5] = m_gt[5];
    3965          66 :             adfMatrix[7] = m_gt[3];
    3966          66 :             adfMatrix[15] = 1.0;
    3967             : 
    3968          66 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3969             :             {
    3970           0 :                 adfMatrix[3] += m_gt[1] * 0.5 + m_gt[2] * 0.5;
    3971           0 :                 adfMatrix[7] += m_gt[4] * 0.5 + m_gt[5] * 0.5;
    3972             :             }
    3973             : 
    3974          66 :             if (m_eProfile != GTiffProfile::BASELINE)
    3975          66 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
    3976             :         }
    3977             : 
    3978             :         // Do we need a world file?
    3979        1689 :         if (CPLFetchBool(m_papszCreationOptions, "TFW", false))
    3980           7 :             GDALWriteWorldFile(m_pszFilename, "tfw", m_gt.data());
    3981        1682 :         else if (CPLFetchBool(m_papszCreationOptions, "WORLDFILE", false))
    3982           2 :             GDALWriteWorldFile(m_pszFilename, "wld", m_gt.data());
    3983             :     }
    3984        3941 :     else if (GetGCPCount() > 0 && GetGCPCount() <= knMAX_GCP_COUNT &&
    3985          14 :              m_eProfile != GTiffProfile::BASELINE)
    3986             :     {
    3987          14 :         m_bNeedsRewrite = true;
    3988             : 
    3989             :         double *padfTiePoints = static_cast<double *>(
    3990          14 :             CPLMalloc(6 * sizeof(double) * GetGCPCount()));
    3991             : 
    3992          74 :         for (size_t iGCP = 0; iGCP < m_aoGCPs.size(); ++iGCP)
    3993             :         {
    3994             : 
    3995          60 :             padfTiePoints[iGCP * 6 + 0] = m_aoGCPs[iGCP].Pixel();
    3996          60 :             padfTiePoints[iGCP * 6 + 1] = m_aoGCPs[iGCP].Line();
    3997          60 :             padfTiePoints[iGCP * 6 + 2] = 0;
    3998          60 :             padfTiePoints[iGCP * 6 + 3] = m_aoGCPs[iGCP].X();
    3999          60 :             padfTiePoints[iGCP * 6 + 4] = m_aoGCPs[iGCP].Y();
    4000          60 :             padfTiePoints[iGCP * 6 + 5] = m_aoGCPs[iGCP].Z();
    4001             : 
    4002          60 :             if (bPixelIsPoint && !bPointGeoIgnore)
    4003             :             {
    4004           0 :                 padfTiePoints[iGCP * 6 + 0] += 0.5;
    4005           0 :                 padfTiePoints[iGCP * 6 + 1] += 0.5;
    4006             :             }
    4007             :         }
    4008             : 
    4009          14 :         TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * GetGCPCount(),
    4010             :                      padfTiePoints);
    4011          14 :         CPLFree(padfTiePoints);
    4012             :     }
    4013             : 
    4014             :     /* -------------------------------------------------------------------- */
    4015             :     /*      Write out projection definition.                                */
    4016             :     /* -------------------------------------------------------------------- */
    4017        5616 :     const bool bHasProjection = !m_oSRS.IsEmpty();
    4018        5616 :     if ((bHasProjection || bPixelIsPoint) &&
    4019        1405 :         m_eProfile != GTiffProfile::BASELINE)
    4020             :     {
    4021        1401 :         m_bNeedsRewrite = true;
    4022             : 
    4023             :         // If we have existing geokeys, try to wipe them
    4024             :         // by writing a dummy geokey directory. (#2546)
    4025        1401 :         GTiffWriteDummyGeokeyDirectory(m_hTIFF);
    4026             : 
    4027        1401 :         GTIF *psGTIF = GTiffDataset::GTIFNew(m_hTIFF);
    4028             : 
    4029             :         // Set according to coordinate system.
    4030        1401 :         if (bHasProjection)
    4031             :         {
    4032        1400 :             if (IsSRSCompatibleOfGeoTIFF(&m_oSRS, m_eGeoTIFFKeysFlavor))
    4033             :             {
    4034        1398 :                 GTIFSetFromOGISDefnEx(psGTIF,
    4035             :                                       OGRSpatialReference::ToHandle(&m_oSRS),
    4036             :                                       m_eGeoTIFFKeysFlavor, m_eGeoTIFFVersion);
    4037             :             }
    4038             :             else
    4039             :             {
    4040           2 :                 GDALPamDataset::SetSpatialRef(&m_oSRS);
    4041             :             }
    4042             :         }
    4043             : 
    4044        1401 :         if (bPixelIsPoint)
    4045             :         {
    4046          17 :             GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
    4047             :                        RasterPixelIsPoint);
    4048             :         }
    4049             : 
    4050        1401 :         GTIFWriteKeys(psGTIF);
    4051        1401 :         GTIFFree(psGTIF);
    4052             :     }
    4053        5616 : }
    4054             : 
    4055             : /************************************************************************/
    4056             : /*                         AppendMetadataItem()                         */
    4057             : /************************************************************************/
    4058             : 
    4059        3516 : static void AppendMetadataItem(CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
    4060             :                                const char *pszKey, const char *pszValue,
    4061             :                                int nBand, const char *pszRole,
    4062             :                                const char *pszDomain)
    4063             : 
    4064             : {
    4065             :     /* -------------------------------------------------------------------- */
    4066             :     /*      Create the Item element, and subcomponents.                     */
    4067             :     /* -------------------------------------------------------------------- */
    4068        3516 :     CPLXMLNode *psItem = CPLCreateXMLNode(nullptr, CXT_Element, "Item");
    4069        3516 :     CPLCreateXMLNode(CPLCreateXMLNode(psItem, CXT_Attribute, "name"), CXT_Text,
    4070             :                      pszKey);
    4071             : 
    4072        3516 :     if (nBand > 0)
    4073             :     {
    4074         844 :         char szBandId[32] = {};
    4075         844 :         snprintf(szBandId, sizeof(szBandId), "%d", nBand - 1);
    4076         844 :         CPLCreateXMLNode(CPLCreateXMLNode(psItem, CXT_Attribute, "sample"),
    4077             :                          CXT_Text, szBandId);
    4078             :     }
    4079             : 
    4080        3516 :     if (pszRole != nullptr)
    4081         348 :         CPLCreateXMLNode(CPLCreateXMLNode(psItem, CXT_Attribute, "role"),
    4082             :                          CXT_Text, pszRole);
    4083             : 
    4084        3516 :     if (pszDomain != nullptr && strlen(pszDomain) > 0)
    4085        1011 :         CPLCreateXMLNode(CPLCreateXMLNode(psItem, CXT_Attribute, "domain"),
    4086             :                          CXT_Text, pszDomain);
    4087             : 
    4088             :     // Note: this escaping should not normally be done, as the serialization
    4089             :     // of the tree to XML also does it, so we end up width double XML escaping,
    4090             :     // but keep it for backward compatibility.
    4091        3516 :     char *pszEscapedItemValue = CPLEscapeString(pszValue, -1, CPLES_XML);
    4092        3516 :     CPLCreateXMLNode(psItem, CXT_Text, pszEscapedItemValue);
    4093        3516 :     CPLFree(pszEscapedItemValue);
    4094             : 
    4095             :     /* -------------------------------------------------------------------- */
    4096             :     /*      Create root, if missing.                                        */
    4097             :     /* -------------------------------------------------------------------- */
    4098        3516 :     if (*ppsRoot == nullptr)
    4099         657 :         *ppsRoot = CPLCreateXMLNode(nullptr, CXT_Element, "GDALMetadata");
    4100             : 
    4101             :     /* -------------------------------------------------------------------- */
    4102             :     /*      Append item to tail.  We keep track of the tail to avoid        */
    4103             :     /*      O(nsquared) time as the list gets longer.                       */
    4104             :     /* -------------------------------------------------------------------- */
    4105        3516 :     if (*ppsTail == nullptr)
    4106         657 :         CPLAddXMLChild(*ppsRoot, psItem);
    4107             :     else
    4108        2859 :         CPLAddXMLSibling(*ppsTail, psItem);
    4109             : 
    4110        3516 :     *ppsTail = psItem;
    4111        3516 : }
    4112             : 
    4113             : /************************************************************************/
    4114             : /*                         WriteMDMetadata()                            */
    4115             : /************************************************************************/
    4116             : 
    4117      310237 : static void WriteMDMetadata(GDALMultiDomainMetadata *poMDMD, TIFF *hTIFF,
    4118             :                             CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
    4119             :                             int nBand, GTiffProfile eProfile)
    4120             : 
    4121             : {
    4122             : 
    4123             :     /* ==================================================================== */
    4124             :     /*      Process each domain.                                            */
    4125             :     /* ==================================================================== */
    4126      310237 :     CSLConstList papszDomainList = poMDMD->GetDomainList();
    4127      318265 :     for (int iDomain = 0; papszDomainList && papszDomainList[iDomain];
    4128             :          ++iDomain)
    4129             :     {
    4130        8028 :         CSLConstList papszMD = poMDMD->GetMetadata(papszDomainList[iDomain]);
    4131        8028 :         bool bIsXMLOrJSON = false;
    4132             : 
    4133        8028 :         if (EQUAL(papszDomainList[iDomain], "IMAGE_STRUCTURE") ||
    4134        2333 :             EQUAL(papszDomainList[iDomain], "DERIVED_SUBDATASETS"))
    4135        5698 :             continue;  // Ignored.
    4136        2330 :         if (EQUAL(papszDomainList[iDomain], "COLOR_PROFILE"))
    4137           3 :             continue;  // Handled elsewhere.
    4138        2327 :         if (EQUAL(papszDomainList[iDomain], MD_DOMAIN_RPC))
    4139           7 :             continue;  // Handled elsewhere.
    4140        2321 :         if (EQUAL(papszDomainList[iDomain], "xml:ESRI") &&
    4141           1 :             CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
    4142           1 :             continue;  // Handled elsewhere.
    4143        2319 :         if (EQUAL(papszDomainList[iDomain], "xml:XMP"))
    4144           2 :             continue;  // Handled in SetMetadata.
    4145             : 
    4146        2317 :         if (STARTS_WITH_CI(papszDomainList[iDomain], "xml:") ||
    4147        2315 :             STARTS_WITH_CI(papszDomainList[iDomain], "json:"))
    4148             :         {
    4149          11 :             bIsXMLOrJSON = true;
    4150             :         }
    4151             : 
    4152             :         /* --------------------------------------------------------------------
    4153             :          */
    4154             :         /*      Process each item in this domain. */
    4155             :         /* --------------------------------------------------------------------
    4156             :          */
    4157        7009 :         for (int iItem = 0; papszMD && papszMD[iItem]; ++iItem)
    4158             :         {
    4159        4692 :             const char *pszItemValue = nullptr;
    4160        4692 :             char *pszItemName = nullptr;
    4161             : 
    4162        4692 :             if (bIsXMLOrJSON)
    4163             :             {
    4164          10 :                 pszItemName = CPLStrdup("doc");
    4165          10 :                 pszItemValue = papszMD[iItem];
    4166             :             }
    4167             :             else
    4168             :             {
    4169        4682 :                 pszItemValue = CPLParseNameValue(papszMD[iItem], &pszItemName);
    4170        4682 :                 if (pszItemName == nullptr)
    4171             :                 {
    4172          49 :                     CPLDebug("GTiff", "Invalid metadata item : %s",
    4173          49 :                              papszMD[iItem]);
    4174          49 :                     continue;
    4175             :                 }
    4176             :             }
    4177             : 
    4178             :             /* --------------------------------------------------------------------
    4179             :              */
    4180             :             /*      Convert into XML item or handle as a special TIFF tag. */
    4181             :             /* --------------------------------------------------------------------
    4182             :              */
    4183        4643 :             if (strlen(papszDomainList[iDomain]) == 0 && nBand == 0 &&
    4184        3575 :                 (STARTS_WITH_CI(pszItemName, "TIFFTAG_") ||
    4185        3514 :                  (EQUAL(pszItemName, "GEO_METADATA") &&
    4186        3513 :                   eProfile == GTiffProfile::GDALGEOTIFF) ||
    4187        3513 :                  (EQUAL(pszItemName, "TIFF_RSID") &&
    4188             :                   eProfile == GTiffProfile::GDALGEOTIFF)))
    4189             :             {
    4190          63 :                 if (EQUAL(pszItemName, "TIFFTAG_RESOLUTIONUNIT"))
    4191             :                 {
    4192             :                     // ResolutionUnit can't be 0, which is the default if
    4193             :                     // atoi() fails.  Set to 1=Unknown.
    4194           9 :                     int v = atoi(pszItemValue);
    4195           9 :                     if (!v)
    4196           1 :                         v = RESUNIT_NONE;
    4197           9 :                     TIFFSetField(hTIFF, TIFFTAG_RESOLUTIONUNIT, v);
    4198             :                 }
    4199             :                 else
    4200             :                 {
    4201          54 :                     bool bFoundTag = false;
    4202          54 :                     size_t iTag = 0;  // Used after for.
    4203          54 :                     const auto *pasTIFFTags = GTiffDataset::GetTIFFTags();
    4204         286 :                     for (; pasTIFFTags[iTag].pszTagName; ++iTag)
    4205             :                     {
    4206         286 :                         if (EQUAL(pszItemName, pasTIFFTags[iTag].pszTagName))
    4207             :                         {
    4208          54 :                             bFoundTag = true;
    4209          54 :                             break;
    4210             :                         }
    4211             :                     }
    4212             : 
    4213          54 :                     if (bFoundTag &&
    4214          54 :                         pasTIFFTags[iTag].eType == GTIFFTAGTYPE_STRING)
    4215          33 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4216             :                                      pszItemValue);
    4217          21 :                     else if (bFoundTag &&
    4218          21 :                              pasTIFFTags[iTag].eType == GTIFFTAGTYPE_FLOAT)
    4219          16 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4220             :                                      CPLAtof(pszItemValue));
    4221           5 :                     else if (bFoundTag &&
    4222           5 :                              pasTIFFTags[iTag].eType == GTIFFTAGTYPE_SHORT)
    4223           4 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4224             :                                      atoi(pszItemValue));
    4225           1 :                     else if (bFoundTag && pasTIFFTags[iTag].eType ==
    4226             :                                               GTIFFTAGTYPE_BYTE_STRING)
    4227             :                     {
    4228           1 :                         uint32_t nLen =
    4229           1 :                             static_cast<uint32_t>(strlen(pszItemValue));
    4230           1 :                         if (nLen)
    4231             :                         {
    4232           1 :                             TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal, nLen,
    4233             :                                          pszItemValue);
    4234           1 :                         }
    4235             :                     }
    4236             :                     else
    4237           0 :                         CPLError(CE_Warning, CPLE_NotSupported,
    4238             :                                  "%s metadata item is unhandled and "
    4239             :                                  "will not be written",
    4240             :                                  pszItemName);
    4241          63 :                 }
    4242             :             }
    4243        4580 :             else if (nBand == 0 && EQUAL(pszItemName, GDALMD_AREA_OR_POINT))
    4244             :             {
    4245             :                 /* Do nothing, handled elsewhere. */;
    4246             :             }
    4247             :             else
    4248             :             {
    4249        2733 :                 AppendMetadataItem(ppsRoot, ppsTail, pszItemName, pszItemValue,
    4250        2733 :                                    nBand, nullptr, papszDomainList[iDomain]);
    4251             :             }
    4252             : 
    4253        4643 :             CPLFree(pszItemName);
    4254             :         }
    4255             : 
    4256             :         /* --------------------------------------------------------------------
    4257             :          */
    4258             :         /*      Remove TIFFTAG_xxxxxx that are already set but no longer in */
    4259             :         /*      the metadata list (#5619) */
    4260             :         /* --------------------------------------------------------------------
    4261             :          */
    4262        2317 :         if (strlen(papszDomainList[iDomain]) == 0 && nBand == 0)
    4263             :         {
    4264        2111 :             const auto *pasTIFFTags = GTiffDataset::GetTIFFTags();
    4265       31665 :             for (size_t iTag = 0; pasTIFFTags[iTag].pszTagName; ++iTag)
    4266             :             {
    4267       29554 :                 uint32_t nCount = 0;
    4268       29554 :                 char *pszText = nullptr;
    4269       29554 :                 int16_t nVal = 0;
    4270       29554 :                 float fVal = 0.0f;
    4271             :                 const char *pszVal =
    4272       29554 :                     CSLFetchNameValue(papszMD, pasTIFFTags[iTag].pszTagName);
    4273       59045 :                 if (pszVal == nullptr &&
    4274       29491 :                     ((pasTIFFTags[iTag].eType == GTIFFTAGTYPE_STRING &&
    4275       16855 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4276       29483 :                                    &pszText)) ||
    4277       29483 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_SHORT &&
    4278        6320 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &nVal)) ||
    4279       29480 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_FLOAT &&
    4280        4206 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &fVal)) ||
    4281       29479 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_BYTE_STRING &&
    4282        2110 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &nCount,
    4283             :                                    &pszText))))
    4284             :                 {
    4285          13 :                     TIFFUnsetField(hTIFF, pasTIFFTags[iTag].nTagVal);
    4286             :                 }
    4287             :             }
    4288             :         }
    4289             :     }
    4290      310237 : }
    4291             : 
    4292             : /************************************************************************/
    4293             : /*                           WriteRPC()                                 */
    4294             : /************************************************************************/
    4295             : 
    4296        9686 : void GTiffDataset::WriteRPC(GDALDataset *poSrcDS, TIFF *l_hTIFF,
    4297             :                             int bSrcIsGeoTIFF, GTiffProfile eProfile,
    4298             :                             const char *pszTIFFFilename,
    4299             :                             CSLConstList papszCreationOptions,
    4300             :                             bool bWriteOnlyInPAMIfNeeded)
    4301             : {
    4302             :     /* -------------------------------------------------------------------- */
    4303             :     /*      Handle RPC data written to TIFF RPCCoefficient tag, RPB file,   */
    4304             :     /*      RPCTEXT file or PAM.                                            */
    4305             :     /* -------------------------------------------------------------------- */
    4306        9686 :     char **papszRPCMD = poSrcDS->GetMetadata(MD_DOMAIN_RPC);
    4307        9686 :     if (papszRPCMD != nullptr)
    4308             :     {
    4309          32 :         bool bRPCSerializedOtherWay = false;
    4310             : 
    4311          32 :         if (eProfile == GTiffProfile::GDALGEOTIFF)
    4312             :         {
    4313          20 :             if (!bWriteOnlyInPAMIfNeeded)
    4314          11 :                 GTiffDatasetWriteRPCTag(l_hTIFF, papszRPCMD);
    4315          20 :             bRPCSerializedOtherWay = true;
    4316             :         }
    4317             : 
    4318             :         // Write RPB file if explicitly asked, or if a non GDAL specific
    4319             :         // profile is selected and RPCTXT is not asked.
    4320             :         bool bRPBExplicitlyAsked =
    4321          32 :             CPLFetchBool(papszCreationOptions, "RPB", false);
    4322             :         bool bRPBExplicitlyDenied =
    4323          32 :             !CPLFetchBool(papszCreationOptions, "RPB", true);
    4324          44 :         if ((eProfile != GTiffProfile::GDALGEOTIFF &&
    4325          12 :              !CPLFetchBool(papszCreationOptions, "RPCTXT", false) &&
    4326          44 :              !bRPBExplicitlyDenied) ||
    4327             :             bRPBExplicitlyAsked)
    4328             :         {
    4329           8 :             if (!bWriteOnlyInPAMIfNeeded)
    4330           4 :                 GDALWriteRPBFile(pszTIFFFilename, papszRPCMD);
    4331           8 :             bRPCSerializedOtherWay = true;
    4332             :         }
    4333             : 
    4334          32 :         if (CPLFetchBool(papszCreationOptions, "RPCTXT", false))
    4335             :         {
    4336           2 :             if (!bWriteOnlyInPAMIfNeeded)
    4337           1 :                 GDALWriteRPCTXTFile(pszTIFFFilename, papszRPCMD);
    4338           2 :             bRPCSerializedOtherWay = true;
    4339             :         }
    4340             : 
    4341          32 :         if (!bRPCSerializedOtherWay && bWriteOnlyInPAMIfNeeded && bSrcIsGeoTIFF)
    4342           1 :             cpl::down_cast<GTiffDataset *>(poSrcDS)
    4343           1 :                 ->GDALPamDataset::SetMetadata(papszRPCMD, MD_DOMAIN_RPC);
    4344             :     }
    4345        9686 : }
    4346             : 
    4347             : /************************************************************************/
    4348             : /*                           WriteMetadata()                            */
    4349             : /************************************************************************/
    4350             : 
    4351        7665 : bool GTiffDataset::WriteMetadata(GDALDataset *poSrcDS, TIFF *l_hTIFF,
    4352             :                                  bool bSrcIsGeoTIFF, GTiffProfile eProfile,
    4353             :                                  const char *pszTIFFFilename,
    4354             :                                  CSLConstList papszCreationOptions,
    4355             :                                  bool bExcludeRPBandIMGFileWriting)
    4356             : 
    4357             : {
    4358             :     /* -------------------------------------------------------------------- */
    4359             :     /*      Convert all the remaining metadata into a simple XML            */
    4360             :     /*      format.                                                         */
    4361             :     /* -------------------------------------------------------------------- */
    4362        7665 :     CPLXMLNode *psRoot = nullptr;
    4363        7665 :     CPLXMLNode *psTail = nullptr;
    4364             : 
    4365             :     const char *pszCopySrcMDD =
    4366        7665 :         CSLFetchNameValueDef(papszCreationOptions, "COPY_SRC_MDD", "AUTO");
    4367             :     char **papszSrcMDD =
    4368        7665 :         CSLFetchNameValueMultiple(papszCreationOptions, "SRC_MDD");
    4369             : 
    4370        7665 :     if (bSrcIsGeoTIFF)
    4371             :     {
    4372        5622 :         GTiffDataset *poSrcDSGTiff = cpl::down_cast<GTiffDataset *>(poSrcDS);
    4373        5622 :         assert(poSrcDSGTiff);
    4374        5622 :         WriteMDMetadata(&poSrcDSGTiff->m_oGTiffMDMD, l_hTIFF, &psRoot, &psTail,
    4375             :                         0, eProfile);
    4376             :     }
    4377             :     else
    4378             :     {
    4379        2043 :         if (EQUAL(pszCopySrcMDD, "AUTO") || CPLTestBool(pszCopySrcMDD) ||
    4380             :             papszSrcMDD)
    4381             :         {
    4382        4080 :             GDALMultiDomainMetadata l_oMDMD;
    4383             :             {
    4384        2040 :                 CSLConstList papszMD = poSrcDS->GetMetadata();
    4385        2044 :                 if (CSLCount(papszMD) > 0 &&
    4386           4 :                     (!papszSrcMDD || CSLFindString(papszSrcMDD, "") >= 0 ||
    4387           2 :                      CSLFindString(papszSrcMDD, "_DEFAULT_") >= 0))
    4388             :                 {
    4389        1568 :                     l_oMDMD.SetMetadata(papszMD);
    4390             :                 }
    4391             :             }
    4392             : 
    4393        2040 :             if (EQUAL(pszCopySrcMDD, "AUTO") && !papszSrcMDD)
    4394             :             {
    4395             :                 // Propagate ISIS3 or VICAR metadata
    4396        6093 :                 for (const char *pszMDD : {"json:ISIS3", "json:VICAR"})
    4397             :                 {
    4398        4062 :                     char **papszMD = poSrcDS->GetMetadata(pszMDD);
    4399        4062 :                     if (papszMD)
    4400             :                     {
    4401           5 :                         l_oMDMD.SetMetadata(papszMD, pszMDD);
    4402             :                     }
    4403             :                 }
    4404             :             }
    4405             : 
    4406        2040 :             if ((!EQUAL(pszCopySrcMDD, "AUTO") && CPLTestBool(pszCopySrcMDD)) ||
    4407             :                 papszSrcMDD)
    4408             :             {
    4409           9 :                 char **papszDomainList = poSrcDS->GetMetadataDomainList();
    4410          39 :                 for (CSLConstList papszIter = papszDomainList;
    4411          39 :                      papszIter && *papszIter; ++papszIter)
    4412             :                 {
    4413          30 :                     const char *pszDomain = *papszIter;
    4414          46 :                     if (pszDomain[0] != 0 &&
    4415          16 :                         (!papszSrcMDD ||
    4416          16 :                          CSLFindString(papszSrcMDD, pszDomain) >= 0))
    4417             :                     {
    4418          12 :                         l_oMDMD.SetMetadata(poSrcDS->GetMetadata(pszDomain),
    4419             :                                             pszDomain);
    4420             :                     }
    4421             :                 }
    4422           9 :                 CSLDestroy(papszDomainList);
    4423             :             }
    4424             : 
    4425        2040 :             WriteMDMetadata(&l_oMDMD, l_hTIFF, &psRoot, &psTail, 0, eProfile);
    4426             :         }
    4427             :     }
    4428             : 
    4429        7665 :     if (!bExcludeRPBandIMGFileWriting)
    4430             :     {
    4431        7659 :         WriteRPC(poSrcDS, l_hTIFF, bSrcIsGeoTIFF, eProfile, pszTIFFFilename,
    4432             :                  papszCreationOptions);
    4433             : 
    4434             :         /* --------------------------------------------------------------------
    4435             :          */
    4436             :         /*      Handle metadata data written to an IMD file. */
    4437             :         /* --------------------------------------------------------------------
    4438             :          */
    4439        7659 :         char **papszIMDMD = poSrcDS->GetMetadata(MD_DOMAIN_IMD);
    4440        7659 :         if (papszIMDMD != nullptr)
    4441             :         {
    4442          20 :             GDALWriteIMDFile(pszTIFFFilename, papszIMDMD);
    4443             :         }
    4444             :     }
    4445             : 
    4446        7665 :     uint16_t nPhotometric = 0;
    4447        7665 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric)))
    4448           1 :         nPhotometric = PHOTOMETRIC_MINISBLACK;
    4449             : 
    4450        7665 :     const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
    4451             :         GDALDataset::ToHandle(poSrcDS), nPhotometric, papszCreationOptions);
    4452             : 
    4453             :     /* -------------------------------------------------------------------- */
    4454             :     /*      We also need to address band specific metadata, and special     */
    4455             :     /*      "role" metadata.                                                */
    4456             :     /* -------------------------------------------------------------------- */
    4457      315048 :     for (int nBand = 1; nBand <= poSrcDS->GetRasterCount(); ++nBand)
    4458             :     {
    4459      307383 :         GDALRasterBand *poBand = poSrcDS->GetRasterBand(nBand);
    4460             : 
    4461      307383 :         if (bSrcIsGeoTIFF)
    4462             :         {
    4463             :             GTiffRasterBand *poSrcBandGTiff =
    4464      302484 :                 cpl::down_cast<GTiffRasterBand *>(poBand);
    4465      302484 :             assert(poSrcBandGTiff);
    4466      302484 :             WriteMDMetadata(&poSrcBandGTiff->m_oGTiffMDMD, l_hTIFF, &psRoot,
    4467             :                             &psTail, nBand, eProfile);
    4468             :         }
    4469             :         else
    4470             :         {
    4471        9798 :             GDALMultiDomainMetadata l_oMDMD;
    4472        4899 :             bool bOMDMDSet = false;
    4473             : 
    4474        4899 :             if (EQUAL(pszCopySrcMDD, "AUTO") && !papszSrcMDD)
    4475             :             {
    4476       14661 :                 for (const char *pszDomain : {"", "IMAGERY"})
    4477             :                 {
    4478        9774 :                     if (CSLConstList papszMD = poBand->GetMetadata(pszDomain))
    4479             :                     {
    4480          89 :                         if (papszMD[0])
    4481             :                         {
    4482          89 :                             bOMDMDSet = true;
    4483          89 :                             l_oMDMD.SetMetadata(papszMD, pszDomain);
    4484             :                         }
    4485             :                     }
    4486        4887 :                 }
    4487             :             }
    4488          12 :             else if (CPLTestBool(pszCopySrcMDD) || papszSrcMDD)
    4489             :             {
    4490           9 :                 char **papszDomainList = poBand->GetMetadataDomainList();
    4491           3 :                 for (const char *pszDomain :
    4492          15 :                      cpl::Iterate(CSLConstList(papszDomainList)))
    4493             :                 {
    4494           9 :                     if (pszDomain[0] != 0 &&
    4495           5 :                         !EQUAL(pszDomain, "IMAGE_STRUCTURE") &&
    4496           2 :                         (!papszSrcMDD ||
    4497           2 :                          CSLFindString(papszSrcMDD, pszDomain) >= 0))
    4498             :                     {
    4499           2 :                         bOMDMDSet = true;
    4500           2 :                         l_oMDMD.SetMetadata(poBand->GetMetadata(pszDomain),
    4501             :                                             pszDomain);
    4502             :                     }
    4503             :                 }
    4504           9 :                 CSLDestroy(papszDomainList);
    4505             :             }
    4506             : 
    4507        4899 :             if (bOMDMDSet)
    4508             :             {
    4509          91 :                 WriteMDMetadata(&l_oMDMD, l_hTIFF, &psRoot, &psTail, nBand,
    4510             :                                 eProfile);
    4511             :             }
    4512             :         }
    4513             : 
    4514      307383 :         const double dfOffset = poBand->GetOffset();
    4515      307383 :         const double dfScale = poBand->GetScale();
    4516      307383 :         bool bGeoTIFFScaleOffsetInZ = false;
    4517      307383 :         GDALGeoTransform gt;
    4518             :         // Check if we have already encoded scale/offset in the GeoTIFF tags
    4519      313146 :         if (poSrcDS->GetGeoTransform(gt) == CE_None && gt[2] == 0.0 &&
    4520        5748 :             gt[4] == 0.0 && gt[5] < 0.0 && poSrcDS->GetSpatialRef() &&
    4521      313153 :             poSrcDS->GetSpatialRef()->IsVertical() &&
    4522           7 :             poSrcDS->GetRasterCount() == 1)
    4523             :         {
    4524           7 :             bGeoTIFFScaleOffsetInZ = true;
    4525             :         }
    4526             : 
    4527      307383 :         if ((dfOffset != 0.0 || dfScale != 1.0) && !bGeoTIFFScaleOffsetInZ)
    4528             :         {
    4529          25 :             char szValue[128] = {};
    4530             : 
    4531          25 :             CPLsnprintf(szValue, sizeof(szValue), "%.17g", dfOffset);
    4532          25 :             AppendMetadataItem(&psRoot, &psTail, "OFFSET", szValue, nBand,
    4533             :                                "offset", "");
    4534          25 :             CPLsnprintf(szValue, sizeof(szValue), "%.17g", dfScale);
    4535          25 :             AppendMetadataItem(&psRoot, &psTail, "SCALE", szValue, nBand,
    4536             :                                "scale", "");
    4537             :         }
    4538             : 
    4539      307383 :         const char *pszUnitType = poBand->GetUnitType();
    4540      307383 :         if (pszUnitType != nullptr && pszUnitType[0] != '\0')
    4541             :         {
    4542          37 :             bool bWriteUnit = true;
    4543          37 :             auto poSRS = poSrcDS->GetSpatialRef();
    4544          37 :             if (poSRS && poSRS->IsCompound())
    4545             :             {
    4546           2 :                 const char *pszVertUnit = nullptr;
    4547           2 :                 poSRS->GetTargetLinearUnits("COMPD_CS|VERT_CS", &pszVertUnit);
    4548           2 :                 if (pszVertUnit && EQUAL(pszVertUnit, pszUnitType))
    4549             :                 {
    4550           2 :                     bWriteUnit = false;
    4551             :                 }
    4552             :             }
    4553          37 :             if (bWriteUnit)
    4554             :             {
    4555          35 :                 AppendMetadataItem(&psRoot, &psTail, "UNITTYPE", pszUnitType,
    4556             :                                    nBand, "unittype", "");
    4557             :             }
    4558             :         }
    4559             : 
    4560      307383 :         if (strlen(poBand->GetDescription()) > 0)
    4561             :         {
    4562          15 :             AppendMetadataItem(&psRoot, &psTail, "DESCRIPTION",
    4563          15 :                                poBand->GetDescription(), nBand, "description",
    4564             :                                "");
    4565             :         }
    4566             : 
    4567      307598 :         if (!bStandardColorInterp &&
    4568         215 :             !(nBand <= 3 && EQUAL(CSLFetchNameValueDef(papszCreationOptions,
    4569             :                                                        "PHOTOMETRIC", ""),
    4570             :                                   "RGB")))
    4571             :         {
    4572         248 :             AppendMetadataItem(&psRoot, &psTail, "COLORINTERP",
    4573             :                                GDALGetColorInterpretationName(
    4574         248 :                                    poBand->GetColorInterpretation()),
    4575             :                                nBand, "colorinterp", "");
    4576             :         }
    4577             :     }
    4578             : 
    4579        7665 :     CSLDestroy(papszSrcMDD);
    4580             : 
    4581             :     const char *pszTilingSchemeName =
    4582        7665 :         CSLFetchNameValue(papszCreationOptions, "@TILING_SCHEME_NAME");
    4583        7665 :     if (pszTilingSchemeName)
    4584             :     {
    4585          23 :         AppendMetadataItem(&psRoot, &psTail, "NAME", pszTilingSchemeName, 0,
    4586             :                            nullptr, "TILING_SCHEME");
    4587             : 
    4588          23 :         const char *pszZoomLevel = CSLFetchNameValue(
    4589             :             papszCreationOptions, "@TILING_SCHEME_ZOOM_LEVEL");
    4590          23 :         if (pszZoomLevel)
    4591             :         {
    4592          23 :             AppendMetadataItem(&psRoot, &psTail, "ZOOM_LEVEL", pszZoomLevel, 0,
    4593             :                                nullptr, "TILING_SCHEME");
    4594             :         }
    4595             : 
    4596          23 :         const char *pszAlignedLevels = CSLFetchNameValue(
    4597             :             papszCreationOptions, "@TILING_SCHEME_ALIGNED_LEVELS");
    4598          23 :         if (pszAlignedLevels)
    4599             :         {
    4600           4 :             AppendMetadataItem(&psRoot, &psTail, "ALIGNED_LEVELS",
    4601             :                                pszAlignedLevels, 0, nullptr, "TILING_SCHEME");
    4602             :         }
    4603             :     }
    4604             : 
    4605        7665 :     if (const char *pszOverviewResampling =
    4606        7665 :             CSLFetchNameValue(papszCreationOptions, "@OVERVIEW_RESAMPLING"))
    4607             :     {
    4608          43 :         AppendMetadataItem(&psRoot, &psTail, "OVERVIEW_RESAMPLING",
    4609             :                            pszOverviewResampling, 0, nullptr,
    4610             :                            "IMAGE_STRUCTURE");
    4611             :     }
    4612             : 
    4613             :     /* -------------------------------------------------------------------- */
    4614             :     /*      Write information about some codecs.                            */
    4615             :     /* -------------------------------------------------------------------- */
    4616        7665 :     if (CPLTestBool(
    4617             :             CPLGetConfigOption("GTIFF_WRITE_IMAGE_STRUCTURE_METADATA", "YES")))
    4618             :     {
    4619             :         const char *pszTileInterleave =
    4620        7660 :             CSLFetchNameValue(papszCreationOptions, "@TILE_INTERLEAVE");
    4621        7660 :         if (pszTileInterleave && CPLTestBool(pszTileInterleave))
    4622             :         {
    4623           7 :             AppendMetadataItem(&psRoot, &psTail, "INTERLEAVE", "TILE", 0,
    4624             :                                nullptr, "IMAGE_STRUCTURE");
    4625             :         }
    4626             : 
    4627             :         const char *pszCompress =
    4628        7660 :             CSLFetchNameValue(papszCreationOptions, "COMPRESS");
    4629        7660 :         if (pszCompress && EQUAL(pszCompress, "WEBP"))
    4630             :         {
    4631          31 :             if (GTiffGetWebPLossless(papszCreationOptions))
    4632             :             {
    4633           6 :                 AppendMetadataItem(&psRoot, &psTail,
    4634             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4635             :                                    nullptr, "IMAGE_STRUCTURE");
    4636             :             }
    4637             :             else
    4638             :             {
    4639          25 :                 AppendMetadataItem(
    4640             :                     &psRoot, &psTail, "WEBP_LEVEL",
    4641          25 :                     CPLSPrintf("%d", GTiffGetWebPLevel(papszCreationOptions)),
    4642             :                     0, nullptr, "IMAGE_STRUCTURE");
    4643             :             }
    4644             :         }
    4645        7629 :         else if (pszCompress && STARTS_WITH_CI(pszCompress, "LERC"))
    4646             :         {
    4647             :             const double dfMaxZError =
    4648          97 :                 GTiffGetLERCMaxZError(papszCreationOptions);
    4649             :             const double dfMaxZErrorOverview =
    4650          97 :                 GTiffGetLERCMaxZErrorOverview(papszCreationOptions);
    4651          97 :             if (dfMaxZError == 0.0 && dfMaxZErrorOverview == 0.0)
    4652             :             {
    4653          83 :                 AppendMetadataItem(&psRoot, &psTail,
    4654             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4655             :                                    nullptr, "IMAGE_STRUCTURE");
    4656             :             }
    4657             :             else
    4658             :             {
    4659          14 :                 AppendMetadataItem(&psRoot, &psTail, "MAX_Z_ERROR",
    4660             :                                    CSLFetchNameValueDef(papszCreationOptions,
    4661             :                                                         "MAX_Z_ERROR", ""),
    4662             :                                    0, nullptr, "IMAGE_STRUCTURE");
    4663          14 :                 if (dfMaxZError != dfMaxZErrorOverview)
    4664             :                 {
    4665           3 :                     AppendMetadataItem(
    4666             :                         &psRoot, &psTail, "MAX_Z_ERROR_OVERVIEW",
    4667             :                         CSLFetchNameValueDef(papszCreationOptions,
    4668             :                                              "MAX_Z_ERROR_OVERVIEW", ""),
    4669             :                         0, nullptr, "IMAGE_STRUCTURE");
    4670             :                 }
    4671          97 :             }
    4672             :         }
    4673             : #if HAVE_JXL
    4674        7532 :         else if (pszCompress && EQUAL(pszCompress, "JXL"))
    4675             :         {
    4676         101 :             float fDistance = 0.0f;
    4677         101 :             if (GTiffGetJXLLossless(papszCreationOptions))
    4678             :             {
    4679          82 :                 AppendMetadataItem(&psRoot, &psTail,
    4680             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4681             :                                    nullptr, "IMAGE_STRUCTURE");
    4682             :             }
    4683             :             else
    4684             :             {
    4685          19 :                 fDistance = GTiffGetJXLDistance(papszCreationOptions);
    4686          19 :                 AppendMetadataItem(
    4687             :                     &psRoot, &psTail, "JXL_DISTANCE",
    4688             :                     CPLSPrintf("%f", static_cast<double>(fDistance)), 0,
    4689             :                     nullptr, "IMAGE_STRUCTURE");
    4690             :             }
    4691             :             const float fAlphaDistance =
    4692         101 :                 GTiffGetJXLAlphaDistance(papszCreationOptions);
    4693         101 :             if (fAlphaDistance >= 0.0f && fAlphaDistance != fDistance)
    4694             :             {
    4695           2 :                 AppendMetadataItem(
    4696             :                     &psRoot, &psTail, "JXL_ALPHA_DISTANCE",
    4697             :                     CPLSPrintf("%f", static_cast<double>(fAlphaDistance)), 0,
    4698             :                     nullptr, "IMAGE_STRUCTURE");
    4699             :             }
    4700         101 :             AppendMetadataItem(
    4701             :                 &psRoot, &psTail, "JXL_EFFORT",
    4702             :                 CPLSPrintf("%d", GTiffGetJXLEffort(papszCreationOptions)), 0,
    4703             :                 nullptr, "IMAGE_STRUCTURE");
    4704             :         }
    4705             : #endif
    4706             :     }
    4707             : 
    4708             :     /* -------------------------------------------------------------------- */
    4709             :     /*      Write out the generic XML metadata if there is any.             */
    4710             :     /* -------------------------------------------------------------------- */
    4711        7665 :     if (psRoot != nullptr)
    4712             :     {
    4713         657 :         bool bRet = true;
    4714             : 
    4715         657 :         if (eProfile == GTiffProfile::GDALGEOTIFF)
    4716             :         {
    4717         640 :             char *pszXML_MD = CPLSerializeXMLTree(psRoot);
    4718         640 :             TIFFSetField(l_hTIFF, TIFFTAG_GDAL_METADATA, pszXML_MD);
    4719         640 :             CPLFree(pszXML_MD);
    4720             :         }
    4721             :         else
    4722             :         {
    4723          17 :             if (bSrcIsGeoTIFF)
    4724          11 :                 cpl::down_cast<GTiffDataset *>(poSrcDS)->PushMetadataToPam();
    4725             :             else
    4726           6 :                 bRet = false;
    4727             :         }
    4728             : 
    4729         657 :         CPLDestroyXMLNode(psRoot);
    4730             : 
    4731         657 :         return bRet;
    4732             :     }
    4733             : 
    4734             :     // If we have no more metadata but it existed before,
    4735             :     // remove the GDAL_METADATA tag.
    4736        7008 :     if (eProfile == GTiffProfile::GDALGEOTIFF)
    4737             :     {
    4738        6984 :         char *pszText = nullptr;
    4739        6984 :         if (TIFFGetField(l_hTIFF, TIFFTAG_GDAL_METADATA, &pszText))
    4740             :         {
    4741           7 :             TIFFUnsetField(l_hTIFF, TIFFTAG_GDAL_METADATA);
    4742             :         }
    4743             :     }
    4744             : 
    4745        7008 :     return true;
    4746             : }
    4747             : 
    4748             : /************************************************************************/
    4749             : /*                         PushMetadataToPam()                          */
    4750             : /*                                                                      */
    4751             : /*      When producing a strict profile TIFF or if our aggregate        */
    4752             : /*      metadata is too big for a single tiff tag we may end up         */
    4753             : /*      needing to write it via the PAM mechanisms.  This method        */
    4754             : /*      copies all the appropriate metadata into the PAM level          */
    4755             : /*      metadata object but with special care to avoid copying          */
    4756             : /*      metadata handled in other ways in TIFF format.                  */
    4757             : /************************************************************************/
    4758             : 
    4759          17 : void GTiffDataset::PushMetadataToPam()
    4760             : 
    4761             : {
    4762          17 :     if (GetPamFlags() & GPF_DISABLED)
    4763           0 :         return;
    4764             : 
    4765          17 :     const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
    4766          17 :         GDALDataset::ToHandle(this), m_nPhotometric, m_papszCreationOptions);
    4767             : 
    4768          55 :     for (int nBand = 0; nBand <= GetRasterCount(); ++nBand)
    4769             :     {
    4770          38 :         GDALMultiDomainMetadata *poSrcMDMD = nullptr;
    4771          38 :         GTiffRasterBand *poBand = nullptr;
    4772             : 
    4773          38 :         if (nBand == 0)
    4774             :         {
    4775          17 :             poSrcMDMD = &(this->m_oGTiffMDMD);
    4776             :         }
    4777             :         else
    4778             :         {
    4779          21 :             poBand = cpl::down_cast<GTiffRasterBand *>(GetRasterBand(nBand));
    4780          21 :             poSrcMDMD = &(poBand->m_oGTiffMDMD);
    4781             :         }
    4782             : 
    4783             :         /* --------------------------------------------------------------------
    4784             :          */
    4785             :         /*      Loop over the available domains. */
    4786             :         /* --------------------------------------------------------------------
    4787             :          */
    4788          38 :         CSLConstList papszDomainList = poSrcMDMD->GetDomainList();
    4789          74 :         for (int iDomain = 0; papszDomainList && papszDomainList[iDomain];
    4790             :              ++iDomain)
    4791             :         {
    4792          36 :             char **papszMD = poSrcMDMD->GetMetadata(papszDomainList[iDomain]);
    4793             : 
    4794          36 :             if (EQUAL(papszDomainList[iDomain], MD_DOMAIN_RPC) ||
    4795          36 :                 EQUAL(papszDomainList[iDomain], MD_DOMAIN_IMD) ||
    4796          36 :                 EQUAL(papszDomainList[iDomain], "_temporary_") ||
    4797          36 :                 EQUAL(papszDomainList[iDomain], "IMAGE_STRUCTURE") ||
    4798          19 :                 EQUAL(papszDomainList[iDomain], "COLOR_PROFILE"))
    4799          17 :                 continue;
    4800             : 
    4801          19 :             papszMD = CSLDuplicate(papszMD);
    4802             : 
    4803          69 :             for (int i = CSLCount(papszMD) - 1; i >= 0; --i)
    4804             :             {
    4805          50 :                 if (STARTS_WITH_CI(papszMD[i], "TIFFTAG_") ||
    4806          50 :                     EQUALN(papszMD[i], GDALMD_AREA_OR_POINT,
    4807             :                            strlen(GDALMD_AREA_OR_POINT)))
    4808           4 :                     papszMD = CSLRemoveStrings(papszMD, i, 1, nullptr);
    4809             :             }
    4810             : 
    4811          19 :             if (nBand == 0)
    4812          10 :                 GDALPamDataset::SetMetadata(papszMD, papszDomainList[iDomain]);
    4813             :             else
    4814           9 :                 poBand->GDALPamRasterBand::SetMetadata(
    4815           9 :                     papszMD, papszDomainList[iDomain]);
    4816             : 
    4817          19 :             CSLDestroy(papszMD);
    4818             :         }
    4819             : 
    4820             :         /* --------------------------------------------------------------------
    4821             :          */
    4822             :         /*      Handle some "special domain" stuff. */
    4823             :         /* --------------------------------------------------------------------
    4824             :          */
    4825          38 :         if (poBand != nullptr)
    4826             :         {
    4827          21 :             poBand->GDALPamRasterBand::SetOffset(poBand->GetOffset());
    4828          21 :             poBand->GDALPamRasterBand::SetScale(poBand->GetScale());
    4829          21 :             poBand->GDALPamRasterBand::SetUnitType(poBand->GetUnitType());
    4830          21 :             poBand->GDALPamRasterBand::SetDescription(poBand->GetDescription());
    4831          21 :             if (!bStandardColorInterp)
    4832             :             {
    4833           3 :                 poBand->GDALPamRasterBand::SetColorInterpretation(
    4834           3 :                     poBand->GetColorInterpretation());
    4835             :             }
    4836             :         }
    4837             :     }
    4838          17 :     MarkPamDirty();
    4839             : }
    4840             : 
    4841             : /************************************************************************/
    4842             : /*                         WriteNoDataValue()                           */
    4843             : /************************************************************************/
    4844             : 
    4845         469 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, double dfNoData)
    4846             : 
    4847             : {
    4848         938 :     CPLString osVal(GTiffFormatGDALNoDataTagValue(dfNoData));
    4849         469 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA, osVal.c_str());
    4850         469 : }
    4851             : 
    4852           3 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, int64_t nNoData)
    4853             : 
    4854             : {
    4855           3 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA,
    4856             :                  CPLSPrintf(CPL_FRMT_GIB, static_cast<GIntBig>(nNoData)));
    4857           3 : }
    4858             : 
    4859           3 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, uint64_t nNoData)
    4860             : 
    4861             : {
    4862           3 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA,
    4863             :                  CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoData)));
    4864           3 : }
    4865             : 
    4866             : /************************************************************************/
    4867             : /*                         UnsetNoDataValue()                           */
    4868             : /************************************************************************/
    4869             : 
    4870          14 : void GTiffDataset::UnsetNoDataValue(TIFF *l_hTIFF)
    4871             : 
    4872             : {
    4873          14 :     TIFFUnsetField(l_hTIFF, TIFFTAG_GDAL_NODATA);
    4874          14 : }
    4875             : 
    4876             : /************************************************************************/
    4877             : /*                             SaveICCProfile()                         */
    4878             : /*                                                                      */
    4879             : /*      Save ICC Profile or colorimetric data into file                 */
    4880             : /* pDS:                                                                 */
    4881             : /*      Dataset that contains the metadata with the ICC or colorimetric */
    4882             : /*      data. If this argument is specified, all other arguments are    */
    4883             : /*      ignored. Set them to NULL or 0.                                 */
    4884             : /* hTIFF:                                                               */
    4885             : /*      Pointer to TIFF handle. Only needed if pDS is NULL or           */
    4886             : /*      pDS->m_hTIFF is NULL.                                             */
    4887             : /* papszParamList:                                                       */
    4888             : /*      Options containing the ICC profile or colorimetric metadata.    */
    4889             : /*      Ignored if pDS is not NULL.                                     */
    4890             : /* nBitsPerSample:                                                      */
    4891             : /*      Bits per sample. Ignored if pDS is not NULL.                    */
    4892             : /************************************************************************/
    4893             : 
    4894        9475 : void GTiffDataset::SaveICCProfile(GTiffDataset *pDS, TIFF *l_hTIFF,
    4895             :                                   char **papszParamList,
    4896             :                                   uint32_t l_nBitsPerSample)
    4897             : {
    4898        9475 :     if ((pDS != nullptr) && (pDS->eAccess != GA_Update))
    4899           0 :         return;
    4900             : 
    4901        9475 :     if (l_hTIFF == nullptr)
    4902             :     {
    4903           2 :         if (pDS == nullptr)
    4904           0 :             return;
    4905             : 
    4906           2 :         l_hTIFF = pDS->m_hTIFF;
    4907           2 :         if (l_hTIFF == nullptr)
    4908           0 :             return;
    4909             :     }
    4910             : 
    4911        9475 :     if ((papszParamList == nullptr) && (pDS == nullptr))
    4912        4674 :         return;
    4913             : 
    4914             :     const char *pszICCProfile =
    4915             :         (pDS != nullptr)
    4916        4801 :             ? pDS->GetMetadataItem("SOURCE_ICC_PROFILE", "COLOR_PROFILE")
    4917        4799 :             : CSLFetchNameValue(papszParamList, "SOURCE_ICC_PROFILE");
    4918        4801 :     if (pszICCProfile != nullptr)
    4919             :     {
    4920           8 :         char *pEmbedBuffer = CPLStrdup(pszICCProfile);
    4921             :         int32_t nEmbedLen =
    4922           8 :             CPLBase64DecodeInPlace(reinterpret_cast<GByte *>(pEmbedBuffer));
    4923             : 
    4924           8 :         TIFFSetField(l_hTIFF, TIFFTAG_ICCPROFILE, nEmbedLen, pEmbedBuffer);
    4925             : 
    4926           8 :         CPLFree(pEmbedBuffer);
    4927             :     }
    4928             :     else
    4929             :     {
    4930             :         // Output colorimetric data.
    4931        4793 :         float pCHR[6] = {};     // Primaries.
    4932        4793 :         uint16_t pTXR[6] = {};  // Transfer range.
    4933        4793 :         const char *pszCHRNames[] = {"SOURCE_PRIMARIES_RED",
    4934             :                                      "SOURCE_PRIMARIES_GREEN",
    4935             :                                      "SOURCE_PRIMARIES_BLUE"};
    4936        4793 :         const char *pszTXRNames[] = {"TIFFTAG_TRANSFERRANGE_BLACK",
    4937             :                                      "TIFFTAG_TRANSFERRANGE_WHITE"};
    4938             : 
    4939             :         // Output chromacities.
    4940        4793 :         bool bOutputCHR = true;
    4941        4808 :         for (int i = 0; i < 3 && bOutputCHR; ++i)
    4942             :         {
    4943             :             const char *pszColorProfile =
    4944             :                 (pDS != nullptr)
    4945        4803 :                     ? pDS->GetMetadataItem(pszCHRNames[i], "COLOR_PROFILE")
    4946        4800 :                     : CSLFetchNameValue(papszParamList, pszCHRNames[i]);
    4947        4803 :             if (pszColorProfile == nullptr)
    4948             :             {
    4949        4788 :                 bOutputCHR = false;
    4950        4788 :                 break;
    4951             :             }
    4952             : 
    4953             :             const CPLStringList aosTokens(CSLTokenizeString2(
    4954             :                 pszColorProfile, ",",
    4955             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    4956          15 :                     CSLT_STRIPENDSPACES));
    4957             : 
    4958          15 :             if (aosTokens.size() != 3)
    4959             :             {
    4960           0 :                 bOutputCHR = false;
    4961           0 :                 break;
    4962             :             }
    4963             : 
    4964          60 :             for (int j = 0; j < 3; ++j)
    4965             :             {
    4966          45 :                 float v = static_cast<float>(CPLAtof(aosTokens[j]));
    4967             : 
    4968          45 :                 if (j == 2)
    4969             :                 {
    4970             :                     // Last term of xyY color must be 1.0.
    4971          15 :                     if (v != 1.0f)
    4972             :                     {
    4973           0 :                         bOutputCHR = false;
    4974           0 :                         break;
    4975             :                     }
    4976             :                 }
    4977             :                 else
    4978             :                 {
    4979          30 :                     pCHR[i * 2 + j] = v;
    4980             :                 }
    4981             :             }
    4982             :         }
    4983             : 
    4984        4793 :         if (bOutputCHR)
    4985             :         {
    4986           5 :             TIFFSetField(l_hTIFF, TIFFTAG_PRIMARYCHROMATICITIES, pCHR);
    4987             :         }
    4988             : 
    4989             :         // Output whitepoint.
    4990             :         const char *pszSourceWhitePoint =
    4991             :             (pDS != nullptr)
    4992        4793 :                 ? pDS->GetMetadataItem("SOURCE_WHITEPOINT", "COLOR_PROFILE")
    4993        4792 :                 : CSLFetchNameValue(papszParamList, "SOURCE_WHITEPOINT");
    4994        4793 :         if (pszSourceWhitePoint != nullptr)
    4995             :         {
    4996             :             const CPLStringList aosTokens(CSLTokenizeString2(
    4997             :                 pszSourceWhitePoint, ",",
    4998             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    4999          10 :                     CSLT_STRIPENDSPACES));
    5000             : 
    5001           5 :             bool bOutputWhitepoint = true;
    5002           5 :             float pWP[2] = {0.0f, 0.0f};  // Whitepoint
    5003           5 :             if (aosTokens.size() != 3)
    5004             :             {
    5005           0 :                 bOutputWhitepoint = false;
    5006             :             }
    5007             :             else
    5008             :             {
    5009          20 :                 for (int j = 0; j < 3; ++j)
    5010             :                 {
    5011          15 :                     const float v = static_cast<float>(CPLAtof(aosTokens[j]));
    5012             : 
    5013          15 :                     if (j == 2)
    5014             :                     {
    5015             :                         // Last term of xyY color must be 1.0.
    5016           5 :                         if (v != 1.0f)
    5017             :                         {
    5018           0 :                             bOutputWhitepoint = false;
    5019           0 :                             break;
    5020             :                         }
    5021             :                     }
    5022             :                     else
    5023             :                     {
    5024          10 :                         pWP[j] = v;
    5025             :                     }
    5026             :                 }
    5027             :             }
    5028             : 
    5029           5 :             if (bOutputWhitepoint)
    5030             :             {
    5031           5 :                 TIFFSetField(l_hTIFF, TIFFTAG_WHITEPOINT, pWP);
    5032             :             }
    5033             :         }
    5034             : 
    5035             :         // Set transfer function metadata.
    5036             :         char const *pszTFRed =
    5037             :             (pDS != nullptr)
    5038        4793 :                 ? pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_RED",
    5039             :                                        "COLOR_PROFILE")
    5040        4792 :                 : CSLFetchNameValue(papszParamList,
    5041        4793 :                                     "TIFFTAG_TRANSFERFUNCTION_RED");
    5042             : 
    5043             :         char const *pszTFGreen =
    5044             :             (pDS != nullptr)
    5045        4793 :                 ? pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_GREEN",
    5046             :                                        "COLOR_PROFILE")
    5047        4792 :                 : CSLFetchNameValue(papszParamList,
    5048        4793 :                                     "TIFFTAG_TRANSFERFUNCTION_GREEN");
    5049             : 
    5050             :         char const *pszTFBlue =
    5051             :             (pDS != nullptr)
    5052        4793 :                 ? pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_BLUE",
    5053             :                                        "COLOR_PROFILE")
    5054        4792 :                 : CSLFetchNameValue(papszParamList,
    5055        4793 :                                     "TIFFTAG_TRANSFERFUNCTION_BLUE");
    5056             : 
    5057        4793 :         if ((pszTFRed != nullptr) && (pszTFGreen != nullptr) &&
    5058             :             (pszTFBlue != nullptr))
    5059             :         {
    5060             :             // Get length of table.
    5061           4 :             const int nTransferFunctionLength =
    5062           4 :                 1 << ((pDS != nullptr) ? pDS->m_nBitsPerSample
    5063             :                                        : l_nBitsPerSample);
    5064             : 
    5065             :             const CPLStringList aosTokensRed(CSLTokenizeString2(
    5066             :                 pszTFRed, ",",
    5067             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5068           8 :                     CSLT_STRIPENDSPACES));
    5069             :             const CPLStringList aosTokensGreen(CSLTokenizeString2(
    5070             :                 pszTFGreen, ",",
    5071             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5072           8 :                     CSLT_STRIPENDSPACES));
    5073             :             const CPLStringList aosTokensBlue(CSLTokenizeString2(
    5074             :                 pszTFBlue, ",",
    5075             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5076           8 :                     CSLT_STRIPENDSPACES));
    5077             : 
    5078           4 :             if ((aosTokensRed.size() == nTransferFunctionLength) &&
    5079           8 :                 (aosTokensGreen.size() == nTransferFunctionLength) &&
    5080           4 :                 (aosTokensBlue.size() == nTransferFunctionLength))
    5081             :             {
    5082             :                 std::vector<uint16_t> anTransferFuncRed(
    5083           8 :                     nTransferFunctionLength);
    5084             :                 std::vector<uint16_t> anTransferFuncGreen(
    5085           8 :                     nTransferFunctionLength);
    5086             :                 std::vector<uint16_t> anTransferFuncBlue(
    5087           8 :                     nTransferFunctionLength);
    5088             : 
    5089             :                 // Convert our table in string format into int16_t format.
    5090        1028 :                 for (int i = 0; i < nTransferFunctionLength; ++i)
    5091             :                 {
    5092        2048 :                     anTransferFuncRed[i] =
    5093        1024 :                         static_cast<uint16_t>(atoi(aosTokensRed[i]));
    5094        2048 :                     anTransferFuncGreen[i] =
    5095        1024 :                         static_cast<uint16_t>(atoi(aosTokensGreen[i]));
    5096        1024 :                     anTransferFuncBlue[i] =
    5097        1024 :                         static_cast<uint16_t>(atoi(aosTokensBlue[i]));
    5098             :                 }
    5099             : 
    5100           4 :                 TIFFSetField(
    5101             :                     l_hTIFF, TIFFTAG_TRANSFERFUNCTION, anTransferFuncRed.data(),
    5102             :                     anTransferFuncGreen.data(), anTransferFuncBlue.data());
    5103             :             }
    5104             :         }
    5105             : 
    5106             :         // Output transfer range.
    5107        4793 :         bool bOutputTransferRange = true;
    5108        4793 :         for (int i = 0; (i < 2) && bOutputTransferRange; ++i)
    5109             :         {
    5110             :             const char *pszTXRVal =
    5111             :                 (pDS != nullptr)
    5112        4793 :                     ? pDS->GetMetadataItem(pszTXRNames[i], "COLOR_PROFILE")
    5113        4792 :                     : CSLFetchNameValue(papszParamList, pszTXRNames[i]);
    5114        4793 :             if (pszTXRVal == nullptr)
    5115             :             {
    5116        4793 :                 bOutputTransferRange = false;
    5117        4793 :                 break;
    5118             :             }
    5119             : 
    5120             :             const CPLStringList aosTokens(CSLTokenizeString2(
    5121             :                 pszTXRVal, ",",
    5122             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5123           0 :                     CSLT_STRIPENDSPACES));
    5124             : 
    5125           0 :             if (aosTokens.size() != 3)
    5126             :             {
    5127           0 :                 bOutputTransferRange = false;
    5128           0 :                 break;
    5129             :             }
    5130             : 
    5131           0 :             for (int j = 0; j < 3; ++j)
    5132             :             {
    5133           0 :                 pTXR[i + j * 2] = static_cast<uint16_t>(atoi(aosTokens[j]));
    5134             :             }
    5135             :         }
    5136             : 
    5137        4793 :         if (bOutputTransferRange)
    5138             :         {
    5139           0 :             const int TIFFTAG_TRANSFERRANGE = 0x0156;
    5140           0 :             TIFFSetField(l_hTIFF, TIFFTAG_TRANSFERRANGE, pTXR);
    5141             :         }
    5142             :     }
    5143             : }
    5144             : 
    5145       17041 : static signed char GTiffGetLZMAPreset(char **papszOptions)
    5146             : {
    5147       17041 :     int nLZMAPreset = -1;
    5148       17041 :     const char *pszValue = CSLFetchNameValue(papszOptions, "LZMA_PRESET");
    5149       17041 :     if (pszValue != nullptr)
    5150             :     {
    5151          20 :         nLZMAPreset = atoi(pszValue);
    5152          20 :         if (!(nLZMAPreset >= 0 && nLZMAPreset <= 9))
    5153             :         {
    5154           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5155             :                      "LZMA_PRESET=%s value not recognised, ignoring.",
    5156             :                      pszValue);
    5157           0 :             nLZMAPreset = -1;
    5158             :         }
    5159             :     }
    5160       17041 :     return static_cast<signed char>(nLZMAPreset);
    5161             : }
    5162             : 
    5163       17041 : static signed char GTiffGetZSTDPreset(char **papszOptions)
    5164             : {
    5165       17041 :     int nZSTDLevel = -1;
    5166       17041 :     const char *pszValue = CSLFetchNameValue(papszOptions, "ZSTD_LEVEL");
    5167       17041 :     if (pszValue != nullptr)
    5168             :     {
    5169          24 :         nZSTDLevel = atoi(pszValue);
    5170          24 :         if (!(nZSTDLevel >= 1 && nZSTDLevel <= 22))
    5171             :         {
    5172           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5173             :                      "ZSTD_LEVEL=%s value not recognised, ignoring.", pszValue);
    5174           0 :             nZSTDLevel = -1;
    5175             :         }
    5176             :     }
    5177       17041 :     return static_cast<signed char>(nZSTDLevel);
    5178             : }
    5179             : 
    5180       17041 : static signed char GTiffGetZLevel(char **papszOptions)
    5181             : {
    5182       17041 :     int nZLevel = -1;
    5183       17041 :     const char *pszValue = CSLFetchNameValue(papszOptions, "ZLEVEL");
    5184       17040 :     if (pszValue != nullptr)
    5185             :     {
    5186          44 :         nZLevel = atoi(pszValue);
    5187             : #ifdef TIFFTAG_DEFLATE_SUBCODEC
    5188          44 :         constexpr int nMaxLevel = 12;
    5189             : #ifndef LIBDEFLATE_SUPPORT
    5190             :         if (nZLevel > 9 && nZLevel <= nMaxLevel)
    5191             :         {
    5192             :             CPLDebug("GTiff",
    5193             :                      "ZLEVEL=%d not supported in a non-libdeflate enabled "
    5194             :                      "libtiff build. Capping to 9",
    5195             :                      nZLevel);
    5196             :             nZLevel = 9;
    5197             :         }
    5198             : #endif
    5199             : #else
    5200             :         constexpr int nMaxLevel = 9;
    5201             : #endif
    5202          44 :         if (nZLevel < 1 || nZLevel > nMaxLevel)
    5203             :         {
    5204           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5205             :                      "ZLEVEL=%s value not recognised, ignoring.", pszValue);
    5206           0 :             nZLevel = -1;
    5207             :         }
    5208             :     }
    5209       17040 :     return static_cast<signed char>(nZLevel);
    5210             : }
    5211             : 
    5212       17040 : static signed char GTiffGetJpegQuality(char **papszOptions)
    5213             : {
    5214       17040 :     int nJpegQuality = -1;
    5215       17040 :     const char *pszValue = CSLFetchNameValue(papszOptions, "JPEG_QUALITY");
    5216       17041 :     if (pszValue != nullptr)
    5217             :     {
    5218        1939 :         nJpegQuality = atoi(pszValue);
    5219        1939 :         if (nJpegQuality < 1 || nJpegQuality > 100)
    5220             :         {
    5221           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5222             :                      "JPEG_QUALITY=%s value not recognised, ignoring.",
    5223             :                      pszValue);
    5224           0 :             nJpegQuality = -1;
    5225             :         }
    5226             :     }
    5227       17041 :     return static_cast<signed char>(nJpegQuality);
    5228             : }
    5229             : 
    5230       17041 : static signed char GTiffGetJpegTablesMode(char **papszOptions)
    5231             : {
    5232       17041 :     return static_cast<signed char>(atoi(
    5233             :         CSLFetchNameValueDef(papszOptions, "JPEGTABLESMODE",
    5234       17041 :                              CPLSPrintf("%d", knGTIFFJpegTablesModeDefault))));
    5235             : }
    5236             : 
    5237             : /************************************************************************/
    5238             : /*                        GetDiscardLsbOption()                         */
    5239             : /************************************************************************/
    5240             : 
    5241        7507 : static GTiffDataset::MaskOffset *GetDiscardLsbOption(TIFF *hTIFF,
    5242             :                                                      char **papszOptions)
    5243             : {
    5244        7507 :     const char *pszBits = CSLFetchNameValue(papszOptions, "DISCARD_LSB");
    5245        7507 :     if (pszBits == nullptr)
    5246        7385 :         return nullptr;
    5247             : 
    5248         122 :     uint16_t nPhotometric = 0;
    5249         122 :     TIFFGetFieldDefaulted(hTIFF, TIFFTAG_PHOTOMETRIC, &nPhotometric);
    5250             : 
    5251         122 :     uint16_t nBitsPerSample = 0;
    5252         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &nBitsPerSample))
    5253           0 :         nBitsPerSample = 1;
    5254             : 
    5255         122 :     uint16_t nSamplesPerPixel = 0;
    5256         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSamplesPerPixel))
    5257           0 :         nSamplesPerPixel = 1;
    5258             : 
    5259         122 :     uint16_t nSampleFormat = 0;
    5260         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_SAMPLEFORMAT, &nSampleFormat))
    5261           0 :         nSampleFormat = SAMPLEFORMAT_UINT;
    5262             : 
    5263         122 :     if (nPhotometric == PHOTOMETRIC_PALETTE)
    5264             :     {
    5265           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    5266             :                  "DISCARD_LSB ignored on a paletted image");
    5267           1 :         return nullptr;
    5268             :     }
    5269         121 :     if (!(nBitsPerSample == 8 || nBitsPerSample == 16 || nBitsPerSample == 32 ||
    5270          13 :           nBitsPerSample == 64))
    5271             :     {
    5272           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    5273             :                  "DISCARD_LSB ignored on non 8, 16, 32 or 64 bits images");
    5274           1 :         return nullptr;
    5275             :     }
    5276             : 
    5277         240 :     const CPLStringList aosTokens(CSLTokenizeString2(pszBits, ",", 0));
    5278         120 :     const int nTokens = aosTokens.size();
    5279         120 :     GTiffDataset::MaskOffset *panMaskOffsetLsb = nullptr;
    5280         120 :     if (nTokens == 1 || nTokens == nSamplesPerPixel)
    5281             :     {
    5282             :         panMaskOffsetLsb = static_cast<GTiffDataset::MaskOffset *>(
    5283         119 :             CPLCalloc(nSamplesPerPixel, sizeof(GTiffDataset::MaskOffset)));
    5284         374 :         for (int i = 0; i < nSamplesPerPixel; ++i)
    5285             :         {
    5286         255 :             const int nBits = atoi(aosTokens[nTokens == 1 ? 0 : i]);
    5287         510 :             const int nMaxBits = (nSampleFormat == SAMPLEFORMAT_IEEEFP)
    5288         510 :                                      ? ((nBitsPerSample == 16)   ? 11 - 1
    5289          78 :                                         : (nBitsPerSample == 32) ? 23 - 1
    5290          26 :                                         : (nBitsPerSample == 64) ? 53 - 1
    5291             :                                                                  : 0)
    5292         203 :                                  : nSampleFormat == SAMPLEFORMAT_INT
    5293         203 :                                      ? nBitsPerSample - 2
    5294         119 :                                      : nBitsPerSample - 1;
    5295             : 
    5296         255 :             if (nBits < 0 || nBits > nMaxBits)
    5297             :             {
    5298           0 :                 CPLError(
    5299             :                     CE_Warning, CPLE_AppDefined,
    5300             :                     "DISCARD_LSB ignored: values should be in [0,%d] range",
    5301             :                     nMaxBits);
    5302           0 :                 VSIFree(panMaskOffsetLsb);
    5303           0 :                 return nullptr;
    5304             :             }
    5305         255 :             panMaskOffsetLsb[i].nMask =
    5306         255 :                 ~((static_cast<uint64_t>(1) << nBits) - 1);
    5307         255 :             if (nBits > 1)
    5308             :             {
    5309         249 :                 panMaskOffsetLsb[i].nRoundUpBitTest = static_cast<uint64_t>(1)
    5310         249 :                                                       << (nBits - 1);
    5311             :             }
    5312         119 :         }
    5313             :     }
    5314             :     else
    5315             :     {
    5316           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    5317             :                  "DISCARD_LSB ignored: wrong number of components");
    5318             :     }
    5319         120 :     return panMaskOffsetLsb;
    5320             : }
    5321             : 
    5322        7507 : void GTiffDataset::GetDiscardLsbOption(char **papszOptions)
    5323             : {
    5324        7507 :     m_panMaskOffsetLsb = ::GetDiscardLsbOption(m_hTIFF, papszOptions);
    5325        7507 : }
    5326             : 
    5327             : /************************************************************************/
    5328             : /*                             GetProfile()                             */
    5329             : /************************************************************************/
    5330             : 
    5331       17092 : static GTiffProfile GetProfile(const char *pszProfile)
    5332             : {
    5333       17092 :     GTiffProfile eProfile = GTiffProfile::GDALGEOTIFF;
    5334       17092 :     if (pszProfile != nullptr)
    5335             :     {
    5336          70 :         if (EQUAL(pszProfile, szPROFILE_BASELINE))
    5337          50 :             eProfile = GTiffProfile::BASELINE;
    5338          20 :         else if (EQUAL(pszProfile, szPROFILE_GeoTIFF))
    5339          18 :             eProfile = GTiffProfile::GEOTIFF;
    5340           2 :         else if (!EQUAL(pszProfile, szPROFILE_GDALGeoTIFF))
    5341             :         {
    5342           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    5343             :                      "Unsupported value for PROFILE: %s", pszProfile);
    5344             :         }
    5345             :     }
    5346       17092 :     return eProfile;
    5347             : }
    5348             : 
    5349             : /************************************************************************/
    5350             : /*                            GTiffCreate()                             */
    5351             : /*                                                                      */
    5352             : /*      Shared functionality between GTiffDataset::Create() and         */
    5353             : /*      GTiffCreateCopy() for creating TIFF file based on a set of      */
    5354             : /*      options and a configuration.                                    */
    5355             : /************************************************************************/
    5356             : 
    5357        9553 : TIFF *GTiffDataset::CreateLL(const char *pszFilename, int nXSize, int nYSize,
    5358             :                              int l_nBands, GDALDataType eType,
    5359             :                              double dfExtraSpaceForOverviews,
    5360             :                              int nColorTableMultiplier, char **papszParamList,
    5361             :                              VSILFILE **pfpL, CPLString &l_osTmpFilename,
    5362             :                              bool bCreateCopy, bool &bTileInterleavingOut)
    5363             : 
    5364             : {
    5365        9553 :     bTileInterleavingOut = false;
    5366             : 
    5367        9553 :     GTiffOneTimeInit();
    5368             : 
    5369             :     /* -------------------------------------------------------------------- */
    5370             :     /*      Blow on a few errors.                                           */
    5371             :     /* -------------------------------------------------------------------- */
    5372        9553 :     if (nXSize < 1 || nYSize < 1 || l_nBands < 1)
    5373             :     {
    5374           1 :         ReportError(
    5375             :             pszFilename, CE_Failure, CPLE_AppDefined,
    5376             :             "Attempt to create %dx%dx%d TIFF file, but width, height and bands"
    5377             :             "must be positive.",
    5378             :             nXSize, nYSize, l_nBands);
    5379             : 
    5380           1 :         return nullptr;
    5381             :     }
    5382             : 
    5383        9552 :     if (l_nBands > 65535)
    5384             :     {
    5385           1 :         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5386             :                     "Attempt to create %dx%dx%d TIFF file, but bands "
    5387             :                     "must be lesser or equal to 65535.",
    5388             :                     nXSize, nYSize, l_nBands);
    5389             : 
    5390           1 :         return nullptr;
    5391             :     }
    5392             : 
    5393             :     /* -------------------------------------------------------------------- */
    5394             :     /*      Setup values based on options.                                  */
    5395             :     /* -------------------------------------------------------------------- */
    5396             :     const GTiffProfile eProfile =
    5397        9551 :         GetProfile(CSLFetchNameValue(papszParamList, "PROFILE"));
    5398             : 
    5399        9551 :     const bool bTiled = CPLFetchBool(papszParamList, "TILED", false);
    5400             : 
    5401        9551 :     int l_nBlockXSize = 0;
    5402        9551 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKXSIZE"))
    5403             :     {
    5404         381 :         l_nBlockXSize = atoi(pszValue);
    5405         381 :         if (l_nBlockXSize < 0)
    5406             :         {
    5407           0 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5408             :                         "Invalid value for BLOCKXSIZE");
    5409           0 :             return nullptr;
    5410             :         }
    5411         381 :         if (!bTiled)
    5412             :         {
    5413          10 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    5414             :                         "BLOCKXSIZE can only be used with TILED=YES");
    5415             :         }
    5416         371 :         else if (l_nBlockXSize % 16 != 0)
    5417             :         {
    5418           1 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5419             :                         "BLOCKXSIZE must be a multiple of 16");
    5420           1 :             return nullptr;
    5421             :         }
    5422             :     }
    5423             : 
    5424        9550 :     int l_nBlockYSize = 0;
    5425        9550 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKYSIZE"))
    5426             :     {
    5427        2493 :         l_nBlockYSize = atoi(pszValue);
    5428        2493 :         if (l_nBlockYSize < 0)
    5429             :         {
    5430           0 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5431             :                         "Invalid value for BLOCKYSIZE");
    5432           0 :             return nullptr;
    5433             :         }
    5434        2493 :         if (bTiled && (l_nBlockYSize % 16 != 0))
    5435             :         {
    5436           2 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5437             :                         "BLOCKYSIZE must be a multiple of 16");
    5438           2 :             return nullptr;
    5439             :         }
    5440             :     }
    5441             : 
    5442        9547 :     if (bTiled)
    5443             :     {
    5444         698 :         if (l_nBlockXSize == 0)
    5445         329 :             l_nBlockXSize = 256;
    5446             : 
    5447         698 :         if (l_nBlockYSize == 0)
    5448         329 :             l_nBlockYSize = 256;
    5449             :     }
    5450             : 
    5451        9547 :     int nPlanar = 0;
    5452             : 
    5453             :     // Hidden @TILE_INTERLEAVE=YES parameter used by the COG driver
    5454        9547 :     if (bCreateCopy && CPLTestBool(CSLFetchNameValueDef(
    5455             :                            papszParamList, "@TILE_INTERLEAVE", "NO")))
    5456             :     {
    5457           7 :         bTileInterleavingOut = true;
    5458           7 :         nPlanar = PLANARCONFIG_SEPARATE;
    5459             :     }
    5460             :     else
    5461             :     {
    5462        9540 :         if (const char *pszValue =
    5463        9540 :                 CSLFetchNameValue(papszParamList, "INTERLEAVE"))
    5464             :         {
    5465        1523 :             if (EQUAL(pszValue, "PIXEL"))
    5466         367 :                 nPlanar = PLANARCONFIG_CONTIG;
    5467        1156 :             else if (EQUAL(pszValue, "BAND"))
    5468             :             {
    5469        1155 :                 nPlanar = PLANARCONFIG_SEPARATE;
    5470             :             }
    5471           1 :             else if (EQUAL(pszValue, "BAND"))
    5472             :             {
    5473           0 :                 nPlanar = PLANARCONFIG_SEPARATE;
    5474             :             }
    5475             :             else
    5476             :             {
    5477           1 :                 ReportError(
    5478             :                     pszFilename, CE_Failure, CPLE_IllegalArg,
    5479             :                     "INTERLEAVE=%s unsupported, value must be PIXEL or BAND.",
    5480             :                     pszValue);
    5481           1 :                 return nullptr;
    5482             :             }
    5483             :         }
    5484             :         else
    5485             :         {
    5486        8017 :             nPlanar = PLANARCONFIG_CONTIG;
    5487             :         }
    5488             :     }
    5489             : 
    5490        9546 :     int l_nCompression = COMPRESSION_NONE;
    5491        9546 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "COMPRESS"))
    5492             :     {
    5493        3250 :         l_nCompression = GTIFFGetCompressionMethod(pszValue, "COMPRESS");
    5494        3250 :         if (l_nCompression < 0)
    5495           0 :             return nullptr;
    5496             :     }
    5497             : 
    5498        9546 :     constexpr int JPEG_MAX_DIMENSION = 65500;  // Defined in jpeglib.h
    5499        9546 :     constexpr int WEBP_MAX_DIMENSION = 16383;
    5500             : 
    5501             :     const struct
    5502             :     {
    5503             :         int nCodecID;
    5504             :         const char *pszCodecName;
    5505             :         int nMaxDim;
    5506        9546 :     } asLimitations[] = {
    5507             :         {COMPRESSION_JPEG, "JPEG", JPEG_MAX_DIMENSION},
    5508             :         {COMPRESSION_WEBP, "WEBP", WEBP_MAX_DIMENSION},
    5509             :     };
    5510             : 
    5511       28628 :     for (const auto &sLimitation : asLimitations)
    5512             :     {
    5513       19090 :         if (l_nCompression == sLimitation.nCodecID && !bTiled &&
    5514        2078 :             nXSize > sLimitation.nMaxDim)
    5515             :         {
    5516           2 :             ReportError(
    5517             :                 pszFilename, CE_Failure, CPLE_IllegalArg,
    5518             :                 "COMPRESS=%s is only compatible of un-tiled images whose "
    5519             :                 "width is lesser or equal to %d pixels. "
    5520             :                 "To overcome this limitation, set the TILED=YES creation "
    5521             :                 "option.",
    5522           2 :                 sLimitation.pszCodecName, sLimitation.nMaxDim);
    5523           2 :             return nullptr;
    5524             :         }
    5525       19088 :         else if (l_nCompression == sLimitation.nCodecID && bTiled &&
    5526          52 :                  l_nBlockXSize > sLimitation.nMaxDim)
    5527             :         {
    5528           2 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5529             :                         "COMPRESS=%s is only compatible of tiled images whose "
    5530             :                         "BLOCKXSIZE is lesser or equal to %d pixels.",
    5531           2 :                         sLimitation.pszCodecName, sLimitation.nMaxDim);
    5532           2 :             return nullptr;
    5533             :         }
    5534       19086 :         else if (l_nCompression == sLimitation.nCodecID &&
    5535        2126 :                  l_nBlockYSize > sLimitation.nMaxDim)
    5536             :         {
    5537           4 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5538             :                         "COMPRESS=%s is only compatible of images whose "
    5539             :                         "BLOCKYSIZE is lesser or equal to %d pixels. "
    5540             :                         "To overcome this limitation, set the TILED=YES "
    5541             :                         "creation option",
    5542           4 :                         sLimitation.pszCodecName, sLimitation.nMaxDim);
    5543           4 :             return nullptr;
    5544             :         }
    5545             :     }
    5546             : 
    5547             :     /* -------------------------------------------------------------------- */
    5548             :     /*      How many bits per sample?  We have a special case if NBITS      */
    5549             :     /*      specified for GDT_Byte, GDT_UInt16, GDT_UInt32.                 */
    5550             :     /* -------------------------------------------------------------------- */
    5551        9538 :     int l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5552        9538 :     if (CSLFetchNameValue(papszParamList, "NBITS") != nullptr)
    5553             :     {
    5554        1751 :         int nMinBits = 0;
    5555        1751 :         int nMaxBits = 0;
    5556        1751 :         l_nBitsPerSample = atoi(CSLFetchNameValue(papszParamList, "NBITS"));
    5557        1751 :         if (eType == GDT_Byte)
    5558             :         {
    5559         531 :             nMinBits = 1;
    5560         531 :             nMaxBits = 8;
    5561             :         }
    5562        1220 :         else if (eType == GDT_UInt16)
    5563             :         {
    5564        1202 :             nMinBits = 9;
    5565        1202 :             nMaxBits = 16;
    5566             :         }
    5567          18 :         else if (eType == GDT_UInt32)
    5568             :         {
    5569          14 :             nMinBits = 17;
    5570          14 :             nMaxBits = 32;
    5571             :         }
    5572           4 :         else if (eType == GDT_Float32)
    5573             :         {
    5574           4 :             if (l_nBitsPerSample != 16 && l_nBitsPerSample != 32)
    5575             :             {
    5576           1 :                 ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5577             :                             "Only NBITS=16 is supported for data type Float32");
    5578           1 :                 l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5579             :             }
    5580             :         }
    5581             :         else
    5582             :         {
    5583           0 :             ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5584             :                         "NBITS is not supported for data type %s",
    5585             :                         GDALGetDataTypeName(eType));
    5586           0 :             l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5587             :         }
    5588             : 
    5589        1751 :         if (nMinBits != 0)
    5590             :         {
    5591        1747 :             if (l_nBitsPerSample < nMinBits)
    5592             :             {
    5593           2 :                 ReportError(
    5594             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    5595             :                     "NBITS=%d is invalid for data type %s. Using NBITS=%d",
    5596             :                     l_nBitsPerSample, GDALGetDataTypeName(eType), nMinBits);
    5597           2 :                 l_nBitsPerSample = nMinBits;
    5598             :             }
    5599        1745 :             else if (l_nBitsPerSample > nMaxBits)
    5600             :             {
    5601           3 :                 ReportError(
    5602             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    5603             :                     "NBITS=%d is invalid for data type %s. Using NBITS=%d",
    5604             :                     l_nBitsPerSample, GDALGetDataTypeName(eType), nMaxBits);
    5605           3 :                 l_nBitsPerSample = nMaxBits;
    5606             :             }
    5607             :         }
    5608             :     }
    5609             : 
    5610             : #ifdef HAVE_JXL
    5611        9539 :     if ((l_nCompression == COMPRESSION_JXL ||
    5612         107 :          l_nCompression == COMPRESSION_JXL_DNG_1_7) &&
    5613         105 :         eType != GDT_Float16 && eType != GDT_Float32)
    5614             :     {
    5615             :         // Reflects tif_jxl's GetJXLDataType()
    5616          85 :         if (eType != GDT_Byte && eType != GDT_UInt16)
    5617             :         {
    5618           1 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5619             :                         "Data type %s not supported for JXL compression. Only "
    5620             :                         "Byte, UInt16, Float16, Float32 are supported",
    5621             :                         GDALGetDataTypeName(eType));
    5622           2 :             return nullptr;
    5623             :         }
    5624             : 
    5625             :         const struct
    5626             :         {
    5627             :             GDALDataType eDT;
    5628             :             int nBitsPerSample;
    5629          84 :         } asSupportedDTBitsPerSample[] = {
    5630             :             {GDT_Byte, 8},
    5631             :             {GDT_UInt16, 16},
    5632             :         };
    5633             : 
    5634         250 :         for (const auto &sSupportedDTBitsPerSample : asSupportedDTBitsPerSample)
    5635             :         {
    5636         167 :             if (eType == sSupportedDTBitsPerSample.eDT &&
    5637          84 :                 l_nBitsPerSample != sSupportedDTBitsPerSample.nBitsPerSample)
    5638             :             {
    5639           1 :                 ReportError(
    5640             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    5641             :                     "Bits per sample=%d not supported for JXL compression. "
    5642             :                     "Only %d is supported for %s data type.",
    5643           1 :                     l_nBitsPerSample, sSupportedDTBitsPerSample.nBitsPerSample,
    5644             :                     GDALGetDataTypeName(eType));
    5645           1 :                 return nullptr;
    5646             :             }
    5647             :         }
    5648             :     }
    5649             : #endif
    5650             : 
    5651        9537 :     int nPredictor = PREDICTOR_NONE;
    5652        9537 :     const char *pszPredictor = CSLFetchNameValue(papszParamList, "PREDICTOR");
    5653        9537 :     if (pszPredictor)
    5654             :     {
    5655          29 :         nPredictor = atoi(pszPredictor);
    5656             :     }
    5657             : 
    5658        9537 :     if (nPredictor != PREDICTOR_NONE &&
    5659          16 :         l_nCompression != COMPRESSION_ADOBE_DEFLATE &&
    5660           2 :         l_nCompression != COMPRESSION_LZW &&
    5661           2 :         l_nCompression != COMPRESSION_LZMA &&
    5662             :         l_nCompression != COMPRESSION_ZSTD)
    5663             :     {
    5664           1 :         ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5665             :                     "PREDICTOR option is ignored for COMPRESS=%s. "
    5666             :                     "Only valid for DEFLATE, LZW, LZMA or ZSTD",
    5667             :                     CSLFetchNameValueDef(papszParamList, "COMPRESS", "NONE"));
    5668             :     }
    5669             : 
    5670             :     // Do early checks as libtiff will only error out when starting to write.
    5671        9564 :     else if (nPredictor != PREDICTOR_NONE &&
    5672          28 :              CPLTestBool(
    5673             :                  CPLGetConfigOption("GDAL_GTIFF_PREDICTOR_CHECKS", "YES")))
    5674             :     {
    5675             : #if (TIFFLIB_VERSION > 20210416) || defined(INTERNAL_LIBTIFF)
    5676             : #define HAVE_PREDICTOR_2_FOR_64BIT
    5677             : #endif
    5678          28 :         if (nPredictor == 2)
    5679             :         {
    5680          23 :             if (l_nBitsPerSample != 8 && l_nBitsPerSample != 16 &&
    5681             :                 l_nBitsPerSample != 32
    5682             : #ifdef HAVE_PREDICTOR_2_FOR_64BIT
    5683           2 :                 && l_nBitsPerSample != 64
    5684             : #endif
    5685             :             )
    5686             :             {
    5687             : #if !defined(HAVE_PREDICTOR_2_FOR_64BIT)
    5688             :                 if (l_nBitsPerSample == 64)
    5689             :                 {
    5690             :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5691             :                                 "PREDICTOR=2 is supported on 64 bit samples "
    5692             :                                 "starting with libtiff > 4.3.0.");
    5693             :                 }
    5694             :                 else
    5695             : #endif
    5696             :                 {
    5697           2 :                     const int nBITSHint = (l_nBitsPerSample < 8)    ? 8
    5698           1 :                                           : (l_nBitsPerSample < 16) ? 16
    5699           0 :                                           : (l_nBitsPerSample < 32) ? 32
    5700             :                                                                     : 64;
    5701           1 :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5702             : #ifdef HAVE_PREDICTOR_2_FOR_64BIT
    5703             :                                 "PREDICTOR=2 is only supported with 8/16/32/64 "
    5704             :                                 "bit samples. You can specify the NBITS=%d "
    5705             :                                 "creation option to promote to the closest "
    5706             :                                 "supported bits per sample value.",
    5707             : #else
    5708             :                                 "PREDICTOR=2 is only supported with 8/16/32 "
    5709             :                                 "bit samples. You can specify the NBITS=%d "
    5710             :                                 "creation option to promote to the closest "
    5711             :                                 "supported bits per sample value.",
    5712             : #endif
    5713             :                                 nBITSHint);
    5714             :                 }
    5715           1 :                 return nullptr;
    5716             :             }
    5717             :         }
    5718           5 :         else if (nPredictor == 3)
    5719             :         {
    5720           4 :             if (eType != GDT_Float32 && eType != GDT_Float64)
    5721             :             {
    5722           1 :                 ReportError(
    5723             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    5724             :                     "PREDICTOR=3 is only supported with Float32 or Float64.");
    5725           1 :                 return nullptr;
    5726             :             }
    5727             :         }
    5728             :         else
    5729             :         {
    5730           1 :             ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5731             :                         "PREDICTOR=%s is not supported.", pszPredictor);
    5732           1 :             return nullptr;
    5733             :         }
    5734             :     }
    5735             : 
    5736        9534 :     const int l_nZLevel = GTiffGetZLevel(papszParamList);
    5737        9533 :     const int l_nLZMAPreset = GTiffGetLZMAPreset(papszParamList);
    5738        9534 :     const int l_nZSTDLevel = GTiffGetZSTDPreset(papszParamList);
    5739        9534 :     const int l_nWebPLevel = GTiffGetWebPLevel(papszParamList);
    5740        9533 :     const bool l_bWebPLossless = GTiffGetWebPLossless(papszParamList);
    5741        9533 :     const int l_nJpegQuality = GTiffGetJpegQuality(papszParamList);
    5742        9534 :     const int l_nJpegTablesMode = GTiffGetJpegTablesMode(papszParamList);
    5743        9534 :     const double l_dfMaxZError = GTiffGetLERCMaxZError(papszParamList);
    5744             : #if HAVE_JXL
    5745        9534 :     bool bJXLLosslessSpecified = false;
    5746             :     const bool l_bJXLLossless =
    5747        9534 :         GTiffGetJXLLossless(papszParamList, &bJXLLosslessSpecified);
    5748        9534 :     const uint32_t l_nJXLEffort = GTiffGetJXLEffort(papszParamList);
    5749        9533 :     bool bJXLDistanceSpecified = false;
    5750             :     const float l_fJXLDistance =
    5751        9533 :         GTiffGetJXLDistance(papszParamList, &bJXLDistanceSpecified);
    5752        9534 :     if (bJXLDistanceSpecified && l_bJXLLossless)
    5753             :     {
    5754           1 :         ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
    5755             :                     "JXL_DISTANCE creation option is ignored, given %s "
    5756             :                     "JXL_LOSSLESS=YES",
    5757             :                     bJXLLosslessSpecified ? "(explicit)" : "(implicit)");
    5758             :     }
    5759        9534 :     bool bJXLAlphaDistanceSpecified = false;
    5760             :     const float l_fJXLAlphaDistance =
    5761        9534 :         GTiffGetJXLAlphaDistance(papszParamList, &bJXLAlphaDistanceSpecified);
    5762        9533 :     if (bJXLAlphaDistanceSpecified && l_bJXLLossless)
    5763             :     {
    5764           1 :         ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
    5765             :                     "JXL_ALPHA_DISTANCE creation option is ignored, given %s "
    5766             :                     "JXL_LOSSLESS=YES",
    5767             :                     bJXLLosslessSpecified ? "(explicit)" : "(implicit)");
    5768             :     }
    5769             : #endif
    5770             :     /* -------------------------------------------------------------------- */
    5771             :     /*      Streaming related code                                          */
    5772             :     /* -------------------------------------------------------------------- */
    5773       19067 :     const CPLString osOriFilename(pszFilename);
    5774       19066 :     bool bStreaming = strcmp(pszFilename, "/vsistdout/") == 0 ||
    5775        9533 :                       CPLFetchBool(papszParamList, "STREAMABLE_OUTPUT", false);
    5776             : #ifdef S_ISFIFO
    5777        9533 :     if (!bStreaming)
    5778             :     {
    5779             :         VSIStatBufL sStat;
    5780        9521 :         if (VSIStatExL(pszFilename, &sStat,
    5781       10402 :                        VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
    5782         880 :             S_ISFIFO(sStat.st_mode))
    5783             :         {
    5784           0 :             bStreaming = true;
    5785             :         }
    5786             :     }
    5787             : #endif
    5788        9534 :     if (bStreaming && !EQUAL("NONE", CSLFetchNameValueDef(papszParamList,
    5789             :                                                           "COMPRESS", "NONE")))
    5790             :     {
    5791           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5792             :                     "Streaming only supported to uncompressed TIFF");
    5793           1 :         return nullptr;
    5794             :     }
    5795        9533 :     if (bStreaming && CPLFetchBool(papszParamList, "SPARSE_OK", false))
    5796             :     {
    5797           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5798             :                     "Streaming not supported with SPARSE_OK");
    5799           1 :         return nullptr;
    5800             :     }
    5801             :     const bool bCopySrcOverviews =
    5802        9532 :         CPLFetchBool(papszParamList, "COPY_SRC_OVERVIEWS", false);
    5803        9532 :     if (bStreaming && bCopySrcOverviews)
    5804             :     {
    5805           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5806             :                     "Streaming not supported with COPY_SRC_OVERVIEWS");
    5807           1 :         return nullptr;
    5808             :     }
    5809        9531 :     if (bStreaming)
    5810             :     {
    5811           9 :         l_osTmpFilename = VSIMemGenerateHiddenFilename("vsistdout.tif");
    5812           9 :         pszFilename = l_osTmpFilename.c_str();
    5813             :     }
    5814             : 
    5815             :     /* -------------------------------------------------------------------- */
    5816             :     /*      Compute the uncompressed size.                                  */
    5817             :     /* -------------------------------------------------------------------- */
    5818        9531 :     const unsigned nTileXCount =
    5819        9531 :         bTiled ? DIV_ROUND_UP(nXSize, l_nBlockXSize) : 0;
    5820        9531 :     const unsigned nTileYCount =
    5821        9531 :         bTiled ? DIV_ROUND_UP(nYSize, l_nBlockYSize) : 0;
    5822             :     const double dfUncompressedImageSize =
    5823        9531 :         (bTiled ? (static_cast<double>(nTileXCount) * nTileYCount *
    5824         694 :                    l_nBlockXSize * l_nBlockYSize)
    5825        8837 :                 : (nXSize * static_cast<double>(nYSize))) *
    5826        9531 :             l_nBands * GDALGetDataTypeSizeBytes(eType) +
    5827        9531 :         dfExtraSpaceForOverviews;
    5828             : 
    5829             :     /* -------------------------------------------------------------------- */
    5830             :     /*      Should the file be created as a bigtiff file?                   */
    5831             :     /* -------------------------------------------------------------------- */
    5832        9531 :     const char *pszBIGTIFF = CSLFetchNameValue(papszParamList, "BIGTIFF");
    5833             : 
    5834        9531 :     if (pszBIGTIFF == nullptr)
    5835        9109 :         pszBIGTIFF = "IF_NEEDED";
    5836             : 
    5837        9531 :     bool bCreateBigTIFF = false;
    5838        9531 :     if (EQUAL(pszBIGTIFF, "IF_NEEDED"))
    5839             :     {
    5840        9110 :         if (l_nCompression == COMPRESSION_NONE &&
    5841             :             dfUncompressedImageSize > 4200000000.0)
    5842          17 :             bCreateBigTIFF = true;
    5843             :     }
    5844         421 :     else if (EQUAL(pszBIGTIFF, "IF_SAFER"))
    5845             :     {
    5846         401 :         if (dfUncompressedImageSize > 2000000000.0)
    5847           1 :             bCreateBigTIFF = true;
    5848             :     }
    5849             :     else
    5850             :     {
    5851          20 :         bCreateBigTIFF = CPLTestBool(pszBIGTIFF);
    5852          20 :         if (!bCreateBigTIFF && l_nCompression == COMPRESSION_NONE &&
    5853             :             dfUncompressedImageSize > 4200000000.0)
    5854             :         {
    5855           2 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5856             :                         "The TIFF file will be larger than 4GB, so BigTIFF is "
    5857             :                         "necessary.  Creation failed.");
    5858           2 :             return nullptr;
    5859             :         }
    5860             :     }
    5861             : 
    5862        9529 :     if (bCreateBigTIFF)
    5863          34 :         CPLDebug("GTiff", "File being created as a BigTIFF.");
    5864             : 
    5865             :     /* -------------------------------------------------------------------- */
    5866             :     /*      Sanity check.                                                   */
    5867             :     /* -------------------------------------------------------------------- */
    5868        9529 :     if (bTiled)
    5869             :     {
    5870             :         // libtiff implementation limitation
    5871         694 :         if (nTileXCount > 0x80000000U / (bCreateBigTIFF ? 8 : 4) / nTileYCount)
    5872             :         {
    5873           3 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5874             :                         "File too large regarding tile size. This would result "
    5875             :                         "in a file with tile arrays larger than 2GB");
    5876           3 :             return nullptr;
    5877             :         }
    5878             :     }
    5879             : 
    5880             :     /* -------------------------------------------------------------------- */
    5881             :     /*      Check free space (only for big, non sparse)                     */
    5882             :     /* -------------------------------------------------------------------- */
    5883        9526 :     const double dfLikelyFloorOfFinalSize =
    5884             :         l_nCompression == COMPRESSION_NONE
    5885        9526 :             ? dfUncompressedImageSize
    5886             :             :
    5887             :             /* For compressed, we target 1% as the most optimistic reduction factor! */
    5888             :             0.01 * dfUncompressedImageSize;
    5889        9547 :     if (dfLikelyFloorOfFinalSize >= 1e9 &&
    5890          21 :         !CPLFetchBool(papszParamList, "SPARSE_OK", false) &&
    5891           4 :         osOriFilename != "/vsistdout/" &&
    5892        9551 :         osOriFilename != "/vsistdout_redirect/" &&
    5893           4 :         CPLTestBool(CPLGetConfigOption("CHECK_DISK_FREE_SPACE", "TRUE")))
    5894             :     {
    5895             :         const GIntBig nFreeDiskSpace =
    5896           3 :             VSIGetDiskFreeSpace(CPLGetDirnameSafe(pszFilename).c_str());
    5897           3 :         if (nFreeDiskSpace >= 0 && nFreeDiskSpace < dfLikelyFloorOfFinalSize)
    5898             :         {
    5899           6 :             ReportError(
    5900             :                 pszFilename, CE_Failure, CPLE_FileIO,
    5901             :                 "Free disk space available is %s, "
    5902             :                 "whereas %s are %s necessary. "
    5903             :                 "You can disable this check by defining the "
    5904             :                 "CHECK_DISK_FREE_SPACE configuration option to FALSE.",
    5905           4 :                 CPLFormatReadableFileSize(static_cast<uint64_t>(nFreeDiskSpace))
    5906             :                     .c_str(),
    5907           4 :                 CPLFormatReadableFileSize(dfLikelyFloorOfFinalSize).c_str(),
    5908             :                 l_nCompression == COMPRESSION_NONE
    5909             :                     ? "at least"
    5910             :                     : "likely at least (probably more)");
    5911           2 :             return nullptr;
    5912             :         }
    5913             :     }
    5914             : 
    5915             :     /* -------------------------------------------------------------------- */
    5916             :     /*      Check if the user wishes a particular endianness                */
    5917             :     /* -------------------------------------------------------------------- */
    5918             : 
    5919        9524 :     int eEndianness = ENDIANNESS_NATIVE;
    5920        9524 :     const char *pszEndianness = CSLFetchNameValue(papszParamList, "ENDIANNESS");
    5921        9524 :     if (pszEndianness == nullptr)
    5922        9461 :         pszEndianness = CPLGetConfigOption("GDAL_TIFF_ENDIANNESS", nullptr);
    5923        9524 :     if (pszEndianness != nullptr)
    5924             :     {
    5925         123 :         if (EQUAL(pszEndianness, "LITTLE"))
    5926             :         {
    5927          36 :             eEndianness = ENDIANNESS_LITTLE;
    5928             :         }
    5929          87 :         else if (EQUAL(pszEndianness, "BIG"))
    5930             :         {
    5931           1 :             eEndianness = ENDIANNESS_BIG;
    5932             :         }
    5933          86 :         else if (EQUAL(pszEndianness, "INVERTED"))
    5934             :         {
    5935             : #ifdef CPL_LSB
    5936          82 :             eEndianness = ENDIANNESS_BIG;
    5937             : #else
    5938             :             eEndianness = ENDIANNESS_LITTLE;
    5939             : #endif
    5940             :         }
    5941           4 :         else if (!EQUAL(pszEndianness, "NATIVE"))
    5942             :         {
    5943           1 :             ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5944             :                         "ENDIANNESS=%s not supported. Defaulting to NATIVE",
    5945             :                         pszEndianness);
    5946             :         }
    5947             :     }
    5948             : 
    5949             :     /* -------------------------------------------------------------------- */
    5950             :     /*      Try opening the dataset.                                        */
    5951             :     /* -------------------------------------------------------------------- */
    5952             : 
    5953             :     const bool bAppend =
    5954        9524 :         CPLFetchBool(papszParamList, "APPEND_SUBDATASET", false);
    5955             : 
    5956        9524 :     char szOpeningFlag[5] = {};
    5957        9524 :     strcpy(szOpeningFlag, bAppend ? "r+" : "w+");
    5958        9524 :     if (bCreateBigTIFF)
    5959          31 :         strcat(szOpeningFlag, "8");
    5960        9524 :     if (eEndianness == ENDIANNESS_BIG)
    5961          83 :         strcat(szOpeningFlag, "b");
    5962        9441 :     else if (eEndianness == ENDIANNESS_LITTLE)
    5963          36 :         strcat(szOpeningFlag, "l");
    5964             : 
    5965        9524 :     VSIErrorReset();
    5966        9524 :     const bool bOnlyVisibleAtCloseTime = CPLTestBool(CSLFetchNameValueDef(
    5967             :         papszParamList, "@CREATE_ONLY_VISIBLE_AT_CLOSE_TIME", "NO"));
    5968        9524 :     const bool bSuppressASAP = CPLTestBool(
    5969             :         CSLFetchNameValueDef(papszParamList, "@SUPPRESS_ASAP", "NO"));
    5970             :     auto l_fpL =
    5971        9524 :         (bOnlyVisibleAtCloseTime || bSuppressASAP) && !bAppend
    5972        9558 :             ? VSIFileManager::GetHandler(pszFilename)
    5973          68 :                   ->CreateOnlyVisibleAtCloseTime(pszFilename, true, nullptr)
    5974          34 :                   .release()
    5975       19014 :             : VSIFilesystemHandler::OpenStatic(pszFilename,
    5976             :                                                bAppend ? "r+b" : "w+b", true)
    5977        9524 :                   .release();
    5978        9524 :     if (l_fpL == nullptr)
    5979             :     {
    5980          21 :         VSIToCPLErrorWithMsg(CE_Failure, CPLE_OpenFailed,
    5981          42 :                              std::string("Attempt to create new tiff file `")
    5982          21 :                                  .append(pszFilename)
    5983          21 :                                  .append("' failed")
    5984             :                                  .c_str());
    5985          21 :         return nullptr;
    5986             :     }
    5987             : 
    5988        9503 :     if (bSuppressASAP)
    5989             :     {
    5990           4 :         l_fpL->CancelCreation();
    5991             :     }
    5992             : 
    5993        9503 :     TIFF *l_hTIFF = VSI_TIFFOpen(pszFilename, szOpeningFlag, l_fpL);
    5994        9503 :     if (l_hTIFF == nullptr)
    5995             :     {
    5996           2 :         if (CPLGetLastErrorNo() == 0)
    5997           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    5998             :                      "Attempt to create new tiff file `%s' "
    5999             :                      "failed in XTIFFOpen().",
    6000             :                      pszFilename);
    6001           2 :         l_fpL->CancelCreation();
    6002           2 :         CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6003           2 :         return nullptr;
    6004             :     }
    6005             : 
    6006        9501 :     if (bAppend)
    6007             :     {
    6008             : #if !(defined(INTERNAL_LIBTIFF) || TIFFLIB_VERSION > 20240911)
    6009             :         // This is a bit of a hack to cause (*tif->tif_cleanup)(tif); to be
    6010             :         // called. See https://trac.osgeo.org/gdal/ticket/2055
    6011             :         // Fixed in libtiff > 4.7.0
    6012             :         TIFFSetField(l_hTIFF, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
    6013             :         TIFFFreeDirectory(l_hTIFF);
    6014             : #endif
    6015           6 :         TIFFCreateDirectory(l_hTIFF);
    6016             :     }
    6017             : 
    6018             :     /* -------------------------------------------------------------------- */
    6019             :     /*      Do we have a custom pixel type (just used for signed byte now). */
    6020             :     /* -------------------------------------------------------------------- */
    6021        9501 :     const char *pszPixelType = CSLFetchNameValue(papszParamList, "PIXELTYPE");
    6022        9501 :     if (pszPixelType == nullptr)
    6023        9493 :         pszPixelType = "";
    6024        9501 :     if (eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE"))
    6025             :     {
    6026           8 :         CPLError(CE_Warning, CPLE_AppDefined,
    6027             :                  "Using PIXELTYPE=SIGNEDBYTE with Byte data type is deprecated "
    6028             :                  "(but still works). "
    6029             :                  "Using Int8 data type instead is now recommended.");
    6030             :     }
    6031             : 
    6032             :     /* -------------------------------------------------------------------- */
    6033             :     /*      Setup some standard flags.                                      */
    6034             :     /* -------------------------------------------------------------------- */
    6035        9501 :     TIFFSetField(l_hTIFF, TIFFTAG_IMAGEWIDTH, nXSize);
    6036        9501 :     TIFFSetField(l_hTIFF, TIFFTAG_IMAGELENGTH, nYSize);
    6037        9501 :     TIFFSetField(l_hTIFF, TIFFTAG_BITSPERSAMPLE, l_nBitsPerSample);
    6038             : 
    6039        9501 :     uint16_t l_nSampleFormat = 0;
    6040        9501 :     if ((eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE")) ||
    6041        9358 :         eType == GDT_Int8 || eType == GDT_Int16 || eType == GDT_Int32 ||
    6042             :         eType == GDT_Int64)
    6043         773 :         l_nSampleFormat = SAMPLEFORMAT_INT;
    6044        8728 :     else if (eType == GDT_CInt16 || eType == GDT_CInt32)
    6045         355 :         l_nSampleFormat = SAMPLEFORMAT_COMPLEXINT;
    6046        8373 :     else if (eType == GDT_Float16 || eType == GDT_Float32 ||
    6047             :              eType == GDT_Float64)
    6048        1066 :         l_nSampleFormat = SAMPLEFORMAT_IEEEFP;
    6049        7307 :     else if (eType == GDT_CFloat16 || eType == GDT_CFloat32 ||
    6050             :              eType == GDT_CFloat64)
    6051         463 :         l_nSampleFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
    6052             :     else
    6053        6844 :         l_nSampleFormat = SAMPLEFORMAT_UINT;
    6054             : 
    6055        9501 :     TIFFSetField(l_hTIFF, TIFFTAG_SAMPLEFORMAT, l_nSampleFormat);
    6056        9501 :     TIFFSetField(l_hTIFF, TIFFTAG_SAMPLESPERPIXEL, l_nBands);
    6057        9501 :     TIFFSetField(l_hTIFF, TIFFTAG_PLANARCONFIG, nPlanar);
    6058             : 
    6059             :     /* -------------------------------------------------------------------- */
    6060             :     /*      Setup Photometric Interpretation. Take this value from the user */
    6061             :     /*      passed option or guess correct value otherwise.                 */
    6062             :     /* -------------------------------------------------------------------- */
    6063        9501 :     int nSamplesAccountedFor = 1;
    6064        9501 :     bool bForceColorTable = false;
    6065             : 
    6066        9501 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "PHOTOMETRIC"))
    6067             :     {
    6068        1896 :         if (EQUAL(pszValue, "MINISBLACK"))
    6069          14 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6070        1882 :         else if (EQUAL(pszValue, "MINISWHITE"))
    6071             :         {
    6072           2 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
    6073             :         }
    6074        1880 :         else if (EQUAL(pszValue, "PALETTE"))
    6075             :         {
    6076           5 :             if (eType == GDT_Byte || eType == GDT_UInt16)
    6077             :             {
    6078           4 :                 TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    6079           4 :                 nSamplesAccountedFor = 1;
    6080           4 :                 bForceColorTable = true;
    6081             :             }
    6082             :             else
    6083             :             {
    6084           1 :                 ReportError(
    6085             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    6086             :                     "PHOTOMETRIC=PALETTE only compatible with Byte or UInt16");
    6087             :             }
    6088             :         }
    6089        1875 :         else if (EQUAL(pszValue, "RGB"))
    6090             :         {
    6091        1131 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    6092        1131 :             nSamplesAccountedFor = 3;
    6093             :         }
    6094         744 :         else if (EQUAL(pszValue, "CMYK"))
    6095             :         {
    6096          10 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
    6097          10 :             nSamplesAccountedFor = 4;
    6098             :         }
    6099         734 :         else if (EQUAL(pszValue, "YCBCR"))
    6100             :         {
    6101             :             // Because of subsampling, setting YCBCR without JPEG compression
    6102             :             // leads to a crash currently. Would need to make
    6103             :             // GTiffRasterBand::IWriteBlock() aware of subsampling so that it
    6104             :             // doesn't overrun buffer size returned by libtiff.
    6105         733 :             if (l_nCompression != COMPRESSION_JPEG)
    6106             :             {
    6107           1 :                 ReportError(
    6108             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    6109             :                     "Currently, PHOTOMETRIC=YCBCR requires COMPRESS=JPEG");
    6110           1 :                 XTIFFClose(l_hTIFF);
    6111           1 :                 l_fpL->CancelCreation();
    6112           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6113           1 :                 return nullptr;
    6114             :             }
    6115             : 
    6116         732 :             if (nPlanar == PLANARCONFIG_SEPARATE)
    6117             :             {
    6118           1 :                 ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    6119             :                             "PHOTOMETRIC=YCBCR requires INTERLEAVE=PIXEL");
    6120           1 :                 XTIFFClose(l_hTIFF);
    6121           1 :                 l_fpL->CancelCreation();
    6122           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6123           1 :                 return nullptr;
    6124             :             }
    6125             : 
    6126             :             // YCBCR strictly requires 3 bands. Not less, not more Issue an
    6127             :             // explicit error message as libtiff one is a bit cryptic:
    6128             :             // TIFFVStripSize64:Invalid td_samplesperpixel value.
    6129         731 :             if (l_nBands != 3)
    6130             :             {
    6131           1 :                 ReportError(
    6132             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    6133             :                     "PHOTOMETRIC=YCBCR not supported on a %d-band raster: "
    6134             :                     "only compatible of a 3-band (RGB) raster",
    6135             :                     l_nBands);
    6136           1 :                 XTIFFClose(l_hTIFF);
    6137           1 :                 l_fpL->CancelCreation();
    6138           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6139           1 :                 return nullptr;
    6140             :             }
    6141             : 
    6142         730 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
    6143         730 :             nSamplesAccountedFor = 3;
    6144             : 
    6145             :             // Explicitly register the subsampling so that JPEGFixupTags
    6146             :             // is a no-op (helps for cloud optimized geotiffs)
    6147         730 :             TIFFSetField(l_hTIFF, TIFFTAG_YCBCRSUBSAMPLING, 2, 2);
    6148             :         }
    6149           1 :         else if (EQUAL(pszValue, "CIELAB"))
    6150             :         {
    6151           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB);
    6152           0 :             nSamplesAccountedFor = 3;
    6153             :         }
    6154           1 :         else if (EQUAL(pszValue, "ICCLAB"))
    6155             :         {
    6156           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ICCLAB);
    6157           0 :             nSamplesAccountedFor = 3;
    6158             :         }
    6159           1 :         else if (EQUAL(pszValue, "ITULAB"))
    6160             :         {
    6161           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ITULAB);
    6162           0 :             nSamplesAccountedFor = 3;
    6163             :         }
    6164             :         else
    6165             :         {
    6166           1 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    6167             :                         "PHOTOMETRIC=%s value not recognised, ignoring.  "
    6168             :                         "Set the Photometric Interpretation as MINISBLACK.",
    6169             :                         pszValue);
    6170           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6171             :         }
    6172             : 
    6173        1893 :         if (l_nBands < nSamplesAccountedFor)
    6174             :         {
    6175           1 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    6176             :                         "PHOTOMETRIC=%s value does not correspond to number "
    6177             :                         "of bands (%d), ignoring.  "
    6178             :                         "Set the Photometric Interpretation as MINISBLACK.",
    6179             :                         pszValue, l_nBands);
    6180           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6181             :         }
    6182             :     }
    6183             :     else
    6184             :     {
    6185             :         // If image contains 3 or 4 bands and datatype is Byte then we will
    6186             :         // assume it is RGB. In all other cases assume it is MINISBLACK.
    6187        7605 :         if (l_nBands == 3 && eType == GDT_Byte)
    6188             :         {
    6189         312 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    6190         312 :             nSamplesAccountedFor = 3;
    6191             :         }
    6192        7293 :         else if (l_nBands == 4 && eType == GDT_Byte)
    6193             :         {
    6194             :             uint16_t v[1] = {
    6195         717 :                 GTiffGetAlphaValue(CSLFetchNameValue(papszParamList, "ALPHA"),
    6196         717 :                                    DEFAULT_ALPHA_TYPE)};
    6197             : 
    6198         717 :             TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
    6199         717 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    6200         717 :             nSamplesAccountedFor = 4;
    6201             :         }
    6202             :         else
    6203             :         {
    6204        6576 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6205        6576 :             nSamplesAccountedFor = 1;
    6206             :         }
    6207             :     }
    6208             : 
    6209             :     /* -------------------------------------------------------------------- */
    6210             :     /*      If there are extra samples, we need to mark them with an        */
    6211             :     /*      appropriate extrasamples definition here.                       */
    6212             :     /* -------------------------------------------------------------------- */
    6213        9498 :     if (l_nBands > nSamplesAccountedFor)
    6214             :     {
    6215        1321 :         const int nExtraSamples = l_nBands - nSamplesAccountedFor;
    6216             : 
    6217             :         uint16_t *v = static_cast<uint16_t *>(
    6218        1321 :             CPLMalloc(sizeof(uint16_t) * nExtraSamples));
    6219             : 
    6220        1321 :         v[0] = GTiffGetAlphaValue(CSLFetchNameValue(papszParamList, "ALPHA"),
    6221             :                                   EXTRASAMPLE_UNSPECIFIED);
    6222             : 
    6223      297583 :         for (int i = 1; i < nExtraSamples; ++i)
    6224      296262 :             v[i] = EXTRASAMPLE_UNSPECIFIED;
    6225             : 
    6226        1321 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples, v);
    6227             : 
    6228        1321 :         CPLFree(v);
    6229             :     }
    6230             : 
    6231             :     // Set the ICC color profile.
    6232        9498 :     if (eProfile != GTiffProfile::BASELINE)
    6233             :     {
    6234        9473 :         SaveICCProfile(nullptr, l_hTIFF, papszParamList, l_nBitsPerSample);
    6235             :     }
    6236             : 
    6237             :     // Set the compression method before asking the default strip size
    6238             :     // This is useful when translating to a JPEG-In-TIFF file where
    6239             :     // the default strip size is 8 or 16 depending on the photometric value.
    6240        9498 :     TIFFSetField(l_hTIFF, TIFFTAG_COMPRESSION, l_nCompression);
    6241             : 
    6242        9498 :     if (l_nCompression == COMPRESSION_LERC)
    6243             :     {
    6244             :         const char *pszCompress =
    6245          97 :             CSLFetchNameValueDef(papszParamList, "COMPRESS", "");
    6246          97 :         if (EQUAL(pszCompress, "LERC_DEFLATE"))
    6247             :         {
    6248          16 :             TIFFSetField(l_hTIFF, TIFFTAG_LERC_ADD_COMPRESSION,
    6249             :                          LERC_ADD_COMPRESSION_DEFLATE);
    6250             :         }
    6251          81 :         else if (EQUAL(pszCompress, "LERC_ZSTD"))
    6252             :         {
    6253          14 :             if (TIFFSetField(l_hTIFF, TIFFTAG_LERC_ADD_COMPRESSION,
    6254          14 :                              LERC_ADD_COMPRESSION_ZSTD) != 1)
    6255             :             {
    6256           0 :                 XTIFFClose(l_hTIFF);
    6257           0 :                 l_fpL->CancelCreation();
    6258           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6259           0 :                 return nullptr;
    6260             :             }
    6261             :         }
    6262             :     }
    6263             :     // TODO later: take into account LERC version
    6264             : 
    6265             :     /* -------------------------------------------------------------------- */
    6266             :     /*      Setup tiling/stripping flags.                                   */
    6267             :     /* -------------------------------------------------------------------- */
    6268        9498 :     if (bTiled)
    6269             :     {
    6270        1368 :         if (!TIFFSetField(l_hTIFF, TIFFTAG_TILEWIDTH, l_nBlockXSize) ||
    6271         684 :             !TIFFSetField(l_hTIFF, TIFFTAG_TILELENGTH, l_nBlockYSize))
    6272             :         {
    6273           0 :             XTIFFClose(l_hTIFF);
    6274           0 :             l_fpL->CancelCreation();
    6275           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6276           0 :             return nullptr;
    6277             :         }
    6278             :     }
    6279             :     else
    6280             :     {
    6281        8814 :         const uint32_t l_nRowsPerStrip = std::min(
    6282             :             nYSize, l_nBlockYSize == 0
    6283        8814 :                         ? static_cast<int>(TIFFDefaultStripSize(l_hTIFF, 0))
    6284        8814 :                         : l_nBlockYSize);
    6285             : 
    6286        8814 :         TIFFSetField(l_hTIFF, TIFFTAG_ROWSPERSTRIP, l_nRowsPerStrip);
    6287             :     }
    6288             : 
    6289             :     /* -------------------------------------------------------------------- */
    6290             :     /*      Set compression related tags.                                   */
    6291             :     /* -------------------------------------------------------------------- */
    6292        9498 :     if (GTIFFSupportsPredictor(l_nCompression))
    6293         866 :         TIFFSetField(l_hTIFF, TIFFTAG_PREDICTOR, nPredictor);
    6294        9498 :     if (l_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    6295             :         l_nCompression == COMPRESSION_LERC)
    6296             :     {
    6297         277 :         GTiffSetDeflateSubCodec(l_hTIFF);
    6298             : 
    6299         277 :         if (l_nZLevel != -1)
    6300          22 :             TIFFSetField(l_hTIFF, TIFFTAG_ZIPQUALITY, l_nZLevel);
    6301             :     }
    6302        9498 :     if (l_nCompression == COMPRESSION_JPEG && l_nJpegQuality != -1)
    6303        1905 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGQUALITY, l_nJpegQuality);
    6304        9498 :     if (l_nCompression == COMPRESSION_LZMA && l_nLZMAPreset != -1)
    6305          10 :         TIFFSetField(l_hTIFF, TIFFTAG_LZMAPRESET, l_nLZMAPreset);
    6306        9498 :     if ((l_nCompression == COMPRESSION_ZSTD ||
    6307         156 :          l_nCompression == COMPRESSION_LERC) &&
    6308             :         l_nZSTDLevel != -1)
    6309          12 :         TIFFSetField(l_hTIFF, TIFFTAG_ZSTD_LEVEL, l_nZSTDLevel);
    6310        9498 :     if (l_nCompression == COMPRESSION_LERC)
    6311             :     {
    6312          97 :         TIFFSetField(l_hTIFF, TIFFTAG_LERC_MAXZERROR, l_dfMaxZError);
    6313             :     }
    6314             : #if HAVE_JXL
    6315        9498 :     if (l_nCompression == COMPRESSION_JXL ||
    6316             :         l_nCompression == COMPRESSION_JXL_DNG_1_7)
    6317             :     {
    6318         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_LOSSYNESS,
    6319             :                      l_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
    6320         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_EFFORT, l_nJXLEffort);
    6321         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_DISTANCE,
    6322             :                      static_cast<double>(l_fJXLDistance));
    6323         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE,
    6324             :                      static_cast<double>(l_fJXLAlphaDistance));
    6325             :     }
    6326             : #endif
    6327        9498 :     if (l_nCompression == COMPRESSION_WEBP)
    6328          33 :         TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LEVEL, l_nWebPLevel);
    6329        9498 :     if (l_nCompression == COMPRESSION_WEBP && l_bWebPLossless)
    6330           7 :         TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LOSSLESS, 1);
    6331             : 
    6332        9498 :     if (l_nCompression == COMPRESSION_JPEG)
    6333        2087 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGTABLESMODE, l_nJpegTablesMode);
    6334             : 
    6335             :     /* -------------------------------------------------------------------- */
    6336             :     /*      If we forced production of a file with photometric=palette,     */
    6337             :     /*      we need to push out a default color table.                      */
    6338             :     /* -------------------------------------------------------------------- */
    6339        9498 :     if (bForceColorTable)
    6340             :     {
    6341           4 :         const int nColors = eType == GDT_Byte ? 256 : 65536;
    6342             : 
    6343             :         unsigned short *panTRed = static_cast<unsigned short *>(
    6344           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    6345             :         unsigned short *panTGreen = static_cast<unsigned short *>(
    6346           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    6347             :         unsigned short *panTBlue = static_cast<unsigned short *>(
    6348           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    6349             : 
    6350        1028 :         for (int iColor = 0; iColor < nColors; ++iColor)
    6351             :         {
    6352        1024 :             if (eType == GDT_Byte)
    6353             :             {
    6354        1024 :                 panTRed[iColor] = GTiffDataset::ClampCTEntry(
    6355             :                     iColor, 1, iColor, nColorTableMultiplier);
    6356        1024 :                 panTGreen[iColor] = GTiffDataset::ClampCTEntry(
    6357             :                     iColor, 2, iColor, nColorTableMultiplier);
    6358        1024 :                 panTBlue[iColor] = GTiffDataset::ClampCTEntry(
    6359             :                     iColor, 3, iColor, nColorTableMultiplier);
    6360             :             }
    6361             :             else
    6362             :             {
    6363           0 :                 panTRed[iColor] = static_cast<unsigned short>(iColor);
    6364           0 :                 panTGreen[iColor] = static_cast<unsigned short>(iColor);
    6365           0 :                 panTBlue[iColor] = static_cast<unsigned short>(iColor);
    6366             :             }
    6367             :         }
    6368             : 
    6369           4 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue);
    6370             : 
    6371           4 :         CPLFree(panTRed);
    6372           4 :         CPLFree(panTGreen);
    6373           4 :         CPLFree(panTBlue);
    6374             :     }
    6375             : 
    6376             :     // This trick
    6377             :     // creates a temporary in-memory file and fetches its JPEG tables so that
    6378             :     // we can directly set them, before tif_jpeg.c compute them at the first
    6379             :     // strip/tile writing, which is too late, since we have already crystalized
    6380             :     // the directory. This way we avoid a directory rewriting.
    6381       11585 :     if (l_nCompression == COMPRESSION_JPEG &&
    6382        2087 :         CPLTestBool(
    6383             :             CSLFetchNameValueDef(papszParamList, "WRITE_JPEGTABLE_TAG", "YES")))
    6384             :     {
    6385        1014 :         GTiffWriteJPEGTables(
    6386             :             l_hTIFF, CSLFetchNameValue(papszParamList, "PHOTOMETRIC"),
    6387             :             CSLFetchNameValue(papszParamList, "JPEG_QUALITY"),
    6388             :             CSLFetchNameValue(papszParamList, "JPEGTABLESMODE"));
    6389             :     }
    6390             : 
    6391        9498 :     *pfpL = l_fpL;
    6392             : 
    6393        9498 :     return l_hTIFF;
    6394             : }
    6395             : 
    6396             : /************************************************************************/
    6397             : /*                            GuessJPEGQuality()                        */
    6398             : /*                                                                      */
    6399             : /*      Guess JPEG quality from JPEGTABLES tag.                         */
    6400             : /************************************************************************/
    6401             : 
    6402        3915 : static const GByte *GTIFFFindNextTable(const GByte *paby, GByte byMarker,
    6403             :                                        int nLen, int *pnLenTable)
    6404             : {
    6405        8097 :     for (int i = 0; i + 1 < nLen;)
    6406             :     {
    6407        8097 :         if (paby[i] != 0xFF)
    6408           0 :             return nullptr;
    6409        8097 :         ++i;
    6410        8097 :         if (paby[i] == 0xD8)
    6411             :         {
    6412        3156 :             ++i;
    6413        3156 :             continue;
    6414             :         }
    6415        4941 :         if (i + 2 >= nLen)
    6416         859 :             return nullptr;
    6417        4082 :         int nMarkerLen = paby[i + 1] * 256 + paby[i + 2];
    6418        4082 :         if (i + 1 + nMarkerLen >= nLen)
    6419           0 :             return nullptr;
    6420        4082 :         if (paby[i] == byMarker)
    6421             :         {
    6422        3056 :             if (pnLenTable)
    6423        2499 :                 *pnLenTable = nMarkerLen;
    6424        3056 :             return paby + i + 1;
    6425             :         }
    6426        1026 :         i += 1 + nMarkerLen;
    6427             :     }
    6428           0 :     return nullptr;
    6429             : }
    6430             : 
    6431             : constexpr GByte MARKER_HUFFMAN_TABLE = 0xC4;
    6432             : constexpr GByte MARKER_QUANT_TABLE = 0xDB;
    6433             : 
    6434             : // We assume that if there are several quantization tables, they are
    6435             : // in the same order. Which is a reasonable assumption for updating
    6436             : // a file generated by ourselves.
    6437         904 : static bool GTIFFQuantizationTablesEqual(const GByte *paby1, int nLen1,
    6438             :                                          const GByte *paby2, int nLen2)
    6439             : {
    6440         904 :     bool bFound = false;
    6441             :     while (true)
    6442             :     {
    6443         945 :         int nLenTable1 = 0;
    6444         945 :         int nLenTable2 = 0;
    6445             :         const GByte *paby1New =
    6446         945 :             GTIFFFindNextTable(paby1, MARKER_QUANT_TABLE, nLen1, &nLenTable1);
    6447             :         const GByte *paby2New =
    6448         945 :             GTIFFFindNextTable(paby2, MARKER_QUANT_TABLE, nLen2, &nLenTable2);
    6449         945 :         if (paby1New == nullptr && paby2New == nullptr)
    6450         904 :             return bFound;
    6451         911 :         if (paby1New == nullptr || paby2New == nullptr)
    6452           0 :             return false;
    6453         911 :         if (nLenTable1 != nLenTable2)
    6454         207 :             return false;
    6455         704 :         if (memcmp(paby1New, paby2New, nLenTable1) != 0)
    6456         663 :             return false;
    6457          41 :         paby1New += nLenTable1;
    6458          41 :         paby2New += nLenTable2;
    6459          41 :         nLen1 -= static_cast<int>(paby1New - paby1);
    6460          41 :         nLen2 -= static_cast<int>(paby2New - paby2);
    6461          41 :         paby1 = paby1New;
    6462          41 :         paby2 = paby2New;
    6463          41 :         bFound = true;
    6464          41 :     }
    6465             : }
    6466             : 
    6467             : // Guess the JPEG quality by comparing against the MD5Sum of precomputed
    6468             : // quantization tables
    6469         422 : static int GuessJPEGQualityFromMD5(const uint8_t md5JPEGQuantTable[][16],
    6470             :                                    const GByte *const pabyJPEGTable,
    6471             :                                    int nJPEGTableSize)
    6472             : {
    6473         422 :     int nRemainingLen = nJPEGTableSize;
    6474         422 :     const GByte *pabyCur = pabyJPEGTable;
    6475             : 
    6476             :     struct CPLMD5Context context;
    6477         422 :     CPLMD5Init(&context);
    6478             : 
    6479             :     while (true)
    6480             :     {
    6481        1099 :         int nLenTable = 0;
    6482        1099 :         const GByte *pabyNew = GTIFFFindNextTable(pabyCur, MARKER_QUANT_TABLE,
    6483             :                                                   nRemainingLen, &nLenTable);
    6484        1099 :         if (pabyNew == nullptr)
    6485         422 :             break;
    6486         677 :         CPLMD5Update(&context, pabyNew, nLenTable);
    6487         677 :         pabyNew += nLenTable;
    6488         677 :         nRemainingLen -= static_cast<int>(pabyNew - pabyCur);
    6489         677 :         pabyCur = pabyNew;
    6490         677 :     }
    6491             : 
    6492             :     GByte digest[16];
    6493         422 :     CPLMD5Final(digest, &context);
    6494             : 
    6495       29821 :     for (int i = 0; i < 100; i++)
    6496             :     {
    6497       29818 :         if (memcmp(md5JPEGQuantTable[i], digest, 16) == 0)
    6498             :         {
    6499         419 :             return i + 1;
    6500             :         }
    6501             :     }
    6502           3 :     return -1;
    6503             : }
    6504             : 
    6505         477 : int GTiffDataset::GuessJPEGQuality(bool &bOutHasQuantizationTable,
    6506             :                                    bool &bOutHasHuffmanTable)
    6507             : {
    6508         477 :     CPLAssert(m_nCompression == COMPRESSION_JPEG);
    6509         477 :     uint32_t nJPEGTableSize = 0;
    6510         477 :     void *pJPEGTable = nullptr;
    6511         477 :     if (!TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize,
    6512             :                       &pJPEGTable))
    6513             :     {
    6514          14 :         bOutHasQuantizationTable = false;
    6515          14 :         bOutHasHuffmanTable = false;
    6516          14 :         return -1;
    6517             :     }
    6518             : 
    6519         463 :     bOutHasQuantizationTable =
    6520         463 :         GTIFFFindNextTable(static_cast<const GByte *>(pJPEGTable),
    6521             :                            MARKER_QUANT_TABLE, nJPEGTableSize,
    6522         463 :                            nullptr) != nullptr;
    6523         463 :     bOutHasHuffmanTable =
    6524         463 :         GTIFFFindNextTable(static_cast<const GByte *>(pJPEGTable),
    6525             :                            MARKER_HUFFMAN_TABLE, nJPEGTableSize,
    6526         463 :                            nullptr) != nullptr;
    6527         463 :     if (!bOutHasQuantizationTable)
    6528           7 :         return -1;
    6529             : 
    6530         456 :     if ((nBands == 1 && m_nBitsPerSample == 8) ||
    6531         395 :         (nBands == 3 && m_nBitsPerSample == 8 &&
    6532         349 :          m_nPhotometric == PHOTOMETRIC_RGB) ||
    6533         301 :         (nBands == 4 && m_nBitsPerSample == 8 &&
    6534          27 :          m_nPhotometric == PHOTOMETRIC_SEPARATED))
    6535             :     {
    6536         167 :         return GuessJPEGQualityFromMD5(md5JPEGQuantTable_generic_8bit,
    6537             :                                        static_cast<const GByte *>(pJPEGTable),
    6538         167 :                                        static_cast<int>(nJPEGTableSize));
    6539             :     }
    6540             : 
    6541         289 :     if (nBands == 3 && m_nBitsPerSample == 8 &&
    6542         255 :         m_nPhotometric == PHOTOMETRIC_YCBCR)
    6543             :     {
    6544             :         int nRet =
    6545         255 :             GuessJPEGQualityFromMD5(md5JPEGQuantTable_3_YCBCR_8bit,
    6546             :                                     static_cast<const GByte *>(pJPEGTable),
    6547             :                                     static_cast<int>(nJPEGTableSize));
    6548         255 :         if (nRet < 0)
    6549             :         {
    6550             :             // libjpeg 9e has modified the YCbCr quantization tables.
    6551             :             nRet =
    6552           0 :                 GuessJPEGQualityFromMD5(md5JPEGQuantTable_3_YCBCR_8bit_jpeg9e,
    6553             :                                         static_cast<const GByte *>(pJPEGTable),
    6554             :                                         static_cast<int>(nJPEGTableSize));
    6555             :         }
    6556         255 :         return nRet;
    6557             :     }
    6558             : 
    6559          34 :     char **papszLocalParameters = nullptr;
    6560             :     papszLocalParameters =
    6561          34 :         CSLSetNameValue(papszLocalParameters, "COMPRESS", "JPEG");
    6562          34 :     if (m_nPhotometric == PHOTOMETRIC_YCBCR)
    6563             :         papszLocalParameters =
    6564           7 :             CSLSetNameValue(papszLocalParameters, "PHOTOMETRIC", "YCBCR");
    6565          27 :     else if (m_nPhotometric == PHOTOMETRIC_SEPARATED)
    6566             :         papszLocalParameters =
    6567           0 :             CSLSetNameValue(papszLocalParameters, "PHOTOMETRIC", "CMYK");
    6568             :     papszLocalParameters =
    6569          34 :         CSLSetNameValue(papszLocalParameters, "BLOCKYSIZE", "16");
    6570          34 :     if (m_nBitsPerSample == 12)
    6571             :         papszLocalParameters =
    6572          16 :             CSLSetNameValue(papszLocalParameters, "NBITS", "12");
    6573             : 
    6574             :     const CPLString osTmpFilenameIn(
    6575          34 :         VSIMemGenerateHiddenFilename("gtiffdataset_guess_jpeg_quality_tmp"));
    6576             : 
    6577          34 :     int nRet = -1;
    6578         938 :     for (int nQuality = 0; nQuality <= 100 && nRet < 0; ++nQuality)
    6579             :     {
    6580         904 :         VSILFILE *fpTmp = nullptr;
    6581         904 :         if (nQuality == 0)
    6582             :             papszLocalParameters =
    6583          34 :                 CSLSetNameValue(papszLocalParameters, "JPEG_QUALITY", "75");
    6584             :         else
    6585             :             papszLocalParameters =
    6586         870 :                 CSLSetNameValue(papszLocalParameters, "JPEG_QUALITY",
    6587             :                                 CPLSPrintf("%d", nQuality));
    6588             : 
    6589         904 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    6590         904 :         CPLString osTmp;
    6591             :         bool bTileInterleaving;
    6592        1808 :         TIFF *hTIFFTmp = CreateLL(
    6593         904 :             osTmpFilenameIn, 16, 16, (nBands <= 4) ? nBands : 1,
    6594             :             GetRasterBand(1)->GetRasterDataType(), 0.0, 0, papszLocalParameters,
    6595             :             &fpTmp, osTmp, /* bCreateCopy=*/false, bTileInterleaving);
    6596         904 :         CPLPopErrorHandler();
    6597         904 :         if (!hTIFFTmp)
    6598             :         {
    6599           0 :             break;
    6600             :         }
    6601             : 
    6602         904 :         TIFFWriteCheck(hTIFFTmp, FALSE, "CreateLL");
    6603         904 :         TIFFWriteDirectory(hTIFFTmp);
    6604         904 :         TIFFSetDirectory(hTIFFTmp, 0);
    6605             :         // Now reset jpegcolormode.
    6606        1196 :         if (m_nPhotometric == PHOTOMETRIC_YCBCR &&
    6607         292 :             CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
    6608             :         {
    6609         292 :             TIFFSetField(hTIFFTmp, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    6610             :         }
    6611             : 
    6612         904 :         GByte abyZeroData[(16 * 16 * 4 * 3) / 2] = {};
    6613         904 :         const int nBlockSize =
    6614         904 :             (16 * 16 * ((nBands <= 4) ? nBands : 1) * m_nBitsPerSample) / 8;
    6615         904 :         TIFFWriteEncodedStrip(hTIFFTmp, 0, abyZeroData, nBlockSize);
    6616             : 
    6617         904 :         uint32_t nJPEGTableSizeTry = 0;
    6618         904 :         void *pJPEGTableTry = nullptr;
    6619         904 :         if (TIFFGetField(hTIFFTmp, TIFFTAG_JPEGTABLES, &nJPEGTableSizeTry,
    6620         904 :                          &pJPEGTableTry))
    6621             :         {
    6622         904 :             if (GTIFFQuantizationTablesEqual(
    6623             :                     static_cast<GByte *>(pJPEGTable), nJPEGTableSize,
    6624             :                     static_cast<GByte *>(pJPEGTableTry), nJPEGTableSizeTry))
    6625             :             {
    6626          34 :                 nRet = (nQuality == 0) ? 75 : nQuality;
    6627             :             }
    6628             :         }
    6629             : 
    6630         904 :         XTIFFClose(hTIFFTmp);
    6631         904 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpTmp));
    6632             :     }
    6633             : 
    6634          34 :     CSLDestroy(papszLocalParameters);
    6635          34 :     VSIUnlink(osTmpFilenameIn);
    6636             : 
    6637          34 :     return nRet;
    6638             : }
    6639             : 
    6640             : /************************************************************************/
    6641             : /*               SetJPEGQualityAndTablesModeFromFile()                  */
    6642             : /************************************************************************/
    6643             : 
    6644         165 : void GTiffDataset::SetJPEGQualityAndTablesModeFromFile(
    6645             :     int nQuality, bool bHasQuantizationTable, bool bHasHuffmanTable)
    6646             : {
    6647         165 :     if (nQuality > 0)
    6648             :     {
    6649         158 :         CPLDebug("GTiff", "Guessed JPEG quality to be %d", nQuality);
    6650         158 :         m_nJpegQuality = static_cast<signed char>(nQuality);
    6651         158 :         TIFFSetField(m_hTIFF, TIFFTAG_JPEGQUALITY, nQuality);
    6652             : 
    6653             :         // This means we will use the quantization tables from the
    6654             :         // JpegTables tag.
    6655         158 :         m_nJpegTablesMode = JPEGTABLESMODE_QUANT;
    6656             :     }
    6657             :     else
    6658             :     {
    6659           7 :         uint32_t nJPEGTableSize = 0;
    6660           7 :         void *pJPEGTable = nullptr;
    6661           7 :         if (!TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize,
    6662             :                           &pJPEGTable))
    6663             :         {
    6664           4 :             toff_t *panByteCounts = nullptr;
    6665           8 :             const int nBlockCount = m_nPlanarConfig == PLANARCONFIG_SEPARATE
    6666           4 :                                         ? m_nBlocksPerBand * nBands
    6667             :                                         : m_nBlocksPerBand;
    6668           4 :             if (TIFFIsTiled(m_hTIFF))
    6669           1 :                 TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts);
    6670             :             else
    6671           3 :                 TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
    6672             : 
    6673           4 :             bool bFoundNonEmptyBlock = false;
    6674           4 :             if (panByteCounts != nullptr)
    6675             :             {
    6676          56 :                 for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
    6677             :                 {
    6678          53 :                     if (panByteCounts[iBlock] != 0)
    6679             :                     {
    6680           1 :                         bFoundNonEmptyBlock = true;
    6681           1 :                         break;
    6682             :                     }
    6683             :                 }
    6684             :             }
    6685           4 :             if (bFoundNonEmptyBlock)
    6686             :             {
    6687           1 :                 CPLDebug("GTiff", "Could not guess JPEG quality. "
    6688             :                                   "JPEG tables are missing, so going in "
    6689             :                                   "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6690             :                 // Write quantization tables in each strile.
    6691           1 :                 m_nJpegTablesMode = 0;
    6692             :             }
    6693             :         }
    6694             :         else
    6695             :         {
    6696           3 :             if (bHasQuantizationTable)
    6697             :             {
    6698             :                 // FIXME in libtiff: this is likely going to cause issues
    6699             :                 // since libtiff will reuse in each strile the number of
    6700             :                 // the global quantization table, which is invalid.
    6701           1 :                 CPLDebug("GTiff",
    6702             :                          "Could not guess JPEG quality although JPEG "
    6703             :                          "quantization tables are present, so going in "
    6704             :                          "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6705             :             }
    6706             :             else
    6707             :             {
    6708           2 :                 CPLDebug("GTiff",
    6709             :                          "Could not guess JPEG quality since JPEG "
    6710             :                          "quantization tables are not present, so going in "
    6711             :                          "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6712             :             }
    6713             : 
    6714             :             // Write quantization tables in each strile.
    6715           3 :             m_nJpegTablesMode = 0;
    6716             :         }
    6717             :     }
    6718         165 :     if (bHasHuffmanTable)
    6719             :     {
    6720             :         // If there are Huffman tables in header use them, otherwise
    6721             :         // if we use optimized tables, libtiff will currently reuse
    6722             :         // the number of the Huffman tables of the header for the
    6723             :         // optimized version of each strile, which is illegal.
    6724          23 :         m_nJpegTablesMode |= JPEGTABLESMODE_HUFF;
    6725             :     }
    6726         165 :     if (m_nJpegTablesMode >= 0)
    6727         163 :         TIFFSetField(m_hTIFF, TIFFTAG_JPEGTABLESMODE, m_nJpegTablesMode);
    6728         165 : }
    6729             : 
    6730             : /************************************************************************/
    6731             : /*                               Create()                               */
    6732             : /*                                                                      */
    6733             : /*      Create a new GeoTIFF or TIFF file.                              */
    6734             : /************************************************************************/
    6735             : 
    6736        5514 : GDALDataset *GTiffDataset::Create(const char *pszFilename, int nXSize,
    6737             :                                   int nYSize, int l_nBands, GDALDataType eType,
    6738             :                                   char **papszParamList)
    6739             : 
    6740             : {
    6741        5514 :     VSILFILE *l_fpL = nullptr;
    6742       11028 :     CPLString l_osTmpFilename;
    6743             : 
    6744             :     const int nColorTableMultiplier = std::max(
    6745       11026 :         1,
    6746       11026 :         std::min(257,
    6747        5514 :                  atoi(CSLFetchNameValueDef(
    6748             :                      papszParamList, "COLOR_TABLE_MULTIPLIER",
    6749        5513 :                      CPLSPrintf("%d", DEFAULT_COLOR_TABLE_MULTIPLIER_257)))));
    6750             : 
    6751             :     /* -------------------------------------------------------------------- */
    6752             :     /*      Create the underlying TIFF file.                                */
    6753             :     /* -------------------------------------------------------------------- */
    6754             :     bool bTileInterleaving;
    6755             :     TIFF *l_hTIFF =
    6756        5513 :         CreateLL(pszFilename, nXSize, nYSize, l_nBands, eType, 0,
    6757             :                  nColorTableMultiplier, papszParamList, &l_fpL, l_osTmpFilename,
    6758             :                  /* bCreateCopy=*/false, bTileInterleaving);
    6759        5514 :     const bool bStreaming = !l_osTmpFilename.empty();
    6760             : 
    6761        5514 :     if (l_hTIFF == nullptr)
    6762          37 :         return nullptr;
    6763             : 
    6764             :     /* -------------------------------------------------------------------- */
    6765             :     /*      Create the new GTiffDataset object.                             */
    6766             :     /* -------------------------------------------------------------------- */
    6767        5477 :     GTiffDataset *poDS = new GTiffDataset();
    6768        5477 :     poDS->m_hTIFF = l_hTIFF;
    6769        5477 :     poDS->m_fpL = l_fpL;
    6770        5477 :     const bool bSuppressASAP = CPLTestBool(
    6771             :         CSLFetchNameValueDef(papszParamList, "@SUPPRESS_ASAP", "NO"));
    6772        5477 :     if (bSuppressASAP)
    6773           0 :         poDS->MarkSuppressOnClose();
    6774        5477 :     if (bStreaming)
    6775             :     {
    6776           4 :         poDS->m_bStreamingOut = true;
    6777           4 :         poDS->m_pszTmpFilename = CPLStrdup(l_osTmpFilename);
    6778           4 :         poDS->m_fpToWrite = VSIFOpenL(pszFilename, "wb");
    6779           4 :         if (poDS->m_fpToWrite == nullptr)
    6780             :         {
    6781           1 :             VSIUnlink(l_osTmpFilename);
    6782           1 :             delete poDS;
    6783           1 :             return nullptr;
    6784             :         }
    6785             :     }
    6786        5476 :     poDS->nRasterXSize = nXSize;
    6787        5476 :     poDS->nRasterYSize = nYSize;
    6788        5476 :     poDS->eAccess = GA_Update;
    6789             : 
    6790        5476 :     poDS->m_nColorTableMultiplier = nColorTableMultiplier;
    6791             : 
    6792        5476 :     poDS->m_bCrystalized = false;
    6793        5476 :     poDS->m_nSamplesPerPixel = static_cast<uint16_t>(l_nBands);
    6794        5476 :     poDS->m_pszFilename = CPLStrdup(pszFilename);
    6795             : 
    6796             :     // Don't try to load external metadata files (#6597).
    6797        5476 :     poDS->m_bIMDRPCMetadataLoaded = true;
    6798             : 
    6799             :     // Avoid premature crystalization that will cause directory re-writing if
    6800             :     // GetProjectionRef() or GetGeoTransform() are called on the newly created
    6801             :     // GeoTIFF.
    6802        5476 :     poDS->m_bLookedForProjection = true;
    6803             : 
    6804        5476 :     TIFFGetField(l_hTIFF, TIFFTAG_SAMPLEFORMAT, &(poDS->m_nSampleFormat));
    6805        5476 :     TIFFGetField(l_hTIFF, TIFFTAG_PLANARCONFIG, &(poDS->m_nPlanarConfig));
    6806             :     // Weird that we need this, but otherwise we get a Valgrind warning on
    6807             :     // tiff_write_124.
    6808        5476 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &(poDS->m_nPhotometric)))
    6809           1 :         poDS->m_nPhotometric = PHOTOMETRIC_MINISBLACK;
    6810        5476 :     TIFFGetField(l_hTIFF, TIFFTAG_BITSPERSAMPLE, &(poDS->m_nBitsPerSample));
    6811        5476 :     TIFFGetField(l_hTIFF, TIFFTAG_COMPRESSION, &(poDS->m_nCompression));
    6812             : 
    6813        5476 :     if (TIFFIsTiled(l_hTIFF))
    6814             :     {
    6815         341 :         TIFFGetField(l_hTIFF, TIFFTAG_TILEWIDTH, &(poDS->m_nBlockXSize));
    6816         341 :         TIFFGetField(l_hTIFF, TIFFTAG_TILELENGTH, &(poDS->m_nBlockYSize));
    6817             :     }
    6818             :     else
    6819             :     {
    6820        5135 :         if (!TIFFGetField(l_hTIFF, TIFFTAG_ROWSPERSTRIP,
    6821             :                           &(poDS->m_nRowsPerStrip)))
    6822           0 :             poDS->m_nRowsPerStrip = 1;  // Dummy value.
    6823             : 
    6824        5135 :         poDS->m_nBlockXSize = nXSize;
    6825        5135 :         poDS->m_nBlockYSize =
    6826        5135 :             std::min(static_cast<int>(poDS->m_nRowsPerStrip), nYSize);
    6827             :     }
    6828             : 
    6829        5476 :     if (!poDS->ComputeBlocksPerColRowAndBand(l_nBands))
    6830             :     {
    6831           0 :         poDS->m_fpL->CancelCreation();
    6832           0 :         delete poDS;
    6833           0 :         return nullptr;
    6834             :     }
    6835             : 
    6836        5476 :     poDS->m_eProfile = GetProfile(CSLFetchNameValue(papszParamList, "PROFILE"));
    6837             : 
    6838             :     /* -------------------------------------------------------------------- */
    6839             :     /*      YCbCr JPEG compressed images should be translated on the fly    */
    6840             :     /*      to RGB by libtiff/libjpeg unless specifically requested         */
    6841             :     /*      otherwise.                                                      */
    6842             :     /* -------------------------------------------------------------------- */
    6843       10986 :     if (poDS->m_nCompression == COMPRESSION_JPEG &&
    6844        5497 :         poDS->m_nPhotometric == PHOTOMETRIC_YCBCR &&
    6845          21 :         CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
    6846             :     {
    6847          21 :         int nColorMode = 0;
    6848             : 
    6849          21 :         poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr", "IMAGE_STRUCTURE");
    6850          42 :         if (!TIFFGetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode) ||
    6851          21 :             nColorMode != JPEGCOLORMODE_RGB)
    6852          21 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    6853             :     }
    6854             : 
    6855        5476 :     if (poDS->m_nCompression == COMPRESSION_LERC)
    6856             :     {
    6857          26 :         uint32_t nLercParamCount = 0;
    6858          26 :         uint32_t *panLercParams = nullptr;
    6859          26 :         if (TIFFGetField(l_hTIFF, TIFFTAG_LERC_PARAMETERS, &nLercParamCount,
    6860          52 :                          &panLercParams) &&
    6861          26 :             nLercParamCount == 2)
    6862             :         {
    6863          26 :             memcpy(poDS->m_anLercAddCompressionAndVersion, panLercParams,
    6864             :                    sizeof(poDS->m_anLercAddCompressionAndVersion));
    6865             :         }
    6866             :     }
    6867             : 
    6868             :     /* -------------------------------------------------------------------- */
    6869             :     /*      Read palette back as a color table if it has one.               */
    6870             :     /* -------------------------------------------------------------------- */
    6871        5476 :     unsigned short *panRed = nullptr;
    6872        5476 :     unsigned short *panGreen = nullptr;
    6873        5476 :     unsigned short *panBlue = nullptr;
    6874             : 
    6875        5480 :     if (poDS->m_nPhotometric == PHOTOMETRIC_PALETTE &&
    6876           4 :         TIFFGetField(l_hTIFF, TIFFTAG_COLORMAP, &panRed, &panGreen, &panBlue))
    6877             :     {
    6878             : 
    6879           4 :         poDS->m_poColorTable = std::make_unique<GDALColorTable>();
    6880             : 
    6881           4 :         const int nColorCount = 1 << poDS->m_nBitsPerSample;
    6882             : 
    6883        1028 :         for (int iColor = nColorCount - 1; iColor >= 0; iColor--)
    6884             :         {
    6885        1024 :             const GDALColorEntry oEntry = {
    6886        1024 :                 static_cast<short>(panRed[iColor] / nColorTableMultiplier),
    6887        1024 :                 static_cast<short>(panGreen[iColor] / nColorTableMultiplier),
    6888        1024 :                 static_cast<short>(panBlue[iColor] / nColorTableMultiplier),
    6889        1024 :                 static_cast<short>(255)};
    6890             : 
    6891        1024 :             poDS->m_poColorTable->SetColorEntry(iColor, &oEntry);
    6892             :         }
    6893             :     }
    6894             : 
    6895             :     /* -------------------------------------------------------------------- */
    6896             :     /*      Do we want to ensure all blocks get written out on close to     */
    6897             :     /*      avoid sparse files?                                             */
    6898             :     /* -------------------------------------------------------------------- */
    6899        5476 :     if (!CPLFetchBool(papszParamList, "SPARSE_OK", false))
    6900        5366 :         poDS->m_bFillEmptyTilesAtClosing = true;
    6901             : 
    6902        5476 :     poDS->m_bWriteEmptyTiles =
    6903        6241 :         bStreaming || (poDS->m_nCompression != COMPRESSION_NONE &&
    6904         765 :                        poDS->m_bFillEmptyTilesAtClosing);
    6905             :     // Only required for people writing non-compressed striped files in the
    6906             :     // right order and wanting all tstrips to be written in the same order
    6907             :     // so that the end result can be memory mapped without knowledge of each
    6908             :     // strip offset.
    6909        5476 :     if (CPLTestBool(CSLFetchNameValueDef(
    6910       10952 :             papszParamList, "WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")) ||
    6911        5476 :         CPLTestBool(CSLFetchNameValueDef(
    6912             :             papszParamList, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")))
    6913             :     {
    6914          26 :         poDS->m_bWriteEmptyTiles = true;
    6915             :     }
    6916             : 
    6917             :     /* -------------------------------------------------------------------- */
    6918             :     /*      Preserve creation options for consulting later (for instance    */
    6919             :     /*      to decide if a TFW file should be written).                     */
    6920             :     /* -------------------------------------------------------------------- */
    6921        5476 :     poDS->m_papszCreationOptions = CSLDuplicate(papszParamList);
    6922             : 
    6923        5476 :     poDS->m_nZLevel = GTiffGetZLevel(papszParamList);
    6924        5476 :     poDS->m_nLZMAPreset = GTiffGetLZMAPreset(papszParamList);
    6925        5476 :     poDS->m_nZSTDLevel = GTiffGetZSTDPreset(papszParamList);
    6926        5476 :     poDS->m_nWebPLevel = GTiffGetWebPLevel(papszParamList);
    6927        5476 :     poDS->m_bWebPLossless = GTiffGetWebPLossless(papszParamList);
    6928        5478 :     if (poDS->m_nWebPLevel != 100 && poDS->m_bWebPLossless &&
    6929           2 :         CSLFetchNameValue(papszParamList, "WEBP_LEVEL"))
    6930             :     {
    6931           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    6932             :                  "WEBP_LEVEL is specified, but WEBP_LOSSLESS=YES. "
    6933             :                  "WEBP_LEVEL will be ignored.");
    6934             :     }
    6935        5476 :     poDS->m_nJpegQuality = GTiffGetJpegQuality(papszParamList);
    6936        5476 :     poDS->m_nJpegTablesMode = GTiffGetJpegTablesMode(papszParamList);
    6937        5476 :     poDS->m_dfMaxZError = GTiffGetLERCMaxZError(papszParamList);
    6938        5476 :     poDS->m_dfMaxZErrorOverview = GTiffGetLERCMaxZErrorOverview(papszParamList);
    6939             : #if HAVE_JXL
    6940        5476 :     poDS->m_bJXLLossless = GTiffGetJXLLossless(papszParamList);
    6941        5476 :     poDS->m_nJXLEffort = GTiffGetJXLEffort(papszParamList);
    6942        5476 :     poDS->m_fJXLDistance = GTiffGetJXLDistance(papszParamList);
    6943        5476 :     poDS->m_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszParamList);
    6944             : #endif
    6945        5476 :     poDS->InitCreationOrOpenOptions(true, papszParamList);
    6946             : 
    6947             :     /* -------------------------------------------------------------------- */
    6948             :     /*      Create band information objects.                                */
    6949             :     /* -------------------------------------------------------------------- */
    6950      307699 :     for (int iBand = 0; iBand < l_nBands; ++iBand)
    6951             :     {
    6952      302223 :         if (poDS->m_nBitsPerSample == 8 || poDS->m_nBitsPerSample == 16 ||
    6953        2588 :             poDS->m_nBitsPerSample == 32 || poDS->m_nBitsPerSample == 64 ||
    6954         237 :             poDS->m_nBitsPerSample == 128)
    6955             :         {
    6956      302158 :             poDS->SetBand(iBand + 1, new GTiffRasterBand(poDS, iBand + 1));
    6957             :         }
    6958             :         else
    6959             :         {
    6960          65 :             poDS->SetBand(iBand + 1, new GTiffOddBitsBand(poDS, iBand + 1));
    6961         130 :             poDS->GetRasterBand(iBand + 1)->SetMetadataItem(
    6962         130 :                 "NBITS", CPLString().Printf("%d", poDS->m_nBitsPerSample),
    6963          65 :                 "IMAGE_STRUCTURE");
    6964             :         }
    6965             :     }
    6966             : 
    6967        5476 :     poDS->GetDiscardLsbOption(papszParamList);
    6968             : 
    6969        5476 :     if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG && l_nBands != 1)
    6970         786 :         poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
    6971             :     else
    6972        4690 :         poDS->SetMetadataItem("INTERLEAVE", "BAND", "IMAGE_STRUCTURE");
    6973             : 
    6974        5476 :     poDS->oOvManager.Initialize(poDS, pszFilename);
    6975             : 
    6976        5476 :     return poDS;
    6977             : }
    6978             : 
    6979             : /************************************************************************/
    6980             : /*                           CopyImageryAndMask()                       */
    6981             : /************************************************************************/
    6982             : 
    6983         311 : CPLErr GTiffDataset::CopyImageryAndMask(GTiffDataset *poDstDS,
    6984             :                                         GDALDataset *poSrcDS,
    6985             :                                         GDALRasterBand *poSrcMaskBand,
    6986             :                                         GDALProgressFunc pfnProgress,
    6987             :                                         void *pProgressData)
    6988             : {
    6989         311 :     CPLErr eErr = CE_None;
    6990             : 
    6991         311 :     const auto eType = poDstDS->GetRasterBand(1)->GetRasterDataType();
    6992         311 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
    6993         311 :     const int l_nBands = poDstDS->GetRasterCount();
    6994             :     GByte *pBlockBuffer = static_cast<GByte *>(
    6995         311 :         VSI_MALLOC3_VERBOSE(poDstDS->m_nBlockXSize, poDstDS->m_nBlockYSize,
    6996             :                             cpl::fits_on<int>(l_nBands * nDataTypeSize)));
    6997         311 :     if (pBlockBuffer == nullptr)
    6998             :     {
    6999           0 :         eErr = CE_Failure;
    7000             :     }
    7001         311 :     const int nYSize = poDstDS->nRasterYSize;
    7002         311 :     const int nXSize = poDstDS->nRasterXSize;
    7003             :     const bool bIsOddBand =
    7004         311 :         dynamic_cast<GTiffOddBitsBand *>(poDstDS->GetRasterBand(1)) != nullptr;
    7005             : 
    7006         311 :     if (poDstDS->m_poMaskDS)
    7007             :     {
    7008          63 :         CPLAssert(poDstDS->m_poMaskDS->m_nBlockXSize == poDstDS->m_nBlockXSize);
    7009          63 :         CPLAssert(poDstDS->m_poMaskDS->m_nBlockYSize == poDstDS->m_nBlockYSize);
    7010             :     }
    7011             : 
    7012         311 :     if (poDstDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE &&
    7013          58 :         !poDstDS->m_bTileInterleave)
    7014             :     {
    7015          45 :         int iBlock = 0;
    7016          90 :         const int nBlocks = poDstDS->m_nBlocksPerBand *
    7017          45 :                             (l_nBands + (poDstDS->m_poMaskDS ? 1 : 0));
    7018         195 :         for (int i = 0; eErr == CE_None && i < l_nBands; i++)
    7019             :         {
    7020         345 :             for (int iY = 0, nYBlock = 0; iY < nYSize && eErr == CE_None;
    7021         195 :                  iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
    7022         195 :                            ? nYSize
    7023          59 :                            : iY + poDstDS->m_nBlockYSize),
    7024             :                      nYBlock++)
    7025             :             {
    7026             :                 const int nReqYSize =
    7027         195 :                     std::min(nYSize - iY, poDstDS->m_nBlockYSize);
    7028         495 :                 for (int iX = 0, nXBlock = 0; iX < nXSize && eErr == CE_None;
    7029         300 :                      iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
    7030         300 :                                ? nXSize
    7031         155 :                                : iX + poDstDS->m_nBlockXSize),
    7032             :                          nXBlock++)
    7033             :                 {
    7034             :                     const int nReqXSize =
    7035         300 :                         std::min(nXSize - iX, poDstDS->m_nBlockXSize);
    7036         300 :                     if (nReqXSize < poDstDS->m_nBlockXSize ||
    7037         155 :                         nReqYSize < poDstDS->m_nBlockYSize)
    7038             :                     {
    7039         190 :                         memset(pBlockBuffer, 0,
    7040         190 :                                static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7041         190 :                                    poDstDS->m_nBlockYSize * nDataTypeSize);
    7042             :                     }
    7043         300 :                     eErr = poSrcDS->GetRasterBand(i + 1)->RasterIO(
    7044             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7045             :                         nReqXSize, nReqYSize, eType, nDataTypeSize,
    7046         300 :                         static_cast<GSpacing>(nDataTypeSize) *
    7047         300 :                             poDstDS->m_nBlockXSize,
    7048             :                         nullptr);
    7049         300 :                     if (eErr == CE_None)
    7050             :                     {
    7051         300 :                         eErr = poDstDS->WriteEncodedTileOrStrip(
    7052             :                             iBlock, pBlockBuffer, false);
    7053             :                     }
    7054             : 
    7055         300 :                     iBlock++;
    7056         600 :                     if (pfnProgress &&
    7057         300 :                         !pfnProgress(static_cast<double>(iBlock) / nBlocks,
    7058             :                                      nullptr, pProgressData))
    7059             :                     {
    7060           0 :                         eErr = CE_Failure;
    7061             :                     }
    7062             : 
    7063         300 :                     if (poDstDS->m_bWriteError)
    7064           0 :                         eErr = CE_Failure;
    7065             :                 }
    7066             :             }
    7067             :         }
    7068          45 :         if (poDstDS->m_poMaskDS && eErr == CE_None)
    7069             :         {
    7070           6 :             int iBlockMask = 0;
    7071          17 :             for (int iY = 0, nYBlock = 0; iY < nYSize && eErr == CE_None;
    7072          11 :                  iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
    7073          11 :                            ? nYSize
    7074           5 :                            : iY + poDstDS->m_nBlockYSize),
    7075             :                      nYBlock++)
    7076             :             {
    7077             :                 const int nReqYSize =
    7078          11 :                     std::min(nYSize - iY, poDstDS->m_nBlockYSize);
    7079          49 :                 for (int iX = 0, nXBlock = 0; iX < nXSize && eErr == CE_None;
    7080          38 :                      iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
    7081          38 :                                ? nXSize
    7082          30 :                                : iX + poDstDS->m_nBlockXSize),
    7083             :                          nXBlock++)
    7084             :                 {
    7085             :                     const int nReqXSize =
    7086          38 :                         std::min(nXSize - iX, poDstDS->m_nBlockXSize);
    7087          38 :                     if (nReqXSize < poDstDS->m_nBlockXSize ||
    7088          30 :                         nReqYSize < poDstDS->m_nBlockYSize)
    7089             :                     {
    7090          16 :                         memset(pBlockBuffer, 0,
    7091          16 :                                static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7092          16 :                                    poDstDS->m_nBlockYSize);
    7093             :                     }
    7094          76 :                     eErr = poSrcMaskBand->RasterIO(
    7095             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7096             :                         nReqXSize, nReqYSize, GDT_Byte, 1,
    7097          38 :                         poDstDS->m_nBlockXSize, nullptr);
    7098          38 :                     if (eErr == CE_None)
    7099             :                     {
    7100             :                         // Avoid any attempt to load from disk
    7101          38 :                         poDstDS->m_poMaskDS->m_nLoadedBlock = iBlockMask;
    7102             :                         eErr =
    7103          38 :                             poDstDS->m_poMaskDS->GetRasterBand(1)->WriteBlock(
    7104             :                                 nXBlock, nYBlock, pBlockBuffer);
    7105          38 :                         if (eErr == CE_None)
    7106          38 :                             eErr = poDstDS->m_poMaskDS->FlushBlockBuf();
    7107             :                     }
    7108             : 
    7109          38 :                     iBlockMask++;
    7110          76 :                     if (pfnProgress &&
    7111          38 :                         !pfnProgress(static_cast<double>(iBlock + iBlockMask) /
    7112             :                                          nBlocks,
    7113             :                                      nullptr, pProgressData))
    7114             :                     {
    7115           0 :                         eErr = CE_Failure;
    7116             :                     }
    7117             : 
    7118          38 :                     if (poDstDS->m_poMaskDS->m_bWriteError)
    7119           0 :                         eErr = CE_Failure;
    7120             :                 }
    7121             :             }
    7122          45 :         }
    7123             :     }
    7124             :     else
    7125             :     {
    7126         266 :         int iBlock = 0;
    7127         266 :         const int nBlocks = poDstDS->m_nBlocksPerBand;
    7128        7023 :         for (int iY = 0, nYBlock = 0; iY < nYSize && eErr == CE_None;
    7129        6757 :              iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
    7130        6757 :                        ? nYSize
    7131        6568 :                        : iY + poDstDS->m_nBlockYSize),
    7132             :                  nYBlock++)
    7133             :         {
    7134        6757 :             const int nReqYSize = std::min(nYSize - iY, poDstDS->m_nBlockYSize);
    7135       26429 :             for (int iX = 0, nXBlock = 0; iX < nXSize && eErr == CE_None;
    7136       19672 :                  iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
    7137       19672 :                            ? nXSize
    7138       19414 :                            : iX + poDstDS->m_nBlockXSize),
    7139             :                      nXBlock++)
    7140             :             {
    7141             :                 const int nReqXSize =
    7142       19672 :                     std::min(nXSize - iX, poDstDS->m_nBlockXSize);
    7143       19672 :                 if (nReqXSize < poDstDS->m_nBlockXSize ||
    7144       19414 :                     nReqYSize < poDstDS->m_nBlockYSize)
    7145             :                 {
    7146         424 :                     memset(pBlockBuffer, 0,
    7147         424 :                            static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7148         424 :                                poDstDS->m_nBlockYSize * l_nBands *
    7149         424 :                                nDataTypeSize);
    7150             :                 }
    7151             : 
    7152       19672 :                 if (poDstDS->m_bTileInterleave)
    7153             :                 {
    7154         114 :                     eErr = poSrcDS->RasterIO(
    7155             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7156             :                         nReqXSize, nReqYSize, eType, l_nBands, nullptr,
    7157             :                         nDataTypeSize,
    7158          57 :                         static_cast<GSpacing>(nDataTypeSize) *
    7159          57 :                             poDstDS->m_nBlockXSize,
    7160          57 :                         static_cast<GSpacing>(nDataTypeSize) *
    7161          57 :                             poDstDS->m_nBlockXSize * poDstDS->m_nBlockYSize,
    7162             :                         nullptr);
    7163          57 :                     if (eErr == CE_None)
    7164             :                     {
    7165         228 :                         for (int i = 0; eErr == CE_None && i < l_nBands; i++)
    7166             :                         {
    7167         171 :                             eErr = poDstDS->WriteEncodedTileOrStrip(
    7168         171 :                                 iBlock + i * poDstDS->m_nBlocksPerBand,
    7169         171 :                                 pBlockBuffer + static_cast<size_t>(i) *
    7170         171 :                                                    poDstDS->m_nBlockXSize *
    7171         171 :                                                    poDstDS->m_nBlockYSize *
    7172         171 :                                                    nDataTypeSize,
    7173             :                                 false);
    7174             :                         }
    7175             :                     }
    7176             :                 }
    7177       19615 :                 else if (!bIsOddBand)
    7178             :                 {
    7179       39108 :                     eErr = poSrcDS->RasterIO(
    7180             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7181             :                         nReqXSize, nReqYSize, eType, l_nBands, nullptr,
    7182       19554 :                         static_cast<GSpacing>(nDataTypeSize) * l_nBands,
    7183       19554 :                         static_cast<GSpacing>(nDataTypeSize) * l_nBands *
    7184       19554 :                             poDstDS->m_nBlockXSize,
    7185             :                         nDataTypeSize, nullptr);
    7186       19554 :                     if (eErr == CE_None)
    7187             :                     {
    7188       19553 :                         eErr = poDstDS->WriteEncodedTileOrStrip(
    7189             :                             iBlock, pBlockBuffer, false);
    7190             :                     }
    7191             :                 }
    7192             :                 else
    7193             :                 {
    7194             :                     // In the odd bit case, this is a bit messy to ensure
    7195             :                     // the strile gets written synchronously.
    7196             :                     // We load the content of the n-1 bands in the cache,
    7197             :                     // and for the last band we invoke WriteBlock() directly
    7198             :                     // We also force FlushBlockBuf()
    7199         122 :                     std::vector<GDALRasterBlock *> apoLockedBlocks;
    7200          91 :                     for (int i = 0; eErr == CE_None && i < l_nBands - 1; i++)
    7201             :                     {
    7202             :                         auto poBlock =
    7203          30 :                             poDstDS->GetRasterBand(i + 1)->GetLockedBlockRef(
    7204          30 :                                 nXBlock, nYBlock, TRUE);
    7205          30 :                         if (poBlock)
    7206             :                         {
    7207          60 :                             eErr = poSrcDS->GetRasterBand(i + 1)->RasterIO(
    7208             :                                 GF_Read, iX, iY, nReqXSize, nReqYSize,
    7209             :                                 poBlock->GetDataRef(), nReqXSize, nReqYSize,
    7210             :                                 eType, nDataTypeSize,
    7211          30 :                                 static_cast<GSpacing>(nDataTypeSize) *
    7212          30 :                                     poDstDS->m_nBlockXSize,
    7213             :                                 nullptr);
    7214          30 :                             poBlock->MarkDirty();
    7215          30 :                             apoLockedBlocks.emplace_back(poBlock);
    7216             :                         }
    7217             :                         else
    7218             :                         {
    7219           0 :                             eErr = CE_Failure;
    7220             :                         }
    7221             :                     }
    7222          61 :                     if (eErr == CE_None)
    7223             :                     {
    7224         122 :                         eErr = poSrcDS->GetRasterBand(l_nBands)->RasterIO(
    7225             :                             GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7226             :                             nReqXSize, nReqYSize, eType, nDataTypeSize,
    7227          61 :                             static_cast<GSpacing>(nDataTypeSize) *
    7228          61 :                                 poDstDS->m_nBlockXSize,
    7229             :                             nullptr);
    7230             :                     }
    7231          61 :                     if (eErr == CE_None)
    7232             :                     {
    7233             :                         // Avoid any attempt to load from disk
    7234          61 :                         poDstDS->m_nLoadedBlock = iBlock;
    7235          61 :                         eErr = poDstDS->GetRasterBand(l_nBands)->WriteBlock(
    7236             :                             nXBlock, nYBlock, pBlockBuffer);
    7237          61 :                         if (eErr == CE_None)
    7238          61 :                             eErr = poDstDS->FlushBlockBuf();
    7239             :                     }
    7240          91 :                     for (auto poBlock : apoLockedBlocks)
    7241             :                     {
    7242          30 :                         poBlock->MarkClean();
    7243          30 :                         poBlock->DropLock();
    7244             :                     }
    7245             :                 }
    7246             : 
    7247       19672 :                 if (eErr == CE_None && poDstDS->m_poMaskDS)
    7248             :                 {
    7249        4668 :                     if (nReqXSize < poDstDS->m_nBlockXSize ||
    7250        4625 :                         nReqYSize < poDstDS->m_nBlockYSize)
    7251             :                     {
    7252          81 :                         memset(pBlockBuffer, 0,
    7253          81 :                                static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7254          81 :                                    poDstDS->m_nBlockYSize);
    7255             :                     }
    7256        9336 :                     eErr = poSrcMaskBand->RasterIO(
    7257             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7258             :                         nReqXSize, nReqYSize, GDT_Byte, 1,
    7259        4668 :                         poDstDS->m_nBlockXSize, nullptr);
    7260        4668 :                     if (eErr == CE_None)
    7261             :                     {
    7262             :                         // Avoid any attempt to load from disk
    7263        4668 :                         poDstDS->m_poMaskDS->m_nLoadedBlock = iBlock;
    7264             :                         eErr =
    7265        4668 :                             poDstDS->m_poMaskDS->GetRasterBand(1)->WriteBlock(
    7266             :                                 nXBlock, nYBlock, pBlockBuffer);
    7267        4668 :                         if (eErr == CE_None)
    7268        4668 :                             eErr = poDstDS->m_poMaskDS->FlushBlockBuf();
    7269             :                     }
    7270             :                 }
    7271       19672 :                 if (poDstDS->m_bWriteError)
    7272           6 :                     eErr = CE_Failure;
    7273             : 
    7274       19672 :                 iBlock++;
    7275       39344 :                 if (pfnProgress &&
    7276       19672 :                     !pfnProgress(static_cast<double>(iBlock) / nBlocks, nullptr,
    7277             :                                  pProgressData))
    7278             :                 {
    7279           0 :                     eErr = CE_Failure;
    7280             :                 }
    7281             :             }
    7282             :         }
    7283             :     }
    7284             : 
    7285         311 :     poDstDS->FlushCache(false);  // mostly to wait for thread completion
    7286         311 :     VSIFree(pBlockBuffer);
    7287             : 
    7288         311 :     return eErr;
    7289             : }
    7290             : 
    7291             : /************************************************************************/
    7292             : /*                             CreateCopy()                             */
    7293             : /************************************************************************/
    7294             : 
    7295        2067 : GDALDataset *GTiffDataset::CreateCopy(const char *pszFilename,
    7296             :                                       GDALDataset *poSrcDS, int bStrict,
    7297             :                                       char **papszOptions,
    7298             :                                       GDALProgressFunc pfnProgress,
    7299             :                                       void *pProgressData)
    7300             : 
    7301             : {
    7302        2067 :     if (poSrcDS->GetRasterCount() == 0)
    7303             :     {
    7304           2 :         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    7305             :                     "Unable to export GeoTIFF files with zero bands.");
    7306           2 :         return nullptr;
    7307             :     }
    7308             : 
    7309        2065 :     GDALRasterBand *const poPBand = poSrcDS->GetRasterBand(1);
    7310        2065 :     GDALDataType eType = poPBand->GetRasterDataType();
    7311             : 
    7312             :     /* -------------------------------------------------------------------- */
    7313             :     /*      Check, whether all bands in input dataset has the same type.    */
    7314             :     /* -------------------------------------------------------------------- */
    7315        2065 :     const int l_nBands = poSrcDS->GetRasterCount();
    7316        4925 :     for (int iBand = 2; iBand <= l_nBands; ++iBand)
    7317             :     {
    7318        2860 :         if (eType != poSrcDS->GetRasterBand(iBand)->GetRasterDataType())
    7319             :         {
    7320           0 :             if (bStrict)
    7321             :             {
    7322           0 :                 ReportError(
    7323             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    7324             :                     "Unable to export GeoTIFF file with different datatypes "
    7325             :                     "per different bands. All bands should have the same "
    7326             :                     "types in TIFF.");
    7327           0 :                 return nullptr;
    7328             :             }
    7329             :             else
    7330             :             {
    7331           0 :                 ReportError(
    7332             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    7333             :                     "Unable to export GeoTIFF file with different datatypes "
    7334             :                     "per different bands. All bands should have the same "
    7335             :                     "types in TIFF.");
    7336             :             }
    7337             :         }
    7338             :     }
    7339             : 
    7340             :     /* -------------------------------------------------------------------- */
    7341             :     /*      Capture the profile.                                            */
    7342             :     /* -------------------------------------------------------------------- */
    7343             :     const GTiffProfile eProfile =
    7344        2065 :         GetProfile(CSLFetchNameValue(papszOptions, "PROFILE"));
    7345             : 
    7346        2065 :     const bool bGeoTIFF = eProfile != GTiffProfile::BASELINE;
    7347             : 
    7348             :     /* -------------------------------------------------------------------- */
    7349             :     /*      Special handling for NBITS.  Copy from band metadata if found.  */
    7350             :     /* -------------------------------------------------------------------- */
    7351        2065 :     char **papszCreateOptions = CSLDuplicate(papszOptions);
    7352             : 
    7353        2065 :     if (poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE") != nullptr &&
    7354        2082 :         atoi(poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE")) > 0 &&
    7355          17 :         CSLFetchNameValue(papszCreateOptions, "NBITS") == nullptr)
    7356             :     {
    7357           3 :         papszCreateOptions = CSLSetNameValue(
    7358             :             papszCreateOptions, "NBITS",
    7359           3 :             poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE"));
    7360             :     }
    7361             : 
    7362        2065 :     if (CSLFetchNameValue(papszOptions, "PIXELTYPE") == nullptr &&
    7363             :         eType == GDT_Byte)
    7364             :     {
    7365        1736 :         poPBand->EnablePixelTypeSignedByteWarning(false);
    7366             :         const char *pszPixelType =
    7367        1736 :             poPBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
    7368        1736 :         poPBand->EnablePixelTypeSignedByteWarning(true);
    7369        1736 :         if (pszPixelType)
    7370             :         {
    7371           1 :             papszCreateOptions =
    7372           1 :                 CSLSetNameValue(papszCreateOptions, "PIXELTYPE", pszPixelType);
    7373             :         }
    7374             :     }
    7375             : 
    7376             :     /* -------------------------------------------------------------------- */
    7377             :     /*      Color profile.  Copy from band metadata if found.              */
    7378             :     /* -------------------------------------------------------------------- */
    7379        2065 :     if (bGeoTIFF)
    7380             :     {
    7381        2048 :         const char *pszOptionsMD[] = {"SOURCE_ICC_PROFILE",
    7382             :                                       "SOURCE_PRIMARIES_RED",
    7383             :                                       "SOURCE_PRIMARIES_GREEN",
    7384             :                                       "SOURCE_PRIMARIES_BLUE",
    7385             :                                       "SOURCE_WHITEPOINT",
    7386             :                                       "TIFFTAG_TRANSFERFUNCTION_RED",
    7387             :                                       "TIFFTAG_TRANSFERFUNCTION_GREEN",
    7388             :                                       "TIFFTAG_TRANSFERFUNCTION_BLUE",
    7389             :                                       "TIFFTAG_TRANSFERRANGE_BLACK",
    7390             :                                       "TIFFTAG_TRANSFERRANGE_WHITE",
    7391             :                                       nullptr};
    7392             : 
    7393             :         // Copy all the tags.  Options will override tags in the source.
    7394        2048 :         int i = 0;
    7395       22508 :         while (pszOptionsMD[i] != nullptr)
    7396             :         {
    7397             :             char const *pszMD =
    7398       20462 :                 CSLFetchNameValue(papszOptions, pszOptionsMD[i]);
    7399       20462 :             if (pszMD == nullptr)
    7400             :                 pszMD =
    7401       20454 :                     poSrcDS->GetMetadataItem(pszOptionsMD[i], "COLOR_PROFILE");
    7402             : 
    7403       20462 :             if ((pszMD != nullptr) && !EQUAL(pszMD, ""))
    7404             :             {
    7405          16 :                 papszCreateOptions =
    7406          16 :                     CSLSetNameValue(papszCreateOptions, pszOptionsMD[i], pszMD);
    7407             : 
    7408             :                 // If an ICC profile exists, other tags are not needed.
    7409          16 :                 if (EQUAL(pszOptionsMD[i], "SOURCE_ICC_PROFILE"))
    7410           2 :                     break;
    7411             :             }
    7412             : 
    7413       20460 :             ++i;
    7414             :         }
    7415             :     }
    7416             : 
    7417        2065 :     double dfExtraSpaceForOverviews = 0;
    7418             :     const bool bCopySrcOverviews =
    7419        2065 :         CPLFetchBool(papszCreateOptions, "COPY_SRC_OVERVIEWS", false);
    7420        2065 :     std::unique_ptr<GDALDataset> poOvrDS;
    7421        2065 :     int nSrcOverviews = 0;
    7422        2065 :     if (bCopySrcOverviews)
    7423             :     {
    7424             :         const char *pszOvrDS =
    7425         193 :             CSLFetchNameValue(papszCreateOptions, "@OVERVIEW_DATASET");
    7426         193 :         if (pszOvrDS)
    7427             :         {
    7428             :             // Empty string is used by COG driver to indicate that we want
    7429             :             // to ignore source overviews.
    7430          41 :             if (!EQUAL(pszOvrDS, ""))
    7431             :             {
    7432          39 :                 poOvrDS.reset(GDALDataset::Open(pszOvrDS));
    7433          39 :                 if (!poOvrDS)
    7434             :                 {
    7435           0 :                     CSLDestroy(papszCreateOptions);
    7436           0 :                     return nullptr;
    7437             :                 }
    7438          39 :                 if (poOvrDS->GetRasterCount() != l_nBands)
    7439             :                 {
    7440           0 :                     CSLDestroy(papszCreateOptions);
    7441           0 :                     return nullptr;
    7442             :                 }
    7443          39 :                 nSrcOverviews =
    7444          39 :                     poOvrDS->GetRasterBand(1)->GetOverviewCount() + 1;
    7445             :             }
    7446             :         }
    7447             :         else
    7448             :         {
    7449         152 :             nSrcOverviews = poSrcDS->GetRasterBand(1)->GetOverviewCount();
    7450             :         }
    7451             : 
    7452             :         // Limit number of overviews if specified
    7453             :         const char *pszOverviewCount =
    7454         193 :             CSLFetchNameValue(papszCreateOptions, "@OVERVIEW_COUNT");
    7455         193 :         if (pszOverviewCount)
    7456           8 :             nSrcOverviews =
    7457           8 :                 std::max(0, std::min(nSrcOverviews, atoi(pszOverviewCount)));
    7458             : 
    7459         193 :         if (nSrcOverviews)
    7460             :         {
    7461         202 :             for (int j = 1; j <= l_nBands; ++j)
    7462             :             {
    7463             :                 const int nOtherBandOverviewCount =
    7464         133 :                     poOvrDS ? poOvrDS->GetRasterBand(j)->GetOverviewCount() + 1
    7465         184 :                             : poSrcDS->GetRasterBand(j)->GetOverviewCount();
    7466         133 :                 if (nOtherBandOverviewCount < nSrcOverviews)
    7467             :                 {
    7468           1 :                     ReportError(
    7469             :                         pszFilename, CE_Failure, CPLE_NotSupported,
    7470             :                         "COPY_SRC_OVERVIEWS cannot be used when the bands have "
    7471             :                         "not the same number of overview levels.");
    7472           1 :                     CSLDestroy(papszCreateOptions);
    7473           1 :                     return nullptr;
    7474             :                 }
    7475         397 :                 for (int i = 0; i < nSrcOverviews; ++i)
    7476             :                 {
    7477             :                     GDALRasterBand *poOvrBand =
    7478             :                         poOvrDS
    7479         375 :                             ? (i == 0 ? poOvrDS->GetRasterBand(j)
    7480         216 :                                       : poOvrDS->GetRasterBand(j)->GetOverview(
    7481         108 :                                             i - 1))
    7482         344 :                             : poSrcDS->GetRasterBand(j)->GetOverview(i);
    7483         267 :                     if (poOvrBand == nullptr)
    7484             :                     {
    7485           1 :                         ReportError(
    7486             :                             pszFilename, CE_Failure, CPLE_NotSupported,
    7487             :                             "COPY_SRC_OVERVIEWS cannot be used when one "
    7488             :                             "overview band is NULL.");
    7489           1 :                         CSLDestroy(papszCreateOptions);
    7490           1 :                         return nullptr;
    7491             :                     }
    7492             :                     GDALRasterBand *poOvrFirstBand =
    7493             :                         poOvrDS
    7494         374 :                             ? (i == 0 ? poOvrDS->GetRasterBand(1)
    7495         216 :                                       : poOvrDS->GetRasterBand(1)->GetOverview(
    7496         108 :                                             i - 1))
    7497         342 :                             : poSrcDS->GetRasterBand(1)->GetOverview(i);
    7498         531 :                     if (poOvrBand->GetXSize() != poOvrFirstBand->GetXSize() ||
    7499         265 :                         poOvrBand->GetYSize() != poOvrFirstBand->GetYSize())
    7500             :                     {
    7501           1 :                         ReportError(
    7502             :                             pszFilename, CE_Failure, CPLE_NotSupported,
    7503             :                             "COPY_SRC_OVERVIEWS cannot be used when the "
    7504             :                             "overview bands have not the same dimensions "
    7505             :                             "among bands.");
    7506           1 :                         CSLDestroy(papszCreateOptions);
    7507           1 :                         return nullptr;
    7508             :                     }
    7509             :                 }
    7510             :             }
    7511             : 
    7512         199 :             for (int i = 0; i < nSrcOverviews; ++i)
    7513             :             {
    7514             :                 GDALRasterBand *poOvrFirstBand =
    7515             :                     poOvrDS
    7516         211 :                         ? (i == 0
    7517          81 :                                ? poOvrDS->GetRasterBand(1)
    7518          42 :                                : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    7519         179 :                         : poSrcDS->GetRasterBand(1)->GetOverview(i);
    7520         130 :                 dfExtraSpaceForOverviews +=
    7521         130 :                     static_cast<double>(poOvrFirstBand->GetXSize()) *
    7522         130 :                     poOvrFirstBand->GetYSize();
    7523             :             }
    7524          69 :             dfExtraSpaceForOverviews *=
    7525          69 :                 l_nBands * GDALGetDataTypeSizeBytes(eType);
    7526             :         }
    7527             :         else
    7528             :         {
    7529         121 :             CPLDebug("GTiff", "No source overviews to copy");
    7530             :         }
    7531             :     }
    7532             : 
    7533             : /* -------------------------------------------------------------------- */
    7534             : /*      Should we use optimized way of copying from an input JPEG       */
    7535             : /*      dataset?                                                        */
    7536             : /* -------------------------------------------------------------------- */
    7537             : 
    7538             : // TODO(schwehr): Refactor bDirectCopyFromJPEG to be a const.
    7539             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    7540        2062 :     bool bDirectCopyFromJPEG = false;
    7541             : #endif
    7542             : 
    7543             :     // Note: JPEG_DIRECT_COPY is not defined by default, because it is mainly
    7544             :     // useful for debugging purposes.
    7545             : #ifdef JPEG_DIRECT_COPY
    7546             :     if (CPLFetchBool(papszCreateOptions, "JPEG_DIRECT_COPY", false) &&
    7547             :         GTIFF_CanDirectCopyFromJPEG(poSrcDS, papszCreateOptions))
    7548             :     {
    7549             :         CPLDebug("GTiff", "Using special direct copy mode from a JPEG dataset");
    7550             : 
    7551             :         bDirectCopyFromJPEG = true;
    7552             :     }
    7553             : #endif
    7554             : 
    7555             : #ifdef HAVE_LIBJPEG
    7556        2062 :     bool bCopyFromJPEG = false;
    7557             : 
    7558             :     // When CreateCopy'ing() from a JPEG dataset, and asking for COMPRESS=JPEG,
    7559             :     // use DCT coefficients (unless other options are incompatible, like
    7560             :     // strip/tile dimensions, specifying JPEG_QUALITY option, incompatible
    7561             :     // PHOTOMETRIC with the source colorspace, etc.) to avoid the lossy steps
    7562             :     // involved by decompression/recompression.
    7563        4124 :     if (!bDirectCopyFromJPEG &&
    7564        2062 :         GTIFF_CanCopyFromJPEG(poSrcDS, papszCreateOptions))
    7565             :     {
    7566          12 :         CPLDebug("GTiff", "Using special copy mode from a JPEG dataset");
    7567             : 
    7568          12 :         bCopyFromJPEG = true;
    7569             :     }
    7570             : #endif
    7571             : 
    7572             :     /* -------------------------------------------------------------------- */
    7573             :     /*      If the source is RGB, then set the PHOTOMETRIC=RGB value        */
    7574             :     /* -------------------------------------------------------------------- */
    7575             : 
    7576             :     const bool bForcePhotometric =
    7577        2062 :         CSLFetchNameValue(papszOptions, "PHOTOMETRIC") != nullptr;
    7578             : 
    7579        1197 :     if (l_nBands >= 3 && !bForcePhotometric &&
    7580             : #ifdef HAVE_LIBJPEG
    7581        1159 :         !bCopyFromJPEG &&
    7582             : #endif
    7583        1153 :         poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_RedBand &&
    7584        4314 :         poSrcDS->GetRasterBand(2)->GetColorInterpretation() == GCI_GreenBand &&
    7585        1055 :         poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_BlueBand)
    7586             :     {
    7587        1049 :         papszCreateOptions =
    7588        1049 :             CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "RGB");
    7589             :     }
    7590             : 
    7591             :     /* -------------------------------------------------------------------- */
    7592             :     /*      Create the file.                                                */
    7593             :     /* -------------------------------------------------------------------- */
    7594        2062 :     VSILFILE *l_fpL = nullptr;
    7595        4124 :     CPLString l_osTmpFilename;
    7596             : 
    7597        2062 :     const int nXSize = poSrcDS->GetRasterXSize();
    7598        2062 :     const int nYSize = poSrcDS->GetRasterYSize();
    7599             : 
    7600             :     const int nColorTableMultiplier = std::max(
    7601        4124 :         1,
    7602        4124 :         std::min(257,
    7603        2062 :                  atoi(CSLFetchNameValueDef(
    7604             :                      papszOptions, "COLOR_TABLE_MULTIPLIER",
    7605        2062 :                      CPLSPrintf("%d", DEFAULT_COLOR_TABLE_MULTIPLIER_257)))));
    7606             : 
    7607        2062 :     bool bTileInterleaving = false;
    7608        2062 :     TIFF *l_hTIFF = CreateLL(pszFilename, nXSize, nYSize, l_nBands, eType,
    7609             :                              dfExtraSpaceForOverviews, nColorTableMultiplier,
    7610             :                              papszCreateOptions, &l_fpL, l_osTmpFilename,
    7611             :                              /* bCreateCopy = */ true, bTileInterleaving);
    7612        2062 :     const bool bStreaming = !l_osTmpFilename.empty();
    7613             : 
    7614        2062 :     CSLDestroy(papszCreateOptions);
    7615        2062 :     papszCreateOptions = nullptr;
    7616             : 
    7617        2062 :     if (l_hTIFF == nullptr)
    7618             :     {
    7619          18 :         if (bStreaming)
    7620           0 :             VSIUnlink(l_osTmpFilename);
    7621          18 :         return nullptr;
    7622             :     }
    7623             : 
    7624        2044 :     uint16_t l_nPlanarConfig = 0;
    7625        2044 :     TIFFGetField(l_hTIFF, TIFFTAG_PLANARCONFIG, &l_nPlanarConfig);
    7626             : 
    7627        2044 :     uint16_t l_nCompression = 0;
    7628             : 
    7629        2044 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_COMPRESSION, &(l_nCompression)))
    7630           0 :         l_nCompression = COMPRESSION_NONE;
    7631             : 
    7632             :     /* -------------------------------------------------------------------- */
    7633             :     /*      Set the alpha channel if we find one.                           */
    7634             :     /* -------------------------------------------------------------------- */
    7635        2044 :     uint16_t *extraSamples = nullptr;
    7636        2044 :     uint16_t nExtraSamples = 0;
    7637        2044 :     if (TIFFGetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples,
    7638        2292 :                      &extraSamples) &&
    7639         248 :         nExtraSamples > 0)
    7640             :     {
    7641             :         // We need to allocate a new array as (current) libtiff
    7642             :         // versions will not like that we reuse the array we got from
    7643             :         // TIFFGetField().
    7644             :         uint16_t *pasNewExtraSamples = static_cast<uint16_t *>(
    7645         248 :             CPLMalloc(nExtraSamples * sizeof(uint16_t)));
    7646         248 :         memcpy(pasNewExtraSamples, extraSamples,
    7647         248 :                nExtraSamples * sizeof(uint16_t));
    7648         248 :         const char *pszAlpha = CPLGetConfigOption(
    7649             :             "GTIFF_ALPHA", CSLFetchNameValue(papszOptions, "ALPHA"));
    7650             :         const uint16_t nAlpha =
    7651         248 :             GTiffGetAlphaValue(pszAlpha, DEFAULT_ALPHA_TYPE);
    7652         248 :         const int nBaseSamples = l_nBands - nExtraSamples;
    7653         845 :         for (int iExtraBand = nBaseSamples + 1; iExtraBand <= l_nBands;
    7654             :              iExtraBand++)
    7655             :         {
    7656         597 :             if (poSrcDS->GetRasterBand(iExtraBand)->GetColorInterpretation() ==
    7657             :                 GCI_AlphaBand)
    7658             :             {
    7659         141 :                 pasNewExtraSamples[iExtraBand - nBaseSamples - 1] = nAlpha;
    7660         141 :                 if (!pszAlpha)
    7661             :                 {
    7662             :                     // Use the ALPHA metadata item from the source band, when
    7663             :                     // present, if no explicit ALPHA creation option
    7664         278 :                     pasNewExtraSamples[iExtraBand - nBaseSamples - 1] =
    7665         139 :                         GTiffGetAlphaValue(
    7666         139 :                             poSrcDS->GetRasterBand(iExtraBand)
    7667         139 :                                 ->GetMetadataItem("ALPHA", "IMAGE_STRUCTURE"),
    7668             :                             nAlpha);
    7669             :                 }
    7670             :             }
    7671             :         }
    7672         248 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples,
    7673             :                      pasNewExtraSamples);
    7674             : 
    7675         248 :         CPLFree(pasNewExtraSamples);
    7676             :     }
    7677             : 
    7678             :     /* -------------------------------------------------------------------- */
    7679             :     /*      If the output is jpeg compressed, and the input is RGB make     */
    7680             :     /*      sure we note that.                                              */
    7681             :     /* -------------------------------------------------------------------- */
    7682             : 
    7683        2044 :     if (l_nCompression == COMPRESSION_JPEG)
    7684             :     {
    7685         134 :         if (l_nBands >= 3 &&
    7686          58 :             (poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
    7687           0 :              GCI_YCbCr_YBand) &&
    7688           0 :             (poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
    7689         134 :              GCI_YCbCr_CbBand) &&
    7690           0 :             (poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
    7691             :              GCI_YCbCr_CrBand))
    7692             :         {
    7693             :             // Do nothing.
    7694             :         }
    7695             :         else
    7696             :         {
    7697             :             // Assume RGB if it is not explicitly YCbCr.
    7698          76 :             CPLDebug("GTiff", "Setting JPEGCOLORMODE_RGB");
    7699          76 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    7700             :         }
    7701             :     }
    7702             : 
    7703             :     /* -------------------------------------------------------------------- */
    7704             :     /*      Does the source image consist of one band, with a palette?      */
    7705             :     /*      If so, copy over.                                               */
    7706             :     /* -------------------------------------------------------------------- */
    7707        1281 :     if ((l_nBands == 1 || l_nBands == 2) &&
    7708        3325 :         poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7709             :         eType == GDT_Byte)
    7710             :     {
    7711          16 :         unsigned short anTRed[256] = {0};
    7712          16 :         unsigned short anTGreen[256] = {0};
    7713          16 :         unsigned short anTBlue[256] = {0};
    7714          16 :         GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    7715             : 
    7716        4112 :         for (int iColor = 0; iColor < 256; ++iColor)
    7717             :         {
    7718        4096 :             if (iColor < poCT->GetColorEntryCount())
    7719             :             {
    7720        3041 :                 GDALColorEntry sRGB = {0, 0, 0, 0};
    7721             : 
    7722        3041 :                 poCT->GetColorEntryAsRGB(iColor, &sRGB);
    7723             : 
    7724        6082 :                 anTRed[iColor] = GTiffDataset::ClampCTEntry(
    7725        3041 :                     iColor, 1, sRGB.c1, nColorTableMultiplier);
    7726        6082 :                 anTGreen[iColor] = GTiffDataset::ClampCTEntry(
    7727        3041 :                     iColor, 2, sRGB.c2, nColorTableMultiplier);
    7728        3041 :                 anTBlue[iColor] = GTiffDataset::ClampCTEntry(
    7729        3041 :                     iColor, 3, sRGB.c3, nColorTableMultiplier);
    7730             :             }
    7731             :             else
    7732             :             {
    7733        1055 :                 anTRed[iColor] = 0;
    7734        1055 :                 anTGreen[iColor] = 0;
    7735        1055 :                 anTBlue[iColor] = 0;
    7736             :             }
    7737             :         }
    7738             : 
    7739          16 :         if (!bForcePhotometric)
    7740          16 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    7741          16 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, anTRed, anTGreen, anTBlue);
    7742             :     }
    7743        1280 :     else if ((l_nBands == 1 || l_nBands == 2) &&
    7744        3308 :              poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7745             :              eType == GDT_UInt16)
    7746             :     {
    7747             :         unsigned short *panTRed = static_cast<unsigned short *>(
    7748           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7749             :         unsigned short *panTGreen = static_cast<unsigned short *>(
    7750           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7751             :         unsigned short *panTBlue = static_cast<unsigned short *>(
    7752           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7753             : 
    7754           1 :         GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    7755             : 
    7756       65537 :         for (int iColor = 0; iColor < 65536; ++iColor)
    7757             :         {
    7758       65536 :             if (iColor < poCT->GetColorEntryCount())
    7759             :             {
    7760       65536 :                 GDALColorEntry sRGB = {0, 0, 0, 0};
    7761             : 
    7762       65536 :                 poCT->GetColorEntryAsRGB(iColor, &sRGB);
    7763             : 
    7764      131072 :                 panTRed[iColor] = GTiffDataset::ClampCTEntry(
    7765       65536 :                     iColor, 1, sRGB.c1, nColorTableMultiplier);
    7766      131072 :                 panTGreen[iColor] = GTiffDataset::ClampCTEntry(
    7767       65536 :                     iColor, 2, sRGB.c2, nColorTableMultiplier);
    7768       65536 :                 panTBlue[iColor] = GTiffDataset::ClampCTEntry(
    7769       65536 :                     iColor, 3, sRGB.c3, nColorTableMultiplier);
    7770             :             }
    7771             :             else
    7772             :             {
    7773           0 :                 panTRed[iColor] = 0;
    7774           0 :                 panTGreen[iColor] = 0;
    7775           0 :                 panTBlue[iColor] = 0;
    7776             :             }
    7777             :         }
    7778             : 
    7779           1 :         if (!bForcePhotometric)
    7780           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    7781           1 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue);
    7782             : 
    7783           1 :         CPLFree(panTRed);
    7784           1 :         CPLFree(panTGreen);
    7785           1 :         CPLFree(panTBlue);
    7786             :     }
    7787        2027 :     else if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
    7788           1 :         ReportError(
    7789             :             pszFilename, CE_Failure, CPLE_AppDefined,
    7790             :             "Unable to export color table to GeoTIFF file.  Color tables "
    7791             :             "can only be written to 1 band or 2 bands Byte or "
    7792             :             "UInt16 GeoTIFF files.");
    7793             : 
    7794        2044 :     if (l_nCompression == COMPRESSION_JPEG)
    7795             :     {
    7796          76 :         uint16_t l_nPhotometric = 0;
    7797          76 :         TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &l_nPhotometric);
    7798             :         // Check done in tif_jpeg.c later, but not with a very clear error
    7799             :         // message
    7800          76 :         if (l_nPhotometric == PHOTOMETRIC_PALETTE)
    7801             :         {
    7802           1 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    7803             :                         "JPEG compression not supported with paletted image");
    7804           1 :             XTIFFClose(l_hTIFF);
    7805           1 :             VSIUnlink(l_osTmpFilename);
    7806           1 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    7807           1 :             return nullptr;
    7808             :         }
    7809             :     }
    7810             : 
    7811        2128 :     if (l_nBands == 2 &&
    7812        2043 :         poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7813           0 :         (eType == GDT_Byte || eType == GDT_UInt16))
    7814             :     {
    7815           1 :         uint16_t v[1] = {EXTRASAMPLE_UNASSALPHA};
    7816             : 
    7817           1 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
    7818             :     }
    7819             : 
    7820        2043 :     const int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags();
    7821        2043 :     bool bCreateMask = false;
    7822        4086 :     CPLString osHiddenStructuralMD;
    7823             :     const char *pszInterleave =
    7824        2043 :         CSLFetchNameValueDef(papszOptions, "INTERLEAVE", "PIXEL");
    7825        2230 :     if (bCopySrcOverviews &&
    7826         187 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "TILED", "NO")))
    7827             :     {
    7828         175 :         osHiddenStructuralMD += "LAYOUT=IFDS_BEFORE_DATA\n";
    7829         175 :         osHiddenStructuralMD += "BLOCK_ORDER=ROW_MAJOR\n";
    7830         175 :         osHiddenStructuralMD += "BLOCK_LEADER=SIZE_AS_UINT4\n";
    7831         175 :         osHiddenStructuralMD += "BLOCK_TRAILER=LAST_4_BYTES_REPEATED\n";
    7832         175 :         if (l_nBands > 1 && !EQUAL(pszInterleave, "PIXEL"))
    7833             :         {
    7834          21 :             osHiddenStructuralMD += "INTERLEAVE=";
    7835          21 :             osHiddenStructuralMD += CPLString(pszInterleave).toupper();
    7836          21 :             osHiddenStructuralMD += "\n";
    7837             :         }
    7838             :         osHiddenStructuralMD +=
    7839         175 :             "KNOWN_INCOMPATIBLE_EDITION=NO\n ";  // Final space intended, so
    7840             :                                                  // this can be replaced by YES
    7841             :     }
    7842        2043 :     if (!(nMaskFlags & (GMF_ALL_VALID | GMF_ALPHA | GMF_NODATA)) &&
    7843          42 :         (nMaskFlags & GMF_PER_DATASET) && !bStreaming)
    7844             :     {
    7845          38 :         bCreateMask = true;
    7846          38 :         if (GTiffDataset::MustCreateInternalMask() &&
    7847          38 :             !osHiddenStructuralMD.empty() && EQUAL(pszInterleave, "PIXEL"))
    7848             :         {
    7849          21 :             osHiddenStructuralMD += "MASK_INTERLEAVED_WITH_IMAGERY=YES\n";
    7850             :         }
    7851             :     }
    7852        2043 :     if (!osHiddenStructuralMD.empty())
    7853             :     {
    7854         175 :         const int nHiddenMDSize = static_cast<int>(osHiddenStructuralMD.size());
    7855             :         osHiddenStructuralMD =
    7856         175 :             CPLOPrintf("GDAL_STRUCTURAL_METADATA_SIZE=%06d bytes\n",
    7857         350 :                        nHiddenMDSize) +
    7858         175 :             osHiddenStructuralMD;
    7859         175 :         VSI_TIFFWrite(l_hTIFF, osHiddenStructuralMD.c_str(),
    7860             :                       osHiddenStructuralMD.size());
    7861             :     }
    7862             : 
    7863             :     // FIXME? libtiff writes extended tags in the order they are specified
    7864             :     // and not in increasing order.
    7865             : 
    7866             :     /* -------------------------------------------------------------------- */
    7867             :     /*      Transfer some TIFF specific metadata, if available.             */
    7868             :     /*      The return value will tell us if we need to try again later with*/
    7869             :     /*      PAM because the profile doesn't allow to write some metadata    */
    7870             :     /*      as TIFF tag                                                     */
    7871             :     /* -------------------------------------------------------------------- */
    7872        2043 :     const bool bHasWrittenMDInGeotiffTAG = GTiffDataset::WriteMetadata(
    7873             :         poSrcDS, l_hTIFF, false, eProfile, pszFilename, papszOptions);
    7874             : 
    7875             :     /* -------------------------------------------------------------------- */
    7876             :     /*      Write NoData value, if exist.                                   */
    7877             :     /* -------------------------------------------------------------------- */
    7878        2043 :     if (eProfile == GTiffProfile::GDALGEOTIFF)
    7879             :     {
    7880        2022 :         int bSuccess = FALSE;
    7881        2022 :         GDALRasterBand *poFirstBand = poSrcDS->GetRasterBand(1);
    7882        2022 :         if (poFirstBand->GetRasterDataType() == GDT_Int64)
    7883             :         {
    7884           2 :             const auto nNoData = poFirstBand->GetNoDataValueAsInt64(&bSuccess);
    7885           2 :             if (bSuccess)
    7886           1 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, nNoData);
    7887             :         }
    7888        2020 :         else if (poFirstBand->GetRasterDataType() == GDT_UInt64)
    7889             :         {
    7890           2 :             const auto nNoData = poFirstBand->GetNoDataValueAsUInt64(&bSuccess);
    7891           2 :             if (bSuccess)
    7892           1 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, nNoData);
    7893             :         }
    7894             :         else
    7895             :         {
    7896        2018 :             const auto dfNoData = poFirstBand->GetNoDataValue(&bSuccess);
    7897        2018 :             if (bSuccess)
    7898         139 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, dfNoData);
    7899             :         }
    7900             :     }
    7901             : 
    7902             :     /* -------------------------------------------------------------------- */
    7903             :     /*      Are we addressing PixelIsPoint mode?                            */
    7904             :     /* -------------------------------------------------------------------- */
    7905        2043 :     bool bPixelIsPoint = false;
    7906        2043 :     bool bPointGeoIgnore = false;
    7907             : 
    7908        3444 :     if (poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT) &&
    7909        1401 :         EQUAL(poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT), GDALMD_AOP_POINT))
    7910             :     {
    7911          10 :         bPixelIsPoint = true;
    7912             :         bPointGeoIgnore =
    7913          10 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    7914             :     }
    7915             : 
    7916             :     /* -------------------------------------------------------------------- */
    7917             :     /*      Write affine transform if it is meaningful.                     */
    7918             :     /* -------------------------------------------------------------------- */
    7919        2043 :     const OGRSpatialReference *l_poSRS = nullptr;
    7920        2043 :     GDALGeoTransform l_gt;
    7921        2043 :     if (poSrcDS->GetGeoTransform(l_gt) == CE_None)
    7922             :     {
    7923        1620 :         if (bGeoTIFF)
    7924             :         {
    7925        1615 :             l_poSRS = poSrcDS->GetSpatialRef();
    7926             : 
    7927        1615 :             if (l_gt[2] == 0.0 && l_gt[4] == 0.0 && l_gt[5] < 0.0)
    7928             :             {
    7929        1600 :                 double dfOffset = 0.0;
    7930             :                 {
    7931             :                     // In the case the SRS has a vertical component and we have
    7932             :                     // a single band, encode its scale/offset in the GeoTIFF
    7933             :                     // tags
    7934        1600 :                     int bHasScale = FALSE;
    7935             :                     double dfScale =
    7936        1600 :                         poSrcDS->GetRasterBand(1)->GetScale(&bHasScale);
    7937        1600 :                     int bHasOffset = FALSE;
    7938             :                     dfOffset =
    7939        1600 :                         poSrcDS->GetRasterBand(1)->GetOffset(&bHasOffset);
    7940             :                     const bool bApplyScaleOffset =
    7941        1604 :                         l_poSRS && l_poSRS->IsVertical() &&
    7942           4 :                         poSrcDS->GetRasterCount() == 1;
    7943        1600 :                     if (bApplyScaleOffset && !bHasScale)
    7944           0 :                         dfScale = 1.0;
    7945        1600 :                     if (!bApplyScaleOffset || !bHasOffset)
    7946        1596 :                         dfOffset = 0.0;
    7947        1600 :                     const double adfPixelScale[3] = {l_gt[1], fabs(l_gt[5]),
    7948        1600 :                                                      bApplyScaleOffset ? dfScale
    7949        1600 :                                                                        : 0.0};
    7950             : 
    7951        1600 :                     TIFFSetField(l_hTIFF, TIFFTAG_GEOPIXELSCALE, 3,
    7952             :                                  adfPixelScale);
    7953             :                 }
    7954             : 
    7955        1600 :                 double adfTiePoints[6] = {0.0,     0.0,     0.0,
    7956        1600 :                                           l_gt[0], l_gt[3], dfOffset};
    7957             : 
    7958        1600 :                 if (bPixelIsPoint && !bPointGeoIgnore)
    7959             :                 {
    7960           6 :                     adfTiePoints[3] += l_gt[1] * 0.5 + l_gt[2] * 0.5;
    7961           6 :                     adfTiePoints[4] += l_gt[4] * 0.5 + l_gt[5] * 0.5;
    7962             :                 }
    7963             : 
    7964        1600 :                 TIFFSetField(l_hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
    7965             :             }
    7966             :             else
    7967             :             {
    7968          15 :                 double adfMatrix[16] = {0.0};
    7969             : 
    7970          15 :                 adfMatrix[0] = l_gt[1];
    7971          15 :                 adfMatrix[1] = l_gt[2];
    7972          15 :                 adfMatrix[3] = l_gt[0];
    7973          15 :                 adfMatrix[4] = l_gt[4];
    7974          15 :                 adfMatrix[5] = l_gt[5];
    7975          15 :                 adfMatrix[7] = l_gt[3];
    7976          15 :                 adfMatrix[15] = 1.0;
    7977             : 
    7978          15 :                 if (bPixelIsPoint && !bPointGeoIgnore)
    7979             :                 {
    7980           0 :                     adfMatrix[3] += l_gt[1] * 0.5 + l_gt[2] * 0.5;
    7981           0 :                     adfMatrix[7] += l_gt[4] * 0.5 + l_gt[5] * 0.5;
    7982             :                 }
    7983             : 
    7984          15 :                 TIFFSetField(l_hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
    7985             :             }
    7986             :         }
    7987             : 
    7988             :         /* --------------------------------------------------------------------
    7989             :          */
    7990             :         /*      Do we need a TFW file? */
    7991             :         /* --------------------------------------------------------------------
    7992             :          */
    7993        1620 :         if (CPLFetchBool(papszOptions, "TFW", false))
    7994           2 :             GDALWriteWorldFile(pszFilename, "tfw", l_gt.data());
    7995        1618 :         else if (CPLFetchBool(papszOptions, "WORLDFILE", false))
    7996           1 :             GDALWriteWorldFile(pszFilename, "wld", l_gt.data());
    7997             :     }
    7998             : 
    7999             :     /* -------------------------------------------------------------------- */
    8000             :     /*      Otherwise write tiepoints if they are available.                */
    8001             :     /* -------------------------------------------------------------------- */
    8002         423 :     else if (poSrcDS->GetGCPCount() > 0 && bGeoTIFF)
    8003             :     {
    8004          12 :         const GDAL_GCP *pasGCPs = poSrcDS->GetGCPs();
    8005             :         double *padfTiePoints = static_cast<double *>(
    8006          12 :             CPLMalloc(6 * sizeof(double) * poSrcDS->GetGCPCount()));
    8007             : 
    8008          60 :         for (int iGCP = 0; iGCP < poSrcDS->GetGCPCount(); ++iGCP)
    8009             :         {
    8010             : 
    8011          48 :             padfTiePoints[iGCP * 6 + 0] = pasGCPs[iGCP].dfGCPPixel;
    8012          48 :             padfTiePoints[iGCP * 6 + 1] = pasGCPs[iGCP].dfGCPLine;
    8013          48 :             padfTiePoints[iGCP * 6 + 2] = 0;
    8014          48 :             padfTiePoints[iGCP * 6 + 3] = pasGCPs[iGCP].dfGCPX;
    8015          48 :             padfTiePoints[iGCP * 6 + 4] = pasGCPs[iGCP].dfGCPY;
    8016          48 :             padfTiePoints[iGCP * 6 + 5] = pasGCPs[iGCP].dfGCPZ;
    8017             : 
    8018          48 :             if (bPixelIsPoint && !bPointGeoIgnore)
    8019             :             {
    8020           4 :                 padfTiePoints[iGCP * 6 + 0] -= 0.5;
    8021           4 :                 padfTiePoints[iGCP * 6 + 1] -= 0.5;
    8022             :             }
    8023             :         }
    8024             : 
    8025          12 :         TIFFSetField(l_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * poSrcDS->GetGCPCount(),
    8026             :                      padfTiePoints);
    8027          12 :         CPLFree(padfTiePoints);
    8028             : 
    8029          12 :         l_poSRS = poSrcDS->GetGCPSpatialRef();
    8030             : 
    8031          24 :         if (CPLFetchBool(papszOptions, "TFW", false) ||
    8032          12 :             CPLFetchBool(papszOptions, "WORLDFILE", false))
    8033             :         {
    8034           0 :             ReportError(
    8035             :                 pszFilename, CE_Warning, CPLE_AppDefined,
    8036             :                 "TFW=ON or WORLDFILE=ON creation options are ignored when "
    8037             :                 "GCPs are available");
    8038             :         }
    8039             :     }
    8040             :     else
    8041             :     {
    8042         411 :         l_poSRS = poSrcDS->GetSpatialRef();
    8043             :     }
    8044             : 
    8045             :     /* -------------------------------------------------------------------- */
    8046             :     /*      Copy xml:XMP data                                               */
    8047             :     /* -------------------------------------------------------------------- */
    8048        2043 :     char **papszXMP = poSrcDS->GetMetadata("xml:XMP");
    8049        2043 :     if (papszXMP != nullptr && *papszXMP != nullptr)
    8050             :     {
    8051           9 :         int nTagSize = static_cast<int>(strlen(*papszXMP));
    8052           9 :         TIFFSetField(l_hTIFF, TIFFTAG_XMLPACKET, nTagSize, *papszXMP);
    8053             :     }
    8054             : 
    8055             :     /* -------------------------------------------------------------------- */
    8056             :     /*      Write the projection information, if possible.                  */
    8057             :     /* -------------------------------------------------------------------- */
    8058        2043 :     const bool bHasProjection = l_poSRS != nullptr;
    8059        2043 :     bool bExportSRSToPAM = false;
    8060        2043 :     if ((bHasProjection || bPixelIsPoint) && bGeoTIFF)
    8061             :     {
    8062        1586 :         GTIF *psGTIF = GTiffDataset::GTIFNew(l_hTIFF);
    8063             : 
    8064        1586 :         if (bHasProjection)
    8065             :         {
    8066        1586 :             const auto eGeoTIFFKeysFlavor = GetGTIFFKeysFlavor(papszOptions);
    8067        1586 :             if (IsSRSCompatibleOfGeoTIFF(l_poSRS, eGeoTIFFKeysFlavor))
    8068             :             {
    8069        1586 :                 GTIFSetFromOGISDefnEx(
    8070             :                     psGTIF,
    8071             :                     OGRSpatialReference::ToHandle(
    8072             :                         const_cast<OGRSpatialReference *>(l_poSRS)),
    8073             :                     eGeoTIFFKeysFlavor, GetGeoTIFFVersion(papszOptions));
    8074             :             }
    8075             :             else
    8076             :             {
    8077           0 :                 bExportSRSToPAM = true;
    8078             :             }
    8079             :         }
    8080             : 
    8081        1586 :         if (bPixelIsPoint)
    8082             :         {
    8083          10 :             GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
    8084             :                        RasterPixelIsPoint);
    8085             :         }
    8086             : 
    8087        1586 :         GTIFWriteKeys(psGTIF);
    8088        1586 :         GTIFFree(psGTIF);
    8089             :     }
    8090             : 
    8091        2043 :     bool l_bDontReloadFirstBlock = false;
    8092             : 
    8093             : #ifdef HAVE_LIBJPEG
    8094        2043 :     if (bCopyFromJPEG)
    8095             :     {
    8096          12 :         GTIFF_CopyFromJPEG_WriteAdditionalTags(l_hTIFF, poSrcDS);
    8097             :     }
    8098             : #endif
    8099             : 
    8100             :     /* -------------------------------------------------------------------- */
    8101             :     /*      Cleanup                                                         */
    8102             :     /* -------------------------------------------------------------------- */
    8103        2043 :     if (bCopySrcOverviews)
    8104             :     {
    8105         187 :         TIFFDeferStrileArrayWriting(l_hTIFF);
    8106             :     }
    8107        2043 :     TIFFWriteCheck(l_hTIFF, TIFFIsTiled(l_hTIFF), "GTiffCreateCopy()");
    8108        2043 :     TIFFWriteDirectory(l_hTIFF);
    8109        2043 :     if (bStreaming)
    8110             :     {
    8111             :         // We need to write twice the directory to be sure that custom
    8112             :         // TIFF tags are correctly sorted and that padding bytes have been
    8113             :         // added.
    8114           5 :         TIFFSetDirectory(l_hTIFF, 0);
    8115           5 :         TIFFWriteDirectory(l_hTIFF);
    8116             : 
    8117           5 :         if (VSIFSeekL(l_fpL, 0, SEEK_END) != 0)
    8118           0 :             ReportError(pszFilename, CE_Failure, CPLE_FileIO, "Cannot seek");
    8119           5 :         const int nSize = static_cast<int>(VSIFTellL(l_fpL));
    8120             : 
    8121           5 :         vsi_l_offset nDataLength = 0;
    8122           5 :         VSIGetMemFileBuffer(l_osTmpFilename, &nDataLength, FALSE);
    8123           5 :         TIFFSetDirectory(l_hTIFF, 0);
    8124           5 :         GTiffFillStreamableOffsetAndCount(l_hTIFF, nSize);
    8125           5 :         TIFFWriteDirectory(l_hTIFF);
    8126             :     }
    8127        2043 :     const auto nDirCount = TIFFNumberOfDirectories(l_hTIFF);
    8128        2043 :     if (nDirCount >= 1)
    8129             :     {
    8130        2036 :         TIFFSetDirectory(l_hTIFF, static_cast<tdir_t>(nDirCount - 1));
    8131             :     }
    8132        2043 :     const toff_t l_nDirOffset = TIFFCurrentDirOffset(l_hTIFF);
    8133        2043 :     TIFFFlush(l_hTIFF);
    8134        2043 :     XTIFFClose(l_hTIFF);
    8135             : 
    8136        2043 :     VSIFSeekL(l_fpL, 0, SEEK_SET);
    8137             : 
    8138             :     // fpStreaming will assigned to the instance and not closed here.
    8139        2043 :     VSILFILE *fpStreaming = nullptr;
    8140        2043 :     if (bStreaming)
    8141             :     {
    8142           5 :         vsi_l_offset nDataLength = 0;
    8143             :         void *pabyBuffer =
    8144           5 :             VSIGetMemFileBuffer(l_osTmpFilename, &nDataLength, FALSE);
    8145           5 :         fpStreaming = VSIFOpenL(pszFilename, "wb");
    8146           5 :         if (fpStreaming == nullptr)
    8147             :         {
    8148           1 :             VSIUnlink(l_osTmpFilename);
    8149           1 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    8150           1 :             return nullptr;
    8151             :         }
    8152           4 :         if (static_cast<vsi_l_offset>(VSIFWriteL(pabyBuffer, 1,
    8153             :                                                  static_cast<int>(nDataLength),
    8154           4 :                                                  fpStreaming)) != nDataLength)
    8155             :         {
    8156           0 :             ReportError(pszFilename, CE_Failure, CPLE_FileIO,
    8157             :                         "Could not write %d bytes",
    8158             :                         static_cast<int>(nDataLength));
    8159           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fpStreaming));
    8160           0 :             VSIUnlink(l_osTmpFilename);
    8161           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    8162           0 :             return nullptr;
    8163             :         }
    8164             :     }
    8165             : 
    8166             :     /* -------------------------------------------------------------------- */
    8167             :     /*      Re-open as a dataset and copy over missing metadata using       */
    8168             :     /*      PAM facilities.                                                 */
    8169             :     /* -------------------------------------------------------------------- */
    8170        2042 :     l_hTIFF = VSI_TIFFOpen(bStreaming ? l_osTmpFilename.c_str() : pszFilename,
    8171             :                            "r+", l_fpL);
    8172        2042 :     if (l_hTIFF == nullptr)
    8173             :     {
    8174          11 :         if (bStreaming)
    8175           0 :             VSIUnlink(l_osTmpFilename);
    8176          11 :         l_fpL->CancelCreation();
    8177          11 :         CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    8178          11 :         return nullptr;
    8179             :     }
    8180             : 
    8181             :     /* -------------------------------------------------------------------- */
    8182             :     /*      Create a corresponding GDALDataset.                             */
    8183             :     /* -------------------------------------------------------------------- */
    8184        2031 :     GTiffDataset *poDS = new GTiffDataset();
    8185             :     const bool bSuppressASAP =
    8186        2031 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "@SUPPRESS_ASAP", "NO"));
    8187        2031 :     if (bSuppressASAP)
    8188           4 :         poDS->MarkSuppressOnClose();
    8189        2031 :     poDS->SetDescription(pszFilename);
    8190        2031 :     poDS->eAccess = GA_Update;
    8191        2031 :     poDS->m_pszFilename = CPLStrdup(pszFilename);
    8192        2031 :     poDS->m_fpL = l_fpL;
    8193        2031 :     poDS->m_bIMDRPCMetadataLoaded = true;
    8194        2031 :     poDS->m_nColorTableMultiplier = nColorTableMultiplier;
    8195        2031 :     poDS->m_bTileInterleave = bTileInterleaving;
    8196             : 
    8197        2031 :     if (bTileInterleaving)
    8198             :     {
    8199           7 :         poDS->m_oGTiffMDMD.SetMetadataItem("INTERLEAVE", "TILE",
    8200             :                                            "IMAGE_STRUCTURE");
    8201             :     }
    8202             : 
    8203        2031 :     const bool bAppend = CPLFetchBool(papszOptions, "APPEND_SUBDATASET", false);
    8204        4061 :     if (poDS->OpenOffset(l_hTIFF,
    8205        2030 :                          bAppend ? l_nDirOffset : TIFFCurrentDirOffset(l_hTIFF),
    8206             :                          GA_Update,
    8207             :                          false,  // bAllowRGBAInterface
    8208             :                          true    // bReadGeoTransform
    8209        2031 :                          ) != CE_None)
    8210             :     {
    8211           0 :         l_fpL->CancelCreation();
    8212           0 :         delete poDS;
    8213           0 :         if (bStreaming)
    8214           0 :             VSIUnlink(l_osTmpFilename);
    8215           0 :         return nullptr;
    8216             :     }
    8217             : 
    8218             :     // Legacy... Patch back GDT_Int8 type to GDT_Byte if the user used
    8219             :     // PIXELTYPE=SIGNEDBYTE
    8220        2031 :     const char *pszPixelType = CSLFetchNameValue(papszOptions, "PIXELTYPE");
    8221        2031 :     if (pszPixelType == nullptr)
    8222        2026 :         pszPixelType = "";
    8223        2031 :     if (eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE"))
    8224             :     {
    8225          10 :         for (int i = 0; i < poDS->nBands; ++i)
    8226             :         {
    8227           5 :             auto poBand = static_cast<GTiffRasterBand *>(poDS->papoBands[i]);
    8228           5 :             poBand->eDataType = GDT_Byte;
    8229           5 :             poBand->EnablePixelTypeSignedByteWarning(false);
    8230           5 :             poBand->SetMetadataItem("PIXELTYPE", "SIGNEDBYTE",
    8231             :                                     "IMAGE_STRUCTURE");
    8232           5 :             poBand->EnablePixelTypeSignedByteWarning(true);
    8233             :         }
    8234             :     }
    8235             : 
    8236        2031 :     poDS->oOvManager.Initialize(poDS, pszFilename);
    8237             : 
    8238        2031 :     if (bStreaming)
    8239             :     {
    8240           4 :         VSIUnlink(l_osTmpFilename);
    8241           4 :         poDS->m_fpToWrite = fpStreaming;
    8242             :     }
    8243        2031 :     poDS->m_eProfile = eProfile;
    8244             : 
    8245        2031 :     int nCloneInfoFlags = GCIF_PAM_DEFAULT & ~GCIF_MASK;
    8246             : 
    8247             :     // If we explicitly asked not to tag the alpha band as such, do not
    8248             :     // reintroduce this alpha color interpretation in PAM.
    8249        2031 :     if (poSrcDS->GetRasterBand(l_nBands)->GetColorInterpretation() ==
    8250        2154 :             GCI_AlphaBand &&
    8251         123 :         GTiffGetAlphaValue(
    8252             :             CPLGetConfigOption("GTIFF_ALPHA",
    8253             :                                CSLFetchNameValue(papszOptions, "ALPHA")),
    8254             :             DEFAULT_ALPHA_TYPE) == EXTRASAMPLE_UNSPECIFIED)
    8255             :     {
    8256           1 :         nCloneInfoFlags &= ~GCIF_COLORINTERP;
    8257             :     }
    8258             :     // Ignore source band color interpretation if requesting PHOTOMETRIC=RGB
    8259        3224 :     else if (l_nBands >= 3 &&
    8260        1194 :              EQUAL(CSLFetchNameValueDef(papszOptions, "PHOTOMETRIC", ""),
    8261             :                    "RGB"))
    8262             :     {
    8263          28 :         for (int i = 1; i <= 3; i++)
    8264             :         {
    8265          21 :             poDS->GetRasterBand(i)->SetColorInterpretation(
    8266          21 :                 static_cast<GDALColorInterp>(GCI_RedBand + (i - 1)));
    8267             :         }
    8268           7 :         nCloneInfoFlags &= ~GCIF_COLORINTERP;
    8269           9 :         if (!(l_nBands == 4 &&
    8270           2 :               CSLFetchNameValue(papszOptions, "ALPHA") != nullptr))
    8271             :         {
    8272          15 :             for (int i = 4; i <= l_nBands; i++)
    8273             :             {
    8274          18 :                 poDS->GetRasterBand(i)->SetColorInterpretation(
    8275           9 :                     poSrcDS->GetRasterBand(i)->GetColorInterpretation());
    8276             :             }
    8277             :         }
    8278             :     }
    8279             : 
    8280             :     CPLString osOldGTIFF_REPORT_COMPD_CSVal(
    8281        4062 :         CPLGetConfigOption("GTIFF_REPORT_COMPD_CS", ""));
    8282        2031 :     CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS", "YES");
    8283        2031 :     poDS->CloneInfo(poSrcDS, nCloneInfoFlags);
    8284        2031 :     CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS",
    8285        2031 :                                   osOldGTIFF_REPORT_COMPD_CSVal.empty()
    8286             :                                       ? nullptr
    8287           0 :                                       : osOldGTIFF_REPORT_COMPD_CSVal.c_str());
    8288             : 
    8289        2048 :     if ((!bGeoTIFF || bExportSRSToPAM) &&
    8290          17 :         (poDS->GetPamFlags() & GPF_DISABLED) == 0)
    8291             :     {
    8292             :         // Copy georeferencing info to PAM if the profile is not GeoTIFF
    8293          16 :         poDS->GDALPamDataset::SetSpatialRef(poDS->GetSpatialRef());
    8294          16 :         GDALGeoTransform gt;
    8295          16 :         if (poDS->GetGeoTransform(gt) == CE_None)
    8296             :         {
    8297           5 :             poDS->GDALPamDataset::SetGeoTransform(gt);
    8298             :         }
    8299          16 :         poDS->GDALPamDataset::SetGCPs(poDS->GetGCPCount(), poDS->GetGCPs(),
    8300             :                                       poDS->GetGCPSpatialRef());
    8301             :     }
    8302             : 
    8303        2031 :     poDS->m_papszCreationOptions = CSLDuplicate(papszOptions);
    8304        2031 :     poDS->m_bDontReloadFirstBlock = l_bDontReloadFirstBlock;
    8305             : 
    8306             :     /* -------------------------------------------------------------------- */
    8307             :     /*      CloneInfo() does not merge metadata, it just replaces it        */
    8308             :     /*      totally.  So we have to merge it.                               */
    8309             :     /* -------------------------------------------------------------------- */
    8310             : 
    8311        2031 :     char **papszSRC_MD = poSrcDS->GetMetadata();
    8312        2031 :     char **papszDST_MD = CSLDuplicate(poDS->GetMetadata());
    8313             : 
    8314        2031 :     papszDST_MD = CSLMerge(papszDST_MD, papszSRC_MD);
    8315             : 
    8316        2031 :     poDS->SetMetadata(papszDST_MD);
    8317        2031 :     CSLDestroy(papszDST_MD);
    8318             : 
    8319             :     // Depending on the PHOTOMETRIC tag, the TIFF file may not have the same
    8320             :     // band count as the source. Will fail later in GDALDatasetCopyWholeRaster
    8321             :     // anyway.
    8322        6916 :     for (int nBand = 1;
    8323        6916 :          nBand <= std::min(poDS->GetRasterCount(), poSrcDS->GetRasterCount());
    8324             :          ++nBand)
    8325             :     {
    8326        4885 :         GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(nBand);
    8327        4885 :         GDALRasterBand *poDstBand = poDS->GetRasterBand(nBand);
    8328        4885 :         papszSRC_MD = poSrcBand->GetMetadata();
    8329        4885 :         papszDST_MD = CSLDuplicate(poDstBand->GetMetadata());
    8330             : 
    8331        4885 :         papszDST_MD = CSLMerge(papszDST_MD, papszSRC_MD);
    8332             : 
    8333        4885 :         poDstBand->SetMetadata(papszDST_MD);
    8334        4885 :         CSLDestroy(papszDST_MD);
    8335             : 
    8336        4885 :         char **papszCatNames = poSrcBand->GetCategoryNames();
    8337        4885 :         if (nullptr != papszCatNames)
    8338           0 :             poDstBand->SetCategoryNames(papszCatNames);
    8339             :     }
    8340             : 
    8341        2031 :     l_hTIFF = static_cast<TIFF *>(poDS->GetInternalHandle("TIFF_HANDLE"));
    8342             : 
    8343             :     /* -------------------------------------------------------------------- */
    8344             :     /*      Handle forcing xml:ESRI data to be written to PAM.              */
    8345             :     /* -------------------------------------------------------------------- */
    8346        2031 :     if (CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
    8347             :     {
    8348           1 :         char **papszESRIMD = poSrcDS->GetMetadata("xml:ESRI");
    8349           1 :         if (papszESRIMD)
    8350             :         {
    8351           1 :             poDS->SetMetadata(papszESRIMD, "xml:ESRI");
    8352             :         }
    8353             :     }
    8354             : 
    8355             :     /* -------------------------------------------------------------------- */
    8356             :     /*      Second chance: now that we have a PAM dataset, it is possible   */
    8357             :     /*      to write metadata that we could not write as a TIFF tag.        */
    8358             :     /* -------------------------------------------------------------------- */
    8359        2031 :     if (!bHasWrittenMDInGeotiffTAG && !bStreaming)
    8360             :     {
    8361           6 :         GTiffDataset::WriteMetadata(
    8362             :             poDS, l_hTIFF, true, eProfile, pszFilename, papszOptions,
    8363             :             true /* don't write RPC and IMD file again */);
    8364             :     }
    8365             : 
    8366        2031 :     if (!bStreaming)
    8367        2027 :         GTiffDataset::WriteRPC(poDS, l_hTIFF, true, eProfile, pszFilename,
    8368             :                                papszOptions,
    8369             :                                true /* write only in PAM AND if needed */);
    8370             : 
    8371        2031 :     poDS->m_bWriteCOGLayout = bCopySrcOverviews;
    8372             : 
    8373             :     // To avoid unnecessary directory rewriting.
    8374        2031 :     poDS->m_bMetadataChanged = false;
    8375        2031 :     poDS->m_bGeoTIFFInfoChanged = false;
    8376        2031 :     poDS->m_bNoDataChanged = false;
    8377        2031 :     poDS->m_bForceUnsetGTOrGCPs = false;
    8378        2031 :     poDS->m_bForceUnsetProjection = false;
    8379        2031 :     poDS->m_bStreamingOut = bStreaming;
    8380             : 
    8381             :     // Don't try to load external metadata files (#6597).
    8382        2031 :     poDS->m_bIMDRPCMetadataLoaded = true;
    8383             : 
    8384             :     // We must re-set the compression level at this point, since it has been
    8385             :     // lost a few lines above when closing the newly create TIFF file The
    8386             :     // TIFFTAG_ZIPQUALITY & TIFFTAG_JPEGQUALITY are not store in the TIFF file.
    8387             :     // They are just TIFF session parameters.
    8388             : 
    8389        2031 :     poDS->m_nZLevel = GTiffGetZLevel(papszOptions);
    8390        2031 :     poDS->m_nLZMAPreset = GTiffGetLZMAPreset(papszOptions);
    8391        2031 :     poDS->m_nZSTDLevel = GTiffGetZSTDPreset(papszOptions);
    8392        2031 :     poDS->m_nWebPLevel = GTiffGetWebPLevel(papszOptions);
    8393        2031 :     poDS->m_bWebPLossless = GTiffGetWebPLossless(papszOptions);
    8394        2034 :     if (poDS->m_nWebPLevel != 100 && poDS->m_bWebPLossless &&
    8395           3 :         CSLFetchNameValue(papszOptions, "WEBP_LEVEL"))
    8396             :     {
    8397           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    8398             :                  "WEBP_LEVEL is specified, but WEBP_LOSSLESS=YES. "
    8399             :                  "WEBP_LEVEL will be ignored.");
    8400             :     }
    8401        2031 :     poDS->m_nJpegQuality = GTiffGetJpegQuality(papszOptions);
    8402        2031 :     poDS->m_nJpegTablesMode = GTiffGetJpegTablesMode(papszOptions);
    8403        2031 :     poDS->GetDiscardLsbOption(papszOptions);
    8404        2031 :     poDS->m_dfMaxZError = GTiffGetLERCMaxZError(papszOptions);
    8405        2031 :     poDS->m_dfMaxZErrorOverview = GTiffGetLERCMaxZErrorOverview(papszOptions);
    8406             : #if HAVE_JXL
    8407        2031 :     poDS->m_bJXLLossless = GTiffGetJXLLossless(papszOptions);
    8408        2031 :     poDS->m_nJXLEffort = GTiffGetJXLEffort(papszOptions);
    8409        2031 :     poDS->m_fJXLDistance = GTiffGetJXLDistance(papszOptions);
    8410        2031 :     poDS->m_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszOptions);
    8411             : #endif
    8412        2031 :     poDS->InitCreationOrOpenOptions(true, papszOptions);
    8413             : 
    8414        2031 :     if (l_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    8415        2006 :         l_nCompression == COMPRESSION_LERC)
    8416             :     {
    8417          96 :         GTiffSetDeflateSubCodec(l_hTIFF);
    8418             : 
    8419          96 :         if (poDS->m_nZLevel != -1)
    8420             :         {
    8421          12 :             TIFFSetField(l_hTIFF, TIFFTAG_ZIPQUALITY, poDS->m_nZLevel);
    8422             :         }
    8423             :     }
    8424        2031 :     if (l_nCompression == COMPRESSION_JPEG)
    8425             :     {
    8426          75 :         if (poDS->m_nJpegQuality != -1)
    8427             :         {
    8428           9 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGQUALITY, poDS->m_nJpegQuality);
    8429             :         }
    8430          75 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGTABLESMODE, poDS->m_nJpegTablesMode);
    8431             :     }
    8432        2031 :     if (l_nCompression == COMPRESSION_LZMA)
    8433             :     {
    8434           7 :         if (poDS->m_nLZMAPreset != -1)
    8435             :         {
    8436           6 :             TIFFSetField(l_hTIFF, TIFFTAG_LZMAPRESET, poDS->m_nLZMAPreset);
    8437             :         }
    8438             :     }
    8439        2031 :     if (l_nCompression == COMPRESSION_ZSTD ||
    8440        2020 :         l_nCompression == COMPRESSION_LERC)
    8441             :     {
    8442          82 :         if (poDS->m_nZSTDLevel != -1)
    8443             :         {
    8444           8 :             TIFFSetField(l_hTIFF, TIFFTAG_ZSTD_LEVEL, poDS->m_nZSTDLevel);
    8445             :         }
    8446             :     }
    8447        2031 :     if (l_nCompression == COMPRESSION_LERC)
    8448             :     {
    8449          71 :         TIFFSetField(l_hTIFF, TIFFTAG_LERC_MAXZERROR, poDS->m_dfMaxZError);
    8450             :     }
    8451             : #if HAVE_JXL
    8452        2031 :     if (l_nCompression == COMPRESSION_JXL ||
    8453        2031 :         l_nCompression == COMPRESSION_JXL_DNG_1_7)
    8454             :     {
    8455          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_LOSSYNESS,
    8456          91 :                      poDS->m_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
    8457          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_EFFORT, poDS->m_nJXLEffort);
    8458          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_DISTANCE,
    8459          91 :                      static_cast<double>(poDS->m_fJXLDistance));
    8460          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE,
    8461          91 :                      static_cast<double>(poDS->m_fJXLAlphaDistance));
    8462             :     }
    8463             : #endif
    8464        2031 :     if (l_nCompression == COMPRESSION_WEBP)
    8465             :     {
    8466          14 :         if (poDS->m_nWebPLevel != -1)
    8467             :         {
    8468          14 :             TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LEVEL, poDS->m_nWebPLevel);
    8469             :         }
    8470             : 
    8471          14 :         if (poDS->m_bWebPLossless)
    8472             :         {
    8473           5 :             TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LOSSLESS, poDS->m_bWebPLossless);
    8474             :         }
    8475             :     }
    8476             : 
    8477             :     /* -------------------------------------------------------------------- */
    8478             :     /*      Do we want to ensure all blocks get written out on close to     */
    8479             :     /*      avoid sparse files?                                             */
    8480             :     /* -------------------------------------------------------------------- */
    8481        2031 :     if (!CPLFetchBool(papszOptions, "SPARSE_OK", false))
    8482        2003 :         poDS->m_bFillEmptyTilesAtClosing = true;
    8483             : 
    8484        2031 :     poDS->m_bWriteEmptyTiles =
    8485        3879 :         (bCopySrcOverviews && poDS->m_bFillEmptyTilesAtClosing) || bStreaming ||
    8486        1848 :         (poDS->m_nCompression != COMPRESSION_NONE &&
    8487         303 :          poDS->m_bFillEmptyTilesAtClosing);
    8488             :     // Only required for people writing non-compressed striped files in the
    8489             :     // rightorder and wanting all tstrips to be written in the same order
    8490             :     // so that the end result can be memory mapped without knowledge of each
    8491             :     // strip offset
    8492        2031 :     if (CPLTestBool(CSLFetchNameValueDef(
    8493        4062 :             papszOptions, "WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")) ||
    8494        2031 :         CPLTestBool(CSLFetchNameValueDef(
    8495             :             papszOptions, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")))
    8496             :     {
    8497           0 :         poDS->m_bWriteEmptyTiles = true;
    8498             :     }
    8499             : 
    8500             :     // Precreate (internal) mask, so that the IBuildOverviews() below
    8501             :     // has a chance to create also the overviews of the mask.
    8502        2031 :     CPLErr eErr = CE_None;
    8503             : 
    8504        2031 :     if (bCreateMask)
    8505             :     {
    8506          38 :         eErr = poDS->CreateMaskBand(nMaskFlags);
    8507          38 :         if (poDS->m_poMaskDS)
    8508             :         {
    8509          37 :             poDS->m_poMaskDS->m_bFillEmptyTilesAtClosing =
    8510          37 :                 poDS->m_bFillEmptyTilesAtClosing;
    8511          37 :             poDS->m_poMaskDS->m_bWriteEmptyTiles = poDS->m_bWriteEmptyTiles;
    8512             :         }
    8513             :     }
    8514             : 
    8515             :     /* -------------------------------------------------------------------- */
    8516             :     /*      Create and then copy existing overviews if requested            */
    8517             :     /*  We do it such that all the IFDs are at the beginning of the file,   */
    8518             :     /*  and that the imagery data for the smallest overview is written      */
    8519             :     /*  first, that way the file is more usable when embedded in a          */
    8520             :     /*  compressed stream.                                                  */
    8521             :     /* -------------------------------------------------------------------- */
    8522             : 
    8523             :     // For scaled progress due to overview copying.
    8524        2031 :     const int nBandsWidthMask = l_nBands + (bCreateMask ? 1 : 0);
    8525        2031 :     double dfTotalPixels =
    8526        2031 :         static_cast<double>(nXSize) * nYSize * nBandsWidthMask;
    8527        2031 :     double dfCurPixels = 0;
    8528             : 
    8529        2031 :     if (eErr == CE_None && bCopySrcOverviews)
    8530             :     {
    8531           0 :         std::unique_ptr<GDALDataset> poMaskOvrDS;
    8532             :         const char *pszMaskOvrDS =
    8533         184 :             CSLFetchNameValue(papszOptions, "@MASK_OVERVIEW_DATASET");
    8534         184 :         if (pszMaskOvrDS)
    8535             :         {
    8536          10 :             poMaskOvrDS.reset(GDALDataset::Open(pszMaskOvrDS));
    8537          10 :             if (!poMaskOvrDS)
    8538             :             {
    8539           0 :                 l_fpL->CancelCreation();
    8540           0 :                 delete poDS;
    8541           0 :                 return nullptr;
    8542             :             }
    8543          10 :             if (poMaskOvrDS->GetRasterCount() != 1)
    8544             :             {
    8545           0 :                 l_fpL->CancelCreation();
    8546           0 :                 delete poDS;
    8547           0 :                 return nullptr;
    8548             :             }
    8549             :         }
    8550         184 :         if (nSrcOverviews)
    8551             :         {
    8552          68 :             eErr = poDS->CreateOverviewsFromSrcOverviews(poSrcDS, poOvrDS.get(),
    8553             :                                                          nSrcOverviews);
    8554             : 
    8555         194 :             if (eErr == CE_None &&
    8556          68 :                 (poMaskOvrDS != nullptr ||
    8557          58 :                  (poSrcDS->GetRasterBand(1)->GetOverview(0) &&
    8558          30 :                   poSrcDS->GetRasterBand(1)->GetOverview(0)->GetMaskFlags() ==
    8559             :                       GMF_PER_DATASET)))
    8560             :             {
    8561          19 :                 int nOvrBlockXSize = 0;
    8562          19 :                 int nOvrBlockYSize = 0;
    8563          19 :                 GTIFFGetOverviewBlockSize(
    8564             :                     GDALRasterBand::ToHandle(poDS->GetRasterBand(1)),
    8565             :                     &nOvrBlockXSize, &nOvrBlockYSize, nullptr, nullptr);
    8566          19 :                 eErr = poDS->CreateInternalMaskOverviews(nOvrBlockXSize,
    8567             :                                                          nOvrBlockYSize);
    8568             :             }
    8569             :         }
    8570             : 
    8571         184 :         TIFFForceStrileArrayWriting(poDS->m_hTIFF);
    8572             : 
    8573         184 :         if (poDS->m_poMaskDS)
    8574             :         {
    8575          27 :             TIFFForceStrileArrayWriting(poDS->m_poMaskDS->m_hTIFF);
    8576             :         }
    8577             : 
    8578         312 :         for (int i = 0; i < poDS->m_nOverviewCount; i++)
    8579             :         {
    8580         128 :             TIFFForceStrileArrayWriting(poDS->m_papoOverviewDS[i]->m_hTIFF);
    8581             : 
    8582         128 :             if (poDS->m_papoOverviewDS[i]->m_poMaskDS)
    8583             :             {
    8584          36 :                 TIFFForceStrileArrayWriting(
    8585          36 :                     poDS->m_papoOverviewDS[i]->m_poMaskDS->m_hTIFF);
    8586             :             }
    8587             :         }
    8588             : 
    8589         184 :         if (eErr == CE_None && nSrcOverviews)
    8590             :         {
    8591          68 :             if (poDS->m_nOverviewCount != nSrcOverviews)
    8592             :             {
    8593           0 :                 ReportError(
    8594             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    8595             :                     "Did only manage to instantiate %d overview levels, "
    8596             :                     "whereas source contains %d",
    8597           0 :                     poDS->m_nOverviewCount, nSrcOverviews);
    8598           0 :                 eErr = CE_Failure;
    8599             :             }
    8600             : 
    8601         196 :             for (int i = 0; eErr == CE_None && i < nSrcOverviews; ++i)
    8602             :             {
    8603             :                 GDALRasterBand *poOvrBand =
    8604             :                     poOvrDS
    8605         207 :                         ? (i == 0
    8606          79 :                                ? poOvrDS->GetRasterBand(1)
    8607          41 :                                : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    8608         177 :                         : poSrcDS->GetRasterBand(1)->GetOverview(i);
    8609             :                 const double dfOvrPixels =
    8610         128 :                     static_cast<double>(poOvrBand->GetXSize()) *
    8611         128 :                     poOvrBand->GetYSize();
    8612         128 :                 dfTotalPixels += dfOvrPixels * l_nBands;
    8613         242 :                 if (poOvrBand->GetMaskFlags() == GMF_PER_DATASET ||
    8614         114 :                     poMaskOvrDS != nullptr)
    8615             :                 {
    8616          36 :                     dfTotalPixels += dfOvrPixels;
    8617             :                 }
    8618          92 :                 else if (i == 0 && poDS->GetRasterBand(1)->GetMaskFlags() ==
    8619             :                                        GMF_PER_DATASET)
    8620             :                 {
    8621           1 :                     ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
    8622             :                                 "Source dataset has a mask band on full "
    8623             :                                 "resolution, overviews on the regular bands, "
    8624             :                                 "but lacks overviews on the mask band.");
    8625             :                 }
    8626             :             }
    8627             : 
    8628             :             // Now copy the imagery.
    8629             :             // Begin with the smallest overview.
    8630          68 :             for (int iOvrLevel = nSrcOverviews - 1;
    8631         195 :                  eErr == CE_None && iOvrLevel >= 0; --iOvrLevel)
    8632             :             {
    8633         127 :                 auto poDstDS = poDS->m_papoOverviewDS[iOvrLevel];
    8634             : 
    8635             :                 // Create a fake dataset with the source overview level so that
    8636             :                 // GDALDatasetCopyWholeRaster can cope with it.
    8637             :                 GDALDataset *poSrcOvrDS =
    8638             :                     poOvrDS
    8639         168 :                         ? (iOvrLevel == 0 ? poOvrDS.get()
    8640          41 :                                           : GDALCreateOverviewDataset(
    8641             :                                                 poOvrDS.get(), iOvrLevel - 1,
    8642             :                                                 /* bThisLevelOnly = */ true))
    8643          48 :                         : GDALCreateOverviewDataset(
    8644             :                               poSrcDS, iOvrLevel,
    8645         127 :                               /* bThisLevelOnly = */ true);
    8646             :                 GDALRasterBand *poSrcOvrBand =
    8647         206 :                     poOvrDS ? (iOvrLevel == 0
    8648          79 :                                    ? poOvrDS->GetRasterBand(1)
    8649          82 :                                    : poOvrDS->GetRasterBand(1)->GetOverview(
    8650          41 :                                          iOvrLevel - 1))
    8651         175 :                             : poSrcDS->GetRasterBand(1)->GetOverview(iOvrLevel);
    8652             :                 double dfNextCurPixels =
    8653             :                     dfCurPixels +
    8654         127 :                     static_cast<double>(poSrcOvrBand->GetXSize()) *
    8655         127 :                         poSrcOvrBand->GetYSize() * l_nBands;
    8656             : 
    8657         127 :                 poDstDS->m_bBlockOrderRowMajor = true;
    8658         127 :                 poDstDS->m_bLeaderSizeAsUInt4 = true;
    8659         127 :                 poDstDS->m_bTrailerRepeatedLast4BytesRepeated = true;
    8660         127 :                 poDstDS->m_bFillEmptyTilesAtClosing =
    8661         127 :                     poDS->m_bFillEmptyTilesAtClosing;
    8662         127 :                 poDstDS->m_bWriteEmptyTiles = poDS->m_bWriteEmptyTiles;
    8663         127 :                 poDstDS->m_bTileInterleave = poDS->m_bTileInterleave;
    8664         127 :                 GDALRasterBand *poSrcMaskBand = nullptr;
    8665         127 :                 if (poDstDS->m_poMaskDS)
    8666             :                 {
    8667          36 :                     poDstDS->m_poMaskDS->m_bBlockOrderRowMajor = true;
    8668          36 :                     poDstDS->m_poMaskDS->m_bLeaderSizeAsUInt4 = true;
    8669          36 :                     poDstDS->m_poMaskDS->m_bTrailerRepeatedLast4BytesRepeated =
    8670             :                         true;
    8671          36 :                     poDstDS->m_poMaskDS->m_bFillEmptyTilesAtClosing =
    8672          36 :                         poDS->m_bFillEmptyTilesAtClosing;
    8673          36 :                     poDstDS->m_poMaskDS->m_bWriteEmptyTiles =
    8674          36 :                         poDS->m_bWriteEmptyTiles;
    8675             : 
    8676          36 :                     poSrcMaskBand =
    8677             :                         poMaskOvrDS
    8678          58 :                             ? (iOvrLevel == 0
    8679          22 :                                    ? poMaskOvrDS->GetRasterBand(1)
    8680          24 :                                    : poMaskOvrDS->GetRasterBand(1)->GetOverview(
    8681          12 :                                          iOvrLevel - 1))
    8682          50 :                             : poSrcOvrBand->GetMaskBand();
    8683             :                 }
    8684             : 
    8685         127 :                 if (poDstDS->m_poMaskDS)
    8686             :                 {
    8687          36 :                     dfNextCurPixels +=
    8688          36 :                         static_cast<double>(poSrcOvrBand->GetXSize()) *
    8689          36 :                         poSrcOvrBand->GetYSize();
    8690             :                 }
    8691             :                 void *pScaledData =
    8692         127 :                     GDALCreateScaledProgress(dfCurPixels / dfTotalPixels,
    8693             :                                              dfNextCurPixels / dfTotalPixels,
    8694             :                                              pfnProgress, pProgressData);
    8695             : 
    8696         127 :                 eErr = CopyImageryAndMask(poDstDS, poSrcOvrDS, poSrcMaskBand,
    8697             :                                           GDALScaledProgress, pScaledData);
    8698             : 
    8699         127 :                 dfCurPixels = dfNextCurPixels;
    8700         127 :                 GDALDestroyScaledProgress(pScaledData);
    8701             : 
    8702         127 :                 if (poSrcOvrDS != poOvrDS.get())
    8703          89 :                     delete poSrcOvrDS;
    8704         127 :                 poSrcOvrDS = nullptr;
    8705             :             }
    8706             :         }
    8707             :     }
    8708             : 
    8709             :     /* -------------------------------------------------------------------- */
    8710             :     /*      Copy actual imagery.                                            */
    8711             :     /* -------------------------------------------------------------------- */
    8712        2031 :     double dfNextCurPixels =
    8713        2031 :         dfCurPixels + static_cast<double>(nXSize) * nYSize * l_nBands;
    8714        2031 :     void *pScaledData = GDALCreateScaledProgress(
    8715             :         dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
    8716             :         pfnProgress, pProgressData);
    8717             : 
    8718             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8719        2031 :     bool bTryCopy = true;
    8720             : #endif
    8721             : 
    8722             : #ifdef HAVE_LIBJPEG
    8723        2031 :     if (bCopyFromJPEG)
    8724             :     {
    8725          12 :         eErr = GTIFF_CopyFromJPEG(poDS, poSrcDS, pfnProgress, pProgressData,
    8726             :                                   bTryCopy);
    8727             : 
    8728             :         // In case of failure in the decompression step, try normal copy.
    8729          12 :         if (bTryCopy)
    8730           0 :             eErr = CE_None;
    8731             :     }
    8732             : #endif
    8733             : 
    8734             : #ifdef JPEG_DIRECT_COPY
    8735             :     if (bDirectCopyFromJPEG)
    8736             :     {
    8737             :         eErr = GTIFF_DirectCopyFromJPEG(poDS, poSrcDS, pfnProgress,
    8738             :                                         pProgressData, bTryCopy);
    8739             : 
    8740             :         // In case of failure in the reading step, try normal copy.
    8741             :         if (bTryCopy)
    8742             :             eErr = CE_None;
    8743             :     }
    8744             : #endif
    8745             : 
    8746        2031 :     bool bWriteMask = true;
    8747        2031 :     if (
    8748             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8749        2019 :         bTryCopy &&
    8750             : #endif
    8751        2019 :         (poDS->m_bTreatAsSplit || poDS->m_bTreatAsSplitBitmap))
    8752             :     {
    8753             :         // For split bands, we use TIFFWriteScanline() interface.
    8754           9 :         CPLAssert(poDS->m_nBitsPerSample == 8 || poDS->m_nBitsPerSample == 1);
    8755             : 
    8756           9 :         if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG && poDS->nBands > 1)
    8757             :         {
    8758             :             GByte *pabyScanline = static_cast<GByte *>(
    8759           3 :                 VSI_MALLOC_VERBOSE(TIFFScanlineSize(l_hTIFF)));
    8760           3 :             if (pabyScanline == nullptr)
    8761           0 :                 eErr = CE_Failure;
    8762        9052 :             for (int j = 0; j < nYSize && eErr == CE_None; ++j)
    8763             :             {
    8764       18098 :                 eErr = poSrcDS->RasterIO(GF_Read, 0, j, nXSize, 1, pabyScanline,
    8765             :                                          nXSize, 1, GDT_Byte, l_nBands, nullptr,
    8766        9049 :                                          poDS->nBands, 0, 1, nullptr);
    8767       18098 :                 if (eErr == CE_None &&
    8768        9049 :                     TIFFWriteScanline(l_hTIFF, pabyScanline, j, 0) == -1)
    8769             :                 {
    8770           0 :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    8771             :                                 "TIFFWriteScanline() failed.");
    8772           0 :                     eErr = CE_Failure;
    8773             :                 }
    8774        9049 :                 if (!GDALScaledProgress((j + 1) * 1.0 / nYSize, nullptr,
    8775             :                                         pScaledData))
    8776           0 :                     eErr = CE_Failure;
    8777             :             }
    8778           3 :             CPLFree(pabyScanline);
    8779             :         }
    8780             :         else
    8781             :         {
    8782             :             GByte *pabyScanline =
    8783           6 :                 static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
    8784           6 :             if (pabyScanline == nullptr)
    8785           0 :                 eErr = CE_Failure;
    8786             :             else
    8787           6 :                 eErr = CE_None;
    8788          14 :             for (int iBand = 1; iBand <= l_nBands && eErr == CE_None; ++iBand)
    8789             :             {
    8790       48211 :                 for (int j = 0; j < nYSize && eErr == CE_None; ++j)
    8791             :                 {
    8792       48203 :                     eErr = poSrcDS->GetRasterBand(iBand)->RasterIO(
    8793             :                         GF_Read, 0, j, nXSize, 1, pabyScanline, nXSize, 1,
    8794             :                         GDT_Byte, 0, 0, nullptr);
    8795       48203 :                     if (poDS->m_bTreatAsSplitBitmap)
    8796             :                     {
    8797     7225210 :                         for (int i = 0; i < nXSize; ++i)
    8798             :                         {
    8799     7216010 :                             const GByte byVal = pabyScanline[i];
    8800     7216010 :                             if ((i & 0x7) == 0)
    8801      902001 :                                 pabyScanline[i >> 3] = 0;
    8802     7216010 :                             if (byVal)
    8803     7097220 :                                 pabyScanline[i >> 3] |= 0x80 >> (i & 0x7);
    8804             :                         }
    8805             :                     }
    8806       96406 :                     if (eErr == CE_None &&
    8807       48203 :                         TIFFWriteScanline(l_hTIFF, pabyScanline, j,
    8808       48203 :                                           static_cast<uint16_t>(iBand - 1)) ==
    8809             :                             -1)
    8810             :                     {
    8811           0 :                         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    8812             :                                     "TIFFWriteScanline() failed.");
    8813           0 :                         eErr = CE_Failure;
    8814             :                     }
    8815       48203 :                     if (!GDALScaledProgress((j + 1 + (iBand - 1) * nYSize) *
    8816       48203 :                                                 1.0 / (l_nBands * nYSize),
    8817             :                                             nullptr, pScaledData))
    8818           0 :                         eErr = CE_Failure;
    8819             :                 }
    8820             :             }
    8821           6 :             CPLFree(pabyScanline);
    8822             :         }
    8823             : 
    8824             :         // Necessary to be able to read the file without re-opening.
    8825           9 :         TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(l_hTIFF);
    8826             : 
    8827           9 :         TIFFFlushData(l_hTIFF);
    8828             : 
    8829           9 :         toff_t nNewDirOffset = pfnSizeProc(TIFFClientdata(l_hTIFF));
    8830           9 :         if ((nNewDirOffset % 2) == 1)
    8831           5 :             ++nNewDirOffset;
    8832             : 
    8833           9 :         TIFFFlush(l_hTIFF);
    8834             : 
    8835           9 :         if (poDS->m_nDirOffset != TIFFCurrentDirOffset(l_hTIFF))
    8836             :         {
    8837           0 :             poDS->m_nDirOffset = nNewDirOffset;
    8838           0 :             CPLDebug("GTiff", "directory moved during flush.");
    8839           9 :         }
    8840             :     }
    8841        2022 :     else if (
    8842             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8843        2010 :         bTryCopy &&
    8844             : #endif
    8845             :         eErr == CE_None)
    8846             :     {
    8847        2009 :         const char *papszCopyWholeRasterOptions[3] = {nullptr, nullptr,
    8848             :                                                       nullptr};
    8849        2009 :         int iNextOption = 0;
    8850        2009 :         papszCopyWholeRasterOptions[iNextOption++] = "SKIP_HOLES=YES";
    8851        2009 :         if (l_nCompression != COMPRESSION_NONE)
    8852             :         {
    8853         453 :             papszCopyWholeRasterOptions[iNextOption++] = "COMPRESSED=YES";
    8854             :         }
    8855             : 
    8856             :         // For streaming with separate, we really want that bands are written
    8857             :         // after each other, even if the source is pixel interleaved.
    8858        1556 :         else if (bStreaming && poDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE)
    8859             :         {
    8860           1 :             papszCopyWholeRasterOptions[iNextOption++] = "INTERLEAVE=BAND";
    8861             :         }
    8862             : 
    8863        2009 :         if (bCopySrcOverviews || bTileInterleaving)
    8864             :         {
    8865         184 :             poDS->m_bBlockOrderRowMajor = true;
    8866         184 :             poDS->m_bLeaderSizeAsUInt4 = bCopySrcOverviews;
    8867         184 :             poDS->m_bTrailerRepeatedLast4BytesRepeated = bCopySrcOverviews;
    8868         184 :             if (poDS->m_poMaskDS)
    8869             :             {
    8870          27 :                 poDS->m_poMaskDS->m_bBlockOrderRowMajor = true;
    8871          27 :                 poDS->m_poMaskDS->m_bLeaderSizeAsUInt4 = bCopySrcOverviews;
    8872          27 :                 poDS->m_poMaskDS->m_bTrailerRepeatedLast4BytesRepeated =
    8873             :                     bCopySrcOverviews;
    8874          27 :                 GDALDestroyScaledProgress(pScaledData);
    8875             :                 pScaledData =
    8876          27 :                     GDALCreateScaledProgress(dfCurPixels / dfTotalPixels, 1.0,
    8877             :                                              pfnProgress, pProgressData);
    8878             :             }
    8879             : 
    8880         184 :             eErr = CopyImageryAndMask(poDS, poSrcDS,
    8881         184 :                                       poSrcDS->GetRasterBand(1)->GetMaskBand(),
    8882             :                                       GDALScaledProgress, pScaledData);
    8883         184 :             if (poDS->m_poMaskDS)
    8884             :             {
    8885          27 :                 bWriteMask = false;
    8886             :             }
    8887             :         }
    8888             :         else
    8889             :         {
    8890        1825 :             eErr = GDALDatasetCopyWholeRaster(
    8891             :                 /* (GDALDatasetH) */ poSrcDS,
    8892             :                 /* (GDALDatasetH) */ poDS, papszCopyWholeRasterOptions,
    8893             :                 GDALScaledProgress, pScaledData);
    8894             :         }
    8895             :     }
    8896             : 
    8897        2031 :     GDALDestroyScaledProgress(pScaledData);
    8898             : 
    8899        2031 :     if (eErr == CE_None && !bStreaming && bWriteMask)
    8900             :     {
    8901        1982 :         pScaledData = GDALCreateScaledProgress(dfNextCurPixels / dfTotalPixels,
    8902             :                                                1.0, pfnProgress, pProgressData);
    8903        1982 :         if (poDS->m_poMaskDS)
    8904             :         {
    8905          10 :             const char *l_papszOptions[2] = {"COMPRESSED=YES", nullptr};
    8906          10 :             eErr = GDALRasterBandCopyWholeRaster(
    8907          10 :                 poSrcDS->GetRasterBand(1)->GetMaskBand(),
    8908          10 :                 poDS->GetRasterBand(1)->GetMaskBand(),
    8909             :                 const_cast<char **>(l_papszOptions), GDALScaledProgress,
    8910             :                 pScaledData);
    8911             :         }
    8912             :         else
    8913             :         {
    8914             :             eErr =
    8915        1972 :                 GDALDriver::DefaultCopyMasks(poSrcDS, poDS, bStrict, nullptr,
    8916             :                                              GDALScaledProgress, pScaledData);
    8917             :         }
    8918        1982 :         GDALDestroyScaledProgress(pScaledData);
    8919             :     }
    8920             : 
    8921        2031 :     poDS->m_bWriteCOGLayout = false;
    8922             : 
    8923        4044 :     if (eErr == CE_None &&
    8924        2013 :         CPLTestBool(CSLFetchNameValueDef(poDS->m_papszCreationOptions,
    8925             :                                          "@FLUSHCACHE", "NO")))
    8926             :     {
    8927         138 :         if (poDS->FlushCache(false) != CE_None)
    8928             :         {
    8929           0 :             eErr = CE_Failure;
    8930             :         }
    8931             :     }
    8932             : 
    8933        2031 :     if (eErr == CE_Failure)
    8934             :     {
    8935          18 :         if (CPLTestBool(CPLGetConfigOption("GTIFF_DELETE_ON_ERROR", "YES")))
    8936             :         {
    8937          17 :             l_fpL->CancelCreation();
    8938          17 :             delete poDS;
    8939          17 :             poDS = nullptr;
    8940             : 
    8941          17 :             if (!bStreaming)
    8942             :             {
    8943             :                 // Should really delete more carefully.
    8944          17 :                 VSIUnlink(pszFilename);
    8945             :             }
    8946             :         }
    8947             :         else
    8948             :         {
    8949           1 :             delete poDS;
    8950           1 :             poDS = nullptr;
    8951             :         }
    8952             :     }
    8953             : 
    8954        2031 :     return poDS;
    8955             : }
    8956             : 
    8957             : /************************************************************************/
    8958             : /*                           SetSpatialRef()                            */
    8959             : /************************************************************************/
    8960             : 
    8961        1405 : CPLErr GTiffDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    8962             : 
    8963             : {
    8964        1405 :     if (m_bStreamingOut && m_bCrystalized)
    8965             :     {
    8966           1 :         ReportError(CE_Failure, CPLE_NotSupported,
    8967             :                     "Cannot modify projection at that point in "
    8968             :                     "a streamed output file");
    8969           1 :         return CE_Failure;
    8970             :     }
    8971             : 
    8972        1404 :     LoadGeoreferencingAndPamIfNeeded();
    8973        1404 :     LookForProjection();
    8974             : 
    8975        1404 :     CPLErr eErr = CE_None;
    8976        1404 :     if (eAccess == GA_Update)
    8977             :     {
    8978        1406 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    8979           7 :             (GetPamFlags() & GPF_DISABLED) == 0)
    8980             :         {
    8981           7 :             eErr = GDALPamDataset::SetSpatialRef(poSRS);
    8982             :         }
    8983             :         else
    8984             :         {
    8985        1392 :             if (GDALPamDataset::GetSpatialRef() != nullptr)
    8986             :             {
    8987             :                 // Cancel any existing SRS from PAM file.
    8988           1 :                 GDALPamDataset::SetSpatialRef(nullptr);
    8989             :             }
    8990        1392 :             m_bGeoTIFFInfoChanged = true;
    8991             :         }
    8992             :     }
    8993             :     else
    8994             :     {
    8995           5 :         CPLDebug("GTIFF", "SetSpatialRef() goes to PAM instead of TIFF tags");
    8996           5 :         eErr = GDALPamDataset::SetSpatialRef(poSRS);
    8997             :     }
    8998             : 
    8999        1404 :     if (eErr == CE_None)
    9000             :     {
    9001        1404 :         if (poSRS == nullptr || poSRS->IsEmpty())
    9002             :         {
    9003          14 :             if (!m_oSRS.IsEmpty())
    9004             :             {
    9005           4 :                 m_bForceUnsetProjection = true;
    9006             :             }
    9007          14 :             m_oSRS.Clear();
    9008             :         }
    9009             :         else
    9010             :         {
    9011        1390 :             m_oSRS = *poSRS;
    9012        1390 :             m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    9013             :         }
    9014             :     }
    9015             : 
    9016        1404 :     return eErr;
    9017             : }
    9018             : 
    9019             : /************************************************************************/
    9020             : /*                          SetGeoTransform()                           */
    9021             : /************************************************************************/
    9022             : 
    9023        1695 : CPLErr GTiffDataset::SetGeoTransform(const GDALGeoTransform &gt)
    9024             : 
    9025             : {
    9026        1695 :     if (m_bStreamingOut && m_bCrystalized)
    9027             :     {
    9028           1 :         ReportError(CE_Failure, CPLE_NotSupported,
    9029             :                     "Cannot modify geotransform at that point in a "
    9030             :                     "streamed output file");
    9031           1 :         return CE_Failure;
    9032             :     }
    9033             : 
    9034        1694 :     LoadGeoreferencingAndPamIfNeeded();
    9035             : 
    9036        1694 :     CPLErr eErr = CE_None;
    9037        1694 :     if (eAccess == GA_Update)
    9038             :     {
    9039        1688 :         if (!m_aoGCPs.empty())
    9040             :         {
    9041           1 :             ReportError(CE_Warning, CPLE_AppDefined,
    9042             :                         "GCPs previously set are going to be cleared "
    9043             :                         "due to the setting of a geotransform.");
    9044           1 :             m_bForceUnsetGTOrGCPs = true;
    9045           1 :             m_aoGCPs.clear();
    9046             :         }
    9047        1888 :         else if (gt[0] == 0.0 && gt[1] == 0.0 && gt[2] == 0.0 && gt[3] == 0.0 &&
    9048        1888 :                  gt[4] == 0.0 && gt[5] == 0.0)
    9049             :         {
    9050           2 :             if (m_bGeoTransformValid)
    9051             :             {
    9052           2 :                 m_bForceUnsetGTOrGCPs = true;
    9053           2 :                 m_bGeoTIFFInfoChanged = true;
    9054             :             }
    9055           2 :             m_bGeoTransformValid = false;
    9056           2 :             m_gt = gt;
    9057           2 :             return CE_None;
    9058             :         }
    9059             : 
    9060        3381 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    9061           9 :             !CPLFetchBool(m_papszCreationOptions, "TFW", false) &&
    9062        1700 :             !CPLFetchBool(m_papszCreationOptions, "WORLDFILE", false) &&
    9063           5 :             (GetPamFlags() & GPF_DISABLED) == 0)
    9064             :         {
    9065           5 :             eErr = GDALPamDataset::SetGeoTransform(gt);
    9066             :         }
    9067             :         else
    9068             :         {
    9069             :             // Cancel any existing geotransform from PAM file.
    9070        1681 :             GDALPamDataset::DeleteGeoTransform();
    9071        1681 :             m_bGeoTIFFInfoChanged = true;
    9072             :         }
    9073             :     }
    9074             :     else
    9075             :     {
    9076           6 :         CPLDebug("GTIFF", "SetGeoTransform() goes to PAM instead of TIFF tags");
    9077           6 :         eErr = GDALPamDataset::SetGeoTransform(gt);
    9078             :     }
    9079             : 
    9080        1692 :     if (eErr == CE_None)
    9081             :     {
    9082        1692 :         m_gt = gt;
    9083        1692 :         m_bGeoTransformValid = true;
    9084             :     }
    9085             : 
    9086        1692 :     return eErr;
    9087             : }
    9088             : 
    9089             : /************************************************************************/
    9090             : /*                               SetGCPs()                              */
    9091             : /************************************************************************/
    9092             : 
    9093          23 : CPLErr GTiffDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
    9094             :                              const OGRSpatialReference *poGCPSRS)
    9095             : {
    9096          23 :     CPLErr eErr = CE_None;
    9097          23 :     LoadGeoreferencingAndPamIfNeeded();
    9098          23 :     LookForProjection();
    9099             : 
    9100          23 :     if (eAccess == GA_Update)
    9101             :     {
    9102          21 :         if (!m_aoGCPs.empty() && nGCPCountIn == 0)
    9103             :         {
    9104           3 :             m_bForceUnsetGTOrGCPs = true;
    9105             :         }
    9106          18 :         else if (nGCPCountIn > 0 && m_bGeoTransformValid)
    9107             :         {
    9108           5 :             ReportError(CE_Warning, CPLE_AppDefined,
    9109             :                         "A geotransform previously set is going to be cleared "
    9110             :                         "due to the setting of GCPs.");
    9111           5 :             m_gt = GDALGeoTransform();
    9112           5 :             m_bGeoTransformValid = false;
    9113           5 :             m_bForceUnsetGTOrGCPs = true;
    9114             :         }
    9115          21 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    9116           0 :             (GetPamFlags() & GPF_DISABLED) == 0)
    9117             :         {
    9118           0 :             eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn, poGCPSRS);
    9119             :         }
    9120             :         else
    9121             :         {
    9122          21 :             if (nGCPCountIn > knMAX_GCP_COUNT)
    9123             :             {
    9124           2 :                 if (GDALPamDataset::GetGCPCount() == 0 && !m_aoGCPs.empty())
    9125             :                 {
    9126           1 :                     m_bForceUnsetGTOrGCPs = true;
    9127             :                 }
    9128           2 :                 ReportError(CE_Warning, CPLE_AppDefined,
    9129             :                             "Trying to write %d GCPs, whereas the maximum "
    9130             :                             "supported in GeoTIFF tag is %d. "
    9131             :                             "Falling back to writing them to PAM",
    9132             :                             nGCPCountIn, knMAX_GCP_COUNT);
    9133           2 :                 eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn,
    9134             :                                                poGCPSRS);
    9135             :             }
    9136          19 :             else if (GDALPamDataset::GetGCPCount() > 0)
    9137             :             {
    9138             :                 // Cancel any existing GCPs from PAM file.
    9139           1 :                 GDALPamDataset::SetGCPs(
    9140             :                     0, nullptr,
    9141             :                     static_cast<const OGRSpatialReference *>(nullptr));
    9142             :             }
    9143          21 :             m_bGeoTIFFInfoChanged = true;
    9144             :         }
    9145             :     }
    9146             :     else
    9147             :     {
    9148           2 :         CPLDebug("GTIFF", "SetGCPs() goes to PAM instead of TIFF tags");
    9149           2 :         eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn, poGCPSRS);
    9150             :     }
    9151             : 
    9152          23 :     if (eErr == CE_None)
    9153             :     {
    9154          23 :         if (poGCPSRS == nullptr || poGCPSRS->IsEmpty())
    9155             :         {
    9156          12 :             if (!m_oSRS.IsEmpty())
    9157             :             {
    9158           5 :                 m_bForceUnsetProjection = true;
    9159             :             }
    9160          12 :             m_oSRS.Clear();
    9161             :         }
    9162             :         else
    9163             :         {
    9164          11 :             m_oSRS = *poGCPSRS;
    9165          11 :             m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    9166             :         }
    9167             : 
    9168          23 :         m_aoGCPs = gdal::GCP::fromC(pasGCPListIn, nGCPCountIn);
    9169             :     }
    9170             : 
    9171          23 :     return eErr;
    9172             : }
    9173             : 
    9174             : /************************************************************************/
    9175             : /*                            SetMetadata()                             */
    9176             : /************************************************************************/
    9177        2615 : CPLErr GTiffDataset::SetMetadata(char **papszMD, const char *pszDomain)
    9178             : 
    9179             : {
    9180        2615 :     LoadGeoreferencingAndPamIfNeeded();
    9181             : 
    9182        2615 :     if (m_bStreamingOut && m_bCrystalized)
    9183             :     {
    9184           1 :         ReportError(
    9185             :             CE_Failure, CPLE_NotSupported,
    9186             :             "Cannot modify metadata at that point in a streamed output file");
    9187           1 :         return CE_Failure;
    9188             :     }
    9189             : 
    9190        2614 :     CPLErr eErr = CE_None;
    9191        2614 :     if (eAccess == GA_Update)
    9192             :     {
    9193        2612 :         if (pszDomain != nullptr && EQUAL(pszDomain, MD_DOMAIN_RPC))
    9194             :         {
    9195             :             // So that a subsequent GetMetadata() wouldn't override our new
    9196             :             // values
    9197          22 :             LoadMetadata();
    9198          22 :             m_bForceUnsetRPC = (CSLCount(papszMD) == 0);
    9199             :         }
    9200             : 
    9201        2612 :         if ((papszMD != nullptr) && (pszDomain != nullptr) &&
    9202        1789 :             EQUAL(pszDomain, "COLOR_PROFILE"))
    9203             :         {
    9204           0 :             m_bColorProfileMetadataChanged = true;
    9205             :         }
    9206        2612 :         else if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
    9207             :         {
    9208        2612 :             m_bMetadataChanged = true;
    9209             :             // Cancel any existing metadata from PAM file.
    9210        2612 :             if (GDALPamDataset::GetMetadata(pszDomain) != nullptr)
    9211           1 :                 GDALPamDataset::SetMetadata(nullptr, pszDomain);
    9212             :         }
    9213             : 
    9214        5189 :         if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    9215        2577 :             CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT) != nullptr)
    9216             :         {
    9217        1951 :             const char *pszPrevValue = GetMetadataItem(GDALMD_AREA_OR_POINT);
    9218             :             const char *pszNewValue =
    9219        1951 :                 CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT);
    9220        1951 :             if (pszPrevValue == nullptr || pszNewValue == nullptr ||
    9221        1532 :                 !EQUAL(pszPrevValue, pszNewValue))
    9222             :             {
    9223         423 :                 LookForProjection();
    9224         423 :                 m_bGeoTIFFInfoChanged = true;
    9225             :             }
    9226             :         }
    9227             : 
    9228        2612 :         if (pszDomain != nullptr && EQUAL(pszDomain, "xml:XMP"))
    9229             :         {
    9230           2 :             if (papszMD != nullptr && *papszMD != nullptr)
    9231             :             {
    9232           1 :                 int nTagSize = static_cast<int>(strlen(*papszMD));
    9233           1 :                 TIFFSetField(m_hTIFF, TIFFTAG_XMLPACKET, nTagSize, *papszMD);
    9234             :             }
    9235             :             else
    9236             :             {
    9237           1 :                 TIFFUnsetField(m_hTIFF, TIFFTAG_XMLPACKET);
    9238             :             }
    9239             :         }
    9240             :     }
    9241             :     else
    9242             :     {
    9243           2 :         CPLDebug(
    9244             :             "GTIFF",
    9245             :             "GTiffDataset::SetMetadata() goes to PAM instead of TIFF tags");
    9246           2 :         eErr = GDALPamDataset::SetMetadata(papszMD, pszDomain);
    9247             :     }
    9248             : 
    9249        2614 :     if (eErr == CE_None)
    9250             :     {
    9251        2614 :         eErr = m_oGTiffMDMD.SetMetadata(papszMD, pszDomain);
    9252             :     }
    9253        2614 :     return eErr;
    9254             : }
    9255             : 
    9256             : /************************************************************************/
    9257             : /*                          SetMetadataItem()                           */
    9258             : /************************************************************************/
    9259             : 
    9260        5592 : CPLErr GTiffDataset::SetMetadataItem(const char *pszName, const char *pszValue,
    9261             :                                      const char *pszDomain)
    9262             : 
    9263             : {
    9264        5592 :     LoadGeoreferencingAndPamIfNeeded();
    9265             : 
    9266        5592 :     if (m_bStreamingOut && m_bCrystalized)
    9267             :     {
    9268           1 :         ReportError(
    9269             :             CE_Failure, CPLE_NotSupported,
    9270             :             "Cannot modify metadata at that point in a streamed output file");
    9271           1 :         return CE_Failure;
    9272             :     }
    9273             : 
    9274        5591 :     CPLErr eErr = CE_None;
    9275        5591 :     if (eAccess == GA_Update)
    9276             :     {
    9277        5584 :         if ((pszDomain != nullptr) && EQUAL(pszDomain, "COLOR_PROFILE"))
    9278             :         {
    9279           8 :             m_bColorProfileMetadataChanged = true;
    9280             :         }
    9281        5576 :         else if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
    9282             :         {
    9283        5576 :             m_bMetadataChanged = true;
    9284             :             // Cancel any existing metadata from PAM file.
    9285        5576 :             if (GDALPamDataset::GetMetadataItem(pszName, pszDomain) != nullptr)
    9286           1 :                 GDALPamDataset::SetMetadataItem(pszName, nullptr, pszDomain);
    9287             :         }
    9288             : 
    9289        5584 :         if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    9290          71 :             pszName != nullptr && EQUAL(pszName, GDALMD_AREA_OR_POINT))
    9291             :         {
    9292           7 :             LookForProjection();
    9293           7 :             m_bGeoTIFFInfoChanged = true;
    9294             :         }
    9295             :     }
    9296             :     else
    9297             :     {
    9298           7 :         CPLDebug(
    9299             :             "GTIFF",
    9300             :             "GTiffDataset::SetMetadataItem() goes to PAM instead of TIFF tags");
    9301           7 :         eErr = GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    9302             :     }
    9303             : 
    9304        5591 :     if (eErr == CE_None)
    9305             :     {
    9306        5591 :         eErr = m_oGTiffMDMD.SetMetadataItem(pszName, pszValue, pszDomain);
    9307             :     }
    9308             : 
    9309        5591 :     return eErr;
    9310             : }
    9311             : 
    9312             : /************************************************************************/
    9313             : /*                         CreateMaskBand()                             */
    9314             : /************************************************************************/
    9315             : 
    9316          96 : CPLErr GTiffDataset::CreateMaskBand(int nFlagsIn)
    9317             : {
    9318          96 :     ScanDirectories();
    9319             : 
    9320          96 :     if (m_poMaskDS != nullptr)
    9321             :     {
    9322           1 :         ReportError(CE_Failure, CPLE_AppDefined,
    9323             :                     "This TIFF dataset has already an internal mask band");
    9324           1 :         return CE_Failure;
    9325             :     }
    9326          95 :     else if (MustCreateInternalMask())
    9327             :     {
    9328          82 :         if (nFlagsIn != GMF_PER_DATASET)
    9329             :         {
    9330           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    9331             :                         "The only flag value supported for internal mask is "
    9332             :                         "GMF_PER_DATASET");
    9333           1 :             return CE_Failure;
    9334             :         }
    9335             : 
    9336          81 :         int l_nCompression = COMPRESSION_PACKBITS;
    9337          81 :         if (strstr(GDALGetMetadataItem(GDALGetDriverByName("GTiff"),
    9338             :                                        GDAL_DMD_CREATIONOPTIONLIST, nullptr),
    9339          81 :                    "<Value>DEFLATE</Value>") != nullptr)
    9340          81 :             l_nCompression = COMPRESSION_ADOBE_DEFLATE;
    9341             : 
    9342             :         /* --------------------------------------------------------------------
    9343             :          */
    9344             :         /*      If we don't have read access, then create the mask externally.
    9345             :          */
    9346             :         /* --------------------------------------------------------------------
    9347             :          */
    9348          81 :         if (GetAccess() != GA_Update)
    9349             :         {
    9350           1 :             ReportError(CE_Warning, CPLE_AppDefined,
    9351             :                         "File open for read-only accessing, "
    9352             :                         "creating mask externally.");
    9353             : 
    9354           1 :             return GDALPamDataset::CreateMaskBand(nFlagsIn);
    9355             :         }
    9356             : 
    9357          80 :         if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    9358           0 :             !m_bWriteKnownIncompatibleEdition)
    9359             :         {
    9360           0 :             ReportError(CE_Warning, CPLE_AppDefined,
    9361             :                         "Adding a mask invalidates the "
    9362             :                         "LAYOUT=IFDS_BEFORE_DATA property");
    9363           0 :             m_bKnownIncompatibleEdition = true;
    9364           0 :             m_bWriteKnownIncompatibleEdition = true;
    9365             :         }
    9366             : 
    9367          80 :         bool bIsOverview = false;
    9368          80 :         uint32_t nSubType = 0;
    9369          80 :         if (TIFFGetField(m_hTIFF, TIFFTAG_SUBFILETYPE, &nSubType))
    9370             :         {
    9371           8 :             bIsOverview = (nSubType & FILETYPE_REDUCEDIMAGE) != 0;
    9372             : 
    9373           8 :             if ((nSubType & FILETYPE_MASK) != 0)
    9374             :             {
    9375           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
    9376             :                             "Cannot create a mask on a TIFF mask IFD !");
    9377           0 :                 return CE_Failure;
    9378             :             }
    9379             :         }
    9380             : 
    9381          80 :         const int bIsTiled = TIFFIsTiled(m_hTIFF);
    9382             : 
    9383          80 :         FlushDirectory();
    9384             : 
    9385          80 :         const toff_t nOffset = GTIFFWriteDirectory(
    9386             :             m_hTIFF,
    9387             :             bIsOverview ? FILETYPE_REDUCEDIMAGE | FILETYPE_MASK : FILETYPE_MASK,
    9388             :             nRasterXSize, nRasterYSize, 1, PLANARCONFIG_CONTIG, 1,
    9389             :             m_nBlockXSize, m_nBlockYSize, bIsTiled, l_nCompression,
    9390             :             PHOTOMETRIC_MASK, PREDICTOR_NONE, SAMPLEFORMAT_UINT, nullptr,
    9391             :             nullptr, nullptr, 0, nullptr, "", nullptr, nullptr, nullptr,
    9392          80 :             nullptr, m_bWriteCOGLayout);
    9393             : 
    9394          80 :         ReloadDirectory();
    9395             : 
    9396          80 :         if (nOffset == 0)
    9397           0 :             return CE_Failure;
    9398             : 
    9399          80 :         m_poMaskDS = new GTiffDataset();
    9400          80 :         m_poMaskDS->m_poBaseDS = this;
    9401          80 :         m_poMaskDS->m_poImageryDS = this;
    9402          80 :         m_poMaskDS->ShareLockWithParentDataset(this);
    9403          80 :         m_poMaskDS->m_bPromoteTo8Bits = CPLTestBool(
    9404             :             CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
    9405          80 :         if (m_poMaskDS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nOffset,
    9406          80 :                                    GA_Update) != CE_None)
    9407             :         {
    9408           0 :             delete m_poMaskDS;
    9409           0 :             m_poMaskDS = nullptr;
    9410           0 :             return CE_Failure;
    9411             :         }
    9412             : 
    9413          80 :         return CE_None;
    9414             :     }
    9415             : 
    9416          13 :     return GDALPamDataset::CreateMaskBand(nFlagsIn);
    9417             : }
    9418             : 
    9419             : /************************************************************************/
    9420             : /*                        MustCreateInternalMask()                      */
    9421             : /************************************************************************/
    9422             : 
    9423         133 : bool GTiffDataset::MustCreateInternalMask()
    9424             : {
    9425         133 :     return CPLTestBool(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", "YES"));
    9426             : }
    9427             : 
    9428             : /************************************************************************/
    9429             : /*                         CreateMaskBand()                             */
    9430             : /************************************************************************/
    9431             : 
    9432          28 : CPLErr GTiffRasterBand::CreateMaskBand(int nFlagsIn)
    9433             : {
    9434          28 :     m_poGDS->ScanDirectories();
    9435             : 
    9436          28 :     if (m_poGDS->m_poMaskDS != nullptr)
    9437             :     {
    9438           5 :         ReportError(CE_Failure, CPLE_AppDefined,
    9439             :                     "This TIFF dataset has already an internal mask band");
    9440           5 :         return CE_Failure;
    9441             :     }
    9442             : 
    9443             :     const char *pszGDAL_TIFF_INTERNAL_MASK =
    9444          23 :         CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", nullptr);
    9445          26 :     if ((pszGDAL_TIFF_INTERNAL_MASK &&
    9446          23 :          CPLTestBool(pszGDAL_TIFF_INTERNAL_MASK)) ||
    9447             :         nFlagsIn == GMF_PER_DATASET)
    9448             :     {
    9449          15 :         return m_poGDS->CreateMaskBand(nFlagsIn);
    9450             :     }
    9451             : 
    9452           8 :     return GDALPamRasterBand::CreateMaskBand(nFlagsIn);
    9453             : }
    9454             : 
    9455             : /************************************************************************/
    9456             : /*                          ClampCTEntry()                              */
    9457             : /************************************************************************/
    9458             : 
    9459      232815 : /* static */ unsigned short GTiffDataset::ClampCTEntry(int iColor, int iComp,
    9460             :                                                        int nCTEntryVal,
    9461             :                                                        int nMultFactor)
    9462             : {
    9463      232815 :     const int nVal = nCTEntryVal * nMultFactor;
    9464      232815 :     if (nVal < 0)
    9465             :     {
    9466           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    9467             :                  "Color table entry [%d][%d] = %d, clamped to 0", iColor, iComp,
    9468             :                  nCTEntryVal);
    9469           0 :         return 0;
    9470             :     }
    9471      232815 :     if (nVal > 65535)
    9472             :     {
    9473           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    9474             :                  "Color table entry [%d][%d] = %d, clamped to 65535", iColor,
    9475             :                  iComp, nCTEntryVal);
    9476           2 :         return 65535;
    9477             :     }
    9478      232813 :     return static_cast<unsigned short>(nVal);
    9479             : }

Generated by: LCOV version 1.14