LCOV - code coverage report
Current view: top level - frmts/gtiff - gtiffdataset_write.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3892 4249 91.6 %
Date: 2026-06-16 02:45:00 Functions: 110 139 79.1 %

          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             : #include "gtiffjpegoverviewds.h"
      18             : 
      19             : #include <cassert>
      20             : #include <cerrno>
      21             : 
      22             : #include <algorithm>
      23             : #include <cmath>
      24             : #include <limits>
      25             : #include <memory>
      26             : #include <mutex>
      27             : #include <set>
      28             : #include <string>
      29             : #include <tuple>
      30             : #include <utility>
      31             : 
      32             : #include "cpl_error.h"
      33             : #include "cpl_error_internal.h"  // CPLErrorHandlerAccumulatorStruct
      34             : #include "cpl_float.h"
      35             : #include "cpl_md5.h"
      36             : #include "cpl_vsi.h"
      37             : #include "cpl_vsi_virtual.h"
      38             : #include "cpl_worker_thread_pool.h"
      39             : #include "fetchbufferdirectio.h"
      40             : #include "gdal_mdreader.h"  // GDALWriteRPCTXTFile()
      41             : #include "gdal_priv.h"
      42             : #include "gdal_priv_templates.hpp"  // GDALIsValueInRange<>
      43             : #include "gdal_thread_pool.h"       // GDALGetGlobalThreadPool()
      44             : #include "geovalues.h"              // RasterPixelIsPoint
      45             : #include "gt_jpeg_copy.h"
      46             : #include "gt_overview.h"  // GTIFFBuildOverviewMetadata()
      47             : #include "quant_table_md5sum.h"
      48             : #include "quant_table_md5sum_jpeg9e.h"
      49             : #include "tif_jxl.h"
      50             : #include "tifvsi.h"
      51             : #include "xtiffio.h"
      52             : 
      53             : #if LIFFLIB_VERSION > 20230908 || defined(INTERNAL_LIBTIFF)
      54             : /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
      55             : #define LIBTIFF_MULTIBAND_LERC_NAN_OK
      56             : #endif
      57             : 
      58             : static const int knGTIFFJpegTablesModeDefault = JPEGTABLESMODE_QUANT;
      59             : 
      60             : static constexpr const char szPROFILE_BASELINE[] = "BASELINE";
      61             : static constexpr const char szPROFILE_GeoTIFF[] = "GeoTIFF";
      62             : static constexpr const char szPROFILE_GDALGeoTIFF[] = "GDALGeoTIFF";
      63             : 
      64             : // Due to libgeotiff/xtiff.c declaring TIFFTAG_GEOTIEPOINTS with field_readcount
      65             : // and field_writecount == -1 == TIFF_VARIABLE, we are limited to writing
      66             : // 65535 values in that tag. That could potentially be overcome by changing the tag
      67             : // declaration to using TIFF_VARIABLE2 where the count is a uint32_t.
      68             : constexpr int knMAX_GCP_COUNT =
      69             :     static_cast<int>(std::numeric_limits<uint16_t>::max() / 6);
      70             : 
      71             : enum
      72             : {
      73             :     ENDIANNESS_NATIVE,
      74             :     ENDIANNESS_LITTLE,
      75             :     ENDIANNESS_BIG
      76             : };
      77             : 
      78       17950 : static signed char GTiffGetWebPLevel(CSLConstList papszOptions)
      79             : {
      80       17950 :     int nWebPLevel = DEFAULT_WEBP_LEVEL;
      81       17950 :     const char *pszValue = CSLFetchNameValue(papszOptions, "WEBP_LEVEL");
      82       17950 :     if (pszValue != nullptr)
      83             :     {
      84          51 :         nWebPLevel = atoi(pszValue);
      85          51 :         if (!(nWebPLevel >= 1 && nWebPLevel <= 100))
      86             :         {
      87           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
      88             :                      "WEBP_LEVEL=%s value not recognised, ignoring.", pszValue);
      89           0 :             nWebPLevel = DEFAULT_WEBP_LEVEL;
      90             :         }
      91             :     }
      92       17950 :     return static_cast<signed char>(nWebPLevel);
      93             : }
      94             : 
      95       17956 : static bool GTiffGetWebPLossless(CSLConstList papszOptions)
      96             : {
      97       17956 :     return CPLFetchBool(papszOptions, "WEBP_LOSSLESS", false);
      98             : }
      99             : 
     100       18022 : static double GTiffGetLERCMaxZError(CSLConstList papszOptions)
     101             : {
     102       18022 :     return CPLAtof(CSLFetchNameValueDef(papszOptions, "MAX_Z_ERROR", "0.0"));
     103             : }
     104             : 
     105        8048 : static double GTiffGetLERCMaxZErrorOverview(CSLConstList papszOptions)
     106             : {
     107        8048 :     return CPLAtof(CSLFetchNameValueDef(
     108             :         papszOptions, "MAX_Z_ERROR_OVERVIEW",
     109        8048 :         CSLFetchNameValueDef(papszOptions, "MAX_Z_ERROR", "0.0")));
     110             : }
     111             : 
     112             : #if HAVE_JXL
     113       18026 : static bool GTiffGetJXLLossless(CSLConstList papszOptions,
     114             :                                 bool *pbIsSpecified = nullptr)
     115             : {
     116       18026 :     const char *pszVal = CSLFetchNameValue(papszOptions, "JXL_LOSSLESS");
     117       18026 :     if (pbIsSpecified)
     118        9974 :         *pbIsSpecified = pszVal != nullptr;
     119       18026 :     return pszVal == nullptr || CPLTestBool(pszVal);
     120             : }
     121             : 
     122       18026 : static uint32_t GTiffGetJXLEffort(CSLConstList papszOptions)
     123             : {
     124       18026 :     return atoi(CSLFetchNameValueDef(papszOptions, "JXL_EFFORT", "5"));
     125             : }
     126             : 
     127       17944 : static float GTiffGetJXLDistance(CSLConstList papszOptions,
     128             :                                  bool *pbIsSpecified = nullptr)
     129             : {
     130       17944 :     const char *pszVal = CSLFetchNameValue(papszOptions, "JXL_DISTANCE");
     131       17944 :     if (pbIsSpecified)
     132        9974 :         *pbIsSpecified = pszVal != nullptr;
     133       17944 :     return pszVal == nullptr ? 1.0f : static_cast<float>(CPLAtof(pszVal));
     134             : }
     135             : 
     136       18026 : static float GTiffGetJXLAlphaDistance(CSLConstList papszOptions,
     137             :                                       bool *pbIsSpecified = nullptr)
     138             : {
     139       18026 :     const char *pszVal = CSLFetchNameValue(papszOptions, "JXL_ALPHA_DISTANCE");
     140       18026 :     if (pbIsSpecified)
     141        9974 :         *pbIsSpecified = pszVal != nullptr;
     142       18026 :     return pszVal == nullptr ? -1.0f : static_cast<float>(CPLAtof(pszVal));
     143             : }
     144             : 
     145             : #endif
     146             : 
     147             : /************************************************************************/
     148             : /*                           FillEmptyTiles()                           */
     149             : /************************************************************************/
     150             : 
     151        8237 : CPLErr GTiffDataset::FillEmptyTiles()
     152             : 
     153             : {
     154             :     /* -------------------------------------------------------------------- */
     155             :     /*      How many blocks are there in this file?                         */
     156             :     /* -------------------------------------------------------------------- */
     157       16474 :     const int nBlockCount = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     158        8237 :                                 ? m_nBlocksPerBand * nBands
     159             :                                 : m_nBlocksPerBand;
     160             : 
     161             :     /* -------------------------------------------------------------------- */
     162             :     /*      Fetch block maps.                                               */
     163             :     /* -------------------------------------------------------------------- */
     164        8237 :     toff_t *panByteCounts = nullptr;
     165             : 
     166        8237 :     if (TIFFIsTiled(m_hTIFF))
     167        1157 :         TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts);
     168             :     else
     169        7080 :         TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
     170             : 
     171        8237 :     if (panByteCounts == nullptr)
     172             :     {
     173             :         // Got here with libtiff 3.9.3 and tiff_write_8 test.
     174           0 :         ReportError(CE_Failure, CPLE_AppDefined,
     175             :                     "FillEmptyTiles() failed because panByteCounts == NULL");
     176           0 :         return CE_Failure;
     177             :     }
     178             : 
     179             :     /* -------------------------------------------------------------------- */
     180             :     /*      Prepare a blank data buffer to write for uninitialized blocks.  */
     181             :     /* -------------------------------------------------------------------- */
     182             :     const GPtrDiff_t nBlockBytes =
     183        8237 :         TIFFIsTiled(m_hTIFF) ? static_cast<GPtrDiff_t>(TIFFTileSize(m_hTIFF))
     184        7080 :                              : static_cast<GPtrDiff_t>(TIFFStripSize(m_hTIFF));
     185             : 
     186        8237 :     GByte *pabyData = static_cast<GByte *>(VSI_CALLOC_VERBOSE(nBlockBytes, 1));
     187        8237 :     if (pabyData == nullptr)
     188             :     {
     189           0 :         return CE_Failure;
     190             :     }
     191             : 
     192             :     // Force tiles completely filled with the nodata value to be written.
     193        8237 :     m_bWriteEmptyTiles = true;
     194             : 
     195             :     /* -------------------------------------------------------------------- */
     196             :     /*      If set, fill data buffer with no data value.                    */
     197             :     /* -------------------------------------------------------------------- */
     198        8237 :     if ((m_bNoDataSet && m_dfNoDataValue != 0.0) ||
     199        7969 :         (m_bNoDataSetAsInt64 && m_nNoDataValueInt64 != 0) ||
     200        7964 :         (m_bNoDataSetAsUInt64 && m_nNoDataValueUInt64 != 0))
     201             :     {
     202         278 :         const GDALDataType eDataType = GetRasterBand(1)->GetRasterDataType();
     203         278 :         const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
     204         278 :         if (nDataTypeSize &&
     205         278 :             nDataTypeSize * 8 == static_cast<int>(m_nBitsPerSample))
     206             :         {
     207         267 :             if (m_bNoDataSetAsInt64)
     208             :             {
     209           6 :                 GDALCopyWords64(&m_nNoDataValueInt64, GDT_Int64, 0, pabyData,
     210             :                                 eDataType, nDataTypeSize,
     211           6 :                                 nBlockBytes / nDataTypeSize);
     212             :             }
     213         261 :             else if (m_bNoDataSetAsUInt64)
     214             :             {
     215           5 :                 GDALCopyWords64(&m_nNoDataValueUInt64, GDT_UInt64, 0, pabyData,
     216             :                                 eDataType, nDataTypeSize,
     217           5 :                                 nBlockBytes / nDataTypeSize);
     218             :             }
     219             :             else
     220             :             {
     221         256 :                 double dfNoData = m_dfNoDataValue;
     222         256 :                 GDALCopyWords64(&dfNoData, GDT_Float64, 0, pabyData, eDataType,
     223         256 :                                 nDataTypeSize, nBlockBytes / nDataTypeSize);
     224         267 :             }
     225             :         }
     226          11 :         else if (nDataTypeSize)
     227             :         {
     228             :             // Handle non power-of-two depths.
     229             :             // Ideally make a packed buffer, but that is a bit tedious,
     230             :             // so use the normal I/O interfaces.
     231             : 
     232          11 :             CPLFree(pabyData);
     233             : 
     234          11 :             pabyData = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
     235             :                 m_nBlockXSize, m_nBlockYSize, nDataTypeSize));
     236          11 :             if (pabyData == nullptr)
     237           0 :                 return CE_Failure;
     238          11 :             if (m_bNoDataSetAsInt64)
     239             :             {
     240           0 :                 GDALCopyWords64(&m_nNoDataValueInt64, GDT_Int64, 0, pabyData,
     241             :                                 eDataType, nDataTypeSize,
     242           0 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     243           0 :                                     m_nBlockYSize);
     244             :             }
     245          11 :             else if (m_bNoDataSetAsUInt64)
     246             :             {
     247           0 :                 GDALCopyWords64(&m_nNoDataValueUInt64, GDT_UInt64, 0, pabyData,
     248             :                                 eDataType, nDataTypeSize,
     249           0 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     250           0 :                                     m_nBlockYSize);
     251             :             }
     252             :             else
     253             :             {
     254          11 :                 GDALCopyWords64(&m_dfNoDataValue, GDT_Float64, 0, pabyData,
     255             :                                 eDataType, nDataTypeSize,
     256          11 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     257          11 :                                     m_nBlockYSize);
     258             :             }
     259          11 :             CPLErr eErr = CE_None;
     260          46 :             for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     261             :             {
     262          35 :                 if (panByteCounts[iBlock] == 0)
     263             :                 {
     264          18 :                     if (m_nPlanarConfig == PLANARCONFIG_SEPARATE || nBands == 1)
     265             :                     {
     266          24 :                         if (GetRasterBand(1 + iBlock / m_nBlocksPerBand)
     267          12 :                                 ->WriteBlock((iBlock % m_nBlocksPerBand) %
     268          12 :                                                  m_nBlocksPerRow,
     269          12 :                                              (iBlock % m_nBlocksPerBand) /
     270          12 :                                                  m_nBlocksPerRow,
     271          12 :                                              pabyData) != CE_None)
     272             :                         {
     273           0 :                             eErr = CE_Failure;
     274             :                         }
     275             :                     }
     276             :                     else
     277             :                     {
     278             :                         // In contig case, don't directly call WriteBlock(), as
     279             :                         // it could cause useless decompression-recompression.
     280           6 :                         const int nXOff =
     281           6 :                             (iBlock % m_nBlocksPerRow) * m_nBlockXSize;
     282           6 :                         const int nYOff =
     283           6 :                             (iBlock / m_nBlocksPerRow) * m_nBlockYSize;
     284           6 :                         const int nXSize =
     285           6 :                             (nXOff + m_nBlockXSize <= nRasterXSize)
     286           6 :                                 ? m_nBlockXSize
     287           2 :                                 : nRasterXSize - nXOff;
     288           6 :                         const int nYSize =
     289           6 :                             (nYOff + m_nBlockYSize <= nRasterYSize)
     290           6 :                                 ? m_nBlockYSize
     291           3 :                                 : nRasterYSize - nYOff;
     292          18 :                         for (int iBand = 1; iBand <= nBands; ++iBand)
     293             :                         {
     294          12 :                             if (GetRasterBand(iBand)->RasterIO(
     295             :                                     GF_Write, nXOff, nYOff, nXSize, nYSize,
     296             :                                     pabyData, nXSize, nYSize, eDataType, 0, 0,
     297          12 :                                     nullptr) != CE_None)
     298             :                             {
     299           0 :                                 eErr = CE_Failure;
     300             :                             }
     301             :                         }
     302             :                     }
     303             :                 }
     304             :             }
     305          11 :             CPLFree(pabyData);
     306          11 :             return eErr;
     307         267 :         }
     308             :     }
     309             : 
     310             :     /* -------------------------------------------------------------------- */
     311             :     /*      When we must fill with zeroes, try to create non-sparse file    */
     312             :     /*      w.r.t TIFF spec ... as a sparse file w.r.t filesystem, ie by    */
     313             :     /*      seeking to end of file instead of writing zero blocks.          */
     314             :     /* -------------------------------------------------------------------- */
     315        7959 :     else if (m_nCompression == COMPRESSION_NONE && (m_nBitsPerSample % 8) == 0)
     316             :     {
     317        6379 :         CPLErr eErr = CE_None;
     318             :         // Only use libtiff to write the first sparse block to ensure that it
     319             :         // will serialize offset and count arrays back to disk.
     320        6379 :         int nCountBlocksToZero = 0;
     321     2330240 :         for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     322             :         {
     323     2323870 :             if (panByteCounts[iBlock] == 0)
     324             :             {
     325     2228340 :                 if (nCountBlocksToZero == 0)
     326             :                 {
     327        1138 :                     const bool bWriteEmptyTilesBak = m_bWriteEmptyTiles;
     328        1138 :                     m_bWriteEmptyTiles = true;
     329        1138 :                     const bool bOK = WriteEncodedTileOrStrip(iBlock, pabyData,
     330        1138 :                                                              FALSE) == CE_None;
     331        1138 :                     m_bWriteEmptyTiles = bWriteEmptyTilesBak;
     332        1138 :                     if (!bOK)
     333             :                     {
     334           2 :                         eErr = CE_Failure;
     335           2 :                         break;
     336             :                     }
     337             :                 }
     338     2228340 :                 nCountBlocksToZero++;
     339             :             }
     340             :         }
     341        6379 :         CPLFree(pabyData);
     342             : 
     343        6379 :         --nCountBlocksToZero;
     344             : 
     345             :         // And then seek to end of file for other ones.
     346        6379 :         if (nCountBlocksToZero > 0)
     347             :         {
     348         348 :             toff_t *panByteOffsets = nullptr;
     349             : 
     350         348 :             if (TIFFIsTiled(m_hTIFF))
     351         101 :                 TIFFGetField(m_hTIFF, TIFFTAG_TILEOFFSETS, &panByteOffsets);
     352             :             else
     353         247 :                 TIFFGetField(m_hTIFF, TIFFTAG_STRIPOFFSETS, &panByteOffsets);
     354             : 
     355         348 :             if (panByteOffsets == nullptr)
     356             :             {
     357           0 :                 ReportError(
     358             :                     CE_Failure, CPLE_AppDefined,
     359             :                     "FillEmptyTiles() failed because panByteOffsets == NULL");
     360           0 :                 return CE_Failure;
     361             :             }
     362             : 
     363         348 :             VSILFILE *fpTIF = VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
     364         348 :             VSIFSeekL(fpTIF, 0, SEEK_END);
     365         348 :             const vsi_l_offset nOffset = VSIFTellL(fpTIF);
     366             : 
     367         348 :             vsi_l_offset iBlockToZero = 0;
     368     2236440 :             for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     369             :             {
     370     2236090 :                 if (panByteCounts[iBlock] == 0)
     371             :                 {
     372     2227200 :                     panByteOffsets[iBlock] = static_cast<toff_t>(
     373     2227200 :                         nOffset + iBlockToZero * nBlockBytes);
     374     2227200 :                     panByteCounts[iBlock] = nBlockBytes;
     375     2227200 :                     iBlockToZero++;
     376             :                 }
     377             :             }
     378         348 :             CPLAssert(iBlockToZero ==
     379             :                       static_cast<vsi_l_offset>(nCountBlocksToZero));
     380             : 
     381         348 :             if (VSIFTruncateL(fpTIF, nOffset + iBlockToZero * nBlockBytes) != 0)
     382             :             {
     383           0 :                 eErr = CE_Failure;
     384           0 :                 ReportError(CE_Failure, CPLE_FileIO,
     385             :                             "Cannot initialize empty blocks");
     386             :             }
     387             :         }
     388             : 
     389        6379 :         return eErr;
     390             :     }
     391             : 
     392             :     /* -------------------------------------------------------------------- */
     393             :     /*      Check all blocks, writing out data for uninitialized blocks.    */
     394             :     /* -------------------------------------------------------------------- */
     395             : 
     396        1847 :     GByte *pabyRaw = nullptr;
     397        1847 :     vsi_l_offset nRawSize = 0;
     398        1847 :     CPLErr eErr = CE_None;
     399       56559 :     for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     400             :     {
     401       54719 :         if (panByteCounts[iBlock] == 0)
     402             :         {
     403       17565 :             if (pabyRaw == nullptr)
     404             :             {
     405       10165 :                 if (WriteEncodedTileOrStrip(iBlock, pabyData, FALSE) != CE_None)
     406             :                 {
     407           7 :                     eErr = CE_Failure;
     408           7 :                     break;
     409             :                 }
     410             : 
     411       10158 :                 vsi_l_offset nOffset = 0;
     412       10158 :                 if (!IsBlockAvailable(iBlock, &nOffset, &nRawSize, nullptr))
     413           0 :                     break;
     414             : 
     415             :                 // When using compression, get back the compressed block
     416             :                 // so we can use the raw API to write it faster.
     417       10158 :                 if (m_nCompression != COMPRESSION_NONE)
     418             :                 {
     419             :                     pabyRaw = static_cast<GByte *>(
     420         484 :                         VSI_MALLOC_VERBOSE(static_cast<size_t>(nRawSize)));
     421         484 :                     if (pabyRaw)
     422             :                     {
     423             :                         VSILFILE *fp =
     424         484 :                             VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
     425         484 :                         const vsi_l_offset nCurOffset = VSIFTellL(fp);
     426         484 :                         VSIFSeekL(fp, nOffset, SEEK_SET);
     427         484 :                         VSIFReadL(pabyRaw, 1, static_cast<size_t>(nRawSize),
     428             :                                   fp);
     429         484 :                         VSIFSeekL(fp, nCurOffset, SEEK_SET);
     430             :                     }
     431             :                 }
     432             :             }
     433             :             else
     434             :             {
     435        7400 :                 WriteRawStripOrTile(iBlock, pabyRaw,
     436             :                                     static_cast<GPtrDiff_t>(nRawSize));
     437             :             }
     438             :         }
     439             :     }
     440             : 
     441        1847 :     CPLFree(pabyData);
     442        1847 :     VSIFree(pabyRaw);
     443        1847 :     return eErr;
     444             : }
     445             : 
     446             : /************************************************************************/
     447             : /*                           HasOnlyNoData()                            */
     448             : /************************************************************************/
     449             : 
     450       42915 : bool GTiffDataset::HasOnlyNoData(const void *pBuffer, int nWidth, int nHeight,
     451             :                                  int nLineStride, int nComponents)
     452             : {
     453       42915 :     if (m_nSampleFormat == SAMPLEFORMAT_COMPLEXINT ||
     454       42915 :         m_nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP)
     455           0 :         return false;
     456       42915 :     if (m_bNoDataSetAsInt64 || m_bNoDataSetAsUInt64)
     457           6 :         return false;  // FIXME: over pessimistic
     458       85818 :     return GDALBufferHasOnlyNoData(
     459       42909 :         pBuffer, m_bNoDataSet ? m_dfNoDataValue : 0.0, nWidth, nHeight,
     460       42909 :         nLineStride, nComponents, m_nBitsPerSample,
     461       42909 :         m_nSampleFormat == SAMPLEFORMAT_UINT  ? GSF_UNSIGNED_INT
     462        4926 :         : m_nSampleFormat == SAMPLEFORMAT_INT ? GSF_SIGNED_INT
     463       42909 :                                               : GSF_FLOATING_POINT);
     464             : }
     465             : 
     466             : /************************************************************************/
     467             : /*                     IsFirstPixelEqualToNoData()                      */
     468             : /************************************************************************/
     469             : 
     470      169372 : inline bool GTiffDataset::IsFirstPixelEqualToNoData(const void *pBuffer)
     471             : {
     472      169372 :     const GDALDataType eDT = GetRasterBand(1)->GetRasterDataType();
     473      169372 :     const double dfEffectiveNoData = (m_bNoDataSet) ? m_dfNoDataValue : 0.0;
     474      169372 :     if (m_bNoDataSetAsInt64 || m_bNoDataSetAsUInt64)
     475          10 :         return true;  // FIXME: over pessimistic
     476      169362 :     if (m_nBitsPerSample == 8 ||
     477       59022 :         (m_nBitsPerSample < 8 && dfEffectiveNoData == 0))
     478             :     {
     479      113786 :         if (eDT == GDT_Int8)
     480             :         {
     481         278 :             return GDALIsValueInRange<signed char>(dfEffectiveNoData) &&
     482         139 :                    *(static_cast<const signed char *>(pBuffer)) ==
     483         278 :                        static_cast<signed char>(dfEffectiveNoData);
     484             :         }
     485      227263 :         return GDALIsValueInRange<GByte>(dfEffectiveNoData) &&
     486      113616 :                *(static_cast<const GByte *>(pBuffer)) ==
     487      227263 :                    static_cast<GByte>(dfEffectiveNoData);
     488             :     }
     489       55576 :     if (m_nBitsPerSample == 16 && eDT == GDT_UInt16)
     490             :     {
     491        4686 :         return GDALIsValueInRange<GUInt16>(dfEffectiveNoData) &&
     492        2343 :                *(static_cast<const GUInt16 *>(pBuffer)) ==
     493        4686 :                    static_cast<GUInt16>(dfEffectiveNoData);
     494             :     }
     495       53233 :     if (m_nBitsPerSample == 16 && eDT == GDT_Int16)
     496             :     {
     497        8476 :         return GDALIsValueInRange<GInt16>(dfEffectiveNoData) &&
     498        4238 :                *(static_cast<const GInt16 *>(pBuffer)) ==
     499        8476 :                    static_cast<GInt16>(dfEffectiveNoData);
     500             :     }
     501       48995 :     if (m_nBitsPerSample == 32 && eDT == GDT_UInt32)
     502             :     {
     503         378 :         return GDALIsValueInRange<GUInt32>(dfEffectiveNoData) &&
     504         189 :                *(static_cast<const GUInt32 *>(pBuffer)) ==
     505         378 :                    static_cast<GUInt32>(dfEffectiveNoData);
     506             :     }
     507       48806 :     if (m_nBitsPerSample == 32 && eDT == GDT_Int32)
     508             :     {
     509         506 :         return GDALIsValueInRange<GInt32>(dfEffectiveNoData) &&
     510         253 :                *(static_cast<const GInt32 *>(pBuffer)) ==
     511         506 :                    static_cast<GInt32>(dfEffectiveNoData);
     512             :     }
     513       48553 :     if (m_nBitsPerSample == 64 && eDT == GDT_UInt64)
     514             :     {
     515         234 :         return GDALIsValueInRange<std::uint64_t>(dfEffectiveNoData) &&
     516         117 :                *(static_cast<const std::uint64_t *>(pBuffer)) ==
     517         234 :                    static_cast<std::uint64_t>(dfEffectiveNoData);
     518             :     }
     519       48436 :     if (m_nBitsPerSample == 64 && eDT == GDT_Int64)
     520             :     {
     521         236 :         return GDALIsValueInRange<std::int64_t>(dfEffectiveNoData) &&
     522         118 :                *(static_cast<const std::int64_t *>(pBuffer)) ==
     523         236 :                    static_cast<std::int64_t>(dfEffectiveNoData);
     524             :     }
     525       48318 :     if (m_nBitsPerSample == 32 && eDT == GDT_Float32)
     526             :     {
     527       41191 :         if (std::isnan(m_dfNoDataValue))
     528           3 :             return std::isnan(*(static_cast<const float *>(pBuffer)));
     529       82376 :         return GDALIsValueInRange<float>(dfEffectiveNoData) &&
     530       41188 :                *(static_cast<const float *>(pBuffer)) ==
     531       82376 :                    static_cast<float>(dfEffectiveNoData);
     532             :     }
     533        7127 :     if (m_nBitsPerSample == 64 && eDT == GDT_Float64)
     534             :     {
     535        4454 :         if (std::isnan(dfEffectiveNoData))
     536           3 :             return std::isnan(*(static_cast<const double *>(pBuffer)));
     537        4451 :         return *(static_cast<const double *>(pBuffer)) == dfEffectiveNoData;
     538             :     }
     539        2673 :     return false;
     540             : }
     541             : 
     542             : /************************************************************************/
     543             : /*                      WriteDealWithLercAndNan()                       */
     544             : /************************************************************************/
     545             : 
     546             : template <typename T>
     547           0 : void GTiffDataset::WriteDealWithLercAndNan(T *pBuffer, int nActualBlockWidth,
     548             :                                            int nActualBlockHeight,
     549             :                                            int nStrileHeight)
     550             : {
     551             :     // This method does 2 things:
     552             :     // - warn the user if he tries to write NaN values with libtiff < 4.6.1
     553             :     //   and multi-band PlanarConfig=Contig configuration
     554             :     // - and in right-most and bottom-most tiles, replace non accessible
     555             :     //   pixel values by a safe one.
     556             : 
     557           0 :     const auto fPaddingValue =
     558             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     559             :         m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1
     560             :             ? 0
     561             :             :
     562             : #endif
     563             :             std::numeric_limits<T>::quiet_NaN();
     564             : 
     565           0 :     const int nBandsPerStrile =
     566           0 :         m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     567           0 :     for (int j = 0; j < nActualBlockHeight; ++j)
     568             :     {
     569             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     570             :         static bool bHasWarned = false;
     571             :         if (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1 && !bHasWarned)
     572             :         {
     573             :             for (int i = 0; i < nActualBlockWidth * nBandsPerStrile; ++i)
     574             :             {
     575             :                 if (std::isnan(
     576             :                         pBuffer[j * m_nBlockXSize * nBandsPerStrile + i]))
     577             :                 {
     578             :                     bHasWarned = true;
     579             :                     CPLError(CE_Warning, CPLE_AppDefined,
     580             :                              "libtiff < 4.6.1 does not handle properly NaN "
     581             :                              "values for multi-band PlanarConfig=Contig "
     582             :                              "configuration. As a workaround, you can set the "
     583             :                              "INTERLEAVE=BAND creation option.");
     584             :                     break;
     585             :                 }
     586             :             }
     587             :         }
     588             : #endif
     589           0 :         for (int i = nActualBlockWidth * nBandsPerStrile;
     590           0 :              i < m_nBlockXSize * nBandsPerStrile; ++i)
     591             :         {
     592           0 :             pBuffer[j * m_nBlockXSize * nBandsPerStrile + i] = fPaddingValue;
     593             :         }
     594             :     }
     595           0 :     for (int j = nActualBlockHeight; j < nStrileHeight; ++j)
     596             :     {
     597           0 :         for (int i = 0; i < m_nBlockXSize * nBandsPerStrile; ++i)
     598             :         {
     599           0 :             pBuffer[j * m_nBlockXSize * nBandsPerStrile + i] = fPaddingValue;
     600             :         }
     601             :     }
     602           0 : }
     603             : 
     604             : /************************************************************************/
     605             : /*                          WriteEncodedTile()                          */
     606             : /************************************************************************/
     607             : 
     608       50548 : bool GTiffDataset::WriteEncodedTile(uint32_t tile, GByte *pabyData,
     609             :                                     int bPreserveDataBuffer)
     610             : {
     611       50548 :     const int iColumn = (tile % m_nBlocksPerBand) % m_nBlocksPerRow;
     612       50548 :     const int iRow = (tile % m_nBlocksPerBand) / m_nBlocksPerRow;
     613             : 
     614      101096 :     const int nActualBlockWidth = (iColumn == m_nBlocksPerRow - 1)
     615       50548 :                                       ? nRasterXSize - iColumn * m_nBlockXSize
     616             :                                       : m_nBlockXSize;
     617      101096 :     const int nActualBlockHeight = (iRow == m_nBlocksPerColumn - 1)
     618       50548 :                                        ? nRasterYSize - iRow * m_nBlockYSize
     619             :                                        : m_nBlockYSize;
     620             : 
     621             :     /* -------------------------------------------------------------------- */
     622             :     /*      Don't write empty blocks in some cases.                         */
     623             :     /* -------------------------------------------------------------------- */
     624       50548 :     if (!m_bWriteEmptyTiles && IsFirstPixelEqualToNoData(pabyData))
     625             :     {
     626        1975 :         if (!IsBlockAvailable(tile, nullptr, nullptr, nullptr))
     627             :         {
     628        1975 :             const int nComponents =
     629        1975 :                 m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     630             : 
     631        1975 :             if (HasOnlyNoData(pabyData, nActualBlockWidth, nActualBlockHeight,
     632             :                               m_nBlockXSize, nComponents))
     633             :             {
     634        1200 :                 return true;
     635             :             }
     636             :         }
     637             :     }
     638             : 
     639             :     // Is this a partial right edge or bottom edge tile?
     640       95650 :     const bool bPartialTile = (nActualBlockWidth < m_nBlockXSize) ||
     641       46302 :                               (nActualBlockHeight < m_nBlockYSize);
     642             : 
     643             :     const bool bIsLercFloatingPoint =
     644       49414 :         m_nCompression == COMPRESSION_LERC &&
     645          66 :         (GetRasterBand(1)->GetRasterDataType() == GDT_Float32 ||
     646          64 :          GetRasterBand(1)->GetRasterDataType() == GDT_Float64);
     647             : 
     648             :     // Do we need to spread edge values right or down for a partial
     649             :     // JPEG encoded tile?  We do this to avoid edge artifacts.
     650             :     // We also need to be careful with LERC and NaN values
     651       49348 :     const bool bNeedTempBuffer =
     652       54091 :         bPartialTile &&
     653        4743 :         (m_nCompression == COMPRESSION_JPEG || bIsLercFloatingPoint);
     654             : 
     655             :     // If we need to fill out the tile, or if we want to prevent
     656             :     // TIFFWriteEncodedTile from altering the buffer as part of
     657             :     // byte swapping the data on write then we will need a temporary
     658             :     // working buffer.  If not, we can just do a direct write.
     659       49348 :     const GPtrDiff_t cc = static_cast<GPtrDiff_t>(TIFFTileSize(m_hTIFF));
     660             : 
     661       63680 :     if (bPreserveDataBuffer &&
     662       14332 :         (TIFFIsByteSwapped(m_hTIFF) || bNeedTempBuffer || m_panMaskOffsetLsb))
     663             :     {
     664         158 :         if (m_pabyTempWriteBuffer == nullptr)
     665             :         {
     666          35 :             m_pabyTempWriteBuffer = CPLMalloc(cc);
     667             :         }
     668         158 :         memcpy(m_pabyTempWriteBuffer, pabyData, cc);
     669             : 
     670         158 :         pabyData = static_cast<GByte *>(m_pabyTempWriteBuffer);
     671             :     }
     672             : 
     673             :     // Perform tile fill if needed.
     674             :     // TODO: we should also handle the case of nBitsPerSample == 12
     675             :     // but this is more involved.
     676       49348 :     if (bPartialTile && m_nCompression == COMPRESSION_JPEG &&
     677         134 :         m_nBitsPerSample == 8)
     678             :     {
     679         132 :         const int nComponents =
     680         132 :             m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     681             : 
     682         132 :         CPLDebug("GTiff", "Filling out jpeg edge tile on write.");
     683             : 
     684         132 :         const int nRightPixelsToFill =
     685         132 :             iColumn == m_nBlocksPerRow - 1
     686         132 :                 ? m_nBlockXSize * (iColumn + 1) - nRasterXSize
     687             :                 : 0;
     688         132 :         const int nBottomPixelsToFill =
     689         132 :             iRow == m_nBlocksPerColumn - 1
     690         132 :                 ? m_nBlockYSize * (iRow + 1) - nRasterYSize
     691             :                 : 0;
     692             : 
     693             :         // Fill out to the right.
     694         132 :         const int iSrcX = m_nBlockXSize - nRightPixelsToFill - 1;
     695             : 
     696       12461 :         for (int iX = iSrcX + 1; iX < m_nBlockXSize; ++iX)
     697             :         {
     698     3955880 :             for (int iY = 0; iY < m_nBlockYSize; ++iY)
     699             :             {
     700     3943550 :                 memcpy(pabyData +
     701     3943550 :                            (static_cast<GPtrDiff_t>(m_nBlockXSize) * iY + iX) *
     702     3943550 :                                nComponents,
     703     3943550 :                        pabyData + (static_cast<GPtrDiff_t>(m_nBlockXSize) * iY +
     704     3943550 :                                    iSrcX) *
     705     3943550 :                                       nComponents,
     706             :                        nComponents);
     707             :             }
     708             :         }
     709             : 
     710             :         // Now fill out the bottom.
     711         132 :         const int iSrcY = m_nBlockYSize - nBottomPixelsToFill - 1;
     712       17682 :         for (int iY = iSrcY + 1; iY < m_nBlockYSize; ++iY)
     713             :         {
     714       17550 :             memcpy(pabyData + static_cast<GPtrDiff_t>(m_nBlockXSize) *
     715       17550 :                                   nComponents * iY,
     716       17550 :                    pabyData + static_cast<GPtrDiff_t>(m_nBlockXSize) *
     717       17550 :                                   nComponents * iSrcY,
     718       17550 :                    static_cast<GPtrDiff_t>(m_nBlockXSize) * nComponents);
     719             :         }
     720             :     }
     721             : 
     722       49348 :     if (bIsLercFloatingPoint &&
     723             :         (bPartialTile
     724             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     725             :          /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
     726             :          || (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1)
     727             : #endif
     728             :              ))
     729             :     {
     730           0 :         if (GetRasterBand(1)->GetRasterDataType() == GDT_Float32)
     731           0 :             WriteDealWithLercAndNan(reinterpret_cast<float *>(pabyData),
     732             :                                     nActualBlockWidth, nActualBlockHeight,
     733             :                                     m_nBlockYSize);
     734             :         else
     735           0 :             WriteDealWithLercAndNan(reinterpret_cast<double *>(pabyData),
     736             :                                     nActualBlockWidth, nActualBlockHeight,
     737             :                                     m_nBlockYSize);
     738             :     }
     739             : 
     740       49348 :     if (m_panMaskOffsetLsb)
     741             :     {
     742           0 :         const int iBand = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     743           0 :                               ? static_cast<int>(tile) / m_nBlocksPerBand
     744             :                               : -1;
     745           0 :         DiscardLsb(pabyData, cc, iBand);
     746             :     }
     747             : 
     748       49348 :     if (m_bStreamingOut)
     749             :     {
     750          17 :         if (tile != static_cast<uint32_t>(m_nLastWrittenBlockId + 1))
     751             :         {
     752           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     753             :                         "Attempt to write block %d whereas %d was expected",
     754           1 :                         tile, m_nLastWrittenBlockId + 1);
     755           1 :             return false;
     756             :         }
     757          16 :         if (static_cast<GPtrDiff_t>(VSIFWriteL(pabyData, 1, cc, m_fpToWrite)) !=
     758             :             cc)
     759             :         {
     760           0 :             ReportError(CE_Failure, CPLE_FileIO,
     761             :                         "Could not write " CPL_FRMT_GUIB " bytes",
     762             :                         static_cast<GUIntBig>(cc));
     763           0 :             return false;
     764             :         }
     765          16 :         m_nLastWrittenBlockId = tile;
     766          16 :         return true;
     767             :     }
     768             : 
     769             :     /* -------------------------------------------------------------------- */
     770             :     /*      Should we do compression in a worker thread ?                   */
     771             :     /* -------------------------------------------------------------------- */
     772       49331 :     if (SubmitCompressionJob(tile, pabyData, cc, m_nBlockYSize))
     773       19945 :         return true;
     774             : 
     775       29386 :     return TIFFWriteEncodedTile(m_hTIFF, tile, pabyData, cc) == cc;
     776             : }
     777             : 
     778             : /************************************************************************/
     779             : /*                         WriteEncodedStrip()                          */
     780             : /************************************************************************/
     781             : 
     782      178387 : bool GTiffDataset::WriteEncodedStrip(uint32_t strip, GByte *pabyData,
     783             :                                      int bPreserveDataBuffer)
     784             : {
     785      178387 :     GPtrDiff_t cc = static_cast<GPtrDiff_t>(TIFFStripSize(m_hTIFF));
     786      178387 :     const auto ccFull = cc;
     787             : 
     788             :     /* -------------------------------------------------------------------- */
     789             :     /*      If this is the last strip in the image, and is partial, then    */
     790             :     /*      we need to trim the number of scanlines written to the          */
     791             :     /*      amount of valid data we have. (#2748)                           */
     792             :     /* -------------------------------------------------------------------- */
     793      178387 :     const int nStripWithinBand = strip % m_nBlocksPerBand;
     794      178387 :     int nStripHeight = m_nRowsPerStrip;
     795             : 
     796      178387 :     if (nStripWithinBand * nStripHeight > GetRasterYSize() - nStripHeight)
     797             :     {
     798         384 :         nStripHeight = GetRasterYSize() - nStripWithinBand * m_nRowsPerStrip;
     799         384 :         cc = (cc / m_nRowsPerStrip) * nStripHeight;
     800         768 :         CPLDebug("GTiff",
     801             :                  "Adjusted bytes to write from " CPL_FRMT_GUIB
     802             :                  " to " CPL_FRMT_GUIB ".",
     803         384 :                  static_cast<GUIntBig>(TIFFStripSize(m_hTIFF)),
     804             :                  static_cast<GUIntBig>(cc));
     805             :     }
     806             : 
     807             :     /* -------------------------------------------------------------------- */
     808             :     /*      Don't write empty blocks in some cases.                         */
     809             :     /* -------------------------------------------------------------------- */
     810      178387 :     if (!m_bWriteEmptyTiles && IsFirstPixelEqualToNoData(pabyData))
     811             :     {
     812       41114 :         if (!IsBlockAvailable(strip, nullptr, nullptr, nullptr))
     813             :         {
     814       40940 :             const int nComponents =
     815       40940 :                 m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     816             : 
     817       40940 :             if (HasOnlyNoData(pabyData, m_nBlockXSize, nStripHeight,
     818             :                               m_nBlockXSize, nComponents))
     819             :             {
     820       28307 :                 return true;
     821             :             }
     822             :         }
     823             :     }
     824             : 
     825             :     /* -------------------------------------------------------------------- */
     826             :     /*      TIFFWriteEncodedStrip can alter the passed buffer if            */
     827             :     /*      byte-swapping is necessary so we use a temporary buffer         */
     828             :     /*      before calling it.                                              */
     829             :     /* -------------------------------------------------------------------- */
     830      239669 :     if (bPreserveDataBuffer &&
     831       89589 :         (TIFFIsByteSwapped(m_hTIFF) || m_panMaskOffsetLsb))
     832             :     {
     833         294 :         if (m_pabyTempWriteBuffer == nullptr)
     834             :         {
     835         126 :             m_pabyTempWriteBuffer = CPLMalloc(ccFull);
     836             :         }
     837         294 :         memcpy(m_pabyTempWriteBuffer, pabyData, cc);
     838         294 :         pabyData = static_cast<GByte *>(m_pabyTempWriteBuffer);
     839             :     }
     840             : 
     841             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     842             :     const bool bIsLercFloatingPoint =
     843             :         m_nCompression == COMPRESSION_LERC &&
     844             :         (GetRasterBand(1)->GetRasterDataType() == GDT_Float32 ||
     845             :          GetRasterBand(1)->GetRasterDataType() == GDT_Float64);
     846             :     if (bIsLercFloatingPoint &&
     847             :         /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
     848             :         m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1)
     849             :     {
     850             :         if (GetRasterBand(1)->GetRasterDataType() == GDT_Float32)
     851             :             WriteDealWithLercAndNan(reinterpret_cast<float *>(pabyData),
     852             :                                     m_nBlockXSize, nStripHeight, nStripHeight);
     853             :         else
     854             :             WriteDealWithLercAndNan(reinterpret_cast<double *>(pabyData),
     855             :                                     m_nBlockXSize, nStripHeight, nStripHeight);
     856             :     }
     857             : #endif
     858             : 
     859      150080 :     if (m_panMaskOffsetLsb)
     860             :     {
     861         366 :         int iBand = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     862         183 :                         ? static_cast<int>(strip) / m_nBlocksPerBand
     863             :                         : -1;
     864         183 :         DiscardLsb(pabyData, cc, iBand);
     865             :     }
     866             : 
     867      150080 :     if (m_bStreamingOut)
     868             :     {
     869        1408 :         if (strip != static_cast<uint32_t>(m_nLastWrittenBlockId + 1))
     870             :         {
     871           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     872             :                         "Attempt to write block %d whereas %d was expected",
     873           1 :                         strip, m_nLastWrittenBlockId + 1);
     874           1 :             return false;
     875             :         }
     876        1407 :         if (static_cast<GPtrDiff_t>(VSIFWriteL(pabyData, 1, cc, m_fpToWrite)) !=
     877             :             cc)
     878             :         {
     879           0 :             ReportError(CE_Failure, CPLE_FileIO,
     880             :                         "Could not write " CPL_FRMT_GUIB " bytes",
     881             :                         static_cast<GUIntBig>(cc));
     882           0 :             return false;
     883             :         }
     884        1407 :         m_nLastWrittenBlockId = strip;
     885        1407 :         return true;
     886             :     }
     887             : 
     888             :     /* -------------------------------------------------------------------- */
     889             :     /*      Should we do compression in a worker thread ?                   */
     890             :     /* -------------------------------------------------------------------- */
     891      148672 :     if (SubmitCompressionJob(strip, pabyData, cc, nStripHeight))
     892        6725 :         return true;
     893             : 
     894      141947 :     return TIFFWriteEncodedStrip(m_hTIFF, strip, pabyData, cc) == cc;
     895             : }
     896             : 
     897             : /************************************************************************/
     898             : /*                       InitCompressionThreads()                       */
     899             : /************************************************************************/
     900             : 
     901       32238 : void GTiffDataset::InitCompressionThreads(bool bUpdateMode,
     902             :                                           CSLConstList papszOptions)
     903             : {
     904             :     // Raster == tile, then no need for threads
     905       32238 :     if (m_nBlockXSize == nRasterXSize && m_nBlockYSize == nRasterYSize)
     906       23544 :         return;
     907             : 
     908        8694 :     const char *pszNumThreads = "";
     909        8694 :     bool bOK = false;
     910        8694 :     const int nThreads = GDALGetNumThreads(
     911             :         papszOptions, "NUM_THREADS", GDAL_DEFAULT_MAX_THREAD_COUNT,
     912             :         /* bDefaultToAllCPUs=*/false, &pszNumThreads, &bOK);
     913        8694 :     if (nThreads > 1)
     914             :     {
     915         106 :         if ((bUpdateMode && m_nCompression != COMPRESSION_NONE) ||
     916          24 :             (nBands >= 1 && IsMultiThreadedReadCompatible()))
     917             :         {
     918          77 :             CPLDebug("GTiff",
     919             :                      "Using up to %d threads for compression/decompression",
     920             :                      nThreads);
     921             : 
     922          77 :             m_poThreadPool = GDALGetGlobalThreadPool(nThreads);
     923          77 :             if (bUpdateMode && m_poThreadPool)
     924          58 :                 m_poCompressQueue = m_poThreadPool->CreateJobQueue();
     925             : 
     926          77 :             if (m_poCompressQueue != nullptr)
     927             :             {
     928             :                 // Add a margin of an extra job w.r.t thread number
     929             :                 // so as to optimize compression time (enables the main
     930             :                 // thread to do boring I/O while all CPUs are working).
     931          58 :                 m_asCompressionJobs.resize(nThreads + 1);
     932          58 :                 memset(&m_asCompressionJobs[0], 0,
     933          58 :                        m_asCompressionJobs.size() *
     934             :                            sizeof(GTiffCompressionJob));
     935          58 :                 for (int i = 0;
     936         280 :                      i < static_cast<int>(m_asCompressionJobs.size()); ++i)
     937             :                 {
     938         444 :                     m_asCompressionJobs[i].pszTmpFilename =
     939         222 :                         CPLStrdup(VSIMemGenerateHiddenFilename(
     940             :                             CPLSPrintf("thread_job_%d.tif", i)));
     941         222 :                     m_asCompressionJobs[i].nStripOrTile = -1;
     942             :                 }
     943             : 
     944             :                 // This is kind of a hack, but basically using
     945             :                 // TIFFWriteRawStrip/Tile and then TIFFReadEncodedStrip/Tile
     946             :                 // does not work on a newly created file, because
     947             :                 // TIFF_MYBUFFER is not set in tif_flags
     948             :                 // (if using TIFFWriteEncodedStrip/Tile first,
     949             :                 // TIFFWriteBufferSetup() is automatically called).
     950             :                 // This should likely rather fixed in libtiff itself.
     951          58 :                 CPL_IGNORE_RET_VAL(TIFFWriteBufferSetup(m_hTIFF, nullptr, -1));
     952             :             }
     953             :         }
     954             :     }
     955        8612 :     else if (!bOK)
     956             :     {
     957           3 :         ReportError(CE_Warning, CPLE_AppDefined,
     958             :                     "Invalid value for NUM_THREADS: %s", pszNumThreads);
     959             :     }
     960             : }
     961             : 
     962             : /************************************************************************/
     963             : /*                       ThreadCompressionFunc()                        */
     964             : /************************************************************************/
     965             : 
     966       26682 : void GTiffDataset::ThreadCompressionFunc(void *pData)
     967             : {
     968       26682 :     GTiffCompressionJob *psJob = static_cast<GTiffCompressionJob *>(pData);
     969       26682 :     GTiffDataset *poDS = psJob->poDS;
     970             : 
     971       26682 :     VSILFILE *fpTmp = VSIFOpenL(psJob->pszTmpFilename, "wb+");
     972       26682 :     TIFF *hTIFFTmp = VSI_TIFFOpen(
     973       53364 :         psJob->pszTmpFilename, psJob->bTIFFIsBigEndian ? "wb+" : "wl+", fpTmp);
     974       26682 :     CPLAssert(hTIFFTmp != nullptr);
     975       26682 :     TIFFSetField(hTIFFTmp, TIFFTAG_IMAGEWIDTH, poDS->m_nBlockXSize);
     976       26682 :     TIFFSetField(hTIFFTmp, TIFFTAG_IMAGELENGTH, psJob->nHeight);
     977       26682 :     TIFFSetField(hTIFFTmp, TIFFTAG_BITSPERSAMPLE, poDS->m_nBitsPerSample);
     978       26682 :     TIFFSetField(hTIFFTmp, TIFFTAG_COMPRESSION, poDS->m_nCompression);
     979       26682 :     TIFFSetField(hTIFFTmp, TIFFTAG_PHOTOMETRIC, poDS->m_nPhotometric);
     980       26682 :     TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLEFORMAT, poDS->m_nSampleFormat);
     981       26682 :     TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLESPERPIXEL, poDS->m_nSamplesPerPixel);
     982       26682 :     TIFFSetField(hTIFFTmp, TIFFTAG_ROWSPERSTRIP, poDS->m_nBlockYSize);
     983       26682 :     TIFFSetField(hTIFFTmp, TIFFTAG_PLANARCONFIG, poDS->m_nPlanarConfig);
     984       26682 :     if (psJob->nPredictor != PREDICTOR_NONE)
     985         263 :         TIFFSetField(hTIFFTmp, TIFFTAG_PREDICTOR, psJob->nPredictor);
     986       26682 :     if (poDS->m_nCompression == COMPRESSION_LERC)
     987             :     {
     988          24 :         TIFFSetField(hTIFFTmp, TIFFTAG_LERC_PARAMETERS, 2,
     989          24 :                      poDS->m_anLercAddCompressionAndVersion);
     990             :     }
     991       26682 :     if (psJob->nExtraSampleCount)
     992             :     {
     993         352 :         TIFFSetField(hTIFFTmp, TIFFTAG_EXTRASAMPLES, psJob->nExtraSampleCount,
     994             :                      psJob->pExtraSamples);
     995             :     }
     996             : 
     997       26682 :     poDS->RestoreVolatileParameters(hTIFFTmp);
     998             : 
     999       53364 :     bool bOK = TIFFWriteEncodedStrip(hTIFFTmp, 0, psJob->pabyBuffer,
    1000       26682 :                                      psJob->nBufferSize) == psJob->nBufferSize;
    1001             : 
    1002       26682 :     toff_t nOffset = 0;
    1003       26682 :     if (bOK)
    1004             :     {
    1005       26682 :         toff_t *panOffsets = nullptr;
    1006       26682 :         toff_t *panByteCounts = nullptr;
    1007       26682 :         TIFFGetField(hTIFFTmp, TIFFTAG_STRIPOFFSETS, &panOffsets);
    1008       26682 :         TIFFGetField(hTIFFTmp, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
    1009             : 
    1010       26682 :         nOffset = panOffsets[0];
    1011       26682 :         psJob->nCompressedBufferSize =
    1012       26682 :             static_cast<GPtrDiff_t>(panByteCounts[0]);
    1013             :     }
    1014             :     else
    1015             :     {
    1016           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1017             :                  "Error when compressing strip/tile %d", psJob->nStripOrTile);
    1018             :     }
    1019             : 
    1020       26682 :     XTIFFClose(hTIFFTmp);
    1021       26682 :     if (VSIFCloseL(fpTmp) != 0)
    1022             :     {
    1023           0 :         if (bOK)
    1024             :         {
    1025           0 :             bOK = false;
    1026           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1027             :                      "Error when compressing strip/tile %d",
    1028             :                      psJob->nStripOrTile);
    1029             :         }
    1030             :     }
    1031             : 
    1032       26682 :     if (bOK)
    1033             :     {
    1034       26682 :         vsi_l_offset nFileSize = 0;
    1035             :         GByte *pabyCompressedBuffer =
    1036       26682 :             VSIGetMemFileBuffer(psJob->pszTmpFilename, &nFileSize, FALSE);
    1037       26682 :         CPLAssert(static_cast<vsi_l_offset>(
    1038             :                       nOffset + psJob->nCompressedBufferSize) <= nFileSize);
    1039       26682 :         psJob->pabyCompressedBuffer = pabyCompressedBuffer + nOffset;
    1040             :     }
    1041             :     else
    1042             :     {
    1043           0 :         psJob->pabyCompressedBuffer = nullptr;
    1044           0 :         psJob->nCompressedBufferSize = 0;
    1045             :     }
    1046             : 
    1047       26682 :     auto poMainDS = poDS->m_poBaseDS ? poDS->m_poBaseDS : poDS;
    1048       26682 :     if (poMainDS->m_poCompressQueue)
    1049             :     {
    1050        1576 :         std::lock_guard oLock(poMainDS->m_oCompressThreadPoolMutex);
    1051        1576 :         psJob->bReady = true;
    1052             :     }
    1053       26682 : }
    1054             : 
    1055             : /************************************************************************/
    1056             : /*                        WriteRawStripOrTile()                         */
    1057             : /************************************************************************/
    1058             : 
    1059       34082 : void GTiffDataset::WriteRawStripOrTile(int nStripOrTile,
    1060             :                                        GByte *pabyCompressedBuffer,
    1061             :                                        GPtrDiff_t nCompressedBufferSize)
    1062             : {
    1063             : #ifdef DEBUG_VERBOSE
    1064             :     CPLDebug("GTIFF", "Writing raw strip/tile %d, size " CPL_FRMT_GUIB,
    1065             :              nStripOrTile, static_cast<GUIntBig>(nCompressedBufferSize));
    1066             : #endif
    1067       34082 :     toff_t *panOffsets = nullptr;
    1068       34082 :     toff_t *panByteCounts = nullptr;
    1069       34082 :     bool bWriteAtEnd = true;
    1070       34082 :     bool bWriteLeader = m_bLeaderSizeAsUInt4;
    1071       34082 :     bool bWriteTrailer = m_bTrailerRepeatedLast4BytesRepeated;
    1072       34082 :     if (TIFFGetField(m_hTIFF,
    1073       34082 :                      TIFFIsTiled(m_hTIFF) ? TIFFTAG_TILEOFFSETS
    1074             :                                           : TIFFTAG_STRIPOFFSETS,
    1075       34082 :                      &panOffsets) &&
    1076       34082 :         panOffsets != nullptr && panOffsets[nStripOrTile] != 0)
    1077             :     {
    1078             :         // Forces TIFFAppendStrip() to consider if the location of the
    1079             :         // tile/strip can be reused or if the strile should be written at end of
    1080             :         // file.
    1081         360 :         TIFFSetWriteOffset(m_hTIFF, 0);
    1082             : 
    1083         360 :         if (m_bBlockOrderRowMajor)
    1084             :         {
    1085         264 :             if (TIFFGetField(m_hTIFF,
    1086         264 :                              TIFFIsTiled(m_hTIFF) ? TIFFTAG_TILEBYTECOUNTS
    1087             :                                                   : TIFFTAG_STRIPBYTECOUNTS,
    1088         528 :                              &panByteCounts) &&
    1089         264 :                 panByteCounts != nullptr)
    1090             :             {
    1091         264 :                 if (static_cast<GUIntBig>(nCompressedBufferSize) >
    1092         264 :                     panByteCounts[nStripOrTile])
    1093             :                 {
    1094           8 :                     GTiffDataset *poRootDS = m_poBaseDS ? m_poBaseDS : this;
    1095           8 :                     if (!poRootDS->m_bKnownIncompatibleEdition &&
    1096           8 :                         !poRootDS->m_bWriteKnownIncompatibleEdition)
    1097             :                     {
    1098           8 :                         ReportError(
    1099             :                             CE_Warning, CPLE_AppDefined,
    1100             :                             "A strile cannot be rewritten in place, which "
    1101             :                             "invalidates the BLOCK_ORDER optimization.");
    1102           8 :                         poRootDS->m_bKnownIncompatibleEdition = true;
    1103           8 :                         poRootDS->m_bWriteKnownIncompatibleEdition = true;
    1104             :                     }
    1105             :                 }
    1106             :                 // For mask interleaving, if the size is not exactly the same,
    1107             :                 // completely give up (we could potentially move the mask in
    1108             :                 // case the imagery is smaller)
    1109         256 :                 else if (m_poMaskDS && m_bMaskInterleavedWithImagery &&
    1110           0 :                          static_cast<GUIntBig>(nCompressedBufferSize) !=
    1111           0 :                              panByteCounts[nStripOrTile])
    1112             :                 {
    1113           0 :                     GTiffDataset *poRootDS = m_poBaseDS ? m_poBaseDS : this;
    1114           0 :                     if (!poRootDS->m_bKnownIncompatibleEdition &&
    1115           0 :                         !poRootDS->m_bWriteKnownIncompatibleEdition)
    1116             :                     {
    1117           0 :                         ReportError(
    1118             :                             CE_Warning, CPLE_AppDefined,
    1119             :                             "A strile cannot be rewritten in place, which "
    1120             :                             "invalidates the MASK_INTERLEAVED_WITH_IMAGERY "
    1121             :                             "optimization.");
    1122           0 :                         poRootDS->m_bKnownIncompatibleEdition = true;
    1123           0 :                         poRootDS->m_bWriteKnownIncompatibleEdition = true;
    1124             :                     }
    1125           0 :                     bWriteLeader = false;
    1126           0 :                     bWriteTrailer = false;
    1127           0 :                     if (m_bLeaderSizeAsUInt4)
    1128             :                     {
    1129             :                         // If there was a valid leader, invalidat it
    1130           0 :                         VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4,
    1131             :                                      SEEK_SET);
    1132             :                         uint32_t nOldSize;
    1133           0 :                         VSIFReadL(&nOldSize, 1, 4,
    1134             :                                   VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF)));
    1135           0 :                         CPL_LSBPTR32(&nOldSize);
    1136           0 :                         if (nOldSize == panByteCounts[nStripOrTile])
    1137             :                         {
    1138           0 :                             uint32_t nInvalidatedSize = 0;
    1139           0 :                             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4,
    1140             :                                          SEEK_SET);
    1141           0 :                             VSI_TIFFWrite(m_hTIFF, &nInvalidatedSize,
    1142             :                                           sizeof(nInvalidatedSize));
    1143             :                         }
    1144             :                     }
    1145             :                 }
    1146             :                 else
    1147             :                 {
    1148         256 :                     bWriteAtEnd = false;
    1149             :                 }
    1150             :             }
    1151             :         }
    1152             :     }
    1153       34082 :     if (bWriteLeader &&
    1154       25111 :         static_cast<GUIntBig>(nCompressedBufferSize) <= 0xFFFFFFFFU)
    1155             :     {
    1156             :         // cppcheck-suppress knownConditionTrueFalse
    1157       25111 :         if (bWriteAtEnd)
    1158             :         {
    1159       24855 :             VSI_TIFFSeek(m_hTIFF, 0, SEEK_END);
    1160             :         }
    1161             :         else
    1162             :         {
    1163             :             // If we rewrite an existing strile in place with an existing
    1164             :             // leader, check that the leader is valid, before rewriting it. And
    1165             :             // if it is not valid, then do not write the trailer, as we could
    1166             :             // corrupt other data.
    1167         256 :             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4, SEEK_SET);
    1168             :             uint32_t nOldSize;
    1169         256 :             VSIFReadL(&nOldSize, 1, 4,
    1170             :                       VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF)));
    1171         256 :             CPL_LSBPTR32(&nOldSize);
    1172         256 :             bWriteLeader =
    1173         256 :                 panByteCounts && nOldSize == panByteCounts[nStripOrTile];
    1174         256 :             bWriteTrailer = bWriteLeader;
    1175         256 :             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4, SEEK_SET);
    1176             :         }
    1177             :         // cppcheck-suppress knownConditionTrueFalse
    1178       25111 :         if (bWriteLeader)
    1179             :         {
    1180       25111 :             uint32_t nSize = static_cast<uint32_t>(nCompressedBufferSize);
    1181       25111 :             CPL_LSBPTR32(&nSize);
    1182       25111 :             if (!VSI_TIFFWrite(m_hTIFF, &nSize, sizeof(nSize)))
    1183           0 :                 m_bWriteError = true;
    1184             :         }
    1185             :     }
    1186             :     tmsize_t written;
    1187       34082 :     if (TIFFIsTiled(m_hTIFF))
    1188       26409 :         written = TIFFWriteRawTile(m_hTIFF, nStripOrTile, pabyCompressedBuffer,
    1189             :                                    nCompressedBufferSize);
    1190             :     else
    1191        7673 :         written = TIFFWriteRawStrip(m_hTIFF, nStripOrTile, pabyCompressedBuffer,
    1192             :                                     nCompressedBufferSize);
    1193       34082 :     if (written != nCompressedBufferSize)
    1194          12 :         m_bWriteError = true;
    1195       34082 :     if (bWriteTrailer &&
    1196       25111 :         static_cast<GUIntBig>(nCompressedBufferSize) <= 0xFFFFFFFFU)
    1197             :     {
    1198       25111 :         GByte abyLastBytes[4] = {};
    1199       25111 :         if (nCompressedBufferSize >= 4)
    1200       25111 :             memcpy(abyLastBytes,
    1201       25111 :                    pabyCompressedBuffer + nCompressedBufferSize - 4, 4);
    1202             :         else
    1203           0 :             memcpy(abyLastBytes, pabyCompressedBuffer, nCompressedBufferSize);
    1204       25111 :         if (!VSI_TIFFWrite(m_hTIFF, abyLastBytes, 4))
    1205           0 :             m_bWriteError = true;
    1206             :     }
    1207       34082 : }
    1208             : 
    1209             : /************************************************************************/
    1210             : /*                      WaitCompletionForJobIdx()                       */
    1211             : /************************************************************************/
    1212             : 
    1213        1576 : void GTiffDataset::WaitCompletionForJobIdx(int i)
    1214             : {
    1215        1576 :     auto poMainDS = m_poBaseDS ? m_poBaseDS : this;
    1216        1576 :     auto poQueue = poMainDS->m_poCompressQueue.get();
    1217        1576 :     auto &oQueue = poMainDS->m_asQueueJobIdx;
    1218        1576 :     auto &asJobs = poMainDS->m_asCompressionJobs;
    1219        1576 :     auto &mutex = poMainDS->m_oCompressThreadPoolMutex;
    1220             : 
    1221        1576 :     CPLAssert(i >= 0 && static_cast<size_t>(i) < asJobs.size());
    1222        1576 :     CPLAssert(asJobs[i].nStripOrTile >= 0);
    1223        1576 :     CPLAssert(!oQueue.empty());
    1224             : 
    1225        1576 :     bool bHasWarned = false;
    1226             :     while (true)
    1227             :     {
    1228             :         bool bReady;
    1229             :         {
    1230        2277 :             std::lock_guard oLock(mutex);
    1231        2277 :             bReady = asJobs[i].bReady;
    1232             :         }
    1233        2277 :         if (!bReady)
    1234             :         {
    1235         701 :             if (!bHasWarned)
    1236             :             {
    1237         449 :                 CPLDebug("GTIFF",
    1238             :                          "Waiting for worker job to finish handling block %d",
    1239         449 :                          asJobs[i].nStripOrTile);
    1240         449 :                 bHasWarned = true;
    1241             :             }
    1242         701 :             poQueue->GetPool()->WaitEvent();
    1243             :         }
    1244             :         else
    1245             :         {
    1246        1576 :             break;
    1247             :         }
    1248         701 :     }
    1249             : 
    1250        1576 :     if (asJobs[i].nCompressedBufferSize)
    1251             :     {
    1252        3152 :         asJobs[i].poDS->WriteRawStripOrTile(asJobs[i].nStripOrTile,
    1253        1576 :                                             asJobs[i].pabyCompressedBuffer,
    1254        1576 :                                             asJobs[i].nCompressedBufferSize);
    1255             :     }
    1256        1576 :     asJobs[i].pabyCompressedBuffer = nullptr;
    1257        1576 :     asJobs[i].nBufferSize = 0;
    1258             :     {
    1259             :         // Likely useless, but makes Coverity happy
    1260        1576 :         std::lock_guard oLock(mutex);
    1261        1576 :         asJobs[i].bReady = false;
    1262             :     }
    1263        1576 :     asJobs[i].nStripOrTile = -1;
    1264        1576 :     oQueue.pop();
    1265        1576 : }
    1266             : 
    1267             : /************************************************************************/
    1268             : /*                       WaitCompletionForBlock()                       */
    1269             : /************************************************************************/
    1270             : 
    1271     2323120 : void GTiffDataset::WaitCompletionForBlock(int nBlockId)
    1272             : {
    1273     2323120 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    1274     2303860 :                               : m_poCompressQueue.get();
    1275             :     // cppcheck-suppress constVariableReference
    1276     2323120 :     auto &oQueue = m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    1277             :     // cppcheck-suppress constVariableReference
    1278     2303860 :     auto &asJobs =
    1279     2323120 :         m_poBaseDS ? m_poBaseDS->m_asCompressionJobs : m_asCompressionJobs;
    1280             : 
    1281     2323120 :     if (poQueue != nullptr && !oQueue.empty())
    1282             :     {
    1283        1066 :         for (int i = 0; i < static_cast<int>(asJobs.size()); ++i)
    1284             :         {
    1285         888 :             if (asJobs[i].poDS == this && asJobs[i].nStripOrTile == nBlockId)
    1286             :             {
    1287         128 :                 while (!oQueue.empty() &&
    1288          64 :                        !(asJobs[oQueue.front()].poDS == this &&
    1289          64 :                          asJobs[oQueue.front()].nStripOrTile == nBlockId))
    1290             :                 {
    1291           0 :                     WaitCompletionForJobIdx(oQueue.front());
    1292             :                 }
    1293          64 :                 CPLAssert(!oQueue.empty() &&
    1294             :                           asJobs[oQueue.front()].poDS == this &&
    1295             :                           asJobs[oQueue.front()].nStripOrTile == nBlockId);
    1296          64 :                 WaitCompletionForJobIdx(oQueue.front());
    1297             :             }
    1298             :         }
    1299             :     }
    1300     2323120 : }
    1301             : 
    1302             : /************************************************************************/
    1303             : /*                        SubmitCompressionJob()                        */
    1304             : /************************************************************************/
    1305             : 
    1306      198003 : bool GTiffDataset::SubmitCompressionJob(int nStripOrTile, GByte *pabyData,
    1307             :                                         GPtrDiff_t cc, int nHeight)
    1308             : {
    1309             :     /* -------------------------------------------------------------------- */
    1310             :     /*      Should we do compression in a worker thread ?                   */
    1311             :     /* -------------------------------------------------------------------- */
    1312      198003 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    1313      183929 :                               : m_poCompressQueue.get();
    1314             : 
    1315      198003 :     if (poQueue && m_nCompression == COMPRESSION_NONE)
    1316             :     {
    1317             :         // We don't do multi-threaded compression for uncompressed...
    1318             :         // but we must wait for other related compression tasks (e.g mask)
    1319             :         // to be completed
    1320           0 :         poQueue->WaitCompletion();
    1321             : 
    1322             :         // Flush remaining data
    1323             :         // cppcheck-suppress constVariableReference
    1324           0 :         auto &oQueue =
    1325           0 :             m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    1326           0 :         while (!oQueue.empty())
    1327             :         {
    1328           0 :             WaitCompletionForJobIdx(oQueue.front());
    1329             :         }
    1330             :     }
    1331             : 
    1332             :     const auto SetupJob =
    1333      123564 :         [this, pabyData, cc, nHeight, nStripOrTile](GTiffCompressionJob &sJob)
    1334             :     {
    1335       26682 :         sJob.poDS = this;
    1336       26682 :         sJob.bTIFFIsBigEndian = CPL_TO_BOOL(TIFFIsBigEndian(m_hTIFF));
    1337             :         GByte *pabyBuffer =
    1338       26682 :             static_cast<GByte *>(VSI_REALLOC_VERBOSE(sJob.pabyBuffer, cc));
    1339       26682 :         if (!pabyBuffer)
    1340           0 :             return false;
    1341       26682 :         sJob.pabyBuffer = pabyBuffer;
    1342       26682 :         memcpy(sJob.pabyBuffer, pabyData, cc);
    1343       26682 :         sJob.nBufferSize = cc;
    1344       26682 :         sJob.nHeight = nHeight;
    1345       26682 :         sJob.nStripOrTile = nStripOrTile;
    1346       26682 :         sJob.nPredictor = PREDICTOR_NONE;
    1347       26682 :         if (GTIFFSupportsPredictor(m_nCompression))
    1348             :         {
    1349       16836 :             TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &sJob.nPredictor);
    1350             :         }
    1351             : 
    1352       26682 :         sJob.pExtraSamples = nullptr;
    1353       26682 :         sJob.nExtraSampleCount = 0;
    1354       26682 :         TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &sJob.nExtraSampleCount,
    1355             :                      &sJob.pExtraSamples);
    1356       26682 :         return true;
    1357      198003 :     };
    1358             : 
    1359      198003 :     if (poQueue == nullptr || !(m_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    1360         806 :                                 m_nCompression == COMPRESSION_LZW ||
    1361          78 :                                 m_nCompression == COMPRESSION_PACKBITS ||
    1362          72 :                                 m_nCompression == COMPRESSION_LZMA ||
    1363          62 :                                 m_nCompression == COMPRESSION_ZSTD ||
    1364          52 :                                 m_nCompression == COMPRESSION_LERC ||
    1365          46 :                                 m_nCompression == COMPRESSION_JXL ||
    1366          46 :                                 m_nCompression == COMPRESSION_JXL_DNG_1_7 ||
    1367          28 :                                 m_nCompression == COMPRESSION_WEBP ||
    1368          18 :                                 m_nCompression == COMPRESSION_JPEG))
    1369             :     {
    1370      196427 :         if (m_bBlockOrderRowMajor || m_bLeaderSizeAsUInt4 ||
    1371      171321 :             m_bTrailerRepeatedLast4BytesRepeated)
    1372             :         {
    1373             :             GTiffCompressionJob sJob;
    1374       25106 :             memset(&sJob, 0, sizeof(sJob));
    1375       25106 :             if (SetupJob(sJob))
    1376             :             {
    1377       25106 :                 sJob.pszTmpFilename =
    1378       25106 :                     CPLStrdup(VSIMemGenerateHiddenFilename("temp.tif"));
    1379             : 
    1380       25106 :                 ThreadCompressionFunc(&sJob);
    1381             : 
    1382       25106 :                 if (sJob.nCompressedBufferSize)
    1383             :                 {
    1384       25106 :                     sJob.poDS->WriteRawStripOrTile(sJob.nStripOrTile,
    1385             :                                                    sJob.pabyCompressedBuffer,
    1386             :                                                    sJob.nCompressedBufferSize);
    1387             :                 }
    1388             : 
    1389       25106 :                 CPLFree(sJob.pabyBuffer);
    1390       25106 :                 VSIUnlink(sJob.pszTmpFilename);
    1391       25106 :                 CPLFree(sJob.pszTmpFilename);
    1392       25106 :                 return sJob.nCompressedBufferSize > 0 && !m_bWriteError;
    1393             :             }
    1394             :         }
    1395             : 
    1396      171321 :         return false;
    1397             :     }
    1398             : 
    1399        1576 :     auto poMainDS = m_poBaseDS ? m_poBaseDS : this;
    1400        1576 :     auto &oQueue = poMainDS->m_asQueueJobIdx;
    1401        1576 :     auto &asJobs = poMainDS->m_asCompressionJobs;
    1402             : 
    1403        1576 :     int nNextCompressionJobAvail = -1;
    1404             : 
    1405        1576 :     if (oQueue.size() == asJobs.size())
    1406             :     {
    1407        1443 :         CPLAssert(!oQueue.empty());
    1408        1443 :         nNextCompressionJobAvail = oQueue.front();
    1409        1443 :         WaitCompletionForJobIdx(nNextCompressionJobAvail);
    1410             :     }
    1411             :     else
    1412             :     {
    1413         133 :         const int nJobs = static_cast<int>(asJobs.size());
    1414         324 :         for (int i = 0; i < nJobs; ++i)
    1415             :         {
    1416         324 :             if (asJobs[i].nBufferSize == 0)
    1417             :             {
    1418         133 :                 nNextCompressionJobAvail = i;
    1419         133 :                 break;
    1420             :             }
    1421             :         }
    1422             :     }
    1423        1576 :     CPLAssert(nNextCompressionJobAvail >= 0);
    1424             : 
    1425        1576 :     GTiffCompressionJob *psJob = &asJobs[nNextCompressionJobAvail];
    1426        1576 :     bool bOK = SetupJob(*psJob);
    1427        1576 :     if (bOK)
    1428             :     {
    1429        1576 :         poQueue->SubmitJob(ThreadCompressionFunc, psJob);
    1430        1576 :         oQueue.push(nNextCompressionJobAvail);
    1431             :     }
    1432             : 
    1433        1576 :     return bOK;
    1434             : }
    1435             : 
    1436             : /************************************************************************/
    1437             : /*                             DiscardLsb()                             */
    1438             : /************************************************************************/
    1439             : 
    1440         272 : template <class T> bool MustNotDiscardLsb(T value, bool bHasNoData, T nodata)
    1441             : {
    1442         272 :     return bHasNoData && value == nodata;
    1443             : }
    1444             : 
    1445             : template <>
    1446          44 : bool MustNotDiscardLsb<float>(float value, bool bHasNoData, float nodata)
    1447             : {
    1448          44 :     return (bHasNoData && value == nodata) || !std::isfinite(value);
    1449             : }
    1450             : 
    1451             : template <>
    1452          44 : bool MustNotDiscardLsb<double>(double value, bool bHasNoData, double nodata)
    1453             : {
    1454          44 :     return (bHasNoData && value == nodata) || !std::isfinite(value);
    1455             : }
    1456             : 
    1457             : template <class T> T AdjustValue(T value, uint64_t nRoundUpBitTest);
    1458             : 
    1459          10 : template <class T> T AdjustValueInt(T value, uint64_t nRoundUpBitTest)
    1460             : {
    1461          10 :     if (value >=
    1462          10 :         static_cast<T>(std::numeric_limits<T>::max() - (nRoundUpBitTest << 1)))
    1463           0 :         return static_cast<T>(value - (nRoundUpBitTest << 1));
    1464          10 :     return static_cast<T>(value + (nRoundUpBitTest << 1));
    1465             : }
    1466             : 
    1467           0 : template <> int8_t AdjustValue<int8_t>(int8_t value, uint64_t nRoundUpBitTest)
    1468             : {
    1469           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1470             : }
    1471             : 
    1472             : template <>
    1473           2 : uint8_t AdjustValue<uint8_t>(uint8_t value, uint64_t nRoundUpBitTest)
    1474             : {
    1475           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1476             : }
    1477             : 
    1478             : template <>
    1479           2 : int16_t AdjustValue<int16_t>(int16_t value, uint64_t nRoundUpBitTest)
    1480             : {
    1481           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1482             : }
    1483             : 
    1484             : template <>
    1485           2 : uint16_t AdjustValue<uint16_t>(uint16_t value, uint64_t nRoundUpBitTest)
    1486             : {
    1487           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1488             : }
    1489             : 
    1490             : template <>
    1491           2 : int32_t AdjustValue<int32_t>(int32_t value, uint64_t nRoundUpBitTest)
    1492             : {
    1493           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1494             : }
    1495             : 
    1496             : template <>
    1497           2 : uint32_t AdjustValue<uint32_t>(uint32_t value, uint64_t nRoundUpBitTest)
    1498             : {
    1499           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1500             : }
    1501             : 
    1502             : template <>
    1503           0 : int64_t AdjustValue<int64_t>(int64_t value, uint64_t nRoundUpBitTest)
    1504             : {
    1505           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1506             : }
    1507             : 
    1508             : template <>
    1509           0 : uint64_t AdjustValue<uint64_t>(uint64_t value, uint64_t nRoundUpBitTest)
    1510             : {
    1511           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1512             : }
    1513             : 
    1514           0 : template <> GFloat16 AdjustValue<GFloat16>(GFloat16 value, uint64_t)
    1515             : {
    1516             :     using std::nextafter;
    1517           0 :     return nextafter(value, cpl::NumericLimits<GFloat16>::max());
    1518             : }
    1519             : 
    1520           0 : template <> float AdjustValue<float>(float value, uint64_t)
    1521             : {
    1522           0 :     return std::nextafter(value, std::numeric_limits<float>::max());
    1523             : }
    1524             : 
    1525           0 : template <> double AdjustValue<double>(double value, uint64_t)
    1526             : {
    1527           0 :     return std::nextafter(value, std::numeric_limits<double>::max());
    1528             : }
    1529             : 
    1530             : template <class Teffective, class T>
    1531             : T RoundValueDiscardLsb(const void *ptr, uint64_t nMask,
    1532             :                        uint64_t nRoundUpBitTest);
    1533             : 
    1534             : template <class T>
    1535          16 : T RoundValueDiscardLsbUnsigned(const void *ptr, uint64_t nMask,
    1536             :                                uint64_t nRoundUpBitTest)
    1537             : {
    1538          32 :     if ((*reinterpret_cast<const T *>(ptr) & nMask) >
    1539          16 :         static_cast<uint64_t>(std::numeric_limits<T>::max()) -
    1540          16 :             (nRoundUpBitTest << 1U))
    1541             :     {
    1542           4 :         return static_cast<T>(std::numeric_limits<T>::max() & nMask);
    1543             :     }
    1544          12 :     const uint64_t newval =
    1545          12 :         (*reinterpret_cast<const T *>(ptr) & nMask) + (nRoundUpBitTest << 1U);
    1546          12 :     return static_cast<T>(newval);
    1547             : }
    1548             : 
    1549             : template <class T>
    1550          18 : T RoundValueDiscardLsbSigned(const void *ptr, uint64_t nMask,
    1551             :                              uint64_t nRoundUpBitTest)
    1552             : {
    1553          18 :     T oldval = *reinterpret_cast<const T *>(ptr);
    1554          18 :     if (oldval < 0)
    1555             :     {
    1556           4 :         return static_cast<T>(oldval & nMask);
    1557             :     }
    1558          14 :     const uint64_t newval =
    1559          14 :         (*reinterpret_cast<const T *>(ptr) & nMask) + (nRoundUpBitTest << 1U);
    1560          14 :     if (newval > static_cast<uint64_t>(std::numeric_limits<T>::max()))
    1561           4 :         return static_cast<T>(std::numeric_limits<T>::max() & nMask);
    1562          10 :     return static_cast<T>(newval);
    1563             : }
    1564             : 
    1565             : template <>
    1566          11 : uint16_t RoundValueDiscardLsb<uint16_t, uint16_t>(const void *ptr,
    1567             :                                                   uint64_t nMask,
    1568             :                                                   uint64_t nRoundUpBitTest)
    1569             : {
    1570          11 :     return RoundValueDiscardLsbUnsigned<uint16_t>(ptr, nMask, nRoundUpBitTest);
    1571             : }
    1572             : 
    1573             : template <>
    1574           5 : uint32_t RoundValueDiscardLsb<uint32_t, uint32_t>(const void *ptr,
    1575             :                                                   uint64_t nMask,
    1576             :                                                   uint64_t nRoundUpBitTest)
    1577             : {
    1578           5 :     return RoundValueDiscardLsbUnsigned<uint32_t>(ptr, nMask, nRoundUpBitTest);
    1579             : }
    1580             : 
    1581             : template <>
    1582           0 : uint64_t RoundValueDiscardLsb<uint64_t, uint64_t>(const void *ptr,
    1583             :                                                   uint64_t nMask,
    1584             :                                                   uint64_t nRoundUpBitTest)
    1585             : {
    1586           0 :     return RoundValueDiscardLsbUnsigned<uint64_t>(ptr, nMask, nRoundUpBitTest);
    1587             : }
    1588             : 
    1589             : template <>
    1590           0 : int8_t RoundValueDiscardLsb<int8_t, int8_t>(const void *ptr, uint64_t nMask,
    1591             :                                             uint64_t nRoundUpBitTest)
    1592             : {
    1593           0 :     return RoundValueDiscardLsbSigned<int8_t>(ptr, nMask, nRoundUpBitTest);
    1594             : }
    1595             : 
    1596             : template <>
    1597          13 : int16_t RoundValueDiscardLsb<int16_t, int16_t>(const void *ptr, uint64_t nMask,
    1598             :                                                uint64_t nRoundUpBitTest)
    1599             : {
    1600          13 :     return RoundValueDiscardLsbSigned<int16_t>(ptr, nMask, nRoundUpBitTest);
    1601             : }
    1602             : 
    1603             : template <>
    1604           5 : int32_t RoundValueDiscardLsb<int32_t, int32_t>(const void *ptr, uint64_t nMask,
    1605             :                                                uint64_t nRoundUpBitTest)
    1606             : {
    1607           5 :     return RoundValueDiscardLsbSigned<int32_t>(ptr, nMask, nRoundUpBitTest);
    1608             : }
    1609             : 
    1610             : template <>
    1611           0 : int64_t RoundValueDiscardLsb<int64_t, int64_t>(const void *ptr, uint64_t nMask,
    1612             :                                                uint64_t nRoundUpBitTest)
    1613             : {
    1614           0 :     return RoundValueDiscardLsbSigned<int64_t>(ptr, nMask, nRoundUpBitTest);
    1615             : }
    1616             : 
    1617             : template <>
    1618           0 : uint16_t RoundValueDiscardLsb<GFloat16, uint16_t>(const void *ptr,
    1619             :                                                   uint64_t nMask,
    1620             :                                                   uint64_t nRoundUpBitTest)
    1621             : {
    1622           0 :     return RoundValueDiscardLsbUnsigned<uint16_t>(ptr, nMask, nRoundUpBitTest);
    1623             : }
    1624             : 
    1625             : template <>
    1626           0 : uint32_t RoundValueDiscardLsb<float, uint32_t>(const void *ptr, uint64_t nMask,
    1627             :                                                uint64_t nRoundUpBitTest)
    1628             : {
    1629           0 :     return RoundValueDiscardLsbUnsigned<uint32_t>(ptr, nMask, nRoundUpBitTest);
    1630             : }
    1631             : 
    1632             : template <>
    1633           0 : uint64_t RoundValueDiscardLsb<double, uint64_t>(const void *ptr, uint64_t nMask,
    1634             :                                                 uint64_t nRoundUpBitTest)
    1635             : {
    1636           0 :     return RoundValueDiscardLsbUnsigned<uint64_t>(ptr, nMask, nRoundUpBitTest);
    1637             : }
    1638             : 
    1639             : template <class Teffective, class T>
    1640         145 : static void DiscardLsbT(GByte *pabyBuffer, size_t nBytes, int iBand, int nBands,
    1641             :                         uint16_t nPlanarConfig,
    1642             :                         const GTiffDataset::MaskOffset *panMaskOffsetLsb,
    1643             :                         bool bHasNoData, Teffective nNoDataValue)
    1644             : {
    1645             :     static_assert(sizeof(Teffective) == sizeof(T),
    1646             :                   "sizeof(Teffective) == sizeof(T)");
    1647         145 :     if (nPlanarConfig == PLANARCONFIG_SEPARATE)
    1648             :     {
    1649          98 :         const auto nMask = panMaskOffsetLsb[iBand].nMask;
    1650          98 :         const auto nRoundUpBitTest = panMaskOffsetLsb[iBand].nRoundUpBitTest;
    1651         196 :         for (size_t i = 0; i < nBytes / sizeof(T); ++i)
    1652             :         {
    1653          98 :             if (MustNotDiscardLsb(reinterpret_cast<Teffective *>(pabyBuffer)[i],
    1654             :                                   bHasNoData, nNoDataValue))
    1655             :             {
    1656          22 :                 continue;
    1657             :             }
    1658             : 
    1659          76 :             if (reinterpret_cast<T *>(pabyBuffer)[i] & nRoundUpBitTest)
    1660             :             {
    1661          30 :                 reinterpret_cast<T *>(pabyBuffer)[i] =
    1662          15 :                     RoundValueDiscardLsb<Teffective, T>(
    1663          15 :                         &(reinterpret_cast<T *>(pabyBuffer)[i]), nMask,
    1664             :                         nRoundUpBitTest);
    1665             :             }
    1666             :             else
    1667             :             {
    1668          61 :                 reinterpret_cast<T *>(pabyBuffer)[i] = static_cast<T>(
    1669          61 :                     reinterpret_cast<T *>(pabyBuffer)[i] & nMask);
    1670             :             }
    1671             : 
    1672             :             // Make sure that by discarding LSB we don't end up to a value
    1673             :             // that is no the nodata value
    1674          76 :             if (MustNotDiscardLsb(reinterpret_cast<Teffective *>(pabyBuffer)[i],
    1675             :                                   bHasNoData, nNoDataValue))
    1676             :             {
    1677           8 :                 reinterpret_cast<Teffective *>(pabyBuffer)[i] =
    1678           4 :                     AdjustValue(nNoDataValue, nRoundUpBitTest);
    1679             :             }
    1680             :         }
    1681             :     }
    1682             :     else
    1683             :     {
    1684          94 :         for (size_t i = 0; i < nBytes / sizeof(T); i += nBands)
    1685             :         {
    1686         147 :             for (int j = 0; j < nBands; ++j)
    1687             :             {
    1688         100 :                 if (MustNotDiscardLsb(
    1689         100 :                         reinterpret_cast<Teffective *>(pabyBuffer)[i + j],
    1690             :                         bHasNoData, nNoDataValue))
    1691             :                 {
    1692          14 :                     continue;
    1693             :                 }
    1694             : 
    1695          86 :                 if (reinterpret_cast<T *>(pabyBuffer)[i + j] &
    1696          86 :                     panMaskOffsetLsb[j].nRoundUpBitTest)
    1697             :                 {
    1698          38 :                     reinterpret_cast<T *>(pabyBuffer)[i + j] =
    1699          19 :                         RoundValueDiscardLsb<Teffective, T>(
    1700          19 :                             &(reinterpret_cast<T *>(pabyBuffer)[i + j]),
    1701          19 :                             panMaskOffsetLsb[j].nMask,
    1702          19 :                             panMaskOffsetLsb[j].nRoundUpBitTest);
    1703             :                 }
    1704             :                 else
    1705             :                 {
    1706          67 :                     reinterpret_cast<T *>(pabyBuffer)[i + j] = static_cast<T>(
    1707          67 :                         (reinterpret_cast<T *>(pabyBuffer)[i + j] &
    1708          67 :                          panMaskOffsetLsb[j].nMask));
    1709             :                 }
    1710             : 
    1711             :                 // Make sure that by discarding LSB we don't end up to a value
    1712             :                 // that is no the nodata value
    1713          86 :                 if (MustNotDiscardLsb(
    1714          86 :                         reinterpret_cast<Teffective *>(pabyBuffer)[i + j],
    1715             :                         bHasNoData, nNoDataValue))
    1716             :                 {
    1717           8 :                     reinterpret_cast<Teffective *>(pabyBuffer)[i + j] =
    1718           4 :                         AdjustValue(nNoDataValue,
    1719           4 :                                     panMaskOffsetLsb[j].nRoundUpBitTest);
    1720             :                 }
    1721             :             }
    1722             :         }
    1723             :     }
    1724         145 : }
    1725             : 
    1726         183 : static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand,
    1727             :                        int nBands, uint16_t nSampleFormat,
    1728             :                        uint16_t nBitsPerSample, uint16_t nPlanarConfig,
    1729             :                        const GTiffDataset::MaskOffset *panMaskOffsetLsb,
    1730             :                        bool bHasNoData, double dfNoDataValue)
    1731             : {
    1732         183 :     if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_UINT)
    1733             :     {
    1734          38 :         uint8_t nNoDataValue = 0;
    1735          38 :         if (bHasNoData && GDALIsValueExactAs<uint8_t>(dfNoDataValue))
    1736             :         {
    1737           6 :             nNoDataValue = static_cast<uint8_t>(dfNoDataValue);
    1738             :         }
    1739             :         else
    1740             :         {
    1741          32 :             bHasNoData = false;
    1742             :         }
    1743          38 :         if (nPlanarConfig == PLANARCONFIG_SEPARATE)
    1744             :         {
    1745          25 :             const auto nMask =
    1746          25 :                 static_cast<unsigned>(panMaskOffsetLsb[iBand].nMask);
    1747          25 :             const auto nRoundUpBitTest =
    1748          25 :                 static_cast<unsigned>(panMaskOffsetLsb[iBand].nRoundUpBitTest);
    1749          50 :             for (decltype(nBytes) i = 0; i < nBytes; ++i)
    1750             :             {
    1751          25 :                 if (bHasNoData && pabyBuffer[i] == nNoDataValue)
    1752           3 :                     continue;
    1753             : 
    1754             :                 // Keep 255 in case it is alpha.
    1755          22 :                 if (pabyBuffer[i] != 255)
    1756             :                 {
    1757          21 :                     if (pabyBuffer[i] & nRoundUpBitTest)
    1758           5 :                         pabyBuffer[i] = static_cast<GByte>(
    1759           5 :                             std::min(255U, (pabyBuffer[i] & nMask) +
    1760           5 :                                                (nRoundUpBitTest << 1U)));
    1761             :                     else
    1762          16 :                         pabyBuffer[i] =
    1763          16 :                             static_cast<GByte>(pabyBuffer[i] & nMask);
    1764             : 
    1765             :                     // Make sure that by discarding LSB we don't end up to a
    1766             :                     // value that is no the nodata value
    1767          21 :                     if (bHasNoData && pabyBuffer[i] == nNoDataValue)
    1768           2 :                         pabyBuffer[i] =
    1769           1 :                             AdjustValue(nNoDataValue, nRoundUpBitTest);
    1770             :                 }
    1771             :             }
    1772             :         }
    1773             :         else
    1774             :         {
    1775          26 :             for (decltype(nBytes) i = 0; i < nBytes; i += nBands)
    1776             :             {
    1777          42 :                 for (int j = 0; j < nBands; ++j)
    1778             :                 {
    1779          29 :                     if (bHasNoData && pabyBuffer[i + j] == nNoDataValue)
    1780           2 :                         continue;
    1781             : 
    1782             :                     // Keep 255 in case it is alpha.
    1783          27 :                     if (pabyBuffer[i + j] != 255)
    1784             :                     {
    1785          25 :                         if (pabyBuffer[i + j] &
    1786          25 :                             panMaskOffsetLsb[j].nRoundUpBitTest)
    1787             :                         {
    1788           6 :                             pabyBuffer[i + j] = static_cast<GByte>(std::min(
    1789          12 :                                 255U,
    1790           6 :                                 (pabyBuffer[i + j] &
    1791             :                                  static_cast<unsigned>(
    1792           6 :                                      panMaskOffsetLsb[j].nMask)) +
    1793             :                                     (static_cast<unsigned>(
    1794           6 :                                          panMaskOffsetLsb[j].nRoundUpBitTest)
    1795           6 :                                      << 1U)));
    1796             :                         }
    1797             :                         else
    1798             :                         {
    1799          19 :                             pabyBuffer[i + j] = static_cast<GByte>(
    1800          19 :                                 pabyBuffer[i + j] & panMaskOffsetLsb[j].nMask);
    1801             :                         }
    1802             : 
    1803             :                         // Make sure that by discarding LSB we don't end up to a
    1804             :                         // value that is no the nodata value
    1805          25 :                         if (bHasNoData && pabyBuffer[i + j] == nNoDataValue)
    1806           1 :                             pabyBuffer[i + j] = AdjustValue(
    1807             :                                 nNoDataValue,
    1808           1 :                                 panMaskOffsetLsb[j].nRoundUpBitTest);
    1809             :                     }
    1810             :                 }
    1811             :             }
    1812          38 :         }
    1813             :     }
    1814         145 :     else if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_INT)
    1815             :     {
    1816           0 :         int8_t nNoDataValue = 0;
    1817           0 :         if (bHasNoData && GDALIsValueExactAs<int8_t>(dfNoDataValue))
    1818             :         {
    1819           0 :             nNoDataValue = static_cast<int8_t>(dfNoDataValue);
    1820             :         }
    1821             :         else
    1822             :         {
    1823           0 :             bHasNoData = false;
    1824             :         }
    1825           0 :         DiscardLsbT<int8_t, int8_t>(pabyBuffer, nBytes, iBand, nBands,
    1826             :                                     nPlanarConfig, panMaskOffsetLsb, bHasNoData,
    1827           0 :                                     nNoDataValue);
    1828             :     }
    1829         145 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_INT)
    1830             :     {
    1831          48 :         int16_t nNoDataValue = 0;
    1832          48 :         if (bHasNoData && GDALIsValueExactAs<int16_t>(dfNoDataValue))
    1833             :         {
    1834           6 :             nNoDataValue = static_cast<int16_t>(dfNoDataValue);
    1835             :         }
    1836             :         else
    1837             :         {
    1838          42 :             bHasNoData = false;
    1839             :         }
    1840          48 :         DiscardLsbT<int16_t, int16_t>(pabyBuffer, nBytes, iBand, nBands,
    1841             :                                       nPlanarConfig, panMaskOffsetLsb,
    1842          48 :                                       bHasNoData, nNoDataValue);
    1843             :     }
    1844          97 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_UINT)
    1845             :     {
    1846          33 :         uint16_t nNoDataValue = 0;
    1847          33 :         if (bHasNoData && GDALIsValueExactAs<uint16_t>(dfNoDataValue))
    1848             :         {
    1849           6 :             nNoDataValue = static_cast<uint16_t>(dfNoDataValue);
    1850             :         }
    1851             :         else
    1852             :         {
    1853          27 :             bHasNoData = false;
    1854             :         }
    1855          33 :         DiscardLsbT<uint16_t, uint16_t>(pabyBuffer, nBytes, iBand, nBands,
    1856             :                                         nPlanarConfig, panMaskOffsetLsb,
    1857          33 :                                         bHasNoData, nNoDataValue);
    1858             :     }
    1859          64 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_INT)
    1860             :     {
    1861          13 :         int32_t nNoDataValue = 0;
    1862          13 :         if (bHasNoData && GDALIsValueExactAs<int32_t>(dfNoDataValue))
    1863             :         {
    1864           6 :             nNoDataValue = static_cast<int32_t>(dfNoDataValue);
    1865             :         }
    1866             :         else
    1867             :         {
    1868           7 :             bHasNoData = false;
    1869             :         }
    1870          13 :         DiscardLsbT<int32_t, int32_t>(pabyBuffer, nBytes, iBand, nBands,
    1871             :                                       nPlanarConfig, panMaskOffsetLsb,
    1872          13 :                                       bHasNoData, nNoDataValue);
    1873             :     }
    1874          51 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_UINT)
    1875             :     {
    1876          13 :         uint32_t nNoDataValue = 0;
    1877          13 :         if (bHasNoData && GDALIsValueExactAs<uint32_t>(dfNoDataValue))
    1878             :         {
    1879           6 :             nNoDataValue = static_cast<uint32_t>(dfNoDataValue);
    1880             :         }
    1881             :         else
    1882             :         {
    1883           7 :             bHasNoData = false;
    1884             :         }
    1885          13 :         DiscardLsbT<uint32_t, uint32_t>(pabyBuffer, nBytes, iBand, nBands,
    1886             :                                         nPlanarConfig, panMaskOffsetLsb,
    1887          13 :                                         bHasNoData, nNoDataValue);
    1888             :     }
    1889          38 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_INT)
    1890             :     {
    1891             :         // FIXME: we should not rely on dfNoDataValue when we support native
    1892             :         // data type for nodata
    1893           0 :         int64_t nNoDataValue = 0;
    1894           0 :         if (bHasNoData && GDALIsValueExactAs<int64_t>(dfNoDataValue))
    1895             :         {
    1896           0 :             nNoDataValue = static_cast<int64_t>(dfNoDataValue);
    1897             :         }
    1898             :         else
    1899             :         {
    1900           0 :             bHasNoData = false;
    1901             :         }
    1902           0 :         DiscardLsbT<int64_t, int64_t>(pabyBuffer, nBytes, iBand, nBands,
    1903             :                                       nPlanarConfig, panMaskOffsetLsb,
    1904           0 :                                       bHasNoData, nNoDataValue);
    1905             :     }
    1906          38 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_UINT)
    1907             :     {
    1908             :         // FIXME: we should not rely on dfNoDataValue when we support native
    1909             :         // data type for nodata
    1910           0 :         uint64_t nNoDataValue = 0;
    1911           0 :         if (bHasNoData && GDALIsValueExactAs<uint64_t>(dfNoDataValue))
    1912             :         {
    1913           0 :             nNoDataValue = static_cast<uint64_t>(dfNoDataValue);
    1914             :         }
    1915             :         else
    1916             :         {
    1917           0 :             bHasNoData = false;
    1918             :         }
    1919           0 :         DiscardLsbT<uint64_t, uint64_t>(pabyBuffer, nBytes, iBand, nBands,
    1920             :                                         nPlanarConfig, panMaskOffsetLsb,
    1921           0 :                                         bHasNoData, nNoDataValue);
    1922             :     }
    1923          38 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1924             :     {
    1925           0 :         const GFloat16 fNoDataValue = static_cast<GFloat16>(dfNoDataValue);
    1926           0 :         DiscardLsbT<GFloat16, uint16_t>(pabyBuffer, nBytes, iBand, nBands,
    1927             :                                         nPlanarConfig, panMaskOffsetLsb,
    1928           0 :                                         bHasNoData, fNoDataValue);
    1929             :     }
    1930          38 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1931             :     {
    1932          19 :         const float fNoDataValue = static_cast<float>(dfNoDataValue);
    1933          19 :         DiscardLsbT<float, uint32_t>(pabyBuffer, nBytes, iBand, nBands,
    1934             :                                      nPlanarConfig, panMaskOffsetLsb,
    1935          19 :                                      bHasNoData, fNoDataValue);
    1936             :     }
    1937          19 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1938             :     {
    1939          19 :         DiscardLsbT<double, uint64_t>(pabyBuffer, nBytes, iBand, nBands,
    1940             :                                       nPlanarConfig, panMaskOffsetLsb,
    1941             :                                       bHasNoData, dfNoDataValue);
    1942             :     }
    1943         183 : }
    1944             : 
    1945         183 : void GTiffDataset::DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes,
    1946             :                               int iBand) const
    1947             : {
    1948         183 :     ::DiscardLsb(pabyBuffer, nBytes, iBand, nBands, m_nSampleFormat,
    1949         183 :                  m_nBitsPerSample, m_nPlanarConfig, m_panMaskOffsetLsb,
    1950         183 :                  m_bNoDataSet, m_dfNoDataValue);
    1951         183 : }
    1952             : 
    1953             : /************************************************************************/
    1954             : /*                      WriteEncodedTileOrStrip()                       */
    1955             : /************************************************************************/
    1956             : 
    1957      228935 : CPLErr GTiffDataset::WriteEncodedTileOrStrip(uint32_t tile_or_strip, void *data,
    1958             :                                              int bPreserveDataBuffer)
    1959             : {
    1960      228935 :     CPLErr eErr = CE_None;
    1961             : 
    1962      228935 :     if (TIFFIsTiled(m_hTIFF))
    1963             :     {
    1964       50548 :         if (!(WriteEncodedTile(tile_or_strip, static_cast<GByte *>(data),
    1965             :                                bPreserveDataBuffer)))
    1966             :         {
    1967          14 :             eErr = CE_Failure;
    1968             :         }
    1969             :     }
    1970             :     else
    1971             :     {
    1972      178387 :         if (!(WriteEncodedStrip(tile_or_strip, static_cast<GByte *>(data),
    1973             :                                 bPreserveDataBuffer)))
    1974             :         {
    1975           8 :             eErr = CE_Failure;
    1976             :         }
    1977             :     }
    1978             : 
    1979      228935 :     return eErr;
    1980             : }
    1981             : 
    1982             : /************************************************************************/
    1983             : /*                           FlushBlockBuf()                            */
    1984             : /************************************************************************/
    1985             : 
    1986        9627 : CPLErr GTiffDataset::FlushBlockBuf()
    1987             : 
    1988             : {
    1989        9627 :     if (m_nLoadedBlock < 0 || !m_bLoadedBlockDirty)
    1990           0 :         return CE_None;
    1991             : 
    1992        9627 :     m_bLoadedBlockDirty = false;
    1993             : 
    1994             :     const CPLErr eErr =
    1995        9627 :         WriteEncodedTileOrStrip(m_nLoadedBlock, m_pabyBlockBuf, true);
    1996        9627 :     if (eErr != CE_None)
    1997             :     {
    1998           0 :         ReportError(CE_Failure, CPLE_AppDefined,
    1999             :                     "WriteEncodedTile/Strip() failed.");
    2000           0 :         m_bWriteError = true;
    2001             :     }
    2002             : 
    2003        9627 :     return eErr;
    2004             : }
    2005             : 
    2006             : /************************************************************************/
    2007             : /*                 GTiffFillStreamableOffsetAndCount()                  */
    2008             : /************************************************************************/
    2009             : 
    2010           8 : static void GTiffFillStreamableOffsetAndCount(TIFF *hTIFF, int nSize)
    2011             : {
    2012           8 :     uint32_t nXSize = 0;
    2013           8 :     uint32_t nYSize = 0;
    2014           8 :     TIFFGetField(hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize);
    2015           8 :     TIFFGetField(hTIFF, TIFFTAG_IMAGELENGTH, &nYSize);
    2016           8 :     const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(hTIFF));
    2017             :     const int nBlockCount =
    2018           8 :         bIsTiled ? TIFFNumberOfTiles(hTIFF) : TIFFNumberOfStrips(hTIFF);
    2019             : 
    2020           8 :     toff_t *panOffset = nullptr;
    2021           8 :     TIFFGetField(hTIFF, bIsTiled ? TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS,
    2022             :                  &panOffset);
    2023           8 :     toff_t *panSize = nullptr;
    2024           8 :     TIFFGetField(hTIFF,
    2025             :                  bIsTiled ? TIFFTAG_TILEBYTECOUNTS : TIFFTAG_STRIPBYTECOUNTS,
    2026             :                  &panSize);
    2027           8 :     toff_t nOffset = nSize;
    2028             :     // Trick to avoid clang static analyzer raising false positive about
    2029             :     // divide by zero later.
    2030           8 :     int nBlocksPerBand = 1;
    2031           8 :     uint32_t nRowsPerStrip = 0;
    2032           8 :     if (!bIsTiled)
    2033             :     {
    2034           6 :         TIFFGetField(hTIFF, TIFFTAG_ROWSPERSTRIP, &nRowsPerStrip);
    2035           6 :         if (nRowsPerStrip > static_cast<uint32_t>(nYSize))
    2036           0 :             nRowsPerStrip = nYSize;
    2037           6 :         nBlocksPerBand = DIV_ROUND_UP(nYSize, nRowsPerStrip);
    2038             :     }
    2039        2947 :     for (int i = 0; i < nBlockCount; ++i)
    2040             :     {
    2041             :         GPtrDiff_t cc = bIsTiled
    2042        2939 :                             ? static_cast<GPtrDiff_t>(TIFFTileSize(hTIFF))
    2043        2907 :                             : static_cast<GPtrDiff_t>(TIFFStripSize(hTIFF));
    2044        2939 :         if (!bIsTiled)
    2045             :         {
    2046             :             /* --------------------------------------------------------------------
    2047             :              */
    2048             :             /*      If this is the last strip in the image, and is partial, then
    2049             :              */
    2050             :             /*      we need to trim the number of scanlines written to the */
    2051             :             /*      amount of valid data we have. (#2748) */
    2052             :             /* --------------------------------------------------------------------
    2053             :              */
    2054        2907 :             int nStripWithinBand = i % nBlocksPerBand;
    2055        2907 :             if (nStripWithinBand * nRowsPerStrip > nYSize - nRowsPerStrip)
    2056             :             {
    2057           1 :                 cc = (cc / nRowsPerStrip) *
    2058           1 :                      (nYSize - nStripWithinBand * nRowsPerStrip);
    2059             :             }
    2060             :         }
    2061        2939 :         panOffset[i] = nOffset;
    2062        2939 :         panSize[i] = cc;
    2063        2939 :         nOffset += cc;
    2064             :     }
    2065           8 : }
    2066             : 
    2067             : /************************************************************************/
    2068             : /*                             Crystalize()                             */
    2069             : /*                                                                      */
    2070             : /*      Make sure that the directory information is written out for     */
    2071             : /*      a new file, require before writing any imagery data.            */
    2072             : /************************************************************************/
    2073             : 
    2074     2658890 : void GTiffDataset::Crystalize()
    2075             : 
    2076             : {
    2077     2658890 :     if (m_bCrystalized)
    2078     2653090 :         return;
    2079             : 
    2080             :     // TODO: libtiff writes extended tags in the order they are specified
    2081             :     // and not in increasing order.
    2082        5797 :     WriteMetadata(this, m_hTIFF, true, m_eProfile, m_osFilename.c_str(),
    2083        5797 :                   m_papszCreationOptions);
    2084        5797 :     WriteGeoTIFFInfo();
    2085        5797 :     if (m_bNoDataSet)
    2086         345 :         WriteNoDataValue(m_hTIFF, m_dfNoDataValue);
    2087        5452 :     else if (m_bNoDataSetAsInt64)
    2088           4 :         WriteNoDataValue(m_hTIFF, m_nNoDataValueInt64);
    2089        5448 :     else if (m_bNoDataSetAsUInt64)
    2090           4 :         WriteNoDataValue(m_hTIFF, m_nNoDataValueUInt64);
    2091             : 
    2092        5797 :     m_bMetadataChanged = false;
    2093        5797 :     m_bGeoTIFFInfoChanged = false;
    2094        5797 :     m_bNoDataChanged = false;
    2095        5797 :     m_bNeedsRewrite = false;
    2096             : 
    2097        5797 :     m_bCrystalized = true;
    2098             : 
    2099        5797 :     TIFFWriteCheck(m_hTIFF, TIFFIsTiled(m_hTIFF), "GTiffDataset::Crystalize");
    2100             : 
    2101        5797 :     TIFFWriteDirectory(m_hTIFF);
    2102        5797 :     if (m_bStreamingOut)
    2103             :     {
    2104             :         // We need to write twice the directory to be sure that custom
    2105             :         // TIFF tags are correctly sorted and that padding bytes have been
    2106             :         // added.
    2107           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2108           3 :         TIFFWriteDirectory(m_hTIFF);
    2109             : 
    2110           3 :         if (VSIFSeekL(m_fpL, 0, SEEK_END) != 0)
    2111             :         {
    2112           0 :             ReportError(CE_Failure, CPLE_FileIO, "Could not seek");
    2113             :         }
    2114           3 :         const int nSize = static_cast<int>(VSIFTellL(m_fpL));
    2115             : 
    2116           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2117           3 :         GTiffFillStreamableOffsetAndCount(m_hTIFF, nSize);
    2118           3 :         TIFFWriteDirectory(m_hTIFF);
    2119             : 
    2120           3 :         vsi_l_offset nDataLength = 0;
    2121             :         void *pabyBuffer =
    2122           3 :             VSIGetMemFileBuffer(m_pszTmpFilename, &nDataLength, FALSE);
    2123           3 :         if (static_cast<int>(VSIFWriteL(
    2124           3 :                 pabyBuffer, 1, static_cast<int>(nDataLength), m_fpToWrite)) !=
    2125             :             static_cast<int>(nDataLength))
    2126             :         {
    2127           0 :             ReportError(CE_Failure, CPLE_FileIO, "Could not write %d bytes",
    2128             :                         static_cast<int>(nDataLength));
    2129             :         }
    2130             :         // In case of single strip file, there's a libtiff check that would
    2131             :         // issue a warning since the file hasn't the required size.
    2132           3 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2133           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2134           3 :         CPLPopErrorHandler();
    2135             :     }
    2136             :     else
    2137             :     {
    2138        5794 :         const tdir_t nNumberOfDirs = TIFFNumberOfDirectories(m_hTIFF);
    2139        5794 :         if (nNumberOfDirs > 0)
    2140             :         {
    2141        5794 :             TIFFSetDirectory(m_hTIFF, static_cast<tdir_t>(nNumberOfDirs - 1));
    2142             :         }
    2143             :     }
    2144             : 
    2145        5797 :     RestoreVolatileParameters(m_hTIFF);
    2146             : 
    2147        5797 :     m_nDirOffset = TIFFCurrentDirOffset(m_hTIFF);
    2148             : }
    2149             : 
    2150             : /************************************************************************/
    2151             : /*                             FlushCache()                             */
    2152             : /*                                                                      */
    2153             : /*      We override this so we can also flush out local tiff strip      */
    2154             : /*      cache if need be.                                               */
    2155             : /************************************************************************/
    2156             : 
    2157        4607 : CPLErr GTiffDataset::FlushCache(bool bAtClosing)
    2158             : 
    2159             : {
    2160        4607 :     return FlushCacheInternal(bAtClosing, true);
    2161             : }
    2162             : 
    2163       46949 : CPLErr GTiffDataset::FlushCacheInternal(bool bAtClosing, bool bFlushDirectory)
    2164             : {
    2165       46949 :     if (m_bIsFinalized)
    2166           2 :         return CE_None;
    2167             : 
    2168       46947 :     CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
    2169             : 
    2170       46947 :     if (m_bLoadedBlockDirty && m_nLoadedBlock != -1)
    2171             :     {
    2172         288 :         if (FlushBlockBuf() != CE_None)
    2173           0 :             eErr = CE_Failure;
    2174             :     }
    2175             : 
    2176       46947 :     CPLFree(m_pabyBlockBuf);
    2177       46947 :     m_pabyBlockBuf = nullptr;
    2178       46947 :     m_nLoadedBlock = -1;
    2179       46947 :     m_bLoadedBlockDirty = false;
    2180             : 
    2181             :     // Finish compression
    2182       46947 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    2183       44562 :                               : m_poCompressQueue.get();
    2184       46947 :     if (poQueue)
    2185             :     {
    2186         161 :         poQueue->WaitCompletion();
    2187             : 
    2188             :         // Flush remaining data
    2189             :         // cppcheck-suppress constVariableReference
    2190             : 
    2191         161 :         auto &oQueue =
    2192         161 :             m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    2193         230 :         while (!oQueue.empty())
    2194             :         {
    2195          69 :             WaitCompletionForJobIdx(oQueue.front());
    2196             :         }
    2197             :     }
    2198             : 
    2199       46947 :     if (bFlushDirectory && GetAccess() == GA_Update)
    2200             :     {
    2201       14096 :         if (FlushDirectory() != CE_None)
    2202          12 :             eErr = CE_Failure;
    2203             :     }
    2204       46947 :     return eErr;
    2205             : }
    2206             : 
    2207             : /************************************************************************/
    2208             : /*                           FlushDirectory()                           */
    2209             : /************************************************************************/
    2210             : 
    2211       22164 : CPLErr GTiffDataset::FlushDirectory()
    2212             : 
    2213             : {
    2214       22164 :     CPLErr eErr = CE_None;
    2215             : 
    2216         688 :     const auto ReloadAllOtherDirectories = [this]()
    2217             :     {
    2218         339 :         const auto poBaseDS = m_poBaseDS ? m_poBaseDS : this;
    2219         342 :         for (auto &poOvrDS : poBaseDS->m_apoOverviewDS)
    2220             :         {
    2221           3 :             if (poOvrDS->m_bCrystalized && poOvrDS.get() != this)
    2222             :             {
    2223           3 :                 poOvrDS->ReloadDirectory(true);
    2224             :             }
    2225             : 
    2226           3 :             if (poOvrDS->m_poMaskDS && poOvrDS->m_poMaskDS.get() != this &&
    2227           0 :                 poOvrDS->m_poMaskDS->m_bCrystalized)
    2228             :             {
    2229           0 :                 poOvrDS->m_poMaskDS->ReloadDirectory(true);
    2230             :             }
    2231             :         }
    2232         339 :         if (poBaseDS->m_poMaskDS && poBaseDS->m_poMaskDS.get() != this &&
    2233           0 :             poBaseDS->m_poMaskDS->m_bCrystalized)
    2234             :         {
    2235           0 :             poBaseDS->m_poMaskDS->ReloadDirectory(true);
    2236             :         }
    2237         339 :         if (poBaseDS->m_bCrystalized && poBaseDS != this)
    2238             :         {
    2239           7 :             poBaseDS->ReloadDirectory(true);
    2240             :         }
    2241         339 :     };
    2242             : 
    2243       22164 :     if (eAccess == GA_Update)
    2244             :     {
    2245       15811 :         if (m_bMetadataChanged)
    2246             :         {
    2247         201 :             m_bNeedsRewrite =
    2248         201 :                 WriteMetadata(this, m_hTIFF, true, m_eProfile,
    2249         201 :                               m_osFilename.c_str(), m_papszCreationOptions);
    2250         201 :             m_bMetadataChanged = false;
    2251             : 
    2252         201 :             if (m_bForceUnsetRPC)
    2253             :             {
    2254           5 :                 double *padfRPCTag = nullptr;
    2255             :                 uint16_t nCount;
    2256           5 :                 if (TIFFGetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT, &nCount,
    2257           5 :                                  &padfRPCTag))
    2258             :                 {
    2259           3 :                     std::vector<double> zeroes(92);
    2260           3 :                     TIFFSetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT, 92,
    2261             :                                  zeroes.data());
    2262           3 :                     TIFFUnsetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT);
    2263           3 :                     m_bNeedsRewrite = true;
    2264             :                 }
    2265             : 
    2266           5 :                 if (m_poBaseDS == nullptr)
    2267             :                 {
    2268           5 :                     GDALWriteRPCTXTFile(m_osFilename.c_str(), nullptr);
    2269           5 :                     GDALWriteRPBFile(m_osFilename.c_str(), nullptr);
    2270             :                 }
    2271             :             }
    2272             :         }
    2273             : 
    2274       15811 :         if (m_bGeoTIFFInfoChanged)
    2275             :         {
    2276         146 :             WriteGeoTIFFInfo();
    2277         146 :             m_bGeoTIFFInfoChanged = false;
    2278             :         }
    2279             : 
    2280       15811 :         if (m_bNoDataChanged)
    2281             :         {
    2282          53 :             if (m_bNoDataSet)
    2283             :             {
    2284          37 :                 WriteNoDataValue(m_hTIFF, m_dfNoDataValue);
    2285             :             }
    2286          16 :             else if (m_bNoDataSetAsInt64)
    2287             :             {
    2288           0 :                 WriteNoDataValue(m_hTIFF, m_nNoDataValueInt64);
    2289             :             }
    2290          16 :             else if (m_bNoDataSetAsUInt64)
    2291             :             {
    2292           0 :                 WriteNoDataValue(m_hTIFF, m_nNoDataValueUInt64);
    2293             :             }
    2294             :             else
    2295             :             {
    2296          16 :                 UnsetNoDataValue(m_hTIFF);
    2297             :             }
    2298          53 :             m_bNeedsRewrite = true;
    2299          53 :             m_bNoDataChanged = false;
    2300             :         }
    2301             : 
    2302       15811 :         if (m_bNeedsRewrite)
    2303             :         {
    2304         364 :             if (!m_bCrystalized)
    2305             :             {
    2306          28 :                 Crystalize();
    2307             :             }
    2308             :             else
    2309             :             {
    2310         336 :                 const TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(m_hTIFF);
    2311             : 
    2312         336 :                 m_nDirOffset = pfnSizeProc(TIFFClientdata(m_hTIFF));
    2313         336 :                 if ((m_nDirOffset % 2) == 1)
    2314          72 :                     ++m_nDirOffset;
    2315             : 
    2316         336 :                 if (TIFFRewriteDirectory(m_hTIFF) == 0)
    2317           0 :                     eErr = CE_Failure;
    2318             : 
    2319         336 :                 TIFFSetSubDirectory(m_hTIFF, m_nDirOffset);
    2320             : 
    2321         336 :                 ReloadAllOtherDirectories();
    2322             : 
    2323         336 :                 if (m_bLayoutIFDSBeforeData && m_bBlockOrderRowMajor &&
    2324           2 :                     m_bLeaderSizeAsUInt4 &&
    2325           2 :                     m_bTrailerRepeatedLast4BytesRepeated &&
    2326           2 :                     !m_bKnownIncompatibleEdition &&
    2327           2 :                     !m_bWriteKnownIncompatibleEdition)
    2328             :                 {
    2329           2 :                     ReportError(CE_Warning, CPLE_AppDefined,
    2330             :                                 "The IFD has been rewritten at the end of "
    2331             :                                 "the file, which breaks COG layout.");
    2332           2 :                     m_bKnownIncompatibleEdition = true;
    2333           2 :                     m_bWriteKnownIncompatibleEdition = true;
    2334             :                 }
    2335             :             }
    2336             : 
    2337         364 :             m_bNeedsRewrite = false;
    2338             :         }
    2339             :     }
    2340             : 
    2341             :     // There are some circumstances in which we can reach this point
    2342             :     // without having made this our directory (SetDirectory()) in which
    2343             :     // case we should not risk a flush.
    2344       37975 :     if (GetAccess() == GA_Update &&
    2345       15811 :         TIFFCurrentDirOffset(m_hTIFF) == m_nDirOffset)
    2346             :     {
    2347       15811 :         const TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(m_hTIFF);
    2348             : 
    2349       15811 :         toff_t nNewDirOffset = pfnSizeProc(TIFFClientdata(m_hTIFF));
    2350       15811 :         if ((nNewDirOffset % 2) == 1)
    2351        3529 :             ++nNewDirOffset;
    2352             : 
    2353       15811 :         if (TIFFFlush(m_hTIFF) == 0)
    2354          12 :             eErr = CE_Failure;
    2355             : 
    2356       15811 :         if (m_nDirOffset != TIFFCurrentDirOffset(m_hTIFF))
    2357             :         {
    2358           3 :             m_nDirOffset = nNewDirOffset;
    2359           3 :             ReloadAllOtherDirectories();
    2360           3 :             CPLDebug("GTiff",
    2361             :                      "directory moved during flush in FlushDirectory()");
    2362             :         }
    2363             :     }
    2364             : 
    2365       22164 :     SetDirectory();
    2366       22164 :     return eErr;
    2367             : }
    2368             : 
    2369             : /************************************************************************/
    2370             : /*                           CleanOverviews()                           */
    2371             : /************************************************************************/
    2372             : 
    2373           5 : CPLErr GTiffDataset::CleanOverviews()
    2374             : 
    2375             : {
    2376           5 :     CPLAssert(!m_poBaseDS);
    2377             : 
    2378           5 :     ScanDirectories();
    2379             : 
    2380           5 :     FlushDirectory();
    2381             : 
    2382             :     /* -------------------------------------------------------------------- */
    2383             :     /*      Cleanup overviews objects, and get offsets to all overview      */
    2384             :     /*      directories.                                                    */
    2385             :     /* -------------------------------------------------------------------- */
    2386          10 :     std::vector<toff_t> anOvDirOffsets;
    2387             : 
    2388          10 :     for (auto &poOvrDS : m_apoOverviewDS)
    2389             :     {
    2390           5 :         anOvDirOffsets.push_back(poOvrDS->m_nDirOffset);
    2391           5 :         if (poOvrDS->m_poMaskDS)
    2392           1 :             anOvDirOffsets.push_back(poOvrDS->m_poMaskDS->m_nDirOffset);
    2393             :     }
    2394           5 :     m_apoOverviewDS.clear();
    2395             : 
    2396             :     /* -------------------------------------------------------------------- */
    2397             :     /*      Loop through all the directories, translating the offsets       */
    2398             :     /*      into indexes we can use with TIFFUnlinkDirectory().             */
    2399             :     /* -------------------------------------------------------------------- */
    2400          10 :     std::vector<uint16_t> anOvDirIndexes;
    2401           5 :     int iThisOffset = 1;
    2402             : 
    2403           5 :     TIFFSetDirectory(m_hTIFF, 0);
    2404             : 
    2405             :     while (true)
    2406             :     {
    2407          28 :         for (toff_t nOffset : anOvDirOffsets)
    2408             :         {
    2409          16 :             if (nOffset == TIFFCurrentDirOffset(m_hTIFF))
    2410             :             {
    2411           6 :                 anOvDirIndexes.push_back(static_cast<uint16_t>(iThisOffset));
    2412             :             }
    2413             :         }
    2414             : 
    2415          12 :         if (TIFFLastDirectory(m_hTIFF))
    2416           5 :             break;
    2417             : 
    2418           7 :         TIFFReadDirectory(m_hTIFF);
    2419           7 :         ++iThisOffset;
    2420           7 :     }
    2421             : 
    2422             :     /* -------------------------------------------------------------------- */
    2423             :     /*      Actually unlink the target directories.  Note that we do        */
    2424             :     /*      this from last to first so as to avoid renumbering any of       */
    2425             :     /*      the earlier directories we need to remove.                      */
    2426             :     /* -------------------------------------------------------------------- */
    2427          11 :     while (!anOvDirIndexes.empty())
    2428             :     {
    2429           6 :         TIFFUnlinkDirectory(m_hTIFF, anOvDirIndexes.back());
    2430           6 :         anOvDirIndexes.pop_back();
    2431             :     }
    2432             : 
    2433           5 :     if (m_poMaskDS)
    2434             :     {
    2435           1 :         m_poMaskDS->m_apoOverviewDS.clear();
    2436             :     }
    2437             : 
    2438           5 :     if (!SetDirectory())
    2439           0 :         return CE_Failure;
    2440             : 
    2441           5 :     return CE_None;
    2442             : }
    2443             : 
    2444             : /************************************************************************/
    2445             : /*                     RegisterNewOverviewDataset()                     */
    2446             : /************************************************************************/
    2447             : 
    2448         516 : CPLErr GTiffDataset::RegisterNewOverviewDataset(toff_t nOverviewOffset,
    2449             :                                                 int l_nJpegQuality,
    2450             :                                                 CSLConstList papszOptions)
    2451             : {
    2452             :     const auto GetOptionValue =
    2453        5676 :         [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
    2454       11351 :                        const char **ppszKeyUsed = nullptr)
    2455             :     {
    2456        5676 :         const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
    2457        5676 :         if (pszVal)
    2458             :         {
    2459           1 :             if (ppszKeyUsed)
    2460           1 :                 *ppszKeyUsed = pszOptionKey;
    2461           1 :             return pszVal;
    2462             :         }
    2463        5675 :         pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey);
    2464        5675 :         if (pszVal)
    2465             :         {
    2466           0 :             if (ppszKeyUsed)
    2467           0 :                 *ppszKeyUsed = pszConfigOptionKey;
    2468           0 :             return pszVal;
    2469             :         }
    2470        5675 :         if (pszConfigOptionKey)
    2471             :         {
    2472        5675 :             pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
    2473        5675 :             if (pszVal && ppszKeyUsed)
    2474          13 :                 *ppszKeyUsed = pszConfigOptionKey;
    2475             :         }
    2476        5675 :         return pszVal;
    2477         516 :     };
    2478             : 
    2479         516 :     int nZLevel = m_nZLevel;
    2480         516 :     if (const char *opt = GetOptionValue("ZLEVEL", "ZLEVEL_OVERVIEW"))
    2481             :     {
    2482           4 :         nZLevel = atoi(opt);
    2483             :     }
    2484             : 
    2485         516 :     int nZSTDLevel = m_nZSTDLevel;
    2486         516 :     if (const char *opt = GetOptionValue("ZSTD_LEVEL", "ZSTD_LEVEL_OVERVIEW"))
    2487             :     {
    2488           4 :         nZSTDLevel = atoi(opt);
    2489             :     }
    2490             : 
    2491         516 :     bool bWebpLossless = m_bWebPLossless;
    2492             :     const char *pszWebPLosslessOverview =
    2493         516 :         GetOptionValue("WEBP_LOSSLESS", "WEBP_LOSSLESS_OVERVIEW");
    2494         516 :     if (pszWebPLosslessOverview)
    2495             :     {
    2496           2 :         bWebpLossless = CPLTestBool(pszWebPLosslessOverview);
    2497             :     }
    2498             : 
    2499         516 :     int nWebpLevel = m_nWebPLevel;
    2500         516 :     const char *pszKeyWebpLevel = "";
    2501         516 :     if (const char *opt = GetOptionValue("WEBP_LEVEL", "WEBP_LEVEL_OVERVIEW",
    2502             :                                          &pszKeyWebpLevel))
    2503             :     {
    2504          14 :         if (pszWebPLosslessOverview == nullptr && m_bWebPLossless)
    2505             :         {
    2506           1 :             CPLDebug("GTiff",
    2507             :                      "%s specified, but not WEBP_LOSSLESS_OVERVIEW. "
    2508             :                      "Assuming WEBP_LOSSLESS_OVERVIEW=NO",
    2509             :                      pszKeyWebpLevel);
    2510           1 :             bWebpLossless = false;
    2511             :         }
    2512          13 :         else if (bWebpLossless)
    2513             :         {
    2514           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2515             :                      "%s is specified, but WEBP_LOSSLESS_OVERVIEW=YES. "
    2516             :                      "%s will be ignored.",
    2517             :                      pszKeyWebpLevel, pszKeyWebpLevel);
    2518             :         }
    2519          14 :         nWebpLevel = atoi(opt);
    2520             :     }
    2521             : 
    2522         516 :     double dfMaxZError = m_dfMaxZErrorOverview;
    2523         516 :     if (const char *opt = GetOptionValue("MAX_Z_ERROR", "MAX_Z_ERROR_OVERVIEW"))
    2524             :     {
    2525          20 :         dfMaxZError = CPLAtof(opt);
    2526             :     }
    2527             : 
    2528         516 :     signed char nJpegTablesMode = m_nJpegTablesMode;
    2529         516 :     if (const char *opt =
    2530         516 :             GetOptionValue("JPEG_TABLESMODE", "JPEG_TABLESMODE_OVERVIEW"))
    2531             :     {
    2532           0 :         nJpegTablesMode = static_cast<signed char>(atoi(opt));
    2533             :     }
    2534             : 
    2535             : #ifdef HAVE_JXL
    2536         516 :     bool bJXLLossless = m_bJXLLossless;
    2537         516 :     if (const char *opt =
    2538         516 :             GetOptionValue("JXL_LOSSLESS", "JXL_LOSSLESS_OVERVIEW"))
    2539             :     {
    2540           0 :         bJXLLossless = CPLTestBool(opt);
    2541             :     }
    2542             : 
    2543         516 :     float fJXLDistance = m_fJXLDistance;
    2544         516 :     if (const char *opt =
    2545         516 :             GetOptionValue("JXL_DISTANCE", "JXL_DISTANCE_OVERVIEW"))
    2546             :     {
    2547           0 :         fJXLDistance = static_cast<float>(CPLAtof(opt));
    2548             :     }
    2549             : 
    2550         516 :     float fJXLAlphaDistance = m_fJXLAlphaDistance;
    2551         516 :     if (const char *opt =
    2552         516 :             GetOptionValue("JXL_ALPHA_DISTANCE", "JXL_ALPHA_DISTANCE_OVERVIEW"))
    2553             :     {
    2554           0 :         fJXLAlphaDistance = static_cast<float>(CPLAtof(opt));
    2555             :     }
    2556             : 
    2557         516 :     int nJXLEffort = m_nJXLEffort;
    2558         516 :     if (const char *opt = GetOptionValue("JXL_EFFORT", "JXL_EFFORT_OVERVIEW"))
    2559             :     {
    2560           0 :         nJXLEffort = atoi(opt);
    2561             :     }
    2562             : #endif
    2563             : 
    2564        1032 :     auto poODS = std::make_shared<GTiffDataset>();
    2565         516 :     poODS->ShareLockWithParentDataset(this);
    2566         516 :     poODS->eAccess = GA_Update;
    2567         516 :     poODS->m_osFilename = m_osFilename;
    2568         516 :     const char *pszSparseOK = GetOptionValue("SPARSE_OK", "SPARSE_OK_OVERVIEW");
    2569         516 :     if (pszSparseOK && CPLTestBool(pszSparseOK))
    2570             :     {
    2571           1 :         poODS->m_bWriteEmptyTiles = false;
    2572           1 :         poODS->m_bFillEmptyTilesAtClosing = false;
    2573             :     }
    2574             :     else
    2575             :     {
    2576         515 :         poODS->m_bWriteEmptyTiles = m_bWriteEmptyTiles;
    2577         515 :         poODS->m_bFillEmptyTilesAtClosing = m_bFillEmptyTilesAtClosing;
    2578             :     }
    2579         516 :     poODS->m_nJpegQuality = static_cast<signed char>(l_nJpegQuality);
    2580         516 :     poODS->m_nWebPLevel = static_cast<signed char>(nWebpLevel);
    2581         516 :     poODS->m_nZLevel = static_cast<signed char>(nZLevel);
    2582         516 :     poODS->m_nLZMAPreset = m_nLZMAPreset;
    2583         516 :     poODS->m_nZSTDLevel = static_cast<signed char>(nZSTDLevel);
    2584         516 :     poODS->m_bWebPLossless = bWebpLossless;
    2585         516 :     poODS->m_nJpegTablesMode = nJpegTablesMode;
    2586         516 :     poODS->m_dfMaxZError = dfMaxZError;
    2587         516 :     poODS->m_dfMaxZErrorOverview = dfMaxZError;
    2588        1032 :     memcpy(poODS->m_anLercAddCompressionAndVersion,
    2589         516 :            m_anLercAddCompressionAndVersion,
    2590             :            sizeof(m_anLercAddCompressionAndVersion));
    2591             : #ifdef HAVE_JXL
    2592         516 :     poODS->m_bJXLLossless = bJXLLossless;
    2593         516 :     poODS->m_fJXLDistance = fJXLDistance;
    2594         516 :     poODS->m_fJXLAlphaDistance = fJXLAlphaDistance;
    2595         516 :     poODS->m_nJXLEffort = nJXLEffort;
    2596             : #endif
    2597             : 
    2598         516 :     if (poODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nOverviewOffset,
    2599         516 :                           GA_Update) != CE_None)
    2600             :     {
    2601           0 :         return CE_Failure;
    2602             :     }
    2603             : 
    2604             :     // Assign color interpretation from main dataset
    2605         516 :     const int l_nBands = GetRasterCount();
    2606        1537 :     for (int i = 1; i <= l_nBands; i++)
    2607             :     {
    2608        1021 :         auto poBand = dynamic_cast<GTiffRasterBand *>(poODS->GetRasterBand(i));
    2609        1021 :         if (poBand)
    2610        1021 :             poBand->m_eBandInterp = GetRasterBand(i)->GetColorInterpretation();
    2611             :     }
    2612             : 
    2613             :     // Do that now that m_nCompression is set
    2614         516 :     poODS->RestoreVolatileParameters(poODS->m_hTIFF);
    2615             : 
    2616         516 :     poODS->m_poBaseDS = this;
    2617         516 :     poODS->m_bIsOverview = true;
    2618             : 
    2619         516 :     m_apoOverviewDS.push_back(std::move(poODS));
    2620         516 :     return CE_None;
    2621             : }
    2622             : 
    2623             : /************************************************************************/
    2624             : /*                        CreateTIFFColorTable()                        */
    2625             : /************************************************************************/
    2626             : 
    2627          12 : static void CreateTIFFColorTable(
    2628             :     GDALColorTable *poColorTable, int nBits, int nColorTableMultiplier,
    2629             :     std::vector<unsigned short> &anTRed, std::vector<unsigned short> &anTGreen,
    2630             :     std::vector<unsigned short> &anTBlue, unsigned short *&panRed,
    2631             :     unsigned short *&panGreen, unsigned short *&panBlue)
    2632             : {
    2633             :     int nColors;
    2634             : 
    2635          12 :     if (nBits == 8)
    2636          12 :         nColors = 256;
    2637           0 :     else if (nBits < 8)
    2638           0 :         nColors = 1 << nBits;
    2639             :     else
    2640           0 :         nColors = 65536;
    2641             : 
    2642          12 :     anTRed.resize(nColors, 0);
    2643          12 :     anTGreen.resize(nColors, 0);
    2644          12 :     anTBlue.resize(nColors, 0);
    2645             : 
    2646        3084 :     for (int iColor = 0; iColor < nColors; ++iColor)
    2647             :     {
    2648        3072 :         if (iColor < poColorTable->GetColorEntryCount())
    2649             :         {
    2650             :             GDALColorEntry sRGB;
    2651             : 
    2652        3072 :             poColorTable->GetColorEntryAsRGB(iColor, &sRGB);
    2653             : 
    2654        3072 :             anTRed[iColor] = GTiffDataset::ClampCTEntry(iColor, 1, sRGB.c1,
    2655             :                                                         nColorTableMultiplier);
    2656        3072 :             anTGreen[iColor] = GTiffDataset::ClampCTEntry(
    2657        3072 :                 iColor, 2, sRGB.c2, nColorTableMultiplier);
    2658        3072 :             anTBlue[iColor] = GTiffDataset::ClampCTEntry(iColor, 3, sRGB.c3,
    2659             :                                                          nColorTableMultiplier);
    2660             :         }
    2661             :         else
    2662             :         {
    2663           0 :             anTRed[iColor] = 0;
    2664           0 :             anTGreen[iColor] = 0;
    2665           0 :             anTBlue[iColor] = 0;
    2666             :         }
    2667             :     }
    2668             : 
    2669          12 :     panRed = &(anTRed[0]);
    2670          12 :     panGreen = &(anTGreen[0]);
    2671          12 :     panBlue = &(anTBlue[0]);
    2672          12 : }
    2673             : 
    2674             : /************************************************************************/
    2675             : /*                       GetOverviewParameters()                        */
    2676             : /************************************************************************/
    2677             : 
    2678         331 : bool GTiffDataset::GetOverviewParameters(
    2679             :     int &nCompression, uint16_t &nPlanarConfig, uint16_t &nPredictor,
    2680             :     uint16_t &nPhotometric, int &nOvrJpegQuality, std::string &osNoData,
    2681             :     uint16_t *&panExtraSampleValues, uint16_t &nExtraSamples,
    2682             :     CSLConstList papszOptions) const
    2683             : {
    2684             :     const auto GetOptionValue =
    2685        1098 :         [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
    2686        2188 :                        const char **ppszKeyUsed = nullptr)
    2687             :     {
    2688        1098 :         const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
    2689        1098 :         if (pszVal)
    2690             :         {
    2691           8 :             if (ppszKeyUsed)
    2692           8 :                 *ppszKeyUsed = pszOptionKey;
    2693           8 :             return pszVal;
    2694             :         }
    2695        1090 :         pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey);
    2696        1090 :         if (pszVal)
    2697             :         {
    2698           0 :             if (ppszKeyUsed)
    2699           0 :                 *ppszKeyUsed = pszConfigOptionKey;
    2700           0 :             return pszVal;
    2701             :         }
    2702        1090 :         pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
    2703        1090 :         if (pszVal && ppszKeyUsed)
    2704          60 :             *ppszKeyUsed = pszConfigOptionKey;
    2705        1090 :         return pszVal;
    2706         331 :     };
    2707             : 
    2708             :     /* -------------------------------------------------------------------- */
    2709             :     /*      Determine compression method.                                   */
    2710             :     /* -------------------------------------------------------------------- */
    2711         331 :     nCompression = m_nCompression;
    2712         331 :     const char *pszOptionKey = "";
    2713             :     const char *pszCompressValue =
    2714         331 :         GetOptionValue("COMPRESS", "COMPRESS_OVERVIEW", &pszOptionKey);
    2715         331 :     if (pszCompressValue != nullptr)
    2716             :     {
    2717          58 :         nCompression =
    2718          58 :             GTIFFGetCompressionMethod(pszCompressValue, pszOptionKey);
    2719          58 :         if (nCompression < 0)
    2720             :         {
    2721           0 :             nCompression = m_nCompression;
    2722             :         }
    2723             :     }
    2724             : 
    2725             :     /* -------------------------------------------------------------------- */
    2726             :     /*      Determine planar configuration.                                 */
    2727             :     /* -------------------------------------------------------------------- */
    2728         331 :     nPlanarConfig = m_nPlanarConfig;
    2729         331 :     if (nCompression == COMPRESSION_WEBP)
    2730             :     {
    2731          11 :         nPlanarConfig = PLANARCONFIG_CONTIG;
    2732             :     }
    2733             :     const char *pszInterleave =
    2734         331 :         GetOptionValue(GDALMD_INTERLEAVE, "INTERLEAVE_OVERVIEW", &pszOptionKey);
    2735         331 :     if (pszInterleave != nullptr && pszInterleave[0] != '\0')
    2736             :     {
    2737           2 :         if (EQUAL(pszInterleave, "PIXEL"))
    2738           1 :             nPlanarConfig = PLANARCONFIG_CONTIG;
    2739           1 :         else if (EQUAL(pszInterleave, "BAND"))
    2740           1 :             nPlanarConfig = PLANARCONFIG_SEPARATE;
    2741             :         else
    2742             :         {
    2743           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2744             :                      "%s=%s unsupported, "
    2745             :                      "value must be PIXEL or BAND. ignoring",
    2746             :                      pszOptionKey, pszInterleave);
    2747             :         }
    2748             :     }
    2749             : 
    2750             :     /* -------------------------------------------------------------------- */
    2751             :     /*      Determine predictor tag                                         */
    2752             :     /* -------------------------------------------------------------------- */
    2753         331 :     nPredictor = PREDICTOR_NONE;
    2754         331 :     if (GTIFFSupportsPredictor(nCompression))
    2755             :     {
    2756             :         const char *pszPredictor =
    2757          78 :             GetOptionValue("PREDICTOR", "PREDICTOR_OVERVIEW");
    2758          78 :         if (pszPredictor != nullptr)
    2759             :         {
    2760           1 :             nPredictor = static_cast<uint16_t>(atoi(pszPredictor));
    2761             :         }
    2762          77 :         else if (GTIFFSupportsPredictor(m_nCompression))
    2763          76 :             TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &nPredictor);
    2764             :     }
    2765             : 
    2766             :     /* -------------------------------------------------------------------- */
    2767             :     /*      Determine photometric tag                                       */
    2768             :     /* -------------------------------------------------------------------- */
    2769         331 :     if (m_nPhotometric == PHOTOMETRIC_YCBCR && nCompression != COMPRESSION_JPEG)
    2770           1 :         nPhotometric = PHOTOMETRIC_RGB;
    2771             :     else
    2772         330 :         nPhotometric = m_nPhotometric;
    2773             :     const char *pszPhotometric =
    2774         331 :         GetOptionValue("PHOTOMETRIC", "PHOTOMETRIC_OVERVIEW", &pszOptionKey);
    2775         331 :     if (!GTIFFUpdatePhotometric(pszPhotometric, pszOptionKey, nCompression,
    2776         331 :                                 pszInterleave, nBands, nPhotometric,
    2777             :                                 nPlanarConfig))
    2778             :     {
    2779           0 :         return false;
    2780             :     }
    2781             : 
    2782             :     /* -------------------------------------------------------------------- */
    2783             :     /*      Determine JPEG quality                                          */
    2784             :     /* -------------------------------------------------------------------- */
    2785         331 :     nOvrJpegQuality = m_nJpegQuality;
    2786         331 :     if (nCompression == COMPRESSION_JPEG)
    2787             :     {
    2788             :         const char *pszJPEGQuality =
    2789          27 :             GetOptionValue("JPEG_QUALITY", "JPEG_QUALITY_OVERVIEW");
    2790          27 :         if (pszJPEGQuality != nullptr)
    2791             :         {
    2792           9 :             nOvrJpegQuality = atoi(pszJPEGQuality);
    2793             :         }
    2794             :     }
    2795             : 
    2796             :     /* -------------------------------------------------------------------- */
    2797             :     /*      Set nodata.                                                     */
    2798             :     /* -------------------------------------------------------------------- */
    2799         331 :     if (m_bNoDataSet)
    2800             :     {
    2801          17 :         osNoData = GTiffFormatGDALNoDataTagValue(m_dfNoDataValue);
    2802             :     }
    2803             : 
    2804             :     /* -------------------------------------------------------------------- */
    2805             :     /*      Fetch extra sample tag                                          */
    2806             :     /* -------------------------------------------------------------------- */
    2807         331 :     panExtraSampleValues = nullptr;
    2808         331 :     nExtraSamples = 0;
    2809         331 :     if (TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples,
    2810         331 :                      &panExtraSampleValues))
    2811             :     {
    2812             :         uint16_t *panExtraSampleValuesNew = static_cast<uint16_t *>(
    2813          41 :             CPLMalloc(nExtraSamples * sizeof(uint16_t)));
    2814          41 :         memcpy(panExtraSampleValuesNew, panExtraSampleValues,
    2815          41 :                nExtraSamples * sizeof(uint16_t));
    2816          41 :         panExtraSampleValues = panExtraSampleValuesNew;
    2817             :     }
    2818             :     else
    2819             :     {
    2820         290 :         panExtraSampleValues = nullptr;
    2821         290 :         nExtraSamples = 0;
    2822             :     }
    2823             : 
    2824         331 :     return true;
    2825             : }
    2826             : 
    2827             : /************************************************************************/
    2828             : /*                  CreateOverviewsFromSrcOverviews()                   */
    2829             : /************************************************************************/
    2830             : 
    2831             : // If poOvrDS is not null, it is used and poSrcDS is ignored.
    2832             : 
    2833          71 : CPLErr GTiffDataset::CreateOverviewsFromSrcOverviews(GDALDataset *poSrcDS,
    2834             :                                                      GDALDataset *poOvrDS,
    2835             :                                                      int nOverviews)
    2836             : {
    2837          71 :     CPLAssert(poSrcDS->GetRasterCount() != 0);
    2838          71 :     CPLAssert(m_apoOverviewDS.empty());
    2839             : 
    2840          71 :     ScanDirectories();
    2841             : 
    2842          71 :     FlushDirectory();
    2843             : 
    2844          71 :     int nOvBitsPerSample = m_nBitsPerSample;
    2845             : 
    2846             :     /* -------------------------------------------------------------------- */
    2847             :     /*      Do we need some metadata for the overviews?                     */
    2848             :     /* -------------------------------------------------------------------- */
    2849         142 :     CPLString osMetadata;
    2850             : 
    2851          71 :     GTIFFBuildOverviewMetadata("NONE", this, false, osMetadata);
    2852             : 
    2853             :     int nCompression;
    2854             :     uint16_t nPlanarConfig;
    2855             :     uint16_t nPredictor;
    2856             :     uint16_t nPhotometric;
    2857             :     int nOvrJpegQuality;
    2858         142 :     std::string osNoData;
    2859          71 :     uint16_t *panExtraSampleValues = nullptr;
    2860          71 :     uint16_t nExtraSamples = 0;
    2861          71 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    2862             :                                nPhotometric, nOvrJpegQuality, osNoData,
    2863             :                                panExtraSampleValues, nExtraSamples,
    2864             :                                /*papszOptions=*/nullptr))
    2865             :     {
    2866           0 :         return CE_Failure;
    2867             :     }
    2868             : 
    2869             :     /* -------------------------------------------------------------------- */
    2870             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    2871             :     /* -------------------------------------------------------------------- */
    2872         142 :     std::vector<unsigned short> anTRed;
    2873         142 :     std::vector<unsigned short> anTGreen;
    2874          71 :     std::vector<unsigned short> anTBlue;
    2875          71 :     unsigned short *panRed = nullptr;
    2876          71 :     unsigned short *panGreen = nullptr;
    2877          71 :     unsigned short *panBlue = nullptr;
    2878             : 
    2879          71 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    2880             :     {
    2881           0 :         if (m_nColorTableMultiplier == 0)
    2882           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    2883             : 
    2884           0 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    2885             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    2886             :                              panRed, panGreen, panBlue);
    2887             :     }
    2888             : 
    2889          71 :     int nOvrBlockXSize = 0;
    2890          71 :     int nOvrBlockYSize = 0;
    2891          71 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    2892             :                               &nOvrBlockXSize, &nOvrBlockYSize, nullptr,
    2893             :                               nullptr);
    2894             : 
    2895          71 :     CPLErr eErr = CE_None;
    2896             : 
    2897         202 :     for (int i = 0; i < nOverviews && eErr == CE_None; ++i)
    2898             :     {
    2899             :         GDALRasterBand *poOvrBand =
    2900         171 :             poOvrDS ? ((i == 0) ? poOvrDS->GetRasterBand(1)
    2901          40 :                                 : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    2902          55 :                     : poSrcDS->GetRasterBand(1)->GetOverview(i);
    2903             : 
    2904         131 :         int nOXSize = poOvrBand->GetXSize();
    2905         131 :         int nOYSize = poOvrBand->GetYSize();
    2906             : 
    2907         262 :         toff_t nOverviewOffset = GTIFFWriteDirectory(
    2908             :             m_hTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize, nOvBitsPerSample,
    2909         131 :             nPlanarConfig, m_nSamplesPerPixel, nOvrBlockXSize, nOvrBlockYSize,
    2910         131 :             TRUE, nCompression, nPhotometric, m_nSampleFormat, nPredictor,
    2911             :             panRed, panGreen, panBlue, nExtraSamples, panExtraSampleValues,
    2912             :             osMetadata,
    2913         131 :             nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality) : nullptr,
    2914         131 :             CPLSPrintf("%d", m_nJpegTablesMode),
    2915           2 :             osNoData.empty() ? nullptr : osNoData.c_str(),
    2916         131 :             m_anLercAddCompressionAndVersion, m_bWriteCOGLayout);
    2917             : 
    2918         131 :         if (nOverviewOffset == 0)
    2919           0 :             eErr = CE_Failure;
    2920             :         else
    2921         131 :             eErr = RegisterNewOverviewDataset(nOverviewOffset, nOvrJpegQuality,
    2922             :                                               nullptr);
    2923             :     }
    2924             : 
    2925             :     // For directory reloading, so that the chaining to the next directory is
    2926             :     // reloaded, as well as compression parameters.
    2927          71 :     ReloadDirectory();
    2928             : 
    2929          71 :     CPLFree(panExtraSampleValues);
    2930          71 :     panExtraSampleValues = nullptr;
    2931             : 
    2932          71 :     return eErr;
    2933             : }
    2934             : 
    2935             : /************************************************************************/
    2936             : /*                    CreateInternalMaskOverviews()                     */
    2937             : /************************************************************************/
    2938             : 
    2939         274 : CPLErr GTiffDataset::CreateInternalMaskOverviews(int nOvrBlockXSize,
    2940             :                                                  int nOvrBlockYSize)
    2941             : {
    2942         274 :     ScanDirectories();
    2943             : 
    2944             :     /* -------------------------------------------------------------------- */
    2945             :     /*      Create overviews for the mask.                                  */
    2946             :     /* -------------------------------------------------------------------- */
    2947         274 :     CPLErr eErr = CE_None;
    2948             : 
    2949         274 :     if (m_poMaskDS != nullptr && m_poMaskDS->GetRasterCount() == 1)
    2950             :     {
    2951             :         int nMaskOvrCompression;
    2952          42 :         if (strstr(GDALGetMetadataItem(GDALGetDriverByName("GTiff"),
    2953             :                                        GDAL_DMD_CREATIONOPTIONLIST, nullptr),
    2954          42 :                    "<Value>DEFLATE</Value>") != nullptr)
    2955          42 :             nMaskOvrCompression = COMPRESSION_ADOBE_DEFLATE;
    2956             :         else
    2957           0 :             nMaskOvrCompression = COMPRESSION_PACKBITS;
    2958             : 
    2959         113 :         for (auto &poOvrDS : m_apoOverviewDS)
    2960             :         {
    2961          71 :             if (poOvrDS->m_poMaskDS == nullptr)
    2962             :             {
    2963          59 :                 const toff_t nOverviewOffset = GTIFFWriteDirectory(
    2964             :                     m_hTIFF, FILETYPE_REDUCEDIMAGE | FILETYPE_MASK,
    2965          59 :                     poOvrDS->nRasterXSize, poOvrDS->nRasterYSize, 1,
    2966             :                     PLANARCONFIG_CONTIG, 1, nOvrBlockXSize, nOvrBlockYSize,
    2967             :                     TRUE, nMaskOvrCompression, PHOTOMETRIC_MASK,
    2968             :                     SAMPLEFORMAT_UINT, PREDICTOR_NONE, nullptr, nullptr,
    2969             :                     nullptr, 0, nullptr, "", nullptr, nullptr, nullptr, nullptr,
    2970          59 :                     m_bWriteCOGLayout);
    2971             : 
    2972          59 :                 if (nOverviewOffset == 0)
    2973             :                 {
    2974           0 :                     eErr = CE_Failure;
    2975           0 :                     continue;
    2976             :                 }
    2977             : 
    2978         118 :                 auto poMaskODS = std::make_shared<GTiffDataset>();
    2979          59 :                 poMaskODS->eAccess = GA_Update;
    2980          59 :                 poMaskODS->ShareLockWithParentDataset(this);
    2981          59 :                 poMaskODS->m_osFilename = m_osFilename;
    2982          59 :                 if (poMaskODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF),
    2983             :                                           nOverviewOffset,
    2984          59 :                                           GA_Update) != CE_None)
    2985             :                 {
    2986           0 :                     eErr = CE_Failure;
    2987             :                 }
    2988             :                 else
    2989             :                 {
    2990         118 :                     poMaskODS->m_bPromoteTo8Bits =
    2991          59 :                         CPLTestBool(CPLGetConfigOption(
    2992             :                             "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
    2993          59 :                     poMaskODS->m_poBaseDS = this;
    2994          59 :                     poMaskODS->m_poImageryDS = poOvrDS.get();
    2995          59 :                     poOvrDS->m_poMaskDS = poMaskODS;
    2996          59 :                     m_poMaskDS->m_apoOverviewDS.push_back(std::move(poMaskODS));
    2997             :                 }
    2998             :             }
    2999             :         }
    3000             :     }
    3001             : 
    3002         274 :     ReloadDirectory();
    3003             : 
    3004         274 :     return eErr;
    3005             : }
    3006             : 
    3007             : /************************************************************************/
    3008             : /*                            AddOverviews()                            */
    3009             : /************************************************************************/
    3010             : 
    3011             : CPLErr
    3012          13 : GTiffDataset::AddOverviews(const std::vector<GDALDataset *> &apoSrcOvrDSIn,
    3013             :                            GDALProgressFunc pfnProgress, void *pProgressData,
    3014             :                            CSLConstList papszOptions)
    3015             : {
    3016             :     /* -------------------------------------------------------------------- */
    3017             :     /*      If we don't have read access, then create the overviews         */
    3018             :     /*      externally.                                                     */
    3019             :     /* -------------------------------------------------------------------- */
    3020          13 :     if (GetAccess() != GA_Update)
    3021             :     {
    3022           4 :         CPLDebug("GTiff", "File open for read-only accessing, "
    3023             :                           "creating overviews externally.");
    3024             : 
    3025           4 :         CPLErr eErr = GDALDataset::AddOverviews(apoSrcOvrDSIn, pfnProgress,
    3026             :                                                 pProgressData, papszOptions);
    3027           4 :         if (eErr == CE_None && m_poMaskDS)
    3028             :         {
    3029           0 :             ReportError(
    3030             :                 CE_Warning, CPLE_NotSupported,
    3031             :                 "Building external overviews whereas there is an internal "
    3032             :                 "mask is not fully supported. "
    3033             :                 "The overviews of the non-mask bands will be created, "
    3034             :                 "but not the overviews of the mask band.");
    3035             :         }
    3036           4 :         return eErr;
    3037             :     }
    3038             : 
    3039          18 :     std::vector<GDALDataset *> apoSrcOvrDS = apoSrcOvrDSIn;
    3040             :     // Sort overviews by descending size
    3041           9 :     std::sort(apoSrcOvrDS.begin(), apoSrcOvrDS.end(),
    3042           0 :               [](const GDALDataset *poDS1, const GDALDataset *poDS2)
    3043           0 :               { return poDS1->GetRasterXSize() > poDS2->GetRasterXSize(); });
    3044             : 
    3045           9 :     if (!GDALDefaultOverviews::CheckSrcOverviewsConsistencyWithBase(
    3046             :             this, apoSrcOvrDS))
    3047           5 :         return CE_Failure;
    3048             : 
    3049           4 :     ScanDirectories();
    3050             : 
    3051             :     // Make implicit JPEG overviews invisible, but do not destroy
    3052             :     // them in case they are already used (not sure that the client
    3053             :     // has the right to do that). Behavior maybe undefined in GDAL API.
    3054           4 :     std::swap(m_apoJPEGOverviewDSOld, m_apoJPEGOverviewDS);
    3055           4 :     m_apoJPEGOverviewDS.clear();
    3056             : 
    3057           4 :     FlushDirectory();
    3058             : 
    3059             :     /* -------------------------------------------------------------------- */
    3060             :     /*      If we are averaging bit data to grayscale we need to create     */
    3061             :     /*      8bit overviews.                                                 */
    3062             :     /* -------------------------------------------------------------------- */
    3063           4 :     int nOvBitsPerSample = m_nBitsPerSample;
    3064             : 
    3065             :     /* -------------------------------------------------------------------- */
    3066             :     /*      Do we need some metadata for the overviews?                     */
    3067             :     /* -------------------------------------------------------------------- */
    3068           8 :     CPLString osMetadata;
    3069             : 
    3070           4 :     const bool bIsForMaskBand = nBands == 1 && GetRasterBand(1)->IsMaskBand();
    3071           4 :     GTIFFBuildOverviewMetadata(/* resampling = */ "", this, bIsForMaskBand,
    3072             :                                osMetadata);
    3073             : 
    3074             :     int nCompression;
    3075             :     uint16_t nPlanarConfig;
    3076             :     uint16_t nPredictor;
    3077             :     uint16_t nPhotometric;
    3078             :     int nOvrJpegQuality;
    3079           8 :     std::string osNoData;
    3080           4 :     uint16_t *panExtraSampleValues = nullptr;
    3081           4 :     uint16_t nExtraSamples = 0;
    3082           4 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    3083             :                                nPhotometric, nOvrJpegQuality, osNoData,
    3084             :                                panExtraSampleValues, nExtraSamples,
    3085             :                                papszOptions))
    3086             :     {
    3087           0 :         return CE_Failure;
    3088             :     }
    3089             : 
    3090             :     /* -------------------------------------------------------------------- */
    3091             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    3092             :     /* -------------------------------------------------------------------- */
    3093           8 :     std::vector<unsigned short> anTRed;
    3094           8 :     std::vector<unsigned short> anTGreen;
    3095           4 :     std::vector<unsigned short> anTBlue;
    3096           4 :     unsigned short *panRed = nullptr;
    3097           4 :     unsigned short *panGreen = nullptr;
    3098           4 :     unsigned short *panBlue = nullptr;
    3099             : 
    3100           4 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    3101             :     {
    3102           0 :         if (m_nColorTableMultiplier == 0)
    3103           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    3104             : 
    3105           0 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    3106             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    3107             :                              panRed, panGreen, panBlue);
    3108             :     }
    3109             : 
    3110             :     /* -------------------------------------------------------------------- */
    3111             :     /*      Establish which of the overview levels we already have, and     */
    3112             :     /*      which are new.  We assume that band 1 of the file is            */
    3113             :     /*      representative.                                                 */
    3114             :     /* -------------------------------------------------------------------- */
    3115           4 :     int nOvrBlockXSize = 0;
    3116           4 :     int nOvrBlockYSize = 0;
    3117           4 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    3118             :                               &nOvrBlockXSize, &nOvrBlockYSize, papszOptions,
    3119             :                               "BLOCKSIZE");
    3120             : 
    3121           4 :     CPLErr eErr = CE_None;
    3122           8 :     for (const auto *poSrcOvrDS : apoSrcOvrDS)
    3123             :     {
    3124           4 :         bool bFound = false;
    3125           4 :         for (auto &poOvrDS : m_apoOverviewDS)
    3126             :         {
    3127           4 :             if (poOvrDS->GetRasterXSize() == poSrcOvrDS->GetRasterXSize() &&
    3128           2 :                 poOvrDS->GetRasterYSize() == poSrcOvrDS->GetRasterYSize())
    3129             :             {
    3130           2 :                 bFound = true;
    3131           2 :                 break;
    3132             :             }
    3133             :         }
    3134           4 :         if (!bFound && eErr == CE_None)
    3135             :         {
    3136           2 :             if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    3137           0 :                 !m_bWriteKnownIncompatibleEdition)
    3138             :             {
    3139           0 :                 ReportError(CE_Warning, CPLE_AppDefined,
    3140             :                             "Adding new overviews invalidates the "
    3141             :                             "LAYOUT=IFDS_BEFORE_DATA property");
    3142           0 :                 m_bKnownIncompatibleEdition = true;
    3143           0 :                 m_bWriteKnownIncompatibleEdition = true;
    3144             :             }
    3145             : 
    3146           6 :             const toff_t nOverviewOffset = GTIFFWriteDirectory(
    3147             :                 m_hTIFF, FILETYPE_REDUCEDIMAGE, poSrcOvrDS->GetRasterXSize(),
    3148             :                 poSrcOvrDS->GetRasterYSize(), nOvBitsPerSample, nPlanarConfig,
    3149           2 :                 m_nSamplesPerPixel, nOvrBlockXSize, nOvrBlockYSize, TRUE,
    3150           2 :                 nCompression, nPhotometric, m_nSampleFormat, nPredictor, panRed,
    3151             :                 panGreen, panBlue, nExtraSamples, panExtraSampleValues,
    3152             :                 osMetadata,
    3153           2 :                 nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality)
    3154             :                                      : nullptr,
    3155           2 :                 CPLSPrintf("%d", m_nJpegTablesMode),
    3156           0 :                 osNoData.empty() ? nullptr : osNoData.c_str(),
    3157           2 :                 m_anLercAddCompressionAndVersion, false);
    3158             : 
    3159           2 :             if (nOverviewOffset == 0)
    3160           0 :                 eErr = CE_Failure;
    3161             :             else
    3162           2 :                 eErr = RegisterNewOverviewDataset(
    3163             :                     nOverviewOffset, nOvrJpegQuality, papszOptions);
    3164             :         }
    3165             :     }
    3166             : 
    3167           4 :     CPLFree(panExtraSampleValues);
    3168           4 :     panExtraSampleValues = nullptr;
    3169             : 
    3170           4 :     ReloadDirectory();
    3171             : 
    3172           4 :     if (!pfnProgress)
    3173           2 :         pfnProgress = GDALDummyProgress;
    3174             : 
    3175             :     // almost 0, but not 0 to please Coverity Scan
    3176           4 :     double dfTotalPixels = std::numeric_limits<double>::min();
    3177           8 :     for (const auto *poSrcOvrDS : apoSrcOvrDS)
    3178             :     {
    3179           4 :         dfTotalPixels += static_cast<double>(poSrcOvrDS->GetRasterXSize()) *
    3180           4 :                          poSrcOvrDS->GetRasterYSize();
    3181             :     }
    3182             : 
    3183             :     // Copy source datasets into target overview datasets
    3184           4 :     double dfCurPixels = 0;
    3185           8 :     for (auto *poSrcOvrDS : apoSrcOvrDS)
    3186             :     {
    3187           4 :         GDALDataset *poDstOvrDS = nullptr;
    3188           4 :         for (auto &poOvrDS : m_apoOverviewDS)
    3189             :         {
    3190           8 :             if (poOvrDS->GetRasterXSize() == poSrcOvrDS->GetRasterXSize() &&
    3191           4 :                 poOvrDS->GetRasterYSize() == poSrcOvrDS->GetRasterYSize())
    3192             :             {
    3193           4 :                 poDstOvrDS = poOvrDS.get();
    3194           4 :                 break;
    3195             :             }
    3196             :         }
    3197           4 :         if (eErr == CE_None && poDstOvrDS)
    3198             :         {
    3199             :             const double dfThisPixels =
    3200           4 :                 static_cast<double>(poSrcOvrDS->GetRasterXSize()) *
    3201           4 :                 poSrcOvrDS->GetRasterYSize();
    3202           8 :             void *pScaledProgressData = GDALCreateScaledProgress(
    3203             :                 dfCurPixels / dfTotalPixels,
    3204           4 :                 (dfCurPixels + dfThisPixels) / dfTotalPixels, pfnProgress,
    3205             :                 pProgressData);
    3206           4 :             dfCurPixels += dfThisPixels;
    3207           4 :             eErr = GDALDatasetCopyWholeRaster(GDALDataset::ToHandle(poSrcOvrDS),
    3208             :                                               GDALDataset::ToHandle(poDstOvrDS),
    3209             :                                               nullptr, GDALScaledProgress,
    3210             :                                               pScaledProgressData);
    3211           4 :             GDALDestroyScaledProgress(pScaledProgressData);
    3212             :         }
    3213             :     }
    3214             : 
    3215           4 :     return eErr;
    3216             : }
    3217             : 
    3218             : /************************************************************************/
    3219             : /*                          IBuildOverviews()                           */
    3220             : /************************************************************************/
    3221             : 
    3222         412 : CPLErr GTiffDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
    3223             :                                      const int *panOverviewList, int nBandsIn,
    3224             :                                      const int *panBandList,
    3225             :                                      GDALProgressFunc pfnProgress,
    3226             :                                      void *pProgressData,
    3227             :                                      CSLConstList papszOptions)
    3228             : 
    3229             : {
    3230         412 :     ScanDirectories();
    3231             : 
    3232             :     // Make implicit JPEG overviews invisible, but do not destroy
    3233             :     // them in case they are already used (not sure that the client
    3234             :     // has the right to do that.  Behavior maybe undefined in GDAL API.
    3235         412 :     std::swap(m_apoJPEGOverviewDSOld, m_apoJPEGOverviewDS);
    3236         412 :     m_apoJPEGOverviewDS.clear();
    3237             : 
    3238             :     /* -------------------------------------------------------------------- */
    3239             :     /*      If RRD or external OVR overviews requested, then invoke         */
    3240             :     /*      generic handling.                                               */
    3241             :     /* -------------------------------------------------------------------- */
    3242         412 :     bool bUseGenericHandling = false;
    3243         412 :     bool bUseRRD = false;
    3244         824 :     CPLStringList aosOptions(papszOptions);
    3245             : 
    3246         412 :     const char *pszLocation = CSLFetchNameValue(papszOptions, "LOCATION");
    3247         412 :     if (pszLocation && EQUAL(pszLocation, "EXTERNAL"))
    3248             :     {
    3249           1 :         bUseGenericHandling = true;
    3250             :     }
    3251         411 :     else if (pszLocation && EQUAL(pszLocation, "INTERNAL"))
    3252             :     {
    3253           0 :         if (GetAccess() != GA_Update)
    3254             :         {
    3255           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3256             :                      "Cannot create internal overviews on file opened in "
    3257             :                      "read-only mode");
    3258           0 :             return CE_Failure;
    3259             :         }
    3260             :     }
    3261         411 :     else if (pszLocation && EQUAL(pszLocation, "RRD"))
    3262             :     {
    3263           3 :         bUseGenericHandling = true;
    3264           3 :         bUseRRD = true;
    3265           3 :         aosOptions.SetNameValue("USE_RRD", "YES");
    3266             :     }
    3267             :     // Legacy
    3268         408 :     else if ((bUseRRD = CPLTestBool(
    3269             :                   CSLFetchNameValueDef(papszOptions, "USE_RRD",
    3270         816 :                                        CPLGetConfigOption("USE_RRD", "NO")))) ||
    3271         408 :              CPLTestBool(CSLFetchNameValueDef(
    3272             :                  papszOptions, "TIFF_USE_OVR",
    3273             :                  CPLGetConfigOption("TIFF_USE_OVR", "NO"))))
    3274             :     {
    3275           0 :         bUseGenericHandling = true;
    3276             :     }
    3277             : 
    3278             :     /* -------------------------------------------------------------------- */
    3279             :     /*      If we don't have read access, then create the overviews         */
    3280             :     /*      externally.                                                     */
    3281             :     /* -------------------------------------------------------------------- */
    3282         412 :     if (GetAccess() != GA_Update)
    3283             :     {
    3284         145 :         CPLDebug("GTiff", "File open for read-only accessing, "
    3285             :                           "creating overviews externally.");
    3286             : 
    3287         145 :         bUseGenericHandling = true;
    3288             :     }
    3289             : 
    3290         412 :     if (bUseGenericHandling)
    3291             :     {
    3292         148 :         if (!m_apoOverviewDS.empty())
    3293             :         {
    3294           0 :             ReportError(CE_Failure, CPLE_NotSupported,
    3295             :                         "Cannot add external overviews when there are already "
    3296             :                         "internal overviews");
    3297           0 :             return CE_Failure;
    3298             :         }
    3299             : 
    3300         148 :         if (!m_bWriteEmptyTiles && !bUseRRD)
    3301             :         {
    3302           1 :             aosOptions.SetNameValue("SPARSE_OK", "YES");
    3303             :         }
    3304             : 
    3305         148 :         return GDALDataset::IBuildOverviews(
    3306             :             pszResampling, nOverviews, panOverviewList, nBandsIn, panBandList,
    3307         148 :             pfnProgress, pProgressData, aosOptions);
    3308             :     }
    3309             : 
    3310             :     /* -------------------------------------------------------------------- */
    3311             :     /*      Our TIFF overview support currently only works safely if all    */
    3312             :     /*      bands are handled at the same time.                             */
    3313             :     /* -------------------------------------------------------------------- */
    3314         264 :     if (nBandsIn != GetRasterCount())
    3315             :     {
    3316           0 :         ReportError(CE_Failure, CPLE_NotSupported,
    3317             :                     "Generation of overviews in TIFF currently only "
    3318             :                     "supported when operating on all bands.  "
    3319             :                     "Operation failed.");
    3320           0 :         return CE_Failure;
    3321             :     }
    3322             : 
    3323             :     /* -------------------------------------------------------------------- */
    3324             :     /*      If zero overviews were requested, we need to clear all          */
    3325             :     /*      existing overviews.                                             */
    3326             :     /* -------------------------------------------------------------------- */
    3327         264 :     if (nOverviews == 0)
    3328             :     {
    3329           8 :         if (m_apoOverviewDS.empty())
    3330           3 :             return GDALDataset::IBuildOverviews(
    3331             :                 pszResampling, nOverviews, panOverviewList, nBandsIn,
    3332           3 :                 panBandList, pfnProgress, pProgressData, papszOptions);
    3333             : 
    3334           5 :         return CleanOverviews();
    3335             :     }
    3336             : 
    3337         256 :     CPLErr eErr = CE_None;
    3338             : 
    3339             :     /* -------------------------------------------------------------------- */
    3340             :     /*      Initialize progress counter.                                    */
    3341             :     /* -------------------------------------------------------------------- */
    3342         256 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    3343             :     {
    3344           0 :         ReportError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    3345           0 :         return CE_Failure;
    3346             :     }
    3347             : 
    3348         256 :     FlushDirectory();
    3349             : 
    3350             :     /* -------------------------------------------------------------------- */
    3351             :     /*      If we are averaging bit data to grayscale we need to create     */
    3352             :     /*      8bit overviews.                                                 */
    3353             :     /* -------------------------------------------------------------------- */
    3354         256 :     int nOvBitsPerSample = m_nBitsPerSample;
    3355             : 
    3356         256 :     if (STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
    3357           2 :         nOvBitsPerSample = 8;
    3358             : 
    3359             :     /* -------------------------------------------------------------------- */
    3360             :     /*      Do we need some metadata for the overviews?                     */
    3361             :     /* -------------------------------------------------------------------- */
    3362         512 :     CPLString osMetadata;
    3363             : 
    3364         256 :     const bool bIsForMaskBand = nBands == 1 && GetRasterBand(1)->IsMaskBand();
    3365         256 :     GTIFFBuildOverviewMetadata(pszResampling, this, bIsForMaskBand, osMetadata);
    3366             : 
    3367             :     int nCompression;
    3368             :     uint16_t nPlanarConfig;
    3369             :     uint16_t nPredictor;
    3370             :     uint16_t nPhotometric;
    3371             :     int nOvrJpegQuality;
    3372         512 :     std::string osNoData;
    3373         256 :     uint16_t *panExtraSampleValues = nullptr;
    3374         256 :     uint16_t nExtraSamples = 0;
    3375         256 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    3376             :                                nPhotometric, nOvrJpegQuality, osNoData,
    3377             :                                panExtraSampleValues, nExtraSamples,
    3378             :                                papszOptions))
    3379             :     {
    3380           0 :         return CE_Failure;
    3381             :     }
    3382             : 
    3383             :     /* -------------------------------------------------------------------- */
    3384             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    3385             :     /* -------------------------------------------------------------------- */
    3386         512 :     std::vector<unsigned short> anTRed;
    3387         512 :     std::vector<unsigned short> anTGreen;
    3388         512 :     std::vector<unsigned short> anTBlue;
    3389         256 :     unsigned short *panRed = nullptr;
    3390         256 :     unsigned short *panGreen = nullptr;
    3391         256 :     unsigned short *panBlue = nullptr;
    3392             : 
    3393         256 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    3394             :     {
    3395          12 :         if (m_nColorTableMultiplier == 0)
    3396           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    3397             : 
    3398          12 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    3399             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    3400             :                              panRed, panGreen, panBlue);
    3401             :     }
    3402             : 
    3403             :     /* -------------------------------------------------------------------- */
    3404             :     /*      Establish which of the overview levels we already have, and     */
    3405             :     /*      which are new.  We assume that band 1 of the file is            */
    3406             :     /*      representative.                                                 */
    3407             :     /* -------------------------------------------------------------------- */
    3408         256 :     int nOvrBlockXSize = 0;
    3409         256 :     int nOvrBlockYSize = 0;
    3410         256 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    3411             :                               &nOvrBlockXSize, &nOvrBlockYSize, papszOptions,
    3412             :                               "BLOCKSIZE");
    3413         512 :     std::vector<bool> abRequireNewOverview(nOverviews, true);
    3414         695 :     for (int i = 0; i < nOverviews && eErr == CE_None; ++i)
    3415             :     {
    3416         771 :         for (auto &poODS : m_apoOverviewDS)
    3417             :         {
    3418             :             const int nOvFactor =
    3419         776 :                 GDALComputeOvFactor(poODS->GetRasterXSize(), GetRasterXSize(),
    3420         388 :                                     poODS->GetRasterYSize(), GetRasterYSize());
    3421             : 
    3422             :             // If we already have a 1x1 overview and this new one would result
    3423             :             // in it too, then don't create it.
    3424         448 :             if (poODS->GetRasterXSize() == 1 && poODS->GetRasterYSize() == 1 &&
    3425         448 :                 DIV_ROUND_UP(GetRasterXSize(), panOverviewList[i]) == 1 &&
    3426          21 :                 DIV_ROUND_UP(GetRasterYSize(), panOverviewList[i]) == 1)
    3427             :             {
    3428          21 :                 abRequireNewOverview[i] = false;
    3429          21 :                 break;
    3430             :             }
    3431             : 
    3432         699 :             if (nOvFactor == panOverviewList[i] ||
    3433         332 :                 nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3434             :                                                 GetRasterXSize(),
    3435             :                                                 GetRasterYSize()))
    3436             :             {
    3437          35 :                 abRequireNewOverview[i] = false;
    3438          35 :                 break;
    3439             :             }
    3440             :         }
    3441             : 
    3442         439 :         if (abRequireNewOverview[i])
    3443             :         {
    3444         383 :             if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    3445           2 :                 !m_bWriteKnownIncompatibleEdition)
    3446             :             {
    3447           2 :                 ReportError(CE_Warning, CPLE_AppDefined,
    3448             :                             "Adding new overviews invalidates the "
    3449             :                             "LAYOUT=IFDS_BEFORE_DATA property");
    3450           2 :                 m_bKnownIncompatibleEdition = true;
    3451           2 :                 m_bWriteKnownIncompatibleEdition = true;
    3452             :             }
    3453             : 
    3454             :             const int nOXSize =
    3455         383 :                 DIV_ROUND_UP(GetRasterXSize(), panOverviewList[i]);
    3456             :             const int nOYSize =
    3457         383 :                 DIV_ROUND_UP(GetRasterYSize(), panOverviewList[i]);
    3458             : 
    3459         766 :             const toff_t nOverviewOffset = GTIFFWriteDirectory(
    3460             :                 m_hTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize,
    3461         383 :                 nOvBitsPerSample, nPlanarConfig, m_nSamplesPerPixel,
    3462             :                 nOvrBlockXSize, nOvrBlockYSize, TRUE, nCompression,
    3463         383 :                 nPhotometric, m_nSampleFormat, nPredictor, panRed, panGreen,
    3464             :                 panBlue, nExtraSamples, panExtraSampleValues, osMetadata,
    3465         383 :                 nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality)
    3466             :                                      : nullptr,
    3467         383 :                 CPLSPrintf("%d", m_nJpegTablesMode),
    3468          25 :                 osNoData.empty() ? nullptr : osNoData.c_str(),
    3469         383 :                 m_anLercAddCompressionAndVersion, false);
    3470             : 
    3471         383 :             if (nOverviewOffset == 0)
    3472           0 :                 eErr = CE_Failure;
    3473             :             else
    3474         383 :                 eErr = RegisterNewOverviewDataset(
    3475             :                     nOverviewOffset, nOvrJpegQuality, papszOptions);
    3476             :         }
    3477             :     }
    3478             : 
    3479         256 :     CPLFree(panExtraSampleValues);
    3480         256 :     panExtraSampleValues = nullptr;
    3481             : 
    3482         256 :     ReloadDirectory();
    3483             : 
    3484             :     /* -------------------------------------------------------------------- */
    3485             :     /*      Create overviews for the mask.                                  */
    3486             :     /* -------------------------------------------------------------------- */
    3487         256 :     if (eErr != CE_None)
    3488           0 :         return eErr;
    3489             : 
    3490         256 :     eErr = CreateInternalMaskOverviews(nOvrBlockXSize, nOvrBlockYSize);
    3491             : 
    3492             :     /* -------------------------------------------------------------------- */
    3493             :     /*      Refresh overviews for the mask                                  */
    3494             :     /* -------------------------------------------------------------------- */
    3495             :     const bool bHasInternalMask =
    3496         256 :         m_poMaskDS != nullptr && m_poMaskDS->GetRasterCount() == 1;
    3497             :     const bool bHasExternalMask =
    3498         256 :         !bHasInternalMask && oOvManager.HaveMaskFile();
    3499         256 :     const bool bHasMask = bHasInternalMask || bHasExternalMask;
    3500             : 
    3501         256 :     if (bHasInternalMask)
    3502             :     {
    3503          48 :         std::vector<GDALRasterBandH> ahOverviewBands;
    3504          64 :         for (auto &poOvrDS : m_apoOverviewDS)
    3505             :         {
    3506          40 :             if (poOvrDS->m_poMaskDS != nullptr)
    3507             :             {
    3508          40 :                 ahOverviewBands.push_back(GDALRasterBand::ToHandle(
    3509          40 :                     poOvrDS->m_poMaskDS->GetRasterBand(1)));
    3510             :             }
    3511             :         }
    3512             : 
    3513          48 :         void *pScaledProgressData = GDALCreateScaledProgress(
    3514          24 :             0, 1.0 / (nBands + 1), pfnProgress, pProgressData);
    3515          24 :         eErr = GDALRegenerateOverviewsEx(
    3516          24 :             m_poMaskDS->GetRasterBand(1),
    3517          24 :             static_cast<int>(ahOverviewBands.size()), ahOverviewBands.data(),
    3518             :             pszResampling, GDALScaledProgress, pScaledProgressData,
    3519             :             papszOptions);
    3520          24 :         GDALDestroyScaledProgress(pScaledProgressData);
    3521             :     }
    3522         232 :     else if (bHasExternalMask)
    3523             :     {
    3524           4 :         void *pScaledProgressData = GDALCreateScaledProgress(
    3525           2 :             0, 1.0 / (nBands + 1), pfnProgress, pProgressData);
    3526           2 :         eErr = oOvManager.BuildOverviewsMask(
    3527             :             pszResampling, nOverviews, panOverviewList, GDALScaledProgress,
    3528             :             pScaledProgressData, papszOptions);
    3529           2 :         GDALDestroyScaledProgress(pScaledProgressData);
    3530             :     }
    3531             : 
    3532             :     // If we have an alpha band, we want it to be generated before downsampling
    3533             :     // other bands
    3534         256 :     bool bHasAlphaBand = false;
    3535       66260 :     for (int iBand = 0; iBand < nBands; iBand++)
    3536             :     {
    3537       66004 :         if (papoBands[iBand]->GetColorInterpretation() == GCI_AlphaBand)
    3538          19 :             bHasAlphaBand = true;
    3539             :     }
    3540             : 
    3541             :     /* -------------------------------------------------------------------- */
    3542             :     /*      Refresh old overviews that were listed.                         */
    3543             :     /* -------------------------------------------------------------------- */
    3544         256 :     const auto poColorTable = GetRasterBand(panBandList[0])->GetColorTable();
    3545          21 :     if ((m_nPlanarConfig == PLANARCONFIG_CONTIG || bHasAlphaBand) &&
    3546         237 :         GDALDataTypeIsComplex(
    3547         237 :             GetRasterBand(panBandList[0])->GetRasterDataType()) == FALSE &&
    3548          12 :         (poColorTable == nullptr || STARTS_WITH_CI(pszResampling, "NEAR") ||
    3549         513 :          poColorTable->IsIdentity()) &&
    3550         229 :         (STARTS_WITH_CI(pszResampling, "NEAR") ||
    3551         119 :          EQUAL(pszResampling, "AVERAGE") || EQUAL(pszResampling, "RMS") ||
    3552          49 :          EQUAL(pszResampling, "GAUSS") || EQUAL(pszResampling, "CUBIC") ||
    3553          30 :          EQUAL(pszResampling, "CUBICSPLINE") ||
    3554          29 :          EQUAL(pszResampling, "LANCZOS") || EQUAL(pszResampling, "BILINEAR") ||
    3555          25 :          EQUAL(pszResampling, "MODE")))
    3556             :     {
    3557             :         // In the case of pixel interleaved compressed overviews, we want to
    3558             :         // generate the overviews for all the bands block by block, and not
    3559             :         // band after band, in order to write the block once and not loose
    3560             :         // space in the TIFF file.  We also use that logic for uncompressed
    3561             :         // overviews, since GDALRegenerateOverviewsMultiBand() will be able to
    3562             :         // trigger cascading overview regeneration even in the presence
    3563             :         // of an alpha band.
    3564             : 
    3565         207 :         int nNewOverviews = 0;
    3566             : 
    3567             :         GDALRasterBand ***papapoOverviewBands = static_cast<GDALRasterBand ***>(
    3568         207 :             CPLCalloc(sizeof(void *), nBandsIn));
    3569             :         GDALRasterBand **papoBandList =
    3570         207 :             static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nBandsIn));
    3571       66114 :         for (int iBand = 0; iBand < nBandsIn; ++iBand)
    3572             :         {
    3573       65907 :             GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
    3574             : 
    3575       65907 :             papoBandList[iBand] = poBand;
    3576      131814 :             papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
    3577       65907 :                 CPLCalloc(sizeof(void *), poBand->GetOverviewCount()));
    3578             : 
    3579       65907 :             int iCurOverview = 0;
    3580             :             std::vector<bool> abAlreadyUsedOverviewBand(
    3581       65907 :                 poBand->GetOverviewCount(), false);
    3582             : 
    3583      132099 :             for (int i = 0; i < nOverviews; ++i)
    3584             :             {
    3585       66651 :                 for (int j = 0; j < poBand->GetOverviewCount(); ++j)
    3586             :                 {
    3587       66636 :                     if (abAlreadyUsedOverviewBand[j])
    3588         458 :                         continue;
    3589             : 
    3590             :                     int nOvFactor;
    3591       66178 :                     GDALRasterBand *poOverview = poBand->GetOverview(j);
    3592             : 
    3593       66178 :                     nOvFactor = GDALComputeOvFactor(
    3594             :                         poOverview->GetXSize(), poBand->GetXSize(),
    3595             :                         poOverview->GetYSize(), poBand->GetYSize());
    3596             : 
    3597       66178 :                     GDALCopyNoDataValue(poOverview, poBand);
    3598             : 
    3599       66179 :                     if (nOvFactor == panOverviewList[i] ||
    3600           1 :                         nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3601             :                                                         poBand->GetXSize(),
    3602             :                                                         poBand->GetYSize()))
    3603             :                     {
    3604       66177 :                         if (iBand == 0)
    3605             :                         {
    3606             :                             const auto osNewResampling =
    3607         666 :                                 GDALGetNormalizedOvrResampling(pszResampling);
    3608             :                             const char *pszExistingResampling =
    3609         333 :                                 poOverview->GetMetadataItem("RESAMPLING");
    3610         666 :                             if (pszExistingResampling &&
    3611         333 :                                 pszExistingResampling != osNewResampling)
    3612             :                             {
    3613           2 :                                 poOverview->SetMetadataItem(
    3614           2 :                                     "RESAMPLING", osNewResampling.c_str());
    3615             :                             }
    3616             :                         }
    3617             : 
    3618       66177 :                         abAlreadyUsedOverviewBand[j] = true;
    3619       66177 :                         CPLAssert(iCurOverview < poBand->GetOverviewCount());
    3620       66177 :                         papapoOverviewBands[iBand][iCurOverview] = poOverview;
    3621       66177 :                         ++iCurOverview;
    3622       66177 :                         break;
    3623             :                     }
    3624             :                 }
    3625             :             }
    3626             : 
    3627       65907 :             if (nNewOverviews == 0)
    3628             :             {
    3629         207 :                 nNewOverviews = iCurOverview;
    3630             :             }
    3631       65700 :             else if (nNewOverviews != iCurOverview)
    3632             :             {
    3633           0 :                 CPLAssert(false);
    3634             :                 return CE_Failure;
    3635             :             }
    3636             :         }
    3637             : 
    3638             :         void *pScaledProgressData =
    3639         207 :             bHasMask ? GDALCreateScaledProgress(1.0 / (nBands + 1), 1.0,
    3640             :                                                 pfnProgress, pProgressData)
    3641         181 :                      : GDALCreateScaledProgress(0.0, 1.0, pfnProgress,
    3642         207 :                                                 pProgressData);
    3643         207 :         GDALRegenerateOverviewsMultiBand(nBandsIn, papoBandList, nNewOverviews,
    3644             :                                          papapoOverviewBands, pszResampling,
    3645             :                                          GDALScaledProgress,
    3646             :                                          pScaledProgressData, papszOptions);
    3647         207 :         GDALDestroyScaledProgress(pScaledProgressData);
    3648             : 
    3649       66114 :         for (int iBand = 0; iBand < nBandsIn; ++iBand)
    3650             :         {
    3651       65907 :             CPLFree(papapoOverviewBands[iBand]);
    3652             :         }
    3653         207 :         CPLFree(papapoOverviewBands);
    3654         207 :         CPLFree(papoBandList);
    3655             :     }
    3656             :     else
    3657             :     {
    3658             :         GDALRasterBand **papoOverviewBands = static_cast<GDALRasterBand **>(
    3659          49 :             CPLCalloc(sizeof(void *), nOverviews));
    3660             : 
    3661          49 :         const int iBandOffset = bHasMask ? 1 : 0;
    3662             : 
    3663         146 :         for (int iBand = 0; iBand < nBandsIn && eErr == CE_None; ++iBand)
    3664             :         {
    3665          97 :             GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
    3666          97 :             if (poBand == nullptr)
    3667             :             {
    3668           0 :                 eErr = CE_Failure;
    3669           0 :                 break;
    3670             :             }
    3671             : 
    3672             :             std::vector<bool> abAlreadyUsedOverviewBand(
    3673         194 :                 poBand->GetOverviewCount(), false);
    3674             : 
    3675          97 :             int nNewOverviews = 0;
    3676         290 :             for (int i = 0; i < nOverviews; ++i)
    3677             :             {
    3678         451 :                 for (int j = 0; j < poBand->GetOverviewCount(); ++j)
    3679             :                 {
    3680         433 :                     if (abAlreadyUsedOverviewBand[j])
    3681         257 :                         continue;
    3682             : 
    3683         176 :                     GDALRasterBand *poOverview = poBand->GetOverview(j);
    3684             : 
    3685         176 :                     GDALCopyNoDataValue(poOverview, poBand);
    3686             : 
    3687         176 :                     const int nOvFactor = GDALComputeOvFactor(
    3688             :                         poOverview->GetXSize(), poBand->GetXSize(),
    3689             :                         poOverview->GetYSize(), poBand->GetYSize());
    3690             : 
    3691         177 :                     if (nOvFactor == panOverviewList[i] ||
    3692           1 :                         nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3693             :                                                         poBand->GetXSize(),
    3694             :                                                         poBand->GetYSize()))
    3695             :                     {
    3696         175 :                         if (iBand == 0)
    3697             :                         {
    3698             :                             const auto osNewResampling =
    3699         170 :                                 GDALGetNormalizedOvrResampling(pszResampling);
    3700             :                             const char *pszExistingResampling =
    3701          85 :                                 poOverview->GetMetadataItem("RESAMPLING");
    3702         137 :                             if (pszExistingResampling &&
    3703          52 :                                 pszExistingResampling != osNewResampling)
    3704             :                             {
    3705           1 :                                 poOverview->SetMetadataItem(
    3706           1 :                                     "RESAMPLING", osNewResampling.c_str());
    3707             :                             }
    3708             :                         }
    3709             : 
    3710         175 :                         abAlreadyUsedOverviewBand[j] = true;
    3711         175 :                         CPLAssert(nNewOverviews < poBand->GetOverviewCount());
    3712         175 :                         papoOverviewBands[nNewOverviews++] = poOverview;
    3713         175 :                         break;
    3714             :                     }
    3715             :                 }
    3716             :             }
    3717             : 
    3718         194 :             void *pScaledProgressData = GDALCreateScaledProgress(
    3719          97 :                 (iBand + iBandOffset) /
    3720          97 :                     static_cast<double>(nBandsIn + iBandOffset),
    3721          97 :                 (iBand + iBandOffset + 1) /
    3722          97 :                     static_cast<double>(nBandsIn + iBandOffset),
    3723             :                 pfnProgress, pProgressData);
    3724             : 
    3725          97 :             eErr = GDALRegenerateOverviewsEx(
    3726             :                 poBand, nNewOverviews,
    3727             :                 reinterpret_cast<GDALRasterBandH *>(papoOverviewBands),
    3728             :                 pszResampling, GDALScaledProgress, pScaledProgressData,
    3729             :                 papszOptions);
    3730             : 
    3731          97 :             GDALDestroyScaledProgress(pScaledProgressData);
    3732             :         }
    3733             : 
    3734             :         /* --------------------------------------------------------------------
    3735             :          */
    3736             :         /*      Cleanup */
    3737             :         /* --------------------------------------------------------------------
    3738             :          */
    3739          49 :         CPLFree(papoOverviewBands);
    3740             :     }
    3741             : 
    3742         256 :     pfnProgress(1.0, nullptr, pProgressData);
    3743             : 
    3744         256 :     return eErr;
    3745             : }
    3746             : 
    3747             : /************************************************************************/
    3748             : /*                   GTiffWriteDummyGeokeyDirectory()                   */
    3749             : /************************************************************************/
    3750             : 
    3751        1532 : static void GTiffWriteDummyGeokeyDirectory(TIFF *hTIFF)
    3752             : {
    3753             :     // If we have existing geokeys, try to wipe them
    3754             :     // by writing a dummy geokey directory. (#2546)
    3755        1532 :     uint16_t *panVI = nullptr;
    3756        1532 :     uint16_t nKeyCount = 0;
    3757             : 
    3758        1532 :     if (TIFFGetField(hTIFF, TIFFTAG_GEOKEYDIRECTORY, &nKeyCount, &panVI))
    3759             :     {
    3760          26 :         GUInt16 anGKVersionInfo[4] = {1, 1, 0, 0};
    3761          26 :         double adfDummyDoubleParams[1] = {0.0};
    3762          26 :         TIFFSetField(hTIFF, TIFFTAG_GEOKEYDIRECTORY, 4, anGKVersionInfo);
    3763          26 :         TIFFSetField(hTIFF, TIFFTAG_GEODOUBLEPARAMS, 1, adfDummyDoubleParams);
    3764          26 :         TIFFSetField(hTIFF, TIFFTAG_GEOASCIIPARAMS, "");
    3765             :     }
    3766        1532 : }
    3767             : 
    3768             : /************************************************************************/
    3769             : /*                      IsSRSCompatibleOfGeoTIFF()                      */
    3770             : /************************************************************************/
    3771             : 
    3772        3231 : static bool IsSRSCompatibleOfGeoTIFF(const OGRSpatialReference *poSRS,
    3773             :                                      GTIFFKeysFlavorEnum eGeoTIFFKeysFlavor)
    3774             : {
    3775        3231 :     char *pszWKT = nullptr;
    3776        3231 :     if ((poSRS->IsGeographic() || poSRS->IsProjected()) && !poSRS->IsCompound())
    3777             :     {
    3778        3213 :         const char *pszAuthName = poSRS->GetAuthorityName();
    3779        3213 :         const char *pszAuthCode = poSRS->GetAuthorityCode();
    3780        3213 :         if (pszAuthName && pszAuthCode && EQUAL(pszAuthName, "EPSG"))
    3781        2642 :             return true;
    3782             :     }
    3783             :     OGRErr eErr;
    3784             :     {
    3785        1178 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    3786        1178 :         if (poSRS->IsDerivedGeographic() ||
    3787         589 :             (poSRS->IsProjected() && !poSRS->IsCompound() &&
    3788          70 :              poSRS->GetAxesCount() == 3))
    3789             :         {
    3790           0 :             eErr = OGRERR_FAILURE;
    3791             :         }
    3792             :         else
    3793             :         {
    3794             :             // Geographic3D CRS can't be exported to WKT1, but are
    3795             :             // valid GeoTIFF 1.1
    3796         589 :             const char *const apszOptions[] = {
    3797         589 :                 poSRS->IsGeographic() ? nullptr : "FORMAT=WKT1", nullptr};
    3798         589 :             eErr = poSRS->exportToWkt(&pszWKT, apszOptions);
    3799         589 :             if (eErr == OGRERR_FAILURE && poSRS->IsProjected() &&
    3800             :                 eGeoTIFFKeysFlavor == GEOTIFF_KEYS_ESRI_PE)
    3801             :             {
    3802           0 :                 CPLFree(pszWKT);
    3803           0 :                 const char *const apszOptionsESRIWKT[] = {"FORMAT=WKT1_ESRI",
    3804             :                                                           nullptr};
    3805           0 :                 eErr = poSRS->exportToWkt(&pszWKT, apszOptionsESRIWKT);
    3806             :             }
    3807             :         }
    3808             :     }
    3809         589 :     const bool bCompatibleOfGeoTIFF =
    3810        1177 :         (eErr == OGRERR_NONE && pszWKT != nullptr &&
    3811         588 :          strstr(pszWKT, "custom_proj4") == nullptr);
    3812         589 :     CPLFree(pszWKT);
    3813         589 :     return bCompatibleOfGeoTIFF;
    3814             : }
    3815             : 
    3816             : /************************************************************************/
    3817             : /*                          WriteGeoTIFFInfo()                          */
    3818             : /************************************************************************/
    3819             : 
    3820        5943 : void GTiffDataset::WriteGeoTIFFInfo()
    3821             : 
    3822             : {
    3823        5943 :     bool bPixelIsPoint = false;
    3824        5943 :     bool bPointGeoIgnore = false;
    3825             : 
    3826             :     const char *pszAreaOrPoint =
    3827        5943 :         GTiffDataset::GetMetadataItem(GDALMD_AREA_OR_POINT);
    3828        5943 :     if (pszAreaOrPoint && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT))
    3829             :     {
    3830          19 :         bPixelIsPoint = true;
    3831             :         bPointGeoIgnore =
    3832          19 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    3833             :     }
    3834             : 
    3835        5943 :     if (m_bForceUnsetGTOrGCPs)
    3836             :     {
    3837          11 :         m_bNeedsRewrite = true;
    3838          11 :         m_bForceUnsetGTOrGCPs = false;
    3839             : 
    3840          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE);
    3841          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS);
    3842          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX);
    3843             :     }
    3844             : 
    3845        5943 :     if (m_bForceUnsetProjection)
    3846             :     {
    3847           8 :         m_bNeedsRewrite = true;
    3848           8 :         m_bForceUnsetProjection = false;
    3849             : 
    3850           8 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOKEYDIRECTORY);
    3851           8 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEODOUBLEPARAMS);
    3852           8 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOASCIIPARAMS);
    3853             :     }
    3854             : 
    3855             :     /* -------------------------------------------------------------------- */
    3856             :     /*      Write geotransform if valid.                                    */
    3857             :     /* -------------------------------------------------------------------- */
    3858        5943 :     if (m_bGeoTransformValid)
    3859             :     {
    3860        1860 :         m_bNeedsRewrite = true;
    3861             : 
    3862             :         /* --------------------------------------------------------------------
    3863             :          */
    3864             :         /*      Clear old tags to ensure we don't end up with conflicting */
    3865             :         /*      information. (#2625) */
    3866             :         /* --------------------------------------------------------------------
    3867             :          */
    3868        1860 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE);
    3869        1860 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS);
    3870        1860 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX);
    3871             : 
    3872             :         /* --------------------------------------------------------------------
    3873             :          */
    3874             :         /*      Write the transform.  If we have a normal north-up image we */
    3875             :         /*      use the tiepoint plus pixelscale otherwise we use a matrix. */
    3876             :         /* --------------------------------------------------------------------
    3877             :          */
    3878        1860 :         if (m_gt.xrot == 0.0 && m_gt.yrot == 0.0 && m_gt.yscale < 0.0)
    3879             :         {
    3880        1767 :             double dfOffset = 0.0;
    3881        1767 :             if (m_eProfile != GTiffProfile::BASELINE)
    3882             :             {
    3883             :                 // In the case the SRS has a vertical component and we have
    3884             :                 // a single band, encode its scale/offset in the GeoTIFF tags
    3885        1761 :                 int bHasScale = FALSE;
    3886        1761 :                 double dfScale = GetRasterBand(1)->GetScale(&bHasScale);
    3887        1761 :                 int bHasOffset = FALSE;
    3888        1761 :                 dfOffset = GetRasterBand(1)->GetOffset(&bHasOffset);
    3889             :                 const bool bApplyScaleOffset =
    3890        1761 :                     m_oSRS.IsVertical() && GetRasterCount() == 1;
    3891        1761 :                 if (bApplyScaleOffset && !bHasScale)
    3892           0 :                     dfScale = 1.0;
    3893        1761 :                 if (!bApplyScaleOffset || !bHasOffset)
    3894        1758 :                     dfOffset = 0.0;
    3895        1761 :                 const double adfPixelScale[3] = {m_gt.xscale, fabs(m_gt.yscale),
    3896        1761 :                                                  bApplyScaleOffset ? dfScale
    3897        1761 :                                                                    : 0.0};
    3898        1761 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale);
    3899             :             }
    3900             : 
    3901        1767 :             double adfTiePoints[6] = {0.0,        0.0,        0.0,
    3902        1767 :                                       m_gt.xorig, m_gt.yorig, dfOffset};
    3903             : 
    3904        1767 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3905             :             {
    3906          15 :                 adfTiePoints[3] += m_gt.xscale * 0.5 + m_gt.xrot * 0.5;
    3907          15 :                 adfTiePoints[4] += m_gt.yrot * 0.5 + m_gt.yscale * 0.5;
    3908             :             }
    3909             : 
    3910        1767 :             if (m_eProfile != GTiffProfile::BASELINE)
    3911        1767 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
    3912             :         }
    3913             :         else
    3914             :         {
    3915          93 :             double adfMatrix[16] = {};
    3916             : 
    3917          93 :             adfMatrix[0] = m_gt.xscale;
    3918          93 :             adfMatrix[1] = m_gt.xrot;
    3919          93 :             adfMatrix[3] = m_gt.xorig;
    3920          93 :             adfMatrix[4] = m_gt.yrot;
    3921          93 :             adfMatrix[5] = m_gt.yscale;
    3922          93 :             adfMatrix[7] = m_gt.yorig;
    3923          93 :             adfMatrix[15] = 1.0;
    3924             : 
    3925          93 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3926             :             {
    3927           0 :                 adfMatrix[3] += m_gt.xscale * 0.5 + m_gt.xrot * 0.5;
    3928           0 :                 adfMatrix[7] += m_gt.yrot * 0.5 + m_gt.yscale * 0.5;
    3929             :             }
    3930             : 
    3931          93 :             if (m_eProfile != GTiffProfile::BASELINE)
    3932          93 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
    3933             :         }
    3934             : 
    3935        1860 :         if (m_poBaseDS == nullptr)
    3936             :         {
    3937             :             // Do we need a world file?
    3938        1860 :             if (CPLFetchBool(m_papszCreationOptions, "TFW", false))
    3939           7 :                 GDALWriteWorldFile(m_osFilename.c_str(), "tfw", m_gt.data());
    3940        1853 :             else if (CPLFetchBool(m_papszCreationOptions, "WORLDFILE", false))
    3941           2 :                 GDALWriteWorldFile(m_osFilename.c_str(), "wld", m_gt.data());
    3942             :         }
    3943             :     }
    3944        4096 :     else if (GetGCPCount() > 0 && GetGCPCount() <= knMAX_GCP_COUNT &&
    3945          13 :              m_eProfile != GTiffProfile::BASELINE)
    3946             :     {
    3947          13 :         m_bNeedsRewrite = true;
    3948             : 
    3949             :         double *padfTiePoints = static_cast<double *>(
    3950          13 :             CPLMalloc(6 * sizeof(double) * GetGCPCount()));
    3951             : 
    3952          69 :         for (size_t iGCP = 0; iGCP < m_aoGCPs.size(); ++iGCP)
    3953             :         {
    3954             : 
    3955          56 :             padfTiePoints[iGCP * 6 + 0] = m_aoGCPs[iGCP].Pixel();
    3956          56 :             padfTiePoints[iGCP * 6 + 1] = m_aoGCPs[iGCP].Line();
    3957          56 :             padfTiePoints[iGCP * 6 + 2] = 0;
    3958          56 :             padfTiePoints[iGCP * 6 + 3] = m_aoGCPs[iGCP].X();
    3959          56 :             padfTiePoints[iGCP * 6 + 4] = m_aoGCPs[iGCP].Y();
    3960          56 :             padfTiePoints[iGCP * 6 + 5] = m_aoGCPs[iGCP].Z();
    3961             : 
    3962          56 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3963             :             {
    3964           0 :                 padfTiePoints[iGCP * 6 + 0] += 0.5;
    3965           0 :                 padfTiePoints[iGCP * 6 + 1] += 0.5;
    3966             :             }
    3967             :         }
    3968             : 
    3969          13 :         TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * GetGCPCount(),
    3970             :                      padfTiePoints);
    3971          13 :         CPLFree(padfTiePoints);
    3972             :     }
    3973             : 
    3974             :     /* -------------------------------------------------------------------- */
    3975             :     /*      Write out projection definition.                                */
    3976             :     /* -------------------------------------------------------------------- */
    3977        5943 :     const bool bHasProjection = !m_oSRS.IsEmpty();
    3978        5943 :     if ((bHasProjection || bPixelIsPoint) &&
    3979        1536 :         m_eProfile != GTiffProfile::BASELINE)
    3980             :     {
    3981        1532 :         m_bNeedsRewrite = true;
    3982             : 
    3983             :         // If we have existing geokeys, try to wipe them
    3984             :         // by writing a dummy geokey directory. (#2546)
    3985        1532 :         GTiffWriteDummyGeokeyDirectory(m_hTIFF);
    3986             : 
    3987        1532 :         GTIF *psGTIF = GTiffDataset::GTIFNew(m_hTIFF);
    3988             : 
    3989             :         // Set according to coordinate system.
    3990        1532 :         if (bHasProjection)
    3991             :         {
    3992        1531 :             if (IsSRSCompatibleOfGeoTIFF(&m_oSRS, m_eGeoTIFFKeysFlavor))
    3993             :             {
    3994        1529 :                 GTIFSetFromOGISDefnEx(psGTIF,
    3995             :                                       OGRSpatialReference::ToHandle(&m_oSRS),
    3996             :                                       m_eGeoTIFFKeysFlavor, m_eGeoTIFFVersion);
    3997             :             }
    3998             :             else
    3999             :             {
    4000           2 :                 GDALPamDataset::SetSpatialRef(&m_oSRS);
    4001             :             }
    4002             :         }
    4003             : 
    4004        1532 :         if (bPixelIsPoint)
    4005             :         {
    4006          19 :             GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
    4007             :                        RasterPixelIsPoint);
    4008             :         }
    4009             : 
    4010        1532 :         GTIFWriteKeys(psGTIF);
    4011        1532 :         GTIFFree(psGTIF);
    4012             :     }
    4013        5943 : }
    4014             : 
    4015             : /************************************************************************/
    4016             : /*                         AppendMetadataItem()                         */
    4017             : /************************************************************************/
    4018             : 
    4019        3897 : static void AppendMetadataItem(CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
    4020             :                                const char *pszKey, const char *pszValue,
    4021             :                                CPLXMLNode *psValueNode, int nBand,
    4022             :                                const char *pszRole, const char *pszDomain)
    4023             : 
    4024             : {
    4025        3897 :     CPLAssert(pszValue || psValueNode);
    4026        3897 :     CPLAssert(!(pszValue && psValueNode));
    4027             : 
    4028             :     /* -------------------------------------------------------------------- */
    4029             :     /*      Create the Item element, and subcomponents.                     */
    4030             :     /* -------------------------------------------------------------------- */
    4031        3897 :     CPLXMLNode *psItem = CPLCreateXMLNode(nullptr, CXT_Element, "Item");
    4032        3897 :     CPLAddXMLAttributeAndValue(psItem, "name", pszKey);
    4033             : 
    4034        3897 :     if (nBand > 0)
    4035             :     {
    4036        1167 :         char szBandId[32] = {};
    4037        1167 :         snprintf(szBandId, sizeof(szBandId), "%d", nBand - 1);
    4038        1167 :         CPLAddXMLAttributeAndValue(psItem, "sample", szBandId);
    4039             :     }
    4040             : 
    4041        3897 :     if (pszRole != nullptr)
    4042         384 :         CPLAddXMLAttributeAndValue(psItem, "role", pszRole);
    4043             : 
    4044        3897 :     if (pszDomain != nullptr && strlen(pszDomain) > 0)
    4045        1012 :         CPLAddXMLAttributeAndValue(psItem, "domain", pszDomain);
    4046             : 
    4047        3897 :     if (pszValue)
    4048             :     {
    4049             :         // Note: this escaping should not normally be done, as the serialization
    4050             :         // of the tree to XML also does it, so we end up width double XML escaping,
    4051             :         // but keep it for backward compatibility.
    4052        3876 :         char *pszEscapedItemValue = CPLEscapeString(pszValue, -1, CPLES_XML);
    4053        3876 :         CPLCreateXMLNode(psItem, CXT_Text, pszEscapedItemValue);
    4054        3876 :         CPLFree(pszEscapedItemValue);
    4055             :     }
    4056             :     else
    4057             :     {
    4058          21 :         CPLAddXMLChild(psItem, psValueNode);
    4059             :     }
    4060             : 
    4061             :     /* -------------------------------------------------------------------- */
    4062             :     /*      Create root, if missing.                                        */
    4063             :     /* -------------------------------------------------------------------- */
    4064        3897 :     if (*ppsRoot == nullptr)
    4065         765 :         *ppsRoot = CPLCreateXMLNode(nullptr, CXT_Element, "GDALMetadata");
    4066             : 
    4067             :     /* -------------------------------------------------------------------- */
    4068             :     /*      Append item to tail.  We keep track of the tail to avoid        */
    4069             :     /*      O(nsquared) time as the list gets longer.                       */
    4070             :     /* -------------------------------------------------------------------- */
    4071        3897 :     if (*ppsTail == nullptr)
    4072         765 :         CPLAddXMLChild(*ppsRoot, psItem);
    4073             :     else
    4074        3132 :         CPLAddXMLSibling(*ppsTail, psItem);
    4075             : 
    4076        3897 :     *ppsTail = psItem;
    4077        3897 : }
    4078             : 
    4079             : /************************************************************************/
    4080             : /*                         AppendMetadataItem()                         */
    4081             : /************************************************************************/
    4082             : 
    4083        3876 : static void AppendMetadataItem(CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
    4084             :                                const char *pszKey, const char *pszValue,
    4085             :                                int nBand, const char *pszRole,
    4086             :                                const char *pszDomain)
    4087             : 
    4088             : {
    4089        3876 :     AppendMetadataItem(ppsRoot, ppsTail, pszKey, pszValue, nullptr, nBand,
    4090             :                        pszRole, pszDomain);
    4091        3876 : }
    4092             : 
    4093             : /************************************************************************/
    4094             : /*                          WriteMDMetadata()                           */
    4095             : /************************************************************************/
    4096             : 
    4097      311265 : static void WriteMDMetadata(GDALMultiDomainMetadata *poMDMD, TIFF *hTIFF,
    4098             :                             CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
    4099             :                             int nBand, GTiffProfile eProfile)
    4100             : 
    4101             : {
    4102             : 
    4103             :     /* ==================================================================== */
    4104             :     /*      Process each domain.                                            */
    4105             :     /* ==================================================================== */
    4106      311265 :     CSLConstList papszDomainList = poMDMD->GetDomainList();
    4107      319872 :     for (int iDomain = 0; papszDomainList && papszDomainList[iDomain];
    4108             :          ++iDomain)
    4109             :     {
    4110        8607 :         CSLConstList papszMD = poMDMD->GetMetadata(papszDomainList[iDomain]);
    4111        8607 :         bool bIsXMLOrJSON = false;
    4112             : 
    4113        8607 :         if (EQUAL(papszDomainList[iDomain], GDAL_MDD_IMAGE_STRUCTURE) ||
    4114        2518 :             EQUAL(papszDomainList[iDomain], "DERIVED_SUBDATASETS"))
    4115        6092 :             continue;  // Ignored.
    4116        2515 :         if (EQUAL(papszDomainList[iDomain], "COLOR_PROFILE"))
    4117           3 :             continue;  // Handled elsewhere.
    4118        2512 :         if (EQUAL(papszDomainList[iDomain], GDAL_MDD_RPC))
    4119           7 :             continue;  // Handled elsewhere.
    4120        2506 :         if (EQUAL(papszDomainList[iDomain], "xml:ESRI") &&
    4121           1 :             CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
    4122           1 :             continue;  // Handled elsewhere.
    4123        2504 :         if (EQUAL(papszDomainList[iDomain], "xml:XMP"))
    4124           2 :             continue;  // Handled in SetMetadata.
    4125             : 
    4126        2502 :         if (STARTS_WITH_CI(papszDomainList[iDomain], "xml:") ||
    4127        2500 :             STARTS_WITH_CI(papszDomainList[iDomain], "json:"))
    4128             :         {
    4129          12 :             bIsXMLOrJSON = true;
    4130             :         }
    4131             : 
    4132             :         /* --------------------------------------------------------------------
    4133             :          */
    4134             :         /*      Process each item in this domain. */
    4135             :         /* --------------------------------------------------------------------
    4136             :          */
    4137        7633 :         for (int iItem = 0; papszMD && papszMD[iItem]; ++iItem)
    4138             :         {
    4139        5131 :             const char *pszItemValue = nullptr;
    4140        5131 :             char *pszItemName = nullptr;
    4141             : 
    4142        5131 :             if (bIsXMLOrJSON)
    4143             :             {
    4144          11 :                 pszItemName = CPLStrdup("doc");
    4145          11 :                 pszItemValue = papszMD[iItem];
    4146             :             }
    4147             :             else
    4148             :             {
    4149        5120 :                 pszItemValue = CPLParseNameValue(papszMD[iItem], &pszItemName);
    4150        5120 :                 if (pszItemName == nullptr)
    4151             :                 {
    4152          49 :                     CPLDebug("GTiff", "Invalid metadata item : %s",
    4153          49 :                              papszMD[iItem]);
    4154          49 :                     continue;
    4155             :                 }
    4156             :             }
    4157             : 
    4158             :             /* --------------------------------------------------------------------
    4159             :              */
    4160             :             /*      Convert into XML item or handle as a special TIFF tag. */
    4161             :             /* --------------------------------------------------------------------
    4162             :              */
    4163        5082 :             if (strlen(papszDomainList[iDomain]) == 0 && nBand == 0 &&
    4164        3726 :                 (STARTS_WITH_CI(pszItemName, "TIFFTAG_") ||
    4165        3665 :                  (EQUAL(pszItemName, "GEO_METADATA") &&
    4166        3664 :                   eProfile == GTiffProfile::GDALGEOTIFF) ||
    4167        3664 :                  (EQUAL(pszItemName, "TIFF_RSID") &&
    4168             :                   eProfile == GTiffProfile::GDALGEOTIFF)))
    4169             :             {
    4170          63 :                 if (EQUAL(pszItemName, "TIFFTAG_RESOLUTIONUNIT"))
    4171             :                 {
    4172             :                     // ResolutionUnit can't be 0, which is the default if
    4173             :                     // atoi() fails.  Set to 1=Unknown.
    4174           9 :                     int v = atoi(pszItemValue);
    4175           9 :                     if (!v)
    4176           1 :                         v = RESUNIT_NONE;
    4177           9 :                     TIFFSetField(hTIFF, TIFFTAG_RESOLUTIONUNIT, v);
    4178             :                 }
    4179             :                 else
    4180             :                 {
    4181          54 :                     bool bFoundTag = false;
    4182          54 :                     size_t iTag = 0;  // Used after for.
    4183          54 :                     const auto *pasTIFFTags = GTiffDataset::GetTIFFTags();
    4184         286 :                     for (; pasTIFFTags[iTag].pszTagName; ++iTag)
    4185             :                     {
    4186         286 :                         if (EQUAL(pszItemName, pasTIFFTags[iTag].pszTagName))
    4187             :                         {
    4188          54 :                             bFoundTag = true;
    4189          54 :                             break;
    4190             :                         }
    4191             :                     }
    4192             : 
    4193          54 :                     if (bFoundTag &&
    4194          54 :                         pasTIFFTags[iTag].eType == GTIFFTAGTYPE_STRING)
    4195          33 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4196             :                                      pszItemValue);
    4197          21 :                     else if (bFoundTag &&
    4198          21 :                              pasTIFFTags[iTag].eType == GTIFFTAGTYPE_FLOAT)
    4199          16 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4200             :                                      CPLAtof(pszItemValue));
    4201           5 :                     else if (bFoundTag &&
    4202           5 :                              pasTIFFTags[iTag].eType == GTIFFTAGTYPE_SHORT)
    4203           4 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4204             :                                      atoi(pszItemValue));
    4205           1 :                     else if (bFoundTag && pasTIFFTags[iTag].eType ==
    4206             :                                               GTIFFTAGTYPE_BYTE_STRING)
    4207             :                     {
    4208           1 :                         uint32_t nLen =
    4209           1 :                             static_cast<uint32_t>(strlen(pszItemValue));
    4210           1 :                         if (nLen)
    4211             :                         {
    4212           1 :                             TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal, nLen,
    4213             :                                          pszItemValue);
    4214           1 :                         }
    4215             :                     }
    4216             :                     else
    4217           0 :                         CPLError(CE_Warning, CPLE_NotSupported,
    4218             :                                  "%s metadata item is unhandled and "
    4219             :                                  "will not be written",
    4220             :                                  pszItemName);
    4221          63 :                 }
    4222             :             }
    4223        5019 :             else if (nBand == 0 && EQUAL(pszItemName, GDALMD_AREA_OR_POINT))
    4224             :             {
    4225             :                 /* Do nothing, handled elsewhere. */;
    4226             :             }
    4227             :             else
    4228             :             {
    4229        3080 :                 AppendMetadataItem(ppsRoot, ppsTail, pszItemName, pszItemValue,
    4230        3080 :                                    nBand, nullptr, papszDomainList[iDomain]);
    4231             :             }
    4232             : 
    4233        5082 :             CPLFree(pszItemName);
    4234             :         }
    4235             : 
    4236             :         /* --------------------------------------------------------------------
    4237             :          */
    4238             :         /*      Remove TIFFTAG_xxxxxx that are already set but no longer in */
    4239             :         /*      the metadata list (#5619) */
    4240             :         /* --------------------------------------------------------------------
    4241             :          */
    4242        2502 :         if (strlen(papszDomainList[iDomain]) == 0 && nBand == 0)
    4243             :         {
    4244        2215 :             const auto *pasTIFFTags = GTiffDataset::GetTIFFTags();
    4245       33225 :             for (size_t iTag = 0; pasTIFFTags[iTag].pszTagName; ++iTag)
    4246             :             {
    4247       31010 :                 uint32_t nCount = 0;
    4248       31010 :                 char *pszText = nullptr;
    4249       31010 :                 int16_t nVal = 0;
    4250       31010 :                 float fVal = 0.0f;
    4251             :                 const char *pszVal =
    4252       31010 :                     CSLFetchNameValue(papszMD, pasTIFFTags[iTag].pszTagName);
    4253       61957 :                 if (pszVal == nullptr &&
    4254       30947 :                     ((pasTIFFTags[iTag].eType == GTIFFTAGTYPE_STRING &&
    4255       17687 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4256       30939 :                                    &pszText)) ||
    4257       30939 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_SHORT &&
    4258        6632 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &nVal)) ||
    4259       30936 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_FLOAT &&
    4260        4414 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &fVal)) ||
    4261       30935 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_BYTE_STRING &&
    4262        2214 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &nCount,
    4263             :                                    &pszText))))
    4264             :                 {
    4265          13 :                     TIFFUnsetField(hTIFF, pasTIFFTags[iTag].nTagVal);
    4266             :                 }
    4267             :             }
    4268             :         }
    4269             :     }
    4270      311265 : }
    4271             : 
    4272             : /************************************************************************/
    4273             : /*                              WriteRPC()                              */
    4274             : /************************************************************************/
    4275             : 
    4276       10309 : void GTiffDataset::WriteRPC(GDALDataset *poSrcDS, TIFF *l_hTIFF,
    4277             :                             int bSrcIsGeoTIFF, GTiffProfile eProfile,
    4278             :                             const char *pszTIFFFilename,
    4279             :                             CSLConstList papszCreationOptions,
    4280             :                             bool bWriteOnlyInPAMIfNeeded)
    4281             : {
    4282             :     /* -------------------------------------------------------------------- */
    4283             :     /*      Handle RPC data written to TIFF RPCCoefficient tag, RPB file,   */
    4284             :     /*      RPCTEXT file or PAM.                                            */
    4285             :     /* -------------------------------------------------------------------- */
    4286       10309 :     CSLConstList papszRPCMD = poSrcDS->GetMetadata(GDAL_MDD_RPC);
    4287       10309 :     if (papszRPCMD != nullptr)
    4288             :     {
    4289          32 :         bool bRPCSerializedOtherWay = false;
    4290             : 
    4291          32 :         if (eProfile == GTiffProfile::GDALGEOTIFF)
    4292             :         {
    4293          20 :             if (!bWriteOnlyInPAMIfNeeded)
    4294          11 :                 GTiffDatasetWriteRPCTag(l_hTIFF, papszRPCMD);
    4295          20 :             bRPCSerializedOtherWay = true;
    4296             :         }
    4297             : 
    4298             :         // Write RPB file if explicitly asked, or if a non GDAL specific
    4299             :         // profile is selected and RPCTXT is not asked.
    4300             :         bool bRPBExplicitlyAsked =
    4301          32 :             CPLFetchBool(papszCreationOptions, "RPB", false);
    4302             :         bool bRPBExplicitlyDenied =
    4303          32 :             !CPLFetchBool(papszCreationOptions, "RPB", true);
    4304          44 :         if ((eProfile != GTiffProfile::GDALGEOTIFF &&
    4305          12 :              !CPLFetchBool(papszCreationOptions, "RPCTXT", false) &&
    4306          44 :              !bRPBExplicitlyDenied) ||
    4307             :             bRPBExplicitlyAsked)
    4308             :         {
    4309           8 :             if (!bWriteOnlyInPAMIfNeeded)
    4310           4 :                 GDALWriteRPBFile(pszTIFFFilename, papszRPCMD);
    4311           8 :             bRPCSerializedOtherWay = true;
    4312             :         }
    4313             : 
    4314          32 :         if (CPLFetchBool(papszCreationOptions, "RPCTXT", false))
    4315             :         {
    4316           2 :             if (!bWriteOnlyInPAMIfNeeded)
    4317           1 :                 GDALWriteRPCTXTFile(pszTIFFFilename, papszRPCMD);
    4318           2 :             bRPCSerializedOtherWay = true;
    4319             :         }
    4320             : 
    4321          32 :         if (!bRPCSerializedOtherWay && bWriteOnlyInPAMIfNeeded && bSrcIsGeoTIFF)
    4322           1 :             cpl::down_cast<GTiffDataset *>(poSrcDS)
    4323           1 :                 ->GDALPamDataset::SetMetadata(papszRPCMD, GDAL_MDD_RPC);
    4324             :     }
    4325       10309 : }
    4326             : 
    4327             : /************************************************************************/
    4328             : /*                           WriteMetadata()                            */
    4329             : /************************************************************************/
    4330             : 
    4331        8170 : bool GTiffDataset::WriteMetadata(GDALDataset *poSrcDS, TIFF *l_hTIFF,
    4332             :                                  bool bSrcIsGeoTIFF, GTiffProfile eProfile,
    4333             :                                  const char *pszTIFFFilename,
    4334             :                                  CSLConstList papszCreationOptions,
    4335             :                                  bool bExcludeRPBandIMGFileWriting)
    4336             : 
    4337             : {
    4338             :     /* -------------------------------------------------------------------- */
    4339             :     /*      Convert all the remaining metadata into a simple XML            */
    4340             :     /*      format.                                                         */
    4341             :     /* -------------------------------------------------------------------- */
    4342        8170 :     CPLXMLNode *psRoot = nullptr;
    4343        8170 :     CPLXMLNode *psTail = nullptr;
    4344             : 
    4345             :     const char *pszCopySrcMDD =
    4346        8170 :         CSLFetchNameValueDef(papszCreationOptions, "COPY_SRC_MDD", "AUTO");
    4347             :     char **papszSrcMDD =
    4348        8170 :         CSLFetchNameValueMultiple(papszCreationOptions, "SRC_MDD");
    4349             : 
    4350             :     GTiffDataset *poSrcDSGTiff =
    4351        8170 :         bSrcIsGeoTIFF ? cpl::down_cast<GTiffDataset *>(poSrcDS) : nullptr;
    4352             : 
    4353        8170 :     if (poSrcDSGTiff)
    4354             :     {
    4355        6004 :         WriteMDMetadata(&poSrcDSGTiff->m_oGTiffMDMD, l_hTIFF, &psRoot, &psTail,
    4356             :                         0, eProfile);
    4357             :     }
    4358             :     else
    4359             :     {
    4360        2166 :         if (EQUAL(pszCopySrcMDD, "AUTO") || CPLTestBool(pszCopySrcMDD) ||
    4361             :             papszSrcMDD)
    4362             :         {
    4363        4326 :             GDALMultiDomainMetadata l_oMDMD;
    4364             :             {
    4365        2163 :                 CSLConstList papszMD = poSrcDS->GetMetadata();
    4366        2167 :                 if (CSLCount(papszMD) > 0 &&
    4367           4 :                     (!papszSrcMDD || CSLFindString(papszSrcMDD, "") >= 0 ||
    4368           2 :                      CSLFindString(papszSrcMDD, "_DEFAULT_") >= 0))
    4369             :                 {
    4370        1645 :                     l_oMDMD.SetMetadata(papszMD);
    4371             :                 }
    4372             :             }
    4373             : 
    4374        2163 :             if (EQUAL(pszCopySrcMDD, "AUTO") && !papszSrcMDD)
    4375             :             {
    4376             :                 // Propagate ISIS3 or VICAR metadata
    4377        6462 :                 for (const char *pszMDD : {"json:ISIS3", "json:VICAR"})
    4378             :                 {
    4379        4308 :                     CSLConstList papszMD = poSrcDS->GetMetadata(pszMDD);
    4380        4308 :                     if (papszMD)
    4381             :                     {
    4382           5 :                         l_oMDMD.SetMetadata(papszMD, pszMDD);
    4383             :                     }
    4384             :                 }
    4385             :             }
    4386             : 
    4387        2163 :             if ((!EQUAL(pszCopySrcMDD, "AUTO") && CPLTestBool(pszCopySrcMDD)) ||
    4388             :                 papszSrcMDD)
    4389             :             {
    4390           9 :                 char **papszDomainList = poSrcDS->GetMetadataDomainList();
    4391          39 :                 for (CSLConstList papszIter = papszDomainList;
    4392          39 :                      papszIter && *papszIter; ++papszIter)
    4393             :                 {
    4394          30 :                     const char *pszDomain = *papszIter;
    4395          46 :                     if (pszDomain[0] != 0 &&
    4396          16 :                         (!papszSrcMDD ||
    4397          16 :                          CSLFindString(papszSrcMDD, pszDomain) >= 0))
    4398             :                     {
    4399          12 :                         l_oMDMD.SetMetadata(poSrcDS->GetMetadata(pszDomain),
    4400             :                                             pszDomain);
    4401             :                     }
    4402             :                 }
    4403           9 :                 CSLDestroy(papszDomainList);
    4404             :             }
    4405             : 
    4406        2163 :             WriteMDMetadata(&l_oMDMD, l_hTIFF, &psRoot, &psTail, 0, eProfile);
    4407             :         }
    4408             :     }
    4409             : 
    4410        8170 :     if (!bExcludeRPBandIMGFileWriting &&
    4411        5998 :         (!poSrcDSGTiff || poSrcDSGTiff->m_poBaseDS == nullptr))
    4412             :     {
    4413        8159 :         WriteRPC(poSrcDS, l_hTIFF, bSrcIsGeoTIFF, eProfile, pszTIFFFilename,
    4414             :                  papszCreationOptions);
    4415             : 
    4416             :         /* ------------------------------------------------------------------ */
    4417             :         /*      Handle metadata data written to an IMD file. */
    4418             :         /* ------------------------------------------------------------------ */
    4419        8159 :         CSLConstList papszIMDMD = poSrcDS->GetMetadata(GDAL_MDD_IMD);
    4420        8159 :         if (papszIMDMD != nullptr)
    4421             :         {
    4422          20 :             GDALWriteIMDFile(pszTIFFFilename, papszIMDMD);
    4423             :         }
    4424             :     }
    4425             : 
    4426        8170 :     uint16_t nPhotometric = 0;
    4427        8170 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric)))
    4428           1 :         nPhotometric = PHOTOMETRIC_MINISBLACK;
    4429             : 
    4430        8170 :     const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
    4431             :         GDALDataset::ToHandle(poSrcDS), nPhotometric, papszCreationOptions);
    4432             : 
    4433             :     /* -------------------------------------------------------------------- */
    4434             :     /*      We also need to address band specific metadata, and special     */
    4435             :     /*      "role" metadata.                                                */
    4436             :     /* -------------------------------------------------------------------- */
    4437      316273 :     for (int nBand = 1; nBand <= poSrcDS->GetRasterCount(); ++nBand)
    4438             :     {
    4439      308103 :         GDALRasterBand *poBand = poSrcDS->GetRasterBand(nBand);
    4440             : 
    4441      308103 :         if (bSrcIsGeoTIFF)
    4442             :         {
    4443             :             GTiffRasterBand *poSrcBandGTiff =
    4444      303006 :                 cpl::down_cast<GTiffRasterBand *>(poBand);
    4445      303006 :             assert(poSrcBandGTiff);
    4446      303006 :             WriteMDMetadata(&poSrcBandGTiff->m_oGTiffMDMD, l_hTIFF, &psRoot,
    4447             :                             &psTail, nBand, eProfile);
    4448             :         }
    4449             :         else
    4450             :         {
    4451       10194 :             GDALMultiDomainMetadata l_oMDMD;
    4452        5097 :             bool bOMDMDSet = false;
    4453             : 
    4454        5097 :             if (EQUAL(pszCopySrcMDD, "AUTO") && !papszSrcMDD)
    4455             :             {
    4456       15255 :                 for (const char *pszDomain : {"", GDAL_MDD_IMAGERY})
    4457             :                 {
    4458       10170 :                     if (CSLConstList papszMD = poBand->GetMetadata(pszDomain))
    4459             :                     {
    4460          90 :                         if (papszMD[0])
    4461             :                         {
    4462          90 :                             bOMDMDSet = true;
    4463          90 :                             l_oMDMD.SetMetadata(papszMD, pszDomain);
    4464             :                         }
    4465             :                     }
    4466        5085 :                 }
    4467             :             }
    4468          12 :             else if (CPLTestBool(pszCopySrcMDD) || papszSrcMDD)
    4469             :             {
    4470           9 :                 char **papszDomainList = poBand->GetMetadataDomainList();
    4471           3 :                 for (const char *pszDomain :
    4472          15 :                      cpl::Iterate(CSLConstList(papszDomainList)))
    4473             :                 {
    4474           9 :                     if (pszDomain[0] != 0 &&
    4475           5 :                         !EQUAL(pszDomain, GDAL_MDD_IMAGE_STRUCTURE) &&
    4476           2 :                         (!papszSrcMDD ||
    4477           2 :                          CSLFindString(papszSrcMDD, pszDomain) >= 0))
    4478             :                     {
    4479           2 :                         bOMDMDSet = true;
    4480           2 :                         l_oMDMD.SetMetadata(poBand->GetMetadata(pszDomain),
    4481             :                                             pszDomain);
    4482             :                     }
    4483             :                 }
    4484           9 :                 CSLDestroy(papszDomainList);
    4485             :             }
    4486             : 
    4487        5097 :             if (bOMDMDSet)
    4488             :             {
    4489          92 :                 WriteMDMetadata(&l_oMDMD, l_hTIFF, &psRoot, &psTail, nBand,
    4490             :                                 eProfile);
    4491             :             }
    4492             :         }
    4493             : 
    4494      308103 :         const double dfOffset = poBand->GetOffset();
    4495      308103 :         const double dfScale = poBand->GetScale();
    4496      308103 :         bool bGeoTIFFScaleOffsetInZ = false;
    4497      308103 :         GDALGeoTransform gt;
    4498             :         // Check if we have already encoded scale/offset in the GeoTIFF tags
    4499      314322 :         if (poSrcDS->GetGeoTransform(gt) == CE_None && gt.xrot == 0.0 &&
    4500        6203 :             gt.yrot == 0.0 && gt.yscale < 0.0 && poSrcDS->GetSpatialRef() &&
    4501      314329 :             poSrcDS->GetSpatialRef()->IsVertical() &&
    4502           7 :             poSrcDS->GetRasterCount() == 1)
    4503             :         {
    4504           7 :             bGeoTIFFScaleOffsetInZ = true;
    4505             :         }
    4506             : 
    4507      308103 :         if ((dfOffset != 0.0 || dfScale != 1.0) && !bGeoTIFFScaleOffsetInZ)
    4508             :         {
    4509          25 :             char szValue[128] = {};
    4510             : 
    4511          25 :             CPLsnprintf(szValue, sizeof(szValue), "%.17g", dfOffset);
    4512          25 :             AppendMetadataItem(&psRoot, &psTail, "OFFSET", szValue, nBand,
    4513             :                                "offset", "");
    4514          25 :             CPLsnprintf(szValue, sizeof(szValue), "%.17g", dfScale);
    4515          25 :             AppendMetadataItem(&psRoot, &psTail, "SCALE", szValue, nBand,
    4516             :                                "scale", "");
    4517             :         }
    4518             : 
    4519      308103 :         const char *pszUnitType = poBand->GetUnitType();
    4520      308103 :         if (pszUnitType != nullptr && pszUnitType[0] != '\0')
    4521             :         {
    4522          40 :             bool bWriteUnit = true;
    4523          40 :             auto poSRS = poSrcDS->GetSpatialRef();
    4524          40 :             if (poSRS && poSRS->IsCompound())
    4525             :             {
    4526           2 :                 const char *pszVertUnit = nullptr;
    4527           2 :                 poSRS->GetTargetLinearUnits("COMPD_CS|VERT_CS", &pszVertUnit);
    4528           2 :                 if (pszVertUnit && EQUAL(pszVertUnit, pszUnitType))
    4529             :                 {
    4530           2 :                     bWriteUnit = false;
    4531             :                 }
    4532             :             }
    4533          40 :             if (bWriteUnit)
    4534             :             {
    4535          38 :                 AppendMetadataItem(&psRoot, &psTail, "UNITTYPE", pszUnitType,
    4536             :                                    nBand, "unittype", "");
    4537             :             }
    4538             :         }
    4539             : 
    4540      308103 :         if (strlen(poBand->GetDescription()) > 0)
    4541             :         {
    4542          25 :             AppendMetadataItem(&psRoot, &psTail, "DESCRIPTION",
    4543          25 :                                poBand->GetDescription(), nBand, "description",
    4544             :                                "");
    4545             :         }
    4546             : 
    4547      308320 :         if (!bStandardColorInterp &&
    4548         217 :             !(nBand <= 3 && EQUAL(CSLFetchNameValueDef(papszCreationOptions,
    4549             :                                                        "PHOTOMETRIC", ""),
    4550             :                                   "RGB")))
    4551             :         {
    4552         250 :             AppendMetadataItem(&psRoot, &psTail, "COLORINTERP",
    4553             :                                GDALGetColorInterpretationName(
    4554         250 :                                    poBand->GetColorInterpretation()),
    4555             :                                nBand, "colorinterp", "");
    4556             :         }
    4557             :     }
    4558             : 
    4559        8170 :     CSLDestroy(papszSrcMDD);
    4560             : 
    4561             :     const char *pszTilingSchemeName =
    4562        8170 :         CSLFetchNameValue(papszCreationOptions, "@TILING_SCHEME_NAME");
    4563        8170 :     if (pszTilingSchemeName)
    4564             :     {
    4565          23 :         AppendMetadataItem(&psRoot, &psTail, "NAME", pszTilingSchemeName, 0,
    4566             :                            nullptr, "TILING_SCHEME");
    4567             : 
    4568          23 :         const char *pszZoomLevel = CSLFetchNameValue(
    4569             :             papszCreationOptions, "@TILING_SCHEME_ZOOM_LEVEL");
    4570          23 :         if (pszZoomLevel)
    4571             :         {
    4572          23 :             AppendMetadataItem(&psRoot, &psTail, "ZOOM_LEVEL", pszZoomLevel, 0,
    4573             :                                nullptr, "TILING_SCHEME");
    4574             :         }
    4575             : 
    4576          23 :         const char *pszAlignedLevels = CSLFetchNameValue(
    4577             :             papszCreationOptions, "@TILING_SCHEME_ALIGNED_LEVELS");
    4578          23 :         if (pszAlignedLevels)
    4579             :         {
    4580           4 :             AppendMetadataItem(&psRoot, &psTail, "ALIGNED_LEVELS",
    4581             :                                pszAlignedLevels, 0, nullptr, "TILING_SCHEME");
    4582             :         }
    4583             :     }
    4584             : 
    4585        8170 :     if (const char *pszOverviewResampling =
    4586        8170 :             CSLFetchNameValue(papszCreationOptions, "@OVERVIEW_RESAMPLING"))
    4587             :     {
    4588          41 :         AppendMetadataItem(&psRoot, &psTail, "OVERVIEW_RESAMPLING",
    4589             :                            pszOverviewResampling, 0, nullptr,
    4590             :                            GDAL_MDD_IMAGE_STRUCTURE);
    4591             :     }
    4592             : 
    4593             :     /* -------------------------------------------------------------------- */
    4594             :     /*      Write information about some codecs.                            */
    4595             :     /* -------------------------------------------------------------------- */
    4596        8170 :     if (CPLTestBool(
    4597             :             CPLGetConfigOption("GTIFF_WRITE_IMAGE_STRUCTURE_METADATA", "YES")))
    4598             :     {
    4599             :         const char *pszTileInterleave =
    4600        8165 :             CSLFetchNameValue(papszCreationOptions, "@TILE_INTERLEAVE");
    4601        8165 :         if (pszTileInterleave && CPLTestBool(pszTileInterleave))
    4602             :         {
    4603           7 :             AppendMetadataItem(&psRoot, &psTail, GDALMD_INTERLEAVE, "TILE", 0,
    4604             :                                nullptr, GDAL_MDD_IMAGE_STRUCTURE);
    4605             :         }
    4606             : 
    4607             :         const char *pszCompress =
    4608        8165 :             CSLFetchNameValue(papszCreationOptions, "COMPRESS");
    4609        8165 :         if (pszCompress && EQUAL(pszCompress, "WEBP"))
    4610             :         {
    4611          31 :             if (GTiffGetWebPLossless(papszCreationOptions))
    4612             :             {
    4613           6 :                 AppendMetadataItem(&psRoot, &psTail,
    4614             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4615             :                                    nullptr, GDAL_MDD_IMAGE_STRUCTURE);
    4616             :             }
    4617             :             else
    4618             :             {
    4619          25 :                 AppendMetadataItem(
    4620             :                     &psRoot, &psTail, "WEBP_LEVEL",
    4621          25 :                     CPLSPrintf("%d", GTiffGetWebPLevel(papszCreationOptions)),
    4622             :                     0, nullptr, GDAL_MDD_IMAGE_STRUCTURE);
    4623             :             }
    4624             :         }
    4625        8134 :         else if (pszCompress && STARTS_WITH_CI(pszCompress, "LERC"))
    4626             :         {
    4627             :             const double dfMaxZError =
    4628          97 :                 GTiffGetLERCMaxZError(papszCreationOptions);
    4629             :             const double dfMaxZErrorOverview =
    4630          97 :                 GTiffGetLERCMaxZErrorOverview(papszCreationOptions);
    4631          97 :             if (dfMaxZError == 0.0 && dfMaxZErrorOverview == 0.0)
    4632             :             {
    4633          83 :                 AppendMetadataItem(&psRoot, &psTail,
    4634             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4635             :                                    nullptr, GDAL_MDD_IMAGE_STRUCTURE);
    4636             :             }
    4637             :             else
    4638             :             {
    4639          14 :                 AppendMetadataItem(&psRoot, &psTail, "MAX_Z_ERROR",
    4640             :                                    CSLFetchNameValueDef(papszCreationOptions,
    4641             :                                                         "MAX_Z_ERROR", ""),
    4642             :                                    0, nullptr, GDAL_MDD_IMAGE_STRUCTURE);
    4643          14 :                 if (dfMaxZError != dfMaxZErrorOverview)
    4644             :                 {
    4645           3 :                     AppendMetadataItem(
    4646             :                         &psRoot, &psTail, "MAX_Z_ERROR_OVERVIEW",
    4647             :                         CSLFetchNameValueDef(papszCreationOptions,
    4648             :                                              "MAX_Z_ERROR_OVERVIEW", ""),
    4649             :                         0, nullptr, GDAL_MDD_IMAGE_STRUCTURE);
    4650             :                 }
    4651          97 :             }
    4652             :         }
    4653             : #if HAVE_JXL
    4654        8037 :         else if (pszCompress && EQUAL(pszCompress, "JXL"))
    4655             :         {
    4656         101 :             float fDistance = 0.0f;
    4657         101 :             if (GTiffGetJXLLossless(papszCreationOptions))
    4658             :             {
    4659          82 :                 AppendMetadataItem(&psRoot, &psTail,
    4660             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4661             :                                    nullptr, GDAL_MDD_IMAGE_STRUCTURE);
    4662             :             }
    4663             :             else
    4664             :             {
    4665          19 :                 fDistance = GTiffGetJXLDistance(papszCreationOptions);
    4666          19 :                 AppendMetadataItem(
    4667             :                     &psRoot, &psTail, "JXL_DISTANCE",
    4668             :                     CPLSPrintf("%f", static_cast<double>(fDistance)), 0,
    4669             :                     nullptr, GDAL_MDD_IMAGE_STRUCTURE);
    4670             :             }
    4671             :             const float fAlphaDistance =
    4672         101 :                 GTiffGetJXLAlphaDistance(papszCreationOptions);
    4673         101 :             if (fAlphaDistance >= 0.0f && fAlphaDistance != fDistance)
    4674             :             {
    4675           2 :                 AppendMetadataItem(
    4676             :                     &psRoot, &psTail, "JXL_ALPHA_DISTANCE",
    4677             :                     CPLSPrintf("%f", static_cast<double>(fAlphaDistance)), 0,
    4678             :                     nullptr, GDAL_MDD_IMAGE_STRUCTURE);
    4679             :             }
    4680         101 :             AppendMetadataItem(
    4681             :                 &psRoot, &psTail, "JXL_EFFORT",
    4682             :                 CPLSPrintf("%d", GTiffGetJXLEffort(papszCreationOptions)), 0,
    4683             :                 nullptr, GDAL_MDD_IMAGE_STRUCTURE);
    4684             :         }
    4685             : #endif
    4686             :     }
    4687             : 
    4688        8170 :     if (!CPLTestBool(CPLGetConfigOption("GTIFF_WRITE_RAT_TO_PAM", "NO")))
    4689             :     {
    4690      316267 :         for (int nBand = 1; nBand <= poSrcDS->GetRasterCount(); ++nBand)
    4691             :         {
    4692      308100 :             GDALRasterAttributeTable *poRAT = nullptr;
    4693      308100 :             if (poSrcDSGTiff)
    4694             :             {
    4695      303004 :                 auto poBand = cpl::down_cast<GTiffRasterBand *>(
    4696             :                     poSrcDSGTiff->GetRasterBand(nBand));
    4697             :                 // Scenario of https://github.com/OSGeo/gdal/issues/13930
    4698             :                 // Do not try to fetch the RAT from auxiliary files if creating
    4699             :                 // a new GeoTIFF file
    4700      303004 :                 if (poBand->m_bRATSet)
    4701         106 :                     poRAT = poBand->GetDefaultRAT();
    4702             :             }
    4703             :             else
    4704             :             {
    4705        5096 :                 poRAT = poSrcDS->GetRasterBand(nBand)->GetDefaultRAT();
    4706             :             }
    4707      308100 :             if (poRAT)
    4708             :             {
    4709          23 :                 auto psSerializedRAT = poRAT->Serialize();
    4710          23 :                 if (psSerializedRAT)
    4711             :                 {
    4712          21 :                     AppendMetadataItem(
    4713             :                         &psRoot, &psTail, DEFAULT_RASTER_ATTRIBUTE_TABLE,
    4714             :                         nullptr, psSerializedRAT, nBand, RAT_ROLE, nullptr);
    4715             :                 }
    4716             :             }
    4717             :         }
    4718             :     }
    4719             : 
    4720             :     /* -------------------------------------------------------------------- */
    4721             :     /*      Write out the generic XML metadata if there is any.             */
    4722             :     /* -------------------------------------------------------------------- */
    4723        8170 :     if (psRoot != nullptr)
    4724             :     {
    4725         765 :         bool bRet = true;
    4726             : 
    4727         765 :         if (eProfile == GTiffProfile::GDALGEOTIFF)
    4728             :         {
    4729         748 :             char *pszXML_MD = CPLSerializeXMLTree(psRoot);
    4730         748 :             TIFFSetField(l_hTIFF, TIFFTAG_GDAL_METADATA, pszXML_MD);
    4731         748 :             CPLFree(pszXML_MD);
    4732             :         }
    4733             :         else
    4734             :         {
    4735          17 :             if (bSrcIsGeoTIFF)
    4736          11 :                 cpl::down_cast<GTiffDataset *>(poSrcDS)->PushMetadataToPam();
    4737             :             else
    4738           6 :                 bRet = false;
    4739             :         }
    4740             : 
    4741         765 :         CPLDestroyXMLNode(psRoot);
    4742             : 
    4743         765 :         return bRet;
    4744             :     }
    4745             : 
    4746             :     // If we have no more metadata but it existed before,
    4747             :     // remove the GDAL_METADATA tag.
    4748        7405 :     if (eProfile == GTiffProfile::GDALGEOTIFF)
    4749             :     {
    4750        7381 :         char *pszText = nullptr;
    4751        7381 :         if (TIFFGetField(l_hTIFF, TIFFTAG_GDAL_METADATA, &pszText))
    4752             :         {
    4753           7 :             TIFFUnsetField(l_hTIFF, TIFFTAG_GDAL_METADATA);
    4754             :         }
    4755             :     }
    4756             : 
    4757        7405 :     return true;
    4758             : }
    4759             : 
    4760             : /************************************************************************/
    4761             : /*                         PushMetadataToPam()                          */
    4762             : /*                                                                      */
    4763             : /*      When producing a strict profile TIFF or if our aggregate        */
    4764             : /*      metadata is too big for a single tiff tag we may end up         */
    4765             : /*      needing to write it via the PAM mechanisms.  This method        */
    4766             : /*      copies all the appropriate metadata into the PAM level          */
    4767             : /*      metadata object but with special care to avoid copying          */
    4768             : /*      metadata handled in other ways in TIFF format.                  */
    4769             : /************************************************************************/
    4770             : 
    4771          17 : void GTiffDataset::PushMetadataToPam()
    4772             : 
    4773             : {
    4774          17 :     if (GetPamFlags() & GPF_DISABLED)
    4775           0 :         return;
    4776             : 
    4777          17 :     const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
    4778          17 :         GDALDataset::ToHandle(this), m_nPhotometric, m_papszCreationOptions);
    4779             : 
    4780          55 :     for (int nBand = 0; nBand <= GetRasterCount(); ++nBand)
    4781             :     {
    4782          38 :         GDALMultiDomainMetadata *poSrcMDMD = nullptr;
    4783          38 :         GTiffRasterBand *poBand = nullptr;
    4784             : 
    4785          38 :         if (nBand == 0)
    4786             :         {
    4787          17 :             poSrcMDMD = &(this->m_oGTiffMDMD);
    4788             :         }
    4789             :         else
    4790             :         {
    4791          21 :             poBand = cpl::down_cast<GTiffRasterBand *>(GetRasterBand(nBand));
    4792          21 :             poSrcMDMD = &(poBand->m_oGTiffMDMD);
    4793             :         }
    4794             : 
    4795             :         /* --------------------------------------------------------------------
    4796             :          */
    4797             :         /*      Loop over the available domains. */
    4798             :         /* --------------------------------------------------------------------
    4799             :          */
    4800          38 :         CSLConstList papszDomainList = poSrcMDMD->GetDomainList();
    4801          74 :         for (int iDomain = 0; papszDomainList && papszDomainList[iDomain];
    4802             :              ++iDomain)
    4803             :         {
    4804          36 :             char **papszMD = poSrcMDMD->GetMetadata(papszDomainList[iDomain]);
    4805             : 
    4806          36 :             if (EQUAL(papszDomainList[iDomain], GDAL_MDD_RPC) ||
    4807          36 :                 EQUAL(papszDomainList[iDomain], GDAL_MDD_IMD) ||
    4808          36 :                 EQUAL(papszDomainList[iDomain], "_temporary_") ||
    4809          36 :                 EQUAL(papszDomainList[iDomain], GDAL_MDD_IMAGE_STRUCTURE) ||
    4810          19 :                 EQUAL(papszDomainList[iDomain], "COLOR_PROFILE"))
    4811          17 :                 continue;
    4812             : 
    4813          19 :             papszMD = CSLDuplicate(papszMD);
    4814             : 
    4815          69 :             for (int i = CSLCount(papszMD) - 1; i >= 0; --i)
    4816             :             {
    4817          50 :                 if (STARTS_WITH_CI(papszMD[i], "TIFFTAG_") ||
    4818          50 :                     EQUALN(papszMD[i], GDALMD_AREA_OR_POINT,
    4819             :                            strlen(GDALMD_AREA_OR_POINT)))
    4820           4 :                     papszMD = CSLRemoveStrings(papszMD, i, 1, nullptr);
    4821             :             }
    4822             : 
    4823          19 :             if (!poBand)
    4824          10 :                 GDALPamDataset::SetMetadata(papszMD, papszDomainList[iDomain]);
    4825             :             else
    4826           9 :                 poBand->GDALPamRasterBand::SetMetadata(
    4827           9 :                     papszMD, papszDomainList[iDomain]);
    4828             : 
    4829          19 :             CSLDestroy(papszMD);
    4830             :         }
    4831             : 
    4832             :         /* --------------------------------------------------------------------
    4833             :          */
    4834             :         /*      Handle some "special domain" stuff. */
    4835             :         /* --------------------------------------------------------------------
    4836             :          */
    4837          38 :         if (poBand != nullptr)
    4838             :         {
    4839          21 :             poBand->GDALPamRasterBand::SetOffset(poBand->GetOffset());
    4840          21 :             poBand->GDALPamRasterBand::SetScale(poBand->GetScale());
    4841          21 :             poBand->GDALPamRasterBand::SetUnitType(poBand->GetUnitType());
    4842          21 :             poBand->GDALPamRasterBand::SetDescription(poBand->GetDescription());
    4843          21 :             if (!bStandardColorInterp)
    4844             :             {
    4845           3 :                 poBand->GDALPamRasterBand::SetColorInterpretation(
    4846           3 :                     poBand->GetColorInterpretation());
    4847             :             }
    4848             :         }
    4849             :     }
    4850          17 :     MarkPamDirty();
    4851             : }
    4852             : 
    4853             : /************************************************************************/
    4854             : /*                          WriteNoDataValue()                          */
    4855             : /************************************************************************/
    4856             : 
    4857         532 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, double dfNoData)
    4858             : 
    4859             : {
    4860        1064 :     CPLString osVal(GTiffFormatGDALNoDataTagValue(dfNoData));
    4861         532 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA, osVal.c_str());
    4862         532 : }
    4863             : 
    4864           5 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, int64_t nNoData)
    4865             : 
    4866             : {
    4867           5 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA,
    4868             :                  CPLSPrintf(CPL_FRMT_GIB, static_cast<GIntBig>(nNoData)));
    4869           5 : }
    4870             : 
    4871           5 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, uint64_t nNoData)
    4872             : 
    4873             : {
    4874           5 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA,
    4875             :                  CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoData)));
    4876           5 : }
    4877             : 
    4878             : /************************************************************************/
    4879             : /*                          UnsetNoDataValue()                          */
    4880             : /************************************************************************/
    4881             : 
    4882          16 : void GTiffDataset::UnsetNoDataValue(TIFF *l_hTIFF)
    4883             : 
    4884             : {
    4885          16 :     TIFFUnsetField(l_hTIFF, TIFFTAG_GDAL_NODATA);
    4886          16 : }
    4887             : 
    4888             : /************************************************************************/
    4889             : /*                             SaveICCProfile()                         */
    4890             : /*                                                                      */
    4891             : /*      Save ICC Profile or colorimetric data into file                 */
    4892             : /* pDS:                                                                 */
    4893             : /*      Dataset that contains the metadata with the ICC or colorimetric */
    4894             : /*      data. If this argument is specified, all other arguments are    */
    4895             : /*      ignored. Set them to NULL or 0.                                 */
    4896             : /* hTIFF:                                                               */
    4897             : /*      Pointer to TIFF handle. Only needed if pDS is NULL or           */
    4898             : /*      pDS->m_hTIFF is NULL.                                             */
    4899             : /* papszParamList:                                                       */
    4900             : /*      Options containing the ICC profile or colorimetric metadata.    */
    4901             : /*      Ignored if pDS is not NULL.                                     */
    4902             : /* nBitsPerSample:                                                      */
    4903             : /*      Bits per sample. Ignored if pDS is not NULL.                    */
    4904             : /************************************************************************/
    4905             : 
    4906        9915 : void GTiffDataset::SaveICCProfile(GTiffDataset *pDS, TIFF *l_hTIFF,
    4907             :                                   CSLConstList papszParamList,
    4908             :                                   uint32_t l_nBitsPerSample)
    4909             : {
    4910        9915 :     if ((pDS != nullptr) && (pDS->eAccess != GA_Update))
    4911           0 :         return;
    4912             : 
    4913        9915 :     if (l_hTIFF == nullptr)
    4914             :     {
    4915           2 :         if (pDS == nullptr)
    4916           0 :             return;
    4917             : 
    4918           2 :         l_hTIFF = pDS->m_hTIFF;
    4919           2 :         if (l_hTIFF == nullptr)
    4920           0 :             return;
    4921             :     }
    4922             : 
    4923        9915 :     if ((papszParamList == nullptr) && (pDS == nullptr))
    4924        4929 :         return;
    4925             : 
    4926             :     const char *pszICCProfile =
    4927             :         (pDS != nullptr)
    4928        4986 :             ? pDS->GetMetadataItem("SOURCE_ICC_PROFILE", "COLOR_PROFILE")
    4929        4984 :             : CSLFetchNameValue(papszParamList, "SOURCE_ICC_PROFILE");
    4930        4986 :     if (pszICCProfile != nullptr)
    4931             :     {
    4932           8 :         char *pEmbedBuffer = CPLStrdup(pszICCProfile);
    4933             :         int32_t nEmbedLen =
    4934           8 :             CPLBase64DecodeInPlace(reinterpret_cast<GByte *>(pEmbedBuffer));
    4935             : 
    4936           8 :         TIFFSetField(l_hTIFF, TIFFTAG_ICCPROFILE, nEmbedLen, pEmbedBuffer);
    4937             : 
    4938           8 :         CPLFree(pEmbedBuffer);
    4939             :     }
    4940             :     else
    4941             :     {
    4942             :         // Output colorimetric data.
    4943        4978 :         float pCHR[6] = {};     // Primaries.
    4944        4978 :         uint16_t pTXR[6] = {};  // Transfer range.
    4945        4978 :         const char *pszCHRNames[] = {"SOURCE_PRIMARIES_RED",
    4946             :                                      "SOURCE_PRIMARIES_GREEN",
    4947             :                                      "SOURCE_PRIMARIES_BLUE"};
    4948        4978 :         const char *pszTXRNames[] = {"TIFFTAG_TRANSFERRANGE_BLACK",
    4949             :                                      "TIFFTAG_TRANSFERRANGE_WHITE"};
    4950             : 
    4951             :         // Output chromacities.
    4952        4978 :         bool bOutputCHR = true;
    4953        4993 :         for (int i = 0; i < 3 && bOutputCHR; ++i)
    4954             :         {
    4955             :             const char *pszColorProfile =
    4956             :                 (pDS != nullptr)
    4957        4988 :                     ? pDS->GetMetadataItem(pszCHRNames[i], "COLOR_PROFILE")
    4958        4985 :                     : CSLFetchNameValue(papszParamList, pszCHRNames[i]);
    4959        4988 :             if (pszColorProfile == nullptr)
    4960             :             {
    4961        4973 :                 bOutputCHR = false;
    4962        4973 :                 break;
    4963             :             }
    4964             : 
    4965             :             const CPLStringList aosTokens(CSLTokenizeString2(
    4966             :                 pszColorProfile, ",",
    4967             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    4968          15 :                     CSLT_STRIPENDSPACES));
    4969             : 
    4970          15 :             if (aosTokens.size() != 3)
    4971             :             {
    4972           0 :                 bOutputCHR = false;
    4973           0 :                 break;
    4974             :             }
    4975             : 
    4976          60 :             for (int j = 0; j < 3; ++j)
    4977             :             {
    4978          45 :                 float v = static_cast<float>(CPLAtof(aosTokens[j]));
    4979             : 
    4980          45 :                 if (j == 2)
    4981             :                 {
    4982             :                     // Last term of xyY color must be 1.0.
    4983          15 :                     if (v != 1.0f)
    4984             :                     {
    4985           0 :                         bOutputCHR = false;
    4986           0 :                         break;
    4987             :                     }
    4988             :                 }
    4989             :                 else
    4990             :                 {
    4991          30 :                     pCHR[i * 2 + j] = v;
    4992             :                 }
    4993             :             }
    4994             :         }
    4995             : 
    4996        4978 :         if (bOutputCHR)
    4997             :         {
    4998           5 :             TIFFSetField(l_hTIFF, TIFFTAG_PRIMARYCHROMATICITIES, pCHR);
    4999             :         }
    5000             : 
    5001             :         // Output whitepoint.
    5002             :         const char *pszSourceWhitePoint =
    5003             :             (pDS != nullptr)
    5004        4978 :                 ? pDS->GetMetadataItem("SOURCE_WHITEPOINT", "COLOR_PROFILE")
    5005        4977 :                 : CSLFetchNameValue(papszParamList, "SOURCE_WHITEPOINT");
    5006        4978 :         if (pszSourceWhitePoint != nullptr)
    5007             :         {
    5008             :             const CPLStringList aosTokens(CSLTokenizeString2(
    5009             :                 pszSourceWhitePoint, ",",
    5010             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5011          10 :                     CSLT_STRIPENDSPACES));
    5012             : 
    5013           5 :             bool bOutputWhitepoint = true;
    5014           5 :             float pWP[2] = {0.0f, 0.0f};  // Whitepoint
    5015           5 :             if (aosTokens.size() != 3)
    5016             :             {
    5017           0 :                 bOutputWhitepoint = false;
    5018             :             }
    5019             :             else
    5020             :             {
    5021          20 :                 for (int j = 0; j < 3; ++j)
    5022             :                 {
    5023          15 :                     const float v = static_cast<float>(CPLAtof(aosTokens[j]));
    5024             : 
    5025          15 :                     if (j == 2)
    5026             :                     {
    5027             :                         // Last term of xyY color must be 1.0.
    5028           5 :                         if (v != 1.0f)
    5029             :                         {
    5030           0 :                             bOutputWhitepoint = false;
    5031           0 :                             break;
    5032             :                         }
    5033             :                     }
    5034             :                     else
    5035             :                     {
    5036          10 :                         pWP[j] = v;
    5037             :                     }
    5038             :                 }
    5039             :             }
    5040             : 
    5041           5 :             if (bOutputWhitepoint)
    5042             :             {
    5043           5 :                 TIFFSetField(l_hTIFF, TIFFTAG_WHITEPOINT, pWP);
    5044             :             }
    5045             :         }
    5046             : 
    5047             :         // Set transfer function metadata.
    5048             :         char const *pszTFRed =
    5049             :             (pDS != nullptr)
    5050        4978 :                 ? pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_RED",
    5051             :                                        "COLOR_PROFILE")
    5052        4977 :                 : CSLFetchNameValue(papszParamList,
    5053        4978 :                                     "TIFFTAG_TRANSFERFUNCTION_RED");
    5054             : 
    5055             :         char const *pszTFGreen =
    5056             :             (pDS != nullptr)
    5057        4978 :                 ? pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_GREEN",
    5058             :                                        "COLOR_PROFILE")
    5059        4977 :                 : CSLFetchNameValue(papszParamList,
    5060        4978 :                                     "TIFFTAG_TRANSFERFUNCTION_GREEN");
    5061             : 
    5062             :         char const *pszTFBlue =
    5063             :             (pDS != nullptr)
    5064        4978 :                 ? pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_BLUE",
    5065             :                                        "COLOR_PROFILE")
    5066        4977 :                 : CSLFetchNameValue(papszParamList,
    5067        4978 :                                     "TIFFTAG_TRANSFERFUNCTION_BLUE");
    5068             : 
    5069        4978 :         if ((pszTFRed != nullptr) && (pszTFGreen != nullptr) &&
    5070             :             (pszTFBlue != nullptr))
    5071             :         {
    5072             :             // Get length of table.
    5073           4 :             const int nTransferFunctionLength =
    5074           4 :                 1 << ((pDS != nullptr) ? pDS->m_nBitsPerSample
    5075             :                                        : l_nBitsPerSample);
    5076             : 
    5077             :             const CPLStringList aosTokensRed(CSLTokenizeString2(
    5078             :                 pszTFRed, ",",
    5079             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5080           8 :                     CSLT_STRIPENDSPACES));
    5081             :             const CPLStringList aosTokensGreen(CSLTokenizeString2(
    5082             :                 pszTFGreen, ",",
    5083             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5084           8 :                     CSLT_STRIPENDSPACES));
    5085             :             const CPLStringList aosTokensBlue(CSLTokenizeString2(
    5086             :                 pszTFBlue, ",",
    5087             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5088           8 :                     CSLT_STRIPENDSPACES));
    5089             : 
    5090           4 :             if ((aosTokensRed.size() == nTransferFunctionLength) &&
    5091           8 :                 (aosTokensGreen.size() == nTransferFunctionLength) &&
    5092           4 :                 (aosTokensBlue.size() == nTransferFunctionLength))
    5093             :             {
    5094             :                 std::vector<uint16_t> anTransferFuncRed(
    5095           8 :                     nTransferFunctionLength);
    5096             :                 std::vector<uint16_t> anTransferFuncGreen(
    5097           8 :                     nTransferFunctionLength);
    5098             :                 std::vector<uint16_t> anTransferFuncBlue(
    5099           8 :                     nTransferFunctionLength);
    5100             : 
    5101             :                 // Convert our table in string format into int16_t format.
    5102        1028 :                 for (int i = 0; i < nTransferFunctionLength; ++i)
    5103             :                 {
    5104        2048 :                     anTransferFuncRed[i] =
    5105        1024 :                         static_cast<uint16_t>(atoi(aosTokensRed[i]));
    5106        2048 :                     anTransferFuncGreen[i] =
    5107        1024 :                         static_cast<uint16_t>(atoi(aosTokensGreen[i]));
    5108        1024 :                     anTransferFuncBlue[i] =
    5109        1024 :                         static_cast<uint16_t>(atoi(aosTokensBlue[i]));
    5110             :                 }
    5111             : 
    5112           4 :                 TIFFSetField(
    5113             :                     l_hTIFF, TIFFTAG_TRANSFERFUNCTION, anTransferFuncRed.data(),
    5114             :                     anTransferFuncGreen.data(), anTransferFuncBlue.data());
    5115             :             }
    5116             :         }
    5117             : 
    5118             :         // Output transfer range.
    5119        4978 :         bool bOutputTransferRange = true;
    5120        4978 :         for (int i = 0; (i < 2) && bOutputTransferRange; ++i)
    5121             :         {
    5122             :             const char *pszTXRVal =
    5123             :                 (pDS != nullptr)
    5124        4978 :                     ? pDS->GetMetadataItem(pszTXRNames[i], "COLOR_PROFILE")
    5125        4977 :                     : CSLFetchNameValue(papszParamList, pszTXRNames[i]);
    5126        4978 :             if (pszTXRVal == nullptr)
    5127             :             {
    5128        4978 :                 bOutputTransferRange = false;
    5129        4978 :                 break;
    5130             :             }
    5131             : 
    5132             :             const CPLStringList aosTokens(CSLTokenizeString2(
    5133             :                 pszTXRVal, ",",
    5134             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5135           0 :                     CSLT_STRIPENDSPACES));
    5136             : 
    5137           0 :             if (aosTokens.size() != 3)
    5138             :             {
    5139           0 :                 bOutputTransferRange = false;
    5140           0 :                 break;
    5141             :             }
    5142             : 
    5143           0 :             for (int j = 0; j < 3; ++j)
    5144             :             {
    5145           0 :                 pTXR[i + j * 2] = static_cast<uint16_t>(atoi(aosTokens[j]));
    5146             :             }
    5147             :         }
    5148             : 
    5149        4978 :         if (bOutputTransferRange)
    5150             :         {
    5151           0 :             const int TIFFTAG_TRANSFERRANGE = 0x0156;
    5152           0 :             TIFFSetField(l_hTIFF, TIFFTAG_TRANSFERRANGE, pTXR);
    5153             :         }
    5154             :     }
    5155             : }
    5156             : 
    5157       17925 : static signed char GTiffGetLZMAPreset(CSLConstList papszOptions)
    5158             : {
    5159       17925 :     int nLZMAPreset = -1;
    5160       17925 :     const char *pszValue = CSLFetchNameValue(papszOptions, "LZMA_PRESET");
    5161       17925 :     if (pszValue != nullptr)
    5162             :     {
    5163          20 :         nLZMAPreset = atoi(pszValue);
    5164          20 :         if (!(nLZMAPreset >= 0 && nLZMAPreset <= 9))
    5165             :         {
    5166           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5167             :                      "LZMA_PRESET=%s value not recognised, ignoring.",
    5168             :                      pszValue);
    5169           0 :             nLZMAPreset = -1;
    5170             :         }
    5171             :     }
    5172       17925 :     return static_cast<signed char>(nLZMAPreset);
    5173             : }
    5174             : 
    5175       17925 : static signed char GTiffGetZSTDPreset(CSLConstList papszOptions)
    5176             : {
    5177       17925 :     int nZSTDLevel = -1;
    5178       17925 :     const char *pszValue = CSLFetchNameValue(papszOptions, "ZSTD_LEVEL");
    5179       17925 :     if (pszValue != nullptr)
    5180             :     {
    5181          24 :         nZSTDLevel = atoi(pszValue);
    5182          24 :         if (!(nZSTDLevel >= 1 && nZSTDLevel <= 22))
    5183             :         {
    5184           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5185             :                      "ZSTD_LEVEL=%s value not recognised, ignoring.", pszValue);
    5186           0 :             nZSTDLevel = -1;
    5187             :         }
    5188             :     }
    5189       17925 :     return static_cast<signed char>(nZSTDLevel);
    5190             : }
    5191             : 
    5192       17925 : static signed char GTiffGetZLevel(CSLConstList papszOptions)
    5193             : {
    5194       17925 :     int nZLevel = -1;
    5195       17925 :     const char *pszValue = CSLFetchNameValue(papszOptions, "ZLEVEL");
    5196       17925 :     if (pszValue != nullptr)
    5197             :     {
    5198          44 :         nZLevel = atoi(pszValue);
    5199             : #ifdef TIFFTAG_DEFLATE_SUBCODEC
    5200          44 :         constexpr int nMaxLevel = 12;
    5201             : #ifndef LIBDEFLATE_SUPPORT
    5202             :         if (nZLevel > 9 && nZLevel <= nMaxLevel)
    5203             :         {
    5204             :             CPLDebug("GTiff",
    5205             :                      "ZLEVEL=%d not supported in a non-libdeflate enabled "
    5206             :                      "libtiff build. Capping to 9",
    5207             :                      nZLevel);
    5208             :             nZLevel = 9;
    5209             :         }
    5210             : #endif
    5211             : #else
    5212             :         constexpr int nMaxLevel = 9;
    5213             : #endif
    5214          44 :         if (nZLevel < 1 || nZLevel > nMaxLevel)
    5215             :         {
    5216           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5217             :                      "ZLEVEL=%s value not recognised, ignoring.", pszValue);
    5218           0 :             nZLevel = -1;
    5219             :         }
    5220             :     }
    5221       17925 :     return static_cast<signed char>(nZLevel);
    5222             : }
    5223             : 
    5224       17925 : static signed char GTiffGetJpegQuality(CSLConstList papszOptions)
    5225             : {
    5226       17925 :     int nJpegQuality = -1;
    5227       17925 :     const char *pszValue = CSLFetchNameValue(papszOptions, "JPEG_QUALITY");
    5228       17925 :     if (pszValue != nullptr)
    5229             :     {
    5230        1939 :         nJpegQuality = atoi(pszValue);
    5231        1939 :         if (nJpegQuality < 1 || nJpegQuality > 100)
    5232             :         {
    5233           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5234             :                      "JPEG_QUALITY=%s value not recognised, ignoring.",
    5235             :                      pszValue);
    5236           0 :             nJpegQuality = -1;
    5237             :         }
    5238             :     }
    5239       17925 :     return static_cast<signed char>(nJpegQuality);
    5240             : }
    5241             : 
    5242       17925 : static signed char GTiffGetJpegTablesMode(CSLConstList papszOptions)
    5243             : {
    5244       17925 :     return static_cast<signed char>(atoi(
    5245             :         CSLFetchNameValueDef(papszOptions, "JPEGTABLESMODE",
    5246       17925 :                              CPLSPrintf("%d", knGTIFFJpegTablesModeDefault))));
    5247             : }
    5248             : 
    5249             : /************************************************************************/
    5250             : /*                        GetDiscardLsbOption()                         */
    5251             : /************************************************************************/
    5252             : 
    5253        7951 : static GTiffDataset::MaskOffset *GetDiscardLsbOption(TIFF *hTIFF,
    5254             :                                                      CSLConstList papszOptions)
    5255             : {
    5256        7951 :     const char *pszBits = CSLFetchNameValue(papszOptions, "DISCARD_LSB");
    5257        7951 :     if (pszBits == nullptr)
    5258        7829 :         return nullptr;
    5259             : 
    5260         122 :     uint16_t nPhotometric = 0;
    5261         122 :     TIFFGetFieldDefaulted(hTIFF, TIFFTAG_PHOTOMETRIC, &nPhotometric);
    5262             : 
    5263         122 :     uint16_t nBitsPerSample = 0;
    5264         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &nBitsPerSample))
    5265           0 :         nBitsPerSample = 1;
    5266             : 
    5267         122 :     uint16_t nSamplesPerPixel = 0;
    5268         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSamplesPerPixel))
    5269           0 :         nSamplesPerPixel = 1;
    5270             : 
    5271         122 :     uint16_t nSampleFormat = 0;
    5272         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_SAMPLEFORMAT, &nSampleFormat))
    5273           0 :         nSampleFormat = SAMPLEFORMAT_UINT;
    5274             : 
    5275         122 :     if (nPhotometric == PHOTOMETRIC_PALETTE)
    5276             :     {
    5277           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    5278             :                  "DISCARD_LSB ignored on a paletted image");
    5279           1 :         return nullptr;
    5280             :     }
    5281         121 :     if (!(nBitsPerSample == 8 || nBitsPerSample == 16 || nBitsPerSample == 32 ||
    5282          13 :           nBitsPerSample == 64))
    5283             :     {
    5284           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    5285             :                  "DISCARD_LSB ignored on non 8, 16, 32 or 64 bits images");
    5286           1 :         return nullptr;
    5287             :     }
    5288             : 
    5289         240 :     const CPLStringList aosTokens(CSLTokenizeString2(pszBits, ",", 0));
    5290         120 :     const int nTokens = aosTokens.size();
    5291         120 :     GTiffDataset::MaskOffset *panMaskOffsetLsb = nullptr;
    5292         120 :     if (nTokens == 1 || nTokens == nSamplesPerPixel)
    5293             :     {
    5294             :         panMaskOffsetLsb = static_cast<GTiffDataset::MaskOffset *>(
    5295         119 :             CPLCalloc(nSamplesPerPixel, sizeof(GTiffDataset::MaskOffset)));
    5296         374 :         for (int i = 0; i < nSamplesPerPixel; ++i)
    5297             :         {
    5298         255 :             const int nBits = atoi(aosTokens[nTokens == 1 ? 0 : i]);
    5299         510 :             const int nMaxBits = (nSampleFormat == SAMPLEFORMAT_IEEEFP)
    5300         510 :                                      ? ((nBitsPerSample == 16)   ? 11 - 1
    5301          78 :                                         : (nBitsPerSample == 32) ? 23 - 1
    5302          26 :                                         : (nBitsPerSample == 64) ? 53 - 1
    5303             :                                                                  : 0)
    5304         203 :                                  : nSampleFormat == SAMPLEFORMAT_INT
    5305         203 :                                      ? nBitsPerSample - 2
    5306         119 :                                      : nBitsPerSample - 1;
    5307             : 
    5308         255 :             if (nBits < 0 || nBits > nMaxBits)
    5309             :             {
    5310           0 :                 CPLError(
    5311             :                     CE_Warning, CPLE_AppDefined,
    5312             :                     "DISCARD_LSB ignored: values should be in [0,%d] range",
    5313             :                     nMaxBits);
    5314           0 :                 VSIFree(panMaskOffsetLsb);
    5315           0 :                 return nullptr;
    5316             :             }
    5317         255 :             panMaskOffsetLsb[i].nMask =
    5318         255 :                 ~((static_cast<uint64_t>(1) << nBits) - 1);
    5319         255 :             if (nBits > 1)
    5320             :             {
    5321         249 :                 panMaskOffsetLsb[i].nRoundUpBitTest = static_cast<uint64_t>(1)
    5322         249 :                                                       << (nBits - 1);
    5323             :             }
    5324         119 :         }
    5325             :     }
    5326             :     else
    5327             :     {
    5328           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    5329             :                  "DISCARD_LSB ignored: wrong number of components");
    5330             :     }
    5331         120 :     return panMaskOffsetLsb;
    5332             : }
    5333             : 
    5334        7951 : void GTiffDataset::GetDiscardLsbOption(CSLConstList papszOptions)
    5335             : {
    5336        7951 :     m_panMaskOffsetLsb = ::GetDiscardLsbOption(m_hTIFF, papszOptions);
    5337        7951 : }
    5338             : 
    5339             : /************************************************************************/
    5340             : /*                             GetProfile()                             */
    5341             : /************************************************************************/
    5342             : 
    5343       17976 : static GTiffProfile GetProfile(const char *pszProfile)
    5344             : {
    5345       17976 :     GTiffProfile eProfile = GTiffProfile::GDALGEOTIFF;
    5346       17976 :     if (pszProfile != nullptr)
    5347             :     {
    5348          70 :         if (EQUAL(pszProfile, szPROFILE_BASELINE))
    5349          50 :             eProfile = GTiffProfile::BASELINE;
    5350          20 :         else if (EQUAL(pszProfile, szPROFILE_GeoTIFF))
    5351          18 :             eProfile = GTiffProfile::GEOTIFF;
    5352           2 :         else if (!EQUAL(pszProfile, szPROFILE_GDALGeoTIFF))
    5353             :         {
    5354           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    5355             :                      "Unsupported value for PROFILE: %s", pszProfile);
    5356             :         }
    5357             :     }
    5358       17976 :     return eProfile;
    5359             : }
    5360             : 
    5361             : /************************************************************************/
    5362             : /*                            GTiffCreate()                             */
    5363             : /*                                                                      */
    5364             : /*      Shared functionality between GTiffDataset::Create() and         */
    5365             : /*      GTiffCreateCopy() for creating TIFF file based on a set of      */
    5366             : /*      options and a configuration.                                    */
    5367             : /************************************************************************/
    5368             : 
    5369        9994 : TIFF *GTiffDataset::CreateLL(const char *pszFilename, int nXSize, int nYSize,
    5370             :                              int l_nBands, GDALDataType eType,
    5371             :                              double dfExtraSpaceForOverviews,
    5372             :                              int nColorTableMultiplier,
    5373             :                              CSLConstList papszParamList, VSILFILE **pfpL,
    5374             :                              CPLString &l_osTmpFilename, bool bCreateCopy,
    5375             :                              bool &bTileInterleavingOut)
    5376             : 
    5377             : {
    5378        9994 :     bTileInterleavingOut = false;
    5379             : 
    5380        9994 :     GTiffOneTimeInit();
    5381             : 
    5382             :     /* -------------------------------------------------------------------- */
    5383             :     /*      Blow on a few errors.                                           */
    5384             :     /* -------------------------------------------------------------------- */
    5385        9994 :     if (nXSize < 1 || nYSize < 1 || l_nBands < 1)
    5386             :     {
    5387           2 :         ReportError(
    5388             :             pszFilename, CE_Failure, CPLE_AppDefined,
    5389             :             "Attempt to create %dx%dx%d TIFF file, but width, height and bands "
    5390             :             "must be positive.",
    5391             :             nXSize, nYSize, l_nBands);
    5392             : 
    5393           2 :         return nullptr;
    5394             :     }
    5395             : 
    5396        9992 :     if (l_nBands > 65535)
    5397             :     {
    5398           1 :         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5399             :                     "Attempt to create %dx%dx%d TIFF file, but bands "
    5400             :                     "must be lesser or equal to 65535.",
    5401             :                     nXSize, nYSize, l_nBands);
    5402             : 
    5403           1 :         return nullptr;
    5404             :     }
    5405             : 
    5406             :     /* -------------------------------------------------------------------- */
    5407             :     /*      Setup values based on options.                                  */
    5408             :     /* -------------------------------------------------------------------- */
    5409             :     const GTiffProfile eProfile =
    5410        9991 :         GetProfile(CSLFetchNameValue(papszParamList, "PROFILE"));
    5411             : 
    5412        9991 :     const bool bTiled = CPLFetchBool(papszParamList, "TILED", false);
    5413             : 
    5414        9991 :     int l_nBlockXSize = 0;
    5415        9991 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKXSIZE"))
    5416             :     {
    5417         474 :         l_nBlockXSize = atoi(pszValue);
    5418         474 :         if (l_nBlockXSize < 0)
    5419             :         {
    5420           0 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5421             :                         "Invalid value for BLOCKXSIZE");
    5422           0 :             return nullptr;
    5423             :         }
    5424         474 :         if (!bTiled)
    5425             :         {
    5426           9 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    5427             :                         "BLOCKXSIZE can only be used with TILED=YES");
    5428             :         }
    5429         465 :         else if (l_nBlockXSize % 16 != 0)
    5430             :         {
    5431           1 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5432             :                         "BLOCKXSIZE must be a multiple of 16");
    5433           1 :             return nullptr;
    5434             :         }
    5435             :     }
    5436             : 
    5437        9990 :     int l_nBlockYSize = 0;
    5438        9990 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKYSIZE"))
    5439             :     {
    5440        2585 :         l_nBlockYSize = atoi(pszValue);
    5441        2585 :         if (l_nBlockYSize < 0)
    5442             :         {
    5443           0 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5444             :                         "Invalid value for BLOCKYSIZE");
    5445           0 :             return nullptr;
    5446             :         }
    5447        2585 :         if (bTiled && (l_nBlockYSize % 16 != 0))
    5448             :         {
    5449           2 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5450             :                         "BLOCKYSIZE must be a multiple of 16");
    5451           2 :             return nullptr;
    5452             :         }
    5453             :     }
    5454             : 
    5455        9988 :     if (bTiled)
    5456             :     {
    5457         824 :         if (l_nBlockXSize == 0)
    5458         361 :             l_nBlockXSize = 256;
    5459             : 
    5460         824 :         if (l_nBlockYSize == 0)
    5461         361 :             l_nBlockYSize = 256;
    5462             :     }
    5463             : 
    5464        9988 :     int nPlanar = 0;
    5465             : 
    5466             :     // Hidden @TILE_INTERLEAVE=YES parameter used by the COG driver
    5467        9988 :     if (bCreateCopy && CPLTestBool(CSLFetchNameValueDef(
    5468             :                            papszParamList, "@TILE_INTERLEAVE", "NO")))
    5469             :     {
    5470           7 :         bTileInterleavingOut = true;
    5471           7 :         nPlanar = PLANARCONFIG_SEPARATE;
    5472             :     }
    5473             :     else
    5474             :     {
    5475        9981 :         if (const char *pszValue =
    5476        9981 :                 CSLFetchNameValue(papszParamList, GDALMD_INTERLEAVE))
    5477             :         {
    5478        1582 :             if (EQUAL(pszValue, "PIXEL"))
    5479             :             {
    5480         409 :                 nPlanar = PLANARCONFIG_CONTIG;
    5481             :             }
    5482        1173 :             else if (EQUAL(pszValue, "BAND"))
    5483             :             {
    5484        1172 :                 nPlanar = PLANARCONFIG_SEPARATE;
    5485             :             }
    5486             :             else
    5487             :             {
    5488           1 :                 ReportError(
    5489             :                     pszFilename, CE_Failure, CPLE_IllegalArg,
    5490             :                     "INTERLEAVE=%s unsupported, value must be PIXEL or BAND.",
    5491             :                     pszValue);
    5492           1 :                 return nullptr;
    5493             :             }
    5494             :         }
    5495             :         else
    5496             :         {
    5497        8399 :             nPlanar = PLANARCONFIG_CONTIG;
    5498             :         }
    5499             :     }
    5500             : 
    5501        9987 :     int l_nCompression = COMPRESSION_NONE;
    5502        9987 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "COMPRESS"))
    5503             :     {
    5504        3362 :         l_nCompression = GTIFFGetCompressionMethod(pszValue, "COMPRESS");
    5505        3362 :         if (l_nCompression < 0)
    5506           0 :             return nullptr;
    5507             :     }
    5508             : 
    5509        9987 :     constexpr int JPEG_MAX_DIMENSION = 65500;  // Defined in jpeglib.h
    5510        9987 :     constexpr int WEBP_MAX_DIMENSION = 16383;
    5511             : 
    5512             :     const struct
    5513             :     {
    5514             :         int nCodecID;
    5515             :         const char *pszCodecName;
    5516             :         int nMaxDim;
    5517        9987 :     } asLimitations[] = {
    5518             :         {COMPRESSION_JPEG, "JPEG", JPEG_MAX_DIMENSION},
    5519             :         {COMPRESSION_WEBP, "WEBP", WEBP_MAX_DIMENSION},
    5520             :     };
    5521             : 
    5522       29949 :     for (const auto &sLimitation : asLimitations)
    5523             :     {
    5524       19970 :         if (l_nCompression == sLimitation.nCodecID && !bTiled &&
    5525        2074 :             nXSize > sLimitation.nMaxDim)
    5526             :         {
    5527           2 :             ReportError(
    5528             :                 pszFilename, CE_Failure, CPLE_IllegalArg,
    5529             :                 "COMPRESS=%s is only compatible with un-tiled images whose "
    5530             :                 "width is lesser or equal to %d pixels. "
    5531             :                 "To overcome this limitation, set the TILED=YES creation "
    5532             :                 "option.",
    5533           2 :                 sLimitation.pszCodecName, sLimitation.nMaxDim);
    5534           2 :             return nullptr;
    5535             :         }
    5536       19968 :         else if (l_nCompression == sLimitation.nCodecID && bTiled &&
    5537          52 :                  l_nBlockXSize > sLimitation.nMaxDim)
    5538             :         {
    5539           2 :             ReportError(
    5540             :                 pszFilename, CE_Failure, CPLE_IllegalArg,
    5541             :                 "COMPRESS=%s is only compatible with tiled images whose "
    5542             :                 "BLOCKXSIZE is lesser or equal to %d pixels.",
    5543           2 :                 sLimitation.pszCodecName, sLimitation.nMaxDim);
    5544           2 :             return nullptr;
    5545             :         }
    5546       19966 :         else if (l_nCompression == sLimitation.nCodecID &&
    5547        2122 :                  l_nBlockYSize > sLimitation.nMaxDim)
    5548             :         {
    5549           4 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5550             :                         "COMPRESS=%s is only compatible with images whose "
    5551             :                         "BLOCKYSIZE is lesser or equal to %d pixels. "
    5552             :                         "To overcome this limitation, set the TILED=YES "
    5553             :                         "creation option",
    5554           4 :                         sLimitation.pszCodecName, sLimitation.nMaxDim);
    5555           4 :             return nullptr;
    5556             :         }
    5557             :     }
    5558             : 
    5559             :     /* -------------------------------------------------------------------- */
    5560             :     /*      How many bits per sample?  We have a special case if NBITS      */
    5561             :     /*      specified for GDT_UInt8, GDT_UInt16, GDT_UInt32.                 */
    5562             :     /* -------------------------------------------------------------------- */
    5563        9979 :     int l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5564        9979 :     if (CSLFetchNameValue(papszParamList, GDALMD_NBITS) != nullptr)
    5565             :     {
    5566        1758 :         int nMinBits = 0;
    5567        1758 :         int nMaxBits = 0;
    5568             :         l_nBitsPerSample =
    5569        1758 :             atoi(CSLFetchNameValue(papszParamList, GDALMD_NBITS));
    5570        1758 :         if (eType == GDT_UInt8)
    5571             :         {
    5572         527 :             nMinBits = 1;
    5573         527 :             nMaxBits = 8;
    5574             :         }
    5575        1231 :         else if (eType == GDT_UInt16)
    5576             :         {
    5577        1213 :             nMinBits = 9;
    5578        1213 :             nMaxBits = 16;
    5579             :         }
    5580          18 :         else if (eType == GDT_UInt32)
    5581             :         {
    5582          14 :             nMinBits = 17;
    5583          14 :             nMaxBits = 32;
    5584             :         }
    5585           4 :         else if (eType == GDT_Float32)
    5586             :         {
    5587           4 :             if (l_nBitsPerSample != 16 && l_nBitsPerSample != 32)
    5588             :             {
    5589           1 :                 ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5590             :                             "Only NBITS=16 is supported for data type Float32");
    5591           1 :                 l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5592             :             }
    5593             :         }
    5594             :         else
    5595             :         {
    5596           0 :             ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5597             :                         "NBITS is not supported for data type %s",
    5598             :                         GDALGetDataTypeName(eType));
    5599           0 :             l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5600             :         }
    5601             : 
    5602        1758 :         if (nMinBits != 0)
    5603             :         {
    5604        1754 :             if (l_nBitsPerSample < nMinBits)
    5605             :             {
    5606           2 :                 ReportError(
    5607             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    5608             :                     "NBITS=%d is invalid for data type %s. Using NBITS=%d",
    5609             :                     l_nBitsPerSample, GDALGetDataTypeName(eType), nMinBits);
    5610           2 :                 l_nBitsPerSample = nMinBits;
    5611             :             }
    5612        1752 :             else if (l_nBitsPerSample > nMaxBits)
    5613             :             {
    5614           3 :                 ReportError(
    5615             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    5616             :                     "NBITS=%d is invalid for data type %s. Using NBITS=%d",
    5617             :                     l_nBitsPerSample, GDALGetDataTypeName(eType), nMaxBits);
    5618           3 :                 l_nBitsPerSample = nMaxBits;
    5619             :             }
    5620             :         }
    5621             :     }
    5622             : 
    5623             : #ifdef HAVE_JXL
    5624        9979 :     if ((l_nCompression == COMPRESSION_JXL ||
    5625         106 :          l_nCompression == COMPRESSION_JXL_DNG_1_7) &&
    5626         105 :         eType != GDT_Float16 && eType != GDT_Float32)
    5627             :     {
    5628             :         // Reflects tif_jxl's GetJXLDataType()
    5629          85 :         if (eType != GDT_UInt8 && eType != GDT_UInt16)
    5630             :         {
    5631           1 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5632             :                         "Data type %s not supported for JXL compression. Only "
    5633             :                         "Byte, UInt16, Float16, Float32 are supported",
    5634             :                         GDALGetDataTypeName(eType));
    5635           2 :             return nullptr;
    5636             :         }
    5637             : 
    5638             :         const struct
    5639             :         {
    5640             :             GDALDataType eDT;
    5641             :             int nBitsPerSample;
    5642          84 :         } asSupportedDTBitsPerSample[] = {
    5643             :             {GDT_UInt8, 8},
    5644             :             {GDT_UInt16, 16},
    5645             :         };
    5646             : 
    5647         250 :         for (const auto &sSupportedDTBitsPerSample : asSupportedDTBitsPerSample)
    5648             :         {
    5649         167 :             if (eType == sSupportedDTBitsPerSample.eDT &&
    5650          84 :                 l_nBitsPerSample != sSupportedDTBitsPerSample.nBitsPerSample)
    5651             :             {
    5652           1 :                 ReportError(
    5653             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    5654             :                     "Bits per sample=%d not supported for JXL compression. "
    5655             :                     "Only %d is supported for %s data type.",
    5656           1 :                     l_nBitsPerSample, sSupportedDTBitsPerSample.nBitsPerSample,
    5657             :                     GDALGetDataTypeName(eType));
    5658           1 :                 return nullptr;
    5659             :             }
    5660             :         }
    5661             :     }
    5662             : #endif
    5663             : 
    5664        9977 :     int nPredictor = PREDICTOR_NONE;
    5665        9977 :     const char *pszPredictor = CSLFetchNameValue(papszParamList, "PREDICTOR");
    5666        9977 :     if (pszPredictor)
    5667             :     {
    5668          31 :         nPredictor = atoi(pszPredictor);
    5669             :     }
    5670             : 
    5671        9977 :     if (nPredictor != PREDICTOR_NONE &&
    5672          18 :         l_nCompression != COMPRESSION_ADOBE_DEFLATE &&
    5673           2 :         l_nCompression != COMPRESSION_LZW &&
    5674           2 :         l_nCompression != COMPRESSION_LZMA &&
    5675             :         l_nCompression != COMPRESSION_ZSTD)
    5676             :     {
    5677           1 :         ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5678             :                     "PREDICTOR option is ignored for COMPRESS=%s. "
    5679             :                     "Only valid for DEFLATE, LZW, LZMA or ZSTD",
    5680             :                     CSLFetchNameValueDef(papszParamList, "COMPRESS", "NONE"));
    5681             :     }
    5682             : 
    5683             :     // Do early checks as libtiff will only error out when starting to write.
    5684       10006 :     else if (nPredictor != PREDICTOR_NONE &&
    5685          30 :              CPLTestBool(
    5686             :                  CPLGetConfigOption("GDAL_GTIFF_PREDICTOR_CHECKS", "YES")))
    5687             :     {
    5688             : #if (TIFFLIB_VERSION > 20210416) || defined(INTERNAL_LIBTIFF)
    5689             : #define HAVE_PREDICTOR_2_FOR_64BIT
    5690             : #endif
    5691          30 :         if (nPredictor == 2)
    5692             :         {
    5693          24 :             if (l_nBitsPerSample != 8 && l_nBitsPerSample != 16 &&
    5694             :                 l_nBitsPerSample != 32
    5695             : #ifdef HAVE_PREDICTOR_2_FOR_64BIT
    5696           2 :                 && l_nBitsPerSample != 64
    5697             : #endif
    5698             :             )
    5699             :             {
    5700             : #if !defined(HAVE_PREDICTOR_2_FOR_64BIT)
    5701             :                 if (l_nBitsPerSample == 64)
    5702             :                 {
    5703             :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5704             :                                 "PREDICTOR=2 is supported on 64 bit samples "
    5705             :                                 "starting with libtiff > 4.3.0.");
    5706             :                 }
    5707             :                 else
    5708             : #endif
    5709             :                 {
    5710           2 :                     const int nBITSHint = (l_nBitsPerSample < 8)    ? 8
    5711           1 :                                           : (l_nBitsPerSample < 16) ? 16
    5712           0 :                                           : (l_nBitsPerSample < 32) ? 32
    5713             :                                                                     : 64;
    5714           1 :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5715             : #ifdef HAVE_PREDICTOR_2_FOR_64BIT
    5716             :                                 "PREDICTOR=2 is only supported with 8/16/32/64 "
    5717             :                                 "bit samples. You can specify the NBITS=%d "
    5718             :                                 "creation option to promote to the closest "
    5719             :                                 "supported bits per sample value.",
    5720             : #else
    5721             :                                 "PREDICTOR=2 is only supported with 8/16/32 "
    5722             :                                 "bit samples. You can specify the NBITS=%d "
    5723             :                                 "creation option to promote to the closest "
    5724             :                                 "supported bits per sample value.",
    5725             : #endif
    5726             :                                 nBITSHint);
    5727             :                 }
    5728           1 :                 return nullptr;
    5729             :             }
    5730             :         }
    5731           6 :         else if (nPredictor == 3)
    5732             :         {
    5733           5 :             if (eType != GDT_Float16 && eType != GDT_Float32 &&
    5734             :                 eType != GDT_Float64)
    5735             :             {
    5736           1 :                 ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5737             :                             "PREDICTOR=3 is only supported with Float16, "
    5738             :                             "Float32 or Float64.");
    5739           1 :                 return nullptr;
    5740             :             }
    5741             :         }
    5742             :         else
    5743             :         {
    5744           1 :             ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5745             :                         "PREDICTOR=%s is not supported.", pszPredictor);
    5746           1 :             return nullptr;
    5747             :         }
    5748             :     }
    5749             : 
    5750        9974 :     const int l_nZLevel = GTiffGetZLevel(papszParamList);
    5751        9974 :     const int l_nLZMAPreset = GTiffGetLZMAPreset(papszParamList);
    5752        9974 :     const int l_nZSTDLevel = GTiffGetZSTDPreset(papszParamList);
    5753        9974 :     const int l_nWebPLevel = GTiffGetWebPLevel(papszParamList);
    5754        9974 :     const bool l_bWebPLossless = GTiffGetWebPLossless(papszParamList);
    5755        9974 :     const int l_nJpegQuality = GTiffGetJpegQuality(papszParamList);
    5756        9974 :     const int l_nJpegTablesMode = GTiffGetJpegTablesMode(papszParamList);
    5757        9974 :     const double l_dfMaxZError = GTiffGetLERCMaxZError(papszParamList);
    5758             : #if HAVE_JXL
    5759        9974 :     bool bJXLLosslessSpecified = false;
    5760             :     const bool l_bJXLLossless =
    5761        9974 :         GTiffGetJXLLossless(papszParamList, &bJXLLosslessSpecified);
    5762        9974 :     const uint32_t l_nJXLEffort = GTiffGetJXLEffort(papszParamList);
    5763        9974 :     bool bJXLDistanceSpecified = false;
    5764             :     const float l_fJXLDistance =
    5765        9974 :         GTiffGetJXLDistance(papszParamList, &bJXLDistanceSpecified);
    5766        9974 :     if (bJXLDistanceSpecified && l_bJXLLossless)
    5767             :     {
    5768           1 :         ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
    5769             :                     "JXL_DISTANCE creation option is ignored, given %s "
    5770             :                     "JXL_LOSSLESS=YES",
    5771             :                     bJXLLosslessSpecified ? "(explicit)" : "(implicit)");
    5772             :     }
    5773        9974 :     bool bJXLAlphaDistanceSpecified = false;
    5774             :     const float l_fJXLAlphaDistance =
    5775        9974 :         GTiffGetJXLAlphaDistance(papszParamList, &bJXLAlphaDistanceSpecified);
    5776        9974 :     if (bJXLAlphaDistanceSpecified && l_bJXLLossless)
    5777             :     {
    5778           1 :         ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
    5779             :                     "JXL_ALPHA_DISTANCE creation option is ignored, given %s "
    5780             :                     "JXL_LOSSLESS=YES",
    5781             :                     bJXLLosslessSpecified ? "(explicit)" : "(implicit)");
    5782             :     }
    5783             : #endif
    5784             :     /* -------------------------------------------------------------------- */
    5785             :     /*      Streaming related code                                          */
    5786             :     /* -------------------------------------------------------------------- */
    5787       19948 :     const CPLString osOriFilename(pszFilename);
    5788       19948 :     bool bStreaming = strcmp(pszFilename, "/vsistdout/") == 0 ||
    5789        9974 :                       CPLFetchBool(papszParamList, "STREAMABLE_OUTPUT", false);
    5790             : #ifdef S_ISFIFO
    5791        9974 :     if (!bStreaming)
    5792             :     {
    5793             :         VSIStatBufL sStat;
    5794        9962 :         if (VSIStatExL(pszFilename, &sStat,
    5795       10841 :                        VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
    5796         879 :             S_ISFIFO(sStat.st_mode))
    5797             :         {
    5798           0 :             bStreaming = true;
    5799             :         }
    5800             :     }
    5801             : #endif
    5802        9974 :     if (bStreaming && !EQUAL("NONE", CSLFetchNameValueDef(papszParamList,
    5803             :                                                           "COMPRESS", "NONE")))
    5804             :     {
    5805           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5806             :                     "Streaming only supported to uncompressed TIFF");
    5807           1 :         return nullptr;
    5808             :     }
    5809        9973 :     if (bStreaming && CPLFetchBool(papszParamList, "SPARSE_OK", false))
    5810             :     {
    5811           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5812             :                     "Streaming not supported with SPARSE_OK");
    5813           1 :         return nullptr;
    5814             :     }
    5815             :     const bool bCopySrcOverviews =
    5816        9972 :         CPLFetchBool(papszParamList, "COPY_SRC_OVERVIEWS", false);
    5817        9972 :     if (bStreaming && bCopySrcOverviews)
    5818             :     {
    5819           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5820             :                     "Streaming not supported with COPY_SRC_OVERVIEWS");
    5821           1 :         return nullptr;
    5822             :     }
    5823        9971 :     if (bStreaming)
    5824             :     {
    5825           9 :         l_osTmpFilename = VSIMemGenerateHiddenFilename("vsistdout.tif");
    5826           9 :         pszFilename = l_osTmpFilename.c_str();
    5827             :     }
    5828             : 
    5829             :     /* -------------------------------------------------------------------- */
    5830             :     /*      Compute the uncompressed size.                                  */
    5831             :     /* -------------------------------------------------------------------- */
    5832        9971 :     const unsigned nTileXCount =
    5833        9971 :         bTiled ? DIV_ROUND_UP(nXSize, l_nBlockXSize) : 0;
    5834        9971 :     const unsigned nTileYCount =
    5835        9971 :         bTiled ? DIV_ROUND_UP(nYSize, l_nBlockYSize) : 0;
    5836             :     const double dfUncompressedImageSize =
    5837        9971 :         (bTiled ? (static_cast<double>(nTileXCount) * nTileYCount *
    5838         820 :                    l_nBlockXSize * l_nBlockYSize)
    5839        9151 :                 : (nXSize * static_cast<double>(nYSize))) *
    5840        9971 :             l_nBands * GDALGetDataTypeSizeBytes(eType) +
    5841        9971 :         dfExtraSpaceForOverviews;
    5842             : 
    5843             :     /* -------------------------------------------------------------------- */
    5844             :     /*      Should the file be created as a bigtiff file?                   */
    5845             :     /* -------------------------------------------------------------------- */
    5846        9971 :     const char *pszBIGTIFF = CSLFetchNameValue(papszParamList, "BIGTIFF");
    5847             : 
    5848        9971 :     if (pszBIGTIFF == nullptr)
    5849        9524 :         pszBIGTIFF = "IF_NEEDED";
    5850             : 
    5851        9971 :     bool bCreateBigTIFF = false;
    5852        9971 :     if (EQUAL(pszBIGTIFF, "IF_NEEDED"))
    5853             :     {
    5854        9525 :         if (l_nCompression == COMPRESSION_NONE &&
    5855             :             dfUncompressedImageSize > 4200000000.0)
    5856          17 :             bCreateBigTIFF = true;
    5857             :     }
    5858         446 :     else if (EQUAL(pszBIGTIFF, "IF_SAFER"))
    5859             :     {
    5860         425 :         if (dfUncompressedImageSize > 2000000000.0)
    5861           1 :             bCreateBigTIFF = true;
    5862             :     }
    5863             :     else
    5864             :     {
    5865          21 :         bCreateBigTIFF = CPLTestBool(pszBIGTIFF);
    5866          21 :         if (!bCreateBigTIFF && l_nCompression == COMPRESSION_NONE &&
    5867             :             dfUncompressedImageSize > 4200000000.0)
    5868             :         {
    5869           2 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5870             :                         "The TIFF file will be larger than 4GB, so BigTIFF is "
    5871             :                         "necessary.  Creation failed.");
    5872           2 :             return nullptr;
    5873             :         }
    5874             :     }
    5875             : 
    5876        9969 :     if (bCreateBigTIFF)
    5877          35 :         CPLDebug("GTiff", "File being created as a BigTIFF.");
    5878             : 
    5879             :     /* -------------------------------------------------------------------- */
    5880             :     /*      Sanity check.                                                   */
    5881             :     /* -------------------------------------------------------------------- */
    5882        9969 :     if (bTiled)
    5883             :     {
    5884             :         // libtiff implementation limitation
    5885         820 :         if (nTileXCount > 0x80000000U / (bCreateBigTIFF ? 8 : 4) / nTileYCount)
    5886             :         {
    5887           3 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5888             :                         "File too large regarding tile size. This would result "
    5889             :                         "in a file with tile arrays larger than 2GB");
    5890           3 :             return nullptr;
    5891             :         }
    5892             :     }
    5893             : 
    5894             :     /* -------------------------------------------------------------------- */
    5895             :     /*      Check free space (only for big, non sparse)                     */
    5896             :     /* -------------------------------------------------------------------- */
    5897        9966 :     const double dfLikelyFloorOfFinalSize =
    5898             :         l_nCompression == COMPRESSION_NONE
    5899        9966 :             ? dfUncompressedImageSize
    5900             :             :
    5901             :             /* For compressed, we target 1% as the most optimistic reduction factor! */
    5902             :             0.01 * dfUncompressedImageSize;
    5903        9988 :     if (dfLikelyFloorOfFinalSize >= 1e9 &&
    5904          22 :         !CPLFetchBool(papszParamList, "SPARSE_OK", false) &&
    5905           5 :         osOriFilename != "/vsistdout/" &&
    5906        9993 :         osOriFilename != "/vsistdout_redirect/" &&
    5907           5 :         CPLTestBool(CPLGetConfigOption("CHECK_DISK_FREE_SPACE", "TRUE")))
    5908             :     {
    5909             :         const GIntBig nFreeDiskSpace =
    5910           4 :             VSIGetDiskFreeSpace(CPLGetDirnameSafe(pszFilename).c_str());
    5911           4 :         if (nFreeDiskSpace >= 0 && nFreeDiskSpace < dfLikelyFloorOfFinalSize)
    5912             :         {
    5913           6 :             ReportError(
    5914             :                 pszFilename, CE_Failure, CPLE_FileIO,
    5915             :                 "Free disk space available is %s, "
    5916             :                 "whereas %s are %s necessary. "
    5917             :                 "You can disable this check by defining the "
    5918             :                 "CHECK_DISK_FREE_SPACE configuration option to FALSE.",
    5919           4 :                 CPLFormatReadableFileSize(static_cast<uint64_t>(nFreeDiskSpace))
    5920             :                     .c_str(),
    5921           4 :                 CPLFormatReadableFileSize(dfLikelyFloorOfFinalSize).c_str(),
    5922             :                 l_nCompression == COMPRESSION_NONE
    5923             :                     ? "at least"
    5924             :                     : "likely at least (probably more)");
    5925           2 :             return nullptr;
    5926             :         }
    5927             :     }
    5928             : 
    5929             :     /* -------------------------------------------------------------------- */
    5930             :     /*      Check if the user wishes a particular endianness                */
    5931             :     /* -------------------------------------------------------------------- */
    5932             : 
    5933        9964 :     int eEndianness = ENDIANNESS_NATIVE;
    5934        9964 :     const char *pszEndianness = CSLFetchNameValue(papszParamList, "ENDIANNESS");
    5935        9964 :     if (pszEndianness == nullptr)
    5936        9901 :         pszEndianness = CPLGetConfigOption("GDAL_TIFF_ENDIANNESS", nullptr);
    5937        9964 :     if (pszEndianness != nullptr)
    5938             :     {
    5939         123 :         if (EQUAL(pszEndianness, "LITTLE"))
    5940             :         {
    5941          36 :             eEndianness = ENDIANNESS_LITTLE;
    5942             :         }
    5943          87 :         else if (EQUAL(pszEndianness, "BIG"))
    5944             :         {
    5945           1 :             eEndianness = ENDIANNESS_BIG;
    5946             :         }
    5947          86 :         else if (EQUAL(pszEndianness, "INVERTED"))
    5948             :         {
    5949             : #ifdef CPL_LSB
    5950          82 :             eEndianness = ENDIANNESS_BIG;
    5951             : #else
    5952             :             eEndianness = ENDIANNESS_LITTLE;
    5953             : #endif
    5954             :         }
    5955           4 :         else if (!EQUAL(pszEndianness, "NATIVE"))
    5956             :         {
    5957           1 :             ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5958             :                         "ENDIANNESS=%s not supported. Defaulting to NATIVE",
    5959             :                         pszEndianness);
    5960             :         }
    5961             :     }
    5962             : 
    5963             :     /* -------------------------------------------------------------------- */
    5964             :     /*      Try opening the dataset.                                        */
    5965             :     /* -------------------------------------------------------------------- */
    5966             : 
    5967             :     const bool bAppend =
    5968        9964 :         CPLFetchBool(papszParamList, "APPEND_SUBDATASET", false);
    5969             : 
    5970        9964 :     char szOpeningFlag[5] = {};
    5971        9964 :     strcpy(szOpeningFlag, bAppend ? "r+" : "w+");
    5972        9964 :     if (bCreateBigTIFF)
    5973          32 :         strcat(szOpeningFlag, "8");
    5974        9964 :     if (eEndianness == ENDIANNESS_BIG)
    5975          83 :         strcat(szOpeningFlag, "b");
    5976        9881 :     else if (eEndianness == ENDIANNESS_LITTLE)
    5977          36 :         strcat(szOpeningFlag, "l");
    5978             : 
    5979        9964 :     VSIErrorReset();
    5980        9964 :     const bool bOnlyVisibleAtCloseTime = CPLTestBool(CSLFetchNameValueDef(
    5981             :         papszParamList, "@CREATE_ONLY_VISIBLE_AT_CLOSE_TIME", "NO"));
    5982        9964 :     const bool bSuppressASAP = CPLTestBool(
    5983             :         CSLFetchNameValueDef(papszParamList, "@SUPPRESS_ASAP", "NO"));
    5984             :     auto l_fpL =
    5985        9964 :         (bOnlyVisibleAtCloseTime || bSuppressASAP) && !bAppend
    5986       10034 :             ? VSIFileManager::GetHandler(pszFilename)
    5987         140 :                   ->CreateOnlyVisibleAtCloseTime(pszFilename, true, nullptr)
    5988          70 :                   .release()
    5989       19858 :             : VSIFilesystemHandler::OpenStatic(pszFilename,
    5990             :                                                bAppend ? "r+b" : "w+b", true)
    5991        9964 :                   .release();
    5992        9964 :     if (l_fpL == nullptr)
    5993             :     {
    5994          21 :         VSIToCPLErrorWithMsg(CE_Failure, CPLE_OpenFailed,
    5995          42 :                              std::string("Attempt to create new tiff file `")
    5996          21 :                                  .append(pszFilename)
    5997          21 :                                  .append("' failed")
    5998             :                                  .c_str());
    5999          21 :         return nullptr;
    6000             :     }
    6001             : 
    6002        9943 :     if (bSuppressASAP)
    6003             :     {
    6004          40 :         l_fpL->CancelCreation();
    6005             :     }
    6006             : 
    6007        9943 :     TIFF *l_hTIFF = VSI_TIFFOpen(pszFilename, szOpeningFlag, l_fpL);
    6008        9943 :     if (l_hTIFF == nullptr)
    6009             :     {
    6010           2 :         if (CPLGetLastErrorNo() == 0)
    6011           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    6012             :                      "Attempt to create new tiff file `%s' "
    6013             :                      "failed in XTIFFOpen().",
    6014             :                      pszFilename);
    6015           2 :         l_fpL->CancelCreation();
    6016           2 :         CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6017           2 :         return nullptr;
    6018             :     }
    6019             : 
    6020        9941 :     if (bAppend)
    6021             :     {
    6022             : #if !(defined(INTERNAL_LIBTIFF) || TIFFLIB_VERSION > 20240911)
    6023             :         // This is a bit of a hack to cause (*tif->tif_cleanup)(tif); to be
    6024             :         // called. See https://trac.osgeo.org/gdal/ticket/2055
    6025             :         // Fixed in libtiff > 4.7.0
    6026             :         TIFFSetField(l_hTIFF, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
    6027             :         TIFFFreeDirectory(l_hTIFF);
    6028             : #endif
    6029           6 :         TIFFCreateDirectory(l_hTIFF);
    6030             :     }
    6031             : 
    6032             :     /* -------------------------------------------------------------------- */
    6033             :     /*      Do we have a custom pixel type (just used for signed byte now). */
    6034             :     /* -------------------------------------------------------------------- */
    6035        9941 :     const char *pszPixelType = CSLFetchNameValue(papszParamList, "PIXELTYPE");
    6036        9941 :     if (pszPixelType == nullptr)
    6037        9933 :         pszPixelType = "";
    6038        9941 :     if (eType == GDT_UInt8 && EQUAL(pszPixelType, "SIGNEDBYTE"))
    6039             :     {
    6040           8 :         CPLError(CE_Warning, CPLE_AppDefined,
    6041             :                  "Using PIXELTYPE=SIGNEDBYTE with Byte data type is deprecated "
    6042             :                  "(but still works). "
    6043             :                  "Using Int8 data type instead is now recommended.");
    6044             :     }
    6045             : 
    6046             :     /* -------------------------------------------------------------------- */
    6047             :     /*      Setup some standard flags.                                      */
    6048             :     /* -------------------------------------------------------------------- */
    6049        9941 :     TIFFSetField(l_hTIFF, TIFFTAG_IMAGEWIDTH, nXSize);
    6050        9941 :     TIFFSetField(l_hTIFF, TIFFTAG_IMAGELENGTH, nYSize);
    6051        9941 :     TIFFSetField(l_hTIFF, TIFFTAG_BITSPERSAMPLE, l_nBitsPerSample);
    6052             : 
    6053        9941 :     uint16_t l_nSampleFormat = 0;
    6054        9941 :     if ((eType == GDT_UInt8 && EQUAL(pszPixelType, "SIGNEDBYTE")) ||
    6055        9792 :         eType == GDT_Int8 || eType == GDT_Int16 || eType == GDT_Int32 ||
    6056             :         eType == GDT_Int64)
    6057         808 :         l_nSampleFormat = SAMPLEFORMAT_INT;
    6058        9133 :     else if (eType == GDT_CInt16 || eType == GDT_CInt32)
    6059         363 :         l_nSampleFormat = SAMPLEFORMAT_COMPLEXINT;
    6060        8770 :     else if (eType == GDT_Float16 || eType == GDT_Float32 ||
    6061             :              eType == GDT_Float64)
    6062        1173 :         l_nSampleFormat = SAMPLEFORMAT_IEEEFP;
    6063        7597 :     else if (eType == GDT_CFloat16 || eType == GDT_CFloat32 ||
    6064             :              eType == GDT_CFloat64)
    6065         471 :         l_nSampleFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
    6066             :     else
    6067        7126 :         l_nSampleFormat = SAMPLEFORMAT_UINT;
    6068             : 
    6069        9941 :     TIFFSetField(l_hTIFF, TIFFTAG_SAMPLEFORMAT, l_nSampleFormat);
    6070        9941 :     TIFFSetField(l_hTIFF, TIFFTAG_SAMPLESPERPIXEL, l_nBands);
    6071        9941 :     TIFFSetField(l_hTIFF, TIFFTAG_PLANARCONFIG, nPlanar);
    6072             : 
    6073             :     /* -------------------------------------------------------------------- */
    6074             :     /*      Setup Photometric Interpretation. Take this value from the user */
    6075             :     /*      passed option or guess correct value otherwise.                 */
    6076             :     /* -------------------------------------------------------------------- */
    6077        9941 :     int nSamplesAccountedFor = 1;
    6078        9941 :     bool bForceColorTable = false;
    6079             : 
    6080        9941 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "PHOTOMETRIC"))
    6081             :     {
    6082        1914 :         if (EQUAL(pszValue, "MINISBLACK"))
    6083          14 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6084        1900 :         else if (EQUAL(pszValue, "MINISWHITE"))
    6085             :         {
    6086           2 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
    6087             :         }
    6088        1898 :         else if (EQUAL(pszValue, "PALETTE"))
    6089             :         {
    6090           5 :             if (eType == GDT_UInt8 || eType == GDT_UInt16)
    6091             :             {
    6092           4 :                 TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    6093           4 :                 nSamplesAccountedFor = 1;
    6094           4 :                 bForceColorTable = true;
    6095             :             }
    6096             :             else
    6097             :             {
    6098           1 :                 ReportError(
    6099             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    6100             :                     "PHOTOMETRIC=PALETTE only compatible with Byte or UInt16");
    6101             :             }
    6102             :         }
    6103        1893 :         else if (EQUAL(pszValue, "RGB"))
    6104             :         {
    6105        1153 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    6106        1153 :             nSamplesAccountedFor = 3;
    6107             :         }
    6108         740 :         else if (EQUAL(pszValue, "CMYK"))
    6109             :         {
    6110          10 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
    6111          10 :             nSamplesAccountedFor = 4;
    6112             :         }
    6113         730 :         else if (EQUAL(pszValue, "YCBCR"))
    6114             :         {
    6115             :             // Because of subsampling, setting YCBCR without JPEG compression
    6116             :             // leads to a crash currently. Would need to make
    6117             :             // GTiffRasterBand::IWriteBlock() aware of subsampling so that it
    6118             :             // doesn't overrun buffer size returned by libtiff.
    6119         729 :             if (l_nCompression != COMPRESSION_JPEG)
    6120             :             {
    6121           1 :                 ReportError(
    6122             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    6123             :                     "Currently, PHOTOMETRIC=YCBCR requires COMPRESS=JPEG");
    6124           1 :                 XTIFFClose(l_hTIFF);
    6125           1 :                 l_fpL->CancelCreation();
    6126           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6127           1 :                 return nullptr;
    6128             :             }
    6129             : 
    6130         728 :             if (nPlanar == PLANARCONFIG_SEPARATE)
    6131             :             {
    6132           1 :                 ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    6133             :                             "PHOTOMETRIC=YCBCR requires INTERLEAVE=PIXEL");
    6134           1 :                 XTIFFClose(l_hTIFF);
    6135           1 :                 l_fpL->CancelCreation();
    6136           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6137           1 :                 return nullptr;
    6138             :             }
    6139             : 
    6140             :             // YCBCR strictly requires 3 bands. Not less, not more Issue an
    6141             :             // explicit error message as libtiff one is a bit cryptic:
    6142             :             // TIFFVStripSize64:Invalid td_samplesperpixel value.
    6143         727 :             if (l_nBands != 3)
    6144             :             {
    6145           1 :                 ReportError(
    6146             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    6147             :                     "PHOTOMETRIC=YCBCR not supported on a %d-band raster: "
    6148             :                     "only compatible with 3-band (RGB) rasters",
    6149             :                     l_nBands);
    6150           1 :                 XTIFFClose(l_hTIFF);
    6151           1 :                 l_fpL->CancelCreation();
    6152           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6153           1 :                 return nullptr;
    6154             :             }
    6155             : 
    6156         726 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
    6157         726 :             nSamplesAccountedFor = 3;
    6158             : 
    6159             :             // Explicitly register the subsampling so that JPEGFixupTags
    6160             :             // is a no-op (helps for cloud optimized geotiffs)
    6161         726 :             TIFFSetField(l_hTIFF, TIFFTAG_YCBCRSUBSAMPLING, 2, 2);
    6162             :         }
    6163           1 :         else if (EQUAL(pszValue, "CIELAB"))
    6164             :         {
    6165           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB);
    6166           0 :             nSamplesAccountedFor = 3;
    6167             :         }
    6168           1 :         else if (EQUAL(pszValue, "ICCLAB"))
    6169             :         {
    6170           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ICCLAB);
    6171           0 :             nSamplesAccountedFor = 3;
    6172             :         }
    6173           1 :         else if (EQUAL(pszValue, "ITULAB"))
    6174             :         {
    6175           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ITULAB);
    6176           0 :             nSamplesAccountedFor = 3;
    6177             :         }
    6178             :         else
    6179             :         {
    6180           1 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    6181             :                         "PHOTOMETRIC=%s value not recognised, ignoring.  "
    6182             :                         "Set the Photometric Interpretation as MINISBLACK.",
    6183             :                         pszValue);
    6184           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6185             :         }
    6186             : 
    6187        1911 :         if (l_nBands < nSamplesAccountedFor)
    6188             :         {
    6189           1 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    6190             :                         "PHOTOMETRIC=%s value does not correspond to number "
    6191             :                         "of bands (%d), ignoring.  "
    6192             :                         "Set the Photometric Interpretation as MINISBLACK.",
    6193             :                         pszValue, l_nBands);
    6194           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6195             :         }
    6196             :     }
    6197             :     else
    6198             :     {
    6199             :         // If image contains 3 or 4 bands and datatype is Byte then we will
    6200             :         // assume it is RGB. In all other cases assume it is MINISBLACK.
    6201        8027 :         if (l_nBands == 3 && eType == GDT_UInt8)
    6202             :         {
    6203         323 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    6204         323 :             nSamplesAccountedFor = 3;
    6205             :         }
    6206        7704 :         else if (l_nBands == 4 && eType == GDT_UInt8)
    6207             :         {
    6208             :             uint16_t v[1] = {
    6209         723 :                 GTiffGetAlphaValue(CSLFetchNameValue(papszParamList, "ALPHA"),
    6210         723 :                                    DEFAULT_ALPHA_TYPE)};
    6211             : 
    6212         723 :             TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
    6213         723 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    6214         723 :             nSamplesAccountedFor = 4;
    6215             :         }
    6216             :         else
    6217             :         {
    6218        6981 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6219        6981 :             nSamplesAccountedFor = 1;
    6220             :         }
    6221             :     }
    6222             : 
    6223             :     /* -------------------------------------------------------------------- */
    6224             :     /*      If there are extra samples, we need to mark them with an        */
    6225             :     /*      appropriate extrasamples definition here.                       */
    6226             :     /* -------------------------------------------------------------------- */
    6227        9938 :     if (l_nBands > nSamplesAccountedFor)
    6228             :     {
    6229        1389 :         const int nExtraSamples = l_nBands - nSamplesAccountedFor;
    6230             : 
    6231             :         uint16_t *v = static_cast<uint16_t *>(
    6232        1389 :             CPLMalloc(sizeof(uint16_t) * nExtraSamples));
    6233             : 
    6234        1389 :         v[0] = GTiffGetAlphaValue(CSLFetchNameValue(papszParamList, "ALPHA"),
    6235             :                                   EXTRASAMPLE_UNSPECIFIED);
    6236             : 
    6237      297700 :         for (int i = 1; i < nExtraSamples; ++i)
    6238      296311 :             v[i] = EXTRASAMPLE_UNSPECIFIED;
    6239             : 
    6240        1389 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples, v);
    6241             : 
    6242        1389 :         CPLFree(v);
    6243             :     }
    6244             : 
    6245             :     // Set the ICC color profile.
    6246        9938 :     if (eProfile != GTiffProfile::BASELINE)
    6247             :     {
    6248        9913 :         SaveICCProfile(nullptr, l_hTIFF, papszParamList, l_nBitsPerSample);
    6249             :     }
    6250             : 
    6251             :     // Set the compression method before asking the default strip size
    6252             :     // This is useful when translating to a JPEG-In-TIFF file where
    6253             :     // the default strip size is 8 or 16 depending on the photometric value.
    6254        9938 :     TIFFSetField(l_hTIFF, TIFFTAG_COMPRESSION, l_nCompression);
    6255             : 
    6256        9938 :     if (l_nCompression == COMPRESSION_LERC)
    6257             :     {
    6258             :         const char *pszCompress =
    6259          97 :             CSLFetchNameValueDef(papszParamList, "COMPRESS", "");
    6260          97 :         if (EQUAL(pszCompress, "LERC_DEFLATE"))
    6261             :         {
    6262          16 :             TIFFSetField(l_hTIFF, TIFFTAG_LERC_ADD_COMPRESSION,
    6263             :                          LERC_ADD_COMPRESSION_DEFLATE);
    6264             :         }
    6265          81 :         else if (EQUAL(pszCompress, "LERC_ZSTD"))
    6266             :         {
    6267          14 :             if (TIFFSetField(l_hTIFF, TIFFTAG_LERC_ADD_COMPRESSION,
    6268          14 :                              LERC_ADD_COMPRESSION_ZSTD) != 1)
    6269             :             {
    6270           0 :                 XTIFFClose(l_hTIFF);
    6271           0 :                 l_fpL->CancelCreation();
    6272           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6273           0 :                 return nullptr;
    6274             :             }
    6275             :         }
    6276             :     }
    6277             :     // TODO later: take into account LERC version
    6278             : 
    6279             :     /* -------------------------------------------------------------------- */
    6280             :     /*      Setup tiling/stripping flags.                                   */
    6281             :     /* -------------------------------------------------------------------- */
    6282        9938 :     if (bTiled)
    6283             :     {
    6284        1620 :         if (!TIFFSetField(l_hTIFF, TIFFTAG_TILEWIDTH, l_nBlockXSize) ||
    6285         810 :             !TIFFSetField(l_hTIFF, TIFFTAG_TILELENGTH, l_nBlockYSize))
    6286             :         {
    6287           0 :             XTIFFClose(l_hTIFF);
    6288           0 :             l_fpL->CancelCreation();
    6289           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6290           0 :             return nullptr;
    6291             :         }
    6292             :     }
    6293             :     else
    6294             :     {
    6295        9128 :         const uint32_t l_nRowsPerStrip = std::min(
    6296             :             nYSize, l_nBlockYSize == 0
    6297        9128 :                         ? static_cast<int>(TIFFDefaultStripSize(l_hTIFF, 0))
    6298        9128 :                         : l_nBlockYSize);
    6299             : 
    6300        9128 :         TIFFSetField(l_hTIFF, TIFFTAG_ROWSPERSTRIP, l_nRowsPerStrip);
    6301             :     }
    6302             : 
    6303             :     /* -------------------------------------------------------------------- */
    6304             :     /*      Set compression related tags.                                   */
    6305             :     /* -------------------------------------------------------------------- */
    6306        9938 :     if (GTIFFSupportsPredictor(l_nCompression))
    6307         981 :         TIFFSetField(l_hTIFF, TIFFTAG_PREDICTOR, nPredictor);
    6308        9938 :     if (l_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    6309             :         l_nCompression == COMPRESSION_LERC)
    6310             :     {
    6311         280 :         GTiffSetDeflateSubCodec(l_hTIFF);
    6312             : 
    6313         280 :         if (l_nZLevel != -1)
    6314          22 :             TIFFSetField(l_hTIFF, TIFFTAG_ZIPQUALITY, l_nZLevel);
    6315             :     }
    6316        9938 :     if (l_nCompression == COMPRESSION_JPEG && l_nJpegQuality != -1)
    6317        1905 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGQUALITY, l_nJpegQuality);
    6318        9938 :     if (l_nCompression == COMPRESSION_LZMA && l_nLZMAPreset != -1)
    6319          10 :         TIFFSetField(l_hTIFF, TIFFTAG_LZMAPRESET, l_nLZMAPreset);
    6320        9938 :     if ((l_nCompression == COMPRESSION_ZSTD ||
    6321         201 :          l_nCompression == COMPRESSION_LERC) &&
    6322             :         l_nZSTDLevel != -1)
    6323          12 :         TIFFSetField(l_hTIFF, TIFFTAG_ZSTD_LEVEL, l_nZSTDLevel);
    6324        9938 :     if (l_nCompression == COMPRESSION_LERC)
    6325             :     {
    6326          97 :         TIFFSetField(l_hTIFF, TIFFTAG_LERC_MAXZERROR, l_dfMaxZError);
    6327             :     }
    6328             : #if HAVE_JXL
    6329        9938 :     if (l_nCompression == COMPRESSION_JXL ||
    6330             :         l_nCompression == COMPRESSION_JXL_DNG_1_7)
    6331             :     {
    6332         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_LOSSYNESS,
    6333             :                      l_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
    6334         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_EFFORT, l_nJXLEffort);
    6335         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_DISTANCE,
    6336             :                      static_cast<double>(l_fJXLDistance));
    6337         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE,
    6338             :                      static_cast<double>(l_fJXLAlphaDistance));
    6339             :     }
    6340             : #endif
    6341        9938 :     if (l_nCompression == COMPRESSION_WEBP)
    6342          33 :         TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LEVEL, l_nWebPLevel);
    6343        9938 :     if (l_nCompression == COMPRESSION_WEBP && l_bWebPLossless)
    6344           7 :         TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LOSSLESS, 1);
    6345             : 
    6346        9938 :     if (l_nCompression == COMPRESSION_JPEG)
    6347        2083 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGTABLESMODE, l_nJpegTablesMode);
    6348             : 
    6349             :     /* -------------------------------------------------------------------- */
    6350             :     /*      If we forced production of a file with photometric=palette,     */
    6351             :     /*      we need to push out a default color table.                      */
    6352             :     /* -------------------------------------------------------------------- */
    6353        9938 :     if (bForceColorTable)
    6354             :     {
    6355           4 :         const int nColors = eType == GDT_UInt8 ? 256 : 65536;
    6356             : 
    6357             :         unsigned short *panTRed = static_cast<unsigned short *>(
    6358           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    6359             :         unsigned short *panTGreen = static_cast<unsigned short *>(
    6360           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    6361             :         unsigned short *panTBlue = static_cast<unsigned short *>(
    6362           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    6363             : 
    6364        1028 :         for (int iColor = 0; iColor < nColors; ++iColor)
    6365             :         {
    6366        1024 :             if (eType == GDT_UInt8)
    6367             :             {
    6368        1024 :                 panTRed[iColor] = GTiffDataset::ClampCTEntry(
    6369             :                     iColor, 1, iColor, nColorTableMultiplier);
    6370        1024 :                 panTGreen[iColor] = GTiffDataset::ClampCTEntry(
    6371             :                     iColor, 2, iColor, nColorTableMultiplier);
    6372        1024 :                 panTBlue[iColor] = GTiffDataset::ClampCTEntry(
    6373             :                     iColor, 3, iColor, nColorTableMultiplier);
    6374             :             }
    6375             :             else
    6376             :             {
    6377           0 :                 panTRed[iColor] = static_cast<unsigned short>(iColor);
    6378           0 :                 panTGreen[iColor] = static_cast<unsigned short>(iColor);
    6379           0 :                 panTBlue[iColor] = static_cast<unsigned short>(iColor);
    6380             :             }
    6381             :         }
    6382             : 
    6383           4 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue);
    6384             : 
    6385           4 :         CPLFree(panTRed);
    6386           4 :         CPLFree(panTGreen);
    6387           4 :         CPLFree(panTBlue);
    6388             :     }
    6389             : 
    6390             :     // This trick
    6391             :     // creates a temporary in-memory file and fetches its JPEG tables so that
    6392             :     // we can directly set them, before tif_jpeg.c compute them at the first
    6393             :     // strip/tile writing, which is too late, since we have already crystalized
    6394             :     // the directory. This way we avoid a directory rewriting.
    6395       12021 :     if (l_nCompression == COMPRESSION_JPEG &&
    6396        2083 :         CPLTestBool(
    6397             :             CSLFetchNameValueDef(papszParamList, "WRITE_JPEGTABLE_TAG", "YES")))
    6398             :     {
    6399        1014 :         GTiffWriteJPEGTables(
    6400             :             l_hTIFF, CSLFetchNameValue(papszParamList, "PHOTOMETRIC"),
    6401             :             CSLFetchNameValue(papszParamList, "JPEG_QUALITY"),
    6402             :             CSLFetchNameValue(papszParamList, "JPEGTABLESMODE"));
    6403             :     }
    6404             : 
    6405        9938 :     *pfpL = l_fpL;
    6406             : 
    6407        9938 :     return l_hTIFF;
    6408             : }
    6409             : 
    6410             : /************************************************************************/
    6411             : /*                            GuessJPEGQuality()                        */
    6412             : /*                                                                      */
    6413             : /*      Guess JPEG quality from JPEGTABLES tag.                         */
    6414             : /************************************************************************/
    6415             : 
    6416        3841 : static const GByte *GTIFFFindNextTable(const GByte *paby, GByte byMarker,
    6417             :                                        int nLen, int *pnLenTable)
    6418             : {
    6419        7949 :     for (int i = 0; i + 1 < nLen;)
    6420             :     {
    6421        7949 :         if (paby[i] != 0xFF)
    6422           0 :             return nullptr;
    6423        7949 :         ++i;
    6424        7949 :         if (paby[i] == 0xD8)
    6425             :         {
    6426        3111 :             ++i;
    6427        3111 :             continue;
    6428             :         }
    6429        4838 :         if (i + 2 >= nLen)
    6430         829 :             return nullptr;
    6431        4009 :         int nMarkerLen = paby[i + 1] * 256 + paby[i + 2];
    6432        4009 :         if (i + 1 + nMarkerLen >= nLen)
    6433           0 :             return nullptr;
    6434        4009 :         if (paby[i] == byMarker)
    6435             :         {
    6436        3012 :             if (pnLenTable)
    6437        2470 :                 *pnLenTable = nMarkerLen;
    6438        3012 :             return paby + i + 1;
    6439             :         }
    6440         997 :         i += 1 + nMarkerLen;
    6441             :     }
    6442           0 :     return nullptr;
    6443             : }
    6444             : 
    6445             : constexpr GByte MARKER_HUFFMAN_TABLE = 0xC4;
    6446             : constexpr GByte MARKER_QUANT_TABLE = 0xDB;
    6447             : 
    6448             : // We assume that if there are several quantization tables, they are
    6449             : // in the same order. Which is a reasonable assumption for updating
    6450             : // a file generated by ourselves.
    6451         904 : static bool GTIFFQuantizationTablesEqual(const GByte *paby1, int nLen1,
    6452             :                                          const GByte *paby2, int nLen2)
    6453             : {
    6454         904 :     bool bFound = false;
    6455             :     while (true)
    6456             :     {
    6457         945 :         int nLenTable1 = 0;
    6458         945 :         int nLenTable2 = 0;
    6459             :         const GByte *paby1New =
    6460         945 :             GTIFFFindNextTable(paby1, MARKER_QUANT_TABLE, nLen1, &nLenTable1);
    6461             :         const GByte *paby2New =
    6462         945 :             GTIFFFindNextTable(paby2, MARKER_QUANT_TABLE, nLen2, &nLenTable2);
    6463         945 :         if (paby1New == nullptr && paby2New == nullptr)
    6464         904 :             return bFound;
    6465         911 :         if (paby1New == nullptr || paby2New == nullptr)
    6466           0 :             return false;
    6467         911 :         if (nLenTable1 != nLenTable2)
    6468         207 :             return false;
    6469         704 :         if (memcmp(paby1New, paby2New, nLenTable1) != 0)
    6470         663 :             return false;
    6471          41 :         paby1New += nLenTable1;
    6472          41 :         paby2New += nLenTable2;
    6473          41 :         nLen1 -= static_cast<int>(paby1New - paby1);
    6474          41 :         nLen2 -= static_cast<int>(paby2New - paby2);
    6475          41 :         paby1 = paby1New;
    6476          41 :         paby2 = paby2New;
    6477          41 :         bFound = true;
    6478          41 :     }
    6479             : }
    6480             : 
    6481             : // Guess the JPEG quality by comparing against the MD5Sum of precomputed
    6482             : // quantization tables
    6483         407 : static int GuessJPEGQualityFromMD5(const uint8_t md5JPEGQuantTable[][16],
    6484             :                                    const GByte *const pabyJPEGTable,
    6485             :                                    int nJPEGTableSize)
    6486             : {
    6487         407 :     int nRemainingLen = nJPEGTableSize;
    6488         407 :     const GByte *pabyCur = pabyJPEGTable;
    6489             : 
    6490             :     struct CPLMD5Context context;
    6491         407 :     CPLMD5Init(&context);
    6492             : 
    6493             :     while (true)
    6494             :     {
    6495        1055 :         int nLenTable = 0;
    6496        1055 :         const GByte *pabyNew = GTIFFFindNextTable(pabyCur, MARKER_QUANT_TABLE,
    6497             :                                                   nRemainingLen, &nLenTable);
    6498        1055 :         if (pabyNew == nullptr)
    6499         407 :             break;
    6500         648 :         CPLMD5Update(&context, pabyNew, nLenTable);
    6501         648 :         pabyNew += nLenTable;
    6502         648 :         nRemainingLen -= static_cast<int>(pabyNew - pabyCur);
    6503         648 :         pabyCur = pabyNew;
    6504         648 :     }
    6505             : 
    6506             :     GByte digest[16];
    6507         407 :     CPLMD5Final(digest, &context);
    6508             : 
    6509       28696 :     for (int i = 0; i < 100; i++)
    6510             :     {
    6511       28693 :         if (memcmp(md5JPEGQuantTable[i], digest, 16) == 0)
    6512             :         {
    6513         404 :             return i + 1;
    6514             :         }
    6515             :     }
    6516           3 :     return -1;
    6517             : }
    6518             : 
    6519         462 : int GTiffDataset::GuessJPEGQuality(bool &bOutHasQuantizationTable,
    6520             :                                    bool &bOutHasHuffmanTable)
    6521             : {
    6522         462 :     CPLAssert(m_nCompression == COMPRESSION_JPEG);
    6523         462 :     uint32_t nJPEGTableSize = 0;
    6524         462 :     void *pJPEGTable = nullptr;
    6525         462 :     if (!TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize,
    6526             :                       &pJPEGTable))
    6527             :     {
    6528          14 :         bOutHasQuantizationTable = false;
    6529          14 :         bOutHasHuffmanTable = false;
    6530          14 :         return -1;
    6531             :     }
    6532             : 
    6533         448 :     bOutHasQuantizationTable =
    6534         448 :         GTIFFFindNextTable(static_cast<const GByte *>(pJPEGTable),
    6535             :                            MARKER_QUANT_TABLE, nJPEGTableSize,
    6536         448 :                            nullptr) != nullptr;
    6537         448 :     bOutHasHuffmanTable =
    6538         448 :         GTIFFFindNextTable(static_cast<const GByte *>(pJPEGTable),
    6539             :                            MARKER_HUFFMAN_TABLE, nJPEGTableSize,
    6540         448 :                            nullptr) != nullptr;
    6541         448 :     if (!bOutHasQuantizationTable)
    6542           7 :         return -1;
    6543             : 
    6544         441 :     if ((nBands == 1 && m_nBitsPerSample == 8) ||
    6545         381 :         (nBands == 3 && m_nBitsPerSample == 8 &&
    6546         335 :          m_nPhotometric == PHOTOMETRIC_RGB) ||
    6547         287 :         (nBands == 4 && m_nBitsPerSample == 8 &&
    6548          27 :          m_nPhotometric == PHOTOMETRIC_SEPARATED))
    6549             :     {
    6550         166 :         return GuessJPEGQualityFromMD5(md5JPEGQuantTable_generic_8bit,
    6551             :                                        static_cast<const GByte *>(pJPEGTable),
    6552         166 :                                        static_cast<int>(nJPEGTableSize));
    6553             :     }
    6554             : 
    6555         275 :     if (nBands == 3 && m_nBitsPerSample == 8 &&
    6556         241 :         m_nPhotometric == PHOTOMETRIC_YCBCR)
    6557             :     {
    6558             :         int nRet =
    6559         241 :             GuessJPEGQualityFromMD5(md5JPEGQuantTable_3_YCBCR_8bit,
    6560             :                                     static_cast<const GByte *>(pJPEGTable),
    6561             :                                     static_cast<int>(nJPEGTableSize));
    6562         241 :         if (nRet < 0)
    6563             :         {
    6564             :             // libjpeg 9e has modified the YCbCr quantization tables.
    6565             :             nRet =
    6566           0 :                 GuessJPEGQualityFromMD5(md5JPEGQuantTable_3_YCBCR_8bit_jpeg9e,
    6567             :                                         static_cast<const GByte *>(pJPEGTable),
    6568             :                                         static_cast<int>(nJPEGTableSize));
    6569             :         }
    6570         241 :         return nRet;
    6571             :     }
    6572             : 
    6573          34 :     char **papszLocalParameters = nullptr;
    6574             :     papszLocalParameters =
    6575          34 :         CSLSetNameValue(papszLocalParameters, "COMPRESS", "JPEG");
    6576          34 :     if (m_nPhotometric == PHOTOMETRIC_YCBCR)
    6577             :         papszLocalParameters =
    6578           7 :             CSLSetNameValue(papszLocalParameters, "PHOTOMETRIC", "YCBCR");
    6579          27 :     else if (m_nPhotometric == PHOTOMETRIC_SEPARATED)
    6580             :         papszLocalParameters =
    6581           0 :             CSLSetNameValue(papszLocalParameters, "PHOTOMETRIC", "CMYK");
    6582             :     papszLocalParameters =
    6583          34 :         CSLSetNameValue(papszLocalParameters, "BLOCKYSIZE", "16");
    6584          34 :     if (m_nBitsPerSample == 12)
    6585             :         papszLocalParameters =
    6586          16 :             CSLSetNameValue(papszLocalParameters, GDALMD_NBITS, "12");
    6587             : 
    6588             :     const CPLString osTmpFilenameIn(
    6589          34 :         VSIMemGenerateHiddenFilename("gtiffdataset_guess_jpeg_quality_tmp"));
    6590             : 
    6591          34 :     int nRet = -1;
    6592         938 :     for (int nQuality = 0; nQuality <= 100 && nRet < 0; ++nQuality)
    6593             :     {
    6594         904 :         VSILFILE *fpTmp = nullptr;
    6595         904 :         if (nQuality == 0)
    6596             :             papszLocalParameters =
    6597          34 :                 CSLSetNameValue(papszLocalParameters, "JPEG_QUALITY", "75");
    6598             :         else
    6599             :             papszLocalParameters =
    6600         870 :                 CSLSetNameValue(papszLocalParameters, "JPEG_QUALITY",
    6601             :                                 CPLSPrintf("%d", nQuality));
    6602             : 
    6603         904 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    6604         904 :         CPLString osTmp;
    6605             :         bool bTileInterleaving;
    6606        1808 :         TIFF *hTIFFTmp = CreateLL(
    6607         904 :             osTmpFilenameIn, 16, 16, (nBands <= 4) ? nBands : 1,
    6608             :             GetRasterBand(1)->GetRasterDataType(), 0.0, 0, papszLocalParameters,
    6609             :             &fpTmp, osTmp, /* bCreateCopy=*/false, bTileInterleaving);
    6610         904 :         CPLPopErrorHandler();
    6611         904 :         if (!hTIFFTmp)
    6612             :         {
    6613           0 :             break;
    6614             :         }
    6615             : 
    6616         904 :         TIFFWriteCheck(hTIFFTmp, FALSE, "CreateLL");
    6617         904 :         TIFFWriteDirectory(hTIFFTmp);
    6618         904 :         TIFFSetDirectory(hTIFFTmp, 0);
    6619             :         // Now reset jpegcolormode.
    6620        1196 :         if (m_nPhotometric == PHOTOMETRIC_YCBCR &&
    6621         292 :             CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
    6622             :         {
    6623         292 :             TIFFSetField(hTIFFTmp, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    6624             :         }
    6625             : 
    6626         904 :         GByte abyZeroData[(16 * 16 * 4 * 3) / 2] = {};
    6627         904 :         const int nBlockSize =
    6628         904 :             (16 * 16 * ((nBands <= 4) ? nBands : 1) * m_nBitsPerSample) / 8;
    6629         904 :         TIFFWriteEncodedStrip(hTIFFTmp, 0, abyZeroData, nBlockSize);
    6630             : 
    6631         904 :         uint32_t nJPEGTableSizeTry = 0;
    6632         904 :         void *pJPEGTableTry = nullptr;
    6633         904 :         if (TIFFGetField(hTIFFTmp, TIFFTAG_JPEGTABLES, &nJPEGTableSizeTry,
    6634         904 :                          &pJPEGTableTry))
    6635             :         {
    6636         904 :             if (GTIFFQuantizationTablesEqual(
    6637             :                     static_cast<GByte *>(pJPEGTable), nJPEGTableSize,
    6638             :                     static_cast<GByte *>(pJPEGTableTry), nJPEGTableSizeTry))
    6639             :             {
    6640          34 :                 nRet = (nQuality == 0) ? 75 : nQuality;
    6641             :             }
    6642             :         }
    6643             : 
    6644         904 :         XTIFFClose(hTIFFTmp);
    6645         904 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpTmp));
    6646             :     }
    6647             : 
    6648          34 :     CSLDestroy(papszLocalParameters);
    6649          34 :     VSIUnlink(osTmpFilenameIn);
    6650             : 
    6651          34 :     return nRet;
    6652             : }
    6653             : 
    6654             : /************************************************************************/
    6655             : /*                SetJPEGQualityAndTablesModeFromFile()                 */
    6656             : /************************************************************************/
    6657             : 
    6658         161 : void GTiffDataset::SetJPEGQualityAndTablesModeFromFile(
    6659             :     int nQuality, bool bHasQuantizationTable, bool bHasHuffmanTable)
    6660             : {
    6661         161 :     if (nQuality > 0)
    6662             :     {
    6663         154 :         CPLDebug("GTiff", "Guessed JPEG quality to be %d", nQuality);
    6664         154 :         m_nJpegQuality = static_cast<signed char>(nQuality);
    6665         154 :         TIFFSetField(m_hTIFF, TIFFTAG_JPEGQUALITY, nQuality);
    6666             : 
    6667             :         // This means we will use the quantization tables from the
    6668             :         // JpegTables tag.
    6669         154 :         m_nJpegTablesMode = JPEGTABLESMODE_QUANT;
    6670             :     }
    6671             :     else
    6672             :     {
    6673           7 :         uint32_t nJPEGTableSize = 0;
    6674           7 :         void *pJPEGTable = nullptr;
    6675           7 :         if (!TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize,
    6676             :                           &pJPEGTable))
    6677             :         {
    6678           4 :             toff_t *panByteCounts = nullptr;
    6679           8 :             const int nBlockCount = m_nPlanarConfig == PLANARCONFIG_SEPARATE
    6680           4 :                                         ? m_nBlocksPerBand * nBands
    6681             :                                         : m_nBlocksPerBand;
    6682           4 :             if (TIFFIsTiled(m_hTIFF))
    6683           1 :                 TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts);
    6684             :             else
    6685           3 :                 TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
    6686             : 
    6687           4 :             bool bFoundNonEmptyBlock = false;
    6688           4 :             if (panByteCounts != nullptr)
    6689             :             {
    6690          56 :                 for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
    6691             :                 {
    6692          53 :                     if (panByteCounts[iBlock] != 0)
    6693             :                     {
    6694           1 :                         bFoundNonEmptyBlock = true;
    6695           1 :                         break;
    6696             :                     }
    6697             :                 }
    6698             :             }
    6699           4 :             if (bFoundNonEmptyBlock)
    6700             :             {
    6701           1 :                 CPLDebug("GTiff", "Could not guess JPEG quality. "
    6702             :                                   "JPEG tables are missing, so going in "
    6703             :                                   "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6704             :                 // Write quantization tables in each strile.
    6705           1 :                 m_nJpegTablesMode = 0;
    6706             :             }
    6707             :         }
    6708             :         else
    6709             :         {
    6710           3 :             if (bHasQuantizationTable)
    6711             :             {
    6712             :                 // FIXME in libtiff: this is likely going to cause issues
    6713             :                 // since libtiff will reuse in each strile the number of
    6714             :                 // the global quantization table, which is invalid.
    6715           1 :                 CPLDebug("GTiff",
    6716             :                          "Could not guess JPEG quality although JPEG "
    6717             :                          "quantization tables are present, so going in "
    6718             :                          "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6719             :             }
    6720             :             else
    6721             :             {
    6722           2 :                 CPLDebug("GTiff",
    6723             :                          "Could not guess JPEG quality since JPEG "
    6724             :                          "quantization tables are not present, so going in "
    6725             :                          "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6726             :             }
    6727             : 
    6728             :             // Write quantization tables in each strile.
    6729           3 :             m_nJpegTablesMode = 0;
    6730             :         }
    6731             :     }
    6732         161 :     if (bHasHuffmanTable)
    6733             :     {
    6734             :         // If there are Huffman tables in header use them, otherwise
    6735             :         // if we use optimized tables, libtiff will currently reuse
    6736             :         // the number of the Huffman tables of the header for the
    6737             :         // optimized version of each strile, which is illegal.
    6738          23 :         m_nJpegTablesMode |= JPEGTABLESMODE_HUFF;
    6739             :     }
    6740         161 :     if (m_nJpegTablesMode >= 0)
    6741         159 :         TIFFSetField(m_hTIFF, TIFFTAG_JPEGTABLESMODE, m_nJpegTablesMode);
    6742         161 : }
    6743             : 
    6744             : /************************************************************************/
    6745             : /*                               Create()                               */
    6746             : /*                                                                      */
    6747             : /*      Create a new GeoTIFF or TIFF file.                              */
    6748             : /************************************************************************/
    6749             : 
    6750        5836 : GDALDataset *GTiffDataset::Create(const char *pszFilename, int nXSize,
    6751             :                                   int nYSize, int l_nBands, GDALDataType eType,
    6752             :                                   CSLConstList papszParamList)
    6753             : 
    6754             : {
    6755        5836 :     VSILFILE *l_fpL = nullptr;
    6756       11672 :     CPLString l_osTmpFilename;
    6757             : 
    6758             :     const int nColorTableMultiplier = std::max(
    6759       11672 :         1,
    6760       11672 :         std::min(257,
    6761        5836 :                  atoi(CSLFetchNameValueDef(
    6762             :                      papszParamList, "COLOR_TABLE_MULTIPLIER",
    6763        5836 :                      CPLSPrintf("%d", DEFAULT_COLOR_TABLE_MULTIPLIER_257)))));
    6764             : 
    6765             :     /* -------------------------------------------------------------------- */
    6766             :     /*      Create the underlying TIFF file.                                */
    6767             :     /* -------------------------------------------------------------------- */
    6768             :     bool bTileInterleaving;
    6769             :     TIFF *l_hTIFF =
    6770        5836 :         CreateLL(pszFilename, nXSize, nYSize, l_nBands, eType, 0,
    6771             :                  nColorTableMultiplier, papszParamList, &l_fpL, l_osTmpFilename,
    6772             :                  /* bCreateCopy=*/false, bTileInterleaving);
    6773        5836 :     const bool bStreaming = !l_osTmpFilename.empty();
    6774             : 
    6775        5836 :     if (l_hTIFF == nullptr)
    6776          38 :         return nullptr;
    6777             : 
    6778             :     /* -------------------------------------------------------------------- */
    6779             :     /*      Create the new GTiffDataset object.                             */
    6780             :     /* -------------------------------------------------------------------- */
    6781       11596 :     auto poDS = std::make_unique<GTiffDataset>();
    6782        5798 :     poDS->m_hTIFF = l_hTIFF;
    6783        5798 :     poDS->m_fpL = l_fpL;
    6784        5798 :     const bool bSuppressASAP = CPLTestBool(
    6785             :         CSLFetchNameValueDef(papszParamList, "@SUPPRESS_ASAP", "NO"));
    6786        5798 :     if (bSuppressASAP)
    6787          36 :         poDS->MarkSuppressOnClose();
    6788        5798 :     if (bStreaming)
    6789             :     {
    6790           4 :         poDS->m_bStreamingOut = true;
    6791           4 :         poDS->m_pszTmpFilename = CPLStrdup(l_osTmpFilename);
    6792           4 :         poDS->m_fpToWrite = VSIFOpenL(pszFilename, "wb");
    6793           4 :         if (poDS->m_fpToWrite == nullptr)
    6794             :         {
    6795           1 :             VSIUnlink(l_osTmpFilename);
    6796           1 :             return nullptr;
    6797             :         }
    6798             :     }
    6799        5797 :     poDS->nRasterXSize = nXSize;
    6800        5797 :     poDS->nRasterYSize = nYSize;
    6801        5797 :     poDS->eAccess = GA_Update;
    6802             : 
    6803             :     // This will avoid GTiffDataset::GetSiblingFiles() to trigger a directory
    6804             :     // listing, which is potentially costly and only makes sense when opening
    6805             :     // new files, not creating new ones. Helps for scenario like
    6806             :     // https://github.com/OSGeo/gdal/issues/13930
    6807        5797 :     poDS->m_bHasGotSiblingFiles = true;
    6808             : 
    6809        5797 :     poDS->m_nColorTableMultiplier = nColorTableMultiplier;
    6810             : 
    6811        5797 :     poDS->m_bCrystalized = false;
    6812        5797 :     poDS->m_nSamplesPerPixel = static_cast<uint16_t>(l_nBands);
    6813        5797 :     poDS->m_osFilename = pszFilename;
    6814             : 
    6815             :     // Don't try to load external metadata files (#6597).
    6816        5797 :     poDS->m_bIMDRPCMetadataLoaded = true;
    6817             : 
    6818             :     // Avoid premature crystalization that will cause directory re-writing if
    6819             :     // GetProjectionRef() or GetGeoTransform() are called on the newly created
    6820             :     // GeoTIFF.
    6821        5797 :     poDS->m_bLookedForProjection = true;
    6822             : 
    6823        5797 :     TIFFGetField(l_hTIFF, TIFFTAG_SAMPLEFORMAT, &(poDS->m_nSampleFormat));
    6824        5797 :     TIFFGetField(l_hTIFF, TIFFTAG_PLANARCONFIG, &(poDS->m_nPlanarConfig));
    6825             :     // Weird that we need this, but otherwise we get a Valgrind warning on
    6826             :     // tiff_write_124.
    6827        5797 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &(poDS->m_nPhotometric)))
    6828           1 :         poDS->m_nPhotometric = PHOTOMETRIC_MINISBLACK;
    6829        5797 :     TIFFGetField(l_hTIFF, TIFFTAG_BITSPERSAMPLE, &(poDS->m_nBitsPerSample));
    6830        5797 :     TIFFGetField(l_hTIFF, TIFFTAG_COMPRESSION, &(poDS->m_nCompression));
    6831             : 
    6832        5797 :     if (TIFFIsTiled(l_hTIFF))
    6833             :     {
    6834         408 :         TIFFGetField(l_hTIFF, TIFFTAG_TILEWIDTH, &(poDS->m_nBlockXSize));
    6835         408 :         TIFFGetField(l_hTIFF, TIFFTAG_TILELENGTH, &(poDS->m_nBlockYSize));
    6836             :     }
    6837             :     else
    6838             :     {
    6839        5389 :         if (!TIFFGetField(l_hTIFF, TIFFTAG_ROWSPERSTRIP,
    6840        5389 :                           &(poDS->m_nRowsPerStrip)))
    6841           0 :             poDS->m_nRowsPerStrip = 1;  // Dummy value.
    6842             : 
    6843        5389 :         poDS->m_nBlockXSize = nXSize;
    6844       10778 :         poDS->m_nBlockYSize =
    6845        5389 :             std::min(static_cast<int>(poDS->m_nRowsPerStrip), nYSize);
    6846             :     }
    6847             : 
    6848        5797 :     if (!poDS->ComputeBlocksPerColRowAndBand(l_nBands))
    6849             :     {
    6850           0 :         poDS->m_fpL->CancelCreation();
    6851           0 :         return nullptr;
    6852             :     }
    6853             : 
    6854        5797 :     poDS->m_eProfile = GetProfile(CSLFetchNameValue(papszParamList, "PROFILE"));
    6855             : 
    6856             :     /* -------------------------------------------------------------------- */
    6857             :     /*      YCbCr JPEG compressed images should be translated on the fly    */
    6858             :     /*      to RGB by libtiff/libjpeg unless specifically requested         */
    6859             :     /*      otherwise.                                                      */
    6860             :     /* -------------------------------------------------------------------- */
    6861        5797 :     if (poDS->m_nCompression == COMPRESSION_JPEG &&
    6862        5818 :         poDS->m_nPhotometric == PHOTOMETRIC_YCBCR &&
    6863          21 :         CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
    6864             :     {
    6865          21 :         int nColorMode = 0;
    6866             : 
    6867          21 :         poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr",
    6868             :                               GDAL_MDD_IMAGE_STRUCTURE);
    6869          42 :         if (!TIFFGetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode) ||
    6870          21 :             nColorMode != JPEGCOLORMODE_RGB)
    6871          21 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    6872             :     }
    6873             : 
    6874        5797 :     if (poDS->m_nCompression == COMPRESSION_LERC)
    6875             :     {
    6876          26 :         uint32_t nLercParamCount = 0;
    6877          26 :         uint32_t *panLercParams = nullptr;
    6878          26 :         if (TIFFGetField(l_hTIFF, TIFFTAG_LERC_PARAMETERS, &nLercParamCount,
    6879          52 :                          &panLercParams) &&
    6880          26 :             nLercParamCount == 2)
    6881             :         {
    6882          26 :             memcpy(poDS->m_anLercAddCompressionAndVersion, panLercParams,
    6883             :                    sizeof(poDS->m_anLercAddCompressionAndVersion));
    6884             :         }
    6885             :     }
    6886             : 
    6887             :     /* -------------------------------------------------------------------- */
    6888             :     /*      Read palette back as a color table if it has one.               */
    6889             :     /* -------------------------------------------------------------------- */
    6890        5797 :     unsigned short *panRed = nullptr;
    6891        5797 :     unsigned short *panGreen = nullptr;
    6892        5797 :     unsigned short *panBlue = nullptr;
    6893             : 
    6894        5801 :     if (poDS->m_nPhotometric == PHOTOMETRIC_PALETTE &&
    6895           4 :         TIFFGetField(l_hTIFF, TIFFTAG_COLORMAP, &panRed, &panGreen, &panBlue))
    6896             :     {
    6897             : 
    6898           4 :         poDS->m_poColorTable = std::make_unique<GDALColorTable>();
    6899             : 
    6900           4 :         const int nColorCount = 1 << poDS->m_nBitsPerSample;
    6901             : 
    6902        1028 :         for (int iColor = nColorCount - 1; iColor >= 0; iColor--)
    6903             :         {
    6904        1024 :             const GDALColorEntry oEntry = {
    6905        1024 :                 static_cast<short>(panRed[iColor] / nColorTableMultiplier),
    6906        1024 :                 static_cast<short>(panGreen[iColor] / nColorTableMultiplier),
    6907        1024 :                 static_cast<short>(panBlue[iColor] / nColorTableMultiplier),
    6908        1024 :                 static_cast<short>(255)};
    6909             : 
    6910        1024 :             poDS->m_poColorTable->SetColorEntry(iColor, &oEntry);
    6911             :         }
    6912             :     }
    6913             : 
    6914             :     /* -------------------------------------------------------------------- */
    6915             :     /*      Do we want to ensure all blocks get written out on close to     */
    6916             :     /*      avoid sparse files?                                             */
    6917             :     /* -------------------------------------------------------------------- */
    6918        5797 :     if (!CPLFetchBool(papszParamList, "SPARSE_OK", false))
    6919        5687 :         poDS->m_bFillEmptyTilesAtClosing = true;
    6920             : 
    6921        5797 :     poDS->m_bWriteEmptyTiles =
    6922        6623 :         bStreaming || (poDS->m_nCompression != COMPRESSION_NONE &&
    6923         826 :                        poDS->m_bFillEmptyTilesAtClosing);
    6924             :     // Only required for people writing non-compressed striped files in the
    6925             :     // right order and wanting all tstrips to be written in the same order
    6926             :     // so that the end result can be memory mapped without knowledge of each
    6927             :     // strip offset.
    6928        5797 :     if (CPLTestBool(CSLFetchNameValueDef(
    6929       11594 :             papszParamList, "WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")) ||
    6930        5797 :         CPLTestBool(CSLFetchNameValueDef(
    6931             :             papszParamList, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")))
    6932             :     {
    6933          26 :         poDS->m_bWriteEmptyTiles = true;
    6934             :     }
    6935             : 
    6936             :     /* -------------------------------------------------------------------- */
    6937             :     /*      Preserve creation options for consulting later (for instance    */
    6938             :     /*      to decide if a TFW file should be written).                     */
    6939             :     /* -------------------------------------------------------------------- */
    6940        5797 :     poDS->m_papszCreationOptions = CSLDuplicate(papszParamList);
    6941             : 
    6942        5797 :     poDS->m_nZLevel = GTiffGetZLevel(papszParamList);
    6943        5797 :     poDS->m_nLZMAPreset = GTiffGetLZMAPreset(papszParamList);
    6944        5797 :     poDS->m_nZSTDLevel = GTiffGetZSTDPreset(papszParamList);
    6945        5797 :     poDS->m_nWebPLevel = GTiffGetWebPLevel(papszParamList);
    6946        5797 :     poDS->m_bWebPLossless = GTiffGetWebPLossless(papszParamList);
    6947        5799 :     if (poDS->m_nWebPLevel != 100 && poDS->m_bWebPLossless &&
    6948           2 :         CSLFetchNameValue(papszParamList, "WEBP_LEVEL"))
    6949             :     {
    6950           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    6951             :                  "WEBP_LEVEL is specified, but WEBP_LOSSLESS=YES. "
    6952             :                  "WEBP_LEVEL will be ignored.");
    6953             :     }
    6954        5797 :     poDS->m_nJpegQuality = GTiffGetJpegQuality(papszParamList);
    6955        5797 :     poDS->m_nJpegTablesMode = GTiffGetJpegTablesMode(papszParamList);
    6956        5797 :     poDS->m_dfMaxZError = GTiffGetLERCMaxZError(papszParamList);
    6957        5797 :     poDS->m_dfMaxZErrorOverview = GTiffGetLERCMaxZErrorOverview(papszParamList);
    6958             : #if HAVE_JXL
    6959        5797 :     poDS->m_bJXLLossless = GTiffGetJXLLossless(papszParamList);
    6960        5797 :     poDS->m_nJXLEffort = GTiffGetJXLEffort(papszParamList);
    6961        5797 :     poDS->m_fJXLDistance = GTiffGetJXLDistance(papszParamList);
    6962        5797 :     poDS->m_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszParamList);
    6963             : #endif
    6964        5797 :     poDS->InitCreationOrOpenOptions(true, papszParamList);
    6965             : 
    6966             :     /* -------------------------------------------------------------------- */
    6967             :     /*      Create band information objects.                                */
    6968             :     /* -------------------------------------------------------------------- */
    6969      308467 :     for (int iBand = 0; iBand < l_nBands; ++iBand)
    6970             :     {
    6971      371903 :         if (poDS->m_nBitsPerSample == 8 || poDS->m_nBitsPerSample == 16 ||
    6972      372155 :             poDS->m_nBitsPerSample == 32 || poDS->m_nBitsPerSample == 64 ||
    6973         252 :             poDS->m_nBitsPerSample == 128)
    6974             :         {
    6975      605188 :             poDS->SetBand(iBand + 1, std::make_unique<GTiffRasterBand>(
    6976      605188 :                                          poDS.get(), iBand + 1));
    6977             :         }
    6978             :         else
    6979             :         {
    6980         152 :             poDS->SetBand(iBand + 1, std::make_unique<GTiffOddBitsBand>(
    6981          76 :                                          poDS.get(), iBand + 1));
    6982         152 :             poDS->GetRasterBand(iBand + 1)->SetMetadataItem(
    6983         152 :                 GDALMD_NBITS, CPLString().Printf("%d", poDS->m_nBitsPerSample),
    6984          76 :                 GDAL_MDD_IMAGE_STRUCTURE);
    6985             :         }
    6986             :     }
    6987             : 
    6988        5797 :     poDS->GetDiscardLsbOption(papszParamList);
    6989             : 
    6990        5797 :     if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG && l_nBands != 1)
    6991         851 :         poDS->SetMetadataItem(GDALMD_INTERLEAVE, "PIXEL",
    6992             :                               GDAL_MDD_IMAGE_STRUCTURE);
    6993             :     else
    6994        4946 :         poDS->SetMetadataItem(GDALMD_INTERLEAVE, "BAND",
    6995             :                               GDAL_MDD_IMAGE_STRUCTURE);
    6996             : 
    6997        5797 :     poDS->oOvManager.Initialize(poDS.get(), pszFilename);
    6998             : 
    6999        5797 :     return poDS.release();
    7000             : }
    7001             : 
    7002             : /************************************************************************/
    7003             : /*                         CopyImageryAndMask()                         */
    7004             : /************************************************************************/
    7005             : 
    7006         355 : CPLErr GTiffDataset::CopyImageryAndMask(GTiffDataset *poDstDS,
    7007             :                                         GDALDataset *poSrcDS,
    7008             :                                         GDALRasterBand *poSrcMaskBand,
    7009             :                                         GDALProgressFunc pfnProgress,
    7010             :                                         void *pProgressData)
    7011             : {
    7012         355 :     CPLErr eErr = CE_None;
    7013             : 
    7014         355 :     const auto eType = poDstDS->GetRasterBand(1)->GetRasterDataType();
    7015         355 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
    7016         355 :     const int l_nBands = poDstDS->GetRasterCount();
    7017             :     GByte *pBlockBuffer = static_cast<GByte *>(
    7018         355 :         VSI_MALLOC3_VERBOSE(poDstDS->m_nBlockXSize, poDstDS->m_nBlockYSize,
    7019             :                             cpl::fits_on<int>(l_nBands * nDataTypeSize)));
    7020         355 :     if (pBlockBuffer == nullptr)
    7021             :     {
    7022           0 :         eErr = CE_Failure;
    7023             :     }
    7024         355 :     const int nYSize = poDstDS->nRasterYSize;
    7025         355 :     const int nXSize = poDstDS->nRasterXSize;
    7026             :     const bool bIsOddBand =
    7027         355 :         dynamic_cast<GTiffOddBitsBand *>(poDstDS->GetRasterBand(1)) != nullptr;
    7028             : 
    7029         355 :     if (poDstDS->m_poMaskDS)
    7030             :     {
    7031          58 :         CPLAssert(poDstDS->m_poMaskDS->m_nBlockXSize == poDstDS->m_nBlockXSize);
    7032          58 :         CPLAssert(poDstDS->m_poMaskDS->m_nBlockYSize == poDstDS->m_nBlockYSize);
    7033             :     }
    7034             : 
    7035         355 :     if (poDstDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE &&
    7036          59 :         !poDstDS->m_bTileInterleave)
    7037             :     {
    7038          46 :         int iBlock = 0;
    7039          46 :         const int nBlocks = poDstDS->m_nBlocksPerBand *
    7040          46 :                             (l_nBands + (poDstDS->m_poMaskDS ? 1 : 0));
    7041         199 :         for (int i = 0; eErr == CE_None && i < l_nBands; i++)
    7042             :         {
    7043         351 :             for (int iY = 0; iY < nYSize && eErr == CE_None;
    7044         198 :                  iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
    7045         198 :                            ? nYSize
    7046          59 :                            : iY + poDstDS->m_nBlockYSize))
    7047             :             {
    7048             :                 const int nReqYSize =
    7049         198 :                     std::min(nYSize - iY, poDstDS->m_nBlockYSize);
    7050         501 :                 for (int iX = 0; iX < nXSize && eErr == CE_None;
    7051         303 :                      iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
    7052         303 :                                ? nXSize
    7053         155 :                                : iX + poDstDS->m_nBlockXSize))
    7054             :                 {
    7055             :                     const int nReqXSize =
    7056         303 :                         std::min(nXSize - iX, poDstDS->m_nBlockXSize);
    7057         303 :                     if (nReqXSize < poDstDS->m_nBlockXSize ||
    7058         155 :                         nReqYSize < poDstDS->m_nBlockYSize)
    7059             :                     {
    7060         193 :                         memset(pBlockBuffer, 0,
    7061         193 :                                static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7062         193 :                                    poDstDS->m_nBlockYSize * nDataTypeSize);
    7063             :                     }
    7064         303 :                     eErr = poSrcDS->GetRasterBand(i + 1)->RasterIO(
    7065             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7066             :                         nReqXSize, nReqYSize, eType, nDataTypeSize,
    7067         303 :                         static_cast<GSpacing>(nDataTypeSize) *
    7068         303 :                             poDstDS->m_nBlockXSize,
    7069             :                         nullptr);
    7070         303 :                     if (eErr == CE_None)
    7071             :                     {
    7072         303 :                         eErr = poDstDS->WriteEncodedTileOrStrip(
    7073             :                             iBlock, pBlockBuffer, false);
    7074             :                     }
    7075             : 
    7076         303 :                     iBlock++;
    7077         606 :                     if (pfnProgress &&
    7078         303 :                         !pfnProgress(static_cast<double>(iBlock) / nBlocks,
    7079             :                                      nullptr, pProgressData))
    7080             :                     {
    7081           0 :                         eErr = CE_Failure;
    7082             :                     }
    7083             : 
    7084         303 :                     if (poDstDS->m_bWriteError)
    7085           0 :                         eErr = CE_Failure;
    7086             :                 }
    7087             :             }
    7088             :         }
    7089          46 :         if (poDstDS->m_poMaskDS && eErr == CE_None)
    7090             :         {
    7091           6 :             int iBlockMask = 0;
    7092          17 :             for (int iY = 0, nYBlock = 0; iY < nYSize && eErr == CE_None;
    7093          11 :                  iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
    7094          11 :                            ? nYSize
    7095           5 :                            : iY + poDstDS->m_nBlockYSize),
    7096             :                      nYBlock++)
    7097             :             {
    7098             :                 const int nReqYSize =
    7099          11 :                     std::min(nYSize - iY, poDstDS->m_nBlockYSize);
    7100          49 :                 for (int iX = 0, nXBlock = 0; iX < nXSize && eErr == CE_None;
    7101          38 :                      iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
    7102          38 :                                ? nXSize
    7103          30 :                                : iX + poDstDS->m_nBlockXSize),
    7104             :                          nXBlock++)
    7105             :                 {
    7106             :                     const int nReqXSize =
    7107          38 :                         std::min(nXSize - iX, poDstDS->m_nBlockXSize);
    7108          38 :                     if (nReqXSize < poDstDS->m_nBlockXSize ||
    7109          30 :                         nReqYSize < poDstDS->m_nBlockYSize)
    7110             :                     {
    7111          16 :                         memset(pBlockBuffer, 0,
    7112          16 :                                static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7113          16 :                                    poDstDS->m_nBlockYSize);
    7114             :                     }
    7115          76 :                     eErr = poSrcMaskBand->RasterIO(
    7116             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7117             :                         nReqXSize, nReqYSize, GDT_UInt8, 1,
    7118          38 :                         poDstDS->m_nBlockXSize, nullptr);
    7119          38 :                     if (eErr == CE_None)
    7120             :                     {
    7121             :                         // Avoid any attempt to load from disk
    7122          38 :                         poDstDS->m_poMaskDS->m_nLoadedBlock = iBlockMask;
    7123             :                         eErr =
    7124          38 :                             poDstDS->m_poMaskDS->GetRasterBand(1)->WriteBlock(
    7125             :                                 nXBlock, nYBlock, pBlockBuffer);
    7126          38 :                         if (eErr == CE_None)
    7127          38 :                             eErr = poDstDS->m_poMaskDS->FlushBlockBuf();
    7128             :                     }
    7129             : 
    7130          38 :                     iBlockMask++;
    7131          76 :                     if (pfnProgress &&
    7132          38 :                         !pfnProgress(static_cast<double>(iBlock + iBlockMask) /
    7133             :                                          nBlocks,
    7134             :                                      nullptr, pProgressData))
    7135             :                     {
    7136           0 :                         eErr = CE_Failure;
    7137             :                     }
    7138             : 
    7139          38 :                     if (poDstDS->m_poMaskDS->m_bWriteError)
    7140           0 :                         eErr = CE_Failure;
    7141             :                 }
    7142             :             }
    7143          46 :         }
    7144             :     }
    7145             :     else
    7146             :     {
    7147         309 :         int iBlock = 0;
    7148         309 :         const int nBlocks = poDstDS->m_nBlocksPerBand;
    7149        7126 :         for (int iY = 0, nYBlock = 0; iY < nYSize && eErr == CE_None;
    7150        6817 :              iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
    7151        6817 :                        ? nYSize
    7152        6584 :                        : iY + poDstDS->m_nBlockYSize),
    7153             :                  nYBlock++)
    7154             :         {
    7155        6817 :             const int nReqYSize = std::min(nYSize - iY, poDstDS->m_nBlockYSize);
    7156       26643 :             for (int iX = 0, nXBlock = 0; iX < nXSize && eErr == CE_None;
    7157       19826 :                  iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
    7158       19826 :                            ? nXSize
    7159       19505 :                            : iX + poDstDS->m_nBlockXSize),
    7160             :                      nXBlock++)
    7161             :             {
    7162             :                 const int nReqXSize =
    7163       19826 :                     std::min(nXSize - iX, poDstDS->m_nBlockXSize);
    7164       19826 :                 if (nReqXSize < poDstDS->m_nBlockXSize ||
    7165       19505 :                     nReqYSize < poDstDS->m_nBlockYSize)
    7166             :                 {
    7167         506 :                     memset(pBlockBuffer, 0,
    7168         506 :                            static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7169         506 :                                poDstDS->m_nBlockYSize * l_nBands *
    7170         506 :                                nDataTypeSize);
    7171             :                 }
    7172             : 
    7173       19826 :                 if (poDstDS->m_bTileInterleave)
    7174             :                 {
    7175         114 :                     eErr = poSrcDS->RasterIO(
    7176             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7177             :                         nReqXSize, nReqYSize, eType, l_nBands, nullptr,
    7178             :                         nDataTypeSize,
    7179          57 :                         static_cast<GSpacing>(nDataTypeSize) *
    7180          57 :                             poDstDS->m_nBlockXSize,
    7181          57 :                         static_cast<GSpacing>(nDataTypeSize) *
    7182          57 :                             poDstDS->m_nBlockXSize * poDstDS->m_nBlockYSize,
    7183             :                         nullptr);
    7184          57 :                     if (eErr == CE_None)
    7185             :                     {
    7186         228 :                         for (int i = 0; eErr == CE_None && i < l_nBands; i++)
    7187             :                         {
    7188         171 :                             eErr = poDstDS->WriteEncodedTileOrStrip(
    7189         171 :                                 iBlock + i * poDstDS->m_nBlocksPerBand,
    7190         171 :                                 pBlockBuffer + static_cast<size_t>(i) *
    7191         171 :                                                    poDstDS->m_nBlockXSize *
    7192         171 :                                                    poDstDS->m_nBlockYSize *
    7193         171 :                                                    nDataTypeSize,
    7194             :                                 false);
    7195             :                         }
    7196             :                     }
    7197             :                 }
    7198       19769 :                 else if (!bIsOddBand)
    7199             :                 {
    7200       39416 :                     eErr = poSrcDS->RasterIO(
    7201             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7202             :                         nReqXSize, nReqYSize, eType, l_nBands, nullptr,
    7203       19708 :                         static_cast<GSpacing>(nDataTypeSize) * l_nBands,
    7204       19708 :                         static_cast<GSpacing>(nDataTypeSize) * l_nBands *
    7205       19708 :                             poDstDS->m_nBlockXSize,
    7206             :                         nDataTypeSize, nullptr);
    7207       19708 :                     if (eErr == CE_None)
    7208             :                     {
    7209       19707 :                         eErr = poDstDS->WriteEncodedTileOrStrip(
    7210             :                             iBlock, pBlockBuffer, false);
    7211             :                     }
    7212             :                 }
    7213             :                 else
    7214             :                 {
    7215             :                     // In the odd bit case, this is a bit messy to ensure
    7216             :                     // the strile gets written synchronously.
    7217             :                     // We load the content of the n-1 bands in the cache,
    7218             :                     // and for the last band we invoke WriteBlock() directly
    7219             :                     // We also force FlushBlockBuf()
    7220         122 :                     std::vector<GDALRasterBlock *> apoLockedBlocks;
    7221          91 :                     for (int i = 0; eErr == CE_None && i < l_nBands - 1; i++)
    7222             :                     {
    7223             :                         auto poBlock =
    7224          30 :                             poDstDS->GetRasterBand(i + 1)->GetLockedBlockRef(
    7225          30 :                                 nXBlock, nYBlock, TRUE);
    7226          30 :                         if (poBlock)
    7227             :                         {
    7228          60 :                             eErr = poSrcDS->GetRasterBand(i + 1)->RasterIO(
    7229             :                                 GF_Read, iX, iY, nReqXSize, nReqYSize,
    7230             :                                 poBlock->GetDataRef(), nReqXSize, nReqYSize,
    7231             :                                 eType, nDataTypeSize,
    7232          30 :                                 static_cast<GSpacing>(nDataTypeSize) *
    7233          30 :                                     poDstDS->m_nBlockXSize,
    7234             :                                 nullptr);
    7235          30 :                             poBlock->MarkDirty();
    7236          30 :                             apoLockedBlocks.emplace_back(poBlock);
    7237             :                         }
    7238             :                         else
    7239             :                         {
    7240           0 :                             eErr = CE_Failure;
    7241             :                         }
    7242             :                     }
    7243          61 :                     if (eErr == CE_None)
    7244             :                     {
    7245         122 :                         eErr = poSrcDS->GetRasterBand(l_nBands)->RasterIO(
    7246             :                             GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7247             :                             nReqXSize, nReqYSize, eType, nDataTypeSize,
    7248          61 :                             static_cast<GSpacing>(nDataTypeSize) *
    7249          61 :                                 poDstDS->m_nBlockXSize,
    7250             :                             nullptr);
    7251             :                     }
    7252          61 :                     if (eErr == CE_None)
    7253             :                     {
    7254             :                         // Avoid any attempt to load from disk
    7255          61 :                         poDstDS->m_nLoadedBlock = iBlock;
    7256          61 :                         eErr = poDstDS->GetRasterBand(l_nBands)->WriteBlock(
    7257             :                             nXBlock, nYBlock, pBlockBuffer);
    7258          61 :                         if (eErr == CE_None)
    7259          61 :                             eErr = poDstDS->FlushBlockBuf();
    7260             :                     }
    7261          91 :                     for (auto poBlock : apoLockedBlocks)
    7262             :                     {
    7263          30 :                         poBlock->MarkClean();
    7264          30 :                         poBlock->DropLock();
    7265             :                     }
    7266             :                 }
    7267             : 
    7268       19826 :                 if (eErr == CE_None && poDstDS->m_poMaskDS)
    7269             :                 {
    7270        4662 :                     if (nReqXSize < poDstDS->m_nBlockXSize ||
    7271        4620 :                         nReqYSize < poDstDS->m_nBlockYSize)
    7272             :                     {
    7273          79 :                         memset(pBlockBuffer, 0,
    7274          79 :                                static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7275          79 :                                    poDstDS->m_nBlockYSize);
    7276             :                     }
    7277        9324 :                     eErr = poSrcMaskBand->RasterIO(
    7278             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7279             :                         nReqXSize, nReqYSize, GDT_UInt8, 1,
    7280        4662 :                         poDstDS->m_nBlockXSize, nullptr);
    7281        4662 :                     if (eErr == CE_None)
    7282             :                     {
    7283             :                         // Avoid any attempt to load from disk
    7284        4662 :                         poDstDS->m_poMaskDS->m_nLoadedBlock = iBlock;
    7285             :                         eErr =
    7286        4662 :                             poDstDS->m_poMaskDS->GetRasterBand(1)->WriteBlock(
    7287             :                                 nXBlock, nYBlock, pBlockBuffer);
    7288        4662 :                         if (eErr == CE_None)
    7289        4662 :                             eErr = poDstDS->m_poMaskDS->FlushBlockBuf();
    7290             :                     }
    7291             :                 }
    7292       19826 :                 if (poDstDS->m_bWriteError)
    7293           6 :                     eErr = CE_Failure;
    7294             : 
    7295       19826 :                 iBlock++;
    7296       39652 :                 if (pfnProgress &&
    7297       19826 :                     !pfnProgress(static_cast<double>(iBlock) / nBlocks, nullptr,
    7298             :                                  pProgressData))
    7299             :                 {
    7300           0 :                     eErr = CE_Failure;
    7301             :                 }
    7302             :             }
    7303             :         }
    7304             :     }
    7305             : 
    7306         355 :     poDstDS->FlushCache(false);  // mostly to wait for thread completion
    7307         355 :     VSIFree(pBlockBuffer);
    7308             : 
    7309         355 :     return eErr;
    7310             : }
    7311             : 
    7312             : /************************************************************************/
    7313             : /*                             CreateCopy()                             */
    7314             : /************************************************************************/
    7315             : 
    7316        2190 : GDALDataset *GTiffDataset::CreateCopy(const char *pszFilename,
    7317             :                                       GDALDataset *poSrcDS, int bStrict,
    7318             :                                       CSLConstList papszOptions,
    7319             :                                       GDALProgressFunc pfnProgress,
    7320             :                                       void *pProgressData)
    7321             : 
    7322             : {
    7323        2190 :     if (poSrcDS->GetRasterCount() == 0)
    7324             :     {
    7325           2 :         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    7326             :                     "Unable to export GeoTIFF files with zero bands.");
    7327           2 :         return nullptr;
    7328             :     }
    7329             : 
    7330        2188 :     GDALRasterBand *const poPBand = poSrcDS->GetRasterBand(1);
    7331        2188 :     GDALDataType eType = poPBand->GetRasterDataType();
    7332             : 
    7333             :     /* -------------------------------------------------------------------- */
    7334             :     /*      Check, whether all bands in input dataset has the same type.    */
    7335             :     /* -------------------------------------------------------------------- */
    7336        2188 :     const int l_nBands = poSrcDS->GetRasterCount();
    7337        5123 :     for (int iBand = 2; iBand <= l_nBands; ++iBand)
    7338             :     {
    7339        2935 :         if (eType != poSrcDS->GetRasterBand(iBand)->GetRasterDataType())
    7340             :         {
    7341           0 :             if (bStrict)
    7342             :             {
    7343           0 :                 ReportError(
    7344             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    7345             :                     "Unable to export GeoTIFF file with different datatypes "
    7346             :                     "per different bands. All bands should have the same "
    7347             :                     "types in TIFF.");
    7348           0 :                 return nullptr;
    7349             :             }
    7350             :             else
    7351             :             {
    7352           0 :                 ReportError(
    7353             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    7354             :                     "Unable to export GeoTIFF file with different datatypes "
    7355             :                     "per different bands. All bands should have the same "
    7356             :                     "types in TIFF.");
    7357             :             }
    7358             :         }
    7359             :     }
    7360             : 
    7361             :     /* -------------------------------------------------------------------- */
    7362             :     /*      Capture the profile.                                            */
    7363             :     /* -------------------------------------------------------------------- */
    7364             :     const GTiffProfile eProfile =
    7365        2188 :         GetProfile(CSLFetchNameValue(papszOptions, "PROFILE"));
    7366             : 
    7367        2188 :     const bool bGeoTIFF = eProfile != GTiffProfile::BASELINE;
    7368             : 
    7369             :     /* -------------------------------------------------------------------- */
    7370             :     /*      Special handling for NBITS.  Copy from band metadata if found.  */
    7371             :     /* -------------------------------------------------------------------- */
    7372        2188 :     char **papszCreateOptions = CSLDuplicate(papszOptions);
    7373             : 
    7374        2188 :     if (poPBand->GetMetadataItem(GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE) !=
    7375          17 :             nullptr &&
    7376          17 :         atoi(poPBand->GetMetadataItem(GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE)) >
    7377        2205 :             0 &&
    7378          17 :         CSLFetchNameValue(papszCreateOptions, GDALMD_NBITS) == nullptr)
    7379             :     {
    7380           3 :         papszCreateOptions = CSLSetNameValue(
    7381             :             papszCreateOptions, GDALMD_NBITS,
    7382           3 :             poPBand->GetMetadataItem(GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE));
    7383             :     }
    7384             : 
    7385        2188 :     if (CSLFetchNameValue(papszOptions, "PIXELTYPE") == nullptr &&
    7386             :         eType == GDT_UInt8)
    7387             :     {
    7388        1813 :         poPBand->EnablePixelTypeSignedByteWarning(false);
    7389             :         const char *pszPixelType =
    7390        1813 :             poPBand->GetMetadataItem("PIXELTYPE", GDAL_MDD_IMAGE_STRUCTURE);
    7391        1813 :         poPBand->EnablePixelTypeSignedByteWarning(true);
    7392        1813 :         if (pszPixelType)
    7393             :         {
    7394           1 :             papszCreateOptions =
    7395           1 :                 CSLSetNameValue(papszCreateOptions, "PIXELTYPE", pszPixelType);
    7396             :         }
    7397             :     }
    7398             : 
    7399             :     /* -------------------------------------------------------------------- */
    7400             :     /*      Color profile.  Copy from band metadata if found.              */
    7401             :     /* -------------------------------------------------------------------- */
    7402        2188 :     if (bGeoTIFF)
    7403             :     {
    7404        2171 :         const char *pszOptionsMD[] = {"SOURCE_ICC_PROFILE",
    7405             :                                       "SOURCE_PRIMARIES_RED",
    7406             :                                       "SOURCE_PRIMARIES_GREEN",
    7407             :                                       "SOURCE_PRIMARIES_BLUE",
    7408             :                                       "SOURCE_WHITEPOINT",
    7409             :                                       "TIFFTAG_TRANSFERFUNCTION_RED",
    7410             :                                       "TIFFTAG_TRANSFERFUNCTION_GREEN",
    7411             :                                       "TIFFTAG_TRANSFERFUNCTION_BLUE",
    7412             :                                       "TIFFTAG_TRANSFERRANGE_BLACK",
    7413             :                                       "TIFFTAG_TRANSFERRANGE_WHITE",
    7414             :                                       nullptr};
    7415             : 
    7416             :         // Copy all the tags.  Options will override tags in the source.
    7417        2171 :         int i = 0;
    7418       23861 :         while (pszOptionsMD[i] != nullptr)
    7419             :         {
    7420             :             char const *pszMD =
    7421       21692 :                 CSLFetchNameValue(papszOptions, pszOptionsMD[i]);
    7422       21692 :             if (pszMD == nullptr)
    7423             :                 pszMD =
    7424       21684 :                     poSrcDS->GetMetadataItem(pszOptionsMD[i], "COLOR_PROFILE");
    7425             : 
    7426       21692 :             if ((pszMD != nullptr) && !EQUAL(pszMD, ""))
    7427             :             {
    7428          16 :                 papszCreateOptions =
    7429          16 :                     CSLSetNameValue(papszCreateOptions, pszOptionsMD[i], pszMD);
    7430             : 
    7431             :                 // If an ICC profile exists, other tags are not needed.
    7432          16 :                 if (EQUAL(pszOptionsMD[i], "SOURCE_ICC_PROFILE"))
    7433           2 :                     break;
    7434             :             }
    7435             : 
    7436       21690 :             ++i;
    7437             :         }
    7438             :     }
    7439             : 
    7440        2188 :     double dfExtraSpaceForOverviews = 0;
    7441             :     const bool bCopySrcOverviews =
    7442        2188 :         CPLFetchBool(papszCreateOptions, "COPY_SRC_OVERVIEWS", false);
    7443        2188 :     std::unique_ptr<GDALDataset> poOvrDS;
    7444        2188 :     int nSrcOverviews = 0;
    7445        2188 :     if (bCopySrcOverviews)
    7446             :     {
    7447             :         const char *pszOvrDS =
    7448         234 :             CSLFetchNameValue(papszCreateOptions, "@OVERVIEW_DATASET");
    7449         234 :         if (pszOvrDS)
    7450             :         {
    7451             :             // Empty string is used by COG driver to indicate that we want
    7452             :             // to ignore source overviews.
    7453          39 :             if (!EQUAL(pszOvrDS, ""))
    7454             :             {
    7455          37 :                 poOvrDS.reset(GDALDataset::Open(pszOvrDS));
    7456          37 :                 if (!poOvrDS)
    7457             :                 {
    7458           0 :                     CSLDestroy(papszCreateOptions);
    7459           0 :                     return nullptr;
    7460             :                 }
    7461          37 :                 if (poOvrDS->GetRasterCount() != l_nBands)
    7462             :                 {
    7463           0 :                     CSLDestroy(papszCreateOptions);
    7464           0 :                     return nullptr;
    7465             :                 }
    7466          37 :                 nSrcOverviews =
    7467          37 :                     poOvrDS->GetRasterBand(1)->GetOverviewCount() + 1;
    7468             :             }
    7469             :         }
    7470             :         else
    7471             :         {
    7472         195 :             nSrcOverviews = poSrcDS->GetRasterBand(1)->GetOverviewCount();
    7473             :         }
    7474             : 
    7475             :         // Limit number of overviews if specified
    7476             :         const char *pszOverviewCount =
    7477         234 :             CSLFetchNameValue(papszCreateOptions, "@OVERVIEW_COUNT");
    7478         234 :         if (pszOverviewCount)
    7479           8 :             nSrcOverviews =
    7480           8 :                 std::max(0, std::min(nSrcOverviews, atoi(pszOverviewCount)));
    7481             : 
    7482         234 :         if (nSrcOverviews)
    7483             :         {
    7484         208 :             for (int j = 1; j <= l_nBands; ++j)
    7485             :             {
    7486             :                 const int nOtherBandOverviewCount =
    7487         136 :                     poOvrDS ? poOvrDS->GetRasterBand(j)->GetOverviewCount() + 1
    7488         200 :                             : poSrcDS->GetRasterBand(j)->GetOverviewCount();
    7489         136 :                 if (nOtherBandOverviewCount < nSrcOverviews)
    7490             :                 {
    7491           1 :                     ReportError(
    7492             :                         pszFilename, CE_Failure, CPLE_NotSupported,
    7493             :                         "COPY_SRC_OVERVIEWS cannot be used when the bands have "
    7494             :                         "not the same number of overview levels.");
    7495           1 :                     CSLDestroy(papszCreateOptions);
    7496           1 :                     return nullptr;
    7497             :                 }
    7498         395 :                 for (int i = 0; i < nSrcOverviews; ++i)
    7499             :                 {
    7500             :                     GDALRasterBand *poOvrBand =
    7501             :                         poOvrDS
    7502         361 :                             ? (i == 0 ? poOvrDS->GetRasterBand(j)
    7503         198 :                                       : poOvrDS->GetRasterBand(j)->GetOverview(
    7504          99 :                                             i - 1))
    7505         353 :                             : poSrcDS->GetRasterBand(j)->GetOverview(i);
    7506         262 :                     if (poOvrBand == nullptr)
    7507             :                     {
    7508           1 :                         ReportError(
    7509             :                             pszFilename, CE_Failure, CPLE_NotSupported,
    7510             :                             "COPY_SRC_OVERVIEWS cannot be used when one "
    7511             :                             "overview band is NULL.");
    7512           1 :                         CSLDestroy(papszCreateOptions);
    7513           1 :                         return nullptr;
    7514             :                     }
    7515             :                     GDALRasterBand *poOvrFirstBand =
    7516             :                         poOvrDS
    7517         360 :                             ? (i == 0 ? poOvrDS->GetRasterBand(1)
    7518         198 :                                       : poOvrDS->GetRasterBand(1)->GetOverview(
    7519          99 :                                             i - 1))
    7520         351 :                             : poSrcDS->GetRasterBand(1)->GetOverview(i);
    7521         521 :                     if (poOvrBand->GetXSize() != poOvrFirstBand->GetXSize() ||
    7522         260 :                         poOvrBand->GetYSize() != poOvrFirstBand->GetYSize())
    7523             :                     {
    7524           1 :                         ReportError(
    7525             :                             pszFilename, CE_Failure, CPLE_NotSupported,
    7526             :                             "COPY_SRC_OVERVIEWS cannot be used when the "
    7527             :                             "overview bands have not the same dimensions "
    7528             :                             "among bands.");
    7529           1 :                         CSLDestroy(papszCreateOptions);
    7530           1 :                         return nullptr;
    7531             :                     }
    7532             :                 }
    7533             :             }
    7534             : 
    7535         205 :             for (int i = 0; i < nSrcOverviews; ++i)
    7536             :             {
    7537             :                 GDALRasterBand *poOvrFirstBand =
    7538             :                     poOvrDS
    7539         211 :                         ? (i == 0
    7540          78 :                                ? poOvrDS->GetRasterBand(1)
    7541          41 :                                : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    7542         188 :                         : poSrcDS->GetRasterBand(1)->GetOverview(i);
    7543         133 :                 dfExtraSpaceForOverviews +=
    7544         133 :                     static_cast<double>(poOvrFirstBand->GetXSize()) *
    7545         133 :                     poOvrFirstBand->GetYSize();
    7546             :             }
    7547          72 :             dfExtraSpaceForOverviews *=
    7548          72 :                 l_nBands * GDALGetDataTypeSizeBytes(eType);
    7549             :         }
    7550             :         else
    7551             :         {
    7552         159 :             CPLDebug("GTiff", "No source overviews to copy");
    7553             :         }
    7554             :     }
    7555             : 
    7556             : /* -------------------------------------------------------------------- */
    7557             : /*      Should we use optimized way of copying from an input JPEG       */
    7558             : /*      dataset?                                                        */
    7559             : /* -------------------------------------------------------------------- */
    7560             : 
    7561             : // TODO(schwehr): Refactor bDirectCopyFromJPEG to be a const.
    7562             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    7563        2185 :     bool bDirectCopyFromJPEG = false;
    7564             : #endif
    7565             : 
    7566             :     // Note: JPEG_DIRECT_COPY is not defined by default, because it is mainly
    7567             :     // useful for debugging purposes.
    7568             : #ifdef JPEG_DIRECT_COPY
    7569             :     if (CPLFetchBool(papszCreateOptions, "JPEG_DIRECT_COPY", false) &&
    7570             :         GTIFF_CanDirectCopyFromJPEG(poSrcDS, papszCreateOptions))
    7571             :     {
    7572             :         CPLDebug("GTiff", "Using special direct copy mode from a JPEG dataset");
    7573             : 
    7574             :         bDirectCopyFromJPEG = true;
    7575             :     }
    7576             : #endif
    7577             : 
    7578             : #ifdef HAVE_LIBJPEG
    7579        2185 :     bool bCopyFromJPEG = false;
    7580             : 
    7581             :     // When CreateCopy'ing() from a JPEG dataset, and asking for COMPRESS=JPEG,
    7582             :     // use DCT coefficients (unless other options are incompatible, like
    7583             :     // strip/tile dimensions, specifying JPEG_QUALITY option, incompatible
    7584             :     // PHOTOMETRIC with the source colorspace, etc.) to avoid the lossy steps
    7585             :     // involved by decompression/recompression.
    7586        4370 :     if (!bDirectCopyFromJPEG &&
    7587        2185 :         GTIFF_CanCopyFromJPEG(poSrcDS, papszCreateOptions))
    7588             :     {
    7589          12 :         CPLDebug("GTiff", "Using special copy mode from a JPEG dataset");
    7590             : 
    7591          12 :         bCopyFromJPEG = true;
    7592             :     }
    7593             : #endif
    7594             : 
    7595             :     /* -------------------------------------------------------------------- */
    7596             :     /*      If the source is RGB, then set the PHOTOMETRIC=RGB value        */
    7597             :     /* -------------------------------------------------------------------- */
    7598             : 
    7599             :     const bool bForcePhotometric =
    7600        2185 :         CSLFetchNameValue(papszOptions, "PHOTOMETRIC") != nullptr;
    7601             : 
    7602        1232 :     if (l_nBands >= 3 && !bForcePhotometric &&
    7603             : #ifdef HAVE_LIBJPEG
    7604        1194 :         !bCopyFromJPEG &&
    7605             : #endif
    7606        1188 :         poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_RedBand &&
    7607        4493 :         poSrcDS->GetRasterBand(2)->GetColorInterpretation() == GCI_GreenBand &&
    7608        1076 :         poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_BlueBand)
    7609             :     {
    7610        1070 :         papszCreateOptions =
    7611        1070 :             CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "RGB");
    7612             :     }
    7613             : 
    7614             :     /* -------------------------------------------------------------------- */
    7615             :     /*      Create the file.                                                */
    7616             :     /* -------------------------------------------------------------------- */
    7617        2185 :     VSILFILE *l_fpL = nullptr;
    7618        4370 :     CPLString l_osTmpFilename;
    7619             : 
    7620        2185 :     const int nXSize = poSrcDS->GetRasterXSize();
    7621        2185 :     const int nYSize = poSrcDS->GetRasterYSize();
    7622             : 
    7623             :     const int nColorTableMultiplier = std::max(
    7624        4370 :         1,
    7625        4370 :         std::min(257,
    7626        2185 :                  atoi(CSLFetchNameValueDef(
    7627             :                      papszOptions, "COLOR_TABLE_MULTIPLIER",
    7628        2185 :                      CPLSPrintf("%d", DEFAULT_COLOR_TABLE_MULTIPLIER_257)))));
    7629             : 
    7630        2185 :     bool bTileInterleaving = false;
    7631        2185 :     TIFF *l_hTIFF = CreateLL(pszFilename, nXSize, nYSize, l_nBands, eType,
    7632             :                              dfExtraSpaceForOverviews, nColorTableMultiplier,
    7633             :                              papszCreateOptions, &l_fpL, l_osTmpFilename,
    7634             :                              /* bCreateCopy = */ true, bTileInterleaving);
    7635        2185 :     const bool bStreaming = !l_osTmpFilename.empty();
    7636             : 
    7637        2185 :     CSLDestroy(papszCreateOptions);
    7638        2185 :     papszCreateOptions = nullptr;
    7639             : 
    7640        2185 :     if (l_hTIFF == nullptr)
    7641             :     {
    7642          18 :         if (bStreaming)
    7643           0 :             VSIUnlink(l_osTmpFilename);
    7644          18 :         return nullptr;
    7645             :     }
    7646             : 
    7647        2167 :     uint16_t l_nPlanarConfig = 0;
    7648        2167 :     TIFFGetField(l_hTIFF, TIFFTAG_PLANARCONFIG, &l_nPlanarConfig);
    7649             : 
    7650        2167 :     uint16_t l_nCompression = 0;
    7651             : 
    7652        2167 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_COMPRESSION, &(l_nCompression)))
    7653           0 :         l_nCompression = COMPRESSION_NONE;
    7654             : 
    7655             :     /* -------------------------------------------------------------------- */
    7656             :     /*      Set the alpha channel if we find one.                           */
    7657             :     /* -------------------------------------------------------------------- */
    7658        2167 :     uint16_t *extraSamples = nullptr;
    7659        2167 :     uint16_t nExtraSamples = 0;
    7660        2167 :     if (TIFFGetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples,
    7661        2432 :                      &extraSamples) &&
    7662         265 :         nExtraSamples > 0)
    7663             :     {
    7664             :         // We need to allocate a new array as (current) libtiff
    7665             :         // versions will not like that we reuse the array we got from
    7666             :         // TIFFGetField().
    7667             :         uint16_t *pasNewExtraSamples = static_cast<uint16_t *>(
    7668         265 :             CPLMalloc(nExtraSamples * sizeof(uint16_t)));
    7669         265 :         memcpy(pasNewExtraSamples, extraSamples,
    7670         265 :                nExtraSamples * sizeof(uint16_t));
    7671         265 :         const char *pszAlpha = CPLGetConfigOption(
    7672             :             "GTIFF_ALPHA", CSLFetchNameValue(papszOptions, "ALPHA"));
    7673             :         const uint16_t nAlpha =
    7674         265 :             GTiffGetAlphaValue(pszAlpha, DEFAULT_ALPHA_TYPE);
    7675         265 :         const int nBaseSamples = l_nBands - nExtraSamples;
    7676         895 :         for (int iExtraBand = nBaseSamples + 1; iExtraBand <= l_nBands;
    7677             :              iExtraBand++)
    7678             :         {
    7679         630 :             if (poSrcDS->GetRasterBand(iExtraBand)->GetColorInterpretation() ==
    7680             :                 GCI_AlphaBand)
    7681             :             {
    7682         145 :                 pasNewExtraSamples[iExtraBand - nBaseSamples - 1] = nAlpha;
    7683         145 :                 if (!pszAlpha)
    7684             :                 {
    7685             :                     // Use the ALPHA metadata item from the source band, when
    7686             :                     // present, if no explicit ALPHA creation option
    7687         286 :                     pasNewExtraSamples[iExtraBand - nBaseSamples - 1] =
    7688         143 :                         GTiffGetAlphaValue(
    7689         143 :                             poSrcDS->GetRasterBand(iExtraBand)
    7690             :                                 ->GetMetadataItem("ALPHA",
    7691         143 :                                                   GDAL_MDD_IMAGE_STRUCTURE),
    7692             :                             nAlpha);
    7693             :                 }
    7694             :             }
    7695             :         }
    7696         265 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples,
    7697             :                      pasNewExtraSamples);
    7698             : 
    7699         265 :         CPLFree(pasNewExtraSamples);
    7700             :     }
    7701             : 
    7702             :     /* -------------------------------------------------------------------- */
    7703             :     /*      If the output is jpeg compressed, and the input is RGB make     */
    7704             :     /*      sure we note that.                                              */
    7705             :     /* -------------------------------------------------------------------- */
    7706             : 
    7707        2167 :     if (l_nCompression == COMPRESSION_JPEG)
    7708             :     {
    7709         134 :         if (l_nBands >= 3 &&
    7710          58 :             (poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
    7711           0 :              GCI_YCbCr_YBand) &&
    7712           0 :             (poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
    7713         134 :              GCI_YCbCr_CbBand) &&
    7714           0 :             (poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
    7715             :              GCI_YCbCr_CrBand))
    7716             :         {
    7717             :             // Do nothing.
    7718             :         }
    7719             :         else
    7720             :         {
    7721             :             // Assume RGB if it is not explicitly YCbCr.
    7722          76 :             CPLDebug("GTiff", "Setting JPEGCOLORMODE_RGB");
    7723          76 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    7724             :         }
    7725             :     }
    7726             : 
    7727             :     /* -------------------------------------------------------------------- */
    7728             :     /*      Does the source image consist of one band, with a palette?      */
    7729             :     /*      If so, copy over.                                               */
    7730             :     /* -------------------------------------------------------------------- */
    7731        1318 :     if ((l_nBands == 1 || l_nBands == 2) &&
    7732        3485 :         poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7733             :         eType == GDT_UInt8)
    7734             :     {
    7735          21 :         unsigned short anTRed[256] = {0};
    7736          21 :         unsigned short anTGreen[256] = {0};
    7737          21 :         unsigned short anTBlue[256] = {0};
    7738          21 :         GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    7739             : 
    7740        5397 :         for (int iColor = 0; iColor < 256; ++iColor)
    7741             :         {
    7742        5376 :             if (iColor < poCT->GetColorEntryCount())
    7743             :             {
    7744        4241 :                 GDALColorEntry sRGB = {0, 0, 0, 0};
    7745             : 
    7746        4241 :                 poCT->GetColorEntryAsRGB(iColor, &sRGB);
    7747             : 
    7748        8482 :                 anTRed[iColor] = GTiffDataset::ClampCTEntry(
    7749        4241 :                     iColor, 1, sRGB.c1, nColorTableMultiplier);
    7750        8482 :                 anTGreen[iColor] = GTiffDataset::ClampCTEntry(
    7751        4241 :                     iColor, 2, sRGB.c2, nColorTableMultiplier);
    7752        4241 :                 anTBlue[iColor] = GTiffDataset::ClampCTEntry(
    7753        4241 :                     iColor, 3, sRGB.c3, nColorTableMultiplier);
    7754             :             }
    7755             :             else
    7756             :             {
    7757        1135 :                 anTRed[iColor] = 0;
    7758        1135 :                 anTGreen[iColor] = 0;
    7759        1135 :                 anTBlue[iColor] = 0;
    7760             :             }
    7761             :         }
    7762             : 
    7763          21 :         if (!bForcePhotometric)
    7764          21 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    7765          21 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, anTRed, anTGreen, anTBlue);
    7766             :     }
    7767        1317 :     else if ((l_nBands == 1 || l_nBands == 2) &&
    7768        3463 :              poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7769             :              eType == GDT_UInt16)
    7770             :     {
    7771             :         unsigned short *panTRed = static_cast<unsigned short *>(
    7772           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7773             :         unsigned short *panTGreen = static_cast<unsigned short *>(
    7774           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7775             :         unsigned short *panTBlue = static_cast<unsigned short *>(
    7776           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7777             : 
    7778           1 :         GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    7779             : 
    7780       65537 :         for (int iColor = 0; iColor < 65536; ++iColor)
    7781             :         {
    7782       65536 :             if (iColor < poCT->GetColorEntryCount())
    7783             :             {
    7784       65536 :                 GDALColorEntry sRGB = {0, 0, 0, 0};
    7785             : 
    7786       65536 :                 poCT->GetColorEntryAsRGB(iColor, &sRGB);
    7787             : 
    7788      131072 :                 panTRed[iColor] = GTiffDataset::ClampCTEntry(
    7789       65536 :                     iColor, 1, sRGB.c1, nColorTableMultiplier);
    7790      131072 :                 panTGreen[iColor] = GTiffDataset::ClampCTEntry(
    7791       65536 :                     iColor, 2, sRGB.c2, nColorTableMultiplier);
    7792       65536 :                 panTBlue[iColor] = GTiffDataset::ClampCTEntry(
    7793       65536 :                     iColor, 3, sRGB.c3, nColorTableMultiplier);
    7794             :             }
    7795             :             else
    7796             :             {
    7797           0 :                 panTRed[iColor] = 0;
    7798           0 :                 panTGreen[iColor] = 0;
    7799           0 :                 panTBlue[iColor] = 0;
    7800             :             }
    7801             :         }
    7802             : 
    7803           1 :         if (!bForcePhotometric)
    7804           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    7805           1 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue);
    7806             : 
    7807           1 :         CPLFree(panTRed);
    7808           1 :         CPLFree(panTGreen);
    7809           1 :         CPLFree(panTBlue);
    7810             :     }
    7811        2145 :     else if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
    7812           1 :         ReportError(
    7813             :             pszFilename, CE_Failure, CPLE_AppDefined,
    7814             :             "Unable to export color table to GeoTIFF file.  Color tables "
    7815             :             "can only be written to 1 band or 2 bands Byte or "
    7816             :             "UInt16 GeoTIFF files.");
    7817             : 
    7818        2167 :     if (l_nCompression == COMPRESSION_JPEG)
    7819             :     {
    7820          76 :         uint16_t l_nPhotometric = 0;
    7821          76 :         TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &l_nPhotometric);
    7822             :         // Check done in tif_jpeg.c later, but not with a very clear error
    7823             :         // message
    7824          76 :         if (l_nPhotometric == PHOTOMETRIC_PALETTE)
    7825             :         {
    7826           1 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    7827             :                         "JPEG compression not supported with paletted image");
    7828           1 :             XTIFFClose(l_hTIFF);
    7829           1 :             VSIUnlink(l_osTmpFilename);
    7830           1 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    7831           1 :             return nullptr;
    7832             :         }
    7833             :     }
    7834             : 
    7835        2253 :     if (l_nBands == 2 &&
    7836        2166 :         poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7837           0 :         (eType == GDT_UInt8 || eType == GDT_UInt16))
    7838             :     {
    7839           1 :         uint16_t v[1] = {EXTRASAMPLE_UNASSALPHA};
    7840             : 
    7841           1 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
    7842             :     }
    7843             : 
    7844        2166 :     const int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags();
    7845        2166 :     bool bCreateMask = false;
    7846        4332 :     CPLString osHiddenStructuralMD;
    7847             :     const char *pszInterleave =
    7848        2166 :         CSLFetchNameValueDef(papszOptions, GDALMD_INTERLEAVE, "PIXEL");
    7849        2394 :     if (bCopySrcOverviews &&
    7850         228 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "TILED", "NO")))
    7851             :     {
    7852         216 :         osHiddenStructuralMD += "LAYOUT=IFDS_BEFORE_DATA\n";
    7853         216 :         osHiddenStructuralMD += "BLOCK_ORDER=ROW_MAJOR\n";
    7854         216 :         osHiddenStructuralMD += "BLOCK_LEADER=SIZE_AS_UINT4\n";
    7855         216 :         osHiddenStructuralMD += "BLOCK_TRAILER=LAST_4_BYTES_REPEATED\n";
    7856         216 :         if (l_nBands > 1 && !EQUAL(pszInterleave, "PIXEL"))
    7857             :         {
    7858          22 :             osHiddenStructuralMD += "INTERLEAVE=";
    7859          22 :             osHiddenStructuralMD += CPLString(pszInterleave).toupper();
    7860          22 :             osHiddenStructuralMD += "\n";
    7861             :         }
    7862             :         osHiddenStructuralMD +=
    7863         216 :             "KNOWN_INCOMPATIBLE_EDITION=NO\n ";  // Final space intended, so
    7864             :                                                  // this can be replaced by YES
    7865             :     }
    7866        2166 :     if (!(nMaskFlags & (GMF_ALL_VALID | GMF_ALPHA | GMF_NODATA)) &&
    7867          42 :         (nMaskFlags & GMF_PER_DATASET) && !bStreaming)
    7868             :     {
    7869          38 :         bCreateMask = true;
    7870          38 :         if (GTiffDataset::MustCreateInternalMask() &&
    7871          38 :             !osHiddenStructuralMD.empty() && EQUAL(pszInterleave, "PIXEL"))
    7872             :         {
    7873          21 :             osHiddenStructuralMD += "MASK_INTERLEAVED_WITH_IMAGERY=YES\n";
    7874             :         }
    7875             :     }
    7876        2382 :     if (!osHiddenStructuralMD.empty() &&
    7877         216 :         CPLTestBool(CPLGetConfigOption("GTIFF_WRITE_COG_GHOST_AREA", "YES")))
    7878             :     {
    7879         215 :         const int nHiddenMDSize = static_cast<int>(osHiddenStructuralMD.size());
    7880             :         osHiddenStructuralMD =
    7881         215 :             CPLOPrintf("GDAL_STRUCTURAL_METADATA_SIZE=%06d bytes\n",
    7882         430 :                        nHiddenMDSize) +
    7883         215 :             osHiddenStructuralMD;
    7884         215 :         VSI_TIFFWrite(l_hTIFF, osHiddenStructuralMD.c_str(),
    7885             :                       osHiddenStructuralMD.size());
    7886             :     }
    7887             : 
    7888             :     // FIXME? libtiff writes extended tags in the order they are specified
    7889             :     // and not in increasing order.
    7890             : 
    7891             :     /* -------------------------------------------------------------------- */
    7892             :     /*      Transfer some TIFF specific metadata, if available.             */
    7893             :     /*      The return value will tell us if we need to try again later with*/
    7894             :     /*      PAM because the profile doesn't allow to write some metadata    */
    7895             :     /*      as TIFF tag                                                     */
    7896             :     /* -------------------------------------------------------------------- */
    7897        2166 :     const bool bHasWrittenMDInGeotiffTAG = GTiffDataset::WriteMetadata(
    7898             :         poSrcDS, l_hTIFF, false, eProfile, pszFilename, papszOptions);
    7899             : 
    7900             :     /* -------------------------------------------------------------------- */
    7901             :     /*      Write NoData value, if exist.                                   */
    7902             :     /* -------------------------------------------------------------------- */
    7903        2166 :     if (eProfile == GTiffProfile::GDALGEOTIFF)
    7904             :     {
    7905        2145 :         int bSuccess = FALSE;
    7906        2145 :         GDALRasterBand *poFirstBand = poSrcDS->GetRasterBand(1);
    7907        2145 :         if (poFirstBand->GetRasterDataType() == GDT_Int64)
    7908             :         {
    7909           4 :             const auto nNoData = poFirstBand->GetNoDataValueAsInt64(&bSuccess);
    7910           4 :             if (bSuccess)
    7911           1 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, nNoData);
    7912             :         }
    7913        2141 :         else if (poFirstBand->GetRasterDataType() == GDT_UInt64)
    7914             :         {
    7915           4 :             const auto nNoData = poFirstBand->GetNoDataValueAsUInt64(&bSuccess);
    7916           4 :             if (bSuccess)
    7917           1 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, nNoData);
    7918             :         }
    7919             :         else
    7920             :         {
    7921        2137 :             const auto dfNoData = poFirstBand->GetNoDataValue(&bSuccess);
    7922        2137 :             if (bSuccess)
    7923         150 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, dfNoData);
    7924             :         }
    7925             :     }
    7926             : 
    7927             :     /* -------------------------------------------------------------------- */
    7928             :     /*      Are we addressing PixelIsPoint mode?                            */
    7929             :     /* -------------------------------------------------------------------- */
    7930        2166 :     bool bPixelIsPoint = false;
    7931        2166 :     bool bPointGeoIgnore = false;
    7932             : 
    7933        3644 :     if (poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT) &&
    7934        1478 :         EQUAL(poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT), GDALMD_AOP_POINT))
    7935             :     {
    7936          10 :         bPixelIsPoint = true;
    7937             :         bPointGeoIgnore =
    7938          10 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    7939             :     }
    7940             : 
    7941             :     /* -------------------------------------------------------------------- */
    7942             :     /*      Write affine transform if it is meaningful.                     */
    7943             :     /* -------------------------------------------------------------------- */
    7944        2166 :     const OGRSpatialReference *l_poSRS = nullptr;
    7945        2166 :     GDALGeoTransform l_gt;
    7946        2166 :     if (poSrcDS->GetGeoTransform(l_gt) == CE_None)
    7947             :     {
    7948        1727 :         if (bGeoTIFF)
    7949             :         {
    7950        1722 :             l_poSRS = poSrcDS->GetSpatialRef();
    7951             : 
    7952        1722 :             if (l_gt.xrot == 0.0 && l_gt.yrot == 0.0 && l_gt.yscale < 0.0)
    7953             :             {
    7954        1714 :                 double dfOffset = 0.0;
    7955             :                 {
    7956             :                     // In the case the SRS has a vertical component and we have
    7957             :                     // a single band, encode its scale/offset in the GeoTIFF
    7958             :                     // tags
    7959        1714 :                     int bHasScale = FALSE;
    7960             :                     double dfScale =
    7961        1714 :                         poSrcDS->GetRasterBand(1)->GetScale(&bHasScale);
    7962        1714 :                     int bHasOffset = FALSE;
    7963             :                     dfOffset =
    7964        1714 :                         poSrcDS->GetRasterBand(1)->GetOffset(&bHasOffset);
    7965             :                     const bool bApplyScaleOffset =
    7966        1718 :                         l_poSRS && l_poSRS->IsVertical() &&
    7967           4 :                         poSrcDS->GetRasterCount() == 1;
    7968        1714 :                     if (bApplyScaleOffset && !bHasScale)
    7969           0 :                         dfScale = 1.0;
    7970        1714 :                     if (!bApplyScaleOffset || !bHasOffset)
    7971        1710 :                         dfOffset = 0.0;
    7972             :                     const double adfPixelScale[3] = {
    7973        1714 :                         l_gt.xscale, fabs(l_gt.yscale),
    7974        1714 :                         bApplyScaleOffset ? dfScale : 0.0};
    7975             : 
    7976        1714 :                     TIFFSetField(l_hTIFF, TIFFTAG_GEOPIXELSCALE, 3,
    7977             :                                  adfPixelScale);
    7978             :                 }
    7979             : 
    7980        1714 :                 double adfTiePoints[6] = {0.0,        0.0,        0.0,
    7981        1714 :                                           l_gt.xorig, l_gt.yorig, dfOffset};
    7982             : 
    7983        1714 :                 if (bPixelIsPoint && !bPointGeoIgnore)
    7984             :                 {
    7985           6 :                     adfTiePoints[3] += l_gt.xscale * 0.5 + l_gt.xrot * 0.5;
    7986           6 :                     adfTiePoints[4] += l_gt.yrot * 0.5 + l_gt.yscale * 0.5;
    7987             :                 }
    7988             : 
    7989        1714 :                 TIFFSetField(l_hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
    7990             :             }
    7991             :             else
    7992             :             {
    7993           8 :                 double adfMatrix[16] = {0.0};
    7994             : 
    7995           8 :                 adfMatrix[0] = l_gt.xscale;
    7996           8 :                 adfMatrix[1] = l_gt.xrot;
    7997           8 :                 adfMatrix[3] = l_gt.xorig;
    7998           8 :                 adfMatrix[4] = l_gt.yrot;
    7999           8 :                 adfMatrix[5] = l_gt.yscale;
    8000           8 :                 adfMatrix[7] = l_gt.yorig;
    8001           8 :                 adfMatrix[15] = 1.0;
    8002             : 
    8003           8 :                 if (bPixelIsPoint && !bPointGeoIgnore)
    8004             :                 {
    8005           0 :                     adfMatrix[3] += l_gt.xscale * 0.5 + l_gt.xrot * 0.5;
    8006           0 :                     adfMatrix[7] += l_gt.yrot * 0.5 + l_gt.yscale * 0.5;
    8007             :                 }
    8008             : 
    8009           8 :                 TIFFSetField(l_hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
    8010             :             }
    8011             :         }
    8012             : 
    8013             :         /* --------------------------------------------------------------------
    8014             :          */
    8015             :         /*      Do we need a TFW file? */
    8016             :         /* --------------------------------------------------------------------
    8017             :          */
    8018        1727 :         if (CPLFetchBool(papszOptions, "TFW", false))
    8019           2 :             GDALWriteWorldFile(pszFilename, "tfw", l_gt.data());
    8020        1725 :         else if (CPLFetchBool(papszOptions, "WORLDFILE", false))
    8021           1 :             GDALWriteWorldFile(pszFilename, "wld", l_gt.data());
    8022             :     }
    8023             : 
    8024             :     /* -------------------------------------------------------------------- */
    8025             :     /*      Otherwise write tiepoints if they are available.                */
    8026             :     /* -------------------------------------------------------------------- */
    8027         439 :     else if (poSrcDS->GetGCPCount() > 0 && bGeoTIFF)
    8028             :     {
    8029          11 :         const GDAL_GCP *pasGCPs = poSrcDS->GetGCPs();
    8030             :         double *padfTiePoints = static_cast<double *>(
    8031          11 :             CPLMalloc(6 * sizeof(double) * poSrcDS->GetGCPCount()));
    8032             : 
    8033          55 :         for (int iGCP = 0; iGCP < poSrcDS->GetGCPCount(); ++iGCP)
    8034             :         {
    8035             : 
    8036          44 :             padfTiePoints[iGCP * 6 + 0] = pasGCPs[iGCP].dfGCPPixel;
    8037          44 :             padfTiePoints[iGCP * 6 + 1] = pasGCPs[iGCP].dfGCPLine;
    8038          44 :             padfTiePoints[iGCP * 6 + 2] = 0;
    8039          44 :             padfTiePoints[iGCP * 6 + 3] = pasGCPs[iGCP].dfGCPX;
    8040          44 :             padfTiePoints[iGCP * 6 + 4] = pasGCPs[iGCP].dfGCPY;
    8041          44 :             padfTiePoints[iGCP * 6 + 5] = pasGCPs[iGCP].dfGCPZ;
    8042             : 
    8043          44 :             if (bPixelIsPoint && !bPointGeoIgnore)
    8044             :             {
    8045           4 :                 padfTiePoints[iGCP * 6 + 0] -= 0.5;
    8046           4 :                 padfTiePoints[iGCP * 6 + 1] -= 0.5;
    8047             :             }
    8048             :         }
    8049             : 
    8050          11 :         TIFFSetField(l_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * poSrcDS->GetGCPCount(),
    8051             :                      padfTiePoints);
    8052          11 :         CPLFree(padfTiePoints);
    8053             : 
    8054          11 :         l_poSRS = poSrcDS->GetGCPSpatialRef();
    8055             : 
    8056          22 :         if (CPLFetchBool(papszOptions, "TFW", false) ||
    8057          11 :             CPLFetchBool(papszOptions, "WORLDFILE", false))
    8058             :         {
    8059           0 :             ReportError(
    8060             :                 pszFilename, CE_Warning, CPLE_AppDefined,
    8061             :                 "TFW=ON or WORLDFILE=ON creation options are ignored when "
    8062             :                 "GCPs are available");
    8063             :         }
    8064             :     }
    8065             :     else
    8066             :     {
    8067         428 :         l_poSRS = poSrcDS->GetSpatialRef();
    8068             :     }
    8069             : 
    8070             :     /* -------------------------------------------------------------------- */
    8071             :     /*      Copy xml:XMP data                                               */
    8072             :     /* -------------------------------------------------------------------- */
    8073        2166 :     CSLConstList papszXMP = poSrcDS->GetMetadata("xml:XMP");
    8074        2166 :     if (papszXMP != nullptr && *papszXMP != nullptr)
    8075             :     {
    8076           9 :         int nTagSize = static_cast<int>(strlen(*papszXMP));
    8077           9 :         TIFFSetField(l_hTIFF, TIFFTAG_XMLPACKET, nTagSize, *papszXMP);
    8078             :     }
    8079             : 
    8080             :     /* -------------------------------------------------------------------- */
    8081             :     /*      Write the projection information, if possible.                  */
    8082             :     /* -------------------------------------------------------------------- */
    8083        2166 :     const bool bHasProjection = l_poSRS != nullptr;
    8084        2166 :     bool bExportSRSToPAM = false;
    8085        2166 :     if ((bHasProjection || bPixelIsPoint) && bGeoTIFF)
    8086             :     {
    8087        1700 :         GTIF *psGTIF = GTiffDataset::GTIFNew(l_hTIFF);
    8088             : 
    8089        1700 :         if (bHasProjection)
    8090             :         {
    8091        1700 :             const auto eGeoTIFFKeysFlavor = GetGTIFFKeysFlavor(papszOptions);
    8092        1700 :             if (IsSRSCompatibleOfGeoTIFF(l_poSRS, eGeoTIFFKeysFlavor))
    8093             :             {
    8094        1700 :                 GTIFSetFromOGISDefnEx(
    8095             :                     psGTIF,
    8096             :                     OGRSpatialReference::ToHandle(
    8097             :                         const_cast<OGRSpatialReference *>(l_poSRS)),
    8098             :                     eGeoTIFFKeysFlavor, GetGeoTIFFVersion(papszOptions));
    8099             :             }
    8100             :             else
    8101             :             {
    8102           0 :                 bExportSRSToPAM = true;
    8103             :             }
    8104             :         }
    8105             : 
    8106        1700 :         if (bPixelIsPoint)
    8107             :         {
    8108          10 :             GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
    8109             :                        RasterPixelIsPoint);
    8110             :         }
    8111             : 
    8112        1700 :         GTIFWriteKeys(psGTIF);
    8113        1700 :         GTIFFree(psGTIF);
    8114             :     }
    8115             : 
    8116        2166 :     bool l_bDontReloadFirstBlock = false;
    8117             : 
    8118             : #ifdef HAVE_LIBJPEG
    8119        2166 :     if (bCopyFromJPEG)
    8120             :     {
    8121          12 :         GTIFF_CopyFromJPEG_WriteAdditionalTags(l_hTIFF, poSrcDS);
    8122             :     }
    8123             : #endif
    8124             : 
    8125             :     /* -------------------------------------------------------------------- */
    8126             :     /*      Cleanup                                                         */
    8127             :     /* -------------------------------------------------------------------- */
    8128        2166 :     if (bCopySrcOverviews)
    8129             :     {
    8130         228 :         TIFFDeferStrileArrayWriting(l_hTIFF);
    8131             :     }
    8132        2166 :     TIFFWriteCheck(l_hTIFF, TIFFIsTiled(l_hTIFF), "GTiffCreateCopy()");
    8133        2166 :     TIFFWriteDirectory(l_hTIFF);
    8134        2166 :     if (bStreaming)
    8135             :     {
    8136             :         // We need to write twice the directory to be sure that custom
    8137             :         // TIFF tags are correctly sorted and that padding bytes have been
    8138             :         // added.
    8139           5 :         TIFFSetDirectory(l_hTIFF, 0);
    8140           5 :         TIFFWriteDirectory(l_hTIFF);
    8141             : 
    8142           5 :         if (VSIFSeekL(l_fpL, 0, SEEK_END) != 0)
    8143           0 :             ReportError(pszFilename, CE_Failure, CPLE_FileIO, "Cannot seek");
    8144           5 :         const int nSize = static_cast<int>(VSIFTellL(l_fpL));
    8145             : 
    8146           5 :         vsi_l_offset nDataLength = 0;
    8147           5 :         VSIGetMemFileBuffer(l_osTmpFilename, &nDataLength, FALSE);
    8148           5 :         TIFFSetDirectory(l_hTIFF, 0);
    8149           5 :         GTiffFillStreamableOffsetAndCount(l_hTIFF, nSize);
    8150           5 :         TIFFWriteDirectory(l_hTIFF);
    8151             :     }
    8152        2166 :     const auto nDirCount = TIFFNumberOfDirectories(l_hTIFF);
    8153        2166 :     if (nDirCount >= 1)
    8154             :     {
    8155        2159 :         TIFFSetDirectory(l_hTIFF, static_cast<tdir_t>(nDirCount - 1));
    8156             :     }
    8157        2166 :     const toff_t l_nDirOffset = TIFFCurrentDirOffset(l_hTIFF);
    8158        2166 :     TIFFFlush(l_hTIFF);
    8159        2166 :     XTIFFClose(l_hTIFF);
    8160             : 
    8161        2166 :     VSIFSeekL(l_fpL, 0, SEEK_SET);
    8162             : 
    8163             :     // fpStreaming will assigned to the instance and not closed here.
    8164        2166 :     VSILFILE *fpStreaming = nullptr;
    8165        2166 :     if (bStreaming)
    8166             :     {
    8167           5 :         vsi_l_offset nDataLength = 0;
    8168             :         void *pabyBuffer =
    8169           5 :             VSIGetMemFileBuffer(l_osTmpFilename, &nDataLength, FALSE);
    8170           5 :         fpStreaming = VSIFOpenL(pszFilename, "wb");
    8171           5 :         if (fpStreaming == nullptr)
    8172             :         {
    8173           1 :             VSIUnlink(l_osTmpFilename);
    8174           1 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    8175           1 :             return nullptr;
    8176             :         }
    8177           4 :         if (static_cast<vsi_l_offset>(VSIFWriteL(pabyBuffer, 1,
    8178             :                                                  static_cast<int>(nDataLength),
    8179           4 :                                                  fpStreaming)) != nDataLength)
    8180             :         {
    8181           0 :             ReportError(pszFilename, CE_Failure, CPLE_FileIO,
    8182             :                         "Could not write %d bytes",
    8183             :                         static_cast<int>(nDataLength));
    8184           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fpStreaming));
    8185           0 :             VSIUnlink(l_osTmpFilename);
    8186           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    8187           0 :             return nullptr;
    8188             :         }
    8189             :     }
    8190             : 
    8191             :     /* -------------------------------------------------------------------- */
    8192             :     /*      Re-open as a dataset and copy over missing metadata using       */
    8193             :     /*      PAM facilities.                                                 */
    8194             :     /* -------------------------------------------------------------------- */
    8195        2165 :     l_hTIFF = VSI_TIFFOpen(bStreaming ? l_osTmpFilename.c_str() : pszFilename,
    8196             :                            "r+", l_fpL);
    8197        2165 :     if (l_hTIFF == nullptr)
    8198             :     {
    8199          11 :         if (bStreaming)
    8200           0 :             VSIUnlink(l_osTmpFilename);
    8201          11 :         l_fpL->CancelCreation();
    8202          11 :         CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    8203          11 :         return nullptr;
    8204             :     }
    8205             : 
    8206             :     /* -------------------------------------------------------------------- */
    8207             :     /*      Create a corresponding GDALDataset.                             */
    8208             :     /* -------------------------------------------------------------------- */
    8209        4308 :     auto poDS = std::make_unique<GTiffDataset>();
    8210             :     const bool bSuppressASAP =
    8211        2154 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "@SUPPRESS_ASAP", "NO"));
    8212        2154 :     if (bSuppressASAP)
    8213           4 :         poDS->MarkSuppressOnClose();
    8214        2154 :     poDS->SetDescription(pszFilename);
    8215        2154 :     poDS->eAccess = GA_Update;
    8216        2154 :     poDS->m_osFilename = pszFilename;
    8217        2154 :     poDS->m_fpL = l_fpL;
    8218        2154 :     poDS->m_bIMDRPCMetadataLoaded = true;
    8219        2154 :     poDS->m_nColorTableMultiplier = nColorTableMultiplier;
    8220        2154 :     poDS->m_bTileInterleave = bTileInterleaving;
    8221             : 
    8222        2154 :     if (bTileInterleaving)
    8223             :     {
    8224           7 :         poDS->m_oGTiffMDMD.SetMetadataItem(GDALMD_INTERLEAVE, "TILE",
    8225             :                                            GDAL_MDD_IMAGE_STRUCTURE);
    8226             :     }
    8227             : 
    8228        2154 :     const bool bAppend = CPLFetchBool(papszOptions, "APPEND_SUBDATASET", false);
    8229        4307 :     if (poDS->OpenOffset(l_hTIFF,
    8230        2153 :                          bAppend ? l_nDirOffset : TIFFCurrentDirOffset(l_hTIFF),
    8231             :                          GA_Update,
    8232             :                          false,  // bAllowRGBAInterface
    8233             :                          true    // bReadGeoTransform
    8234        2154 :                          ) != CE_None)
    8235             :     {
    8236           0 :         l_fpL->CancelCreation();
    8237           0 :         poDS.reset();
    8238           0 :         if (bStreaming)
    8239           0 :             VSIUnlink(l_osTmpFilename);
    8240           0 :         return nullptr;
    8241             :     }
    8242             : 
    8243             :     // Legacy... Patch back GDT_Int8 type to GDT_UInt8 if the user used
    8244             :     // PIXELTYPE=SIGNEDBYTE
    8245        2154 :     const char *pszPixelType = CSLFetchNameValue(papszOptions, "PIXELTYPE");
    8246        2154 :     if (pszPixelType == nullptr)
    8247        2149 :         pszPixelType = "";
    8248        2154 :     if (eType == GDT_UInt8 && EQUAL(pszPixelType, "SIGNEDBYTE"))
    8249             :     {
    8250          10 :         for (int i = 0; i < poDS->nBands; ++i)
    8251             :         {
    8252           5 :             auto poBand = static_cast<GTiffRasterBand *>(poDS->papoBands[i]);
    8253           5 :             poBand->eDataType = GDT_UInt8;
    8254           5 :             poBand->EnablePixelTypeSignedByteWarning(false);
    8255           5 :             poBand->SetMetadataItem("PIXELTYPE", "SIGNEDBYTE",
    8256             :                                     GDAL_MDD_IMAGE_STRUCTURE);
    8257           5 :             poBand->EnablePixelTypeSignedByteWarning(true);
    8258             :         }
    8259             :     }
    8260             : 
    8261        2154 :     poDS->oOvManager.Initialize(poDS.get(), pszFilename);
    8262             : 
    8263        2154 :     if (bStreaming)
    8264             :     {
    8265           4 :         VSIUnlink(l_osTmpFilename);
    8266           4 :         poDS->m_fpToWrite = fpStreaming;
    8267             :     }
    8268        2154 :     poDS->m_eProfile = eProfile;
    8269             : 
    8270        2154 :     int nCloneInfoFlags = GCIF_PAM_DEFAULT & ~GCIF_MASK;
    8271             : 
    8272             :     // If we explicitly asked not to tag the alpha band as such, do not
    8273             :     // reintroduce this alpha color interpretation in PAM.
    8274        2154 :     if (poSrcDS->GetRasterBand(l_nBands)->GetColorInterpretation() ==
    8275        2282 :             GCI_AlphaBand &&
    8276         128 :         GTiffGetAlphaValue(
    8277             :             CPLGetConfigOption("GTIFF_ALPHA",
    8278             :                                CSLFetchNameValue(papszOptions, "ALPHA")),
    8279             :             DEFAULT_ALPHA_TYPE) == EXTRASAMPLE_UNSPECIFIED)
    8280             :     {
    8281           1 :         nCloneInfoFlags &= ~GCIF_COLORINTERP;
    8282             :     }
    8283             :     // Ignore source band color interpretation if requesting PHOTOMETRIC=RGB
    8284        3382 :     else if (l_nBands >= 3 &&
    8285        1229 :              EQUAL(CSLFetchNameValueDef(papszOptions, "PHOTOMETRIC", ""),
    8286             :                    "RGB"))
    8287             :     {
    8288          28 :         for (int i = 1; i <= 3; i++)
    8289             :         {
    8290          21 :             poDS->GetRasterBand(i)->SetColorInterpretation(
    8291          21 :                 static_cast<GDALColorInterp>(GCI_RedBand + (i - 1)));
    8292             :         }
    8293           7 :         nCloneInfoFlags &= ~GCIF_COLORINTERP;
    8294           9 :         if (!(l_nBands == 4 &&
    8295           2 :               CSLFetchNameValue(papszOptions, "ALPHA") != nullptr))
    8296             :         {
    8297          15 :             for (int i = 4; i <= l_nBands; i++)
    8298             :             {
    8299          18 :                 poDS->GetRasterBand(i)->SetColorInterpretation(
    8300           9 :                     poSrcDS->GetRasterBand(i)->GetColorInterpretation());
    8301             :             }
    8302             :         }
    8303             :     }
    8304             : 
    8305             :     CPLString osOldGTIFF_REPORT_COMPD_CSVal(
    8306        4308 :         CPLGetConfigOption("GTIFF_REPORT_COMPD_CS", ""));
    8307        2154 :     CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS", "YES");
    8308        2154 :     poDS->CloneInfo(poSrcDS, nCloneInfoFlags);
    8309        2154 :     CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS",
    8310        2154 :                                   osOldGTIFF_REPORT_COMPD_CSVal.empty()
    8311             :                                       ? nullptr
    8312           0 :                                       : osOldGTIFF_REPORT_COMPD_CSVal.c_str());
    8313             : 
    8314        2171 :     if ((!bGeoTIFF || bExportSRSToPAM) &&
    8315          17 :         (poDS->GetPamFlags() & GPF_DISABLED) == 0)
    8316             :     {
    8317             :         // Copy georeferencing info to PAM if the profile is not GeoTIFF
    8318          16 :         poDS->GDALPamDataset::SetSpatialRef(poDS->GetSpatialRef());
    8319          16 :         GDALGeoTransform gt;
    8320          16 :         if (poDS->GetGeoTransform(gt) == CE_None)
    8321             :         {
    8322           5 :             poDS->GDALPamDataset::SetGeoTransform(gt);
    8323             :         }
    8324          16 :         poDS->GDALPamDataset::SetGCPs(poDS->GetGCPCount(), poDS->GetGCPs(),
    8325             :                                       poDS->GetGCPSpatialRef());
    8326             :     }
    8327             : 
    8328        2154 :     poDS->m_papszCreationOptions = CSLDuplicate(papszOptions);
    8329        2154 :     poDS->m_bDontReloadFirstBlock = l_bDontReloadFirstBlock;
    8330             : 
    8331             :     /* -------------------------------------------------------------------- */
    8332             :     /*      CloneInfo() does not merge metadata, it just replaces it        */
    8333             :     /*      totally.  So we have to merge it.                               */
    8334             :     /* -------------------------------------------------------------------- */
    8335             : 
    8336        2154 :     CSLConstList papszSRC_MD = poSrcDS->GetMetadata();
    8337        2154 :     char **papszDST_MD = CSLDuplicate(poDS->GetMetadata());
    8338             : 
    8339        2154 :     papszDST_MD = CSLMerge(papszDST_MD, papszSRC_MD);
    8340             : 
    8341        2154 :     poDS->SetMetadata(papszDST_MD);
    8342        2154 :     CSLDestroy(papszDST_MD);
    8343             : 
    8344             :     // Depending on the PHOTOMETRIC tag, the TIFF file may not have the same
    8345             :     // band count as the source. Will fail later in GDALDatasetCopyWholeRaster
    8346             :     // anyway.
    8347        7237 :     for (int nBand = 1;
    8348        7237 :          nBand <= std::min(poDS->GetRasterCount(), poSrcDS->GetRasterCount());
    8349             :          ++nBand)
    8350             :     {
    8351        5083 :         GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(nBand);
    8352        5083 :         GDALRasterBand *poDstBand = poDS->GetRasterBand(nBand);
    8353        5083 :         papszSRC_MD = poSrcBand->GetMetadata();
    8354        5083 :         papszDST_MD = CSLDuplicate(poDstBand->GetMetadata());
    8355             : 
    8356        5083 :         papszDST_MD = CSLMerge(papszDST_MD, papszSRC_MD);
    8357             : 
    8358        5083 :         poDstBand->SetMetadata(papszDST_MD);
    8359        5083 :         CSLDestroy(papszDST_MD);
    8360             : 
    8361        5083 :         char **papszCatNames = poSrcBand->GetCategoryNames();
    8362        5083 :         if (nullptr != papszCatNames)
    8363           0 :             poDstBand->SetCategoryNames(papszCatNames);
    8364             :     }
    8365             : 
    8366        2154 :     l_hTIFF = static_cast<TIFF *>(poDS->GetInternalHandle("TIFF_HANDLE"));
    8367             : 
    8368             :     /* -------------------------------------------------------------------- */
    8369             :     /*      Handle forcing xml:ESRI data to be written to PAM.              */
    8370             :     /* -------------------------------------------------------------------- */
    8371        2154 :     if (CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
    8372             :     {
    8373           1 :         CSLConstList papszESRIMD = poSrcDS->GetMetadata("xml:ESRI");
    8374           1 :         if (papszESRIMD)
    8375             :         {
    8376           1 :             poDS->SetMetadata(papszESRIMD, "xml:ESRI");
    8377             :         }
    8378             :     }
    8379             : 
    8380             :     /* -------------------------------------------------------------------- */
    8381             :     /*      Second chance: now that we have a PAM dataset, it is possible   */
    8382             :     /*      to write metadata that we could not write as a TIFF tag.        */
    8383             :     /* -------------------------------------------------------------------- */
    8384        2154 :     if (!bHasWrittenMDInGeotiffTAG && !bStreaming)
    8385             :     {
    8386           6 :         GTiffDataset::WriteMetadata(
    8387           6 :             poDS.get(), l_hTIFF, true, eProfile, pszFilename, papszOptions,
    8388             :             true /* don't write RPC and IMD file again */);
    8389             :     }
    8390             : 
    8391        2154 :     if (!bStreaming)
    8392        2150 :         GTiffDataset::WriteRPC(poDS.get(), l_hTIFF, true, eProfile, pszFilename,
    8393             :                                papszOptions,
    8394             :                                true /* write only in PAM AND if needed */);
    8395             : 
    8396        2154 :     poDS->m_bWriteCOGLayout = bCopySrcOverviews;
    8397             : 
    8398             :     // To avoid unnecessary directory rewriting.
    8399        2154 :     poDS->m_bMetadataChanged = false;
    8400        2154 :     poDS->m_bGeoTIFFInfoChanged = false;
    8401        2154 :     poDS->m_bNoDataChanged = false;
    8402        2154 :     poDS->m_bForceUnsetGTOrGCPs = false;
    8403        2154 :     poDS->m_bForceUnsetProjection = false;
    8404        2154 :     poDS->m_bStreamingOut = bStreaming;
    8405             : 
    8406             :     // Don't try to load external metadata files (#6597).
    8407        2154 :     poDS->m_bIMDRPCMetadataLoaded = true;
    8408             : 
    8409             :     // We must re-set the compression level at this point, since it has been
    8410             :     // lost a few lines above when closing the newly create TIFF file The
    8411             :     // TIFFTAG_ZIPQUALITY & TIFFTAG_JPEGQUALITY are not store in the TIFF file.
    8412             :     // They are just TIFF session parameters.
    8413             : 
    8414        2154 :     poDS->m_nZLevel = GTiffGetZLevel(papszOptions);
    8415        2154 :     poDS->m_nLZMAPreset = GTiffGetLZMAPreset(papszOptions);
    8416        2154 :     poDS->m_nZSTDLevel = GTiffGetZSTDPreset(papszOptions);
    8417        2154 :     poDS->m_nWebPLevel = GTiffGetWebPLevel(papszOptions);
    8418        2154 :     poDS->m_bWebPLossless = GTiffGetWebPLossless(papszOptions);
    8419        2157 :     if (poDS->m_nWebPLevel != 100 && poDS->m_bWebPLossless &&
    8420           3 :         CSLFetchNameValue(papszOptions, "WEBP_LEVEL"))
    8421             :     {
    8422           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    8423             :                  "WEBP_LEVEL is specified, but WEBP_LOSSLESS=YES. "
    8424             :                  "WEBP_LEVEL will be ignored.");
    8425             :     }
    8426        2154 :     poDS->m_nJpegQuality = GTiffGetJpegQuality(papszOptions);
    8427        2154 :     poDS->m_nJpegTablesMode = GTiffGetJpegTablesMode(papszOptions);
    8428        2154 :     poDS->GetDiscardLsbOption(papszOptions);
    8429        2154 :     poDS->m_dfMaxZError = GTiffGetLERCMaxZError(papszOptions);
    8430        2154 :     poDS->m_dfMaxZErrorOverview = GTiffGetLERCMaxZErrorOverview(papszOptions);
    8431             : #if HAVE_JXL
    8432        2154 :     poDS->m_bJXLLossless = GTiffGetJXLLossless(papszOptions);
    8433        2154 :     poDS->m_nJXLEffort = GTiffGetJXLEffort(papszOptions);
    8434        2154 :     poDS->m_fJXLDistance = GTiffGetJXLDistance(papszOptions);
    8435        2154 :     poDS->m_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszOptions);
    8436             : #endif
    8437        2154 :     poDS->InitCreationOrOpenOptions(true, papszOptions);
    8438             : 
    8439        2154 :     if (l_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    8440        2126 :         l_nCompression == COMPRESSION_LERC)
    8441             :     {
    8442          99 :         GTiffSetDeflateSubCodec(l_hTIFF);
    8443             : 
    8444          99 :         if (poDS->m_nZLevel != -1)
    8445             :         {
    8446          12 :             TIFFSetField(l_hTIFF, TIFFTAG_ZIPQUALITY, poDS->m_nZLevel);
    8447             :         }
    8448             :     }
    8449        2154 :     if (l_nCompression == COMPRESSION_JPEG)
    8450             :     {
    8451          75 :         if (poDS->m_nJpegQuality != -1)
    8452             :         {
    8453           9 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGQUALITY, poDS->m_nJpegQuality);
    8454             :         }
    8455          75 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGTABLESMODE, poDS->m_nJpegTablesMode);
    8456             :     }
    8457        2154 :     if (l_nCompression == COMPRESSION_LZMA)
    8458             :     {
    8459           7 :         if (poDS->m_nLZMAPreset != -1)
    8460             :         {
    8461           6 :             TIFFSetField(l_hTIFF, TIFFTAG_LZMAPRESET, poDS->m_nLZMAPreset);
    8462             :         }
    8463             :     }
    8464        2154 :     if (l_nCompression == COMPRESSION_ZSTD ||
    8465        2134 :         l_nCompression == COMPRESSION_LERC)
    8466             :     {
    8467          91 :         if (poDS->m_nZSTDLevel != -1)
    8468             :         {
    8469           8 :             TIFFSetField(l_hTIFF, TIFFTAG_ZSTD_LEVEL, poDS->m_nZSTDLevel);
    8470             :         }
    8471             :     }
    8472        2154 :     if (l_nCompression == COMPRESSION_LERC)
    8473             :     {
    8474          71 :         TIFFSetField(l_hTIFF, TIFFTAG_LERC_MAXZERROR, poDS->m_dfMaxZError);
    8475             :     }
    8476             : #if HAVE_JXL
    8477        2154 :     if (l_nCompression == COMPRESSION_JXL ||
    8478        2154 :         l_nCompression == COMPRESSION_JXL_DNG_1_7)
    8479             :     {
    8480          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_LOSSYNESS,
    8481          91 :                      poDS->m_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
    8482          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_EFFORT, poDS->m_nJXLEffort);
    8483          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_DISTANCE,
    8484          91 :                      static_cast<double>(poDS->m_fJXLDistance));
    8485          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE,
    8486          91 :                      static_cast<double>(poDS->m_fJXLAlphaDistance));
    8487             :     }
    8488             : #endif
    8489        2154 :     if (l_nCompression == COMPRESSION_WEBP)
    8490             :     {
    8491          14 :         if (poDS->m_nWebPLevel != -1)
    8492             :         {
    8493          14 :             TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LEVEL, poDS->m_nWebPLevel);
    8494             :         }
    8495             : 
    8496          14 :         if (poDS->m_bWebPLossless)
    8497             :         {
    8498           5 :             TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LOSSLESS, poDS->m_bWebPLossless);
    8499             :         }
    8500             :     }
    8501             : 
    8502             :     /* -------------------------------------------------------------------- */
    8503             :     /*      Do we want to ensure all blocks get written out on close to     */
    8504             :     /*      avoid sparse files?                                             */
    8505             :     /* -------------------------------------------------------------------- */
    8506        2154 :     if (!CPLFetchBool(papszOptions, "SPARSE_OK", false))
    8507        2126 :         poDS->m_bFillEmptyTilesAtClosing = true;
    8508             : 
    8509        2154 :     poDS->m_bWriteEmptyTiles =
    8510        4084 :         (bCopySrcOverviews && poDS->m_bFillEmptyTilesAtClosing) || bStreaming ||
    8511        1930 :         (poDS->m_nCompression != COMPRESSION_NONE &&
    8512         317 :          poDS->m_bFillEmptyTilesAtClosing);
    8513             :     // Only required for people writing non-compressed striped files in the
    8514             :     // rightorder and wanting all tstrips to be written in the same order
    8515             :     // so that the end result can be memory mapped without knowledge of each
    8516             :     // strip offset
    8517        2154 :     if (CPLTestBool(CSLFetchNameValueDef(
    8518        4308 :             papszOptions, "WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")) ||
    8519        2154 :         CPLTestBool(CSLFetchNameValueDef(
    8520             :             papszOptions, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")))
    8521             :     {
    8522           0 :         poDS->m_bWriteEmptyTiles = true;
    8523             :     }
    8524             : 
    8525             :     // Precreate (internal) mask, so that the IBuildOverviews() below
    8526             :     // has a chance to create also the overviews of the mask.
    8527        2154 :     CPLErr eErr = CE_None;
    8528             : 
    8529        2154 :     if (bCreateMask)
    8530             :     {
    8531          38 :         eErr = poDS->CreateMaskBand(nMaskFlags);
    8532          38 :         if (poDS->m_poMaskDS)
    8533             :         {
    8534          37 :             poDS->m_poMaskDS->m_bFillEmptyTilesAtClosing =
    8535          37 :                 poDS->m_bFillEmptyTilesAtClosing;
    8536          37 :             poDS->m_poMaskDS->m_bWriteEmptyTiles = poDS->m_bWriteEmptyTiles;
    8537             :         }
    8538             :     }
    8539             : 
    8540             :     /* -------------------------------------------------------------------- */
    8541             :     /*      Create and then copy existing overviews if requested            */
    8542             :     /*  We do it such that all the IFDs are at the beginning of the file,   */
    8543             :     /*  and that the imagery data for the smallest overview is written      */
    8544             :     /*  first, that way the file is more usable when embedded in a          */
    8545             :     /*  compressed stream.                                                  */
    8546             :     /* -------------------------------------------------------------------- */
    8547             : 
    8548             :     // For scaled progress due to overview copying.
    8549        2154 :     const int nBandsWidthMask = l_nBands + (bCreateMask ? 1 : 0);
    8550        2154 :     double dfTotalPixels =
    8551        2154 :         static_cast<double>(nXSize) * nYSize * nBandsWidthMask;
    8552        2154 :     double dfCurPixels = 0;
    8553             : 
    8554        2154 :     if (eErr == CE_None && bCopySrcOverviews)
    8555             :     {
    8556           0 :         std::unique_ptr<GDALDataset> poMaskOvrDS;
    8557             :         const char *pszMaskOvrDS =
    8558         225 :             CSLFetchNameValue(papszOptions, "@MASK_OVERVIEW_DATASET");
    8559         225 :         if (pszMaskOvrDS)
    8560             :         {
    8561           6 :             poMaskOvrDS.reset(GDALDataset::Open(pszMaskOvrDS));
    8562           6 :             if (!poMaskOvrDS)
    8563             :             {
    8564           0 :                 l_fpL->CancelCreation();
    8565           0 :                 return nullptr;
    8566             :             }
    8567           6 :             if (poMaskOvrDS->GetRasterCount() != 1)
    8568             :             {
    8569           0 :                 l_fpL->CancelCreation();
    8570           0 :                 return nullptr;
    8571             :             }
    8572             :         }
    8573         225 :         if (nSrcOverviews)
    8574             :         {
    8575          71 :             eErr = poDS->CreateOverviewsFromSrcOverviews(poSrcDS, poOvrDS.get(),
    8576             :                                                          nSrcOverviews);
    8577             : 
    8578         207 :             if (eErr == CE_None &&
    8579          71 :                 (poMaskOvrDS != nullptr ||
    8580          65 :                  (poSrcDS->GetRasterBand(1)->GetOverview(0) &&
    8581          35 :                   poSrcDS->GetRasterBand(1)->GetOverview(0)->GetMaskFlags() ==
    8582             :                       GMF_PER_DATASET)))
    8583             :             {
    8584          18 :                 int nOvrBlockXSize = 0;
    8585          18 :                 int nOvrBlockYSize = 0;
    8586          18 :                 GTIFFGetOverviewBlockSize(
    8587          18 :                     GDALRasterBand::ToHandle(poDS->GetRasterBand(1)),
    8588             :                     &nOvrBlockXSize, &nOvrBlockYSize, nullptr, nullptr);
    8589          18 :                 eErr = poDS->CreateInternalMaskOverviews(nOvrBlockXSize,
    8590             :                                                          nOvrBlockYSize);
    8591             :             }
    8592             :         }
    8593             : 
    8594         225 :         TIFFForceStrileArrayWriting(poDS->m_hTIFF);
    8595             : 
    8596         225 :         if (poDS->m_poMaskDS)
    8597             :         {
    8598          27 :             TIFFForceStrileArrayWriting(poDS->m_poMaskDS->m_hTIFF);
    8599             :         }
    8600             : 
    8601         356 :         for (auto &poIterOvrDS : poDS->m_apoOverviewDS)
    8602             :         {
    8603         131 :             TIFFForceStrileArrayWriting(poIterOvrDS->m_hTIFF);
    8604             : 
    8605         131 :             if (poIterOvrDS->m_poMaskDS)
    8606             :             {
    8607          31 :                 TIFFForceStrileArrayWriting(poIterOvrDS->m_poMaskDS->m_hTIFF);
    8608             :             }
    8609             :         }
    8610             : 
    8611         225 :         if (eErr == CE_None && nSrcOverviews)
    8612             :         {
    8613          71 :             if (poDS->m_apoOverviewDS.size() !=
    8614          71 :                 static_cast<size_t>(nSrcOverviews))
    8615             :             {
    8616           0 :                 ReportError(
    8617             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    8618             :                     "Did only manage to instantiate %d overview levels, "
    8619             :                     "whereas source contains %d",
    8620           0 :                     static_cast<int>(poDS->m_apoOverviewDS.size()),
    8621             :                     nSrcOverviews);
    8622           0 :                 eErr = CE_Failure;
    8623             :             }
    8624             : 
    8625         202 :             for (int i = 0; eErr == CE_None && i < nSrcOverviews; ++i)
    8626             :             {
    8627             :                 GDALRasterBand *poOvrBand =
    8628             :                     poOvrDS
    8629         207 :                         ? (i == 0
    8630          76 :                                ? poOvrDS->GetRasterBand(1)
    8631          40 :                                : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    8632         186 :                         : poSrcDS->GetRasterBand(1)->GetOverview(i);
    8633             :                 const double dfOvrPixels =
    8634         131 :                     static_cast<double>(poOvrBand->GetXSize()) *
    8635         131 :                     poOvrBand->GetYSize();
    8636         131 :                 dfTotalPixels += dfOvrPixels * l_nBands;
    8637         245 :                 if (poOvrBand->GetMaskFlags() == GMF_PER_DATASET ||
    8638         114 :                     poMaskOvrDS != nullptr)
    8639             :                 {
    8640          31 :                     dfTotalPixels += dfOvrPixels;
    8641             :                 }
    8642         100 :                 else if (i == 0 && poDS->GetRasterBand(1)->GetMaskFlags() ==
    8643             :                                        GMF_PER_DATASET)
    8644             :                 {
    8645           2 :                     ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
    8646             :                                 "Source dataset has a mask band on full "
    8647             :                                 "resolution, overviews on the regular bands, "
    8648             :                                 "but lacks overviews on the mask band.");
    8649             :                 }
    8650             :             }
    8651             : 
    8652             :             // Now copy the imagery.
    8653             :             // Begin with the smallest overview.
    8654          71 :             for (int iOvrLevel = nSrcOverviews - 1;
    8655         201 :                  eErr == CE_None && iOvrLevel >= 0; --iOvrLevel)
    8656             :             {
    8657         130 :                 auto poDstDS = poDS->m_apoOverviewDS[iOvrLevel].get();
    8658             : 
    8659             :                 // Create a fake dataset with the source overview level so that
    8660             :                 // GDALDatasetCopyWholeRaster can cope with it.
    8661             :                 GDALDataset *poSrcOvrDS =
    8662             :                     poOvrDS
    8663         170 :                         ? (iOvrLevel == 0 ? poOvrDS.get()
    8664          40 :                                           : GDALCreateOverviewDataset(
    8665             :                                                 poOvrDS.get(), iOvrLevel - 1,
    8666             :                                                 /* bThisLevelOnly = */ true))
    8667          54 :                         : GDALCreateOverviewDataset(
    8668             :                               poSrcDS, iOvrLevel,
    8669         130 :                               /* bThisLevelOnly = */ true);
    8670             :                 GDALRasterBand *poSrcOvrBand =
    8671         206 :                     poOvrDS ? (iOvrLevel == 0
    8672          76 :                                    ? poOvrDS->GetRasterBand(1)
    8673          80 :                                    : poOvrDS->GetRasterBand(1)->GetOverview(
    8674          40 :                                          iOvrLevel - 1))
    8675         184 :                             : poSrcDS->GetRasterBand(1)->GetOverview(iOvrLevel);
    8676             :                 double dfNextCurPixels =
    8677             :                     dfCurPixels +
    8678         130 :                     static_cast<double>(poSrcOvrBand->GetXSize()) *
    8679         130 :                         poSrcOvrBand->GetYSize() * l_nBands;
    8680             : 
    8681         130 :                 poDstDS->m_bBlockOrderRowMajor = true;
    8682         130 :                 poDstDS->m_bLeaderSizeAsUInt4 = true;
    8683         130 :                 poDstDS->m_bTrailerRepeatedLast4BytesRepeated = true;
    8684         130 :                 poDstDS->m_bFillEmptyTilesAtClosing =
    8685         130 :                     poDS->m_bFillEmptyTilesAtClosing;
    8686         130 :                 poDstDS->m_bWriteEmptyTiles = poDS->m_bWriteEmptyTiles;
    8687         130 :                 poDstDS->m_bTileInterleave = poDS->m_bTileInterleave;
    8688         130 :                 GDALRasterBand *poSrcMaskBand = nullptr;
    8689         130 :                 if (poDstDS->m_poMaskDS)
    8690             :                 {
    8691          31 :                     poDstDS->m_poMaskDS->m_bBlockOrderRowMajor = true;
    8692          31 :                     poDstDS->m_poMaskDS->m_bLeaderSizeAsUInt4 = true;
    8693          31 :                     poDstDS->m_poMaskDS->m_bTrailerRepeatedLast4BytesRepeated =
    8694             :                         true;
    8695          62 :                     poDstDS->m_poMaskDS->m_bFillEmptyTilesAtClosing =
    8696          31 :                         poDS->m_bFillEmptyTilesAtClosing;
    8697          62 :                     poDstDS->m_poMaskDS->m_bWriteEmptyTiles =
    8698          31 :                         poDS->m_bWriteEmptyTiles;
    8699             : 
    8700          31 :                     poSrcMaskBand =
    8701             :                         poMaskOvrDS
    8702          45 :                             ? (iOvrLevel == 0
    8703          14 :                                    ? poMaskOvrDS->GetRasterBand(1)
    8704          16 :                                    : poMaskOvrDS->GetRasterBand(1)->GetOverview(
    8705           8 :                                          iOvrLevel - 1))
    8706          48 :                             : poSrcOvrBand->GetMaskBand();
    8707             :                 }
    8708             : 
    8709         130 :                 if (poDstDS->m_poMaskDS)
    8710             :                 {
    8711          31 :                     dfNextCurPixels +=
    8712          31 :                         static_cast<double>(poSrcOvrBand->GetXSize()) *
    8713          31 :                         poSrcOvrBand->GetYSize();
    8714             :                 }
    8715             :                 void *pScaledData =
    8716         130 :                     GDALCreateScaledProgress(dfCurPixels / dfTotalPixels,
    8717             :                                              dfNextCurPixels / dfTotalPixels,
    8718             :                                              pfnProgress, pProgressData);
    8719             : 
    8720         130 :                 eErr = CopyImageryAndMask(poDstDS, poSrcOvrDS, poSrcMaskBand,
    8721             :                                           GDALScaledProgress, pScaledData);
    8722             : 
    8723         130 :                 dfCurPixels = dfNextCurPixels;
    8724         130 :                 GDALDestroyScaledProgress(pScaledData);
    8725             : 
    8726         130 :                 if (poSrcOvrDS != poOvrDS.get())
    8727          94 :                     delete poSrcOvrDS;
    8728         130 :                 poSrcOvrDS = nullptr;
    8729             :             }
    8730             :         }
    8731             :     }
    8732             : 
    8733             :     /* -------------------------------------------------------------------- */
    8734             :     /*      Copy actual imagery.                                            */
    8735             :     /* -------------------------------------------------------------------- */
    8736        2154 :     double dfNextCurPixels =
    8737        2154 :         dfCurPixels + static_cast<double>(nXSize) * nYSize * l_nBands;
    8738        2154 :     void *pScaledData = GDALCreateScaledProgress(
    8739             :         dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
    8740             :         pfnProgress, pProgressData);
    8741             : 
    8742             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8743        2154 :     bool bTryCopy = true;
    8744             : #endif
    8745             : 
    8746             : #ifdef HAVE_LIBJPEG
    8747        2154 :     if (bCopyFromJPEG)
    8748             :     {
    8749          12 :         eErr = GTIFF_CopyFromJPEG(poDS.get(), poSrcDS, pfnProgress,
    8750             :                                   pProgressData, bTryCopy);
    8751             : 
    8752             :         // In case of failure in the decompression step, try normal copy.
    8753          12 :         if (bTryCopy)
    8754           0 :             eErr = CE_None;
    8755             :     }
    8756             : #endif
    8757             : 
    8758             : #ifdef JPEG_DIRECT_COPY
    8759             :     if (bDirectCopyFromJPEG)
    8760             :     {
    8761             :         eErr = GTIFF_DirectCopyFromJPEG(poDS.get(), poSrcDS, pfnProgress,
    8762             :                                         pProgressData, bTryCopy);
    8763             : 
    8764             :         // In case of failure in the reading step, try normal copy.
    8765             :         if (bTryCopy)
    8766             :             eErr = CE_None;
    8767             :     }
    8768             : #endif
    8769             : 
    8770        2154 :     bool bWriteMask = true;
    8771        2154 :     if (
    8772             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8773        4296 :         bTryCopy &&
    8774             : #endif
    8775        2142 :         (poDS->m_bTreatAsSplit || poDS->m_bTreatAsSplitBitmap))
    8776             :     {
    8777             :         // For split bands, we use TIFFWriteScanline() interface.
    8778           9 :         CPLAssert(poDS->m_nBitsPerSample == 8 || poDS->m_nBitsPerSample == 1);
    8779             : 
    8780           9 :         if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG && poDS->nBands > 1)
    8781             :         {
    8782             :             GByte *pabyScanline = static_cast<GByte *>(
    8783           3 :                 VSI_MALLOC_VERBOSE(TIFFScanlineSize(l_hTIFF)));
    8784           3 :             if (pabyScanline == nullptr)
    8785           0 :                 eErr = CE_Failure;
    8786        9052 :             for (int j = 0; j < nYSize && eErr == CE_None; ++j)
    8787             :             {
    8788       18098 :                 eErr = poSrcDS->RasterIO(GF_Read, 0, j, nXSize, 1, pabyScanline,
    8789             :                                          nXSize, 1, GDT_UInt8, l_nBands,
    8790        9049 :                                          nullptr, poDS->nBands, 0, 1, nullptr);
    8791       18098 :                 if (eErr == CE_None &&
    8792        9049 :                     TIFFWriteScanline(l_hTIFF, pabyScanline, j, 0) == -1)
    8793             :                 {
    8794           0 :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    8795             :                                 "TIFFWriteScanline() failed.");
    8796           0 :                     eErr = CE_Failure;
    8797             :                 }
    8798        9049 :                 if (!GDALScaledProgress((j + 1) * 1.0 / nYSize, nullptr,
    8799             :                                         pScaledData))
    8800           0 :                     eErr = CE_Failure;
    8801             :             }
    8802           3 :             CPLFree(pabyScanline);
    8803             :         }
    8804             :         else
    8805             :         {
    8806             :             GByte *pabyScanline =
    8807           6 :                 static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
    8808           6 :             if (pabyScanline == nullptr)
    8809           0 :                 eErr = CE_Failure;
    8810             :             else
    8811           6 :                 eErr = CE_None;
    8812          14 :             for (int iBand = 1; iBand <= l_nBands && eErr == CE_None; ++iBand)
    8813             :             {
    8814       48211 :                 for (int j = 0; j < nYSize && eErr == CE_None; ++j)
    8815             :                 {
    8816       48203 :                     eErr = poSrcDS->GetRasterBand(iBand)->RasterIO(
    8817             :                         GF_Read, 0, j, nXSize, 1, pabyScanline, nXSize, 1,
    8818             :                         GDT_UInt8, 0, 0, nullptr);
    8819       48203 :                     if (poDS->m_bTreatAsSplitBitmap)
    8820             :                     {
    8821     7225210 :                         for (int i = 0; i < nXSize; ++i)
    8822             :                         {
    8823     7216010 :                             const GByte byVal = pabyScanline[i];
    8824     7216010 :                             if ((i & 0x7) == 0)
    8825      902001 :                                 pabyScanline[i >> 3] = 0;
    8826     7216010 :                             if (byVal)
    8827     7097220 :                                 pabyScanline[i >> 3] |= 0x80 >> (i & 0x7);
    8828             :                         }
    8829             :                     }
    8830       96406 :                     if (eErr == CE_None &&
    8831       48203 :                         TIFFWriteScanline(l_hTIFF, pabyScanline, j,
    8832       48203 :                                           static_cast<uint16_t>(iBand - 1)) ==
    8833             :                             -1)
    8834             :                     {
    8835           0 :                         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    8836             :                                     "TIFFWriteScanline() failed.");
    8837           0 :                         eErr = CE_Failure;
    8838             :                     }
    8839       48203 :                     if (!GDALScaledProgress((j + 1 + (iBand - 1) * nYSize) *
    8840       48203 :                                                 1.0 / (l_nBands * nYSize),
    8841             :                                             nullptr, pScaledData))
    8842           0 :                         eErr = CE_Failure;
    8843             :                 }
    8844             :             }
    8845           6 :             CPLFree(pabyScanline);
    8846             :         }
    8847             : 
    8848             :         // Necessary to be able to read the file without re-opening.
    8849           9 :         TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(l_hTIFF);
    8850             : 
    8851           9 :         TIFFFlushData(l_hTIFF);
    8852             : 
    8853           9 :         toff_t nNewDirOffset = pfnSizeProc(TIFFClientdata(l_hTIFF));
    8854           9 :         if ((nNewDirOffset % 2) == 1)
    8855           5 :             ++nNewDirOffset;
    8856             : 
    8857           9 :         TIFFFlush(l_hTIFF);
    8858             : 
    8859           9 :         if (poDS->m_nDirOffset != TIFFCurrentDirOffset(l_hTIFF))
    8860             :         {
    8861           0 :             poDS->m_nDirOffset = nNewDirOffset;
    8862           0 :             CPLDebug("GTiff", "directory moved during flush.");
    8863             :         }
    8864             :     }
    8865        2145 :     else if (
    8866             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8867        2133 :         bTryCopy &&
    8868             : #endif
    8869             :         eErr == CE_None)
    8870             :     {
    8871        2132 :         const char *papszCopyWholeRasterOptions[3] = {nullptr, nullptr,
    8872             :                                                       nullptr};
    8873        2132 :         int iNextOption = 0;
    8874        2132 :         papszCopyWholeRasterOptions[iNextOption++] = "SKIP_HOLES=YES";
    8875        2132 :         if (l_nCompression != COMPRESSION_NONE)
    8876             :         {
    8877         507 :             papszCopyWholeRasterOptions[iNextOption++] = "COMPRESSED=YES";
    8878             :         }
    8879             : 
    8880             :         // For streaming with separate, we really want that bands are written
    8881             :         // after each other, even if the source is pixel interleaved.
    8882        1625 :         else if (bStreaming && poDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE)
    8883             :         {
    8884           1 :             papszCopyWholeRasterOptions[iNextOption++] = "INTERLEAVE=BAND";
    8885             :         }
    8886             : 
    8887        2132 :         if (bCopySrcOverviews || bTileInterleaving)
    8888             :         {
    8889         225 :             poDS->m_bBlockOrderRowMajor = true;
    8890         225 :             poDS->m_bLeaderSizeAsUInt4 = bCopySrcOverviews;
    8891         225 :             poDS->m_bTrailerRepeatedLast4BytesRepeated = bCopySrcOverviews;
    8892         225 :             if (poDS->m_poMaskDS)
    8893             :             {
    8894          27 :                 poDS->m_poMaskDS->m_bBlockOrderRowMajor = true;
    8895          27 :                 poDS->m_poMaskDS->m_bLeaderSizeAsUInt4 = bCopySrcOverviews;
    8896          27 :                 poDS->m_poMaskDS->m_bTrailerRepeatedLast4BytesRepeated =
    8897             :                     bCopySrcOverviews;
    8898          27 :                 GDALDestroyScaledProgress(pScaledData);
    8899             :                 pScaledData =
    8900          27 :                     GDALCreateScaledProgress(dfCurPixels / dfTotalPixels, 1.0,
    8901             :                                              pfnProgress, pProgressData);
    8902             :             }
    8903             : 
    8904         225 :             eErr = CopyImageryAndMask(poDS.get(), poSrcDS,
    8905         225 :                                       poSrcDS->GetRasterBand(1)->GetMaskBand(),
    8906             :                                       GDALScaledProgress, pScaledData);
    8907         225 :             if (poDS->m_poMaskDS)
    8908             :             {
    8909          27 :                 bWriteMask = false;
    8910             :             }
    8911             :         }
    8912             :         else
    8913             :         {
    8914        1907 :             eErr = GDALDatasetCopyWholeRaster(GDALDataset::ToHandle(poSrcDS),
    8915        1907 :                                               GDALDataset::ToHandle(poDS.get()),
    8916             :                                               papszCopyWholeRasterOptions,
    8917             :                                               GDALScaledProgress, pScaledData);
    8918             :         }
    8919             :     }
    8920             : 
    8921        2154 :     GDALDestroyScaledProgress(pScaledData);
    8922             : 
    8923        2154 :     if (eErr == CE_None && !bStreaming && bWriteMask)
    8924             :     {
    8925        2105 :         pScaledData = GDALCreateScaledProgress(dfNextCurPixels / dfTotalPixels,
    8926             :                                                1.0, pfnProgress, pProgressData);
    8927        2105 :         if (poDS->m_poMaskDS)
    8928             :         {
    8929          10 :             const char *l_papszOptions[2] = {"COMPRESSED=YES", nullptr};
    8930          10 :             eErr = GDALRasterBandCopyWholeRaster(
    8931          10 :                 poSrcDS->GetRasterBand(1)->GetMaskBand(),
    8932          10 :                 poDS->GetRasterBand(1)->GetMaskBand(),
    8933             :                 const_cast<char **>(l_papszOptions), GDALScaledProgress,
    8934             :                 pScaledData);
    8935             :         }
    8936             :         else
    8937             :         {
    8938        2095 :             eErr = GDALDriver::DefaultCopyMasks(poSrcDS, poDS.get(), bStrict,
    8939             :                                                 nullptr, GDALScaledProgress,
    8940             :                                                 pScaledData);
    8941             :         }
    8942        2105 :         GDALDestroyScaledProgress(pScaledData);
    8943             :     }
    8944             : 
    8945        2154 :     poDS->m_bWriteCOGLayout = false;
    8946             : 
    8947        4290 :     if (eErr == CE_None &&
    8948        2136 :         CPLTestBool(CSLFetchNameValueDef(poDS->m_papszCreationOptions,
    8949             :                                          "@FLUSHCACHE", "NO")))
    8950             :     {
    8951         176 :         if (poDS->FlushCache(false) != CE_None)
    8952             :         {
    8953           0 :             eErr = CE_Failure;
    8954             :         }
    8955             :     }
    8956             : 
    8957        2154 :     if (eErr == CE_Failure)
    8958             :     {
    8959          18 :         if (CPLTestBool(CPLGetConfigOption("GTIFF_DELETE_ON_ERROR", "YES")))
    8960             :         {
    8961          17 :             l_fpL->CancelCreation();
    8962          17 :             poDS.reset();
    8963             : 
    8964          17 :             if (!bStreaming)
    8965             :             {
    8966             :                 // Should really delete more carefully.
    8967          17 :                 VSIUnlink(pszFilename);
    8968             :             }
    8969             :         }
    8970             :         else
    8971             :         {
    8972           1 :             poDS.reset();
    8973             :         }
    8974             :     }
    8975             : 
    8976        2154 :     return poDS.release();
    8977             : }
    8978             : 
    8979             : /************************************************************************/
    8980             : /*                           SetSpatialRef()                            */
    8981             : /************************************************************************/
    8982             : 
    8983        1535 : CPLErr GTiffDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    8984             : 
    8985             : {
    8986        1535 :     if (m_bStreamingOut && m_bCrystalized)
    8987             :     {
    8988           1 :         ReportError(CE_Failure, CPLE_NotSupported,
    8989             :                     "Cannot modify projection at that point in "
    8990             :                     "a streamed output file");
    8991           1 :         return CE_Failure;
    8992             :     }
    8993             : 
    8994        1534 :     LoadGeoreferencingAndPamIfNeeded();
    8995        1534 :     LookForProjection();
    8996             : 
    8997        1534 :     CPLErr eErr = CE_None;
    8998        1534 :     if (eAccess == GA_Update)
    8999             :     {
    9000        1536 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    9001           7 :             (GetPamFlags() & GPF_DISABLED) == 0)
    9002             :         {
    9003           7 :             eErr = GDALPamDataset::SetSpatialRef(poSRS);
    9004             :         }
    9005             :         else
    9006             :         {
    9007        1522 :             if (GDALPamDataset::GetSpatialRef() != nullptr)
    9008             :             {
    9009             :                 // Cancel any existing SRS from PAM file.
    9010           1 :                 GDALPamDataset::SetSpatialRef(nullptr);
    9011             :             }
    9012        1522 :             m_bGeoTIFFInfoChanged = true;
    9013             :         }
    9014             :     }
    9015             :     else
    9016             :     {
    9017           5 :         CPLDebug("GTIFF", "SetSpatialRef() goes to PAM instead of TIFF tags");
    9018           5 :         eErr = GDALPamDataset::SetSpatialRef(poSRS);
    9019             :     }
    9020             : 
    9021        1534 :     if (eErr == CE_None)
    9022             :     {
    9023        1534 :         if (poSRS == nullptr || poSRS->IsEmpty())
    9024             :         {
    9025          14 :             if (!m_oSRS.IsEmpty())
    9026             :             {
    9027           4 :                 m_bForceUnsetProjection = true;
    9028             :             }
    9029          14 :             m_oSRS.Clear();
    9030             :         }
    9031             :         else
    9032             :         {
    9033        1520 :             m_oSRS = *poSRS;
    9034        1520 :             m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    9035             :         }
    9036             :     }
    9037             : 
    9038        1534 :     return eErr;
    9039             : }
    9040             : 
    9041             : /************************************************************************/
    9042             : /*                          SetGeoTransform()                           */
    9043             : /************************************************************************/
    9044             : 
    9045        1863 : CPLErr GTiffDataset::SetGeoTransform(const GDALGeoTransform &gt)
    9046             : 
    9047             : {
    9048        1863 :     if (m_bStreamingOut && m_bCrystalized)
    9049             :     {
    9050           1 :         ReportError(CE_Failure, CPLE_NotSupported,
    9051             :                     "Cannot modify geotransform at that point in a "
    9052             :                     "streamed output file");
    9053           1 :         return CE_Failure;
    9054             :     }
    9055             : 
    9056        1862 :     LoadGeoreferencingAndPamIfNeeded();
    9057             : 
    9058        1862 :     CPLErr eErr = CE_None;
    9059        1862 :     if (eAccess == GA_Update)
    9060             :     {
    9061        1856 :         if (!m_aoGCPs.empty())
    9062             :         {
    9063           1 :             ReportError(CE_Warning, CPLE_AppDefined,
    9064             :                         "GCPs previously set are going to be cleared "
    9065             :                         "due to the setting of a geotransform.");
    9066           1 :             m_bForceUnsetGTOrGCPs = true;
    9067           1 :             m_aoGCPs.clear();
    9068             :         }
    9069        1855 :         else if (gt.xorig == 0.0 && gt.xscale == 0.0 && gt.xrot == 0.0 &&
    9070           2 :                  gt.yorig == 0.0 && gt.yrot == 0.0 && gt.yscale == 0.0)
    9071             :         {
    9072           2 :             if (m_bGeoTransformValid)
    9073             :             {
    9074           2 :                 m_bForceUnsetGTOrGCPs = true;
    9075           2 :                 m_bGeoTIFFInfoChanged = true;
    9076             :             }
    9077           2 :             m_bGeoTransformValid = false;
    9078           2 :             m_gt = gt;
    9079           2 :             return CE_None;
    9080             :         }
    9081             : 
    9082        3717 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    9083           9 :             !CPLFetchBool(m_papszCreationOptions, "TFW", false) &&
    9084        1868 :             !CPLFetchBool(m_papszCreationOptions, "WORLDFILE", false) &&
    9085           5 :             (GetPamFlags() & GPF_DISABLED) == 0)
    9086             :         {
    9087           5 :             eErr = GDALPamDataset::SetGeoTransform(gt);
    9088             :         }
    9089             :         else
    9090             :         {
    9091             :             // Cancel any existing geotransform from PAM file.
    9092        1849 :             GDALPamDataset::DeleteGeoTransform();
    9093        1849 :             m_bGeoTIFFInfoChanged = true;
    9094             :         }
    9095             :     }
    9096             :     else
    9097             :     {
    9098           6 :         CPLDebug("GTIFF", "SetGeoTransform() goes to PAM instead of TIFF tags");
    9099           6 :         eErr = GDALPamDataset::SetGeoTransform(gt);
    9100             :     }
    9101             : 
    9102        1860 :     if (eErr == CE_None)
    9103             :     {
    9104        1860 :         m_gt = gt;
    9105        1860 :         m_bGeoTransformValid = true;
    9106             :     }
    9107             : 
    9108        1860 :     return eErr;
    9109             : }
    9110             : 
    9111             : /************************************************************************/
    9112             : /*                              SetGCPs()                               */
    9113             : /************************************************************************/
    9114             : 
    9115          22 : CPLErr GTiffDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
    9116             :                              const OGRSpatialReference *poGCPSRS)
    9117             : {
    9118          22 :     CPLErr eErr = CE_None;
    9119          22 :     LoadGeoreferencingAndPamIfNeeded();
    9120          22 :     LookForProjection();
    9121             : 
    9122          22 :     if (eAccess == GA_Update)
    9123             :     {
    9124          20 :         if (!m_aoGCPs.empty() && nGCPCountIn == 0)
    9125             :         {
    9126           3 :             m_bForceUnsetGTOrGCPs = true;
    9127             :         }
    9128          17 :         else if (nGCPCountIn > 0 && m_bGeoTransformValid)
    9129             :         {
    9130           5 :             ReportError(CE_Warning, CPLE_AppDefined,
    9131             :                         "A geotransform previously set is going to be cleared "
    9132             :                         "due to the setting of GCPs.");
    9133           5 :             m_gt = GDALGeoTransform();
    9134           5 :             m_bGeoTransformValid = false;
    9135           5 :             m_bForceUnsetGTOrGCPs = true;
    9136             :         }
    9137          20 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    9138           0 :             (GetPamFlags() & GPF_DISABLED) == 0)
    9139             :         {
    9140           0 :             eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn, poGCPSRS);
    9141             :         }
    9142             :         else
    9143             :         {
    9144          20 :             if (nGCPCountIn > knMAX_GCP_COUNT)
    9145             :             {
    9146           2 :                 if (GDALPamDataset::GetGCPCount() == 0 && !m_aoGCPs.empty())
    9147             :                 {
    9148           1 :                     m_bForceUnsetGTOrGCPs = true;
    9149             :                 }
    9150           2 :                 ReportError(CE_Warning, CPLE_AppDefined,
    9151             :                             "Trying to write %d GCPs, whereas the maximum "
    9152             :                             "supported in GeoTIFF tag is %d. "
    9153             :                             "Falling back to writing them to PAM",
    9154             :                             nGCPCountIn, knMAX_GCP_COUNT);
    9155           2 :                 eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn,
    9156             :                                                poGCPSRS);
    9157             :             }
    9158          18 :             else if (GDALPamDataset::GetGCPCount() > 0)
    9159             :             {
    9160             :                 // Cancel any existing GCPs from PAM file.
    9161           1 :                 GDALPamDataset::SetGCPs(
    9162             :                     0, nullptr,
    9163             :                     static_cast<const OGRSpatialReference *>(nullptr));
    9164             :             }
    9165          20 :             m_bGeoTIFFInfoChanged = true;
    9166             :         }
    9167             :     }
    9168             :     else
    9169             :     {
    9170           2 :         CPLDebug("GTIFF", "SetGCPs() goes to PAM instead of TIFF tags");
    9171           2 :         eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn, poGCPSRS);
    9172             :     }
    9173             : 
    9174          22 :     if (eErr == CE_None)
    9175             :     {
    9176          22 :         if (poGCPSRS == nullptr || poGCPSRS->IsEmpty())
    9177             :         {
    9178          11 :             if (!m_oSRS.IsEmpty())
    9179             :             {
    9180           5 :                 m_bForceUnsetProjection = true;
    9181             :             }
    9182          11 :             m_oSRS.Clear();
    9183             :         }
    9184             :         else
    9185             :         {
    9186          11 :             m_oSRS = *poGCPSRS;
    9187          11 :             m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    9188             :         }
    9189             : 
    9190          22 :         m_aoGCPs = gdal::GCP::fromC(pasGCPListIn, nGCPCountIn);
    9191             :     }
    9192             : 
    9193          22 :     return eErr;
    9194             : }
    9195             : 
    9196             : /************************************************************************/
    9197             : /*                            SetMetadata()                             */
    9198             : /************************************************************************/
    9199        2748 : CPLErr GTiffDataset::SetMetadata(CSLConstList papszMD, const char *pszDomain)
    9200             : 
    9201             : {
    9202        2748 :     LoadGeoreferencingAndPamIfNeeded();
    9203             : 
    9204        2748 :     if (m_bStreamingOut && m_bCrystalized)
    9205             :     {
    9206           1 :         ReportError(
    9207             :             CE_Failure, CPLE_NotSupported,
    9208             :             "Cannot modify metadata at that point in a streamed output file");
    9209           1 :         return CE_Failure;
    9210             :     }
    9211             : 
    9212        2747 :     if (pszDomain && EQUAL(pszDomain, "json:ISIS3"))
    9213             :     {
    9214           5 :         m_oISIS3Metadata.Deinit();
    9215           5 :         m_oMapISIS3MetadataItems.clear();
    9216             :     }
    9217             : 
    9218        2747 :     CPLErr eErr = CE_None;
    9219        2747 :     if (eAccess == GA_Update)
    9220             :     {
    9221        2744 :         if (pszDomain != nullptr && EQUAL(pszDomain, GDAL_MDD_RPC))
    9222             :         {
    9223             :             // So that a subsequent GetMetadata() wouldn't override our new
    9224             :             // values
    9225          22 :             LoadMetadata();
    9226          22 :             m_bForceUnsetRPC = (CSLCount(papszMD) == 0);
    9227             :         }
    9228             : 
    9229        2744 :         if ((papszMD != nullptr) && (pszDomain != nullptr) &&
    9230        1904 :             EQUAL(pszDomain, "COLOR_PROFILE"))
    9231             :         {
    9232           0 :             m_bColorProfileMetadataChanged = true;
    9233             :         }
    9234        2744 :         else if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
    9235             :         {
    9236        2744 :             m_bMetadataChanged = true;
    9237             :             // Cancel any existing metadata from PAM file.
    9238        2744 :             if (GDALPamDataset::GetMetadata(pszDomain) != nullptr)
    9239           1 :                 GDALPamDataset::SetMetadata(nullptr, pszDomain);
    9240             :         }
    9241             : 
    9242        5452 :         if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    9243        2708 :             CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT) != nullptr)
    9244             :         {
    9245        2073 :             const char *pszPrevValue = GetMetadataItem(GDALMD_AREA_OR_POINT);
    9246             :             const char *pszNewValue =
    9247        2073 :                 CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT);
    9248        2073 :             if (pszPrevValue == nullptr || pszNewValue == nullptr ||
    9249        1646 :                 !EQUAL(pszPrevValue, pszNewValue))
    9250             :             {
    9251         431 :                 LookForProjection();
    9252         431 :                 m_bGeoTIFFInfoChanged = true;
    9253             :             }
    9254             :         }
    9255             : 
    9256        2744 :         if (pszDomain != nullptr && EQUAL(pszDomain, "xml:XMP"))
    9257             :         {
    9258           2 :             if (papszMD != nullptr && *papszMD != nullptr)
    9259             :             {
    9260           1 :                 int nTagSize = static_cast<int>(strlen(*papszMD));
    9261           1 :                 TIFFSetField(m_hTIFF, TIFFTAG_XMLPACKET, nTagSize, *papszMD);
    9262             :             }
    9263             :             else
    9264             :             {
    9265           1 :                 TIFFUnsetField(m_hTIFF, TIFFTAG_XMLPACKET);
    9266             :             }
    9267             :         }
    9268             :     }
    9269             :     else
    9270             :     {
    9271           3 :         CPLDebug(
    9272             :             "GTIFF",
    9273             :             "GTiffDataset::SetMetadata() goes to PAM instead of TIFF tags");
    9274           3 :         eErr = GDALPamDataset::SetMetadata(papszMD, pszDomain);
    9275             :     }
    9276             : 
    9277        2747 :     if (eErr == CE_None)
    9278             :     {
    9279        2747 :         eErr = m_oGTiffMDMD.SetMetadata(papszMD, pszDomain);
    9280             :     }
    9281        2747 :     return eErr;
    9282             : }
    9283             : 
    9284             : /************************************************************************/
    9285             : /*                          SetMetadataItem()                           */
    9286             : /************************************************************************/
    9287             : 
    9288        5927 : CPLErr GTiffDataset::SetMetadataItem(const char *pszName, const char *pszValue,
    9289             :                                      const char *pszDomain)
    9290             : 
    9291             : {
    9292        5927 :     LoadGeoreferencingAndPamIfNeeded();
    9293             : 
    9294        5927 :     if (m_bStreamingOut && m_bCrystalized)
    9295             :     {
    9296           1 :         ReportError(
    9297             :             CE_Failure, CPLE_NotSupported,
    9298             :             "Cannot modify metadata at that point in a streamed output file");
    9299           1 :         return CE_Failure;
    9300             :     }
    9301             : 
    9302        5926 :     if (pszDomain && EQUAL(pszDomain, "json:ISIS3"))
    9303             :     {
    9304           1 :         ReportError(CE_Failure, CPLE_NotSupported,
    9305             :                     "Updating part of json:ISIS3 is not supported. "
    9306             :                     "Use SetMetadata() instead");
    9307           1 :         return CE_Failure;
    9308             :     }
    9309             : 
    9310        5925 :     CPLErr eErr = CE_None;
    9311        5925 :     if (eAccess == GA_Update)
    9312             :     {
    9313        5918 :         if ((pszDomain != nullptr) && EQUAL(pszDomain, "COLOR_PROFILE"))
    9314             :         {
    9315           8 :             m_bColorProfileMetadataChanged = true;
    9316             :         }
    9317        5910 :         else if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
    9318             :         {
    9319        5910 :             m_bMetadataChanged = true;
    9320             :             // Cancel any existing metadata from PAM file.
    9321        5910 :             if (GDALPamDataset::GetMetadataItem(pszName, pszDomain) != nullptr)
    9322           1 :                 GDALPamDataset::SetMetadataItem(pszName, nullptr, pszDomain);
    9323             :         }
    9324             : 
    9325        5918 :         if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    9326          84 :             pszName != nullptr && EQUAL(pszName, GDALMD_AREA_OR_POINT))
    9327             :         {
    9328           7 :             LookForProjection();
    9329           7 :             m_bGeoTIFFInfoChanged = true;
    9330             :         }
    9331             :     }
    9332             :     else
    9333             :     {
    9334           7 :         CPLDebug(
    9335             :             "GTIFF",
    9336             :             "GTiffDataset::SetMetadataItem() goes to PAM instead of TIFF tags");
    9337           7 :         eErr = GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    9338             :     }
    9339             : 
    9340        5925 :     if (eErr == CE_None)
    9341             :     {
    9342        5925 :         eErr = m_oGTiffMDMD.SetMetadataItem(pszName, pszValue, pszDomain);
    9343             :     }
    9344             : 
    9345        5925 :     return eErr;
    9346             : }
    9347             : 
    9348             : /************************************************************************/
    9349             : /*                           CreateMaskBand()                           */
    9350             : /************************************************************************/
    9351             : 
    9352         100 : CPLErr GTiffDataset::CreateMaskBand(int nFlagsIn)
    9353             : {
    9354         100 :     ScanDirectories();
    9355             : 
    9356         100 :     if (m_poMaskDS != nullptr)
    9357             :     {
    9358           1 :         ReportError(CE_Failure, CPLE_AppDefined,
    9359             :                     "This TIFF dataset already has an internal mask band");
    9360           1 :         return CE_Failure;
    9361             :     }
    9362          99 :     else if (MustCreateInternalMask())
    9363             :     {
    9364          86 :         if (nFlagsIn != GMF_PER_DATASET)
    9365             :         {
    9366           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    9367             :                         "The only flag value supported for internal mask is "
    9368             :                         "GMF_PER_DATASET");
    9369           1 :             return CE_Failure;
    9370             :         }
    9371             : 
    9372          85 :         int l_nCompression = COMPRESSION_PACKBITS;
    9373          85 :         if (strstr(GDALGetMetadataItem(GDALGetDriverByName("GTiff"),
    9374             :                                        GDAL_DMD_CREATIONOPTIONLIST, nullptr),
    9375          85 :                    "<Value>DEFLATE</Value>") != nullptr)
    9376          85 :             l_nCompression = COMPRESSION_ADOBE_DEFLATE;
    9377             : 
    9378             :         /* --------------------------------------------------------------------
    9379             :          */
    9380             :         /*      If we don't have read access, then create the mask externally.
    9381             :          */
    9382             :         /* --------------------------------------------------------------------
    9383             :          */
    9384          85 :         if (GetAccess() != GA_Update)
    9385             :         {
    9386           1 :             ReportError(CE_Warning, CPLE_AppDefined,
    9387             :                         "File open for read-only accessing, "
    9388             :                         "creating mask externally.");
    9389             : 
    9390           1 :             return GDALPamDataset::CreateMaskBand(nFlagsIn);
    9391             :         }
    9392             : 
    9393          84 :         if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    9394           0 :             !m_bWriteKnownIncompatibleEdition)
    9395             :         {
    9396           0 :             ReportError(CE_Warning, CPLE_AppDefined,
    9397             :                         "Adding a mask invalidates the "
    9398             :                         "LAYOUT=IFDS_BEFORE_DATA property");
    9399           0 :             m_bKnownIncompatibleEdition = true;
    9400           0 :             m_bWriteKnownIncompatibleEdition = true;
    9401             :         }
    9402             : 
    9403          84 :         bool bIsOverview = false;
    9404          84 :         uint32_t nSubType = 0;
    9405          84 :         if (TIFFGetField(m_hTIFF, TIFFTAG_SUBFILETYPE, &nSubType))
    9406             :         {
    9407           8 :             bIsOverview = (nSubType & FILETYPE_REDUCEDIMAGE) != 0;
    9408             : 
    9409           8 :             if ((nSubType & FILETYPE_MASK) != 0)
    9410             :             {
    9411           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
    9412             :                             "Cannot create a mask on a TIFF mask IFD !");
    9413           0 :                 return CE_Failure;
    9414             :             }
    9415             :         }
    9416             : 
    9417          84 :         const int bIsTiled = TIFFIsTiled(m_hTIFF);
    9418             : 
    9419          84 :         FlushDirectory();
    9420             : 
    9421          84 :         const toff_t nOffset = GTIFFWriteDirectory(
    9422             :             m_hTIFF,
    9423             :             bIsOverview ? FILETYPE_REDUCEDIMAGE | FILETYPE_MASK : FILETYPE_MASK,
    9424             :             nRasterXSize, nRasterYSize, 1, PLANARCONFIG_CONTIG, 1,
    9425             :             m_nBlockXSize, m_nBlockYSize, bIsTiled, l_nCompression,
    9426             :             PHOTOMETRIC_MASK, PREDICTOR_NONE, SAMPLEFORMAT_UINT, nullptr,
    9427             :             nullptr, nullptr, 0, nullptr, "", nullptr, nullptr, nullptr,
    9428          84 :             nullptr, m_bWriteCOGLayout);
    9429             : 
    9430          84 :         ReloadDirectory();
    9431             : 
    9432          84 :         if (nOffset == 0)
    9433           0 :             return CE_Failure;
    9434             : 
    9435          84 :         m_poMaskDS = std::make_shared<GTiffDataset>();
    9436          84 :         m_poMaskDS->eAccess = GA_Update;
    9437          84 :         m_poMaskDS->m_poBaseDS = this;
    9438          84 :         m_poMaskDS->m_poImageryDS = this;
    9439          84 :         m_poMaskDS->ShareLockWithParentDataset(this);
    9440          84 :         m_poMaskDS->m_osFilename = m_osFilename;
    9441          84 :         m_poMaskDS->m_bPromoteTo8Bits = CPLTestBool(
    9442             :             CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
    9443          84 :         return m_poMaskDS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nOffset,
    9444          84 :                                       GA_Update);
    9445             :     }
    9446             : 
    9447          13 :     return GDALPamDataset::CreateMaskBand(nFlagsIn);
    9448             : }
    9449             : 
    9450             : /************************************************************************/
    9451             : /*                       MustCreateInternalMask()                       */
    9452             : /************************************************************************/
    9453             : 
    9454         137 : bool GTiffDataset::MustCreateInternalMask()
    9455             : {
    9456         137 :     return CPLTestBool(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", "YES"));
    9457             : }
    9458             : 
    9459             : /************************************************************************/
    9460             : /*                           CreateMaskBand()                           */
    9461             : /************************************************************************/
    9462             : 
    9463          29 : CPLErr GTiffRasterBand::CreateMaskBand(int nFlagsIn)
    9464             : {
    9465          29 :     m_poGDS->ScanDirectories();
    9466             : 
    9467          29 :     if (m_poGDS->m_poMaskDS != nullptr)
    9468             :     {
    9469           5 :         ReportError(CE_Failure, CPLE_AppDefined,
    9470             :                     "This TIFF dataset already has an internal mask band");
    9471           5 :         return CE_Failure;
    9472             :     }
    9473             : 
    9474             :     const char *pszGDAL_TIFF_INTERNAL_MASK =
    9475          24 :         CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", nullptr);
    9476          27 :     if ((pszGDAL_TIFF_INTERNAL_MASK &&
    9477          24 :          CPLTestBool(pszGDAL_TIFF_INTERNAL_MASK)) ||
    9478             :         nFlagsIn == GMF_PER_DATASET)
    9479             :     {
    9480          16 :         return m_poGDS->CreateMaskBand(nFlagsIn);
    9481             :     }
    9482             : 
    9483           8 :     return GDALPamRasterBand::CreateMaskBand(nFlagsIn);
    9484             : }
    9485             : 
    9486             : /************************************************************************/
    9487             : /*                            ClampCTEntry()                            */
    9488             : /************************************************************************/
    9489             : 
    9490      236415 : /* static */ unsigned short GTiffDataset::ClampCTEntry(int iColor, int iComp,
    9491             :                                                        int nCTEntryVal,
    9492             :                                                        int nMultFactor)
    9493             : {
    9494      236415 :     const int nVal = nCTEntryVal * nMultFactor;
    9495      236415 :     if (nVal < 0)
    9496             :     {
    9497           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    9498             :                  "Color table entry [%d][%d] = %d, clamped to 0", iColor, iComp,
    9499             :                  nCTEntryVal);
    9500           0 :         return 0;
    9501             :     }
    9502      236415 :     if (nVal > 65535)
    9503             :     {
    9504           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    9505             :                  "Color table entry [%d][%d] = %d, clamped to 65535", iColor,
    9506             :                  iComp, nCTEntryVal);
    9507           2 :         return 65535;
    9508             :     }
    9509      236413 :     return static_cast<unsigned short>(nVal);
    9510             : }

Generated by: LCOV version 1.14