LCOV - code coverage report
Current view: top level - frmts/gtiff - gtiffdataset_write.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3668 4012 91.4 %
Date: 2024-11-21 22:18:42 Functions: 108 132 81.8 %

          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_md5.h"
      34             : #include "cpl_vsi.h"
      35             : #include "cpl_vsi_virtual.h"
      36             : #include "cpl_worker_thread_pool.h"
      37             : #include "fetchbufferdirectio.h"
      38             : #include "gdal_mdreader.h"          // GDALWriteRPCTXTFile()
      39             : #include "gdal_priv_templates.hpp"  // GDALIsValueInRange<>
      40             : #include "gdal_thread_pool.h"       // GDALGetGlobalThreadPool()
      41             : #include "geovalues.h"              // RasterPixelIsPoint
      42             : #include "gt_jpeg_copy.h"
      43             : #include "gt_overview.h"  // GTIFFBuildOverviewMetadata()
      44             : #include "quant_table_md5sum.h"
      45             : #include "quant_table_md5sum_jpeg9e.h"
      46             : #include "tif_jxl.h"
      47             : #include "tifvsi.h"
      48             : #include "xtiffio.h"
      49             : 
      50             : #if LIFFLIB_VERSION > 20230908 || defined(INTERNAL_LIBTIFF)
      51             : /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
      52             : #define LIBTIFF_MULTIBAND_LERC_NAN_OK
      53             : #endif
      54             : 
      55             : static const int knGTIFFJpegTablesModeDefault = JPEGTABLESMODE_QUANT;
      56             : 
      57             : static constexpr const char szPROFILE_BASELINE[] = "BASELINE";
      58             : static constexpr const char szPROFILE_GeoTIFF[] = "GeoTIFF";
      59             : static constexpr const char szPROFILE_GDALGeoTIFF[] = "GDALGeoTIFF";
      60             : 
      61             : // Due to libgeotiff/xtiff.c declaring TIFFTAG_GEOTIEPOINTS with field_readcount
      62             : // and field_writecount == -1 == TIFF_VARIABLE, we are limited to writing
      63             : // 65535 values in that tag. That could potentially be overcome by changing the tag
      64             : // declaration to using TIFF_VARIABLE2 where the count is a uint32_t.
      65             : constexpr int knMAX_GCP_COUNT =
      66             :     static_cast<int>(std::numeric_limits<uint16_t>::max() / 6);
      67             : 
      68             : enum
      69             : {
      70             :     ENDIANNESS_NATIVE,
      71             :     ENDIANNESS_LITTLE,
      72             :     ENDIANNESS_BIG
      73             : };
      74             : 
      75       11864 : static signed char GTiffGetWebPLevel(CSLConstList papszOptions)
      76             : {
      77       11864 :     int nWebPLevel = DEFAULT_WEBP_LEVEL;
      78       11864 :     const char *pszValue = CSLFetchNameValue(papszOptions, "WEBP_LEVEL");
      79       11864 :     if (pszValue != nullptr)
      80             :     {
      81          51 :         nWebPLevel = atoi(pszValue);
      82          51 :         if (!(nWebPLevel >= 1 && nWebPLevel <= 100))
      83             :         {
      84           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
      85             :                      "WEBP_LEVEL=%s value not recognised, ignoring.", pszValue);
      86           0 :             nWebPLevel = DEFAULT_WEBP_LEVEL;
      87             :         }
      88             :     }
      89       11864 :     return static_cast<signed char>(nWebPLevel);
      90             : }
      91             : 
      92       11870 : static bool GTiffGetWebPLossless(CSLConstList papszOptions)
      93             : {
      94       11870 :     return CPLFetchBool(papszOptions, "WEBP_LOSSLESS", false);
      95             : }
      96             : 
      97       11936 : static double GTiffGetLERCMaxZError(CSLConstList papszOptions)
      98             : {
      99       11936 :     return CPLAtof(CSLFetchNameValueDef(papszOptions, "MAX_Z_ERROR", "0.0"));
     100             : }
     101             : 
     102        5009 : static double GTiffGetLERCMaxZErrorOverview(CSLConstList papszOptions)
     103             : {
     104        5009 :     return CPLAtof(CSLFetchNameValueDef(
     105             :         papszOptions, "MAX_Z_ERROR_OVERVIEW",
     106        5009 :         CSLFetchNameValueDef(papszOptions, "MAX_Z_ERROR", "0.0")));
     107             : }
     108             : 
     109             : #if HAVE_JXL
     110       11937 : static bool GTiffGetJXLLossless(CSLConstList papszOptions)
     111             : {
     112       11937 :     return CPLTestBool(
     113       11937 :         CSLFetchNameValueDef(papszOptions, "JXL_LOSSLESS", "TRUE"));
     114             : }
     115             : 
     116       11937 : static uint32_t GTiffGetJXLEffort(CSLConstList papszOptions)
     117             : {
     118       11937 :     return atoi(CSLFetchNameValueDef(papszOptions, "JXL_EFFORT", "5"));
     119             : }
     120             : 
     121       11857 : static float GTiffGetJXLDistance(CSLConstList papszOptions)
     122             : {
     123             :     return static_cast<float>(
     124       11857 :         CPLAtof(CSLFetchNameValueDef(papszOptions, "JXL_DISTANCE", "1.0")));
     125             : }
     126             : 
     127       11937 : static float GTiffGetJXLAlphaDistance(CSLConstList papszOptions)
     128             : {
     129       11937 :     return static_cast<float>(CPLAtof(
     130       11937 :         CSLFetchNameValueDef(papszOptions, "JXL_ALPHA_DISTANCE", "-1.0")));
     131             : }
     132             : 
     133             : #endif
     134             : 
     135             : /************************************************************************/
     136             : /*                           FillEmptyTiles()                           */
     137             : /************************************************************************/
     138             : 
     139        5135 : CPLErr GTiffDataset::FillEmptyTiles()
     140             : 
     141             : {
     142             :     /* -------------------------------------------------------------------- */
     143             :     /*      How many blocks are there in this file?                         */
     144             :     /* -------------------------------------------------------------------- */
     145       10270 :     const int nBlockCount = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     146        5135 :                                 ? m_nBlocksPerBand * nBands
     147             :                                 : m_nBlocksPerBand;
     148             : 
     149             :     /* -------------------------------------------------------------------- */
     150             :     /*      Fetch block maps.                                               */
     151             :     /* -------------------------------------------------------------------- */
     152        5135 :     toff_t *panByteCounts = nullptr;
     153             : 
     154        5135 :     if (TIFFIsTiled(m_hTIFF))
     155         908 :         TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts);
     156             :     else
     157        4227 :         TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
     158             : 
     159        5135 :     if (panByteCounts == nullptr)
     160             :     {
     161             :         // Got here with libtiff 3.9.3 and tiff_write_8 test.
     162           0 :         ReportError(CE_Failure, CPLE_AppDefined,
     163             :                     "FillEmptyTiles() failed because panByteCounts == NULL");
     164           0 :         return CE_Failure;
     165             :     }
     166             : 
     167             :     /* -------------------------------------------------------------------- */
     168             :     /*      Prepare a blank data buffer to write for uninitialized blocks.  */
     169             :     /* -------------------------------------------------------------------- */
     170             :     const GPtrDiff_t nBlockBytes =
     171        5135 :         TIFFIsTiled(m_hTIFF) ? static_cast<GPtrDiff_t>(TIFFTileSize(m_hTIFF))
     172        4227 :                              : static_cast<GPtrDiff_t>(TIFFStripSize(m_hTIFF));
     173             : 
     174        5135 :     GByte *pabyData = static_cast<GByte *>(VSI_CALLOC_VERBOSE(nBlockBytes, 1));
     175        5135 :     if (pabyData == nullptr)
     176             :     {
     177           0 :         return CE_Failure;
     178             :     }
     179             : 
     180             :     // Force tiles completely filled with the nodata value to be written.
     181        5135 :     m_bWriteEmptyTiles = true;
     182             : 
     183             :     /* -------------------------------------------------------------------- */
     184             :     /*      If set, fill data buffer with no data value.                    */
     185             :     /* -------------------------------------------------------------------- */
     186        5135 :     if ((m_bNoDataSet && m_dfNoDataValue != 0.0) ||
     187        4931 :         (m_bNoDataSetAsInt64 && m_nNoDataValueInt64 != 0) ||
     188        4929 :         (m_bNoDataSetAsUInt64 && m_nNoDataValueUInt64 != 0))
     189             :     {
     190         208 :         const GDALDataType eDataType = GetRasterBand(1)->GetRasterDataType();
     191         208 :         const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
     192         208 :         if (nDataTypeSize &&
     193         208 :             nDataTypeSize * 8 == static_cast<int>(m_nBitsPerSample))
     194             :         {
     195         197 :             if (m_bNoDataSetAsInt64)
     196             :             {
     197           3 :                 GDALCopyWords64(&m_nNoDataValueInt64, GDT_Int64, 0, pabyData,
     198             :                                 eDataType, nDataTypeSize,
     199           3 :                                 nBlockBytes / nDataTypeSize);
     200             :             }
     201         194 :             else if (m_bNoDataSetAsUInt64)
     202             :             {
     203           2 :                 GDALCopyWords64(&m_nNoDataValueUInt64, GDT_UInt64, 0, pabyData,
     204             :                                 eDataType, nDataTypeSize,
     205           2 :                                 nBlockBytes / nDataTypeSize);
     206             :             }
     207             :             else
     208             :             {
     209         192 :                 double dfNoData = m_dfNoDataValue;
     210         192 :                 GDALCopyWords64(&dfNoData, GDT_Float64, 0, pabyData, eDataType,
     211         192 :                                 nDataTypeSize, nBlockBytes / nDataTypeSize);
     212         197 :             }
     213             :         }
     214          11 :         else if (nDataTypeSize)
     215             :         {
     216             :             // Handle non power-of-two depths.
     217             :             // Ideally make a packed buffer, but that is a bit tedious,
     218             :             // so use the normal I/O interfaces.
     219             : 
     220          11 :             CPLFree(pabyData);
     221             : 
     222          11 :             pabyData = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
     223             :                 m_nBlockXSize, m_nBlockYSize, nDataTypeSize));
     224          11 :             if (pabyData == nullptr)
     225           0 :                 return CE_Failure;
     226          11 :             if (m_bNoDataSetAsInt64)
     227             :             {
     228           0 :                 GDALCopyWords64(&m_nNoDataValueInt64, GDT_Int64, 0, pabyData,
     229             :                                 eDataType, nDataTypeSize,
     230           0 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     231           0 :                                     m_nBlockYSize);
     232             :             }
     233          11 :             else if (m_bNoDataSetAsUInt64)
     234             :             {
     235           0 :                 GDALCopyWords64(&m_nNoDataValueUInt64, GDT_UInt64, 0, pabyData,
     236             :                                 eDataType, nDataTypeSize,
     237           0 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     238           0 :                                     m_nBlockYSize);
     239             :             }
     240             :             else
     241             :             {
     242          11 :                 GDALCopyWords64(&m_dfNoDataValue, GDT_Float64, 0, pabyData,
     243             :                                 eDataType, nDataTypeSize,
     244          11 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     245          11 :                                     m_nBlockYSize);
     246             :             }
     247          11 :             CPLErr eErr = CE_None;
     248          46 :             for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     249             :             {
     250          35 :                 if (panByteCounts[iBlock] == 0)
     251             :                 {
     252          18 :                     if (m_nPlanarConfig == PLANARCONFIG_SEPARATE || nBands == 1)
     253             :                     {
     254          24 :                         if (GetRasterBand(1 + iBlock / m_nBlocksPerBand)
     255          12 :                                 ->WriteBlock((iBlock % m_nBlocksPerBand) %
     256          12 :                                                  m_nBlocksPerRow,
     257          12 :                                              (iBlock % m_nBlocksPerBand) /
     258          12 :                                                  m_nBlocksPerRow,
     259          12 :                                              pabyData) != CE_None)
     260             :                         {
     261           0 :                             eErr = CE_Failure;
     262             :                         }
     263             :                     }
     264             :                     else
     265             :                     {
     266             :                         // In contig case, don't directly call WriteBlock(), as
     267             :                         // it could cause useless decompression-recompression.
     268           6 :                         const int nXOff =
     269           6 :                             (iBlock % m_nBlocksPerRow) * m_nBlockXSize;
     270           6 :                         const int nYOff =
     271           6 :                             (iBlock / m_nBlocksPerRow) * m_nBlockYSize;
     272           6 :                         const int nXSize =
     273           6 :                             (nXOff + m_nBlockXSize <= nRasterXSize)
     274           6 :                                 ? m_nBlockXSize
     275           2 :                                 : nRasterXSize - nXOff;
     276           6 :                         const int nYSize =
     277           6 :                             (nYOff + m_nBlockYSize <= nRasterYSize)
     278           6 :                                 ? m_nBlockYSize
     279           3 :                                 : nRasterYSize - nYOff;
     280          18 :                         for (int iBand = 1; iBand <= nBands; ++iBand)
     281             :                         {
     282          12 :                             if (GetRasterBand(iBand)->RasterIO(
     283             :                                     GF_Write, nXOff, nYOff, nXSize, nYSize,
     284             :                                     pabyData, nXSize, nYSize, eDataType, 0, 0,
     285          12 :                                     nullptr) != CE_None)
     286             :                             {
     287           0 :                                 eErr = CE_Failure;
     288             :                             }
     289             :                         }
     290             :                     }
     291             :                 }
     292             :             }
     293          11 :             CPLFree(pabyData);
     294          11 :             return eErr;
     295         197 :         }
     296             :     }
     297             : 
     298             :     /* -------------------------------------------------------------------- */
     299             :     /*      When we must fill with zeroes, try to create non-sparse file    */
     300             :     /*      w.r.t TIFF spec ... as a sparse file w.r.t filesystem, ie by    */
     301             :     /*      seeking to end of file instead of writing zero blocks.          */
     302             :     /* -------------------------------------------------------------------- */
     303        4927 :     else if (m_nCompression == COMPRESSION_NONE && (m_nBitsPerSample % 8) == 0)
     304             :     {
     305        3664 :         CPLErr eErr = CE_None;
     306             :         // Only use libtiff to write the first sparse block to ensure that it
     307             :         // will serialize offset and count arrays back to disk.
     308        3664 :         int nCountBlocksToZero = 0;
     309     2273530 :         for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     310             :         {
     311     2269870 :             if (panByteCounts[iBlock] == 0)
     312             :             {
     313     2180810 :                 if (nCountBlocksToZero == 0)
     314             :                 {
     315         918 :                     const bool bWriteEmptyTilesBak = m_bWriteEmptyTiles;
     316         918 :                     m_bWriteEmptyTiles = true;
     317         918 :                     const bool bOK = WriteEncodedTileOrStrip(iBlock, pabyData,
     318         918 :                                                              FALSE) == CE_None;
     319         918 :                     m_bWriteEmptyTiles = bWriteEmptyTilesBak;
     320         918 :                     if (!bOK)
     321             :                     {
     322           2 :                         eErr = CE_Failure;
     323           2 :                         break;
     324             :                     }
     325             :                 }
     326     2180810 :                 nCountBlocksToZero++;
     327             :             }
     328             :         }
     329        3664 :         CPLFree(pabyData);
     330             : 
     331        3664 :         --nCountBlocksToZero;
     332             : 
     333             :         // And then seek to end of file for other ones.
     334        3664 :         if (nCountBlocksToZero > 0)
     335             :         {
     336         305 :             toff_t *panByteOffsets = nullptr;
     337             : 
     338         305 :             if (TIFFIsTiled(m_hTIFF))
     339          85 :                 TIFFGetField(m_hTIFF, TIFFTAG_TILEOFFSETS, &panByteOffsets);
     340             :             else
     341         220 :                 TIFFGetField(m_hTIFF, TIFFTAG_STRIPOFFSETS, &panByteOffsets);
     342             : 
     343         305 :             if (panByteOffsets == nullptr)
     344             :             {
     345           0 :                 ReportError(
     346             :                     CE_Failure, CPLE_AppDefined,
     347             :                     "FillEmptyTiles() failed because panByteOffsets == NULL");
     348           0 :                 return CE_Failure;
     349             :             }
     350             : 
     351         305 :             VSILFILE *fpTIF = VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
     352         305 :             VSIFSeekL(fpTIF, 0, SEEK_END);
     353         305 :             const vsi_l_offset nOffset = VSIFTellL(fpTIF);
     354             : 
     355         305 :             vsi_l_offset iBlockToZero = 0;
     356     2187920 :             for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     357             :             {
     358     2187610 :                 if (panByteCounts[iBlock] == 0)
     359             :                 {
     360     2179890 :                     panByteOffsets[iBlock] = static_cast<toff_t>(
     361     2179890 :                         nOffset + iBlockToZero * nBlockBytes);
     362     2179890 :                     panByteCounts[iBlock] = nBlockBytes;
     363     2179890 :                     iBlockToZero++;
     364             :                 }
     365             :             }
     366         305 :             CPLAssert(iBlockToZero ==
     367             :                       static_cast<vsi_l_offset>(nCountBlocksToZero));
     368             : 
     369         305 :             if (VSIFTruncateL(fpTIF, nOffset + iBlockToZero * nBlockBytes) != 0)
     370             :             {
     371           0 :                 eErr = CE_Failure;
     372           0 :                 ReportError(CE_Failure, CPLE_FileIO,
     373             :                             "Cannot initialize empty blocks");
     374             :             }
     375             :         }
     376             : 
     377        3664 :         return eErr;
     378             :     }
     379             : 
     380             :     /* -------------------------------------------------------------------- */
     381             :     /*      Check all blocks, writing out data for uninitialized blocks.    */
     382             :     /* -------------------------------------------------------------------- */
     383             : 
     384        1460 :     GByte *pabyRaw = nullptr;
     385        1460 :     vsi_l_offset nRawSize = 0;
     386        1460 :     CPLErr eErr = CE_None;
     387       42575 :     for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     388             :     {
     389       41122 :         if (panByteCounts[iBlock] == 0)
     390             :         {
     391        8627 :             if (pabyRaw == nullptr)
     392             :             {
     393        1562 :                 if (WriteEncodedTileOrStrip(iBlock, pabyData, FALSE) != CE_None)
     394             :                 {
     395           7 :                     eErr = CE_Failure;
     396           7 :                     break;
     397             :                 }
     398             : 
     399        1555 :                 vsi_l_offset nOffset = 0;
     400        1555 :                 if (!IsBlockAvailable(iBlock, &nOffset, &nRawSize))
     401           0 :                     break;
     402             : 
     403             :                 // When using compression, get back the compressed block
     404             :                 // so we can use the raw API to write it faster.
     405        1555 :                 if (m_nCompression != COMPRESSION_NONE)
     406             :                 {
     407             :                     pabyRaw = static_cast<GByte *>(
     408         355 :                         VSI_MALLOC_VERBOSE(static_cast<size_t>(nRawSize)));
     409         355 :                     if (pabyRaw)
     410             :                     {
     411             :                         VSILFILE *fp =
     412         355 :                             VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
     413         355 :                         const vsi_l_offset nCurOffset = VSIFTellL(fp);
     414         355 :                         VSIFSeekL(fp, nOffset, SEEK_SET);
     415         355 :                         VSIFReadL(pabyRaw, 1, static_cast<size_t>(nRawSize),
     416             :                                   fp);
     417         355 :                         VSIFSeekL(fp, nCurOffset, SEEK_SET);
     418             :                     }
     419             :                 }
     420             :             }
     421             :             else
     422             :             {
     423        7065 :                 WriteRawStripOrTile(iBlock, pabyRaw,
     424             :                                     static_cast<GPtrDiff_t>(nRawSize));
     425             :             }
     426             :         }
     427             :     }
     428             : 
     429        1460 :     CPLFree(pabyData);
     430        1460 :     VSIFree(pabyRaw);
     431        1460 :     return eErr;
     432             : }
     433             : 
     434             : /************************************************************************/
     435             : /*                         HasOnlyNoData()                              */
     436             : /************************************************************************/
     437             : 
     438       25016 : bool GTiffDataset::HasOnlyNoData(const void *pBuffer, int nWidth, int nHeight,
     439             :                                  int nLineStride, int nComponents)
     440             : {
     441       25016 :     if (m_nSampleFormat == SAMPLEFORMAT_COMPLEXINT ||
     442       25016 :         m_nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP)
     443           0 :         return false;
     444       25016 :     if (m_bNoDataSetAsInt64 || m_bNoDataSetAsUInt64)
     445           2 :         return false;  // FIXME: over pessimistic
     446       50028 :     return GDALBufferHasOnlyNoData(
     447       25014 :         pBuffer, m_bNoDataSet ? m_dfNoDataValue : 0.0, nWidth, nHeight,
     448       25014 :         nLineStride, nComponents, m_nBitsPerSample,
     449       25014 :         m_nSampleFormat == SAMPLEFORMAT_UINT  ? GSF_UNSIGNED_INT
     450         721 :         : m_nSampleFormat == SAMPLEFORMAT_INT ? GSF_SIGNED_INT
     451       25014 :                                               : GSF_FLOATING_POINT);
     452             : }
     453             : 
     454             : /************************************************************************/
     455             : /*                     IsFirstPixelEqualToNoData()                      */
     456             : /************************************************************************/
     457             : 
     458      108123 : inline bool GTiffDataset::IsFirstPixelEqualToNoData(const void *pBuffer)
     459             : {
     460      108123 :     const GDALDataType eDT = GetRasterBand(1)->GetRasterDataType();
     461      108123 :     const double dfEffectiveNoData = (m_bNoDataSet) ? m_dfNoDataValue : 0.0;
     462      108123 :     if (m_bNoDataSetAsInt64 || m_bNoDataSetAsUInt64)
     463           2 :         return true;  // FIXME: over pessimistic
     464      108121 :     if (m_nBitsPerSample == 8 ||
     465        9653 :         (m_nBitsPerSample < 8 && dfEffectiveNoData == 0))
     466             :     {
     467      100494 :         if (eDT == GDT_Int8)
     468             :         {
     469          40 :             return GDALIsValueInRange<signed char>(dfEffectiveNoData) &&
     470          20 :                    *(static_cast<const signed char *>(pBuffer)) ==
     471          40 :                        static_cast<signed char>(dfEffectiveNoData);
     472             :         }
     473      200921 :         return GDALIsValueInRange<GByte>(dfEffectiveNoData) &&
     474      100447 :                *(static_cast<const GByte *>(pBuffer)) ==
     475      200921 :                    static_cast<GByte>(dfEffectiveNoData);
     476             :     }
     477        7627 :     if (m_nBitsPerSample == 16 && eDT == GDT_UInt16)
     478             :     {
     479        3498 :         return GDALIsValueInRange<GUInt16>(dfEffectiveNoData) &&
     480        1749 :                *(static_cast<const GUInt16 *>(pBuffer)) ==
     481        3498 :                    static_cast<GUInt16>(dfEffectiveNoData);
     482             :     }
     483        5878 :     if (m_nBitsPerSample == 16 && eDT == GDT_Int16)
     484             :     {
     485        6636 :         return GDALIsValueInRange<GInt16>(dfEffectiveNoData) &&
     486        3318 :                *(static_cast<const GInt16 *>(pBuffer)) ==
     487        6636 :                    static_cast<GInt16>(dfEffectiveNoData);
     488             :     }
     489        2560 :     if (m_nBitsPerSample == 32 && eDT == GDT_UInt32)
     490             :     {
     491         148 :         return GDALIsValueInRange<GUInt32>(dfEffectiveNoData) &&
     492          74 :                *(static_cast<const GUInt32 *>(pBuffer)) ==
     493         148 :                    static_cast<GUInt32>(dfEffectiveNoData);
     494             :     }
     495        2486 :     if (m_nBitsPerSample == 32 && eDT == GDT_Int32)
     496             :     {
     497         258 :         return GDALIsValueInRange<GInt32>(dfEffectiveNoData) &&
     498         129 :                *(static_cast<const GInt32 *>(pBuffer)) ==
     499         258 :                    static_cast<GInt32>(dfEffectiveNoData);
     500             :     }
     501        2357 :     if (m_nBitsPerSample == 64 && eDT == GDT_UInt64)
     502             :     {
     503          10 :         return GDALIsValueInRange<std::uint64_t>(dfEffectiveNoData) &&
     504           5 :                *(static_cast<const std::uint64_t *>(pBuffer)) ==
     505          10 :                    static_cast<std::uint64_t>(dfEffectiveNoData);
     506             :     }
     507        2352 :     if (m_nBitsPerSample == 64 && eDT == GDT_Int64)
     508             :     {
     509          12 :         return GDALIsValueInRange<std::int64_t>(dfEffectiveNoData) &&
     510           6 :                *(static_cast<const std::int64_t *>(pBuffer)) ==
     511          12 :                    static_cast<std::int64_t>(dfEffectiveNoData);
     512             :     }
     513        2346 :     if (m_nBitsPerSample == 32 && eDT == GDT_Float32)
     514             :     {
     515         979 :         if (std::isnan(m_dfNoDataValue))
     516           3 :             return CPL_TO_BOOL(
     517           6 :                 std::isnan(*(static_cast<const float *>(pBuffer))));
     518        1952 :         return GDALIsValueInRange<float>(dfEffectiveNoData) &&
     519         976 :                *(static_cast<const float *>(pBuffer)) ==
     520        1952 :                    static_cast<float>(dfEffectiveNoData);
     521             :     }
     522        1367 :     if (m_nBitsPerSample == 64 && eDT == GDT_Float64)
     523             :     {
     524         185 :         if (std::isnan(dfEffectiveNoData))
     525           0 :             return CPL_TO_BOOL(
     526           0 :                 std::isnan(*(static_cast<const double *>(pBuffer))));
     527         185 :         return *(static_cast<const double *>(pBuffer)) == dfEffectiveNoData;
     528             :     }
     529        1182 :     return false;
     530             : }
     531             : 
     532             : /************************************************************************/
     533             : /*                      WriteDealWithLercAndNan()                       */
     534             : /************************************************************************/
     535             : 
     536             : template <typename T>
     537           0 : void GTiffDataset::WriteDealWithLercAndNan(T *pBuffer, int nActualBlockWidth,
     538             :                                            int nActualBlockHeight,
     539             :                                            int nStrileHeight)
     540             : {
     541             :     // This method does 2 things:
     542             :     // - warn the user if he tries to write NaN values with libtiff < 4.6.1
     543             :     //   and multi-band PlanarConfig=Contig configuration
     544             :     // - and in right-most and bottom-most tiles, replace non accessible
     545             :     //   pixel values by a safe one.
     546             : 
     547           0 :     const auto fPaddingValue =
     548             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     549             :         m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1
     550             :             ? 0
     551             :             :
     552             : #endif
     553             :             std::numeric_limits<T>::quiet_NaN();
     554             : 
     555           0 :     const int nBandsPerStrile =
     556           0 :         m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     557           0 :     for (int j = 0; j < nActualBlockHeight; ++j)
     558             :     {
     559             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     560             :         static bool bHasWarned = false;
     561             :         if (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1 && !bHasWarned)
     562             :         {
     563             :             for (int i = 0; i < nActualBlockWidth * nBandsPerStrile; ++i)
     564             :             {
     565             :                 if (std::isnan(
     566             :                         pBuffer[j * m_nBlockXSize * nBandsPerStrile + i]))
     567             :                 {
     568             :                     bHasWarned = true;
     569             :                     CPLError(CE_Warning, CPLE_AppDefined,
     570             :                              "libtiff < 4.6.1 does not handle properly NaN "
     571             :                              "values for multi-band PlanarConfig=Contig "
     572             :                              "configuration. As a workaround, you can set the "
     573             :                              "INTERLEAVE=BAND creation option.");
     574             :                     break;
     575             :                 }
     576             :             }
     577             :         }
     578             : #endif
     579           0 :         for (int i = nActualBlockWidth * nBandsPerStrile;
     580           0 :              i < m_nBlockXSize * nBandsPerStrile; ++i)
     581             :         {
     582           0 :             pBuffer[j * m_nBlockXSize * nBandsPerStrile + i] = fPaddingValue;
     583             :         }
     584             :     }
     585           0 :     for (int j = nActualBlockHeight; j < nStrileHeight; ++j)
     586             :     {
     587           0 :         for (int i = 0; i < m_nBlockXSize * nBandsPerStrile; ++i)
     588             :         {
     589           0 :             pBuffer[j * m_nBlockXSize * nBandsPerStrile + i] = fPaddingValue;
     590             :         }
     591             :     }
     592           0 : }
     593             : 
     594             : /************************************************************************/
     595             : /*                        WriteEncodedTile()                            */
     596             : /************************************************************************/
     597             : 
     598       46815 : bool GTiffDataset::WriteEncodedTile(uint32_t tile, GByte *pabyData,
     599             :                                     int bPreserveDataBuffer)
     600             : {
     601             : 
     602       46815 :     const int iColumn = (tile % m_nBlocksPerBand) % m_nBlocksPerRow;
     603       46815 :     const int iRow = (tile % m_nBlocksPerBand) / m_nBlocksPerRow;
     604             : 
     605       93630 :     const int nActualBlockWidth = (iColumn == m_nBlocksPerRow - 1)
     606       46815 :                                       ? nRasterXSize - iColumn * m_nBlockXSize
     607             :                                       : m_nBlockXSize;
     608       93630 :     const int nActualBlockHeight = (iRow == m_nBlocksPerColumn - 1)
     609       46815 :                                        ? nRasterYSize - iRow * m_nBlockYSize
     610             :                                        : m_nBlockYSize;
     611             : 
     612             :     /* -------------------------------------------------------------------- */
     613             :     /*      Don't write empty blocks in some cases.                         */
     614             :     /* -------------------------------------------------------------------- */
     615       46815 :     if (!m_bWriteEmptyTiles && IsFirstPixelEqualToNoData(pabyData))
     616             :     {
     617        1835 :         if (!IsBlockAvailable(tile))
     618             :         {
     619        1835 :             const int nComponents =
     620        1835 :                 m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     621             : 
     622        1835 :             if (HasOnlyNoData(pabyData, nActualBlockWidth, nActualBlockHeight,
     623             :                               m_nBlockXSize, nComponents))
     624             :             {
     625        1152 :                 return true;
     626             :             }
     627             :         }
     628             :     }
     629             : 
     630             :     // Is this a partial right edge or bottom edge tile?
     631       89056 :     const bool bPartialTile = (nActualBlockWidth < m_nBlockXSize) ||
     632       43393 :                               (nActualBlockHeight < m_nBlockYSize);
     633             : 
     634             :     const bool bIsLercFloatingPoint =
     635       45721 :         m_nCompression == COMPRESSION_LERC &&
     636          66 :         (GetRasterBand(1)->GetRasterDataType() == GDT_Float32 ||
     637          64 :          GetRasterBand(1)->GetRasterDataType() == GDT_Float64);
     638             : 
     639             :     // Do we need to spread edge values right or down for a partial
     640             :     // JPEG encoded tile?  We do this to avoid edge artifacts.
     641             :     // We also need to be careful with LERC and NaN values
     642       45655 :     const bool bNeedTempBuffer =
     643       49241 :         bPartialTile &&
     644        3586 :         (m_nCompression == COMPRESSION_JPEG || bIsLercFloatingPoint);
     645             : 
     646             :     // If we need to fill out the tile, or if we want to prevent
     647             :     // TIFFWriteEncodedTile from altering the buffer as part of
     648             :     // byte swapping the data on write then we will need a temporary
     649             :     // working buffer.  If not, we can just do a direct write.
     650       45655 :     const GPtrDiff_t cc = static_cast<GPtrDiff_t>(TIFFTileSize(m_hTIFF));
     651             : 
     652       59266 :     if (bPreserveDataBuffer &&
     653       13604 :         (TIFFIsByteSwapped(m_hTIFF) || bNeedTempBuffer || m_panMaskOffsetLsb))
     654             :     {
     655         176 :         if (m_pabyTempWriteBuffer == nullptr)
     656             :         {
     657          39 :             m_pabyTempWriteBuffer = CPLMalloc(cc);
     658             :         }
     659         176 :         memcpy(m_pabyTempWriteBuffer, pabyData, cc);
     660             : 
     661         176 :         pabyData = static_cast<GByte *>(m_pabyTempWriteBuffer);
     662             :     }
     663             : 
     664             :     // Perform tile fill if needed.
     665             :     // TODO: we should also handle the case of nBitsPerSample == 12
     666             :     // but this is more involved.
     667       45662 :     if (bPartialTile && m_nCompression == COMPRESSION_JPEG &&
     668         131 :         m_nBitsPerSample == 8)
     669             :     {
     670         129 :         const int nComponents =
     671         129 :             m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     672             : 
     673         129 :         CPLDebug("GTiff", "Filling out jpeg edge tile on write.");
     674             : 
     675         129 :         const int nRightPixelsToFill =
     676         129 :             iColumn == m_nBlocksPerRow - 1
     677         129 :                 ? m_nBlockXSize * (iColumn + 1) - nRasterXSize
     678             :                 : 0;
     679         129 :         const int nBottomPixelsToFill =
     680         129 :             iRow == m_nBlocksPerColumn - 1
     681         129 :                 ? m_nBlockYSize * (iRow + 1) - nRasterYSize
     682             :                 : 0;
     683             : 
     684             :         // Fill out to the right.
     685         129 :         const int iSrcX = m_nBlockXSize - nRightPixelsToFill - 1;
     686             : 
     687       11072 :         for (int iX = iSrcX + 1; iX < m_nBlockXSize; ++iX)
     688             :         {
     689     3244860 :             for (int iY = 0; iY < m_nBlockYSize; ++iY)
     690             :             {
     691     3233920 :                 memcpy(pabyData +
     692     3233920 :                            (static_cast<GPtrDiff_t>(m_nBlockXSize) * iY + iX) *
     693     3233920 :                                nComponents,
     694     3233920 :                        pabyData + (static_cast<GPtrDiff_t>(m_nBlockXSize) * iY +
     695     3233920 :                                    iSrcX) *
     696     3233920 :                                       nComponents,
     697             :                        nComponents);
     698             :             }
     699             :         }
     700             : 
     701             :         // Now fill out the bottom.
     702         129 :         const int iSrcY = m_nBlockYSize - nBottomPixelsToFill - 1;
     703       16293 :         for (int iY = iSrcY + 1; iY < m_nBlockYSize; ++iY)
     704             :         {
     705       16164 :             memcpy(pabyData + static_cast<GPtrDiff_t>(m_nBlockXSize) *
     706       16164 :                                   nComponents * iY,
     707       16164 :                    pabyData + static_cast<GPtrDiff_t>(m_nBlockXSize) *
     708       16164 :                                   nComponents * iSrcY,
     709       16164 :                    static_cast<GPtrDiff_t>(m_nBlockXSize) * nComponents);
     710             :         }
     711             :     }
     712             : 
     713       45662 :     if (bIsLercFloatingPoint &&
     714             :         (bPartialTile
     715             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     716             :          /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
     717             :          || (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1)
     718             : #endif
     719             :              ))
     720             :     {
     721           0 :         if (GetRasterBand(1)->GetRasterDataType() == GDT_Float32)
     722           0 :             WriteDealWithLercAndNan(reinterpret_cast<float *>(pabyData),
     723             :                                     nActualBlockWidth, nActualBlockHeight,
     724             :                                     m_nBlockYSize);
     725             :         else
     726           0 :             WriteDealWithLercAndNan(reinterpret_cast<double *>(pabyData),
     727             :                                     nActualBlockWidth, nActualBlockHeight,
     728             :                                     m_nBlockYSize);
     729             :     }
     730             : 
     731       45662 :     if (m_panMaskOffsetLsb)
     732             :     {
     733           0 :         const int iBand = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     734           0 :                               ? static_cast<int>(tile) / m_nBlocksPerBand
     735             :                               : -1;
     736           0 :         DiscardLsb(pabyData, cc, iBand);
     737             :     }
     738             : 
     739       45661 :     if (m_bStreamingOut)
     740             :     {
     741          17 :         if (tile != static_cast<uint32_t>(m_nLastWrittenBlockId + 1))
     742             :         {
     743           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     744             :                         "Attempt to write block %d whereas %d was expected",
     745           1 :                         tile, m_nLastWrittenBlockId + 1);
     746           1 :             return false;
     747             :         }
     748          16 :         if (static_cast<GPtrDiff_t>(VSIFWriteL(pabyData, 1, cc, m_fpToWrite)) !=
     749             :             cc)
     750             :         {
     751           0 :             ReportError(CE_Failure, CPLE_FileIO,
     752             :                         "Could not write " CPL_FRMT_GUIB " bytes",
     753             :                         static_cast<GUIntBig>(cc));
     754           0 :             return false;
     755             :         }
     756          16 :         m_nLastWrittenBlockId = tile;
     757          16 :         return true;
     758             :     }
     759             : 
     760             :     /* -------------------------------------------------------------------- */
     761             :     /*      Should we do compression in a worker thread ?                   */
     762             :     /* -------------------------------------------------------------------- */
     763       45644 :     if (SubmitCompressionJob(tile, pabyData, cc, m_nBlockYSize))
     764       19282 :         return true;
     765             : 
     766       26358 :     return TIFFWriteEncodedTile(m_hTIFF, tile, pabyData, cc) == cc;
     767             : }
     768             : 
     769             : /************************************************************************/
     770             : /*                        WriteEncodedStrip()                           */
     771             : /************************************************************************/
     772             : 
     773      108520 : bool GTiffDataset::WriteEncodedStrip(uint32_t strip, GByte *pabyData,
     774             :                                      int bPreserveDataBuffer)
     775             : {
     776      108520 :     GPtrDiff_t cc = static_cast<GPtrDiff_t>(TIFFStripSize(m_hTIFF));
     777      108520 :     const auto ccFull = cc;
     778             : 
     779             :     /* -------------------------------------------------------------------- */
     780             :     /*      If this is the last strip in the image, and is partial, then    */
     781             :     /*      we need to trim the number of scanlines written to the          */
     782             :     /*      amount of valid data we have. (#2748)                           */
     783             :     /* -------------------------------------------------------------------- */
     784      108520 :     const int nStripWithinBand = strip % m_nBlocksPerBand;
     785      108520 :     int nStripHeight = m_nRowsPerStrip;
     786             : 
     787      108520 :     if (nStripWithinBand * nStripHeight > GetRasterYSize() - nStripHeight)
     788             :     {
     789        1105 :         nStripHeight = GetRasterYSize() - nStripWithinBand * m_nRowsPerStrip;
     790        1105 :         cc = (cc / m_nRowsPerStrip) * nStripHeight;
     791        2210 :         CPLDebug("GTiff",
     792             :                  "Adjusted bytes to write from " CPL_FRMT_GUIB
     793             :                  " to " CPL_FRMT_GUIB ".",
     794        1105 :                  static_cast<GUIntBig>(TIFFStripSize(m_hTIFF)),
     795             :                  static_cast<GUIntBig>(cc));
     796             :     }
     797             : 
     798             :     /* -------------------------------------------------------------------- */
     799             :     /*      Don't write empty blocks in some cases.                         */
     800             :     /* -------------------------------------------------------------------- */
     801      108520 :     if (!m_bWriteEmptyTiles && IsFirstPixelEqualToNoData(pabyData))
     802             :     {
     803       23253 :         if (!IsBlockAvailable(strip))
     804             :         {
     805       23181 :             const int nComponents =
     806       23181 :                 m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     807             : 
     808       23181 :             if (HasOnlyNoData(pabyData, m_nBlockXSize, nStripHeight,
     809             :                               m_nBlockXSize, nComponents))
     810             :             {
     811       14112 :                 return true;
     812             :             }
     813             :         }
     814             :     }
     815             : 
     816             :     /* -------------------------------------------------------------------- */
     817             :     /*      TIFFWriteEncodedStrip can alter the passed buffer if            */
     818             :     /*      byte-swapping is necessary so we use a temporary buffer         */
     819             :     /*      before calling it.                                              */
     820             :     /* -------------------------------------------------------------------- */
     821      141622 :     if (bPreserveDataBuffer &&
     822       47214 :         (TIFFIsByteSwapped(m_hTIFF) || m_panMaskOffsetLsb))
     823             :     {
     824         293 :         if (m_pabyTempWriteBuffer == nullptr)
     825             :         {
     826         125 :             m_pabyTempWriteBuffer = CPLMalloc(ccFull);
     827             :         }
     828         293 :         memcpy(m_pabyTempWriteBuffer, pabyData, cc);
     829         293 :         pabyData = static_cast<GByte *>(m_pabyTempWriteBuffer);
     830             :     }
     831             : 
     832             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     833             :     const bool bIsLercFloatingPoint =
     834             :         m_nCompression == COMPRESSION_LERC &&
     835             :         (GetRasterBand(1)->GetRasterDataType() == GDT_Float32 ||
     836             :          GetRasterBand(1)->GetRasterDataType() == GDT_Float64);
     837             :     if (bIsLercFloatingPoint &&
     838             :         /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
     839             :         m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1)
     840             :     {
     841             :         if (GetRasterBand(1)->GetRasterDataType() == GDT_Float32)
     842             :             WriteDealWithLercAndNan(reinterpret_cast<float *>(pabyData),
     843             :                                     m_nBlockXSize, nStripHeight, nStripHeight);
     844             :         else
     845             :             WriteDealWithLercAndNan(reinterpret_cast<double *>(pabyData),
     846             :                                     m_nBlockXSize, nStripHeight, nStripHeight);
     847             :     }
     848             : #endif
     849             : 
     850       94408 :     if (m_panMaskOffsetLsb)
     851             :     {
     852         366 :         int iBand = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     853         183 :                         ? static_cast<int>(strip) / m_nBlocksPerBand
     854             :                         : -1;
     855         183 :         DiscardLsb(pabyData, cc, iBand);
     856             :     }
     857             : 
     858       94408 :     if (m_bStreamingOut)
     859             :     {
     860        1408 :         if (strip != static_cast<uint32_t>(m_nLastWrittenBlockId + 1))
     861             :         {
     862           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     863             :                         "Attempt to write block %d whereas %d was expected",
     864           1 :                         strip, m_nLastWrittenBlockId + 1);
     865           1 :             return false;
     866             :         }
     867        1407 :         if (static_cast<GPtrDiff_t>(VSIFWriteL(pabyData, 1, cc, m_fpToWrite)) !=
     868             :             cc)
     869             :         {
     870           0 :             ReportError(CE_Failure, CPLE_FileIO,
     871             :                         "Could not write " CPL_FRMT_GUIB " bytes",
     872             :                         static_cast<GUIntBig>(cc));
     873           0 :             return false;
     874             :         }
     875        1407 :         m_nLastWrittenBlockId = strip;
     876        1407 :         return true;
     877             :     }
     878             : 
     879             :     /* -------------------------------------------------------------------- */
     880             :     /*      Should we do compression in a worker thread ?                   */
     881             :     /* -------------------------------------------------------------------- */
     882       93000 :     if (SubmitCompressionJob(strip, pabyData, cc, nStripHeight))
     883        6703 :         return true;
     884             : 
     885       86297 :     return TIFFWriteEncodedStrip(m_hTIFF, strip, pabyData, cc) == cc;
     886             : }
     887             : 
     888             : /************************************************************************/
     889             : /*                        InitCompressionThreads()                      */
     890             : /************************************************************************/
     891             : 
     892       24448 : void GTiffDataset::InitCompressionThreads(bool bUpdateMode,
     893             :                                           CSLConstList papszOptions)
     894             : {
     895             :     // Raster == tile, then no need for threads
     896       24448 :     if (m_nBlockXSize == nRasterXSize && m_nBlockYSize == nRasterYSize)
     897       16830 :         return;
     898             : 
     899        7618 :     const char *pszValue = CSLFetchNameValue(papszOptions, "NUM_THREADS");
     900        7603 :     if (pszValue == nullptr)
     901        7539 :         pszValue = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
     902        7609 :     if (pszValue)
     903             :     {
     904             :         int nThreads =
     905          78 :             EQUAL(pszValue, "ALL_CPUS") ? CPLGetNumCPUs() : atoi(pszValue);
     906          78 :         if (nThreads > 1024)
     907           0 :             nThreads = 1024;  // to please Coverity
     908          78 :         if (nThreads > 1)
     909             :         {
     910          85 :             if ((bUpdateMode && m_nCompression != COMPRESSION_NONE) ||
     911          14 :                 (nBands >= 1 && IsMultiThreadedReadCompatible()))
     912             :             {
     913          65 :                 CPLDebug("GTiff",
     914             :                          "Using up to %d threads for compression/decompression",
     915             :                          nThreads);
     916             : 
     917          65 :                 m_poThreadPool = GDALGetGlobalThreadPool(nThreads);
     918          65 :                 if (bUpdateMode && m_poThreadPool)
     919          57 :                     m_poCompressQueue = m_poThreadPool->CreateJobQueue();
     920             : 
     921          65 :                 if (m_poCompressQueue != nullptr)
     922             :                 {
     923             :                     // Add a margin of an extra job w.r.t thread number
     924             :                     // so as to optimize compression time (enables the main
     925             :                     // thread to do boring I/O while all CPUs are working).
     926          57 :                     m_asCompressionJobs.resize(nThreads + 1);
     927          57 :                     memset(&m_asCompressionJobs[0], 0,
     928          57 :                            m_asCompressionJobs.size() *
     929             :                                sizeof(GTiffCompressionJob));
     930          57 :                     for (int i = 0;
     931         276 :                          i < static_cast<int>(m_asCompressionJobs.size()); ++i)
     932             :                     {
     933         438 :                         m_asCompressionJobs[i].pszTmpFilename =
     934         219 :                             CPLStrdup(VSIMemGenerateHiddenFilename(
     935             :                                 CPLSPrintf("thread_job_%d.tif", i)));
     936         219 :                         m_asCompressionJobs[i].nStripOrTile = -1;
     937             :                     }
     938             : 
     939             :                     // This is kind of a hack, but basically using
     940             :                     // TIFFWriteRawStrip/Tile and then TIFFReadEncodedStrip/Tile
     941             :                     // does not work on a newly created file, because
     942             :                     // TIFF_MYBUFFER is not set in tif_flags
     943             :                     // (if using TIFFWriteEncodedStrip/Tile first,
     944             :                     // TIFFWriteBufferSetup() is automatically called).
     945             :                     // This should likely rather fixed in libtiff itself.
     946          57 :                     CPL_IGNORE_RET_VAL(
     947          57 :                         TIFFWriteBufferSetup(m_hTIFF, nullptr, -1));
     948             :                 }
     949             :             }
     950             :         }
     951           7 :         else if (nThreads < 0 ||
     952           7 :                  (!EQUAL(pszValue, "0") && !EQUAL(pszValue, "1") &&
     953           3 :                   !EQUAL(pszValue, "ALL_CPUS")))
     954             :         {
     955           3 :             ReportError(CE_Warning, CPLE_AppDefined,
     956             :                         "Invalid value for NUM_THREADS: %s", pszValue);
     957             :         }
     958             :     }
     959             : }
     960             : 
     961             : /************************************************************************/
     962             : /*                      ThreadCompressionFunc()                         */
     963             : /************************************************************************/
     964             : 
     965       25995 : void GTiffDataset::ThreadCompressionFunc(void *pData)
     966             : {
     967       25995 :     GTiffCompressionJob *psJob = static_cast<GTiffCompressionJob *>(pData);
     968       25995 :     GTiffDataset *poDS = psJob->poDS;
     969             : 
     970       25995 :     VSILFILE *fpTmp = VSIFOpenL(psJob->pszTmpFilename, "wb+");
     971       25997 :     TIFF *hTIFFTmp = VSI_TIFFOpen(
     972       51994 :         psJob->pszTmpFilename, psJob->bTIFFIsBigEndian ? "wb+" : "wl+", fpTmp);
     973       25996 :     CPLAssert(hTIFFTmp != nullptr);
     974       25996 :     TIFFSetField(hTIFFTmp, TIFFTAG_IMAGEWIDTH, poDS->m_nBlockXSize);
     975       25997 :     TIFFSetField(hTIFFTmp, TIFFTAG_IMAGELENGTH, psJob->nHeight);
     976       25997 :     TIFFSetField(hTIFFTmp, TIFFTAG_BITSPERSAMPLE, poDS->m_nBitsPerSample);
     977       25997 :     TIFFSetField(hTIFFTmp, TIFFTAG_COMPRESSION, poDS->m_nCompression);
     978       25997 :     TIFFSetField(hTIFFTmp, TIFFTAG_PHOTOMETRIC, poDS->m_nPhotometric);
     979       25997 :     TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLEFORMAT, poDS->m_nSampleFormat);
     980       25997 :     TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLESPERPIXEL, poDS->m_nSamplesPerPixel);
     981       25997 :     TIFFSetField(hTIFFTmp, TIFFTAG_ROWSPERSTRIP, poDS->m_nBlockYSize);
     982       25996 :     TIFFSetField(hTIFFTmp, TIFFTAG_PLANARCONFIG, poDS->m_nPlanarConfig);
     983       25997 :     if (psJob->nPredictor != PREDICTOR_NONE)
     984         262 :         TIFFSetField(hTIFFTmp, TIFFTAG_PREDICTOR, psJob->nPredictor);
     985       25997 :     if (poDS->m_nCompression == COMPRESSION_LERC)
     986             :     {
     987          24 :         TIFFSetField(hTIFFTmp, TIFFTAG_LERC_PARAMETERS, 2,
     988          24 :                      poDS->m_anLercAddCompressionAndVersion);
     989             :     }
     990       25997 :     if (psJob->nExtraSampleCount)
     991             :     {
     992         282 :         TIFFSetField(hTIFFTmp, TIFFTAG_EXTRASAMPLES, psJob->nExtraSampleCount,
     993             :                      psJob->pExtraSamples);
     994             :     }
     995             : 
     996       25997 :     poDS->RestoreVolatileParameters(hTIFFTmp);
     997             : 
     998       51992 :     bool bOK = TIFFWriteEncodedStrip(hTIFFTmp, 0, psJob->pabyBuffer,
     999       25997 :                                      psJob->nBufferSize) == psJob->nBufferSize;
    1000             : 
    1001       25995 :     toff_t nOffset = 0;
    1002       25995 :     if (bOK)
    1003             :     {
    1004       25995 :         toff_t *panOffsets = nullptr;
    1005       25995 :         toff_t *panByteCounts = nullptr;
    1006       25995 :         TIFFGetField(hTIFFTmp, TIFFTAG_STRIPOFFSETS, &panOffsets);
    1007       25994 :         TIFFGetField(hTIFFTmp, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
    1008             : 
    1009       25995 :         nOffset = panOffsets[0];
    1010       25995 :         psJob->nCompressedBufferSize =
    1011       25995 :             static_cast<GPtrDiff_t>(panByteCounts[0]);
    1012             :     }
    1013             :     else
    1014             :     {
    1015           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1016             :                  "Error when compressing strip/tile %d", psJob->nStripOrTile);
    1017             :     }
    1018             : 
    1019       25995 :     XTIFFClose(hTIFFTmp);
    1020       25996 :     if (VSIFCloseL(fpTmp) != 0)
    1021             :     {
    1022           0 :         if (bOK)
    1023             :         {
    1024           0 :             bOK = false;
    1025           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1026             :                      "Error when compressing strip/tile %d",
    1027             :                      psJob->nStripOrTile);
    1028             :         }
    1029             :     }
    1030             : 
    1031       25994 :     if (bOK)
    1032             :     {
    1033       25994 :         vsi_l_offset nFileSize = 0;
    1034             :         GByte *pabyCompressedBuffer =
    1035       25994 :             VSIGetMemFileBuffer(psJob->pszTmpFilename, &nFileSize, FALSE);
    1036       25997 :         CPLAssert(static_cast<vsi_l_offset>(
    1037             :                       nOffset + psJob->nCompressedBufferSize) <= nFileSize);
    1038       25997 :         psJob->pabyCompressedBuffer = pabyCompressedBuffer + nOffset;
    1039             :     }
    1040             :     else
    1041             :     {
    1042           0 :         psJob->pabyCompressedBuffer = nullptr;
    1043           0 :         psJob->nCompressedBufferSize = 0;
    1044             :     }
    1045             : 
    1046       25997 :     auto poMainDS = poDS->m_poBaseDS ? poDS->m_poBaseDS : poDS;
    1047       25997 :     if (poMainDS->m_poCompressQueue)
    1048             :     {
    1049        1575 :         std::lock_guard oLock(poMainDS->m_oCompressThreadPoolMutex);
    1050        1575 :         psJob->bReady = true;
    1051             :     }
    1052       25997 : }
    1053             : 
    1054             : /************************************************************************/
    1055             : /*                        WriteRawStripOrTile()                         */
    1056             : /************************************************************************/
    1057             : 
    1058       33062 : void GTiffDataset::WriteRawStripOrTile(int nStripOrTile,
    1059             :                                        GByte *pabyCompressedBuffer,
    1060             :                                        GPtrDiff_t nCompressedBufferSize)
    1061             : {
    1062             : #ifdef DEBUG_VERBOSE
    1063             :     CPLDebug("GTIFF", "Writing raw strip/tile %d, size " CPL_FRMT_GUIB,
    1064             :              nStripOrTile, static_cast<GUIntBig>(nCompressedBufferSize));
    1065             : #endif
    1066       33062 :     toff_t *panOffsets = nullptr;
    1067       33062 :     toff_t *panByteCounts = nullptr;
    1068       33062 :     bool bWriteAtEnd = true;
    1069       33062 :     bool bWriteLeader = m_bLeaderSizeAsUInt4;
    1070       33062 :     bool bWriteTrailer = m_bTrailerRepeatedLast4BytesRepeated;
    1071       33062 :     if (TIFFGetField(m_hTIFF,
    1072       33062 :                      TIFFIsTiled(m_hTIFF) ? TIFFTAG_TILEOFFSETS
    1073             :                                           : TIFFTAG_STRIPOFFSETS,
    1074       33062 :                      &panOffsets) &&
    1075       33062 :         panOffsets != nullptr && panOffsets[nStripOrTile] != 0)
    1076             :     {
    1077             :         // Forces TIFFAppendStrip() to consider if the location of the
    1078             :         // tile/strip can be reused or if the strile should be written at end of
    1079             :         // file.
    1080         360 :         TIFFSetWriteOffset(m_hTIFF, 0);
    1081             : 
    1082         360 :         if (m_bBlockOrderRowMajor)
    1083             :         {
    1084         264 :             if (TIFFGetField(m_hTIFF,
    1085         264 :                              TIFFIsTiled(m_hTIFF) ? TIFFTAG_TILEBYTECOUNTS
    1086             :                                                   : TIFFTAG_STRIPBYTECOUNTS,
    1087         528 :                              &panByteCounts) &&
    1088         264 :                 panByteCounts != nullptr)
    1089             :             {
    1090         264 :                 if (static_cast<GUIntBig>(nCompressedBufferSize) >
    1091         264 :                     panByteCounts[nStripOrTile])
    1092             :                 {
    1093           8 :                     GTiffDataset *poRootDS = m_poBaseDS ? m_poBaseDS : this;
    1094           8 :                     if (!poRootDS->m_bKnownIncompatibleEdition &&
    1095           8 :                         !poRootDS->m_bWriteKnownIncompatibleEdition)
    1096             :                     {
    1097           8 :                         ReportError(
    1098             :                             CE_Warning, CPLE_AppDefined,
    1099             :                             "A strile cannot be rewritten in place, which "
    1100             :                             "invalidates the BLOCK_ORDER optimization.");
    1101           8 :                         poRootDS->m_bKnownIncompatibleEdition = true;
    1102           8 :                         poRootDS->m_bWriteKnownIncompatibleEdition = true;
    1103             :                     }
    1104             :                 }
    1105             :                 // For mask interleaving, if the size is not exactly the same,
    1106             :                 // completely give up (we could potentially move the mask in
    1107             :                 // case the imagery is smaller)
    1108         256 :                 else if (m_poMaskDS && m_bMaskInterleavedWithImagery &&
    1109           0 :                          static_cast<GUIntBig>(nCompressedBufferSize) !=
    1110           0 :                              panByteCounts[nStripOrTile])
    1111             :                 {
    1112           0 :                     GTiffDataset *poRootDS = m_poBaseDS ? m_poBaseDS : this;
    1113           0 :                     if (!poRootDS->m_bKnownIncompatibleEdition &&
    1114           0 :                         !poRootDS->m_bWriteKnownIncompatibleEdition)
    1115             :                     {
    1116           0 :                         ReportError(
    1117             :                             CE_Warning, CPLE_AppDefined,
    1118             :                             "A strile cannot be rewritten in place, which "
    1119             :                             "invalidates the MASK_INTERLEAVED_WITH_IMAGERY "
    1120             :                             "optimization.");
    1121           0 :                         poRootDS->m_bKnownIncompatibleEdition = true;
    1122           0 :                         poRootDS->m_bWriteKnownIncompatibleEdition = true;
    1123             :                     }
    1124           0 :                     bWriteLeader = false;
    1125           0 :                     bWriteTrailer = false;
    1126           0 :                     if (m_bLeaderSizeAsUInt4)
    1127             :                     {
    1128             :                         // If there was a valid leader, invalidat it
    1129           0 :                         VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4,
    1130             :                                      SEEK_SET);
    1131             :                         uint32_t nOldSize;
    1132           0 :                         VSIFReadL(&nOldSize, 1, 4,
    1133             :                                   VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF)));
    1134           0 :                         CPL_LSBPTR32(&nOldSize);
    1135           0 :                         if (nOldSize == panByteCounts[nStripOrTile])
    1136             :                         {
    1137           0 :                             uint32_t nInvalidatedSize = 0;
    1138           0 :                             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4,
    1139             :                                          SEEK_SET);
    1140           0 :                             VSI_TIFFWrite(m_hTIFF, &nInvalidatedSize,
    1141             :                                           sizeof(nInvalidatedSize));
    1142             :                         }
    1143           0 :                     }
    1144             :                 }
    1145             :                 else
    1146             :                 {
    1147         256 :                     bWriteAtEnd = false;
    1148             :                 }
    1149             :             }
    1150             :         }
    1151             :     }
    1152       33062 :     if (bWriteLeader &&
    1153       24439 :         static_cast<GUIntBig>(nCompressedBufferSize) <= 0xFFFFFFFFU)
    1154             :     {
    1155             :         // cppcheck-suppress knownConditionTrueFalse
    1156       24439 :         if (bWriteAtEnd)
    1157             :         {
    1158       24183 :             VSI_TIFFSeek(m_hTIFF, 0, SEEK_END);
    1159             :         }
    1160             :         else
    1161             :         {
    1162             :             // If we rewrite an existing strile in place with an existing
    1163             :             // leader, check that the leader is valid, before rewriting it. And
    1164             :             // if it is not valid, then do not write the trailer, as we could
    1165             :             // corrupt other data.
    1166         256 :             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4, SEEK_SET);
    1167             :             uint32_t nOldSize;
    1168         256 :             VSIFReadL(&nOldSize, 1, 4,
    1169             :                       VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF)));
    1170         256 :             CPL_LSBPTR32(&nOldSize);
    1171         256 :             bWriteLeader =
    1172         256 :                 panByteCounts && nOldSize == panByteCounts[nStripOrTile];
    1173         256 :             bWriteTrailer = bWriteLeader;
    1174         256 :             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4, SEEK_SET);
    1175             :         }
    1176             :         // cppcheck-suppress knownConditionTrueFalse
    1177       24439 :         if (bWriteLeader)
    1178             :         {
    1179       24439 :             uint32_t nSize = static_cast<uint32_t>(nCompressedBufferSize);
    1180       24439 :             CPL_LSBPTR32(&nSize);
    1181       24439 :             if (!VSI_TIFFWrite(m_hTIFF, &nSize, sizeof(nSize)))
    1182           0 :                 m_bWriteError = true;
    1183             :         }
    1184             :     }
    1185             :     tmsize_t written;
    1186       33062 :     if (TIFFIsTiled(m_hTIFF))
    1187       25671 :         written = TIFFWriteRawTile(m_hTIFF, nStripOrTile, pabyCompressedBuffer,
    1188             :                                    nCompressedBufferSize);
    1189             :     else
    1190        7391 :         written = TIFFWriteRawStrip(m_hTIFF, nStripOrTile, pabyCompressedBuffer,
    1191             :                                     nCompressedBufferSize);
    1192       33062 :     if (written != nCompressedBufferSize)
    1193          12 :         m_bWriteError = true;
    1194       33062 :     if (bWriteTrailer &&
    1195       24439 :         static_cast<GUIntBig>(nCompressedBufferSize) <= 0xFFFFFFFFU)
    1196             :     {
    1197       24439 :         GByte abyLastBytes[4] = {};
    1198       24439 :         if (nCompressedBufferSize >= 4)
    1199       24439 :             memcpy(abyLastBytes,
    1200       24439 :                    pabyCompressedBuffer + nCompressedBufferSize - 4, 4);
    1201             :         else
    1202           0 :             memcpy(abyLastBytes, pabyCompressedBuffer, nCompressedBufferSize);
    1203       24439 :         if (!VSI_TIFFWrite(m_hTIFF, abyLastBytes, 4))
    1204           0 :             m_bWriteError = true;
    1205             :     }
    1206       33062 : }
    1207             : 
    1208             : /************************************************************************/
    1209             : /*                        WaitCompletionForJobIdx()                     */
    1210             : /************************************************************************/
    1211             : 
    1212        1575 : void GTiffDataset::WaitCompletionForJobIdx(int i)
    1213             : {
    1214        1575 :     auto poMainDS = m_poBaseDS ? m_poBaseDS : this;
    1215        1575 :     auto poQueue = poMainDS->m_poCompressQueue.get();
    1216        1575 :     auto &oQueue = poMainDS->m_asQueueJobIdx;
    1217        1575 :     auto &asJobs = poMainDS->m_asCompressionJobs;
    1218        1575 :     auto &mutex = poMainDS->m_oCompressThreadPoolMutex;
    1219             : 
    1220        1575 :     CPLAssert(i >= 0 && static_cast<size_t>(i) < asJobs.size());
    1221        1575 :     CPLAssert(asJobs[i].nStripOrTile >= 0);
    1222        1575 :     CPLAssert(!oQueue.empty());
    1223             : 
    1224        1575 :     bool bHasWarned = false;
    1225             :     while (true)
    1226             :     {
    1227             :         bool bReady;
    1228             :         {
    1229        2167 :             std::lock_guard oLock(mutex);
    1230        2167 :             bReady = asJobs[i].bReady;
    1231             :         }
    1232        2167 :         if (!bReady)
    1233             :         {
    1234         592 :             if (!bHasWarned)
    1235             :             {
    1236         336 :                 CPLDebug("GTIFF",
    1237             :                          "Waiting for worker job to finish handling block %d",
    1238         336 :                          asJobs[i].nStripOrTile);
    1239         336 :                 bHasWarned = true;
    1240             :             }
    1241         592 :             poQueue->GetPool()->WaitEvent();
    1242             :         }
    1243             :         else
    1244             :         {
    1245        1575 :             break;
    1246             :         }
    1247         592 :     }
    1248             : 
    1249        1575 :     if (asJobs[i].nCompressedBufferSize)
    1250             :     {
    1251        3150 :         asJobs[i].poDS->WriteRawStripOrTile(asJobs[i].nStripOrTile,
    1252        1575 :                                             asJobs[i].pabyCompressedBuffer,
    1253        1575 :                                             asJobs[i].nCompressedBufferSize);
    1254             :     }
    1255        1575 :     asJobs[i].pabyCompressedBuffer = nullptr;
    1256        1575 :     asJobs[i].nBufferSize = 0;
    1257             :     {
    1258             :         // Likely useless, but makes Coverity happy
    1259        1575 :         std::lock_guard oLock(mutex);
    1260        1575 :         asJobs[i].bReady = false;
    1261             :     }
    1262        1575 :     asJobs[i].nStripOrTile = -1;
    1263        1575 :     oQueue.pop();
    1264        1575 : }
    1265             : 
    1266             : /************************************************************************/
    1267             : /*                        WaitCompletionForBlock()                      */
    1268             : /************************************************************************/
    1269             : 
    1270     2268320 : void GTiffDataset::WaitCompletionForBlock(int nBlockId)
    1271             : {
    1272     2268320 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    1273     2254040 :                               : m_poCompressQueue.get();
    1274             :     // cppcheck-suppress constVariableReference
    1275     2268300 :     auto &oQueue = m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    1276             :     // cppcheck-suppress constVariableReference
    1277     2254020 :     auto &asJobs =
    1278     2268300 :         m_poBaseDS ? m_poBaseDS->m_asCompressionJobs : m_asCompressionJobs;
    1279             : 
    1280     2268300 :     if (poQueue != nullptr && !oQueue.empty())
    1281             :     {
    1282        1062 :         for (int i = 0; i < static_cast<int>(asJobs.size()); ++i)
    1283             :         {
    1284         885 :             if (asJobs[i].poDS == this && asJobs[i].nStripOrTile == nBlockId)
    1285             :             {
    1286         126 :                 while (!oQueue.empty() &&
    1287          63 :                        !(asJobs[oQueue.front()].poDS == this &&
    1288          63 :                          asJobs[oQueue.front()].nStripOrTile == nBlockId))
    1289             :                 {
    1290           0 :                     WaitCompletionForJobIdx(oQueue.front());
    1291             :                 }
    1292          63 :                 CPLAssert(!oQueue.empty() &&
    1293             :                           asJobs[oQueue.front()].poDS == this &&
    1294             :                           asJobs[oQueue.front()].nStripOrTile == nBlockId);
    1295          63 :                 WaitCompletionForJobIdx(oQueue.front());
    1296             :             }
    1297             :         }
    1298             :     }
    1299     2268300 : }
    1300             : 
    1301             : /************************************************************************/
    1302             : /*                      SubmitCompressionJob()                          */
    1303             : /************************************************************************/
    1304             : 
    1305      138646 : bool GTiffDataset::SubmitCompressionJob(int nStripOrTile, GByte *pabyData,
    1306             :                                         GPtrDiff_t cc, int nHeight)
    1307             : {
    1308             :     /* -------------------------------------------------------------------- */
    1309             :     /*      Should we do compression in a worker thread ?                   */
    1310             :     /* -------------------------------------------------------------------- */
    1311      138646 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    1312      126333 :                               : m_poCompressQueue.get();
    1313             : 
    1314      138639 :     if (poQueue && m_nCompression == COMPRESSION_NONE)
    1315             :     {
    1316             :         // We don't do multi-threaded compression for uncompressed...
    1317             :         // but we must wait for other related compression tasks (e.g mask)
    1318             :         // to be completed
    1319           0 :         poQueue->WaitCompletion();
    1320             : 
    1321             :         // Flush remaining data
    1322             :         // cppcheck-suppress constVariableReference
    1323           0 :         auto &oQueue =
    1324           0 :             m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    1325           0 :         while (!oQueue.empty())
    1326             :         {
    1327           0 :             WaitCompletionForJobIdx(oQueue.front());
    1328             :         }
    1329             :     }
    1330             : 
    1331             :     const auto SetupJob =
    1332      120203 :         [this, pabyData, cc, nHeight, nStripOrTile](GTiffCompressionJob &sJob)
    1333             :     {
    1334       25997 :         sJob.poDS = this;
    1335       25997 :         sJob.bTIFFIsBigEndian = CPL_TO_BOOL(TIFFIsBigEndian(m_hTIFF));
    1336       25997 :         sJob.pabyBuffer = static_cast<GByte *>(CPLRealloc(sJob.pabyBuffer, cc));
    1337       25997 :         memcpy(sJob.pabyBuffer, pabyData, cc);
    1338       25997 :         sJob.nBufferSize = cc;
    1339       25997 :         sJob.nHeight = nHeight;
    1340       25997 :         sJob.nStripOrTile = nStripOrTile;
    1341       25997 :         sJob.nPredictor = PREDICTOR_NONE;
    1342       25997 :         if (GTIFFSupportsPredictor(m_nCompression))
    1343             :         {
    1344       16215 :             TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &sJob.nPredictor);
    1345             :         }
    1346             : 
    1347       25997 :         sJob.pExtraSamples = nullptr;
    1348       25997 :         sJob.nExtraSampleCount = 0;
    1349       25997 :         TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &sJob.nExtraSampleCount,
    1350             :                      &sJob.pExtraSamples);
    1351      164636 :     };
    1352             : 
    1353      138639 :     if (poQueue == nullptr || !(m_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    1354         806 :                                 m_nCompression == COMPRESSION_LZW ||
    1355          78 :                                 m_nCompression == COMPRESSION_PACKBITS ||
    1356          72 :                                 m_nCompression == COMPRESSION_LZMA ||
    1357          62 :                                 m_nCompression == COMPRESSION_ZSTD ||
    1358          52 :                                 m_nCompression == COMPRESSION_LERC ||
    1359          46 :                                 m_nCompression == COMPRESSION_JXL ||
    1360          28 :                                 m_nCompression == COMPRESSION_WEBP ||
    1361          18 :                                 m_nCompression == COMPRESSION_JPEG))
    1362             :     {
    1363      137064 :         if (m_bBlockOrderRowMajor || m_bLeaderSizeAsUInt4 ||
    1364      112643 :             m_bTrailerRepeatedLast4BytesRepeated)
    1365             :         {
    1366             :             GTiffCompressionJob sJob;
    1367       24423 :             memset(&sJob, 0, sizeof(sJob));
    1368       24423 :             SetupJob(sJob);
    1369       24422 :             sJob.pszTmpFilename =
    1370       24422 :                 CPLStrdup(VSIMemGenerateHiddenFilename("temp.tif"));
    1371             : 
    1372       24422 :             ThreadCompressionFunc(&sJob);
    1373             : 
    1374       24422 :             if (sJob.nCompressedBufferSize)
    1375             :             {
    1376       24422 :                 sJob.poDS->WriteRawStripOrTile(sJob.nStripOrTile,
    1377             :                                                sJob.pabyCompressedBuffer,
    1378             :                                                sJob.nCompressedBufferSize);
    1379             :             }
    1380             : 
    1381       24422 :             CPLFree(sJob.pabyBuffer);
    1382       24422 :             VSIUnlink(sJob.pszTmpFilename);
    1383       24422 :             CPLFree(sJob.pszTmpFilename);
    1384       24422 :             return sJob.nCompressedBufferSize > 0 && !m_bWriteError;
    1385             :         }
    1386             : 
    1387      112641 :         return false;
    1388             :     }
    1389             : 
    1390        1575 :     auto poMainDS = m_poBaseDS ? m_poBaseDS : this;
    1391        1575 :     auto &oQueue = poMainDS->m_asQueueJobIdx;
    1392        1575 :     auto &asJobs = poMainDS->m_asCompressionJobs;
    1393             : 
    1394        1575 :     int nNextCompressionJobAvail = -1;
    1395             : 
    1396        1575 :     if (oQueue.size() == asJobs.size())
    1397             :     {
    1398        1443 :         CPLAssert(!oQueue.empty());
    1399        1443 :         nNextCompressionJobAvail = oQueue.front();
    1400        1443 :         WaitCompletionForJobIdx(nNextCompressionJobAvail);
    1401             :     }
    1402             :     else
    1403             :     {
    1404         132 :         const int nJobs = static_cast<int>(asJobs.size());
    1405         323 :         for (int i = 0; i < nJobs; ++i)
    1406             :         {
    1407         323 :             if (asJobs[i].nBufferSize == 0)
    1408             :             {
    1409         132 :                 nNextCompressionJobAvail = i;
    1410         132 :                 break;
    1411             :             }
    1412             :         }
    1413             :     }
    1414        1575 :     CPLAssert(nNextCompressionJobAvail >= 0);
    1415             : 
    1416        1575 :     GTiffCompressionJob *psJob = &asJobs[nNextCompressionJobAvail];
    1417        1575 :     SetupJob(*psJob);
    1418        1575 :     poQueue->SubmitJob(ThreadCompressionFunc, psJob);
    1419        1575 :     oQueue.push(nNextCompressionJobAvail);
    1420             : 
    1421        1575 :     return true;
    1422             : }
    1423             : 
    1424             : /************************************************************************/
    1425             : /*                          DiscardLsb()                                */
    1426             : /************************************************************************/
    1427             : 
    1428         272 : template <class T> bool MustNotDiscardLsb(T value, bool bHasNoData, T nodata)
    1429             : {
    1430         272 :     return bHasNoData && value == nodata;
    1431             : }
    1432             : 
    1433             : template <>
    1434          44 : bool MustNotDiscardLsb<float>(float value, bool bHasNoData, float nodata)
    1435             : {
    1436          44 :     return (bHasNoData && value == nodata) || !std::isfinite(value);
    1437             : }
    1438             : 
    1439             : template <>
    1440          44 : bool MustNotDiscardLsb<double>(double value, bool bHasNoData, double nodata)
    1441             : {
    1442          44 :     return (bHasNoData && value == nodata) || !std::isfinite(value);
    1443             : }
    1444             : 
    1445             : template <class T> T AdjustValue(T value, uint64_t nRoundUpBitTest);
    1446             : 
    1447          10 : template <class T> T AdjustValueInt(T value, uint64_t nRoundUpBitTest)
    1448             : {
    1449          10 :     if (value >=
    1450          10 :         static_cast<T>(std::numeric_limits<T>::max() - (nRoundUpBitTest << 1)))
    1451           0 :         return static_cast<T>(value - (nRoundUpBitTest << 1));
    1452          10 :     return static_cast<T>(value + (nRoundUpBitTest << 1));
    1453             : }
    1454             : 
    1455           0 : template <> int8_t AdjustValue<int8_t>(int8_t value, uint64_t nRoundUpBitTest)
    1456             : {
    1457           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1458             : }
    1459             : 
    1460             : template <>
    1461           2 : uint8_t AdjustValue<uint8_t>(uint8_t value, uint64_t nRoundUpBitTest)
    1462             : {
    1463           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1464             : }
    1465             : 
    1466             : template <>
    1467           2 : int16_t AdjustValue<int16_t>(int16_t value, uint64_t nRoundUpBitTest)
    1468             : {
    1469           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1470             : }
    1471             : 
    1472             : template <>
    1473           2 : uint16_t AdjustValue<uint16_t>(uint16_t value, uint64_t nRoundUpBitTest)
    1474             : {
    1475           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1476             : }
    1477             : 
    1478             : template <>
    1479           2 : int32_t AdjustValue<int32_t>(int32_t value, uint64_t nRoundUpBitTest)
    1480             : {
    1481           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1482             : }
    1483             : 
    1484             : template <>
    1485           2 : uint32_t AdjustValue<uint32_t>(uint32_t value, uint64_t nRoundUpBitTest)
    1486             : {
    1487           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1488             : }
    1489             : 
    1490             : template <>
    1491           0 : int64_t AdjustValue<int64_t>(int64_t value, uint64_t nRoundUpBitTest)
    1492             : {
    1493           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1494             : }
    1495             : 
    1496             : template <>
    1497           0 : uint64_t AdjustValue<uint64_t>(uint64_t value, uint64_t nRoundUpBitTest)
    1498             : {
    1499           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1500             : }
    1501             : 
    1502           0 : template <> float AdjustValue<float>(float value, uint64_t)
    1503             : {
    1504           0 :     return std::nextafter(value, std::numeric_limits<float>::max());
    1505             : }
    1506             : 
    1507           0 : template <> double AdjustValue<double>(double value, uint64_t)
    1508             : {
    1509           0 :     return std::nextafter(value, std::numeric_limits<double>::max());
    1510             : }
    1511             : 
    1512             : template <class Teffective, class T>
    1513             : T RoundValueDiscardLsb(const void *ptr, uint64_t nMask,
    1514             :                        uint64_t nRoundUpBitTest);
    1515             : 
    1516             : template <class T>
    1517          16 : T RoundValueDiscardLsbUnsigned(const void *ptr, uint64_t nMask,
    1518             :                                uint64_t nRoundUpBitTest)
    1519             : {
    1520          32 :     if ((*reinterpret_cast<const T *>(ptr) & nMask) >
    1521          16 :         static_cast<uint64_t>(std::numeric_limits<T>::max()) -
    1522          16 :             (nRoundUpBitTest << 1U))
    1523             :     {
    1524           4 :         return static_cast<T>(std::numeric_limits<T>::max() & nMask);
    1525             :     }
    1526          12 :     const uint64_t newval =
    1527          12 :         (*reinterpret_cast<const T *>(ptr) & nMask) + (nRoundUpBitTest << 1U);
    1528          12 :     return static_cast<T>(newval);
    1529             : }
    1530             : 
    1531             : template <class T>
    1532          18 : T RoundValueDiscardLsbSigned(const void *ptr, uint64_t nMask,
    1533             :                              uint64_t nRoundUpBitTest)
    1534             : {
    1535          18 :     T oldval = *reinterpret_cast<const T *>(ptr);
    1536          18 :     if (oldval < 0)
    1537             :     {
    1538           4 :         return static_cast<T>(oldval & nMask);
    1539             :     }
    1540          14 :     const uint64_t newval =
    1541          14 :         (*reinterpret_cast<const T *>(ptr) & nMask) + (nRoundUpBitTest << 1U);
    1542          14 :     if (newval > static_cast<uint64_t>(std::numeric_limits<T>::max()))
    1543           4 :         return static_cast<T>(std::numeric_limits<T>::max() & nMask);
    1544          10 :     return static_cast<T>(newval);
    1545             : }
    1546             : 
    1547             : template <>
    1548          11 : uint16_t RoundValueDiscardLsb<uint16_t, uint16_t>(const void *ptr,
    1549             :                                                   uint64_t nMask,
    1550             :                                                   uint64_t nRoundUpBitTest)
    1551             : {
    1552          11 :     return RoundValueDiscardLsbUnsigned<uint16_t>(ptr, nMask, nRoundUpBitTest);
    1553             : }
    1554             : 
    1555             : template <>
    1556           5 : uint32_t RoundValueDiscardLsb<uint32_t, uint32_t>(const void *ptr,
    1557             :                                                   uint64_t nMask,
    1558             :                                                   uint64_t nRoundUpBitTest)
    1559             : {
    1560           5 :     return RoundValueDiscardLsbUnsigned<uint32_t>(ptr, nMask, nRoundUpBitTest);
    1561             : }
    1562             : 
    1563             : template <>
    1564           0 : uint64_t RoundValueDiscardLsb<uint64_t, uint64_t>(const void *ptr,
    1565             :                                                   uint64_t nMask,
    1566             :                                                   uint64_t nRoundUpBitTest)
    1567             : {
    1568           0 :     return RoundValueDiscardLsbUnsigned<uint64_t>(ptr, nMask, nRoundUpBitTest);
    1569             : }
    1570             : 
    1571             : template <>
    1572           0 : int8_t RoundValueDiscardLsb<int8_t, int8_t>(const void *ptr, uint64_t nMask,
    1573             :                                             uint64_t nRoundUpBitTest)
    1574             : {
    1575           0 :     return RoundValueDiscardLsbSigned<int8_t>(ptr, nMask, nRoundUpBitTest);
    1576             : }
    1577             : 
    1578             : template <>
    1579          13 : int16_t RoundValueDiscardLsb<int16_t, int16_t>(const void *ptr, uint64_t nMask,
    1580             :                                                uint64_t nRoundUpBitTest)
    1581             : {
    1582          13 :     return RoundValueDiscardLsbSigned<int16_t>(ptr, nMask, nRoundUpBitTest);
    1583             : }
    1584             : 
    1585             : template <>
    1586           5 : int32_t RoundValueDiscardLsb<int32_t, int32_t>(const void *ptr, uint64_t nMask,
    1587             :                                                uint64_t nRoundUpBitTest)
    1588             : {
    1589           5 :     return RoundValueDiscardLsbSigned<int32_t>(ptr, nMask, nRoundUpBitTest);
    1590             : }
    1591             : 
    1592             : template <>
    1593           0 : int64_t RoundValueDiscardLsb<int64_t, int64_t>(const void *ptr, uint64_t nMask,
    1594             :                                                uint64_t nRoundUpBitTest)
    1595             : {
    1596           0 :     return RoundValueDiscardLsbSigned<int64_t>(ptr, nMask, nRoundUpBitTest);
    1597             : }
    1598             : 
    1599             : template <>
    1600           0 : uint32_t RoundValueDiscardLsb<float, uint32_t>(const void *ptr, uint64_t nMask,
    1601             :                                                uint64_t nRoundUpBitTest)
    1602             : {
    1603           0 :     return RoundValueDiscardLsbUnsigned<uint32_t>(ptr, nMask, nRoundUpBitTest);
    1604             : }
    1605             : 
    1606             : template <>
    1607           0 : uint64_t RoundValueDiscardLsb<double, uint64_t>(const void *ptr, uint64_t nMask,
    1608             :                                                 uint64_t nRoundUpBitTest)
    1609             : {
    1610           0 :     return RoundValueDiscardLsbUnsigned<uint64_t>(ptr, nMask, nRoundUpBitTest);
    1611             : }
    1612             : 
    1613             : template <class Teffective, class T>
    1614         145 : static void DiscardLsbT(GByte *pabyBuffer, size_t nBytes, int iBand, int nBands,
    1615             :                         uint16_t nPlanarConfig,
    1616             :                         const GTiffDataset::MaskOffset *panMaskOffsetLsb,
    1617             :                         bool bHasNoData, Teffective nNoDataValue)
    1618             : {
    1619             :     static_assert(sizeof(Teffective) == sizeof(T),
    1620             :                   "sizeof(Teffective) == sizeof(T)");
    1621         145 :     if (nPlanarConfig == PLANARCONFIG_SEPARATE)
    1622             :     {
    1623          98 :         const auto nMask = panMaskOffsetLsb[iBand].nMask;
    1624          98 :         const auto nRoundUpBitTest = panMaskOffsetLsb[iBand].nRoundUpBitTest;
    1625         196 :         for (size_t i = 0; i < nBytes / sizeof(T); ++i)
    1626             :         {
    1627          98 :             if (MustNotDiscardLsb(reinterpret_cast<Teffective *>(pabyBuffer)[i],
    1628             :                                   bHasNoData, nNoDataValue))
    1629             :             {
    1630          22 :                 continue;
    1631             :             }
    1632             : 
    1633          76 :             if (reinterpret_cast<T *>(pabyBuffer)[i] & nRoundUpBitTest)
    1634             :             {
    1635          30 :                 reinterpret_cast<T *>(pabyBuffer)[i] =
    1636          15 :                     RoundValueDiscardLsb<Teffective, T>(
    1637          15 :                         &(reinterpret_cast<T *>(pabyBuffer)[i]), nMask,
    1638             :                         nRoundUpBitTest);
    1639             :             }
    1640             :             else
    1641             :             {
    1642          61 :                 reinterpret_cast<T *>(pabyBuffer)[i] = static_cast<T>(
    1643          61 :                     reinterpret_cast<T *>(pabyBuffer)[i] & nMask);
    1644             :             }
    1645             : 
    1646             :             // Make sure that by discarding LSB we don't end up to a value
    1647             :             // that is no the nodata value
    1648          76 :             if (MustNotDiscardLsb(reinterpret_cast<Teffective *>(pabyBuffer)[i],
    1649             :                                   bHasNoData, nNoDataValue))
    1650             :             {
    1651           8 :                 reinterpret_cast<Teffective *>(pabyBuffer)[i] =
    1652           4 :                     AdjustValue(nNoDataValue, nRoundUpBitTest);
    1653             :             }
    1654             :         }
    1655             :     }
    1656             :     else
    1657             :     {
    1658          94 :         for (size_t i = 0; i < nBytes / sizeof(T); i += nBands)
    1659             :         {
    1660         147 :             for (int j = 0; j < nBands; ++j)
    1661             :             {
    1662         100 :                 if (MustNotDiscardLsb(
    1663         100 :                         reinterpret_cast<Teffective *>(pabyBuffer)[i + j],
    1664             :                         bHasNoData, nNoDataValue))
    1665             :                 {
    1666          14 :                     continue;
    1667             :                 }
    1668             : 
    1669          86 :                 if (reinterpret_cast<T *>(pabyBuffer)[i + j] &
    1670          86 :                     panMaskOffsetLsb[j].nRoundUpBitTest)
    1671             :                 {
    1672          38 :                     reinterpret_cast<T *>(pabyBuffer)[i + j] =
    1673          19 :                         RoundValueDiscardLsb<Teffective, T>(
    1674          19 :                             &(reinterpret_cast<T *>(pabyBuffer)[i + j]),
    1675          19 :                             panMaskOffsetLsb[j].nMask,
    1676          19 :                             panMaskOffsetLsb[j].nRoundUpBitTest);
    1677             :                 }
    1678             :                 else
    1679             :                 {
    1680          67 :                     reinterpret_cast<T *>(pabyBuffer)[i + j] = static_cast<T>(
    1681          67 :                         (reinterpret_cast<T *>(pabyBuffer)[i + j] &
    1682          67 :                          panMaskOffsetLsb[j].nMask));
    1683             :                 }
    1684             : 
    1685             :                 // Make sure that by discarding LSB we don't end up to a value
    1686             :                 // that is no the nodata value
    1687          86 :                 if (MustNotDiscardLsb(
    1688          86 :                         reinterpret_cast<Teffective *>(pabyBuffer)[i + j],
    1689             :                         bHasNoData, nNoDataValue))
    1690             :                 {
    1691           8 :                     reinterpret_cast<Teffective *>(pabyBuffer)[i + j] =
    1692           4 :                         AdjustValue(nNoDataValue,
    1693           4 :                                     panMaskOffsetLsb[j].nRoundUpBitTest);
    1694             :                 }
    1695             :             }
    1696             :         }
    1697             :     }
    1698         145 : }
    1699             : 
    1700         183 : static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand,
    1701             :                        int nBands, uint16_t nSampleFormat,
    1702             :                        uint16_t nBitsPerSample, uint16_t nPlanarConfig,
    1703             :                        const GTiffDataset::MaskOffset *panMaskOffsetLsb,
    1704             :                        bool bHasNoData, double dfNoDataValue)
    1705             : {
    1706         183 :     if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_UINT)
    1707             :     {
    1708          38 :         uint8_t nNoDataValue = 0;
    1709          38 :         if (bHasNoData && GDALIsValueExactAs<uint8_t>(dfNoDataValue))
    1710             :         {
    1711           6 :             nNoDataValue = static_cast<uint8_t>(dfNoDataValue);
    1712             :         }
    1713             :         else
    1714             :         {
    1715          32 :             bHasNoData = false;
    1716             :         }
    1717          38 :         if (nPlanarConfig == PLANARCONFIG_SEPARATE)
    1718             :         {
    1719          25 :             const auto nMask =
    1720          25 :                 static_cast<unsigned>(panMaskOffsetLsb[iBand].nMask);
    1721          25 :             const auto nRoundUpBitTest =
    1722          25 :                 static_cast<unsigned>(panMaskOffsetLsb[iBand].nRoundUpBitTest);
    1723          50 :             for (decltype(nBytes) i = 0; i < nBytes; ++i)
    1724             :             {
    1725          25 :                 if (bHasNoData && pabyBuffer[i] == nNoDataValue)
    1726           3 :                     continue;
    1727             : 
    1728             :                 // Keep 255 in case it is alpha.
    1729          22 :                 if (pabyBuffer[i] != 255)
    1730             :                 {
    1731          21 :                     if (pabyBuffer[i] & nRoundUpBitTest)
    1732           5 :                         pabyBuffer[i] = static_cast<GByte>(
    1733           5 :                             std::min(255U, (pabyBuffer[i] & nMask) +
    1734           5 :                                                (nRoundUpBitTest << 1U)));
    1735             :                     else
    1736          16 :                         pabyBuffer[i] =
    1737          16 :                             static_cast<GByte>(pabyBuffer[i] & nMask);
    1738             : 
    1739             :                     // Make sure that by discarding LSB we don't end up to a
    1740             :                     // value that is no the nodata value
    1741          21 :                     if (bHasNoData && pabyBuffer[i] == nNoDataValue)
    1742           2 :                         pabyBuffer[i] =
    1743           1 :                             AdjustValue(nNoDataValue, nRoundUpBitTest);
    1744             :                 }
    1745             :             }
    1746             :         }
    1747             :         else
    1748             :         {
    1749          26 :             for (decltype(nBytes) i = 0; i < nBytes; i += nBands)
    1750             :             {
    1751          42 :                 for (int j = 0; j < nBands; ++j)
    1752             :                 {
    1753          29 :                     if (bHasNoData && pabyBuffer[i + j] == nNoDataValue)
    1754           2 :                         continue;
    1755             : 
    1756             :                     // Keep 255 in case it is alpha.
    1757          27 :                     if (pabyBuffer[i + j] != 255)
    1758             :                     {
    1759          25 :                         if (pabyBuffer[i + j] &
    1760          25 :                             panMaskOffsetLsb[j].nRoundUpBitTest)
    1761             :                         {
    1762           6 :                             pabyBuffer[i + j] = static_cast<GByte>(std::min(
    1763          12 :                                 255U,
    1764           6 :                                 (pabyBuffer[i + j] &
    1765             :                                  static_cast<unsigned>(
    1766           6 :                                      panMaskOffsetLsb[j].nMask)) +
    1767             :                                     (static_cast<unsigned>(
    1768           6 :                                          panMaskOffsetLsb[j].nRoundUpBitTest)
    1769           6 :                                      << 1U)));
    1770             :                         }
    1771             :                         else
    1772             :                         {
    1773          19 :                             pabyBuffer[i + j] = static_cast<GByte>(
    1774          19 :                                 pabyBuffer[i + j] & panMaskOffsetLsb[j].nMask);
    1775             :                         }
    1776             : 
    1777             :                         // Make sure that by discarding LSB we don't end up to a
    1778             :                         // value that is no the nodata value
    1779          25 :                         if (bHasNoData && pabyBuffer[i + j] == nNoDataValue)
    1780           1 :                             pabyBuffer[i + j] = AdjustValue(
    1781             :                                 nNoDataValue,
    1782           1 :                                 panMaskOffsetLsb[j].nRoundUpBitTest);
    1783             :                     }
    1784             :                 }
    1785             :             }
    1786          38 :         }
    1787             :     }
    1788         145 :     else if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_INT)
    1789             :     {
    1790           0 :         int8_t nNoDataValue = 0;
    1791           0 :         if (bHasNoData && GDALIsValueExactAs<int8_t>(dfNoDataValue))
    1792             :         {
    1793           0 :             nNoDataValue = static_cast<int8_t>(dfNoDataValue);
    1794             :         }
    1795             :         else
    1796             :         {
    1797           0 :             bHasNoData = false;
    1798             :         }
    1799           0 :         DiscardLsbT<int8_t, int8_t>(pabyBuffer, nBytes, iBand, nBands,
    1800             :                                     nPlanarConfig, panMaskOffsetLsb, bHasNoData,
    1801           0 :                                     nNoDataValue);
    1802             :     }
    1803         145 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_INT)
    1804             :     {
    1805          48 :         int16_t nNoDataValue = 0;
    1806          48 :         if (bHasNoData && GDALIsValueExactAs<int16_t>(dfNoDataValue))
    1807             :         {
    1808           6 :             nNoDataValue = static_cast<int16_t>(dfNoDataValue);
    1809             :         }
    1810             :         else
    1811             :         {
    1812          42 :             bHasNoData = false;
    1813             :         }
    1814          48 :         DiscardLsbT<int16_t, int16_t>(pabyBuffer, nBytes, iBand, nBands,
    1815             :                                       nPlanarConfig, panMaskOffsetLsb,
    1816          48 :                                       bHasNoData, nNoDataValue);
    1817             :     }
    1818          97 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_UINT)
    1819             :     {
    1820          33 :         uint16_t nNoDataValue = 0;
    1821          33 :         if (bHasNoData && GDALIsValueExactAs<uint16_t>(dfNoDataValue))
    1822             :         {
    1823           6 :             nNoDataValue = static_cast<uint16_t>(dfNoDataValue);
    1824             :         }
    1825             :         else
    1826             :         {
    1827          27 :             bHasNoData = false;
    1828             :         }
    1829          33 :         DiscardLsbT<uint16_t, uint16_t>(pabyBuffer, nBytes, iBand, nBands,
    1830             :                                         nPlanarConfig, panMaskOffsetLsb,
    1831          33 :                                         bHasNoData, nNoDataValue);
    1832             :     }
    1833          64 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_INT)
    1834             :     {
    1835          13 :         int32_t nNoDataValue = 0;
    1836          13 :         if (bHasNoData && GDALIsValueExactAs<int32_t>(dfNoDataValue))
    1837             :         {
    1838           6 :             nNoDataValue = static_cast<int32_t>(dfNoDataValue);
    1839             :         }
    1840             :         else
    1841             :         {
    1842           7 :             bHasNoData = false;
    1843             :         }
    1844          13 :         DiscardLsbT<int32_t, int32_t>(pabyBuffer, nBytes, iBand, nBands,
    1845             :                                       nPlanarConfig, panMaskOffsetLsb,
    1846          13 :                                       bHasNoData, nNoDataValue);
    1847             :     }
    1848          51 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_UINT)
    1849             :     {
    1850          13 :         uint32_t nNoDataValue = 0;
    1851          13 :         if (bHasNoData && GDALIsValueExactAs<uint32_t>(dfNoDataValue))
    1852             :         {
    1853           6 :             nNoDataValue = static_cast<uint32_t>(dfNoDataValue);
    1854             :         }
    1855             :         else
    1856             :         {
    1857           7 :             bHasNoData = false;
    1858             :         }
    1859          13 :         DiscardLsbT<uint32_t, uint32_t>(pabyBuffer, nBytes, iBand, nBands,
    1860             :                                         nPlanarConfig, panMaskOffsetLsb,
    1861          13 :                                         bHasNoData, nNoDataValue);
    1862             :     }
    1863          38 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_INT)
    1864             :     {
    1865             :         // FIXME: we should not rely on dfNoDataValue when we support native
    1866             :         // data type for nodata
    1867           0 :         int64_t nNoDataValue = 0;
    1868           0 :         if (bHasNoData && GDALIsValueExactAs<int64_t>(dfNoDataValue))
    1869             :         {
    1870           0 :             nNoDataValue = static_cast<int64_t>(dfNoDataValue);
    1871             :         }
    1872             :         else
    1873             :         {
    1874           0 :             bHasNoData = false;
    1875             :         }
    1876           0 :         DiscardLsbT<int64_t, int64_t>(pabyBuffer, nBytes, iBand, nBands,
    1877             :                                       nPlanarConfig, panMaskOffsetLsb,
    1878           0 :                                       bHasNoData, nNoDataValue);
    1879             :     }
    1880          38 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_UINT)
    1881             :     {
    1882             :         // FIXME: we should not rely on dfNoDataValue when we support native
    1883             :         // data type for nodata
    1884           0 :         uint64_t nNoDataValue = 0;
    1885           0 :         if (bHasNoData && GDALIsValueExactAs<uint64_t>(dfNoDataValue))
    1886             :         {
    1887           0 :             nNoDataValue = static_cast<uint64_t>(dfNoDataValue);
    1888             :         }
    1889             :         else
    1890             :         {
    1891           0 :             bHasNoData = false;
    1892             :         }
    1893           0 :         DiscardLsbT<uint64_t, uint64_t>(pabyBuffer, nBytes, iBand, nBands,
    1894             :                                         nPlanarConfig, panMaskOffsetLsb,
    1895           0 :                                         bHasNoData, nNoDataValue);
    1896             :     }
    1897          38 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1898             :     {
    1899          19 :         float fNoDataValue = static_cast<float>(dfNoDataValue);
    1900          19 :         DiscardLsbT<float, uint32_t>(pabyBuffer, nBytes, iBand, nBands,
    1901             :                                      nPlanarConfig, panMaskOffsetLsb,
    1902          19 :                                      bHasNoData, fNoDataValue);
    1903             :     }
    1904          19 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1905             :     {
    1906          19 :         DiscardLsbT<double, uint64_t>(pabyBuffer, nBytes, iBand, nBands,
    1907             :                                       nPlanarConfig, panMaskOffsetLsb,
    1908             :                                       bHasNoData, dfNoDataValue);
    1909             :     }
    1910         183 : }
    1911             : 
    1912         183 : void GTiffDataset::DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes,
    1913             :                               int iBand) const
    1914             : {
    1915         183 :     ::DiscardLsb(pabyBuffer, nBytes, iBand, nBands, m_nSampleFormat,
    1916         183 :                  m_nBitsPerSample, m_nPlanarConfig, m_panMaskOffsetLsb,
    1917         183 :                  m_bNoDataSet, m_dfNoDataValue);
    1918         183 : }
    1919             : 
    1920             : /************************************************************************/
    1921             : /*                  WriteEncodedTileOrStrip()                           */
    1922             : /************************************************************************/
    1923             : 
    1924      155335 : CPLErr GTiffDataset::WriteEncodedTileOrStrip(uint32_t tile_or_strip, void *data,
    1925             :                                              int bPreserveDataBuffer)
    1926             : {
    1927      155335 :     CPLErr eErr = CE_None;
    1928             : 
    1929      155335 :     if (TIFFIsTiled(m_hTIFF))
    1930             :     {
    1931       46815 :         if (!(WriteEncodedTile(tile_or_strip, static_cast<GByte *>(data),
    1932             :                                bPreserveDataBuffer)))
    1933             :         {
    1934          14 :             eErr = CE_Failure;
    1935             :         }
    1936             :     }
    1937             :     else
    1938             :     {
    1939      108520 :         if (!(WriteEncodedStrip(tile_or_strip, static_cast<GByte *>(data),
    1940             :                                 bPreserveDataBuffer)))
    1941             :         {
    1942           8 :             eErr = CE_Failure;
    1943             :         }
    1944             :     }
    1945             : 
    1946      155330 :     return eErr;
    1947             : }
    1948             : 
    1949             : /************************************************************************/
    1950             : /*                           FlushBlockBuf()                            */
    1951             : /************************************************************************/
    1952             : 
    1953        8102 : CPLErr GTiffDataset::FlushBlockBuf()
    1954             : 
    1955             : {
    1956        8102 :     if (m_nLoadedBlock < 0 || !m_bLoadedBlockDirty)
    1957           0 :         return CE_None;
    1958             : 
    1959        8102 :     m_bLoadedBlockDirty = false;
    1960             : 
    1961             :     const CPLErr eErr =
    1962        8102 :         WriteEncodedTileOrStrip(m_nLoadedBlock, m_pabyBlockBuf, true);
    1963        8102 :     if (eErr != CE_None)
    1964             :     {
    1965           0 :         ReportError(CE_Failure, CPLE_AppDefined,
    1966             :                     "WriteEncodedTile/Strip() failed.");
    1967           0 :         m_bWriteError = true;
    1968             :     }
    1969             : 
    1970        8102 :     return eErr;
    1971             : }
    1972             : 
    1973             : /************************************************************************/
    1974             : /*                   GTiffFillStreamableOffsetAndCount()                */
    1975             : /************************************************************************/
    1976             : 
    1977           8 : static void GTiffFillStreamableOffsetAndCount(TIFF *hTIFF, int nSize)
    1978             : {
    1979           8 :     uint32_t nXSize = 0;
    1980           8 :     uint32_t nYSize = 0;
    1981           8 :     TIFFGetField(hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize);
    1982           8 :     TIFFGetField(hTIFF, TIFFTAG_IMAGELENGTH, &nYSize);
    1983           8 :     const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(hTIFF));
    1984             :     const int nBlockCount =
    1985           8 :         bIsTiled ? TIFFNumberOfTiles(hTIFF) : TIFFNumberOfStrips(hTIFF);
    1986             : 
    1987           8 :     toff_t *panOffset = nullptr;
    1988           8 :     TIFFGetField(hTIFF, bIsTiled ? TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS,
    1989             :                  &panOffset);
    1990           8 :     toff_t *panSize = nullptr;
    1991           8 :     TIFFGetField(hTIFF,
    1992             :                  bIsTiled ? TIFFTAG_TILEBYTECOUNTS : TIFFTAG_STRIPBYTECOUNTS,
    1993             :                  &panSize);
    1994           8 :     toff_t nOffset = nSize;
    1995             :     // Trick to avoid clang static analyzer raising false positive about
    1996             :     // divide by zero later.
    1997           8 :     int nBlocksPerBand = 1;
    1998           8 :     uint32_t nRowsPerStrip = 0;
    1999           8 :     if (!bIsTiled)
    2000             :     {
    2001           6 :         TIFFGetField(hTIFF, TIFFTAG_ROWSPERSTRIP, &nRowsPerStrip);
    2002           6 :         if (nRowsPerStrip > static_cast<uint32_t>(nYSize))
    2003           0 :             nRowsPerStrip = nYSize;
    2004           6 :         nBlocksPerBand = DIV_ROUND_UP(nYSize, nRowsPerStrip);
    2005             :     }
    2006        2947 :     for (int i = 0; i < nBlockCount; ++i)
    2007             :     {
    2008             :         GPtrDiff_t cc = bIsTiled
    2009        2939 :                             ? static_cast<GPtrDiff_t>(TIFFTileSize(hTIFF))
    2010        2907 :                             : static_cast<GPtrDiff_t>(TIFFStripSize(hTIFF));
    2011        2939 :         if (!bIsTiled)
    2012             :         {
    2013             :             /* --------------------------------------------------------------------
    2014             :              */
    2015             :             /*      If this is the last strip in the image, and is partial, then
    2016             :              */
    2017             :             /*      we need to trim the number of scanlines written to the */
    2018             :             /*      amount of valid data we have. (#2748) */
    2019             :             /* --------------------------------------------------------------------
    2020             :              */
    2021        2907 :             int nStripWithinBand = i % nBlocksPerBand;
    2022        2907 :             if (nStripWithinBand * nRowsPerStrip > nYSize - nRowsPerStrip)
    2023             :             {
    2024           1 :                 cc = (cc / nRowsPerStrip) *
    2025           1 :                      (nYSize - nStripWithinBand * nRowsPerStrip);
    2026             :             }
    2027             :         }
    2028        2939 :         panOffset[i] = nOffset;
    2029        2939 :         panSize[i] = cc;
    2030        2939 :         nOffset += cc;
    2031             :     }
    2032           8 : }
    2033             : 
    2034             : /************************************************************************/
    2035             : /*                             Crystalize()                             */
    2036             : /*                                                                      */
    2037             : /*      Make sure that the directory information is written out for     */
    2038             : /*      a new file, require before writing any imagery data.            */
    2039             : /************************************************************************/
    2040             : 
    2041     2452600 : void GTiffDataset::Crystalize()
    2042             : 
    2043             : {
    2044     2452600 :     if (m_bCrystalized)
    2045     2449520 :         return;
    2046             : 
    2047             :     // TODO: libtiff writes extended tags in the order they are specified
    2048             :     // and not in increasing order.
    2049        3080 :     WriteMetadata(this, m_hTIFF, true, m_eProfile, m_pszFilename,
    2050        3080 :                   m_papszCreationOptions);
    2051        3063 :     WriteGeoTIFFInfo();
    2052        3063 :     if (m_bNoDataSet)
    2053         256 :         WriteNoDataValue(m_hTIFF, m_dfNoDataValue);
    2054        2807 :     else if (m_bNoDataSetAsInt64)
    2055           1 :         WriteNoDataValue(m_hTIFF, m_nNoDataValueInt64);
    2056        2806 :     else if (m_bNoDataSetAsUInt64)
    2057           1 :         WriteNoDataValue(m_hTIFF, m_nNoDataValueUInt64);
    2058             : 
    2059        3063 :     m_bMetadataChanged = false;
    2060        3063 :     m_bGeoTIFFInfoChanged = false;
    2061        3063 :     m_bNoDataChanged = false;
    2062        3063 :     m_bNeedsRewrite = false;
    2063             : 
    2064        3063 :     m_bCrystalized = true;
    2065             : 
    2066        3063 :     TIFFWriteCheck(m_hTIFF, TIFFIsTiled(m_hTIFF), "GTiffDataset::Crystalize");
    2067             : 
    2068        3063 :     TIFFWriteDirectory(m_hTIFF);
    2069        3063 :     if (m_bStreamingOut)
    2070             :     {
    2071             :         // We need to write twice the directory to be sure that custom
    2072             :         // TIFF tags are correctly sorted and that padding bytes have been
    2073             :         // added.
    2074           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2075           3 :         TIFFWriteDirectory(m_hTIFF);
    2076             : 
    2077           3 :         if (VSIFSeekL(m_fpL, 0, SEEK_END) != 0)
    2078             :         {
    2079           0 :             ReportError(CE_Failure, CPLE_FileIO, "Could not seek");
    2080             :         }
    2081           3 :         const int nSize = static_cast<int>(VSIFTellL(m_fpL));
    2082             : 
    2083           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2084           3 :         GTiffFillStreamableOffsetAndCount(m_hTIFF, nSize);
    2085           3 :         TIFFWriteDirectory(m_hTIFF);
    2086             : 
    2087           3 :         vsi_l_offset nDataLength = 0;
    2088             :         void *pabyBuffer =
    2089           3 :             VSIGetMemFileBuffer(m_pszTmpFilename, &nDataLength, FALSE);
    2090           3 :         if (static_cast<int>(VSIFWriteL(
    2091           3 :                 pabyBuffer, 1, static_cast<int>(nDataLength), m_fpToWrite)) !=
    2092             :             static_cast<int>(nDataLength))
    2093             :         {
    2094           0 :             ReportError(CE_Failure, CPLE_FileIO, "Could not write %d bytes",
    2095             :                         static_cast<int>(nDataLength));
    2096             :         }
    2097             :         // In case of single strip file, there's a libtiff check that would
    2098             :         // issue a warning since the file hasn't the required size.
    2099           3 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2100           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2101           3 :         CPLPopErrorHandler();
    2102             :     }
    2103             :     else
    2104             :     {
    2105        3060 :         const tdir_t nNumberOfDirs = TIFFNumberOfDirectories(m_hTIFF);
    2106        3060 :         if (nNumberOfDirs > 0)
    2107             :         {
    2108        3060 :             TIFFSetDirectory(m_hTIFF, static_cast<tdir_t>(nNumberOfDirs - 1));
    2109             :         }
    2110             :     }
    2111             : 
    2112        3063 :     RestoreVolatileParameters(m_hTIFF);
    2113             : 
    2114        3063 :     m_nDirOffset = TIFFCurrentDirOffset(m_hTIFF);
    2115             : }
    2116             : 
    2117             : /************************************************************************/
    2118             : /*                             FlushCache()                             */
    2119             : /*                                                                      */
    2120             : /*      We override this so we can also flush out local tiff strip      */
    2121             : /*      cache if need be.                                               */
    2122             : /************************************************************************/
    2123             : 
    2124        4118 : CPLErr GTiffDataset::FlushCache(bool bAtClosing)
    2125             : 
    2126             : {
    2127        4118 :     return FlushCacheInternal(bAtClosing, true);
    2128             : }
    2129             : 
    2130       35317 : CPLErr GTiffDataset::FlushCacheInternal(bool bAtClosing, bool bFlushDirectory)
    2131             : {
    2132       35317 :     if (m_bIsFinalized)
    2133           2 :         return CE_None;
    2134             : 
    2135       35315 :     CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
    2136             : 
    2137       35315 :     if (m_bLoadedBlockDirty && m_nLoadedBlock != -1)
    2138             :     {
    2139         243 :         if (FlushBlockBuf() != CE_None)
    2140           0 :             eErr = CE_Failure;
    2141             :     }
    2142             : 
    2143       35315 :     CPLFree(m_pabyBlockBuf);
    2144       35315 :     m_pabyBlockBuf = nullptr;
    2145       35315 :     m_nLoadedBlock = -1;
    2146       35315 :     m_bLoadedBlockDirty = false;
    2147             : 
    2148             :     // Finish compression
    2149       35315 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    2150       33287 :                               : m_poCompressQueue.get();
    2151       35315 :     if (poQueue)
    2152             :     {
    2153         159 :         poQueue->WaitCompletion();
    2154             : 
    2155             :         // Flush remaining data
    2156             :         // cppcheck-suppress constVariableReference
    2157             : 
    2158         159 :         auto &oQueue =
    2159         159 :             m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    2160         228 :         while (!oQueue.empty())
    2161             :         {
    2162          69 :             WaitCompletionForJobIdx(oQueue.front());
    2163             :         }
    2164             :     }
    2165             : 
    2166       35315 :     if (bFlushDirectory && GetAccess() == GA_Update)
    2167             :     {
    2168       10325 :         if (FlushDirectory() != CE_None)
    2169          12 :             eErr = CE_Failure;
    2170             :     }
    2171       35315 :     return eErr;
    2172             : }
    2173             : 
    2174             : /************************************************************************/
    2175             : /*                           FlushDirectory()                           */
    2176             : /************************************************************************/
    2177             : 
    2178       16674 : CPLErr GTiffDataset::FlushDirectory()
    2179             : 
    2180             : {
    2181       16674 :     CPLErr eErr = CE_None;
    2182             : 
    2183         435 :     const auto ReloadAllOtherDirectories = [this]()
    2184             :     {
    2185         213 :         const auto poBaseDS = m_poBaseDS ? m_poBaseDS : this;
    2186         213 :         if (poBaseDS->m_papoOverviewDS)
    2187             :         {
    2188          12 :             for (int i = 0; i < poBaseDS->m_nOverviewCount; ++i)
    2189             :             {
    2190           3 :                 if (poBaseDS->m_papoOverviewDS[i]->m_bCrystalized &&
    2191           3 :                     poBaseDS->m_papoOverviewDS[i] != this)
    2192             :                 {
    2193           3 :                     poBaseDS->m_papoOverviewDS[i]->ReloadDirectory(true);
    2194             :                 }
    2195             : 
    2196           3 :                 if (poBaseDS->m_papoOverviewDS[i]->m_poMaskDS &&
    2197           0 :                     poBaseDS->m_papoOverviewDS[i]->m_poMaskDS != this &&
    2198           0 :                     poBaseDS->m_papoOverviewDS[i]->m_poMaskDS->m_bCrystalized)
    2199             :                 {
    2200           0 :                     poBaseDS->m_papoOverviewDS[i]->m_poMaskDS->ReloadDirectory(
    2201             :                         true);
    2202             :                 }
    2203             :             }
    2204             :         }
    2205         213 :         if (poBaseDS->m_poMaskDS && poBaseDS->m_poMaskDS != this &&
    2206           0 :             poBaseDS->m_poMaskDS->m_bCrystalized)
    2207             :         {
    2208           0 :             poBaseDS->m_poMaskDS->ReloadDirectory(true);
    2209             :         }
    2210         213 :         if (poBaseDS->m_bCrystalized && poBaseDS != this)
    2211             :         {
    2212           6 :             poBaseDS->ReloadDirectory(true);
    2213             :         }
    2214       16887 :     };
    2215             : 
    2216       16674 :     if (eAccess == GA_Update)
    2217             :     {
    2218       11751 :         if (m_bMetadataChanged)
    2219             :         {
    2220         120 :             m_bNeedsRewrite =
    2221         240 :                 WriteMetadata(this, m_hTIFF, true, m_eProfile, m_pszFilename,
    2222         120 :                               m_papszCreationOptions);
    2223         120 :             m_bMetadataChanged = false;
    2224             : 
    2225         120 :             if (m_bForceUnsetRPC)
    2226             :             {
    2227           5 :                 double *padfRPCTag = nullptr;
    2228             :                 uint16_t nCount;
    2229           5 :                 if (TIFFGetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT, &nCount,
    2230           5 :                                  &padfRPCTag))
    2231             :                 {
    2232           3 :                     std::vector<double> zeroes(92);
    2233           3 :                     TIFFSetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT, 92,
    2234             :                                  zeroes.data());
    2235           3 :                     TIFFUnsetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT);
    2236           3 :                     m_bNeedsRewrite = true;
    2237             :                 }
    2238             : 
    2239           5 :                 GDALWriteRPCTXTFile(m_pszFilename, nullptr);
    2240           5 :                 GDALWriteRPBFile(m_pszFilename, nullptr);
    2241             :             }
    2242             :         }
    2243             : 
    2244       11751 :         if (m_bGeoTIFFInfoChanged)
    2245             :         {
    2246         110 :             WriteGeoTIFFInfo();
    2247         110 :             m_bGeoTIFFInfoChanged = false;
    2248             :         }
    2249             : 
    2250       11751 :         if (m_bNoDataChanged)
    2251             :         {
    2252          17 :             if (m_bNoDataSet)
    2253             :             {
    2254          16 :                 WriteNoDataValue(m_hTIFF, m_dfNoDataValue);
    2255             :             }
    2256           1 :             else if (m_bNoDataSetAsInt64)
    2257             :             {
    2258           0 :                 WriteNoDataValue(m_hTIFF, m_nNoDataValueInt64);
    2259             :             }
    2260           1 :             else if (m_bNoDataSetAsUInt64)
    2261             :             {
    2262           0 :                 WriteNoDataValue(m_hTIFF, m_nNoDataValueUInt64);
    2263             :             }
    2264             :             else
    2265             :             {
    2266           1 :                 UnsetNoDataValue(m_hTIFF);
    2267             :             }
    2268          17 :             m_bNeedsRewrite = true;
    2269          17 :             m_bNoDataChanged = false;
    2270             :         }
    2271             : 
    2272       11751 :         if (m_bNeedsRewrite)
    2273             :         {
    2274         235 :             if (!m_bCrystalized)
    2275             :             {
    2276          25 :                 Crystalize();
    2277             :             }
    2278             :             else
    2279             :             {
    2280         210 :                 const TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(m_hTIFF);
    2281             : 
    2282         210 :                 m_nDirOffset = pfnSizeProc(TIFFClientdata(m_hTIFF));
    2283         210 :                 if ((m_nDirOffset % 2) == 1)
    2284          49 :                     ++m_nDirOffset;
    2285             : 
    2286         210 :                 if (TIFFRewriteDirectory(m_hTIFF) == 0)
    2287           0 :                     eErr = CE_Failure;
    2288             : 
    2289         210 :                 TIFFSetSubDirectory(m_hTIFF, m_nDirOffset);
    2290             : 
    2291         210 :                 ReloadAllOtherDirectories();
    2292             : 
    2293         210 :                 if (m_bLayoutIFDSBeforeData && m_bBlockOrderRowMajor &&
    2294           1 :                     m_bLeaderSizeAsUInt4 &&
    2295           1 :                     m_bTrailerRepeatedLast4BytesRepeated &&
    2296           1 :                     !m_bKnownIncompatibleEdition &&
    2297           1 :                     !m_bWriteKnownIncompatibleEdition)
    2298             :                 {
    2299           1 :                     ReportError(CE_Warning, CPLE_AppDefined,
    2300             :                                 "The IFD has been rewritten at the end of "
    2301             :                                 "the file, which breaks COG layout.");
    2302           1 :                     m_bKnownIncompatibleEdition = true;
    2303           1 :                     m_bWriteKnownIncompatibleEdition = true;
    2304             :                 }
    2305             :             }
    2306             : 
    2307         235 :             m_bNeedsRewrite = false;
    2308             :         }
    2309             :     }
    2310             : 
    2311             :     // There are some circumstances in which we can reach this point
    2312             :     // without having made this our directory (SetDirectory()) in which
    2313             :     // case we should not risk a flush.
    2314       28425 :     if (GetAccess() == GA_Update &&
    2315       11751 :         TIFFCurrentDirOffset(m_hTIFF) == m_nDirOffset)
    2316             :     {
    2317       11751 :         const TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(m_hTIFF);
    2318             : 
    2319       11751 :         toff_t nNewDirOffset = pfnSizeProc(TIFFClientdata(m_hTIFF));
    2320       11751 :         if ((nNewDirOffset % 2) == 1)
    2321        2936 :             ++nNewDirOffset;
    2322             : 
    2323       11751 :         if (TIFFFlush(m_hTIFF) == 0)
    2324          12 :             eErr = CE_Failure;
    2325             : 
    2326       11751 :         if (m_nDirOffset != TIFFCurrentDirOffset(m_hTIFF))
    2327             :         {
    2328           3 :             m_nDirOffset = nNewDirOffset;
    2329           3 :             ReloadAllOtherDirectories();
    2330           3 :             CPLDebug("GTiff",
    2331             :                      "directory moved during flush in FlushDirectory()");
    2332             :         }
    2333             :     }
    2334             : 
    2335       16674 :     SetDirectory();
    2336       16674 :     return eErr;
    2337             : }
    2338             : 
    2339             : /************************************************************************/
    2340             : /*                           CleanOverviews()                           */
    2341             : /************************************************************************/
    2342             : 
    2343           3 : CPLErr GTiffDataset::CleanOverviews()
    2344             : 
    2345             : {
    2346           3 :     CPLAssert(!m_poBaseDS);
    2347             : 
    2348           3 :     ScanDirectories();
    2349             : 
    2350           3 :     FlushDirectory();
    2351             : 
    2352             :     /* -------------------------------------------------------------------- */
    2353             :     /*      Cleanup overviews objects, and get offsets to all overview      */
    2354             :     /*      directories.                                                    */
    2355             :     /* -------------------------------------------------------------------- */
    2356           6 :     std::vector<toff_t> anOvDirOffsets;
    2357             : 
    2358           6 :     for (int i = 0; i < m_nOverviewCount; ++i)
    2359             :     {
    2360           3 :         anOvDirOffsets.push_back(m_papoOverviewDS[i]->m_nDirOffset);
    2361           3 :         if (m_papoOverviewDS[i]->m_poMaskDS)
    2362           1 :             anOvDirOffsets.push_back(
    2363           1 :                 m_papoOverviewDS[i]->m_poMaskDS->m_nDirOffset);
    2364           3 :         delete m_papoOverviewDS[i];
    2365             :     }
    2366             : 
    2367             :     /* -------------------------------------------------------------------- */
    2368             :     /*      Loop through all the directories, translating the offsets       */
    2369             :     /*      into indexes we can use with TIFFUnlinkDirectory().             */
    2370             :     /* -------------------------------------------------------------------- */
    2371           6 :     std::vector<uint16_t> anOvDirIndexes;
    2372           3 :     int iThisOffset = 1;
    2373             : 
    2374           3 :     TIFFSetDirectory(m_hTIFF, 0);
    2375             : 
    2376             :     while (true)
    2377             :     {
    2378          20 :         for (toff_t nOffset : anOvDirOffsets)
    2379             :         {
    2380          12 :             if (nOffset == TIFFCurrentDirOffset(m_hTIFF))
    2381             :             {
    2382           4 :                 anOvDirIndexes.push_back(static_cast<uint16_t>(iThisOffset));
    2383             :             }
    2384             :         }
    2385             : 
    2386           8 :         if (TIFFLastDirectory(m_hTIFF))
    2387           3 :             break;
    2388             : 
    2389           5 :         TIFFReadDirectory(m_hTIFF);
    2390           5 :         ++iThisOffset;
    2391           5 :     }
    2392             : 
    2393             :     /* -------------------------------------------------------------------- */
    2394             :     /*      Actually unlink the target directories.  Note that we do        */
    2395             :     /*      this from last to first so as to avoid renumbering any of       */
    2396             :     /*      the earlier directories we need to remove.                      */
    2397             :     /* -------------------------------------------------------------------- */
    2398           7 :     while (!anOvDirIndexes.empty())
    2399             :     {
    2400           4 :         TIFFUnlinkDirectory(m_hTIFF, anOvDirIndexes.back());
    2401           4 :         anOvDirIndexes.pop_back();
    2402             :     }
    2403             : 
    2404           3 :     CPLFree(m_papoOverviewDS);
    2405           3 :     m_nOverviewCount = 0;
    2406           3 :     m_papoOverviewDS = nullptr;
    2407             : 
    2408           3 :     if (m_poMaskDS)
    2409             :     {
    2410           1 :         CPLFree(m_poMaskDS->m_papoOverviewDS);
    2411           1 :         m_poMaskDS->m_nOverviewCount = 0;
    2412           1 :         m_poMaskDS->m_papoOverviewDS = nullptr;
    2413             :     }
    2414             : 
    2415           3 :     if (!SetDirectory())
    2416           0 :         return CE_Failure;
    2417             : 
    2418           3 :     return CE_None;
    2419             : }
    2420             : 
    2421             : /************************************************************************/
    2422             : /*                   RegisterNewOverviewDataset()                       */
    2423             : /************************************************************************/
    2424             : 
    2425         458 : CPLErr GTiffDataset::RegisterNewOverviewDataset(toff_t nOverviewOffset,
    2426             :                                                 int l_nJpegQuality,
    2427             :                                                 CSLConstList papszOptions)
    2428             : {
    2429         458 :     if (m_nOverviewCount == 127)
    2430           0 :         return CE_Failure;
    2431             : 
    2432             :     const auto GetOptionValue =
    2433        2748 :         [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
    2434        5495 :                        const char **ppszKeyUsed = nullptr)
    2435             :     {
    2436        2748 :         const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
    2437        2748 :         if (pszVal)
    2438             :         {
    2439           1 :             if (ppszKeyUsed)
    2440           1 :                 *ppszKeyUsed = pszOptionKey;
    2441           1 :             return pszVal;
    2442             :         }
    2443        2747 :         pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey);
    2444        2747 :         if (pszVal)
    2445             :         {
    2446           0 :             if (ppszKeyUsed)
    2447           0 :                 *ppszKeyUsed = pszConfigOptionKey;
    2448           0 :             return pszVal;
    2449             :         }
    2450        2747 :         pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
    2451        2747 :         if (pszVal && ppszKeyUsed)
    2452          13 :             *ppszKeyUsed = pszConfigOptionKey;
    2453        2747 :         return pszVal;
    2454         458 :     };
    2455             : 
    2456         458 :     int nZLevel = m_nZLevel;
    2457         458 :     if (const char *opt = GetOptionValue("ZLEVEL", "ZLEVEL_OVERVIEW"))
    2458             :     {
    2459           4 :         nZLevel = atoi(opt);
    2460             :     }
    2461             : 
    2462         458 :     int nZSTDLevel = m_nZSTDLevel;
    2463         458 :     if (const char *opt = GetOptionValue("ZSTD_LEVEL", "ZSTD_LEVEL_OVERVIEW"))
    2464             :     {
    2465           4 :         nZSTDLevel = atoi(opt);
    2466             :     }
    2467             : 
    2468         458 :     bool bWebpLossless = m_bWebPLossless;
    2469             :     const char *pszWebPLosslessOverview =
    2470         458 :         GetOptionValue("WEBP_LOSSLESS", "WEBP_LOSSLESS_OVERVIEW");
    2471         458 :     if (pszWebPLosslessOverview)
    2472             :     {
    2473           2 :         bWebpLossless = CPLTestBool(pszWebPLosslessOverview);
    2474             :     }
    2475             : 
    2476         458 :     int nWebpLevel = m_nWebPLevel;
    2477         458 :     const char *pszKeyWebpLevel = "";
    2478         458 :     if (const char *opt = GetOptionValue("WEBP_LEVEL", "WEBP_LEVEL_OVERVIEW",
    2479             :                                          &pszKeyWebpLevel))
    2480             :     {
    2481          14 :         if (pszWebPLosslessOverview == nullptr && m_bWebPLossless)
    2482             :         {
    2483           1 :             CPLDebug("GTiff",
    2484             :                      "%s specified, but not WEBP_LOSSLESS_OVERVIEW. "
    2485             :                      "Assuming WEBP_LOSSLESS_OVERVIEW=NO",
    2486             :                      pszKeyWebpLevel);
    2487           1 :             bWebpLossless = false;
    2488             :         }
    2489          13 :         else if (bWebpLossless)
    2490             :         {
    2491           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2492             :                      "%s is specified, but WEBP_LOSSLESS_OVERVIEW=YES. "
    2493             :                      "%s will be ignored.",
    2494             :                      pszKeyWebpLevel, pszKeyWebpLevel);
    2495             :         }
    2496          14 :         nWebpLevel = atoi(opt);
    2497             :     }
    2498             : 
    2499         458 :     double dfMaxZError = m_dfMaxZErrorOverview;
    2500         458 :     if (const char *opt = GetOptionValue("MAX_Z_ERROR", "MAX_Z_ERROR_OVERVIEW"))
    2501             :     {
    2502          20 :         dfMaxZError = CPLAtof(opt);
    2503             :     }
    2504             : 
    2505         458 :     GTiffDataset *poODS = new GTiffDataset();
    2506         458 :     poODS->ShareLockWithParentDataset(this);
    2507         458 :     poODS->m_pszFilename = CPLStrdup(m_pszFilename);
    2508         458 :     const char *pszSparseOK = GetOptionValue("SPARSE_OK", "SPARSE_OK_OVERVIEW");
    2509         458 :     if (pszSparseOK && CPLTestBool(pszSparseOK))
    2510             :     {
    2511           1 :         poODS->m_bWriteEmptyTiles = false;
    2512           1 :         poODS->m_bFillEmptyTilesAtClosing = false;
    2513             :     }
    2514             :     else
    2515             :     {
    2516         457 :         poODS->m_bWriteEmptyTiles = m_bWriteEmptyTiles;
    2517         457 :         poODS->m_bFillEmptyTilesAtClosing = m_bFillEmptyTilesAtClosing;
    2518             :     }
    2519         458 :     poODS->m_nJpegQuality = static_cast<signed char>(l_nJpegQuality);
    2520         458 :     poODS->m_nWebPLevel = static_cast<signed char>(nWebpLevel);
    2521         458 :     poODS->m_nZLevel = static_cast<signed char>(nZLevel);
    2522         458 :     poODS->m_nLZMAPreset = m_nLZMAPreset;
    2523         458 :     poODS->m_nZSTDLevel = static_cast<signed char>(nZSTDLevel);
    2524         458 :     poODS->m_bWebPLossless = bWebpLossless;
    2525         458 :     poODS->m_nJpegTablesMode = m_nJpegTablesMode;
    2526         458 :     poODS->m_dfMaxZError = dfMaxZError;
    2527         458 :     poODS->m_dfMaxZErrorOverview = dfMaxZError;
    2528         458 :     memcpy(poODS->m_anLercAddCompressionAndVersion,
    2529         458 :            m_anLercAddCompressionAndVersion,
    2530             :            sizeof(m_anLercAddCompressionAndVersion));
    2531             : #ifdef HAVE_JXL
    2532         458 :     poODS->m_bJXLLossless = m_bJXLLossless;
    2533         458 :     poODS->m_fJXLDistance = m_fJXLDistance;
    2534         458 :     poODS->m_fJXLAlphaDistance = m_fJXLAlphaDistance;
    2535         458 :     poODS->m_nJXLEffort = m_nJXLEffort;
    2536             : #endif
    2537             : 
    2538         458 :     if (poODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nOverviewOffset,
    2539         458 :                           GA_Update) != CE_None)
    2540             :     {
    2541           0 :         delete poODS;
    2542           0 :         return CE_Failure;
    2543             :     }
    2544             : 
    2545             :     // Assign color interpretation from main dataset
    2546         458 :     const int l_nBands = GetRasterCount();
    2547        1367 :     for (int i = 1; i <= l_nBands; i++)
    2548             :     {
    2549         909 :         auto poBand = dynamic_cast<GTiffRasterBand *>(poODS->GetRasterBand(i));
    2550         909 :         if (poBand)
    2551         909 :             poBand->m_eBandInterp = GetRasterBand(i)->GetColorInterpretation();
    2552             :     }
    2553             : 
    2554             :     // Do that now that m_nCompression is set
    2555         458 :     poODS->RestoreVolatileParameters(poODS->m_hTIFF);
    2556             : 
    2557         458 :     ++m_nOverviewCount;
    2558         458 :     m_papoOverviewDS = static_cast<GTiffDataset **>(
    2559         458 :         CPLRealloc(m_papoOverviewDS, m_nOverviewCount * (sizeof(void *))));
    2560         458 :     m_papoOverviewDS[m_nOverviewCount - 1] = poODS;
    2561         458 :     poODS->m_poBaseDS = this;
    2562         458 :     poODS->m_bIsOverview = true;
    2563         458 :     return CE_None;
    2564             : }
    2565             : 
    2566             : /************************************************************************/
    2567             : /*                     CreateTIFFColorTable()                           */
    2568             : /************************************************************************/
    2569             : 
    2570          12 : static void CreateTIFFColorTable(
    2571             :     GDALColorTable *poColorTable, int nBits, int nColorTableMultiplier,
    2572             :     std::vector<unsigned short> &anTRed, std::vector<unsigned short> &anTGreen,
    2573             :     std::vector<unsigned short> &anTBlue, unsigned short *&panRed,
    2574             :     unsigned short *&panGreen, unsigned short *&panBlue)
    2575             : {
    2576             :     int nColors;
    2577             : 
    2578          12 :     if (nBits == 8)
    2579          12 :         nColors = 256;
    2580           0 :     else if (nBits < 8)
    2581           0 :         nColors = 1 << nBits;
    2582             :     else
    2583           0 :         nColors = 65536;
    2584             : 
    2585          12 :     anTRed.resize(nColors, 0);
    2586          12 :     anTGreen.resize(nColors, 0);
    2587          12 :     anTBlue.resize(nColors, 0);
    2588             : 
    2589        3084 :     for (int iColor = 0; iColor < nColors; ++iColor)
    2590             :     {
    2591        3072 :         if (iColor < poColorTable->GetColorEntryCount())
    2592             :         {
    2593             :             GDALColorEntry sRGB;
    2594             : 
    2595        3072 :             poColorTable->GetColorEntryAsRGB(iColor, &sRGB);
    2596             : 
    2597        3072 :             anTRed[iColor] = GTiffDataset::ClampCTEntry(iColor, 1, sRGB.c1,
    2598             :                                                         nColorTableMultiplier);
    2599        3072 :             anTGreen[iColor] = GTiffDataset::ClampCTEntry(
    2600        3072 :                 iColor, 2, sRGB.c2, nColorTableMultiplier);
    2601        3072 :             anTBlue[iColor] = GTiffDataset::ClampCTEntry(iColor, 3, sRGB.c3,
    2602             :                                                          nColorTableMultiplier);
    2603             :         }
    2604             :         else
    2605             :         {
    2606           0 :             anTRed[iColor] = 0;
    2607           0 :             anTGreen[iColor] = 0;
    2608           0 :             anTBlue[iColor] = 0;
    2609             :         }
    2610             :     }
    2611             : 
    2612          12 :     panRed = &(anTRed[0]);
    2613          12 :     panGreen = &(anTGreen[0]);
    2614          12 :     panBlue = &(anTBlue[0]);
    2615          12 : }
    2616             : 
    2617             : /************************************************************************/
    2618             : /*                        GetOverviewParameters()                       */
    2619             : /************************************************************************/
    2620             : 
    2621         283 : bool GTiffDataset::GetOverviewParameters(
    2622             :     int &nCompression, uint16_t &nPlanarConfig, uint16_t &nPredictor,
    2623             :     uint16_t &nPhotometric, int &nOvrJpegQuality, std::string &osNoData,
    2624             :     uint16_t *&panExtraSampleValues, uint16_t &nExtraSamples,
    2625             :     CSLConstList papszOptions) const
    2626             : {
    2627             :     const auto GetOptionValue =
    2628         934 :         [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
    2629        1864 :                        const char **ppszKeyUsed = nullptr)
    2630             :     {
    2631         934 :         const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
    2632         934 :         if (pszVal)
    2633             :         {
    2634           4 :             if (ppszKeyUsed)
    2635           4 :                 *ppszKeyUsed = pszOptionKey;
    2636           4 :             return pszVal;
    2637             :         }
    2638         930 :         pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey);
    2639         930 :         if (pszVal)
    2640             :         {
    2641           4 :             if (ppszKeyUsed)
    2642           4 :                 *ppszKeyUsed = pszConfigOptionKey;
    2643           4 :             return pszVal;
    2644             :         }
    2645         926 :         pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
    2646         926 :         if (pszVal && ppszKeyUsed)
    2647          46 :             *ppszKeyUsed = pszConfigOptionKey;
    2648         926 :         return pszVal;
    2649         283 :     };
    2650             : 
    2651             :     /* -------------------------------------------------------------------- */
    2652             :     /*      Determine compression method.                                   */
    2653             :     /* -------------------------------------------------------------------- */
    2654         283 :     nCompression = m_nCompression;
    2655         283 :     const char *pszOptionKey = "";
    2656             :     const char *pszCompressValue =
    2657         283 :         GetOptionValue("COMPRESS", "COMPRESS_OVERVIEW", &pszOptionKey);
    2658         283 :     if (pszCompressValue != nullptr)
    2659             :     {
    2660          44 :         nCompression =
    2661          44 :             GTIFFGetCompressionMethod(pszCompressValue, pszOptionKey);
    2662          44 :         if (nCompression < 0)
    2663             :         {
    2664           0 :             nCompression = m_nCompression;
    2665             :         }
    2666             :     }
    2667             : 
    2668             :     /* -------------------------------------------------------------------- */
    2669             :     /*      Determine planar configuration.                                 */
    2670             :     /* -------------------------------------------------------------------- */
    2671         283 :     nPlanarConfig = m_nPlanarConfig;
    2672         283 :     if (nCompression == COMPRESSION_WEBP)
    2673             :     {
    2674          11 :         nPlanarConfig = PLANARCONFIG_CONTIG;
    2675             :     }
    2676             :     const char *pszInterleave =
    2677         283 :         GetOptionValue("INTERLEAVE", "INTERLEAVE_OVERVIEW", &pszOptionKey);
    2678         283 :     if (pszInterleave != nullptr && pszInterleave[0] != '\0')
    2679             :     {
    2680           2 :         if (EQUAL(pszInterleave, "PIXEL"))
    2681           1 :             nPlanarConfig = PLANARCONFIG_CONTIG;
    2682           1 :         else if (EQUAL(pszInterleave, "BAND"))
    2683           1 :             nPlanarConfig = PLANARCONFIG_SEPARATE;
    2684             :         else
    2685             :         {
    2686           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2687             :                      "%s=%s unsupported, "
    2688             :                      "value must be PIXEL or BAND. ignoring",
    2689             :                      pszOptionKey, pszInterleave);
    2690             :         }
    2691             :     }
    2692             : 
    2693             :     /* -------------------------------------------------------------------- */
    2694             :     /*      Determine predictor tag                                         */
    2695             :     /* -------------------------------------------------------------------- */
    2696         283 :     nPredictor = PREDICTOR_NONE;
    2697         283 :     if (GTIFFSupportsPredictor(nCompression))
    2698             :     {
    2699             :         const char *pszPredictor =
    2700          58 :             GetOptionValue("PREDICTOR", "PREDICTOR_OVERVIEW");
    2701          58 :         if (pszPredictor != nullptr)
    2702             :         {
    2703           1 :             nPredictor = static_cast<uint16_t>(atoi(pszPredictor));
    2704             :         }
    2705          57 :         else if (GTIFFSupportsPredictor(m_nCompression))
    2706          57 :             TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &nPredictor);
    2707             :     }
    2708             : 
    2709             :     /* -------------------------------------------------------------------- */
    2710             :     /*      Determine photometric tag                                       */
    2711             :     /* -------------------------------------------------------------------- */
    2712         283 :     nPhotometric = m_nPhotometric;
    2713             :     const char *pszPhotometric =
    2714         283 :         GetOptionValue("PHOTOMETRIC", "PHOTOMETRIC_OVERVIEW", &pszOptionKey);
    2715         283 :     if (!GTIFFUpdatePhotometric(pszPhotometric, pszOptionKey, nCompression,
    2716         283 :                                 pszInterleave, nBands, nPhotometric,
    2717             :                                 nPlanarConfig))
    2718             :     {
    2719           0 :         return false;
    2720             :     }
    2721             : 
    2722             :     /* -------------------------------------------------------------------- */
    2723             :     /*      Determine JPEG quality                                          */
    2724             :     /* -------------------------------------------------------------------- */
    2725         283 :     nOvrJpegQuality = m_nJpegQuality;
    2726         283 :     if (nCompression == COMPRESSION_JPEG)
    2727             :     {
    2728             :         const char *pszJPEGQuality =
    2729          27 :             GetOptionValue("JPEG_QUALITY", "JPEG_QUALITY_OVERVIEW");
    2730          27 :         if (pszJPEGQuality != nullptr)
    2731             :         {
    2732           9 :             nOvrJpegQuality = atoi(pszJPEGQuality);
    2733             :         }
    2734             :     }
    2735             : 
    2736             :     /* -------------------------------------------------------------------- */
    2737             :     /*      Set nodata.                                                     */
    2738             :     /* -------------------------------------------------------------------- */
    2739         283 :     if (m_bNoDataSet)
    2740             :     {
    2741          16 :         osNoData = GTiffFormatGDALNoDataTagValue(m_dfNoDataValue);
    2742             :     }
    2743             : 
    2744             :     /* -------------------------------------------------------------------- */
    2745             :     /*      Fetch extra sample tag                                          */
    2746             :     /* -------------------------------------------------------------------- */
    2747         283 :     panExtraSampleValues = nullptr;
    2748         283 :     nExtraSamples = 0;
    2749         283 :     if (TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples,
    2750         283 :                      &panExtraSampleValues))
    2751             :     {
    2752             :         uint16_t *panExtraSampleValuesNew = static_cast<uint16_t *>(
    2753          36 :             CPLMalloc(nExtraSamples * sizeof(uint16_t)));
    2754          36 :         memcpy(panExtraSampleValuesNew, panExtraSampleValues,
    2755          36 :                nExtraSamples * sizeof(uint16_t));
    2756          36 :         panExtraSampleValues = panExtraSampleValuesNew;
    2757             :     }
    2758             :     else
    2759             :     {
    2760         247 :         panExtraSampleValues = nullptr;
    2761         247 :         nExtraSamples = 0;
    2762             :     }
    2763             : 
    2764         283 :     return true;
    2765             : }
    2766             : 
    2767             : /************************************************************************/
    2768             : /*                  CreateOverviewsFromSrcOverviews()                   */
    2769             : /************************************************************************/
    2770             : 
    2771             : // If poOvrDS is not null, it is used and poSrcDS is ignored.
    2772             : 
    2773          57 : CPLErr GTiffDataset::CreateOverviewsFromSrcOverviews(GDALDataset *poSrcDS,
    2774             :                                                      GDALDataset *poOvrDS,
    2775             :                                                      int nOverviews)
    2776             : {
    2777          57 :     CPLAssert(poSrcDS->GetRasterCount() != 0);
    2778          57 :     CPLAssert(m_nOverviewCount == 0);
    2779             : 
    2780          57 :     ScanDirectories();
    2781             : 
    2782          57 :     FlushDirectory();
    2783             : 
    2784          57 :     int nOvBitsPerSample = m_nBitsPerSample;
    2785             : 
    2786             :     /* -------------------------------------------------------------------- */
    2787             :     /*      Do we need some metadata for the overviews?                     */
    2788             :     /* -------------------------------------------------------------------- */
    2789         114 :     CPLString osMetadata;
    2790             : 
    2791          57 :     GTIFFBuildOverviewMetadata("NONE", this, false, osMetadata);
    2792             : 
    2793             :     int nCompression;
    2794             :     uint16_t nPlanarConfig;
    2795             :     uint16_t nPredictor;
    2796             :     uint16_t nPhotometric;
    2797             :     int nOvrJpegQuality;
    2798         114 :     std::string osNoData;
    2799          57 :     uint16_t *panExtraSampleValues = nullptr;
    2800          57 :     uint16_t nExtraSamples = 0;
    2801          57 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    2802             :                                nPhotometric, nOvrJpegQuality, osNoData,
    2803             :                                panExtraSampleValues, nExtraSamples,
    2804             :                                /*papszOptions=*/nullptr))
    2805             :     {
    2806           0 :         return CE_Failure;
    2807             :     }
    2808             : 
    2809             :     /* -------------------------------------------------------------------- */
    2810             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    2811             :     /* -------------------------------------------------------------------- */
    2812         114 :     std::vector<unsigned short> anTRed;
    2813         114 :     std::vector<unsigned short> anTGreen;
    2814          57 :     std::vector<unsigned short> anTBlue;
    2815          57 :     unsigned short *panRed = nullptr;
    2816          57 :     unsigned short *panGreen = nullptr;
    2817          57 :     unsigned short *panBlue = nullptr;
    2818             : 
    2819          57 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    2820             :     {
    2821           0 :         if (m_nColorTableMultiplier == 0)
    2822           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    2823             : 
    2824           0 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    2825             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    2826             :                              panRed, panGreen, panBlue);
    2827             :     }
    2828             : 
    2829          57 :     int nOvrBlockXSize = 0;
    2830          57 :     int nOvrBlockYSize = 0;
    2831          57 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    2832             :                               &nOvrBlockXSize, &nOvrBlockYSize);
    2833             : 
    2834          57 :     CPLErr eErr = CE_None;
    2835             : 
    2836         169 :     for (int i = 0; i < nOverviews && eErr == CE_None; ++i)
    2837             :     {
    2838             :         GDALRasterBand *poOvrBand =
    2839         149 :             poOvrDS ? ((i == 0) ? poOvrDS->GetRasterBand(1)
    2840          37 :                                 : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    2841          44 :                     : poSrcDS->GetRasterBand(1)->GetOverview(i);
    2842             : 
    2843         112 :         int nOXSize = poOvrBand->GetXSize();
    2844         112 :         int nOYSize = poOvrBand->GetYSize();
    2845             : 
    2846         224 :         toff_t nOverviewOffset = GTIFFWriteDirectory(
    2847             :             m_hTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize, nOvBitsPerSample,
    2848         112 :             nPlanarConfig, m_nSamplesPerPixel, nOvrBlockXSize, nOvrBlockYSize,
    2849         112 :             TRUE, nCompression, nPhotometric, m_nSampleFormat, nPredictor,
    2850             :             panRed, panGreen, panBlue, nExtraSamples, panExtraSampleValues,
    2851             :             osMetadata,
    2852         112 :             nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality) : nullptr,
    2853         112 :             CPLSPrintf("%d", m_nJpegTablesMode),
    2854           2 :             osNoData.empty() ? nullptr : osNoData.c_str(),
    2855         112 :             m_anLercAddCompressionAndVersion, m_bWriteCOGLayout);
    2856             : 
    2857         112 :         if (nOverviewOffset == 0)
    2858           0 :             eErr = CE_Failure;
    2859             :         else
    2860         112 :             eErr = RegisterNewOverviewDataset(nOverviewOffset, nOvrJpegQuality,
    2861             :                                               nullptr);
    2862             :     }
    2863             : 
    2864             :     // For directory reloading, so that the chaining to the next directory is
    2865             :     // reloaded, as well as compression parameters.
    2866          57 :     ReloadDirectory();
    2867             : 
    2868          57 :     CPLFree(panExtraSampleValues);
    2869          57 :     panExtraSampleValues = nullptr;
    2870             : 
    2871          57 :     return eErr;
    2872             : }
    2873             : 
    2874             : /************************************************************************/
    2875             : /*                       CreateInternalMaskOverviews()                  */
    2876             : /************************************************************************/
    2877             : 
    2878         240 : CPLErr GTiffDataset::CreateInternalMaskOverviews(int nOvrBlockXSize,
    2879             :                                                  int nOvrBlockYSize)
    2880             : {
    2881         240 :     ScanDirectories();
    2882             : 
    2883             :     /* -------------------------------------------------------------------- */
    2884             :     /*      Create overviews for the mask.                                  */
    2885             :     /* -------------------------------------------------------------------- */
    2886         240 :     CPLErr eErr = CE_None;
    2887             : 
    2888         240 :     if (m_poMaskDS != nullptr && m_poMaskDS->GetRasterCount() == 1)
    2889             :     {
    2890             :         int nMaskOvrCompression;
    2891          31 :         if (strstr(GDALGetMetadataItem(GDALGetDriverByName("GTiff"),
    2892             :                                        GDAL_DMD_CREATIONOPTIONLIST, nullptr),
    2893          31 :                    "<Value>DEFLATE</Value>") != nullptr)
    2894          31 :             nMaskOvrCompression = COMPRESSION_ADOBE_DEFLATE;
    2895             :         else
    2896           0 :             nMaskOvrCompression = COMPRESSION_PACKBITS;
    2897             : 
    2898          91 :         for (int i = 0; i < m_nOverviewCount; ++i)
    2899             :         {
    2900          60 :             if (m_papoOverviewDS[i]->m_poMaskDS == nullptr)
    2901             :             {
    2902          96 :                 const toff_t nOverviewOffset = GTIFFWriteDirectory(
    2903             :                     m_hTIFF, FILETYPE_REDUCEDIMAGE | FILETYPE_MASK,
    2904          48 :                     m_papoOverviewDS[i]->nRasterXSize,
    2905          48 :                     m_papoOverviewDS[i]->nRasterYSize, 1, PLANARCONFIG_CONTIG,
    2906             :                     1, nOvrBlockXSize, nOvrBlockYSize, TRUE,
    2907             :                     nMaskOvrCompression, PHOTOMETRIC_MASK, SAMPLEFORMAT_UINT,
    2908             :                     PREDICTOR_NONE, nullptr, nullptr, nullptr, 0, nullptr, "",
    2909          48 :                     nullptr, nullptr, nullptr, nullptr, m_bWriteCOGLayout);
    2910             : 
    2911          48 :                 if (nOverviewOffset == 0)
    2912             :                 {
    2913           0 :                     eErr = CE_Failure;
    2914           0 :                     continue;
    2915             :                 }
    2916             : 
    2917          48 :                 GTiffDataset *poODS = new GTiffDataset();
    2918          48 :                 poODS->ShareLockWithParentDataset(this);
    2919          48 :                 poODS->m_pszFilename = CPLStrdup(m_pszFilename);
    2920          48 :                 if (poODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF),
    2921          48 :                                       nOverviewOffset, GA_Update) != CE_None)
    2922             :                 {
    2923           0 :                     delete poODS;
    2924           0 :                     eErr = CE_Failure;
    2925             :                 }
    2926             :                 else
    2927             :                 {
    2928          48 :                     poODS->m_bPromoteTo8Bits = CPLTestBool(CPLGetConfigOption(
    2929             :                         "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
    2930          48 :                     poODS->m_poBaseDS = this;
    2931          48 :                     poODS->m_poImageryDS = m_papoOverviewDS[i];
    2932          48 :                     m_papoOverviewDS[i]->m_poMaskDS = poODS;
    2933          48 :                     ++m_poMaskDS->m_nOverviewCount;
    2934          96 :                     m_poMaskDS->m_papoOverviewDS =
    2935          96 :                         static_cast<GTiffDataset **>(CPLRealloc(
    2936          48 :                             m_poMaskDS->m_papoOverviewDS,
    2937          48 :                             m_poMaskDS->m_nOverviewCount * (sizeof(void *))));
    2938          48 :                     m_poMaskDS
    2939          48 :                         ->m_papoOverviewDS[m_poMaskDS->m_nOverviewCount - 1] =
    2940             :                         poODS;
    2941             :                 }
    2942             :             }
    2943             :         }
    2944             :     }
    2945             : 
    2946         240 :     ReloadDirectory();
    2947             : 
    2948         240 :     return eErr;
    2949             : }
    2950             : 
    2951             : /************************************************************************/
    2952             : /*                          IBuildOverviews()                           */
    2953             : /************************************************************************/
    2954             : 
    2955         372 : CPLErr GTiffDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
    2956             :                                      const int *panOverviewList, int nBandsIn,
    2957             :                                      const int *panBandList,
    2958             :                                      GDALProgressFunc pfnProgress,
    2959             :                                      void *pProgressData,
    2960             :                                      CSLConstList papszOptions)
    2961             : 
    2962             : {
    2963         372 :     ScanDirectories();
    2964             : 
    2965             :     // Make implicit JPEG overviews invisible, but do not destroy
    2966             :     // them in case they are already used (not sure that the client
    2967             :     // has the right to do that.  Behavior maybe undefined in GDAL API.
    2968         372 :     m_nJPEGOverviewCount = 0;
    2969             : 
    2970             :     /* -------------------------------------------------------------------- */
    2971             :     /*      If RRD or external OVR overviews requested, then invoke         */
    2972             :     /*      generic handling.                                               */
    2973             :     /* -------------------------------------------------------------------- */
    2974         372 :     bool bUseGenericHandling = false;
    2975             : 
    2976         372 :     if (CPLTestBool(CSLFetchNameValueDef(
    2977         742 :             papszOptions, "USE_RRD", CPLGetConfigOption("USE_RRD", "NO"))) ||
    2978         370 :         CPLTestBool(
    2979             :             CSLFetchNameValueDef(papszOptions, "TIFF_USE_OVR",
    2980             :                                  CPLGetConfigOption("TIFF_USE_OVR", "NO"))))
    2981             :     {
    2982           2 :         bUseGenericHandling = true;
    2983             :     }
    2984             : 
    2985             :     /* -------------------------------------------------------------------- */
    2986             :     /*      If we don't have read access, then create the overviews         */
    2987             :     /*      externally.                                                     */
    2988             :     /* -------------------------------------------------------------------- */
    2989         372 :     if (GetAccess() != GA_Update)
    2990             :     {
    2991         138 :         CPLDebug("GTiff", "File open for read-only accessing, "
    2992             :                           "creating overviews externally.");
    2993             : 
    2994         138 :         bUseGenericHandling = true;
    2995             :     }
    2996             : 
    2997         372 :     if (bUseGenericHandling)
    2998             :     {
    2999         140 :         if (m_nOverviewCount != 0)
    3000             :         {
    3001           0 :             ReportError(CE_Failure, CPLE_NotSupported,
    3002             :                         "Cannot add external overviews when there are already "
    3003             :                         "internal overviews");
    3004           0 :             return CE_Failure;
    3005             :         }
    3006             : 
    3007         140 :         CPLStringList aosOptions(papszOptions);
    3008         140 :         if (!m_bWriteEmptyTiles)
    3009             :         {
    3010           1 :             aosOptions.SetNameValue("SPARSE_OK", "YES");
    3011             :         }
    3012             : 
    3013         140 :         CPLErr eErr = GDALDataset::IBuildOverviews(
    3014             :             pszResampling, nOverviews, panOverviewList, nBandsIn, panBandList,
    3015         140 :             pfnProgress, pProgressData, aosOptions);
    3016         140 :         if (eErr == CE_None && m_poMaskDS)
    3017             :         {
    3018           1 :             ReportError(
    3019             :                 CE_Warning, CPLE_NotSupported,
    3020             :                 "Building external overviews whereas there is an internal "
    3021             :                 "mask is not fully supported. "
    3022             :                 "The overviews of the non-mask bands will be created, "
    3023             :                 "but not the overviews of the mask band.");
    3024             :         }
    3025         140 :         return eErr;
    3026             :     }
    3027             : 
    3028             :     /* -------------------------------------------------------------------- */
    3029             :     /*      Our TIFF overview support currently only works safely if all    */
    3030             :     /*      bands are handled at the same time.                             */
    3031             :     /* -------------------------------------------------------------------- */
    3032         232 :     if (nBandsIn != GetRasterCount())
    3033             :     {
    3034           0 :         ReportError(CE_Failure, CPLE_NotSupported,
    3035             :                     "Generation of overviews in TIFF currently only "
    3036             :                     "supported when operating on all bands.  "
    3037             :                     "Operation failed.");
    3038           0 :         return CE_Failure;
    3039             :     }
    3040             : 
    3041             :     /* -------------------------------------------------------------------- */
    3042             :     /*      If zero overviews were requested, we need to clear all          */
    3043             :     /*      existing overviews.                                             */
    3044             :     /* -------------------------------------------------------------------- */
    3045         232 :     if (nOverviews == 0)
    3046             :     {
    3047           6 :         if (m_nOverviewCount == 0)
    3048           3 :             return GDALDataset::IBuildOverviews(
    3049             :                 pszResampling, nOverviews, panOverviewList, nBandsIn,
    3050           3 :                 panBandList, pfnProgress, pProgressData, papszOptions);
    3051             : 
    3052           3 :         return CleanOverviews();
    3053             :     }
    3054             : 
    3055         226 :     CPLErr eErr = CE_None;
    3056             : 
    3057             :     /* -------------------------------------------------------------------- */
    3058             :     /*      Initialize progress counter.                                    */
    3059             :     /* -------------------------------------------------------------------- */
    3060         226 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    3061             :     {
    3062           0 :         ReportError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    3063           0 :         return CE_Failure;
    3064             :     }
    3065             : 
    3066         226 :     FlushDirectory();
    3067             : 
    3068             :     /* -------------------------------------------------------------------- */
    3069             :     /*      If we are averaging bit data to grayscale we need to create     */
    3070             :     /*      8bit overviews.                                                 */
    3071             :     /* -------------------------------------------------------------------- */
    3072         226 :     int nOvBitsPerSample = m_nBitsPerSample;
    3073             : 
    3074         226 :     if (STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
    3075           2 :         nOvBitsPerSample = 8;
    3076             : 
    3077             :     /* -------------------------------------------------------------------- */
    3078             :     /*      Do we need some metadata for the overviews?                     */
    3079             :     /* -------------------------------------------------------------------- */
    3080         452 :     CPLString osMetadata;
    3081             : 
    3082         226 :     const bool bIsForMaskBand = nBands == 1 && GetRasterBand(1)->IsMaskBand();
    3083         226 :     GTIFFBuildOverviewMetadata(pszResampling, this, bIsForMaskBand, osMetadata);
    3084             : 
    3085             :     int nCompression;
    3086             :     uint16_t nPlanarConfig;
    3087             :     uint16_t nPredictor;
    3088             :     uint16_t nPhotometric;
    3089             :     int nOvrJpegQuality;
    3090         452 :     std::string osNoData;
    3091         226 :     uint16_t *panExtraSampleValues = nullptr;
    3092         226 :     uint16_t nExtraSamples = 0;
    3093         226 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    3094             :                                nPhotometric, nOvrJpegQuality, osNoData,
    3095             :                                panExtraSampleValues, nExtraSamples,
    3096             :                                papszOptions))
    3097             :     {
    3098           0 :         return CE_Failure;
    3099             :     }
    3100             : 
    3101             :     /* -------------------------------------------------------------------- */
    3102             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    3103             :     /* -------------------------------------------------------------------- */
    3104         452 :     std::vector<unsigned short> anTRed;
    3105         452 :     std::vector<unsigned short> anTGreen;
    3106         452 :     std::vector<unsigned short> anTBlue;
    3107         226 :     unsigned short *panRed = nullptr;
    3108         226 :     unsigned short *panGreen = nullptr;
    3109         226 :     unsigned short *panBlue = nullptr;
    3110             : 
    3111         226 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    3112             :     {
    3113          12 :         if (m_nColorTableMultiplier == 0)
    3114           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    3115             : 
    3116          12 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    3117             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    3118             :                              panRed, panGreen, panBlue);
    3119             :     }
    3120             : 
    3121             :     /* -------------------------------------------------------------------- */
    3122             :     /*      Establish which of the overview levels we already have, and     */
    3123             :     /*      which are new.  We assume that band 1 of the file is            */
    3124             :     /*      representative.                                                 */
    3125             :     /* -------------------------------------------------------------------- */
    3126         226 :     int nOvrBlockXSize = 0;
    3127         226 :     int nOvrBlockYSize = 0;
    3128         226 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    3129             :                               &nOvrBlockXSize, &nOvrBlockYSize);
    3130         452 :     std::vector<bool> abRequireNewOverview(nOverviews, true);
    3131         626 :     for (int i = 0; i < nOverviews && eErr == CE_None; ++i)
    3132             :     {
    3133         720 :         for (int j = 0; j < m_nOverviewCount && eErr == CE_None; ++j)
    3134             :         {
    3135         374 :             GTiffDataset *poODS = m_papoOverviewDS[j];
    3136             : 
    3137             :             const int nOvFactor =
    3138         374 :                 GDALComputeOvFactor(poODS->GetRasterXSize(), GetRasterXSize(),
    3139             :                                     poODS->GetRasterYSize(), GetRasterYSize());
    3140             : 
    3141             :             // If we already have a 1x1 overview and this new one would result
    3142             :             // in it too, then don't create it.
    3143         413 :             if (poODS->GetRasterXSize() == 1 && poODS->GetRasterYSize() == 1 &&
    3144          21 :                 (GetRasterXSize() + panOverviewList[i] - 1) /
    3145          21 :                         panOverviewList[i] ==
    3146         413 :                     1 &&
    3147          21 :                 (GetRasterYSize() + panOverviewList[i] - 1) /
    3148          21 :                         panOverviewList[i] ==
    3149             :                     1)
    3150             :             {
    3151          21 :                 abRequireNewOverview[i] = false;
    3152          21 :                 break;
    3153             :             }
    3154             : 
    3155         675 :             if (nOvFactor == panOverviewList[i] ||
    3156         322 :                 nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3157             :                                                 GetRasterXSize(),
    3158             :                                                 GetRasterYSize()))
    3159             :             {
    3160          33 :                 abRequireNewOverview[i] = false;
    3161          33 :                 break;
    3162             :             }
    3163             :         }
    3164             : 
    3165         400 :         if (abRequireNewOverview[i])
    3166             :         {
    3167         346 :             if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    3168           0 :                 !m_bWriteKnownIncompatibleEdition)
    3169             :             {
    3170           0 :                 ReportError(CE_Warning, CPLE_AppDefined,
    3171             :                             "Adding new overviews invalidates the "
    3172             :                             "LAYOUT=IFDS_BEFORE_DATA property");
    3173           0 :                 m_bKnownIncompatibleEdition = true;
    3174           0 :                 m_bWriteKnownIncompatibleEdition = true;
    3175             :             }
    3176             : 
    3177         346 :             const int nOXSize = (GetRasterXSize() + panOverviewList[i] - 1) /
    3178         346 :                                 panOverviewList[i];
    3179         346 :             const int nOYSize = (GetRasterYSize() + panOverviewList[i] - 1) /
    3180         346 :                                 panOverviewList[i];
    3181             : 
    3182         692 :             const toff_t nOverviewOffset = GTIFFWriteDirectory(
    3183             :                 m_hTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize,
    3184         346 :                 nOvBitsPerSample, nPlanarConfig, m_nSamplesPerPixel,
    3185             :                 nOvrBlockXSize, nOvrBlockYSize, TRUE, nCompression,
    3186         346 :                 nPhotometric, m_nSampleFormat, nPredictor, panRed, panGreen,
    3187             :                 panBlue, nExtraSamples, panExtraSampleValues, osMetadata,
    3188         346 :                 nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality)
    3189             :                                      : nullptr,
    3190         346 :                 CPLSPrintf("%d", m_nJpegTablesMode),
    3191          24 :                 osNoData.empty() ? nullptr : osNoData.c_str(),
    3192         346 :                 m_anLercAddCompressionAndVersion, false);
    3193             : 
    3194         346 :             if (nOverviewOffset == 0)
    3195           0 :                 eErr = CE_Failure;
    3196             :             else
    3197         346 :                 eErr = RegisterNewOverviewDataset(
    3198             :                     nOverviewOffset, nOvrJpegQuality, papszOptions);
    3199             :         }
    3200             :     }
    3201             : 
    3202         226 :     CPLFree(panExtraSampleValues);
    3203         226 :     panExtraSampleValues = nullptr;
    3204             : 
    3205         226 :     ReloadDirectory();
    3206             : 
    3207             :     /* -------------------------------------------------------------------- */
    3208             :     /*      Create overviews for the mask.                                  */
    3209             :     /* -------------------------------------------------------------------- */
    3210         226 :     if (eErr != CE_None)
    3211           0 :         return eErr;
    3212             : 
    3213         226 :     eErr = CreateInternalMaskOverviews(nOvrBlockXSize, nOvrBlockYSize);
    3214             : 
    3215             :     /* -------------------------------------------------------------------- */
    3216             :     /*      Refresh overviews for the mask                                  */
    3217             :     /* -------------------------------------------------------------------- */
    3218             :     const bool bHasInternalMask =
    3219         226 :         m_poMaskDS != nullptr && m_poMaskDS->GetRasterCount() == 1;
    3220             :     const bool bHasExternalMask =
    3221         226 :         !bHasInternalMask && oOvManager.HaveMaskFile();
    3222         226 :     const bool bHasMask = bHasInternalMask || bHasExternalMask;
    3223             : 
    3224         226 :     if (bHasInternalMask)
    3225             :     {
    3226          17 :         int nMaskOverviews = 0;
    3227             : 
    3228             :         GDALRasterBand **papoOverviewBands = static_cast<GDALRasterBand **>(
    3229          17 :             CPLCalloc(sizeof(void *), m_nOverviewCount));
    3230          50 :         for (int i = 0; i < m_nOverviewCount; ++i)
    3231             :         {
    3232          33 :             if (m_papoOverviewDS[i]->m_poMaskDS != nullptr)
    3233             :             {
    3234          33 :                 papoOverviewBands[nMaskOverviews++] =
    3235          33 :                     m_papoOverviewDS[i]->m_poMaskDS->GetRasterBand(1);
    3236             :             }
    3237             :         }
    3238             : 
    3239          34 :         void *pScaledProgressData = GDALCreateScaledProgress(
    3240          17 :             0, 1.0 / (nBands + 1), pfnProgress, pProgressData);
    3241          34 :         eErr = GDALRegenerateOverviewsEx(
    3242          17 :             m_poMaskDS->GetRasterBand(1), nMaskOverviews,
    3243             :             reinterpret_cast<GDALRasterBandH *>(papoOverviewBands),
    3244             :             pszResampling, GDALScaledProgress, pScaledProgressData,
    3245             :             papszOptions);
    3246          17 :         GDALDestroyScaledProgress(pScaledProgressData);
    3247          17 :         CPLFree(papoOverviewBands);
    3248             :     }
    3249         209 :     else if (bHasExternalMask)
    3250             :     {
    3251           4 :         void *pScaledProgressData = GDALCreateScaledProgress(
    3252           2 :             0, 1.0 / (nBands + 1), pfnProgress, pProgressData);
    3253           2 :         eErr = oOvManager.BuildOverviewsMask(
    3254             :             pszResampling, nOverviews, panOverviewList, GDALScaledProgress,
    3255             :             pScaledProgressData, papszOptions);
    3256           2 :         GDALDestroyScaledProgress(pScaledProgressData);
    3257             :     }
    3258             : 
    3259             :     // If we have an alpha band, we want it to be generated before downsampling
    3260             :     // other bands
    3261         226 :     bool bHasAlphaBand = false;
    3262         638 :     for (int iBand = 0; iBand < nBands; iBand++)
    3263             :     {
    3264         412 :         if (papoBands[iBand]->GetColorInterpretation() == GCI_AlphaBand)
    3265          15 :             bHasAlphaBand = true;
    3266             :     }
    3267             : 
    3268             :     /* -------------------------------------------------------------------- */
    3269             :     /*      Refresh old overviews that were listed.                         */
    3270             :     /* -------------------------------------------------------------------- */
    3271         226 :     const auto poColorTable = GetRasterBand(panBandList[0])->GetColorTable();
    3272          15 :     if ((m_nPlanarConfig == PLANARCONFIG_CONTIG || bHasAlphaBand) &&
    3273         213 :         GDALDataTypeIsComplex(
    3274         213 :             GetRasterBand(panBandList[0])->GetRasterDataType()) == FALSE &&
    3275          12 :         (poColorTable == nullptr || STARTS_WITH_CI(pszResampling, "NEAR") ||
    3276         453 :          poColorTable->IsIdentity()) &&
    3277         205 :         (STARTS_WITH_CI(pszResampling, "NEAR") ||
    3278         112 :          EQUAL(pszResampling, "AVERAGE") || EQUAL(pszResampling, "RMS") ||
    3279          48 :          EQUAL(pszResampling, "GAUSS") || EQUAL(pszResampling, "CUBIC") ||
    3280          29 :          EQUAL(pszResampling, "CUBICSPLINE") ||
    3281          28 :          EQUAL(pszResampling, "LANCZOS") || EQUAL(pszResampling, "BILINEAR") ||
    3282          24 :          EQUAL(pszResampling, "MODE")))
    3283             :     {
    3284             :         // In the case of pixel interleaved compressed overviews, we want to
    3285             :         // generate the overviews for all the bands block by block, and not
    3286             :         // band after band, in order to write the block once and not loose
    3287             :         // space in the TIFF file.  We also use that logic for uncompressed
    3288             :         // overviews, since GDALRegenerateOverviewsMultiBand() will be able to
    3289             :         // trigger cascading overview regeneration even in the presence
    3290             :         // of an alpha band.
    3291             : 
    3292         184 :         int nNewOverviews = 0;
    3293             : 
    3294             :         GDALRasterBand ***papapoOverviewBands = static_cast<GDALRasterBand ***>(
    3295         184 :             CPLCalloc(sizeof(void *), nBandsIn));
    3296             :         GDALRasterBand **papoBandList =
    3297         184 :             static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nBandsIn));
    3298         518 :         for (int iBand = 0; iBand < nBandsIn; ++iBand)
    3299             :         {
    3300         334 :             GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
    3301             : 
    3302         334 :             papoBandList[iBand] = poBand;
    3303         668 :             papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
    3304         334 :                 CPLCalloc(sizeof(void *), poBand->GetOverviewCount()));
    3305             : 
    3306         334 :             int iCurOverview = 0;
    3307             :             std::vector<bool> abAlreadyUsedOverviewBand(
    3308         334 :                 poBand->GetOverviewCount(), false);
    3309             : 
    3310         968 :             for (int i = 0; i < nOverviews; ++i)
    3311             :             {
    3312        1125 :                 for (int j = 0; j < poBand->GetOverviewCount(); ++j)
    3313             :                 {
    3314        1111 :                     if (abAlreadyUsedOverviewBand[j])
    3315         491 :                         continue;
    3316             : 
    3317             :                     int nOvFactor;
    3318         620 :                     GDALRasterBand *poOverview = poBand->GetOverview(j);
    3319             : 
    3320         620 :                     nOvFactor = GDALComputeOvFactor(
    3321             :                         poOverview->GetXSize(), poBand->GetXSize(),
    3322             :                         poOverview->GetYSize(), poBand->GetYSize());
    3323             : 
    3324         620 :                     GDALCopyNoDataValue(poOverview, poBand);
    3325             : 
    3326         629 :                     if (nOvFactor == panOverviewList[i] ||
    3327           9 :                         nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3328             :                                                         poBand->GetXSize(),
    3329             :                                                         poBand->GetYSize()))
    3330             :                     {
    3331         620 :                         if (iBand == 0)
    3332             :                         {
    3333             :                             const auto osNewResampling =
    3334         628 :                                 GDALGetNormalizedOvrResampling(pszResampling);
    3335             :                             const char *pszExistingResampling =
    3336         314 :                                 poOverview->GetMetadataItem("RESAMPLING");
    3337         628 :                             if (pszExistingResampling &&
    3338         314 :                                 pszExistingResampling != osNewResampling)
    3339             :                             {
    3340           2 :                                 poOverview->SetMetadataItem(
    3341           2 :                                     "RESAMPLING", osNewResampling.c_str());
    3342             :                             }
    3343             :                         }
    3344             : 
    3345         620 :                         abAlreadyUsedOverviewBand[j] = true;
    3346         620 :                         CPLAssert(iCurOverview < poBand->GetOverviewCount());
    3347         620 :                         papapoOverviewBands[iBand][iCurOverview] = poOverview;
    3348         620 :                         ++iCurOverview;
    3349         620 :                         break;
    3350             :                     }
    3351             :                 }
    3352             :             }
    3353             : 
    3354         334 :             if (nNewOverviews == 0)
    3355             :             {
    3356         184 :                 nNewOverviews = iCurOverview;
    3357             :             }
    3358         150 :             else if (nNewOverviews != iCurOverview)
    3359             :             {
    3360           0 :                 CPLAssert(false);
    3361             :                 return CE_Failure;
    3362             :             }
    3363             :         }
    3364             : 
    3365             :         void *pScaledProgressData =
    3366         184 :             bHasMask ? GDALCreateScaledProgress(1.0 / (nBands + 1), 1.0,
    3367             :                                                 pfnProgress, pProgressData)
    3368         165 :                      : GDALCreateScaledProgress(0.0, 1.0, pfnProgress,
    3369         184 :                                                 pProgressData);
    3370         184 :         GDALRegenerateOverviewsMultiBand(nBandsIn, papoBandList, nNewOverviews,
    3371             :                                          papapoOverviewBands, pszResampling,
    3372             :                                          GDALScaledProgress,
    3373             :                                          pScaledProgressData, papszOptions);
    3374         184 :         GDALDestroyScaledProgress(pScaledProgressData);
    3375             : 
    3376         518 :         for (int iBand = 0; iBand < nBandsIn; ++iBand)
    3377             :         {
    3378         334 :             CPLFree(papapoOverviewBands[iBand]);
    3379             :         }
    3380         184 :         CPLFree(papapoOverviewBands);
    3381         184 :         CPLFree(papoBandList);
    3382             :     }
    3383             :     else
    3384             :     {
    3385             :         GDALRasterBand **papoOverviewBands = static_cast<GDALRasterBand **>(
    3386          42 :             CPLCalloc(sizeof(void *), nOverviews));
    3387             : 
    3388          42 :         const int iBandOffset = bHasMask ? 1 : 0;
    3389             : 
    3390         120 :         for (int iBand = 0; iBand < nBandsIn && eErr == CE_None; ++iBand)
    3391             :         {
    3392          78 :             GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
    3393          78 :             if (poBand == nullptr)
    3394             :             {
    3395           0 :                 eErr = CE_Failure;
    3396           0 :                 break;
    3397             :             }
    3398             : 
    3399             :             std::vector<bool> abAlreadyUsedOverviewBand(
    3400         156 :                 poBand->GetOverviewCount(), false);
    3401             : 
    3402          78 :             int nNewOverviews = 0;
    3403         216 :             for (int i = 0; i < nOverviews; ++i)
    3404             :             {
    3405         336 :                 for (int j = 0; j < poBand->GetOverviewCount(); ++j)
    3406             :                 {
    3407         315 :                     if (abAlreadyUsedOverviewBand[j])
    3408         176 :                         continue;
    3409             : 
    3410         139 :                     GDALRasterBand *poOverview = poBand->GetOverview(j);
    3411             : 
    3412         139 :                     GDALCopyNoDataValue(poOverview, poBand);
    3413             : 
    3414         139 :                     const int nOvFactor = GDALComputeOvFactor(
    3415             :                         poOverview->GetXSize(), poBand->GetXSize(),
    3416             :                         poOverview->GetYSize(), poBand->GetYSize());
    3417             : 
    3418         171 :                     if (nOvFactor == panOverviewList[i] ||
    3419          32 :                         nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3420             :                                                         poBand->GetXSize(),
    3421             :                                                         poBand->GetYSize()))
    3422             :                     {
    3423         117 :                         if (iBand == 0)
    3424             :                         {
    3425             :                             const auto osNewResampling =
    3426         130 :                                 GDALGetNormalizedOvrResampling(pszResampling);
    3427             :                             const char *pszExistingResampling =
    3428          65 :                                 poOverview->GetMetadataItem("RESAMPLING");
    3429          98 :                             if (pszExistingResampling &&
    3430          33 :                                 pszExistingResampling != osNewResampling)
    3431             :                             {
    3432           1 :                                 poOverview->SetMetadataItem(
    3433           1 :                                     "RESAMPLING", osNewResampling.c_str());
    3434             :                             }
    3435             :                         }
    3436             : 
    3437         117 :                         abAlreadyUsedOverviewBand[j] = true;
    3438         117 :                         CPLAssert(nNewOverviews < poBand->GetOverviewCount());
    3439         117 :                         papoOverviewBands[nNewOverviews++] = poOverview;
    3440         117 :                         break;
    3441             :                     }
    3442             :                 }
    3443             :             }
    3444             : 
    3445         156 :             void *pScaledProgressData = GDALCreateScaledProgress(
    3446          78 :                 (iBand + iBandOffset) /
    3447          78 :                     static_cast<double>(nBandsIn + iBandOffset),
    3448          78 :                 (iBand + iBandOffset + 1) /
    3449          78 :                     static_cast<double>(nBandsIn + iBandOffset),
    3450             :                 pfnProgress, pProgressData);
    3451             : 
    3452          78 :             eErr = GDALRegenerateOverviewsEx(
    3453             :                 poBand, nNewOverviews,
    3454             :                 reinterpret_cast<GDALRasterBandH *>(papoOverviewBands),
    3455             :                 pszResampling, GDALScaledProgress, pScaledProgressData,
    3456             :                 papszOptions);
    3457             : 
    3458          78 :             GDALDestroyScaledProgress(pScaledProgressData);
    3459             :         }
    3460             : 
    3461             :         /* --------------------------------------------------------------------
    3462             :          */
    3463             :         /*      Cleanup */
    3464             :         /* --------------------------------------------------------------------
    3465             :          */
    3466          42 :         CPLFree(papoOverviewBands);
    3467             :     }
    3468             : 
    3469         226 :     pfnProgress(1.0, nullptr, pProgressData);
    3470             : 
    3471         226 :     return eErr;
    3472             : }
    3473             : 
    3474             : /************************************************************************/
    3475             : /*                      GTiffWriteDummyGeokeyDirectory()                */
    3476             : /************************************************************************/
    3477             : 
    3478        1345 : static void GTiffWriteDummyGeokeyDirectory(TIFF *hTIFF)
    3479             : {
    3480             :     // If we have existing geokeys, try to wipe them
    3481             :     // by writing a dummy geokey directory. (#2546)
    3482        1345 :     uint16_t *panVI = nullptr;
    3483        1345 :     uint16_t nKeyCount = 0;
    3484             : 
    3485        1345 :     if (TIFFGetField(hTIFF, TIFFTAG_GEOKEYDIRECTORY, &nKeyCount, &panVI))
    3486             :     {
    3487          19 :         GUInt16 anGKVersionInfo[4] = {1, 1, 0, 0};
    3488          19 :         double adfDummyDoubleParams[1] = {0.0};
    3489          19 :         TIFFSetField(hTIFF, TIFFTAG_GEOKEYDIRECTORY, 4, anGKVersionInfo);
    3490          19 :         TIFFSetField(hTIFF, TIFFTAG_GEODOUBLEPARAMS, 1, adfDummyDoubleParams);
    3491          19 :         TIFFSetField(hTIFF, TIFFTAG_GEOASCIIPARAMS, "");
    3492             :     }
    3493        1345 : }
    3494             : 
    3495             : /************************************************************************/
    3496             : /*                    IsSRSCompatibleOfGeoTIFF()                        */
    3497             : /************************************************************************/
    3498             : 
    3499        2761 : static bool IsSRSCompatibleOfGeoTIFF(const OGRSpatialReference *poSRS,
    3500             :                                      GTIFFKeysFlavorEnum eGeoTIFFKeysFlavor)
    3501             : {
    3502        2761 :     char *pszWKT = nullptr;
    3503        2761 :     if ((poSRS->IsGeographic() || poSRS->IsProjected()) && !poSRS->IsCompound())
    3504             :     {
    3505        2743 :         const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
    3506        2743 :         const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
    3507        2743 :         if (pszAuthName && pszAuthCode && EQUAL(pszAuthName, "EPSG"))
    3508        2221 :             return true;
    3509             :     }
    3510             :     OGRErr eErr;
    3511             :     {
    3512        1080 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    3513        1080 :         if (poSRS->IsDerivedGeographic() ||
    3514         540 :             (poSRS->IsProjected() && !poSRS->IsCompound() &&
    3515          72 :              poSRS->GetAxesCount() == 3))
    3516             :         {
    3517           0 :             eErr = OGRERR_FAILURE;
    3518             :         }
    3519             :         else
    3520             :         {
    3521             :             // Geographic3D CRS can't be exported to WKT1, but are
    3522             :             // valid GeoTIFF 1.1
    3523         540 :             const char *const apszOptions[] = {
    3524         540 :                 poSRS->IsGeographic() ? nullptr : "FORMAT=WKT1", nullptr};
    3525         540 :             eErr = poSRS->exportToWkt(&pszWKT, apszOptions);
    3526         540 :             if (eErr == OGRERR_FAILURE && poSRS->IsProjected() &&
    3527             :                 eGeoTIFFKeysFlavor == GEOTIFF_KEYS_ESRI_PE)
    3528             :             {
    3529           0 :                 CPLFree(pszWKT);
    3530           0 :                 const char *const apszOptionsESRIWKT[] = {"FORMAT=WKT1_ESRI",
    3531             :                                                           nullptr};
    3532           0 :                 eErr = poSRS->exportToWkt(&pszWKT, apszOptionsESRIWKT);
    3533             :             }
    3534             :         }
    3535             :     }
    3536         540 :     const bool bCompatibleOfGeoTIFF =
    3537        1079 :         (eErr == OGRERR_NONE && pszWKT != nullptr &&
    3538         539 :          strstr(pszWKT, "custom_proj4") == nullptr);
    3539         540 :     CPLFree(pszWKT);
    3540         540 :     return bCompatibleOfGeoTIFF;
    3541             : }
    3542             : 
    3543             : /************************************************************************/
    3544             : /*                          WriteGeoTIFFInfo()                          */
    3545             : /************************************************************************/
    3546             : 
    3547        3173 : void GTiffDataset::WriteGeoTIFFInfo()
    3548             : 
    3549             : {
    3550        3173 :     bool bPixelIsPoint = false;
    3551        3173 :     bool bPointGeoIgnore = false;
    3552             : 
    3553             :     const char *pszAreaOrPoint =
    3554        3173 :         GTiffDataset::GetMetadataItem(GDALMD_AREA_OR_POINT);
    3555        3173 :     if (pszAreaOrPoint && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT))
    3556             :     {
    3557          17 :         bPixelIsPoint = true;
    3558             :         bPointGeoIgnore =
    3559          17 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    3560             :     }
    3561             : 
    3562        3173 :     if (m_bForceUnsetGTOrGCPs)
    3563             :     {
    3564          11 :         m_bNeedsRewrite = true;
    3565          11 :         m_bForceUnsetGTOrGCPs = false;
    3566             : 
    3567          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE);
    3568          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS);
    3569          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX);
    3570             :     }
    3571             : 
    3572        3173 :     if (m_bForceUnsetProjection)
    3573             :     {
    3574           7 :         m_bNeedsRewrite = true;
    3575           7 :         m_bForceUnsetProjection = false;
    3576             : 
    3577           7 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOKEYDIRECTORY);
    3578           7 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEODOUBLEPARAMS);
    3579           7 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOASCIIPARAMS);
    3580             :     }
    3581             : 
    3582             :     /* -------------------------------------------------------------------- */
    3583             :     /*      Write geotransform if valid.                                    */
    3584             :     /* -------------------------------------------------------------------- */
    3585        3173 :     if (m_bGeoTransformValid)
    3586             :     {
    3587        1515 :         m_bNeedsRewrite = true;
    3588             : 
    3589             :         /* --------------------------------------------------------------------
    3590             :          */
    3591             :         /*      Clear old tags to ensure we don't end up with conflicting */
    3592             :         /*      information. (#2625) */
    3593             :         /* --------------------------------------------------------------------
    3594             :          */
    3595        1515 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE);
    3596        1515 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS);
    3597        1515 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX);
    3598             : 
    3599             :         /* --------------------------------------------------------------------
    3600             :          */
    3601             :         /*      Write the transform.  If we have a normal north-up image we */
    3602             :         /*      use the tiepoint plus pixelscale otherwise we use a matrix. */
    3603             :         /* --------------------------------------------------------------------
    3604             :          */
    3605        1515 :         if (m_adfGeoTransform[2] == 0.0 && m_adfGeoTransform[4] == 0.0 &&
    3606        1497 :             m_adfGeoTransform[5] < 0.0)
    3607             :         {
    3608        1466 :             double dfOffset = 0.0;
    3609        1466 :             if (m_eProfile != GTiffProfile::BASELINE)
    3610             :             {
    3611             :                 // In the case the SRS has a vertical component and we have
    3612             :                 // a single band, encode its scale/offset in the GeoTIFF tags
    3613        1461 :                 int bHasScale = FALSE;
    3614        1461 :                 double dfScale = GetRasterBand(1)->GetScale(&bHasScale);
    3615        1461 :                 int bHasOffset = FALSE;
    3616        1461 :                 dfOffset = GetRasterBand(1)->GetOffset(&bHasOffset);
    3617             :                 const bool bApplyScaleOffset =
    3618        1461 :                     m_oSRS.IsVertical() && GetRasterCount() == 1;
    3619        1461 :                 if (bApplyScaleOffset && !bHasScale)
    3620           0 :                     dfScale = 1.0;
    3621        1461 :                 if (!bApplyScaleOffset || !bHasOffset)
    3622        1458 :                     dfOffset = 0.0;
    3623             :                 const double adfPixelScale[3] = {
    3624        1461 :                     m_adfGeoTransform[1], fabs(m_adfGeoTransform[5]),
    3625        1461 :                     bApplyScaleOffset ? dfScale : 0.0};
    3626        1461 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale);
    3627             :             }
    3628             : 
    3629        1466 :             double adfTiePoints[6] = {
    3630        1466 :                 0.0,     0.0, 0.0, m_adfGeoTransform[0], m_adfGeoTransform[3],
    3631        1466 :                 dfOffset};
    3632             : 
    3633        1466 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3634             :             {
    3635          13 :                 adfTiePoints[3] +=
    3636          13 :                     m_adfGeoTransform[1] * 0.5 + m_adfGeoTransform[2] * 0.5;
    3637          13 :                 adfTiePoints[4] +=
    3638          13 :                     m_adfGeoTransform[4] * 0.5 + m_adfGeoTransform[5] * 0.5;
    3639             :             }
    3640             : 
    3641        1466 :             if (m_eProfile != GTiffProfile::BASELINE)
    3642        1466 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
    3643             :         }
    3644             :         else
    3645             :         {
    3646          49 :             double adfMatrix[16] = {};
    3647             : 
    3648          49 :             adfMatrix[0] = m_adfGeoTransform[1];
    3649          49 :             adfMatrix[1] = m_adfGeoTransform[2];
    3650          49 :             adfMatrix[3] = m_adfGeoTransform[0];
    3651          49 :             adfMatrix[4] = m_adfGeoTransform[4];
    3652          49 :             adfMatrix[5] = m_adfGeoTransform[5];
    3653          49 :             adfMatrix[7] = m_adfGeoTransform[3];
    3654          49 :             adfMatrix[15] = 1.0;
    3655             : 
    3656          49 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3657             :             {
    3658           0 :                 adfMatrix[3] +=
    3659           0 :                     m_adfGeoTransform[1] * 0.5 + m_adfGeoTransform[2] * 0.5;
    3660           0 :                 adfMatrix[7] +=
    3661           0 :                     m_adfGeoTransform[4] * 0.5 + m_adfGeoTransform[5] * 0.5;
    3662             :             }
    3663             : 
    3664          49 :             if (m_eProfile != GTiffProfile::BASELINE)
    3665          49 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
    3666             :         }
    3667             : 
    3668             :         // Do we need a world file?
    3669        1515 :         if (CPLFetchBool(m_papszCreationOptions, "TFW", false))
    3670           7 :             GDALWriteWorldFile(m_pszFilename, "tfw", m_adfGeoTransform);
    3671        1508 :         else if (CPLFetchBool(m_papszCreationOptions, "WORLDFILE", false))
    3672           2 :             GDALWriteWorldFile(m_pszFilename, "wld", m_adfGeoTransform);
    3673             :     }
    3674        1670 :     else if (GetGCPCount() > 0 && GetGCPCount() <= knMAX_GCP_COUNT &&
    3675          12 :              m_eProfile != GTiffProfile::BASELINE)
    3676             :     {
    3677          12 :         m_bNeedsRewrite = true;
    3678             : 
    3679             :         double *padfTiePoints = static_cast<double *>(
    3680          12 :             CPLMalloc(6 * sizeof(double) * GetGCPCount()));
    3681             : 
    3682          63 :         for (size_t iGCP = 0; iGCP < m_aoGCPs.size(); ++iGCP)
    3683             :         {
    3684             : 
    3685          51 :             padfTiePoints[iGCP * 6 + 0] = m_aoGCPs[iGCP].Pixel();
    3686          51 :             padfTiePoints[iGCP * 6 + 1] = m_aoGCPs[iGCP].Line();
    3687          51 :             padfTiePoints[iGCP * 6 + 2] = 0;
    3688          51 :             padfTiePoints[iGCP * 6 + 3] = m_aoGCPs[iGCP].X();
    3689          51 :             padfTiePoints[iGCP * 6 + 4] = m_aoGCPs[iGCP].Y();
    3690          51 :             padfTiePoints[iGCP * 6 + 5] = m_aoGCPs[iGCP].Z();
    3691             : 
    3692          51 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3693             :             {
    3694           0 :                 padfTiePoints[iGCP * 6 + 0] += 0.5;
    3695           0 :                 padfTiePoints[iGCP * 6 + 1] += 0.5;
    3696             :             }
    3697             :         }
    3698             : 
    3699          12 :         TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * GetGCPCount(),
    3700             :                      padfTiePoints);
    3701          12 :         CPLFree(padfTiePoints);
    3702             :     }
    3703             : 
    3704             :     /* -------------------------------------------------------------------- */
    3705             :     /*      Write out projection definition.                                */
    3706             :     /* -------------------------------------------------------------------- */
    3707        3173 :     const bool bHasProjection = !m_oSRS.IsEmpty();
    3708        3173 :     if ((bHasProjection || bPixelIsPoint) &&
    3709        1348 :         m_eProfile != GTiffProfile::BASELINE)
    3710             :     {
    3711        1345 :         m_bNeedsRewrite = true;
    3712             : 
    3713             :         // If we have existing geokeys, try to wipe them
    3714             :         // by writing a dummy geokey directory. (#2546)
    3715        1345 :         GTiffWriteDummyGeokeyDirectory(m_hTIFF);
    3716             : 
    3717        1345 :         GTIF *psGTIF = GTiffDataset::GTIFNew(m_hTIFF);
    3718             : 
    3719             :         // Set according to coordinate system.
    3720        1345 :         if (bHasProjection)
    3721             :         {
    3722        1344 :             if (IsSRSCompatibleOfGeoTIFF(&m_oSRS, m_eGeoTIFFKeysFlavor))
    3723             :             {
    3724        1342 :                 GTIFSetFromOGISDefnEx(psGTIF,
    3725             :                                       OGRSpatialReference::ToHandle(&m_oSRS),
    3726             :                                       m_eGeoTIFFKeysFlavor, m_eGeoTIFFVersion);
    3727             :             }
    3728             :             else
    3729             :             {
    3730           2 :                 GDALPamDataset::SetSpatialRef(&m_oSRS);
    3731             :             }
    3732             :         }
    3733             : 
    3734        1345 :         if (bPixelIsPoint)
    3735             :         {
    3736          17 :             GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
    3737             :                        RasterPixelIsPoint);
    3738             :         }
    3739             : 
    3740        1345 :         GTIFWriteKeys(psGTIF);
    3741        1345 :         GTIFFree(psGTIF);
    3742             :     }
    3743        3173 : }
    3744             : 
    3745             : /************************************************************************/
    3746             : /*                         AppendMetadataItem()                         */
    3747             : /************************************************************************/
    3748             : 
    3749        3411 : static void AppendMetadataItem(CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
    3750             :                                const char *pszKey, const char *pszValue,
    3751             :                                int nBand, const char *pszRole,
    3752             :                                const char *pszDomain)
    3753             : 
    3754             : {
    3755             :     /* -------------------------------------------------------------------- */
    3756             :     /*      Create the Item element, and subcomponents.                     */
    3757             :     /* -------------------------------------------------------------------- */
    3758        3411 :     CPLXMLNode *psItem = CPLCreateXMLNode(nullptr, CXT_Element, "Item");
    3759        3411 :     CPLCreateXMLNode(CPLCreateXMLNode(psItem, CXT_Attribute, "name"), CXT_Text,
    3760             :                      pszKey);
    3761             : 
    3762        3411 :     if (nBand > 0)
    3763             :     {
    3764         777 :         char szBandId[32] = {};
    3765         777 :         snprintf(szBandId, sizeof(szBandId), "%d", nBand - 1);
    3766         777 :         CPLCreateXMLNode(CPLCreateXMLNode(psItem, CXT_Attribute, "sample"),
    3767             :                          CXT_Text, szBandId);
    3768             :     }
    3769             : 
    3770        3411 :     if (pszRole != nullptr)
    3771         312 :         CPLCreateXMLNode(CPLCreateXMLNode(psItem, CXT_Attribute, "role"),
    3772             :                          CXT_Text, pszRole);
    3773             : 
    3774        3411 :     if (pszDomain != nullptr && strlen(pszDomain) > 0)
    3775         945 :         CPLCreateXMLNode(CPLCreateXMLNode(psItem, CXT_Attribute, "domain"),
    3776             :                          CXT_Text, pszDomain);
    3777             : 
    3778             :     // Note: this escaping should not normally be done, as the serialization
    3779             :     // of the tree to XML also does it, so we end up width double XML escaping,
    3780             :     // but keep it for backward compatibility.
    3781        3411 :     char *pszEscapedItemValue = CPLEscapeString(pszValue, -1, CPLES_XML);
    3782        3411 :     CPLCreateXMLNode(psItem, CXT_Text, pszEscapedItemValue);
    3783        3411 :     CPLFree(pszEscapedItemValue);
    3784             : 
    3785             :     /* -------------------------------------------------------------------- */
    3786             :     /*      Create root, if missing.                                        */
    3787             :     /* -------------------------------------------------------------------- */
    3788        3411 :     if (*ppsRoot == nullptr)
    3789         586 :         *ppsRoot = CPLCreateXMLNode(nullptr, CXT_Element, "GDALMetadata");
    3790             : 
    3791             :     /* -------------------------------------------------------------------- */
    3792             :     /*      Append item to tail.  We keep track of the tail to avoid        */
    3793             :     /*      O(nsquared) time as the list gets longer.                       */
    3794             :     /* -------------------------------------------------------------------- */
    3795        3411 :     if (*ppsTail == nullptr)
    3796         586 :         CPLAddXMLChild(*ppsRoot, psItem);
    3797             :     else
    3798        2825 :         CPLAddXMLSibling(*ppsTail, psItem);
    3799             : 
    3800        3411 :     *ppsTail = psItem;
    3801        3411 : }
    3802             : 
    3803             : /************************************************************************/
    3804             : /*                         WriteMDMetadata()                            */
    3805             : /************************************************************************/
    3806             : 
    3807      206213 : static void WriteMDMetadata(GDALMultiDomainMetadata *poMDMD, TIFF *hTIFF,
    3808             :                             CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
    3809             :                             int nBand, GTiffProfile eProfile)
    3810             : 
    3811             : {
    3812             : 
    3813             :     /* ==================================================================== */
    3814             :     /*      Process each domain.                                            */
    3815             :     /* ==================================================================== */
    3816      206213 :     CSLConstList papszDomainList = poMDMD->GetDomainList();
    3817      211650 :     for (int iDomain = 0; papszDomainList && papszDomainList[iDomain];
    3818             :          ++iDomain)
    3819             :     {
    3820        5437 :         CSLConstList papszMD = poMDMD->GetMetadata(papszDomainList[iDomain]);
    3821        5437 :         bool bIsXML = false;
    3822             : 
    3823        5437 :         if (EQUAL(papszDomainList[iDomain], "IMAGE_STRUCTURE") ||
    3824        2176 :             EQUAL(papszDomainList[iDomain], "DERIVED_SUBDATASETS"))
    3825        3264 :             continue;  // Ignored.
    3826        2173 :         if (EQUAL(papszDomainList[iDomain], "COLOR_PROFILE"))
    3827           3 :             continue;  // Handled elsewhere.
    3828        2170 :         if (EQUAL(papszDomainList[iDomain], MD_DOMAIN_RPC))
    3829           7 :             continue;  // Handled elsewhere.
    3830        2164 :         if (EQUAL(papszDomainList[iDomain], "xml:ESRI") &&
    3831           1 :             CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
    3832           1 :             continue;  // Handled elsewhere.
    3833        2162 :         if (EQUAL(papszDomainList[iDomain], "xml:XMP"))
    3834           2 :             continue;  // Handled in SetMetadata.
    3835             : 
    3836        2160 :         if (STARTS_WITH_CI(papszDomainList[iDomain], "xml:"))
    3837           2 :             bIsXML = true;
    3838             : 
    3839             :         /* --------------------------------------------------------------------
    3840             :          */
    3841             :         /*      Process each item in this domain. */
    3842             :         /* --------------------------------------------------------------------
    3843             :          */
    3844        6703 :         for (int iItem = 0; papszMD && papszMD[iItem]; ++iItem)
    3845             :         {
    3846        4543 :             const char *pszItemValue = nullptr;
    3847        4543 :             char *pszItemName = nullptr;
    3848             : 
    3849        4543 :             if (bIsXML)
    3850             :             {
    3851           2 :                 pszItemName = CPLStrdup("doc");
    3852           2 :                 pszItemValue = papszMD[iItem];
    3853             :             }
    3854             :             else
    3855             :             {
    3856        4541 :                 pszItemValue = CPLParseNameValue(papszMD[iItem], &pszItemName);
    3857        4541 :                 if (pszItemName == nullptr)
    3858             :                 {
    3859          49 :                     CPLDebug("GTiff", "Invalid metadata item : %s",
    3860          49 :                              papszMD[iItem]);
    3861          49 :                     continue;
    3862             :                 }
    3863             :             }
    3864             : 
    3865             :             /* --------------------------------------------------------------------
    3866             :              */
    3867             :             /*      Convert into XML item or handle as a special TIFF tag. */
    3868             :             /* --------------------------------------------------------------------
    3869             :              */
    3870        4494 :             if (strlen(papszDomainList[iDomain]) == 0 && nBand == 0 &&
    3871        3465 :                 (STARTS_WITH_CI(pszItemName, "TIFFTAG_") ||
    3872        3404 :                  (EQUAL(pszItemName, "GEO_METADATA") &&
    3873        3403 :                   eProfile == GTiffProfile::GDALGEOTIFF) ||
    3874        3403 :                  (EQUAL(pszItemName, "TIFF_RSID") &&
    3875             :                   eProfile == GTiffProfile::GDALGEOTIFF)))
    3876             :             {
    3877          63 :                 if (EQUAL(pszItemName, "TIFFTAG_RESOLUTIONUNIT"))
    3878             :                 {
    3879             :                     // ResolutionUnit can't be 0, which is the default if
    3880             :                     // atoi() fails.  Set to 1=Unknown.
    3881           9 :                     int v = atoi(pszItemValue);
    3882           9 :                     if (!v)
    3883           1 :                         v = RESUNIT_NONE;
    3884           9 :                     TIFFSetField(hTIFF, TIFFTAG_RESOLUTIONUNIT, v);
    3885             :                 }
    3886             :                 else
    3887             :                 {
    3888          54 :                     bool bFoundTag = false;
    3889          54 :                     size_t iTag = 0;  // Used after for.
    3890          54 :                     const auto *pasTIFFTags = GTiffDataset::GetTIFFTags();
    3891         286 :                     for (; pasTIFFTags[iTag].pszTagName; ++iTag)
    3892             :                     {
    3893         286 :                         if (EQUAL(pszItemName, pasTIFFTags[iTag].pszTagName))
    3894             :                         {
    3895          54 :                             bFoundTag = true;
    3896          54 :                             break;
    3897             :                         }
    3898             :                     }
    3899             : 
    3900          54 :                     if (bFoundTag &&
    3901          54 :                         pasTIFFTags[iTag].eType == GTIFFTAGTYPE_STRING)
    3902          33 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    3903             :                                      pszItemValue);
    3904          21 :                     else if (bFoundTag &&
    3905          21 :                              pasTIFFTags[iTag].eType == GTIFFTAGTYPE_FLOAT)
    3906          16 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    3907             :                                      CPLAtof(pszItemValue));
    3908           5 :                     else if (bFoundTag &&
    3909           5 :                              pasTIFFTags[iTag].eType == GTIFFTAGTYPE_SHORT)
    3910           4 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    3911             :                                      atoi(pszItemValue));
    3912           1 :                     else if (bFoundTag && pasTIFFTags[iTag].eType ==
    3913             :                                               GTIFFTAGTYPE_BYTE_STRING)
    3914             :                     {
    3915           1 :                         uint32_t nLen =
    3916           1 :                             static_cast<uint32_t>(strlen(pszItemValue));
    3917           1 :                         if (nLen)
    3918             :                         {
    3919           1 :                             TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal, nLen,
    3920             :                                          pszItemValue);
    3921           1 :                         }
    3922             :                     }
    3923             :                     else
    3924           0 :                         CPLError(CE_Warning, CPLE_NotSupported,
    3925             :                                  "%s metadata item is unhandled and "
    3926             :                                  "will not be written",
    3927             :                                  pszItemName);
    3928          63 :                 }
    3929             :             }
    3930        4431 :             else if (nBand == 0 && EQUAL(pszItemName, GDALMD_AREA_OR_POINT))
    3931             :             {
    3932             :                 /* Do nothing, handled elsewhere. */;
    3933             :             }
    3934             :             else
    3935             :             {
    3936        2722 :                 AppendMetadataItem(ppsRoot, ppsTail, pszItemName, pszItemValue,
    3937        2722 :                                    nBand, nullptr, papszDomainList[iDomain]);
    3938             :             }
    3939             : 
    3940        4494 :             CPLFree(pszItemName);
    3941             :         }
    3942             : 
    3943             :         /* --------------------------------------------------------------------
    3944             :          */
    3945             :         /*      Remove TIFFTAG_xxxxxx that are already set but no longer in */
    3946             :         /*      the metadata list (#5619) */
    3947             :         /* --------------------------------------------------------------------
    3948             :          */
    3949        2160 :         if (strlen(papszDomainList[iDomain]) == 0 && nBand == 0)
    3950             :         {
    3951        1970 :             const auto *pasTIFFTags = GTiffDataset::GetTIFFTags();
    3952       29550 :             for (size_t iTag = 0; pasTIFFTags[iTag].pszTagName; ++iTag)
    3953             :             {
    3954       27580 :                 uint32_t nCount = 0;
    3955       27580 :                 char *pszText = nullptr;
    3956       27580 :                 int16_t nVal = 0;
    3957       27580 :                 float fVal = 0.0f;
    3958             :                 const char *pszVal =
    3959       27580 :                     CSLFetchNameValue(papszMD, pasTIFFTags[iTag].pszTagName);
    3960       55097 :                 if (pszVal == nullptr &&
    3961       27517 :                     ((pasTIFFTags[iTag].eType == GTIFFTAGTYPE_STRING &&
    3962       15727 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    3963       27509 :                                    &pszText)) ||
    3964       27509 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_SHORT &&
    3965        5897 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &nVal)) ||
    3966       27506 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_FLOAT &&
    3967        3924 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &fVal)) ||
    3968       27505 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_BYTE_STRING &&
    3969        1969 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &nCount,
    3970             :                                    &pszText))))
    3971             :                 {
    3972          13 :                     TIFFUnsetField(hTIFF, pasTIFFTags[iTag].nTagVal);
    3973             :                 }
    3974             :             }
    3975             :         }
    3976             :     }
    3977      206213 : }
    3978             : 
    3979             : /************************************************************************/
    3980             : /*                           WriteRPC()                                 */
    3981             : /************************************************************************/
    3982             : 
    3983        6888 : void GTiffDataset::WriteRPC(GDALDataset *poSrcDS, TIFF *l_hTIFF,
    3984             :                             int bSrcIsGeoTIFF, GTiffProfile eProfile,
    3985             :                             const char *pszTIFFFilename,
    3986             :                             CSLConstList papszCreationOptions,
    3987             :                             bool bWriteOnlyInPAMIfNeeded)
    3988             : {
    3989             :     /* -------------------------------------------------------------------- */
    3990             :     /*      Handle RPC data written to TIFF RPCCoefficient tag, RPB file,   */
    3991             :     /*      RPCTEXT file or PAM.                                            */
    3992             :     /* -------------------------------------------------------------------- */
    3993        6888 :     char **papszRPCMD = poSrcDS->GetMetadata(MD_DOMAIN_RPC);
    3994        6888 :     if (papszRPCMD != nullptr)
    3995             :     {
    3996          32 :         bool bRPCSerializedOtherWay = false;
    3997             : 
    3998          32 :         if (eProfile == GTiffProfile::GDALGEOTIFF)
    3999             :         {
    4000          20 :             if (!bWriteOnlyInPAMIfNeeded)
    4001          11 :                 GTiffDatasetWriteRPCTag(l_hTIFF, papszRPCMD);
    4002          20 :             bRPCSerializedOtherWay = true;
    4003             :         }
    4004             : 
    4005             :         // Write RPB file if explicitly asked, or if a non GDAL specific
    4006             :         // profile is selected and RPCTXT is not asked.
    4007             :         bool bRPBExplicitlyAsked =
    4008          32 :             CPLFetchBool(papszCreationOptions, "RPB", false);
    4009             :         bool bRPBExplicitlyDenied =
    4010          32 :             !CPLFetchBool(papszCreationOptions, "RPB", true);
    4011          44 :         if ((eProfile != GTiffProfile::GDALGEOTIFF &&
    4012          12 :              !CPLFetchBool(papszCreationOptions, "RPCTXT", false) &&
    4013          44 :              !bRPBExplicitlyDenied) ||
    4014             :             bRPBExplicitlyAsked)
    4015             :         {
    4016           8 :             if (!bWriteOnlyInPAMIfNeeded)
    4017           4 :                 GDALWriteRPBFile(pszTIFFFilename, papszRPCMD);
    4018           8 :             bRPCSerializedOtherWay = true;
    4019             :         }
    4020             : 
    4021          32 :         if (CPLFetchBool(papszCreationOptions, "RPCTXT", false))
    4022             :         {
    4023           2 :             if (!bWriteOnlyInPAMIfNeeded)
    4024           1 :                 GDALWriteRPCTXTFile(pszTIFFFilename, papszRPCMD);
    4025           2 :             bRPCSerializedOtherWay = true;
    4026             :         }
    4027             : 
    4028          32 :         if (!bRPCSerializedOtherWay && bWriteOnlyInPAMIfNeeded && bSrcIsGeoTIFF)
    4029           1 :             cpl::down_cast<GTiffDataset *>(poSrcDS)
    4030           1 :                 ->GDALPamDataset::SetMetadata(papszRPCMD, MD_DOMAIN_RPC);
    4031             :     }
    4032        6888 : }
    4033             : 
    4034             : /************************************************************************/
    4035             : /*                           WriteMetadata()                            */
    4036             : /************************************************************************/
    4037             : 
    4038        5049 : bool GTiffDataset::WriteMetadata(GDALDataset *poSrcDS, TIFF *l_hTIFF,
    4039             :                                  bool bSrcIsGeoTIFF, GTiffProfile eProfile,
    4040             :                                  const char *pszTIFFFilename,
    4041             :                                  CSLConstList papszCreationOptions,
    4042             :                                  bool bExcludeRPBandIMGFileWriting)
    4043             : 
    4044             : {
    4045             :     /* -------------------------------------------------------------------- */
    4046             :     /*      Convert all the remaining metadata into a simple XML            */
    4047             :     /*      format.                                                         */
    4048             :     /* -------------------------------------------------------------------- */
    4049        5049 :     CPLXMLNode *psRoot = nullptr;
    4050        5049 :     CPLXMLNode *psTail = nullptr;
    4051             : 
    4052             :     const char *pszCopySrcMDD =
    4053        5049 :         CSLFetchNameValueDef(papszCreationOptions, "COPY_SRC_MDD", "AUTO");
    4054             :     char **papszSrcMDD =
    4055        5049 :         CSLFetchNameValueMultiple(papszCreationOptions, "SRC_MDD");
    4056             : 
    4057        5049 :     if (bSrcIsGeoTIFF)
    4058             :     {
    4059        3189 :         GTiffDataset *poSrcDSGTiff = cpl::down_cast<GTiffDataset *>(poSrcDS);
    4060        3189 :         assert(poSrcDSGTiff);
    4061        3189 :         WriteMDMetadata(&poSrcDSGTiff->m_oGTiffMDMD, l_hTIFF, &psRoot, &psTail,
    4062             :                         0, eProfile);
    4063             :     }
    4064             :     else
    4065             :     {
    4066        1860 :         if (EQUAL(pszCopySrcMDD, "AUTO") || CPLTestBool(pszCopySrcMDD) ||
    4067             :             papszSrcMDD)
    4068             :         {
    4069        3714 :             GDALMultiDomainMetadata l_oMDMD;
    4070        1857 :             CSLConstList papszMD = poSrcDS->GetMetadata();
    4071        1861 :             if (CSLCount(papszMD) > 0 &&
    4072           4 :                 (!papszSrcMDD || CSLFindString(papszSrcMDD, "") >= 0 ||
    4073           2 :                  CSLFindString(papszSrcMDD, "_DEFAULT_") >= 0))
    4074             :             {
    4075        1452 :                 l_oMDMD.SetMetadata(papszMD);
    4076             :             }
    4077             : 
    4078        1857 :             if ((!EQUAL(pszCopySrcMDD, "AUTO") && CPLTestBool(pszCopySrcMDD)) ||
    4079             :                 papszSrcMDD)
    4080             :             {
    4081           9 :                 char **papszDomainList = poSrcDS->GetMetadataDomainList();
    4082          39 :                 for (CSLConstList papszIter = papszDomainList;
    4083          39 :                      papszIter && *papszIter; ++papszIter)
    4084             :                 {
    4085          30 :                     const char *pszDomain = *papszIter;
    4086          46 :                     if (pszDomain[0] != 0 &&
    4087          16 :                         (!papszSrcMDD ||
    4088          16 :                          CSLFindString(papszSrcMDD, pszDomain) >= 0))
    4089             :                     {
    4090          12 :                         l_oMDMD.SetMetadata(poSrcDS->GetMetadata(pszDomain),
    4091             :                                             pszDomain);
    4092             :                     }
    4093             :                 }
    4094           9 :                 CSLDestroy(papszDomainList);
    4095             :             }
    4096             : 
    4097        1857 :             WriteMDMetadata(&l_oMDMD, l_hTIFF, &psRoot, &psTail, 0, eProfile);
    4098             :         }
    4099             :     }
    4100             : 
    4101        5049 :     if (!bExcludeRPBandIMGFileWriting)
    4102             :     {
    4103        5043 :         WriteRPC(poSrcDS, l_hTIFF, bSrcIsGeoTIFF, eProfile, pszTIFFFilename,
    4104             :                  papszCreationOptions);
    4105             : 
    4106             :         /* --------------------------------------------------------------------
    4107             :          */
    4108             :         /*      Handle metadata data written to an IMD file. */
    4109             :         /* --------------------------------------------------------------------
    4110             :          */
    4111        5043 :         char **papszIMDMD = poSrcDS->GetMetadata(MD_DOMAIN_IMD);
    4112        5043 :         if (papszIMDMD != nullptr)
    4113             :         {
    4114          20 :             GDALWriteIMDFile(pszTIFFFilename, papszIMDMD);
    4115             :         }
    4116             :     }
    4117             : 
    4118        5049 :     uint16_t nPhotometric = 0;
    4119        5049 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric)))
    4120           1 :         nPhotometric = PHOTOMETRIC_MINISBLACK;
    4121             : 
    4122        5049 :     const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
    4123             :         GDALDataset::ToHandle(poSrcDS), nPhotometric, papszCreationOptions);
    4124             : 
    4125             :     /* -------------------------------------------------------------------- */
    4126             :     /*      We also need to address band specific metadata, and special     */
    4127             :     /*      "role" metadata.                                                */
    4128             :     /* -------------------------------------------------------------------- */
    4129      210779 :     for (int nBand = 1; nBand <= poSrcDS->GetRasterCount(); ++nBand)
    4130             :     {
    4131      205730 :         GDALRasterBand *poBand = poSrcDS->GetRasterBand(nBand);
    4132             : 
    4133      205730 :         if (bSrcIsGeoTIFF)
    4134             :         {
    4135             :             GTiffRasterBand *poSrcBandGTiff =
    4136      201082 :                 cpl::down_cast<GTiffRasterBand *>(poBand);
    4137      201082 :             assert(poSrcBandGTiff);
    4138      201082 :             WriteMDMetadata(&poSrcBandGTiff->m_oGTiffMDMD, l_hTIFF, &psRoot,
    4139             :                             &psTail, nBand, eProfile);
    4140             :         }
    4141             :         else
    4142             :         {
    4143        9296 :             GDALMultiDomainMetadata l_oMDMD;
    4144        4648 :             bool bOMDMDSet = false;
    4145             : 
    4146        4648 :             if (EQUAL(pszCopySrcMDD, "AUTO") && !papszSrcMDD)
    4147             :             {
    4148       13908 :                 for (const char *pszDomain : {"", "IMAGERY"})
    4149             :                 {
    4150        9272 :                     if (CSLConstList papszMD = poBand->GetMetadata(pszDomain))
    4151             :                     {
    4152          83 :                         if (papszMD[0])
    4153             :                         {
    4154          83 :                             bOMDMDSet = true;
    4155          83 :                             l_oMDMD.SetMetadata(papszMD, pszDomain);
    4156             :                         }
    4157             :                     }
    4158        4636 :                 }
    4159             :             }
    4160          12 :             else if (CPLTestBool(pszCopySrcMDD) || papszSrcMDD)
    4161             :             {
    4162           9 :                 char **papszDomainList = poBand->GetMetadataDomainList();
    4163           3 :                 for (const char *pszDomain :
    4164          15 :                      cpl::Iterate(CSLConstList(papszDomainList)))
    4165             :                 {
    4166           9 :                     if (pszDomain[0] != 0 &&
    4167           5 :                         !EQUAL(pszDomain, "IMAGE_STRUCTURE") &&
    4168           2 :                         (!papszSrcMDD ||
    4169           2 :                          CSLFindString(papszSrcMDD, pszDomain) >= 0))
    4170             :                     {
    4171           2 :                         bOMDMDSet = true;
    4172           2 :                         l_oMDMD.SetMetadata(poBand->GetMetadata(pszDomain),
    4173             :                                             pszDomain);
    4174             :                     }
    4175             :                 }
    4176           9 :                 CSLDestroy(papszDomainList);
    4177             :             }
    4178             : 
    4179        4648 :             if (bOMDMDSet)
    4180             :             {
    4181          85 :                 WriteMDMetadata(&l_oMDMD, l_hTIFF, &psRoot, &psTail, nBand,
    4182             :                                 eProfile);
    4183             :             }
    4184             :         }
    4185             : 
    4186      205730 :         const double dfOffset = poBand->GetOffset();
    4187      205730 :         const double dfScale = poBand->GetScale();
    4188      205730 :         bool bGeoTIFFScaleOffsetInZ = false;
    4189             :         double adfGeoTransform[6];
    4190             :         // Check if we have already encoded scale/offset in the GeoTIFF tags
    4191      205730 :         if (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None &&
    4192        5287 :             adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0 &&
    4193        5275 :             adfGeoTransform[5] < 0.0 && poSrcDS->GetSpatialRef() &&
    4194      211024 :             poSrcDS->GetSpatialRef()->IsVertical() &&
    4195           7 :             poSrcDS->GetRasterCount() == 1)
    4196             :         {
    4197           7 :             bGeoTIFFScaleOffsetInZ = true;
    4198             :         }
    4199             : 
    4200      205730 :         if ((dfOffset != 0.0 || dfScale != 1.0) && !bGeoTIFFScaleOffsetInZ)
    4201             :         {
    4202          13 :             char szValue[128] = {};
    4203             : 
    4204          13 :             CPLsnprintf(szValue, sizeof(szValue), "%.17g", dfOffset);
    4205          13 :             AppendMetadataItem(&psRoot, &psTail, "OFFSET", szValue, nBand,
    4206             :                                "offset", "");
    4207          13 :             CPLsnprintf(szValue, sizeof(szValue), "%.17g", dfScale);
    4208          13 :             AppendMetadataItem(&psRoot, &psTail, "SCALE", szValue, nBand,
    4209             :                                "scale", "");
    4210             :         }
    4211             : 
    4212      205730 :         const char *pszUnitType = poBand->GetUnitType();
    4213      205730 :         if (pszUnitType != nullptr && pszUnitType[0] != '\0')
    4214             :         {
    4215          39 :             bool bWriteUnit = true;
    4216          39 :             auto poSRS = poSrcDS->GetSpatialRef();
    4217          39 :             if (poSRS && poSRS->IsCompound())
    4218             :             {
    4219           2 :                 const char *pszVertUnit = nullptr;
    4220           2 :                 poSRS->GetTargetLinearUnits("COMPD_CS|VERT_CS", &pszVertUnit);
    4221           2 :                 if (pszVertUnit && EQUAL(pszVertUnit, pszUnitType))
    4222             :                 {
    4223           2 :                     bWriteUnit = false;
    4224             :                 }
    4225             :             }
    4226          39 :             if (bWriteUnit)
    4227             :             {
    4228          37 :                 AppendMetadataItem(&psRoot, &psTail, "UNITTYPE", pszUnitType,
    4229             :                                    nBand, "unittype", "");
    4230             :             }
    4231             :         }
    4232             : 
    4233      205730 :         if (strlen(poBand->GetDescription()) > 0)
    4234             :         {
    4235          15 :             AppendMetadataItem(&psRoot, &psTail, "DESCRIPTION",
    4236          15 :                                poBand->GetDescription(), nBand, "description",
    4237             :                                "");
    4238             :         }
    4239             : 
    4240      205932 :         if (!bStandardColorInterp &&
    4241         202 :             !(nBand <= 3 && EQUAL(CSLFetchNameValueDef(papszCreationOptions,
    4242             :                                                        "PHOTOMETRIC", ""),
    4243             :                                   "RGB")))
    4244             :         {
    4245         234 :             AppendMetadataItem(&psRoot, &psTail, "COLORINTERP",
    4246             :                                GDALGetColorInterpretationName(
    4247         234 :                                    poBand->GetColorInterpretation()),
    4248             :                                nBand, "colorinterp", "");
    4249             :         }
    4250             :     }
    4251             : 
    4252        5049 :     CSLDestroy(papszSrcMDD);
    4253             : 
    4254             :     const char *pszTilingSchemeName =
    4255        5049 :         CSLFetchNameValue(papszCreationOptions, "@TILING_SCHEME_NAME");
    4256        5049 :     if (pszTilingSchemeName)
    4257             :     {
    4258          22 :         AppendMetadataItem(&psRoot, &psTail, "NAME", pszTilingSchemeName, 0,
    4259             :                            nullptr, "TILING_SCHEME");
    4260             : 
    4261          22 :         const char *pszZoomLevel = CSLFetchNameValue(
    4262             :             papszCreationOptions, "@TILING_SCHEME_ZOOM_LEVEL");
    4263          22 :         if (pszZoomLevel)
    4264             :         {
    4265          22 :             AppendMetadataItem(&psRoot, &psTail, "ZOOM_LEVEL", pszZoomLevel, 0,
    4266             :                                nullptr, "TILING_SCHEME");
    4267             :         }
    4268             : 
    4269          22 :         const char *pszAlignedLevels = CSLFetchNameValue(
    4270             :             papszCreationOptions, "@TILING_SCHEME_ALIGNED_LEVELS");
    4271          22 :         if (pszAlignedLevels)
    4272             :         {
    4273           4 :             AppendMetadataItem(&psRoot, &psTail, "ALIGNED_LEVELS",
    4274             :                                pszAlignedLevels, 0, nullptr, "TILING_SCHEME");
    4275             :         }
    4276             :     }
    4277             : 
    4278             :     /* -------------------------------------------------------------------- */
    4279             :     /*      Write information about some codecs.                            */
    4280             :     /* -------------------------------------------------------------------- */
    4281        5049 :     if (CPLTestBool(
    4282             :             CPLGetConfigOption("GTIFF_WRITE_IMAGE_STRUCTURE_METADATA", "YES")))
    4283             :     {
    4284             :         const char *pszCompress =
    4285        5044 :             CSLFetchNameValue(papszCreationOptions, "COMPRESS");
    4286        5044 :         if (pszCompress && EQUAL(pszCompress, "WEBP"))
    4287             :         {
    4288          31 :             if (GTiffGetWebPLossless(papszCreationOptions))
    4289             :             {
    4290           6 :                 AppendMetadataItem(&psRoot, &psTail,
    4291             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4292             :                                    nullptr, "IMAGE_STRUCTURE");
    4293             :             }
    4294             :             else
    4295             :             {
    4296          25 :                 AppendMetadataItem(
    4297             :                     &psRoot, &psTail, "WEBP_LEVEL",
    4298          25 :                     CPLSPrintf("%d", GTiffGetWebPLevel(papszCreationOptions)),
    4299             :                     0, nullptr, "IMAGE_STRUCTURE");
    4300             :             }
    4301             :         }
    4302        5013 :         else if (pszCompress && STARTS_WITH_CI(pszCompress, "LERC"))
    4303             :         {
    4304             :             const double dfMaxZError =
    4305          97 :                 GTiffGetLERCMaxZError(papszCreationOptions);
    4306             :             const double dfMaxZErrorOverview =
    4307          97 :                 GTiffGetLERCMaxZErrorOverview(papszCreationOptions);
    4308          97 :             if (dfMaxZError == 0.0 && dfMaxZErrorOverview == 0.0)
    4309             :             {
    4310          83 :                 AppendMetadataItem(&psRoot, &psTail,
    4311             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4312             :                                    nullptr, "IMAGE_STRUCTURE");
    4313             :             }
    4314             :             else
    4315             :             {
    4316          14 :                 AppendMetadataItem(&psRoot, &psTail, "MAX_Z_ERROR",
    4317             :                                    CSLFetchNameValueDef(papszCreationOptions,
    4318             :                                                         "MAX_Z_ERROR", ""),
    4319             :                                    0, nullptr, "IMAGE_STRUCTURE");
    4320          14 :                 if (dfMaxZError != dfMaxZErrorOverview)
    4321             :                 {
    4322           3 :                     AppendMetadataItem(
    4323             :                         &psRoot, &psTail, "MAX_Z_ERROR_OVERVIEW",
    4324             :                         CSLFetchNameValueDef(papszCreationOptions,
    4325             :                                              "MAX_Z_ERROR_OVERVIEW", ""),
    4326             :                         0, nullptr, "IMAGE_STRUCTURE");
    4327             :                 }
    4328          97 :             }
    4329             :         }
    4330             : #if HAVE_JXL
    4331        4916 :         else if (pszCompress && EQUAL(pszCompress, "JXL"))
    4332             :         {
    4333          98 :             float fDistance = 0.0f;
    4334          98 :             if (GTiffGetJXLLossless(papszCreationOptions))
    4335             :             {
    4336          80 :                 AppendMetadataItem(&psRoot, &psTail,
    4337             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4338             :                                    nullptr, "IMAGE_STRUCTURE");
    4339             :             }
    4340             :             else
    4341             :             {
    4342          18 :                 fDistance = GTiffGetJXLDistance(papszCreationOptions);
    4343          18 :                 AppendMetadataItem(&psRoot, &psTail, "JXL_DISTANCE",
    4344             :                                    CPLSPrintf("%f", fDistance), 0, nullptr,
    4345             :                                    "IMAGE_STRUCTURE");
    4346             :             }
    4347             :             const float fAlphaDistance =
    4348          98 :                 GTiffGetJXLAlphaDistance(papszCreationOptions);
    4349          98 :             if (fAlphaDistance >= 0.0f && fAlphaDistance != fDistance)
    4350             :             {
    4351           2 :                 AppendMetadataItem(&psRoot, &psTail, "JXL_ALPHA_DISTANCE",
    4352             :                                    CPLSPrintf("%f", fAlphaDistance), 0, nullptr,
    4353             :                                    "IMAGE_STRUCTURE");
    4354             :             }
    4355          98 :             AppendMetadataItem(
    4356             :                 &psRoot, &psTail, "JXL_EFFORT",
    4357             :                 CPLSPrintf("%d", GTiffGetJXLEffort(papszCreationOptions)), 0,
    4358             :                 nullptr, "IMAGE_STRUCTURE");
    4359             :         }
    4360             : #endif
    4361             :     }
    4362             : 
    4363             :     /* -------------------------------------------------------------------- */
    4364             :     /*      Write out the generic XML metadata if there is any.             */
    4365             :     /* -------------------------------------------------------------------- */
    4366        5049 :     if (psRoot != nullptr)
    4367             :     {
    4368         586 :         bool bRet = true;
    4369             : 
    4370         586 :         if (eProfile == GTiffProfile::GDALGEOTIFF)
    4371             :         {
    4372         569 :             char *pszXML_MD = CPLSerializeXMLTree(psRoot);
    4373         569 :             TIFFSetField(l_hTIFF, TIFFTAG_GDAL_METADATA, pszXML_MD);
    4374         569 :             CPLFree(pszXML_MD);
    4375             :         }
    4376             :         else
    4377             :         {
    4378          17 :             if (bSrcIsGeoTIFF)
    4379          11 :                 cpl::down_cast<GTiffDataset *>(poSrcDS)->PushMetadataToPam();
    4380             :             else
    4381           6 :                 bRet = false;
    4382             :         }
    4383             : 
    4384         586 :         CPLDestroyXMLNode(psRoot);
    4385             : 
    4386         586 :         return bRet;
    4387             :     }
    4388             : 
    4389             :     // If we have no more metadata but it existed before,
    4390             :     // remove the GDAL_METADATA tag.
    4391        4463 :     if (eProfile == GTiffProfile::GDALGEOTIFF)
    4392             :     {
    4393        4442 :         char *pszText = nullptr;
    4394        4442 :         if (TIFFGetField(l_hTIFF, TIFFTAG_GDAL_METADATA, &pszText))
    4395             :         {
    4396           6 :             TIFFUnsetField(l_hTIFF, TIFFTAG_GDAL_METADATA);
    4397             :         }
    4398             :     }
    4399             : 
    4400        4463 :     return true;
    4401             : }
    4402             : 
    4403             : /************************************************************************/
    4404             : /*                         PushMetadataToPam()                          */
    4405             : /*                                                                      */
    4406             : /*      When producing a strict profile TIFF or if our aggregate        */
    4407             : /*      metadata is too big for a single tiff tag we may end up         */
    4408             : /*      needing to write it via the PAM mechanisms.  This method        */
    4409             : /*      copies all the appropriate metadata into the PAM level          */
    4410             : /*      metadata object but with special care to avoid copying          */
    4411             : /*      metadata handled in other ways in TIFF format.                  */
    4412             : /************************************************************************/
    4413             : 
    4414          20 : void GTiffDataset::PushMetadataToPam()
    4415             : 
    4416             : {
    4417          20 :     if (GetPamFlags() & GPF_DISABLED)
    4418           0 :         return;
    4419             : 
    4420          20 :     const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
    4421          20 :         GDALDataset::ToHandle(this), m_nPhotometric, m_papszCreationOptions);
    4422             : 
    4423          66 :     for (int nBand = 0; nBand <= GetRasterCount(); ++nBand)
    4424             :     {
    4425          46 :         GDALMultiDomainMetadata *poSrcMDMD = nullptr;
    4426          46 :         GTiffRasterBand *poBand = nullptr;
    4427             : 
    4428          46 :         if (nBand == 0)
    4429             :         {
    4430          20 :             poSrcMDMD = &(this->m_oGTiffMDMD);
    4431             :         }
    4432             :         else
    4433             :         {
    4434          26 :             poBand = cpl::down_cast<GTiffRasterBand *>(GetRasterBand(nBand));
    4435          26 :             poSrcMDMD = &(poBand->m_oGTiffMDMD);
    4436             :         }
    4437             : 
    4438             :         /* --------------------------------------------------------------------
    4439             :          */
    4440             :         /*      Loop over the available domains. */
    4441             :         /* --------------------------------------------------------------------
    4442             :          */
    4443          46 :         CSLConstList papszDomainList = poSrcMDMD->GetDomainList();
    4444          96 :         for (int iDomain = 0; papszDomainList && papszDomainList[iDomain];
    4445             :              ++iDomain)
    4446             :         {
    4447          50 :             char **papszMD = poSrcMDMD->GetMetadata(papszDomainList[iDomain]);
    4448             : 
    4449          50 :             if (EQUAL(papszDomainList[iDomain], MD_DOMAIN_RPC) ||
    4450          50 :                 EQUAL(papszDomainList[iDomain], MD_DOMAIN_IMD) ||
    4451          50 :                 EQUAL(papszDomainList[iDomain], "_temporary_") ||
    4452          50 :                 EQUAL(papszDomainList[iDomain], "IMAGE_STRUCTURE") ||
    4453          30 :                 EQUAL(papszDomainList[iDomain], "COLOR_PROFILE"))
    4454          20 :                 continue;
    4455             : 
    4456          30 :             papszMD = CSLDuplicate(papszMD);
    4457             : 
    4458         105 :             for (int i = CSLCount(papszMD) - 1; i >= 0; --i)
    4459             :             {
    4460          75 :                 if (STARTS_WITH_CI(papszMD[i], "TIFFTAG_") ||
    4461          75 :                     EQUALN(papszMD[i], GDALMD_AREA_OR_POINT,
    4462             :                            strlen(GDALMD_AREA_OR_POINT)))
    4463           4 :                     papszMD = CSLRemoveStrings(papszMD, i, 1, nullptr);
    4464             :             }
    4465             : 
    4466          30 :             if (nBand == 0)
    4467          16 :                 GDALPamDataset::SetMetadata(papszMD, papszDomainList[iDomain]);
    4468             :             else
    4469          14 :                 poBand->GDALPamRasterBand::SetMetadata(
    4470          14 :                     papszMD, papszDomainList[iDomain]);
    4471             : 
    4472          30 :             CSLDestroy(papszMD);
    4473             :         }
    4474             : 
    4475             :         /* --------------------------------------------------------------------
    4476             :          */
    4477             :         /*      Handle some "special domain" stuff. */
    4478             :         /* --------------------------------------------------------------------
    4479             :          */
    4480          46 :         if (poBand != nullptr)
    4481             :         {
    4482          26 :             poBand->GDALPamRasterBand::SetOffset(poBand->GetOffset());
    4483          26 :             poBand->GDALPamRasterBand::SetScale(poBand->GetScale());
    4484          26 :             poBand->GDALPamRasterBand::SetUnitType(poBand->GetUnitType());
    4485          26 :             poBand->GDALPamRasterBand::SetDescription(poBand->GetDescription());
    4486          26 :             if (!bStandardColorInterp)
    4487             :             {
    4488           3 :                 poBand->GDALPamRasterBand::SetColorInterpretation(
    4489           3 :                     poBand->GetColorInterpretation());
    4490             :             }
    4491             :         }
    4492             :     }
    4493          20 :     MarkPamDirty();
    4494             : }
    4495             : 
    4496             : /************************************************************************/
    4497             : /*                         WriteNoDataValue()                           */
    4498             : /************************************************************************/
    4499             : 
    4500         361 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, double dfNoData)
    4501             : 
    4502             : {
    4503         722 :     CPLString osVal(GTiffFormatGDALNoDataTagValue(dfNoData));
    4504         361 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA, osVal.c_str());
    4505         361 : }
    4506             : 
    4507           2 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, int64_t nNoData)
    4508             : 
    4509             : {
    4510           2 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA,
    4511             :                  CPLSPrintf(CPL_FRMT_GIB, static_cast<GIntBig>(nNoData)));
    4512           2 : }
    4513             : 
    4514           2 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, uint64_t nNoData)
    4515             : 
    4516             : {
    4517           2 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA,
    4518             :                  CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoData)));
    4519           2 : }
    4520             : 
    4521             : /************************************************************************/
    4522             : /*                         UnsetNoDataValue()                           */
    4523             : /************************************************************************/
    4524             : 
    4525           1 : void GTiffDataset::UnsetNoDataValue(TIFF *l_hTIFF)
    4526             : 
    4527             : {
    4528           1 :     TIFFUnsetField(l_hTIFF, TIFFTAG_GDAL_NODATA);
    4529           1 : }
    4530             : 
    4531             : /************************************************************************/
    4532             : /*                             SaveICCProfile()                         */
    4533             : /*                                                                      */
    4534             : /*      Save ICC Profile or colorimetric data into file                 */
    4535             : /* pDS:                                                                 */
    4536             : /*      Dataset that contains the metadata with the ICC or colorimetric */
    4537             : /*      data. If this argument is specified, all other arguments are    */
    4538             : /*      ignored. Set them to NULL or 0.                                 */
    4539             : /* hTIFF:                                                               */
    4540             : /*      Pointer to TIFF handle. Only needed if pDS is NULL or           */
    4541             : /*      pDS->m_hTIFF is NULL.                                             */
    4542             : /* papszParamList:                                                       */
    4543             : /*      Options containing the ICC profile or colorimetric metadata.    */
    4544             : /*      Ignored if pDS is not NULL.                                     */
    4545             : /* nBitsPerSample:                                                      */
    4546             : /*      Bits per sample. Ignored if pDS is not NULL.                    */
    4547             : /************************************************************************/
    4548             : 
    4549        6880 : void GTiffDataset::SaveICCProfile(GTiffDataset *pDS, TIFF *l_hTIFF,
    4550             :                                   char **papszParamList,
    4551             :                                   uint32_t l_nBitsPerSample)
    4552             : {
    4553        6880 :     if ((pDS != nullptr) && (pDS->eAccess != GA_Update))
    4554           0 :         return;
    4555             : 
    4556        6880 :     if (l_hTIFF == nullptr)
    4557             :     {
    4558           2 :         if (pDS == nullptr)
    4559           0 :             return;
    4560             : 
    4561           2 :         l_hTIFF = pDS->m_hTIFF;
    4562           2 :         if (l_hTIFF == nullptr)
    4563           0 :             return;
    4564             :     }
    4565             : 
    4566        6880 :     if ((papszParamList == nullptr) && (pDS == nullptr))
    4567        2305 :         return;
    4568             : 
    4569        4575 :     const char *pszValue = nullptr;
    4570        4575 :     if (pDS != nullptr)
    4571           2 :         pszValue = pDS->GetMetadataItem("SOURCE_ICC_PROFILE", "COLOR_PROFILE");
    4572             :     else
    4573        4573 :         pszValue = CSLFetchNameValue(papszParamList, "SOURCE_ICC_PROFILE");
    4574        4575 :     if (pszValue != nullptr)
    4575             :     {
    4576           8 :         char *pEmbedBuffer = CPLStrdup(pszValue);
    4577             :         int32_t nEmbedLen =
    4578           8 :             CPLBase64DecodeInPlace(reinterpret_cast<GByte *>(pEmbedBuffer));
    4579             : 
    4580           8 :         TIFFSetField(l_hTIFF, TIFFTAG_ICCPROFILE, nEmbedLen, pEmbedBuffer);
    4581             : 
    4582           8 :         CPLFree(pEmbedBuffer);
    4583             :     }
    4584             :     else
    4585             :     {
    4586             :         // Output colorimetric data.
    4587        4567 :         float pCHR[6] = {};     // Primaries.
    4588        4567 :         uint16_t pTXR[6] = {};  // Transfer range.
    4589        4567 :         const char *pszCHRNames[] = {"SOURCE_PRIMARIES_RED",
    4590             :                                      "SOURCE_PRIMARIES_GREEN",
    4591             :                                      "SOURCE_PRIMARIES_BLUE"};
    4592        4567 :         const char *pszTXRNames[] = {"TIFFTAG_TRANSFERRANGE_BLACK",
    4593             :                                      "TIFFTAG_TRANSFERRANGE_WHITE"};
    4594             : 
    4595             :         // Output chromacities.
    4596        4567 :         bool bOutputCHR = true;
    4597        4582 :         for (int i = 0; i < 3 && bOutputCHR; ++i)
    4598             :         {
    4599        4577 :             if (pDS != nullptr)
    4600             :                 pszValue =
    4601           3 :                     pDS->GetMetadataItem(pszCHRNames[i], "COLOR_PROFILE");
    4602             :             else
    4603        4574 :                 pszValue = CSLFetchNameValue(papszParamList, pszCHRNames[i]);
    4604        4577 :             if (pszValue == nullptr)
    4605             :             {
    4606        4562 :                 bOutputCHR = false;
    4607        4562 :                 break;
    4608             :             }
    4609             : 
    4610          15 :             char **papszTokens = CSLTokenizeString2(pszValue, ",",
    4611             :                                                     CSLT_ALLOWEMPTYTOKENS |
    4612             :                                                         CSLT_STRIPLEADSPACES |
    4613             :                                                         CSLT_STRIPENDSPACES);
    4614             : 
    4615          15 :             if (CSLCount(papszTokens) != 3)
    4616             :             {
    4617           0 :                 bOutputCHR = false;
    4618           0 :                 CSLDestroy(papszTokens);
    4619           0 :                 break;
    4620             :             }
    4621             : 
    4622          60 :             for (int j = 0; j < 3; ++j)
    4623             :             {
    4624          45 :                 float v = static_cast<float>(CPLAtof(papszTokens[j]));
    4625             : 
    4626          45 :                 if (j == 2)
    4627             :                 {
    4628             :                     // Last term of xyY color must be 1.0.
    4629          15 :                     if (v != 1.0)
    4630             :                     {
    4631           0 :                         bOutputCHR = false;
    4632           0 :                         break;
    4633             :                     }
    4634             :                 }
    4635             :                 else
    4636             :                 {
    4637          30 :                     pCHR[i * 2 + j] = v;
    4638             :                 }
    4639             :             }
    4640             : 
    4641          15 :             CSLDestroy(papszTokens);
    4642             :         }
    4643             : 
    4644        4567 :         if (bOutputCHR)
    4645             :         {
    4646           5 :             TIFFSetField(l_hTIFF, TIFFTAG_PRIMARYCHROMATICITIES, pCHR);
    4647             :         }
    4648             : 
    4649             :         // Output whitepoint.
    4650        4567 :         if (pDS != nullptr)
    4651             :             pszValue =
    4652           1 :                 pDS->GetMetadataItem("SOURCE_WHITEPOINT", "COLOR_PROFILE");
    4653             :         else
    4654        4566 :             pszValue = CSLFetchNameValue(papszParamList, "SOURCE_WHITEPOINT");
    4655        4567 :         if (pszValue != nullptr)
    4656             :         {
    4657           5 :             char **papszTokens = CSLTokenizeString2(pszValue, ",",
    4658             :                                                     CSLT_ALLOWEMPTYTOKENS |
    4659             :                                                         CSLT_STRIPLEADSPACES |
    4660             :                                                         CSLT_STRIPENDSPACES);
    4661             : 
    4662           5 :             bool bOutputWhitepoint = true;
    4663           5 :             float pWP[2] = {0.0f, 0.0f};  // Whitepoint
    4664           5 :             if (CSLCount(papszTokens) != 3)
    4665             :             {
    4666           0 :                 bOutputWhitepoint = false;
    4667             :             }
    4668             :             else
    4669             :             {
    4670          20 :                 for (int j = 0; j < 3; ++j)
    4671             :                 {
    4672          15 :                     const float v = static_cast<float>(CPLAtof(papszTokens[j]));
    4673             : 
    4674          15 :                     if (j == 2)
    4675             :                     {
    4676             :                         // Last term of xyY color must be 1.0.
    4677           5 :                         if (v != 1.0)
    4678             :                         {
    4679           0 :                             bOutputWhitepoint = false;
    4680           0 :                             break;
    4681             :                         }
    4682             :                     }
    4683             :                     else
    4684             :                     {
    4685          10 :                         pWP[j] = v;
    4686             :                     }
    4687             :                 }
    4688             :             }
    4689           5 :             CSLDestroy(papszTokens);
    4690             : 
    4691           5 :             if (bOutputWhitepoint)
    4692             :             {
    4693           5 :                 TIFFSetField(l_hTIFF, TIFFTAG_WHITEPOINT, pWP);
    4694             :             }
    4695             :         }
    4696             : 
    4697             :         // Set transfer function metadata.
    4698        4567 :         char const *pszTFRed = nullptr;
    4699        4567 :         if (pDS != nullptr)
    4700           1 :             pszTFRed = pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_RED",
    4701             :                                             "COLOR_PROFILE");
    4702             :         else
    4703        4566 :             pszTFRed = CSLFetchNameValue(papszParamList,
    4704             :                                          "TIFFTAG_TRANSFERFUNCTION_RED");
    4705             : 
    4706        4567 :         char const *pszTFGreen = nullptr;
    4707        4567 :         if (pDS != nullptr)
    4708           1 :             pszTFGreen = pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_GREEN",
    4709             :                                               "COLOR_PROFILE");
    4710             :         else
    4711        4566 :             pszTFGreen = CSLFetchNameValue(papszParamList,
    4712             :                                            "TIFFTAG_TRANSFERFUNCTION_GREEN");
    4713             : 
    4714        4567 :         char const *pszTFBlue = nullptr;
    4715        4567 :         if (pDS != nullptr)
    4716           1 :             pszTFBlue = pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_BLUE",
    4717             :                                              "COLOR_PROFILE");
    4718             :         else
    4719        4566 :             pszTFBlue = CSLFetchNameValue(papszParamList,
    4720             :                                           "TIFFTAG_TRANSFERFUNCTION_BLUE");
    4721             : 
    4722        4567 :         if ((pszTFRed != nullptr) && (pszTFGreen != nullptr) &&
    4723             :             (pszTFBlue != nullptr))
    4724             :         {
    4725             :             // Get length of table.
    4726           4 :             const int nTransferFunctionLength =
    4727           4 :                 1 << ((pDS != nullptr) ? pDS->m_nBitsPerSample
    4728             :                                        : l_nBitsPerSample);
    4729             : 
    4730           4 :             char **papszTokensRed = CSLTokenizeString2(
    4731             :                 pszTFRed, ",",
    4732             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    4733             :                     CSLT_STRIPENDSPACES);
    4734           4 :             char **papszTokensGreen = CSLTokenizeString2(
    4735             :                 pszTFGreen, ",",
    4736             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    4737             :                     CSLT_STRIPENDSPACES);
    4738           4 :             char **papszTokensBlue = CSLTokenizeString2(
    4739             :                 pszTFBlue, ",",
    4740             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    4741             :                     CSLT_STRIPENDSPACES);
    4742             : 
    4743           4 :             if ((CSLCount(papszTokensRed) == nTransferFunctionLength) &&
    4744           8 :                 (CSLCount(papszTokensGreen) == nTransferFunctionLength) &&
    4745           4 :                 (CSLCount(papszTokensBlue) == nTransferFunctionLength))
    4746             :             {
    4747             :                 uint16_t *pTransferFuncRed = static_cast<uint16_t *>(
    4748           4 :                     CPLMalloc(sizeof(uint16_t) * nTransferFunctionLength));
    4749             :                 uint16_t *pTransferFuncGreen = static_cast<uint16_t *>(
    4750           4 :                     CPLMalloc(sizeof(uint16_t) * nTransferFunctionLength));
    4751             :                 uint16_t *pTransferFuncBlue = static_cast<uint16_t *>(
    4752           4 :                     CPLMalloc(sizeof(uint16_t) * nTransferFunctionLength));
    4753             : 
    4754             :                 // Convert our table in string format into int16_t format.
    4755        1028 :                 for (int i = 0; i < nTransferFunctionLength; ++i)
    4756             :                 {
    4757        1024 :                     pTransferFuncRed[i] =
    4758        1024 :                         static_cast<uint16_t>(atoi(papszTokensRed[i]));
    4759        1024 :                     pTransferFuncGreen[i] =
    4760        1024 :                         static_cast<uint16_t>(atoi(papszTokensGreen[i]));
    4761        1024 :                     pTransferFuncBlue[i] =
    4762        1024 :                         static_cast<uint16_t>(atoi(papszTokensBlue[i]));
    4763             :                 }
    4764             : 
    4765           4 :                 TIFFSetField(l_hTIFF, TIFFTAG_TRANSFERFUNCTION,
    4766             :                              pTransferFuncRed, pTransferFuncGreen,
    4767             :                              pTransferFuncBlue);
    4768             : 
    4769           4 :                 CPLFree(pTransferFuncRed);
    4770           4 :                 CPLFree(pTransferFuncGreen);
    4771           4 :                 CPLFree(pTransferFuncBlue);
    4772             :             }
    4773             : 
    4774           4 :             CSLDestroy(papszTokensRed);
    4775           4 :             CSLDestroy(papszTokensGreen);
    4776           4 :             CSLDestroy(papszTokensBlue);
    4777             :         }
    4778             : 
    4779             :         // Output transfer range.
    4780        4567 :         bool bOutputTransferRange = true;
    4781        4567 :         for (int i = 0; (i < 2) && bOutputTransferRange; ++i)
    4782             :         {
    4783        4567 :             if (pDS != nullptr)
    4784             :                 pszValue =
    4785           1 :                     pDS->GetMetadataItem(pszTXRNames[i], "COLOR_PROFILE");
    4786             :             else
    4787        4566 :                 pszValue = CSLFetchNameValue(papszParamList, pszTXRNames[i]);
    4788        4567 :             if (pszValue == nullptr)
    4789             :             {
    4790        4567 :                 bOutputTransferRange = false;
    4791        4567 :                 break;
    4792             :             }
    4793             : 
    4794           0 :             char **papszTokens = CSLTokenizeString2(pszValue, ",",
    4795             :                                                     CSLT_ALLOWEMPTYTOKENS |
    4796             :                                                         CSLT_STRIPLEADSPACES |
    4797             :                                                         CSLT_STRIPENDSPACES);
    4798             : 
    4799           0 :             if (CSLCount(papszTokens) != 3)
    4800             :             {
    4801           0 :                 bOutputTransferRange = false;
    4802           0 :                 CSLDestroy(papszTokens);
    4803           0 :                 break;
    4804             :             }
    4805             : 
    4806           0 :             for (int j = 0; j < 3; ++j)
    4807             :             {
    4808           0 :                 pTXR[i + j * 2] = static_cast<uint16_t>(atoi(papszTokens[j]));
    4809             :             }
    4810             : 
    4811           0 :             CSLDestroy(papszTokens);
    4812             :         }
    4813             : 
    4814        4567 :         if (bOutputTransferRange)
    4815             :         {
    4816           0 :             const int TIFFTAG_TRANSFERRANGE = 0x0156;
    4817           0 :             TIFFSetField(l_hTIFF, TIFFTAG_TRANSFERRANGE, pTXR);
    4818             :         }
    4819             :     }
    4820             : }
    4821             : 
    4822       11839 : static signed char GTiffGetLZMAPreset(char **papszOptions)
    4823             : {
    4824       11839 :     int nLZMAPreset = -1;
    4825       11839 :     const char *pszValue = CSLFetchNameValue(papszOptions, "LZMA_PRESET");
    4826       11839 :     if (pszValue != nullptr)
    4827             :     {
    4828          20 :         nLZMAPreset = atoi(pszValue);
    4829          20 :         if (!(nLZMAPreset >= 0 && nLZMAPreset <= 9))
    4830             :         {
    4831           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    4832             :                      "LZMA_PRESET=%s value not recognised, ignoring.",
    4833             :                      pszValue);
    4834           0 :             nLZMAPreset = -1;
    4835             :         }
    4836             :     }
    4837       11839 :     return static_cast<signed char>(nLZMAPreset);
    4838             : }
    4839             : 
    4840       11839 : static signed char GTiffGetZSTDPreset(char **papszOptions)
    4841             : {
    4842       11839 :     int nZSTDLevel = -1;
    4843       11839 :     const char *pszValue = CSLFetchNameValue(papszOptions, "ZSTD_LEVEL");
    4844       11839 :     if (pszValue != nullptr)
    4845             :     {
    4846          24 :         nZSTDLevel = atoi(pszValue);
    4847          24 :         if (!(nZSTDLevel >= 1 && nZSTDLevel <= 22))
    4848             :         {
    4849           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    4850             :                      "ZSTD_LEVEL=%s value not recognised, ignoring.", pszValue);
    4851           0 :             nZSTDLevel = -1;
    4852             :         }
    4853             :     }
    4854       11839 :     return static_cast<signed char>(nZSTDLevel);
    4855             : }
    4856             : 
    4857       11839 : static signed char GTiffGetZLevel(char **papszOptions)
    4858             : {
    4859       11839 :     int nZLevel = -1;
    4860       11839 :     const char *pszValue = CSLFetchNameValue(papszOptions, "ZLEVEL");
    4861       11839 :     if (pszValue != nullptr)
    4862             :     {
    4863          44 :         nZLevel = atoi(pszValue);
    4864             : #ifdef TIFFTAG_DEFLATE_SUBCODEC
    4865          44 :         constexpr int nMaxLevel = 12;
    4866             : #ifndef LIBDEFLATE_SUPPORT
    4867             :         if (nZLevel > 9 && nZLevel <= nMaxLevel)
    4868             :         {
    4869             :             CPLDebug("GTiff",
    4870             :                      "ZLEVEL=%d not supported in a non-libdeflate enabled "
    4871             :                      "libtiff build. Capping to 9",
    4872             :                      nZLevel);
    4873             :             nZLevel = 9;
    4874             :         }
    4875             : #endif
    4876             : #else
    4877             :         constexpr int nMaxLevel = 9;
    4878             : #endif
    4879          44 :         if (nZLevel < 1 || nZLevel > nMaxLevel)
    4880             :         {
    4881           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    4882             :                      "ZLEVEL=%s value not recognised, ignoring.", pszValue);
    4883           0 :             nZLevel = -1;
    4884             :         }
    4885             :     }
    4886       11839 :     return static_cast<signed char>(nZLevel);
    4887             : }
    4888             : 
    4889       11839 : static signed char GTiffGetJpegQuality(char **papszOptions)
    4890             : {
    4891       11839 :     int nJpegQuality = -1;
    4892       11839 :     const char *pszValue = CSLFetchNameValue(papszOptions, "JPEG_QUALITY");
    4893       11839 :     if (pszValue != nullptr)
    4894             :     {
    4895        1939 :         nJpegQuality = atoi(pszValue);
    4896        1939 :         if (nJpegQuality < 1 || nJpegQuality > 100)
    4897             :         {
    4898           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    4899             :                      "JPEG_QUALITY=%s value not recognised, ignoring.",
    4900             :                      pszValue);
    4901           0 :             nJpegQuality = -1;
    4902             :         }
    4903             :     }
    4904       11839 :     return static_cast<signed char>(nJpegQuality);
    4905             : }
    4906             : 
    4907       11839 : static signed char GTiffGetJpegTablesMode(char **papszOptions)
    4908             : {
    4909       11839 :     return static_cast<signed char>(atoi(
    4910             :         CSLFetchNameValueDef(papszOptions, "JPEGTABLESMODE",
    4911       11839 :                              CPLSPrintf("%d", knGTIFFJpegTablesModeDefault))));
    4912             : }
    4913             : 
    4914             : /************************************************************************/
    4915             : /*                        GetDiscardLsbOption()                         */
    4916             : /************************************************************************/
    4917             : 
    4918        4912 : static GTiffDataset::MaskOffset *GetDiscardLsbOption(TIFF *hTIFF,
    4919             :                                                      char **papszOptions)
    4920             : {
    4921        4912 :     const char *pszBits = CSLFetchNameValue(papszOptions, "DISCARD_LSB");
    4922        4912 :     if (pszBits == nullptr)
    4923        4790 :         return nullptr;
    4924             : 
    4925         122 :     uint16_t nPhotometric = 0;
    4926         122 :     TIFFGetFieldDefaulted(hTIFF, TIFFTAG_PHOTOMETRIC, &nPhotometric);
    4927             : 
    4928         122 :     uint16_t nBitsPerSample = 0;
    4929         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &nBitsPerSample))
    4930           0 :         nBitsPerSample = 1;
    4931             : 
    4932         122 :     uint16_t nSamplesPerPixel = 0;
    4933         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSamplesPerPixel))
    4934           0 :         nSamplesPerPixel = 1;
    4935             : 
    4936         122 :     uint16_t nSampleFormat = 0;
    4937         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_SAMPLEFORMAT, &nSampleFormat))
    4938           0 :         nSampleFormat = SAMPLEFORMAT_UINT;
    4939             : 
    4940         122 :     if (nPhotometric == PHOTOMETRIC_PALETTE)
    4941             :     {
    4942           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    4943             :                  "DISCARD_LSB ignored on a paletted image");
    4944           1 :         return nullptr;
    4945             :     }
    4946         121 :     if (!(nBitsPerSample == 8 || nBitsPerSample == 16 || nBitsPerSample == 32 ||
    4947          13 :           nBitsPerSample == 64))
    4948             :     {
    4949           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    4950             :                  "DISCARD_LSB ignored on non 8, 16, 32 or 64 bits images");
    4951           1 :         return nullptr;
    4952             :     }
    4953             : 
    4954         240 :     const CPLStringList aosTokens(CSLTokenizeString2(pszBits, ",", 0));
    4955         120 :     const int nTokens = aosTokens.size();
    4956         120 :     GTiffDataset::MaskOffset *panMaskOffsetLsb = nullptr;
    4957         120 :     if (nTokens == 1 || nTokens == nSamplesPerPixel)
    4958             :     {
    4959             :         panMaskOffsetLsb = static_cast<GTiffDataset::MaskOffset *>(
    4960         119 :             CPLCalloc(nSamplesPerPixel, sizeof(GTiffDataset::MaskOffset)));
    4961         374 :         for (int i = 0; i < nSamplesPerPixel; ++i)
    4962             :         {
    4963         255 :             const int nBits = atoi(aosTokens[nTokens == 1 ? 0 : i]);
    4964         510 :             const int nMaxBits = (nSampleFormat == SAMPLEFORMAT_IEEEFP)
    4965         484 :                                      ? ((nBitsPerSample == 32)   ? 23 - 1
    4966          26 :                                         : (nBitsPerSample == 64) ? 53 - 1
    4967             :                                                                  : 0)
    4968         203 :                                  : nSampleFormat == SAMPLEFORMAT_INT
    4969         203 :                                      ? nBitsPerSample - 2
    4970         119 :                                      : nBitsPerSample - 1;
    4971             : 
    4972         255 :             if (nBits < 0 || nBits > nMaxBits)
    4973             :             {
    4974           0 :                 CPLError(
    4975             :                     CE_Warning, CPLE_AppDefined,
    4976             :                     "DISCARD_LSB ignored: values should be in [0,%d] range",
    4977             :                     nMaxBits);
    4978           0 :                 VSIFree(panMaskOffsetLsb);
    4979           0 :                 return nullptr;
    4980             :             }
    4981         255 :             panMaskOffsetLsb[i].nMask =
    4982         255 :                 ~((static_cast<uint64_t>(1) << nBits) - 1);
    4983         255 :             if (nBits > 1)
    4984             :             {
    4985         249 :                 panMaskOffsetLsb[i].nRoundUpBitTest = static_cast<uint64_t>(1)
    4986         249 :                                                       << (nBits - 1);
    4987             :             }
    4988         119 :         }
    4989             :     }
    4990             :     else
    4991             :     {
    4992           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    4993             :                  "DISCARD_LSB ignored: wrong number of components");
    4994             :     }
    4995         120 :     return panMaskOffsetLsb;
    4996             : }
    4997             : 
    4998        4912 : void GTiffDataset::GetDiscardLsbOption(char **papszOptions)
    4999             : {
    5000        4912 :     m_panMaskOffsetLsb = ::GetDiscardLsbOption(m_hTIFF, papszOptions);
    5001        4912 : }
    5002             : 
    5003             : /************************************************************************/
    5004             : /*                             GetProfile()                             */
    5005             : /************************************************************************/
    5006             : 
    5007       11882 : static GTiffProfile GetProfile(const char *pszProfile)
    5008             : {
    5009       11882 :     GTiffProfile eProfile = GTiffProfile::GDALGEOTIFF;
    5010       11882 :     if (pszProfile != nullptr)
    5011             :     {
    5012          64 :         if (EQUAL(pszProfile, szPROFILE_BASELINE))
    5013          44 :             eProfile = GTiffProfile::BASELINE;
    5014          20 :         else if (EQUAL(pszProfile, szPROFILE_GeoTIFF))
    5015          18 :             eProfile = GTiffProfile::GEOTIFF;
    5016           2 :         else if (!EQUAL(pszProfile, szPROFILE_GDALGeoTIFF))
    5017             :         {
    5018           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    5019             :                      "Unsupported value for PROFILE: %s", pszProfile);
    5020             :         }
    5021             :     }
    5022       11882 :     return eProfile;
    5023             : }
    5024             : 
    5025             : /************************************************************************/
    5026             : /*                            GTiffCreate()                             */
    5027             : /*                                                                      */
    5028             : /*      Shared functionality between GTiffDataset::Create() and         */
    5029             : /*      GTiffCreateCopy() for creating TIFF file based on a set of      */
    5030             : /*      options and a configuration.                                    */
    5031             : /************************************************************************/
    5032             : 
    5033        6943 : TIFF *GTiffDataset::CreateLL(const char *pszFilename, int nXSize, int nYSize,
    5034             :                              int l_nBands, GDALDataType eType,
    5035             :                              double dfExtraSpaceForOverviews,
    5036             :                              int nColorTableMultiplier, char **papszParamList,
    5037             :                              VSILFILE **pfpL, CPLString &l_osTmpFilename)
    5038             : 
    5039             : {
    5040        6943 :     GTiffOneTimeInit();
    5041             : 
    5042             :     /* -------------------------------------------------------------------- */
    5043             :     /*      Blow on a few errors.                                           */
    5044             :     /* -------------------------------------------------------------------- */
    5045        6943 :     if (nXSize < 1 || nYSize < 1 || l_nBands < 1)
    5046             :     {
    5047           1 :         ReportError(
    5048             :             pszFilename, CE_Failure, CPLE_AppDefined,
    5049             :             "Attempt to create %dx%dx%d TIFF file, but width, height and bands"
    5050             :             "must be positive.",
    5051             :             nXSize, nYSize, l_nBands);
    5052             : 
    5053           1 :         return nullptr;
    5054             :     }
    5055             : 
    5056        6942 :     if (l_nBands > 65535)
    5057             :     {
    5058           1 :         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5059             :                     "Attempt to create %dx%dx%d TIFF file, but bands "
    5060             :                     "must be lesser or equal to 65535.",
    5061             :                     nXSize, nYSize, l_nBands);
    5062             : 
    5063           1 :         return nullptr;
    5064             :     }
    5065             : 
    5066             :     /* -------------------------------------------------------------------- */
    5067             :     /*      Setup values based on options.                                  */
    5068             :     /* -------------------------------------------------------------------- */
    5069             :     const GTiffProfile eProfile =
    5070        6941 :         GetProfile(CSLFetchNameValue(papszParamList, "PROFILE"));
    5071             : 
    5072        6941 :     const bool bTiled = CPLFetchBool(papszParamList, "TILED", false);
    5073             : 
    5074        6941 :     int l_nBlockXSize = 0;
    5075        6941 :     const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKXSIZE");
    5076        6941 :     if (pszValue != nullptr)
    5077             :     {
    5078         339 :         l_nBlockXSize = atoi(pszValue);
    5079         339 :         if (l_nBlockXSize < 0)
    5080             :         {
    5081           0 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5082             :                         "Invalid value for BLOCKXSIZE");
    5083           0 :             return nullptr;
    5084             :         }
    5085             :     }
    5086             : 
    5087        6941 :     int l_nBlockYSize = 0;
    5088        6941 :     pszValue = CSLFetchNameValue(papszParamList, "BLOCKYSIZE");
    5089        6941 :     if (pszValue != nullptr)
    5090             :     {
    5091        2406 :         l_nBlockYSize = atoi(pszValue);
    5092        2406 :         if (l_nBlockYSize < 0)
    5093             :         {
    5094           0 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5095             :                         "Invalid value for BLOCKYSIZE");
    5096           0 :             return nullptr;
    5097             :         }
    5098             :     }
    5099             : 
    5100        6941 :     if (bTiled)
    5101             :     {
    5102         631 :         if (l_nBlockXSize == 0)
    5103         300 :             l_nBlockXSize = 256;
    5104             : 
    5105         631 :         if (l_nBlockYSize == 0)
    5106         298 :             l_nBlockYSize = 256;
    5107             :     }
    5108             : 
    5109        6941 :     int nPlanar = 0;
    5110        6941 :     pszValue = CSLFetchNameValue(papszParamList, "INTERLEAVE");
    5111        6941 :     if (pszValue != nullptr)
    5112             :     {
    5113         531 :         if (EQUAL(pszValue, "PIXEL"))
    5114         229 :             nPlanar = PLANARCONFIG_CONTIG;
    5115         302 :         else if (EQUAL(pszValue, "BAND"))
    5116             :         {
    5117         301 :             nPlanar = PLANARCONFIG_SEPARATE;
    5118             :         }
    5119             :         else
    5120             :         {
    5121           1 :             ReportError(
    5122             :                 pszFilename, CE_Failure, CPLE_IllegalArg,
    5123             :                 "INTERLEAVE=%s unsupported, value must be PIXEL or BAND.",
    5124             :                 pszValue);
    5125           1 :             return nullptr;
    5126             :         }
    5127             :     }
    5128             :     else
    5129             :     {
    5130        6410 :         nPlanar = PLANARCONFIG_CONTIG;
    5131             :     }
    5132             : 
    5133        6940 :     int l_nCompression = COMPRESSION_NONE;
    5134        6940 :     pszValue = CSLFetchNameValue(papszParamList, "COMPRESS");
    5135        6940 :     if (pszValue != nullptr)
    5136             :     {
    5137        3086 :         l_nCompression = GTIFFGetCompressionMethod(pszValue, "COMPRESS");
    5138        3086 :         if (l_nCompression < 0)
    5139           0 :             return nullptr;
    5140             :     }
    5141             : 
    5142        6940 :     constexpr int JPEG_MAX_DIMENSION = 65500;  // Defined in jpeglib.h
    5143        6940 :     constexpr int WEBP_MAX_DIMENSION = 16383;
    5144             : 
    5145             :     const struct
    5146             :     {
    5147             :         int nCodecID;
    5148             :         const char *pszCodecName;
    5149             :         int nMaxDim;
    5150        6940 :     } asLimitations[] = {
    5151             :         {COMPRESSION_JPEG, "JPEG", JPEG_MAX_DIMENSION},
    5152             :         {COMPRESSION_WEBP, "WEBP", WEBP_MAX_DIMENSION},
    5153             :     };
    5154             : 
    5155       20808 :     for (const auto &sLimitation : asLimitations)
    5156             :     {
    5157       13876 :         if (l_nCompression == sLimitation.nCodecID && !bTiled &&
    5158        2073 :             nXSize > sLimitation.nMaxDim)
    5159             :         {
    5160           2 :             ReportError(
    5161             :                 pszFilename, CE_Failure, CPLE_IllegalArg,
    5162             :                 "COMPRESS=%s is only compatible of un-tiled images whose "
    5163             :                 "width is lesser or equal to %d pixels. "
    5164             :                 "To overcome this limitation, set the TILED=YES creation "
    5165             :                 "option.",
    5166           2 :                 sLimitation.pszCodecName, sLimitation.nMaxDim);
    5167           2 :             return nullptr;
    5168             :         }
    5169       13874 :         else if (l_nCompression == sLimitation.nCodecID && bTiled &&
    5170          51 :                  l_nBlockXSize > sLimitation.nMaxDim)
    5171             :         {
    5172           2 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5173             :                         "COMPRESS=%s is only compatible of tiled images whose "
    5174             :                         "BLOCKXSIZE is lesser or equal to %d pixels.",
    5175           2 :                         sLimitation.pszCodecName, sLimitation.nMaxDim);
    5176           2 :             return nullptr;
    5177             :         }
    5178       13872 :         else if (l_nCompression == sLimitation.nCodecID &&
    5179        2120 :                  l_nBlockYSize > sLimitation.nMaxDim)
    5180             :         {
    5181           4 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5182             :                         "COMPRESS=%s is only compatible of images whose "
    5183             :                         "BLOCKYSIZE is lesser or equal to %d pixels. "
    5184             :                         "To overcome this limitation, set the TILED=YES "
    5185             :                         "creation option",
    5186           4 :                         sLimitation.pszCodecName, sLimitation.nMaxDim);
    5187           4 :             return nullptr;
    5188             :         }
    5189             :     }
    5190             : 
    5191             :     /* -------------------------------------------------------------------- */
    5192             :     /*      How many bits per sample?  We have a special case if NBITS      */
    5193             :     /*      specified for GDT_Byte, GDT_UInt16, GDT_UInt32.                 */
    5194             :     /* -------------------------------------------------------------------- */
    5195        6932 :     int l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5196        6932 :     if (CSLFetchNameValue(papszParamList, "NBITS") != nullptr)
    5197             :     {
    5198        1750 :         int nMinBits = 0;
    5199        1750 :         int nMaxBits = 0;
    5200        1750 :         l_nBitsPerSample = atoi(CSLFetchNameValue(papszParamList, "NBITS"));
    5201        1750 :         if (eType == GDT_Byte)
    5202             :         {
    5203         528 :             nMinBits = 1;
    5204         528 :             nMaxBits = 8;
    5205             :         }
    5206        1222 :         else if (eType == GDT_UInt16)
    5207             :         {
    5208        1202 :             nMinBits = 9;
    5209        1202 :             nMaxBits = 16;
    5210             :         }
    5211          20 :         else if (eType == GDT_UInt32)
    5212             :         {
    5213          14 :             nMinBits = 17;
    5214          14 :             nMaxBits = 32;
    5215             :         }
    5216           6 :         else if (eType == GDT_Float32)
    5217             :         {
    5218           6 :             if (l_nBitsPerSample != 16 && l_nBitsPerSample != 32)
    5219             :             {
    5220           1 :                 ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5221             :                             "Only NBITS=16 is supported for data type Float32");
    5222           1 :                 l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5223             :             }
    5224             :         }
    5225             :         else
    5226             :         {
    5227           0 :             ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5228             :                         "NBITS is not supported for data type %s",
    5229             :                         GDALGetDataTypeName(eType));
    5230           0 :             l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5231             :         }
    5232             : 
    5233        1750 :         if (nMinBits != 0)
    5234             :         {
    5235        1744 :             if (l_nBitsPerSample < nMinBits)
    5236             :             {
    5237           2 :                 ReportError(
    5238             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    5239             :                     "NBITS=%d is invalid for data type %s. Using NBITS=%d",
    5240             :                     l_nBitsPerSample, GDALGetDataTypeName(eType), nMinBits);
    5241           2 :                 l_nBitsPerSample = nMinBits;
    5242             :             }
    5243        1742 :             else if (l_nBitsPerSample > nMaxBits)
    5244             :             {
    5245           3 :                 ReportError(
    5246             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    5247             :                     "NBITS=%d is invalid for data type %s. Using NBITS=%d",
    5248             :                     l_nBitsPerSample, GDALGetDataTypeName(eType), nMaxBits);
    5249           3 :                 l_nBitsPerSample = nMaxBits;
    5250             :             }
    5251             :         }
    5252             :     }
    5253             : 
    5254             : #ifdef HAVE_JXL
    5255        6932 :     if (l_nCompression == COMPRESSION_JXL && eType != GDT_Float32)
    5256             :     {
    5257             :         // Reflects tif_jxl's GetJXLDataType()
    5258          82 :         if (eType != GDT_Byte && eType != GDT_UInt16)
    5259             :         {
    5260           1 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5261             :                         "Data type %s not supported for JXL compression. Only "
    5262             :                         "Byte, UInt16, Float32 are supported",
    5263             :                         GDALGetDataTypeName(eType));
    5264           2 :             return nullptr;
    5265             :         }
    5266             : 
    5267             :         const struct
    5268             :         {
    5269             :             GDALDataType eDT;
    5270             :             int nBitsPerSample;
    5271          81 :         } asSupportedDTBitsPerSample[] = {
    5272             :             {GDT_Byte, 8},
    5273             :             {GDT_UInt16, 16},
    5274             :         };
    5275             : 
    5276         241 :         for (const auto &sSupportedDTBitsPerSample : asSupportedDTBitsPerSample)
    5277             :         {
    5278         161 :             if (eType == sSupportedDTBitsPerSample.eDT &&
    5279          81 :                 l_nBitsPerSample != sSupportedDTBitsPerSample.nBitsPerSample)
    5280             :             {
    5281           1 :                 ReportError(
    5282             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    5283             :                     "Bits per sample=%d not supported for JXL compression. "
    5284             :                     "Only %d is supported for %s data type.",
    5285           1 :                     l_nBitsPerSample, sSupportedDTBitsPerSample.nBitsPerSample,
    5286             :                     GDALGetDataTypeName(eType));
    5287           1 :                 return nullptr;
    5288             :             }
    5289             :         }
    5290             :     }
    5291             : #endif
    5292             : 
    5293        6930 :     int nPredictor = PREDICTOR_NONE;
    5294        6930 :     pszValue = CSLFetchNameValue(papszParamList, "PREDICTOR");
    5295        6930 :     if (pszValue != nullptr)
    5296             :     {
    5297          25 :         nPredictor = atoi(pszValue);
    5298             :     }
    5299             : 
    5300             :     // Do early checks as libtiff will only error out when starting to write.
    5301        6955 :     if (nPredictor != PREDICTOR_NONE &&
    5302          25 :         CPLTestBool(CPLGetConfigOption("GDAL_GTIFF_PREDICTOR_CHECKS", "YES")))
    5303             :     {
    5304             : #if (TIFFLIB_VERSION > 20210416) || defined(INTERNAL_LIBTIFF)
    5305             : #define HAVE_PREDICTOR_2_FOR_64BIT
    5306             : #endif
    5307          25 :         if (nPredictor == 2)
    5308             :         {
    5309          22 :             if (l_nBitsPerSample != 8 && l_nBitsPerSample != 16 &&
    5310             :                 l_nBitsPerSample != 32
    5311             : #ifdef HAVE_PREDICTOR_2_FOR_64BIT
    5312           2 :                 && l_nBitsPerSample != 64
    5313             : #endif
    5314             :             )
    5315             :             {
    5316             : #if !defined(HAVE_PREDICTOR_2_FOR_64BIT)
    5317             :                 if (l_nBitsPerSample == 64)
    5318             :                 {
    5319             :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5320             :                                 "PREDICTOR=2 is supported on 64 bit samples "
    5321             :                                 "starting with libtiff > 4.3.0.");
    5322             :                 }
    5323             :                 else
    5324             : #endif
    5325             :                 {
    5326           1 :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5327             : #ifdef HAVE_PREDICTOR_2_FOR_64BIT
    5328             :                                 "PREDICTOR=2 is only supported with 8/16/32/64 "
    5329             :                                 "bit samples."
    5330             : #else
    5331             :                                 "PREDICTOR=2 is only supported with 8/16/32 "
    5332             :                                 "bit samples."
    5333             : #endif
    5334             :                     );
    5335             :                 }
    5336           1 :                 return nullptr;
    5337             :             }
    5338             :         }
    5339           3 :         else if (nPredictor == 3)
    5340             :         {
    5341           2 :             if (eType != GDT_Float32 && eType != GDT_Float64)
    5342             :             {
    5343           1 :                 ReportError(
    5344             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    5345             :                     "PREDICTOR=3 is only supported with Float32 or Float64.");
    5346           1 :                 return nullptr;
    5347             :             }
    5348             :         }
    5349             :         else
    5350             :         {
    5351           1 :             ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5352             :                         "PREDICTOR=%s is not supported.", pszValue);
    5353           1 :             return nullptr;
    5354             :         }
    5355             :     }
    5356             : 
    5357        6927 :     const int l_nZLevel = GTiffGetZLevel(papszParamList);
    5358        6927 :     const int l_nLZMAPreset = GTiffGetLZMAPreset(papszParamList);
    5359        6927 :     const int l_nZSTDLevel = GTiffGetZSTDPreset(papszParamList);
    5360        6927 :     const int l_nWebPLevel = GTiffGetWebPLevel(papszParamList);
    5361        6927 :     const bool l_bWebPLossless = GTiffGetWebPLossless(papszParamList);
    5362        6927 :     const int l_nJpegQuality = GTiffGetJpegQuality(papszParamList);
    5363        6927 :     const int l_nJpegTablesMode = GTiffGetJpegTablesMode(papszParamList);
    5364        6927 :     const double l_dfMaxZError = GTiffGetLERCMaxZError(papszParamList);
    5365             : #if HAVE_JXL
    5366        6927 :     const bool l_bJXLLossless = GTiffGetJXLLossless(papszParamList);
    5367        6927 :     const uint32_t l_nJXLEffort = GTiffGetJXLEffort(papszParamList);
    5368        6927 :     const float l_fJXLDistance = GTiffGetJXLDistance(papszParamList);
    5369        6927 :     const float l_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszParamList);
    5370             : #endif
    5371             :     /* -------------------------------------------------------------------- */
    5372             :     /*      Streaming related code                                          */
    5373             :     /* -------------------------------------------------------------------- */
    5374       13854 :     const CPLString osOriFilename(pszFilename);
    5375       13854 :     bool bStreaming = strcmp(pszFilename, "/vsistdout/") == 0 ||
    5376        6927 :                       CPLFetchBool(papszParamList, "STREAMABLE_OUTPUT", false);
    5377             : #ifdef S_ISFIFO
    5378        6927 :     if (!bStreaming)
    5379             :     {
    5380             :         VSIStatBufL sStat;
    5381        6915 :         if (VSIStatExL(pszFilename, &sStat,
    5382        7793 :                        VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
    5383         878 :             S_ISFIFO(sStat.st_mode))
    5384             :         {
    5385           0 :             bStreaming = true;
    5386             :         }
    5387             :     }
    5388             : #endif
    5389        6927 :     if (bStreaming && !EQUAL("NONE", CSLFetchNameValueDef(papszParamList,
    5390             :                                                           "COMPRESS", "NONE")))
    5391             :     {
    5392           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5393             :                     "Streaming only supported to uncompressed TIFF");
    5394           1 :         return nullptr;
    5395             :     }
    5396        6926 :     if (bStreaming && CPLFetchBool(papszParamList, "SPARSE_OK", false))
    5397             :     {
    5398           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5399             :                     "Streaming not supported with SPARSE_OK");
    5400           1 :         return nullptr;
    5401             :     }
    5402             :     const bool bCopySrcOverviews =
    5403        6925 :         CPLFetchBool(papszParamList, "COPY_SRC_OVERVIEWS", false);
    5404        6925 :     if (bStreaming && bCopySrcOverviews)
    5405             :     {
    5406           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5407             :                     "Streaming not supported with COPY_SRC_OVERVIEWS");
    5408           1 :         return nullptr;
    5409             :     }
    5410        6924 :     if (bStreaming)
    5411             :     {
    5412           9 :         l_osTmpFilename = VSIMemGenerateHiddenFilename("vsistdout.tif");
    5413           9 :         pszFilename = l_osTmpFilename.c_str();
    5414             :     }
    5415             : 
    5416             :     /* -------------------------------------------------------------------- */
    5417             :     /*      Compute the uncompressed size.                                  */
    5418             :     /* -------------------------------------------------------------------- */
    5419        6924 :     const unsigned nTileXCount =
    5420        6924 :         bTiled ? DIV_ROUND_UP(nXSize, l_nBlockXSize) : 0;
    5421        6924 :     const unsigned nTileYCount =
    5422        6924 :         bTiled ? DIV_ROUND_UP(nYSize, l_nBlockYSize) : 0;
    5423             :     const double dfUncompressedImageSize =
    5424        6924 :         (bTiled ? (static_cast<double>(nTileXCount) * nTileYCount *
    5425         627 :                    l_nBlockXSize * l_nBlockYSize)
    5426        6297 :                 : (nXSize * static_cast<double>(nYSize))) *
    5427        6924 :             l_nBands * GDALGetDataTypeSizeBytes(eType) +
    5428        6924 :         dfExtraSpaceForOverviews;
    5429             : 
    5430             :     /* -------------------------------------------------------------------- */
    5431             :     /*      Should the file be created as a bigtiff file?                   */
    5432             :     /* -------------------------------------------------------------------- */
    5433        6924 :     const char *pszBIGTIFF = CSLFetchNameValue(papszParamList, "BIGTIFF");
    5434             : 
    5435        6924 :     if (pszBIGTIFF == nullptr)
    5436        6550 :         pszBIGTIFF = "IF_NEEDED";
    5437             : 
    5438        6924 :     bool bCreateBigTIFF = false;
    5439        6924 :     if (EQUAL(pszBIGTIFF, "IF_NEEDED"))
    5440             :     {
    5441        6551 :         if (l_nCompression == COMPRESSION_NONE &&
    5442             :             dfUncompressedImageSize > 4200000000.0)
    5443          16 :             bCreateBigTIFF = true;
    5444             :     }
    5445         373 :     else if (EQUAL(pszBIGTIFF, "IF_SAFER"))
    5446             :     {
    5447         355 :         if (dfUncompressedImageSize > 2000000000.0)
    5448           1 :             bCreateBigTIFF = true;
    5449             :     }
    5450             :     else
    5451             :     {
    5452          18 :         bCreateBigTIFF = CPLTestBool(pszBIGTIFF);
    5453          18 :         if (!bCreateBigTIFF && l_nCompression == COMPRESSION_NONE &&
    5454             :             dfUncompressedImageSize > 4200000000.0)
    5455             :         {
    5456           2 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5457             :                         "The TIFF file will be larger than 4GB, so BigTIFF is "
    5458             :                         "necessary.  Creation failed.");
    5459           2 :             return nullptr;
    5460             :         }
    5461             :     }
    5462             : 
    5463        6922 :     if (bCreateBigTIFF)
    5464          31 :         CPLDebug("GTiff", "File being created as a BigTIFF.");
    5465             : 
    5466             :     /* -------------------------------------------------------------------- */
    5467             :     /*      Sanity check.                                                   */
    5468             :     /* -------------------------------------------------------------------- */
    5469        6922 :     if (bTiled)
    5470             :     {
    5471             :         // libtiff implementation limitation
    5472         627 :         if (nTileXCount > 0x80000000U / (bCreateBigTIFF ? 8 : 4) / nTileYCount)
    5473             :         {
    5474           1 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5475             :                         "File too large regarding tile size. This would result "
    5476             :                         "in a file with tile arrays larger than 2GB");
    5477           1 :             return nullptr;
    5478             :         }
    5479             :     }
    5480             : 
    5481             :     /* -------------------------------------------------------------------- */
    5482             :     /*      Check free space (only for big, non sparse, uncompressed)       */
    5483             :     /* -------------------------------------------------------------------- */
    5484        3853 :     if (l_nCompression == COMPRESSION_NONE && dfUncompressedImageSize >= 1e9 &&
    5485          18 :         !CPLFetchBool(papszParamList, "SPARSE_OK", false) &&
    5486           3 :         osOriFilename != "/vsistdout/" &&
    5487       10777 :         osOriFilename != "/vsistdout_redirect/" &&
    5488           3 :         CPLTestBool(CPLGetConfigOption("CHECK_DISK_FREE_SPACE", "TRUE")))
    5489             :     {
    5490             :         GIntBig nFreeDiskSpace =
    5491           2 :             VSIGetDiskFreeSpace(CPLGetDirname(pszFilename));
    5492           2 :         if (nFreeDiskSpace >= 0 && nFreeDiskSpace < dfUncompressedImageSize)
    5493             :         {
    5494           1 :             ReportError(pszFilename, CE_Failure, CPLE_FileIO,
    5495             :                         "Free disk space available is " CPL_FRMT_GIB " bytes, "
    5496             :                         "whereas " CPL_FRMT_GIB " are at least necessary. "
    5497             :                         "You can disable this check by defining the "
    5498             :                         "CHECK_DISK_FREE_SPACE configuration option to FALSE.",
    5499             :                         nFreeDiskSpace,
    5500             :                         static_cast<GIntBig>(dfUncompressedImageSize));
    5501           1 :             return nullptr;
    5502             :         }
    5503             :     }
    5504             : 
    5505             :     /* -------------------------------------------------------------------- */
    5506             :     /*      Check if the user wishes a particular endianness                */
    5507             :     /* -------------------------------------------------------------------- */
    5508             : 
    5509        6920 :     int eEndianness = ENDIANNESS_NATIVE;
    5510        6920 :     pszValue = CSLFetchNameValue(papszParamList, "ENDIANNESS");
    5511        6920 :     if (pszValue == nullptr)
    5512        6858 :         pszValue = CPLGetConfigOption("GDAL_TIFF_ENDIANNESS", nullptr);
    5513        6920 :     if (pszValue != nullptr)
    5514             :     {
    5515         122 :         if (EQUAL(pszValue, "LITTLE"))
    5516             :         {
    5517          36 :             eEndianness = ENDIANNESS_LITTLE;
    5518             :         }
    5519          86 :         else if (EQUAL(pszValue, "BIG"))
    5520             :         {
    5521           1 :             eEndianness = ENDIANNESS_BIG;
    5522             :         }
    5523          85 :         else if (EQUAL(pszValue, "INVERTED"))
    5524             :         {
    5525             : #ifdef CPL_LSB
    5526          81 :             eEndianness = ENDIANNESS_BIG;
    5527             : #else
    5528             :             eEndianness = ENDIANNESS_LITTLE;
    5529             : #endif
    5530             :         }
    5531           4 :         else if (!EQUAL(pszValue, "NATIVE"))
    5532             :         {
    5533           1 :             ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5534             :                         "ENDIANNESS=%s not supported. Defaulting to NATIVE",
    5535             :                         pszValue);
    5536             :         }
    5537             :     }
    5538             : 
    5539             :     /* -------------------------------------------------------------------- */
    5540             :     /*      Try opening the dataset.                                        */
    5541             :     /* -------------------------------------------------------------------- */
    5542             : 
    5543             :     const bool bAppend =
    5544        6920 :         CPLFetchBool(papszParamList, "APPEND_SUBDATASET", false);
    5545             : 
    5546        6920 :     char szOpeningFlag[5] = {};
    5547        6920 :     strcpy(szOpeningFlag, bAppend ? "r+" : "w+");
    5548        6920 :     if (bCreateBigTIFF)
    5549          29 :         strcat(szOpeningFlag, "8");
    5550        6920 :     if (eEndianness == ENDIANNESS_BIG)
    5551          82 :         strcat(szOpeningFlag, "b");
    5552        6838 :     else if (eEndianness == ENDIANNESS_LITTLE)
    5553          36 :         strcat(szOpeningFlag, "l");
    5554             : 
    5555        6920 :     VSIErrorReset();
    5556        6920 :     VSILFILE *l_fpL = VSIFOpenExL(pszFilename, bAppend ? "r+b" : "w+b", true);
    5557        6920 :     if (l_fpL == nullptr)
    5558             :     {
    5559          15 :         VSIToCPLErrorWithMsg(CE_Failure, CPLE_OpenFailed,
    5560          30 :                              std::string("Attempt to create new tiff file `")
    5561          15 :                                  .append(pszFilename)
    5562          15 :                                  .append("' failed")
    5563             :                                  .c_str());
    5564          15 :         return nullptr;
    5565             :     }
    5566        6905 :     TIFF *l_hTIFF = VSI_TIFFOpen(pszFilename, szOpeningFlag, l_fpL);
    5567        6905 :     if (l_hTIFF == nullptr)
    5568             :     {
    5569           2 :         if (CPLGetLastErrorNo() == 0)
    5570           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    5571             :                      "Attempt to create new tiff file `%s' "
    5572             :                      "failed in XTIFFOpen().",
    5573             :                      pszFilename);
    5574           2 :         CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    5575           2 :         return nullptr;
    5576             :     }
    5577             : 
    5578        6903 :     if (bAppend)
    5579             :     {
    5580             :         // This is a bit of a hack to cause (*tif->tif_cleanup)(tif); to be
    5581             :         // called. See https://trac.osgeo.org/gdal/ticket/2055
    5582           5 :         TIFFSetField(l_hTIFF, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
    5583           5 :         TIFFFreeDirectory(l_hTIFF);
    5584           5 :         TIFFCreateDirectory(l_hTIFF);
    5585             :     }
    5586             : 
    5587             :     /* -------------------------------------------------------------------- */
    5588             :     /*      Do we have a custom pixel type (just used for signed byte now). */
    5589             :     /* -------------------------------------------------------------------- */
    5590        6903 :     const char *pszPixelType = CSLFetchNameValue(papszParamList, "PIXELTYPE");
    5591        6903 :     if (pszPixelType == nullptr)
    5592        6895 :         pszPixelType = "";
    5593        6903 :     if (eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE"))
    5594             :     {
    5595           8 :         CPLError(CE_Warning, CPLE_AppDefined,
    5596             :                  "Using PIXELTYPE=SIGNEDBYTE with Byte data type is deprecated "
    5597             :                  "(but still works). "
    5598             :                  "Using Int8 data type instead is now recommended.");
    5599             :     }
    5600             : 
    5601             :     /* -------------------------------------------------------------------- */
    5602             :     /*      Setup some standard flags.                                      */
    5603             :     /* -------------------------------------------------------------------- */
    5604        6903 :     TIFFSetField(l_hTIFF, TIFFTAG_IMAGEWIDTH, nXSize);
    5605        6903 :     TIFFSetField(l_hTIFF, TIFFTAG_IMAGELENGTH, nYSize);
    5606        6903 :     TIFFSetField(l_hTIFF, TIFFTAG_BITSPERSAMPLE, l_nBitsPerSample);
    5607             : 
    5608        6903 :     uint16_t l_nSampleFormat = 0;
    5609        6903 :     if ((eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE")) ||
    5610        6875 :         eType == GDT_Int8 || eType == GDT_Int16 || eType == GDT_Int32 ||
    5611             :         eType == GDT_Int64)
    5612         295 :         l_nSampleFormat = SAMPLEFORMAT_INT;
    5613        6608 :     else if (eType == GDT_CInt16 || eType == GDT_CInt32)
    5614         133 :         l_nSampleFormat = SAMPLEFORMAT_COMPLEXINT;
    5615        6475 :     else if (eType == GDT_Float32 || eType == GDT_Float64)
    5616         479 :         l_nSampleFormat = SAMPLEFORMAT_IEEEFP;
    5617        5996 :     else if (eType == GDT_CFloat32 || eType == GDT_CFloat64)
    5618         128 :         l_nSampleFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
    5619             :     else
    5620        5868 :         l_nSampleFormat = SAMPLEFORMAT_UINT;
    5621             : 
    5622        6903 :     TIFFSetField(l_hTIFF, TIFFTAG_SAMPLEFORMAT, l_nSampleFormat);
    5623        6903 :     TIFFSetField(l_hTIFF, TIFFTAG_SAMPLESPERPIXEL, l_nBands);
    5624        6903 :     TIFFSetField(l_hTIFF, TIFFTAG_PLANARCONFIG, nPlanar);
    5625             : 
    5626             :     /* -------------------------------------------------------------------- */
    5627             :     /*      Setup Photometric Interpretation. Take this value from the user */
    5628             :     /*      passed option or guess correct value otherwise.                 */
    5629             :     /* -------------------------------------------------------------------- */
    5630        6903 :     int nSamplesAccountedFor = 1;
    5631        6903 :     bool bForceColorTable = false;
    5632             : 
    5633        6903 :     pszValue = CSLFetchNameValue(papszParamList, "PHOTOMETRIC");
    5634        6903 :     if (pszValue != nullptr)
    5635             :     {
    5636        1867 :         if (EQUAL(pszValue, "MINISBLACK"))
    5637          14 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    5638        1853 :         else if (EQUAL(pszValue, "MINISWHITE"))
    5639             :         {
    5640           2 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
    5641             :         }
    5642        1851 :         else if (EQUAL(pszValue, "PALETTE"))
    5643             :         {
    5644           5 :             if (eType == GDT_Byte || eType == GDT_UInt16)
    5645             :             {
    5646           4 :                 TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    5647           4 :                 nSamplesAccountedFor = 1;
    5648           4 :                 bForceColorTable = true;
    5649             :             }
    5650             :             else
    5651             :             {
    5652           1 :                 ReportError(
    5653             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    5654             :                     "PHOTOMETRIC=PALETTE only compatible with Byte or UInt16");
    5655             :             }
    5656             :         }
    5657        1846 :         else if (EQUAL(pszValue, "RGB"))
    5658             :         {
    5659        1104 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    5660        1104 :             nSamplesAccountedFor = 3;
    5661             :         }
    5662         742 :         else if (EQUAL(pszValue, "CMYK"))
    5663             :         {
    5664          10 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
    5665          10 :             nSamplesAccountedFor = 4;
    5666             :         }
    5667         732 :         else if (EQUAL(pszValue, "YCBCR"))
    5668             :         {
    5669             :             // Because of subsampling, setting YCBCR without JPEG compression
    5670             :             // leads to a crash currently. Would need to make
    5671             :             // GTiffRasterBand::IWriteBlock() aware of subsampling so that it
    5672             :             // doesn't overrun buffer size returned by libtiff.
    5673         731 :             if (l_nCompression != COMPRESSION_JPEG)
    5674             :             {
    5675           1 :                 ReportError(
    5676             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    5677             :                     "Currently, PHOTOMETRIC=YCBCR requires COMPRESS=JPEG");
    5678           1 :                 XTIFFClose(l_hTIFF);
    5679           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    5680           1 :                 return nullptr;
    5681             :             }
    5682             : 
    5683         730 :             if (nPlanar == PLANARCONFIG_SEPARATE)
    5684             :             {
    5685           1 :                 ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5686             :                             "PHOTOMETRIC=YCBCR requires INTERLEAVE=PIXEL");
    5687           1 :                 XTIFFClose(l_hTIFF);
    5688           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    5689           1 :                 return nullptr;
    5690             :             }
    5691             : 
    5692             :             // YCBCR strictly requires 3 bands. Not less, not more Issue an
    5693             :             // explicit error message as libtiff one is a bit cryptic:
    5694             :             // TIFFVStripSize64:Invalid td_samplesperpixel value.
    5695         729 :             if (l_nBands != 3)
    5696             :             {
    5697           1 :                 ReportError(
    5698             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    5699             :                     "PHOTOMETRIC=YCBCR not supported on a %d-band raster: "
    5700             :                     "only compatible of a 3-band (RGB) raster",
    5701             :                     l_nBands);
    5702           1 :                 XTIFFClose(l_hTIFF);
    5703           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    5704           1 :                 return nullptr;
    5705             :             }
    5706             : 
    5707         728 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
    5708         728 :             nSamplesAccountedFor = 3;
    5709             : 
    5710             :             // Explicitly register the subsampling so that JPEGFixupTags
    5711             :             // is a no-op (helps for cloud optimized geotiffs)
    5712         728 :             TIFFSetField(l_hTIFF, TIFFTAG_YCBCRSUBSAMPLING, 2, 2);
    5713             :         }
    5714           1 :         else if (EQUAL(pszValue, "CIELAB"))
    5715             :         {
    5716           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB);
    5717           0 :             nSamplesAccountedFor = 3;
    5718             :         }
    5719           1 :         else if (EQUAL(pszValue, "ICCLAB"))
    5720             :         {
    5721           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ICCLAB);
    5722           0 :             nSamplesAccountedFor = 3;
    5723             :         }
    5724           1 :         else if (EQUAL(pszValue, "ITULAB"))
    5725             :         {
    5726           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ITULAB);
    5727           0 :             nSamplesAccountedFor = 3;
    5728             :         }
    5729             :         else
    5730             :         {
    5731           1 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    5732             :                         "PHOTOMETRIC=%s value not recognised, ignoring.  "
    5733             :                         "Set the Photometric Interpretation as MINISBLACK.",
    5734             :                         pszValue);
    5735           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    5736             :         }
    5737             : 
    5738        1864 :         if (l_nBands < nSamplesAccountedFor)
    5739             :         {
    5740           1 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    5741             :                         "PHOTOMETRIC=%s value does not correspond to number "
    5742             :                         "of bands (%d), ignoring.  "
    5743             :                         "Set the Photometric Interpretation as MINISBLACK.",
    5744             :                         pszValue, l_nBands);
    5745           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    5746             :         }
    5747             :     }
    5748             :     else
    5749             :     {
    5750             :         // If image contains 3 or 4 bands and datatype is Byte then we will
    5751             :         // assume it is RGB. In all other cases assume it is MINISBLACK.
    5752        5036 :         if (l_nBands == 3 && eType == GDT_Byte)
    5753             :         {
    5754         244 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    5755         244 :             nSamplesAccountedFor = 3;
    5756             :         }
    5757        4792 :         else if (l_nBands == 4 && eType == GDT_Byte)
    5758             :         {
    5759             :             uint16_t v[1] = {
    5760         713 :                 GTiffGetAlphaValue(CSLFetchNameValue(papszParamList, "ALPHA"),
    5761         713 :                                    DEFAULT_ALPHA_TYPE)};
    5762             : 
    5763         713 :             TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
    5764         713 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    5765         713 :             nSamplesAccountedFor = 4;
    5766             :         }
    5767             :         else
    5768             :         {
    5769        4079 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    5770        4079 :             nSamplesAccountedFor = 1;
    5771             :         }
    5772             :     }
    5773             : 
    5774             :     /* -------------------------------------------------------------------- */
    5775             :     /*      If there are extra samples, we need to mark them with an        */
    5776             :     /*      appropriate extrasamples definition here.                       */
    5777             :     /* -------------------------------------------------------------------- */
    5778        6900 :     if (l_nBands > nSamplesAccountedFor)
    5779             :     {
    5780        1120 :         const int nExtraSamples = l_nBands - nSamplesAccountedFor;
    5781             : 
    5782             :         uint16_t *v = static_cast<uint16_t *>(
    5783        1120 :             CPLMalloc(sizeof(uint16_t) * nExtraSamples));
    5784             : 
    5785        1120 :         v[0] = GTiffGetAlphaValue(CSLFetchNameValue(papszParamList, "ALPHA"),
    5786             :                                   EXTRASAMPLE_UNSPECIFIED);
    5787             : 
    5788      198758 :         for (int i = 1; i < nExtraSamples; ++i)
    5789      197638 :             v[i] = EXTRASAMPLE_UNSPECIFIED;
    5790             : 
    5791        1120 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples, v);
    5792             : 
    5793        1120 :         CPLFree(v);
    5794             :     }
    5795             : 
    5796             :     // Set the ICC color profile.
    5797        6900 :     if (eProfile != GTiffProfile::BASELINE)
    5798             :     {
    5799        6878 :         SaveICCProfile(nullptr, l_hTIFF, papszParamList, l_nBitsPerSample);
    5800             :     }
    5801             : 
    5802             :     // Set the compression method before asking the default strip size
    5803             :     // This is useful when translating to a JPEG-In-TIFF file where
    5804             :     // the default strip size is 8 or 16 depending on the photometric value.
    5805        6900 :     TIFFSetField(l_hTIFF, TIFFTAG_COMPRESSION, l_nCompression);
    5806             : 
    5807        6900 :     if (l_nCompression == COMPRESSION_LERC)
    5808             :     {
    5809             :         const char *pszCompress =
    5810          97 :             CSLFetchNameValueDef(papszParamList, "COMPRESS", "");
    5811          97 :         if (EQUAL(pszCompress, "LERC_DEFLATE"))
    5812             :         {
    5813          16 :             TIFFSetField(l_hTIFF, TIFFTAG_LERC_ADD_COMPRESSION,
    5814             :                          LERC_ADD_COMPRESSION_DEFLATE);
    5815             :         }
    5816          81 :         else if (EQUAL(pszCompress, "LERC_ZSTD"))
    5817             :         {
    5818          14 :             if (TIFFSetField(l_hTIFF, TIFFTAG_LERC_ADD_COMPRESSION,
    5819          14 :                              LERC_ADD_COMPRESSION_ZSTD) != 1)
    5820             :             {
    5821           0 :                 XTIFFClose(l_hTIFF);
    5822           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    5823           0 :                 return nullptr;
    5824             :             }
    5825             :         }
    5826             :     }
    5827             :     // TODO later: take into account LERC version
    5828             : 
    5829             :     /* -------------------------------------------------------------------- */
    5830             :     /*      Setup tiling/stripping flags.                                   */
    5831             :     /* -------------------------------------------------------------------- */
    5832        6900 :     if (bTiled)
    5833             :     {
    5834        1246 :         if (!TIFFSetField(l_hTIFF, TIFFTAG_TILEWIDTH, l_nBlockXSize) ||
    5835         623 :             !TIFFSetField(l_hTIFF, TIFFTAG_TILELENGTH, l_nBlockYSize))
    5836             :         {
    5837           1 :             XTIFFClose(l_hTIFF);
    5838           1 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    5839           1 :             return nullptr;
    5840             :         }
    5841             :     }
    5842             :     else
    5843             :     {
    5844        6277 :         const uint32_t l_nRowsPerStrip = std::min(
    5845             :             nYSize, l_nBlockYSize == 0
    5846        6277 :                         ? static_cast<int>(TIFFDefaultStripSize(l_hTIFF, 0))
    5847        6277 :                         : l_nBlockYSize);
    5848             : 
    5849        6277 :         TIFFSetField(l_hTIFF, TIFFTAG_ROWSPERSTRIP, l_nRowsPerStrip);
    5850             :     }
    5851             : 
    5852             :     /* -------------------------------------------------------------------- */
    5853             :     /*      Set compression related tags.                                   */
    5854             :     /* -------------------------------------------------------------------- */
    5855        6899 :     if (GTIFFSupportsPredictor(l_nCompression))
    5856         717 :         TIFFSetField(l_hTIFF, TIFFTAG_PREDICTOR, nPredictor);
    5857        6899 :     if (l_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    5858             :         l_nCompression == COMPRESSION_LERC)
    5859             :     {
    5860         259 :         GTiffSetDeflateSubCodec(l_hTIFF);
    5861             : 
    5862         259 :         if (l_nZLevel != -1)
    5863          22 :             TIFFSetField(l_hTIFF, TIFFTAG_ZIPQUALITY, l_nZLevel);
    5864             :     }
    5865        6899 :     if (l_nCompression == COMPRESSION_JPEG && l_nJpegQuality != -1)
    5866        1905 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGQUALITY, l_nJpegQuality);
    5867        6899 :     if (l_nCompression == COMPRESSION_LZMA && l_nLZMAPreset != -1)
    5868          10 :         TIFFSetField(l_hTIFF, TIFFTAG_LZMAPRESET, l_nLZMAPreset);
    5869        6899 :     if ((l_nCompression == COMPRESSION_ZSTD ||
    5870         133 :          l_nCompression == COMPRESSION_LERC) &&
    5871             :         l_nZSTDLevel != -1)
    5872          12 :         TIFFSetField(l_hTIFF, TIFFTAG_ZSTD_LEVEL, l_nZSTDLevel);
    5873        6899 :     if (l_nCompression == COMPRESSION_LERC)
    5874             :     {
    5875          97 :         TIFFSetField(l_hTIFF, TIFFTAG_LERC_MAXZERROR, l_dfMaxZError);
    5876             :     }
    5877             : #if HAVE_JXL
    5878        6899 :     if (l_nCompression == COMPRESSION_JXL)
    5879             :     {
    5880         101 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_LOSSYNESS,
    5881             :                      l_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
    5882         101 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_EFFORT, l_nJXLEffort);
    5883         101 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_DISTANCE, l_fJXLDistance);
    5884         101 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE, l_fJXLAlphaDistance);
    5885             :     }
    5886             : #endif
    5887        6899 :     if (l_nCompression == COMPRESSION_WEBP)
    5888          33 :         TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LEVEL, l_nWebPLevel);
    5889        6899 :     if (l_nCompression == COMPRESSION_WEBP && l_bWebPLossless)
    5890           7 :         TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LOSSLESS, 1);
    5891             : 
    5892        6899 :     if (l_nCompression == COMPRESSION_JPEG)
    5893        2081 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGTABLESMODE, l_nJpegTablesMode);
    5894             : 
    5895             :     /* -------------------------------------------------------------------- */
    5896             :     /*      If we forced production of a file with photometric=palette,     */
    5897             :     /*      we need to push out a default color table.                      */
    5898             :     /* -------------------------------------------------------------------- */
    5899        6899 :     if (bForceColorTable)
    5900             :     {
    5901           4 :         const int nColors = eType == GDT_Byte ? 256 : 65536;
    5902             : 
    5903             :         unsigned short *panTRed = static_cast<unsigned short *>(
    5904           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    5905             :         unsigned short *panTGreen = static_cast<unsigned short *>(
    5906           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    5907             :         unsigned short *panTBlue = static_cast<unsigned short *>(
    5908           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    5909             : 
    5910        1028 :         for (int iColor = 0; iColor < nColors; ++iColor)
    5911             :         {
    5912        1024 :             if (eType == GDT_Byte)
    5913             :             {
    5914        1024 :                 panTRed[iColor] = GTiffDataset::ClampCTEntry(
    5915             :                     iColor, 1, iColor, nColorTableMultiplier);
    5916        1024 :                 panTGreen[iColor] = GTiffDataset::ClampCTEntry(
    5917             :                     iColor, 2, iColor, nColorTableMultiplier);
    5918        1024 :                 panTBlue[iColor] = GTiffDataset::ClampCTEntry(
    5919             :                     iColor, 3, iColor, nColorTableMultiplier);
    5920             :             }
    5921             :             else
    5922             :             {
    5923           0 :                 panTRed[iColor] = static_cast<unsigned short>(iColor);
    5924           0 :                 panTGreen[iColor] = static_cast<unsigned short>(iColor);
    5925           0 :                 panTBlue[iColor] = static_cast<unsigned short>(iColor);
    5926             :             }
    5927             :         }
    5928             : 
    5929           4 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue);
    5930             : 
    5931           4 :         CPLFree(panTRed);
    5932           4 :         CPLFree(panTGreen);
    5933           4 :         CPLFree(panTBlue);
    5934             :     }
    5935             : 
    5936             :     // This trick
    5937             :     // creates a temporary in-memory file and fetches its JPEG tables so that
    5938             :     // we can directly set them, before tif_jpeg.c compute them at the first
    5939             :     // strip/tile writing, which is too late, since we have already crystalized
    5940             :     // the directory. This way we avoid a directory rewriting.
    5941        8980 :     if (l_nCompression == COMPRESSION_JPEG &&
    5942        2081 :         CPLTestBool(
    5943             :             CSLFetchNameValueDef(papszParamList, "WRITE_JPEGTABLE_TAG", "YES")))
    5944             :     {
    5945        1011 :         GTiffWriteJPEGTables(
    5946             :             l_hTIFF, CSLFetchNameValue(papszParamList, "PHOTOMETRIC"),
    5947             :             CSLFetchNameValue(papszParamList, "JPEG_QUALITY"),
    5948             :             CSLFetchNameValue(papszParamList, "JPEGTABLESMODE"));
    5949             :     }
    5950             : 
    5951        6899 :     *pfpL = l_fpL;
    5952             : 
    5953        6899 :     return l_hTIFF;
    5954             : }
    5955             : 
    5956             : /************************************************************************/
    5957             : /*                            GuessJPEGQuality()                        */
    5958             : /*                                                                      */
    5959             : /*      Guess JPEG quality from JPEGTABLES tag.                         */
    5960             : /************************************************************************/
    5961             : 
    5962        3804 : static const GByte *GTIFFFindNextTable(const GByte *paby, GByte byMarker,
    5963             :                                        int nLen, int *pnLenTable)
    5964             : {
    5965        7867 :     for (int i = 0; i + 1 < nLen;)
    5966             :     {
    5967        7867 :         if (paby[i] != 0xFF)
    5968           0 :             return nullptr;
    5969        7867 :         ++i;
    5970        7867 :         if (paby[i] == 0xD8)
    5971             :         {
    5972        3078 :             ++i;
    5973        3078 :             continue;
    5974             :         }
    5975        4789 :         if (i + 2 >= nLen)
    5976         809 :             return nullptr;
    5977        3980 :         int nMarkerLen = paby[i + 1] * 256 + paby[i + 2];
    5978        3980 :         if (i + 1 + nMarkerLen >= nLen)
    5979           0 :             return nullptr;
    5980        3980 :         if (paby[i] == byMarker)
    5981             :         {
    5982        2995 :             if (pnLenTable)
    5983        2466 :                 *pnLenTable = nMarkerLen;
    5984        2995 :             return paby + i + 1;
    5985             :         }
    5986         985 :         i += 1 + nMarkerLen;
    5987             :     }
    5988           0 :     return nullptr;
    5989             : }
    5990             : 
    5991             : constexpr GByte MARKER_HUFFMAN_TABLE = 0xC4;
    5992             : constexpr GByte MARKER_QUANT_TABLE = 0xDB;
    5993             : 
    5994             : // We assume that if there are several quantization tables, they are
    5995             : // in the same order. Which is a reasonable assumption for updating
    5996             : // a file generated by ourselves.
    5997         904 : static bool GTIFFQuantizationTablesEqual(const GByte *paby1, int nLen1,
    5998             :                                          const GByte *paby2, int nLen2)
    5999             : {
    6000         904 :     bool bFound = false;
    6001             :     while (true)
    6002             :     {
    6003         945 :         int nLenTable1 = 0;
    6004         945 :         int nLenTable2 = 0;
    6005             :         const GByte *paby1New =
    6006         945 :             GTIFFFindNextTable(paby1, MARKER_QUANT_TABLE, nLen1, &nLenTable1);
    6007             :         const GByte *paby2New =
    6008         945 :             GTIFFFindNextTable(paby2, MARKER_QUANT_TABLE, nLen2, &nLenTable2);
    6009         945 :         if (paby1New == nullptr && paby2New == nullptr)
    6010         904 :             return bFound;
    6011         911 :         if (paby1New == nullptr || paby2New == nullptr)
    6012           0 :             return false;
    6013         911 :         if (nLenTable1 != nLenTable2)
    6014         207 :             return false;
    6015         704 :         if (memcmp(paby1New, paby2New, nLenTable1) != 0)
    6016         663 :             return false;
    6017          41 :         paby1New += nLenTable1;
    6018          41 :         paby2New += nLenTable2;
    6019          41 :         nLen1 -= static_cast<int>(paby1New - paby1);
    6020          41 :         nLen2 -= static_cast<int>(paby2New - paby2);
    6021          41 :         paby1 = paby1New;
    6022          41 :         paby2 = paby2New;
    6023          41 :         bFound = true;
    6024          41 :     }
    6025             : }
    6026             : 
    6027             : // Guess the JPEG quality by comparing against the MD5Sum of precomputed
    6028             : // quantization tables
    6029         396 : static int GuessJPEGQualityFromMD5(const uint8_t md5JPEGQuantTable[][16],
    6030             :                                    const GByte *const pabyJPEGTable,
    6031             :                                    int nJPEGTableSize)
    6032             : {
    6033         396 :     int nRemainingLen = nJPEGTableSize;
    6034         396 :     const GByte *pabyCur = pabyJPEGTable;
    6035             : 
    6036             :     struct CPLMD5Context context;
    6037         396 :     CPLMD5Init(&context);
    6038             : 
    6039             :     while (true)
    6040             :     {
    6041        1040 :         int nLenTable = 0;
    6042        1040 :         const GByte *pabyNew = GTIFFFindNextTable(pabyCur, MARKER_QUANT_TABLE,
    6043             :                                                   nRemainingLen, &nLenTable);
    6044        1040 :         if (pabyNew == nullptr)
    6045         396 :             break;
    6046         644 :         CPLMD5Update(&context, pabyNew, nLenTable);
    6047         644 :         pabyNew += nLenTable;
    6048         644 :         nRemainingLen -= static_cast<int>(pabyNew - pabyCur);
    6049         644 :         pabyCur = pabyNew;
    6050         644 :     }
    6051             : 
    6052             :     GByte digest[16];
    6053         396 :     CPLMD5Final(digest, &context);
    6054             : 
    6055       27871 :     for (int i = 0; i < 100; i++)
    6056             :     {
    6057       27868 :         if (memcmp(md5JPEGQuantTable[i], digest, 16) == 0)
    6058             :         {
    6059         393 :             return i + 1;
    6060             :         }
    6061             :     }
    6062           3 :     return -1;
    6063             : }
    6064             : 
    6065         451 : int GTiffDataset::GuessJPEGQuality(bool &bOutHasQuantizationTable,
    6066             :                                    bool &bOutHasHuffmanTable)
    6067             : {
    6068         451 :     CPLAssert(m_nCompression == COMPRESSION_JPEG);
    6069         451 :     uint32_t nJPEGTableSize = 0;
    6070         451 :     void *pJPEGTable = nullptr;
    6071         451 :     if (!TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize,
    6072             :                       &pJPEGTable))
    6073             :     {
    6074          14 :         bOutHasQuantizationTable = false;
    6075          14 :         bOutHasHuffmanTable = false;
    6076          14 :         return -1;
    6077             :     }
    6078             : 
    6079         437 :     bOutHasQuantizationTable =
    6080         437 :         GTIFFFindNextTable(static_cast<const GByte *>(pJPEGTable),
    6081             :                            MARKER_QUANT_TABLE, nJPEGTableSize,
    6082         437 :                            nullptr) != nullptr;
    6083         437 :     bOutHasHuffmanTable =
    6084         437 :         GTIFFFindNextTable(static_cast<const GByte *>(pJPEGTable),
    6085             :                            MARKER_HUFFMAN_TABLE, nJPEGTableSize,
    6086         437 :                            nullptr) != nullptr;
    6087         437 :     if (!bOutHasQuantizationTable)
    6088           7 :         return -1;
    6089             : 
    6090         430 :     if ((nBands == 1 && m_nBitsPerSample == 8) ||
    6091         371 :         (nBands == 3 && m_nBitsPerSample == 8 &&
    6092         325 :          m_nPhotometric == PHOTOMETRIC_RGB) ||
    6093         294 :         (nBands == 4 && m_nBitsPerSample == 8 &&
    6094          27 :          m_nPhotometric == PHOTOMETRIC_SEPARATED))
    6095             :     {
    6096         148 :         return GuessJPEGQualityFromMD5(md5JPEGQuantTable_generic_8bit,
    6097             :                                        static_cast<const GByte *>(pJPEGTable),
    6098         148 :                                        static_cast<int>(nJPEGTableSize));
    6099             :     }
    6100             : 
    6101         282 :     if (nBands == 3 && m_nBitsPerSample == 8 &&
    6102         248 :         m_nPhotometric == PHOTOMETRIC_YCBCR)
    6103             :     {
    6104             :         int nRet =
    6105         248 :             GuessJPEGQualityFromMD5(md5JPEGQuantTable_3_YCBCR_8bit,
    6106             :                                     static_cast<const GByte *>(pJPEGTable),
    6107             :                                     static_cast<int>(nJPEGTableSize));
    6108         248 :         if (nRet < 0)
    6109             :         {
    6110             :             // libjpeg 9e has modified the YCbCr quantization tables.
    6111             :             nRet =
    6112           0 :                 GuessJPEGQualityFromMD5(md5JPEGQuantTable_3_YCBCR_8bit_jpeg9e,
    6113             :                                         static_cast<const GByte *>(pJPEGTable),
    6114             :                                         static_cast<int>(nJPEGTableSize));
    6115             :         }
    6116         248 :         return nRet;
    6117             :     }
    6118             : 
    6119          34 :     char **papszLocalParameters = nullptr;
    6120             :     papszLocalParameters =
    6121          34 :         CSLSetNameValue(papszLocalParameters, "COMPRESS", "JPEG");
    6122          34 :     if (m_nPhotometric == PHOTOMETRIC_YCBCR)
    6123             :         papszLocalParameters =
    6124           7 :             CSLSetNameValue(papszLocalParameters, "PHOTOMETRIC", "YCBCR");
    6125          27 :     else if (m_nPhotometric == PHOTOMETRIC_SEPARATED)
    6126             :         papszLocalParameters =
    6127           0 :             CSLSetNameValue(papszLocalParameters, "PHOTOMETRIC", "CMYK");
    6128             :     papszLocalParameters =
    6129          34 :         CSLSetNameValue(papszLocalParameters, "BLOCKYSIZE", "16");
    6130          34 :     if (m_nBitsPerSample == 12)
    6131             :         papszLocalParameters =
    6132          16 :             CSLSetNameValue(papszLocalParameters, "NBITS", "12");
    6133             : 
    6134             :     const CPLString osTmpFilenameIn(
    6135          34 :         VSIMemGenerateHiddenFilename("gtiffdataset_guess_jpeg_quality_tmp"));
    6136             : 
    6137          34 :     int nRet = -1;
    6138         938 :     for (int nQuality = 0; nQuality <= 100 && nRet < 0; ++nQuality)
    6139             :     {
    6140         904 :         VSILFILE *fpTmp = nullptr;
    6141         904 :         if (nQuality == 0)
    6142             :             papszLocalParameters =
    6143          34 :                 CSLSetNameValue(papszLocalParameters, "JPEG_QUALITY", "75");
    6144             :         else
    6145             :             papszLocalParameters =
    6146         870 :                 CSLSetNameValue(papszLocalParameters, "JPEG_QUALITY",
    6147             :                                 CPLSPrintf("%d", nQuality));
    6148             : 
    6149         904 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    6150         904 :         CPLString osTmp;
    6151             :         TIFF *hTIFFTmp =
    6152         904 :             CreateLL(osTmpFilenameIn, 16, 16, (nBands <= 4) ? nBands : 1,
    6153             :                      GetRasterBand(1)->GetRasterDataType(), 0.0, 0,
    6154             :                      papszLocalParameters, &fpTmp, osTmp);
    6155         904 :         CPLPopErrorHandler();
    6156         904 :         if (!hTIFFTmp)
    6157             :         {
    6158           0 :             break;
    6159             :         }
    6160             : 
    6161         904 :         TIFFWriteCheck(hTIFFTmp, FALSE, "CreateLL");
    6162         904 :         TIFFWriteDirectory(hTIFFTmp);
    6163         904 :         TIFFSetDirectory(hTIFFTmp, 0);
    6164             :         // Now reset jpegcolormode.
    6165        1196 :         if (m_nPhotometric == PHOTOMETRIC_YCBCR &&
    6166         292 :             CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
    6167             :         {
    6168         292 :             TIFFSetField(hTIFFTmp, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    6169             :         }
    6170             : 
    6171         904 :         GByte abyZeroData[(16 * 16 * 4 * 3) / 2] = {};
    6172         904 :         const int nBlockSize =
    6173         904 :             (16 * 16 * ((nBands <= 4) ? nBands : 1) * m_nBitsPerSample) / 8;
    6174         904 :         TIFFWriteEncodedStrip(hTIFFTmp, 0, abyZeroData, nBlockSize);
    6175             : 
    6176         904 :         uint32_t nJPEGTableSizeTry = 0;
    6177         904 :         void *pJPEGTableTry = nullptr;
    6178         904 :         if (TIFFGetField(hTIFFTmp, TIFFTAG_JPEGTABLES, &nJPEGTableSizeTry,
    6179         904 :                          &pJPEGTableTry))
    6180             :         {
    6181         904 :             if (GTIFFQuantizationTablesEqual(
    6182             :                     static_cast<GByte *>(pJPEGTable), nJPEGTableSize,
    6183             :                     static_cast<GByte *>(pJPEGTableTry), nJPEGTableSizeTry))
    6184             :             {
    6185          34 :                 nRet = (nQuality == 0) ? 75 : nQuality;
    6186             :             }
    6187             :         }
    6188             : 
    6189         904 :         XTIFFClose(hTIFFTmp);
    6190         904 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpTmp));
    6191             :     }
    6192             : 
    6193          34 :     CSLDestroy(papszLocalParameters);
    6194          34 :     VSIUnlink(osTmpFilenameIn);
    6195             : 
    6196          34 :     return nRet;
    6197             : }
    6198             : 
    6199             : /************************************************************************/
    6200             : /*               SetJPEGQualityAndTablesModeFromFile()                  */
    6201             : /************************************************************************/
    6202             : 
    6203         162 : void GTiffDataset::SetJPEGQualityAndTablesModeFromFile(
    6204             :     int nQuality, bool bHasQuantizationTable, bool bHasHuffmanTable)
    6205             : {
    6206         162 :     if (nQuality > 0)
    6207             :     {
    6208         155 :         CPLDebug("GTiff", "Guessed JPEG quality to be %d", nQuality);
    6209         155 :         m_nJpegQuality = static_cast<signed char>(nQuality);
    6210         155 :         TIFFSetField(m_hTIFF, TIFFTAG_JPEGQUALITY, nQuality);
    6211             : 
    6212             :         // This means we will use the quantization tables from the
    6213             :         // JpegTables tag.
    6214         155 :         m_nJpegTablesMode = JPEGTABLESMODE_QUANT;
    6215             :     }
    6216             :     else
    6217             :     {
    6218           7 :         uint32_t nJPEGTableSize = 0;
    6219           7 :         void *pJPEGTable = nullptr;
    6220           7 :         if (!TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize,
    6221             :                           &pJPEGTable))
    6222             :         {
    6223           4 :             toff_t *panByteCounts = nullptr;
    6224           8 :             const int nBlockCount = m_nPlanarConfig == PLANARCONFIG_SEPARATE
    6225           4 :                                         ? m_nBlocksPerBand * nBands
    6226             :                                         : m_nBlocksPerBand;
    6227           4 :             if (TIFFIsTiled(m_hTIFF))
    6228           1 :                 TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts);
    6229             :             else
    6230           3 :                 TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
    6231             : 
    6232           4 :             bool bFoundNonEmptyBlock = false;
    6233           4 :             if (panByteCounts != nullptr)
    6234             :             {
    6235          56 :                 for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
    6236             :                 {
    6237          53 :                     if (panByteCounts[iBlock] != 0)
    6238             :                     {
    6239           1 :                         bFoundNonEmptyBlock = true;
    6240           1 :                         break;
    6241             :                     }
    6242             :                 }
    6243             :             }
    6244           4 :             if (bFoundNonEmptyBlock)
    6245             :             {
    6246           1 :                 CPLDebug("GTiff", "Could not guess JPEG quality. "
    6247             :                                   "JPEG tables are missing, so going in "
    6248             :                                   "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6249             :                 // Write quantization tables in each strile.
    6250           1 :                 m_nJpegTablesMode = 0;
    6251             :             }
    6252             :         }
    6253             :         else
    6254             :         {
    6255           3 :             if (bHasQuantizationTable)
    6256             :             {
    6257             :                 // FIXME in libtiff: this is likely going to cause issues
    6258             :                 // since libtiff will reuse in each strile the number of
    6259             :                 // the global quantization table, which is invalid.
    6260           1 :                 CPLDebug("GTiff",
    6261             :                          "Could not guess JPEG quality although JPEG "
    6262             :                          "quantization tables are present, so going in "
    6263             :                          "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6264             :             }
    6265             :             else
    6266             :             {
    6267           2 :                 CPLDebug("GTiff",
    6268             :                          "Could not guess JPEG quality since JPEG "
    6269             :                          "quantization tables are not present, so going in "
    6270             :                          "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6271             :             }
    6272             : 
    6273             :             // Write quantization tables in each strile.
    6274           3 :             m_nJpegTablesMode = 0;
    6275             :         }
    6276             :     }
    6277         162 :     if (bHasHuffmanTable)
    6278             :     {
    6279             :         // If there are Huffman tables in header use them, otherwise
    6280             :         // if we use optimized tables, libtiff will currently reuse
    6281             :         // the number of the Huffman tables of the header for the
    6282             :         // optimized version of each strile, which is illegal.
    6283          23 :         m_nJpegTablesMode |= JPEGTABLESMODE_HUFF;
    6284             :     }
    6285         162 :     if (m_nJpegTablesMode >= 0)
    6286         160 :         TIFFSetField(m_hTIFF, TIFFTAG_JPEGTABLESMODE, m_nJpegTablesMode);
    6287         162 : }
    6288             : 
    6289             : /************************************************************************/
    6290             : /*                               Create()                               */
    6291             : /*                                                                      */
    6292             : /*      Create a new GeoTIFF or TIFF file.                              */
    6293             : /************************************************************************/
    6294             : 
    6295        3094 : GDALDataset *GTiffDataset::Create(const char *pszFilename, int nXSize,
    6296             :                                   int nYSize, int l_nBands, GDALDataType eType,
    6297             :                                   char **papszParamList)
    6298             : 
    6299             : {
    6300        3094 :     VSILFILE *l_fpL = nullptr;
    6301        6188 :     CPLString l_osTmpFilename;
    6302             : 
    6303             :     const int nColorTableMultiplier = std::max(
    6304        6188 :         1,
    6305        6188 :         std::min(257,
    6306        3094 :                  atoi(CSLFetchNameValueDef(
    6307             :                      papszParamList, "COLOR_TABLE_MULTIPLIER",
    6308        3094 :                      CPLSPrintf("%d", DEFAULT_COLOR_TABLE_MULTIPLIER_257)))));
    6309             : 
    6310             :     /* -------------------------------------------------------------------- */
    6311             :     /*      Create the underlying TIFF file.                                */
    6312             :     /* -------------------------------------------------------------------- */
    6313        3094 :     TIFF *l_hTIFF = CreateLL(pszFilename, nXSize, nYSize, l_nBands, eType, 0,
    6314             :                              nColorTableMultiplier, papszParamList, &l_fpL,
    6315             :                              l_osTmpFilename);
    6316        3094 :     const bool bStreaming = !l_osTmpFilename.empty();
    6317             : 
    6318        3094 :     if (l_hTIFF == nullptr)
    6319          30 :         return nullptr;
    6320             : 
    6321             :     /* -------------------------------------------------------------------- */
    6322             :     /*      Create the new GTiffDataset object.                             */
    6323             :     /* -------------------------------------------------------------------- */
    6324        3064 :     GTiffDataset *poDS = new GTiffDataset();
    6325        3064 :     poDS->m_hTIFF = l_hTIFF;
    6326        3064 :     poDS->m_fpL = l_fpL;
    6327        3064 :     if (bStreaming)
    6328             :     {
    6329           4 :         poDS->m_bStreamingOut = true;
    6330           4 :         poDS->m_pszTmpFilename = CPLStrdup(l_osTmpFilename);
    6331           4 :         poDS->m_fpToWrite = VSIFOpenL(pszFilename, "wb");
    6332           4 :         if (poDS->m_fpToWrite == nullptr)
    6333             :         {
    6334           1 :             VSIUnlink(l_osTmpFilename);
    6335           1 :             delete poDS;
    6336           1 :             return nullptr;
    6337             :         }
    6338             :     }
    6339        3063 :     poDS->nRasterXSize = nXSize;
    6340        3063 :     poDS->nRasterYSize = nYSize;
    6341        3063 :     poDS->eAccess = GA_Update;
    6342             : 
    6343        3063 :     poDS->m_nColorTableMultiplier = nColorTableMultiplier;
    6344             : 
    6345        3063 :     poDS->m_bCrystalized = false;
    6346        3063 :     poDS->m_nSamplesPerPixel = static_cast<uint16_t>(l_nBands);
    6347        3063 :     poDS->m_pszFilename = CPLStrdup(pszFilename);
    6348             : 
    6349             :     // Don't try to load external metadata files (#6597).
    6350        3063 :     poDS->m_bIMDRPCMetadataLoaded = true;
    6351             : 
    6352             :     // Avoid premature crystalization that will cause directory re-writing if
    6353             :     // GetProjectionRef() or GetGeoTransform() are called on the newly created
    6354             :     // GeoTIFF.
    6355        3063 :     poDS->m_bLookedForProjection = true;
    6356             : 
    6357        3063 :     TIFFGetField(l_hTIFF, TIFFTAG_SAMPLEFORMAT, &(poDS->m_nSampleFormat));
    6358        3063 :     TIFFGetField(l_hTIFF, TIFFTAG_PLANARCONFIG, &(poDS->m_nPlanarConfig));
    6359             :     // Weird that we need this, but otherwise we get a Valgrind warning on
    6360             :     // tiff_write_124.
    6361        3063 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &(poDS->m_nPhotometric)))
    6362           1 :         poDS->m_nPhotometric = PHOTOMETRIC_MINISBLACK;
    6363        3063 :     TIFFGetField(l_hTIFF, TIFFTAG_BITSPERSAMPLE, &(poDS->m_nBitsPerSample));
    6364        3063 :     TIFFGetField(l_hTIFF, TIFFTAG_COMPRESSION, &(poDS->m_nCompression));
    6365             : 
    6366        3063 :     if (TIFFIsTiled(l_hTIFF))
    6367             :     {
    6368         318 :         TIFFGetField(l_hTIFF, TIFFTAG_TILEWIDTH, &(poDS->m_nBlockXSize));
    6369         318 :         TIFFGetField(l_hTIFF, TIFFTAG_TILELENGTH, &(poDS->m_nBlockYSize));
    6370             :     }
    6371             :     else
    6372             :     {
    6373        2745 :         if (!TIFFGetField(l_hTIFF, TIFFTAG_ROWSPERSTRIP,
    6374             :                           &(poDS->m_nRowsPerStrip)))
    6375           0 :             poDS->m_nRowsPerStrip = 1;  // Dummy value.
    6376             : 
    6377        2745 :         poDS->m_nBlockXSize = nXSize;
    6378        2745 :         poDS->m_nBlockYSize =
    6379        2745 :             std::min(static_cast<int>(poDS->m_nRowsPerStrip), nYSize);
    6380             :     }
    6381             : 
    6382        3063 :     if (!poDS->ComputeBlocksPerColRowAndBand(l_nBands))
    6383             :     {
    6384           0 :         delete poDS;
    6385           0 :         return nullptr;
    6386             :     }
    6387             : 
    6388        3063 :     poDS->m_eProfile = GetProfile(CSLFetchNameValue(papszParamList, "PROFILE"));
    6389             : 
    6390             :     /* -------------------------------------------------------------------- */
    6391             :     /*      YCbCr JPEG compressed images should be translated on the fly    */
    6392             :     /*      to RGB by libtiff/libjpeg unless specifically requested         */
    6393             :     /*      otherwise.                                                      */
    6394             :     /* -------------------------------------------------------------------- */
    6395        6159 :     if (poDS->m_nCompression == COMPRESSION_JPEG &&
    6396        3083 :         poDS->m_nPhotometric == PHOTOMETRIC_YCBCR &&
    6397          20 :         CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
    6398             :     {
    6399          20 :         int nColorMode = 0;
    6400             : 
    6401          20 :         poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr", "IMAGE_STRUCTURE");
    6402          40 :         if (!TIFFGetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode) ||
    6403          20 :             nColorMode != JPEGCOLORMODE_RGB)
    6404          20 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    6405             :     }
    6406             : 
    6407        3063 :     if (poDS->m_nCompression == COMPRESSION_LERC)
    6408             :     {
    6409          26 :         uint32_t nLercParamCount = 0;
    6410          26 :         uint32_t *panLercParams = nullptr;
    6411          26 :         if (TIFFGetField(l_hTIFF, TIFFTAG_LERC_PARAMETERS, &nLercParamCount,
    6412          52 :                          &panLercParams) &&
    6413          26 :             nLercParamCount == 2)
    6414             :         {
    6415          26 :             memcpy(poDS->m_anLercAddCompressionAndVersion, panLercParams,
    6416             :                    sizeof(poDS->m_anLercAddCompressionAndVersion));
    6417             :         }
    6418             :     }
    6419             : 
    6420             :     /* -------------------------------------------------------------------- */
    6421             :     /*      Read palette back as a color table if it has one.               */
    6422             :     /* -------------------------------------------------------------------- */
    6423        3063 :     unsigned short *panRed = nullptr;
    6424        3063 :     unsigned short *panGreen = nullptr;
    6425        3063 :     unsigned short *panBlue = nullptr;
    6426             : 
    6427        3067 :     if (poDS->m_nPhotometric == PHOTOMETRIC_PALETTE &&
    6428           4 :         TIFFGetField(l_hTIFF, TIFFTAG_COLORMAP, &panRed, &panGreen, &panBlue))
    6429             :     {
    6430             : 
    6431           4 :         poDS->m_poColorTable = std::make_unique<GDALColorTable>();
    6432             : 
    6433           4 :         const int nColorCount = 1 << poDS->m_nBitsPerSample;
    6434             : 
    6435        1028 :         for (int iColor = nColorCount - 1; iColor >= 0; iColor--)
    6436             :         {
    6437        1024 :             const GDALColorEntry oEntry = {
    6438        1024 :                 static_cast<short>(panRed[iColor] / nColorTableMultiplier),
    6439        1024 :                 static_cast<short>(panGreen[iColor] / nColorTableMultiplier),
    6440        1024 :                 static_cast<short>(panBlue[iColor] / nColorTableMultiplier),
    6441        1024 :                 static_cast<short>(255)};
    6442             : 
    6443        1024 :             poDS->m_poColorTable->SetColorEntry(iColor, &oEntry);
    6444             :         }
    6445             :     }
    6446             : 
    6447             :     /* -------------------------------------------------------------------- */
    6448             :     /*      Do we want to ensure all blocks get written out on close to     */
    6449             :     /*      avoid sparse files?                                             */
    6450             :     /* -------------------------------------------------------------------- */
    6451        3063 :     if (!CPLFetchBool(papszParamList, "SPARSE_OK", false))
    6452        2959 :         poDS->m_bFillEmptyTilesAtClosing = true;
    6453             : 
    6454        3063 :     poDS->m_bWriteEmptyTiles =
    6455        3730 :         bStreaming || (poDS->m_nCompression != COMPRESSION_NONE &&
    6456         667 :                        poDS->m_bFillEmptyTilesAtClosing);
    6457             :     // Only required for people writing non-compressed striped files in the
    6458             :     // right order and wanting all tstrips to be written in the same order
    6459             :     // so that the end result can be memory mapped without knowledge of each
    6460             :     // strip offset.
    6461        3063 :     if (CPLTestBool(CSLFetchNameValueDef(
    6462        6126 :             papszParamList, "WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")) ||
    6463        3063 :         CPLTestBool(CSLFetchNameValueDef(
    6464             :             papszParamList, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")))
    6465             :     {
    6466          19 :         poDS->m_bWriteEmptyTiles = true;
    6467             :     }
    6468             : 
    6469             :     /* -------------------------------------------------------------------- */
    6470             :     /*      Preserve creation options for consulting later (for instance    */
    6471             :     /*      to decide if a TFW file should be written).                     */
    6472             :     /* -------------------------------------------------------------------- */
    6473        3063 :     poDS->m_papszCreationOptions = CSLDuplicate(papszParamList);
    6474             : 
    6475        3063 :     poDS->m_nZLevel = GTiffGetZLevel(papszParamList);
    6476        3063 :     poDS->m_nLZMAPreset = GTiffGetLZMAPreset(papszParamList);
    6477        3063 :     poDS->m_nZSTDLevel = GTiffGetZSTDPreset(papszParamList);
    6478        3063 :     poDS->m_nWebPLevel = GTiffGetWebPLevel(papszParamList);
    6479        3063 :     poDS->m_bWebPLossless = GTiffGetWebPLossless(papszParamList);
    6480        3065 :     if (poDS->m_nWebPLevel != 100 && poDS->m_bWebPLossless &&
    6481           2 :         CSLFetchNameValue(papszParamList, "WEBP_LEVEL"))
    6482             :     {
    6483           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    6484             :                  "WEBP_LEVEL is specified, but WEBP_LOSSLESS=YES. "
    6485             :                  "WEBP_LEVEL will be ignored.");
    6486             :     }
    6487        3063 :     poDS->m_nJpegQuality = GTiffGetJpegQuality(papszParamList);
    6488        3063 :     poDS->m_nJpegTablesMode = GTiffGetJpegTablesMode(papszParamList);
    6489        3063 :     poDS->m_dfMaxZError = GTiffGetLERCMaxZError(papszParamList);
    6490        3063 :     poDS->m_dfMaxZErrorOverview = GTiffGetLERCMaxZErrorOverview(papszParamList);
    6491             : #if HAVE_JXL
    6492        3063 :     poDS->m_bJXLLossless = GTiffGetJXLLossless(papszParamList);
    6493        3063 :     poDS->m_nJXLEffort = GTiffGetJXLEffort(papszParamList);
    6494        3063 :     poDS->m_fJXLDistance = GTiffGetJXLDistance(papszParamList);
    6495        3063 :     poDS->m_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszParamList);
    6496             : #endif
    6497        3063 :     poDS->InitCreationOrOpenOptions(true, papszParamList);
    6498             : 
    6499             :     /* -------------------------------------------------------------------- */
    6500             :     /*      Create band information objects.                                */
    6501             :     /* -------------------------------------------------------------------- */
    6502      203916 :     for (int iBand = 0; iBand < l_nBands; ++iBand)
    6503             :     {
    6504      200853 :         if (poDS->m_nBitsPerSample == 8 ||
    6505        1412 :             (poDS->m_nBitsPerSample == 16 && eType != GDT_Float32) ||
    6506         918 :             poDS->m_nBitsPerSample == 32 || poDS->m_nBitsPerSample == 64 ||
    6507         127 :             poDS->m_nBitsPerSample == 128)
    6508             :         {
    6509      200786 :             poDS->SetBand(iBand + 1, new GTiffRasterBand(poDS, iBand + 1));
    6510             :         }
    6511             :         else
    6512             :         {
    6513          67 :             poDS->SetBand(iBand + 1, new GTiffOddBitsBand(poDS, iBand + 1));
    6514         134 :             poDS->GetRasterBand(iBand + 1)->SetMetadataItem(
    6515         134 :                 "NBITS", CPLString().Printf("%d", poDS->m_nBitsPerSample),
    6516          67 :                 "IMAGE_STRUCTURE");
    6517             :         }
    6518             :     }
    6519             : 
    6520        3063 :     poDS->GetDiscardLsbOption(papszParamList);
    6521             : 
    6522        3063 :     if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG && l_nBands != 1)
    6523         532 :         poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
    6524             :     else
    6525        2531 :         poDS->SetMetadataItem("INTERLEAVE", "BAND", "IMAGE_STRUCTURE");
    6526             : 
    6527        3063 :     poDS->oOvManager.Initialize(poDS, pszFilename);
    6528             : 
    6529        3063 :     return poDS;
    6530             : }
    6531             : 
    6532             : /************************************************************************/
    6533             : /*                           CopyImageryAndMask()                       */
    6534             : /************************************************************************/
    6535             : 
    6536         270 : CPLErr GTiffDataset::CopyImageryAndMask(GTiffDataset *poDstDS,
    6537             :                                         GDALDataset *poSrcDS,
    6538             :                                         GDALRasterBand *poSrcMaskBand,
    6539             :                                         GDALProgressFunc pfnProgress,
    6540             :                                         void *pProgressData)
    6541             : {
    6542         270 :     CPLErr eErr = CE_None;
    6543             : 
    6544         270 :     const auto eType = poDstDS->GetRasterBand(1)->GetRasterDataType();
    6545         270 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
    6546         270 :     const int l_nBands = poDstDS->GetRasterCount();
    6547             :     void *pBlockBuffer =
    6548         270 :         VSI_MALLOC3_VERBOSE(poDstDS->m_nBlockXSize, poDstDS->m_nBlockYSize,
    6549             :                             cpl::fits_on<int>(l_nBands * nDataTypeSize));
    6550         270 :     if (pBlockBuffer == nullptr)
    6551             :     {
    6552           0 :         eErr = CE_Failure;
    6553             :     }
    6554         270 :     const int nYSize = poDstDS->nRasterYSize;
    6555         270 :     const int nXSize = poDstDS->nRasterXSize;
    6556         270 :     const int nBlocks = poDstDS->m_nBlocksPerBand;
    6557             : 
    6558         270 :     CPLAssert(l_nBands == 1 || poDstDS->m_nPlanarConfig == PLANARCONFIG_CONTIG);
    6559             : 
    6560             :     const bool bIsOddBand =
    6561         270 :         dynamic_cast<GTiffOddBitsBand *>(poDstDS->GetRasterBand(1)) != nullptr;
    6562             : 
    6563         270 :     if (poDstDS->m_poMaskDS)
    6564             :     {
    6565          49 :         CPLAssert(poDstDS->m_poMaskDS->m_nBlockXSize == poDstDS->m_nBlockXSize);
    6566          49 :         CPLAssert(poDstDS->m_poMaskDS->m_nBlockYSize == poDstDS->m_nBlockYSize);
    6567             :     }
    6568             : 
    6569         270 :     int iBlock = 0;
    6570        7020 :     for (int iY = 0, nYBlock = 0; iY < nYSize && eErr == CE_None;
    6571        6750 :          iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
    6572        6750 :                    ? nYSize
    6573        6552 :                    : iY + poDstDS->m_nBlockYSize),
    6574             :              nYBlock++)
    6575             :     {
    6576        6750 :         const int nReqYSize = std::min(nYSize - iY, poDstDS->m_nBlockYSize);
    6577       26374 :         for (int iX = 0, nXBlock = 0; iX < nXSize && eErr == CE_None;
    6578       19624 :              iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
    6579       19624 :                        ? nXSize
    6580       19358 :                        : iX + poDstDS->m_nBlockXSize),
    6581             :                  nXBlock++)
    6582             :         {
    6583       19624 :             const int nReqXSize = std::min(nXSize - iX, poDstDS->m_nBlockXSize);
    6584       19624 :             if (nReqXSize < poDstDS->m_nBlockXSize ||
    6585       19358 :                 nReqYSize < poDstDS->m_nBlockYSize)
    6586             :             {
    6587         416 :                 memset(pBlockBuffer, 0,
    6588         416 :                        static_cast<size_t>(poDstDS->m_nBlockXSize) *
    6589         416 :                            poDstDS->m_nBlockYSize * l_nBands * nDataTypeSize);
    6590             :             }
    6591             : 
    6592       19624 :             if (!bIsOddBand)
    6593             :             {
    6594       39126 :                 eErr = poSrcDS->RasterIO(
    6595             :                     GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    6596             :                     nReqXSize, nReqYSize, eType, l_nBands, nullptr,
    6597       19563 :                     static_cast<GSpacing>(nDataTypeSize) * l_nBands,
    6598       19563 :                     static_cast<GSpacing>(nDataTypeSize) * l_nBands *
    6599       19563 :                         poDstDS->m_nBlockXSize,
    6600             :                     nDataTypeSize, nullptr);
    6601       19563 :                 if (eErr == CE_None)
    6602             :                 {
    6603       19562 :                     eErr = poDstDS->WriteEncodedTileOrStrip(
    6604             :                         iBlock, pBlockBuffer, false);
    6605             :                 }
    6606             :             }
    6607             :             else
    6608             :             {
    6609             :                 // In the odd bit case, this is a bit messy to ensure
    6610             :                 // the strile gets written synchronously.
    6611             :                 // We load the content of the n-1 bands in the cache,
    6612             :                 // and for the last band we invoke WriteBlock() directly
    6613             :                 // We also force FlushBlockBuf()
    6614         122 :                 std::vector<GDALRasterBlock *> apoLockedBlocks;
    6615          91 :                 for (int i = 0; eErr == CE_None && i < l_nBands - 1; i++)
    6616             :                 {
    6617             :                     auto poBlock =
    6618          30 :                         poDstDS->GetRasterBand(i + 1)->GetLockedBlockRef(
    6619          30 :                             nXBlock, nYBlock, TRUE);
    6620          30 :                     if (poBlock)
    6621             :                     {
    6622          60 :                         eErr = poSrcDS->GetRasterBand(i + 1)->RasterIO(
    6623             :                             GF_Read, iX, iY, nReqXSize, nReqYSize,
    6624             :                             poBlock->GetDataRef(), nReqXSize, nReqYSize, eType,
    6625             :                             nDataTypeSize,
    6626          30 :                             static_cast<GSpacing>(nDataTypeSize) *
    6627          30 :                                 poDstDS->m_nBlockXSize,
    6628             :                             nullptr);
    6629          30 :                         poBlock->MarkDirty();
    6630          30 :                         apoLockedBlocks.emplace_back(poBlock);
    6631             :                     }
    6632             :                     else
    6633             :                     {
    6634           0 :                         eErr = CE_Failure;
    6635             :                     }
    6636             :                 }
    6637          61 :                 if (eErr == CE_None)
    6638             :                 {
    6639         122 :                     eErr = poSrcDS->GetRasterBand(l_nBands)->RasterIO(
    6640             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    6641             :                         nReqXSize, nReqYSize, eType, nDataTypeSize,
    6642          61 :                         static_cast<GSpacing>(nDataTypeSize) *
    6643          61 :                             poDstDS->m_nBlockXSize,
    6644             :                         nullptr);
    6645             :                 }
    6646          61 :                 if (eErr == CE_None)
    6647             :                 {
    6648             :                     // Avoid any attempt to load from disk
    6649          61 :                     poDstDS->m_nLoadedBlock = iBlock;
    6650          61 :                     eErr = poDstDS->GetRasterBand(l_nBands)->WriteBlock(
    6651             :                         nXBlock, nYBlock, pBlockBuffer);
    6652          61 :                     if (eErr == CE_None)
    6653          61 :                         eErr = poDstDS->FlushBlockBuf();
    6654             :                 }
    6655          91 :                 for (auto poBlock : apoLockedBlocks)
    6656             :                 {
    6657          30 :                     poBlock->MarkClean();
    6658          30 :                     poBlock->DropLock();
    6659             :                 }
    6660             :             }
    6661             : 
    6662       19624 :             if (eErr == CE_None && poDstDS->m_poMaskDS)
    6663             :             {
    6664        4626 :                 if (nReqXSize < poDstDS->m_nBlockXSize ||
    6665        4592 :                     nReqYSize < poDstDS->m_nBlockYSize)
    6666             :                 {
    6667          62 :                     memset(pBlockBuffer, 0,
    6668          62 :                            static_cast<size_t>(poDstDS->m_nBlockXSize) *
    6669          62 :                                poDstDS->m_nBlockYSize);
    6670             :                 }
    6671        9252 :                 eErr = poSrcMaskBand->RasterIO(
    6672             :                     GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    6673        4626 :                     nReqXSize, nReqYSize, GDT_Byte, 1, poDstDS->m_nBlockXSize,
    6674             :                     nullptr);
    6675        4626 :                 if (eErr == CE_None)
    6676             :                 {
    6677             :                     // Avoid any attempt to load from disk
    6678        4626 :                     poDstDS->m_poMaskDS->m_nLoadedBlock = iBlock;
    6679        4626 :                     eErr = poDstDS->m_poMaskDS->GetRasterBand(1)->WriteBlock(
    6680             :                         nXBlock, nYBlock, pBlockBuffer);
    6681        4626 :                     if (eErr == CE_None)
    6682        4626 :                         eErr = poDstDS->m_poMaskDS->FlushBlockBuf();
    6683             :                 }
    6684             :             }
    6685       19624 :             if (poDstDS->m_bWriteError)
    6686           6 :                 eErr = CE_Failure;
    6687             : 
    6688       19624 :             iBlock++;
    6689       39248 :             if (pfnProgress &&
    6690       19624 :                 !pfnProgress(static_cast<double>(iBlock) / nBlocks, nullptr,
    6691             :                              pProgressData))
    6692             :             {
    6693           0 :                 eErr = CE_Failure;
    6694             :             }
    6695             :         }
    6696             :     }
    6697         270 :     poDstDS->FlushCache(false);  // mostly to wait for thread completion
    6698         270 :     VSIFree(pBlockBuffer);
    6699             : 
    6700         270 :     return eErr;
    6701             : }
    6702             : 
    6703             : /************************************************************************/
    6704             : /*                             CreateCopy()                             */
    6705             : /************************************************************************/
    6706             : 
    6707        1880 : GDALDataset *GTiffDataset::CreateCopy(const char *pszFilename,
    6708             :                                       GDALDataset *poSrcDS, int bStrict,
    6709             :                                       char **papszOptions,
    6710             :                                       GDALProgressFunc pfnProgress,
    6711             :                                       void *pProgressData)
    6712             : 
    6713             : {
    6714        1880 :     if (poSrcDS->GetRasterCount() == 0)
    6715             :     {
    6716           2 :         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    6717             :                     "Unable to export GeoTIFF files with zero bands.");
    6718           2 :         return nullptr;
    6719             :     }
    6720             : 
    6721        1878 :     GDALRasterBand *const poPBand = poSrcDS->GetRasterBand(1);
    6722        1878 :     GDALDataType eType = poPBand->GetRasterDataType();
    6723             : 
    6724             :     /* -------------------------------------------------------------------- */
    6725             :     /*      Check, whether all bands in input dataset has the same type.    */
    6726             :     /* -------------------------------------------------------------------- */
    6727        1878 :     const int l_nBands = poSrcDS->GetRasterCount();
    6728        4670 :     for (int iBand = 2; iBand <= l_nBands; ++iBand)
    6729             :     {
    6730        2792 :         if (eType != poSrcDS->GetRasterBand(iBand)->GetRasterDataType())
    6731             :         {
    6732           0 :             if (bStrict)
    6733             :             {
    6734           0 :                 ReportError(
    6735             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    6736             :                     "Unable to export GeoTIFF file with different datatypes "
    6737             :                     "per different bands. All bands should have the same "
    6738             :                     "types in TIFF.");
    6739           0 :                 return nullptr;
    6740             :             }
    6741             :             else
    6742             :             {
    6743           0 :                 ReportError(
    6744             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    6745             :                     "Unable to export GeoTIFF file with different datatypes "
    6746             :                     "per different bands. All bands should have the same "
    6747             :                     "types in TIFF.");
    6748             :             }
    6749             :         }
    6750             :     }
    6751             : 
    6752             :     /* -------------------------------------------------------------------- */
    6753             :     /*      Capture the profile.                                            */
    6754             :     /* -------------------------------------------------------------------- */
    6755             :     const GTiffProfile eProfile =
    6756        1878 :         GetProfile(CSLFetchNameValue(papszOptions, "PROFILE"));
    6757             : 
    6758        1878 :     const bool bGeoTIFF = eProfile != GTiffProfile::BASELINE;
    6759             : 
    6760             :     /* -------------------------------------------------------------------- */
    6761             :     /*      Special handling for NBITS.  Copy from band metadata if found.  */
    6762             :     /* -------------------------------------------------------------------- */
    6763        1878 :     char **papszCreateOptions = CSLDuplicate(papszOptions);
    6764             : 
    6765        1878 :     if (poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE") != nullptr &&
    6766        1897 :         atoi(poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE")) > 0 &&
    6767          19 :         CSLFetchNameValue(papszCreateOptions, "NBITS") == nullptr)
    6768             :     {
    6769           5 :         papszCreateOptions = CSLSetNameValue(
    6770             :             papszCreateOptions, "NBITS",
    6771           5 :             poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE"));
    6772             :     }
    6773             : 
    6774        1878 :     if (CSLFetchNameValue(papszOptions, "PIXELTYPE") == nullptr &&
    6775             :         eType == GDT_Byte)
    6776             :     {
    6777        1585 :         poPBand->EnablePixelTypeSignedByteWarning(false);
    6778             :         const char *pszPixelType =
    6779        1585 :             poPBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
    6780        1585 :         poPBand->EnablePixelTypeSignedByteWarning(true);
    6781        1585 :         if (pszPixelType)
    6782             :         {
    6783           1 :             papszCreateOptions =
    6784           1 :                 CSLSetNameValue(papszCreateOptions, "PIXELTYPE", pszPixelType);
    6785             :         }
    6786             :     }
    6787             : 
    6788             :     /* -------------------------------------------------------------------- */
    6789             :     /*      Color profile.  Copy from band metadata if found.              */
    6790             :     /* -------------------------------------------------------------------- */
    6791        1878 :     if (bGeoTIFF)
    6792             :     {
    6793        1862 :         const char *pszOptionsMD[] = {"SOURCE_ICC_PROFILE",
    6794             :                                       "SOURCE_PRIMARIES_RED",
    6795             :                                       "SOURCE_PRIMARIES_GREEN",
    6796             :                                       "SOURCE_PRIMARIES_BLUE",
    6797             :                                       "SOURCE_WHITEPOINT",
    6798             :                                       "TIFFTAG_TRANSFERFUNCTION_RED",
    6799             :                                       "TIFFTAG_TRANSFERFUNCTION_GREEN",
    6800             :                                       "TIFFTAG_TRANSFERFUNCTION_BLUE",
    6801             :                                       "TIFFTAG_TRANSFERRANGE_BLACK",
    6802             :                                       "TIFFTAG_TRANSFERRANGE_WHITE",
    6803             :                                       nullptr};
    6804             : 
    6805             :         // Copy all the tags.  Options will override tags in the source.
    6806        1862 :         int i = 0;
    6807       20462 :         while (pszOptionsMD[i] != nullptr)
    6808             :         {
    6809             :             char const *pszMD =
    6810       18602 :                 CSLFetchNameValue(papszOptions, pszOptionsMD[i]);
    6811       18602 :             if (pszMD == nullptr)
    6812             :                 pszMD =
    6813       18594 :                     poSrcDS->GetMetadataItem(pszOptionsMD[i], "COLOR_PROFILE");
    6814             : 
    6815       18602 :             if ((pszMD != nullptr) && !EQUAL(pszMD, ""))
    6816             :             {
    6817          16 :                 papszCreateOptions =
    6818          16 :                     CSLSetNameValue(papszCreateOptions, pszOptionsMD[i], pszMD);
    6819             : 
    6820             :                 // If an ICC profile exists, other tags are not needed.
    6821          16 :                 if (EQUAL(pszOptionsMD[i], "SOURCE_ICC_PROFILE"))
    6822           2 :                     break;
    6823             :             }
    6824             : 
    6825       18600 :             ++i;
    6826             :         }
    6827             :     }
    6828             : 
    6829        1878 :     double dfExtraSpaceForOverviews = 0;
    6830             :     const bool bCopySrcOverviews =
    6831        1878 :         CPLFetchBool(papszCreateOptions, "COPY_SRC_OVERVIEWS", false);
    6832        1878 :     std::unique_ptr<GDALDataset> poOvrDS;
    6833        1878 :     int nSrcOverviews = 0;
    6834        1878 :     if (bCopySrcOverviews)
    6835             :     {
    6836             :         const char *pszOvrDS =
    6837         171 :             CSLFetchNameValue(papszCreateOptions, "@OVERVIEW_DATASET");
    6838         171 :         if (pszOvrDS)
    6839             :         {
    6840             :             // Empty string is used by COG driver to indicate that we want
    6841             :             // to ignore source overviews.
    6842          34 :             if (!EQUAL(pszOvrDS, ""))
    6843             :             {
    6844          32 :                 poOvrDS.reset(GDALDataset::Open(pszOvrDS));
    6845          32 :                 if (!poOvrDS)
    6846             :                 {
    6847           0 :                     CSLDestroy(papszCreateOptions);
    6848           0 :                     return nullptr;
    6849             :                 }
    6850          32 :                 if (poOvrDS->GetRasterCount() != l_nBands)
    6851             :                 {
    6852           0 :                     CSLDestroy(papszCreateOptions);
    6853           0 :                     return nullptr;
    6854             :                 }
    6855          32 :                 nSrcOverviews =
    6856          32 :                     poOvrDS->GetRasterBand(1)->GetOverviewCount() + 1;
    6857             :             }
    6858             :         }
    6859             :         else
    6860             :         {
    6861         137 :             nSrcOverviews = poSrcDS->GetRasterBand(1)->GetOverviewCount();
    6862             :         }
    6863             : 
    6864             :         // Limit number of overviews if specified
    6865             :         const char *pszOverviewCount =
    6866         171 :             CSLFetchNameValue(papszCreateOptions, "@OVERVIEW_COUNT");
    6867         171 :         if (pszOverviewCount)
    6868           8 :             nSrcOverviews =
    6869           8 :                 std::max(0, std::min(nSrcOverviews, atoi(pszOverviewCount)));
    6870             : 
    6871         171 :         if (nSrcOverviews)
    6872             :         {
    6873         166 :             for (int j = 1; j <= l_nBands; ++j)
    6874             :             {
    6875             :                 const int nOtherBandOverviewCount =
    6876         108 :                     poOvrDS ? poOvrDS->GetRasterBand(j)->GetOverviewCount() + 1
    6877         149 :                             : poSrcDS->GetRasterBand(j)->GetOverviewCount();
    6878         108 :                 if (nOtherBandOverviewCount < nSrcOverviews)
    6879             :                 {
    6880           1 :                     ReportError(
    6881             :                         pszFilename, CE_Failure, CPLE_NotSupported,
    6882             :                         "COPY_SRC_OVERVIEWS cannot be used when the bands have "
    6883             :                         "not the same number of overview levels.");
    6884           1 :                     CSLDestroy(papszCreateOptions);
    6885           1 :                     return nullptr;
    6886             :                 }
    6887         334 :                 for (int i = 0; i < nSrcOverviews; ++i)
    6888             :                 {
    6889             :                     GDALRasterBand *poOvrBand =
    6890             :                         poOvrDS
    6891         325 :                             ? (i == 0 ? poOvrDS->GetRasterBand(j)
    6892         192 :                                       : poOvrDS->GetRasterBand(j)->GetOverview(
    6893          96 :                                             i - 1))
    6894         295 :                             : poSrcDS->GetRasterBand(j)->GetOverview(i);
    6895         229 :                     if (poOvrBand == nullptr)
    6896             :                     {
    6897           1 :                         ReportError(
    6898             :                             pszFilename, CE_Failure, CPLE_NotSupported,
    6899             :                             "COPY_SRC_OVERVIEWS cannot be used when one "
    6900             :                             "overview band is NULL.");
    6901           1 :                         CSLDestroy(papszCreateOptions);
    6902           1 :                         return nullptr;
    6903             :                     }
    6904             :                     GDALRasterBand *poOvrFirstBand =
    6905             :                         poOvrDS
    6906         324 :                             ? (i == 0 ? poOvrDS->GetRasterBand(1)
    6907         192 :                                       : poOvrDS->GetRasterBand(1)->GetOverview(
    6908          96 :                                             i - 1))
    6909         293 :                             : poSrcDS->GetRasterBand(1)->GetOverview(i);
    6910         455 :                     if (poOvrBand->GetXSize() != poOvrFirstBand->GetXSize() ||
    6911         227 :                         poOvrBand->GetYSize() != poOvrFirstBand->GetYSize())
    6912             :                     {
    6913           1 :                         ReportError(
    6914             :                             pszFilename, CE_Failure, CPLE_NotSupported,
    6915             :                             "COPY_SRC_OVERVIEWS cannot be used when the "
    6916             :                             "overview bands have not the same dimensions "
    6917             :                             "among bands.");
    6918           1 :                         CSLDestroy(papszCreateOptions);
    6919           1 :                         return nullptr;
    6920             :                     }
    6921             :                 }
    6922             :             }
    6923             : 
    6924         172 :             for (int i = 0; i < nSrcOverviews; ++i)
    6925             :             {
    6926             :                 GDALRasterBand *poOvrFirstBand =
    6927             :                     poOvrDS
    6928         184 :                         ? (i == 0
    6929          70 :                                ? poOvrDS->GetRasterBand(1)
    6930          38 :                                : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    6931         158 :                         : poSrcDS->GetRasterBand(1)->GetOverview(i);
    6932         114 :                 dfExtraSpaceForOverviews +=
    6933         114 :                     static_cast<double>(poOvrFirstBand->GetXSize()) *
    6934         114 :                     poOvrFirstBand->GetYSize();
    6935             :             }
    6936          58 :             dfExtraSpaceForOverviews *=
    6937          58 :                 l_nBands * GDALGetDataTypeSizeBytes(eType);
    6938             :         }
    6939             :         else
    6940             :         {
    6941         110 :             CPLDebug("GTiff", "No source overviews to copy");
    6942             :         }
    6943             :     }
    6944             : 
    6945             : /* -------------------------------------------------------------------- */
    6946             : /*      Should we use optimized way of copying from an input JPEG       */
    6947             : /*      dataset?                                                        */
    6948             : /* -------------------------------------------------------------------- */
    6949             : 
    6950             : // TODO(schwehr): Refactor bDirectCopyFromJPEG to be a const.
    6951             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    6952        1875 :     bool bDirectCopyFromJPEG = false;
    6953             : #endif
    6954             : 
    6955             :     // Note: JPEG_DIRECT_COPY is not defined by default, because it is mainly
    6956             :     // useful for debugging purposes.
    6957             : #ifdef JPEG_DIRECT_COPY
    6958             :     if (CPLFetchBool(papszCreateOptions, "JPEG_DIRECT_COPY", false) &&
    6959             :         GTIFF_CanDirectCopyFromJPEG(poSrcDS, papszCreateOptions))
    6960             :     {
    6961             :         CPLDebug("GTiff", "Using special direct copy mode from a JPEG dataset");
    6962             : 
    6963             :         bDirectCopyFromJPEG = true;
    6964             :     }
    6965             : #endif
    6966             : 
    6967             : #ifdef HAVE_LIBJPEG
    6968        1875 :     bool bCopyFromJPEG = false;
    6969             : 
    6970             :     // When CreateCopy'ing() from a JPEG dataset, and asking for COMPRESS=JPEG,
    6971             :     // use DCT coefficients (unless other options are incompatible, like
    6972             :     // strip/tile dimensions, specifying JPEG_QUALITY option, incompatible
    6973             :     // PHOTOMETRIC with the source colorspace, etc.) to avoid the lossy steps
    6974             :     // involved by decompression/recompression.
    6975        3750 :     if (!bDirectCopyFromJPEG &&
    6976        1875 :         GTIFF_CanCopyFromJPEG(poSrcDS, papszCreateOptions))
    6977             :     {
    6978          12 :         CPLDebug("GTiff", "Using special copy mode from a JPEG dataset");
    6979             : 
    6980          12 :         bCopyFromJPEG = true;
    6981             :     }
    6982             : #endif
    6983             : 
    6984             :     /* -------------------------------------------------------------------- */
    6985             :     /*      If the source is RGB, then set the PHOTOMETRIC=RGB value        */
    6986             :     /* -------------------------------------------------------------------- */
    6987             : 
    6988             :     const bool bForcePhotometric =
    6989        1875 :         CSLFetchNameValue(papszOptions, "PHOTOMETRIC") != nullptr;
    6990             : 
    6991        1170 :     if (l_nBands >= 3 && !bForcePhotometric &&
    6992             : #ifdef HAVE_LIBJPEG
    6993        1132 :         !bCopyFromJPEG &&
    6994             : #endif
    6995        1126 :         poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_RedBand &&
    6996        4077 :         poSrcDS->GetRasterBand(2)->GetColorInterpretation() == GCI_GreenBand &&
    6997        1032 :         poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_BlueBand)
    6998             :     {
    6999        1026 :         papszCreateOptions =
    7000        1026 :             CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "RGB");
    7001             :     }
    7002             : 
    7003             :     /* -------------------------------------------------------------------- */
    7004             :     /*      Create the file.                                                */
    7005             :     /* -------------------------------------------------------------------- */
    7006        1875 :     VSILFILE *l_fpL = nullptr;
    7007        3750 :     CPLString l_osTmpFilename;
    7008             : 
    7009        1875 :     const int nXSize = poSrcDS->GetRasterXSize();
    7010        1875 :     const int nYSize = poSrcDS->GetRasterYSize();
    7011             : 
    7012             :     const int nColorTableMultiplier = std::max(
    7013        3750 :         1,
    7014        3750 :         std::min(257,
    7015        1875 :                  atoi(CSLFetchNameValueDef(
    7016             :                      papszOptions, "COLOR_TABLE_MULTIPLIER",
    7017        1875 :                      CPLSPrintf("%d", DEFAULT_COLOR_TABLE_MULTIPLIER_257)))));
    7018             : 
    7019        1875 :     TIFF *l_hTIFF = CreateLL(pszFilename, nXSize, nYSize, l_nBands, eType,
    7020             :                              dfExtraSpaceForOverviews, nColorTableMultiplier,
    7021             :                              papszCreateOptions, &l_fpL, l_osTmpFilename);
    7022        1875 :     const bool bStreaming = !l_osTmpFilename.empty();
    7023             : 
    7024        1875 :     CSLDestroy(papszCreateOptions);
    7025        1875 :     papszCreateOptions = nullptr;
    7026             : 
    7027        1875 :     if (l_hTIFF == nullptr)
    7028             :     {
    7029          14 :         if (bStreaming)
    7030           0 :             VSIUnlink(l_osTmpFilename);
    7031          14 :         return nullptr;
    7032             :     }
    7033             : 
    7034        1861 :     uint16_t l_nPlanarConfig = 0;
    7035        1861 :     TIFFGetField(l_hTIFF, TIFFTAG_PLANARCONFIG, &l_nPlanarConfig);
    7036             : 
    7037        1861 :     uint16_t l_nCompression = 0;
    7038             : 
    7039        1861 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_COMPRESSION, &(l_nCompression)))
    7040           0 :         l_nCompression = COMPRESSION_NONE;
    7041             : 
    7042             :     /* -------------------------------------------------------------------- */
    7043             :     /*      Set the alpha channel if we find one.                           */
    7044             :     /* -------------------------------------------------------------------- */
    7045        1861 :     uint16_t *extraSamples = nullptr;
    7046        1861 :     uint16_t nExtraSamples = 0;
    7047        1861 :     if (TIFFGetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples,
    7048        2092 :                      &extraSamples) &&
    7049         231 :         nExtraSamples > 0)
    7050             :     {
    7051             :         // We need to allocate a new array as (current) libtiff
    7052             :         // versions will not like that we reuse the array we got from
    7053             :         // TIFFGetField().
    7054             :         uint16_t *pasNewExtraSamples = static_cast<uint16_t *>(
    7055         231 :             CPLMalloc(nExtraSamples * sizeof(uint16_t)));
    7056         231 :         memcpy(pasNewExtraSamples, extraSamples,
    7057         231 :                nExtraSamples * sizeof(uint16_t));
    7058         231 :         uint16_t nAlpha = GTiffGetAlphaValue(
    7059             :             CPLGetConfigOption("GTIFF_ALPHA",
    7060             :                                CSLFetchNameValue(papszOptions, "ALPHA")),
    7061             :             DEFAULT_ALPHA_TYPE);
    7062         231 :         const int nBaseSamples = l_nBands - nExtraSamples;
    7063         812 :         for (int iExtraBand = nBaseSamples + 1; iExtraBand <= l_nBands;
    7064             :              iExtraBand++)
    7065             :         {
    7066         581 :             if (poSrcDS->GetRasterBand(iExtraBand)->GetColorInterpretation() ==
    7067             :                 GCI_AlphaBand)
    7068             :             {
    7069         130 :                 pasNewExtraSamples[iExtraBand - nBaseSamples - 1] = nAlpha;
    7070             :             }
    7071             :         }
    7072         231 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples,
    7073             :                      pasNewExtraSamples);
    7074             : 
    7075         231 :         CPLFree(pasNewExtraSamples);
    7076             :     }
    7077             : 
    7078             :     /* -------------------------------------------------------------------- */
    7079             :     /*      If the output is jpeg compressed, and the input is RGB make     */
    7080             :     /*      sure we note that.                                              */
    7081             :     /* -------------------------------------------------------------------- */
    7082             : 
    7083        1861 :     if (l_nCompression == COMPRESSION_JPEG)
    7084             :     {
    7085         130 :         if (l_nBands >= 3 &&
    7086          56 :             (poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
    7087           0 :              GCI_YCbCr_YBand) &&
    7088           0 :             (poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
    7089         130 :              GCI_YCbCr_CbBand) &&
    7090           0 :             (poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
    7091             :              GCI_YCbCr_CrBand))
    7092             :         {
    7093             :             // Do nothing.
    7094             :         }
    7095             :         else
    7096             :         {
    7097             :             // Assume RGB if it is not explicitly YCbCr.
    7098          74 :             CPLDebug("GTiff", "Setting JPEGCOLORMODE_RGB");
    7099          74 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    7100             :         }
    7101             :     }
    7102             : 
    7103             :     /* -------------------------------------------------------------------- */
    7104             :     /*      Does the source image consist of one band, with a palette?      */
    7105             :     /*      If so, copy over.                                               */
    7106             :     /* -------------------------------------------------------------------- */
    7107        1241 :     if ((l_nBands == 1 || l_nBands == 2) &&
    7108        3102 :         poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7109             :         eType == GDT_Byte)
    7110             :     {
    7111          10 :         unsigned short anTRed[256] = {0};
    7112          10 :         unsigned short anTGreen[256] = {0};
    7113          10 :         unsigned short anTBlue[256] = {0};
    7114          10 :         GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    7115             : 
    7116        2570 :         for (int iColor = 0; iColor < 256; ++iColor)
    7117             :         {
    7118        2560 :             if (iColor < poCT->GetColorEntryCount())
    7119             :             {
    7120        1505 :                 GDALColorEntry sRGB = {0, 0, 0, 0};
    7121             : 
    7122        1505 :                 poCT->GetColorEntryAsRGB(iColor, &sRGB);
    7123             : 
    7124        3010 :                 anTRed[iColor] = GTiffDataset::ClampCTEntry(
    7125        1505 :                     iColor, 1, sRGB.c1, nColorTableMultiplier);
    7126        3010 :                 anTGreen[iColor] = GTiffDataset::ClampCTEntry(
    7127        1505 :                     iColor, 2, sRGB.c2, nColorTableMultiplier);
    7128        1505 :                 anTBlue[iColor] = GTiffDataset::ClampCTEntry(
    7129        1505 :                     iColor, 3, sRGB.c3, nColorTableMultiplier);
    7130             :             }
    7131             :             else
    7132             :             {
    7133        1055 :                 anTRed[iColor] = 0;
    7134        1055 :                 anTGreen[iColor] = 0;
    7135        1055 :                 anTBlue[iColor] = 0;
    7136             :             }
    7137             :         }
    7138             : 
    7139          10 :         if (!bForcePhotometric)
    7140          10 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    7141          10 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, anTRed, anTGreen, anTBlue);
    7142             :     }
    7143        1240 :     else if ((l_nBands == 1 || l_nBands == 2) &&
    7144        3091 :              poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7145             :              eType == GDT_UInt16)
    7146             :     {
    7147             :         unsigned short *panTRed = static_cast<unsigned short *>(
    7148           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7149             :         unsigned short *panTGreen = static_cast<unsigned short *>(
    7150           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7151             :         unsigned short *panTBlue = static_cast<unsigned short *>(
    7152           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7153             : 
    7154           1 :         GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    7155             : 
    7156       65537 :         for (int iColor = 0; iColor < 65536; ++iColor)
    7157             :         {
    7158       65536 :             if (iColor < poCT->GetColorEntryCount())
    7159             :             {
    7160       65536 :                 GDALColorEntry sRGB = {0, 0, 0, 0};
    7161             : 
    7162       65536 :                 poCT->GetColorEntryAsRGB(iColor, &sRGB);
    7163             : 
    7164      131072 :                 panTRed[iColor] = GTiffDataset::ClampCTEntry(
    7165       65536 :                     iColor, 1, sRGB.c1, nColorTableMultiplier);
    7166      131072 :                 panTGreen[iColor] = GTiffDataset::ClampCTEntry(
    7167       65536 :                     iColor, 2, sRGB.c2, nColorTableMultiplier);
    7168       65536 :                 panTBlue[iColor] = GTiffDataset::ClampCTEntry(
    7169       65536 :                     iColor, 3, sRGB.c3, nColorTableMultiplier);
    7170             :             }
    7171             :             else
    7172             :             {
    7173           0 :                 panTRed[iColor] = 0;
    7174           0 :                 panTGreen[iColor] = 0;
    7175           0 :                 panTBlue[iColor] = 0;
    7176             :             }
    7177             :         }
    7178             : 
    7179           1 :         if (!bForcePhotometric)
    7180           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    7181           1 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue);
    7182             : 
    7183           1 :         CPLFree(panTRed);
    7184           1 :         CPLFree(panTGreen);
    7185           1 :         CPLFree(panTBlue);
    7186             :     }
    7187        1850 :     else if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
    7188           1 :         ReportError(
    7189             :             pszFilename, CE_Failure, CPLE_AppDefined,
    7190             :             "Unable to export color table to GeoTIFF file.  Color tables "
    7191             :             "can only be written to 1 band or 2 bands Byte or "
    7192             :             "UInt16 GeoTIFF files.");
    7193             : 
    7194        1861 :     if (l_nCompression == COMPRESSION_JPEG)
    7195             :     {
    7196          74 :         uint16_t l_nPhotometric = 0;
    7197          74 :         TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &l_nPhotometric);
    7198             :         // Check done in tif_jpeg.c later, but not with a very clear error
    7199             :         // message
    7200          74 :         if (l_nPhotometric == PHOTOMETRIC_PALETTE)
    7201             :         {
    7202           1 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    7203             :                         "JPEG compression not supported with paletted image");
    7204           1 :             XTIFFClose(l_hTIFF);
    7205           1 :             VSIUnlink(l_osTmpFilename);
    7206           1 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    7207           1 :             return nullptr;
    7208             :         }
    7209             :     }
    7210             : 
    7211        1932 :     if (l_nBands == 2 &&
    7212        1860 :         poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7213           0 :         (eType == GDT_Byte || eType == GDT_UInt16))
    7214             :     {
    7215           1 :         uint16_t v[1] = {EXTRASAMPLE_UNASSALPHA};
    7216             : 
    7217           1 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
    7218             :     }
    7219             : 
    7220        1860 :     const int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags();
    7221        1860 :     bool bCreateMask = false;
    7222        3720 :     CPLString osHiddenStructuralMD;
    7223        1860 :     if ((l_nBands == 1 || l_nPlanarConfig == PLANARCONFIG_CONTIG) &&
    7224             :         bCopySrcOverviews)
    7225             :     {
    7226         164 :         osHiddenStructuralMD += "LAYOUT=IFDS_BEFORE_DATA\n";
    7227         164 :         osHiddenStructuralMD += "BLOCK_ORDER=ROW_MAJOR\n";
    7228         164 :         osHiddenStructuralMD += "BLOCK_LEADER=SIZE_AS_UINT4\n";
    7229         164 :         osHiddenStructuralMD += "BLOCK_TRAILER=LAST_4_BYTES_REPEATED\n";
    7230             :         osHiddenStructuralMD +=
    7231         164 :             "KNOWN_INCOMPATIBLE_EDITION=NO\n ";  // Final space intended, so
    7232             :                                                  // this can be replaced by YES
    7233             :     }
    7234        1860 :     if (!(nMaskFlags & (GMF_ALL_VALID | GMF_ALPHA | GMF_NODATA)) &&
    7235          31 :         (nMaskFlags & GMF_PER_DATASET) && !bStreaming)
    7236             :     {
    7237          28 :         bCreateMask = true;
    7238          55 :         if (GTiffDataset::MustCreateInternalMask() &&
    7239          27 :             !osHiddenStructuralMD.empty())
    7240             :         {
    7241          22 :             osHiddenStructuralMD += "MASK_INTERLEAVED_WITH_IMAGERY=YES\n";
    7242             :         }
    7243             :     }
    7244        1860 :     if (!osHiddenStructuralMD.empty())
    7245             :     {
    7246         164 :         const int nHiddenMDSize = static_cast<int>(osHiddenStructuralMD.size());
    7247             :         osHiddenStructuralMD =
    7248         164 :             CPLOPrintf("GDAL_STRUCTURAL_METADATA_SIZE=%06d bytes\n",
    7249         328 :                        nHiddenMDSize) +
    7250         164 :             osHiddenStructuralMD;
    7251         164 :         VSI_TIFFWrite(l_hTIFF, osHiddenStructuralMD.c_str(),
    7252             :                       osHiddenStructuralMD.size());
    7253             :     }
    7254             : 
    7255             :     // FIXME? libtiff writes extended tags in the order they are specified
    7256             :     // and not in increasing order.
    7257             : 
    7258             :     /* -------------------------------------------------------------------- */
    7259             :     /*      Transfer some TIFF specific metadata, if available.             */
    7260             :     /*      The return value will tell us if we need to try again later with*/
    7261             :     /*      PAM because the profile doesn't allow to write some metadata    */
    7262             :     /*      as TIFF tag                                                     */
    7263             :     /* -------------------------------------------------------------------- */
    7264        1860 :     const bool bHasWrittenMDInGeotiffTAG = GTiffDataset::WriteMetadata(
    7265             :         poSrcDS, l_hTIFF, false, eProfile, pszFilename, papszOptions);
    7266             : 
    7267             :     /* -------------------------------------------------------------------- */
    7268             :     /*      Write NoData value, if exist.                                   */
    7269             :     /* -------------------------------------------------------------------- */
    7270        1860 :     if (eProfile == GTiffProfile::GDALGEOTIFF)
    7271             :     {
    7272        1840 :         int bSuccess = FALSE;
    7273        1840 :         GDALRasterBand *poFirstBand = poSrcDS->GetRasterBand(1);
    7274        1840 :         if (poFirstBand->GetRasterDataType() == GDT_Int64)
    7275             :         {
    7276           2 :             const auto nNoData = poFirstBand->GetNoDataValueAsInt64(&bSuccess);
    7277           2 :             if (bSuccess)
    7278           1 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, nNoData);
    7279             :         }
    7280        1838 :         else if (poFirstBand->GetRasterDataType() == GDT_UInt64)
    7281             :         {
    7282           2 :             const auto nNoData = poFirstBand->GetNoDataValueAsUInt64(&bSuccess);
    7283           2 :             if (bSuccess)
    7284           1 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, nNoData);
    7285             :         }
    7286             :         else
    7287             :         {
    7288        1836 :             const auto dfNoData = poFirstBand->GetNoDataValue(&bSuccess);
    7289        1836 :             if (bSuccess)
    7290          89 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, dfNoData);
    7291             :         }
    7292             :     }
    7293             : 
    7294             :     /* -------------------------------------------------------------------- */
    7295             :     /*      Are we addressing PixelIsPoint mode?                            */
    7296             :     /* -------------------------------------------------------------------- */
    7297        1860 :     bool bPixelIsPoint = false;
    7298        1860 :     bool bPointGeoIgnore = false;
    7299             : 
    7300        3144 :     if (poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT) &&
    7301        1284 :         EQUAL(poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT), GDALMD_AOP_POINT))
    7302             :     {
    7303          12 :         bPixelIsPoint = true;
    7304             :         bPointGeoIgnore =
    7305          12 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    7306             :     }
    7307             : 
    7308             :     /* -------------------------------------------------------------------- */
    7309             :     /*      Write affine transform if it is meaningful.                     */
    7310             :     /* -------------------------------------------------------------------- */
    7311        1860 :     const OGRSpatialReference *l_poSRS = nullptr;
    7312        1860 :     double l_adfGeoTransform[6] = {0.0};
    7313             : 
    7314        1860 :     if (poSrcDS->GetGeoTransform(l_adfGeoTransform) == CE_None)
    7315             :     {
    7316        1425 :         if (bGeoTIFF)
    7317             :         {
    7318        1420 :             l_poSRS = poSrcDS->GetSpatialRef();
    7319             : 
    7320        1420 :             if (l_adfGeoTransform[2] == 0.0 && l_adfGeoTransform[4] == 0.0 &&
    7321        1417 :                 l_adfGeoTransform[5] < 0.0)
    7322             :             {
    7323        1414 :                 double dfOffset = 0.0;
    7324             :                 {
    7325             :                     // In the case the SRS has a vertical component and we have
    7326             :                     // a single band, encode its scale/offset in the GeoTIFF
    7327             :                     // tags
    7328        1414 :                     int bHasScale = FALSE;
    7329             :                     double dfScale =
    7330        1414 :                         poSrcDS->GetRasterBand(1)->GetScale(&bHasScale);
    7331        1414 :                     int bHasOffset = FALSE;
    7332             :                     dfOffset =
    7333        1414 :                         poSrcDS->GetRasterBand(1)->GetOffset(&bHasOffset);
    7334             :                     const bool bApplyScaleOffset =
    7335        1418 :                         l_poSRS && l_poSRS->IsVertical() &&
    7336           4 :                         poSrcDS->GetRasterCount() == 1;
    7337        1414 :                     if (bApplyScaleOffset && !bHasScale)
    7338           0 :                         dfScale = 1.0;
    7339        1414 :                     if (!bApplyScaleOffset || !bHasOffset)
    7340        1410 :                         dfOffset = 0.0;
    7341             :                     const double adfPixelScale[3] = {
    7342        1414 :                         l_adfGeoTransform[1], fabs(l_adfGeoTransform[5]),
    7343        1414 :                         bApplyScaleOffset ? dfScale : 0.0};
    7344             : 
    7345        1414 :                     TIFFSetField(l_hTIFF, TIFFTAG_GEOPIXELSCALE, 3,
    7346             :                                  adfPixelScale);
    7347             :                 }
    7348             : 
    7349        1414 :                 double adfTiePoints[6] = {0.0,
    7350             :                                           0.0,
    7351             :                                           0.0,
    7352        1414 :                                           l_adfGeoTransform[0],
    7353        1414 :                                           l_adfGeoTransform[3],
    7354        1414 :                                           dfOffset};
    7355             : 
    7356        1414 :                 if (bPixelIsPoint && !bPointGeoIgnore)
    7357             :                 {
    7358           8 :                     adfTiePoints[3] +=
    7359           8 :                         l_adfGeoTransform[1] * 0.5 + l_adfGeoTransform[2] * 0.5;
    7360           8 :                     adfTiePoints[4] +=
    7361           8 :                         l_adfGeoTransform[4] * 0.5 + l_adfGeoTransform[5] * 0.5;
    7362             :                 }
    7363             : 
    7364        1414 :                 TIFFSetField(l_hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
    7365             :             }
    7366             :             else
    7367             :             {
    7368           6 :                 double adfMatrix[16] = {0.0};
    7369             : 
    7370           6 :                 adfMatrix[0] = l_adfGeoTransform[1];
    7371           6 :                 adfMatrix[1] = l_adfGeoTransform[2];
    7372           6 :                 adfMatrix[3] = l_adfGeoTransform[0];
    7373           6 :                 adfMatrix[4] = l_adfGeoTransform[4];
    7374           6 :                 adfMatrix[5] = l_adfGeoTransform[5];
    7375           6 :                 adfMatrix[7] = l_adfGeoTransform[3];
    7376           6 :                 adfMatrix[15] = 1.0;
    7377             : 
    7378           6 :                 if (bPixelIsPoint && !bPointGeoIgnore)
    7379             :                 {
    7380           0 :                     adfMatrix[3] +=
    7381           0 :                         l_adfGeoTransform[1] * 0.5 + l_adfGeoTransform[2] * 0.5;
    7382           0 :                     adfMatrix[7] +=
    7383           0 :                         l_adfGeoTransform[4] * 0.5 + l_adfGeoTransform[5] * 0.5;
    7384             :                 }
    7385             : 
    7386           6 :                 TIFFSetField(l_hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
    7387             :             }
    7388             :         }
    7389             : 
    7390             :         /* --------------------------------------------------------------------
    7391             :          */
    7392             :         /*      Do we need a TFW file? */
    7393             :         /* --------------------------------------------------------------------
    7394             :          */
    7395        1425 :         if (CPLFetchBool(papszOptions, "TFW", false))
    7396           2 :             GDALWriteWorldFile(pszFilename, "tfw", l_adfGeoTransform);
    7397        1423 :         else if (CPLFetchBool(papszOptions, "WORLDFILE", false))
    7398           1 :             GDALWriteWorldFile(pszFilename, "wld", l_adfGeoTransform);
    7399             :     }
    7400             : 
    7401             :     /* -------------------------------------------------------------------- */
    7402             :     /*      Otherwise write tiepoints if they are available.                */
    7403             :     /* -------------------------------------------------------------------- */
    7404         435 :     else if (poSrcDS->GetGCPCount() > 0 && bGeoTIFF)
    7405             :     {
    7406          11 :         const GDAL_GCP *pasGCPs = poSrcDS->GetGCPs();
    7407             :         double *padfTiePoints = static_cast<double *>(
    7408          11 :             CPLMalloc(6 * sizeof(double) * poSrcDS->GetGCPCount()));
    7409             : 
    7410          55 :         for (int iGCP = 0; iGCP < poSrcDS->GetGCPCount(); ++iGCP)
    7411             :         {
    7412             : 
    7413          44 :             padfTiePoints[iGCP * 6 + 0] = pasGCPs[iGCP].dfGCPPixel;
    7414          44 :             padfTiePoints[iGCP * 6 + 1] = pasGCPs[iGCP].dfGCPLine;
    7415          44 :             padfTiePoints[iGCP * 6 + 2] = 0;
    7416          44 :             padfTiePoints[iGCP * 6 + 3] = pasGCPs[iGCP].dfGCPX;
    7417          44 :             padfTiePoints[iGCP * 6 + 4] = pasGCPs[iGCP].dfGCPY;
    7418          44 :             padfTiePoints[iGCP * 6 + 5] = pasGCPs[iGCP].dfGCPZ;
    7419             : 
    7420          44 :             if (bPixelIsPoint && !bPointGeoIgnore)
    7421             :             {
    7422           4 :                 padfTiePoints[iGCP * 6 + 0] -= 0.5;
    7423           4 :                 padfTiePoints[iGCP * 6 + 1] -= 0.5;
    7424             :             }
    7425             :         }
    7426             : 
    7427          11 :         TIFFSetField(l_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * poSrcDS->GetGCPCount(),
    7428             :                      padfTiePoints);
    7429          11 :         CPLFree(padfTiePoints);
    7430             : 
    7431          11 :         l_poSRS = poSrcDS->GetGCPSpatialRef();
    7432             : 
    7433          22 :         if (CPLFetchBool(papszOptions, "TFW", false) ||
    7434          11 :             CPLFetchBool(papszOptions, "WORLDFILE", false))
    7435             :         {
    7436           0 :             ReportError(
    7437             :                 pszFilename, CE_Warning, CPLE_AppDefined,
    7438             :                 "TFW=ON or WORLDFILE=ON creation options are ignored when "
    7439             :                 "GCPs are available");
    7440             :         }
    7441             :     }
    7442             :     else
    7443             :     {
    7444         424 :         l_poSRS = poSrcDS->GetSpatialRef();
    7445             :     }
    7446             : 
    7447             :     /* -------------------------------------------------------------------- */
    7448             :     /*      Copy xml:XMP data                                               */
    7449             :     /* -------------------------------------------------------------------- */
    7450        1860 :     char **papszXMP = poSrcDS->GetMetadata("xml:XMP");
    7451        1860 :     if (papszXMP != nullptr && *papszXMP != nullptr)
    7452             :     {
    7453           9 :         int nTagSize = static_cast<int>(strlen(*papszXMP));
    7454           9 :         TIFFSetField(l_hTIFF, TIFFTAG_XMLPACKET, nTagSize, *papszXMP);
    7455             :     }
    7456             : 
    7457             :     /* -------------------------------------------------------------------- */
    7458             :     /*      Write the projection information, if possible.                  */
    7459             :     /* -------------------------------------------------------------------- */
    7460        1860 :     const bool bHasProjection = l_poSRS != nullptr;
    7461        1860 :     bool bExportSRSToPAM = false;
    7462        1860 :     if ((bHasProjection || bPixelIsPoint) && bGeoTIFF)
    7463             :     {
    7464        1417 :         GTIF *psGTIF = GTiffDataset::GTIFNew(l_hTIFF);
    7465             : 
    7466        1417 :         if (bHasProjection)
    7467             :         {
    7468        1417 :             const auto eGeoTIFFKeysFlavor = GetGTIFFKeysFlavor(papszOptions);
    7469        1417 :             if (IsSRSCompatibleOfGeoTIFF(l_poSRS, eGeoTIFFKeysFlavor))
    7470             :             {
    7471        1417 :                 GTIFSetFromOGISDefnEx(
    7472             :                     psGTIF,
    7473             :                     OGRSpatialReference::ToHandle(
    7474             :                         const_cast<OGRSpatialReference *>(l_poSRS)),
    7475             :                     eGeoTIFFKeysFlavor, GetGeoTIFFVersion(papszOptions));
    7476             :             }
    7477             :             else
    7478             :             {
    7479           0 :                 bExportSRSToPAM = true;
    7480             :             }
    7481             :         }
    7482             : 
    7483        1417 :         if (bPixelIsPoint)
    7484             :         {
    7485          12 :             GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
    7486             :                        RasterPixelIsPoint);
    7487             :         }
    7488             : 
    7489        1417 :         GTIFWriteKeys(psGTIF);
    7490        1417 :         GTIFFree(psGTIF);
    7491             :     }
    7492             : 
    7493        1860 :     bool l_bDontReloadFirstBlock = false;
    7494             : 
    7495             : #ifdef HAVE_LIBJPEG
    7496        1860 :     if (bCopyFromJPEG)
    7497             :     {
    7498          12 :         GTIFF_CopyFromJPEG_WriteAdditionalTags(l_hTIFF, poSrcDS);
    7499             :     }
    7500             : #endif
    7501             : 
    7502             :     /* -------------------------------------------------------------------- */
    7503             :     /*      Cleanup                                                         */
    7504             :     /* -------------------------------------------------------------------- */
    7505        1860 :     if (bCopySrcOverviews)
    7506             :     {
    7507         165 :         TIFFDeferStrileArrayWriting(l_hTIFF);
    7508             :     }
    7509        1860 :     TIFFWriteCheck(l_hTIFF, TIFFIsTiled(l_hTIFF), "GTiffCreateCopy()");
    7510        1860 :     TIFFWriteDirectory(l_hTIFF);
    7511        1860 :     if (bStreaming)
    7512             :     {
    7513             :         // We need to write twice the directory to be sure that custom
    7514             :         // TIFF tags are correctly sorted and that padding bytes have been
    7515             :         // added.
    7516           5 :         TIFFSetDirectory(l_hTIFF, 0);
    7517           5 :         TIFFWriteDirectory(l_hTIFF);
    7518             : 
    7519           5 :         if (VSIFSeekL(l_fpL, 0, SEEK_END) != 0)
    7520           0 :             ReportError(pszFilename, CE_Failure, CPLE_FileIO, "Cannot seek");
    7521           5 :         const int nSize = static_cast<int>(VSIFTellL(l_fpL));
    7522             : 
    7523           5 :         vsi_l_offset nDataLength = 0;
    7524           5 :         VSIGetMemFileBuffer(l_osTmpFilename, &nDataLength, FALSE);
    7525           5 :         TIFFSetDirectory(l_hTIFF, 0);
    7526           5 :         GTiffFillStreamableOffsetAndCount(l_hTIFF, nSize);
    7527           5 :         TIFFWriteDirectory(l_hTIFF);
    7528             :     }
    7529        1860 :     const auto nDirCount = TIFFNumberOfDirectories(l_hTIFF);
    7530        1860 :     if (nDirCount >= 1)
    7531             :     {
    7532        1854 :         TIFFSetDirectory(l_hTIFF, static_cast<tdir_t>(nDirCount - 1));
    7533             :     }
    7534        1860 :     const toff_t l_nDirOffset = TIFFCurrentDirOffset(l_hTIFF);
    7535        1860 :     TIFFFlush(l_hTIFF);
    7536        1860 :     XTIFFClose(l_hTIFF);
    7537             : 
    7538        1860 :     VSIFSeekL(l_fpL, 0, SEEK_SET);
    7539             : 
    7540             :     // fpStreaming will assigned to the instance and not closed here.
    7541        1860 :     VSILFILE *fpStreaming = nullptr;
    7542        1860 :     if (bStreaming)
    7543             :     {
    7544           5 :         vsi_l_offset nDataLength = 0;
    7545             :         void *pabyBuffer =
    7546           5 :             VSIGetMemFileBuffer(l_osTmpFilename, &nDataLength, FALSE);
    7547           5 :         fpStreaming = VSIFOpenL(pszFilename, "wb");
    7548           5 :         if (fpStreaming == nullptr)
    7549             :         {
    7550           1 :             VSIUnlink(l_osTmpFilename);
    7551           1 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    7552           1 :             return nullptr;
    7553             :         }
    7554           4 :         if (static_cast<vsi_l_offset>(VSIFWriteL(pabyBuffer, 1,
    7555             :                                                  static_cast<int>(nDataLength),
    7556           4 :                                                  fpStreaming)) != nDataLength)
    7557             :         {
    7558           0 :             ReportError(pszFilename, CE_Failure, CPLE_FileIO,
    7559             :                         "Could not write %d bytes",
    7560             :                         static_cast<int>(nDataLength));
    7561           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fpStreaming));
    7562           0 :             VSIUnlink(l_osTmpFilename);
    7563           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    7564           0 :             return nullptr;
    7565             :         }
    7566             :     }
    7567             : 
    7568             :     /* -------------------------------------------------------------------- */
    7569             :     /*      Re-open as a dataset and copy over missing metadata using       */
    7570             :     /*      PAM facilities.                                                 */
    7571             :     /* -------------------------------------------------------------------- */
    7572        1859 :     l_hTIFF = VSI_TIFFOpen(bStreaming ? l_osTmpFilename.c_str() : pszFilename,
    7573             :                            "r+", l_fpL);
    7574        1859 :     if (l_hTIFF == nullptr)
    7575             :     {
    7576          10 :         if (bStreaming)
    7577           0 :             VSIUnlink(l_osTmpFilename);
    7578          10 :         CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    7579          10 :         return nullptr;
    7580             :     }
    7581             : 
    7582             :     /* -------------------------------------------------------------------- */
    7583             :     /*      Create a corresponding GDALDataset.                             */
    7584             :     /* -------------------------------------------------------------------- */
    7585        1849 :     GTiffDataset *poDS = new GTiffDataset();
    7586        1849 :     poDS->SetDescription(pszFilename);
    7587        1849 :     poDS->eAccess = GA_Update;
    7588        1849 :     poDS->m_pszFilename = CPLStrdup(pszFilename);
    7589        1849 :     poDS->m_fpL = l_fpL;
    7590        1849 :     poDS->m_bIMDRPCMetadataLoaded = true;
    7591        1849 :     poDS->m_nColorTableMultiplier = nColorTableMultiplier;
    7592             : 
    7593        1849 :     const bool bAppend = CPLFetchBool(papszOptions, "APPEND_SUBDATASET", false);
    7594        3697 :     if (poDS->OpenOffset(l_hTIFF,
    7595        1848 :                          bAppend ? l_nDirOffset : TIFFCurrentDirOffset(l_hTIFF),
    7596             :                          GA_Update,
    7597             :                          false,  // bAllowRGBAInterface
    7598             :                          true    // bReadGeoTransform
    7599        1849 :                          ) != CE_None)
    7600             :     {
    7601           0 :         delete poDS;
    7602           0 :         if (bStreaming)
    7603           0 :             VSIUnlink(l_osTmpFilename);
    7604           0 :         return nullptr;
    7605             :     }
    7606             : 
    7607             :     // Legacy... Patch back GDT_Int8 type to GDT_Byte if the user used
    7608             :     // PIXELTYPE=SIGNEDBYTE
    7609        1849 :     const char *pszPixelType = CSLFetchNameValue(papszOptions, "PIXELTYPE");
    7610        1849 :     if (pszPixelType == nullptr)
    7611        1844 :         pszPixelType = "";
    7612        1849 :     if (eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE"))
    7613             :     {
    7614          10 :         for (int i = 0; i < poDS->nBands; ++i)
    7615             :         {
    7616           5 :             auto poBand = static_cast<GTiffRasterBand *>(poDS->papoBands[i]);
    7617           5 :             poBand->eDataType = GDT_Byte;
    7618           5 :             poBand->EnablePixelTypeSignedByteWarning(false);
    7619           5 :             poBand->SetMetadataItem("PIXELTYPE", "SIGNEDBYTE",
    7620             :                                     "IMAGE_STRUCTURE");
    7621           5 :             poBand->EnablePixelTypeSignedByteWarning(true);
    7622             :         }
    7623             :     }
    7624             : 
    7625        1849 :     poDS->oOvManager.Initialize(poDS, pszFilename);
    7626             : 
    7627        1849 :     if (bStreaming)
    7628             :     {
    7629           4 :         VSIUnlink(l_osTmpFilename);
    7630           4 :         poDS->m_fpToWrite = fpStreaming;
    7631             :     }
    7632        1849 :     poDS->m_eProfile = eProfile;
    7633             : 
    7634        1849 :     int nCloneInfoFlags = GCIF_PAM_DEFAULT & ~GCIF_MASK;
    7635             : 
    7636             :     // If we explicitly asked not to tag the alpha band as such, do not
    7637             :     // reintroduce this alpha color interpretation in PAM.
    7638        1849 :     if (poSrcDS->GetRasterBand(l_nBands)->GetColorInterpretation() ==
    7639        1961 :             GCI_AlphaBand &&
    7640         112 :         GTiffGetAlphaValue(
    7641             :             CPLGetConfigOption("GTIFF_ALPHA",
    7642             :                                CSLFetchNameValue(papszOptions, "ALPHA")),
    7643             :             DEFAULT_ALPHA_TYPE) == EXTRASAMPLE_UNSPECIFIED)
    7644             :     {
    7645           1 :         nCloneInfoFlags &= ~GCIF_COLORINTERP;
    7646             :     }
    7647             :     // Ignore source band color interpretation if requesting PHOTOMETRIC=RGB
    7648        3015 :     else if (l_nBands >= 3 &&
    7649        1167 :              EQUAL(CSLFetchNameValueDef(papszOptions, "PHOTOMETRIC", ""),
    7650             :                    "RGB"))
    7651             :     {
    7652          28 :         for (int i = 1; i <= 3; i++)
    7653             :         {
    7654          21 :             poDS->GetRasterBand(i)->SetColorInterpretation(
    7655          21 :                 static_cast<GDALColorInterp>(GCI_RedBand + (i - 1)));
    7656             :         }
    7657           7 :         nCloneInfoFlags &= ~GCIF_COLORINTERP;
    7658           9 :         if (!(l_nBands == 4 &&
    7659           2 :               CSLFetchNameValue(papszOptions, "ALPHA") != nullptr))
    7660             :         {
    7661          15 :             for (int i = 4; i <= l_nBands; i++)
    7662             :             {
    7663          18 :                 poDS->GetRasterBand(i)->SetColorInterpretation(
    7664           9 :                     poSrcDS->GetRasterBand(i)->GetColorInterpretation());
    7665             :             }
    7666             :         }
    7667             :     }
    7668             : 
    7669             :     CPLString osOldGTIFF_REPORT_COMPD_CSVal(
    7670        3698 :         CPLGetConfigOption("GTIFF_REPORT_COMPD_CS", ""));
    7671        1849 :     CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS", "YES");
    7672        1849 :     poDS->CloneInfo(poSrcDS, nCloneInfoFlags);
    7673        1849 :     CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS",
    7674        1849 :                                   osOldGTIFF_REPORT_COMPD_CSVal.empty()
    7675             :                                       ? nullptr
    7676           0 :                                       : osOldGTIFF_REPORT_COMPD_CSVal.c_str());
    7677             : 
    7678        1865 :     if ((!bGeoTIFF || bExportSRSToPAM) &&
    7679          16 :         (poDS->GetPamFlags() & GPF_DISABLED) == 0)
    7680             :     {
    7681             :         // Copy georeferencing info to PAM if the profile is not GeoTIFF
    7682          15 :         poDS->GDALPamDataset::SetSpatialRef(poDS->GetSpatialRef());
    7683             :         double adfGeoTransform[6];
    7684          15 :         if (poDS->GetGeoTransform(adfGeoTransform) == CE_None)
    7685             :         {
    7686           5 :             poDS->GDALPamDataset::SetGeoTransform(adfGeoTransform);
    7687             :         }
    7688          15 :         poDS->GDALPamDataset::SetGCPs(poDS->GetGCPCount(), poDS->GetGCPs(),
    7689             :                                       poDS->GetGCPSpatialRef());
    7690             :     }
    7691             : 
    7692        1849 :     poDS->m_papszCreationOptions = CSLDuplicate(papszOptions);
    7693        1849 :     poDS->m_bDontReloadFirstBlock = l_bDontReloadFirstBlock;
    7694             : 
    7695             :     /* -------------------------------------------------------------------- */
    7696             :     /*      CloneInfo() does not merge metadata, it just replaces it        */
    7697             :     /*      totally.  So we have to merge it.                               */
    7698             :     /* -------------------------------------------------------------------- */
    7699             : 
    7700        1849 :     char **papszSRC_MD = poSrcDS->GetMetadata();
    7701        1849 :     char **papszDST_MD = CSLDuplicate(poDS->GetMetadata());
    7702             : 
    7703        1849 :     papszDST_MD = CSLMerge(papszDST_MD, papszSRC_MD);
    7704             : 
    7705        1849 :     poDS->SetMetadata(papszDST_MD);
    7706        1849 :     CSLDestroy(papszDST_MD);
    7707             : 
    7708             :     // Depending on the PHOTOMETRIC tag, the TIFF file may not have the same
    7709             :     // band count as the source. Will fail later in GDALDatasetCopyWholeRaster
    7710             :     // anyway.
    7711        6484 :     for (int nBand = 1;
    7712        6484 :          nBand <= std::min(poDS->GetRasterCount(), poSrcDS->GetRasterCount());
    7713             :          ++nBand)
    7714             :     {
    7715        4635 :         GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(nBand);
    7716        4635 :         GDALRasterBand *poDstBand = poDS->GetRasterBand(nBand);
    7717        4635 :         papszSRC_MD = poSrcBand->GetMetadata();
    7718        4635 :         papszDST_MD = CSLDuplicate(poDstBand->GetMetadata());
    7719             : 
    7720        4635 :         papszDST_MD = CSLMerge(papszDST_MD, papszSRC_MD);
    7721             : 
    7722        4635 :         poDstBand->SetMetadata(papszDST_MD);
    7723        4635 :         CSLDestroy(papszDST_MD);
    7724             : 
    7725        4635 :         char **papszCatNames = poSrcBand->GetCategoryNames();
    7726        4635 :         if (nullptr != papszCatNames)
    7727           0 :             poDstBand->SetCategoryNames(papszCatNames);
    7728             :     }
    7729             : 
    7730        1849 :     l_hTIFF = static_cast<TIFF *>(poDS->GetInternalHandle(nullptr));
    7731             : 
    7732             :     /* -------------------------------------------------------------------- */
    7733             :     /*      Handle forcing xml:ESRI data to be written to PAM.              */
    7734             :     /* -------------------------------------------------------------------- */
    7735        1849 :     if (CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
    7736             :     {
    7737           1 :         char **papszESRIMD = poSrcDS->GetMetadata("xml:ESRI");
    7738           1 :         if (papszESRIMD)
    7739             :         {
    7740           1 :             poDS->SetMetadata(papszESRIMD, "xml:ESRI");
    7741             :         }
    7742             :     }
    7743             : 
    7744             :     /* -------------------------------------------------------------------- */
    7745             :     /*      Second chance: now that we have a PAM dataset, it is possible   */
    7746             :     /*      to write metadata that we could not write as a TIFF tag.        */
    7747             :     /* -------------------------------------------------------------------- */
    7748        1849 :     if (!bHasWrittenMDInGeotiffTAG && !bStreaming)
    7749             :     {
    7750           6 :         GTiffDataset::WriteMetadata(
    7751             :             poDS, l_hTIFF, true, eProfile, pszFilename, papszOptions,
    7752             :             true /* don't write RPC and IMD file again */);
    7753             :     }
    7754             : 
    7755        1849 :     if (!bStreaming)
    7756        1845 :         GTiffDataset::WriteRPC(poDS, l_hTIFF, true, eProfile, pszFilename,
    7757             :                                papszOptions,
    7758             :                                true /* write only in PAM AND if needed */);
    7759             : 
    7760             :     // Propagate ISIS3 or VICAR metadata, but only as PAM metadata.
    7761        5547 :     for (const char *pszMDD : {"json:ISIS3", "json:VICAR"})
    7762             :     {
    7763        3698 :         char **papszMD = poSrcDS->GetMetadata(pszMDD);
    7764        3698 :         if (papszMD)
    7765             :         {
    7766           3 :             poDS->SetMetadata(papszMD, pszMDD);
    7767           3 :             poDS->PushMetadataToPam();
    7768             :         }
    7769             :     }
    7770             : 
    7771        1849 :     poDS->m_bWriteCOGLayout = bCopySrcOverviews;
    7772             : 
    7773             :     // To avoid unnecessary directory rewriting.
    7774        1849 :     poDS->m_bMetadataChanged = false;
    7775        1849 :     poDS->m_bGeoTIFFInfoChanged = false;
    7776        1849 :     poDS->m_bNoDataChanged = false;
    7777        1849 :     poDS->m_bForceUnsetGTOrGCPs = false;
    7778        1849 :     poDS->m_bForceUnsetProjection = false;
    7779        1849 :     poDS->m_bStreamingOut = bStreaming;
    7780             : 
    7781             :     // Don't try to load external metadata files (#6597).
    7782        1849 :     poDS->m_bIMDRPCMetadataLoaded = true;
    7783             : 
    7784             :     // We must re-set the compression level at this point, since it has been
    7785             :     // lost a few lines above when closing the newly create TIFF file The
    7786             :     // TIFFTAG_ZIPQUALITY & TIFFTAG_JPEGQUALITY are not store in the TIFF file.
    7787             :     // They are just TIFF session parameters.
    7788             : 
    7789        1849 :     poDS->m_nZLevel = GTiffGetZLevel(papszOptions);
    7790        1849 :     poDS->m_nLZMAPreset = GTiffGetLZMAPreset(papszOptions);
    7791        1849 :     poDS->m_nZSTDLevel = GTiffGetZSTDPreset(papszOptions);
    7792        1849 :     poDS->m_nWebPLevel = GTiffGetWebPLevel(papszOptions);
    7793        1849 :     poDS->m_bWebPLossless = GTiffGetWebPLossless(papszOptions);
    7794        1852 :     if (poDS->m_nWebPLevel != 100 && poDS->m_bWebPLossless &&
    7795           3 :         CSLFetchNameValue(papszOptions, "WEBP_LEVEL"))
    7796             :     {
    7797           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    7798             :                  "WEBP_LEVEL is specified, but WEBP_LOSSLESS=YES. "
    7799             :                  "WEBP_LEVEL will be ignored.");
    7800             :     }
    7801        1849 :     poDS->m_nJpegQuality = GTiffGetJpegQuality(papszOptions);
    7802        1849 :     poDS->m_nJpegTablesMode = GTiffGetJpegTablesMode(papszOptions);
    7803        1849 :     poDS->GetDiscardLsbOption(papszOptions);
    7804        1849 :     poDS->m_dfMaxZError = GTiffGetLERCMaxZError(papszOptions);
    7805        1849 :     poDS->m_dfMaxZErrorOverview = GTiffGetLERCMaxZErrorOverview(papszOptions);
    7806             : #if HAVE_JXL
    7807        1849 :     poDS->m_bJXLLossless = GTiffGetJXLLossless(papszOptions);
    7808        1849 :     poDS->m_nJXLEffort = GTiffGetJXLEffort(papszOptions);
    7809        1849 :     poDS->m_fJXLDistance = GTiffGetJXLDistance(papszOptions);
    7810        1849 :     poDS->m_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszOptions);
    7811             : #endif
    7812        1849 :     poDS->InitCreationOrOpenOptions(true, papszOptions);
    7813             : 
    7814        1849 :     if (l_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    7815        1824 :         l_nCompression == COMPRESSION_LERC)
    7816             :     {
    7817          96 :         GTiffSetDeflateSubCodec(l_hTIFF);
    7818             : 
    7819          96 :         if (poDS->m_nZLevel != -1)
    7820             :         {
    7821          12 :             TIFFSetField(l_hTIFF, TIFFTAG_ZIPQUALITY, poDS->m_nZLevel);
    7822             :         }
    7823             :     }
    7824        1849 :     if (l_nCompression == COMPRESSION_JPEG)
    7825             :     {
    7826          73 :         if (poDS->m_nJpegQuality != -1)
    7827             :         {
    7828           9 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGQUALITY, poDS->m_nJpegQuality);
    7829             :         }
    7830          73 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGTABLESMODE, poDS->m_nJpegTablesMode);
    7831             :     }
    7832        1849 :     if (l_nCompression == COMPRESSION_LZMA)
    7833             :     {
    7834           7 :         if (poDS->m_nLZMAPreset != -1)
    7835             :         {
    7836           6 :             TIFFSetField(l_hTIFF, TIFFTAG_LZMAPRESET, poDS->m_nLZMAPreset);
    7837             :         }
    7838             :     }
    7839        1849 :     if (l_nCompression == COMPRESSION_ZSTD ||
    7840        1842 :         l_nCompression == COMPRESSION_LERC)
    7841             :     {
    7842          78 :         if (poDS->m_nZSTDLevel != -1)
    7843             :         {
    7844           8 :             TIFFSetField(l_hTIFF, TIFFTAG_ZSTD_LEVEL, poDS->m_nZSTDLevel);
    7845             :         }
    7846             :     }
    7847        1849 :     if (l_nCompression == COMPRESSION_LERC)
    7848             :     {
    7849          71 :         TIFFSetField(l_hTIFF, TIFFTAG_LERC_MAXZERROR, poDS->m_dfMaxZError);
    7850             :     }
    7851             : #if HAVE_JXL
    7852        1849 :     if (l_nCompression == COMPRESSION_JXL)
    7853             :     {
    7854          88 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_LOSSYNESS,
    7855          88 :                      poDS->m_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
    7856          88 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_EFFORT, poDS->m_nJXLEffort);
    7857          88 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_DISTANCE, poDS->m_fJXLDistance);
    7858          88 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE,
    7859          88 :                      poDS->m_fJXLAlphaDistance);
    7860             :     }
    7861             : #endif
    7862        1849 :     if (l_nCompression == COMPRESSION_WEBP)
    7863             :     {
    7864          14 :         if (poDS->m_nWebPLevel != -1)
    7865             :         {
    7866          14 :             TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LEVEL, poDS->m_nWebPLevel);
    7867             :         }
    7868             : 
    7869          14 :         if (poDS->m_bWebPLossless)
    7870             :         {
    7871           5 :             TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LOSSLESS, poDS->m_bWebPLossless);
    7872             :         }
    7873             :     }
    7874             : 
    7875             :     /* -------------------------------------------------------------------- */
    7876             :     /*      Do we want to ensure all blocks get written out on close to     */
    7877             :     /*      avoid sparse files?                                             */
    7878             :     /* -------------------------------------------------------------------- */
    7879        1849 :     if (!CPLFetchBool(papszOptions, "SPARSE_OK", false))
    7880        1821 :         poDS->m_bFillEmptyTilesAtClosing = true;
    7881             : 
    7882        1849 :     poDS->m_bWriteEmptyTiles =
    7883        3537 :         (bCopySrcOverviews && poDS->m_bFillEmptyTilesAtClosing) || bStreaming ||
    7884        1688 :         (poDS->m_nCompression != COMPRESSION_NONE &&
    7885         267 :          poDS->m_bFillEmptyTilesAtClosing);
    7886             :     // Only required for people writing non-compressed striped files in the
    7887             :     // rightorder and wanting all tstrips to be written in the same order
    7888             :     // so that the end result can be memory mapped without knowledge of each
    7889             :     // strip offset
    7890        1849 :     if (CPLTestBool(CSLFetchNameValueDef(
    7891        3698 :             papszOptions, "WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")) ||
    7892        1849 :         CPLTestBool(CSLFetchNameValueDef(
    7893             :             papszOptions, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")))
    7894             :     {
    7895           0 :         poDS->m_bWriteEmptyTiles = true;
    7896             :     }
    7897             : 
    7898             :     // Precreate (internal) mask, so that the IBuildOverviews() below
    7899             :     // has a chance to create also the overviews of the mask.
    7900        1849 :     CPLErr eErr = CE_None;
    7901             : 
    7902        1849 :     if (bCreateMask)
    7903             :     {
    7904          28 :         eErr = poDS->CreateMaskBand(nMaskFlags);
    7905          28 :         if (poDS->m_poMaskDS)
    7906             :         {
    7907          27 :             poDS->m_poMaskDS->m_bFillEmptyTilesAtClosing =
    7908          27 :                 poDS->m_bFillEmptyTilesAtClosing;
    7909          27 :             poDS->m_poMaskDS->m_bWriteEmptyTiles = poDS->m_bWriteEmptyTiles;
    7910             :         }
    7911             :     }
    7912             : 
    7913             :     /* -------------------------------------------------------------------- */
    7914             :     /*      Create and then copy existing overviews if requested            */
    7915             :     /*  We do it such that all the IFDs are at the beginning of the file,   */
    7916             :     /*  and that the imagery data for the smallest overview is written      */
    7917             :     /*  first, that way the file is more usable when embedded in a          */
    7918             :     /*  compressed stream.                                                  */
    7919             :     /* -------------------------------------------------------------------- */
    7920             : 
    7921             :     // For scaled progress due to overview copying.
    7922        1849 :     const int nBandsWidthMask = l_nBands + (bCreateMask ? 1 : 0);
    7923        1849 :     double dfTotalPixels =
    7924        1849 :         static_cast<double>(nXSize) * nYSize * nBandsWidthMask;
    7925        1849 :     double dfCurPixels = 0;
    7926             : 
    7927        1849 :     if (eErr == CE_None && bCopySrcOverviews)
    7928             :     {
    7929           0 :         std::unique_ptr<GDALDataset> poMaskOvrDS;
    7930             :         const char *pszMaskOvrDS =
    7931         162 :             CSLFetchNameValue(papszOptions, "@MASK_OVERVIEW_DATASET");
    7932         162 :         if (pszMaskOvrDS)
    7933             :         {
    7934           8 :             poMaskOvrDS.reset(GDALDataset::Open(pszMaskOvrDS));
    7935           8 :             if (!poMaskOvrDS)
    7936             :             {
    7937           0 :                 delete poDS;
    7938           0 :                 return nullptr;
    7939             :             }
    7940           8 :             if (poMaskOvrDS->GetRasterCount() != 1)
    7941             :             {
    7942           0 :                 delete poDS;
    7943           0 :                 return nullptr;
    7944             :             }
    7945             :         }
    7946         162 :         if (nSrcOverviews)
    7947             :         {
    7948          57 :             eErr = poDS->CreateOverviewsFromSrcOverviews(poSrcDS, poOvrDS.get(),
    7949             :                                                          nSrcOverviews);
    7950             : 
    7951         163 :             if (eErr == CE_None &&
    7952          57 :                 (poMaskOvrDS != nullptr ||
    7953          49 :                  (poSrcDS->GetRasterBand(1)->GetOverview(0) &&
    7954          26 :                   poSrcDS->GetRasterBand(1)->GetOverview(0)->GetMaskFlags() ==
    7955             :                       GMF_PER_DATASET)))
    7956             :             {
    7957          14 :                 int nOvrBlockXSize = 0;
    7958          14 :                 int nOvrBlockYSize = 0;
    7959          14 :                 GTIFFGetOverviewBlockSize(
    7960             :                     GDALRasterBand::ToHandle(poDS->GetRasterBand(1)),
    7961             :                     &nOvrBlockXSize, &nOvrBlockYSize);
    7962          14 :                 eErr = poDS->CreateInternalMaskOverviews(nOvrBlockXSize,
    7963             :                                                          nOvrBlockYSize);
    7964             :             }
    7965             :         }
    7966             : 
    7967         162 :         TIFFForceStrileArrayWriting(poDS->m_hTIFF);
    7968             : 
    7969         162 :         if (poDS->m_poMaskDS)
    7970             :         {
    7971          22 :             TIFFForceStrileArrayWriting(poDS->m_poMaskDS->m_hTIFF);
    7972             :         }
    7973             : 
    7974         274 :         for (int i = 0; i < poDS->m_nOverviewCount; i++)
    7975             :         {
    7976         112 :             TIFFForceStrileArrayWriting(poDS->m_papoOverviewDS[i]->m_hTIFF);
    7977             : 
    7978         112 :             if (poDS->m_papoOverviewDS[i]->m_poMaskDS)
    7979             :             {
    7980          27 :                 TIFFForceStrileArrayWriting(
    7981          27 :                     poDS->m_papoOverviewDS[i]->m_poMaskDS->m_hTIFF);
    7982             :             }
    7983             :         }
    7984             : 
    7985         162 :         if (eErr == CE_None && nSrcOverviews)
    7986             :         {
    7987          57 :             if (poDS->m_nOverviewCount != nSrcOverviews)
    7988             :             {
    7989           0 :                 ReportError(
    7990             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    7991             :                     "Did only manage to instantiate %d overview levels, "
    7992             :                     "whereas source contains %d",
    7993           0 :                     poDS->m_nOverviewCount, nSrcOverviews);
    7994           0 :                 eErr = CE_Failure;
    7995             :             }
    7996             : 
    7997         169 :             for (int i = 0; eErr == CE_None && i < nSrcOverviews; ++i)
    7998             :             {
    7999             :                 GDALRasterBand *poOvrBand =
    8000             :                     poOvrDS
    8001         180 :                         ? (i == 0
    8002          68 :                                ? poOvrDS->GetRasterBand(1)
    8003          37 :                                : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    8004         156 :                         : poSrcDS->GetRasterBand(1)->GetOverview(i);
    8005             :                 const double dfOvrPixels =
    8006         112 :                     static_cast<double>(poOvrBand->GetXSize()) *
    8007         112 :                     poOvrBand->GetYSize();
    8008         112 :                 dfTotalPixels += dfOvrPixels * l_nBands;
    8009         213 :                 if (poOvrBand->GetMaskFlags() == GMF_PER_DATASET ||
    8010         101 :                     poMaskOvrDS != nullptr)
    8011             :                 {
    8012          27 :                     dfTotalPixels += dfOvrPixels;
    8013             :                 }
    8014          85 :                 else if (i == 0 && poDS->GetRasterBand(1)->GetMaskFlags() ==
    8015             :                                        GMF_PER_DATASET)
    8016             :                 {
    8017           1 :                     ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
    8018             :                                 "Source dataset has a mask band on full "
    8019             :                                 "resolution, overviews on the regular bands, "
    8020             :                                 "but lacks overviews on the mask band.");
    8021             :                 }
    8022             :             }
    8023             : 
    8024          57 :             char *papszCopyWholeRasterOptions[2] = {nullptr, nullptr};
    8025          57 :             if (l_nCompression != COMPRESSION_NONE)
    8026          49 :                 papszCopyWholeRasterOptions[0] =
    8027             :                     const_cast<char *>("COMPRESSED=YES");
    8028             :             // Now copy the imagery.
    8029             :             // Begin with the smallest overview.
    8030          57 :             for (int iOvrLevel = nSrcOverviews - 1;
    8031         168 :                  eErr == CE_None && iOvrLevel >= 0; --iOvrLevel)
    8032             :             {
    8033         111 :                 auto poDstDS = poDS->m_papoOverviewDS[iOvrLevel];
    8034             : 
    8035             :                 // Create a fake dataset with the source overview level so that
    8036             :                 // GDALDatasetCopyWholeRaster can cope with it.
    8037             :                 GDALDataset *poSrcOvrDS =
    8038             :                     poOvrDS
    8039         148 :                         ? (iOvrLevel == 0 ? poOvrDS.get()
    8040          37 :                                           : GDALCreateOverviewDataset(
    8041             :                                                 poOvrDS.get(), iOvrLevel - 1,
    8042             :                                                 /* bThisLevelOnly = */ true))
    8043          43 :                         : GDALCreateOverviewDataset(
    8044             :                               poSrcDS, iOvrLevel,
    8045         111 :                               /* bThisLevelOnly = */ true);
    8046             :                 GDALRasterBand *poSrcOvrBand =
    8047         179 :                     poOvrDS ? (iOvrLevel == 0
    8048          68 :                                    ? poOvrDS->GetRasterBand(1)
    8049          74 :                                    : poOvrDS->GetRasterBand(1)->GetOverview(
    8050          37 :                                          iOvrLevel - 1))
    8051         154 :                             : poSrcDS->GetRasterBand(1)->GetOverview(iOvrLevel);
    8052             :                 double dfNextCurPixels =
    8053             :                     dfCurPixels +
    8054         111 :                     static_cast<double>(poSrcOvrBand->GetXSize()) *
    8055         111 :                         poSrcOvrBand->GetYSize() * l_nBands;
    8056             : 
    8057         111 :                 poDstDS->m_bBlockOrderRowMajor = true;
    8058         111 :                 poDstDS->m_bLeaderSizeAsUInt4 = true;
    8059         111 :                 poDstDS->m_bTrailerRepeatedLast4BytesRepeated = true;
    8060         111 :                 poDstDS->m_bFillEmptyTilesAtClosing =
    8061         111 :                     poDS->m_bFillEmptyTilesAtClosing;
    8062         111 :                 poDstDS->m_bWriteEmptyTiles = poDS->m_bWriteEmptyTiles;
    8063         111 :                 GDALRasterBand *poSrcMaskBand = nullptr;
    8064         111 :                 if (poDstDS->m_poMaskDS)
    8065             :                 {
    8066          27 :                     poDstDS->m_poMaskDS->m_bBlockOrderRowMajor = true;
    8067          27 :                     poDstDS->m_poMaskDS->m_bLeaderSizeAsUInt4 = true;
    8068          27 :                     poDstDS->m_poMaskDS->m_bTrailerRepeatedLast4BytesRepeated =
    8069             :                         true;
    8070          27 :                     poDstDS->m_poMaskDS->m_bFillEmptyTilesAtClosing =
    8071          27 :                         poDS->m_bFillEmptyTilesAtClosing;
    8072          27 :                     poDstDS->m_poMaskDS->m_bWriteEmptyTiles =
    8073          27 :                         poDS->m_bWriteEmptyTiles;
    8074             : 
    8075          27 :                     poSrcMaskBand =
    8076             :                         poMaskOvrDS
    8077          43 :                             ? (iOvrLevel == 0
    8078          16 :                                    ? poMaskOvrDS->GetRasterBand(1)
    8079          16 :                                    : poMaskOvrDS->GetRasterBand(1)->GetOverview(
    8080           8 :                                          iOvrLevel - 1))
    8081          38 :                             : poSrcOvrBand->GetMaskBand();
    8082             :                 }
    8083             : 
    8084         111 :                 if (l_nBands == 1 ||
    8085          56 :                     poDstDS->m_nPlanarConfig == PLANARCONFIG_CONTIG)
    8086             :                 {
    8087         110 :                     if (poDstDS->m_poMaskDS)
    8088             :                     {
    8089          27 :                         dfNextCurPixels +=
    8090          27 :                             static_cast<double>(poSrcOvrBand->GetXSize()) *
    8091          27 :                             poSrcOvrBand->GetYSize();
    8092             :                     }
    8093         110 :                     void *pScaledData = GDALCreateScaledProgress(
    8094             :                         dfCurPixels / dfTotalPixels,
    8095             :                         dfNextCurPixels / dfTotalPixels, pfnProgress,
    8096             :                         pProgressData);
    8097             : 
    8098             :                     eErr =
    8099         110 :                         CopyImageryAndMask(poDstDS, poSrcOvrDS, poSrcMaskBand,
    8100             :                                            GDALScaledProgress, pScaledData);
    8101             : 
    8102         110 :                     dfCurPixels = dfNextCurPixels;
    8103         110 :                     GDALDestroyScaledProgress(pScaledData);
    8104             :                 }
    8105             :                 else
    8106             :                 {
    8107           1 :                     void *pScaledData = GDALCreateScaledProgress(
    8108             :                         dfCurPixels / dfTotalPixels,
    8109             :                         dfNextCurPixels / dfTotalPixels, pfnProgress,
    8110             :                         pProgressData);
    8111             : 
    8112           1 :                     eErr = GDALDatasetCopyWholeRaster(
    8113             :                         GDALDataset::ToHandle(poSrcOvrDS),
    8114             :                         GDALDataset::ToHandle(poDstDS),
    8115             :                         papszCopyWholeRasterOptions, GDALScaledProgress,
    8116             :                         pScaledData);
    8117             : 
    8118           1 :                     dfCurPixels = dfNextCurPixels;
    8119           1 :                     GDALDestroyScaledProgress(pScaledData);
    8120             : 
    8121           1 :                     poDstDS->FlushCache(false);
    8122             : 
    8123             :                     // Copy mask of the overview.
    8124           1 :                     if (eErr == CE_None &&
    8125           1 :                         (poMaskOvrDS ||
    8126           2 :                          poSrcOvrBand->GetMaskFlags() == GMF_PER_DATASET) &&
    8127           0 :                         poDstDS->m_poMaskDS != nullptr)
    8128             :                     {
    8129           0 :                         dfNextCurPixels +=
    8130           0 :                             static_cast<double>(poSrcOvrBand->GetXSize()) *
    8131           0 :                             poSrcOvrBand->GetYSize();
    8132           0 :                         pScaledData = GDALCreateScaledProgress(
    8133             :                             dfCurPixels / dfTotalPixels,
    8134             :                             dfNextCurPixels / dfTotalPixels, pfnProgress,
    8135             :                             pProgressData);
    8136           0 :                         eErr = GDALRasterBandCopyWholeRaster(
    8137             :                             poSrcMaskBand,
    8138           0 :                             poDstDS->m_poMaskDS->GetRasterBand(1),
    8139             :                             papszCopyWholeRasterOptions, GDALScaledProgress,
    8140             :                             pScaledData);
    8141           0 :                         dfCurPixels = dfNextCurPixels;
    8142           0 :                         GDALDestroyScaledProgress(pScaledData);
    8143           0 :                         poDstDS->m_poMaskDS->FlushCache(false);
    8144             :                     }
    8145             :                 }
    8146             : 
    8147         111 :                 if (poSrcOvrDS != poOvrDS.get())
    8148          80 :                     delete poSrcOvrDS;
    8149         111 :                 poSrcOvrDS = nullptr;
    8150             :             }
    8151             :         }
    8152             :     }
    8153             : 
    8154             :     /* -------------------------------------------------------------------- */
    8155             :     /*      Copy actual imagery.                                            */
    8156             :     /* -------------------------------------------------------------------- */
    8157        1849 :     double dfNextCurPixels =
    8158        1849 :         dfCurPixels + static_cast<double>(nXSize) * nYSize * l_nBands;
    8159        1849 :     void *pScaledData = GDALCreateScaledProgress(
    8160             :         dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
    8161             :         pfnProgress, pProgressData);
    8162             : 
    8163             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8164        1849 :     bool bTryCopy = true;
    8165             : #endif
    8166             : 
    8167             : #ifdef HAVE_LIBJPEG
    8168        1849 :     if (bCopyFromJPEG)
    8169             :     {
    8170          12 :         eErr = GTIFF_CopyFromJPEG(poDS, poSrcDS, pfnProgress, pProgressData,
    8171             :                                   bTryCopy);
    8172             : 
    8173             :         // In case of failure in the decompression step, try normal copy.
    8174          12 :         if (bTryCopy)
    8175           0 :             eErr = CE_None;
    8176             :     }
    8177             : #endif
    8178             : 
    8179             : #ifdef JPEG_DIRECT_COPY
    8180             :     if (bDirectCopyFromJPEG)
    8181             :     {
    8182             :         eErr = GTIFF_DirectCopyFromJPEG(poDS, poSrcDS, pfnProgress,
    8183             :                                         pProgressData, bTryCopy);
    8184             : 
    8185             :         // In case of failure in the reading step, try normal copy.
    8186             :         if (bTryCopy)
    8187             :             eErr = CE_None;
    8188             :     }
    8189             : #endif
    8190             : 
    8191        1849 :     bool bWriteMask = true;
    8192        1849 :     if (
    8193             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8194        1837 :         bTryCopy &&
    8195             : #endif
    8196        1837 :         (poDS->m_bTreatAsSplit || poDS->m_bTreatAsSplitBitmap))
    8197             :     {
    8198             :         // For split bands, we use TIFFWriteScanline() interface.
    8199           9 :         CPLAssert(poDS->m_nBitsPerSample == 8 || poDS->m_nBitsPerSample == 1);
    8200             : 
    8201           9 :         if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG && poDS->nBands > 1)
    8202             :         {
    8203             :             GByte *pabyScanline = static_cast<GByte *>(
    8204           3 :                 VSI_MALLOC_VERBOSE(TIFFScanlineSize(l_hTIFF)));
    8205           3 :             if (pabyScanline == nullptr)
    8206           0 :                 eErr = CE_Failure;
    8207        9052 :             for (int j = 0; j < nYSize && eErr == CE_None; ++j)
    8208             :             {
    8209       18098 :                 eErr = poSrcDS->RasterIO(GF_Read, 0, j, nXSize, 1, pabyScanline,
    8210             :                                          nXSize, 1, GDT_Byte, l_nBands, nullptr,
    8211        9049 :                                          poDS->nBands, 0, 1, nullptr);
    8212       18098 :                 if (eErr == CE_None &&
    8213        9049 :                     TIFFWriteScanline(l_hTIFF, pabyScanline, j, 0) == -1)
    8214             :                 {
    8215           0 :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    8216             :                                 "TIFFWriteScanline() failed.");
    8217           0 :                     eErr = CE_Failure;
    8218             :                 }
    8219        9049 :                 if (!GDALScaledProgress((j + 1) * 1.0 / nYSize, nullptr,
    8220             :                                         pScaledData))
    8221           0 :                     eErr = CE_Failure;
    8222             :             }
    8223           3 :             CPLFree(pabyScanline);
    8224             :         }
    8225             :         else
    8226             :         {
    8227             :             GByte *pabyScanline =
    8228           6 :                 static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
    8229           6 :             if (pabyScanline == nullptr)
    8230           0 :                 eErr = CE_Failure;
    8231             :             else
    8232           6 :                 eErr = CE_None;
    8233          14 :             for (int iBand = 1; iBand <= l_nBands && eErr == CE_None; ++iBand)
    8234             :             {
    8235       48211 :                 for (int j = 0; j < nYSize && eErr == CE_None; ++j)
    8236             :                 {
    8237       48203 :                     eErr = poSrcDS->GetRasterBand(iBand)->RasterIO(
    8238             :                         GF_Read, 0, j, nXSize, 1, pabyScanline, nXSize, 1,
    8239             :                         GDT_Byte, 0, 0, nullptr);
    8240       48203 :                     if (poDS->m_bTreatAsSplitBitmap)
    8241             :                     {
    8242     7225210 :                         for (int i = 0; i < nXSize; ++i)
    8243             :                         {
    8244     7216010 :                             const GByte byVal = pabyScanline[i];
    8245     7216010 :                             if ((i & 0x7) == 0)
    8246      902001 :                                 pabyScanline[i >> 3] = 0;
    8247     7216010 :                             if (byVal)
    8248     7097220 :                                 pabyScanline[i >> 3] |= 0x80 >> (i & 0x7);
    8249             :                         }
    8250             :                     }
    8251       96406 :                     if (eErr == CE_None &&
    8252       48203 :                         TIFFWriteScanline(l_hTIFF, pabyScanline, j,
    8253       48203 :                                           static_cast<uint16_t>(iBand - 1)) ==
    8254             :                             -1)
    8255             :                     {
    8256           0 :                         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    8257             :                                     "TIFFWriteScanline() failed.");
    8258           0 :                         eErr = CE_Failure;
    8259             :                     }
    8260       48203 :                     if (!GDALScaledProgress((j + 1 + (iBand - 1) * nYSize) *
    8261       48203 :                                                 1.0 / (l_nBands * nYSize),
    8262             :                                             nullptr, pScaledData))
    8263           0 :                         eErr = CE_Failure;
    8264             :                 }
    8265             :             }
    8266           6 :             CPLFree(pabyScanline);
    8267             :         }
    8268             : 
    8269             :         // Necessary to be able to read the file without re-opening.
    8270           9 :         TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(l_hTIFF);
    8271             : 
    8272           9 :         TIFFFlushData(l_hTIFF);
    8273             : 
    8274           9 :         toff_t nNewDirOffset = pfnSizeProc(TIFFClientdata(l_hTIFF));
    8275           9 :         if ((nNewDirOffset % 2) == 1)
    8276           5 :             ++nNewDirOffset;
    8277             : 
    8278           9 :         TIFFFlush(l_hTIFF);
    8279             : 
    8280           9 :         if (poDS->m_nDirOffset != TIFFCurrentDirOffset(l_hTIFF))
    8281             :         {
    8282           0 :             poDS->m_nDirOffset = nNewDirOffset;
    8283           0 :             CPLDebug("GTiff", "directory moved during flush.");
    8284           9 :         }
    8285             :     }
    8286        1840 :     else if (
    8287             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8288        1828 :         bTryCopy &&
    8289             : #endif
    8290             :         eErr == CE_None)
    8291             :     {
    8292        1827 :         const char *papszCopyWholeRasterOptions[3] = {nullptr, nullptr,
    8293             :                                                       nullptr};
    8294        1827 :         int iNextOption = 0;
    8295        1827 :         papszCopyWholeRasterOptions[iNextOption++] = "SKIP_HOLES=YES";
    8296        1827 :         if (l_nCompression != COMPRESSION_NONE)
    8297             :         {
    8298         396 :             papszCopyWholeRasterOptions[iNextOption++] = "COMPRESSED=YES";
    8299             :         }
    8300             :         // For streaming with separate, we really want that bands are written
    8301             :         // after each other, even if the source is pixel interleaved.
    8302        1431 :         else if (bStreaming && poDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE)
    8303             :         {
    8304           1 :             papszCopyWholeRasterOptions[iNextOption++] = "INTERLEAVE=BAND";
    8305             :         }
    8306             : 
    8307        1827 :         if (bCopySrcOverviews &&
    8308          87 :             (l_nBands == 1 || poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG))
    8309             :         {
    8310         160 :             poDS->m_bBlockOrderRowMajor = true;
    8311         160 :             poDS->m_bLeaderSizeAsUInt4 = true;
    8312         160 :             poDS->m_bTrailerRepeatedLast4BytesRepeated = true;
    8313         160 :             if (poDS->m_poMaskDS)
    8314             :             {
    8315          22 :                 poDS->m_poMaskDS->m_bBlockOrderRowMajor = true;
    8316          22 :                 poDS->m_poMaskDS->m_bLeaderSizeAsUInt4 = true;
    8317          22 :                 poDS->m_poMaskDS->m_bTrailerRepeatedLast4BytesRepeated = true;
    8318             :             }
    8319             : 
    8320         160 :             if (poDS->m_poMaskDS)
    8321             :             {
    8322          22 :                 GDALDestroyScaledProgress(pScaledData);
    8323             :                 pScaledData =
    8324          22 :                     GDALCreateScaledProgress(dfCurPixels / dfTotalPixels, 1.0,
    8325             :                                              pfnProgress, pProgressData);
    8326             :             }
    8327             : 
    8328         160 :             eErr = CopyImageryAndMask(poDS, poSrcDS,
    8329         160 :                                       poSrcDS->GetRasterBand(1)->GetMaskBand(),
    8330             :                                       GDALScaledProgress, pScaledData);
    8331         160 :             if (poDS->m_poMaskDS)
    8332             :             {
    8333          22 :                 bWriteMask = false;
    8334             :             }
    8335             :         }
    8336             :         else
    8337             :         {
    8338        1667 :             eErr = GDALDatasetCopyWholeRaster(
    8339             :                 /* (GDALDatasetH) */ poSrcDS,
    8340             :                 /* (GDALDatasetH) */ poDS, papszCopyWholeRasterOptions,
    8341             :                 GDALScaledProgress, pScaledData);
    8342             :         }
    8343             :     }
    8344             : 
    8345        1849 :     GDALDestroyScaledProgress(pScaledData);
    8346             : 
    8347        1849 :     if (eErr == CE_None && !bStreaming && bWriteMask)
    8348             :     {
    8349        1808 :         pScaledData = GDALCreateScaledProgress(dfNextCurPixels / dfTotalPixels,
    8350             :                                                1.0, pfnProgress, pProgressData);
    8351        1808 :         if (poDS->m_poMaskDS)
    8352             :         {
    8353           5 :             const char *l_papszOptions[2] = {"COMPRESSED=YES", nullptr};
    8354           5 :             eErr = GDALRasterBandCopyWholeRaster(
    8355           5 :                 poSrcDS->GetRasterBand(1)->GetMaskBand(),
    8356           5 :                 poDS->GetRasterBand(1)->GetMaskBand(),
    8357             :                 const_cast<char **>(l_papszOptions), GDALScaledProgress,
    8358             :                 pScaledData);
    8359             :         }
    8360             :         else
    8361             :         {
    8362             :             eErr =
    8363        1803 :                 GDALDriver::DefaultCopyMasks(poSrcDS, poDS, bStrict, nullptr,
    8364             :                                              GDALScaledProgress, pScaledData);
    8365             :         }
    8366        1808 :         GDALDestroyScaledProgress(pScaledData);
    8367             :     }
    8368             : 
    8369        1849 :     poDS->m_bWriteCOGLayout = false;
    8370             : 
    8371        1849 :     if (eErr == CE_Failure)
    8372             :     {
    8373          15 :         delete poDS;
    8374          15 :         poDS = nullptr;
    8375             : 
    8376          15 :         if (CPLTestBool(CPLGetConfigOption("GTIFF_DELETE_ON_ERROR", "YES")))
    8377             :         {
    8378          14 :             if (!bStreaming)
    8379             :             {
    8380             :                 // Should really delete more carefully.
    8381          14 :                 VSIUnlink(pszFilename);
    8382             :             }
    8383             :         }
    8384             :     }
    8385             : 
    8386        1849 :     return poDS;
    8387             : }
    8388             : 
    8389             : /************************************************************************/
    8390             : /*                           SetSpatialRef()                            */
    8391             : /************************************************************************/
    8392             : 
    8393        1351 : CPLErr GTiffDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    8394             : 
    8395             : {
    8396        1351 :     if (m_bStreamingOut && m_bCrystalized)
    8397             :     {
    8398           1 :         ReportError(CE_Failure, CPLE_NotSupported,
    8399             :                     "Cannot modify projection at that point in "
    8400             :                     "a streamed output file");
    8401           1 :         return CE_Failure;
    8402             :     }
    8403             : 
    8404        1350 :     LoadGeoreferencingAndPamIfNeeded();
    8405        1350 :     LookForProjection();
    8406             : 
    8407        1350 :     CPLErr eErr = CE_None;
    8408        1350 :     if (eAccess == GA_Update)
    8409             :     {
    8410        1352 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    8411           6 :             (GetPamFlags() & GPF_DISABLED) == 0)
    8412             :         {
    8413           6 :             eErr = GDALPamDataset::SetSpatialRef(poSRS);
    8414             :         }
    8415             :         else
    8416             :         {
    8417        1340 :             if (GDALPamDataset::GetSpatialRef() != nullptr)
    8418             :             {
    8419             :                 // Cancel any existing SRS from PAM file.
    8420           1 :                 GDALPamDataset::SetSpatialRef(nullptr);
    8421             :             }
    8422        1340 :             m_bGeoTIFFInfoChanged = true;
    8423             :         }
    8424             :     }
    8425             :     else
    8426             :     {
    8427           4 :         CPLDebug("GTIFF", "SetSpatialRef() goes to PAM instead of TIFF tags");
    8428           4 :         eErr = GDALPamDataset::SetSpatialRef(poSRS);
    8429             :     }
    8430             : 
    8431        1350 :     if (eErr == CE_None)
    8432             :     {
    8433        1350 :         if (poSRS == nullptr || poSRS->IsEmpty())
    8434             :         {
    8435          15 :             if (!m_oSRS.IsEmpty())
    8436             :             {
    8437           3 :                 m_bForceUnsetProjection = true;
    8438             :             }
    8439          15 :             m_oSRS.Clear();
    8440             :         }
    8441             :         else
    8442             :         {
    8443        1335 :             m_oSRS = *poSRS;
    8444        1335 :             m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    8445             :         }
    8446             :     }
    8447             : 
    8448        1350 :     return eErr;
    8449             : }
    8450             : 
    8451             : /************************************************************************/
    8452             : /*                          SetGeoTransform()                           */
    8453             : /************************************************************************/
    8454             : 
    8455        1525 : CPLErr GTiffDataset::SetGeoTransform(double *padfTransform)
    8456             : 
    8457             : {
    8458        1525 :     if (m_bStreamingOut && m_bCrystalized)
    8459             :     {
    8460           1 :         ReportError(CE_Failure, CPLE_NotSupported,
    8461             :                     "Cannot modify geotransform at that point in a "
    8462             :                     "streamed output file");
    8463           1 :         return CE_Failure;
    8464             :     }
    8465             : 
    8466        1524 :     LoadGeoreferencingAndPamIfNeeded();
    8467             : 
    8468        1524 :     CPLErr eErr = CE_None;
    8469        1524 :     if (eAccess == GA_Update)
    8470             :     {
    8471        1518 :         if (!m_aoGCPs.empty())
    8472             :         {
    8473           1 :             ReportError(CE_Warning, CPLE_AppDefined,
    8474             :                         "GCPs previously set are going to be cleared "
    8475             :                         "due to the setting of a geotransform.");
    8476           1 :             m_bForceUnsetGTOrGCPs = true;
    8477           1 :             m_aoGCPs.clear();
    8478             :         }
    8479        1517 :         else if (padfTransform[0] == 0.0 && padfTransform[1] == 0.0 &&
    8480           2 :                  padfTransform[2] == 0.0 && padfTransform[3] == 0.0 &&
    8481           2 :                  padfTransform[4] == 0.0 && padfTransform[5] == 0.0)
    8482             :         {
    8483           2 :             if (m_bGeoTransformValid)
    8484             :             {
    8485           2 :                 m_bForceUnsetGTOrGCPs = true;
    8486           2 :                 m_bGeoTIFFInfoChanged = true;
    8487             :             }
    8488           2 :             m_bGeoTransformValid = false;
    8489           2 :             memcpy(m_adfGeoTransform, padfTransform, sizeof(double) * 6);
    8490           2 :             return CE_None;
    8491             :         }
    8492             : 
    8493        3040 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    8494           8 :             !CPLFetchBool(m_papszCreationOptions, "TFW", false) &&
    8495        1528 :             !CPLFetchBool(m_papszCreationOptions, "WORLDFILE", false) &&
    8496           4 :             (GetPamFlags() & GPF_DISABLED) == 0)
    8497             :         {
    8498           4 :             eErr = GDALPamDataset::SetGeoTransform(padfTransform);
    8499             :         }
    8500             :         else
    8501             :         {
    8502             :             // Cancel any existing geotransform from PAM file.
    8503        1512 :             GDALPamDataset::DeleteGeoTransform();
    8504        1512 :             m_bGeoTIFFInfoChanged = true;
    8505             :         }
    8506             :     }
    8507             :     else
    8508             :     {
    8509           6 :         CPLDebug("GTIFF", "SetGeoTransform() goes to PAM instead of TIFF tags");
    8510           6 :         eErr = GDALPamDataset::SetGeoTransform(padfTransform);
    8511             :     }
    8512             : 
    8513        1522 :     if (eErr == CE_None)
    8514             :     {
    8515        1522 :         memcpy(m_adfGeoTransform, padfTransform, sizeof(double) * 6);
    8516        1522 :         m_bGeoTransformValid = true;
    8517             :     }
    8518             : 
    8519        1522 :     return eErr;
    8520             : }
    8521             : 
    8522             : /************************************************************************/
    8523             : /*                               SetGCPs()                              */
    8524             : /************************************************************************/
    8525             : 
    8526          20 : CPLErr GTiffDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
    8527             :                              const OGRSpatialReference *poGCPSRS)
    8528             : {
    8529          20 :     CPLErr eErr = CE_None;
    8530          20 :     LoadGeoreferencingAndPamIfNeeded();
    8531          20 :     LookForProjection();
    8532             : 
    8533          20 :     if (eAccess == GA_Update)
    8534             :     {
    8535          18 :         if (!m_aoGCPs.empty() && nGCPCountIn == 0)
    8536             :         {
    8537           3 :             m_bForceUnsetGTOrGCPs = true;
    8538             :         }
    8539          15 :         else if (nGCPCountIn > 0 && m_bGeoTransformValid)
    8540             :         {
    8541           4 :             ReportError(CE_Warning, CPLE_AppDefined,
    8542             :                         "A geotransform previously set is going to be cleared "
    8543             :                         "due to the setting of GCPs.");
    8544           4 :             m_adfGeoTransform[0] = 0.0;
    8545           4 :             m_adfGeoTransform[1] = 1.0;
    8546           4 :             m_adfGeoTransform[2] = 0.0;
    8547           4 :             m_adfGeoTransform[3] = 0.0;
    8548           4 :             m_adfGeoTransform[4] = 0.0;
    8549           4 :             m_adfGeoTransform[5] = 1.0;
    8550           4 :             m_bGeoTransformValid = false;
    8551           4 :             m_bForceUnsetGTOrGCPs = true;
    8552             :         }
    8553          18 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    8554           0 :             (GetPamFlags() & GPF_DISABLED) == 0)
    8555             :         {
    8556           0 :             eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn, poGCPSRS);
    8557             :         }
    8558             :         else
    8559             :         {
    8560          18 :             if (nGCPCountIn > knMAX_GCP_COUNT)
    8561             :             {
    8562           2 :                 if (GDALPamDataset::GetGCPCount() == 0 && !m_aoGCPs.empty())
    8563             :                 {
    8564           1 :                     m_bForceUnsetGTOrGCPs = true;
    8565             :                 }
    8566           2 :                 ReportError(CE_Warning, CPLE_AppDefined,
    8567             :                             "Trying to write %d GCPs, whereas the maximum "
    8568             :                             "supported in GeoTIFF tag is %d. "
    8569             :                             "Falling back to writing them to PAM",
    8570             :                             nGCPCountIn, knMAX_GCP_COUNT);
    8571           2 :                 eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn,
    8572             :                                                poGCPSRS);
    8573             :             }
    8574          16 :             else if (GDALPamDataset::GetGCPCount() > 0)
    8575             :             {
    8576             :                 // Cancel any existing GCPs from PAM file.
    8577           1 :                 GDALPamDataset::SetGCPs(
    8578             :                     0, nullptr,
    8579             :                     static_cast<const OGRSpatialReference *>(nullptr));
    8580             :             }
    8581          18 :             m_bGeoTIFFInfoChanged = true;
    8582             :         }
    8583             :     }
    8584             :     else
    8585             :     {
    8586           2 :         CPLDebug("GTIFF", "SetGCPs() goes to PAM instead of TIFF tags");
    8587           2 :         eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn, poGCPSRS);
    8588             :     }
    8589             : 
    8590          20 :     if (eErr == CE_None)
    8591             :     {
    8592          20 :         if (poGCPSRS == nullptr || poGCPSRS->IsEmpty())
    8593             :         {
    8594          10 :             if (!m_oSRS.IsEmpty())
    8595             :             {
    8596           4 :                 m_bForceUnsetProjection = true;
    8597             :             }
    8598          10 :             m_oSRS.Clear();
    8599             :         }
    8600             :         else
    8601             :         {
    8602          10 :             m_oSRS = *poGCPSRS;
    8603          10 :             m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    8604             :         }
    8605             : 
    8606          20 :         m_aoGCPs = gdal::GCP::fromC(pasGCPListIn, nGCPCountIn);
    8607             :     }
    8608             : 
    8609          20 :     return eErr;
    8610             : }
    8611             : 
    8612             : /************************************************************************/
    8613             : /*                            SetMetadata()                             */
    8614             : /************************************************************************/
    8615        2415 : CPLErr GTiffDataset::SetMetadata(char **papszMD, const char *pszDomain)
    8616             : 
    8617             : {
    8618        2415 :     LoadGeoreferencingAndPamIfNeeded();
    8619             : 
    8620        2415 :     if (m_bStreamingOut && m_bCrystalized)
    8621             :     {
    8622           1 :         ReportError(
    8623             :             CE_Failure, CPLE_NotSupported,
    8624             :             "Cannot modify metadata at that point in a streamed output file");
    8625           1 :         return CE_Failure;
    8626             :     }
    8627             : 
    8628        2414 :     CPLErr eErr = CE_None;
    8629        2414 :     if (eAccess == GA_Update)
    8630             :     {
    8631        2412 :         if (pszDomain != nullptr && EQUAL(pszDomain, MD_DOMAIN_RPC))
    8632             :         {
    8633             :             // So that a subsequent GetMetadata() wouldn't override our new
    8634             :             // values
    8635          22 :             LoadMetadata();
    8636          22 :             m_bForceUnsetRPC = (CSLCount(papszMD) == 0);
    8637             :         }
    8638             : 
    8639        2412 :         if ((papszMD != nullptr) && (pszDomain != nullptr) &&
    8640        1622 :             EQUAL(pszDomain, "COLOR_PROFILE"))
    8641             :         {
    8642           0 :             m_bColorProfileMetadataChanged = true;
    8643             :         }
    8644        2412 :         else if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
    8645             :         {
    8646        2412 :             m_bMetadataChanged = true;
    8647             :             // Cancel any existing metadata from PAM file.
    8648        2412 :             if (GDALPamDataset::GetMetadata(pszDomain) != nullptr)
    8649           1 :                 GDALPamDataset::SetMetadata(nullptr, pszDomain);
    8650             :         }
    8651             : 
    8652        4787 :         if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    8653        2375 :             CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT) != nullptr)
    8654             :         {
    8655        1761 :             const char *pszPrevValue = GetMetadataItem(GDALMD_AREA_OR_POINT);
    8656             :             const char *pszNewValue =
    8657        1761 :                 CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT);
    8658        1761 :             if (pszPrevValue == nullptr || pszNewValue == nullptr ||
    8659        1361 :                 !EQUAL(pszPrevValue, pszNewValue))
    8660             :             {
    8661         404 :                 LookForProjection();
    8662         404 :                 m_bGeoTIFFInfoChanged = true;
    8663             :             }
    8664             :         }
    8665             : 
    8666        2412 :         if (pszDomain != nullptr && EQUAL(pszDomain, "xml:XMP"))
    8667             :         {
    8668           2 :             if (papszMD != nullptr && *papszMD != nullptr)
    8669             :             {
    8670           1 :                 int nTagSize = static_cast<int>(strlen(*papszMD));
    8671           1 :                 TIFFSetField(m_hTIFF, TIFFTAG_XMLPACKET, nTagSize, *papszMD);
    8672             :             }
    8673             :             else
    8674             :             {
    8675           1 :                 TIFFUnsetField(m_hTIFF, TIFFTAG_XMLPACKET);
    8676             :             }
    8677             :         }
    8678             :     }
    8679             :     else
    8680             :     {
    8681           2 :         CPLDebug(
    8682             :             "GTIFF",
    8683             :             "GTiffDataset::SetMetadata() goes to PAM instead of TIFF tags");
    8684           2 :         eErr = GDALPamDataset::SetMetadata(papszMD, pszDomain);
    8685             :     }
    8686             : 
    8687        2414 :     if (eErr == CE_None)
    8688             :     {
    8689        2414 :         eErr = m_oGTiffMDMD.SetMetadata(papszMD, pszDomain);
    8690             :     }
    8691        2414 :     return eErr;
    8692             : }
    8693             : 
    8694             : /************************************************************************/
    8695             : /*                          SetMetadataItem()                           */
    8696             : /************************************************************************/
    8697             : 
    8698        3174 : CPLErr GTiffDataset::SetMetadataItem(const char *pszName, const char *pszValue,
    8699             :                                      const char *pszDomain)
    8700             : 
    8701             : {
    8702        3174 :     LoadGeoreferencingAndPamIfNeeded();
    8703             : 
    8704        3174 :     if (m_bStreamingOut && m_bCrystalized)
    8705             :     {
    8706           1 :         ReportError(
    8707             :             CE_Failure, CPLE_NotSupported,
    8708             :             "Cannot modify metadata at that point in a streamed output file");
    8709           1 :         return CE_Failure;
    8710             :     }
    8711             : 
    8712        3173 :     CPLErr eErr = CE_None;
    8713        3173 :     if (eAccess == GA_Update)
    8714             :     {
    8715        3166 :         if ((pszDomain != nullptr) && EQUAL(pszDomain, "COLOR_PROFILE"))
    8716             :         {
    8717           8 :             m_bColorProfileMetadataChanged = true;
    8718             :         }
    8719        3158 :         else if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
    8720             :         {
    8721        3158 :             m_bMetadataChanged = true;
    8722             :             // Cancel any existing metadata from PAM file.
    8723        3158 :             if (GDALPamDataset::GetMetadataItem(pszName, pszDomain) != nullptr)
    8724           1 :                 GDALPamDataset::SetMetadataItem(pszName, nullptr, pszDomain);
    8725             :         }
    8726             : 
    8727        3166 :         if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    8728          67 :             pszName != nullptr && EQUAL(pszName, GDALMD_AREA_OR_POINT))
    8729             :         {
    8730           7 :             LookForProjection();
    8731           7 :             m_bGeoTIFFInfoChanged = true;
    8732             :         }
    8733             :     }
    8734             :     else
    8735             :     {
    8736           7 :         CPLDebug(
    8737             :             "GTIFF",
    8738             :             "GTiffDataset::SetMetadataItem() goes to PAM instead of TIFF tags");
    8739           7 :         eErr = GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    8740             :     }
    8741             : 
    8742        3173 :     if (eErr == CE_None)
    8743             :     {
    8744        3173 :         eErr = m_oGTiffMDMD.SetMetadataItem(pszName, pszValue, pszDomain);
    8745             :     }
    8746             : 
    8747        3173 :     return eErr;
    8748             : }
    8749             : 
    8750             : /************************************************************************/
    8751             : /*                         CreateMaskBand()                             */
    8752             : /************************************************************************/
    8753             : 
    8754          83 : CPLErr GTiffDataset::CreateMaskBand(int nFlagsIn)
    8755             : {
    8756          83 :     ScanDirectories();
    8757             : 
    8758          83 :     if (m_poMaskDS != nullptr)
    8759             :     {
    8760           1 :         ReportError(CE_Failure, CPLE_AppDefined,
    8761             :                     "This TIFF dataset has already an internal mask band");
    8762           1 :         return CE_Failure;
    8763             :     }
    8764          82 :     else if (MustCreateInternalMask())
    8765             :     {
    8766          69 :         if (nFlagsIn != GMF_PER_DATASET)
    8767             :         {
    8768           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    8769             :                         "The only flag value supported for internal mask is "
    8770             :                         "GMF_PER_DATASET");
    8771           1 :             return CE_Failure;
    8772             :         }
    8773             : 
    8774          68 :         int l_nCompression = COMPRESSION_PACKBITS;
    8775          68 :         if (strstr(GDALGetMetadataItem(GDALGetDriverByName("GTiff"),
    8776             :                                        GDAL_DMD_CREATIONOPTIONLIST, nullptr),
    8777          68 :                    "<Value>DEFLATE</Value>") != nullptr)
    8778          68 :             l_nCompression = COMPRESSION_ADOBE_DEFLATE;
    8779             : 
    8780             :         /* --------------------------------------------------------------------
    8781             :          */
    8782             :         /*      If we don't have read access, then create the mask externally.
    8783             :          */
    8784             :         /* --------------------------------------------------------------------
    8785             :          */
    8786          68 :         if (GetAccess() != GA_Update)
    8787             :         {
    8788           1 :             ReportError(CE_Warning, CPLE_AppDefined,
    8789             :                         "File open for read-only accessing, "
    8790             :                         "creating mask externally.");
    8791             : 
    8792           1 :             return GDALPamDataset::CreateMaskBand(nFlagsIn);
    8793             :         }
    8794             : 
    8795          67 :         if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    8796           0 :             !m_bWriteKnownIncompatibleEdition)
    8797             :         {
    8798           0 :             ReportError(CE_Warning, CPLE_AppDefined,
    8799             :                         "Adding a mask invalidates the "
    8800             :                         "LAYOUT=IFDS_BEFORE_DATA property");
    8801           0 :             m_bKnownIncompatibleEdition = true;
    8802           0 :             m_bWriteKnownIncompatibleEdition = true;
    8803             :         }
    8804             : 
    8805          67 :         bool bIsOverview = false;
    8806          67 :         uint32_t nSubType = 0;
    8807          67 :         if (TIFFGetField(m_hTIFF, TIFFTAG_SUBFILETYPE, &nSubType))
    8808             :         {
    8809           8 :             bIsOverview = (nSubType & FILETYPE_REDUCEDIMAGE) != 0;
    8810             : 
    8811           8 :             if ((nSubType & FILETYPE_MASK) != 0)
    8812             :             {
    8813           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
    8814             :                             "Cannot create a mask on a TIFF mask IFD !");
    8815           0 :                 return CE_Failure;
    8816             :             }
    8817             :         }
    8818             : 
    8819          67 :         const int bIsTiled = TIFFIsTiled(m_hTIFF);
    8820             : 
    8821          67 :         FlushDirectory();
    8822             : 
    8823          67 :         const toff_t nOffset = GTIFFWriteDirectory(
    8824             :             m_hTIFF,
    8825             :             bIsOverview ? FILETYPE_REDUCEDIMAGE | FILETYPE_MASK : FILETYPE_MASK,
    8826             :             nRasterXSize, nRasterYSize, 1, PLANARCONFIG_CONTIG, 1,
    8827             :             m_nBlockXSize, m_nBlockYSize, bIsTiled, l_nCompression,
    8828             :             PHOTOMETRIC_MASK, PREDICTOR_NONE, SAMPLEFORMAT_UINT, nullptr,
    8829             :             nullptr, nullptr, 0, nullptr, "", nullptr, nullptr, nullptr,
    8830          67 :             nullptr, m_bWriteCOGLayout);
    8831             : 
    8832          67 :         ReloadDirectory();
    8833             : 
    8834          67 :         if (nOffset == 0)
    8835           0 :             return CE_Failure;
    8836             : 
    8837          67 :         m_poMaskDS = new GTiffDataset();
    8838          67 :         m_poMaskDS->m_poBaseDS = this;
    8839          67 :         m_poMaskDS->m_poImageryDS = this;
    8840          67 :         m_poMaskDS->ShareLockWithParentDataset(this);
    8841          67 :         m_poMaskDS->m_bPromoteTo8Bits = CPLTestBool(
    8842             :             CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
    8843          67 :         if (m_poMaskDS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nOffset,
    8844          67 :                                    GA_Update) != CE_None)
    8845             :         {
    8846           0 :             delete m_poMaskDS;
    8847           0 :             m_poMaskDS = nullptr;
    8848           0 :             return CE_Failure;
    8849             :         }
    8850             : 
    8851          67 :         return CE_None;
    8852             :     }
    8853             : 
    8854          13 :     return GDALPamDataset::CreateMaskBand(nFlagsIn);
    8855             : }
    8856             : 
    8857             : /************************************************************************/
    8858             : /*                        MustCreateInternalMask()                      */
    8859             : /************************************************************************/
    8860             : 
    8861         110 : bool GTiffDataset::MustCreateInternalMask()
    8862             : {
    8863         110 :     return CPLTestBool(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", "YES"));
    8864             : }
    8865             : 
    8866             : /************************************************************************/
    8867             : /*                         CreateMaskBand()                             */
    8868             : /************************************************************************/
    8869             : 
    8870          27 : CPLErr GTiffRasterBand::CreateMaskBand(int nFlagsIn)
    8871             : {
    8872          27 :     m_poGDS->ScanDirectories();
    8873             : 
    8874          27 :     if (m_poGDS->m_poMaskDS != nullptr)
    8875             :     {
    8876           5 :         ReportError(CE_Failure, CPLE_AppDefined,
    8877             :                     "This TIFF dataset has already an internal mask band");
    8878           5 :         return CE_Failure;
    8879             :     }
    8880             : 
    8881             :     const char *pszGDAL_TIFF_INTERNAL_MASK =
    8882          22 :         CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", nullptr);
    8883          25 :     if ((pszGDAL_TIFF_INTERNAL_MASK &&
    8884          22 :          CPLTestBool(pszGDAL_TIFF_INTERNAL_MASK)) ||
    8885             :         nFlagsIn == GMF_PER_DATASET)
    8886             :     {
    8887          15 :         return m_poGDS->CreateMaskBand(nFlagsIn);
    8888             :     }
    8889             : 
    8890           7 :     return GDALPamRasterBand::CreateMaskBand(nFlagsIn);
    8891             : }
    8892             : 
    8893             : /************************************************************************/
    8894             : /*                          ClampCTEntry()                              */
    8895             : /************************************************************************/
    8896             : 
    8897      227439 : /* static */ unsigned short GTiffDataset::ClampCTEntry(int iColor, int iComp,
    8898             :                                                        int nCTEntryVal,
    8899             :                                                        int nMultFactor)
    8900             : {
    8901      227439 :     const int nVal = nCTEntryVal * nMultFactor;
    8902      227439 :     if (nVal < 0)
    8903             :     {
    8904           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    8905             :                  "Color table entry [%d][%d] = %d, clamped to 0", iColor, iComp,
    8906             :                  nCTEntryVal);
    8907           0 :         return 0;
    8908             :     }
    8909      227439 :     if (nVal > 65535)
    8910             :     {
    8911           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    8912             :                  "Color table entry [%d][%d] = %d, clamped to 65535", iColor,
    8913             :                  iComp, nCTEntryVal);
    8914           2 :         return 65535;
    8915             :     }
    8916      227437 :     return static_cast<unsigned short>(nVal);
    8917             : }

Generated by: LCOV version 1.14