LCOV - code coverage report
Current view: top level - frmts/gtiff - gtiffdataset_write.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3903 4277 91.3 %
Date: 2025-10-21 22:35:35 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             : 
      18             : #include <cassert>
      19             : #include <cerrno>
      20             : 
      21             : #include <algorithm>
      22             : #include <cmath>
      23             : #include <limits>
      24             : #include <memory>
      25             : #include <mutex>
      26             : #include <set>
      27             : #include <string>
      28             : #include <tuple>
      29             : #include <utility>
      30             : 
      31             : #include "cpl_error.h"
      32             : #include "cpl_error_internal.h"  // CPLErrorHandlerAccumulatorStruct
      33             : #include "cpl_float.h"
      34             : #include "cpl_md5.h"
      35             : #include "cpl_vsi.h"
      36             : #include "cpl_vsi_virtual.h"
      37             : #include "cpl_worker_thread_pool.h"
      38             : #include "fetchbufferdirectio.h"
      39             : #include "gdal_mdreader.h"  // GDALWriteRPCTXTFile()
      40             : #include "gdal_priv.h"
      41             : #include "gdal_priv_templates.hpp"  // GDALIsValueInRange<>
      42             : #include "gdal_thread_pool.h"       // GDALGetGlobalThreadPool()
      43             : #include "geovalues.h"              // RasterPixelIsPoint
      44             : #include "gt_jpeg_copy.h"
      45             : #include "gt_overview.h"  // GTIFFBuildOverviewMetadata()
      46             : #include "quant_table_md5sum.h"
      47             : #include "quant_table_md5sum_jpeg9e.h"
      48             : #include "tif_jxl.h"
      49             : #include "tifvsi.h"
      50             : #include "xtiffio.h"
      51             : 
      52             : #if LIFFLIB_VERSION > 20230908 || defined(INTERNAL_LIBTIFF)
      53             : /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
      54             : #define LIBTIFF_MULTIBAND_LERC_NAN_OK
      55             : #endif
      56             : 
      57             : static const int knGTIFFJpegTablesModeDefault = JPEGTABLESMODE_QUANT;
      58             : 
      59             : static constexpr const char szPROFILE_BASELINE[] = "BASELINE";
      60             : static constexpr const char szPROFILE_GeoTIFF[] = "GeoTIFF";
      61             : static constexpr const char szPROFILE_GDALGeoTIFF[] = "GDALGeoTIFF";
      62             : 
      63             : // Due to libgeotiff/xtiff.c declaring TIFFTAG_GEOTIEPOINTS with field_readcount
      64             : // and field_writecount == -1 == TIFF_VARIABLE, we are limited to writing
      65             : // 65535 values in that tag. That could potentially be overcome by changing the tag
      66             : // declaration to using TIFF_VARIABLE2 where the count is a uint32_t.
      67             : constexpr int knMAX_GCP_COUNT =
      68             :     static_cast<int>(std::numeric_limits<uint16_t>::max() / 6);
      69             : 
      70             : enum
      71             : {
      72             :     ENDIANNESS_NATIVE,
      73             :     ENDIANNESS_LITTLE,
      74             :     ENDIANNESS_BIG
      75             : };
      76             : 
      77       17156 : static signed char GTiffGetWebPLevel(CSLConstList papszOptions)
      78             : {
      79       17156 :     int nWebPLevel = DEFAULT_WEBP_LEVEL;
      80       17156 :     const char *pszValue = CSLFetchNameValue(papszOptions, "WEBP_LEVEL");
      81       17156 :     if (pszValue != nullptr)
      82             :     {
      83          51 :         nWebPLevel = atoi(pszValue);
      84          51 :         if (!(nWebPLevel >= 1 && nWebPLevel <= 100))
      85             :         {
      86           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
      87             :                      "WEBP_LEVEL=%s value not recognised, ignoring.", pszValue);
      88           0 :             nWebPLevel = DEFAULT_WEBP_LEVEL;
      89             :         }
      90             :     }
      91       17156 :     return static_cast<signed char>(nWebPLevel);
      92             : }
      93             : 
      94       17162 : static bool GTiffGetWebPLossless(CSLConstList papszOptions)
      95             : {
      96       17162 :     return CPLFetchBool(papszOptions, "WEBP_LOSSLESS", false);
      97             : }
      98             : 
      99       17228 : static double GTiffGetLERCMaxZError(CSLConstList papszOptions)
     100             : {
     101       17228 :     return CPLAtof(CSLFetchNameValueDef(papszOptions, "MAX_Z_ERROR", "0.0"));
     102             : }
     103             : 
     104        7651 : static double GTiffGetLERCMaxZErrorOverview(CSLConstList papszOptions)
     105             : {
     106        7651 :     return CPLAtof(CSLFetchNameValueDef(
     107             :         papszOptions, "MAX_Z_ERROR_OVERVIEW",
     108        7651 :         CSLFetchNameValueDef(papszOptions, "MAX_Z_ERROR", "0.0")));
     109             : }
     110             : 
     111             : #if HAVE_JXL
     112       17232 : static bool GTiffGetJXLLossless(CSLConstList papszOptions,
     113             :                                 bool *pbIsSpecified = nullptr)
     114             : {
     115       17232 :     const char *pszVal = CSLFetchNameValue(papszOptions, "JXL_LOSSLESS");
     116       17232 :     if (pbIsSpecified)
     117        9577 :         *pbIsSpecified = pszVal != nullptr;
     118       17232 :     return pszVal == nullptr || CPLTestBool(pszVal);
     119             : }
     120             : 
     121       17232 : static uint32_t GTiffGetJXLEffort(CSLConstList papszOptions)
     122             : {
     123       17232 :     return atoi(CSLFetchNameValueDef(papszOptions, "JXL_EFFORT", "5"));
     124             : }
     125             : 
     126       17150 : static float GTiffGetJXLDistance(CSLConstList papszOptions,
     127             :                                  bool *pbIsSpecified = nullptr)
     128             : {
     129       17150 :     const char *pszVal = CSLFetchNameValue(papszOptions, "JXL_DISTANCE");
     130       17150 :     if (pbIsSpecified)
     131        9577 :         *pbIsSpecified = pszVal != nullptr;
     132       17150 :     return pszVal == nullptr ? 1.0f : static_cast<float>(CPLAtof(pszVal));
     133             : }
     134             : 
     135       17232 : static float GTiffGetJXLAlphaDistance(CSLConstList papszOptions,
     136             :                                       bool *pbIsSpecified = nullptr)
     137             : {
     138       17232 :     const char *pszVal = CSLFetchNameValue(papszOptions, "JXL_ALPHA_DISTANCE");
     139       17232 :     if (pbIsSpecified)
     140        9577 :         *pbIsSpecified = pszVal != nullptr;
     141       17232 :     return pszVal == nullptr ? -1.0f : static_cast<float>(CPLAtof(pszVal));
     142             : }
     143             : 
     144             : #endif
     145             : 
     146             : /************************************************************************/
     147             : /*                           FillEmptyTiles()                           */
     148             : /************************************************************************/
     149             : 
     150        7828 : CPLErr GTiffDataset::FillEmptyTiles()
     151             : 
     152             : {
     153             :     /* -------------------------------------------------------------------- */
     154             :     /*      How many blocks are there in this file?                         */
     155             :     /* -------------------------------------------------------------------- */
     156       15656 :     const int nBlockCount = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     157        7828 :                                 ? m_nBlocksPerBand * nBands
     158             :                                 : m_nBlocksPerBand;
     159             : 
     160             :     /* -------------------------------------------------------------------- */
     161             :     /*      Fetch block maps.                                               */
     162             :     /* -------------------------------------------------------------------- */
     163        7828 :     toff_t *panByteCounts = nullptr;
     164             : 
     165        7828 :     if (TIFFIsTiled(m_hTIFF))
     166        1036 :         TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts);
     167             :     else
     168        6792 :         TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
     169             : 
     170        7828 :     if (panByteCounts == nullptr)
     171             :     {
     172             :         // Got here with libtiff 3.9.3 and tiff_write_8 test.
     173           0 :         ReportError(CE_Failure, CPLE_AppDefined,
     174             :                     "FillEmptyTiles() failed because panByteCounts == NULL");
     175           0 :         return CE_Failure;
     176             :     }
     177             : 
     178             :     /* -------------------------------------------------------------------- */
     179             :     /*      Prepare a blank data buffer to write for uninitialized blocks.  */
     180             :     /* -------------------------------------------------------------------- */
     181             :     const GPtrDiff_t nBlockBytes =
     182        7828 :         TIFFIsTiled(m_hTIFF) ? static_cast<GPtrDiff_t>(TIFFTileSize(m_hTIFF))
     183        6792 :                              : static_cast<GPtrDiff_t>(TIFFStripSize(m_hTIFF));
     184             : 
     185        7828 :     GByte *pabyData = static_cast<GByte *>(VSI_CALLOC_VERBOSE(nBlockBytes, 1));
     186        7828 :     if (pabyData == nullptr)
     187             :     {
     188           0 :         return CE_Failure;
     189             :     }
     190             : 
     191             :     // Force tiles completely filled with the nodata value to be written.
     192        7828 :     m_bWriteEmptyTiles = true;
     193             : 
     194             :     /* -------------------------------------------------------------------- */
     195             :     /*      If set, fill data buffer with no data value.                    */
     196             :     /* -------------------------------------------------------------------- */
     197        7828 :     if ((m_bNoDataSet && m_dfNoDataValue != 0.0) ||
     198        7583 :         (m_bNoDataSetAsInt64 && m_nNoDataValueInt64 != 0) ||
     199        7580 :         (m_bNoDataSetAsUInt64 && m_nNoDataValueUInt64 != 0))
     200             :     {
     201         251 :         const GDALDataType eDataType = GetRasterBand(1)->GetRasterDataType();
     202         251 :         const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
     203         251 :         if (nDataTypeSize &&
     204         251 :             nDataTypeSize * 8 == static_cast<int>(m_nBitsPerSample))
     205             :         {
     206         240 :             if (m_bNoDataSetAsInt64)
     207             :             {
     208           4 :                 GDALCopyWords64(&m_nNoDataValueInt64, GDT_Int64, 0, pabyData,
     209             :                                 eDataType, nDataTypeSize,
     210           4 :                                 nBlockBytes / nDataTypeSize);
     211             :             }
     212         236 :             else if (m_bNoDataSetAsUInt64)
     213             :             {
     214           3 :                 GDALCopyWords64(&m_nNoDataValueUInt64, GDT_UInt64, 0, pabyData,
     215             :                                 eDataType, nDataTypeSize,
     216           3 :                                 nBlockBytes / nDataTypeSize);
     217             :             }
     218             :             else
     219             :             {
     220         233 :                 double dfNoData = m_dfNoDataValue;
     221         233 :                 GDALCopyWords64(&dfNoData, GDT_Float64, 0, pabyData, eDataType,
     222         233 :                                 nDataTypeSize, nBlockBytes / nDataTypeSize);
     223         240 :             }
     224             :         }
     225          11 :         else if (nDataTypeSize)
     226             :         {
     227             :             // Handle non power-of-two depths.
     228             :             // Ideally make a packed buffer, but that is a bit tedious,
     229             :             // so use the normal I/O interfaces.
     230             : 
     231          11 :             CPLFree(pabyData);
     232             : 
     233          11 :             pabyData = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
     234             :                 m_nBlockXSize, m_nBlockYSize, nDataTypeSize));
     235          11 :             if (pabyData == nullptr)
     236           0 :                 return CE_Failure;
     237          11 :             if (m_bNoDataSetAsInt64)
     238             :             {
     239           0 :                 GDALCopyWords64(&m_nNoDataValueInt64, GDT_Int64, 0, pabyData,
     240             :                                 eDataType, nDataTypeSize,
     241           0 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     242           0 :                                     m_nBlockYSize);
     243             :             }
     244          11 :             else if (m_bNoDataSetAsUInt64)
     245             :             {
     246           0 :                 GDALCopyWords64(&m_nNoDataValueUInt64, GDT_UInt64, 0, pabyData,
     247             :                                 eDataType, nDataTypeSize,
     248           0 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     249           0 :                                     m_nBlockYSize);
     250             :             }
     251             :             else
     252             :             {
     253          11 :                 GDALCopyWords64(&m_dfNoDataValue, GDT_Float64, 0, pabyData,
     254             :                                 eDataType, nDataTypeSize,
     255          11 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     256          11 :                                     m_nBlockYSize);
     257             :             }
     258          11 :             CPLErr eErr = CE_None;
     259          46 :             for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     260             :             {
     261          35 :                 if (panByteCounts[iBlock] == 0)
     262             :                 {
     263          18 :                     if (m_nPlanarConfig == PLANARCONFIG_SEPARATE || nBands == 1)
     264             :                     {
     265          24 :                         if (GetRasterBand(1 + iBlock / m_nBlocksPerBand)
     266          12 :                                 ->WriteBlock((iBlock % m_nBlocksPerBand) %
     267          12 :                                                  m_nBlocksPerRow,
     268          12 :                                              (iBlock % m_nBlocksPerBand) /
     269          12 :                                                  m_nBlocksPerRow,
     270          12 :                                              pabyData) != CE_None)
     271             :                         {
     272           0 :                             eErr = CE_Failure;
     273             :                         }
     274             :                     }
     275             :                     else
     276             :                     {
     277             :                         // In contig case, don't directly call WriteBlock(), as
     278             :                         // it could cause useless decompression-recompression.
     279           6 :                         const int nXOff =
     280           6 :                             (iBlock % m_nBlocksPerRow) * m_nBlockXSize;
     281           6 :                         const int nYOff =
     282           6 :                             (iBlock / m_nBlocksPerRow) * m_nBlockYSize;
     283           6 :                         const int nXSize =
     284           6 :                             (nXOff + m_nBlockXSize <= nRasterXSize)
     285           6 :                                 ? m_nBlockXSize
     286           2 :                                 : nRasterXSize - nXOff;
     287           6 :                         const int nYSize =
     288           6 :                             (nYOff + m_nBlockYSize <= nRasterYSize)
     289           6 :                                 ? m_nBlockYSize
     290           3 :                                 : nRasterYSize - nYOff;
     291          18 :                         for (int iBand = 1; iBand <= nBands; ++iBand)
     292             :                         {
     293          12 :                             if (GetRasterBand(iBand)->RasterIO(
     294             :                                     GF_Write, nXOff, nYOff, nXSize, nYSize,
     295             :                                     pabyData, nXSize, nYSize, eDataType, 0, 0,
     296          12 :                                     nullptr) != CE_None)
     297             :                             {
     298           0 :                                 eErr = CE_Failure;
     299             :                             }
     300             :                         }
     301             :                     }
     302             :                 }
     303             :             }
     304          11 :             CPLFree(pabyData);
     305          11 :             return eErr;
     306         240 :         }
     307             :     }
     308             : 
     309             :     /* -------------------------------------------------------------------- */
     310             :     /*      When we must fill with zeroes, try to create non-sparse file    */
     311             :     /*      w.r.t TIFF spec ... as a sparse file w.r.t filesystem, ie by    */
     312             :     /*      seeking to end of file instead of writing zero blocks.          */
     313             :     /* -------------------------------------------------------------------- */
     314        7577 :     else if (m_nCompression == COMPRESSION_NONE && (m_nBitsPerSample % 8) == 0)
     315             :     {
     316        6127 :         CPLErr eErr = CE_None;
     317             :         // Only use libtiff to write the first sparse block to ensure that it
     318             :         // will serialize offset and count arrays back to disk.
     319        6127 :         int nCountBlocksToZero = 0;
     320     2319140 :         for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     321             :         {
     322     2313020 :             if (panByteCounts[iBlock] == 0)
     323             :             {
     324     2218820 :                 if (nCountBlocksToZero == 0)
     325             :                 {
     326        1085 :                     const bool bWriteEmptyTilesBak = m_bWriteEmptyTiles;
     327        1085 :                     m_bWriteEmptyTiles = true;
     328        1085 :                     const bool bOK = WriteEncodedTileOrStrip(iBlock, pabyData,
     329        1085 :                                                              FALSE) == CE_None;
     330        1085 :                     m_bWriteEmptyTiles = bWriteEmptyTilesBak;
     331        1085 :                     if (!bOK)
     332             :                     {
     333           2 :                         eErr = CE_Failure;
     334           2 :                         break;
     335             :                     }
     336             :                 }
     337     2218810 :                 nCountBlocksToZero++;
     338             :             }
     339             :         }
     340        6127 :         CPLFree(pabyData);
     341             : 
     342        6127 :         --nCountBlocksToZero;
     343             : 
     344             :         // And then seek to end of file for other ones.
     345        6127 :         if (nCountBlocksToZero > 0)
     346             :         {
     347         328 :             toff_t *panByteOffsets = nullptr;
     348             : 
     349         328 :             if (TIFFIsTiled(m_hTIFF))
     350          92 :                 TIFFGetField(m_hTIFF, TIFFTAG_TILEOFFSETS, &panByteOffsets);
     351             :             else
     352         236 :                 TIFFGetField(m_hTIFF, TIFFTAG_STRIPOFFSETS, &panByteOffsets);
     353             : 
     354         328 :             if (panByteOffsets == nullptr)
     355             :             {
     356           0 :                 ReportError(
     357             :                     CE_Failure, CPLE_AppDefined,
     358             :                     "FillEmptyTiles() failed because panByteOffsets == NULL");
     359           0 :                 return CE_Failure;
     360             :             }
     361             : 
     362         328 :             VSILFILE *fpTIF = VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
     363         328 :             VSIFSeekL(fpTIF, 0, SEEK_END);
     364         328 :             const vsi_l_offset nOffset = VSIFTellL(fpTIF);
     365             : 
     366         328 :             vsi_l_offset iBlockToZero = 0;
     367     2226920 :             for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     368             :             {
     369     2226590 :                 if (panByteCounts[iBlock] == 0)
     370             :                 {
     371     2217730 :                     panByteOffsets[iBlock] = static_cast<toff_t>(
     372     2217730 :                         nOffset + iBlockToZero * nBlockBytes);
     373     2217730 :                     panByteCounts[iBlock] = nBlockBytes;
     374     2217730 :                     iBlockToZero++;
     375             :                 }
     376             :             }
     377         328 :             CPLAssert(iBlockToZero ==
     378             :                       static_cast<vsi_l_offset>(nCountBlocksToZero));
     379             : 
     380         328 :             if (VSIFTruncateL(fpTIF, nOffset + iBlockToZero * nBlockBytes) != 0)
     381             :             {
     382           0 :                 eErr = CE_Failure;
     383           0 :                 ReportError(CE_Failure, CPLE_FileIO,
     384             :                             "Cannot initialize empty blocks");
     385             :             }
     386             :         }
     387             : 
     388        6127 :         return eErr;
     389             :     }
     390             : 
     391             :     /* -------------------------------------------------------------------- */
     392             :     /*      Check all blocks, writing out data for uninitialized blocks.    */
     393             :     /* -------------------------------------------------------------------- */
     394             : 
     395        1690 :     GByte *pabyRaw = nullptr;
     396        1690 :     vsi_l_offset nRawSize = 0;
     397        1690 :     CPLErr eErr = CE_None;
     398       45521 :     for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     399             :     {
     400       43838 :         if (panByteCounts[iBlock] == 0)
     401             :         {
     402        8941 :             if (pabyRaw == nullptr)
     403             :             {
     404        1638 :                 if (WriteEncodedTileOrStrip(iBlock, pabyData, FALSE) != CE_None)
     405             :                 {
     406           7 :                     eErr = CE_Failure;
     407           7 :                     break;
     408             :                 }
     409             : 
     410        1631 :                 vsi_l_offset nOffset = 0;
     411        1631 :                 if (!IsBlockAvailable(iBlock, &nOffset, &nRawSize, nullptr))
     412           0 :                     break;
     413             : 
     414             :                 // When using compression, get back the compressed block
     415             :                 // so we can use the raw API to write it faster.
     416        1631 :                 if (m_nCompression != COMPRESSION_NONE)
     417             :                 {
     418             :                     pabyRaw = static_cast<GByte *>(
     419         427 :                         VSI_MALLOC_VERBOSE(static_cast<size_t>(nRawSize)));
     420         427 :                     if (pabyRaw)
     421             :                     {
     422             :                         VSILFILE *fp =
     423         427 :                             VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
     424         427 :                         const vsi_l_offset nCurOffset = VSIFTellL(fp);
     425         427 :                         VSIFSeekL(fp, nOffset, SEEK_SET);
     426         427 :                         VSIFReadL(pabyRaw, 1, static_cast<size_t>(nRawSize),
     427             :                                   fp);
     428         427 :                         VSIFSeekL(fp, nCurOffset, SEEK_SET);
     429             :                     }
     430             :                 }
     431             :             }
     432             :             else
     433             :             {
     434        7303 :                 WriteRawStripOrTile(iBlock, pabyRaw,
     435             :                                     static_cast<GPtrDiff_t>(nRawSize));
     436             :             }
     437             :         }
     438             :     }
     439             : 
     440        1690 :     CPLFree(pabyData);
     441        1690 :     VSIFree(pabyRaw);
     442        1690 :     return eErr;
     443             : }
     444             : 
     445             : /************************************************************************/
     446             : /*                         HasOnlyNoData()                              */
     447             : /************************************************************************/
     448             : 
     449       42265 : bool GTiffDataset::HasOnlyNoData(const void *pBuffer, int nWidth, int nHeight,
     450             :                                  int nLineStride, int nComponents)
     451             : {
     452       42265 :     if (m_nSampleFormat == SAMPLEFORMAT_COMPLEXINT ||
     453       42265 :         m_nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP)
     454           0 :         return false;
     455       42265 :     if (m_bNoDataSetAsInt64 || m_bNoDataSetAsUInt64)
     456           2 :         return false;  // FIXME: over pessimistic
     457       84526 :     return GDALBufferHasOnlyNoData(
     458       42263 :         pBuffer, m_bNoDataSet ? m_dfNoDataValue : 0.0, nWidth, nHeight,
     459       42263 :         nLineStride, nComponents, m_nBitsPerSample,
     460       42263 :         m_nSampleFormat == SAMPLEFORMAT_UINT  ? GSF_UNSIGNED_INT
     461        4746 :         : m_nSampleFormat == SAMPLEFORMAT_INT ? GSF_SIGNED_INT
     462       42263 :                                               : GSF_FLOATING_POINT);
     463             : }
     464             : 
     465             : /************************************************************************/
     466             : /*                     IsFirstPixelEqualToNoData()                      */
     467             : /************************************************************************/
     468             : 
     469      167934 : inline bool GTiffDataset::IsFirstPixelEqualToNoData(const void *pBuffer)
     470             : {
     471      167934 :     const GDALDataType eDT = GetRasterBand(1)->GetRasterDataType();
     472      167930 :     const double dfEffectiveNoData = (m_bNoDataSet) ? m_dfNoDataValue : 0.0;
     473      167930 :     if (m_bNoDataSetAsInt64 || m_bNoDataSetAsUInt64)
     474           3 :         return true;  // FIXME: over pessimistic
     475      167927 :     if (m_nBitsPerSample == 8 ||
     476       58507 :         (m_nBitsPerSample < 8 && dfEffectiveNoData == 0))
     477             :     {
     478      112869 :         if (eDT == GDT_Int8)
     479             :         {
     480         270 :             return GDALIsValueInRange<signed char>(dfEffectiveNoData) &&
     481         135 :                    *(static_cast<const signed char *>(pBuffer)) ==
     482         270 :                        static_cast<signed char>(dfEffectiveNoData);
     483             :         }
     484      225437 :         return GDALIsValueInRange<GByte>(dfEffectiveNoData) &&
     485      112703 :                *(static_cast<const GByte *>(pBuffer)) ==
     486      225437 :                    static_cast<GByte>(dfEffectiveNoData);
     487             :     }
     488       55058 :     if (m_nBitsPerSample == 16 && eDT == GDT_UInt16)
     489             :     {
     490        4170 :         return GDALIsValueInRange<GUInt16>(dfEffectiveNoData) &&
     491        2085 :                *(static_cast<const GUInt16 *>(pBuffer)) ==
     492        4170 :                    static_cast<GUInt16>(dfEffectiveNoData);
     493             :     }
     494       52973 :     if (m_nBitsPerSample == 16 && eDT == GDT_Int16)
     495             :     {
     496        8432 :         return GDALIsValueInRange<GInt16>(dfEffectiveNoData) &&
     497        4216 :                *(static_cast<const GInt16 *>(pBuffer)) ==
     498        8432 :                    static_cast<GInt16>(dfEffectiveNoData);
     499             :     }
     500       48757 :     if (m_nBitsPerSample == 32 && eDT == GDT_UInt32)
     501             :     {
     502         370 :         return GDALIsValueInRange<GUInt32>(dfEffectiveNoData) &&
     503         185 :                *(static_cast<const GUInt32 *>(pBuffer)) ==
     504         370 :                    static_cast<GUInt32>(dfEffectiveNoData);
     505             :     }
     506       48572 :     if (m_nBitsPerSample == 32 && eDT == GDT_Int32)
     507             :     {
     508         498 :         return GDALIsValueInRange<GInt32>(dfEffectiveNoData) &&
     509         249 :                *(static_cast<const GInt32 *>(pBuffer)) ==
     510         498 :                    static_cast<GInt32>(dfEffectiveNoData);
     511             :     }
     512       48323 :     if (m_nBitsPerSample == 64 && eDT == GDT_UInt64)
     513             :     {
     514         234 :         return GDALIsValueInRange<std::uint64_t>(dfEffectiveNoData) &&
     515         117 :                *(static_cast<const std::uint64_t *>(pBuffer)) ==
     516         234 :                    static_cast<std::uint64_t>(dfEffectiveNoData);
     517             :     }
     518       48206 :     if (m_nBitsPerSample == 64 && eDT == GDT_Int64)
     519             :     {
     520         236 :         return GDALIsValueInRange<std::int64_t>(dfEffectiveNoData) &&
     521         118 :                *(static_cast<const std::int64_t *>(pBuffer)) ==
     522         236 :                    static_cast<std::int64_t>(dfEffectiveNoData);
     523             :     }
     524       48088 :     if (m_nBitsPerSample == 32 && eDT == GDT_Float32)
     525             :     {
     526       41201 :         if (std::isnan(m_dfNoDataValue))
     527           3 :             return CPL_TO_BOOL(
     528           6 :                 std::isnan(*(static_cast<const float *>(pBuffer))));
     529       82382 :         return GDALIsValueInRange<float>(dfEffectiveNoData) &&
     530       41186 :                *(static_cast<const float *>(pBuffer)) ==
     531       82369 :                    static_cast<float>(dfEffectiveNoData);
     532             :     }
     533        6887 :     if (m_nBitsPerSample == 64 && eDT == GDT_Float64)
     534             :     {
     535        4239 :         if (std::isnan(dfEffectiveNoData))
     536           0 :             return CPL_TO_BOOL(
     537           0 :                 std::isnan(*(static_cast<const double *>(pBuffer))));
     538        4239 :         return *(static_cast<const double *>(pBuffer)) == dfEffectiveNoData;
     539             :     }
     540        2648 :     return false;
     541             : }
     542             : 
     543             : /************************************************************************/
     544             : /*                      WriteDealWithLercAndNan()                       */
     545             : /************************************************************************/
     546             : 
     547             : template <typename T>
     548           0 : void GTiffDataset::WriteDealWithLercAndNan(T *pBuffer, int nActualBlockWidth,
     549             :                                            int nActualBlockHeight,
     550             :                                            int nStrileHeight)
     551             : {
     552             :     // This method does 2 things:
     553             :     // - warn the user if he tries to write NaN values with libtiff < 4.6.1
     554             :     //   and multi-band PlanarConfig=Contig configuration
     555             :     // - and in right-most and bottom-most tiles, replace non accessible
     556             :     //   pixel values by a safe one.
     557             : 
     558           0 :     const auto fPaddingValue =
     559             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     560             :         m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1
     561             :             ? 0
     562             :             :
     563             : #endif
     564             :             std::numeric_limits<T>::quiet_NaN();
     565             : 
     566           0 :     const int nBandsPerStrile =
     567           0 :         m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     568           0 :     for (int j = 0; j < nActualBlockHeight; ++j)
     569             :     {
     570             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     571             :         static bool bHasWarned = false;
     572             :         if (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1 && !bHasWarned)
     573             :         {
     574             :             for (int i = 0; i < nActualBlockWidth * nBandsPerStrile; ++i)
     575             :             {
     576             :                 if (std::isnan(
     577             :                         pBuffer[j * m_nBlockXSize * nBandsPerStrile + i]))
     578             :                 {
     579             :                     bHasWarned = true;
     580             :                     CPLError(CE_Warning, CPLE_AppDefined,
     581             :                              "libtiff < 4.6.1 does not handle properly NaN "
     582             :                              "values for multi-band PlanarConfig=Contig "
     583             :                              "configuration. As a workaround, you can set the "
     584             :                              "INTERLEAVE=BAND creation option.");
     585             :                     break;
     586             :                 }
     587             :             }
     588             :         }
     589             : #endif
     590           0 :         for (int i = nActualBlockWidth * nBandsPerStrile;
     591           0 :              i < m_nBlockXSize * nBandsPerStrile; ++i)
     592             :         {
     593           0 :             pBuffer[j * m_nBlockXSize * nBandsPerStrile + i] = fPaddingValue;
     594             :         }
     595             :     }
     596           0 :     for (int j = nActualBlockHeight; j < nStrileHeight; ++j)
     597             :     {
     598           0 :         for (int i = 0; i < m_nBlockXSize * nBandsPerStrile; ++i)
     599             :         {
     600           0 :             pBuffer[j * m_nBlockXSize * nBandsPerStrile + i] = fPaddingValue;
     601             :         }
     602             :     }
     603           0 : }
     604             : 
     605             : /************************************************************************/
     606             : /*                        WriteEncodedTile()                            */
     607             : /************************************************************************/
     608             : 
     609       50111 : bool GTiffDataset::WriteEncodedTile(uint32_t tile, GByte *pabyData,
     610             :                                     int bPreserveDataBuffer)
     611             : {
     612       50111 :     const int iColumn = (tile % m_nBlocksPerBand) % m_nBlocksPerRow;
     613       50111 :     const int iRow = (tile % m_nBlocksPerBand) / m_nBlocksPerRow;
     614             : 
     615      100222 :     const int nActualBlockWidth = (iColumn == m_nBlocksPerRow - 1)
     616       50111 :                                       ? nRasterXSize - iColumn * m_nBlockXSize
     617             :                                       : m_nBlockXSize;
     618      100222 :     const int nActualBlockHeight = (iRow == m_nBlocksPerColumn - 1)
     619       50111 :                                        ? nRasterYSize - iRow * m_nBlockYSize
     620             :                                        : m_nBlockYSize;
     621             : 
     622             :     /* -------------------------------------------------------------------- */
     623             :     /*      Don't write empty blocks in some cases.                         */
     624             :     /* -------------------------------------------------------------------- */
     625       50111 :     if (!m_bWriteEmptyTiles && IsFirstPixelEqualToNoData(pabyData))
     626             :     {
     627        1919 :         if (!IsBlockAvailable(tile, nullptr, nullptr, nullptr))
     628             :         {
     629        1919 :             const int nComponents =
     630        1919 :                 m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     631             : 
     632        1919 :             if (HasOnlyNoData(pabyData, nActualBlockWidth, nActualBlockHeight,
     633             :                               m_nBlockXSize, nComponents))
     634             :             {
     635        1170 :                 return true;
     636             :             }
     637             :         }
     638             :     }
     639             : 
     640             :     // Is this a partial right edge or bottom edge tile?
     641       94997 :     const bool bPartialTile = (nActualBlockWidth < m_nBlockXSize) ||
     642       46056 :                               (nActualBlockHeight < m_nBlockYSize);
     643             : 
     644             :     const bool bIsLercFloatingPoint =
     645       49007 :         m_nCompression == COMPRESSION_LERC &&
     646          66 :         (GetRasterBand(1)->GetRasterDataType() == GDT_Float32 ||
     647          64 :          GetRasterBand(1)->GetRasterDataType() == GDT_Float64);
     648             : 
     649             :     // Do we need to spread edge values right or down for a partial
     650             :     // JPEG encoded tile?  We do this to avoid edge artifacts.
     651             :     // We also need to be careful with LERC and NaN values
     652       48941 :     const bool bNeedTempBuffer =
     653       53468 :         bPartialTile &&
     654        4527 :         (m_nCompression == COMPRESSION_JPEG || bIsLercFloatingPoint);
     655             : 
     656             :     // If we need to fill out the tile, or if we want to prevent
     657             :     // TIFFWriteEncodedTile from altering the buffer as part of
     658             :     // byte swapping the data on write then we will need a temporary
     659             :     // working buffer.  If not, we can just do a direct write.
     660       48941 :     const GPtrDiff_t cc = static_cast<GPtrDiff_t>(TIFFTileSize(m_hTIFF));
     661             : 
     662       63200 :     if (bPreserveDataBuffer &&
     663       14286 :         (TIFFIsByteSwapped(m_hTIFF) || bNeedTempBuffer || m_panMaskOffsetLsb))
     664             :     {
     665         158 :         if (m_pabyTempWriteBuffer == nullptr)
     666             :         {
     667          35 :             m_pabyTempWriteBuffer = CPLMalloc(cc);
     668             :         }
     669         158 :         memcpy(m_pabyTempWriteBuffer, pabyData, cc);
     670             : 
     671         158 :         pabyData = static_cast<GByte *>(m_pabyTempWriteBuffer);
     672             :     }
     673             : 
     674             :     // Perform tile fill if needed.
     675             :     // TODO: we should also handle the case of nBitsPerSample == 12
     676             :     // but this is more involved.
     677       48914 :     if (bPartialTile && m_nCompression == COMPRESSION_JPEG &&
     678         134 :         m_nBitsPerSample == 8)
     679             :     {
     680         132 :         const int nComponents =
     681         132 :             m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     682             : 
     683         132 :         CPLDebug("GTiff", "Filling out jpeg edge tile on write.");
     684             : 
     685         132 :         const int nRightPixelsToFill =
     686         132 :             iColumn == m_nBlocksPerRow - 1
     687         132 :                 ? m_nBlockXSize * (iColumn + 1) - nRasterXSize
     688             :                 : 0;
     689         132 :         const int nBottomPixelsToFill =
     690         132 :             iRow == m_nBlocksPerColumn - 1
     691         132 :                 ? m_nBlockYSize * (iRow + 1) - nRasterYSize
     692             :                 : 0;
     693             : 
     694             :         // Fill out to the right.
     695         132 :         const int iSrcX = m_nBlockXSize - nRightPixelsToFill - 1;
     696             : 
     697       12461 :         for (int iX = iSrcX + 1; iX < m_nBlockXSize; ++iX)
     698             :         {
     699     3955880 :             for (int iY = 0; iY < m_nBlockYSize; ++iY)
     700             :             {
     701     3943550 :                 memcpy(pabyData +
     702     3943550 :                            (static_cast<GPtrDiff_t>(m_nBlockXSize) * iY + iX) *
     703     3943550 :                                nComponents,
     704     3943550 :                        pabyData + (static_cast<GPtrDiff_t>(m_nBlockXSize) * iY +
     705     3943550 :                                    iSrcX) *
     706     3943550 :                                       nComponents,
     707             :                        nComponents);
     708             :             }
     709             :         }
     710             : 
     711             :         // Now fill out the bottom.
     712         132 :         const int iSrcY = m_nBlockYSize - nBottomPixelsToFill - 1;
     713       17682 :         for (int iY = iSrcY + 1; iY < m_nBlockYSize; ++iY)
     714             :         {
     715       17550 :             memcpy(pabyData + static_cast<GPtrDiff_t>(m_nBlockXSize) *
     716       17550 :                                   nComponents * iY,
     717       17550 :                    pabyData + static_cast<GPtrDiff_t>(m_nBlockXSize) *
     718       17550 :                                   nComponents * iSrcY,
     719       17550 :                    static_cast<GPtrDiff_t>(m_nBlockXSize) * nComponents);
     720             :         }
     721             :     }
     722             : 
     723       48914 :     if (bIsLercFloatingPoint &&
     724             :         (bPartialTile
     725             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     726             :          /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
     727             :          || (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1)
     728             : #endif
     729             :              ))
     730             :     {
     731           0 :         if (GetRasterBand(1)->GetRasterDataType() == GDT_Float32)
     732           0 :             WriteDealWithLercAndNan(reinterpret_cast<float *>(pabyData),
     733             :                                     nActualBlockWidth, nActualBlockHeight,
     734             :                                     m_nBlockYSize);
     735             :         else
     736           0 :             WriteDealWithLercAndNan(reinterpret_cast<double *>(pabyData),
     737             :                                     nActualBlockWidth, nActualBlockHeight,
     738             :                                     m_nBlockYSize);
     739             :     }
     740             : 
     741       48914 :     if (m_panMaskOffsetLsb)
     742             :     {
     743           0 :         const int iBand = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     744           0 :                               ? static_cast<int>(tile) / m_nBlocksPerBand
     745             :                               : -1;
     746           0 :         DiscardLsb(pabyData, cc, iBand);
     747             :     }
     748             : 
     749       48933 :     if (m_bStreamingOut)
     750             :     {
     751          17 :         if (tile != static_cast<uint32_t>(m_nLastWrittenBlockId + 1))
     752             :         {
     753           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     754             :                         "Attempt to write block %d whereas %d was expected",
     755           1 :                         tile, m_nLastWrittenBlockId + 1);
     756           1 :             return false;
     757             :         }
     758          16 :         if (static_cast<GPtrDiff_t>(VSIFWriteL(pabyData, 1, cc, m_fpToWrite)) !=
     759             :             cc)
     760             :         {
     761           0 :             ReportError(CE_Failure, CPLE_FileIO,
     762             :                         "Could not write " CPL_FRMT_GUIB " bytes",
     763             :                         static_cast<GUIntBig>(cc));
     764           0 :             return false;
     765             :         }
     766          16 :         m_nLastWrittenBlockId = tile;
     767          16 :         return true;
     768             :     }
     769             : 
     770             :     /* -------------------------------------------------------------------- */
     771             :     /*      Should we do compression in a worker thread ?                   */
     772             :     /* -------------------------------------------------------------------- */
     773       48916 :     if (SubmitCompressionJob(tile, pabyData, cc, m_nBlockYSize))
     774       19784 :         return true;
     775             : 
     776       29114 :     return TIFFWriteEncodedTile(m_hTIFF, tile, pabyData, cc) == cc;
     777             : }
     778             : 
     779             : /************************************************************************/
     780             : /*                        WriteEncodedStrip()                           */
     781             : /************************************************************************/
     782             : 
     783      166532 : bool GTiffDataset::WriteEncodedStrip(uint32_t strip, GByte *pabyData,
     784             :                                      int bPreserveDataBuffer)
     785             : {
     786      166532 :     GPtrDiff_t cc = static_cast<GPtrDiff_t>(TIFFStripSize(m_hTIFF));
     787      166529 :     const auto ccFull = cc;
     788             : 
     789             :     /* -------------------------------------------------------------------- */
     790             :     /*      If this is the last strip in the image, and is partial, then    */
     791             :     /*      we need to trim the number of scanlines written to the          */
     792             :     /*      amount of valid data we have. (#2748)                           */
     793             :     /* -------------------------------------------------------------------- */
     794      166529 :     const int nStripWithinBand = strip % m_nBlocksPerBand;
     795      166529 :     int nStripHeight = m_nRowsPerStrip;
     796             : 
     797      166529 :     if (nStripWithinBand * nStripHeight > GetRasterYSize() - nStripHeight)
     798             :     {
     799         379 :         nStripHeight = GetRasterYSize() - nStripWithinBand * m_nRowsPerStrip;
     800         379 :         cc = (cc / m_nRowsPerStrip) * nStripHeight;
     801         758 :         CPLDebug("GTiff",
     802             :                  "Adjusted bytes to write from " CPL_FRMT_GUIB
     803             :                  " to " CPL_FRMT_GUIB ".",
     804         379 :                  static_cast<GUIntBig>(TIFFStripSize(m_hTIFF)),
     805             :                  static_cast<GUIntBig>(cc));
     806             :     }
     807             : 
     808             :     /* -------------------------------------------------------------------- */
     809             :     /*      Don't write empty blocks in some cases.                         */
     810             :     /* -------------------------------------------------------------------- */
     811      166542 :     if (!m_bWriteEmptyTiles && IsFirstPixelEqualToNoData(pabyData))
     812             :     {
     813       40516 :         if (!IsBlockAvailable(strip, nullptr, nullptr, nullptr))
     814             :         {
     815       40346 :             const int nComponents =
     816       40346 :                 m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     817             : 
     818       40346 :             if (HasOnlyNoData(pabyData, m_nBlockXSize, nStripHeight,
     819             :                               m_nBlockXSize, nComponents))
     820             :             {
     821       28300 :                 return true;
     822             :             }
     823             :         }
     824             :     }
     825             : 
     826             :     /* -------------------------------------------------------------------- */
     827             :     /*      TIFFWriteEncodedStrip can alter the passed buffer if            */
     828             :     /*      byte-swapping is necessary so we use a temporary buffer         */
     829             :     /*      before calling it.                                              */
     830             :     /* -------------------------------------------------------------------- */
     831      227464 :     if (bPreserveDataBuffer &&
     832       89236 :         (TIFFIsByteSwapped(m_hTIFF) || m_panMaskOffsetLsb))
     833             :     {
     834         294 :         if (m_pabyTempWriteBuffer == nullptr)
     835             :         {
     836         126 :             m_pabyTempWriteBuffer = CPLMalloc(ccFull);
     837             :         }
     838         294 :         memcpy(m_pabyTempWriteBuffer, pabyData, cc);
     839         294 :         pabyData = static_cast<GByte *>(m_pabyTempWriteBuffer);
     840             :     }
     841             : 
     842             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     843             :     const bool bIsLercFloatingPoint =
     844             :         m_nCompression == COMPRESSION_LERC &&
     845             :         (GetRasterBand(1)->GetRasterDataType() == GDT_Float32 ||
     846             :          GetRasterBand(1)->GetRasterDataType() == GDT_Float64);
     847             :     if (bIsLercFloatingPoint &&
     848             :         /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
     849             :         m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1)
     850             :     {
     851             :         if (GetRasterBand(1)->GetRasterDataType() == GDT_Float32)
     852             :             WriteDealWithLercAndNan(reinterpret_cast<float *>(pabyData),
     853             :                                     m_nBlockXSize, nStripHeight, nStripHeight);
     854             :         else
     855             :             WriteDealWithLercAndNan(reinterpret_cast<double *>(pabyData),
     856             :                                     m_nBlockXSize, nStripHeight, nStripHeight);
     857             :     }
     858             : #endif
     859             : 
     860      138228 :     if (m_panMaskOffsetLsb)
     861             :     {
     862         366 :         int iBand = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     863         183 :                         ? static_cast<int>(strip) / m_nBlocksPerBand
     864             :                         : -1;
     865         183 :         DiscardLsb(pabyData, cc, iBand);
     866             :     }
     867             : 
     868      138238 :     if (m_bStreamingOut)
     869             :     {
     870        1408 :         if (strip != static_cast<uint32_t>(m_nLastWrittenBlockId + 1))
     871             :         {
     872           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     873             :                         "Attempt to write block %d whereas %d was expected",
     874           1 :                         strip, m_nLastWrittenBlockId + 1);
     875           1 :             return false;
     876             :         }
     877        1407 :         if (static_cast<GPtrDiff_t>(VSIFWriteL(pabyData, 1, cc, m_fpToWrite)) !=
     878             :             cc)
     879             :         {
     880           0 :             ReportError(CE_Failure, CPLE_FileIO,
     881             :                         "Could not write " CPL_FRMT_GUIB " bytes",
     882             :                         static_cast<GUIntBig>(cc));
     883           0 :             return false;
     884             :         }
     885        1407 :         m_nLastWrittenBlockId = strip;
     886        1407 :         return true;
     887             :     }
     888             : 
     889             :     /* -------------------------------------------------------------------- */
     890             :     /*      Should we do compression in a worker thread ?                   */
     891             :     /* -------------------------------------------------------------------- */
     892      136830 :     if (SubmitCompressionJob(strip, pabyData, cc, nStripHeight))
     893        6727 :         return true;
     894             : 
     895      130086 :     return TIFFWriteEncodedStrip(m_hTIFF, strip, pabyData, cc) == cc;
     896             : }
     897             : 
     898             : /************************************************************************/
     899             : /*                        InitCompressionThreads()                      */
     900             : /************************************************************************/
     901             : 
     902       31061 : void GTiffDataset::InitCompressionThreads(bool bUpdateMode,
     903             :                                           CSLConstList papszOptions)
     904             : {
     905             :     // Raster == tile, then no need for threads
     906       31061 :     if (m_nBlockXSize == nRasterXSize && m_nBlockYSize == nRasterYSize)
     907       22742 :         return;
     908             : 
     909        8319 :     const char *pszValue = CSLFetchNameValue(papszOptions, "NUM_THREADS");
     910        8310 :     if (pszValue == nullptr)
     911        8248 :         pszValue = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
     912        8312 :     if (pszValue)
     913             :     {
     914             :         int nThreads =
     915         132 :             EQUAL(pszValue, "ALL_CPUS") ? CPLGetNumCPUs() : atoi(pszValue);
     916         132 :         if (nThreads > 1024)
     917           0 :             nThreads = 1024;  // to please Coverity
     918         132 :         if (nThreads > 1)
     919             :         {
     920          92 :             if ((bUpdateMode && m_nCompression != COMPRESSION_NONE) ||
     921          17 :                 (nBands >= 1 && IsMultiThreadedReadCompatible()))
     922             :             {
     923          70 :                 CPLDebug("GTiff",
     924             :                          "Using up to %d threads for compression/decompression",
     925             :                          nThreads);
     926             : 
     927          70 :                 m_poThreadPool = GDALGetGlobalThreadPool(nThreads);
     928          70 :                 if (bUpdateMode && m_poThreadPool)
     929          58 :                     m_poCompressQueue = m_poThreadPool->CreateJobQueue();
     930             : 
     931          70 :                 if (m_poCompressQueue != nullptr)
     932             :                 {
     933             :                     // Add a margin of an extra job w.r.t thread number
     934             :                     // so as to optimize compression time (enables the main
     935             :                     // thread to do boring I/O while all CPUs are working).
     936          58 :                     m_asCompressionJobs.resize(nThreads + 1);
     937          58 :                     memset(&m_asCompressionJobs[0], 0,
     938          58 :                            m_asCompressionJobs.size() *
     939             :                                sizeof(GTiffCompressionJob));
     940          58 :                     for (int i = 0;
     941         280 :                          i < static_cast<int>(m_asCompressionJobs.size()); ++i)
     942             :                     {
     943         444 :                         m_asCompressionJobs[i].pszTmpFilename =
     944         222 :                             CPLStrdup(VSIMemGenerateHiddenFilename(
     945             :                                 CPLSPrintf("thread_job_%d.tif", i)));
     946         222 :                         m_asCompressionJobs[i].nStripOrTile = -1;
     947             :                     }
     948             : 
     949             :                     // This is kind of a hack, but basically using
     950             :                     // TIFFWriteRawStrip/Tile and then TIFFReadEncodedStrip/Tile
     951             :                     // does not work on a newly created file, because
     952             :                     // TIFF_MYBUFFER is not set in tif_flags
     953             :                     // (if using TIFFWriteEncodedStrip/Tile first,
     954             :                     // TIFFWriteBufferSetup() is automatically called).
     955             :                     // This should likely rather fixed in libtiff itself.
     956          58 :                     CPL_IGNORE_RET_VAL(
     957          58 :                         TIFFWriteBufferSetup(m_hTIFF, nullptr, -1));
     958             :                 }
     959             :             }
     960             :         }
     961          57 :         else if (nThreads < 0 ||
     962          57 :                  (!EQUAL(pszValue, "0") && !EQUAL(pszValue, "1") &&
     963           3 :                   !EQUAL(pszValue, "ALL_CPUS")))
     964             :         {
     965           3 :             ReportError(CE_Warning, CPLE_AppDefined,
     966             :                         "Invalid value for NUM_THREADS: %s", pszValue);
     967             :         }
     968             :     }
     969             : }
     970             : 
     971             : /************************************************************************/
     972             : /*                      ThreadCompressionFunc()                         */
     973             : /************************************************************************/
     974             : 
     975       26523 : void GTiffDataset::ThreadCompressionFunc(void *pData)
     976             : {
     977       26523 :     GTiffCompressionJob *psJob = static_cast<GTiffCompressionJob *>(pData);
     978       26523 :     GTiffDataset *poDS = psJob->poDS;
     979             : 
     980       26523 :     VSILFILE *fpTmp = VSIFOpenL(psJob->pszTmpFilename, "wb+");
     981       26523 :     TIFF *hTIFFTmp = VSI_TIFFOpen(
     982       53046 :         psJob->pszTmpFilename, psJob->bTIFFIsBigEndian ? "wb+" : "wl+", fpTmp);
     983       26521 :     CPLAssert(hTIFFTmp != nullptr);
     984       26521 :     TIFFSetField(hTIFFTmp, TIFFTAG_IMAGEWIDTH, poDS->m_nBlockXSize);
     985       26521 :     TIFFSetField(hTIFFTmp, TIFFTAG_IMAGELENGTH, psJob->nHeight);
     986       26522 :     TIFFSetField(hTIFFTmp, TIFFTAG_BITSPERSAMPLE, poDS->m_nBitsPerSample);
     987       26523 :     TIFFSetField(hTIFFTmp, TIFFTAG_COMPRESSION, poDS->m_nCompression);
     988       26523 :     TIFFSetField(hTIFFTmp, TIFFTAG_PHOTOMETRIC, poDS->m_nPhotometric);
     989       26523 :     TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLEFORMAT, poDS->m_nSampleFormat);
     990       26523 :     TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLESPERPIXEL, poDS->m_nSamplesPerPixel);
     991       26523 :     TIFFSetField(hTIFFTmp, TIFFTAG_ROWSPERSTRIP, poDS->m_nBlockYSize);
     992       26523 :     TIFFSetField(hTIFFTmp, TIFFTAG_PLANARCONFIG, poDS->m_nPlanarConfig);
     993       26523 :     if (psJob->nPredictor != PREDICTOR_NONE)
     994         262 :         TIFFSetField(hTIFFTmp, TIFFTAG_PREDICTOR, psJob->nPredictor);
     995       26523 :     if (poDS->m_nCompression == COMPRESSION_LERC)
     996             :     {
     997          24 :         TIFFSetField(hTIFFTmp, TIFFTAG_LERC_PARAMETERS, 2,
     998          24 :                      poDS->m_anLercAddCompressionAndVersion);
     999             :     }
    1000       26523 :     if (psJob->nExtraSampleCount)
    1001             :     {
    1002         336 :         TIFFSetField(hTIFFTmp, TIFFTAG_EXTRASAMPLES, psJob->nExtraSampleCount,
    1003             :                      psJob->pExtraSamples);
    1004             :     }
    1005             : 
    1006       26523 :     poDS->RestoreVolatileParameters(hTIFFTmp);
    1007             : 
    1008       53044 :     bool bOK = TIFFWriteEncodedStrip(hTIFFTmp, 0, psJob->pabyBuffer,
    1009       26523 :                                      psJob->nBufferSize) == psJob->nBufferSize;
    1010             : 
    1011       26521 :     toff_t nOffset = 0;
    1012       26521 :     if (bOK)
    1013             :     {
    1014       26521 :         toff_t *panOffsets = nullptr;
    1015       26521 :         toff_t *panByteCounts = nullptr;
    1016       26521 :         TIFFGetField(hTIFFTmp, TIFFTAG_STRIPOFFSETS, &panOffsets);
    1017       26520 :         TIFFGetField(hTIFFTmp, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
    1018             : 
    1019       26522 :         nOffset = panOffsets[0];
    1020       26522 :         psJob->nCompressedBufferSize =
    1021       26522 :             static_cast<GPtrDiff_t>(panByteCounts[0]);
    1022             :     }
    1023             :     else
    1024             :     {
    1025           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1026             :                  "Error when compressing strip/tile %d", psJob->nStripOrTile);
    1027             :     }
    1028             : 
    1029       26522 :     XTIFFClose(hTIFFTmp);
    1030       26522 :     if (VSIFCloseL(fpTmp) != 0)
    1031             :     {
    1032           0 :         if (bOK)
    1033             :         {
    1034           0 :             bOK = false;
    1035           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1036             :                      "Error when compressing strip/tile %d",
    1037             :                      psJob->nStripOrTile);
    1038             :         }
    1039             :     }
    1040             : 
    1041       26518 :     if (bOK)
    1042             :     {
    1043       26518 :         vsi_l_offset nFileSize = 0;
    1044             :         GByte *pabyCompressedBuffer =
    1045       26518 :             VSIGetMemFileBuffer(psJob->pszTmpFilename, &nFileSize, FALSE);
    1046       26523 :         CPLAssert(static_cast<vsi_l_offset>(
    1047             :                       nOffset + psJob->nCompressedBufferSize) <= nFileSize);
    1048       26523 :         psJob->pabyCompressedBuffer = pabyCompressedBuffer + nOffset;
    1049             :     }
    1050             :     else
    1051             :     {
    1052           0 :         psJob->pabyCompressedBuffer = nullptr;
    1053           0 :         psJob->nCompressedBufferSize = 0;
    1054             :     }
    1055             : 
    1056       26523 :     auto poMainDS = poDS->m_poBaseDS ? poDS->m_poBaseDS : poDS;
    1057       26523 :     if (poMainDS->m_poCompressQueue)
    1058             :     {
    1059        1576 :         std::lock_guard oLock(poMainDS->m_oCompressThreadPoolMutex);
    1060        1576 :         psJob->bReady = true;
    1061             :     }
    1062       26523 : }
    1063             : 
    1064             : /************************************************************************/
    1065             : /*                        WriteRawStripOrTile()                         */
    1066             : /************************************************************************/
    1067             : 
    1068       33826 : void GTiffDataset::WriteRawStripOrTile(int nStripOrTile,
    1069             :                                        GByte *pabyCompressedBuffer,
    1070             :                                        GPtrDiff_t nCompressedBufferSize)
    1071             : {
    1072             : #ifdef DEBUG_VERBOSE
    1073             :     CPLDebug("GTIFF", "Writing raw strip/tile %d, size " CPL_FRMT_GUIB,
    1074             :              nStripOrTile, static_cast<GUIntBig>(nCompressedBufferSize));
    1075             : #endif
    1076       33826 :     toff_t *panOffsets = nullptr;
    1077       33826 :     toff_t *panByteCounts = nullptr;
    1078       33826 :     bool bWriteAtEnd = true;
    1079       33826 :     bool bWriteLeader = m_bLeaderSizeAsUInt4;
    1080       33826 :     bool bWriteTrailer = m_bTrailerRepeatedLast4BytesRepeated;
    1081       33826 :     if (TIFFGetField(m_hTIFF,
    1082       33826 :                      TIFFIsTiled(m_hTIFF) ? TIFFTAG_TILEOFFSETS
    1083             :                                           : TIFFTAG_STRIPOFFSETS,
    1084       33826 :                      &panOffsets) &&
    1085       33826 :         panOffsets != nullptr && panOffsets[nStripOrTile] != 0)
    1086             :     {
    1087             :         // Forces TIFFAppendStrip() to consider if the location of the
    1088             :         // tile/strip can be reused or if the strile should be written at end of
    1089             :         // file.
    1090         360 :         TIFFSetWriteOffset(m_hTIFF, 0);
    1091             : 
    1092         360 :         if (m_bBlockOrderRowMajor)
    1093             :         {
    1094         264 :             if (TIFFGetField(m_hTIFF,
    1095         264 :                              TIFFIsTiled(m_hTIFF) ? TIFFTAG_TILEBYTECOUNTS
    1096             :                                                   : TIFFTAG_STRIPBYTECOUNTS,
    1097         528 :                              &panByteCounts) &&
    1098         264 :                 panByteCounts != nullptr)
    1099             :             {
    1100         264 :                 if (static_cast<GUIntBig>(nCompressedBufferSize) >
    1101         264 :                     panByteCounts[nStripOrTile])
    1102             :                 {
    1103           8 :                     GTiffDataset *poRootDS = m_poBaseDS ? m_poBaseDS : this;
    1104           8 :                     if (!poRootDS->m_bKnownIncompatibleEdition &&
    1105           8 :                         !poRootDS->m_bWriteKnownIncompatibleEdition)
    1106             :                     {
    1107           8 :                         ReportError(
    1108             :                             CE_Warning, CPLE_AppDefined,
    1109             :                             "A strile cannot be rewritten in place, which "
    1110             :                             "invalidates the BLOCK_ORDER optimization.");
    1111           8 :                         poRootDS->m_bKnownIncompatibleEdition = true;
    1112           8 :                         poRootDS->m_bWriteKnownIncompatibleEdition = true;
    1113             :                     }
    1114             :                 }
    1115             :                 // For mask interleaving, if the size is not exactly the same,
    1116             :                 // completely give up (we could potentially move the mask in
    1117             :                 // case the imagery is smaller)
    1118         256 :                 else if (m_poMaskDS && m_bMaskInterleavedWithImagery &&
    1119           0 :                          static_cast<GUIntBig>(nCompressedBufferSize) !=
    1120           0 :                              panByteCounts[nStripOrTile])
    1121             :                 {
    1122           0 :                     GTiffDataset *poRootDS = m_poBaseDS ? m_poBaseDS : this;
    1123           0 :                     if (!poRootDS->m_bKnownIncompatibleEdition &&
    1124           0 :                         !poRootDS->m_bWriteKnownIncompatibleEdition)
    1125             :                     {
    1126           0 :                         ReportError(
    1127             :                             CE_Warning, CPLE_AppDefined,
    1128             :                             "A strile cannot be rewritten in place, which "
    1129             :                             "invalidates the MASK_INTERLEAVED_WITH_IMAGERY "
    1130             :                             "optimization.");
    1131           0 :                         poRootDS->m_bKnownIncompatibleEdition = true;
    1132           0 :                         poRootDS->m_bWriteKnownIncompatibleEdition = true;
    1133             :                     }
    1134           0 :                     bWriteLeader = false;
    1135           0 :                     bWriteTrailer = false;
    1136           0 :                     if (m_bLeaderSizeAsUInt4)
    1137             :                     {
    1138             :                         // If there was a valid leader, invalidat it
    1139           0 :                         VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4,
    1140             :                                      SEEK_SET);
    1141             :                         uint32_t nOldSize;
    1142           0 :                         VSIFReadL(&nOldSize, 1, 4,
    1143             :                                   VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF)));
    1144           0 :                         CPL_LSBPTR32(&nOldSize);
    1145           0 :                         if (nOldSize == panByteCounts[nStripOrTile])
    1146             :                         {
    1147           0 :                             uint32_t nInvalidatedSize = 0;
    1148           0 :                             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4,
    1149             :                                          SEEK_SET);
    1150           0 :                             VSI_TIFFWrite(m_hTIFF, &nInvalidatedSize,
    1151             :                                           sizeof(nInvalidatedSize));
    1152             :                         }
    1153           0 :                     }
    1154             :                 }
    1155             :                 else
    1156             :                 {
    1157         256 :                     bWriteAtEnd = false;
    1158             :                 }
    1159             :             }
    1160             :         }
    1161             :     }
    1162       33826 :     if (bWriteLeader &&
    1163       24952 :         static_cast<GUIntBig>(nCompressedBufferSize) <= 0xFFFFFFFFU)
    1164             :     {
    1165             :         // cppcheck-suppress knownConditionTrueFalse
    1166       24952 :         if (bWriteAtEnd)
    1167             :         {
    1168       24696 :             VSI_TIFFSeek(m_hTIFF, 0, SEEK_END);
    1169             :         }
    1170             :         else
    1171             :         {
    1172             :             // If we rewrite an existing strile in place with an existing
    1173             :             // leader, check that the leader is valid, before rewriting it. And
    1174             :             // if it is not valid, then do not write the trailer, as we could
    1175             :             // corrupt other data.
    1176         256 :             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4, SEEK_SET);
    1177             :             uint32_t nOldSize;
    1178         256 :             VSIFReadL(&nOldSize, 1, 4,
    1179             :                       VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF)));
    1180         256 :             CPL_LSBPTR32(&nOldSize);
    1181         256 :             bWriteLeader =
    1182         256 :                 panByteCounts && nOldSize == panByteCounts[nStripOrTile];
    1183         256 :             bWriteTrailer = bWriteLeader;
    1184         256 :             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4, SEEK_SET);
    1185             :         }
    1186             :         // cppcheck-suppress knownConditionTrueFalse
    1187       24952 :         if (bWriteLeader)
    1188             :         {
    1189       24952 :             uint32_t nSize = static_cast<uint32_t>(nCompressedBufferSize);
    1190       24952 :             CPL_LSBPTR32(&nSize);
    1191       24952 :             if (!VSI_TIFFWrite(m_hTIFF, &nSize, sizeof(nSize)))
    1192           0 :                 m_bWriteError = true;
    1193             :         }
    1194             :     }
    1195             :     tmsize_t written;
    1196       33826 :     if (TIFFIsTiled(m_hTIFF))
    1197       26173 :         written = TIFFWriteRawTile(m_hTIFF, nStripOrTile, pabyCompressedBuffer,
    1198             :                                    nCompressedBufferSize);
    1199             :     else
    1200        7653 :         written = TIFFWriteRawStrip(m_hTIFF, nStripOrTile, pabyCompressedBuffer,
    1201             :                                     nCompressedBufferSize);
    1202       33826 :     if (written != nCompressedBufferSize)
    1203          12 :         m_bWriteError = true;
    1204       33826 :     if (bWriteTrailer &&
    1205       24952 :         static_cast<GUIntBig>(nCompressedBufferSize) <= 0xFFFFFFFFU)
    1206             :     {
    1207       24952 :         GByte abyLastBytes[4] = {};
    1208       24952 :         if (nCompressedBufferSize >= 4)
    1209       24952 :             memcpy(abyLastBytes,
    1210       24952 :                    pabyCompressedBuffer + nCompressedBufferSize - 4, 4);
    1211             :         else
    1212           0 :             memcpy(abyLastBytes, pabyCompressedBuffer, nCompressedBufferSize);
    1213       24952 :         if (!VSI_TIFFWrite(m_hTIFF, abyLastBytes, 4))
    1214           0 :             m_bWriteError = true;
    1215             :     }
    1216       33826 : }
    1217             : 
    1218             : /************************************************************************/
    1219             : /*                        WaitCompletionForJobIdx()                     */
    1220             : /************************************************************************/
    1221             : 
    1222        1576 : void GTiffDataset::WaitCompletionForJobIdx(int i)
    1223             : {
    1224        1576 :     auto poMainDS = m_poBaseDS ? m_poBaseDS : this;
    1225        1576 :     auto poQueue = poMainDS->m_poCompressQueue.get();
    1226        1576 :     auto &oQueue = poMainDS->m_asQueueJobIdx;
    1227        1576 :     auto &asJobs = poMainDS->m_asCompressionJobs;
    1228        1576 :     auto &mutex = poMainDS->m_oCompressThreadPoolMutex;
    1229             : 
    1230        1576 :     CPLAssert(i >= 0 && static_cast<size_t>(i) < asJobs.size());
    1231        1576 :     CPLAssert(asJobs[i].nStripOrTile >= 0);
    1232        1576 :     CPLAssert(!oQueue.empty());
    1233             : 
    1234        1576 :     bool bHasWarned = false;
    1235             :     while (true)
    1236             :     {
    1237             :         bool bReady;
    1238             :         {
    1239        2340 :             std::lock_guard oLock(mutex);
    1240        2340 :             bReady = asJobs[i].bReady;
    1241             :         }
    1242        2340 :         if (!bReady)
    1243             :         {
    1244         764 :             if (!bHasWarned)
    1245             :             {
    1246         460 :                 CPLDebug("GTIFF",
    1247             :                          "Waiting for worker job to finish handling block %d",
    1248         460 :                          asJobs[i].nStripOrTile);
    1249         460 :                 bHasWarned = true;
    1250             :             }
    1251         764 :             poQueue->GetPool()->WaitEvent();
    1252             :         }
    1253             :         else
    1254             :         {
    1255        1576 :             break;
    1256             :         }
    1257         764 :     }
    1258             : 
    1259        1576 :     if (asJobs[i].nCompressedBufferSize)
    1260             :     {
    1261        3152 :         asJobs[i].poDS->WriteRawStripOrTile(asJobs[i].nStripOrTile,
    1262        1576 :                                             asJobs[i].pabyCompressedBuffer,
    1263        1576 :                                             asJobs[i].nCompressedBufferSize);
    1264             :     }
    1265        1576 :     asJobs[i].pabyCompressedBuffer = nullptr;
    1266        1576 :     asJobs[i].nBufferSize = 0;
    1267             :     {
    1268             :         // Likely useless, but makes Coverity happy
    1269        1576 :         std::lock_guard oLock(mutex);
    1270        1576 :         asJobs[i].bReady = false;
    1271             :     }
    1272        1576 :     asJobs[i].nStripOrTile = -1;
    1273        1576 :     oQueue.pop();
    1274        1576 : }
    1275             : 
    1276             : /************************************************************************/
    1277             : /*                        WaitCompletionForBlock()                      */
    1278             : /************************************************************************/
    1279             : 
    1280     2301500 : void GTiffDataset::WaitCompletionForBlock(int nBlockId)
    1281             : {
    1282     2301500 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    1283     2282380 :                               : m_poCompressQueue.get();
    1284             :     // cppcheck-suppress constVariableReference
    1285     2301480 :     auto &oQueue = m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    1286             :     // cppcheck-suppress constVariableReference
    1287     2282360 :     auto &asJobs =
    1288     2301480 :         m_poBaseDS ? m_poBaseDS->m_asCompressionJobs : m_asCompressionJobs;
    1289             : 
    1290     2301480 :     if (poQueue != nullptr && !oQueue.empty())
    1291             :     {
    1292        1066 :         for (int i = 0; i < static_cast<int>(asJobs.size()); ++i)
    1293             :         {
    1294         888 :             if (asJobs[i].poDS == this && asJobs[i].nStripOrTile == nBlockId)
    1295             :             {
    1296         128 :                 while (!oQueue.empty() &&
    1297          64 :                        !(asJobs[oQueue.front()].poDS == this &&
    1298          64 :                          asJobs[oQueue.front()].nStripOrTile == nBlockId))
    1299             :                 {
    1300           0 :                     WaitCompletionForJobIdx(oQueue.front());
    1301             :                 }
    1302          64 :                 CPLAssert(!oQueue.empty() &&
    1303             :                           asJobs[oQueue.front()].poDS == this &&
    1304             :                           asJobs[oQueue.front()].nStripOrTile == nBlockId);
    1305          64 :                 WaitCompletionForJobIdx(oQueue.front());
    1306             :             }
    1307             :         }
    1308             :     }
    1309     2301480 : }
    1310             : 
    1311             : /************************************************************************/
    1312             : /*                      SubmitCompressionJob()                          */
    1313             : /************************************************************************/
    1314             : 
    1315      185748 : bool GTiffDataset::SubmitCompressionJob(int nStripOrTile, GByte *pabyData,
    1316             :                                         GPtrDiff_t cc, int nHeight)
    1317             : {
    1318             :     /* -------------------------------------------------------------------- */
    1319             :     /*      Should we do compression in a worker thread ?                   */
    1320             :     /* -------------------------------------------------------------------- */
    1321      185748 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    1322      171784 :                               : m_poCompressQueue.get();
    1323             : 
    1324      185710 :     if (poQueue && m_nCompression == COMPRESSION_NONE)
    1325             :     {
    1326             :         // We don't do multi-threaded compression for uncompressed...
    1327             :         // but we must wait for other related compression tasks (e.g mask)
    1328             :         // to be completed
    1329           0 :         poQueue->WaitCompletion();
    1330             : 
    1331             :         // Flush remaining data
    1332             :         // cppcheck-suppress constVariableReference
    1333           0 :         auto &oQueue =
    1334           0 :             m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    1335           0 :         while (!oQueue.empty())
    1336             :         {
    1337           0 :             WaitCompletionForJobIdx(oQueue.front());
    1338             :         }
    1339             :     }
    1340             : 
    1341             :     const auto SetupJob =
    1342      122782 :         [this, pabyData, cc, nHeight, nStripOrTile](GTiffCompressionJob &sJob)
    1343             :     {
    1344       26523 :         sJob.poDS = this;
    1345       26523 :         sJob.bTIFFIsBigEndian = CPL_TO_BOOL(TIFFIsBigEndian(m_hTIFF));
    1346             :         GByte *pabyBuffer =
    1347       26523 :             static_cast<GByte *>(VSI_REALLOC_VERBOSE(sJob.pabyBuffer, cc));
    1348       26523 :         if (!pabyBuffer)
    1349           0 :             return false;
    1350       26523 :         sJob.pabyBuffer = pabyBuffer;
    1351       26523 :         memcpy(sJob.pabyBuffer, pabyData, cc);
    1352       26523 :         sJob.nBufferSize = cc;
    1353       26523 :         sJob.nHeight = nHeight;
    1354       26523 :         sJob.nStripOrTile = nStripOrTile;
    1355       26523 :         sJob.nPredictor = PREDICTOR_NONE;
    1356       26523 :         if (GTIFFSupportsPredictor(m_nCompression))
    1357             :         {
    1358       16690 :             TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &sJob.nPredictor);
    1359             :         }
    1360             : 
    1361       26523 :         sJob.pExtraSamples = nullptr;
    1362       26523 :         sJob.nExtraSampleCount = 0;
    1363       26523 :         TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &sJob.nExtraSampleCount,
    1364             :                      &sJob.pExtraSamples);
    1365       26523 :         return true;
    1366      185710 :     };
    1367             : 
    1368      185710 :     if (poQueue == nullptr || !(m_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    1369         806 :                                 m_nCompression == COMPRESSION_LZW ||
    1370          78 :                                 m_nCompression == COMPRESSION_PACKBITS ||
    1371          72 :                                 m_nCompression == COMPRESSION_LZMA ||
    1372          62 :                                 m_nCompression == COMPRESSION_ZSTD ||
    1373          52 :                                 m_nCompression == COMPRESSION_LERC ||
    1374          46 :                                 m_nCompression == COMPRESSION_JXL ||
    1375          46 :                                 m_nCompression == COMPRESSION_JXL_DNG_1_7 ||
    1376          28 :                                 m_nCompression == COMPRESSION_WEBP ||
    1377          18 :                                 m_nCompression == COMPRESSION_JPEG))
    1378             :     {
    1379      184134 :         if (m_bBlockOrderRowMajor || m_bLeaderSizeAsUInt4 ||
    1380      159199 :             m_bTrailerRepeatedLast4BytesRepeated)
    1381             :         {
    1382             :             GTiffCompressionJob sJob;
    1383       24936 :             memset(&sJob, 0, sizeof(sJob));
    1384       24936 :             if (SetupJob(sJob))
    1385             :             {
    1386       24947 :                 sJob.pszTmpFilename =
    1387       24947 :                     CPLStrdup(VSIMemGenerateHiddenFilename("temp.tif"));
    1388             : 
    1389       24947 :                 ThreadCompressionFunc(&sJob);
    1390             : 
    1391       24947 :                 if (sJob.nCompressedBufferSize)
    1392             :                 {
    1393       24947 :                     sJob.poDS->WriteRawStripOrTile(sJob.nStripOrTile,
    1394             :                                                    sJob.pabyCompressedBuffer,
    1395             :                                                    sJob.nCompressedBufferSize);
    1396             :                 }
    1397             : 
    1398       24947 :                 CPLFree(sJob.pabyBuffer);
    1399       24947 :                 VSIUnlink(sJob.pszTmpFilename);
    1400       24947 :                 CPLFree(sJob.pszTmpFilename);
    1401       24947 :                 return sJob.nCompressedBufferSize > 0 && !m_bWriteError;
    1402             :             }
    1403             :         }
    1404             : 
    1405      159198 :         return false;
    1406             :     }
    1407             : 
    1408        1576 :     auto poMainDS = m_poBaseDS ? m_poBaseDS : this;
    1409        1576 :     auto &oQueue = poMainDS->m_asQueueJobIdx;
    1410        1576 :     auto &asJobs = poMainDS->m_asCompressionJobs;
    1411             : 
    1412        1576 :     int nNextCompressionJobAvail = -1;
    1413             : 
    1414        1576 :     if (oQueue.size() == asJobs.size())
    1415             :     {
    1416        1443 :         CPLAssert(!oQueue.empty());
    1417        1443 :         nNextCompressionJobAvail = oQueue.front();
    1418        1443 :         WaitCompletionForJobIdx(nNextCompressionJobAvail);
    1419             :     }
    1420             :     else
    1421             :     {
    1422         133 :         const int nJobs = static_cast<int>(asJobs.size());
    1423         324 :         for (int i = 0; i < nJobs; ++i)
    1424             :         {
    1425         324 :             if (asJobs[i].nBufferSize == 0)
    1426             :             {
    1427         133 :                 nNextCompressionJobAvail = i;
    1428         133 :                 break;
    1429             :             }
    1430             :         }
    1431             :     }
    1432        1576 :     CPLAssert(nNextCompressionJobAvail >= 0);
    1433             : 
    1434        1576 :     GTiffCompressionJob *psJob = &asJobs[nNextCompressionJobAvail];
    1435        1576 :     bool bOK = SetupJob(*psJob);
    1436        1576 :     if (bOK)
    1437             :     {
    1438        1576 :         poQueue->SubmitJob(ThreadCompressionFunc, psJob);
    1439        1576 :         oQueue.push(nNextCompressionJobAvail);
    1440             :     }
    1441             : 
    1442        1576 :     return bOK;
    1443             : }
    1444             : 
    1445             : /************************************************************************/
    1446             : /*                          DiscardLsb()                                */
    1447             : /************************************************************************/
    1448             : 
    1449         272 : template <class T> bool MustNotDiscardLsb(T value, bool bHasNoData, T nodata)
    1450             : {
    1451         272 :     return bHasNoData && value == nodata;
    1452             : }
    1453             : 
    1454             : template <>
    1455          44 : bool MustNotDiscardLsb<float>(float value, bool bHasNoData, float nodata)
    1456             : {
    1457          44 :     return (bHasNoData && value == nodata) || !std::isfinite(value);
    1458             : }
    1459             : 
    1460             : template <>
    1461          44 : bool MustNotDiscardLsb<double>(double value, bool bHasNoData, double nodata)
    1462             : {
    1463          44 :     return (bHasNoData && value == nodata) || !std::isfinite(value);
    1464             : }
    1465             : 
    1466             : template <class T> T AdjustValue(T value, uint64_t nRoundUpBitTest);
    1467             : 
    1468          10 : template <class T> T AdjustValueInt(T value, uint64_t nRoundUpBitTest)
    1469             : {
    1470          10 :     if (value >=
    1471          10 :         static_cast<T>(std::numeric_limits<T>::max() - (nRoundUpBitTest << 1)))
    1472           0 :         return static_cast<T>(value - (nRoundUpBitTest << 1));
    1473          10 :     return static_cast<T>(value + (nRoundUpBitTest << 1));
    1474             : }
    1475             : 
    1476           0 : template <> int8_t AdjustValue<int8_t>(int8_t value, uint64_t nRoundUpBitTest)
    1477             : {
    1478           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1479             : }
    1480             : 
    1481             : template <>
    1482           2 : uint8_t AdjustValue<uint8_t>(uint8_t value, uint64_t nRoundUpBitTest)
    1483             : {
    1484           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1485             : }
    1486             : 
    1487             : template <>
    1488           2 : int16_t AdjustValue<int16_t>(int16_t value, uint64_t nRoundUpBitTest)
    1489             : {
    1490           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1491             : }
    1492             : 
    1493             : template <>
    1494           2 : uint16_t AdjustValue<uint16_t>(uint16_t value, uint64_t nRoundUpBitTest)
    1495             : {
    1496           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1497             : }
    1498             : 
    1499             : template <>
    1500           2 : int32_t AdjustValue<int32_t>(int32_t value, uint64_t nRoundUpBitTest)
    1501             : {
    1502           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1503             : }
    1504             : 
    1505             : template <>
    1506           2 : uint32_t AdjustValue<uint32_t>(uint32_t value, uint64_t nRoundUpBitTest)
    1507             : {
    1508           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1509             : }
    1510             : 
    1511             : template <>
    1512           0 : int64_t AdjustValue<int64_t>(int64_t value, uint64_t nRoundUpBitTest)
    1513             : {
    1514           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1515             : }
    1516             : 
    1517             : template <>
    1518           0 : uint64_t AdjustValue<uint64_t>(uint64_t value, uint64_t nRoundUpBitTest)
    1519             : {
    1520           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1521             : }
    1522             : 
    1523           0 : template <> GFloat16 AdjustValue<GFloat16>(GFloat16 value, uint64_t)
    1524             : {
    1525             :     using std::nextafter;
    1526           0 :     return nextafter(value, cpl::NumericLimits<GFloat16>::max());
    1527             : }
    1528             : 
    1529           0 : template <> float AdjustValue<float>(float value, uint64_t)
    1530             : {
    1531           0 :     return std::nextafter(value, std::numeric_limits<float>::max());
    1532             : }
    1533             : 
    1534           0 : template <> double AdjustValue<double>(double value, uint64_t)
    1535             : {
    1536           0 :     return std::nextafter(value, std::numeric_limits<double>::max());
    1537             : }
    1538             : 
    1539             : template <class Teffective, class T>
    1540             : T RoundValueDiscardLsb(const void *ptr, uint64_t nMask,
    1541             :                        uint64_t nRoundUpBitTest);
    1542             : 
    1543             : template <class T>
    1544          16 : T RoundValueDiscardLsbUnsigned(const void *ptr, uint64_t nMask,
    1545             :                                uint64_t nRoundUpBitTest)
    1546             : {
    1547          32 :     if ((*reinterpret_cast<const T *>(ptr) & nMask) >
    1548          16 :         static_cast<uint64_t>(std::numeric_limits<T>::max()) -
    1549          16 :             (nRoundUpBitTest << 1U))
    1550             :     {
    1551           4 :         return static_cast<T>(std::numeric_limits<T>::max() & nMask);
    1552             :     }
    1553          12 :     const uint64_t newval =
    1554          12 :         (*reinterpret_cast<const T *>(ptr) & nMask) + (nRoundUpBitTest << 1U);
    1555          12 :     return static_cast<T>(newval);
    1556             : }
    1557             : 
    1558             : template <class T>
    1559          18 : T RoundValueDiscardLsbSigned(const void *ptr, uint64_t nMask,
    1560             :                              uint64_t nRoundUpBitTest)
    1561             : {
    1562          18 :     T oldval = *reinterpret_cast<const T *>(ptr);
    1563          18 :     if (oldval < 0)
    1564             :     {
    1565           4 :         return static_cast<T>(oldval & nMask);
    1566             :     }
    1567          14 :     const uint64_t newval =
    1568          14 :         (*reinterpret_cast<const T *>(ptr) & nMask) + (nRoundUpBitTest << 1U);
    1569          14 :     if (newval > static_cast<uint64_t>(std::numeric_limits<T>::max()))
    1570           4 :         return static_cast<T>(std::numeric_limits<T>::max() & nMask);
    1571          10 :     return static_cast<T>(newval);
    1572             : }
    1573             : 
    1574             : template <>
    1575          11 : uint16_t RoundValueDiscardLsb<uint16_t, uint16_t>(const void *ptr,
    1576             :                                                   uint64_t nMask,
    1577             :                                                   uint64_t nRoundUpBitTest)
    1578             : {
    1579          11 :     return RoundValueDiscardLsbUnsigned<uint16_t>(ptr, nMask, nRoundUpBitTest);
    1580             : }
    1581             : 
    1582             : template <>
    1583           5 : uint32_t RoundValueDiscardLsb<uint32_t, uint32_t>(const void *ptr,
    1584             :                                                   uint64_t nMask,
    1585             :                                                   uint64_t nRoundUpBitTest)
    1586             : {
    1587           5 :     return RoundValueDiscardLsbUnsigned<uint32_t>(ptr, nMask, nRoundUpBitTest);
    1588             : }
    1589             : 
    1590             : template <>
    1591           0 : uint64_t RoundValueDiscardLsb<uint64_t, uint64_t>(const void *ptr,
    1592             :                                                   uint64_t nMask,
    1593             :                                                   uint64_t nRoundUpBitTest)
    1594             : {
    1595           0 :     return RoundValueDiscardLsbUnsigned<uint64_t>(ptr, nMask, nRoundUpBitTest);
    1596             : }
    1597             : 
    1598             : template <>
    1599           0 : int8_t RoundValueDiscardLsb<int8_t, int8_t>(const void *ptr, uint64_t nMask,
    1600             :                                             uint64_t nRoundUpBitTest)
    1601             : {
    1602           0 :     return RoundValueDiscardLsbSigned<int8_t>(ptr, nMask, nRoundUpBitTest);
    1603             : }
    1604             : 
    1605             : template <>
    1606          13 : int16_t RoundValueDiscardLsb<int16_t, int16_t>(const void *ptr, uint64_t nMask,
    1607             :                                                uint64_t nRoundUpBitTest)
    1608             : {
    1609          13 :     return RoundValueDiscardLsbSigned<int16_t>(ptr, nMask, nRoundUpBitTest);
    1610             : }
    1611             : 
    1612             : template <>
    1613           5 : int32_t RoundValueDiscardLsb<int32_t, int32_t>(const void *ptr, uint64_t nMask,
    1614             :                                                uint64_t nRoundUpBitTest)
    1615             : {
    1616           5 :     return RoundValueDiscardLsbSigned<int32_t>(ptr, nMask, nRoundUpBitTest);
    1617             : }
    1618             : 
    1619             : template <>
    1620           0 : int64_t RoundValueDiscardLsb<int64_t, int64_t>(const void *ptr, uint64_t nMask,
    1621             :                                                uint64_t nRoundUpBitTest)
    1622             : {
    1623           0 :     return RoundValueDiscardLsbSigned<int64_t>(ptr, nMask, nRoundUpBitTest);
    1624             : }
    1625             : 
    1626             : template <>
    1627           0 : uint16_t RoundValueDiscardLsb<GFloat16, uint16_t>(const void *ptr,
    1628             :                                                   uint64_t nMask,
    1629             :                                                   uint64_t nRoundUpBitTest)
    1630             : {
    1631           0 :     return RoundValueDiscardLsbUnsigned<uint16_t>(ptr, nMask, nRoundUpBitTest);
    1632             : }
    1633             : 
    1634             : template <>
    1635           0 : uint32_t RoundValueDiscardLsb<float, uint32_t>(const void *ptr, uint64_t nMask,
    1636             :                                                uint64_t nRoundUpBitTest)
    1637             : {
    1638           0 :     return RoundValueDiscardLsbUnsigned<uint32_t>(ptr, nMask, nRoundUpBitTest);
    1639             : }
    1640             : 
    1641             : template <>
    1642           0 : uint64_t RoundValueDiscardLsb<double, uint64_t>(const void *ptr, uint64_t nMask,
    1643             :                                                 uint64_t nRoundUpBitTest)
    1644             : {
    1645           0 :     return RoundValueDiscardLsbUnsigned<uint64_t>(ptr, nMask, nRoundUpBitTest);
    1646             : }
    1647             : 
    1648             : template <class Teffective, class T>
    1649         145 : static void DiscardLsbT(GByte *pabyBuffer, size_t nBytes, int iBand, int nBands,
    1650             :                         uint16_t nPlanarConfig,
    1651             :                         const GTiffDataset::MaskOffset *panMaskOffsetLsb,
    1652             :                         bool bHasNoData, Teffective nNoDataValue)
    1653             : {
    1654             :     static_assert(sizeof(Teffective) == sizeof(T),
    1655             :                   "sizeof(Teffective) == sizeof(T)");
    1656         145 :     if (nPlanarConfig == PLANARCONFIG_SEPARATE)
    1657             :     {
    1658          98 :         const auto nMask = panMaskOffsetLsb[iBand].nMask;
    1659          98 :         const auto nRoundUpBitTest = panMaskOffsetLsb[iBand].nRoundUpBitTest;
    1660         196 :         for (size_t i = 0; i < nBytes / sizeof(T); ++i)
    1661             :         {
    1662          98 :             if (MustNotDiscardLsb(reinterpret_cast<Teffective *>(pabyBuffer)[i],
    1663             :                                   bHasNoData, nNoDataValue))
    1664             :             {
    1665          22 :                 continue;
    1666             :             }
    1667             : 
    1668          76 :             if (reinterpret_cast<T *>(pabyBuffer)[i] & nRoundUpBitTest)
    1669             :             {
    1670          30 :                 reinterpret_cast<T *>(pabyBuffer)[i] =
    1671          15 :                     RoundValueDiscardLsb<Teffective, T>(
    1672          15 :                         &(reinterpret_cast<T *>(pabyBuffer)[i]), nMask,
    1673             :                         nRoundUpBitTest);
    1674             :             }
    1675             :             else
    1676             :             {
    1677          61 :                 reinterpret_cast<T *>(pabyBuffer)[i] = static_cast<T>(
    1678          61 :                     reinterpret_cast<T *>(pabyBuffer)[i] & nMask);
    1679             :             }
    1680             : 
    1681             :             // Make sure that by discarding LSB we don't end up to a value
    1682             :             // that is no the nodata value
    1683          76 :             if (MustNotDiscardLsb(reinterpret_cast<Teffective *>(pabyBuffer)[i],
    1684             :                                   bHasNoData, nNoDataValue))
    1685             :             {
    1686           8 :                 reinterpret_cast<Teffective *>(pabyBuffer)[i] =
    1687           4 :                     AdjustValue(nNoDataValue, nRoundUpBitTest);
    1688             :             }
    1689             :         }
    1690             :     }
    1691             :     else
    1692             :     {
    1693          94 :         for (size_t i = 0; i < nBytes / sizeof(T); i += nBands)
    1694             :         {
    1695         147 :             for (int j = 0; j < nBands; ++j)
    1696             :             {
    1697         100 :                 if (MustNotDiscardLsb(
    1698         100 :                         reinterpret_cast<Teffective *>(pabyBuffer)[i + j],
    1699             :                         bHasNoData, nNoDataValue))
    1700             :                 {
    1701          14 :                     continue;
    1702             :                 }
    1703             : 
    1704          86 :                 if (reinterpret_cast<T *>(pabyBuffer)[i + j] &
    1705          86 :                     panMaskOffsetLsb[j].nRoundUpBitTest)
    1706             :                 {
    1707          38 :                     reinterpret_cast<T *>(pabyBuffer)[i + j] =
    1708          19 :                         RoundValueDiscardLsb<Teffective, T>(
    1709          19 :                             &(reinterpret_cast<T *>(pabyBuffer)[i + j]),
    1710          19 :                             panMaskOffsetLsb[j].nMask,
    1711          19 :                             panMaskOffsetLsb[j].nRoundUpBitTest);
    1712             :                 }
    1713             :                 else
    1714             :                 {
    1715          67 :                     reinterpret_cast<T *>(pabyBuffer)[i + j] = static_cast<T>(
    1716          67 :                         (reinterpret_cast<T *>(pabyBuffer)[i + j] &
    1717          67 :                          panMaskOffsetLsb[j].nMask));
    1718             :                 }
    1719             : 
    1720             :                 // Make sure that by discarding LSB we don't end up to a value
    1721             :                 // that is no the nodata value
    1722          86 :                 if (MustNotDiscardLsb(
    1723          86 :                         reinterpret_cast<Teffective *>(pabyBuffer)[i + j],
    1724             :                         bHasNoData, nNoDataValue))
    1725             :                 {
    1726           8 :                     reinterpret_cast<Teffective *>(pabyBuffer)[i + j] =
    1727           4 :                         AdjustValue(nNoDataValue,
    1728           4 :                                     panMaskOffsetLsb[j].nRoundUpBitTest);
    1729             :                 }
    1730             :             }
    1731             :         }
    1732             :     }
    1733         145 : }
    1734             : 
    1735         183 : static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand,
    1736             :                        int nBands, uint16_t nSampleFormat,
    1737             :                        uint16_t nBitsPerSample, uint16_t nPlanarConfig,
    1738             :                        const GTiffDataset::MaskOffset *panMaskOffsetLsb,
    1739             :                        bool bHasNoData, double dfNoDataValue)
    1740             : {
    1741         183 :     if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_UINT)
    1742             :     {
    1743          38 :         uint8_t nNoDataValue = 0;
    1744          38 :         if (bHasNoData && GDALIsValueExactAs<uint8_t>(dfNoDataValue))
    1745             :         {
    1746           6 :             nNoDataValue = static_cast<uint8_t>(dfNoDataValue);
    1747             :         }
    1748             :         else
    1749             :         {
    1750          32 :             bHasNoData = false;
    1751             :         }
    1752          38 :         if (nPlanarConfig == PLANARCONFIG_SEPARATE)
    1753             :         {
    1754          25 :             const auto nMask =
    1755          25 :                 static_cast<unsigned>(panMaskOffsetLsb[iBand].nMask);
    1756          25 :             const auto nRoundUpBitTest =
    1757          25 :                 static_cast<unsigned>(panMaskOffsetLsb[iBand].nRoundUpBitTest);
    1758          50 :             for (decltype(nBytes) i = 0; i < nBytes; ++i)
    1759             :             {
    1760          25 :                 if (bHasNoData && pabyBuffer[i] == nNoDataValue)
    1761           3 :                     continue;
    1762             : 
    1763             :                 // Keep 255 in case it is alpha.
    1764          22 :                 if (pabyBuffer[i] != 255)
    1765             :                 {
    1766          21 :                     if (pabyBuffer[i] & nRoundUpBitTest)
    1767           5 :                         pabyBuffer[i] = static_cast<GByte>(
    1768           5 :                             std::min(255U, (pabyBuffer[i] & nMask) +
    1769           5 :                                                (nRoundUpBitTest << 1U)));
    1770             :                     else
    1771          16 :                         pabyBuffer[i] =
    1772          16 :                             static_cast<GByte>(pabyBuffer[i] & nMask);
    1773             : 
    1774             :                     // Make sure that by discarding LSB we don't end up to a
    1775             :                     // value that is no the nodata value
    1776          21 :                     if (bHasNoData && pabyBuffer[i] == nNoDataValue)
    1777           2 :                         pabyBuffer[i] =
    1778           1 :                             AdjustValue(nNoDataValue, nRoundUpBitTest);
    1779             :                 }
    1780             :             }
    1781             :         }
    1782             :         else
    1783             :         {
    1784          26 :             for (decltype(nBytes) i = 0; i < nBytes; i += nBands)
    1785             :             {
    1786          42 :                 for (int j = 0; j < nBands; ++j)
    1787             :                 {
    1788          29 :                     if (bHasNoData && pabyBuffer[i + j] == nNoDataValue)
    1789           2 :                         continue;
    1790             : 
    1791             :                     // Keep 255 in case it is alpha.
    1792          27 :                     if (pabyBuffer[i + j] != 255)
    1793             :                     {
    1794          25 :                         if (pabyBuffer[i + j] &
    1795          25 :                             panMaskOffsetLsb[j].nRoundUpBitTest)
    1796             :                         {
    1797           6 :                             pabyBuffer[i + j] = static_cast<GByte>(std::min(
    1798          12 :                                 255U,
    1799           6 :                                 (pabyBuffer[i + j] &
    1800             :                                  static_cast<unsigned>(
    1801           6 :                                      panMaskOffsetLsb[j].nMask)) +
    1802             :                                     (static_cast<unsigned>(
    1803           6 :                                          panMaskOffsetLsb[j].nRoundUpBitTest)
    1804           6 :                                      << 1U)));
    1805             :                         }
    1806             :                         else
    1807             :                         {
    1808          19 :                             pabyBuffer[i + j] = static_cast<GByte>(
    1809          19 :                                 pabyBuffer[i + j] & panMaskOffsetLsb[j].nMask);
    1810             :                         }
    1811             : 
    1812             :                         // Make sure that by discarding LSB we don't end up to a
    1813             :                         // value that is no the nodata value
    1814          25 :                         if (bHasNoData && pabyBuffer[i + j] == nNoDataValue)
    1815           1 :                             pabyBuffer[i + j] = AdjustValue(
    1816             :                                 nNoDataValue,
    1817           1 :                                 panMaskOffsetLsb[j].nRoundUpBitTest);
    1818             :                     }
    1819             :                 }
    1820             :             }
    1821          38 :         }
    1822             :     }
    1823         145 :     else if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_INT)
    1824             :     {
    1825           0 :         int8_t nNoDataValue = 0;
    1826           0 :         if (bHasNoData && GDALIsValueExactAs<int8_t>(dfNoDataValue))
    1827             :         {
    1828           0 :             nNoDataValue = static_cast<int8_t>(dfNoDataValue);
    1829             :         }
    1830             :         else
    1831             :         {
    1832           0 :             bHasNoData = false;
    1833             :         }
    1834           0 :         DiscardLsbT<int8_t, int8_t>(pabyBuffer, nBytes, iBand, nBands,
    1835             :                                     nPlanarConfig, panMaskOffsetLsb, bHasNoData,
    1836           0 :                                     nNoDataValue);
    1837             :     }
    1838         145 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_INT)
    1839             :     {
    1840          48 :         int16_t nNoDataValue = 0;
    1841          48 :         if (bHasNoData && GDALIsValueExactAs<int16_t>(dfNoDataValue))
    1842             :         {
    1843           6 :             nNoDataValue = static_cast<int16_t>(dfNoDataValue);
    1844             :         }
    1845             :         else
    1846             :         {
    1847          42 :             bHasNoData = false;
    1848             :         }
    1849          48 :         DiscardLsbT<int16_t, int16_t>(pabyBuffer, nBytes, iBand, nBands,
    1850             :                                       nPlanarConfig, panMaskOffsetLsb,
    1851          48 :                                       bHasNoData, nNoDataValue);
    1852             :     }
    1853          97 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_UINT)
    1854             :     {
    1855          33 :         uint16_t nNoDataValue = 0;
    1856          33 :         if (bHasNoData && GDALIsValueExactAs<uint16_t>(dfNoDataValue))
    1857             :         {
    1858           6 :             nNoDataValue = static_cast<uint16_t>(dfNoDataValue);
    1859             :         }
    1860             :         else
    1861             :         {
    1862          27 :             bHasNoData = false;
    1863             :         }
    1864          33 :         DiscardLsbT<uint16_t, uint16_t>(pabyBuffer, nBytes, iBand, nBands,
    1865             :                                         nPlanarConfig, panMaskOffsetLsb,
    1866          33 :                                         bHasNoData, nNoDataValue);
    1867             :     }
    1868          64 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_INT)
    1869             :     {
    1870          13 :         int32_t nNoDataValue = 0;
    1871          13 :         if (bHasNoData && GDALIsValueExactAs<int32_t>(dfNoDataValue))
    1872             :         {
    1873           6 :             nNoDataValue = static_cast<int32_t>(dfNoDataValue);
    1874             :         }
    1875             :         else
    1876             :         {
    1877           7 :             bHasNoData = false;
    1878             :         }
    1879          13 :         DiscardLsbT<int32_t, int32_t>(pabyBuffer, nBytes, iBand, nBands,
    1880             :                                       nPlanarConfig, panMaskOffsetLsb,
    1881          13 :                                       bHasNoData, nNoDataValue);
    1882             :     }
    1883          51 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_UINT)
    1884             :     {
    1885          13 :         uint32_t nNoDataValue = 0;
    1886          13 :         if (bHasNoData && GDALIsValueExactAs<uint32_t>(dfNoDataValue))
    1887             :         {
    1888           6 :             nNoDataValue = static_cast<uint32_t>(dfNoDataValue);
    1889             :         }
    1890             :         else
    1891             :         {
    1892           7 :             bHasNoData = false;
    1893             :         }
    1894          13 :         DiscardLsbT<uint32_t, uint32_t>(pabyBuffer, nBytes, iBand, nBands,
    1895             :                                         nPlanarConfig, panMaskOffsetLsb,
    1896          13 :                                         bHasNoData, nNoDataValue);
    1897             :     }
    1898          38 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_INT)
    1899             :     {
    1900             :         // FIXME: we should not rely on dfNoDataValue when we support native
    1901             :         // data type for nodata
    1902           0 :         int64_t nNoDataValue = 0;
    1903           0 :         if (bHasNoData && GDALIsValueExactAs<int64_t>(dfNoDataValue))
    1904             :         {
    1905           0 :             nNoDataValue = static_cast<int64_t>(dfNoDataValue);
    1906             :         }
    1907             :         else
    1908             :         {
    1909           0 :             bHasNoData = false;
    1910             :         }
    1911           0 :         DiscardLsbT<int64_t, int64_t>(pabyBuffer, nBytes, iBand, nBands,
    1912             :                                       nPlanarConfig, panMaskOffsetLsb,
    1913           0 :                                       bHasNoData, nNoDataValue);
    1914             :     }
    1915          38 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_UINT)
    1916             :     {
    1917             :         // FIXME: we should not rely on dfNoDataValue when we support native
    1918             :         // data type for nodata
    1919           0 :         uint64_t nNoDataValue = 0;
    1920           0 :         if (bHasNoData && GDALIsValueExactAs<uint64_t>(dfNoDataValue))
    1921             :         {
    1922           0 :             nNoDataValue = static_cast<uint64_t>(dfNoDataValue);
    1923             :         }
    1924             :         else
    1925             :         {
    1926           0 :             bHasNoData = false;
    1927             :         }
    1928           0 :         DiscardLsbT<uint64_t, uint64_t>(pabyBuffer, nBytes, iBand, nBands,
    1929             :                                         nPlanarConfig, panMaskOffsetLsb,
    1930           0 :                                         bHasNoData, nNoDataValue);
    1931             :     }
    1932          38 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1933             :     {
    1934           0 :         const GFloat16 fNoDataValue = static_cast<GFloat16>(dfNoDataValue);
    1935           0 :         DiscardLsbT<GFloat16, uint16_t>(pabyBuffer, nBytes, iBand, nBands,
    1936             :                                         nPlanarConfig, panMaskOffsetLsb,
    1937           0 :                                         bHasNoData, fNoDataValue);
    1938             :     }
    1939          38 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1940             :     {
    1941          19 :         const float fNoDataValue = static_cast<float>(dfNoDataValue);
    1942          19 :         DiscardLsbT<float, uint32_t>(pabyBuffer, nBytes, iBand, nBands,
    1943             :                                      nPlanarConfig, panMaskOffsetLsb,
    1944          19 :                                      bHasNoData, fNoDataValue);
    1945             :     }
    1946          19 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1947             :     {
    1948          19 :         DiscardLsbT<double, uint64_t>(pabyBuffer, nBytes, iBand, nBands,
    1949             :                                       nPlanarConfig, panMaskOffsetLsb,
    1950             :                                       bHasNoData, dfNoDataValue);
    1951             :     }
    1952         183 : }
    1953             : 
    1954         183 : void GTiffDataset::DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes,
    1955             :                               int iBand) const
    1956             : {
    1957         183 :     ::DiscardLsb(pabyBuffer, nBytes, iBand, nBands, m_nSampleFormat,
    1958         183 :                  m_nBitsPerSample, m_nPlanarConfig, m_panMaskOffsetLsb,
    1959         183 :                  m_bNoDataSet, m_dfNoDataValue);
    1960         183 : }
    1961             : 
    1962             : /************************************************************************/
    1963             : /*                  WriteEncodedTileOrStrip()                           */
    1964             : /************************************************************************/
    1965             : 
    1966      216638 : CPLErr GTiffDataset::WriteEncodedTileOrStrip(uint32_t tile_or_strip, void *data,
    1967             :                                              int bPreserveDataBuffer)
    1968             : {
    1969      216638 :     CPLErr eErr = CE_None;
    1970             : 
    1971      216638 :     if (TIFFIsTiled(m_hTIFF))
    1972             :     {
    1973       50106 :         if (!(WriteEncodedTile(tile_or_strip, static_cast<GByte *>(data),
    1974             :                                bPreserveDataBuffer)))
    1975             :         {
    1976          14 :             eErr = CE_Failure;
    1977             :         }
    1978             :     }
    1979             :     else
    1980             :     {
    1981      166531 :         if (!(WriteEncodedStrip(tile_or_strip, static_cast<GByte *>(data),
    1982             :                                 bPreserveDataBuffer)))
    1983             :         {
    1984           8 :             eErr = CE_Failure;
    1985             :         }
    1986             :     }
    1987             : 
    1988      216640 :     return eErr;
    1989             : }
    1990             : 
    1991             : /************************************************************************/
    1992             : /*                           FlushBlockBuf()                            */
    1993             : /************************************************************************/
    1994             : 
    1995        9601 : CPLErr GTiffDataset::FlushBlockBuf()
    1996             : 
    1997             : {
    1998        9601 :     if (m_nLoadedBlock < 0 || !m_bLoadedBlockDirty)
    1999           0 :         return CE_None;
    2000             : 
    2001        9601 :     m_bLoadedBlockDirty = false;
    2002             : 
    2003             :     const CPLErr eErr =
    2004        9601 :         WriteEncodedTileOrStrip(m_nLoadedBlock, m_pabyBlockBuf, true);
    2005        9601 :     if (eErr != CE_None)
    2006             :     {
    2007           0 :         ReportError(CE_Failure, CPLE_AppDefined,
    2008             :                     "WriteEncodedTile/Strip() failed.");
    2009           0 :         m_bWriteError = true;
    2010             :     }
    2011             : 
    2012        9601 :     return eErr;
    2013             : }
    2014             : 
    2015             : /************************************************************************/
    2016             : /*                   GTiffFillStreamableOffsetAndCount()                */
    2017             : /************************************************************************/
    2018             : 
    2019           8 : static void GTiffFillStreamableOffsetAndCount(TIFF *hTIFF, int nSize)
    2020             : {
    2021           8 :     uint32_t nXSize = 0;
    2022           8 :     uint32_t nYSize = 0;
    2023           8 :     TIFFGetField(hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize);
    2024           8 :     TIFFGetField(hTIFF, TIFFTAG_IMAGELENGTH, &nYSize);
    2025           8 :     const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(hTIFF));
    2026             :     const int nBlockCount =
    2027           8 :         bIsTiled ? TIFFNumberOfTiles(hTIFF) : TIFFNumberOfStrips(hTIFF);
    2028             : 
    2029           8 :     toff_t *panOffset = nullptr;
    2030           8 :     TIFFGetField(hTIFF, bIsTiled ? TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS,
    2031             :                  &panOffset);
    2032           8 :     toff_t *panSize = nullptr;
    2033           8 :     TIFFGetField(hTIFF,
    2034             :                  bIsTiled ? TIFFTAG_TILEBYTECOUNTS : TIFFTAG_STRIPBYTECOUNTS,
    2035             :                  &panSize);
    2036           8 :     toff_t nOffset = nSize;
    2037             :     // Trick to avoid clang static analyzer raising false positive about
    2038             :     // divide by zero later.
    2039           8 :     int nBlocksPerBand = 1;
    2040           8 :     uint32_t nRowsPerStrip = 0;
    2041           8 :     if (!bIsTiled)
    2042             :     {
    2043           6 :         TIFFGetField(hTIFF, TIFFTAG_ROWSPERSTRIP, &nRowsPerStrip);
    2044           6 :         if (nRowsPerStrip > static_cast<uint32_t>(nYSize))
    2045           0 :             nRowsPerStrip = nYSize;
    2046           6 :         nBlocksPerBand = DIV_ROUND_UP(nYSize, nRowsPerStrip);
    2047             :     }
    2048        2947 :     for (int i = 0; i < nBlockCount; ++i)
    2049             :     {
    2050             :         GPtrDiff_t cc = bIsTiled
    2051        2939 :                             ? static_cast<GPtrDiff_t>(TIFFTileSize(hTIFF))
    2052        2907 :                             : static_cast<GPtrDiff_t>(TIFFStripSize(hTIFF));
    2053        2939 :         if (!bIsTiled)
    2054             :         {
    2055             :             /* --------------------------------------------------------------------
    2056             :              */
    2057             :             /*      If this is the last strip in the image, and is partial, then
    2058             :              */
    2059             :             /*      we need to trim the number of scanlines written to the */
    2060             :             /*      amount of valid data we have. (#2748) */
    2061             :             /* --------------------------------------------------------------------
    2062             :              */
    2063        2907 :             int nStripWithinBand = i % nBlocksPerBand;
    2064        2907 :             if (nStripWithinBand * nRowsPerStrip > nYSize - nRowsPerStrip)
    2065             :             {
    2066           1 :                 cc = (cc / nRowsPerStrip) *
    2067           1 :                      (nYSize - nStripWithinBand * nRowsPerStrip);
    2068             :             }
    2069             :         }
    2070        2939 :         panOffset[i] = nOffset;
    2071        2939 :         panSize[i] = cc;
    2072        2939 :         nOffset += cc;
    2073             :     }
    2074           8 : }
    2075             : 
    2076             : /************************************************************************/
    2077             : /*                             Crystalize()                             */
    2078             : /*                                                                      */
    2079             : /*      Make sure that the directory information is written out for     */
    2080             : /*      a new file, require before writing any imagery data.            */
    2081             : /************************************************************************/
    2082             : 
    2083     2641610 : void GTiffDataset::Crystalize()
    2084             : 
    2085             : {
    2086     2641610 :     if (m_bCrystalized)
    2087     2636140 :         return;
    2088             : 
    2089             :     // TODO: libtiff writes extended tags in the order they are specified
    2090             :     // and not in increasing order.
    2091        5510 :     WriteMetadata(this, m_hTIFF, true, m_eProfile, m_osFilename.c_str(),
    2092        5477 :                   m_papszCreationOptions);
    2093        5510 :     WriteGeoTIFFInfo();
    2094        5510 :     if (m_bNoDataSet)
    2095         302 :         WriteNoDataValue(m_hTIFF, m_dfNoDataValue);
    2096        5208 :     else if (m_bNoDataSetAsInt64)
    2097           2 :         WriteNoDataValue(m_hTIFF, m_nNoDataValueInt64);
    2098        5206 :     else if (m_bNoDataSetAsUInt64)
    2099           2 :         WriteNoDataValue(m_hTIFF, m_nNoDataValueUInt64);
    2100             : 
    2101        5510 :     m_bMetadataChanged = false;
    2102        5510 :     m_bGeoTIFFInfoChanged = false;
    2103        5510 :     m_bNoDataChanged = false;
    2104        5510 :     m_bNeedsRewrite = false;
    2105             : 
    2106        5510 :     m_bCrystalized = true;
    2107             : 
    2108        5510 :     TIFFWriteCheck(m_hTIFF, TIFFIsTiled(m_hTIFF), "GTiffDataset::Crystalize");
    2109             : 
    2110        5510 :     TIFFWriteDirectory(m_hTIFF);
    2111        5510 :     if (m_bStreamingOut)
    2112             :     {
    2113             :         // We need to write twice the directory to be sure that custom
    2114             :         // TIFF tags are correctly sorted and that padding bytes have been
    2115             :         // added.
    2116           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2117           3 :         TIFFWriteDirectory(m_hTIFF);
    2118             : 
    2119           3 :         if (VSIFSeekL(m_fpL, 0, SEEK_END) != 0)
    2120             :         {
    2121           0 :             ReportError(CE_Failure, CPLE_FileIO, "Could not seek");
    2122             :         }
    2123           3 :         const int nSize = static_cast<int>(VSIFTellL(m_fpL));
    2124             : 
    2125           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2126           3 :         GTiffFillStreamableOffsetAndCount(m_hTIFF, nSize);
    2127           3 :         TIFFWriteDirectory(m_hTIFF);
    2128             : 
    2129           3 :         vsi_l_offset nDataLength = 0;
    2130             :         void *pabyBuffer =
    2131           3 :             VSIGetMemFileBuffer(m_pszTmpFilename, &nDataLength, FALSE);
    2132           3 :         if (static_cast<int>(VSIFWriteL(
    2133           3 :                 pabyBuffer, 1, static_cast<int>(nDataLength), m_fpToWrite)) !=
    2134             :             static_cast<int>(nDataLength))
    2135             :         {
    2136           0 :             ReportError(CE_Failure, CPLE_FileIO, "Could not write %d bytes",
    2137             :                         static_cast<int>(nDataLength));
    2138             :         }
    2139             :         // In case of single strip file, there's a libtiff check that would
    2140             :         // issue a warning since the file hasn't the required size.
    2141           3 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2142           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2143           3 :         CPLPopErrorHandler();
    2144             :     }
    2145             :     else
    2146             :     {
    2147        5507 :         const tdir_t nNumberOfDirs = TIFFNumberOfDirectories(m_hTIFF);
    2148        5507 :         if (nNumberOfDirs > 0)
    2149             :         {
    2150        5507 :             TIFFSetDirectory(m_hTIFF, static_cast<tdir_t>(nNumberOfDirs - 1));
    2151             :         }
    2152             :     }
    2153             : 
    2154        5510 :     RestoreVolatileParameters(m_hTIFF);
    2155             : 
    2156        5510 :     m_nDirOffset = TIFFCurrentDirOffset(m_hTIFF);
    2157             : }
    2158             : 
    2159             : /************************************************************************/
    2160             : /*                             FlushCache()                             */
    2161             : /*                                                                      */
    2162             : /*      We override this so we can also flush out local tiff strip      */
    2163             : /*      cache if need be.                                               */
    2164             : /************************************************************************/
    2165             : 
    2166        4415 : CPLErr GTiffDataset::FlushCache(bool bAtClosing)
    2167             : 
    2168             : {
    2169        4415 :     return FlushCacheInternal(bAtClosing, true);
    2170             : }
    2171             : 
    2172       45149 : CPLErr GTiffDataset::FlushCacheInternal(bool bAtClosing, bool bFlushDirectory)
    2173             : {
    2174       45149 :     if (m_bIsFinalized)
    2175           0 :         return CE_None;
    2176             : 
    2177       45149 :     CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
    2178             : 
    2179       45149 :     if (m_bLoadedBlockDirty && m_nLoadedBlock != -1)
    2180             :     {
    2181         261 :         if (FlushBlockBuf() != CE_None)
    2182           0 :             eErr = CE_Failure;
    2183             :     }
    2184             : 
    2185       45149 :     CPLFree(m_pabyBlockBuf);
    2186       45149 :     m_pabyBlockBuf = nullptr;
    2187       45149 :     m_nLoadedBlock = -1;
    2188       45149 :     m_bLoadedBlockDirty = false;
    2189             : 
    2190             :     // Finish compression
    2191       45149 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    2192       42851 :                               : m_poCompressQueue.get();
    2193       45149 :     if (poQueue)
    2194             :     {
    2195         161 :         poQueue->WaitCompletion();
    2196             : 
    2197             :         // Flush remaining data
    2198             :         // cppcheck-suppress constVariableReference
    2199             : 
    2200         161 :         auto &oQueue =
    2201         161 :             m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    2202         230 :         while (!oQueue.empty())
    2203             :         {
    2204          69 :             WaitCompletionForJobIdx(oQueue.front());
    2205             :         }
    2206             :     }
    2207             : 
    2208       45149 :     if (bFlushDirectory && GetAccess() == GA_Update)
    2209             :     {
    2210       13426 :         if (FlushDirectory() != CE_None)
    2211          12 :             eErr = CE_Failure;
    2212             :     }
    2213       45149 :     return eErr;
    2214             : }
    2215             : 
    2216             : /************************************************************************/
    2217             : /*                           FlushDirectory()                           */
    2218             : /************************************************************************/
    2219             : 
    2220       20867 : CPLErr GTiffDataset::FlushDirectory()
    2221             : 
    2222             : {
    2223       20867 :     CPLErr eErr = CE_None;
    2224             : 
    2225         598 :     const auto ReloadAllOtherDirectories = [this]()
    2226             :     {
    2227         294 :         const auto poBaseDS = m_poBaseDS ? m_poBaseDS : this;
    2228         294 :         if (poBaseDS->m_papoOverviewDS)
    2229             :         {
    2230          12 :             for (int i = 0; i < poBaseDS->m_nOverviewCount; ++i)
    2231             :             {
    2232           3 :                 if (poBaseDS->m_papoOverviewDS[i]->m_bCrystalized &&
    2233           3 :                     poBaseDS->m_papoOverviewDS[i] != this)
    2234             :                 {
    2235           3 :                     poBaseDS->m_papoOverviewDS[i]->ReloadDirectory(true);
    2236             :                 }
    2237             : 
    2238           3 :                 if (poBaseDS->m_papoOverviewDS[i]->m_poMaskDS &&
    2239           0 :                     poBaseDS->m_papoOverviewDS[i]->m_poMaskDS != this &&
    2240           0 :                     poBaseDS->m_papoOverviewDS[i]->m_poMaskDS->m_bCrystalized)
    2241             :                 {
    2242           0 :                     poBaseDS->m_papoOverviewDS[i]->m_poMaskDS->ReloadDirectory(
    2243             :                         true);
    2244             :                 }
    2245             :             }
    2246             :         }
    2247         294 :         if (poBaseDS->m_poMaskDS && poBaseDS->m_poMaskDS != this &&
    2248           0 :             poBaseDS->m_poMaskDS->m_bCrystalized)
    2249             :         {
    2250           0 :             poBaseDS->m_poMaskDS->ReloadDirectory(true);
    2251             :         }
    2252         294 :         if (poBaseDS->m_bCrystalized && poBaseDS != this)
    2253             :         {
    2254           7 :             poBaseDS->ReloadDirectory(true);
    2255             :         }
    2256       21161 :     };
    2257             : 
    2258       20867 :     if (eAccess == GA_Update)
    2259             :     {
    2260       15024 :         if (m_bMetadataChanged)
    2261             :         {
    2262         160 :             m_bNeedsRewrite =
    2263         160 :                 WriteMetadata(this, m_hTIFF, true, m_eProfile,
    2264         160 :                               m_osFilename.c_str(), m_papszCreationOptions);
    2265         160 :             m_bMetadataChanged = false;
    2266             : 
    2267         160 :             if (m_bForceUnsetRPC)
    2268             :             {
    2269           5 :                 double *padfRPCTag = nullptr;
    2270             :                 uint16_t nCount;
    2271           5 :                 if (TIFFGetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT, &nCount,
    2272           5 :                                  &padfRPCTag))
    2273             :                 {
    2274           3 :                     std::vector<double> zeroes(92);
    2275           3 :                     TIFFSetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT, 92,
    2276             :                                  zeroes.data());
    2277           3 :                     TIFFUnsetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT);
    2278           3 :                     m_bNeedsRewrite = true;
    2279             :                 }
    2280             : 
    2281           5 :                 if (m_poBaseDS == nullptr)
    2282             :                 {
    2283           5 :                     GDALWriteRPCTXTFile(m_osFilename.c_str(), nullptr);
    2284           5 :                     GDALWriteRPBFile(m_osFilename.c_str(), nullptr);
    2285             :                 }
    2286             :             }
    2287             :         }
    2288             : 
    2289       15024 :         if (m_bGeoTIFFInfoChanged)
    2290             :         {
    2291         140 :             WriteGeoTIFFInfo();
    2292         140 :             m_bGeoTIFFInfoChanged = false;
    2293             :         }
    2294             : 
    2295       15024 :         if (m_bNoDataChanged)
    2296             :         {
    2297          50 :             if (m_bNoDataSet)
    2298             :             {
    2299          36 :                 WriteNoDataValue(m_hTIFF, m_dfNoDataValue);
    2300             :             }
    2301          14 :             else if (m_bNoDataSetAsInt64)
    2302             :             {
    2303           0 :                 WriteNoDataValue(m_hTIFF, m_nNoDataValueInt64);
    2304             :             }
    2305          14 :             else if (m_bNoDataSetAsUInt64)
    2306             :             {
    2307           0 :                 WriteNoDataValue(m_hTIFF, m_nNoDataValueUInt64);
    2308             :             }
    2309             :             else
    2310             :             {
    2311          14 :                 UnsetNoDataValue(m_hTIFF);
    2312             :             }
    2313          50 :             m_bNeedsRewrite = true;
    2314          50 :             m_bNoDataChanged = false;
    2315             :         }
    2316             : 
    2317       15024 :         if (m_bNeedsRewrite)
    2318             :         {
    2319         318 :             if (!m_bCrystalized)
    2320             :             {
    2321          27 :                 Crystalize();
    2322             :             }
    2323             :             else
    2324             :             {
    2325         291 :                 const TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(m_hTIFF);
    2326             : 
    2327         291 :                 m_nDirOffset = pfnSizeProc(TIFFClientdata(m_hTIFF));
    2328         291 :                 if ((m_nDirOffset % 2) == 1)
    2329          65 :                     ++m_nDirOffset;
    2330             : 
    2331         291 :                 if (TIFFRewriteDirectory(m_hTIFF) == 0)
    2332           0 :                     eErr = CE_Failure;
    2333             : 
    2334         291 :                 TIFFSetSubDirectory(m_hTIFF, m_nDirOffset);
    2335             : 
    2336         291 :                 ReloadAllOtherDirectories();
    2337             : 
    2338         291 :                 if (m_bLayoutIFDSBeforeData && m_bBlockOrderRowMajor &&
    2339           1 :                     m_bLeaderSizeAsUInt4 &&
    2340           1 :                     m_bTrailerRepeatedLast4BytesRepeated &&
    2341           1 :                     !m_bKnownIncompatibleEdition &&
    2342           1 :                     !m_bWriteKnownIncompatibleEdition)
    2343             :                 {
    2344           1 :                     ReportError(CE_Warning, CPLE_AppDefined,
    2345             :                                 "The IFD has been rewritten at the end of "
    2346             :                                 "the file, which breaks COG layout.");
    2347           1 :                     m_bKnownIncompatibleEdition = true;
    2348           1 :                     m_bWriteKnownIncompatibleEdition = true;
    2349             :                 }
    2350             :             }
    2351             : 
    2352         318 :             m_bNeedsRewrite = false;
    2353             :         }
    2354             :     }
    2355             : 
    2356             :     // There are some circumstances in which we can reach this point
    2357             :     // without having made this our directory (SetDirectory()) in which
    2358             :     // case we should not risk a flush.
    2359       35891 :     if (GetAccess() == GA_Update &&
    2360       15024 :         TIFFCurrentDirOffset(m_hTIFF) == m_nDirOffset)
    2361             :     {
    2362       15024 :         const TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(m_hTIFF);
    2363             : 
    2364       15024 :         toff_t nNewDirOffset = pfnSizeProc(TIFFClientdata(m_hTIFF));
    2365       15024 :         if ((nNewDirOffset % 2) == 1)
    2366        3284 :             ++nNewDirOffset;
    2367             : 
    2368       15024 :         if (TIFFFlush(m_hTIFF) == 0)
    2369          12 :             eErr = CE_Failure;
    2370             : 
    2371       15024 :         if (m_nDirOffset != TIFFCurrentDirOffset(m_hTIFF))
    2372             :         {
    2373           3 :             m_nDirOffset = nNewDirOffset;
    2374           3 :             ReloadAllOtherDirectories();
    2375           3 :             CPLDebug("GTiff",
    2376             :                      "directory moved during flush in FlushDirectory()");
    2377             :         }
    2378             :     }
    2379             : 
    2380       20867 :     SetDirectory();
    2381       20867 :     return eErr;
    2382             : }
    2383             : 
    2384             : /************************************************************************/
    2385             : /*                           CleanOverviews()                           */
    2386             : /************************************************************************/
    2387             : 
    2388           5 : CPLErr GTiffDataset::CleanOverviews()
    2389             : 
    2390             : {
    2391           5 :     CPLAssert(!m_poBaseDS);
    2392             : 
    2393           5 :     ScanDirectories();
    2394             : 
    2395           5 :     FlushDirectory();
    2396             : 
    2397             :     /* -------------------------------------------------------------------- */
    2398             :     /*      Cleanup overviews objects, and get offsets to all overview      */
    2399             :     /*      directories.                                                    */
    2400             :     /* -------------------------------------------------------------------- */
    2401          10 :     std::vector<toff_t> anOvDirOffsets;
    2402             : 
    2403          10 :     for (int i = 0; i < m_nOverviewCount; ++i)
    2404             :     {
    2405           5 :         anOvDirOffsets.push_back(m_papoOverviewDS[i]->m_nDirOffset);
    2406           5 :         if (m_papoOverviewDS[i]->m_poMaskDS)
    2407           1 :             anOvDirOffsets.push_back(
    2408           1 :                 m_papoOverviewDS[i]->m_poMaskDS->m_nDirOffset);
    2409           5 :         delete m_papoOverviewDS[i];
    2410             :     }
    2411             : 
    2412             :     /* -------------------------------------------------------------------- */
    2413             :     /*      Loop through all the directories, translating the offsets       */
    2414             :     /*      into indexes we can use with TIFFUnlinkDirectory().             */
    2415             :     /* -------------------------------------------------------------------- */
    2416          10 :     std::vector<uint16_t> anOvDirIndexes;
    2417           5 :     int iThisOffset = 1;
    2418             : 
    2419           5 :     TIFFSetDirectory(m_hTIFF, 0);
    2420             : 
    2421             :     while (true)
    2422             :     {
    2423          28 :         for (toff_t nOffset : anOvDirOffsets)
    2424             :         {
    2425          16 :             if (nOffset == TIFFCurrentDirOffset(m_hTIFF))
    2426             :             {
    2427           6 :                 anOvDirIndexes.push_back(static_cast<uint16_t>(iThisOffset));
    2428             :             }
    2429             :         }
    2430             : 
    2431          12 :         if (TIFFLastDirectory(m_hTIFF))
    2432           5 :             break;
    2433             : 
    2434           7 :         TIFFReadDirectory(m_hTIFF);
    2435           7 :         ++iThisOffset;
    2436           7 :     }
    2437             : 
    2438             :     /* -------------------------------------------------------------------- */
    2439             :     /*      Actually unlink the target directories.  Note that we do        */
    2440             :     /*      this from last to first so as to avoid renumbering any of       */
    2441             :     /*      the earlier directories we need to remove.                      */
    2442             :     /* -------------------------------------------------------------------- */
    2443          11 :     while (!anOvDirIndexes.empty())
    2444             :     {
    2445           6 :         TIFFUnlinkDirectory(m_hTIFF, anOvDirIndexes.back());
    2446           6 :         anOvDirIndexes.pop_back();
    2447             :     }
    2448             : 
    2449           5 :     CPLFree(m_papoOverviewDS);
    2450           5 :     m_nOverviewCount = 0;
    2451           5 :     m_papoOverviewDS = nullptr;
    2452             : 
    2453           5 :     if (m_poMaskDS)
    2454             :     {
    2455           1 :         CPLFree(m_poMaskDS->m_papoOverviewDS);
    2456           1 :         m_poMaskDS->m_nOverviewCount = 0;
    2457           1 :         m_poMaskDS->m_papoOverviewDS = nullptr;
    2458             :     }
    2459             : 
    2460           5 :     if (!SetDirectory())
    2461           0 :         return CE_Failure;
    2462             : 
    2463           5 :     return CE_None;
    2464             : }
    2465             : 
    2466             : /************************************************************************/
    2467             : /*                   RegisterNewOverviewDataset()                       */
    2468             : /************************************************************************/
    2469             : 
    2470         504 : CPLErr GTiffDataset::RegisterNewOverviewDataset(toff_t nOverviewOffset,
    2471             :                                                 int l_nJpegQuality,
    2472             :                                                 CSLConstList papszOptions)
    2473             : {
    2474         504 :     if (m_nOverviewCount == 127)
    2475           0 :         return CE_Failure;
    2476             : 
    2477             :     const auto GetOptionValue =
    2478        5544 :         [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
    2479       11087 :                        const char **ppszKeyUsed = nullptr)
    2480             :     {
    2481        5544 :         const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
    2482        5544 :         if (pszVal)
    2483             :         {
    2484           1 :             if (ppszKeyUsed)
    2485           1 :                 *ppszKeyUsed = pszOptionKey;
    2486           1 :             return pszVal;
    2487             :         }
    2488        5543 :         pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey);
    2489        5543 :         if (pszVal)
    2490             :         {
    2491           0 :             if (ppszKeyUsed)
    2492           0 :                 *ppszKeyUsed = pszConfigOptionKey;
    2493           0 :             return pszVal;
    2494             :         }
    2495        5543 :         if (pszConfigOptionKey)
    2496             :         {
    2497        5543 :             pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
    2498        5543 :             if (pszVal && ppszKeyUsed)
    2499          13 :                 *ppszKeyUsed = pszConfigOptionKey;
    2500             :         }
    2501        5543 :         return pszVal;
    2502         504 :     };
    2503             : 
    2504         504 :     int nZLevel = m_nZLevel;
    2505         504 :     if (const char *opt = GetOptionValue("ZLEVEL", "ZLEVEL_OVERVIEW"))
    2506             :     {
    2507           4 :         nZLevel = atoi(opt);
    2508             :     }
    2509             : 
    2510         504 :     int nZSTDLevel = m_nZSTDLevel;
    2511         504 :     if (const char *opt = GetOptionValue("ZSTD_LEVEL", "ZSTD_LEVEL_OVERVIEW"))
    2512             :     {
    2513           4 :         nZSTDLevel = atoi(opt);
    2514             :     }
    2515             : 
    2516         504 :     bool bWebpLossless = m_bWebPLossless;
    2517             :     const char *pszWebPLosslessOverview =
    2518         504 :         GetOptionValue("WEBP_LOSSLESS", "WEBP_LOSSLESS_OVERVIEW");
    2519         504 :     if (pszWebPLosslessOverview)
    2520             :     {
    2521           2 :         bWebpLossless = CPLTestBool(pszWebPLosslessOverview);
    2522             :     }
    2523             : 
    2524         504 :     int nWebpLevel = m_nWebPLevel;
    2525         504 :     const char *pszKeyWebpLevel = "";
    2526         504 :     if (const char *opt = GetOptionValue("WEBP_LEVEL", "WEBP_LEVEL_OVERVIEW",
    2527             :                                          &pszKeyWebpLevel))
    2528             :     {
    2529          14 :         if (pszWebPLosslessOverview == nullptr && m_bWebPLossless)
    2530             :         {
    2531           1 :             CPLDebug("GTiff",
    2532             :                      "%s specified, but not WEBP_LOSSLESS_OVERVIEW. "
    2533             :                      "Assuming WEBP_LOSSLESS_OVERVIEW=NO",
    2534             :                      pszKeyWebpLevel);
    2535           1 :             bWebpLossless = false;
    2536             :         }
    2537          13 :         else if (bWebpLossless)
    2538             :         {
    2539           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2540             :                      "%s is specified, but WEBP_LOSSLESS_OVERVIEW=YES. "
    2541             :                      "%s will be ignored.",
    2542             :                      pszKeyWebpLevel, pszKeyWebpLevel);
    2543             :         }
    2544          14 :         nWebpLevel = atoi(opt);
    2545             :     }
    2546             : 
    2547         504 :     double dfMaxZError = m_dfMaxZErrorOverview;
    2548         504 :     if (const char *opt = GetOptionValue("MAX_Z_ERROR", "MAX_Z_ERROR_OVERVIEW"))
    2549             :     {
    2550          20 :         dfMaxZError = CPLAtof(opt);
    2551             :     }
    2552             : 
    2553         504 :     signed char nJpegTablesMode = m_nJpegTablesMode;
    2554         504 :     if (const char *opt =
    2555         504 :             GetOptionValue("JPEG_TABLESMODE", "JPEG_TABLESMODE_OVERVIEW"))
    2556             :     {
    2557           0 :         nJpegTablesMode = static_cast<signed char>(atoi(opt));
    2558             :     }
    2559             : 
    2560             : #ifdef HAVE_JXL
    2561         504 :     bool bJXLLossless = m_bJXLLossless;
    2562         504 :     if (const char *opt =
    2563         504 :             GetOptionValue("JXL_LOSSLESS", "JXL_LOSSLESS_OVERVIEW"))
    2564             :     {
    2565           0 :         bJXLLossless = CPLTestBool(opt);
    2566             :     }
    2567             : 
    2568         504 :     float fJXLDistance = m_fJXLDistance;
    2569         504 :     if (const char *opt =
    2570         504 :             GetOptionValue("JXL_DISTANCE", "JXL_DISTANCE_OVERVIEW"))
    2571             :     {
    2572           0 :         fJXLDistance = static_cast<float>(CPLAtof(opt));
    2573             :     }
    2574             : 
    2575         504 :     float fJXLAlphaDistance = m_fJXLAlphaDistance;
    2576         504 :     if (const char *opt =
    2577         504 :             GetOptionValue("JXL_ALPHA_DISTANCE", "JXL_ALPHA_DISTANCE_OVERVIEW"))
    2578             :     {
    2579           0 :         fJXLAlphaDistance = static_cast<float>(CPLAtof(opt));
    2580             :     }
    2581             : 
    2582         504 :     int nJXLEffort = m_nJXLEffort;
    2583         504 :     if (const char *opt = GetOptionValue("JXL_EFFORT", "JXL_EFFORT_OVERVIEW"))
    2584             :     {
    2585           0 :         nJXLEffort = atoi(opt);
    2586             :     }
    2587             : #endif
    2588             : 
    2589         504 :     GTiffDataset *poODS = new GTiffDataset();
    2590         504 :     poODS->ShareLockWithParentDataset(this);
    2591         504 :     poODS->eAccess = GA_Update;
    2592         504 :     poODS->m_osFilename = m_osFilename;
    2593         504 :     const char *pszSparseOK = GetOptionValue("SPARSE_OK", "SPARSE_OK_OVERVIEW");
    2594         504 :     if (pszSparseOK && CPLTestBool(pszSparseOK))
    2595             :     {
    2596           1 :         poODS->m_bWriteEmptyTiles = false;
    2597           1 :         poODS->m_bFillEmptyTilesAtClosing = false;
    2598             :     }
    2599             :     else
    2600             :     {
    2601         503 :         poODS->m_bWriteEmptyTiles = m_bWriteEmptyTiles;
    2602         503 :         poODS->m_bFillEmptyTilesAtClosing = m_bFillEmptyTilesAtClosing;
    2603             :     }
    2604         504 :     poODS->m_nJpegQuality = static_cast<signed char>(l_nJpegQuality);
    2605         504 :     poODS->m_nWebPLevel = static_cast<signed char>(nWebpLevel);
    2606         504 :     poODS->m_nZLevel = static_cast<signed char>(nZLevel);
    2607         504 :     poODS->m_nLZMAPreset = m_nLZMAPreset;
    2608         504 :     poODS->m_nZSTDLevel = static_cast<signed char>(nZSTDLevel);
    2609         504 :     poODS->m_bWebPLossless = bWebpLossless;
    2610         504 :     poODS->m_nJpegTablesMode = nJpegTablesMode;
    2611         504 :     poODS->m_dfMaxZError = dfMaxZError;
    2612         504 :     poODS->m_dfMaxZErrorOverview = dfMaxZError;
    2613         504 :     memcpy(poODS->m_anLercAddCompressionAndVersion,
    2614         504 :            m_anLercAddCompressionAndVersion,
    2615             :            sizeof(m_anLercAddCompressionAndVersion));
    2616             : #ifdef HAVE_JXL
    2617         504 :     poODS->m_bJXLLossless = bJXLLossless;
    2618         504 :     poODS->m_fJXLDistance = fJXLDistance;
    2619         504 :     poODS->m_fJXLAlphaDistance = fJXLAlphaDistance;
    2620         504 :     poODS->m_nJXLEffort = nJXLEffort;
    2621             : #endif
    2622             : 
    2623         504 :     if (poODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nOverviewOffset,
    2624         504 :                           GA_Update) != CE_None)
    2625             :     {
    2626           0 :         delete poODS;
    2627           0 :         return CE_Failure;
    2628             :     }
    2629             : 
    2630             :     // Assign color interpretation from main dataset
    2631         504 :     const int l_nBands = GetRasterCount();
    2632        1510 :     for (int i = 1; i <= l_nBands; i++)
    2633             :     {
    2634        1006 :         auto poBand = dynamic_cast<GTiffRasterBand *>(poODS->GetRasterBand(i));
    2635        1006 :         if (poBand)
    2636        1006 :             poBand->m_eBandInterp = GetRasterBand(i)->GetColorInterpretation();
    2637             :     }
    2638             : 
    2639             :     // Do that now that m_nCompression is set
    2640         504 :     poODS->RestoreVolatileParameters(poODS->m_hTIFF);
    2641             : 
    2642         504 :     ++m_nOverviewCount;
    2643         504 :     m_papoOverviewDS = static_cast<GTiffDataset **>(
    2644         504 :         CPLRealloc(m_papoOverviewDS, m_nOverviewCount * (sizeof(void *))));
    2645         504 :     m_papoOverviewDS[m_nOverviewCount - 1] = poODS;
    2646         504 :     poODS->m_poBaseDS = this;
    2647         504 :     poODS->m_bIsOverview = true;
    2648         504 :     return CE_None;
    2649             : }
    2650             : 
    2651             : /************************************************************************/
    2652             : /*                     CreateTIFFColorTable()                           */
    2653             : /************************************************************************/
    2654             : 
    2655          12 : static void CreateTIFFColorTable(
    2656             :     GDALColorTable *poColorTable, int nBits, int nColorTableMultiplier,
    2657             :     std::vector<unsigned short> &anTRed, std::vector<unsigned short> &anTGreen,
    2658             :     std::vector<unsigned short> &anTBlue, unsigned short *&panRed,
    2659             :     unsigned short *&panGreen, unsigned short *&panBlue)
    2660             : {
    2661             :     int nColors;
    2662             : 
    2663          12 :     if (nBits == 8)
    2664          12 :         nColors = 256;
    2665           0 :     else if (nBits < 8)
    2666           0 :         nColors = 1 << nBits;
    2667             :     else
    2668           0 :         nColors = 65536;
    2669             : 
    2670          12 :     anTRed.resize(nColors, 0);
    2671          12 :     anTGreen.resize(nColors, 0);
    2672          12 :     anTBlue.resize(nColors, 0);
    2673             : 
    2674        3084 :     for (int iColor = 0; iColor < nColors; ++iColor)
    2675             :     {
    2676        3072 :         if (iColor < poColorTable->GetColorEntryCount())
    2677             :         {
    2678             :             GDALColorEntry sRGB;
    2679             : 
    2680        3072 :             poColorTable->GetColorEntryAsRGB(iColor, &sRGB);
    2681             : 
    2682        3072 :             anTRed[iColor] = GTiffDataset::ClampCTEntry(iColor, 1, sRGB.c1,
    2683             :                                                         nColorTableMultiplier);
    2684        3072 :             anTGreen[iColor] = GTiffDataset::ClampCTEntry(
    2685        3072 :                 iColor, 2, sRGB.c2, nColorTableMultiplier);
    2686        3072 :             anTBlue[iColor] = GTiffDataset::ClampCTEntry(iColor, 3, sRGB.c3,
    2687             :                                                          nColorTableMultiplier);
    2688             :         }
    2689             :         else
    2690             :         {
    2691           0 :             anTRed[iColor] = 0;
    2692           0 :             anTGreen[iColor] = 0;
    2693           0 :             anTBlue[iColor] = 0;
    2694             :         }
    2695             :     }
    2696             : 
    2697          12 :     panRed = &(anTRed[0]);
    2698          12 :     panGreen = &(anTGreen[0]);
    2699          12 :     panBlue = &(anTBlue[0]);
    2700          12 : }
    2701             : 
    2702             : /************************************************************************/
    2703             : /*                        GetOverviewParameters()                       */
    2704             : /************************************************************************/
    2705             : 
    2706         322 : bool GTiffDataset::GetOverviewParameters(
    2707             :     int &nCompression, uint16_t &nPlanarConfig, uint16_t &nPredictor,
    2708             :     uint16_t &nPhotometric, int &nOvrJpegQuality, std::string &osNoData,
    2709             :     uint16_t *&panExtraSampleValues, uint16_t &nExtraSamples,
    2710             :     CSLConstList papszOptions) const
    2711             : {
    2712             :     const auto GetOptionValue =
    2713        1068 :         [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
    2714        2128 :                        const char **ppszKeyUsed = nullptr)
    2715             :     {
    2716        1068 :         const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
    2717        1068 :         if (pszVal)
    2718             :         {
    2719           8 :             if (ppszKeyUsed)
    2720           8 :                 *ppszKeyUsed = pszOptionKey;
    2721           8 :             return pszVal;
    2722             :         }
    2723        1060 :         pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey);
    2724        1060 :         if (pszVal)
    2725             :         {
    2726           0 :             if (ppszKeyUsed)
    2727           0 :                 *ppszKeyUsed = pszConfigOptionKey;
    2728           0 :             return pszVal;
    2729             :         }
    2730        1060 :         pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
    2731        1060 :         if (pszVal && ppszKeyUsed)
    2732          58 :             *ppszKeyUsed = pszConfigOptionKey;
    2733        1060 :         return pszVal;
    2734         322 :     };
    2735             : 
    2736             :     /* -------------------------------------------------------------------- */
    2737             :     /*      Determine compression method.                                   */
    2738             :     /* -------------------------------------------------------------------- */
    2739         322 :     nCompression = m_nCompression;
    2740         322 :     const char *pszOptionKey = "";
    2741             :     const char *pszCompressValue =
    2742         322 :         GetOptionValue("COMPRESS", "COMPRESS_OVERVIEW", &pszOptionKey);
    2743         322 :     if (pszCompressValue != nullptr)
    2744             :     {
    2745          56 :         nCompression =
    2746          56 :             GTIFFGetCompressionMethod(pszCompressValue, pszOptionKey);
    2747          56 :         if (nCompression < 0)
    2748             :         {
    2749           0 :             nCompression = m_nCompression;
    2750             :         }
    2751             :     }
    2752             : 
    2753             :     /* -------------------------------------------------------------------- */
    2754             :     /*      Determine planar configuration.                                 */
    2755             :     /* -------------------------------------------------------------------- */
    2756         322 :     nPlanarConfig = m_nPlanarConfig;
    2757         322 :     if (nCompression == COMPRESSION_WEBP)
    2758             :     {
    2759          11 :         nPlanarConfig = PLANARCONFIG_CONTIG;
    2760             :     }
    2761             :     const char *pszInterleave =
    2762         322 :         GetOptionValue("INTERLEAVE", "INTERLEAVE_OVERVIEW", &pszOptionKey);
    2763         322 :     if (pszInterleave != nullptr && pszInterleave[0] != '\0')
    2764             :     {
    2765           2 :         if (EQUAL(pszInterleave, "PIXEL"))
    2766           1 :             nPlanarConfig = PLANARCONFIG_CONTIG;
    2767           1 :         else if (EQUAL(pszInterleave, "BAND"))
    2768           1 :             nPlanarConfig = PLANARCONFIG_SEPARATE;
    2769             :         else
    2770             :         {
    2771           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2772             :                      "%s=%s unsupported, "
    2773             :                      "value must be PIXEL or BAND. ignoring",
    2774             :                      pszOptionKey, pszInterleave);
    2775             :         }
    2776             :     }
    2777             : 
    2778             :     /* -------------------------------------------------------------------- */
    2779             :     /*      Determine predictor tag                                         */
    2780             :     /* -------------------------------------------------------------------- */
    2781         322 :     nPredictor = PREDICTOR_NONE;
    2782         322 :     if (GTIFFSupportsPredictor(nCompression))
    2783             :     {
    2784             :         const char *pszPredictor =
    2785          75 :             GetOptionValue("PREDICTOR", "PREDICTOR_OVERVIEW");
    2786          75 :         if (pszPredictor != nullptr)
    2787             :         {
    2788           1 :             nPredictor = static_cast<uint16_t>(atoi(pszPredictor));
    2789             :         }
    2790          74 :         else if (GTIFFSupportsPredictor(m_nCompression))
    2791          73 :             TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &nPredictor);
    2792             :     }
    2793             : 
    2794             :     /* -------------------------------------------------------------------- */
    2795             :     /*      Determine photometric tag                                       */
    2796             :     /* -------------------------------------------------------------------- */
    2797         322 :     if (m_nPhotometric == PHOTOMETRIC_YCBCR && nCompression != COMPRESSION_JPEG)
    2798           1 :         nPhotometric = PHOTOMETRIC_RGB;
    2799             :     else
    2800         321 :         nPhotometric = m_nPhotometric;
    2801             :     const char *pszPhotometric =
    2802         322 :         GetOptionValue("PHOTOMETRIC", "PHOTOMETRIC_OVERVIEW", &pszOptionKey);
    2803         322 :     if (!GTIFFUpdatePhotometric(pszPhotometric, pszOptionKey, nCompression,
    2804         322 :                                 pszInterleave, nBands, nPhotometric,
    2805             :                                 nPlanarConfig))
    2806             :     {
    2807           0 :         return false;
    2808             :     }
    2809             : 
    2810             :     /* -------------------------------------------------------------------- */
    2811             :     /*      Determine JPEG quality                                          */
    2812             :     /* -------------------------------------------------------------------- */
    2813         322 :     nOvrJpegQuality = m_nJpegQuality;
    2814         322 :     if (nCompression == COMPRESSION_JPEG)
    2815             :     {
    2816             :         const char *pszJPEGQuality =
    2817          27 :             GetOptionValue("JPEG_QUALITY", "JPEG_QUALITY_OVERVIEW");
    2818          27 :         if (pszJPEGQuality != nullptr)
    2819             :         {
    2820           9 :             nOvrJpegQuality = atoi(pszJPEGQuality);
    2821             :         }
    2822             :     }
    2823             : 
    2824             :     /* -------------------------------------------------------------------- */
    2825             :     /*      Set nodata.                                                     */
    2826             :     /* -------------------------------------------------------------------- */
    2827         322 :     if (m_bNoDataSet)
    2828             :     {
    2829          17 :         osNoData = GTiffFormatGDALNoDataTagValue(m_dfNoDataValue);
    2830             :     }
    2831             : 
    2832             :     /* -------------------------------------------------------------------- */
    2833             :     /*      Fetch extra sample tag                                          */
    2834             :     /* -------------------------------------------------------------------- */
    2835         322 :     panExtraSampleValues = nullptr;
    2836         322 :     nExtraSamples = 0;
    2837         322 :     if (TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples,
    2838         322 :                      &panExtraSampleValues))
    2839             :     {
    2840             :         uint16_t *panExtraSampleValuesNew = static_cast<uint16_t *>(
    2841          40 :             CPLMalloc(nExtraSamples * sizeof(uint16_t)));
    2842          40 :         memcpy(panExtraSampleValuesNew, panExtraSampleValues,
    2843          40 :                nExtraSamples * sizeof(uint16_t));
    2844          40 :         panExtraSampleValues = panExtraSampleValuesNew;
    2845             :     }
    2846             :     else
    2847             :     {
    2848         282 :         panExtraSampleValues = nullptr;
    2849         282 :         nExtraSamples = 0;
    2850             :     }
    2851             : 
    2852         322 :     return true;
    2853             : }
    2854             : 
    2855             : /************************************************************************/
    2856             : /*                  CreateOverviewsFromSrcOverviews()                   */
    2857             : /************************************************************************/
    2858             : 
    2859             : // If poOvrDS is not null, it is used and poSrcDS is ignored.
    2860             : 
    2861          68 : CPLErr GTiffDataset::CreateOverviewsFromSrcOverviews(GDALDataset *poSrcDS,
    2862             :                                                      GDALDataset *poOvrDS,
    2863             :                                                      int nOverviews)
    2864             : {
    2865          68 :     CPLAssert(poSrcDS->GetRasterCount() != 0);
    2866          68 :     CPLAssert(m_nOverviewCount == 0);
    2867             : 
    2868          68 :     ScanDirectories();
    2869             : 
    2870          68 :     FlushDirectory();
    2871             : 
    2872          68 :     int nOvBitsPerSample = m_nBitsPerSample;
    2873             : 
    2874             :     /* -------------------------------------------------------------------- */
    2875             :     /*      Do we need some metadata for the overviews?                     */
    2876             :     /* -------------------------------------------------------------------- */
    2877         136 :     CPLString osMetadata;
    2878             : 
    2879          68 :     GTIFFBuildOverviewMetadata("NONE", this, false, osMetadata);
    2880             : 
    2881             :     int nCompression;
    2882             :     uint16_t nPlanarConfig;
    2883             :     uint16_t nPredictor;
    2884             :     uint16_t nPhotometric;
    2885             :     int nOvrJpegQuality;
    2886         136 :     std::string osNoData;
    2887          68 :     uint16_t *panExtraSampleValues = nullptr;
    2888          68 :     uint16_t nExtraSamples = 0;
    2889          68 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    2890             :                                nPhotometric, nOvrJpegQuality, osNoData,
    2891             :                                panExtraSampleValues, nExtraSamples,
    2892             :                                /*papszOptions=*/nullptr))
    2893             :     {
    2894           0 :         return CE_Failure;
    2895             :     }
    2896             : 
    2897             :     /* -------------------------------------------------------------------- */
    2898             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    2899             :     /* -------------------------------------------------------------------- */
    2900         136 :     std::vector<unsigned short> anTRed;
    2901         136 :     std::vector<unsigned short> anTGreen;
    2902          68 :     std::vector<unsigned short> anTBlue;
    2903          68 :     unsigned short *panRed = nullptr;
    2904          68 :     unsigned short *panGreen = nullptr;
    2905          68 :     unsigned short *panBlue = nullptr;
    2906             : 
    2907          68 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    2908             :     {
    2909           0 :         if (m_nColorTableMultiplier == 0)
    2910           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    2911             : 
    2912           0 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    2913             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    2914             :                              panRed, panGreen, panBlue);
    2915             :     }
    2916             : 
    2917          68 :     int nOvrBlockXSize = 0;
    2918          68 :     int nOvrBlockYSize = 0;
    2919          68 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    2920             :                               &nOvrBlockXSize, &nOvrBlockYSize, nullptr,
    2921             :                               nullptr);
    2922             : 
    2923          68 :     CPLErr eErr = CE_None;
    2924             : 
    2925         192 :     for (int i = 0; i < nOverviews && eErr == CE_None; ++i)
    2926             :     {
    2927             :         GDALRasterBand *poOvrBand =
    2928         161 :             poOvrDS ? ((i == 0) ? poOvrDS->GetRasterBand(1)
    2929          37 :                                 : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    2930          53 :                     : poSrcDS->GetRasterBand(1)->GetOverview(i);
    2931             : 
    2932         124 :         int nOXSize = poOvrBand->GetXSize();
    2933         124 :         int nOYSize = poOvrBand->GetYSize();
    2934             : 
    2935         248 :         toff_t nOverviewOffset = GTIFFWriteDirectory(
    2936             :             m_hTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize, nOvBitsPerSample,
    2937         124 :             nPlanarConfig, m_nSamplesPerPixel, nOvrBlockXSize, nOvrBlockYSize,
    2938         124 :             TRUE, nCompression, nPhotometric, m_nSampleFormat, nPredictor,
    2939             :             panRed, panGreen, panBlue, nExtraSamples, panExtraSampleValues,
    2940             :             osMetadata,
    2941         124 :             nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality) : nullptr,
    2942         124 :             CPLSPrintf("%d", m_nJpegTablesMode),
    2943           2 :             osNoData.empty() ? nullptr : osNoData.c_str(),
    2944         124 :             m_anLercAddCompressionAndVersion, m_bWriteCOGLayout);
    2945             : 
    2946         124 :         if (nOverviewOffset == 0)
    2947           0 :             eErr = CE_Failure;
    2948             :         else
    2949         124 :             eErr = RegisterNewOverviewDataset(nOverviewOffset, nOvrJpegQuality,
    2950             :                                               nullptr);
    2951             :     }
    2952             : 
    2953             :     // For directory reloading, so that the chaining to the next directory is
    2954             :     // reloaded, as well as compression parameters.
    2955          68 :     ReloadDirectory();
    2956             : 
    2957          68 :     CPLFree(panExtraSampleValues);
    2958          68 :     panExtraSampleValues = nullptr;
    2959             : 
    2960          68 :     return eErr;
    2961             : }
    2962             : 
    2963             : /************************************************************************/
    2964             : /*                       CreateInternalMaskOverviews()                  */
    2965             : /************************************************************************/
    2966             : 
    2967         269 : CPLErr GTiffDataset::CreateInternalMaskOverviews(int nOvrBlockXSize,
    2968             :                                                  int nOvrBlockYSize)
    2969             : {
    2970         269 :     ScanDirectories();
    2971             : 
    2972             :     /* -------------------------------------------------------------------- */
    2973             :     /*      Create overviews for the mask.                                  */
    2974             :     /* -------------------------------------------------------------------- */
    2975         269 :     CPLErr eErr = CE_None;
    2976             : 
    2977         269 :     if (m_poMaskDS != nullptr && m_poMaskDS->GetRasterCount() == 1)
    2978             :     {
    2979             :         int nMaskOvrCompression;
    2980          43 :         if (strstr(GDALGetMetadataItem(GDALGetDriverByName("GTiff"),
    2981             :                                        GDAL_DMD_CREATIONOPTIONLIST, nullptr),
    2982          43 :                    "<Value>DEFLATE</Value>") != nullptr)
    2983          43 :             nMaskOvrCompression = COMPRESSION_ADOBE_DEFLATE;
    2984             :         else
    2985           0 :             nMaskOvrCompression = COMPRESSION_PACKBITS;
    2986             : 
    2987         115 :         for (int i = 0; i < m_nOverviewCount; ++i)
    2988             :         {
    2989          72 :             if (m_papoOverviewDS[i]->m_poMaskDS == nullptr)
    2990             :             {
    2991         120 :                 const toff_t nOverviewOffset = GTIFFWriteDirectory(
    2992             :                     m_hTIFF, FILETYPE_REDUCEDIMAGE | FILETYPE_MASK,
    2993          60 :                     m_papoOverviewDS[i]->nRasterXSize,
    2994          60 :                     m_papoOverviewDS[i]->nRasterYSize, 1, PLANARCONFIG_CONTIG,
    2995             :                     1, nOvrBlockXSize, nOvrBlockYSize, TRUE,
    2996             :                     nMaskOvrCompression, PHOTOMETRIC_MASK, SAMPLEFORMAT_UINT,
    2997             :                     PREDICTOR_NONE, nullptr, nullptr, nullptr, 0, nullptr, "",
    2998          60 :                     nullptr, nullptr, nullptr, nullptr, m_bWriteCOGLayout);
    2999             : 
    3000          60 :                 if (nOverviewOffset == 0)
    3001             :                 {
    3002           0 :                     eErr = CE_Failure;
    3003           0 :                     continue;
    3004             :                 }
    3005             : 
    3006          60 :                 GTiffDataset *poODS = new GTiffDataset();
    3007          60 :                 poODS->eAccess = GA_Update;
    3008          60 :                 poODS->ShareLockWithParentDataset(this);
    3009          60 :                 poODS->m_osFilename = m_osFilename;
    3010          60 :                 if (poODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF),
    3011          60 :                                       nOverviewOffset, GA_Update) != CE_None)
    3012             :                 {
    3013           0 :                     delete poODS;
    3014           0 :                     eErr = CE_Failure;
    3015             :                 }
    3016             :                 else
    3017             :                 {
    3018          60 :                     poODS->m_bPromoteTo8Bits = CPLTestBool(CPLGetConfigOption(
    3019             :                         "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
    3020          60 :                     poODS->m_poBaseDS = this;
    3021          60 :                     poODS->m_poImageryDS = m_papoOverviewDS[i];
    3022          60 :                     m_papoOverviewDS[i]->m_poMaskDS = poODS;
    3023          60 :                     ++m_poMaskDS->m_nOverviewCount;
    3024         120 :                     m_poMaskDS->m_papoOverviewDS =
    3025         120 :                         static_cast<GTiffDataset **>(CPLRealloc(
    3026          60 :                             m_poMaskDS->m_papoOverviewDS,
    3027          60 :                             m_poMaskDS->m_nOverviewCount * (sizeof(void *))));
    3028          60 :                     m_poMaskDS
    3029          60 :                         ->m_papoOverviewDS[m_poMaskDS->m_nOverviewCount - 1] =
    3030             :                         poODS;
    3031             :                 }
    3032             :             }
    3033             :         }
    3034             :     }
    3035             : 
    3036         269 :     ReloadDirectory();
    3037             : 
    3038         269 :     return eErr;
    3039             : }
    3040             : 
    3041             : /************************************************************************/
    3042             : /*                            AddOverviews()                            */
    3043             : /************************************************************************/
    3044             : 
    3045             : CPLErr
    3046          13 : GTiffDataset::AddOverviews(const std::vector<GDALDataset *> &apoSrcOvrDSIn,
    3047             :                            GDALProgressFunc pfnProgress, void *pProgressData,
    3048             :                            CSLConstList papszOptions)
    3049             : {
    3050             :     /* -------------------------------------------------------------------- */
    3051             :     /*      If we don't have read access, then create the overviews         */
    3052             :     /*      externally.                                                     */
    3053             :     /* -------------------------------------------------------------------- */
    3054          13 :     if (GetAccess() != GA_Update)
    3055             :     {
    3056           4 :         CPLDebug("GTiff", "File open for read-only accessing, "
    3057             :                           "creating overviews externally.");
    3058             : 
    3059           4 :         CPLErr eErr = GDALDataset::AddOverviews(apoSrcOvrDSIn, pfnProgress,
    3060             :                                                 pProgressData, papszOptions);
    3061           4 :         if (eErr == CE_None && m_poMaskDS)
    3062             :         {
    3063           0 :             ReportError(
    3064             :                 CE_Warning, CPLE_NotSupported,
    3065             :                 "Building external overviews whereas there is an internal "
    3066             :                 "mask is not fully supported. "
    3067             :                 "The overviews of the non-mask bands will be created, "
    3068             :                 "but not the overviews of the mask band.");
    3069             :         }
    3070           4 :         return eErr;
    3071             :     }
    3072             : 
    3073          18 :     std::vector<GDALDataset *> apoSrcOvrDS = apoSrcOvrDSIn;
    3074             :     // Sort overviews by descending size
    3075           9 :     std::sort(apoSrcOvrDS.begin(), apoSrcOvrDS.end(),
    3076           0 :               [](const GDALDataset *poDS1, const GDALDataset *poDS2)
    3077           0 :               { return poDS1->GetRasterXSize() > poDS2->GetRasterXSize(); });
    3078             : 
    3079           9 :     if (!GDALDefaultOverviews::CheckSrcOverviewsConsistencyWithBase(
    3080             :             this, apoSrcOvrDS))
    3081           5 :         return CE_Failure;
    3082             : 
    3083           4 :     ScanDirectories();
    3084             : 
    3085             :     // Make implicit JPEG overviews invisible, but do not destroy
    3086             :     // them in case they are already used (not sure that the client
    3087             :     // has the right to do that). Behavior maybe undefined in GDAL API.
    3088           4 :     m_nJPEGOverviewCount = 0;
    3089             : 
    3090           4 :     FlushDirectory();
    3091             : 
    3092             :     /* -------------------------------------------------------------------- */
    3093             :     /*      If we are averaging bit data to grayscale we need to create     */
    3094             :     /*      8bit overviews.                                                 */
    3095             :     /* -------------------------------------------------------------------- */
    3096           4 :     int nOvBitsPerSample = m_nBitsPerSample;
    3097             : 
    3098             :     /* -------------------------------------------------------------------- */
    3099             :     /*      Do we need some metadata for the overviews?                     */
    3100             :     /* -------------------------------------------------------------------- */
    3101           8 :     CPLString osMetadata;
    3102             : 
    3103           4 :     const bool bIsForMaskBand = nBands == 1 && GetRasterBand(1)->IsMaskBand();
    3104           4 :     GTIFFBuildOverviewMetadata(/* resampling = */ "", this, bIsForMaskBand,
    3105             :                                osMetadata);
    3106             : 
    3107             :     int nCompression;
    3108             :     uint16_t nPlanarConfig;
    3109             :     uint16_t nPredictor;
    3110             :     uint16_t nPhotometric;
    3111             :     int nOvrJpegQuality;
    3112           8 :     std::string osNoData;
    3113           4 :     uint16_t *panExtraSampleValues = nullptr;
    3114           4 :     uint16_t nExtraSamples = 0;
    3115           4 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    3116             :                                nPhotometric, nOvrJpegQuality, osNoData,
    3117             :                                panExtraSampleValues, nExtraSamples,
    3118             :                                papszOptions))
    3119             :     {
    3120           0 :         return CE_Failure;
    3121             :     }
    3122             : 
    3123             :     /* -------------------------------------------------------------------- */
    3124             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    3125             :     /* -------------------------------------------------------------------- */
    3126           8 :     std::vector<unsigned short> anTRed;
    3127           8 :     std::vector<unsigned short> anTGreen;
    3128           4 :     std::vector<unsigned short> anTBlue;
    3129           4 :     unsigned short *panRed = nullptr;
    3130           4 :     unsigned short *panGreen = nullptr;
    3131           4 :     unsigned short *panBlue = nullptr;
    3132             : 
    3133           4 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    3134             :     {
    3135           0 :         if (m_nColorTableMultiplier == 0)
    3136           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    3137             : 
    3138           0 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    3139             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    3140             :                              panRed, panGreen, panBlue);
    3141             :     }
    3142             : 
    3143             :     /* -------------------------------------------------------------------- */
    3144             :     /*      Establish which of the overview levels we already have, and     */
    3145             :     /*      which are new.  We assume that band 1 of the file is            */
    3146             :     /*      representative.                                                 */
    3147             :     /* -------------------------------------------------------------------- */
    3148           4 :     int nOvrBlockXSize = 0;
    3149           4 :     int nOvrBlockYSize = 0;
    3150           4 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    3151             :                               &nOvrBlockXSize, &nOvrBlockYSize, papszOptions,
    3152             :                               "BLOCKSIZE");
    3153             : 
    3154           4 :     CPLErr eErr = CE_None;
    3155           8 :     for (const auto *poSrcOvrDS : apoSrcOvrDS)
    3156             :     {
    3157           4 :         bool bFound = false;
    3158           4 :         for (int i = 0; i < m_nOverviewCount && eErr == CE_None; ++i)
    3159             :         {
    3160           2 :             const GTiffDataset *poExistingODS = m_papoOverviewDS[i];
    3161           2 :             if (poExistingODS->GetRasterXSize() ==
    3162           4 :                     poSrcOvrDS->GetRasterXSize() &&
    3163           2 :                 poExistingODS->GetRasterYSize() == poSrcOvrDS->GetRasterYSize())
    3164             :             {
    3165           2 :                 bFound = true;
    3166           2 :                 break;
    3167             :             }
    3168             :         }
    3169           4 :         if (!bFound && eErr == CE_None)
    3170             :         {
    3171           2 :             if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    3172           0 :                 !m_bWriteKnownIncompatibleEdition)
    3173             :             {
    3174           0 :                 ReportError(CE_Warning, CPLE_AppDefined,
    3175             :                             "Adding new overviews invalidates the "
    3176             :                             "LAYOUT=IFDS_BEFORE_DATA property");
    3177           0 :                 m_bKnownIncompatibleEdition = true;
    3178           0 :                 m_bWriteKnownIncompatibleEdition = true;
    3179             :             }
    3180             : 
    3181           6 :             const toff_t nOverviewOffset = GTIFFWriteDirectory(
    3182             :                 m_hTIFF, FILETYPE_REDUCEDIMAGE, poSrcOvrDS->GetRasterXSize(),
    3183             :                 poSrcOvrDS->GetRasterYSize(), nOvBitsPerSample, nPlanarConfig,
    3184           2 :                 m_nSamplesPerPixel, nOvrBlockXSize, nOvrBlockYSize, TRUE,
    3185           2 :                 nCompression, nPhotometric, m_nSampleFormat, nPredictor, panRed,
    3186             :                 panGreen, panBlue, nExtraSamples, panExtraSampleValues,
    3187             :                 osMetadata,
    3188           2 :                 nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality)
    3189             :                                      : nullptr,
    3190           2 :                 CPLSPrintf("%d", m_nJpegTablesMode),
    3191           0 :                 osNoData.empty() ? nullptr : osNoData.c_str(),
    3192           2 :                 m_anLercAddCompressionAndVersion, false);
    3193             : 
    3194           2 :             if (nOverviewOffset == 0)
    3195           0 :                 eErr = CE_Failure;
    3196             :             else
    3197           2 :                 eErr = RegisterNewOverviewDataset(
    3198             :                     nOverviewOffset, nOvrJpegQuality, papszOptions);
    3199             :         }
    3200             :     }
    3201             : 
    3202           4 :     CPLFree(panExtraSampleValues);
    3203           4 :     panExtraSampleValues = nullptr;
    3204             : 
    3205           4 :     ReloadDirectory();
    3206             : 
    3207           4 :     if (!pfnProgress)
    3208           2 :         pfnProgress = GDALDummyProgress;
    3209             : 
    3210             :     // almost 0, but not 0 to please Coverity Scan
    3211           4 :     double dfTotalPixels = std::numeric_limits<double>::min();
    3212           8 :     for (const auto *poSrcOvrDS : apoSrcOvrDS)
    3213             :     {
    3214           4 :         dfTotalPixels += static_cast<double>(poSrcOvrDS->GetRasterXSize()) *
    3215           4 :                          poSrcOvrDS->GetRasterYSize();
    3216             :     }
    3217             : 
    3218             :     // Copy source datasets into target overview datasets
    3219           4 :     double dfCurPixels = 0;
    3220           8 :     for (auto *poSrcOvrDS : apoSrcOvrDS)
    3221             :     {
    3222           4 :         GDALDataset *poDstOvrDS = nullptr;
    3223           4 :         for (int i = 0; i < m_nOverviewCount && eErr == CE_None; ++i)
    3224             :         {
    3225           4 :             GTiffDataset *poExistingODS = m_papoOverviewDS[i];
    3226           4 :             if (poExistingODS->GetRasterXSize() ==
    3227           8 :                     poSrcOvrDS->GetRasterXSize() &&
    3228           4 :                 poExistingODS->GetRasterYSize() == poSrcOvrDS->GetRasterYSize())
    3229             :             {
    3230           4 :                 poDstOvrDS = poExistingODS;
    3231           4 :                 break;
    3232             :             }
    3233             :         }
    3234           4 :         if (poDstOvrDS)
    3235             :         {
    3236             :             const double dfThisPixels =
    3237           4 :                 static_cast<double>(poSrcOvrDS->GetRasterXSize()) *
    3238           4 :                 poSrcOvrDS->GetRasterYSize();
    3239           8 :             void *pScaledProgressData = GDALCreateScaledProgress(
    3240             :                 dfCurPixels / dfTotalPixels,
    3241           4 :                 (dfCurPixels + dfThisPixels) / dfTotalPixels, pfnProgress,
    3242             :                 pProgressData);
    3243           4 :             dfCurPixels += dfThisPixels;
    3244           4 :             eErr = GDALDatasetCopyWholeRaster(GDALDataset::ToHandle(poSrcOvrDS),
    3245             :                                               GDALDataset::ToHandle(poDstOvrDS),
    3246             :                                               nullptr, GDALScaledProgress,
    3247             :                                               pScaledProgressData);
    3248           4 :             GDALDestroyScaledProgress(pScaledProgressData);
    3249             :         }
    3250             :     }
    3251             : 
    3252           4 :     return eErr;
    3253             : }
    3254             : 
    3255             : /************************************************************************/
    3256             : /*                          IBuildOverviews()                           */
    3257             : /************************************************************************/
    3258             : 
    3259         404 : CPLErr GTiffDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
    3260             :                                      const int *panOverviewList, int nBandsIn,
    3261             :                                      const int *panBandList,
    3262             :                                      GDALProgressFunc pfnProgress,
    3263             :                                      void *pProgressData,
    3264             :                                      CSLConstList papszOptions)
    3265             : 
    3266             : {
    3267         404 :     ScanDirectories();
    3268             : 
    3269             :     // Make implicit JPEG overviews invisible, but do not destroy
    3270             :     // them in case they are already used (not sure that the client
    3271             :     // has the right to do that.  Behavior maybe undefined in GDAL API.
    3272         404 :     m_nJPEGOverviewCount = 0;
    3273             : 
    3274             :     /* -------------------------------------------------------------------- */
    3275             :     /*      If RRD or external OVR overviews requested, then invoke         */
    3276             :     /*      generic handling.                                               */
    3277             :     /* -------------------------------------------------------------------- */
    3278         404 :     bool bUseGenericHandling = false;
    3279         808 :     CPLStringList aosOptions(papszOptions);
    3280             : 
    3281         404 :     const char *pszLocation = CSLFetchNameValue(papszOptions, "LOCATION");
    3282         404 :     if (pszLocation && EQUAL(pszLocation, "EXTERNAL"))
    3283             :     {
    3284           1 :         bUseGenericHandling = true;
    3285             :     }
    3286         403 :     else if (pszLocation && EQUAL(pszLocation, "INTERNAL"))
    3287             :     {
    3288           0 :         if (GetAccess() != GA_Update)
    3289             :         {
    3290           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3291             :                      "Cannot create internal overviews on file opened in "
    3292             :                      "read-only mode");
    3293           0 :             return CE_Failure;
    3294             :         }
    3295             :     }
    3296         403 :     else if (pszLocation && EQUAL(pszLocation, "RRD"))
    3297             :     {
    3298           3 :         bUseGenericHandling = true;
    3299           3 :         aosOptions.SetNameValue("USE_RRD", "YES");
    3300             :     }
    3301             :     // Legacy
    3302         400 :     else if (CPLTestBool(
    3303             :                  CSLFetchNameValueDef(papszOptions, "USE_RRD",
    3304         800 :                                       CPLGetConfigOption("USE_RRD", "NO"))) ||
    3305         400 :              CPLTestBool(CSLFetchNameValueDef(
    3306             :                  papszOptions, "TIFF_USE_OVR",
    3307             :                  CPLGetConfigOption("TIFF_USE_OVR", "NO"))))
    3308             :     {
    3309           0 :         bUseGenericHandling = true;
    3310             :     }
    3311             : 
    3312             :     /* -------------------------------------------------------------------- */
    3313             :     /*      If we don't have read access, then create the overviews         */
    3314             :     /*      externally.                                                     */
    3315             :     /* -------------------------------------------------------------------- */
    3316         404 :     if (GetAccess() != GA_Update)
    3317             :     {
    3318         143 :         CPLDebug("GTiff", "File open for read-only accessing, "
    3319             :                           "creating overviews externally.");
    3320             : 
    3321         143 :         bUseGenericHandling = true;
    3322             :     }
    3323             : 
    3324         404 :     if (bUseGenericHandling)
    3325             :     {
    3326         146 :         if (m_nOverviewCount != 0)
    3327             :         {
    3328           0 :             ReportError(CE_Failure, CPLE_NotSupported,
    3329             :                         "Cannot add external overviews when there are already "
    3330             :                         "internal overviews");
    3331           0 :             return CE_Failure;
    3332             :         }
    3333             : 
    3334         146 :         if (!m_bWriteEmptyTiles)
    3335             :         {
    3336           1 :             aosOptions.SetNameValue("SPARSE_OK", "YES");
    3337             :         }
    3338             : 
    3339         146 :         CPLErr eErr = GDALDataset::IBuildOverviews(
    3340             :             pszResampling, nOverviews, panOverviewList, nBandsIn, panBandList,
    3341         146 :             pfnProgress, pProgressData, aosOptions);
    3342         146 :         if (eErr == CE_None && m_poMaskDS)
    3343             :         {
    3344           1 :             ReportError(
    3345             :                 CE_Warning, CPLE_NotSupported,
    3346             :                 "Building external overviews whereas there is an internal "
    3347             :                 "mask is not fully supported. "
    3348             :                 "The overviews of the non-mask bands will be created, "
    3349             :                 "but not the overviews of the mask band.");
    3350             :         }
    3351         146 :         return eErr;
    3352             :     }
    3353             : 
    3354             :     /* -------------------------------------------------------------------- */
    3355             :     /*      Our TIFF overview support currently only works safely if all    */
    3356             :     /*      bands are handled at the same time.                             */
    3357             :     /* -------------------------------------------------------------------- */
    3358         258 :     if (nBandsIn != GetRasterCount())
    3359             :     {
    3360           0 :         ReportError(CE_Failure, CPLE_NotSupported,
    3361             :                     "Generation of overviews in TIFF currently only "
    3362             :                     "supported when operating on all bands.  "
    3363             :                     "Operation failed.");
    3364           0 :         return CE_Failure;
    3365             :     }
    3366             : 
    3367             :     /* -------------------------------------------------------------------- */
    3368             :     /*      If zero overviews were requested, we need to clear all          */
    3369             :     /*      existing overviews.                                             */
    3370             :     /* -------------------------------------------------------------------- */
    3371         258 :     if (nOverviews == 0)
    3372             :     {
    3373           8 :         if (m_nOverviewCount == 0)
    3374           3 :             return GDALDataset::IBuildOverviews(
    3375             :                 pszResampling, nOverviews, panOverviewList, nBandsIn,
    3376           3 :                 panBandList, pfnProgress, pProgressData, papszOptions);
    3377             : 
    3378           5 :         return CleanOverviews();
    3379             :     }
    3380             : 
    3381         250 :     CPLErr eErr = CE_None;
    3382             : 
    3383             :     /* -------------------------------------------------------------------- */
    3384             :     /*      Initialize progress counter.                                    */
    3385             :     /* -------------------------------------------------------------------- */
    3386         250 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    3387             :     {
    3388           0 :         ReportError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    3389           0 :         return CE_Failure;
    3390             :     }
    3391             : 
    3392         250 :     FlushDirectory();
    3393             : 
    3394             :     /* -------------------------------------------------------------------- */
    3395             :     /*      If we are averaging bit data to grayscale we need to create     */
    3396             :     /*      8bit overviews.                                                 */
    3397             :     /* -------------------------------------------------------------------- */
    3398         250 :     int nOvBitsPerSample = m_nBitsPerSample;
    3399             : 
    3400         250 :     if (STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
    3401           2 :         nOvBitsPerSample = 8;
    3402             : 
    3403             :     /* -------------------------------------------------------------------- */
    3404             :     /*      Do we need some metadata for the overviews?                     */
    3405             :     /* -------------------------------------------------------------------- */
    3406         500 :     CPLString osMetadata;
    3407             : 
    3408         250 :     const bool bIsForMaskBand = nBands == 1 && GetRasterBand(1)->IsMaskBand();
    3409         250 :     GTIFFBuildOverviewMetadata(pszResampling, this, bIsForMaskBand, osMetadata);
    3410             : 
    3411             :     int nCompression;
    3412             :     uint16_t nPlanarConfig;
    3413             :     uint16_t nPredictor;
    3414             :     uint16_t nPhotometric;
    3415             :     int nOvrJpegQuality;
    3416         500 :     std::string osNoData;
    3417         250 :     uint16_t *panExtraSampleValues = nullptr;
    3418         250 :     uint16_t nExtraSamples = 0;
    3419         250 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    3420             :                                nPhotometric, nOvrJpegQuality, osNoData,
    3421             :                                panExtraSampleValues, nExtraSamples,
    3422             :                                papszOptions))
    3423             :     {
    3424           0 :         return CE_Failure;
    3425             :     }
    3426             : 
    3427             :     /* -------------------------------------------------------------------- */
    3428             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    3429             :     /* -------------------------------------------------------------------- */
    3430         500 :     std::vector<unsigned short> anTRed;
    3431         500 :     std::vector<unsigned short> anTGreen;
    3432         500 :     std::vector<unsigned short> anTBlue;
    3433         250 :     unsigned short *panRed = nullptr;
    3434         250 :     unsigned short *panGreen = nullptr;
    3435         250 :     unsigned short *panBlue = nullptr;
    3436             : 
    3437         250 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    3438             :     {
    3439          12 :         if (m_nColorTableMultiplier == 0)
    3440           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    3441             : 
    3442          12 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    3443             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    3444             :                              panRed, panGreen, panBlue);
    3445             :     }
    3446             : 
    3447             :     /* -------------------------------------------------------------------- */
    3448             :     /*      Establish which of the overview levels we already have, and     */
    3449             :     /*      which are new.  We assume that band 1 of the file is            */
    3450             :     /*      representative.                                                 */
    3451             :     /* -------------------------------------------------------------------- */
    3452         250 :     int nOvrBlockXSize = 0;
    3453         250 :     int nOvrBlockYSize = 0;
    3454         250 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    3455             :                               &nOvrBlockXSize, &nOvrBlockYSize, papszOptions,
    3456             :                               "BLOCKSIZE");
    3457         500 :     std::vector<bool> abRequireNewOverview(nOverviews, true);
    3458         684 :     for (int i = 0; i < nOverviews && eErr == CE_None; ++i)
    3459             :     {
    3460         767 :         for (int j = 0; j < m_nOverviewCount && eErr == CE_None; ++j)
    3461             :         {
    3462         389 :             GTiffDataset *poODS = m_papoOverviewDS[j];
    3463             : 
    3464             :             const int nOvFactor =
    3465         389 :                 GDALComputeOvFactor(poODS->GetRasterXSize(), GetRasterXSize(),
    3466             :                                     poODS->GetRasterYSize(), GetRasterYSize());
    3467             : 
    3468             :             // If we already have a 1x1 overview and this new one would result
    3469             :             // in it too, then don't create it.
    3470         449 :             if (poODS->GetRasterXSize() == 1 && poODS->GetRasterYSize() == 1 &&
    3471         449 :                 DIV_ROUND_UP(GetRasterXSize(), panOverviewList[i]) == 1 &&
    3472          21 :                 DIV_ROUND_UP(GetRasterYSize(), panOverviewList[i]) == 1)
    3473             :             {
    3474          21 :                 abRequireNewOverview[i] = false;
    3475          21 :                 break;
    3476             :             }
    3477             : 
    3478         701 :             if (nOvFactor == panOverviewList[i] ||
    3479         333 :                 nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3480             :                                                 GetRasterXSize(),
    3481             :                                                 GetRasterYSize()))
    3482             :             {
    3483          35 :                 abRequireNewOverview[i] = false;
    3484          35 :                 break;
    3485             :             }
    3486             :         }
    3487             : 
    3488         434 :         if (abRequireNewOverview[i])
    3489             :         {
    3490         378 :             if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    3491           2 :                 !m_bWriteKnownIncompatibleEdition)
    3492             :             {
    3493           2 :                 ReportError(CE_Warning, CPLE_AppDefined,
    3494             :                             "Adding new overviews invalidates the "
    3495             :                             "LAYOUT=IFDS_BEFORE_DATA property");
    3496           2 :                 m_bKnownIncompatibleEdition = true;
    3497           2 :                 m_bWriteKnownIncompatibleEdition = true;
    3498             :             }
    3499             : 
    3500             :             const int nOXSize =
    3501         378 :                 DIV_ROUND_UP(GetRasterXSize(), panOverviewList[i]);
    3502             :             const int nOYSize =
    3503         378 :                 DIV_ROUND_UP(GetRasterYSize(), panOverviewList[i]);
    3504             : 
    3505         756 :             const toff_t nOverviewOffset = GTIFFWriteDirectory(
    3506             :                 m_hTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize,
    3507         378 :                 nOvBitsPerSample, nPlanarConfig, m_nSamplesPerPixel,
    3508             :                 nOvrBlockXSize, nOvrBlockYSize, TRUE, nCompression,
    3509         378 :                 nPhotometric, m_nSampleFormat, nPredictor, panRed, panGreen,
    3510             :                 panBlue, nExtraSamples, panExtraSampleValues, osMetadata,
    3511         378 :                 nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality)
    3512             :                                      : nullptr,
    3513         378 :                 CPLSPrintf("%d", m_nJpegTablesMode),
    3514          25 :                 osNoData.empty() ? nullptr : osNoData.c_str(),
    3515         378 :                 m_anLercAddCompressionAndVersion, false);
    3516             : 
    3517         378 :             if (nOverviewOffset == 0)
    3518           0 :                 eErr = CE_Failure;
    3519             :             else
    3520         378 :                 eErr = RegisterNewOverviewDataset(
    3521             :                     nOverviewOffset, nOvrJpegQuality, papszOptions);
    3522             :         }
    3523             :     }
    3524             : 
    3525         250 :     CPLFree(panExtraSampleValues);
    3526         250 :     panExtraSampleValues = nullptr;
    3527             : 
    3528         250 :     ReloadDirectory();
    3529             : 
    3530             :     /* -------------------------------------------------------------------- */
    3531             :     /*      Create overviews for the mask.                                  */
    3532             :     /* -------------------------------------------------------------------- */
    3533         250 :     if (eErr != CE_None)
    3534           0 :         return eErr;
    3535             : 
    3536         250 :     eErr = CreateInternalMaskOverviews(nOvrBlockXSize, nOvrBlockYSize);
    3537             : 
    3538             :     /* -------------------------------------------------------------------- */
    3539             :     /*      Refresh overviews for the mask                                  */
    3540             :     /* -------------------------------------------------------------------- */
    3541             :     const bool bHasInternalMask =
    3542         250 :         m_poMaskDS != nullptr && m_poMaskDS->GetRasterCount() == 1;
    3543             :     const bool bHasExternalMask =
    3544         250 :         !bHasInternalMask && oOvManager.HaveMaskFile();
    3545         250 :     const bool bHasMask = bHasInternalMask || bHasExternalMask;
    3546             : 
    3547         250 :     if (bHasInternalMask)
    3548             :     {
    3549          24 :         int nMaskOverviews = 0;
    3550             : 
    3551             :         GDALRasterBand **papoOverviewBands = static_cast<GDALRasterBand **>(
    3552          24 :             CPLCalloc(sizeof(void *), m_nOverviewCount));
    3553          64 :         for (int i = 0; i < m_nOverviewCount; ++i)
    3554             :         {
    3555          40 :             if (m_papoOverviewDS[i]->m_poMaskDS != nullptr)
    3556             :             {
    3557          40 :                 papoOverviewBands[nMaskOverviews++] =
    3558          40 :                     m_papoOverviewDS[i]->m_poMaskDS->GetRasterBand(1);
    3559             :             }
    3560             :         }
    3561             : 
    3562          48 :         void *pScaledProgressData = GDALCreateScaledProgress(
    3563          24 :             0, 1.0 / (nBands + 1), pfnProgress, pProgressData);
    3564          48 :         eErr = GDALRegenerateOverviewsEx(
    3565          24 :             m_poMaskDS->GetRasterBand(1), nMaskOverviews,
    3566             :             reinterpret_cast<GDALRasterBandH *>(papoOverviewBands),
    3567             :             pszResampling, GDALScaledProgress, pScaledProgressData,
    3568             :             papszOptions);
    3569          24 :         GDALDestroyScaledProgress(pScaledProgressData);
    3570          24 :         CPLFree(papoOverviewBands);
    3571             :     }
    3572         226 :     else if (bHasExternalMask)
    3573             :     {
    3574           4 :         void *pScaledProgressData = GDALCreateScaledProgress(
    3575           2 :             0, 1.0 / (nBands + 1), pfnProgress, pProgressData);
    3576           2 :         eErr = oOvManager.BuildOverviewsMask(
    3577             :             pszResampling, nOverviews, panOverviewList, GDALScaledProgress,
    3578             :             pScaledProgressData, papszOptions);
    3579           2 :         GDALDestroyScaledProgress(pScaledProgressData);
    3580             :     }
    3581             : 
    3582             :     // If we have an alpha band, we want it to be generated before downsampling
    3583             :     // other bands
    3584         250 :     bool bHasAlphaBand = false;
    3585       66245 :     for (int iBand = 0; iBand < nBands; iBand++)
    3586             :     {
    3587       65995 :         if (papoBands[iBand]->GetColorInterpretation() == GCI_AlphaBand)
    3588          18 :             bHasAlphaBand = true;
    3589             :     }
    3590             : 
    3591             :     /* -------------------------------------------------------------------- */
    3592             :     /*      Refresh old overviews that were listed.                         */
    3593             :     /* -------------------------------------------------------------------- */
    3594         250 :     const auto poColorTable = GetRasterBand(panBandList[0])->GetColorTable();
    3595          20 :     if ((m_nPlanarConfig == PLANARCONFIG_CONTIG || bHasAlphaBand) &&
    3596         232 :         GDALDataTypeIsComplex(
    3597         232 :             GetRasterBand(panBandList[0])->GetRasterDataType()) == FALSE &&
    3598          12 :         (poColorTable == nullptr || STARTS_WITH_CI(pszResampling, "NEAR") ||
    3599         501 :          poColorTable->IsIdentity()) &&
    3600         224 :         (STARTS_WITH_CI(pszResampling, "NEAR") ||
    3601         117 :          EQUAL(pszResampling, "AVERAGE") || EQUAL(pszResampling, "RMS") ||
    3602          47 :          EQUAL(pszResampling, "GAUSS") || EQUAL(pszResampling, "CUBIC") ||
    3603          29 :          EQUAL(pszResampling, "CUBICSPLINE") ||
    3604          28 :          EQUAL(pszResampling, "LANCZOS") || EQUAL(pszResampling, "BILINEAR") ||
    3605          24 :          EQUAL(pszResampling, "MODE")))
    3606             :     {
    3607             :         // In the case of pixel interleaved compressed overviews, we want to
    3608             :         // generate the overviews for all the bands block by block, and not
    3609             :         // band after band, in order to write the block once and not loose
    3610             :         // space in the TIFF file.  We also use that logic for uncompressed
    3611             :         // overviews, since GDALRegenerateOverviewsMultiBand() will be able to
    3612             :         // trigger cascading overview regeneration even in the presence
    3613             :         // of an alpha band.
    3614             : 
    3615         203 :         int nNewOverviews = 0;
    3616             : 
    3617             :         GDALRasterBand ***papapoOverviewBands = static_cast<GDALRasterBand ***>(
    3618         203 :             CPLCalloc(sizeof(void *), nBandsIn));
    3619             :         GDALRasterBand **papoBandList =
    3620         203 :             static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nBandsIn));
    3621       66105 :         for (int iBand = 0; iBand < nBandsIn; ++iBand)
    3622             :         {
    3623       65902 :             GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
    3624             : 
    3625       65902 :             papoBandList[iBand] = poBand;
    3626      131804 :             papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
    3627       65902 :                 CPLCalloc(sizeof(void *), poBand->GetOverviewCount()));
    3628             : 
    3629       65902 :             int iCurOverview = 0;
    3630             :             std::vector<bool> abAlreadyUsedOverviewBand(
    3631       65902 :                 poBand->GetOverviewCount(), false);
    3632             : 
    3633      132090 :             for (int i = 0; i < nOverviews; ++i)
    3634             :             {
    3635       66648 :                 for (int j = 0; j < poBand->GetOverviewCount(); ++j)
    3636             :                 {
    3637       66633 :                     if (abAlreadyUsedOverviewBand[j])
    3638         459 :                         continue;
    3639             : 
    3640             :                     int nOvFactor;
    3641       66174 :                     GDALRasterBand *poOverview = poBand->GetOverview(j);
    3642             : 
    3643       66174 :                     nOvFactor = GDALComputeOvFactor(
    3644             :                         poOverview->GetXSize(), poBand->GetXSize(),
    3645             :                         poOverview->GetYSize(), poBand->GetYSize());
    3646             : 
    3647       66174 :                     GDALCopyNoDataValue(poOverview, poBand);
    3648             : 
    3649       66175 :                     if (nOvFactor == panOverviewList[i] ||
    3650           1 :                         nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3651             :                                                         poBand->GetXSize(),
    3652             :                                                         poBand->GetYSize()))
    3653             :                     {
    3654       66173 :                         if (iBand == 0)
    3655             :                         {
    3656             :                             const auto osNewResampling =
    3657         660 :                                 GDALGetNormalizedOvrResampling(pszResampling);
    3658             :                             const char *pszExistingResampling =
    3659         330 :                                 poOverview->GetMetadataItem("RESAMPLING");
    3660         660 :                             if (pszExistingResampling &&
    3661         330 :                                 pszExistingResampling != osNewResampling)
    3662             :                             {
    3663           2 :                                 poOverview->SetMetadataItem(
    3664           2 :                                     "RESAMPLING", osNewResampling.c_str());
    3665             :                             }
    3666             :                         }
    3667             : 
    3668       66173 :                         abAlreadyUsedOverviewBand[j] = true;
    3669       66173 :                         CPLAssert(iCurOverview < poBand->GetOverviewCount());
    3670       66173 :                         papapoOverviewBands[iBand][iCurOverview] = poOverview;
    3671       66173 :                         ++iCurOverview;
    3672       66173 :                         break;
    3673             :                     }
    3674             :                 }
    3675             :             }
    3676             : 
    3677       65902 :             if (nNewOverviews == 0)
    3678             :             {
    3679         203 :                 nNewOverviews = iCurOverview;
    3680             :             }
    3681       65699 :             else if (nNewOverviews != iCurOverview)
    3682             :             {
    3683           0 :                 CPLAssert(false);
    3684             :                 return CE_Failure;
    3685             :             }
    3686             :         }
    3687             : 
    3688             :         void *pScaledProgressData =
    3689         203 :             bHasMask ? GDALCreateScaledProgress(1.0 / (nBands + 1), 1.0,
    3690             :                                                 pfnProgress, pProgressData)
    3691         177 :                      : GDALCreateScaledProgress(0.0, 1.0, pfnProgress,
    3692         203 :                                                 pProgressData);
    3693         203 :         GDALRegenerateOverviewsMultiBand(nBandsIn, papoBandList, nNewOverviews,
    3694             :                                          papapoOverviewBands, pszResampling,
    3695             :                                          GDALScaledProgress,
    3696             :                                          pScaledProgressData, papszOptions);
    3697         203 :         GDALDestroyScaledProgress(pScaledProgressData);
    3698             : 
    3699       66105 :         for (int iBand = 0; iBand < nBandsIn; ++iBand)
    3700             :         {
    3701       65902 :             CPLFree(papapoOverviewBands[iBand]);
    3702             :         }
    3703         203 :         CPLFree(papapoOverviewBands);
    3704         203 :         CPLFree(papoBandList);
    3705             :     }
    3706             :     else
    3707             :     {
    3708             :         GDALRasterBand **papoOverviewBands = static_cast<GDALRasterBand **>(
    3709          47 :             CPLCalloc(sizeof(void *), nOverviews));
    3710             : 
    3711          47 :         const int iBandOffset = bHasMask ? 1 : 0;
    3712             : 
    3713         140 :         for (int iBand = 0; iBand < nBandsIn && eErr == CE_None; ++iBand)
    3714             :         {
    3715          93 :             GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
    3716          93 :             if (poBand == nullptr)
    3717             :             {
    3718           0 :                 eErr = CE_Failure;
    3719           0 :                 break;
    3720             :             }
    3721             : 
    3722             :             std::vector<bool> abAlreadyUsedOverviewBand(
    3723         186 :                 poBand->GetOverviewCount(), false);
    3724             : 
    3725          93 :             int nNewOverviews = 0;
    3726         282 :             for (int i = 0; i < nOverviews; ++i)
    3727             :             {
    3728         447 :                 for (int j = 0; j < poBand->GetOverviewCount(); ++j)
    3729             :                 {
    3730         429 :                     if (abAlreadyUsedOverviewBand[j])
    3731         257 :                         continue;
    3732             : 
    3733         172 :                     GDALRasterBand *poOverview = poBand->GetOverview(j);
    3734             : 
    3735         172 :                     GDALCopyNoDataValue(poOverview, poBand);
    3736             : 
    3737         172 :                     const int nOvFactor = GDALComputeOvFactor(
    3738             :                         poOverview->GetXSize(), poBand->GetXSize(),
    3739             :                         poOverview->GetYSize(), poBand->GetYSize());
    3740             : 
    3741         173 :                     if (nOvFactor == panOverviewList[i] ||
    3742           1 :                         nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3743             :                                                         poBand->GetXSize(),
    3744             :                                                         poBand->GetYSize()))
    3745             :                     {
    3746         171 :                         if (iBand == 0)
    3747             :                         {
    3748             :                             const auto osNewResampling =
    3749         166 :                                 GDALGetNormalizedOvrResampling(pszResampling);
    3750             :                             const char *pszExistingResampling =
    3751          83 :                                 poOverview->GetMetadataItem("RESAMPLING");
    3752         134 :                             if (pszExistingResampling &&
    3753          51 :                                 pszExistingResampling != osNewResampling)
    3754             :                             {
    3755           1 :                                 poOverview->SetMetadataItem(
    3756           1 :                                     "RESAMPLING", osNewResampling.c_str());
    3757             :                             }
    3758             :                         }
    3759             : 
    3760         171 :                         abAlreadyUsedOverviewBand[j] = true;
    3761         171 :                         CPLAssert(nNewOverviews < poBand->GetOverviewCount());
    3762         171 :                         papoOverviewBands[nNewOverviews++] = poOverview;
    3763         171 :                         break;
    3764             :                     }
    3765             :                 }
    3766             :             }
    3767             : 
    3768         186 :             void *pScaledProgressData = GDALCreateScaledProgress(
    3769          93 :                 (iBand + iBandOffset) /
    3770          93 :                     static_cast<double>(nBandsIn + iBandOffset),
    3771          93 :                 (iBand + iBandOffset + 1) /
    3772          93 :                     static_cast<double>(nBandsIn + iBandOffset),
    3773             :                 pfnProgress, pProgressData);
    3774             : 
    3775          93 :             eErr = GDALRegenerateOverviewsEx(
    3776             :                 poBand, nNewOverviews,
    3777             :                 reinterpret_cast<GDALRasterBandH *>(papoOverviewBands),
    3778             :                 pszResampling, GDALScaledProgress, pScaledProgressData,
    3779             :                 papszOptions);
    3780             : 
    3781          93 :             GDALDestroyScaledProgress(pScaledProgressData);
    3782             :         }
    3783             : 
    3784             :         /* --------------------------------------------------------------------
    3785             :          */
    3786             :         /*      Cleanup */
    3787             :         /* --------------------------------------------------------------------
    3788             :          */
    3789          47 :         CPLFree(papoOverviewBands);
    3790             :     }
    3791             : 
    3792         250 :     pfnProgress(1.0, nullptr, pProgressData);
    3793             : 
    3794         250 :     return eErr;
    3795             : }
    3796             : 
    3797             : /************************************************************************/
    3798             : /*                      GTiffWriteDummyGeokeyDirectory()                */
    3799             : /************************************************************************/
    3800             : 
    3801        1401 : static void GTiffWriteDummyGeokeyDirectory(TIFF *hTIFF)
    3802             : {
    3803             :     // If we have existing geokeys, try to wipe them
    3804             :     // by writing a dummy geokey directory. (#2546)
    3805        1401 :     uint16_t *panVI = nullptr;
    3806        1401 :     uint16_t nKeyCount = 0;
    3807             : 
    3808        1401 :     if (TIFFGetField(hTIFF, TIFFTAG_GEOKEYDIRECTORY, &nKeyCount, &panVI))
    3809             :     {
    3810          23 :         GUInt16 anGKVersionInfo[4] = {1, 1, 0, 0};
    3811          23 :         double adfDummyDoubleParams[1] = {0.0};
    3812          23 :         TIFFSetField(hTIFF, TIFFTAG_GEOKEYDIRECTORY, 4, anGKVersionInfo);
    3813          23 :         TIFFSetField(hTIFF, TIFFTAG_GEODOUBLEPARAMS, 1, adfDummyDoubleParams);
    3814          23 :         TIFFSetField(hTIFF, TIFFTAG_GEOASCIIPARAMS, "");
    3815             :     }
    3816        1401 : }
    3817             : 
    3818             : /************************************************************************/
    3819             : /*                    IsSRSCompatibleOfGeoTIFF()                        */
    3820             : /************************************************************************/
    3821             : 
    3822        2996 : static bool IsSRSCompatibleOfGeoTIFF(const OGRSpatialReference *poSRS,
    3823             :                                      GTIFFKeysFlavorEnum eGeoTIFFKeysFlavor)
    3824             : {
    3825        2996 :     char *pszWKT = nullptr;
    3826        2996 :     if ((poSRS->IsGeographic() || poSRS->IsProjected()) && !poSRS->IsCompound())
    3827             :     {
    3828        2978 :         const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
    3829        2978 :         const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
    3830        2978 :         if (pszAuthName && pszAuthCode && EQUAL(pszAuthName, "EPSG"))
    3831        2475 :             return true;
    3832             :     }
    3833             :     OGRErr eErr;
    3834             :     {
    3835        1042 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    3836        1042 :         if (poSRS->IsDerivedGeographic() ||
    3837         521 :             (poSRS->IsProjected() && !poSRS->IsCompound() &&
    3838          70 :              poSRS->GetAxesCount() == 3))
    3839             :         {
    3840           0 :             eErr = OGRERR_FAILURE;
    3841             :         }
    3842             :         else
    3843             :         {
    3844             :             // Geographic3D CRS can't be exported to WKT1, but are
    3845             :             // valid GeoTIFF 1.1
    3846         521 :             const char *const apszOptions[] = {
    3847         521 :                 poSRS->IsGeographic() ? nullptr : "FORMAT=WKT1", nullptr};
    3848         521 :             eErr = poSRS->exportToWkt(&pszWKT, apszOptions);
    3849         521 :             if (eErr == OGRERR_FAILURE && poSRS->IsProjected() &&
    3850             :                 eGeoTIFFKeysFlavor == GEOTIFF_KEYS_ESRI_PE)
    3851             :             {
    3852           0 :                 CPLFree(pszWKT);
    3853           0 :                 const char *const apszOptionsESRIWKT[] = {"FORMAT=WKT1_ESRI",
    3854             :                                                           nullptr};
    3855           0 :                 eErr = poSRS->exportToWkt(&pszWKT, apszOptionsESRIWKT);
    3856             :             }
    3857             :         }
    3858             :     }
    3859         521 :     const bool bCompatibleOfGeoTIFF =
    3860        1041 :         (eErr == OGRERR_NONE && pszWKT != nullptr &&
    3861         520 :          strstr(pszWKT, "custom_proj4") == nullptr);
    3862         521 :     CPLFree(pszWKT);
    3863         521 :     return bCompatibleOfGeoTIFF;
    3864             : }
    3865             : 
    3866             : /************************************************************************/
    3867             : /*                          WriteGeoTIFFInfo()                          */
    3868             : /************************************************************************/
    3869             : 
    3870        5650 : void GTiffDataset::WriteGeoTIFFInfo()
    3871             : 
    3872             : {
    3873        5650 :     bool bPixelIsPoint = false;
    3874        5650 :     bool bPointGeoIgnore = false;
    3875             : 
    3876             :     const char *pszAreaOrPoint =
    3877        5650 :         GTiffDataset::GetMetadataItem(GDALMD_AREA_OR_POINT);
    3878        5650 :     if (pszAreaOrPoint && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT))
    3879             :     {
    3880          17 :         bPixelIsPoint = true;
    3881             :         bPointGeoIgnore =
    3882          17 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    3883             :     }
    3884             : 
    3885        5650 :     if (m_bForceUnsetGTOrGCPs)
    3886             :     {
    3887          11 :         m_bNeedsRewrite = true;
    3888          11 :         m_bForceUnsetGTOrGCPs = false;
    3889             : 
    3890          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE);
    3891          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS);
    3892          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX);
    3893             :     }
    3894             : 
    3895        5650 :     if (m_bForceUnsetProjection)
    3896             :     {
    3897           8 :         m_bNeedsRewrite = true;
    3898           8 :         m_bForceUnsetProjection = false;
    3899             : 
    3900           8 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOKEYDIRECTORY);
    3901           8 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEODOUBLEPARAMS);
    3902           8 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOASCIIPARAMS);
    3903             :     }
    3904             : 
    3905             :     /* -------------------------------------------------------------------- */
    3906             :     /*      Write geotransform if valid.                                    */
    3907             :     /* -------------------------------------------------------------------- */
    3908        5650 :     if (m_bGeoTransformValid)
    3909             :     {
    3910        1691 :         m_bNeedsRewrite = true;
    3911             : 
    3912             :         /* --------------------------------------------------------------------
    3913             :          */
    3914             :         /*      Clear old tags to ensure we don't end up with conflicting */
    3915             :         /*      information. (#2625) */
    3916             :         /* --------------------------------------------------------------------
    3917             :          */
    3918        1691 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE);
    3919        1691 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS);
    3920        1691 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX);
    3921             : 
    3922             :         /* --------------------------------------------------------------------
    3923             :          */
    3924             :         /*      Write the transform.  If we have a normal north-up image we */
    3925             :         /*      use the tiepoint plus pixelscale otherwise we use a matrix. */
    3926             :         /* --------------------------------------------------------------------
    3927             :          */
    3928        1691 :         if (m_gt[2] == 0.0 && m_gt[4] == 0.0 && m_gt[5] < 0.0)
    3929             :         {
    3930        1625 :             double dfOffset = 0.0;
    3931        1625 :             if (m_eProfile != GTiffProfile::BASELINE)
    3932             :             {
    3933             :                 // In the case the SRS has a vertical component and we have
    3934             :                 // a single band, encode its scale/offset in the GeoTIFF tags
    3935        1619 :                 int bHasScale = FALSE;
    3936        1619 :                 double dfScale = GetRasterBand(1)->GetScale(&bHasScale);
    3937        1619 :                 int bHasOffset = FALSE;
    3938        1619 :                 dfOffset = GetRasterBand(1)->GetOffset(&bHasOffset);
    3939             :                 const bool bApplyScaleOffset =
    3940        1619 :                     m_oSRS.IsVertical() && GetRasterCount() == 1;
    3941        1619 :                 if (bApplyScaleOffset && !bHasScale)
    3942           0 :                     dfScale = 1.0;
    3943        1619 :                 if (!bApplyScaleOffset || !bHasOffset)
    3944        1616 :                     dfOffset = 0.0;
    3945             :                 const double adfPixelScale[3] = {
    3946        1619 :                     m_gt[1], fabs(m_gt[5]), bApplyScaleOffset ? dfScale : 0.0};
    3947        1619 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale);
    3948             :             }
    3949             : 
    3950        1625 :             double adfTiePoints[6] = {0.0,     0.0,     0.0,
    3951        1625 :                                       m_gt[0], m_gt[3], dfOffset};
    3952             : 
    3953        1625 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3954             :             {
    3955          13 :                 adfTiePoints[3] += m_gt[1] * 0.5 + m_gt[2] * 0.5;
    3956          13 :                 adfTiePoints[4] += m_gt[4] * 0.5 + m_gt[5] * 0.5;
    3957             :             }
    3958             : 
    3959        1625 :             if (m_eProfile != GTiffProfile::BASELINE)
    3960        1619 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
    3961             :         }
    3962             :         else
    3963             :         {
    3964          66 :             double adfMatrix[16] = {};
    3965             : 
    3966          66 :             adfMatrix[0] = m_gt[1];
    3967          66 :             adfMatrix[1] = m_gt[2];
    3968          66 :             adfMatrix[3] = m_gt[0];
    3969          66 :             adfMatrix[4] = m_gt[4];
    3970          66 :             adfMatrix[5] = m_gt[5];
    3971          66 :             adfMatrix[7] = m_gt[3];
    3972          66 :             adfMatrix[15] = 1.0;
    3973             : 
    3974          66 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3975             :             {
    3976           0 :                 adfMatrix[3] += m_gt[1] * 0.5 + m_gt[2] * 0.5;
    3977           0 :                 adfMatrix[7] += m_gt[4] * 0.5 + m_gt[5] * 0.5;
    3978             :             }
    3979             : 
    3980          66 :             if (m_eProfile != GTiffProfile::BASELINE)
    3981          66 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
    3982             :         }
    3983             : 
    3984        1691 :         if (m_poBaseDS == nullptr)
    3985             :         {
    3986             :             // Do we need a world file?
    3987        1691 :             if (CPLFetchBool(m_papszCreationOptions, "TFW", false))
    3988           7 :                 GDALWriteWorldFile(m_osFilename.c_str(), "tfw", m_gt.data());
    3989        1684 :             else if (CPLFetchBool(m_papszCreationOptions, "WORLDFILE", false))
    3990           2 :                 GDALWriteWorldFile(m_osFilename.c_str(), "wld", m_gt.data());
    3991             :         }
    3992             :     }
    3993        3973 :     else if (GetGCPCount() > 0 && GetGCPCount() <= knMAX_GCP_COUNT &&
    3994          14 :              m_eProfile != GTiffProfile::BASELINE)
    3995             :     {
    3996          14 :         m_bNeedsRewrite = true;
    3997             : 
    3998             :         double *padfTiePoints = static_cast<double *>(
    3999          14 :             CPLMalloc(6 * sizeof(double) * GetGCPCount()));
    4000             : 
    4001          74 :         for (size_t iGCP = 0; iGCP < m_aoGCPs.size(); ++iGCP)
    4002             :         {
    4003             : 
    4004          60 :             padfTiePoints[iGCP * 6 + 0] = m_aoGCPs[iGCP].Pixel();
    4005          60 :             padfTiePoints[iGCP * 6 + 1] = m_aoGCPs[iGCP].Line();
    4006          60 :             padfTiePoints[iGCP * 6 + 2] = 0;
    4007          60 :             padfTiePoints[iGCP * 6 + 3] = m_aoGCPs[iGCP].X();
    4008          60 :             padfTiePoints[iGCP * 6 + 4] = m_aoGCPs[iGCP].Y();
    4009          60 :             padfTiePoints[iGCP * 6 + 5] = m_aoGCPs[iGCP].Z();
    4010             : 
    4011          60 :             if (bPixelIsPoint && !bPointGeoIgnore)
    4012             :             {
    4013           0 :                 padfTiePoints[iGCP * 6 + 0] += 0.5;
    4014           0 :                 padfTiePoints[iGCP * 6 + 1] += 0.5;
    4015             :             }
    4016             :         }
    4017             : 
    4018          14 :         TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * GetGCPCount(),
    4019             :                      padfTiePoints);
    4020          14 :         CPLFree(padfTiePoints);
    4021             :     }
    4022             : 
    4023             :     /* -------------------------------------------------------------------- */
    4024             :     /*      Write out projection definition.                                */
    4025             :     /* -------------------------------------------------------------------- */
    4026        5650 :     const bool bHasProjection = !m_oSRS.IsEmpty();
    4027        5650 :     if ((bHasProjection || bPixelIsPoint) &&
    4028        1405 :         m_eProfile != GTiffProfile::BASELINE)
    4029             :     {
    4030        1401 :         m_bNeedsRewrite = true;
    4031             : 
    4032             :         // If we have existing geokeys, try to wipe them
    4033             :         // by writing a dummy geokey directory. (#2546)
    4034        1401 :         GTiffWriteDummyGeokeyDirectory(m_hTIFF);
    4035             : 
    4036        1401 :         GTIF *psGTIF = GTiffDataset::GTIFNew(m_hTIFF);
    4037             : 
    4038             :         // Set according to coordinate system.
    4039        1401 :         if (bHasProjection)
    4040             :         {
    4041        1400 :             if (IsSRSCompatibleOfGeoTIFF(&m_oSRS, m_eGeoTIFFKeysFlavor))
    4042             :             {
    4043        1398 :                 GTIFSetFromOGISDefnEx(psGTIF,
    4044             :                                       OGRSpatialReference::ToHandle(&m_oSRS),
    4045             :                                       m_eGeoTIFFKeysFlavor, m_eGeoTIFFVersion);
    4046             :             }
    4047             :             else
    4048             :             {
    4049           2 :                 GDALPamDataset::SetSpatialRef(&m_oSRS);
    4050             :             }
    4051             :         }
    4052             : 
    4053        1401 :         if (bPixelIsPoint)
    4054             :         {
    4055          17 :             GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
    4056             :                        RasterPixelIsPoint);
    4057             :         }
    4058             : 
    4059        1401 :         GTIFWriteKeys(psGTIF);
    4060        1401 :         GTIFFree(psGTIF);
    4061             :     }
    4062        5650 : }
    4063             : 
    4064             : /************************************************************************/
    4065             : /*                         AppendMetadataItem()                         */
    4066             : /************************************************************************/
    4067             : 
    4068        3621 : static void AppendMetadataItem(CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
    4069             :                                const char *pszKey, const char *pszValue,
    4070             :                                CPLXMLNode *psValueNode, int nBand,
    4071             :                                const char *pszRole, const char *pszDomain)
    4072             : 
    4073             : {
    4074        3621 :     CPLAssert(pszValue || psValueNode);
    4075        3621 :     CPLAssert(!(pszValue && psValueNode));
    4076             : 
    4077             :     /* -------------------------------------------------------------------- */
    4078             :     /*      Create the Item element, and subcomponents.                     */
    4079             :     /* -------------------------------------------------------------------- */
    4080        3621 :     CPLXMLNode *psItem = CPLCreateXMLNode(nullptr, CXT_Element, "Item");
    4081        3621 :     CPLAddXMLAttributeAndValue(psItem, "name", pszKey);
    4082             : 
    4083        3621 :     if (nBand > 0)
    4084             :     {
    4085         953 :         char szBandId[32] = {};
    4086         953 :         snprintf(szBandId, sizeof(szBandId), "%d", nBand - 1);
    4087         953 :         CPLAddXMLAttributeAndValue(psItem, "sample", szBandId);
    4088             :     }
    4089             : 
    4090        3621 :     if (pszRole != nullptr)
    4091         356 :         CPLAddXMLAttributeAndValue(psItem, "role", pszRole);
    4092             : 
    4093        3621 :     if (pszDomain != nullptr && strlen(pszDomain) > 0)
    4094        1007 :         CPLAddXMLAttributeAndValue(psItem, "domain", pszDomain);
    4095             : 
    4096        3621 :     if (pszValue)
    4097             :     {
    4098             :         // Note: this escaping should not normally be done, as the serialization
    4099             :         // of the tree to XML also does it, so we end up width double XML escaping,
    4100             :         // but keep it for backward compatibility.
    4101        3615 :         char *pszEscapedItemValue = CPLEscapeString(pszValue, -1, CPLES_XML);
    4102        3615 :         CPLCreateXMLNode(psItem, CXT_Text, pszEscapedItemValue);
    4103        3615 :         CPLFree(pszEscapedItemValue);
    4104             :     }
    4105             :     else
    4106             :     {
    4107           6 :         CPLAddXMLChild(psItem, psValueNode);
    4108             :     }
    4109             : 
    4110             :     /* -------------------------------------------------------------------- */
    4111             :     /*      Create root, if missing.                                        */
    4112             :     /* -------------------------------------------------------------------- */
    4113        3621 :     if (*ppsRoot == nullptr)
    4114         679 :         *ppsRoot = CPLCreateXMLNode(nullptr, CXT_Element, "GDALMetadata");
    4115             : 
    4116             :     /* -------------------------------------------------------------------- */
    4117             :     /*      Append item to tail.  We keep track of the tail to avoid        */
    4118             :     /*      O(nsquared) time as the list gets longer.                       */
    4119             :     /* -------------------------------------------------------------------- */
    4120        3621 :     if (*ppsTail == nullptr)
    4121         679 :         CPLAddXMLChild(*ppsRoot, psItem);
    4122             :     else
    4123        2942 :         CPLAddXMLSibling(*ppsTail, psItem);
    4124             : 
    4125        3621 :     *ppsTail = psItem;
    4126        3621 : }
    4127             : 
    4128             : /************************************************************************/
    4129             : /*                         AppendMetadataItem()                         */
    4130             : /************************************************************************/
    4131             : 
    4132        3615 : static void AppendMetadataItem(CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
    4133             :                                const char *pszKey, const char *pszValue,
    4134             :                                int nBand, const char *pszRole,
    4135             :                                const char *pszDomain)
    4136             : 
    4137             : {
    4138        3615 :     AppendMetadataItem(ppsRoot, ppsTail, pszKey, pszValue, nullptr, nBand,
    4139             :                        pszRole, pszDomain);
    4140        3615 : }
    4141             : 
    4142             : /************************************************************************/
    4143             : /*                         WriteMDMetadata()                            */
    4144             : /************************************************************************/
    4145             : 
    4146      310376 : static void WriteMDMetadata(GDALMultiDomainMetadata *poMDMD, TIFF *hTIFF,
    4147             :                             CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
    4148             :                             int nBand, GTiffProfile eProfile)
    4149             : 
    4150             : {
    4151             : 
    4152             :     /* ==================================================================== */
    4153             :     /*      Process each domain.                                            */
    4154             :     /* ==================================================================== */
    4155      310376 :     CSLConstList papszDomainList = poMDMD->GetDomainList();
    4156      318507 :     for (int iDomain = 0; papszDomainList && papszDomainList[iDomain];
    4157             :          ++iDomain)
    4158             :     {
    4159        8131 :         CSLConstList papszMD = poMDMD->GetMetadata(papszDomainList[iDomain]);
    4160        8131 :         bool bIsXMLOrJSON = false;
    4161             : 
    4162        8131 :         if (EQUAL(papszDomainList[iDomain], "IMAGE_STRUCTURE") ||
    4163        2381 :             EQUAL(papszDomainList[iDomain], "DERIVED_SUBDATASETS"))
    4164        5753 :             continue;  // Ignored.
    4165        2378 :         if (EQUAL(papszDomainList[iDomain], "COLOR_PROFILE"))
    4166           3 :             continue;  // Handled elsewhere.
    4167        2375 :         if (EQUAL(papszDomainList[iDomain], MD_DOMAIN_RPC))
    4168           7 :             continue;  // Handled elsewhere.
    4169        2369 :         if (EQUAL(papszDomainList[iDomain], "xml:ESRI") &&
    4170           1 :             CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
    4171           1 :             continue;  // Handled elsewhere.
    4172        2367 :         if (EQUAL(papszDomainList[iDomain], "xml:XMP"))
    4173           2 :             continue;  // Handled in SetMetadata.
    4174             : 
    4175        2365 :         if (STARTS_WITH_CI(papszDomainList[iDomain], "xml:") ||
    4176        2363 :             STARTS_WITH_CI(papszDomainList[iDomain], "json:"))
    4177             :         {
    4178          11 :             bIsXMLOrJSON = true;
    4179             :         }
    4180             : 
    4181             :         /* --------------------------------------------------------------------
    4182             :          */
    4183             :         /*      Process each item in this domain. */
    4184             :         /* --------------------------------------------------------------------
    4185             :          */
    4186        7173 :         for (int iItem = 0; papszMD && papszMD[iItem]; ++iItem)
    4187             :         {
    4188        4808 :             const char *pszItemValue = nullptr;
    4189        4808 :             char *pszItemName = nullptr;
    4190             : 
    4191        4808 :             if (bIsXMLOrJSON)
    4192             :             {
    4193          10 :                 pszItemName = CPLStrdup("doc");
    4194          10 :                 pszItemValue = papszMD[iItem];
    4195             :             }
    4196             :             else
    4197             :             {
    4198        4798 :                 pszItemValue = CPLParseNameValue(papszMD[iItem], &pszItemName);
    4199        4798 :                 if (pszItemName == nullptr)
    4200             :                 {
    4201          49 :                     CPLDebug("GTiff", "Invalid metadata item : %s",
    4202          49 :                              papszMD[iItem]);
    4203          49 :                     continue;
    4204             :                 }
    4205             :             }
    4206             : 
    4207             :             /* --------------------------------------------------------------------
    4208             :              */
    4209             :             /*      Convert into XML item or handle as a special TIFF tag. */
    4210             :             /* --------------------------------------------------------------------
    4211             :              */
    4212        4759 :             if (strlen(papszDomainList[iDomain]) == 0 && nBand == 0 &&
    4213        3590 :                 (STARTS_WITH_CI(pszItemName, "TIFFTAG_") ||
    4214        3529 :                  (EQUAL(pszItemName, "GEO_METADATA") &&
    4215        3528 :                   eProfile == GTiffProfile::GDALGEOTIFF) ||
    4216        3528 :                  (EQUAL(pszItemName, "TIFF_RSID") &&
    4217             :                   eProfile == GTiffProfile::GDALGEOTIFF)))
    4218             :             {
    4219          63 :                 if (EQUAL(pszItemName, "TIFFTAG_RESOLUTIONUNIT"))
    4220             :                 {
    4221             :                     // ResolutionUnit can't be 0, which is the default if
    4222             :                     // atoi() fails.  Set to 1=Unknown.
    4223           9 :                     int v = atoi(pszItemValue);
    4224           9 :                     if (!v)
    4225           1 :                         v = RESUNIT_NONE;
    4226           9 :                     TIFFSetField(hTIFF, TIFFTAG_RESOLUTIONUNIT, v);
    4227             :                 }
    4228             :                 else
    4229             :                 {
    4230          54 :                     bool bFoundTag = false;
    4231          54 :                     size_t iTag = 0;  // Used after for.
    4232          54 :                     const auto *pasTIFFTags = GTiffDataset::GetTIFFTags();
    4233         286 :                     for (; pasTIFFTags[iTag].pszTagName; ++iTag)
    4234             :                     {
    4235         286 :                         if (EQUAL(pszItemName, pasTIFFTags[iTag].pszTagName))
    4236             :                         {
    4237          54 :                             bFoundTag = true;
    4238          54 :                             break;
    4239             :                         }
    4240             :                     }
    4241             : 
    4242          54 :                     if (bFoundTag &&
    4243          54 :                         pasTIFFTags[iTag].eType == GTIFFTAGTYPE_STRING)
    4244          33 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4245             :                                      pszItemValue);
    4246          21 :                     else if (bFoundTag &&
    4247          21 :                              pasTIFFTags[iTag].eType == GTIFFTAGTYPE_FLOAT)
    4248          16 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4249             :                                      CPLAtof(pszItemValue));
    4250           5 :                     else if (bFoundTag &&
    4251           5 :                              pasTIFFTags[iTag].eType == GTIFFTAGTYPE_SHORT)
    4252           4 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4253             :                                      atoi(pszItemValue));
    4254           1 :                     else if (bFoundTag && pasTIFFTags[iTag].eType ==
    4255             :                                               GTIFFTAGTYPE_BYTE_STRING)
    4256             :                     {
    4257           1 :                         uint32_t nLen =
    4258           1 :                             static_cast<uint32_t>(strlen(pszItemValue));
    4259           1 :                         if (nLen)
    4260             :                         {
    4261           1 :                             TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal, nLen,
    4262             :                                          pszItemValue);
    4263           1 :                         }
    4264             :                     }
    4265             :                     else
    4266           0 :                         CPLError(CE_Warning, CPLE_NotSupported,
    4267             :                                  "%s metadata item is unhandled and "
    4268             :                                  "will not be written",
    4269             :                                  pszItemName);
    4270          63 :                 }
    4271             :             }
    4272        4696 :             else if (nBand == 0 && EQUAL(pszItemName, GDALMD_AREA_OR_POINT))
    4273             :             {
    4274             :                 /* Do nothing, handled elsewhere. */;
    4275             :             }
    4276             :             else
    4277             :             {
    4278        2834 :                 AppendMetadataItem(ppsRoot, ppsTail, pszItemName, pszItemValue,
    4279        2834 :                                    nBand, nullptr, papszDomainList[iDomain]);
    4280             :             }
    4281             : 
    4282        4759 :             CPLFree(pszItemName);
    4283             :         }
    4284             : 
    4285             :         /* --------------------------------------------------------------------
    4286             :          */
    4287             :         /*      Remove TIFFTAG_xxxxxx that are already set but no longer in */
    4288             :         /*      the metadata list (#5619) */
    4289             :         /* --------------------------------------------------------------------
    4290             :          */
    4291        2365 :         if (strlen(papszDomainList[iDomain]) == 0 && nBand == 0)
    4292             :         {
    4293        2126 :             const auto *pasTIFFTags = GTiffDataset::GetTIFFTags();
    4294       31890 :             for (size_t iTag = 0; pasTIFFTags[iTag].pszTagName; ++iTag)
    4295             :             {
    4296       29764 :                 uint32_t nCount = 0;
    4297       29764 :                 char *pszText = nullptr;
    4298       29764 :                 int16_t nVal = 0;
    4299       29764 :                 float fVal = 0.0f;
    4300             :                 const char *pszVal =
    4301       29764 :                     CSLFetchNameValue(papszMD, pasTIFFTags[iTag].pszTagName);
    4302       59465 :                 if (pszVal == nullptr &&
    4303       29701 :                     ((pasTIFFTags[iTag].eType == GTIFFTAGTYPE_STRING &&
    4304       16975 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4305       29693 :                                    &pszText)) ||
    4306       29693 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_SHORT &&
    4307        6365 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &nVal)) ||
    4308       29690 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_FLOAT &&
    4309        4236 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &fVal)) ||
    4310       29689 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_BYTE_STRING &&
    4311        2125 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &nCount,
    4312             :                                    &pszText))))
    4313             :                 {
    4314          13 :                     TIFFUnsetField(hTIFF, pasTIFFTags[iTag].nTagVal);
    4315             :                 }
    4316             :             }
    4317             :         }
    4318             :     }
    4319      310376 : }
    4320             : 
    4321             : /************************************************************************/
    4322             : /*                           WriteRPC()                                 */
    4323             : /************************************************************************/
    4324             : 
    4325        9761 : void GTiffDataset::WriteRPC(GDALDataset *poSrcDS, TIFF *l_hTIFF,
    4326             :                             int bSrcIsGeoTIFF, GTiffProfile eProfile,
    4327             :                             const char *pszTIFFFilename,
    4328             :                             CSLConstList papszCreationOptions,
    4329             :                             bool bWriteOnlyInPAMIfNeeded)
    4330             : {
    4331             :     /* -------------------------------------------------------------------- */
    4332             :     /*      Handle RPC data written to TIFF RPCCoefficient tag, RPB file,   */
    4333             :     /*      RPCTEXT file or PAM.                                            */
    4334             :     /* -------------------------------------------------------------------- */
    4335        9761 :     char **papszRPCMD = poSrcDS->GetMetadata(MD_DOMAIN_RPC);
    4336        9761 :     if (papszRPCMD != nullptr)
    4337             :     {
    4338          32 :         bool bRPCSerializedOtherWay = false;
    4339             : 
    4340          32 :         if (eProfile == GTiffProfile::GDALGEOTIFF)
    4341             :         {
    4342          20 :             if (!bWriteOnlyInPAMIfNeeded)
    4343          11 :                 GTiffDatasetWriteRPCTag(l_hTIFF, papszRPCMD);
    4344          20 :             bRPCSerializedOtherWay = true;
    4345             :         }
    4346             : 
    4347             :         // Write RPB file if explicitly asked, or if a non GDAL specific
    4348             :         // profile is selected and RPCTXT is not asked.
    4349             :         bool bRPBExplicitlyAsked =
    4350          32 :             CPLFetchBool(papszCreationOptions, "RPB", false);
    4351             :         bool bRPBExplicitlyDenied =
    4352          32 :             !CPLFetchBool(papszCreationOptions, "RPB", true);
    4353          44 :         if ((eProfile != GTiffProfile::GDALGEOTIFF &&
    4354          12 :              !CPLFetchBool(papszCreationOptions, "RPCTXT", false) &&
    4355          44 :              !bRPBExplicitlyDenied) ||
    4356             :             bRPBExplicitlyAsked)
    4357             :         {
    4358           8 :             if (!bWriteOnlyInPAMIfNeeded)
    4359           4 :                 GDALWriteRPBFile(pszTIFFFilename, papszRPCMD);
    4360           8 :             bRPCSerializedOtherWay = true;
    4361             :         }
    4362             : 
    4363          32 :         if (CPLFetchBool(papszCreationOptions, "RPCTXT", false))
    4364             :         {
    4365           2 :             if (!bWriteOnlyInPAMIfNeeded)
    4366           1 :                 GDALWriteRPCTXTFile(pszTIFFFilename, papszRPCMD);
    4367           2 :             bRPCSerializedOtherWay = true;
    4368             :         }
    4369             : 
    4370          32 :         if (!bRPCSerializedOtherWay && bWriteOnlyInPAMIfNeeded && bSrcIsGeoTIFF)
    4371           1 :             cpl::down_cast<GTiffDataset *>(poSrcDS)
    4372           1 :                 ->GDALPamDataset::SetMetadata(papszRPCMD, MD_DOMAIN_RPC);
    4373             :     }
    4374        9761 : }
    4375             : 
    4376             : /************************************************************************/
    4377             : /*                           WriteMetadata()                            */
    4378             : /************************************************************************/
    4379             : 
    4380        7732 : bool GTiffDataset::WriteMetadata(GDALDataset *poSrcDS, TIFF *l_hTIFF,
    4381             :                                  bool bSrcIsGeoTIFF, GTiffProfile eProfile,
    4382             :                                  const char *pszTIFFFilename,
    4383             :                                  CSLConstList papszCreationOptions,
    4384             :                                  bool bExcludeRPBandIMGFileWriting)
    4385             : 
    4386             : {
    4387             :     /* -------------------------------------------------------------------- */
    4388             :     /*      Convert all the remaining metadata into a simple XML            */
    4389             :     /*      format.                                                         */
    4390             :     /* -------------------------------------------------------------------- */
    4391        7732 :     CPLXMLNode *psRoot = nullptr;
    4392        7732 :     CPLXMLNode *psTail = nullptr;
    4393             : 
    4394             :     const char *pszCopySrcMDD =
    4395        7732 :         CSLFetchNameValueDef(papszCreationOptions, "COPY_SRC_MDD", "AUTO");
    4396             :     char **papszSrcMDD =
    4397        7732 :         CSLFetchNameValueMultiple(papszCreationOptions, "SRC_MDD");
    4398             : 
    4399             :     GTiffDataset *poSrcDSGTiff =
    4400        7732 :         bSrcIsGeoTIFF ? cpl::down_cast<GTiffDataset *>(poSrcDS) : nullptr;
    4401             : 
    4402        7732 :     if (poSrcDSGTiff)
    4403             :     {
    4404        5676 :         WriteMDMetadata(&poSrcDSGTiff->m_oGTiffMDMD, l_hTIFF, &psRoot, &psTail,
    4405             :                         0, eProfile);
    4406             :     }
    4407             :     else
    4408             :     {
    4409        2056 :         if (EQUAL(pszCopySrcMDD, "AUTO") || CPLTestBool(pszCopySrcMDD) ||
    4410             :             papszSrcMDD)
    4411             :         {
    4412        4106 :             GDALMultiDomainMetadata l_oMDMD;
    4413             :             {
    4414        2053 :                 CSLConstList papszMD = poSrcDS->GetMetadata();
    4415        2057 :                 if (CSLCount(papszMD) > 0 &&
    4416           4 :                     (!papszSrcMDD || CSLFindString(papszSrcMDD, "") >= 0 ||
    4417           2 :                      CSLFindString(papszSrcMDD, "_DEFAULT_") >= 0))
    4418             :                 {
    4419        1577 :                     l_oMDMD.SetMetadata(papszMD);
    4420             :                 }
    4421             :             }
    4422             : 
    4423        2053 :             if (EQUAL(pszCopySrcMDD, "AUTO") && !papszSrcMDD)
    4424             :             {
    4425             :                 // Propagate ISIS3 or VICAR metadata
    4426        6132 :                 for (const char *pszMDD : {"json:ISIS3", "json:VICAR"})
    4427             :                 {
    4428        4088 :                     char **papszMD = poSrcDS->GetMetadata(pszMDD);
    4429        4088 :                     if (papszMD)
    4430             :                     {
    4431           5 :                         l_oMDMD.SetMetadata(papszMD, pszMDD);
    4432             :                     }
    4433             :                 }
    4434             :             }
    4435             : 
    4436        2053 :             if ((!EQUAL(pszCopySrcMDD, "AUTO") && CPLTestBool(pszCopySrcMDD)) ||
    4437             :                 papszSrcMDD)
    4438             :             {
    4439           9 :                 char **papszDomainList = poSrcDS->GetMetadataDomainList();
    4440          39 :                 for (CSLConstList papszIter = papszDomainList;
    4441          39 :                      papszIter && *papszIter; ++papszIter)
    4442             :                 {
    4443          30 :                     const char *pszDomain = *papszIter;
    4444          46 :                     if (pszDomain[0] != 0 &&
    4445          16 :                         (!papszSrcMDD ||
    4446          16 :                          CSLFindString(papszSrcMDD, pszDomain) >= 0))
    4447             :                     {
    4448          12 :                         l_oMDMD.SetMetadata(poSrcDS->GetMetadata(pszDomain),
    4449             :                                             pszDomain);
    4450             :                     }
    4451             :                 }
    4452           9 :                 CSLDestroy(papszDomainList);
    4453             :             }
    4454             : 
    4455        2053 :             WriteMDMetadata(&l_oMDMD, l_hTIFF, &psRoot, &psTail, 0, eProfile);
    4456             :         }
    4457             :     }
    4458             : 
    4459        7732 :     if (!bExcludeRPBandIMGFileWriting &&
    4460        5670 :         (!poSrcDSGTiff || poSrcDSGTiff->m_poBaseDS == nullptr))
    4461             :     {
    4462        7721 :         WriteRPC(poSrcDS, l_hTIFF, bSrcIsGeoTIFF, eProfile, pszTIFFFilename,
    4463             :                  papszCreationOptions);
    4464             : 
    4465             :         /* --------------------------------------------------------------------
    4466             :          */
    4467             :         /*      Handle metadata data written to an IMD file. */
    4468             :         /* --------------------------------------------------------------------
    4469             :          */
    4470        7721 :         char **papszIMDMD = poSrcDS->GetMetadata(MD_DOMAIN_IMD);
    4471        7721 :         if (papszIMDMD != nullptr)
    4472             :         {
    4473          20 :             GDALWriteIMDFile(pszTIFFFilename, papszIMDMD);
    4474             :         }
    4475             :     }
    4476             : 
    4477        7732 :     uint16_t nPhotometric = 0;
    4478        7732 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric)))
    4479           1 :         nPhotometric = PHOTOMETRIC_MINISBLACK;
    4480             : 
    4481        7732 :     const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
    4482             :         GDALDataset::ToHandle(poSrcDS), nPhotometric, papszCreationOptions);
    4483             : 
    4484             :     /* -------------------------------------------------------------------- */
    4485             :     /*      We also need to address band specific metadata, and special     */
    4486             :     /*      "role" metadata.                                                */
    4487             :     /* -------------------------------------------------------------------- */
    4488      315216 :     for (int nBand = 1; nBand <= poSrcDS->GetRasterCount(); ++nBand)
    4489             :     {
    4490      307484 :         GDALRasterBand *poBand = poSrcDS->GetRasterBand(nBand);
    4491             : 
    4492      307484 :         if (bSrcIsGeoTIFF)
    4493             :         {
    4494             :             GTiffRasterBand *poSrcBandGTiff =
    4495      302556 :                 cpl::down_cast<GTiffRasterBand *>(poBand);
    4496      302556 :             assert(poSrcBandGTiff);
    4497      302556 :             WriteMDMetadata(&poSrcBandGTiff->m_oGTiffMDMD, l_hTIFF, &psRoot,
    4498             :                             &psTail, nBand, eProfile);
    4499             :         }
    4500             :         else
    4501             :         {
    4502        9856 :             GDALMultiDomainMetadata l_oMDMD;
    4503        4928 :             bool bOMDMDSet = false;
    4504             : 
    4505        4928 :             if (EQUAL(pszCopySrcMDD, "AUTO") && !papszSrcMDD)
    4506             :             {
    4507       14748 :                 for (const char *pszDomain : {"", "IMAGERY"})
    4508             :                 {
    4509        9832 :                     if (CSLConstList papszMD = poBand->GetMetadata(pszDomain))
    4510             :                     {
    4511          89 :                         if (papszMD[0])
    4512             :                         {
    4513          89 :                             bOMDMDSet = true;
    4514          89 :                             l_oMDMD.SetMetadata(papszMD, pszDomain);
    4515             :                         }
    4516             :                     }
    4517        4916 :                 }
    4518             :             }
    4519          12 :             else if (CPLTestBool(pszCopySrcMDD) || papszSrcMDD)
    4520             :             {
    4521           9 :                 char **papszDomainList = poBand->GetMetadataDomainList();
    4522           3 :                 for (const char *pszDomain :
    4523          15 :                      cpl::Iterate(CSLConstList(papszDomainList)))
    4524             :                 {
    4525           9 :                     if (pszDomain[0] != 0 &&
    4526           5 :                         !EQUAL(pszDomain, "IMAGE_STRUCTURE") &&
    4527           2 :                         (!papszSrcMDD ||
    4528           2 :                          CSLFindString(papszSrcMDD, pszDomain) >= 0))
    4529             :                     {
    4530           2 :                         bOMDMDSet = true;
    4531           2 :                         l_oMDMD.SetMetadata(poBand->GetMetadata(pszDomain),
    4532             :                                             pszDomain);
    4533             :                     }
    4534             :                 }
    4535           9 :                 CSLDestroy(papszDomainList);
    4536             :             }
    4537             : 
    4538        4928 :             if (bOMDMDSet)
    4539             :             {
    4540          91 :                 WriteMDMetadata(&l_oMDMD, l_hTIFF, &psRoot, &psTail, nBand,
    4541             :                                 eProfile);
    4542             :             }
    4543             :         }
    4544             : 
    4545      307484 :         const double dfOffset = poBand->GetOffset();
    4546      307484 :         const double dfScale = poBand->GetScale();
    4547      307484 :         bool bGeoTIFFScaleOffsetInZ = false;
    4548      307484 :         GDALGeoTransform gt;
    4549             :         // Check if we have already encoded scale/offset in the GeoTIFF tags
    4550      313297 :         if (poSrcDS->GetGeoTransform(gt) == CE_None && gt[2] == 0.0 &&
    4551        5798 :             gt[4] == 0.0 && gt[5] < 0.0 && poSrcDS->GetSpatialRef() &&
    4552      313304 :             poSrcDS->GetSpatialRef()->IsVertical() &&
    4553           7 :             poSrcDS->GetRasterCount() == 1)
    4554             :         {
    4555           7 :             bGeoTIFFScaleOffsetInZ = true;
    4556             :         }
    4557             : 
    4558      307484 :         if ((dfOffset != 0.0 || dfScale != 1.0) && !bGeoTIFFScaleOffsetInZ)
    4559             :         {
    4560          25 :             char szValue[128] = {};
    4561             : 
    4562          25 :             CPLsnprintf(szValue, sizeof(szValue), "%.17g", dfOffset);
    4563          25 :             AppendMetadataItem(&psRoot, &psTail, "OFFSET", szValue, nBand,
    4564             :                                "offset", "");
    4565          25 :             CPLsnprintf(szValue, sizeof(szValue), "%.17g", dfScale);
    4566          25 :             AppendMetadataItem(&psRoot, &psTail, "SCALE", szValue, nBand,
    4567             :                                "scale", "");
    4568             :         }
    4569             : 
    4570      307484 :         const char *pszUnitType = poBand->GetUnitType();
    4571      307484 :         if (pszUnitType != nullptr && pszUnitType[0] != '\0')
    4572             :         {
    4573          37 :             bool bWriteUnit = true;
    4574          37 :             auto poSRS = poSrcDS->GetSpatialRef();
    4575          37 :             if (poSRS && poSRS->IsCompound())
    4576             :             {
    4577           2 :                 const char *pszVertUnit = nullptr;
    4578           2 :                 poSRS->GetTargetLinearUnits("COMPD_CS|VERT_CS", &pszVertUnit);
    4579           2 :                 if (pszVertUnit && EQUAL(pszVertUnit, pszUnitType))
    4580             :                 {
    4581           2 :                     bWriteUnit = false;
    4582             :                 }
    4583             :             }
    4584          37 :             if (bWriteUnit)
    4585             :             {
    4586          35 :                 AppendMetadataItem(&psRoot, &psTail, "UNITTYPE", pszUnitType,
    4587             :                                    nBand, "unittype", "");
    4588             :             }
    4589             :         }
    4590             : 
    4591      307484 :         if (strlen(poBand->GetDescription()) > 0)
    4592             :         {
    4593          15 :             AppendMetadataItem(&psRoot, &psTail, "DESCRIPTION",
    4594          15 :                                poBand->GetDescription(), nBand, "description",
    4595             :                                "");
    4596             :         }
    4597             : 
    4598      307701 :         if (!bStandardColorInterp &&
    4599         217 :             !(nBand <= 3 && EQUAL(CSLFetchNameValueDef(papszCreationOptions,
    4600             :                                                        "PHOTOMETRIC", ""),
    4601             :                                   "RGB")))
    4602             :         {
    4603         250 :             AppendMetadataItem(&psRoot, &psTail, "COLORINTERP",
    4604             :                                GDALGetColorInterpretationName(
    4605         250 :                                    poBand->GetColorInterpretation()),
    4606             :                                nBand, "colorinterp", "");
    4607             :         }
    4608             :     }
    4609             : 
    4610        7732 :     CSLDestroy(papszSrcMDD);
    4611             : 
    4612             :     const char *pszTilingSchemeName =
    4613        7732 :         CSLFetchNameValue(papszCreationOptions, "@TILING_SCHEME_NAME");
    4614        7732 :     if (pszTilingSchemeName)
    4615             :     {
    4616          23 :         AppendMetadataItem(&psRoot, &psTail, "NAME", pszTilingSchemeName, 0,
    4617             :                            nullptr, "TILING_SCHEME");
    4618             : 
    4619          23 :         const char *pszZoomLevel = CSLFetchNameValue(
    4620             :             papszCreationOptions, "@TILING_SCHEME_ZOOM_LEVEL");
    4621          23 :         if (pszZoomLevel)
    4622             :         {
    4623          23 :             AppendMetadataItem(&psRoot, &psTail, "ZOOM_LEVEL", pszZoomLevel, 0,
    4624             :                                nullptr, "TILING_SCHEME");
    4625             :         }
    4626             : 
    4627          23 :         const char *pszAlignedLevels = CSLFetchNameValue(
    4628             :             papszCreationOptions, "@TILING_SCHEME_ALIGNED_LEVELS");
    4629          23 :         if (pszAlignedLevels)
    4630             :         {
    4631           4 :             AppendMetadataItem(&psRoot, &psTail, "ALIGNED_LEVELS",
    4632             :                                pszAlignedLevels, 0, nullptr, "TILING_SCHEME");
    4633             :         }
    4634             :     }
    4635             : 
    4636        7732 :     if (const char *pszOverviewResampling =
    4637        7732 :             CSLFetchNameValue(papszCreationOptions, "@OVERVIEW_RESAMPLING"))
    4638             :     {
    4639          39 :         AppendMetadataItem(&psRoot, &psTail, "OVERVIEW_RESAMPLING",
    4640             :                            pszOverviewResampling, 0, nullptr,
    4641             :                            "IMAGE_STRUCTURE");
    4642             :     }
    4643             : 
    4644             :     /* -------------------------------------------------------------------- */
    4645             :     /*      Write information about some codecs.                            */
    4646             :     /* -------------------------------------------------------------------- */
    4647        7732 :     if (CPLTestBool(
    4648             :             CPLGetConfigOption("GTIFF_WRITE_IMAGE_STRUCTURE_METADATA", "YES")))
    4649             :     {
    4650             :         const char *pszTileInterleave =
    4651        7727 :             CSLFetchNameValue(papszCreationOptions, "@TILE_INTERLEAVE");
    4652        7727 :         if (pszTileInterleave && CPLTestBool(pszTileInterleave))
    4653             :         {
    4654           7 :             AppendMetadataItem(&psRoot, &psTail, "INTERLEAVE", "TILE", 0,
    4655             :                                nullptr, "IMAGE_STRUCTURE");
    4656             :         }
    4657             : 
    4658             :         const char *pszCompress =
    4659        7727 :             CSLFetchNameValue(papszCreationOptions, "COMPRESS");
    4660        7727 :         if (pszCompress && EQUAL(pszCompress, "WEBP"))
    4661             :         {
    4662          31 :             if (GTiffGetWebPLossless(papszCreationOptions))
    4663             :             {
    4664           6 :                 AppendMetadataItem(&psRoot, &psTail,
    4665             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4666             :                                    nullptr, "IMAGE_STRUCTURE");
    4667             :             }
    4668             :             else
    4669             :             {
    4670          25 :                 AppendMetadataItem(
    4671             :                     &psRoot, &psTail, "WEBP_LEVEL",
    4672          25 :                     CPLSPrintf("%d", GTiffGetWebPLevel(papszCreationOptions)),
    4673             :                     0, nullptr, "IMAGE_STRUCTURE");
    4674             :             }
    4675             :         }
    4676        7696 :         else if (pszCompress && STARTS_WITH_CI(pszCompress, "LERC"))
    4677             :         {
    4678             :             const double dfMaxZError =
    4679          97 :                 GTiffGetLERCMaxZError(papszCreationOptions);
    4680             :             const double dfMaxZErrorOverview =
    4681          97 :                 GTiffGetLERCMaxZErrorOverview(papszCreationOptions);
    4682          97 :             if (dfMaxZError == 0.0 && dfMaxZErrorOverview == 0.0)
    4683             :             {
    4684          83 :                 AppendMetadataItem(&psRoot, &psTail,
    4685             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4686             :                                    nullptr, "IMAGE_STRUCTURE");
    4687             :             }
    4688             :             else
    4689             :             {
    4690          14 :                 AppendMetadataItem(&psRoot, &psTail, "MAX_Z_ERROR",
    4691             :                                    CSLFetchNameValueDef(papszCreationOptions,
    4692             :                                                         "MAX_Z_ERROR", ""),
    4693             :                                    0, nullptr, "IMAGE_STRUCTURE");
    4694          14 :                 if (dfMaxZError != dfMaxZErrorOverview)
    4695             :                 {
    4696           3 :                     AppendMetadataItem(
    4697             :                         &psRoot, &psTail, "MAX_Z_ERROR_OVERVIEW",
    4698             :                         CSLFetchNameValueDef(papszCreationOptions,
    4699             :                                              "MAX_Z_ERROR_OVERVIEW", ""),
    4700             :                         0, nullptr, "IMAGE_STRUCTURE");
    4701             :                 }
    4702          97 :             }
    4703             :         }
    4704             : #if HAVE_JXL
    4705        7599 :         else if (pszCompress && EQUAL(pszCompress, "JXL"))
    4706             :         {
    4707         101 :             float fDistance = 0.0f;
    4708         101 :             if (GTiffGetJXLLossless(papszCreationOptions))
    4709             :             {
    4710          82 :                 AppendMetadataItem(&psRoot, &psTail,
    4711             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4712             :                                    nullptr, "IMAGE_STRUCTURE");
    4713             :             }
    4714             :             else
    4715             :             {
    4716          19 :                 fDistance = GTiffGetJXLDistance(papszCreationOptions);
    4717          19 :                 AppendMetadataItem(
    4718             :                     &psRoot, &psTail, "JXL_DISTANCE",
    4719             :                     CPLSPrintf("%f", static_cast<double>(fDistance)), 0,
    4720             :                     nullptr, "IMAGE_STRUCTURE");
    4721             :             }
    4722             :             const float fAlphaDistance =
    4723         101 :                 GTiffGetJXLAlphaDistance(papszCreationOptions);
    4724         101 :             if (fAlphaDistance >= 0.0f && fAlphaDistance != fDistance)
    4725             :             {
    4726           2 :                 AppendMetadataItem(
    4727             :                     &psRoot, &psTail, "JXL_ALPHA_DISTANCE",
    4728             :                     CPLSPrintf("%f", static_cast<double>(fAlphaDistance)), 0,
    4729             :                     nullptr, "IMAGE_STRUCTURE");
    4730             :             }
    4731         101 :             AppendMetadataItem(
    4732             :                 &psRoot, &psTail, "JXL_EFFORT",
    4733             :                 CPLSPrintf("%d", GTiffGetJXLEffort(papszCreationOptions)), 0,
    4734             :                 nullptr, "IMAGE_STRUCTURE");
    4735             :         }
    4736             : #endif
    4737             :     }
    4738             : 
    4739        7732 :     if (!CPLTestBool(CPLGetConfigOption("GTIFF_WRITE_RAT_TO_PAM", "NO")))
    4740             :     {
    4741      315212 :         for (int nBand = 1; nBand <= poSrcDS->GetRasterCount(); ++nBand)
    4742             :         {
    4743      307482 :             GDALRasterBand *poBand = poSrcDS->GetRasterBand(nBand);
    4744      307482 :             const auto poRAT = poBand->GetDefaultRAT();
    4745      307482 :             if (poRAT)
    4746             :             {
    4747           7 :                 auto psSerializedRAT = poRAT->Serialize();
    4748           7 :                 if (psSerializedRAT)
    4749             :                 {
    4750           6 :                     AppendMetadataItem(
    4751             :                         &psRoot, &psTail, DEFAULT_RASTER_ATTRIBUTE_TABLE,
    4752             :                         nullptr, psSerializedRAT, nBand, RAT_ROLE, nullptr);
    4753             :                 }
    4754             :             }
    4755             :         }
    4756             :     }
    4757             : 
    4758             :     /* -------------------------------------------------------------------- */
    4759             :     /*      Write out the generic XML metadata if there is any.             */
    4760             :     /* -------------------------------------------------------------------- */
    4761        7732 :     if (psRoot != nullptr)
    4762             :     {
    4763         679 :         bool bRet = true;
    4764             : 
    4765         679 :         if (eProfile == GTiffProfile::GDALGEOTIFF)
    4766             :         {
    4767         662 :             char *pszXML_MD = CPLSerializeXMLTree(psRoot);
    4768         662 :             TIFFSetField(l_hTIFF, TIFFTAG_GDAL_METADATA, pszXML_MD);
    4769         662 :             CPLFree(pszXML_MD);
    4770             :         }
    4771             :         else
    4772             :         {
    4773          17 :             if (bSrcIsGeoTIFF)
    4774          11 :                 cpl::down_cast<GTiffDataset *>(poSrcDS)->PushMetadataToPam();
    4775             :             else
    4776           6 :                 bRet = false;
    4777             :         }
    4778             : 
    4779         679 :         CPLDestroyXMLNode(psRoot);
    4780             : 
    4781         679 :         return bRet;
    4782             :     }
    4783             : 
    4784             :     // If we have no more metadata but it existed before,
    4785             :     // remove the GDAL_METADATA tag.
    4786        7053 :     if (eProfile == GTiffProfile::GDALGEOTIFF)
    4787             :     {
    4788        7029 :         char *pszText = nullptr;
    4789        7029 :         if (TIFFGetField(l_hTIFF, TIFFTAG_GDAL_METADATA, &pszText))
    4790             :         {
    4791           7 :             TIFFUnsetField(l_hTIFF, TIFFTAG_GDAL_METADATA);
    4792             :         }
    4793             :     }
    4794             : 
    4795        7053 :     return true;
    4796             : }
    4797             : 
    4798             : /************************************************************************/
    4799             : /*                         PushMetadataToPam()                          */
    4800             : /*                                                                      */
    4801             : /*      When producing a strict profile TIFF or if our aggregate        */
    4802             : /*      metadata is too big for a single tiff tag we may end up         */
    4803             : /*      needing to write it via the PAM mechanisms.  This method        */
    4804             : /*      copies all the appropriate metadata into the PAM level          */
    4805             : /*      metadata object but with special care to avoid copying          */
    4806             : /*      metadata handled in other ways in TIFF format.                  */
    4807             : /************************************************************************/
    4808             : 
    4809          17 : void GTiffDataset::PushMetadataToPam()
    4810             : 
    4811             : {
    4812          17 :     if (GetPamFlags() & GPF_DISABLED)
    4813           0 :         return;
    4814             : 
    4815          17 :     const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
    4816          17 :         GDALDataset::ToHandle(this), m_nPhotometric, m_papszCreationOptions);
    4817             : 
    4818          55 :     for (int nBand = 0; nBand <= GetRasterCount(); ++nBand)
    4819             :     {
    4820          38 :         GDALMultiDomainMetadata *poSrcMDMD = nullptr;
    4821          38 :         GTiffRasterBand *poBand = nullptr;
    4822             : 
    4823          38 :         if (nBand == 0)
    4824             :         {
    4825          17 :             poSrcMDMD = &(this->m_oGTiffMDMD);
    4826             :         }
    4827             :         else
    4828             :         {
    4829          21 :             poBand = cpl::down_cast<GTiffRasterBand *>(GetRasterBand(nBand));
    4830          21 :             poSrcMDMD = &(poBand->m_oGTiffMDMD);
    4831             :         }
    4832             : 
    4833             :         /* --------------------------------------------------------------------
    4834             :          */
    4835             :         /*      Loop over the available domains. */
    4836             :         /* --------------------------------------------------------------------
    4837             :          */
    4838          38 :         CSLConstList papszDomainList = poSrcMDMD->GetDomainList();
    4839          74 :         for (int iDomain = 0; papszDomainList && papszDomainList[iDomain];
    4840             :              ++iDomain)
    4841             :         {
    4842          36 :             char **papszMD = poSrcMDMD->GetMetadata(papszDomainList[iDomain]);
    4843             : 
    4844          36 :             if (EQUAL(papszDomainList[iDomain], MD_DOMAIN_RPC) ||
    4845          36 :                 EQUAL(papszDomainList[iDomain], MD_DOMAIN_IMD) ||
    4846          36 :                 EQUAL(papszDomainList[iDomain], "_temporary_") ||
    4847          36 :                 EQUAL(papszDomainList[iDomain], "IMAGE_STRUCTURE") ||
    4848          19 :                 EQUAL(papszDomainList[iDomain], "COLOR_PROFILE"))
    4849          17 :                 continue;
    4850             : 
    4851          19 :             papszMD = CSLDuplicate(papszMD);
    4852             : 
    4853          69 :             for (int i = CSLCount(papszMD) - 1; i >= 0; --i)
    4854             :             {
    4855          50 :                 if (STARTS_WITH_CI(papszMD[i], "TIFFTAG_") ||
    4856          50 :                     EQUALN(papszMD[i], GDALMD_AREA_OR_POINT,
    4857             :                            strlen(GDALMD_AREA_OR_POINT)))
    4858           4 :                     papszMD = CSLRemoveStrings(papszMD, i, 1, nullptr);
    4859             :             }
    4860             : 
    4861          19 :             if (nBand == 0)
    4862          10 :                 GDALPamDataset::SetMetadata(papszMD, papszDomainList[iDomain]);
    4863             :             else
    4864           9 :                 poBand->GDALPamRasterBand::SetMetadata(
    4865           9 :                     papszMD, papszDomainList[iDomain]);
    4866             : 
    4867          19 :             CSLDestroy(papszMD);
    4868             :         }
    4869             : 
    4870             :         /* --------------------------------------------------------------------
    4871             :          */
    4872             :         /*      Handle some "special domain" stuff. */
    4873             :         /* --------------------------------------------------------------------
    4874             :          */
    4875          38 :         if (poBand != nullptr)
    4876             :         {
    4877          21 :             poBand->GDALPamRasterBand::SetOffset(poBand->GetOffset());
    4878          21 :             poBand->GDALPamRasterBand::SetScale(poBand->GetScale());
    4879          21 :             poBand->GDALPamRasterBand::SetUnitType(poBand->GetUnitType());
    4880          21 :             poBand->GDALPamRasterBand::SetDescription(poBand->GetDescription());
    4881          21 :             if (!bStandardColorInterp)
    4882             :             {
    4883           3 :                 poBand->GDALPamRasterBand::SetColorInterpretation(
    4884           3 :                     poBand->GetColorInterpretation());
    4885             :             }
    4886             :         }
    4887             :     }
    4888          17 :     MarkPamDirty();
    4889             : }
    4890             : 
    4891             : /************************************************************************/
    4892             : /*                         WriteNoDataValue()                           */
    4893             : /************************************************************************/
    4894             : 
    4895         477 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, double dfNoData)
    4896             : 
    4897             : {
    4898         954 :     CPLString osVal(GTiffFormatGDALNoDataTagValue(dfNoData));
    4899         477 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA, osVal.c_str());
    4900         477 : }
    4901             : 
    4902           3 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, int64_t nNoData)
    4903             : 
    4904             : {
    4905           3 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA,
    4906             :                  CPLSPrintf(CPL_FRMT_GIB, static_cast<GIntBig>(nNoData)));
    4907           3 : }
    4908             : 
    4909           3 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, uint64_t nNoData)
    4910             : 
    4911             : {
    4912           3 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA,
    4913             :                  CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoData)));
    4914           3 : }
    4915             : 
    4916             : /************************************************************************/
    4917             : /*                         UnsetNoDataValue()                           */
    4918             : /************************************************************************/
    4919             : 
    4920          14 : void GTiffDataset::UnsetNoDataValue(TIFF *l_hTIFF)
    4921             : 
    4922             : {
    4923          14 :     TIFFUnsetField(l_hTIFF, TIFFTAG_GDAL_NODATA);
    4924          14 : }
    4925             : 
    4926             : /************************************************************************/
    4927             : /*                             SaveICCProfile()                         */
    4928             : /*                                                                      */
    4929             : /*      Save ICC Profile or colorimetric data into file                 */
    4930             : /* pDS:                                                                 */
    4931             : /*      Dataset that contains the metadata with the ICC or colorimetric */
    4932             : /*      data. If this argument is specified, all other arguments are    */
    4933             : /*      ignored. Set them to NULL or 0.                                 */
    4934             : /* hTIFF:                                                               */
    4935             : /*      Pointer to TIFF handle. Only needed if pDS is NULL or           */
    4936             : /*      pDS->m_hTIFF is NULL.                                             */
    4937             : /* papszParamList:                                                       */
    4938             : /*      Options containing the ICC profile or colorimetric metadata.    */
    4939             : /*      Ignored if pDS is not NULL.                                     */
    4940             : /* nBitsPerSample:                                                      */
    4941             : /*      Bits per sample. Ignored if pDS is not NULL.                    */
    4942             : /************************************************************************/
    4943             : 
    4944        9518 : void GTiffDataset::SaveICCProfile(GTiffDataset *pDS, TIFF *l_hTIFF,
    4945             :                                   char **papszParamList,
    4946             :                                   uint32_t l_nBitsPerSample)
    4947             : {
    4948        9518 :     if ((pDS != nullptr) && (pDS->eAccess != GA_Update))
    4949           0 :         return;
    4950             : 
    4951        9518 :     if (l_hTIFF == nullptr)
    4952             :     {
    4953           2 :         if (pDS == nullptr)
    4954           0 :             return;
    4955             : 
    4956           2 :         l_hTIFF = pDS->m_hTIFF;
    4957           2 :         if (l_hTIFF == nullptr)
    4958           0 :             return;
    4959             :     }
    4960             : 
    4961        9518 :     if ((papszParamList == nullptr) && (pDS == nullptr))
    4962        4699 :         return;
    4963             : 
    4964             :     const char *pszICCProfile =
    4965             :         (pDS != nullptr)
    4966        4819 :             ? pDS->GetMetadataItem("SOURCE_ICC_PROFILE", "COLOR_PROFILE")
    4967        4817 :             : CSLFetchNameValue(papszParamList, "SOURCE_ICC_PROFILE");
    4968        4819 :     if (pszICCProfile != nullptr)
    4969             :     {
    4970           8 :         char *pEmbedBuffer = CPLStrdup(pszICCProfile);
    4971             :         int32_t nEmbedLen =
    4972           8 :             CPLBase64DecodeInPlace(reinterpret_cast<GByte *>(pEmbedBuffer));
    4973             : 
    4974           8 :         TIFFSetField(l_hTIFF, TIFFTAG_ICCPROFILE, nEmbedLen, pEmbedBuffer);
    4975             : 
    4976           8 :         CPLFree(pEmbedBuffer);
    4977             :     }
    4978             :     else
    4979             :     {
    4980             :         // Output colorimetric data.
    4981        4811 :         float pCHR[6] = {};     // Primaries.
    4982        4811 :         uint16_t pTXR[6] = {};  // Transfer range.
    4983        4811 :         const char *pszCHRNames[] = {"SOURCE_PRIMARIES_RED",
    4984             :                                      "SOURCE_PRIMARIES_GREEN",
    4985             :                                      "SOURCE_PRIMARIES_BLUE"};
    4986        4811 :         const char *pszTXRNames[] = {"TIFFTAG_TRANSFERRANGE_BLACK",
    4987             :                                      "TIFFTAG_TRANSFERRANGE_WHITE"};
    4988             : 
    4989             :         // Output chromacities.
    4990        4811 :         bool bOutputCHR = true;
    4991        4826 :         for (int i = 0; i < 3 && bOutputCHR; ++i)
    4992             :         {
    4993             :             const char *pszColorProfile =
    4994             :                 (pDS != nullptr)
    4995        4821 :                     ? pDS->GetMetadataItem(pszCHRNames[i], "COLOR_PROFILE")
    4996        4818 :                     : CSLFetchNameValue(papszParamList, pszCHRNames[i]);
    4997        4821 :             if (pszColorProfile == nullptr)
    4998             :             {
    4999        4806 :                 bOutputCHR = false;
    5000        4806 :                 break;
    5001             :             }
    5002             : 
    5003             :             const CPLStringList aosTokens(CSLTokenizeString2(
    5004             :                 pszColorProfile, ",",
    5005             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5006          15 :                     CSLT_STRIPENDSPACES));
    5007             : 
    5008          15 :             if (aosTokens.size() != 3)
    5009             :             {
    5010           0 :                 bOutputCHR = false;
    5011           0 :                 break;
    5012             :             }
    5013             : 
    5014          60 :             for (int j = 0; j < 3; ++j)
    5015             :             {
    5016          45 :                 float v = static_cast<float>(CPLAtof(aosTokens[j]));
    5017             : 
    5018          45 :                 if (j == 2)
    5019             :                 {
    5020             :                     // Last term of xyY color must be 1.0.
    5021          15 :                     if (v != 1.0f)
    5022             :                     {
    5023           0 :                         bOutputCHR = false;
    5024           0 :                         break;
    5025             :                     }
    5026             :                 }
    5027             :                 else
    5028             :                 {
    5029          30 :                     pCHR[i * 2 + j] = v;
    5030             :                 }
    5031             :             }
    5032             :         }
    5033             : 
    5034        4811 :         if (bOutputCHR)
    5035             :         {
    5036           5 :             TIFFSetField(l_hTIFF, TIFFTAG_PRIMARYCHROMATICITIES, pCHR);
    5037             :         }
    5038             : 
    5039             :         // Output whitepoint.
    5040             :         const char *pszSourceWhitePoint =
    5041             :             (pDS != nullptr)
    5042        4811 :                 ? pDS->GetMetadataItem("SOURCE_WHITEPOINT", "COLOR_PROFILE")
    5043        4810 :                 : CSLFetchNameValue(papszParamList, "SOURCE_WHITEPOINT");
    5044        4811 :         if (pszSourceWhitePoint != nullptr)
    5045             :         {
    5046             :             const CPLStringList aosTokens(CSLTokenizeString2(
    5047             :                 pszSourceWhitePoint, ",",
    5048             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5049          10 :                     CSLT_STRIPENDSPACES));
    5050             : 
    5051           5 :             bool bOutputWhitepoint = true;
    5052           5 :             float pWP[2] = {0.0f, 0.0f};  // Whitepoint
    5053           5 :             if (aosTokens.size() != 3)
    5054             :             {
    5055           0 :                 bOutputWhitepoint = false;
    5056             :             }
    5057             :             else
    5058             :             {
    5059          20 :                 for (int j = 0; j < 3; ++j)
    5060             :                 {
    5061          15 :                     const float v = static_cast<float>(CPLAtof(aosTokens[j]));
    5062             : 
    5063          15 :                     if (j == 2)
    5064             :                     {
    5065             :                         // Last term of xyY color must be 1.0.
    5066           5 :                         if (v != 1.0f)
    5067             :                         {
    5068           0 :                             bOutputWhitepoint = false;
    5069           0 :                             break;
    5070             :                         }
    5071             :                     }
    5072             :                     else
    5073             :                     {
    5074          10 :                         pWP[j] = v;
    5075             :                     }
    5076             :                 }
    5077             :             }
    5078             : 
    5079           5 :             if (bOutputWhitepoint)
    5080             :             {
    5081           5 :                 TIFFSetField(l_hTIFF, TIFFTAG_WHITEPOINT, pWP);
    5082             :             }
    5083             :         }
    5084             : 
    5085             :         // Set transfer function metadata.
    5086             :         char const *pszTFRed =
    5087             :             (pDS != nullptr)
    5088        4811 :                 ? pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_RED",
    5089             :                                        "COLOR_PROFILE")
    5090        4810 :                 : CSLFetchNameValue(papszParamList,
    5091        4811 :                                     "TIFFTAG_TRANSFERFUNCTION_RED");
    5092             : 
    5093             :         char const *pszTFGreen =
    5094             :             (pDS != nullptr)
    5095        4811 :                 ? pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_GREEN",
    5096             :                                        "COLOR_PROFILE")
    5097        4810 :                 : CSLFetchNameValue(papszParamList,
    5098        4811 :                                     "TIFFTAG_TRANSFERFUNCTION_GREEN");
    5099             : 
    5100             :         char const *pszTFBlue =
    5101             :             (pDS != nullptr)
    5102        4811 :                 ? pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_BLUE",
    5103             :                                        "COLOR_PROFILE")
    5104        4810 :                 : CSLFetchNameValue(papszParamList,
    5105        4811 :                                     "TIFFTAG_TRANSFERFUNCTION_BLUE");
    5106             : 
    5107        4811 :         if ((pszTFRed != nullptr) && (pszTFGreen != nullptr) &&
    5108             :             (pszTFBlue != nullptr))
    5109             :         {
    5110             :             // Get length of table.
    5111           4 :             const int nTransferFunctionLength =
    5112           4 :                 1 << ((pDS != nullptr) ? pDS->m_nBitsPerSample
    5113             :                                        : l_nBitsPerSample);
    5114             : 
    5115             :             const CPLStringList aosTokensRed(CSLTokenizeString2(
    5116             :                 pszTFRed, ",",
    5117             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5118           8 :                     CSLT_STRIPENDSPACES));
    5119             :             const CPLStringList aosTokensGreen(CSLTokenizeString2(
    5120             :                 pszTFGreen, ",",
    5121             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5122           8 :                     CSLT_STRIPENDSPACES));
    5123             :             const CPLStringList aosTokensBlue(CSLTokenizeString2(
    5124             :                 pszTFBlue, ",",
    5125             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5126           8 :                     CSLT_STRIPENDSPACES));
    5127             : 
    5128           4 :             if ((aosTokensRed.size() == nTransferFunctionLength) &&
    5129           8 :                 (aosTokensGreen.size() == nTransferFunctionLength) &&
    5130           4 :                 (aosTokensBlue.size() == nTransferFunctionLength))
    5131             :             {
    5132             :                 std::vector<uint16_t> anTransferFuncRed(
    5133           8 :                     nTransferFunctionLength);
    5134             :                 std::vector<uint16_t> anTransferFuncGreen(
    5135           8 :                     nTransferFunctionLength);
    5136             :                 std::vector<uint16_t> anTransferFuncBlue(
    5137           8 :                     nTransferFunctionLength);
    5138             : 
    5139             :                 // Convert our table in string format into int16_t format.
    5140        1028 :                 for (int i = 0; i < nTransferFunctionLength; ++i)
    5141             :                 {
    5142        2048 :                     anTransferFuncRed[i] =
    5143        1024 :                         static_cast<uint16_t>(atoi(aosTokensRed[i]));
    5144        2048 :                     anTransferFuncGreen[i] =
    5145        1024 :                         static_cast<uint16_t>(atoi(aosTokensGreen[i]));
    5146        1024 :                     anTransferFuncBlue[i] =
    5147        1024 :                         static_cast<uint16_t>(atoi(aosTokensBlue[i]));
    5148             :                 }
    5149             : 
    5150           4 :                 TIFFSetField(
    5151             :                     l_hTIFF, TIFFTAG_TRANSFERFUNCTION, anTransferFuncRed.data(),
    5152             :                     anTransferFuncGreen.data(), anTransferFuncBlue.data());
    5153             :             }
    5154             :         }
    5155             : 
    5156             :         // Output transfer range.
    5157        4811 :         bool bOutputTransferRange = true;
    5158        4811 :         for (int i = 0; (i < 2) && bOutputTransferRange; ++i)
    5159             :         {
    5160             :             const char *pszTXRVal =
    5161             :                 (pDS != nullptr)
    5162        4811 :                     ? pDS->GetMetadataItem(pszTXRNames[i], "COLOR_PROFILE")
    5163        4810 :                     : CSLFetchNameValue(papszParamList, pszTXRNames[i]);
    5164        4811 :             if (pszTXRVal == nullptr)
    5165             :             {
    5166        4811 :                 bOutputTransferRange = false;
    5167        4811 :                 break;
    5168             :             }
    5169             : 
    5170             :             const CPLStringList aosTokens(CSLTokenizeString2(
    5171             :                 pszTXRVal, ",",
    5172             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5173           0 :                     CSLT_STRIPENDSPACES));
    5174             : 
    5175           0 :             if (aosTokens.size() != 3)
    5176             :             {
    5177           0 :                 bOutputTransferRange = false;
    5178           0 :                 break;
    5179             :             }
    5180             : 
    5181           0 :             for (int j = 0; j < 3; ++j)
    5182             :             {
    5183           0 :                 pTXR[i + j * 2] = static_cast<uint16_t>(atoi(aosTokens[j]));
    5184             :             }
    5185             :         }
    5186             : 
    5187        4811 :         if (bOutputTransferRange)
    5188             :         {
    5189           0 :             const int TIFFTAG_TRANSFERRANGE = 0x0156;
    5190           0 :             TIFFSetField(l_hTIFF, TIFFTAG_TRANSFERRANGE, pTXR);
    5191             :         }
    5192             :     }
    5193             : }
    5194             : 
    5195       17131 : static signed char GTiffGetLZMAPreset(char **papszOptions)
    5196             : {
    5197       17131 :     int nLZMAPreset = -1;
    5198       17131 :     const char *pszValue = CSLFetchNameValue(papszOptions, "LZMA_PRESET");
    5199       17131 :     if (pszValue != nullptr)
    5200             :     {
    5201          20 :         nLZMAPreset = atoi(pszValue);
    5202          20 :         if (!(nLZMAPreset >= 0 && nLZMAPreset <= 9))
    5203             :         {
    5204           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5205             :                      "LZMA_PRESET=%s value not recognised, ignoring.",
    5206             :                      pszValue);
    5207           0 :             nLZMAPreset = -1;
    5208             :         }
    5209             :     }
    5210       17131 :     return static_cast<signed char>(nLZMAPreset);
    5211             : }
    5212             : 
    5213       17131 : static signed char GTiffGetZSTDPreset(char **papszOptions)
    5214             : {
    5215       17131 :     int nZSTDLevel = -1;
    5216       17131 :     const char *pszValue = CSLFetchNameValue(papszOptions, "ZSTD_LEVEL");
    5217       17131 :     if (pszValue != nullptr)
    5218             :     {
    5219          24 :         nZSTDLevel = atoi(pszValue);
    5220          24 :         if (!(nZSTDLevel >= 1 && nZSTDLevel <= 22))
    5221             :         {
    5222           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5223             :                      "ZSTD_LEVEL=%s value not recognised, ignoring.", pszValue);
    5224           0 :             nZSTDLevel = -1;
    5225             :         }
    5226             :     }
    5227       17131 :     return static_cast<signed char>(nZSTDLevel);
    5228             : }
    5229             : 
    5230       17131 : static signed char GTiffGetZLevel(char **papszOptions)
    5231             : {
    5232       17131 :     int nZLevel = -1;
    5233       17131 :     const char *pszValue = CSLFetchNameValue(papszOptions, "ZLEVEL");
    5234       17131 :     if (pszValue != nullptr)
    5235             :     {
    5236          44 :         nZLevel = atoi(pszValue);
    5237             : #ifdef TIFFTAG_DEFLATE_SUBCODEC
    5238          44 :         constexpr int nMaxLevel = 12;
    5239             : #ifndef LIBDEFLATE_SUPPORT
    5240             :         if (nZLevel > 9 && nZLevel <= nMaxLevel)
    5241             :         {
    5242             :             CPLDebug("GTiff",
    5243             :                      "ZLEVEL=%d not supported in a non-libdeflate enabled "
    5244             :                      "libtiff build. Capping to 9",
    5245             :                      nZLevel);
    5246             :             nZLevel = 9;
    5247             :         }
    5248             : #endif
    5249             : #else
    5250             :         constexpr int nMaxLevel = 9;
    5251             : #endif
    5252          44 :         if (nZLevel < 1 || nZLevel > nMaxLevel)
    5253             :         {
    5254           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5255             :                      "ZLEVEL=%s value not recognised, ignoring.", pszValue);
    5256           0 :             nZLevel = -1;
    5257             :         }
    5258             :     }
    5259       17131 :     return static_cast<signed char>(nZLevel);
    5260             : }
    5261             : 
    5262       17131 : static signed char GTiffGetJpegQuality(char **papszOptions)
    5263             : {
    5264       17131 :     int nJpegQuality = -1;
    5265       17131 :     const char *pszValue = CSLFetchNameValue(papszOptions, "JPEG_QUALITY");
    5266       17131 :     if (pszValue != nullptr)
    5267             :     {
    5268        1939 :         nJpegQuality = atoi(pszValue);
    5269        1939 :         if (nJpegQuality < 1 || nJpegQuality > 100)
    5270             :         {
    5271           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5272             :                      "JPEG_QUALITY=%s value not recognised, ignoring.",
    5273             :                      pszValue);
    5274           0 :             nJpegQuality = -1;
    5275             :         }
    5276             :     }
    5277       17131 :     return static_cast<signed char>(nJpegQuality);
    5278             : }
    5279             : 
    5280       17131 : static signed char GTiffGetJpegTablesMode(char **papszOptions)
    5281             : {
    5282       17131 :     return static_cast<signed char>(atoi(
    5283             :         CSLFetchNameValueDef(papszOptions, "JPEGTABLESMODE",
    5284       17131 :                              CPLSPrintf("%d", knGTIFFJpegTablesModeDefault))));
    5285             : }
    5286             : 
    5287             : /************************************************************************/
    5288             : /*                        GetDiscardLsbOption()                         */
    5289             : /************************************************************************/
    5290             : 
    5291        7554 : static GTiffDataset::MaskOffset *GetDiscardLsbOption(TIFF *hTIFF,
    5292             :                                                      char **papszOptions)
    5293             : {
    5294        7554 :     const char *pszBits = CSLFetchNameValue(papszOptions, "DISCARD_LSB");
    5295        7554 :     if (pszBits == nullptr)
    5296        7432 :         return nullptr;
    5297             : 
    5298         122 :     uint16_t nPhotometric = 0;
    5299         122 :     TIFFGetFieldDefaulted(hTIFF, TIFFTAG_PHOTOMETRIC, &nPhotometric);
    5300             : 
    5301         122 :     uint16_t nBitsPerSample = 0;
    5302         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &nBitsPerSample))
    5303           0 :         nBitsPerSample = 1;
    5304             : 
    5305         122 :     uint16_t nSamplesPerPixel = 0;
    5306         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSamplesPerPixel))
    5307           0 :         nSamplesPerPixel = 1;
    5308             : 
    5309         122 :     uint16_t nSampleFormat = 0;
    5310         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_SAMPLEFORMAT, &nSampleFormat))
    5311           0 :         nSampleFormat = SAMPLEFORMAT_UINT;
    5312             : 
    5313         122 :     if (nPhotometric == PHOTOMETRIC_PALETTE)
    5314             :     {
    5315           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    5316             :                  "DISCARD_LSB ignored on a paletted image");
    5317           1 :         return nullptr;
    5318             :     }
    5319         121 :     if (!(nBitsPerSample == 8 || nBitsPerSample == 16 || nBitsPerSample == 32 ||
    5320          13 :           nBitsPerSample == 64))
    5321             :     {
    5322           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    5323             :                  "DISCARD_LSB ignored on non 8, 16, 32 or 64 bits images");
    5324           1 :         return nullptr;
    5325             :     }
    5326             : 
    5327         240 :     const CPLStringList aosTokens(CSLTokenizeString2(pszBits, ",", 0));
    5328         120 :     const int nTokens = aosTokens.size();
    5329         120 :     GTiffDataset::MaskOffset *panMaskOffsetLsb = nullptr;
    5330         120 :     if (nTokens == 1 || nTokens == nSamplesPerPixel)
    5331             :     {
    5332             :         panMaskOffsetLsb = static_cast<GTiffDataset::MaskOffset *>(
    5333         119 :             CPLCalloc(nSamplesPerPixel, sizeof(GTiffDataset::MaskOffset)));
    5334         374 :         for (int i = 0; i < nSamplesPerPixel; ++i)
    5335             :         {
    5336         255 :             const int nBits = atoi(aosTokens[nTokens == 1 ? 0 : i]);
    5337         510 :             const int nMaxBits = (nSampleFormat == SAMPLEFORMAT_IEEEFP)
    5338         510 :                                      ? ((nBitsPerSample == 16)   ? 11 - 1
    5339          78 :                                         : (nBitsPerSample == 32) ? 23 - 1
    5340          26 :                                         : (nBitsPerSample == 64) ? 53 - 1
    5341             :                                                                  : 0)
    5342         203 :                                  : nSampleFormat == SAMPLEFORMAT_INT
    5343         203 :                                      ? nBitsPerSample - 2
    5344         119 :                                      : nBitsPerSample - 1;
    5345             : 
    5346         255 :             if (nBits < 0 || nBits > nMaxBits)
    5347             :             {
    5348           0 :                 CPLError(
    5349             :                     CE_Warning, CPLE_AppDefined,
    5350             :                     "DISCARD_LSB ignored: values should be in [0,%d] range",
    5351             :                     nMaxBits);
    5352           0 :                 VSIFree(panMaskOffsetLsb);
    5353           0 :                 return nullptr;
    5354             :             }
    5355         255 :             panMaskOffsetLsb[i].nMask =
    5356         255 :                 ~((static_cast<uint64_t>(1) << nBits) - 1);
    5357         255 :             if (nBits > 1)
    5358             :             {
    5359         249 :                 panMaskOffsetLsb[i].nRoundUpBitTest = static_cast<uint64_t>(1)
    5360         249 :                                                       << (nBits - 1);
    5361             :             }
    5362         119 :         }
    5363             :     }
    5364             :     else
    5365             :     {
    5366           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    5367             :                  "DISCARD_LSB ignored: wrong number of components");
    5368             :     }
    5369         120 :     return panMaskOffsetLsb;
    5370             : }
    5371             : 
    5372        7554 : void GTiffDataset::GetDiscardLsbOption(char **papszOptions)
    5373             : {
    5374        7554 :     m_panMaskOffsetLsb = ::GetDiscardLsbOption(m_hTIFF, papszOptions);
    5375        7554 : }
    5376             : 
    5377             : /************************************************************************/
    5378             : /*                             GetProfile()                             */
    5379             : /************************************************************************/
    5380             : 
    5381       17182 : static GTiffProfile GetProfile(const char *pszProfile)
    5382             : {
    5383       17182 :     GTiffProfile eProfile = GTiffProfile::GDALGEOTIFF;
    5384       17182 :     if (pszProfile != nullptr)
    5385             :     {
    5386          70 :         if (EQUAL(pszProfile, szPROFILE_BASELINE))
    5387          50 :             eProfile = GTiffProfile::BASELINE;
    5388          20 :         else if (EQUAL(pszProfile, szPROFILE_GeoTIFF))
    5389          18 :             eProfile = GTiffProfile::GEOTIFF;
    5390           2 :         else if (!EQUAL(pszProfile, szPROFILE_GDALGeoTIFF))
    5391             :         {
    5392           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    5393             :                      "Unsupported value for PROFILE: %s", pszProfile);
    5394             :         }
    5395             :     }
    5396       17182 :     return eProfile;
    5397             : }
    5398             : 
    5399             : /************************************************************************/
    5400             : /*                            GTiffCreate()                             */
    5401             : /*                                                                      */
    5402             : /*      Shared functionality between GTiffDataset::Create() and         */
    5403             : /*      GTiffCreateCopy() for creating TIFF file based on a set of      */
    5404             : /*      options and a configuration.                                    */
    5405             : /************************************************************************/
    5406             : 
    5407        9596 : TIFF *GTiffDataset::CreateLL(const char *pszFilename, int nXSize, int nYSize,
    5408             :                              int l_nBands, GDALDataType eType,
    5409             :                              double dfExtraSpaceForOverviews,
    5410             :                              int nColorTableMultiplier, char **papszParamList,
    5411             :                              VSILFILE **pfpL, CPLString &l_osTmpFilename,
    5412             :                              bool bCreateCopy, bool &bTileInterleavingOut)
    5413             : 
    5414             : {
    5415        9596 :     bTileInterleavingOut = false;
    5416             : 
    5417        9596 :     GTiffOneTimeInit();
    5418             : 
    5419             :     /* -------------------------------------------------------------------- */
    5420             :     /*      Blow on a few errors.                                           */
    5421             :     /* -------------------------------------------------------------------- */
    5422        9596 :     if (nXSize < 1 || nYSize < 1 || l_nBands < 1)
    5423             :     {
    5424           1 :         ReportError(
    5425             :             pszFilename, CE_Failure, CPLE_AppDefined,
    5426             :             "Attempt to create %dx%dx%d TIFF file, but width, height and bands"
    5427             :             "must be positive.",
    5428             :             nXSize, nYSize, l_nBands);
    5429             : 
    5430           1 :         return nullptr;
    5431             :     }
    5432             : 
    5433        9595 :     if (l_nBands > 65535)
    5434             :     {
    5435           1 :         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5436             :                     "Attempt to create %dx%dx%d TIFF file, but bands "
    5437             :                     "must be lesser or equal to 65535.",
    5438             :                     nXSize, nYSize, l_nBands);
    5439             : 
    5440           1 :         return nullptr;
    5441             :     }
    5442             : 
    5443             :     /* -------------------------------------------------------------------- */
    5444             :     /*      Setup values based on options.                                  */
    5445             :     /* -------------------------------------------------------------------- */
    5446             :     const GTiffProfile eProfile =
    5447        9594 :         GetProfile(CSLFetchNameValue(papszParamList, "PROFILE"));
    5448             : 
    5449        9594 :     const bool bTiled = CPLFetchBool(papszParamList, "TILED", false);
    5450             : 
    5451        9594 :     int l_nBlockXSize = 0;
    5452        9594 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKXSIZE"))
    5453             :     {
    5454         390 :         l_nBlockXSize = atoi(pszValue);
    5455         390 :         if (l_nBlockXSize < 0)
    5456             :         {
    5457           0 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5458             :                         "Invalid value for BLOCKXSIZE");
    5459           0 :             return nullptr;
    5460             :         }
    5461         390 :         if (!bTiled)
    5462             :         {
    5463          10 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    5464             :                         "BLOCKXSIZE can only be used with TILED=YES");
    5465             :         }
    5466         380 :         else if (l_nBlockXSize % 16 != 0)
    5467             :         {
    5468           1 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5469             :                         "BLOCKXSIZE must be a multiple of 16");
    5470           1 :             return nullptr;
    5471             :         }
    5472             :     }
    5473             : 
    5474        9593 :     int l_nBlockYSize = 0;
    5475        9593 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKYSIZE"))
    5476             :     {
    5477        2498 :         l_nBlockYSize = atoi(pszValue);
    5478        2498 :         if (l_nBlockYSize < 0)
    5479             :         {
    5480           0 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5481             :                         "Invalid value for BLOCKYSIZE");
    5482           0 :             return nullptr;
    5483             :         }
    5484        2498 :         if (bTiled && (l_nBlockYSize % 16 != 0))
    5485             :         {
    5486           2 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5487             :                         "BLOCKYSIZE must be a multiple of 16");
    5488           2 :             return nullptr;
    5489             :         }
    5490             :     }
    5491             : 
    5492        9591 :     if (bTiled)
    5493             :     {
    5494         715 :         if (l_nBlockXSize == 0)
    5495         337 :             l_nBlockXSize = 256;
    5496             : 
    5497         715 :         if (l_nBlockYSize == 0)
    5498         337 :             l_nBlockYSize = 256;
    5499             :     }
    5500             : 
    5501        9591 :     int nPlanar = 0;
    5502             : 
    5503             :     // Hidden @TILE_INTERLEAVE=YES parameter used by the COG driver
    5504        9591 :     if (bCreateCopy && CPLTestBool(CSLFetchNameValueDef(
    5505             :                            papszParamList, "@TILE_INTERLEAVE", "NO")))
    5506             :     {
    5507           7 :         bTileInterleavingOut = true;
    5508           7 :         nPlanar = PLANARCONFIG_SEPARATE;
    5509             :     }
    5510             :     else
    5511             :     {
    5512        9584 :         if (const char *pszValue =
    5513        9584 :                 CSLFetchNameValue(papszParamList, "INTERLEAVE"))
    5514             :         {
    5515        1531 :             if (EQUAL(pszValue, "PIXEL"))
    5516         367 :                 nPlanar = PLANARCONFIG_CONTIG;
    5517        1164 :             else if (EQUAL(pszValue, "BAND"))
    5518             :             {
    5519        1163 :                 nPlanar = PLANARCONFIG_SEPARATE;
    5520             :             }
    5521           1 :             else if (EQUAL(pszValue, "BAND"))
    5522             :             {
    5523           0 :                 nPlanar = PLANARCONFIG_SEPARATE;
    5524             :             }
    5525             :             else
    5526             :             {
    5527           1 :                 ReportError(
    5528             :                     pszFilename, CE_Failure, CPLE_IllegalArg,
    5529             :                     "INTERLEAVE=%s unsupported, value must be PIXEL or BAND.",
    5530             :                     pszValue);
    5531           1 :                 return nullptr;
    5532             :             }
    5533             :         }
    5534             :         else
    5535             :         {
    5536        8053 :             nPlanar = PLANARCONFIG_CONTIG;
    5537             :         }
    5538             :     }
    5539             : 
    5540        9590 :     int l_nCompression = COMPRESSION_NONE;
    5541        9590 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "COMPRESS"))
    5542             :     {
    5543        3247 :         l_nCompression = GTIFFGetCompressionMethod(pszValue, "COMPRESS");
    5544        3247 :         if (l_nCompression < 0)
    5545           0 :             return nullptr;
    5546             :     }
    5547             : 
    5548        9590 :     constexpr int JPEG_MAX_DIMENSION = 65500;  // Defined in jpeglib.h
    5549        9590 :     constexpr int WEBP_MAX_DIMENSION = 16383;
    5550             : 
    5551             :     const struct
    5552             :     {
    5553             :         int nCodecID;
    5554             :         const char *pszCodecName;
    5555             :         int nMaxDim;
    5556        9590 :     } asLimitations[] = {
    5557             :         {COMPRESSION_JPEG, "JPEG", JPEG_MAX_DIMENSION},
    5558             :         {COMPRESSION_WEBP, "WEBP", WEBP_MAX_DIMENSION},
    5559             :     };
    5560             : 
    5561       28758 :     for (const auto &sLimitation : asLimitations)
    5562             :     {
    5563       19176 :         if (l_nCompression == sLimitation.nCodecID && !bTiled &&
    5564        2074 :             nXSize > sLimitation.nMaxDim)
    5565             :         {
    5566           2 :             ReportError(
    5567             :                 pszFilename, CE_Failure, CPLE_IllegalArg,
    5568             :                 "COMPRESS=%s is only compatible of un-tiled images whose "
    5569             :                 "width is lesser or equal to %d pixels. "
    5570             :                 "To overcome this limitation, set the TILED=YES creation "
    5571             :                 "option.",
    5572           2 :                 sLimitation.pszCodecName, sLimitation.nMaxDim);
    5573           2 :             return nullptr;
    5574             :         }
    5575       19174 :         else if (l_nCompression == sLimitation.nCodecID && bTiled &&
    5576          52 :                  l_nBlockXSize > sLimitation.nMaxDim)
    5577             :         {
    5578           2 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5579             :                         "COMPRESS=%s is only compatible of tiled images whose "
    5580             :                         "BLOCKXSIZE is lesser or equal to %d pixels.",
    5581           2 :                         sLimitation.pszCodecName, sLimitation.nMaxDim);
    5582           2 :             return nullptr;
    5583             :         }
    5584       19172 :         else if (l_nCompression == sLimitation.nCodecID &&
    5585        2122 :                  l_nBlockYSize > sLimitation.nMaxDim)
    5586             :         {
    5587           4 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5588             :                         "COMPRESS=%s is only compatible of images whose "
    5589             :                         "BLOCKYSIZE is lesser or equal to %d pixels. "
    5590             :                         "To overcome this limitation, set the TILED=YES "
    5591             :                         "creation option",
    5592           4 :                         sLimitation.pszCodecName, sLimitation.nMaxDim);
    5593           4 :             return nullptr;
    5594             :         }
    5595             :     }
    5596             : 
    5597             :     /* -------------------------------------------------------------------- */
    5598             :     /*      How many bits per sample?  We have a special case if NBITS      */
    5599             :     /*      specified for GDT_Byte, GDT_UInt16, GDT_UInt32.                 */
    5600             :     /* -------------------------------------------------------------------- */
    5601        9582 :     int l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5602        9582 :     if (CSLFetchNameValue(papszParamList, "NBITS") != nullptr)
    5603             :     {
    5604        1747 :         int nMinBits = 0;
    5605        1747 :         int nMaxBits = 0;
    5606        1747 :         l_nBitsPerSample = atoi(CSLFetchNameValue(papszParamList, "NBITS"));
    5607        1747 :         if (eType == GDT_Byte)
    5608             :         {
    5609         527 :             nMinBits = 1;
    5610         527 :             nMaxBits = 8;
    5611             :         }
    5612        1220 :         else if (eType == GDT_UInt16)
    5613             :         {
    5614        1202 :             nMinBits = 9;
    5615        1202 :             nMaxBits = 16;
    5616             :         }
    5617          18 :         else if (eType == GDT_UInt32)
    5618             :         {
    5619          14 :             nMinBits = 17;
    5620          14 :             nMaxBits = 32;
    5621             :         }
    5622           4 :         else if (eType == GDT_Float32)
    5623             :         {
    5624           4 :             if (l_nBitsPerSample != 16 && l_nBitsPerSample != 32)
    5625             :             {
    5626           1 :                 ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5627             :                             "Only NBITS=16 is supported for data type Float32");
    5628           1 :                 l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5629             :             }
    5630             :         }
    5631             :         else
    5632             :         {
    5633           0 :             ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5634             :                         "NBITS is not supported for data type %s",
    5635             :                         GDALGetDataTypeName(eType));
    5636           0 :             l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5637             :         }
    5638             : 
    5639        1747 :         if (nMinBits != 0)
    5640             :         {
    5641        1743 :             if (l_nBitsPerSample < nMinBits)
    5642             :             {
    5643           2 :                 ReportError(
    5644             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    5645             :                     "NBITS=%d is invalid for data type %s. Using NBITS=%d",
    5646             :                     l_nBitsPerSample, GDALGetDataTypeName(eType), nMinBits);
    5647           2 :                 l_nBitsPerSample = nMinBits;
    5648             :             }
    5649        1741 :             else if (l_nBitsPerSample > nMaxBits)
    5650             :             {
    5651           3 :                 ReportError(
    5652             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    5653             :                     "NBITS=%d is invalid for data type %s. Using NBITS=%d",
    5654             :                     l_nBitsPerSample, GDALGetDataTypeName(eType), nMaxBits);
    5655           3 :                 l_nBitsPerSample = nMaxBits;
    5656             :             }
    5657             :         }
    5658             :     }
    5659             : 
    5660             : #ifdef HAVE_JXL
    5661        9582 :     if ((l_nCompression == COMPRESSION_JXL ||
    5662         106 :          l_nCompression == COMPRESSION_JXL_DNG_1_7) &&
    5663         105 :         eType != GDT_Float16 && eType != GDT_Float32)
    5664             :     {
    5665             :         // Reflects tif_jxl's GetJXLDataType()
    5666          85 :         if (eType != GDT_Byte && eType != GDT_UInt16)
    5667             :         {
    5668           1 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5669             :                         "Data type %s not supported for JXL compression. Only "
    5670             :                         "Byte, UInt16, Float16, Float32 are supported",
    5671             :                         GDALGetDataTypeName(eType));
    5672           2 :             return nullptr;
    5673             :         }
    5674             : 
    5675             :         const struct
    5676             :         {
    5677             :             GDALDataType eDT;
    5678             :             int nBitsPerSample;
    5679          84 :         } asSupportedDTBitsPerSample[] = {
    5680             :             {GDT_Byte, 8},
    5681             :             {GDT_UInt16, 16},
    5682             :         };
    5683             : 
    5684         250 :         for (const auto &sSupportedDTBitsPerSample : asSupportedDTBitsPerSample)
    5685             :         {
    5686         167 :             if (eType == sSupportedDTBitsPerSample.eDT &&
    5687          84 :                 l_nBitsPerSample != sSupportedDTBitsPerSample.nBitsPerSample)
    5688             :             {
    5689           1 :                 ReportError(
    5690             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    5691             :                     "Bits per sample=%d not supported for JXL compression. "
    5692             :                     "Only %d is supported for %s data type.",
    5693           1 :                     l_nBitsPerSample, sSupportedDTBitsPerSample.nBitsPerSample,
    5694             :                     GDALGetDataTypeName(eType));
    5695           1 :                 return nullptr;
    5696             :             }
    5697             :         }
    5698             :     }
    5699             : #endif
    5700             : 
    5701        9580 :     int nPredictor = PREDICTOR_NONE;
    5702        9580 :     const char *pszPredictor = CSLFetchNameValue(papszParamList, "PREDICTOR");
    5703        9580 :     if (pszPredictor)
    5704             :     {
    5705          29 :         nPredictor = atoi(pszPredictor);
    5706             :     }
    5707             : 
    5708        9580 :     if (nPredictor != PREDICTOR_NONE &&
    5709          16 :         l_nCompression != COMPRESSION_ADOBE_DEFLATE &&
    5710           2 :         l_nCompression != COMPRESSION_LZW &&
    5711           2 :         l_nCompression != COMPRESSION_LZMA &&
    5712             :         l_nCompression != COMPRESSION_ZSTD)
    5713             :     {
    5714           1 :         ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5715             :                     "PREDICTOR option is ignored for COMPRESS=%s. "
    5716             :                     "Only valid for DEFLATE, LZW, LZMA or ZSTD",
    5717             :                     CSLFetchNameValueDef(papszParamList, "COMPRESS", "NONE"));
    5718             :     }
    5719             : 
    5720             :     // Do early checks as libtiff will only error out when starting to write.
    5721        9607 :     else if (nPredictor != PREDICTOR_NONE &&
    5722          28 :              CPLTestBool(
    5723             :                  CPLGetConfigOption("GDAL_GTIFF_PREDICTOR_CHECKS", "YES")))
    5724             :     {
    5725             : #if (TIFFLIB_VERSION > 20210416) || defined(INTERNAL_LIBTIFF)
    5726             : #define HAVE_PREDICTOR_2_FOR_64BIT
    5727             : #endif
    5728          28 :         if (nPredictor == 2)
    5729             :         {
    5730          23 :             if (l_nBitsPerSample != 8 && l_nBitsPerSample != 16 &&
    5731             :                 l_nBitsPerSample != 32
    5732             : #ifdef HAVE_PREDICTOR_2_FOR_64BIT
    5733           2 :                 && l_nBitsPerSample != 64
    5734             : #endif
    5735             :             )
    5736             :             {
    5737             : #if !defined(HAVE_PREDICTOR_2_FOR_64BIT)
    5738             :                 if (l_nBitsPerSample == 64)
    5739             :                 {
    5740             :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5741             :                                 "PREDICTOR=2 is supported on 64 bit samples "
    5742             :                                 "starting with libtiff > 4.3.0.");
    5743             :                 }
    5744             :                 else
    5745             : #endif
    5746             :                 {
    5747           2 :                     const int nBITSHint = (l_nBitsPerSample < 8)    ? 8
    5748           1 :                                           : (l_nBitsPerSample < 16) ? 16
    5749           0 :                                           : (l_nBitsPerSample < 32) ? 32
    5750             :                                                                     : 64;
    5751           1 :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5752             : #ifdef HAVE_PREDICTOR_2_FOR_64BIT
    5753             :                                 "PREDICTOR=2 is only supported with 8/16/32/64 "
    5754             :                                 "bit samples. You can specify the NBITS=%d "
    5755             :                                 "creation option to promote to the closest "
    5756             :                                 "supported bits per sample value.",
    5757             : #else
    5758             :                                 "PREDICTOR=2 is only supported with 8/16/32 "
    5759             :                                 "bit samples. You can specify the NBITS=%d "
    5760             :                                 "creation option to promote to the closest "
    5761             :                                 "supported bits per sample value.",
    5762             : #endif
    5763             :                                 nBITSHint);
    5764             :                 }
    5765           1 :                 return nullptr;
    5766             :             }
    5767             :         }
    5768           5 :         else if (nPredictor == 3)
    5769             :         {
    5770           4 :             if (eType != GDT_Float32 && eType != GDT_Float64)
    5771             :             {
    5772           1 :                 ReportError(
    5773             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    5774             :                     "PREDICTOR=3 is only supported with Float32 or Float64.");
    5775           1 :                 return nullptr;
    5776             :             }
    5777             :         }
    5778             :         else
    5779             :         {
    5780           1 :             ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5781             :                         "PREDICTOR=%s is not supported.", pszPredictor);
    5782           1 :             return nullptr;
    5783             :         }
    5784             :     }
    5785             : 
    5786        9577 :     const int l_nZLevel = GTiffGetZLevel(papszParamList);
    5787        9577 :     const int l_nLZMAPreset = GTiffGetLZMAPreset(papszParamList);
    5788        9577 :     const int l_nZSTDLevel = GTiffGetZSTDPreset(papszParamList);
    5789        9577 :     const int l_nWebPLevel = GTiffGetWebPLevel(papszParamList);
    5790        9577 :     const bool l_bWebPLossless = GTiffGetWebPLossless(papszParamList);
    5791        9577 :     const int l_nJpegQuality = GTiffGetJpegQuality(papszParamList);
    5792        9577 :     const int l_nJpegTablesMode = GTiffGetJpegTablesMode(papszParamList);
    5793        9577 :     const double l_dfMaxZError = GTiffGetLERCMaxZError(papszParamList);
    5794             : #if HAVE_JXL
    5795        9577 :     bool bJXLLosslessSpecified = false;
    5796             :     const bool l_bJXLLossless =
    5797        9577 :         GTiffGetJXLLossless(papszParamList, &bJXLLosslessSpecified);
    5798        9577 :     const uint32_t l_nJXLEffort = GTiffGetJXLEffort(papszParamList);
    5799        9577 :     bool bJXLDistanceSpecified = false;
    5800             :     const float l_fJXLDistance =
    5801        9577 :         GTiffGetJXLDistance(papszParamList, &bJXLDistanceSpecified);
    5802        9577 :     if (bJXLDistanceSpecified && l_bJXLLossless)
    5803             :     {
    5804           1 :         ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
    5805             :                     "JXL_DISTANCE creation option is ignored, given %s "
    5806             :                     "JXL_LOSSLESS=YES",
    5807             :                     bJXLLosslessSpecified ? "(explicit)" : "(implicit)");
    5808             :     }
    5809        9577 :     bool bJXLAlphaDistanceSpecified = false;
    5810             :     const float l_fJXLAlphaDistance =
    5811        9577 :         GTiffGetJXLAlphaDistance(papszParamList, &bJXLAlphaDistanceSpecified);
    5812        9577 :     if (bJXLAlphaDistanceSpecified && l_bJXLLossless)
    5813             :     {
    5814           1 :         ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
    5815             :                     "JXL_ALPHA_DISTANCE creation option is ignored, given %s "
    5816             :                     "JXL_LOSSLESS=YES",
    5817             :                     bJXLLosslessSpecified ? "(explicit)" : "(implicit)");
    5818             :     }
    5819             : #endif
    5820             :     /* -------------------------------------------------------------------- */
    5821             :     /*      Streaming related code                                          */
    5822             :     /* -------------------------------------------------------------------- */
    5823       19154 :     const CPLString osOriFilename(pszFilename);
    5824       19154 :     bool bStreaming = strcmp(pszFilename, "/vsistdout/") == 0 ||
    5825        9577 :                       CPLFetchBool(papszParamList, "STREAMABLE_OUTPUT", false);
    5826             : #ifdef S_ISFIFO
    5827        9577 :     if (!bStreaming)
    5828             :     {
    5829             :         VSIStatBufL sStat;
    5830        9565 :         if (VSIStatExL(pszFilename, &sStat,
    5831       10445 :                        VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
    5832         880 :             S_ISFIFO(sStat.st_mode))
    5833             :         {
    5834           0 :             bStreaming = true;
    5835             :         }
    5836             :     }
    5837             : #endif
    5838        9577 :     if (bStreaming && !EQUAL("NONE", CSLFetchNameValueDef(papszParamList,
    5839             :                                                           "COMPRESS", "NONE")))
    5840             :     {
    5841           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5842             :                     "Streaming only supported to uncompressed TIFF");
    5843           1 :         return nullptr;
    5844             :     }
    5845        9576 :     if (bStreaming && CPLFetchBool(papszParamList, "SPARSE_OK", false))
    5846             :     {
    5847           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5848             :                     "Streaming not supported with SPARSE_OK");
    5849           1 :         return nullptr;
    5850             :     }
    5851             :     const bool bCopySrcOverviews =
    5852        9575 :         CPLFetchBool(papszParamList, "COPY_SRC_OVERVIEWS", false);
    5853        9575 :     if (bStreaming && bCopySrcOverviews)
    5854             :     {
    5855           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5856             :                     "Streaming not supported with COPY_SRC_OVERVIEWS");
    5857           1 :         return nullptr;
    5858             :     }
    5859        9574 :     if (bStreaming)
    5860             :     {
    5861           9 :         l_osTmpFilename = VSIMemGenerateHiddenFilename("vsistdout.tif");
    5862           9 :         pszFilename = l_osTmpFilename.c_str();
    5863             :     }
    5864             : 
    5865             :     /* -------------------------------------------------------------------- */
    5866             :     /*      Compute the uncompressed size.                                  */
    5867             :     /* -------------------------------------------------------------------- */
    5868        9574 :     const unsigned nTileXCount =
    5869        9574 :         bTiled ? DIV_ROUND_UP(nXSize, l_nBlockXSize) : 0;
    5870        9574 :     const unsigned nTileYCount =
    5871        9574 :         bTiled ? DIV_ROUND_UP(nYSize, l_nBlockYSize) : 0;
    5872             :     const double dfUncompressedImageSize =
    5873        9574 :         (bTiled ? (static_cast<double>(nTileXCount) * nTileYCount *
    5874         711 :                    l_nBlockXSize * l_nBlockYSize)
    5875        8863 :                 : (nXSize * static_cast<double>(nYSize))) *
    5876        9574 :             l_nBands * GDALGetDataTypeSizeBytes(eType) +
    5877        9574 :         dfExtraSpaceForOverviews;
    5878             : 
    5879             :     /* -------------------------------------------------------------------- */
    5880             :     /*      Should the file be created as a bigtiff file?                   */
    5881             :     /* -------------------------------------------------------------------- */
    5882        9574 :     const char *pszBIGTIFF = CSLFetchNameValue(papszParamList, "BIGTIFF");
    5883             : 
    5884        9574 :     if (pszBIGTIFF == nullptr)
    5885        9152 :         pszBIGTIFF = "IF_NEEDED";
    5886             : 
    5887        9574 :     bool bCreateBigTIFF = false;
    5888        9574 :     if (EQUAL(pszBIGTIFF, "IF_NEEDED"))
    5889             :     {
    5890        9153 :         if (l_nCompression == COMPRESSION_NONE &&
    5891             :             dfUncompressedImageSize > 4200000000.0)
    5892          17 :             bCreateBigTIFF = true;
    5893             :     }
    5894         421 :     else if (EQUAL(pszBIGTIFF, "IF_SAFER"))
    5895             :     {
    5896         401 :         if (dfUncompressedImageSize > 2000000000.0)
    5897           1 :             bCreateBigTIFF = true;
    5898             :     }
    5899             :     else
    5900             :     {
    5901          20 :         bCreateBigTIFF = CPLTestBool(pszBIGTIFF);
    5902          20 :         if (!bCreateBigTIFF && l_nCompression == COMPRESSION_NONE &&
    5903             :             dfUncompressedImageSize > 4200000000.0)
    5904             :         {
    5905           2 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5906             :                         "The TIFF file will be larger than 4GB, so BigTIFF is "
    5907             :                         "necessary.  Creation failed.");
    5908           2 :             return nullptr;
    5909             :         }
    5910             :     }
    5911             : 
    5912        9572 :     if (bCreateBigTIFF)
    5913          34 :         CPLDebug("GTiff", "File being created as a BigTIFF.");
    5914             : 
    5915             :     /* -------------------------------------------------------------------- */
    5916             :     /*      Sanity check.                                                   */
    5917             :     /* -------------------------------------------------------------------- */
    5918        9572 :     if (bTiled)
    5919             :     {
    5920             :         // libtiff implementation limitation
    5921         711 :         if (nTileXCount > 0x80000000U / (bCreateBigTIFF ? 8 : 4) / nTileYCount)
    5922             :         {
    5923           3 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5924             :                         "File too large regarding tile size. This would result "
    5925             :                         "in a file with tile arrays larger than 2GB");
    5926           3 :             return nullptr;
    5927             :         }
    5928             :     }
    5929             : 
    5930             :     /* -------------------------------------------------------------------- */
    5931             :     /*      Check free space (only for big, non sparse)                     */
    5932             :     /* -------------------------------------------------------------------- */
    5933        9569 :     const double dfLikelyFloorOfFinalSize =
    5934             :         l_nCompression == COMPRESSION_NONE
    5935        9569 :             ? dfUncompressedImageSize
    5936             :             :
    5937             :             /* For compressed, we target 1% as the most optimistic reduction factor! */
    5938             :             0.01 * dfUncompressedImageSize;
    5939        9590 :     if (dfLikelyFloorOfFinalSize >= 1e9 &&
    5940          21 :         !CPLFetchBool(papszParamList, "SPARSE_OK", false) &&
    5941           4 :         osOriFilename != "/vsistdout/" &&
    5942        9594 :         osOriFilename != "/vsistdout_redirect/" &&
    5943           4 :         CPLTestBool(CPLGetConfigOption("CHECK_DISK_FREE_SPACE", "TRUE")))
    5944             :     {
    5945             :         const GIntBig nFreeDiskSpace =
    5946           3 :             VSIGetDiskFreeSpace(CPLGetDirnameSafe(pszFilename).c_str());
    5947           3 :         if (nFreeDiskSpace >= 0 && nFreeDiskSpace < dfLikelyFloorOfFinalSize)
    5948             :         {
    5949           6 :             ReportError(
    5950             :                 pszFilename, CE_Failure, CPLE_FileIO,
    5951             :                 "Free disk space available is %s, "
    5952             :                 "whereas %s are %s necessary. "
    5953             :                 "You can disable this check by defining the "
    5954             :                 "CHECK_DISK_FREE_SPACE configuration option to FALSE.",
    5955           4 :                 CPLFormatReadableFileSize(static_cast<uint64_t>(nFreeDiskSpace))
    5956             :                     .c_str(),
    5957           4 :                 CPLFormatReadableFileSize(dfLikelyFloorOfFinalSize).c_str(),
    5958             :                 l_nCompression == COMPRESSION_NONE
    5959             :                     ? "at least"
    5960             :                     : "likely at least (probably more)");
    5961           2 :             return nullptr;
    5962             :         }
    5963             :     }
    5964             : 
    5965             :     /* -------------------------------------------------------------------- */
    5966             :     /*      Check if the user wishes a particular endianness                */
    5967             :     /* -------------------------------------------------------------------- */
    5968             : 
    5969        9567 :     int eEndianness = ENDIANNESS_NATIVE;
    5970        9567 :     const char *pszEndianness = CSLFetchNameValue(papszParamList, "ENDIANNESS");
    5971        9567 :     if (pszEndianness == nullptr)
    5972        9504 :         pszEndianness = CPLGetConfigOption("GDAL_TIFF_ENDIANNESS", nullptr);
    5973        9567 :     if (pszEndianness != nullptr)
    5974             :     {
    5975         123 :         if (EQUAL(pszEndianness, "LITTLE"))
    5976             :         {
    5977          36 :             eEndianness = ENDIANNESS_LITTLE;
    5978             :         }
    5979          87 :         else if (EQUAL(pszEndianness, "BIG"))
    5980             :         {
    5981           1 :             eEndianness = ENDIANNESS_BIG;
    5982             :         }
    5983          86 :         else if (EQUAL(pszEndianness, "INVERTED"))
    5984             :         {
    5985             : #ifdef CPL_LSB
    5986          82 :             eEndianness = ENDIANNESS_BIG;
    5987             : #else
    5988             :             eEndianness = ENDIANNESS_LITTLE;
    5989             : #endif
    5990             :         }
    5991           4 :         else if (!EQUAL(pszEndianness, "NATIVE"))
    5992             :         {
    5993           1 :             ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5994             :                         "ENDIANNESS=%s not supported. Defaulting to NATIVE",
    5995             :                         pszEndianness);
    5996             :         }
    5997             :     }
    5998             : 
    5999             :     /* -------------------------------------------------------------------- */
    6000             :     /*      Try opening the dataset.                                        */
    6001             :     /* -------------------------------------------------------------------- */
    6002             : 
    6003             :     const bool bAppend =
    6004        9567 :         CPLFetchBool(papszParamList, "APPEND_SUBDATASET", false);
    6005             : 
    6006        9567 :     char szOpeningFlag[5] = {};
    6007        9567 :     strcpy(szOpeningFlag, bAppend ? "r+" : "w+");
    6008        9567 :     if (bCreateBigTIFF)
    6009          31 :         strcat(szOpeningFlag, "8");
    6010        9567 :     if (eEndianness == ENDIANNESS_BIG)
    6011          83 :         strcat(szOpeningFlag, "b");
    6012        9484 :     else if (eEndianness == ENDIANNESS_LITTLE)
    6013          36 :         strcat(szOpeningFlag, "l");
    6014             : 
    6015        9567 :     VSIErrorReset();
    6016        9567 :     const bool bOnlyVisibleAtCloseTime = CPLTestBool(CSLFetchNameValueDef(
    6017             :         papszParamList, "@CREATE_ONLY_VISIBLE_AT_CLOSE_TIME", "NO"));
    6018        9567 :     const bool bSuppressASAP = CPLTestBool(
    6019             :         CSLFetchNameValueDef(papszParamList, "@SUPPRESS_ASAP", "NO"));
    6020             :     auto l_fpL =
    6021        9567 :         (bOnlyVisibleAtCloseTime || bSuppressASAP) && !bAppend
    6022        9601 :             ? VSIFileManager::GetHandler(pszFilename)
    6023          68 :                   ->CreateOnlyVisibleAtCloseTime(pszFilename, true, nullptr)
    6024          34 :                   .release()
    6025       19100 :             : VSIFilesystemHandler::OpenStatic(pszFilename,
    6026             :                                                bAppend ? "r+b" : "w+b", true)
    6027        9567 :                   .release();
    6028        9567 :     if (l_fpL == nullptr)
    6029             :     {
    6030          21 :         VSIToCPLErrorWithMsg(CE_Failure, CPLE_OpenFailed,
    6031          42 :                              std::string("Attempt to create new tiff file `")
    6032          21 :                                  .append(pszFilename)
    6033          21 :                                  .append("' failed")
    6034             :                                  .c_str());
    6035          21 :         return nullptr;
    6036             :     }
    6037             : 
    6038        9546 :     if (bSuppressASAP)
    6039             :     {
    6040           4 :         l_fpL->CancelCreation();
    6041             :     }
    6042             : 
    6043        9546 :     TIFF *l_hTIFF = VSI_TIFFOpen(pszFilename, szOpeningFlag, l_fpL);
    6044        9546 :     if (l_hTIFF == nullptr)
    6045             :     {
    6046           2 :         if (CPLGetLastErrorNo() == 0)
    6047           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    6048             :                      "Attempt to create new tiff file `%s' "
    6049             :                      "failed in XTIFFOpen().",
    6050             :                      pszFilename);
    6051           2 :         l_fpL->CancelCreation();
    6052           2 :         CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6053           2 :         return nullptr;
    6054             :     }
    6055             : 
    6056        9544 :     if (bAppend)
    6057             :     {
    6058             : #if !(defined(INTERNAL_LIBTIFF) || TIFFLIB_VERSION > 20240911)
    6059             :         // This is a bit of a hack to cause (*tif->tif_cleanup)(tif); to be
    6060             :         // called. See https://trac.osgeo.org/gdal/ticket/2055
    6061             :         // Fixed in libtiff > 4.7.0
    6062             :         TIFFSetField(l_hTIFF, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
    6063             :         TIFFFreeDirectory(l_hTIFF);
    6064             : #endif
    6065           6 :         TIFFCreateDirectory(l_hTIFF);
    6066             :     }
    6067             : 
    6068             :     /* -------------------------------------------------------------------- */
    6069             :     /*      Do we have a custom pixel type (just used for signed byte now). */
    6070             :     /* -------------------------------------------------------------------- */
    6071        9544 :     const char *pszPixelType = CSLFetchNameValue(papszParamList, "PIXELTYPE");
    6072        9544 :     if (pszPixelType == nullptr)
    6073        9536 :         pszPixelType = "";
    6074        9544 :     if (eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE"))
    6075             :     {
    6076           8 :         CPLError(CE_Warning, CPLE_AppDefined,
    6077             :                  "Using PIXELTYPE=SIGNEDBYTE with Byte data type is deprecated "
    6078             :                  "(but still works). "
    6079             :                  "Using Int8 data type instead is now recommended.");
    6080             :     }
    6081             : 
    6082             :     /* -------------------------------------------------------------------- */
    6083             :     /*      Setup some standard flags.                                      */
    6084             :     /* -------------------------------------------------------------------- */
    6085        9544 :     TIFFSetField(l_hTIFF, TIFFTAG_IMAGEWIDTH, nXSize);
    6086        9544 :     TIFFSetField(l_hTIFF, TIFFTAG_IMAGELENGTH, nYSize);
    6087        9544 :     TIFFSetField(l_hTIFF, TIFFTAG_BITSPERSAMPLE, l_nBitsPerSample);
    6088             : 
    6089        9544 :     uint16_t l_nSampleFormat = 0;
    6090        9544 :     if ((eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE")) ||
    6091        9401 :         eType == GDT_Int8 || eType == GDT_Int16 || eType == GDT_Int32 ||
    6092             :         eType == GDT_Int64)
    6093         774 :         l_nSampleFormat = SAMPLEFORMAT_INT;
    6094        8770 :     else if (eType == GDT_CInt16 || eType == GDT_CInt32)
    6095         355 :         l_nSampleFormat = SAMPLEFORMAT_COMPLEXINT;
    6096        8415 :     else if (eType == GDT_Float16 || eType == GDT_Float32 ||
    6097             :              eType == GDT_Float64)
    6098        1076 :         l_nSampleFormat = SAMPLEFORMAT_IEEEFP;
    6099        7339 :     else if (eType == GDT_CFloat16 || eType == GDT_CFloat32 ||
    6100             :              eType == GDT_CFloat64)
    6101         463 :         l_nSampleFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
    6102             :     else
    6103        6876 :         l_nSampleFormat = SAMPLEFORMAT_UINT;
    6104             : 
    6105        9544 :     TIFFSetField(l_hTIFF, TIFFTAG_SAMPLEFORMAT, l_nSampleFormat);
    6106        9544 :     TIFFSetField(l_hTIFF, TIFFTAG_SAMPLESPERPIXEL, l_nBands);
    6107        9544 :     TIFFSetField(l_hTIFF, TIFFTAG_PLANARCONFIG, nPlanar);
    6108             : 
    6109             :     /* -------------------------------------------------------------------- */
    6110             :     /*      Setup Photometric Interpretation. Take this value from the user */
    6111             :     /*      passed option or guess correct value otherwise.                 */
    6112             :     /* -------------------------------------------------------------------- */
    6113        9544 :     int nSamplesAccountedFor = 1;
    6114        9544 :     bool bForceColorTable = false;
    6115             : 
    6116        9544 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "PHOTOMETRIC"))
    6117             :     {
    6118        1900 :         if (EQUAL(pszValue, "MINISBLACK"))
    6119          14 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6120        1886 :         else if (EQUAL(pszValue, "MINISWHITE"))
    6121             :         {
    6122           2 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
    6123             :         }
    6124        1884 :         else if (EQUAL(pszValue, "PALETTE"))
    6125             :         {
    6126           5 :             if (eType == GDT_Byte || eType == GDT_UInt16)
    6127             :             {
    6128           4 :                 TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    6129           4 :                 nSamplesAccountedFor = 1;
    6130           4 :                 bForceColorTable = true;
    6131             :             }
    6132             :             else
    6133             :             {
    6134           1 :                 ReportError(
    6135             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    6136             :                     "PHOTOMETRIC=PALETTE only compatible with Byte or UInt16");
    6137             :             }
    6138             :         }
    6139        1879 :         else if (EQUAL(pszValue, "RGB"))
    6140             :         {
    6141        1139 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    6142        1139 :             nSamplesAccountedFor = 3;
    6143             :         }
    6144         740 :         else if (EQUAL(pszValue, "CMYK"))
    6145             :         {
    6146          10 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
    6147          10 :             nSamplesAccountedFor = 4;
    6148             :         }
    6149         730 :         else if (EQUAL(pszValue, "YCBCR"))
    6150             :         {
    6151             :             // Because of subsampling, setting YCBCR without JPEG compression
    6152             :             // leads to a crash currently. Would need to make
    6153             :             // GTiffRasterBand::IWriteBlock() aware of subsampling so that it
    6154             :             // doesn't overrun buffer size returned by libtiff.
    6155         729 :             if (l_nCompression != COMPRESSION_JPEG)
    6156             :             {
    6157           1 :                 ReportError(
    6158             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    6159             :                     "Currently, PHOTOMETRIC=YCBCR requires COMPRESS=JPEG");
    6160           1 :                 XTIFFClose(l_hTIFF);
    6161           1 :                 l_fpL->CancelCreation();
    6162           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6163           1 :                 return nullptr;
    6164             :             }
    6165             : 
    6166         728 :             if (nPlanar == PLANARCONFIG_SEPARATE)
    6167             :             {
    6168           1 :                 ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    6169             :                             "PHOTOMETRIC=YCBCR requires INTERLEAVE=PIXEL");
    6170           1 :                 XTIFFClose(l_hTIFF);
    6171           1 :                 l_fpL->CancelCreation();
    6172           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6173           1 :                 return nullptr;
    6174             :             }
    6175             : 
    6176             :             // YCBCR strictly requires 3 bands. Not less, not more Issue an
    6177             :             // explicit error message as libtiff one is a bit cryptic:
    6178             :             // TIFFVStripSize64:Invalid td_samplesperpixel value.
    6179         727 :             if (l_nBands != 3)
    6180             :             {
    6181           1 :                 ReportError(
    6182             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    6183             :                     "PHOTOMETRIC=YCBCR not supported on a %d-band raster: "
    6184             :                     "only compatible of a 3-band (RGB) raster",
    6185             :                     l_nBands);
    6186           1 :                 XTIFFClose(l_hTIFF);
    6187           1 :                 l_fpL->CancelCreation();
    6188           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6189           1 :                 return nullptr;
    6190             :             }
    6191             : 
    6192         726 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
    6193         726 :             nSamplesAccountedFor = 3;
    6194             : 
    6195             :             // Explicitly register the subsampling so that JPEGFixupTags
    6196             :             // is a no-op (helps for cloud optimized geotiffs)
    6197         726 :             TIFFSetField(l_hTIFF, TIFFTAG_YCBCRSUBSAMPLING, 2, 2);
    6198             :         }
    6199           1 :         else if (EQUAL(pszValue, "CIELAB"))
    6200             :         {
    6201           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB);
    6202           0 :             nSamplesAccountedFor = 3;
    6203             :         }
    6204           1 :         else if (EQUAL(pszValue, "ICCLAB"))
    6205             :         {
    6206           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ICCLAB);
    6207           0 :             nSamplesAccountedFor = 3;
    6208             :         }
    6209           1 :         else if (EQUAL(pszValue, "ITULAB"))
    6210             :         {
    6211           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ITULAB);
    6212           0 :             nSamplesAccountedFor = 3;
    6213             :         }
    6214             :         else
    6215             :         {
    6216           1 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    6217             :                         "PHOTOMETRIC=%s value not recognised, ignoring.  "
    6218             :                         "Set the Photometric Interpretation as MINISBLACK.",
    6219             :                         pszValue);
    6220           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6221             :         }
    6222             : 
    6223        1897 :         if (l_nBands < nSamplesAccountedFor)
    6224             :         {
    6225           1 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    6226             :                         "PHOTOMETRIC=%s value does not correspond to number "
    6227             :                         "of bands (%d), ignoring.  "
    6228             :                         "Set the Photometric Interpretation as MINISBLACK.",
    6229             :                         pszValue, l_nBands);
    6230           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6231             :         }
    6232             :     }
    6233             :     else
    6234             :     {
    6235             :         // If image contains 3 or 4 bands and datatype is Byte then we will
    6236             :         // assume it is RGB. In all other cases assume it is MINISBLACK.
    6237        7644 :         if (l_nBands == 3 && eType == GDT_Byte)
    6238             :         {
    6239         315 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    6240         315 :             nSamplesAccountedFor = 3;
    6241             :         }
    6242        7329 :         else if (l_nBands == 4 && eType == GDT_Byte)
    6243             :         {
    6244             :             uint16_t v[1] = {
    6245         717 :                 GTiffGetAlphaValue(CSLFetchNameValue(papszParamList, "ALPHA"),
    6246         717 :                                    DEFAULT_ALPHA_TYPE)};
    6247             : 
    6248         717 :             TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
    6249         717 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    6250         717 :             nSamplesAccountedFor = 4;
    6251             :         }
    6252             :         else
    6253             :         {
    6254        6612 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6255        6612 :             nSamplesAccountedFor = 1;
    6256             :         }
    6257             :     }
    6258             : 
    6259             :     /* -------------------------------------------------------------------- */
    6260             :     /*      If there are extra samples, we need to mark them with an        */
    6261             :     /*      appropriate extrasamples definition here.                       */
    6262             :     /* -------------------------------------------------------------------- */
    6263        9541 :     if (l_nBands > nSamplesAccountedFor)
    6264             :     {
    6265        1321 :         const int nExtraSamples = l_nBands - nSamplesAccountedFor;
    6266             : 
    6267             :         uint16_t *v = static_cast<uint16_t *>(
    6268        1321 :             CPLMalloc(sizeof(uint16_t) * nExtraSamples));
    6269             : 
    6270        1321 :         v[0] = GTiffGetAlphaValue(CSLFetchNameValue(papszParamList, "ALPHA"),
    6271             :                                   EXTRASAMPLE_UNSPECIFIED);
    6272             : 
    6273      297583 :         for (int i = 1; i < nExtraSamples; ++i)
    6274      296262 :             v[i] = EXTRASAMPLE_UNSPECIFIED;
    6275             : 
    6276        1321 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples, v);
    6277             : 
    6278        1321 :         CPLFree(v);
    6279             :     }
    6280             : 
    6281             :     // Set the ICC color profile.
    6282        9541 :     if (eProfile != GTiffProfile::BASELINE)
    6283             :     {
    6284        9516 :         SaveICCProfile(nullptr, l_hTIFF, papszParamList, l_nBitsPerSample);
    6285             :     }
    6286             : 
    6287             :     // Set the compression method before asking the default strip size
    6288             :     // This is useful when translating to a JPEG-In-TIFF file where
    6289             :     // the default strip size is 8 or 16 depending on the photometric value.
    6290        9541 :     TIFFSetField(l_hTIFF, TIFFTAG_COMPRESSION, l_nCompression);
    6291             : 
    6292        9541 :     if (l_nCompression == COMPRESSION_LERC)
    6293             :     {
    6294             :         const char *pszCompress =
    6295          97 :             CSLFetchNameValueDef(papszParamList, "COMPRESS", "");
    6296          97 :         if (EQUAL(pszCompress, "LERC_DEFLATE"))
    6297             :         {
    6298          16 :             TIFFSetField(l_hTIFF, TIFFTAG_LERC_ADD_COMPRESSION,
    6299             :                          LERC_ADD_COMPRESSION_DEFLATE);
    6300             :         }
    6301          81 :         else if (EQUAL(pszCompress, "LERC_ZSTD"))
    6302             :         {
    6303          14 :             if (TIFFSetField(l_hTIFF, TIFFTAG_LERC_ADD_COMPRESSION,
    6304          14 :                              LERC_ADD_COMPRESSION_ZSTD) != 1)
    6305             :             {
    6306           0 :                 XTIFFClose(l_hTIFF);
    6307           0 :                 l_fpL->CancelCreation();
    6308           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6309           0 :                 return nullptr;
    6310             :             }
    6311             :         }
    6312             :     }
    6313             :     // TODO later: take into account LERC version
    6314             : 
    6315             :     /* -------------------------------------------------------------------- */
    6316             :     /*      Setup tiling/stripping flags.                                   */
    6317             :     /* -------------------------------------------------------------------- */
    6318        9541 :     if (bTiled)
    6319             :     {
    6320        1402 :         if (!TIFFSetField(l_hTIFF, TIFFTAG_TILEWIDTH, l_nBlockXSize) ||
    6321         701 :             !TIFFSetField(l_hTIFF, TIFFTAG_TILELENGTH, l_nBlockYSize))
    6322             :         {
    6323           0 :             XTIFFClose(l_hTIFF);
    6324           0 :             l_fpL->CancelCreation();
    6325           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6326           0 :             return nullptr;
    6327             :         }
    6328             :     }
    6329             :     else
    6330             :     {
    6331        8840 :         const uint32_t l_nRowsPerStrip = std::min(
    6332             :             nYSize, l_nBlockYSize == 0
    6333        8840 :                         ? static_cast<int>(TIFFDefaultStripSize(l_hTIFF, 0))
    6334        8840 :                         : l_nBlockYSize);
    6335             : 
    6336        8840 :         TIFFSetField(l_hTIFF, TIFFTAG_ROWSPERSTRIP, l_nRowsPerStrip);
    6337             :     }
    6338             : 
    6339             :     /* -------------------------------------------------------------------- */
    6340             :     /*      Set compression related tags.                                   */
    6341             :     /* -------------------------------------------------------------------- */
    6342        9541 :     if (GTIFFSupportsPredictor(l_nCompression))
    6343         866 :         TIFFSetField(l_hTIFF, TIFFTAG_PREDICTOR, nPredictor);
    6344        9541 :     if (l_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    6345             :         l_nCompression == COMPRESSION_LERC)
    6346             :     {
    6347         277 :         GTiffSetDeflateSubCodec(l_hTIFF);
    6348             : 
    6349         277 :         if (l_nZLevel != -1)
    6350          22 :             TIFFSetField(l_hTIFF, TIFFTAG_ZIPQUALITY, l_nZLevel);
    6351             :     }
    6352        9541 :     if (l_nCompression == COMPRESSION_JPEG && l_nJpegQuality != -1)
    6353        1905 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGQUALITY, l_nJpegQuality);
    6354        9541 :     if (l_nCompression == COMPRESSION_LZMA && l_nLZMAPreset != -1)
    6355          10 :         TIFFSetField(l_hTIFF, TIFFTAG_LZMAPRESET, l_nLZMAPreset);
    6356        9541 :     if ((l_nCompression == COMPRESSION_ZSTD ||
    6357         156 :          l_nCompression == COMPRESSION_LERC) &&
    6358             :         l_nZSTDLevel != -1)
    6359          12 :         TIFFSetField(l_hTIFF, TIFFTAG_ZSTD_LEVEL, l_nZSTDLevel);
    6360        9541 :     if (l_nCompression == COMPRESSION_LERC)
    6361             :     {
    6362          97 :         TIFFSetField(l_hTIFF, TIFFTAG_LERC_MAXZERROR, l_dfMaxZError);
    6363             :     }
    6364             : #if HAVE_JXL
    6365        9541 :     if (l_nCompression == COMPRESSION_JXL ||
    6366             :         l_nCompression == COMPRESSION_JXL_DNG_1_7)
    6367             :     {
    6368         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_LOSSYNESS,
    6369             :                      l_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
    6370         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_EFFORT, l_nJXLEffort);
    6371         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_DISTANCE,
    6372             :                      static_cast<double>(l_fJXLDistance));
    6373         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE,
    6374             :                      static_cast<double>(l_fJXLAlphaDistance));
    6375             :     }
    6376             : #endif
    6377        9541 :     if (l_nCompression == COMPRESSION_WEBP)
    6378          33 :         TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LEVEL, l_nWebPLevel);
    6379        9541 :     if (l_nCompression == COMPRESSION_WEBP && l_bWebPLossless)
    6380           7 :         TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LOSSLESS, 1);
    6381             : 
    6382        9541 :     if (l_nCompression == COMPRESSION_JPEG)
    6383        2083 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGTABLESMODE, l_nJpegTablesMode);
    6384             : 
    6385             :     /* -------------------------------------------------------------------- */
    6386             :     /*      If we forced production of a file with photometric=palette,     */
    6387             :     /*      we need to push out a default color table.                      */
    6388             :     /* -------------------------------------------------------------------- */
    6389        9541 :     if (bForceColorTable)
    6390             :     {
    6391           4 :         const int nColors = eType == GDT_Byte ? 256 : 65536;
    6392             : 
    6393             :         unsigned short *panTRed = static_cast<unsigned short *>(
    6394           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    6395             :         unsigned short *panTGreen = static_cast<unsigned short *>(
    6396           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    6397             :         unsigned short *panTBlue = static_cast<unsigned short *>(
    6398           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    6399             : 
    6400        1028 :         for (int iColor = 0; iColor < nColors; ++iColor)
    6401             :         {
    6402        1024 :             if (eType == GDT_Byte)
    6403             :             {
    6404        1024 :                 panTRed[iColor] = GTiffDataset::ClampCTEntry(
    6405             :                     iColor, 1, iColor, nColorTableMultiplier);
    6406        1024 :                 panTGreen[iColor] = GTiffDataset::ClampCTEntry(
    6407             :                     iColor, 2, iColor, nColorTableMultiplier);
    6408        1024 :                 panTBlue[iColor] = GTiffDataset::ClampCTEntry(
    6409             :                     iColor, 3, iColor, nColorTableMultiplier);
    6410             :             }
    6411             :             else
    6412             :             {
    6413           0 :                 panTRed[iColor] = static_cast<unsigned short>(iColor);
    6414           0 :                 panTGreen[iColor] = static_cast<unsigned short>(iColor);
    6415           0 :                 panTBlue[iColor] = static_cast<unsigned short>(iColor);
    6416             :             }
    6417             :         }
    6418             : 
    6419           4 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue);
    6420             : 
    6421           4 :         CPLFree(panTRed);
    6422           4 :         CPLFree(panTGreen);
    6423           4 :         CPLFree(panTBlue);
    6424             :     }
    6425             : 
    6426             :     // This trick
    6427             :     // creates a temporary in-memory file and fetches its JPEG tables so that
    6428             :     // we can directly set them, before tif_jpeg.c compute them at the first
    6429             :     // strip/tile writing, which is too late, since we have already crystalized
    6430             :     // the directory. This way we avoid a directory rewriting.
    6431       11624 :     if (l_nCompression == COMPRESSION_JPEG &&
    6432        2083 :         CPLTestBool(
    6433             :             CSLFetchNameValueDef(papszParamList, "WRITE_JPEGTABLE_TAG", "YES")))
    6434             :     {
    6435        1014 :         GTiffWriteJPEGTables(
    6436             :             l_hTIFF, CSLFetchNameValue(papszParamList, "PHOTOMETRIC"),
    6437             :             CSLFetchNameValue(papszParamList, "JPEG_QUALITY"),
    6438             :             CSLFetchNameValue(papszParamList, "JPEGTABLESMODE"));
    6439             :     }
    6440             : 
    6441        9541 :     *pfpL = l_fpL;
    6442             : 
    6443        9541 :     return l_hTIFF;
    6444             : }
    6445             : 
    6446             : /************************************************************************/
    6447             : /*                            GuessJPEGQuality()                        */
    6448             : /*                                                                      */
    6449             : /*      Guess JPEG quality from JPEGTABLES tag.                         */
    6450             : /************************************************************************/
    6451             : 
    6452        3850 : static const GByte *GTIFFFindNextTable(const GByte *paby, GByte byMarker,
    6453             :                                        int nLen, int *pnLenTable)
    6454             : {
    6455        7967 :     for (int i = 0; i + 1 < nLen;)
    6456             :     {
    6457        7967 :         if (paby[i] != 0xFF)
    6458           0 :             return nullptr;
    6459        7967 :         ++i;
    6460        7967 :         if (paby[i] == 0xD8)
    6461             :         {
    6462        3117 :             ++i;
    6463        3117 :             continue;
    6464             :         }
    6465        4850 :         if (i + 2 >= nLen)
    6466         833 :             return nullptr;
    6467        4017 :         int nMarkerLen = paby[i + 1] * 256 + paby[i + 2];
    6468        4017 :         if (i + 1 + nMarkerLen >= nLen)
    6469           0 :             return nullptr;
    6470        4017 :         if (paby[i] == byMarker)
    6471             :         {
    6472        3017 :             if (pnLenTable)
    6473        2473 :                 *pnLenTable = nMarkerLen;
    6474        3017 :             return paby + i + 1;
    6475             :         }
    6476        1000 :         i += 1 + nMarkerLen;
    6477             :     }
    6478           0 :     return nullptr;
    6479             : }
    6480             : 
    6481             : constexpr GByte MARKER_HUFFMAN_TABLE = 0xC4;
    6482             : constexpr GByte MARKER_QUANT_TABLE = 0xDB;
    6483             : 
    6484             : // We assume that if there are several quantization tables, they are
    6485             : // in the same order. Which is a reasonable assumption for updating
    6486             : // a file generated by ourselves.
    6487         904 : static bool GTIFFQuantizationTablesEqual(const GByte *paby1, int nLen1,
    6488             :                                          const GByte *paby2, int nLen2)
    6489             : {
    6490         904 :     bool bFound = false;
    6491             :     while (true)
    6492             :     {
    6493         945 :         int nLenTable1 = 0;
    6494         945 :         int nLenTable2 = 0;
    6495             :         const GByte *paby1New =
    6496         945 :             GTIFFFindNextTable(paby1, MARKER_QUANT_TABLE, nLen1, &nLenTable1);
    6497             :         const GByte *paby2New =
    6498         945 :             GTIFFFindNextTable(paby2, MARKER_QUANT_TABLE, nLen2, &nLenTable2);
    6499         945 :         if (paby1New == nullptr && paby2New == nullptr)
    6500         904 :             return bFound;
    6501         911 :         if (paby1New == nullptr || paby2New == nullptr)
    6502           0 :             return false;
    6503         911 :         if (nLenTable1 != nLenTable2)
    6504         207 :             return false;
    6505         704 :         if (memcmp(paby1New, paby2New, nLenTable1) != 0)
    6506         663 :             return false;
    6507          41 :         paby1New += nLenTable1;
    6508          41 :         paby2New += nLenTable2;
    6509          41 :         nLen1 -= static_cast<int>(paby1New - paby1);
    6510          41 :         nLen2 -= static_cast<int>(paby2New - paby2);
    6511          41 :         paby1 = paby1New;
    6512          41 :         paby2 = paby2New;
    6513          41 :         bFound = true;
    6514          41 :     }
    6515             : }
    6516             : 
    6517             : // Guess the JPEG quality by comparing against the MD5Sum of precomputed
    6518             : // quantization tables
    6519         409 : static int GuessJPEGQualityFromMD5(const uint8_t md5JPEGQuantTable[][16],
    6520             :                                    const GByte *const pabyJPEGTable,
    6521             :                                    int nJPEGTableSize)
    6522             : {
    6523         409 :     int nRemainingLen = nJPEGTableSize;
    6524         409 :     const GByte *pabyCur = pabyJPEGTable;
    6525             : 
    6526             :     struct CPLMD5Context context;
    6527         409 :     CPLMD5Init(&context);
    6528             : 
    6529             :     while (true)
    6530             :     {
    6531        1060 :         int nLenTable = 0;
    6532        1060 :         const GByte *pabyNew = GTIFFFindNextTable(pabyCur, MARKER_QUANT_TABLE,
    6533             :                                                   nRemainingLen, &nLenTable);
    6534        1060 :         if (pabyNew == nullptr)
    6535         409 :             break;
    6536         651 :         CPLMD5Update(&context, pabyNew, nLenTable);
    6537         651 :         pabyNew += nLenTable;
    6538         651 :         nRemainingLen -= static_cast<int>(pabyNew - pabyCur);
    6539         651 :         pabyCur = pabyNew;
    6540         651 :     }
    6541             : 
    6542             :     GByte digest[16];
    6543         409 :     CPLMD5Final(digest, &context);
    6544             : 
    6545       28846 :     for (int i = 0; i < 100; i++)
    6546             :     {
    6547       28843 :         if (memcmp(md5JPEGQuantTable[i], digest, 16) == 0)
    6548             :         {
    6549         406 :             return i + 1;
    6550             :         }
    6551             :     }
    6552           3 :     return -1;
    6553             : }
    6554             : 
    6555         464 : int GTiffDataset::GuessJPEGQuality(bool &bOutHasQuantizationTable,
    6556             :                                    bool &bOutHasHuffmanTable)
    6557             : {
    6558         464 :     CPLAssert(m_nCompression == COMPRESSION_JPEG);
    6559         464 :     uint32_t nJPEGTableSize = 0;
    6560         464 :     void *pJPEGTable = nullptr;
    6561         464 :     if (!TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize,
    6562             :                       &pJPEGTable))
    6563             :     {
    6564          14 :         bOutHasQuantizationTable = false;
    6565          14 :         bOutHasHuffmanTable = false;
    6566          14 :         return -1;
    6567             :     }
    6568             : 
    6569         450 :     bOutHasQuantizationTable =
    6570         450 :         GTIFFFindNextTable(static_cast<const GByte *>(pJPEGTable),
    6571             :                            MARKER_QUANT_TABLE, nJPEGTableSize,
    6572         450 :                            nullptr) != nullptr;
    6573         450 :     bOutHasHuffmanTable =
    6574         450 :         GTIFFFindNextTable(static_cast<const GByte *>(pJPEGTable),
    6575             :                            MARKER_HUFFMAN_TABLE, nJPEGTableSize,
    6576         450 :                            nullptr) != nullptr;
    6577         450 :     if (!bOutHasQuantizationTable)
    6578           7 :         return -1;
    6579             : 
    6580         443 :     if ((nBands == 1 && m_nBitsPerSample == 8) ||
    6581         382 :         (nBands == 3 && m_nBitsPerSample == 8 &&
    6582         336 :          m_nPhotometric == PHOTOMETRIC_RGB) ||
    6583         288 :         (nBands == 4 && m_nBitsPerSample == 8 &&
    6584          27 :          m_nPhotometric == PHOTOMETRIC_SEPARATED))
    6585             :     {
    6586         167 :         return GuessJPEGQualityFromMD5(md5JPEGQuantTable_generic_8bit,
    6587             :                                        static_cast<const GByte *>(pJPEGTable),
    6588         167 :                                        static_cast<int>(nJPEGTableSize));
    6589             :     }
    6590             : 
    6591         276 :     if (nBands == 3 && m_nBitsPerSample == 8 &&
    6592         242 :         m_nPhotometric == PHOTOMETRIC_YCBCR)
    6593             :     {
    6594             :         int nRet =
    6595         242 :             GuessJPEGQualityFromMD5(md5JPEGQuantTable_3_YCBCR_8bit,
    6596             :                                     static_cast<const GByte *>(pJPEGTable),
    6597             :                                     static_cast<int>(nJPEGTableSize));
    6598         242 :         if (nRet < 0)
    6599             :         {
    6600             :             // libjpeg 9e has modified the YCbCr quantization tables.
    6601             :             nRet =
    6602           0 :                 GuessJPEGQualityFromMD5(md5JPEGQuantTable_3_YCBCR_8bit_jpeg9e,
    6603             :                                         static_cast<const GByte *>(pJPEGTable),
    6604             :                                         static_cast<int>(nJPEGTableSize));
    6605             :         }
    6606         242 :         return nRet;
    6607             :     }
    6608             : 
    6609          34 :     char **papszLocalParameters = nullptr;
    6610             :     papszLocalParameters =
    6611          34 :         CSLSetNameValue(papszLocalParameters, "COMPRESS", "JPEG");
    6612          34 :     if (m_nPhotometric == PHOTOMETRIC_YCBCR)
    6613             :         papszLocalParameters =
    6614           7 :             CSLSetNameValue(papszLocalParameters, "PHOTOMETRIC", "YCBCR");
    6615          27 :     else if (m_nPhotometric == PHOTOMETRIC_SEPARATED)
    6616             :         papszLocalParameters =
    6617           0 :             CSLSetNameValue(papszLocalParameters, "PHOTOMETRIC", "CMYK");
    6618             :     papszLocalParameters =
    6619          34 :         CSLSetNameValue(papszLocalParameters, "BLOCKYSIZE", "16");
    6620          34 :     if (m_nBitsPerSample == 12)
    6621             :         papszLocalParameters =
    6622          16 :             CSLSetNameValue(papszLocalParameters, "NBITS", "12");
    6623             : 
    6624             :     const CPLString osTmpFilenameIn(
    6625          34 :         VSIMemGenerateHiddenFilename("gtiffdataset_guess_jpeg_quality_tmp"));
    6626             : 
    6627          34 :     int nRet = -1;
    6628         938 :     for (int nQuality = 0; nQuality <= 100 && nRet < 0; ++nQuality)
    6629             :     {
    6630         904 :         VSILFILE *fpTmp = nullptr;
    6631         904 :         if (nQuality == 0)
    6632             :             papszLocalParameters =
    6633          34 :                 CSLSetNameValue(papszLocalParameters, "JPEG_QUALITY", "75");
    6634             :         else
    6635             :             papszLocalParameters =
    6636         870 :                 CSLSetNameValue(papszLocalParameters, "JPEG_QUALITY",
    6637             :                                 CPLSPrintf("%d", nQuality));
    6638             : 
    6639         904 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    6640         904 :         CPLString osTmp;
    6641             :         bool bTileInterleaving;
    6642        1808 :         TIFF *hTIFFTmp = CreateLL(
    6643         904 :             osTmpFilenameIn, 16, 16, (nBands <= 4) ? nBands : 1,
    6644             :             GetRasterBand(1)->GetRasterDataType(), 0.0, 0, papszLocalParameters,
    6645             :             &fpTmp, osTmp, /* bCreateCopy=*/false, bTileInterleaving);
    6646         904 :         CPLPopErrorHandler();
    6647         904 :         if (!hTIFFTmp)
    6648             :         {
    6649           0 :             break;
    6650             :         }
    6651             : 
    6652         904 :         TIFFWriteCheck(hTIFFTmp, FALSE, "CreateLL");
    6653         904 :         TIFFWriteDirectory(hTIFFTmp);
    6654         904 :         TIFFSetDirectory(hTIFFTmp, 0);
    6655             :         // Now reset jpegcolormode.
    6656        1196 :         if (m_nPhotometric == PHOTOMETRIC_YCBCR &&
    6657         292 :             CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
    6658             :         {
    6659         292 :             TIFFSetField(hTIFFTmp, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    6660             :         }
    6661             : 
    6662         904 :         GByte abyZeroData[(16 * 16 * 4 * 3) / 2] = {};
    6663         904 :         const int nBlockSize =
    6664         904 :             (16 * 16 * ((nBands <= 4) ? nBands : 1) * m_nBitsPerSample) / 8;
    6665         904 :         TIFFWriteEncodedStrip(hTIFFTmp, 0, abyZeroData, nBlockSize);
    6666             : 
    6667         904 :         uint32_t nJPEGTableSizeTry = 0;
    6668         904 :         void *pJPEGTableTry = nullptr;
    6669         904 :         if (TIFFGetField(hTIFFTmp, TIFFTAG_JPEGTABLES, &nJPEGTableSizeTry,
    6670         904 :                          &pJPEGTableTry))
    6671             :         {
    6672         904 :             if (GTIFFQuantizationTablesEqual(
    6673             :                     static_cast<GByte *>(pJPEGTable), nJPEGTableSize,
    6674             :                     static_cast<GByte *>(pJPEGTableTry), nJPEGTableSizeTry))
    6675             :             {
    6676          34 :                 nRet = (nQuality == 0) ? 75 : nQuality;
    6677             :             }
    6678             :         }
    6679             : 
    6680         904 :         XTIFFClose(hTIFFTmp);
    6681         904 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpTmp));
    6682             :     }
    6683             : 
    6684          34 :     CSLDestroy(papszLocalParameters);
    6685          34 :     VSIUnlink(osTmpFilenameIn);
    6686             : 
    6687          34 :     return nRet;
    6688             : }
    6689             : 
    6690             : /************************************************************************/
    6691             : /*               SetJPEGQualityAndTablesModeFromFile()                  */
    6692             : /************************************************************************/
    6693             : 
    6694         161 : void GTiffDataset::SetJPEGQualityAndTablesModeFromFile(
    6695             :     int nQuality, bool bHasQuantizationTable, bool bHasHuffmanTable)
    6696             : {
    6697         161 :     if (nQuality > 0)
    6698             :     {
    6699         154 :         CPLDebug("GTiff", "Guessed JPEG quality to be %d", nQuality);
    6700         154 :         m_nJpegQuality = static_cast<signed char>(nQuality);
    6701         154 :         TIFFSetField(m_hTIFF, TIFFTAG_JPEGQUALITY, nQuality);
    6702             : 
    6703             :         // This means we will use the quantization tables from the
    6704             :         // JpegTables tag.
    6705         154 :         m_nJpegTablesMode = JPEGTABLESMODE_QUANT;
    6706             :     }
    6707             :     else
    6708             :     {
    6709           7 :         uint32_t nJPEGTableSize = 0;
    6710           7 :         void *pJPEGTable = nullptr;
    6711           7 :         if (!TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize,
    6712             :                           &pJPEGTable))
    6713             :         {
    6714           4 :             toff_t *panByteCounts = nullptr;
    6715           8 :             const int nBlockCount = m_nPlanarConfig == PLANARCONFIG_SEPARATE
    6716           4 :                                         ? m_nBlocksPerBand * nBands
    6717             :                                         : m_nBlocksPerBand;
    6718           4 :             if (TIFFIsTiled(m_hTIFF))
    6719           1 :                 TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts);
    6720             :             else
    6721           3 :                 TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
    6722             : 
    6723           4 :             bool bFoundNonEmptyBlock = false;
    6724           4 :             if (panByteCounts != nullptr)
    6725             :             {
    6726          56 :                 for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
    6727             :                 {
    6728          53 :                     if (panByteCounts[iBlock] != 0)
    6729             :                     {
    6730           1 :                         bFoundNonEmptyBlock = true;
    6731           1 :                         break;
    6732             :                     }
    6733             :                 }
    6734             :             }
    6735           4 :             if (bFoundNonEmptyBlock)
    6736             :             {
    6737           1 :                 CPLDebug("GTiff", "Could not guess JPEG quality. "
    6738             :                                   "JPEG tables are missing, so going in "
    6739             :                                   "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6740             :                 // Write quantization tables in each strile.
    6741           1 :                 m_nJpegTablesMode = 0;
    6742             :             }
    6743             :         }
    6744             :         else
    6745             :         {
    6746           3 :             if (bHasQuantizationTable)
    6747             :             {
    6748             :                 // FIXME in libtiff: this is likely going to cause issues
    6749             :                 // since libtiff will reuse in each strile the number of
    6750             :                 // the global quantization table, which is invalid.
    6751           1 :                 CPLDebug("GTiff",
    6752             :                          "Could not guess JPEG quality although JPEG "
    6753             :                          "quantization tables are present, so going in "
    6754             :                          "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6755             :             }
    6756             :             else
    6757             :             {
    6758           2 :                 CPLDebug("GTiff",
    6759             :                          "Could not guess JPEG quality since JPEG "
    6760             :                          "quantization tables are not present, so going in "
    6761             :                          "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6762             :             }
    6763             : 
    6764             :             // Write quantization tables in each strile.
    6765           3 :             m_nJpegTablesMode = 0;
    6766             :         }
    6767             :     }
    6768         161 :     if (bHasHuffmanTable)
    6769             :     {
    6770             :         // If there are Huffman tables in header use them, otherwise
    6771             :         // if we use optimized tables, libtiff will currently reuse
    6772             :         // the number of the Huffman tables of the header for the
    6773             :         // optimized version of each strile, which is illegal.
    6774          23 :         m_nJpegTablesMode |= JPEGTABLESMODE_HUFF;
    6775             :     }
    6776         161 :     if (m_nJpegTablesMode >= 0)
    6777         159 :         TIFFSetField(m_hTIFF, TIFFTAG_JPEGTABLESMODE, m_nJpegTablesMode);
    6778         161 : }
    6779             : 
    6780             : /************************************************************************/
    6781             : /*                               Create()                               */
    6782             : /*                                                                      */
    6783             : /*      Create a new GeoTIFF or TIFF file.                              */
    6784             : /************************************************************************/
    6785             : 
    6786        5548 : GDALDataset *GTiffDataset::Create(const char *pszFilename, int nXSize,
    6787             :                                   int nYSize, int l_nBands, GDALDataType eType,
    6788             :                                   char **papszParamList)
    6789             : 
    6790             : {
    6791        5548 :     VSILFILE *l_fpL = nullptr;
    6792       11096 :     CPLString l_osTmpFilename;
    6793             : 
    6794             :     const int nColorTableMultiplier = std::max(
    6795       11096 :         1,
    6796       11096 :         std::min(257,
    6797        5548 :                  atoi(CSLFetchNameValueDef(
    6798             :                      papszParamList, "COLOR_TABLE_MULTIPLIER",
    6799        5548 :                      CPLSPrintf("%d", DEFAULT_COLOR_TABLE_MULTIPLIER_257)))));
    6800             : 
    6801             :     /* -------------------------------------------------------------------- */
    6802             :     /*      Create the underlying TIFF file.                                */
    6803             :     /* -------------------------------------------------------------------- */
    6804             :     bool bTileInterleaving;
    6805             :     TIFF *l_hTIFF =
    6806        5548 :         CreateLL(pszFilename, nXSize, nYSize, l_nBands, eType, 0,
    6807             :                  nColorTableMultiplier, papszParamList, &l_fpL, l_osTmpFilename,
    6808             :                  /* bCreateCopy=*/false, bTileInterleaving);
    6809        5548 :     const bool bStreaming = !l_osTmpFilename.empty();
    6810             : 
    6811        5548 :     if (l_hTIFF == nullptr)
    6812          37 :         return nullptr;
    6813             : 
    6814             :     /* -------------------------------------------------------------------- */
    6815             :     /*      Create the new GTiffDataset object.                             */
    6816             :     /* -------------------------------------------------------------------- */
    6817        5511 :     GTiffDataset *poDS = new GTiffDataset();
    6818        5511 :     poDS->m_hTIFF = l_hTIFF;
    6819        5511 :     poDS->m_fpL = l_fpL;
    6820        5511 :     const bool bSuppressASAP = CPLTestBool(
    6821             :         CSLFetchNameValueDef(papszParamList, "@SUPPRESS_ASAP", "NO"));
    6822        5511 :     if (bSuppressASAP)
    6823           0 :         poDS->MarkSuppressOnClose();
    6824        5511 :     if (bStreaming)
    6825             :     {
    6826           4 :         poDS->m_bStreamingOut = true;
    6827           4 :         poDS->m_pszTmpFilename = CPLStrdup(l_osTmpFilename);
    6828           4 :         poDS->m_fpToWrite = VSIFOpenL(pszFilename, "wb");
    6829           4 :         if (poDS->m_fpToWrite == nullptr)
    6830             :         {
    6831           1 :             VSIUnlink(l_osTmpFilename);
    6832           1 :             delete poDS;
    6833           1 :             return nullptr;
    6834             :         }
    6835             :     }
    6836        5510 :     poDS->nRasterXSize = nXSize;
    6837        5510 :     poDS->nRasterYSize = nYSize;
    6838        5510 :     poDS->eAccess = GA_Update;
    6839             : 
    6840        5510 :     poDS->m_nColorTableMultiplier = nColorTableMultiplier;
    6841             : 
    6842        5510 :     poDS->m_bCrystalized = false;
    6843        5510 :     poDS->m_nSamplesPerPixel = static_cast<uint16_t>(l_nBands);
    6844        5510 :     poDS->m_osFilename = pszFilename;
    6845             : 
    6846             :     // Don't try to load external metadata files (#6597).
    6847        5510 :     poDS->m_bIMDRPCMetadataLoaded = true;
    6848             : 
    6849             :     // Avoid premature crystalization that will cause directory re-writing if
    6850             :     // GetProjectionRef() or GetGeoTransform() are called on the newly created
    6851             :     // GeoTIFF.
    6852        5510 :     poDS->m_bLookedForProjection = true;
    6853             : 
    6854        5510 :     TIFFGetField(l_hTIFF, TIFFTAG_SAMPLEFORMAT, &(poDS->m_nSampleFormat));
    6855        5510 :     TIFFGetField(l_hTIFF, TIFFTAG_PLANARCONFIG, &(poDS->m_nPlanarConfig));
    6856             :     // Weird that we need this, but otherwise we get a Valgrind warning on
    6857             :     // tiff_write_124.
    6858        5510 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &(poDS->m_nPhotometric)))
    6859           1 :         poDS->m_nPhotometric = PHOTOMETRIC_MINISBLACK;
    6860        5510 :     TIFFGetField(l_hTIFF, TIFFTAG_BITSPERSAMPLE, &(poDS->m_nBitsPerSample));
    6861        5510 :     TIFFGetField(l_hTIFF, TIFFTAG_COMPRESSION, &(poDS->m_nCompression));
    6862             : 
    6863        5510 :     if (TIFFIsTiled(l_hTIFF))
    6864             :     {
    6865         355 :         TIFFGetField(l_hTIFF, TIFFTAG_TILEWIDTH, &(poDS->m_nBlockXSize));
    6866         355 :         TIFFGetField(l_hTIFF, TIFFTAG_TILELENGTH, &(poDS->m_nBlockYSize));
    6867             :     }
    6868             :     else
    6869             :     {
    6870        5155 :         if (!TIFFGetField(l_hTIFF, TIFFTAG_ROWSPERSTRIP,
    6871             :                           &(poDS->m_nRowsPerStrip)))
    6872           0 :             poDS->m_nRowsPerStrip = 1;  // Dummy value.
    6873             : 
    6874        5155 :         poDS->m_nBlockXSize = nXSize;
    6875        5155 :         poDS->m_nBlockYSize =
    6876        5155 :             std::min(static_cast<int>(poDS->m_nRowsPerStrip), nYSize);
    6877             :     }
    6878             : 
    6879        5510 :     if (!poDS->ComputeBlocksPerColRowAndBand(l_nBands))
    6880             :     {
    6881           0 :         poDS->m_fpL->CancelCreation();
    6882           0 :         delete poDS;
    6883           0 :         return nullptr;
    6884             :     }
    6885             : 
    6886        5510 :     poDS->m_eProfile = GetProfile(CSLFetchNameValue(papszParamList, "PROFILE"));
    6887             : 
    6888             :     /* -------------------------------------------------------------------- */
    6889             :     /*      YCbCr JPEG compressed images should be translated on the fly    */
    6890             :     /*      to RGB by libtiff/libjpeg unless specifically requested         */
    6891             :     /*      otherwise.                                                      */
    6892             :     /* -------------------------------------------------------------------- */
    6893       11054 :     if (poDS->m_nCompression == COMPRESSION_JPEG &&
    6894        5531 :         poDS->m_nPhotometric == PHOTOMETRIC_YCBCR &&
    6895          21 :         CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
    6896             :     {
    6897          21 :         int nColorMode = 0;
    6898             : 
    6899          21 :         poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr", "IMAGE_STRUCTURE");
    6900          42 :         if (!TIFFGetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode) ||
    6901          21 :             nColorMode != JPEGCOLORMODE_RGB)
    6902          21 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    6903             :     }
    6904             : 
    6905        5510 :     if (poDS->m_nCompression == COMPRESSION_LERC)
    6906             :     {
    6907          26 :         uint32_t nLercParamCount = 0;
    6908          26 :         uint32_t *panLercParams = nullptr;
    6909          26 :         if (TIFFGetField(l_hTIFF, TIFFTAG_LERC_PARAMETERS, &nLercParamCount,
    6910          52 :                          &panLercParams) &&
    6911          26 :             nLercParamCount == 2)
    6912             :         {
    6913          26 :             memcpy(poDS->m_anLercAddCompressionAndVersion, panLercParams,
    6914             :                    sizeof(poDS->m_anLercAddCompressionAndVersion));
    6915             :         }
    6916             :     }
    6917             : 
    6918             :     /* -------------------------------------------------------------------- */
    6919             :     /*      Read palette back as a color table if it has one.               */
    6920             :     /* -------------------------------------------------------------------- */
    6921        5510 :     unsigned short *panRed = nullptr;
    6922        5510 :     unsigned short *panGreen = nullptr;
    6923        5510 :     unsigned short *panBlue = nullptr;
    6924             : 
    6925        5514 :     if (poDS->m_nPhotometric == PHOTOMETRIC_PALETTE &&
    6926           4 :         TIFFGetField(l_hTIFF, TIFFTAG_COLORMAP, &panRed, &panGreen, &panBlue))
    6927             :     {
    6928             : 
    6929           4 :         poDS->m_poColorTable = std::make_unique<GDALColorTable>();
    6930             : 
    6931           4 :         const int nColorCount = 1 << poDS->m_nBitsPerSample;
    6932             : 
    6933        1028 :         for (int iColor = nColorCount - 1; iColor >= 0; iColor--)
    6934             :         {
    6935        1024 :             const GDALColorEntry oEntry = {
    6936        1024 :                 static_cast<short>(panRed[iColor] / nColorTableMultiplier),
    6937        1024 :                 static_cast<short>(panGreen[iColor] / nColorTableMultiplier),
    6938        1024 :                 static_cast<short>(panBlue[iColor] / nColorTableMultiplier),
    6939        1024 :                 static_cast<short>(255)};
    6940             : 
    6941        1024 :             poDS->m_poColorTable->SetColorEntry(iColor, &oEntry);
    6942             :         }
    6943             :     }
    6944             : 
    6945             :     /* -------------------------------------------------------------------- */
    6946             :     /*      Do we want to ensure all blocks get written out on close to     */
    6947             :     /*      avoid sparse files?                                             */
    6948             :     /* -------------------------------------------------------------------- */
    6949        5510 :     if (!CPLFetchBool(papszParamList, "SPARSE_OK", false))
    6950        5400 :         poDS->m_bFillEmptyTilesAtClosing = true;
    6951             : 
    6952        5510 :     poDS->m_bWriteEmptyTiles =
    6953        6275 :         bStreaming || (poDS->m_nCompression != COMPRESSION_NONE &&
    6954         765 :                        poDS->m_bFillEmptyTilesAtClosing);
    6955             :     // Only required for people writing non-compressed striped files in the
    6956             :     // right order and wanting all tstrips to be written in the same order
    6957             :     // so that the end result can be memory mapped without knowledge of each
    6958             :     // strip offset.
    6959        5510 :     if (CPLTestBool(CSLFetchNameValueDef(
    6960       11020 :             papszParamList, "WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")) ||
    6961        5510 :         CPLTestBool(CSLFetchNameValueDef(
    6962             :             papszParamList, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")))
    6963             :     {
    6964          26 :         poDS->m_bWriteEmptyTiles = true;
    6965             :     }
    6966             : 
    6967             :     /* -------------------------------------------------------------------- */
    6968             :     /*      Preserve creation options for consulting later (for instance    */
    6969             :     /*      to decide if a TFW file should be written).                     */
    6970             :     /* -------------------------------------------------------------------- */
    6971        5510 :     poDS->m_papszCreationOptions = CSLDuplicate(papszParamList);
    6972             : 
    6973        5510 :     poDS->m_nZLevel = GTiffGetZLevel(papszParamList);
    6974        5510 :     poDS->m_nLZMAPreset = GTiffGetLZMAPreset(papszParamList);
    6975        5510 :     poDS->m_nZSTDLevel = GTiffGetZSTDPreset(papszParamList);
    6976        5510 :     poDS->m_nWebPLevel = GTiffGetWebPLevel(papszParamList);
    6977        5510 :     poDS->m_bWebPLossless = GTiffGetWebPLossless(papszParamList);
    6978        5512 :     if (poDS->m_nWebPLevel != 100 && poDS->m_bWebPLossless &&
    6979           2 :         CSLFetchNameValue(papszParamList, "WEBP_LEVEL"))
    6980             :     {
    6981           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    6982             :                  "WEBP_LEVEL is specified, but WEBP_LOSSLESS=YES. "
    6983             :                  "WEBP_LEVEL will be ignored.");
    6984             :     }
    6985        5510 :     poDS->m_nJpegQuality = GTiffGetJpegQuality(papszParamList);
    6986        5510 :     poDS->m_nJpegTablesMode = GTiffGetJpegTablesMode(papszParamList);
    6987        5510 :     poDS->m_dfMaxZError = GTiffGetLERCMaxZError(papszParamList);
    6988        5510 :     poDS->m_dfMaxZErrorOverview = GTiffGetLERCMaxZErrorOverview(papszParamList);
    6989             : #if HAVE_JXL
    6990        5510 :     poDS->m_bJXLLossless = GTiffGetJXLLossless(papszParamList);
    6991        5510 :     poDS->m_nJXLEffort = GTiffGetJXLEffort(papszParamList);
    6992        5510 :     poDS->m_fJXLDistance = GTiffGetJXLDistance(papszParamList);
    6993        5510 :     poDS->m_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszParamList);
    6994             : #endif
    6995        5510 :     poDS->InitCreationOrOpenOptions(true, papszParamList);
    6996             : 
    6997             :     /* -------------------------------------------------------------------- */
    6998             :     /*      Create band information objects.                                */
    6999             :     /* -------------------------------------------------------------------- */
    7000      307773 :     for (int iBand = 0; iBand < l_nBands; ++iBand)
    7001             :     {
    7002      302263 :         if (poDS->m_nBitsPerSample == 8 || poDS->m_nBitsPerSample == 16 ||
    7003        2598 :             poDS->m_nBitsPerSample == 32 || poDS->m_nBitsPerSample == 64 ||
    7004         237 :             poDS->m_nBitsPerSample == 128)
    7005             :         {
    7006      302198 :             poDS->SetBand(iBand + 1, new GTiffRasterBand(poDS, iBand + 1));
    7007             :         }
    7008             :         else
    7009             :         {
    7010          65 :             poDS->SetBand(iBand + 1, new GTiffOddBitsBand(poDS, iBand + 1));
    7011         130 :             poDS->GetRasterBand(iBand + 1)->SetMetadataItem(
    7012         130 :                 "NBITS", CPLString().Printf("%d", poDS->m_nBitsPerSample),
    7013          65 :                 "IMAGE_STRUCTURE");
    7014             :         }
    7015             :     }
    7016             : 
    7017        5510 :     poDS->GetDiscardLsbOption(papszParamList);
    7018             : 
    7019        5510 :     if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG && l_nBands != 1)
    7020         789 :         poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
    7021             :     else
    7022        4721 :         poDS->SetMetadataItem("INTERLEAVE", "BAND", "IMAGE_STRUCTURE");
    7023             : 
    7024        5510 :     poDS->oOvManager.Initialize(poDS, pszFilename);
    7025             : 
    7026        5510 :     return poDS;
    7027             : }
    7028             : 
    7029             : /************************************************************************/
    7030             : /*                           CopyImageryAndMask()                       */
    7031             : /************************************************************************/
    7032             : 
    7033         307 : CPLErr GTiffDataset::CopyImageryAndMask(GTiffDataset *poDstDS,
    7034             :                                         GDALDataset *poSrcDS,
    7035             :                                         GDALRasterBand *poSrcMaskBand,
    7036             :                                         GDALProgressFunc pfnProgress,
    7037             :                                         void *pProgressData)
    7038             : {
    7039         307 :     CPLErr eErr = CE_None;
    7040             : 
    7041         307 :     const auto eType = poDstDS->GetRasterBand(1)->GetRasterDataType();
    7042         307 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
    7043         307 :     const int l_nBands = poDstDS->GetRasterCount();
    7044             :     GByte *pBlockBuffer = static_cast<GByte *>(
    7045         307 :         VSI_MALLOC3_VERBOSE(poDstDS->m_nBlockXSize, poDstDS->m_nBlockYSize,
    7046             :                             cpl::fits_on<int>(l_nBands * nDataTypeSize)));
    7047         307 :     if (pBlockBuffer == nullptr)
    7048             :     {
    7049           0 :         eErr = CE_Failure;
    7050             :     }
    7051         307 :     const int nYSize = poDstDS->nRasterYSize;
    7052         307 :     const int nXSize = poDstDS->nRasterXSize;
    7053             :     const bool bIsOddBand =
    7054         307 :         dynamic_cast<GTiffOddBitsBand *>(poDstDS->GetRasterBand(1)) != nullptr;
    7055             : 
    7056         307 :     if (poDstDS->m_poMaskDS)
    7057             :     {
    7058          59 :         CPLAssert(poDstDS->m_poMaskDS->m_nBlockXSize == poDstDS->m_nBlockXSize);
    7059          59 :         CPLAssert(poDstDS->m_poMaskDS->m_nBlockYSize == poDstDS->m_nBlockYSize);
    7060             :     }
    7061             : 
    7062         307 :     if (poDstDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE &&
    7063          58 :         !poDstDS->m_bTileInterleave)
    7064             :     {
    7065          45 :         int iBlock = 0;
    7066          90 :         const int nBlocks = poDstDS->m_nBlocksPerBand *
    7067          45 :                             (l_nBands + (poDstDS->m_poMaskDS ? 1 : 0));
    7068         195 :         for (int i = 0; eErr == CE_None && i < l_nBands; i++)
    7069             :         {
    7070         345 :             for (int iY = 0, nYBlock = 0; iY < nYSize && eErr == CE_None;
    7071         195 :                  iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
    7072         195 :                            ? nYSize
    7073          59 :                            : iY + poDstDS->m_nBlockYSize),
    7074             :                      nYBlock++)
    7075             :             {
    7076             :                 const int nReqYSize =
    7077         195 :                     std::min(nYSize - iY, poDstDS->m_nBlockYSize);
    7078         495 :                 for (int iX = 0, nXBlock = 0; iX < nXSize && eErr == CE_None;
    7079         300 :                      iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
    7080         300 :                                ? nXSize
    7081         155 :                                : iX + poDstDS->m_nBlockXSize),
    7082             :                          nXBlock++)
    7083             :                 {
    7084             :                     const int nReqXSize =
    7085         300 :                         std::min(nXSize - iX, poDstDS->m_nBlockXSize);
    7086         300 :                     if (nReqXSize < poDstDS->m_nBlockXSize ||
    7087         155 :                         nReqYSize < poDstDS->m_nBlockYSize)
    7088             :                     {
    7089         190 :                         memset(pBlockBuffer, 0,
    7090         190 :                                static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7091         190 :                                    poDstDS->m_nBlockYSize * nDataTypeSize);
    7092             :                     }
    7093         300 :                     eErr = poSrcDS->GetRasterBand(i + 1)->RasterIO(
    7094             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7095             :                         nReqXSize, nReqYSize, eType, nDataTypeSize,
    7096         300 :                         static_cast<GSpacing>(nDataTypeSize) *
    7097         300 :                             poDstDS->m_nBlockXSize,
    7098             :                         nullptr);
    7099         300 :                     if (eErr == CE_None)
    7100             :                     {
    7101         300 :                         eErr = poDstDS->WriteEncodedTileOrStrip(
    7102             :                             iBlock, pBlockBuffer, false);
    7103             :                     }
    7104             : 
    7105         300 :                     iBlock++;
    7106         600 :                     if (pfnProgress &&
    7107         300 :                         !pfnProgress(static_cast<double>(iBlock) / nBlocks,
    7108             :                                      nullptr, pProgressData))
    7109             :                     {
    7110           0 :                         eErr = CE_Failure;
    7111             :                     }
    7112             : 
    7113         300 :                     if (poDstDS->m_bWriteError)
    7114           0 :                         eErr = CE_Failure;
    7115             :                 }
    7116             :             }
    7117             :         }
    7118          45 :         if (poDstDS->m_poMaskDS && eErr == CE_None)
    7119             :         {
    7120           6 :             int iBlockMask = 0;
    7121          17 :             for (int iY = 0, nYBlock = 0; iY < nYSize && eErr == CE_None;
    7122          11 :                  iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
    7123          11 :                            ? nYSize
    7124           5 :                            : iY + poDstDS->m_nBlockYSize),
    7125             :                      nYBlock++)
    7126             :             {
    7127             :                 const int nReqYSize =
    7128          11 :                     std::min(nYSize - iY, poDstDS->m_nBlockYSize);
    7129          49 :                 for (int iX = 0, nXBlock = 0; iX < nXSize && eErr == CE_None;
    7130          38 :                      iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
    7131          38 :                                ? nXSize
    7132          30 :                                : iX + poDstDS->m_nBlockXSize),
    7133             :                          nXBlock++)
    7134             :                 {
    7135             :                     const int nReqXSize =
    7136          38 :                         std::min(nXSize - iX, poDstDS->m_nBlockXSize);
    7137          38 :                     if (nReqXSize < poDstDS->m_nBlockXSize ||
    7138          30 :                         nReqYSize < poDstDS->m_nBlockYSize)
    7139             :                     {
    7140          16 :                         memset(pBlockBuffer, 0,
    7141          16 :                                static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7142          16 :                                    poDstDS->m_nBlockYSize);
    7143             :                     }
    7144          76 :                     eErr = poSrcMaskBand->RasterIO(
    7145             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7146             :                         nReqXSize, nReqYSize, GDT_Byte, 1,
    7147          38 :                         poDstDS->m_nBlockXSize, nullptr);
    7148          38 :                     if (eErr == CE_None)
    7149             :                     {
    7150             :                         // Avoid any attempt to load from disk
    7151          38 :                         poDstDS->m_poMaskDS->m_nLoadedBlock = iBlockMask;
    7152             :                         eErr =
    7153          38 :                             poDstDS->m_poMaskDS->GetRasterBand(1)->WriteBlock(
    7154             :                                 nXBlock, nYBlock, pBlockBuffer);
    7155          38 :                         if (eErr == CE_None)
    7156          38 :                             eErr = poDstDS->m_poMaskDS->FlushBlockBuf();
    7157             :                     }
    7158             : 
    7159          38 :                     iBlockMask++;
    7160          76 :                     if (pfnProgress &&
    7161          38 :                         !pfnProgress(static_cast<double>(iBlock + iBlockMask) /
    7162             :                                          nBlocks,
    7163             :                                      nullptr, pProgressData))
    7164             :                     {
    7165           0 :                         eErr = CE_Failure;
    7166             :                     }
    7167             : 
    7168          38 :                     if (poDstDS->m_poMaskDS->m_bWriteError)
    7169           0 :                         eErr = CE_Failure;
    7170             :                 }
    7171             :             }
    7172          45 :         }
    7173             :     }
    7174             :     else
    7175             :     {
    7176         262 :         int iBlock = 0;
    7177         262 :         const int nBlocks = poDstDS->m_nBlocksPerBand;
    7178        7015 :         for (int iY = 0, nYBlock = 0; iY < nYSize && eErr == CE_None;
    7179        6753 :              iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
    7180        6753 :                        ? nYSize
    7181        6564 :                        : iY + poDstDS->m_nBlockYSize),
    7182             :                  nYBlock++)
    7183             :         {
    7184        6753 :             const int nReqYSize = std::min(nYSize - iY, poDstDS->m_nBlockYSize);
    7185       26421 :             for (int iX = 0, nXBlock = 0; iX < nXSize && eErr == CE_None;
    7186       19668 :                  iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
    7187       19668 :                            ? nXSize
    7188       19410 :                            : iX + poDstDS->m_nBlockXSize),
    7189             :                      nXBlock++)
    7190             :             {
    7191             :                 const int nReqXSize =
    7192       19668 :                     std::min(nXSize - iX, poDstDS->m_nBlockXSize);
    7193       19668 :                 if (nReqXSize < poDstDS->m_nBlockXSize ||
    7194       19410 :                     nReqYSize < poDstDS->m_nBlockYSize)
    7195             :                 {
    7196         424 :                     memset(pBlockBuffer, 0,
    7197         424 :                            static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7198         424 :                                poDstDS->m_nBlockYSize * l_nBands *
    7199         424 :                                nDataTypeSize);
    7200             :                 }
    7201             : 
    7202       19668 :                 if (poDstDS->m_bTileInterleave)
    7203             :                 {
    7204         114 :                     eErr = poSrcDS->RasterIO(
    7205             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7206             :                         nReqXSize, nReqYSize, eType, l_nBands, nullptr,
    7207             :                         nDataTypeSize,
    7208          57 :                         static_cast<GSpacing>(nDataTypeSize) *
    7209          57 :                             poDstDS->m_nBlockXSize,
    7210          57 :                         static_cast<GSpacing>(nDataTypeSize) *
    7211          57 :                             poDstDS->m_nBlockXSize * poDstDS->m_nBlockYSize,
    7212             :                         nullptr);
    7213          57 :                     if (eErr == CE_None)
    7214             :                     {
    7215         228 :                         for (int i = 0; eErr == CE_None && i < l_nBands; i++)
    7216             :                         {
    7217         171 :                             eErr = poDstDS->WriteEncodedTileOrStrip(
    7218         171 :                                 iBlock + i * poDstDS->m_nBlocksPerBand,
    7219         171 :                                 pBlockBuffer + static_cast<size_t>(i) *
    7220         171 :                                                    poDstDS->m_nBlockXSize *
    7221         171 :                                                    poDstDS->m_nBlockYSize *
    7222         171 :                                                    nDataTypeSize,
    7223             :                                 false);
    7224             :                         }
    7225             :                     }
    7226             :                 }
    7227       19611 :                 else if (!bIsOddBand)
    7228             :                 {
    7229       39100 :                     eErr = poSrcDS->RasterIO(
    7230             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7231             :                         nReqXSize, nReqYSize, eType, l_nBands, nullptr,
    7232       19550 :                         static_cast<GSpacing>(nDataTypeSize) * l_nBands,
    7233       19550 :                         static_cast<GSpacing>(nDataTypeSize) * l_nBands *
    7234       19550 :                             poDstDS->m_nBlockXSize,
    7235             :                         nDataTypeSize, nullptr);
    7236       19550 :                     if (eErr == CE_None)
    7237             :                     {
    7238       19549 :                         eErr = poDstDS->WriteEncodedTileOrStrip(
    7239             :                             iBlock, pBlockBuffer, false);
    7240             :                     }
    7241             :                 }
    7242             :                 else
    7243             :                 {
    7244             :                     // In the odd bit case, this is a bit messy to ensure
    7245             :                     // the strile gets written synchronously.
    7246             :                     // We load the content of the n-1 bands in the cache,
    7247             :                     // and for the last band we invoke WriteBlock() directly
    7248             :                     // We also force FlushBlockBuf()
    7249         122 :                     std::vector<GDALRasterBlock *> apoLockedBlocks;
    7250          91 :                     for (int i = 0; eErr == CE_None && i < l_nBands - 1; i++)
    7251             :                     {
    7252             :                         auto poBlock =
    7253          30 :                             poDstDS->GetRasterBand(i + 1)->GetLockedBlockRef(
    7254          30 :                                 nXBlock, nYBlock, TRUE);
    7255          30 :                         if (poBlock)
    7256             :                         {
    7257          60 :                             eErr = poSrcDS->GetRasterBand(i + 1)->RasterIO(
    7258             :                                 GF_Read, iX, iY, nReqXSize, nReqYSize,
    7259             :                                 poBlock->GetDataRef(), nReqXSize, nReqYSize,
    7260             :                                 eType, nDataTypeSize,
    7261          30 :                                 static_cast<GSpacing>(nDataTypeSize) *
    7262          30 :                                     poDstDS->m_nBlockXSize,
    7263             :                                 nullptr);
    7264          30 :                             poBlock->MarkDirty();
    7265          30 :                             apoLockedBlocks.emplace_back(poBlock);
    7266             :                         }
    7267             :                         else
    7268             :                         {
    7269           0 :                             eErr = CE_Failure;
    7270             :                         }
    7271             :                     }
    7272          61 :                     if (eErr == CE_None)
    7273             :                     {
    7274         122 :                         eErr = poSrcDS->GetRasterBand(l_nBands)->RasterIO(
    7275             :                             GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7276             :                             nReqXSize, nReqYSize, eType, nDataTypeSize,
    7277          61 :                             static_cast<GSpacing>(nDataTypeSize) *
    7278          61 :                                 poDstDS->m_nBlockXSize,
    7279             :                             nullptr);
    7280             :                     }
    7281          61 :                     if (eErr == CE_None)
    7282             :                     {
    7283             :                         // Avoid any attempt to load from disk
    7284          61 :                         poDstDS->m_nLoadedBlock = iBlock;
    7285          61 :                         eErr = poDstDS->GetRasterBand(l_nBands)->WriteBlock(
    7286             :                             nXBlock, nYBlock, pBlockBuffer);
    7287          61 :                         if (eErr == CE_None)
    7288          61 :                             eErr = poDstDS->FlushBlockBuf();
    7289             :                     }
    7290          91 :                     for (auto poBlock : apoLockedBlocks)
    7291             :                     {
    7292          30 :                         poBlock->MarkClean();
    7293          30 :                         poBlock->DropLock();
    7294             :                     }
    7295             :                 }
    7296             : 
    7297       19668 :                 if (eErr == CE_None && poDstDS->m_poMaskDS)
    7298             :                 {
    7299        4664 :                     if (nReqXSize < poDstDS->m_nBlockXSize ||
    7300        4621 :                         nReqYSize < poDstDS->m_nBlockYSize)
    7301             :                     {
    7302          81 :                         memset(pBlockBuffer, 0,
    7303          81 :                                static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7304          81 :                                    poDstDS->m_nBlockYSize);
    7305             :                     }
    7306        9328 :                     eErr = poSrcMaskBand->RasterIO(
    7307             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7308             :                         nReqXSize, nReqYSize, GDT_Byte, 1,
    7309        4664 :                         poDstDS->m_nBlockXSize, nullptr);
    7310        4664 :                     if (eErr == CE_None)
    7311             :                     {
    7312             :                         // Avoid any attempt to load from disk
    7313        4664 :                         poDstDS->m_poMaskDS->m_nLoadedBlock = iBlock;
    7314             :                         eErr =
    7315        4664 :                             poDstDS->m_poMaskDS->GetRasterBand(1)->WriteBlock(
    7316             :                                 nXBlock, nYBlock, pBlockBuffer);
    7317        4664 :                         if (eErr == CE_None)
    7318        4664 :                             eErr = poDstDS->m_poMaskDS->FlushBlockBuf();
    7319             :                     }
    7320             :                 }
    7321       19668 :                 if (poDstDS->m_bWriteError)
    7322           6 :                     eErr = CE_Failure;
    7323             : 
    7324       19668 :                 iBlock++;
    7325       39336 :                 if (pfnProgress &&
    7326       19668 :                     !pfnProgress(static_cast<double>(iBlock) / nBlocks, nullptr,
    7327             :                                  pProgressData))
    7328             :                 {
    7329           0 :                     eErr = CE_Failure;
    7330             :                 }
    7331             :             }
    7332             :         }
    7333             :     }
    7334             : 
    7335         307 :     poDstDS->FlushCache(false);  // mostly to wait for thread completion
    7336         307 :     VSIFree(pBlockBuffer);
    7337             : 
    7338         307 :     return eErr;
    7339             : }
    7340             : 
    7341             : /************************************************************************/
    7342             : /*                             CreateCopy()                             */
    7343             : /************************************************************************/
    7344             : 
    7345        2080 : GDALDataset *GTiffDataset::CreateCopy(const char *pszFilename,
    7346             :                                       GDALDataset *poSrcDS, int bStrict,
    7347             :                                       char **papszOptions,
    7348             :                                       GDALProgressFunc pfnProgress,
    7349             :                                       void *pProgressData)
    7350             : 
    7351             : {
    7352        2080 :     if (poSrcDS->GetRasterCount() == 0)
    7353             :     {
    7354           2 :         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    7355             :                     "Unable to export GeoTIFF files with zero bands.");
    7356           2 :         return nullptr;
    7357             :     }
    7358             : 
    7359        2078 :     GDALRasterBand *const poPBand = poSrcDS->GetRasterBand(1);
    7360        2078 :     GDALDataType eType = poPBand->GetRasterDataType();
    7361             : 
    7362             :     /* -------------------------------------------------------------------- */
    7363             :     /*      Check, whether all bands in input dataset has the same type.    */
    7364             :     /* -------------------------------------------------------------------- */
    7365        2078 :     const int l_nBands = poSrcDS->GetRasterCount();
    7366        4954 :     for (int iBand = 2; iBand <= l_nBands; ++iBand)
    7367             :     {
    7368        2876 :         if (eType != poSrcDS->GetRasterBand(iBand)->GetRasterDataType())
    7369             :         {
    7370           0 :             if (bStrict)
    7371             :             {
    7372           0 :                 ReportError(
    7373             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    7374             :                     "Unable to export GeoTIFF file with different datatypes "
    7375             :                     "per different bands. All bands should have the same "
    7376             :                     "types in TIFF.");
    7377           0 :                 return nullptr;
    7378             :             }
    7379             :             else
    7380             :             {
    7381           0 :                 ReportError(
    7382             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    7383             :                     "Unable to export GeoTIFF file with different datatypes "
    7384             :                     "per different bands. All bands should have the same "
    7385             :                     "types in TIFF.");
    7386             :             }
    7387             :         }
    7388             :     }
    7389             : 
    7390             :     /* -------------------------------------------------------------------- */
    7391             :     /*      Capture the profile.                                            */
    7392             :     /* -------------------------------------------------------------------- */
    7393             :     const GTiffProfile eProfile =
    7394        2078 :         GetProfile(CSLFetchNameValue(papszOptions, "PROFILE"));
    7395             : 
    7396        2078 :     const bool bGeoTIFF = eProfile != GTiffProfile::BASELINE;
    7397             : 
    7398             :     /* -------------------------------------------------------------------- */
    7399             :     /*      Special handling for NBITS.  Copy from band metadata if found.  */
    7400             :     /* -------------------------------------------------------------------- */
    7401        2078 :     char **papszCreateOptions = CSLDuplicate(papszOptions);
    7402             : 
    7403        2078 :     if (poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE") != nullptr &&
    7404        2095 :         atoi(poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE")) > 0 &&
    7405          17 :         CSLFetchNameValue(papszCreateOptions, "NBITS") == nullptr)
    7406             :     {
    7407           3 :         papszCreateOptions = CSLSetNameValue(
    7408             :             papszCreateOptions, "NBITS",
    7409           3 :             poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE"));
    7410             :     }
    7411             : 
    7412        2078 :     if (CSLFetchNameValue(papszOptions, "PIXELTYPE") == nullptr &&
    7413             :         eType == GDT_Byte)
    7414             :     {
    7415        1745 :         poPBand->EnablePixelTypeSignedByteWarning(false);
    7416             :         const char *pszPixelType =
    7417        1745 :             poPBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
    7418        1745 :         poPBand->EnablePixelTypeSignedByteWarning(true);
    7419        1745 :         if (pszPixelType)
    7420             :         {
    7421           1 :             papszCreateOptions =
    7422           1 :                 CSLSetNameValue(papszCreateOptions, "PIXELTYPE", pszPixelType);
    7423             :         }
    7424             :     }
    7425             : 
    7426             :     /* -------------------------------------------------------------------- */
    7427             :     /*      Color profile.  Copy from band metadata if found.              */
    7428             :     /* -------------------------------------------------------------------- */
    7429        2078 :     if (bGeoTIFF)
    7430             :     {
    7431        2061 :         const char *pszOptionsMD[] = {"SOURCE_ICC_PROFILE",
    7432             :                                       "SOURCE_PRIMARIES_RED",
    7433             :                                       "SOURCE_PRIMARIES_GREEN",
    7434             :                                       "SOURCE_PRIMARIES_BLUE",
    7435             :                                       "SOURCE_WHITEPOINT",
    7436             :                                       "TIFFTAG_TRANSFERFUNCTION_RED",
    7437             :                                       "TIFFTAG_TRANSFERFUNCTION_GREEN",
    7438             :                                       "TIFFTAG_TRANSFERFUNCTION_BLUE",
    7439             :                                       "TIFFTAG_TRANSFERRANGE_BLACK",
    7440             :                                       "TIFFTAG_TRANSFERRANGE_WHITE",
    7441             :                                       nullptr};
    7442             : 
    7443             :         // Copy all the tags.  Options will override tags in the source.
    7444        2061 :         int i = 0;
    7445       22651 :         while (pszOptionsMD[i] != nullptr)
    7446             :         {
    7447             :             char const *pszMD =
    7448       20592 :                 CSLFetchNameValue(papszOptions, pszOptionsMD[i]);
    7449       20592 :             if (pszMD == nullptr)
    7450             :                 pszMD =
    7451       20584 :                     poSrcDS->GetMetadataItem(pszOptionsMD[i], "COLOR_PROFILE");
    7452             : 
    7453       20592 :             if ((pszMD != nullptr) && !EQUAL(pszMD, ""))
    7454             :             {
    7455          16 :                 papszCreateOptions =
    7456          16 :                     CSLSetNameValue(papszCreateOptions, pszOptionsMD[i], pszMD);
    7457             : 
    7458             :                 // If an ICC profile exists, other tags are not needed.
    7459          16 :                 if (EQUAL(pszOptionsMD[i], "SOURCE_ICC_PROFILE"))
    7460           2 :                     break;
    7461             :             }
    7462             : 
    7463       20590 :             ++i;
    7464             :         }
    7465             :     }
    7466             : 
    7467        2078 :     double dfExtraSpaceForOverviews = 0;
    7468             :     const bool bCopySrcOverviews =
    7469        2078 :         CPLFetchBool(papszCreateOptions, "COPY_SRC_OVERVIEWS", false);
    7470        2078 :     std::unique_ptr<GDALDataset> poOvrDS;
    7471        2078 :     int nSrcOverviews = 0;
    7472        2078 :     if (bCopySrcOverviews)
    7473             :     {
    7474             :         const char *pszOvrDS =
    7475         193 :             CSLFetchNameValue(papszCreateOptions, "@OVERVIEW_DATASET");
    7476         193 :         if (pszOvrDS)
    7477             :         {
    7478             :             // Empty string is used by COG driver to indicate that we want
    7479             :             // to ignore source overviews.
    7480          37 :             if (!EQUAL(pszOvrDS, ""))
    7481             :             {
    7482          35 :                 poOvrDS.reset(GDALDataset::Open(pszOvrDS));
    7483          35 :                 if (!poOvrDS)
    7484             :                 {
    7485           0 :                     CSLDestroy(papszCreateOptions);
    7486           0 :                     return nullptr;
    7487             :                 }
    7488          35 :                 if (poOvrDS->GetRasterCount() != l_nBands)
    7489             :                 {
    7490           0 :                     CSLDestroy(papszCreateOptions);
    7491           0 :                     return nullptr;
    7492             :                 }
    7493          35 :                 nSrcOverviews =
    7494          35 :                     poOvrDS->GetRasterBand(1)->GetOverviewCount() + 1;
    7495             :             }
    7496             :         }
    7497             :         else
    7498             :         {
    7499         156 :             nSrcOverviews = poSrcDS->GetRasterBand(1)->GetOverviewCount();
    7500             :         }
    7501             : 
    7502             :         // Limit number of overviews if specified
    7503             :         const char *pszOverviewCount =
    7504         193 :             CSLFetchNameValue(papszCreateOptions, "@OVERVIEW_COUNT");
    7505         193 :         if (pszOverviewCount)
    7506           8 :             nSrcOverviews =
    7507           8 :                 std::max(0, std::min(nSrcOverviews, atoi(pszOverviewCount)));
    7508             : 
    7509         193 :         if (nSrcOverviews)
    7510             :         {
    7511         202 :             for (int j = 1; j <= l_nBands; ++j)
    7512             :             {
    7513             :                 const int nOtherBandOverviewCount =
    7514         133 :                     poOvrDS ? poOvrDS->GetRasterBand(j)->GetOverviewCount() + 1
    7515         196 :                             : poSrcDS->GetRasterBand(j)->GetOverviewCount();
    7516         133 :                 if (nOtherBandOverviewCount < nSrcOverviews)
    7517             :                 {
    7518           1 :                     ReportError(
    7519             :                         pszFilename, CE_Failure, CPLE_NotSupported,
    7520             :                         "COPY_SRC_OVERVIEWS cannot be used when the bands have "
    7521             :                         "not the same number of overview levels.");
    7522           1 :                     CSLDestroy(papszCreateOptions);
    7523           1 :                     return nullptr;
    7524             :                 }
    7525         385 :                 for (int i = 0; i < nSrcOverviews; ++i)
    7526             :                 {
    7527             :                     GDALRasterBand *poOvrBand =
    7528             :                         poOvrDS
    7529         351 :                             ? (i == 0 ? poOvrDS->GetRasterBand(j)
    7530         192 :                                       : poOvrDS->GetRasterBand(j)->GetOverview(
    7531          96 :                                             i - 1))
    7532         344 :                             : poSrcDS->GetRasterBand(j)->GetOverview(i);
    7533         255 :                     if (poOvrBand == nullptr)
    7534             :                     {
    7535           1 :                         ReportError(
    7536             :                             pszFilename, CE_Failure, CPLE_NotSupported,
    7537             :                             "COPY_SRC_OVERVIEWS cannot be used when one "
    7538             :                             "overview band is NULL.");
    7539           1 :                         CSLDestroy(papszCreateOptions);
    7540           1 :                         return nullptr;
    7541             :                     }
    7542             :                     GDALRasterBand *poOvrFirstBand =
    7543             :                         poOvrDS
    7544         350 :                             ? (i == 0 ? poOvrDS->GetRasterBand(1)
    7545         192 :                                       : poOvrDS->GetRasterBand(1)->GetOverview(
    7546          96 :                                             i - 1))
    7547         342 :                             : poSrcDS->GetRasterBand(1)->GetOverview(i);
    7548         507 :                     if (poOvrBand->GetXSize() != poOvrFirstBand->GetXSize() ||
    7549         253 :                         poOvrBand->GetYSize() != poOvrFirstBand->GetYSize())
    7550             :                     {
    7551           1 :                         ReportError(
    7552             :                             pszFilename, CE_Failure, CPLE_NotSupported,
    7553             :                             "COPY_SRC_OVERVIEWS cannot be used when the "
    7554             :                             "overview bands have not the same dimensions "
    7555             :                             "among bands.");
    7556           1 :                         CSLDestroy(papszCreateOptions);
    7557           1 :                         return nullptr;
    7558             :                     }
    7559             :                 }
    7560             :             }
    7561             : 
    7562         195 :             for (int i = 0; i < nSrcOverviews; ++i)
    7563             :             {
    7564             :                 GDALRasterBand *poOvrFirstBand =
    7565             :                     poOvrDS
    7566         199 :                         ? (i == 0
    7567          73 :                                ? poOvrDS->GetRasterBand(1)
    7568          38 :                                : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    7569         179 :                         : poSrcDS->GetRasterBand(1)->GetOverview(i);
    7570         126 :                 dfExtraSpaceForOverviews +=
    7571         126 :                     static_cast<double>(poOvrFirstBand->GetXSize()) *
    7572         126 :                     poOvrFirstBand->GetYSize();
    7573             :             }
    7574          69 :             dfExtraSpaceForOverviews *=
    7575          69 :                 l_nBands * GDALGetDataTypeSizeBytes(eType);
    7576             :         }
    7577             :         else
    7578             :         {
    7579         121 :             CPLDebug("GTiff", "No source overviews to copy");
    7580             :         }
    7581             :     }
    7582             : 
    7583             : /* -------------------------------------------------------------------- */
    7584             : /*      Should we use optimized way of copying from an input JPEG       */
    7585             : /*      dataset?                                                        */
    7586             : /* -------------------------------------------------------------------- */
    7587             : 
    7588             : // TODO(schwehr): Refactor bDirectCopyFromJPEG to be a const.
    7589             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    7590        2075 :     bool bDirectCopyFromJPEG = false;
    7591             : #endif
    7592             : 
    7593             :     // Note: JPEG_DIRECT_COPY is not defined by default, because it is mainly
    7594             :     // useful for debugging purposes.
    7595             : #ifdef JPEG_DIRECT_COPY
    7596             :     if (CPLFetchBool(papszCreateOptions, "JPEG_DIRECT_COPY", false) &&
    7597             :         GTIFF_CanDirectCopyFromJPEG(poSrcDS, papszCreateOptions))
    7598             :     {
    7599             :         CPLDebug("GTiff", "Using special direct copy mode from a JPEG dataset");
    7600             : 
    7601             :         bDirectCopyFromJPEG = true;
    7602             :     }
    7603             : #endif
    7604             : 
    7605             : #ifdef HAVE_LIBJPEG
    7606        2075 :     bool bCopyFromJPEG = false;
    7607             : 
    7608             :     // When CreateCopy'ing() from a JPEG dataset, and asking for COMPRESS=JPEG,
    7609             :     // use DCT coefficients (unless other options are incompatible, like
    7610             :     // strip/tile dimensions, specifying JPEG_QUALITY option, incompatible
    7611             :     // PHOTOMETRIC with the source colorspace, etc.) to avoid the lossy steps
    7612             :     // involved by decompression/recompression.
    7613        4150 :     if (!bDirectCopyFromJPEG &&
    7614        2075 :         GTIFF_CanCopyFromJPEG(poSrcDS, papszCreateOptions))
    7615             :     {
    7616          12 :         CPLDebug("GTiff", "Using special copy mode from a JPEG dataset");
    7617             : 
    7618          12 :         bCopyFromJPEG = true;
    7619             :     }
    7620             : #endif
    7621             : 
    7622             :     /* -------------------------------------------------------------------- */
    7623             :     /*      If the source is RGB, then set the PHOTOMETRIC=RGB value        */
    7624             :     /* -------------------------------------------------------------------- */
    7625             : 
    7626             :     const bool bForcePhotometric =
    7627        2075 :         CSLFetchNameValue(papszOptions, "PHOTOMETRIC") != nullptr;
    7628             : 
    7629        1205 :     if (l_nBands >= 3 && !bForcePhotometric &&
    7630             : #ifdef HAVE_LIBJPEG
    7631        1167 :         !bCopyFromJPEG &&
    7632             : #endif
    7633        1161 :         poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_RedBand &&
    7634        4343 :         poSrcDS->GetRasterBand(2)->GetColorInterpretation() == GCI_GreenBand &&
    7635        1063 :         poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_BlueBand)
    7636             :     {
    7637        1057 :         papszCreateOptions =
    7638        1057 :             CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "RGB");
    7639             :     }
    7640             : 
    7641             :     /* -------------------------------------------------------------------- */
    7642             :     /*      Create the file.                                                */
    7643             :     /* -------------------------------------------------------------------- */
    7644        2075 :     VSILFILE *l_fpL = nullptr;
    7645        4150 :     CPLString l_osTmpFilename;
    7646             : 
    7647        2075 :     const int nXSize = poSrcDS->GetRasterXSize();
    7648        2075 :     const int nYSize = poSrcDS->GetRasterYSize();
    7649             : 
    7650             :     const int nColorTableMultiplier = std::max(
    7651        4150 :         1,
    7652        4150 :         std::min(257,
    7653        2075 :                  atoi(CSLFetchNameValueDef(
    7654             :                      papszOptions, "COLOR_TABLE_MULTIPLIER",
    7655        2075 :                      CPLSPrintf("%d", DEFAULT_COLOR_TABLE_MULTIPLIER_257)))));
    7656             : 
    7657        2075 :     bool bTileInterleaving = false;
    7658        2075 :     TIFF *l_hTIFF = CreateLL(pszFilename, nXSize, nYSize, l_nBands, eType,
    7659             :                              dfExtraSpaceForOverviews, nColorTableMultiplier,
    7660             :                              papszCreateOptions, &l_fpL, l_osTmpFilename,
    7661             :                              /* bCreateCopy = */ true, bTileInterleaving);
    7662        2075 :     const bool bStreaming = !l_osTmpFilename.empty();
    7663             : 
    7664        2075 :     CSLDestroy(papszCreateOptions);
    7665        2075 :     papszCreateOptions = nullptr;
    7666             : 
    7667        2075 :     if (l_hTIFF == nullptr)
    7668             :     {
    7669          18 :         if (bStreaming)
    7670           0 :             VSIUnlink(l_osTmpFilename);
    7671          18 :         return nullptr;
    7672             :     }
    7673             : 
    7674        2057 :     uint16_t l_nPlanarConfig = 0;
    7675        2057 :     TIFFGetField(l_hTIFF, TIFFTAG_PLANARCONFIG, &l_nPlanarConfig);
    7676             : 
    7677        2057 :     uint16_t l_nCompression = 0;
    7678             : 
    7679        2057 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_COMPRESSION, &(l_nCompression)))
    7680           0 :         l_nCompression = COMPRESSION_NONE;
    7681             : 
    7682             :     /* -------------------------------------------------------------------- */
    7683             :     /*      Set the alpha channel if we find one.                           */
    7684             :     /* -------------------------------------------------------------------- */
    7685        2057 :     uint16_t *extraSamples = nullptr;
    7686        2057 :     uint16_t nExtraSamples = 0;
    7687        2057 :     if (TIFFGetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples,
    7688        2305 :                      &extraSamples) &&
    7689         248 :         nExtraSamples > 0)
    7690             :     {
    7691             :         // We need to allocate a new array as (current) libtiff
    7692             :         // versions will not like that we reuse the array we got from
    7693             :         // TIFFGetField().
    7694             :         uint16_t *pasNewExtraSamples = static_cast<uint16_t *>(
    7695         248 :             CPLMalloc(nExtraSamples * sizeof(uint16_t)));
    7696         248 :         memcpy(pasNewExtraSamples, extraSamples,
    7697         248 :                nExtraSamples * sizeof(uint16_t));
    7698         248 :         const char *pszAlpha = CPLGetConfigOption(
    7699             :             "GTIFF_ALPHA", CSLFetchNameValue(papszOptions, "ALPHA"));
    7700             :         const uint16_t nAlpha =
    7701         248 :             GTiffGetAlphaValue(pszAlpha, DEFAULT_ALPHA_TYPE);
    7702         248 :         const int nBaseSamples = l_nBands - nExtraSamples;
    7703         845 :         for (int iExtraBand = nBaseSamples + 1; iExtraBand <= l_nBands;
    7704             :              iExtraBand++)
    7705             :         {
    7706         597 :             if (poSrcDS->GetRasterBand(iExtraBand)->GetColorInterpretation() ==
    7707             :                 GCI_AlphaBand)
    7708             :             {
    7709         144 :                 pasNewExtraSamples[iExtraBand - nBaseSamples - 1] = nAlpha;
    7710         144 :                 if (!pszAlpha)
    7711             :                 {
    7712             :                     // Use the ALPHA metadata item from the source band, when
    7713             :                     // present, if no explicit ALPHA creation option
    7714         284 :                     pasNewExtraSamples[iExtraBand - nBaseSamples - 1] =
    7715         142 :                         GTiffGetAlphaValue(
    7716         142 :                             poSrcDS->GetRasterBand(iExtraBand)
    7717         142 :                                 ->GetMetadataItem("ALPHA", "IMAGE_STRUCTURE"),
    7718             :                             nAlpha);
    7719             :                 }
    7720             :             }
    7721             :         }
    7722         248 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples,
    7723             :                      pasNewExtraSamples);
    7724             : 
    7725         248 :         CPLFree(pasNewExtraSamples);
    7726             :     }
    7727             : 
    7728             :     /* -------------------------------------------------------------------- */
    7729             :     /*      If the output is jpeg compressed, and the input is RGB make     */
    7730             :     /*      sure we note that.                                              */
    7731             :     /* -------------------------------------------------------------------- */
    7732             : 
    7733        2057 :     if (l_nCompression == COMPRESSION_JPEG)
    7734             :     {
    7735         134 :         if (l_nBands >= 3 &&
    7736          58 :             (poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
    7737           0 :              GCI_YCbCr_YBand) &&
    7738           0 :             (poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
    7739         134 :              GCI_YCbCr_CbBand) &&
    7740           0 :             (poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
    7741             :              GCI_YCbCr_CrBand))
    7742             :         {
    7743             :             // Do nothing.
    7744             :         }
    7745             :         else
    7746             :         {
    7747             :             // Assume RGB if it is not explicitly YCbCr.
    7748          76 :             CPLDebug("GTiff", "Setting JPEGCOLORMODE_RGB");
    7749          76 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    7750             :         }
    7751             :     }
    7752             : 
    7753             :     /* -------------------------------------------------------------------- */
    7754             :     /*      Does the source image consist of one band, with a palette?      */
    7755             :     /*      If so, copy over.                                               */
    7756             :     /* -------------------------------------------------------------------- */
    7757        1289 :     if ((l_nBands == 1 || l_nBands == 2) &&
    7758        3346 :         poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7759             :         eType == GDT_Byte)
    7760             :     {
    7761          16 :         unsigned short anTRed[256] = {0};
    7762          16 :         unsigned short anTGreen[256] = {0};
    7763          16 :         unsigned short anTBlue[256] = {0};
    7764          16 :         GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    7765             : 
    7766        4112 :         for (int iColor = 0; iColor < 256; ++iColor)
    7767             :         {
    7768        4096 :             if (iColor < poCT->GetColorEntryCount())
    7769             :             {
    7770        3041 :                 GDALColorEntry sRGB = {0, 0, 0, 0};
    7771             : 
    7772        3041 :                 poCT->GetColorEntryAsRGB(iColor, &sRGB);
    7773             : 
    7774        6082 :                 anTRed[iColor] = GTiffDataset::ClampCTEntry(
    7775        3041 :                     iColor, 1, sRGB.c1, nColorTableMultiplier);
    7776        6082 :                 anTGreen[iColor] = GTiffDataset::ClampCTEntry(
    7777        3041 :                     iColor, 2, sRGB.c2, nColorTableMultiplier);
    7778        3041 :                 anTBlue[iColor] = GTiffDataset::ClampCTEntry(
    7779        3041 :                     iColor, 3, sRGB.c3, nColorTableMultiplier);
    7780             :             }
    7781             :             else
    7782             :             {
    7783        1055 :                 anTRed[iColor] = 0;
    7784        1055 :                 anTGreen[iColor] = 0;
    7785        1055 :                 anTBlue[iColor] = 0;
    7786             :             }
    7787             :         }
    7788             : 
    7789          16 :         if (!bForcePhotometric)
    7790          16 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    7791          16 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, anTRed, anTGreen, anTBlue);
    7792             :     }
    7793        1288 :     else if ((l_nBands == 1 || l_nBands == 2) &&
    7794        3329 :              poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7795             :              eType == GDT_UInt16)
    7796             :     {
    7797             :         unsigned short *panTRed = static_cast<unsigned short *>(
    7798           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7799             :         unsigned short *panTGreen = static_cast<unsigned short *>(
    7800           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7801             :         unsigned short *panTBlue = static_cast<unsigned short *>(
    7802           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7803             : 
    7804           1 :         GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    7805             : 
    7806       65537 :         for (int iColor = 0; iColor < 65536; ++iColor)
    7807             :         {
    7808       65536 :             if (iColor < poCT->GetColorEntryCount())
    7809             :             {
    7810       65536 :                 GDALColorEntry sRGB = {0, 0, 0, 0};
    7811             : 
    7812       65536 :                 poCT->GetColorEntryAsRGB(iColor, &sRGB);
    7813             : 
    7814      131072 :                 panTRed[iColor] = GTiffDataset::ClampCTEntry(
    7815       65536 :                     iColor, 1, sRGB.c1, nColorTableMultiplier);
    7816      131072 :                 panTGreen[iColor] = GTiffDataset::ClampCTEntry(
    7817       65536 :                     iColor, 2, sRGB.c2, nColorTableMultiplier);
    7818       65536 :                 panTBlue[iColor] = GTiffDataset::ClampCTEntry(
    7819       65536 :                     iColor, 3, sRGB.c3, nColorTableMultiplier);
    7820             :             }
    7821             :             else
    7822             :             {
    7823           0 :                 panTRed[iColor] = 0;
    7824           0 :                 panTGreen[iColor] = 0;
    7825           0 :                 panTBlue[iColor] = 0;
    7826             :             }
    7827             :         }
    7828             : 
    7829           1 :         if (!bForcePhotometric)
    7830           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    7831           1 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue);
    7832             : 
    7833           1 :         CPLFree(panTRed);
    7834           1 :         CPLFree(panTGreen);
    7835           1 :         CPLFree(panTBlue);
    7836             :     }
    7837        2040 :     else if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
    7838           1 :         ReportError(
    7839             :             pszFilename, CE_Failure, CPLE_AppDefined,
    7840             :             "Unable to export color table to GeoTIFF file.  Color tables "
    7841             :             "can only be written to 1 band or 2 bands Byte or "
    7842             :             "UInt16 GeoTIFF files.");
    7843             : 
    7844        2057 :     if (l_nCompression == COMPRESSION_JPEG)
    7845             :     {
    7846          76 :         uint16_t l_nPhotometric = 0;
    7847          76 :         TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &l_nPhotometric);
    7848             :         // Check done in tif_jpeg.c later, but not with a very clear error
    7849             :         // message
    7850          76 :         if (l_nPhotometric == PHOTOMETRIC_PALETTE)
    7851             :         {
    7852           1 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    7853             :                         "JPEG compression not supported with paletted image");
    7854           1 :             XTIFFClose(l_hTIFF);
    7855           1 :             VSIUnlink(l_osTmpFilename);
    7856           1 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    7857           1 :             return nullptr;
    7858             :         }
    7859             :     }
    7860             : 
    7861        2141 :     if (l_nBands == 2 &&
    7862        2056 :         poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7863           0 :         (eType == GDT_Byte || eType == GDT_UInt16))
    7864             :     {
    7865           1 :         uint16_t v[1] = {EXTRASAMPLE_UNASSALPHA};
    7866             : 
    7867           1 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
    7868             :     }
    7869             : 
    7870        2056 :     const int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags();
    7871        2056 :     bool bCreateMask = false;
    7872        4112 :     CPLString osHiddenStructuralMD;
    7873             :     const char *pszInterleave =
    7874        2056 :         CSLFetchNameValueDef(papszOptions, "INTERLEAVE", "PIXEL");
    7875        2243 :     if (bCopySrcOverviews &&
    7876         187 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "TILED", "NO")))
    7877             :     {
    7878         175 :         osHiddenStructuralMD += "LAYOUT=IFDS_BEFORE_DATA\n";
    7879         175 :         osHiddenStructuralMD += "BLOCK_ORDER=ROW_MAJOR\n";
    7880         175 :         osHiddenStructuralMD += "BLOCK_LEADER=SIZE_AS_UINT4\n";
    7881         175 :         osHiddenStructuralMD += "BLOCK_TRAILER=LAST_4_BYTES_REPEATED\n";
    7882         175 :         if (l_nBands > 1 && !EQUAL(pszInterleave, "PIXEL"))
    7883             :         {
    7884          21 :             osHiddenStructuralMD += "INTERLEAVE=";
    7885          21 :             osHiddenStructuralMD += CPLString(pszInterleave).toupper();
    7886          21 :             osHiddenStructuralMD += "\n";
    7887             :         }
    7888             :         osHiddenStructuralMD +=
    7889         175 :             "KNOWN_INCOMPATIBLE_EDITION=NO\n ";  // Final space intended, so
    7890             :                                                  // this can be replaced by YES
    7891             :     }
    7892        2056 :     if (!(nMaskFlags & (GMF_ALL_VALID | GMF_ALPHA | GMF_NODATA)) &&
    7893          42 :         (nMaskFlags & GMF_PER_DATASET) && !bStreaming)
    7894             :     {
    7895          38 :         bCreateMask = true;
    7896          38 :         if (GTiffDataset::MustCreateInternalMask() &&
    7897          38 :             !osHiddenStructuralMD.empty() && EQUAL(pszInterleave, "PIXEL"))
    7898             :         {
    7899          21 :             osHiddenStructuralMD += "MASK_INTERLEAVED_WITH_IMAGERY=YES\n";
    7900             :         }
    7901             :     }
    7902        2056 :     if (!osHiddenStructuralMD.empty())
    7903             :     {
    7904         175 :         const int nHiddenMDSize = static_cast<int>(osHiddenStructuralMD.size());
    7905             :         osHiddenStructuralMD =
    7906         175 :             CPLOPrintf("GDAL_STRUCTURAL_METADATA_SIZE=%06d bytes\n",
    7907         350 :                        nHiddenMDSize) +
    7908         175 :             osHiddenStructuralMD;
    7909         175 :         VSI_TIFFWrite(l_hTIFF, osHiddenStructuralMD.c_str(),
    7910             :                       osHiddenStructuralMD.size());
    7911             :     }
    7912             : 
    7913             :     // FIXME? libtiff writes extended tags in the order they are specified
    7914             :     // and not in increasing order.
    7915             : 
    7916             :     /* -------------------------------------------------------------------- */
    7917             :     /*      Transfer some TIFF specific metadata, if available.             */
    7918             :     /*      The return value will tell us if we need to try again later with*/
    7919             :     /*      PAM because the profile doesn't allow to write some metadata    */
    7920             :     /*      as TIFF tag                                                     */
    7921             :     /* -------------------------------------------------------------------- */
    7922        2056 :     const bool bHasWrittenMDInGeotiffTAG = GTiffDataset::WriteMetadata(
    7923             :         poSrcDS, l_hTIFF, false, eProfile, pszFilename, papszOptions);
    7924             : 
    7925             :     /* -------------------------------------------------------------------- */
    7926             :     /*      Write NoData value, if exist.                                   */
    7927             :     /* -------------------------------------------------------------------- */
    7928        2056 :     if (eProfile == GTiffProfile::GDALGEOTIFF)
    7929             :     {
    7930        2035 :         int bSuccess = FALSE;
    7931        2035 :         GDALRasterBand *poFirstBand = poSrcDS->GetRasterBand(1);
    7932        2035 :         if (poFirstBand->GetRasterDataType() == GDT_Int64)
    7933             :         {
    7934           2 :             const auto nNoData = poFirstBand->GetNoDataValueAsInt64(&bSuccess);
    7935           2 :             if (bSuccess)
    7936           1 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, nNoData);
    7937             :         }
    7938        2033 :         else if (poFirstBand->GetRasterDataType() == GDT_UInt64)
    7939             :         {
    7940           2 :             const auto nNoData = poFirstBand->GetNoDataValueAsUInt64(&bSuccess);
    7941           2 :             if (bSuccess)
    7942           1 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, nNoData);
    7943             :         }
    7944             :         else
    7945             :         {
    7946        2031 :             const auto dfNoData = poFirstBand->GetNoDataValue(&bSuccess);
    7947        2031 :             if (bSuccess)
    7948         139 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, dfNoData);
    7949             :         }
    7950             :     }
    7951             : 
    7952             :     /* -------------------------------------------------------------------- */
    7953             :     /*      Are we addressing PixelIsPoint mode?                            */
    7954             :     /* -------------------------------------------------------------------- */
    7955        2056 :     bool bPixelIsPoint = false;
    7956        2056 :     bool bPointGeoIgnore = false;
    7957             : 
    7958        3466 :     if (poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT) &&
    7959        1410 :         EQUAL(poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT), GDALMD_AOP_POINT))
    7960             :     {
    7961          10 :         bPixelIsPoint = true;
    7962             :         bPointGeoIgnore =
    7963          10 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    7964             :     }
    7965             : 
    7966             :     /* -------------------------------------------------------------------- */
    7967             :     /*      Write affine transform if it is meaningful.                     */
    7968             :     /* -------------------------------------------------------------------- */
    7969        2056 :     const OGRSpatialReference *l_poSRS = nullptr;
    7970        2056 :     GDALGeoTransform l_gt;
    7971        2056 :     if (poSrcDS->GetGeoTransform(l_gt) == CE_None)
    7972             :     {
    7973        1630 :         if (bGeoTIFF)
    7974             :         {
    7975        1625 :             l_poSRS = poSrcDS->GetSpatialRef();
    7976             : 
    7977        1625 :             if (l_gt[2] == 0.0 && l_gt[4] == 0.0 && l_gt[5] < 0.0)
    7978             :             {
    7979        1610 :                 double dfOffset = 0.0;
    7980             :                 {
    7981             :                     // In the case the SRS has a vertical component and we have
    7982             :                     // a single band, encode its scale/offset in the GeoTIFF
    7983             :                     // tags
    7984        1610 :                     int bHasScale = FALSE;
    7985             :                     double dfScale =
    7986        1610 :                         poSrcDS->GetRasterBand(1)->GetScale(&bHasScale);
    7987        1610 :                     int bHasOffset = FALSE;
    7988             :                     dfOffset =
    7989        1610 :                         poSrcDS->GetRasterBand(1)->GetOffset(&bHasOffset);
    7990             :                     const bool bApplyScaleOffset =
    7991        1614 :                         l_poSRS && l_poSRS->IsVertical() &&
    7992           4 :                         poSrcDS->GetRasterCount() == 1;
    7993        1610 :                     if (bApplyScaleOffset && !bHasScale)
    7994           0 :                         dfScale = 1.0;
    7995        1610 :                     if (!bApplyScaleOffset || !bHasOffset)
    7996        1606 :                         dfOffset = 0.0;
    7997        1610 :                     const double adfPixelScale[3] = {l_gt[1], fabs(l_gt[5]),
    7998        1610 :                                                      bApplyScaleOffset ? dfScale
    7999        1610 :                                                                        : 0.0};
    8000             : 
    8001        1610 :                     TIFFSetField(l_hTIFF, TIFFTAG_GEOPIXELSCALE, 3,
    8002             :                                  adfPixelScale);
    8003             :                 }
    8004             : 
    8005        1610 :                 double adfTiePoints[6] = {0.0,     0.0,     0.0,
    8006        1610 :                                           l_gt[0], l_gt[3], dfOffset};
    8007             : 
    8008        1610 :                 if (bPixelIsPoint && !bPointGeoIgnore)
    8009             :                 {
    8010           6 :                     adfTiePoints[3] += l_gt[1] * 0.5 + l_gt[2] * 0.5;
    8011           6 :                     adfTiePoints[4] += l_gt[4] * 0.5 + l_gt[5] * 0.5;
    8012             :                 }
    8013             : 
    8014        1610 :                 TIFFSetField(l_hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
    8015             :             }
    8016             :             else
    8017             :             {
    8018          15 :                 double adfMatrix[16] = {0.0};
    8019             : 
    8020          15 :                 adfMatrix[0] = l_gt[1];
    8021          15 :                 adfMatrix[1] = l_gt[2];
    8022          15 :                 adfMatrix[3] = l_gt[0];
    8023          15 :                 adfMatrix[4] = l_gt[4];
    8024          15 :                 adfMatrix[5] = l_gt[5];
    8025          15 :                 adfMatrix[7] = l_gt[3];
    8026          15 :                 adfMatrix[15] = 1.0;
    8027             : 
    8028          15 :                 if (bPixelIsPoint && !bPointGeoIgnore)
    8029             :                 {
    8030           0 :                     adfMatrix[3] += l_gt[1] * 0.5 + l_gt[2] * 0.5;
    8031           0 :                     adfMatrix[7] += l_gt[4] * 0.5 + l_gt[5] * 0.5;
    8032             :                 }
    8033             : 
    8034          15 :                 TIFFSetField(l_hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
    8035             :             }
    8036             :         }
    8037             : 
    8038             :         /* --------------------------------------------------------------------
    8039             :          */
    8040             :         /*      Do we need a TFW file? */
    8041             :         /* --------------------------------------------------------------------
    8042             :          */
    8043        1630 :         if (CPLFetchBool(papszOptions, "TFW", false))
    8044           2 :             GDALWriteWorldFile(pszFilename, "tfw", l_gt.data());
    8045        1628 :         else if (CPLFetchBool(papszOptions, "WORLDFILE", false))
    8046           1 :             GDALWriteWorldFile(pszFilename, "wld", l_gt.data());
    8047             :     }
    8048             : 
    8049             :     /* -------------------------------------------------------------------- */
    8050             :     /*      Otherwise write tiepoints if they are available.                */
    8051             :     /* -------------------------------------------------------------------- */
    8052         426 :     else if (poSrcDS->GetGCPCount() > 0 && bGeoTIFF)
    8053             :     {
    8054          12 :         const GDAL_GCP *pasGCPs = poSrcDS->GetGCPs();
    8055             :         double *padfTiePoints = static_cast<double *>(
    8056          12 :             CPLMalloc(6 * sizeof(double) * poSrcDS->GetGCPCount()));
    8057             : 
    8058          60 :         for (int iGCP = 0; iGCP < poSrcDS->GetGCPCount(); ++iGCP)
    8059             :         {
    8060             : 
    8061          48 :             padfTiePoints[iGCP * 6 + 0] = pasGCPs[iGCP].dfGCPPixel;
    8062          48 :             padfTiePoints[iGCP * 6 + 1] = pasGCPs[iGCP].dfGCPLine;
    8063          48 :             padfTiePoints[iGCP * 6 + 2] = 0;
    8064          48 :             padfTiePoints[iGCP * 6 + 3] = pasGCPs[iGCP].dfGCPX;
    8065          48 :             padfTiePoints[iGCP * 6 + 4] = pasGCPs[iGCP].dfGCPY;
    8066          48 :             padfTiePoints[iGCP * 6 + 5] = pasGCPs[iGCP].dfGCPZ;
    8067             : 
    8068          48 :             if (bPixelIsPoint && !bPointGeoIgnore)
    8069             :             {
    8070           4 :                 padfTiePoints[iGCP * 6 + 0] -= 0.5;
    8071           4 :                 padfTiePoints[iGCP * 6 + 1] -= 0.5;
    8072             :             }
    8073             :         }
    8074             : 
    8075          12 :         TIFFSetField(l_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * poSrcDS->GetGCPCount(),
    8076             :                      padfTiePoints);
    8077          12 :         CPLFree(padfTiePoints);
    8078             : 
    8079          12 :         l_poSRS = poSrcDS->GetGCPSpatialRef();
    8080             : 
    8081          24 :         if (CPLFetchBool(papszOptions, "TFW", false) ||
    8082          12 :             CPLFetchBool(papszOptions, "WORLDFILE", false))
    8083             :         {
    8084           0 :             ReportError(
    8085             :                 pszFilename, CE_Warning, CPLE_AppDefined,
    8086             :                 "TFW=ON or WORLDFILE=ON creation options are ignored when "
    8087             :                 "GCPs are available");
    8088             :         }
    8089             :     }
    8090             :     else
    8091             :     {
    8092         414 :         l_poSRS = poSrcDS->GetSpatialRef();
    8093             :     }
    8094             : 
    8095             :     /* -------------------------------------------------------------------- */
    8096             :     /*      Copy xml:XMP data                                               */
    8097             :     /* -------------------------------------------------------------------- */
    8098        2056 :     char **papszXMP = poSrcDS->GetMetadata("xml:XMP");
    8099        2056 :     if (papszXMP != nullptr && *papszXMP != nullptr)
    8100             :     {
    8101           9 :         int nTagSize = static_cast<int>(strlen(*papszXMP));
    8102           9 :         TIFFSetField(l_hTIFF, TIFFTAG_XMLPACKET, nTagSize, *papszXMP);
    8103             :     }
    8104             : 
    8105             :     /* -------------------------------------------------------------------- */
    8106             :     /*      Write the projection information, if possible.                  */
    8107             :     /* -------------------------------------------------------------------- */
    8108        2056 :     const bool bHasProjection = l_poSRS != nullptr;
    8109        2056 :     bool bExportSRSToPAM = false;
    8110        2056 :     if ((bHasProjection || bPixelIsPoint) && bGeoTIFF)
    8111             :     {
    8112        1596 :         GTIF *psGTIF = GTiffDataset::GTIFNew(l_hTIFF);
    8113             : 
    8114        1596 :         if (bHasProjection)
    8115             :         {
    8116        1596 :             const auto eGeoTIFFKeysFlavor = GetGTIFFKeysFlavor(papszOptions);
    8117        1596 :             if (IsSRSCompatibleOfGeoTIFF(l_poSRS, eGeoTIFFKeysFlavor))
    8118             :             {
    8119        1596 :                 GTIFSetFromOGISDefnEx(
    8120             :                     psGTIF,
    8121             :                     OGRSpatialReference::ToHandle(
    8122             :                         const_cast<OGRSpatialReference *>(l_poSRS)),
    8123             :                     eGeoTIFFKeysFlavor, GetGeoTIFFVersion(papszOptions));
    8124             :             }
    8125             :             else
    8126             :             {
    8127           0 :                 bExportSRSToPAM = true;
    8128             :             }
    8129             :         }
    8130             : 
    8131        1596 :         if (bPixelIsPoint)
    8132             :         {
    8133          10 :             GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
    8134             :                        RasterPixelIsPoint);
    8135             :         }
    8136             : 
    8137        1596 :         GTIFWriteKeys(psGTIF);
    8138        1596 :         GTIFFree(psGTIF);
    8139             :     }
    8140             : 
    8141        2056 :     bool l_bDontReloadFirstBlock = false;
    8142             : 
    8143             : #ifdef HAVE_LIBJPEG
    8144        2056 :     if (bCopyFromJPEG)
    8145             :     {
    8146          12 :         GTIFF_CopyFromJPEG_WriteAdditionalTags(l_hTIFF, poSrcDS);
    8147             :     }
    8148             : #endif
    8149             : 
    8150             :     /* -------------------------------------------------------------------- */
    8151             :     /*      Cleanup                                                         */
    8152             :     /* -------------------------------------------------------------------- */
    8153        2056 :     if (bCopySrcOverviews)
    8154             :     {
    8155         187 :         TIFFDeferStrileArrayWriting(l_hTIFF);
    8156             :     }
    8157        2056 :     TIFFWriteCheck(l_hTIFF, TIFFIsTiled(l_hTIFF), "GTiffCreateCopy()");
    8158        2056 :     TIFFWriteDirectory(l_hTIFF);
    8159        2056 :     if (bStreaming)
    8160             :     {
    8161             :         // We need to write twice the directory to be sure that custom
    8162             :         // TIFF tags are correctly sorted and that padding bytes have been
    8163             :         // added.
    8164           5 :         TIFFSetDirectory(l_hTIFF, 0);
    8165           5 :         TIFFWriteDirectory(l_hTIFF);
    8166             : 
    8167           5 :         if (VSIFSeekL(l_fpL, 0, SEEK_END) != 0)
    8168           0 :             ReportError(pszFilename, CE_Failure, CPLE_FileIO, "Cannot seek");
    8169           5 :         const int nSize = static_cast<int>(VSIFTellL(l_fpL));
    8170             : 
    8171           5 :         vsi_l_offset nDataLength = 0;
    8172           5 :         VSIGetMemFileBuffer(l_osTmpFilename, &nDataLength, FALSE);
    8173           5 :         TIFFSetDirectory(l_hTIFF, 0);
    8174           5 :         GTiffFillStreamableOffsetAndCount(l_hTIFF, nSize);
    8175           5 :         TIFFWriteDirectory(l_hTIFF);
    8176             :     }
    8177        2056 :     const auto nDirCount = TIFFNumberOfDirectories(l_hTIFF);
    8178        2056 :     if (nDirCount >= 1)
    8179             :     {
    8180        2049 :         TIFFSetDirectory(l_hTIFF, static_cast<tdir_t>(nDirCount - 1));
    8181             :     }
    8182        2056 :     const toff_t l_nDirOffset = TIFFCurrentDirOffset(l_hTIFF);
    8183        2056 :     TIFFFlush(l_hTIFF);
    8184        2056 :     XTIFFClose(l_hTIFF);
    8185             : 
    8186        2056 :     VSIFSeekL(l_fpL, 0, SEEK_SET);
    8187             : 
    8188             :     // fpStreaming will assigned to the instance and not closed here.
    8189        2056 :     VSILFILE *fpStreaming = nullptr;
    8190        2056 :     if (bStreaming)
    8191             :     {
    8192           5 :         vsi_l_offset nDataLength = 0;
    8193             :         void *pabyBuffer =
    8194           5 :             VSIGetMemFileBuffer(l_osTmpFilename, &nDataLength, FALSE);
    8195           5 :         fpStreaming = VSIFOpenL(pszFilename, "wb");
    8196           5 :         if (fpStreaming == nullptr)
    8197             :         {
    8198           1 :             VSIUnlink(l_osTmpFilename);
    8199           1 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    8200           1 :             return nullptr;
    8201             :         }
    8202           4 :         if (static_cast<vsi_l_offset>(VSIFWriteL(pabyBuffer, 1,
    8203             :                                                  static_cast<int>(nDataLength),
    8204           4 :                                                  fpStreaming)) != nDataLength)
    8205             :         {
    8206           0 :             ReportError(pszFilename, CE_Failure, CPLE_FileIO,
    8207             :                         "Could not write %d bytes",
    8208             :                         static_cast<int>(nDataLength));
    8209           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fpStreaming));
    8210           0 :             VSIUnlink(l_osTmpFilename);
    8211           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    8212           0 :             return nullptr;
    8213             :         }
    8214             :     }
    8215             : 
    8216             :     /* -------------------------------------------------------------------- */
    8217             :     /*      Re-open as a dataset and copy over missing metadata using       */
    8218             :     /*      PAM facilities.                                                 */
    8219             :     /* -------------------------------------------------------------------- */
    8220        2055 :     l_hTIFF = VSI_TIFFOpen(bStreaming ? l_osTmpFilename.c_str() : pszFilename,
    8221             :                            "r+", l_fpL);
    8222        2055 :     if (l_hTIFF == nullptr)
    8223             :     {
    8224          11 :         if (bStreaming)
    8225           0 :             VSIUnlink(l_osTmpFilename);
    8226          11 :         l_fpL->CancelCreation();
    8227          11 :         CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    8228          11 :         return nullptr;
    8229             :     }
    8230             : 
    8231             :     /* -------------------------------------------------------------------- */
    8232             :     /*      Create a corresponding GDALDataset.                             */
    8233             :     /* -------------------------------------------------------------------- */
    8234        2044 :     GTiffDataset *poDS = new GTiffDataset();
    8235             :     const bool bSuppressASAP =
    8236        2044 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "@SUPPRESS_ASAP", "NO"));
    8237        2044 :     if (bSuppressASAP)
    8238           4 :         poDS->MarkSuppressOnClose();
    8239        2044 :     poDS->SetDescription(pszFilename);
    8240        2044 :     poDS->eAccess = GA_Update;
    8241        2044 :     poDS->m_osFilename = pszFilename;
    8242        2044 :     poDS->m_fpL = l_fpL;
    8243        2044 :     poDS->m_bIMDRPCMetadataLoaded = true;
    8244        2044 :     poDS->m_nColorTableMultiplier = nColorTableMultiplier;
    8245        2044 :     poDS->m_bTileInterleave = bTileInterleaving;
    8246             : 
    8247        2044 :     if (bTileInterleaving)
    8248             :     {
    8249           7 :         poDS->m_oGTiffMDMD.SetMetadataItem("INTERLEAVE", "TILE",
    8250             :                                            "IMAGE_STRUCTURE");
    8251             :     }
    8252             : 
    8253        2044 :     const bool bAppend = CPLFetchBool(papszOptions, "APPEND_SUBDATASET", false);
    8254        4087 :     if (poDS->OpenOffset(l_hTIFF,
    8255        2043 :                          bAppend ? l_nDirOffset : TIFFCurrentDirOffset(l_hTIFF),
    8256             :                          GA_Update,
    8257             :                          false,  // bAllowRGBAInterface
    8258             :                          true    // bReadGeoTransform
    8259        2044 :                          ) != CE_None)
    8260             :     {
    8261           0 :         l_fpL->CancelCreation();
    8262           0 :         delete poDS;
    8263           0 :         if (bStreaming)
    8264           0 :             VSIUnlink(l_osTmpFilename);
    8265           0 :         return nullptr;
    8266             :     }
    8267             : 
    8268             :     // Legacy... Patch back GDT_Int8 type to GDT_Byte if the user used
    8269             :     // PIXELTYPE=SIGNEDBYTE
    8270        2044 :     const char *pszPixelType = CSLFetchNameValue(papszOptions, "PIXELTYPE");
    8271        2044 :     if (pszPixelType == nullptr)
    8272        2039 :         pszPixelType = "";
    8273        2044 :     if (eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE"))
    8274             :     {
    8275          10 :         for (int i = 0; i < poDS->nBands; ++i)
    8276             :         {
    8277           5 :             auto poBand = static_cast<GTiffRasterBand *>(poDS->papoBands[i]);
    8278           5 :             poBand->eDataType = GDT_Byte;
    8279           5 :             poBand->EnablePixelTypeSignedByteWarning(false);
    8280           5 :             poBand->SetMetadataItem("PIXELTYPE", "SIGNEDBYTE",
    8281             :                                     "IMAGE_STRUCTURE");
    8282           5 :             poBand->EnablePixelTypeSignedByteWarning(true);
    8283             :         }
    8284             :     }
    8285             : 
    8286        2044 :     poDS->oOvManager.Initialize(poDS, pszFilename);
    8287             : 
    8288        2044 :     if (bStreaming)
    8289             :     {
    8290           4 :         VSIUnlink(l_osTmpFilename);
    8291           4 :         poDS->m_fpToWrite = fpStreaming;
    8292             :     }
    8293        2044 :     poDS->m_eProfile = eProfile;
    8294             : 
    8295        2044 :     int nCloneInfoFlags = GCIF_PAM_DEFAULT & ~GCIF_MASK;
    8296             : 
    8297             :     // If we explicitly asked not to tag the alpha band as such, do not
    8298             :     // reintroduce this alpha color interpretation in PAM.
    8299        2044 :     if (poSrcDS->GetRasterBand(l_nBands)->GetColorInterpretation() ==
    8300        2171 :             GCI_AlphaBand &&
    8301         127 :         GTiffGetAlphaValue(
    8302             :             CPLGetConfigOption("GTIFF_ALPHA",
    8303             :                                CSLFetchNameValue(papszOptions, "ALPHA")),
    8304             :             DEFAULT_ALPHA_TYPE) == EXTRASAMPLE_UNSPECIFIED)
    8305             :     {
    8306           1 :         nCloneInfoFlags &= ~GCIF_COLORINTERP;
    8307             :     }
    8308             :     // Ignore source band color interpretation if requesting PHOTOMETRIC=RGB
    8309        3245 :     else if (l_nBands >= 3 &&
    8310        1202 :              EQUAL(CSLFetchNameValueDef(papszOptions, "PHOTOMETRIC", ""),
    8311             :                    "RGB"))
    8312             :     {
    8313          28 :         for (int i = 1; i <= 3; i++)
    8314             :         {
    8315          21 :             poDS->GetRasterBand(i)->SetColorInterpretation(
    8316          21 :                 static_cast<GDALColorInterp>(GCI_RedBand + (i - 1)));
    8317             :         }
    8318           7 :         nCloneInfoFlags &= ~GCIF_COLORINTERP;
    8319           9 :         if (!(l_nBands == 4 &&
    8320           2 :               CSLFetchNameValue(papszOptions, "ALPHA") != nullptr))
    8321             :         {
    8322          15 :             for (int i = 4; i <= l_nBands; i++)
    8323             :             {
    8324          18 :                 poDS->GetRasterBand(i)->SetColorInterpretation(
    8325           9 :                     poSrcDS->GetRasterBand(i)->GetColorInterpretation());
    8326             :             }
    8327             :         }
    8328             :     }
    8329             : 
    8330             :     CPLString osOldGTIFF_REPORT_COMPD_CSVal(
    8331        4088 :         CPLGetConfigOption("GTIFF_REPORT_COMPD_CS", ""));
    8332        2044 :     CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS", "YES");
    8333        2044 :     poDS->CloneInfo(poSrcDS, nCloneInfoFlags);
    8334        2044 :     CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS",
    8335        2044 :                                   osOldGTIFF_REPORT_COMPD_CSVal.empty()
    8336             :                                       ? nullptr
    8337           0 :                                       : osOldGTIFF_REPORT_COMPD_CSVal.c_str());
    8338             : 
    8339        2061 :     if ((!bGeoTIFF || bExportSRSToPAM) &&
    8340          17 :         (poDS->GetPamFlags() & GPF_DISABLED) == 0)
    8341             :     {
    8342             :         // Copy georeferencing info to PAM if the profile is not GeoTIFF
    8343          16 :         poDS->GDALPamDataset::SetSpatialRef(poDS->GetSpatialRef());
    8344          16 :         GDALGeoTransform gt;
    8345          16 :         if (poDS->GetGeoTransform(gt) == CE_None)
    8346             :         {
    8347           5 :             poDS->GDALPamDataset::SetGeoTransform(gt);
    8348             :         }
    8349          16 :         poDS->GDALPamDataset::SetGCPs(poDS->GetGCPCount(), poDS->GetGCPs(),
    8350             :                                       poDS->GetGCPSpatialRef());
    8351             :     }
    8352             : 
    8353        2044 :     poDS->m_papszCreationOptions = CSLDuplicate(papszOptions);
    8354        2044 :     poDS->m_bDontReloadFirstBlock = l_bDontReloadFirstBlock;
    8355             : 
    8356             :     /* -------------------------------------------------------------------- */
    8357             :     /*      CloneInfo() does not merge metadata, it just replaces it        */
    8358             :     /*      totally.  So we have to merge it.                               */
    8359             :     /* -------------------------------------------------------------------- */
    8360             : 
    8361        2044 :     char **papszSRC_MD = poSrcDS->GetMetadata();
    8362        2044 :     char **papszDST_MD = CSLDuplicate(poDS->GetMetadata());
    8363             : 
    8364        2044 :     papszDST_MD = CSLMerge(papszDST_MD, papszSRC_MD);
    8365             : 
    8366        2044 :     poDS->SetMetadata(papszDST_MD);
    8367        2044 :     CSLDestroy(papszDST_MD);
    8368             : 
    8369             :     // Depending on the PHOTOMETRIC tag, the TIFF file may not have the same
    8370             :     // band count as the source. Will fail later in GDALDatasetCopyWholeRaster
    8371             :     // anyway.
    8372        6958 :     for (int nBand = 1;
    8373        6958 :          nBand <= std::min(poDS->GetRasterCount(), poSrcDS->GetRasterCount());
    8374             :          ++nBand)
    8375             :     {
    8376        4914 :         GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(nBand);
    8377        4914 :         GDALRasterBand *poDstBand = poDS->GetRasterBand(nBand);
    8378        4914 :         papszSRC_MD = poSrcBand->GetMetadata();
    8379        4914 :         papszDST_MD = CSLDuplicate(poDstBand->GetMetadata());
    8380             : 
    8381        4914 :         papszDST_MD = CSLMerge(papszDST_MD, papszSRC_MD);
    8382             : 
    8383        4914 :         poDstBand->SetMetadata(papszDST_MD);
    8384        4914 :         CSLDestroy(papszDST_MD);
    8385             : 
    8386        4914 :         char **papszCatNames = poSrcBand->GetCategoryNames();
    8387        4914 :         if (nullptr != papszCatNames)
    8388           0 :             poDstBand->SetCategoryNames(papszCatNames);
    8389             :     }
    8390             : 
    8391        2044 :     l_hTIFF = static_cast<TIFF *>(poDS->GetInternalHandle("TIFF_HANDLE"));
    8392             : 
    8393             :     /* -------------------------------------------------------------------- */
    8394             :     /*      Handle forcing xml:ESRI data to be written to PAM.              */
    8395             :     /* -------------------------------------------------------------------- */
    8396        2044 :     if (CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
    8397             :     {
    8398           1 :         char **papszESRIMD = poSrcDS->GetMetadata("xml:ESRI");
    8399           1 :         if (papszESRIMD)
    8400             :         {
    8401           1 :             poDS->SetMetadata(papszESRIMD, "xml:ESRI");
    8402             :         }
    8403             :     }
    8404             : 
    8405             :     /* -------------------------------------------------------------------- */
    8406             :     /*      Second chance: now that we have a PAM dataset, it is possible   */
    8407             :     /*      to write metadata that we could not write as a TIFF tag.        */
    8408             :     /* -------------------------------------------------------------------- */
    8409        2044 :     if (!bHasWrittenMDInGeotiffTAG && !bStreaming)
    8410             :     {
    8411           6 :         GTiffDataset::WriteMetadata(
    8412             :             poDS, l_hTIFF, true, eProfile, pszFilename, papszOptions,
    8413             :             true /* don't write RPC and IMD file again */);
    8414             :     }
    8415             : 
    8416        2044 :     if (!bStreaming)
    8417        2040 :         GTiffDataset::WriteRPC(poDS, l_hTIFF, true, eProfile, pszFilename,
    8418             :                                papszOptions,
    8419             :                                true /* write only in PAM AND if needed */);
    8420             : 
    8421        2044 :     poDS->m_bWriteCOGLayout = bCopySrcOverviews;
    8422             : 
    8423             :     // To avoid unnecessary directory rewriting.
    8424        2044 :     poDS->m_bMetadataChanged = false;
    8425        2044 :     poDS->m_bGeoTIFFInfoChanged = false;
    8426        2044 :     poDS->m_bNoDataChanged = false;
    8427        2044 :     poDS->m_bForceUnsetGTOrGCPs = false;
    8428        2044 :     poDS->m_bForceUnsetProjection = false;
    8429        2044 :     poDS->m_bStreamingOut = bStreaming;
    8430             : 
    8431             :     // Don't try to load external metadata files (#6597).
    8432        2044 :     poDS->m_bIMDRPCMetadataLoaded = true;
    8433             : 
    8434             :     // We must re-set the compression level at this point, since it has been
    8435             :     // lost a few lines above when closing the newly create TIFF file The
    8436             :     // TIFFTAG_ZIPQUALITY & TIFFTAG_JPEGQUALITY are not store in the TIFF file.
    8437             :     // They are just TIFF session parameters.
    8438             : 
    8439        2044 :     poDS->m_nZLevel = GTiffGetZLevel(papszOptions);
    8440        2044 :     poDS->m_nLZMAPreset = GTiffGetLZMAPreset(papszOptions);
    8441        2044 :     poDS->m_nZSTDLevel = GTiffGetZSTDPreset(papszOptions);
    8442        2044 :     poDS->m_nWebPLevel = GTiffGetWebPLevel(papszOptions);
    8443        2044 :     poDS->m_bWebPLossless = GTiffGetWebPLossless(papszOptions);
    8444        2047 :     if (poDS->m_nWebPLevel != 100 && poDS->m_bWebPLossless &&
    8445           3 :         CSLFetchNameValue(papszOptions, "WEBP_LEVEL"))
    8446             :     {
    8447           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    8448             :                  "WEBP_LEVEL is specified, but WEBP_LOSSLESS=YES. "
    8449             :                  "WEBP_LEVEL will be ignored.");
    8450             :     }
    8451        2044 :     poDS->m_nJpegQuality = GTiffGetJpegQuality(papszOptions);
    8452        2044 :     poDS->m_nJpegTablesMode = GTiffGetJpegTablesMode(papszOptions);
    8453        2044 :     poDS->GetDiscardLsbOption(papszOptions);
    8454        2044 :     poDS->m_dfMaxZError = GTiffGetLERCMaxZError(papszOptions);
    8455        2044 :     poDS->m_dfMaxZErrorOverview = GTiffGetLERCMaxZErrorOverview(papszOptions);
    8456             : #if HAVE_JXL
    8457        2044 :     poDS->m_bJXLLossless = GTiffGetJXLLossless(papszOptions);
    8458        2044 :     poDS->m_nJXLEffort = GTiffGetJXLEffort(papszOptions);
    8459        2044 :     poDS->m_fJXLDistance = GTiffGetJXLDistance(papszOptions);
    8460        2044 :     poDS->m_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszOptions);
    8461             : #endif
    8462        2044 :     poDS->InitCreationOrOpenOptions(true, papszOptions);
    8463             : 
    8464        2044 :     if (l_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    8465        2019 :         l_nCompression == COMPRESSION_LERC)
    8466             :     {
    8467          96 :         GTiffSetDeflateSubCodec(l_hTIFF);
    8468             : 
    8469          96 :         if (poDS->m_nZLevel != -1)
    8470             :         {
    8471          12 :             TIFFSetField(l_hTIFF, TIFFTAG_ZIPQUALITY, poDS->m_nZLevel);
    8472             :         }
    8473             :     }
    8474        2044 :     if (l_nCompression == COMPRESSION_JPEG)
    8475             :     {
    8476          75 :         if (poDS->m_nJpegQuality != -1)
    8477             :         {
    8478           9 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGQUALITY, poDS->m_nJpegQuality);
    8479             :         }
    8480          75 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGTABLESMODE, poDS->m_nJpegTablesMode);
    8481             :     }
    8482        2044 :     if (l_nCompression == COMPRESSION_LZMA)
    8483             :     {
    8484           7 :         if (poDS->m_nLZMAPreset != -1)
    8485             :         {
    8486           6 :             TIFFSetField(l_hTIFF, TIFFTAG_LZMAPRESET, poDS->m_nLZMAPreset);
    8487             :         }
    8488             :     }
    8489        2044 :     if (l_nCompression == COMPRESSION_ZSTD ||
    8490        2033 :         l_nCompression == COMPRESSION_LERC)
    8491             :     {
    8492          82 :         if (poDS->m_nZSTDLevel != -1)
    8493             :         {
    8494           8 :             TIFFSetField(l_hTIFF, TIFFTAG_ZSTD_LEVEL, poDS->m_nZSTDLevel);
    8495             :         }
    8496             :     }
    8497        2044 :     if (l_nCompression == COMPRESSION_LERC)
    8498             :     {
    8499          71 :         TIFFSetField(l_hTIFF, TIFFTAG_LERC_MAXZERROR, poDS->m_dfMaxZError);
    8500             :     }
    8501             : #if HAVE_JXL
    8502        2044 :     if (l_nCompression == COMPRESSION_JXL ||
    8503        2044 :         l_nCompression == COMPRESSION_JXL_DNG_1_7)
    8504             :     {
    8505          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_LOSSYNESS,
    8506          91 :                      poDS->m_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
    8507          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_EFFORT, poDS->m_nJXLEffort);
    8508          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_DISTANCE,
    8509          91 :                      static_cast<double>(poDS->m_fJXLDistance));
    8510          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE,
    8511          91 :                      static_cast<double>(poDS->m_fJXLAlphaDistance));
    8512             :     }
    8513             : #endif
    8514        2044 :     if (l_nCompression == COMPRESSION_WEBP)
    8515             :     {
    8516          14 :         if (poDS->m_nWebPLevel != -1)
    8517             :         {
    8518          14 :             TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LEVEL, poDS->m_nWebPLevel);
    8519             :         }
    8520             : 
    8521          14 :         if (poDS->m_bWebPLossless)
    8522             :         {
    8523           5 :             TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LOSSLESS, poDS->m_bWebPLossless);
    8524             :         }
    8525             :     }
    8526             : 
    8527             :     /* -------------------------------------------------------------------- */
    8528             :     /*      Do we want to ensure all blocks get written out on close to     */
    8529             :     /*      avoid sparse files?                                             */
    8530             :     /* -------------------------------------------------------------------- */
    8531        2044 :     if (!CPLFetchBool(papszOptions, "SPARSE_OK", false))
    8532        2016 :         poDS->m_bFillEmptyTilesAtClosing = true;
    8533             : 
    8534        2044 :     poDS->m_bWriteEmptyTiles =
    8535        3905 :         (bCopySrcOverviews && poDS->m_bFillEmptyTilesAtClosing) || bStreaming ||
    8536        1861 :         (poDS->m_nCompression != COMPRESSION_NONE &&
    8537         303 :          poDS->m_bFillEmptyTilesAtClosing);
    8538             :     // Only required for people writing non-compressed striped files in the
    8539             :     // rightorder and wanting all tstrips to be written in the same order
    8540             :     // so that the end result can be memory mapped without knowledge of each
    8541             :     // strip offset
    8542        2044 :     if (CPLTestBool(CSLFetchNameValueDef(
    8543        4088 :             papszOptions, "WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")) ||
    8544        2044 :         CPLTestBool(CSLFetchNameValueDef(
    8545             :             papszOptions, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")))
    8546             :     {
    8547           0 :         poDS->m_bWriteEmptyTiles = true;
    8548             :     }
    8549             : 
    8550             :     // Precreate (internal) mask, so that the IBuildOverviews() below
    8551             :     // has a chance to create also the overviews of the mask.
    8552        2044 :     CPLErr eErr = CE_None;
    8553             : 
    8554        2044 :     if (bCreateMask)
    8555             :     {
    8556          38 :         eErr = poDS->CreateMaskBand(nMaskFlags);
    8557          38 :         if (poDS->m_poMaskDS)
    8558             :         {
    8559          37 :             poDS->m_poMaskDS->m_bFillEmptyTilesAtClosing =
    8560          37 :                 poDS->m_bFillEmptyTilesAtClosing;
    8561          37 :             poDS->m_poMaskDS->m_bWriteEmptyTiles = poDS->m_bWriteEmptyTiles;
    8562             :         }
    8563             :     }
    8564             : 
    8565             :     /* -------------------------------------------------------------------- */
    8566             :     /*      Create and then copy existing overviews if requested            */
    8567             :     /*  We do it such that all the IFDs are at the beginning of the file,   */
    8568             :     /*  and that the imagery data for the smallest overview is written      */
    8569             :     /*  first, that way the file is more usable when embedded in a          */
    8570             :     /*  compressed stream.                                                  */
    8571             :     /* -------------------------------------------------------------------- */
    8572             : 
    8573             :     // For scaled progress due to overview copying.
    8574        2044 :     const int nBandsWidthMask = l_nBands + (bCreateMask ? 1 : 0);
    8575        2044 :     double dfTotalPixels =
    8576        2044 :         static_cast<double>(nXSize) * nYSize * nBandsWidthMask;
    8577        2044 :     double dfCurPixels = 0;
    8578             : 
    8579        2044 :     if (eErr == CE_None && bCopySrcOverviews)
    8580             :     {
    8581           0 :         std::unique_ptr<GDALDataset> poMaskOvrDS;
    8582             :         const char *pszMaskOvrDS =
    8583         184 :             CSLFetchNameValue(papszOptions, "@MASK_OVERVIEW_DATASET");
    8584         184 :         if (pszMaskOvrDS)
    8585             :         {
    8586           6 :             poMaskOvrDS.reset(GDALDataset::Open(pszMaskOvrDS));
    8587           6 :             if (!poMaskOvrDS)
    8588             :             {
    8589           0 :                 l_fpL->CancelCreation();
    8590           0 :                 delete poDS;
    8591           0 :                 return nullptr;
    8592             :             }
    8593           6 :             if (poMaskOvrDS->GetRasterCount() != 1)
    8594             :             {
    8595           0 :                 l_fpL->CancelCreation();
    8596           0 :                 delete poDS;
    8597           0 :                 return nullptr;
    8598             :             }
    8599             :         }
    8600         184 :         if (nSrcOverviews)
    8601             :         {
    8602          68 :             eErr = poDS->CreateOverviewsFromSrcOverviews(poSrcDS, poOvrDS.get(),
    8603             :                                                          nSrcOverviews);
    8604             : 
    8605         198 :             if (eErr == CE_None &&
    8606          68 :                 (poMaskOvrDS != nullptr ||
    8607          62 :                  (poSrcDS->GetRasterBand(1)->GetOverview(0) &&
    8608          34 :                   poSrcDS->GetRasterBand(1)->GetOverview(0)->GetMaskFlags() ==
    8609             :                       GMF_PER_DATASET)))
    8610             :             {
    8611          19 :                 int nOvrBlockXSize = 0;
    8612          19 :                 int nOvrBlockYSize = 0;
    8613          19 :                 GTIFFGetOverviewBlockSize(
    8614             :                     GDALRasterBand::ToHandle(poDS->GetRasterBand(1)),
    8615             :                     &nOvrBlockXSize, &nOvrBlockYSize, nullptr, nullptr);
    8616          19 :                 eErr = poDS->CreateInternalMaskOverviews(nOvrBlockXSize,
    8617             :                                                          nOvrBlockYSize);
    8618             :             }
    8619             :         }
    8620             : 
    8621         184 :         TIFFForceStrileArrayWriting(poDS->m_hTIFF);
    8622             : 
    8623         184 :         if (poDS->m_poMaskDS)
    8624             :         {
    8625          27 :             TIFFForceStrileArrayWriting(poDS->m_poMaskDS->m_hTIFF);
    8626             :         }
    8627             : 
    8628         308 :         for (int i = 0; i < poDS->m_nOverviewCount; i++)
    8629             :         {
    8630         124 :             TIFFForceStrileArrayWriting(poDS->m_papoOverviewDS[i]->m_hTIFF);
    8631             : 
    8632         124 :             if (poDS->m_papoOverviewDS[i]->m_poMaskDS)
    8633             :             {
    8634          32 :                 TIFFForceStrileArrayWriting(
    8635          32 :                     poDS->m_papoOverviewDS[i]->m_poMaskDS->m_hTIFF);
    8636             :             }
    8637             :         }
    8638             : 
    8639         184 :         if (eErr == CE_None && nSrcOverviews)
    8640             :         {
    8641          68 :             if (poDS->m_nOverviewCount != nSrcOverviews)
    8642             :             {
    8643           0 :                 ReportError(
    8644             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    8645             :                     "Did only manage to instantiate %d overview levels, "
    8646             :                     "whereas source contains %d",
    8647           0 :                     poDS->m_nOverviewCount, nSrcOverviews);
    8648           0 :                 eErr = CE_Failure;
    8649             :             }
    8650             : 
    8651         192 :             for (int i = 0; eErr == CE_None && i < nSrcOverviews; ++i)
    8652             :             {
    8653             :                 GDALRasterBand *poOvrBand =
    8654             :                     poOvrDS
    8655         195 :                         ? (i == 0
    8656          71 :                                ? poOvrDS->GetRasterBand(1)
    8657          37 :                                : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    8658         177 :                         : poSrcDS->GetRasterBand(1)->GetOverview(i);
    8659             :                 const double dfOvrPixels =
    8660         124 :                     static_cast<double>(poOvrBand->GetXSize()) *
    8661         124 :                     poOvrBand->GetYSize();
    8662         124 :                 dfTotalPixels += dfOvrPixels * l_nBands;
    8663         230 :                 if (poOvrBand->GetMaskFlags() == GMF_PER_DATASET ||
    8664         106 :                     poMaskOvrDS != nullptr)
    8665             :                 {
    8666          32 :                     dfTotalPixels += dfOvrPixels;
    8667             :                 }
    8668          92 :                 else if (i == 0 && poDS->GetRasterBand(1)->GetMaskFlags() ==
    8669             :                                        GMF_PER_DATASET)
    8670             :                 {
    8671           1 :                     ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
    8672             :                                 "Source dataset has a mask band on full "
    8673             :                                 "resolution, overviews on the regular bands, "
    8674             :                                 "but lacks overviews on the mask band.");
    8675             :                 }
    8676             :             }
    8677             : 
    8678             :             // Now copy the imagery.
    8679             :             // Begin with the smallest overview.
    8680          68 :             for (int iOvrLevel = nSrcOverviews - 1;
    8681         191 :                  eErr == CE_None && iOvrLevel >= 0; --iOvrLevel)
    8682             :             {
    8683         123 :                 auto poDstDS = poDS->m_papoOverviewDS[iOvrLevel];
    8684             : 
    8685             :                 // Create a fake dataset with the source overview level so that
    8686             :                 // GDALDatasetCopyWholeRaster can cope with it.
    8687             :                 GDALDataset *poSrcOvrDS =
    8688             :                     poOvrDS
    8689         160 :                         ? (iOvrLevel == 0 ? poOvrDS.get()
    8690          37 :                                           : GDALCreateOverviewDataset(
    8691             :                                                 poOvrDS.get(), iOvrLevel - 1,
    8692             :                                                 /* bThisLevelOnly = */ true))
    8693          52 :                         : GDALCreateOverviewDataset(
    8694             :                               poSrcDS, iOvrLevel,
    8695         123 :                               /* bThisLevelOnly = */ true);
    8696             :                 GDALRasterBand *poSrcOvrBand =
    8697         194 :                     poOvrDS ? (iOvrLevel == 0
    8698          71 :                                    ? poOvrDS->GetRasterBand(1)
    8699          74 :                                    : poOvrDS->GetRasterBand(1)->GetOverview(
    8700          37 :                                          iOvrLevel - 1))
    8701         175 :                             : poSrcDS->GetRasterBand(1)->GetOverview(iOvrLevel);
    8702             :                 double dfNextCurPixels =
    8703             :                     dfCurPixels +
    8704         123 :                     static_cast<double>(poSrcOvrBand->GetXSize()) *
    8705         123 :                         poSrcOvrBand->GetYSize() * l_nBands;
    8706             : 
    8707         123 :                 poDstDS->m_bBlockOrderRowMajor = true;
    8708         123 :                 poDstDS->m_bLeaderSizeAsUInt4 = true;
    8709         123 :                 poDstDS->m_bTrailerRepeatedLast4BytesRepeated = true;
    8710         123 :                 poDstDS->m_bFillEmptyTilesAtClosing =
    8711         123 :                     poDS->m_bFillEmptyTilesAtClosing;
    8712         123 :                 poDstDS->m_bWriteEmptyTiles = poDS->m_bWriteEmptyTiles;
    8713         123 :                 poDstDS->m_bTileInterleave = poDS->m_bTileInterleave;
    8714         123 :                 GDALRasterBand *poSrcMaskBand = nullptr;
    8715         123 :                 if (poDstDS->m_poMaskDS)
    8716             :                 {
    8717          32 :                     poDstDS->m_poMaskDS->m_bBlockOrderRowMajor = true;
    8718          32 :                     poDstDS->m_poMaskDS->m_bLeaderSizeAsUInt4 = true;
    8719          32 :                     poDstDS->m_poMaskDS->m_bTrailerRepeatedLast4BytesRepeated =
    8720             :                         true;
    8721          32 :                     poDstDS->m_poMaskDS->m_bFillEmptyTilesAtClosing =
    8722          32 :                         poDS->m_bFillEmptyTilesAtClosing;
    8723          32 :                     poDstDS->m_poMaskDS->m_bWriteEmptyTiles =
    8724          32 :                         poDS->m_bWriteEmptyTiles;
    8725             : 
    8726          32 :                     poSrcMaskBand =
    8727             :                         poMaskOvrDS
    8728          46 :                             ? (iOvrLevel == 0
    8729          14 :                                    ? poMaskOvrDS->GetRasterBand(1)
    8730          16 :                                    : poMaskOvrDS->GetRasterBand(1)->GetOverview(
    8731           8 :                                          iOvrLevel - 1))
    8732          50 :                             : poSrcOvrBand->GetMaskBand();
    8733             :                 }
    8734             : 
    8735         123 :                 if (poDstDS->m_poMaskDS)
    8736             :                 {
    8737          32 :                     dfNextCurPixels +=
    8738          32 :                         static_cast<double>(poSrcOvrBand->GetXSize()) *
    8739          32 :                         poSrcOvrBand->GetYSize();
    8740             :                 }
    8741             :                 void *pScaledData =
    8742         123 :                     GDALCreateScaledProgress(dfCurPixels / dfTotalPixels,
    8743             :                                              dfNextCurPixels / dfTotalPixels,
    8744             :                                              pfnProgress, pProgressData);
    8745             : 
    8746         123 :                 eErr = CopyImageryAndMask(poDstDS, poSrcOvrDS, poSrcMaskBand,
    8747             :                                           GDALScaledProgress, pScaledData);
    8748             : 
    8749         123 :                 dfCurPixels = dfNextCurPixels;
    8750         123 :                 GDALDestroyScaledProgress(pScaledData);
    8751             : 
    8752         123 :                 if (poSrcOvrDS != poOvrDS.get())
    8753          89 :                     delete poSrcOvrDS;
    8754         123 :                 poSrcOvrDS = nullptr;
    8755             :             }
    8756             :         }
    8757             :     }
    8758             : 
    8759             :     /* -------------------------------------------------------------------- */
    8760             :     /*      Copy actual imagery.                                            */
    8761             :     /* -------------------------------------------------------------------- */
    8762        2044 :     double dfNextCurPixels =
    8763        2044 :         dfCurPixels + static_cast<double>(nXSize) * nYSize * l_nBands;
    8764        2044 :     void *pScaledData = GDALCreateScaledProgress(
    8765             :         dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
    8766             :         pfnProgress, pProgressData);
    8767             : 
    8768             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8769        2044 :     bool bTryCopy = true;
    8770             : #endif
    8771             : 
    8772             : #ifdef HAVE_LIBJPEG
    8773        2044 :     if (bCopyFromJPEG)
    8774             :     {
    8775          12 :         eErr = GTIFF_CopyFromJPEG(poDS, poSrcDS, pfnProgress, pProgressData,
    8776             :                                   bTryCopy);
    8777             : 
    8778             :         // In case of failure in the decompression step, try normal copy.
    8779          12 :         if (bTryCopy)
    8780           0 :             eErr = CE_None;
    8781             :     }
    8782             : #endif
    8783             : 
    8784             : #ifdef JPEG_DIRECT_COPY
    8785             :     if (bDirectCopyFromJPEG)
    8786             :     {
    8787             :         eErr = GTIFF_DirectCopyFromJPEG(poDS, poSrcDS, pfnProgress,
    8788             :                                         pProgressData, bTryCopy);
    8789             : 
    8790             :         // In case of failure in the reading step, try normal copy.
    8791             :         if (bTryCopy)
    8792             :             eErr = CE_None;
    8793             :     }
    8794             : #endif
    8795             : 
    8796        2044 :     bool bWriteMask = true;
    8797        2044 :     if (
    8798             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8799        2032 :         bTryCopy &&
    8800             : #endif
    8801        2032 :         (poDS->m_bTreatAsSplit || poDS->m_bTreatAsSplitBitmap))
    8802             :     {
    8803             :         // For split bands, we use TIFFWriteScanline() interface.
    8804           9 :         CPLAssert(poDS->m_nBitsPerSample == 8 || poDS->m_nBitsPerSample == 1);
    8805             : 
    8806           9 :         if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG && poDS->nBands > 1)
    8807             :         {
    8808             :             GByte *pabyScanline = static_cast<GByte *>(
    8809           3 :                 VSI_MALLOC_VERBOSE(TIFFScanlineSize(l_hTIFF)));
    8810           3 :             if (pabyScanline == nullptr)
    8811           0 :                 eErr = CE_Failure;
    8812        9052 :             for (int j = 0; j < nYSize && eErr == CE_None; ++j)
    8813             :             {
    8814       18098 :                 eErr = poSrcDS->RasterIO(GF_Read, 0, j, nXSize, 1, pabyScanline,
    8815             :                                          nXSize, 1, GDT_Byte, l_nBands, nullptr,
    8816        9049 :                                          poDS->nBands, 0, 1, nullptr);
    8817       18098 :                 if (eErr == CE_None &&
    8818        9049 :                     TIFFWriteScanline(l_hTIFF, pabyScanline, j, 0) == -1)
    8819             :                 {
    8820           0 :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    8821             :                                 "TIFFWriteScanline() failed.");
    8822           0 :                     eErr = CE_Failure;
    8823             :                 }
    8824        9049 :                 if (!GDALScaledProgress((j + 1) * 1.0 / nYSize, nullptr,
    8825             :                                         pScaledData))
    8826           0 :                     eErr = CE_Failure;
    8827             :             }
    8828           3 :             CPLFree(pabyScanline);
    8829             :         }
    8830             :         else
    8831             :         {
    8832             :             GByte *pabyScanline =
    8833           6 :                 static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
    8834           6 :             if (pabyScanline == nullptr)
    8835           0 :                 eErr = CE_Failure;
    8836             :             else
    8837           6 :                 eErr = CE_None;
    8838          14 :             for (int iBand = 1; iBand <= l_nBands && eErr == CE_None; ++iBand)
    8839             :             {
    8840       48211 :                 for (int j = 0; j < nYSize && eErr == CE_None; ++j)
    8841             :                 {
    8842       48203 :                     eErr = poSrcDS->GetRasterBand(iBand)->RasterIO(
    8843             :                         GF_Read, 0, j, nXSize, 1, pabyScanline, nXSize, 1,
    8844             :                         GDT_Byte, 0, 0, nullptr);
    8845       48203 :                     if (poDS->m_bTreatAsSplitBitmap)
    8846             :                     {
    8847     7225210 :                         for (int i = 0; i < nXSize; ++i)
    8848             :                         {
    8849     7216010 :                             const GByte byVal = pabyScanline[i];
    8850     7216010 :                             if ((i & 0x7) == 0)
    8851      902001 :                                 pabyScanline[i >> 3] = 0;
    8852     7216010 :                             if (byVal)
    8853     7097220 :                                 pabyScanline[i >> 3] |= 0x80 >> (i & 0x7);
    8854             :                         }
    8855             :                     }
    8856       96406 :                     if (eErr == CE_None &&
    8857       48203 :                         TIFFWriteScanline(l_hTIFF, pabyScanline, j,
    8858       48203 :                                           static_cast<uint16_t>(iBand - 1)) ==
    8859             :                             -1)
    8860             :                     {
    8861           0 :                         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    8862             :                                     "TIFFWriteScanline() failed.");
    8863           0 :                         eErr = CE_Failure;
    8864             :                     }
    8865       48203 :                     if (!GDALScaledProgress((j + 1 + (iBand - 1) * nYSize) *
    8866       48203 :                                                 1.0 / (l_nBands * nYSize),
    8867             :                                             nullptr, pScaledData))
    8868           0 :                         eErr = CE_Failure;
    8869             :                 }
    8870             :             }
    8871           6 :             CPLFree(pabyScanline);
    8872             :         }
    8873             : 
    8874             :         // Necessary to be able to read the file without re-opening.
    8875           9 :         TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(l_hTIFF);
    8876             : 
    8877           9 :         TIFFFlushData(l_hTIFF);
    8878             : 
    8879           9 :         toff_t nNewDirOffset = pfnSizeProc(TIFFClientdata(l_hTIFF));
    8880           9 :         if ((nNewDirOffset % 2) == 1)
    8881           5 :             ++nNewDirOffset;
    8882             : 
    8883           9 :         TIFFFlush(l_hTIFF);
    8884             : 
    8885           9 :         if (poDS->m_nDirOffset != TIFFCurrentDirOffset(l_hTIFF))
    8886             :         {
    8887           0 :             poDS->m_nDirOffset = nNewDirOffset;
    8888           0 :             CPLDebug("GTiff", "directory moved during flush.");
    8889           9 :         }
    8890             :     }
    8891        2035 :     else if (
    8892             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8893        2023 :         bTryCopy &&
    8894             : #endif
    8895             :         eErr == CE_None)
    8896             :     {
    8897        2022 :         const char *papszCopyWholeRasterOptions[3] = {nullptr, nullptr,
    8898             :                                                       nullptr};
    8899        2022 :         int iNextOption = 0;
    8900        2022 :         papszCopyWholeRasterOptions[iNextOption++] = "SKIP_HOLES=YES";
    8901        2022 :         if (l_nCompression != COMPRESSION_NONE)
    8902             :         {
    8903         453 :             papszCopyWholeRasterOptions[iNextOption++] = "COMPRESSED=YES";
    8904             :         }
    8905             : 
    8906             :         // For streaming with separate, we really want that bands are written
    8907             :         // after each other, even if the source is pixel interleaved.
    8908        1569 :         else if (bStreaming && poDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE)
    8909             :         {
    8910           1 :             papszCopyWholeRasterOptions[iNextOption++] = "INTERLEAVE=BAND";
    8911             :         }
    8912             : 
    8913        2022 :         if (bCopySrcOverviews || bTileInterleaving)
    8914             :         {
    8915         184 :             poDS->m_bBlockOrderRowMajor = true;
    8916         184 :             poDS->m_bLeaderSizeAsUInt4 = bCopySrcOverviews;
    8917         184 :             poDS->m_bTrailerRepeatedLast4BytesRepeated = bCopySrcOverviews;
    8918         184 :             if (poDS->m_poMaskDS)
    8919             :             {
    8920          27 :                 poDS->m_poMaskDS->m_bBlockOrderRowMajor = true;
    8921          27 :                 poDS->m_poMaskDS->m_bLeaderSizeAsUInt4 = bCopySrcOverviews;
    8922          27 :                 poDS->m_poMaskDS->m_bTrailerRepeatedLast4BytesRepeated =
    8923             :                     bCopySrcOverviews;
    8924          27 :                 GDALDestroyScaledProgress(pScaledData);
    8925             :                 pScaledData =
    8926          27 :                     GDALCreateScaledProgress(dfCurPixels / dfTotalPixels, 1.0,
    8927             :                                              pfnProgress, pProgressData);
    8928             :             }
    8929             : 
    8930         184 :             eErr = CopyImageryAndMask(poDS, poSrcDS,
    8931         184 :                                       poSrcDS->GetRasterBand(1)->GetMaskBand(),
    8932             :                                       GDALScaledProgress, pScaledData);
    8933         184 :             if (poDS->m_poMaskDS)
    8934             :             {
    8935          27 :                 bWriteMask = false;
    8936             :             }
    8937             :         }
    8938             :         else
    8939             :         {
    8940        1838 :             eErr = GDALDatasetCopyWholeRaster(
    8941             :                 /* (GDALDatasetH) */ poSrcDS,
    8942             :                 /* (GDALDatasetH) */ poDS, papszCopyWholeRasterOptions,
    8943             :                 GDALScaledProgress, pScaledData);
    8944             :         }
    8945             :     }
    8946             : 
    8947        2044 :     GDALDestroyScaledProgress(pScaledData);
    8948             : 
    8949        2044 :     if (eErr == CE_None && !bStreaming && bWriteMask)
    8950             :     {
    8951        1995 :         pScaledData = GDALCreateScaledProgress(dfNextCurPixels / dfTotalPixels,
    8952             :                                                1.0, pfnProgress, pProgressData);
    8953        1995 :         if (poDS->m_poMaskDS)
    8954             :         {
    8955          10 :             const char *l_papszOptions[2] = {"COMPRESSED=YES", nullptr};
    8956          10 :             eErr = GDALRasterBandCopyWholeRaster(
    8957          10 :                 poSrcDS->GetRasterBand(1)->GetMaskBand(),
    8958          10 :                 poDS->GetRasterBand(1)->GetMaskBand(),
    8959             :                 const_cast<char **>(l_papszOptions), GDALScaledProgress,
    8960             :                 pScaledData);
    8961             :         }
    8962             :         else
    8963             :         {
    8964             :             eErr =
    8965        1985 :                 GDALDriver::DefaultCopyMasks(poSrcDS, poDS, bStrict, nullptr,
    8966             :                                              GDALScaledProgress, pScaledData);
    8967             :         }
    8968        1995 :         GDALDestroyScaledProgress(pScaledData);
    8969             :     }
    8970             : 
    8971        2044 :     poDS->m_bWriteCOGLayout = false;
    8972             : 
    8973        4070 :     if (eErr == CE_None &&
    8974        2026 :         CPLTestBool(CSLFetchNameValueDef(poDS->m_papszCreationOptions,
    8975             :                                          "@FLUSHCACHE", "NO")))
    8976             :     {
    8977         138 :         if (poDS->FlushCache(false) != CE_None)
    8978             :         {
    8979           0 :             eErr = CE_Failure;
    8980             :         }
    8981             :     }
    8982             : 
    8983        2044 :     if (eErr == CE_Failure)
    8984             :     {
    8985          18 :         if (CPLTestBool(CPLGetConfigOption("GTIFF_DELETE_ON_ERROR", "YES")))
    8986             :         {
    8987          17 :             l_fpL->CancelCreation();
    8988          17 :             delete poDS;
    8989          17 :             poDS = nullptr;
    8990             : 
    8991          17 :             if (!bStreaming)
    8992             :             {
    8993             :                 // Should really delete more carefully.
    8994          17 :                 VSIUnlink(pszFilename);
    8995             :             }
    8996             :         }
    8997             :         else
    8998             :         {
    8999           1 :             delete poDS;
    9000           1 :             poDS = nullptr;
    9001             :         }
    9002             :     }
    9003             : 
    9004        2044 :     return poDS;
    9005             : }
    9006             : 
    9007             : /************************************************************************/
    9008             : /*                           SetSpatialRef()                            */
    9009             : /************************************************************************/
    9010             : 
    9011        1405 : CPLErr GTiffDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    9012             : 
    9013             : {
    9014        1405 :     if (m_bStreamingOut && m_bCrystalized)
    9015             :     {
    9016           1 :         ReportError(CE_Failure, CPLE_NotSupported,
    9017             :                     "Cannot modify projection at that point in "
    9018             :                     "a streamed output file");
    9019           1 :         return CE_Failure;
    9020             :     }
    9021             : 
    9022        1404 :     LoadGeoreferencingAndPamIfNeeded();
    9023        1404 :     LookForProjection();
    9024             : 
    9025        1404 :     CPLErr eErr = CE_None;
    9026        1404 :     if (eAccess == GA_Update)
    9027             :     {
    9028        1406 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    9029           7 :             (GetPamFlags() & GPF_DISABLED) == 0)
    9030             :         {
    9031           7 :             eErr = GDALPamDataset::SetSpatialRef(poSRS);
    9032             :         }
    9033             :         else
    9034             :         {
    9035        1392 :             if (GDALPamDataset::GetSpatialRef() != nullptr)
    9036             :             {
    9037             :                 // Cancel any existing SRS from PAM file.
    9038           1 :                 GDALPamDataset::SetSpatialRef(nullptr);
    9039             :             }
    9040        1392 :             m_bGeoTIFFInfoChanged = true;
    9041             :         }
    9042             :     }
    9043             :     else
    9044             :     {
    9045           5 :         CPLDebug("GTIFF", "SetSpatialRef() goes to PAM instead of TIFF tags");
    9046           5 :         eErr = GDALPamDataset::SetSpatialRef(poSRS);
    9047             :     }
    9048             : 
    9049        1404 :     if (eErr == CE_None)
    9050             :     {
    9051        1404 :         if (poSRS == nullptr || poSRS->IsEmpty())
    9052             :         {
    9053          14 :             if (!m_oSRS.IsEmpty())
    9054             :             {
    9055           4 :                 m_bForceUnsetProjection = true;
    9056             :             }
    9057          14 :             m_oSRS.Clear();
    9058             :         }
    9059             :         else
    9060             :         {
    9061        1390 :             m_oSRS = *poSRS;
    9062        1390 :             m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    9063             :         }
    9064             :     }
    9065             : 
    9066        1404 :     return eErr;
    9067             : }
    9068             : 
    9069             : /************************************************************************/
    9070             : /*                          SetGeoTransform()                           */
    9071             : /************************************************************************/
    9072             : 
    9073        1697 : CPLErr GTiffDataset::SetGeoTransform(const GDALGeoTransform &gt)
    9074             : 
    9075             : {
    9076        1697 :     if (m_bStreamingOut && m_bCrystalized)
    9077             :     {
    9078           1 :         ReportError(CE_Failure, CPLE_NotSupported,
    9079             :                     "Cannot modify geotransform at that point in a "
    9080             :                     "streamed output file");
    9081           1 :         return CE_Failure;
    9082             :     }
    9083             : 
    9084        1696 :     LoadGeoreferencingAndPamIfNeeded();
    9085             : 
    9086        1696 :     CPLErr eErr = CE_None;
    9087        1696 :     if (eAccess == GA_Update)
    9088             :     {
    9089        1690 :         if (!m_aoGCPs.empty())
    9090             :         {
    9091           1 :             ReportError(CE_Warning, CPLE_AppDefined,
    9092             :                         "GCPs previously set are going to be cleared "
    9093             :                         "due to the setting of a geotransform.");
    9094           1 :             m_bForceUnsetGTOrGCPs = true;
    9095           1 :             m_aoGCPs.clear();
    9096             :         }
    9097        1892 :         else if (gt[0] == 0.0 && gt[1] == 0.0 && gt[2] == 0.0 && gt[3] == 0.0 &&
    9098        1892 :                  gt[4] == 0.0 && gt[5] == 0.0)
    9099             :         {
    9100           2 :             if (m_bGeoTransformValid)
    9101             :             {
    9102           2 :                 m_bForceUnsetGTOrGCPs = true;
    9103           2 :                 m_bGeoTIFFInfoChanged = true;
    9104             :             }
    9105           2 :             m_bGeoTransformValid = false;
    9106           2 :             m_gt = gt;
    9107           2 :             return CE_None;
    9108             :         }
    9109             : 
    9110        3385 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    9111           9 :             !CPLFetchBool(m_papszCreationOptions, "TFW", false) &&
    9112        1702 :             !CPLFetchBool(m_papszCreationOptions, "WORLDFILE", false) &&
    9113           5 :             (GetPamFlags() & GPF_DISABLED) == 0)
    9114             :         {
    9115           5 :             eErr = GDALPamDataset::SetGeoTransform(gt);
    9116             :         }
    9117             :         else
    9118             :         {
    9119             :             // Cancel any existing geotransform from PAM file.
    9120        1683 :             GDALPamDataset::DeleteGeoTransform();
    9121        1683 :             m_bGeoTIFFInfoChanged = true;
    9122             :         }
    9123             :     }
    9124             :     else
    9125             :     {
    9126           6 :         CPLDebug("GTIFF", "SetGeoTransform() goes to PAM instead of TIFF tags");
    9127           6 :         eErr = GDALPamDataset::SetGeoTransform(gt);
    9128             :     }
    9129             : 
    9130        1694 :     if (eErr == CE_None)
    9131             :     {
    9132        1694 :         m_gt = gt;
    9133        1694 :         m_bGeoTransformValid = true;
    9134             :     }
    9135             : 
    9136        1694 :     return eErr;
    9137             : }
    9138             : 
    9139             : /************************************************************************/
    9140             : /*                               SetGCPs()                              */
    9141             : /************************************************************************/
    9142             : 
    9143          23 : CPLErr GTiffDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
    9144             :                              const OGRSpatialReference *poGCPSRS)
    9145             : {
    9146          23 :     CPLErr eErr = CE_None;
    9147          23 :     LoadGeoreferencingAndPamIfNeeded();
    9148          23 :     LookForProjection();
    9149             : 
    9150          23 :     if (eAccess == GA_Update)
    9151             :     {
    9152          21 :         if (!m_aoGCPs.empty() && nGCPCountIn == 0)
    9153             :         {
    9154           3 :             m_bForceUnsetGTOrGCPs = true;
    9155             :         }
    9156          18 :         else if (nGCPCountIn > 0 && m_bGeoTransformValid)
    9157             :         {
    9158           5 :             ReportError(CE_Warning, CPLE_AppDefined,
    9159             :                         "A geotransform previously set is going to be cleared "
    9160             :                         "due to the setting of GCPs.");
    9161           5 :             m_gt = GDALGeoTransform();
    9162           5 :             m_bGeoTransformValid = false;
    9163           5 :             m_bForceUnsetGTOrGCPs = true;
    9164             :         }
    9165          21 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    9166           0 :             (GetPamFlags() & GPF_DISABLED) == 0)
    9167             :         {
    9168           0 :             eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn, poGCPSRS);
    9169             :         }
    9170             :         else
    9171             :         {
    9172          21 :             if (nGCPCountIn > knMAX_GCP_COUNT)
    9173             :             {
    9174           2 :                 if (GDALPamDataset::GetGCPCount() == 0 && !m_aoGCPs.empty())
    9175             :                 {
    9176           1 :                     m_bForceUnsetGTOrGCPs = true;
    9177             :                 }
    9178           2 :                 ReportError(CE_Warning, CPLE_AppDefined,
    9179             :                             "Trying to write %d GCPs, whereas the maximum "
    9180             :                             "supported in GeoTIFF tag is %d. "
    9181             :                             "Falling back to writing them to PAM",
    9182             :                             nGCPCountIn, knMAX_GCP_COUNT);
    9183           2 :                 eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn,
    9184             :                                                poGCPSRS);
    9185             :             }
    9186          19 :             else if (GDALPamDataset::GetGCPCount() > 0)
    9187             :             {
    9188             :                 // Cancel any existing GCPs from PAM file.
    9189           1 :                 GDALPamDataset::SetGCPs(
    9190             :                     0, nullptr,
    9191             :                     static_cast<const OGRSpatialReference *>(nullptr));
    9192             :             }
    9193          21 :             m_bGeoTIFFInfoChanged = true;
    9194             :         }
    9195             :     }
    9196             :     else
    9197             :     {
    9198           2 :         CPLDebug("GTIFF", "SetGCPs() goes to PAM instead of TIFF tags");
    9199           2 :         eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn, poGCPSRS);
    9200             :     }
    9201             : 
    9202          23 :     if (eErr == CE_None)
    9203             :     {
    9204          23 :         if (poGCPSRS == nullptr || poGCPSRS->IsEmpty())
    9205             :         {
    9206          12 :             if (!m_oSRS.IsEmpty())
    9207             :             {
    9208           5 :                 m_bForceUnsetProjection = true;
    9209             :             }
    9210          12 :             m_oSRS.Clear();
    9211             :         }
    9212             :         else
    9213             :         {
    9214          11 :             m_oSRS = *poGCPSRS;
    9215          11 :             m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    9216             :         }
    9217             : 
    9218          23 :         m_aoGCPs = gdal::GCP::fromC(pasGCPListIn, nGCPCountIn);
    9219             :     }
    9220             : 
    9221          23 :     return eErr;
    9222             : }
    9223             : 
    9224             : /************************************************************************/
    9225             : /*                            SetMetadata()                             */
    9226             : /************************************************************************/
    9227        2628 : CPLErr GTiffDataset::SetMetadata(char **papszMD, const char *pszDomain)
    9228             : 
    9229             : {
    9230        2628 :     LoadGeoreferencingAndPamIfNeeded();
    9231             : 
    9232        2628 :     if (m_bStreamingOut && m_bCrystalized)
    9233             :     {
    9234           1 :         ReportError(
    9235             :             CE_Failure, CPLE_NotSupported,
    9236             :             "Cannot modify metadata at that point in a streamed output file");
    9237           1 :         return CE_Failure;
    9238             :     }
    9239             : 
    9240        2627 :     CPLErr eErr = CE_None;
    9241        2627 :     if (eAccess == GA_Update)
    9242             :     {
    9243        2625 :         if (pszDomain != nullptr && EQUAL(pszDomain, MD_DOMAIN_RPC))
    9244             :         {
    9245             :             // So that a subsequent GetMetadata() wouldn't override our new
    9246             :             // values
    9247          22 :             LoadMetadata();
    9248          22 :             m_bForceUnsetRPC = (CSLCount(papszMD) == 0);
    9249             :         }
    9250             : 
    9251        2625 :         if ((papszMD != nullptr) && (pszDomain != nullptr) &&
    9252        1799 :             EQUAL(pszDomain, "COLOR_PROFILE"))
    9253             :         {
    9254           0 :             m_bColorProfileMetadataChanged = true;
    9255             :         }
    9256        2625 :         else if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
    9257             :         {
    9258        2625 :             m_bMetadataChanged = true;
    9259             :             // Cancel any existing metadata from PAM file.
    9260        2625 :             if (GDALPamDataset::GetMetadata(pszDomain) != nullptr)
    9261           1 :                 GDALPamDataset::SetMetadata(nullptr, pszDomain);
    9262             :         }
    9263             : 
    9264        5215 :         if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    9265        2590 :             CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT) != nullptr)
    9266             :         {
    9267        1961 :             const char *pszPrevValue = GetMetadataItem(GDALMD_AREA_OR_POINT);
    9268             :             const char *pszNewValue =
    9269        1961 :                 CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT);
    9270        1961 :             if (pszPrevValue == nullptr || pszNewValue == nullptr ||
    9271        1542 :                 !EQUAL(pszPrevValue, pszNewValue))
    9272             :             {
    9273         423 :                 LookForProjection();
    9274         423 :                 m_bGeoTIFFInfoChanged = true;
    9275             :             }
    9276             :         }
    9277             : 
    9278        2625 :         if (pszDomain != nullptr && EQUAL(pszDomain, "xml:XMP"))
    9279             :         {
    9280           2 :             if (papszMD != nullptr && *papszMD != nullptr)
    9281             :             {
    9282           1 :                 int nTagSize = static_cast<int>(strlen(*papszMD));
    9283           1 :                 TIFFSetField(m_hTIFF, TIFFTAG_XMLPACKET, nTagSize, *papszMD);
    9284             :             }
    9285             :             else
    9286             :             {
    9287           1 :                 TIFFUnsetField(m_hTIFF, TIFFTAG_XMLPACKET);
    9288             :             }
    9289             :         }
    9290             :     }
    9291             :     else
    9292             :     {
    9293           2 :         CPLDebug(
    9294             :             "GTIFF",
    9295             :             "GTiffDataset::SetMetadata() goes to PAM instead of TIFF tags");
    9296           2 :         eErr = GDALPamDataset::SetMetadata(papszMD, pszDomain);
    9297             :     }
    9298             : 
    9299        2627 :     if (eErr == CE_None)
    9300             :     {
    9301        2627 :         eErr = m_oGTiffMDMD.SetMetadata(papszMD, pszDomain);
    9302             :     }
    9303        2627 :     return eErr;
    9304             : }
    9305             : 
    9306             : /************************************************************************/
    9307             : /*                          SetMetadataItem()                           */
    9308             : /************************************************************************/
    9309             : 
    9310        5626 : CPLErr GTiffDataset::SetMetadataItem(const char *pszName, const char *pszValue,
    9311             :                                      const char *pszDomain)
    9312             : 
    9313             : {
    9314        5626 :     LoadGeoreferencingAndPamIfNeeded();
    9315             : 
    9316        5626 :     if (m_bStreamingOut && m_bCrystalized)
    9317             :     {
    9318           1 :         ReportError(
    9319             :             CE_Failure, CPLE_NotSupported,
    9320             :             "Cannot modify metadata at that point in a streamed output file");
    9321           1 :         return CE_Failure;
    9322             :     }
    9323             : 
    9324        5625 :     CPLErr eErr = CE_None;
    9325        5625 :     if (eAccess == GA_Update)
    9326             :     {
    9327        5618 :         if ((pszDomain != nullptr) && EQUAL(pszDomain, "COLOR_PROFILE"))
    9328             :         {
    9329           8 :             m_bColorProfileMetadataChanged = true;
    9330             :         }
    9331        5610 :         else if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
    9332             :         {
    9333        5610 :             m_bMetadataChanged = true;
    9334             :             // Cancel any existing metadata from PAM file.
    9335        5610 :             if (GDALPamDataset::GetMetadataItem(pszName, pszDomain) != nullptr)
    9336           1 :                 GDALPamDataset::SetMetadataItem(pszName, nullptr, pszDomain);
    9337             :         }
    9338             : 
    9339        5618 :         if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    9340          71 :             pszName != nullptr && EQUAL(pszName, GDALMD_AREA_OR_POINT))
    9341             :         {
    9342           7 :             LookForProjection();
    9343           7 :             m_bGeoTIFFInfoChanged = true;
    9344             :         }
    9345             :     }
    9346             :     else
    9347             :     {
    9348           7 :         CPLDebug(
    9349             :             "GTIFF",
    9350             :             "GTiffDataset::SetMetadataItem() goes to PAM instead of TIFF tags");
    9351           7 :         eErr = GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    9352             :     }
    9353             : 
    9354        5625 :     if (eErr == CE_None)
    9355             :     {
    9356        5625 :         eErr = m_oGTiffMDMD.SetMetadataItem(pszName, pszValue, pszDomain);
    9357             :     }
    9358             : 
    9359        5625 :     return eErr;
    9360             : }
    9361             : 
    9362             : /************************************************************************/
    9363             : /*                         CreateMaskBand()                             */
    9364             : /************************************************************************/
    9365             : 
    9366          98 : CPLErr GTiffDataset::CreateMaskBand(int nFlagsIn)
    9367             : {
    9368          98 :     ScanDirectories();
    9369             : 
    9370          98 :     if (m_poMaskDS != nullptr)
    9371             :     {
    9372           1 :         ReportError(CE_Failure, CPLE_AppDefined,
    9373             :                     "This TIFF dataset has already an internal mask band");
    9374           1 :         return CE_Failure;
    9375             :     }
    9376          97 :     else if (MustCreateInternalMask())
    9377             :     {
    9378          84 :         if (nFlagsIn != GMF_PER_DATASET)
    9379             :         {
    9380           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    9381             :                         "The only flag value supported for internal mask is "
    9382             :                         "GMF_PER_DATASET");
    9383           1 :             return CE_Failure;
    9384             :         }
    9385             : 
    9386          83 :         int l_nCompression = COMPRESSION_PACKBITS;
    9387          83 :         if (strstr(GDALGetMetadataItem(GDALGetDriverByName("GTiff"),
    9388             :                                        GDAL_DMD_CREATIONOPTIONLIST, nullptr),
    9389          83 :                    "<Value>DEFLATE</Value>") != nullptr)
    9390          83 :             l_nCompression = COMPRESSION_ADOBE_DEFLATE;
    9391             : 
    9392             :         /* --------------------------------------------------------------------
    9393             :          */
    9394             :         /*      If we don't have read access, then create the mask externally.
    9395             :          */
    9396             :         /* --------------------------------------------------------------------
    9397             :          */
    9398          83 :         if (GetAccess() != GA_Update)
    9399             :         {
    9400           1 :             ReportError(CE_Warning, CPLE_AppDefined,
    9401             :                         "File open for read-only accessing, "
    9402             :                         "creating mask externally.");
    9403             : 
    9404           1 :             return GDALPamDataset::CreateMaskBand(nFlagsIn);
    9405             :         }
    9406             : 
    9407          82 :         if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    9408           0 :             !m_bWriteKnownIncompatibleEdition)
    9409             :         {
    9410           0 :             ReportError(CE_Warning, CPLE_AppDefined,
    9411             :                         "Adding a mask invalidates the "
    9412             :                         "LAYOUT=IFDS_BEFORE_DATA property");
    9413           0 :             m_bKnownIncompatibleEdition = true;
    9414           0 :             m_bWriteKnownIncompatibleEdition = true;
    9415             :         }
    9416             : 
    9417          82 :         bool bIsOverview = false;
    9418          82 :         uint32_t nSubType = 0;
    9419          82 :         if (TIFFGetField(m_hTIFF, TIFFTAG_SUBFILETYPE, &nSubType))
    9420             :         {
    9421           8 :             bIsOverview = (nSubType & FILETYPE_REDUCEDIMAGE) != 0;
    9422             : 
    9423           8 :             if ((nSubType & FILETYPE_MASK) != 0)
    9424             :             {
    9425           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
    9426             :                             "Cannot create a mask on a TIFF mask IFD !");
    9427           0 :                 return CE_Failure;
    9428             :             }
    9429             :         }
    9430             : 
    9431          82 :         const int bIsTiled = TIFFIsTiled(m_hTIFF);
    9432             : 
    9433          82 :         FlushDirectory();
    9434             : 
    9435          82 :         const toff_t nOffset = GTIFFWriteDirectory(
    9436             :             m_hTIFF,
    9437             :             bIsOverview ? FILETYPE_REDUCEDIMAGE | FILETYPE_MASK : FILETYPE_MASK,
    9438             :             nRasterXSize, nRasterYSize, 1, PLANARCONFIG_CONTIG, 1,
    9439             :             m_nBlockXSize, m_nBlockYSize, bIsTiled, l_nCompression,
    9440             :             PHOTOMETRIC_MASK, PREDICTOR_NONE, SAMPLEFORMAT_UINT, nullptr,
    9441             :             nullptr, nullptr, 0, nullptr, "", nullptr, nullptr, nullptr,
    9442          82 :             nullptr, m_bWriteCOGLayout);
    9443             : 
    9444          82 :         ReloadDirectory();
    9445             : 
    9446          82 :         if (nOffset == 0)
    9447           0 :             return CE_Failure;
    9448             : 
    9449          82 :         m_poMaskDS = new GTiffDataset();
    9450          82 :         m_poMaskDS->eAccess = GA_Update;
    9451          82 :         m_poMaskDS->m_poBaseDS = this;
    9452          82 :         m_poMaskDS->m_poImageryDS = this;
    9453          82 :         m_poMaskDS->ShareLockWithParentDataset(this);
    9454          82 :         m_poMaskDS->m_osFilename = m_osFilename;
    9455          82 :         m_poMaskDS->m_bPromoteTo8Bits = CPLTestBool(
    9456             :             CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
    9457          82 :         if (m_poMaskDS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nOffset,
    9458          82 :                                    GA_Update) != CE_None)
    9459             :         {
    9460           0 :             delete m_poMaskDS;
    9461           0 :             m_poMaskDS = nullptr;
    9462           0 :             return CE_Failure;
    9463             :         }
    9464             : 
    9465          82 :         return CE_None;
    9466             :     }
    9467             : 
    9468          13 :     return GDALPamDataset::CreateMaskBand(nFlagsIn);
    9469             : }
    9470             : 
    9471             : /************************************************************************/
    9472             : /*                        MustCreateInternalMask()                      */
    9473             : /************************************************************************/
    9474             : 
    9475         135 : bool GTiffDataset::MustCreateInternalMask()
    9476             : {
    9477         135 :     return CPLTestBool(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", "YES"));
    9478             : }
    9479             : 
    9480             : /************************************************************************/
    9481             : /*                         CreateMaskBand()                             */
    9482             : /************************************************************************/
    9483             : 
    9484          29 : CPLErr GTiffRasterBand::CreateMaskBand(int nFlagsIn)
    9485             : {
    9486          29 :     m_poGDS->ScanDirectories();
    9487             : 
    9488          29 :     if (m_poGDS->m_poMaskDS != nullptr)
    9489             :     {
    9490           5 :         ReportError(CE_Failure, CPLE_AppDefined,
    9491             :                     "This TIFF dataset has already an internal mask band");
    9492           5 :         return CE_Failure;
    9493             :     }
    9494             : 
    9495             :     const char *pszGDAL_TIFF_INTERNAL_MASK =
    9496          24 :         CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", nullptr);
    9497          27 :     if ((pszGDAL_TIFF_INTERNAL_MASK &&
    9498          24 :          CPLTestBool(pszGDAL_TIFF_INTERNAL_MASK)) ||
    9499             :         nFlagsIn == GMF_PER_DATASET)
    9500             :     {
    9501          16 :         return m_poGDS->CreateMaskBand(nFlagsIn);
    9502             :     }
    9503             : 
    9504           8 :     return GDALPamRasterBand::CreateMaskBand(nFlagsIn);
    9505             : }
    9506             : 
    9507             : /************************************************************************/
    9508             : /*                          ClampCTEntry()                              */
    9509             : /************************************************************************/
    9510             : 
    9511      232815 : /* static */ unsigned short GTiffDataset::ClampCTEntry(int iColor, int iComp,
    9512             :                                                        int nCTEntryVal,
    9513             :                                                        int nMultFactor)
    9514             : {
    9515      232815 :     const int nVal = nCTEntryVal * nMultFactor;
    9516      232815 :     if (nVal < 0)
    9517             :     {
    9518           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    9519             :                  "Color table entry [%d][%d] = %d, clamped to 0", iColor, iComp,
    9520             :                  nCTEntryVal);
    9521           0 :         return 0;
    9522             :     }
    9523      232815 :     if (nVal > 65535)
    9524             :     {
    9525           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    9526             :                  "Color table entry [%d][%d] = %d, clamped to 65535", iColor,
    9527             :                  iComp, nCTEntryVal);
    9528           2 :         return 65535;
    9529             :     }
    9530      232813 :     return static_cast<unsigned short>(nVal);
    9531             : }

Generated by: LCOV version 1.14