LCOV - code coverage report
Current view: top level - frmts/gtiff - gtiffdataset_write.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3890 4249 91.6 %
Date: 2025-12-24 19:12:58 Functions: 110 139 79.1 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GeoTIFF Driver
       4             :  * Purpose:  Write/set operations on GTiffDataset
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1998, 2002, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2007-2015, Even Rouault <even dot rouault at spatialys dot com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gtiffdataset.h"
      15             : #include "gtiffrasterband.h"
      16             : #include "gtiffoddbitsband.h"
      17             : #include "gtiffjpegoverviewds.h"
      18             : 
      19             : #include <cassert>
      20             : #include <cerrno>
      21             : 
      22             : #include <algorithm>
      23             : #include <cmath>
      24             : #include <limits>
      25             : #include <memory>
      26             : #include <mutex>
      27             : #include <set>
      28             : #include <string>
      29             : #include <tuple>
      30             : #include <utility>
      31             : 
      32             : #include "cpl_error.h"
      33             : #include "cpl_error_internal.h"  // CPLErrorHandlerAccumulatorStruct
      34             : #include "cpl_float.h"
      35             : #include "cpl_md5.h"
      36             : #include "cpl_vsi.h"
      37             : #include "cpl_vsi_virtual.h"
      38             : #include "cpl_worker_thread_pool.h"
      39             : #include "fetchbufferdirectio.h"
      40             : #include "gdal_mdreader.h"  // GDALWriteRPCTXTFile()
      41             : #include "gdal_priv.h"
      42             : #include "gdal_priv_templates.hpp"  // GDALIsValueInRange<>
      43             : #include "gdal_thread_pool.h"       // GDALGetGlobalThreadPool()
      44             : #include "geovalues.h"              // RasterPixelIsPoint
      45             : #include "gt_jpeg_copy.h"
      46             : #include "gt_overview.h"  // GTIFFBuildOverviewMetadata()
      47             : #include "quant_table_md5sum.h"
      48             : #include "quant_table_md5sum_jpeg9e.h"
      49             : #include "tif_jxl.h"
      50             : #include "tifvsi.h"
      51             : #include "xtiffio.h"
      52             : 
      53             : #if LIFFLIB_VERSION > 20230908 || defined(INTERNAL_LIBTIFF)
      54             : /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
      55             : #define LIBTIFF_MULTIBAND_LERC_NAN_OK
      56             : #endif
      57             : 
      58             : static const int knGTIFFJpegTablesModeDefault = JPEGTABLESMODE_QUANT;
      59             : 
      60             : static constexpr const char szPROFILE_BASELINE[] = "BASELINE";
      61             : static constexpr const char szPROFILE_GeoTIFF[] = "GeoTIFF";
      62             : static constexpr const char szPROFILE_GDALGeoTIFF[] = "GDALGeoTIFF";
      63             : 
      64             : // Due to libgeotiff/xtiff.c declaring TIFFTAG_GEOTIEPOINTS with field_readcount
      65             : // and field_writecount == -1 == TIFF_VARIABLE, we are limited to writing
      66             : // 65535 values in that tag. That could potentially be overcome by changing the tag
      67             : // declaration to using TIFF_VARIABLE2 where the count is a uint32_t.
      68             : constexpr int knMAX_GCP_COUNT =
      69             :     static_cast<int>(std::numeric_limits<uint16_t>::max() / 6);
      70             : 
      71             : enum
      72             : {
      73             :     ENDIANNESS_NATIVE,
      74             :     ENDIANNESS_LITTLE,
      75             :     ENDIANNESS_BIG
      76             : };
      77             : 
      78       17462 : static signed char GTiffGetWebPLevel(CSLConstList papszOptions)
      79             : {
      80       17462 :     int nWebPLevel = DEFAULT_WEBP_LEVEL;
      81       17462 :     const char *pszValue = CSLFetchNameValue(papszOptions, "WEBP_LEVEL");
      82       17462 :     if (pszValue != nullptr)
      83             :     {
      84          51 :         nWebPLevel = atoi(pszValue);
      85          51 :         if (!(nWebPLevel >= 1 && nWebPLevel <= 100))
      86             :         {
      87           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
      88             :                      "WEBP_LEVEL=%s value not recognised, ignoring.", pszValue);
      89           0 :             nWebPLevel = DEFAULT_WEBP_LEVEL;
      90             :         }
      91             :     }
      92       17462 :     return static_cast<signed char>(nWebPLevel);
      93             : }
      94             : 
      95       17468 : static bool GTiffGetWebPLossless(CSLConstList papszOptions)
      96             : {
      97       17468 :     return CPLFetchBool(papszOptions, "WEBP_LOSSLESS", false);
      98             : }
      99             : 
     100       17534 : static double GTiffGetLERCMaxZError(CSLConstList papszOptions)
     101             : {
     102       17534 :     return CPLAtof(CSLFetchNameValueDef(papszOptions, "MAX_Z_ERROR", "0.0"));
     103             : }
     104             : 
     105        7804 : static double GTiffGetLERCMaxZErrorOverview(CSLConstList papszOptions)
     106             : {
     107        7804 :     return CPLAtof(CSLFetchNameValueDef(
     108             :         papszOptions, "MAX_Z_ERROR_OVERVIEW",
     109        7804 :         CSLFetchNameValueDef(papszOptions, "MAX_Z_ERROR", "0.0")));
     110             : }
     111             : 
     112             : #if HAVE_JXL
     113       17538 : static bool GTiffGetJXLLossless(CSLConstList papszOptions,
     114             :                                 bool *pbIsSpecified = nullptr)
     115             : {
     116       17538 :     const char *pszVal = CSLFetchNameValue(papszOptions, "JXL_LOSSLESS");
     117       17538 :     if (pbIsSpecified)
     118        9730 :         *pbIsSpecified = pszVal != nullptr;
     119       17538 :     return pszVal == nullptr || CPLTestBool(pszVal);
     120             : }
     121             : 
     122       17538 : static uint32_t GTiffGetJXLEffort(CSLConstList papszOptions)
     123             : {
     124       17538 :     return atoi(CSLFetchNameValueDef(papszOptions, "JXL_EFFORT", "5"));
     125             : }
     126             : 
     127       17456 : static float GTiffGetJXLDistance(CSLConstList papszOptions,
     128             :                                  bool *pbIsSpecified = nullptr)
     129             : {
     130       17456 :     const char *pszVal = CSLFetchNameValue(papszOptions, "JXL_DISTANCE");
     131       17456 :     if (pbIsSpecified)
     132        9730 :         *pbIsSpecified = pszVal != nullptr;
     133       17456 :     return pszVal == nullptr ? 1.0f : static_cast<float>(CPLAtof(pszVal));
     134             : }
     135             : 
     136       17538 : static float GTiffGetJXLAlphaDistance(CSLConstList papszOptions,
     137             :                                       bool *pbIsSpecified = nullptr)
     138             : {
     139       17538 :     const char *pszVal = CSLFetchNameValue(papszOptions, "JXL_ALPHA_DISTANCE");
     140       17538 :     if (pbIsSpecified)
     141        9730 :         *pbIsSpecified = pszVal != nullptr;
     142       17538 :     return pszVal == nullptr ? -1.0f : static_cast<float>(CPLAtof(pszVal));
     143             : }
     144             : 
     145             : #endif
     146             : 
     147             : /************************************************************************/
     148             : /*                           FillEmptyTiles()                           */
     149             : /************************************************************************/
     150             : 
     151        7986 : CPLErr GTiffDataset::FillEmptyTiles()
     152             : 
     153             : {
     154             :     /* -------------------------------------------------------------------- */
     155             :     /*      How many blocks are there in this file?                         */
     156             :     /* -------------------------------------------------------------------- */
     157       15972 :     const int nBlockCount = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     158        7986 :                                 ? m_nBlocksPerBand * nBands
     159             :                                 : m_nBlocksPerBand;
     160             : 
     161             :     /* -------------------------------------------------------------------- */
     162             :     /*      Fetch block maps.                                               */
     163             :     /* -------------------------------------------------------------------- */
     164        7986 :     toff_t *panByteCounts = nullptr;
     165             : 
     166        7986 :     if (TIFFIsTiled(m_hTIFF))
     167        1119 :         TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts);
     168             :     else
     169        6867 :         TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
     170             : 
     171        7986 :     if (panByteCounts == nullptr)
     172             :     {
     173             :         // Got here with libtiff 3.9.3 and tiff_write_8 test.
     174           0 :         ReportError(CE_Failure, CPLE_AppDefined,
     175             :                     "FillEmptyTiles() failed because panByteCounts == NULL");
     176           0 :         return CE_Failure;
     177             :     }
     178             : 
     179             :     /* -------------------------------------------------------------------- */
     180             :     /*      Prepare a blank data buffer to write for uninitialized blocks.  */
     181             :     /* -------------------------------------------------------------------- */
     182             :     const GPtrDiff_t nBlockBytes =
     183        7986 :         TIFFIsTiled(m_hTIFF) ? static_cast<GPtrDiff_t>(TIFFTileSize(m_hTIFF))
     184        6867 :                              : static_cast<GPtrDiff_t>(TIFFStripSize(m_hTIFF));
     185             : 
     186        7986 :     GByte *pabyData = static_cast<GByte *>(VSI_CALLOC_VERBOSE(nBlockBytes, 1));
     187        7986 :     if (pabyData == nullptr)
     188             :     {
     189           0 :         return CE_Failure;
     190             :     }
     191             : 
     192             :     // Force tiles completely filled with the nodata value to be written.
     193        7986 :     m_bWriteEmptyTiles = true;
     194             : 
     195             :     /* -------------------------------------------------------------------- */
     196             :     /*      If set, fill data buffer with no data value.                    */
     197             :     /* -------------------------------------------------------------------- */
     198        7986 :     if ((m_bNoDataSet && m_dfNoDataValue != 0.0) ||
     199        7739 :         (m_bNoDataSetAsInt64 && m_nNoDataValueInt64 != 0) ||
     200        7736 :         (m_bNoDataSetAsUInt64 && m_nNoDataValueUInt64 != 0))
     201             :     {
     202         253 :         const GDALDataType eDataType = GetRasterBand(1)->GetRasterDataType();
     203         253 :         const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
     204         253 :         if (nDataTypeSize &&
     205         253 :             nDataTypeSize * 8 == static_cast<int>(m_nBitsPerSample))
     206             :         {
     207         242 :             if (m_bNoDataSetAsInt64)
     208             :             {
     209           4 :                 GDALCopyWords64(&m_nNoDataValueInt64, GDT_Int64, 0, pabyData,
     210             :                                 eDataType, nDataTypeSize,
     211           4 :                                 nBlockBytes / nDataTypeSize);
     212             :             }
     213         238 :             else if (m_bNoDataSetAsUInt64)
     214             :             {
     215           3 :                 GDALCopyWords64(&m_nNoDataValueUInt64, GDT_UInt64, 0, pabyData,
     216             :                                 eDataType, nDataTypeSize,
     217           3 :                                 nBlockBytes / nDataTypeSize);
     218             :             }
     219             :             else
     220             :             {
     221         235 :                 double dfNoData = m_dfNoDataValue;
     222         235 :                 GDALCopyWords64(&dfNoData, GDT_Float64, 0, pabyData, eDataType,
     223         235 :                                 nDataTypeSize, nBlockBytes / nDataTypeSize);
     224         242 :             }
     225             :         }
     226          11 :         else if (nDataTypeSize)
     227             :         {
     228             :             // Handle non power-of-two depths.
     229             :             // Ideally make a packed buffer, but that is a bit tedious,
     230             :             // so use the normal I/O interfaces.
     231             : 
     232          11 :             CPLFree(pabyData);
     233             : 
     234          11 :             pabyData = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
     235             :                 m_nBlockXSize, m_nBlockYSize, nDataTypeSize));
     236          11 :             if (pabyData == nullptr)
     237           0 :                 return CE_Failure;
     238          11 :             if (m_bNoDataSetAsInt64)
     239             :             {
     240           0 :                 GDALCopyWords64(&m_nNoDataValueInt64, GDT_Int64, 0, pabyData,
     241             :                                 eDataType, nDataTypeSize,
     242           0 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     243           0 :                                     m_nBlockYSize);
     244             :             }
     245          11 :             else if (m_bNoDataSetAsUInt64)
     246             :             {
     247           0 :                 GDALCopyWords64(&m_nNoDataValueUInt64, GDT_UInt64, 0, pabyData,
     248             :                                 eDataType, nDataTypeSize,
     249           0 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     250           0 :                                     m_nBlockYSize);
     251             :             }
     252             :             else
     253             :             {
     254          11 :                 GDALCopyWords64(&m_dfNoDataValue, GDT_Float64, 0, pabyData,
     255             :                                 eDataType, nDataTypeSize,
     256          11 :                                 static_cast<GPtrDiff_t>(m_nBlockXSize) *
     257          11 :                                     m_nBlockYSize);
     258             :             }
     259          11 :             CPLErr eErr = CE_None;
     260          46 :             for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     261             :             {
     262          35 :                 if (panByteCounts[iBlock] == 0)
     263             :                 {
     264          18 :                     if (m_nPlanarConfig == PLANARCONFIG_SEPARATE || nBands == 1)
     265             :                     {
     266          24 :                         if (GetRasterBand(1 + iBlock / m_nBlocksPerBand)
     267          12 :                                 ->WriteBlock((iBlock % m_nBlocksPerBand) %
     268          12 :                                                  m_nBlocksPerRow,
     269          12 :                                              (iBlock % m_nBlocksPerBand) /
     270          12 :                                                  m_nBlocksPerRow,
     271          12 :                                              pabyData) != CE_None)
     272             :                         {
     273           0 :                             eErr = CE_Failure;
     274             :                         }
     275             :                     }
     276             :                     else
     277             :                     {
     278             :                         // In contig case, don't directly call WriteBlock(), as
     279             :                         // it could cause useless decompression-recompression.
     280           6 :                         const int nXOff =
     281           6 :                             (iBlock % m_nBlocksPerRow) * m_nBlockXSize;
     282           6 :                         const int nYOff =
     283           6 :                             (iBlock / m_nBlocksPerRow) * m_nBlockYSize;
     284           6 :                         const int nXSize =
     285           6 :                             (nXOff + m_nBlockXSize <= nRasterXSize)
     286           6 :                                 ? m_nBlockXSize
     287           2 :                                 : nRasterXSize - nXOff;
     288           6 :                         const int nYSize =
     289           6 :                             (nYOff + m_nBlockYSize <= nRasterYSize)
     290           6 :                                 ? m_nBlockYSize
     291           3 :                                 : nRasterYSize - nYOff;
     292          18 :                         for (int iBand = 1; iBand <= nBands; ++iBand)
     293             :                         {
     294          12 :                             if (GetRasterBand(iBand)->RasterIO(
     295             :                                     GF_Write, nXOff, nYOff, nXSize, nYSize,
     296             :                                     pabyData, nXSize, nYSize, eDataType, 0, 0,
     297          12 :                                     nullptr) != CE_None)
     298             :                             {
     299           0 :                                 eErr = CE_Failure;
     300             :                             }
     301             :                         }
     302             :                     }
     303             :                 }
     304             :             }
     305          11 :             CPLFree(pabyData);
     306          11 :             return eErr;
     307         242 :         }
     308             :     }
     309             : 
     310             :     /* -------------------------------------------------------------------- */
     311             :     /*      When we must fill with zeroes, try to create non-sparse file    */
     312             :     /*      w.r.t TIFF spec ... as a sparse file w.r.t filesystem, ie by    */
     313             :     /*      seeking to end of file instead of writing zero blocks.          */
     314             :     /* -------------------------------------------------------------------- */
     315        7733 :     else if (m_nCompression == COMPRESSION_NONE && (m_nBitsPerSample % 8) == 0)
     316             :     {
     317        6212 :         CPLErr eErr = CE_None;
     318             :         // Only use libtiff to write the first sparse block to ensure that it
     319             :         // will serialize offset and count arrays back to disk.
     320        6212 :         int nCountBlocksToZero = 0;
     321     2320400 :         for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     322             :         {
     323     2314180 :             if (panByteCounts[iBlock] == 0)
     324             :             {
     325     2219430 :                 if (nCountBlocksToZero == 0)
     326             :                 {
     327        1093 :                     const bool bWriteEmptyTilesBak = m_bWriteEmptyTiles;
     328        1093 :                     m_bWriteEmptyTiles = true;
     329        1093 :                     const bool bOK = WriteEncodedTileOrStrip(iBlock, pabyData,
     330        1093 :                                                              FALSE) == CE_None;
     331        1093 :                     m_bWriteEmptyTiles = bWriteEmptyTilesBak;
     332        1093 :                     if (!bOK)
     333             :                     {
     334           2 :                         eErr = CE_Failure;
     335           2 :                         break;
     336             :                     }
     337             :                 }
     338     2219420 :                 nCountBlocksToZero++;
     339             :             }
     340             :         }
     341        6212 :         CPLFree(pabyData);
     342             : 
     343        6212 :         --nCountBlocksToZero;
     344             : 
     345             :         // And then seek to end of file for other ones.
     346        6212 :         if (nCountBlocksToZero > 0)
     347             :         {
     348         333 :             toff_t *panByteOffsets = nullptr;
     349             : 
     350         333 :             if (TIFFIsTiled(m_hTIFF))
     351          92 :                 TIFFGetField(m_hTIFF, TIFFTAG_TILEOFFSETS, &panByteOffsets);
     352             :             else
     353         241 :                 TIFFGetField(m_hTIFF, TIFFTAG_STRIPOFFSETS, &panByteOffsets);
     354             : 
     355         333 :             if (panByteOffsets == nullptr)
     356             :             {
     357           0 :                 ReportError(
     358             :                     CE_Failure, CPLE_AppDefined,
     359             :                     "FillEmptyTiles() failed because panByteOffsets == NULL");
     360           0 :                 return CE_Failure;
     361             :             }
     362             : 
     363         333 :             VSILFILE *fpTIF = VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
     364         333 :             VSIFSeekL(fpTIF, 0, SEEK_END);
     365         333 :             const vsi_l_offset nOffset = VSIFTellL(fpTIF);
     366             : 
     367         333 :             vsi_l_offset iBlockToZero = 0;
     368     2227530 :             for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     369             :             {
     370     2227200 :                 if (panByteCounts[iBlock] == 0)
     371             :                 {
     372     2218330 :                     panByteOffsets[iBlock] = static_cast<toff_t>(
     373     2218330 :                         nOffset + iBlockToZero * nBlockBytes);
     374     2218330 :                     panByteCounts[iBlock] = nBlockBytes;
     375     2218330 :                     iBlockToZero++;
     376             :                 }
     377             :             }
     378         333 :             CPLAssert(iBlockToZero ==
     379             :                       static_cast<vsi_l_offset>(nCountBlocksToZero));
     380             : 
     381         333 :             if (VSIFTruncateL(fpTIF, nOffset + iBlockToZero * nBlockBytes) != 0)
     382             :             {
     383           0 :                 eErr = CE_Failure;
     384           0 :                 ReportError(CE_Failure, CPLE_FileIO,
     385             :                             "Cannot initialize empty blocks");
     386             :             }
     387             :         }
     388             : 
     389        6212 :         return eErr;
     390             :     }
     391             : 
     392             :     /* -------------------------------------------------------------------- */
     393             :     /*      Check all blocks, writing out data for uninitialized blocks.    */
     394             :     /* -------------------------------------------------------------------- */
     395             : 
     396        1763 :     GByte *pabyRaw = nullptr;
     397        1763 :     vsi_l_offset nRawSize = 0;
     398        1763 :     CPLErr eErr = CE_None;
     399       56159 :     for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
     400             :     {
     401       54403 :         if (panByteCounts[iBlock] == 0)
     402             :         {
     403       17444 :             if (pabyRaw == nullptr)
     404             :             {
     405       10141 :                 if (WriteEncodedTileOrStrip(iBlock, pabyData, FALSE) != CE_None)
     406             :                 {
     407           7 :                     eErr = CE_Failure;
     408           7 :                     break;
     409             :                 }
     410             : 
     411       10134 :                 vsi_l_offset nOffset = 0;
     412       10134 :                 if (!IsBlockAvailable(iBlock, &nOffset, &nRawSize, nullptr))
     413           0 :                     break;
     414             : 
     415             :                 // When using compression, get back the compressed block
     416             :                 // so we can use the raw API to write it faster.
     417       10134 :                 if (m_nCompression != COMPRESSION_NONE)
     418             :                 {
     419             :                     pabyRaw = static_cast<GByte *>(
     420         460 :                         VSI_MALLOC_VERBOSE(static_cast<size_t>(nRawSize)));
     421         460 :                     if (pabyRaw)
     422             :                     {
     423             :                         VSILFILE *fp =
     424         460 :                             VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
     425         460 :                         const vsi_l_offset nCurOffset = VSIFTellL(fp);
     426         460 :                         VSIFSeekL(fp, nOffset, SEEK_SET);
     427         460 :                         VSIFReadL(pabyRaw, 1, static_cast<size_t>(nRawSize),
     428             :                                   fp);
     429         460 :                         VSIFSeekL(fp, nCurOffset, SEEK_SET);
     430             :                     }
     431             :                 }
     432             :             }
     433             :             else
     434             :             {
     435        7303 :                 WriteRawStripOrTile(iBlock, pabyRaw,
     436             :                                     static_cast<GPtrDiff_t>(nRawSize));
     437             :             }
     438             :         }
     439             :     }
     440             : 
     441        1763 :     CPLFree(pabyData);
     442        1763 :     VSIFree(pabyRaw);
     443        1763 :     return eErr;
     444             : }
     445             : 
     446             : /************************************************************************/
     447             : /*                         HasOnlyNoData()                              */
     448             : /************************************************************************/
     449             : 
     450       42683 : bool GTiffDataset::HasOnlyNoData(const void *pBuffer, int nWidth, int nHeight,
     451             :                                  int nLineStride, int nComponents)
     452             : {
     453       42683 :     if (m_nSampleFormat == SAMPLEFORMAT_COMPLEXINT ||
     454       42683 :         m_nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP)
     455           0 :         return false;
     456       42683 :     if (m_bNoDataSetAsInt64 || m_bNoDataSetAsUInt64)
     457           2 :         return false;  // FIXME: over pessimistic
     458       85362 :     return GDALBufferHasOnlyNoData(
     459       42681 :         pBuffer, m_bNoDataSet ? m_dfNoDataValue : 0.0, nWidth, nHeight,
     460       42681 :         nLineStride, nComponents, m_nBitsPerSample,
     461       42681 :         m_nSampleFormat == SAMPLEFORMAT_UINT  ? GSF_UNSIGNED_INT
     462        4753 :         : m_nSampleFormat == SAMPLEFORMAT_INT ? GSF_SIGNED_INT
     463       42681 :                                               : GSF_FLOATING_POINT);
     464             : }
     465             : 
     466             : /************************************************************************/
     467             : /*                     IsFirstPixelEqualToNoData()                      */
     468             : /************************************************************************/
     469             : 
     470      168508 : inline bool GTiffDataset::IsFirstPixelEqualToNoData(const void *pBuffer)
     471             : {
     472      168508 :     const GDALDataType eDT = GetRasterBand(1)->GetRasterDataType();
     473      168508 :     const double dfEffectiveNoData = (m_bNoDataSet) ? m_dfNoDataValue : 0.0;
     474      168508 :     if (m_bNoDataSetAsInt64 || m_bNoDataSetAsUInt64)
     475           2 :         return true;  // FIXME: over pessimistic
     476      168506 :     if (m_nBitsPerSample == 8 ||
     477       58570 :         (m_nBitsPerSample < 8 && dfEffectiveNoData == 0))
     478             :     {
     479      113382 :         if (eDT == GDT_Int8)
     480             :         {
     481         270 :             return GDALIsValueInRange<signed char>(dfEffectiveNoData) &&
     482         135 :                    *(static_cast<const signed char *>(pBuffer)) ==
     483         270 :                        static_cast<signed char>(dfEffectiveNoData);
     484             :         }
     485      226463 :         return GDALIsValueInRange<GByte>(dfEffectiveNoData) &&
     486      113216 :                *(static_cast<const GByte *>(pBuffer)) ==
     487      226463 :                    static_cast<GByte>(dfEffectiveNoData);
     488             :     }
     489       55124 :     if (m_nBitsPerSample == 16 && eDT == GDT_UInt16)
     490             :     {
     491        4170 :         return GDALIsValueInRange<GUInt16>(dfEffectiveNoData) &&
     492        2085 :                *(static_cast<const GUInt16 *>(pBuffer)) ==
     493        4170 :                    static_cast<GUInt16>(dfEffectiveNoData);
     494             :     }
     495       53039 :     if (m_nBitsPerSample == 16 && eDT == GDT_Int16)
     496             :     {
     497        8432 :         return GDALIsValueInRange<GInt16>(dfEffectiveNoData) &&
     498        4216 :                *(static_cast<const GInt16 *>(pBuffer)) ==
     499        8432 :                    static_cast<GInt16>(dfEffectiveNoData);
     500             :     }
     501       48823 :     if (m_nBitsPerSample == 32 && eDT == GDT_UInt32)
     502             :     {
     503         370 :         return GDALIsValueInRange<GUInt32>(dfEffectiveNoData) &&
     504         185 :                *(static_cast<const GUInt32 *>(pBuffer)) ==
     505         370 :                    static_cast<GUInt32>(dfEffectiveNoData);
     506             :     }
     507       48638 :     if (m_nBitsPerSample == 32 && eDT == GDT_Int32)
     508             :     {
     509         498 :         return GDALIsValueInRange<GInt32>(dfEffectiveNoData) &&
     510         249 :                *(static_cast<const GInt32 *>(pBuffer)) ==
     511         498 :                    static_cast<GInt32>(dfEffectiveNoData);
     512             :     }
     513       48389 :     if (m_nBitsPerSample == 64 && eDT == GDT_UInt64)
     514             :     {
     515         234 :         return GDALIsValueInRange<std::uint64_t>(dfEffectiveNoData) &&
     516         117 :                *(static_cast<const std::uint64_t *>(pBuffer)) ==
     517         234 :                    static_cast<std::uint64_t>(dfEffectiveNoData);
     518             :     }
     519       48272 :     if (m_nBitsPerSample == 64 && eDT == GDT_Int64)
     520             :     {
     521         236 :         return GDALIsValueInRange<std::int64_t>(dfEffectiveNoData) &&
     522         118 :                *(static_cast<const std::int64_t *>(pBuffer)) ==
     523         236 :                    static_cast<std::int64_t>(dfEffectiveNoData);
     524             :     }
     525       48154 :     if (m_nBitsPerSample == 32 && eDT == GDT_Float32)
     526             :     {
     527       41243 :         if (std::isnan(m_dfNoDataValue))
     528           3 :             return CPL_TO_BOOL(
     529           6 :                 std::isnan(*(static_cast<const float *>(pBuffer))));
     530       82480 :         return GDALIsValueInRange<float>(dfEffectiveNoData) &&
     531       41240 :                *(static_cast<const float *>(pBuffer)) ==
     532       82480 :                    static_cast<float>(dfEffectiveNoData);
     533             :     }
     534        6911 :     if (m_nBitsPerSample == 64 && eDT == GDT_Float64)
     535             :     {
     536        4255 :         if (std::isnan(dfEffectiveNoData))
     537           3 :             return CPL_TO_BOOL(
     538           6 :                 std::isnan(*(static_cast<const double *>(pBuffer))));
     539        4252 :         return *(static_cast<const double *>(pBuffer)) == dfEffectiveNoData;
     540             :     }
     541        2656 :     return false;
     542             : }
     543             : 
     544             : /************************************************************************/
     545             : /*                      WriteDealWithLercAndNan()                       */
     546             : /************************************************************************/
     547             : 
     548             : template <typename T>
     549           0 : void GTiffDataset::WriteDealWithLercAndNan(T *pBuffer, int nActualBlockWidth,
     550             :                                            int nActualBlockHeight,
     551             :                                            int nStrileHeight)
     552             : {
     553             :     // This method does 2 things:
     554             :     // - warn the user if he tries to write NaN values with libtiff < 4.6.1
     555             :     //   and multi-band PlanarConfig=Contig configuration
     556             :     // - and in right-most and bottom-most tiles, replace non accessible
     557             :     //   pixel values by a safe one.
     558             : 
     559           0 :     const auto fPaddingValue =
     560             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     561             :         m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1
     562             :             ? 0
     563             :             :
     564             : #endif
     565             :             std::numeric_limits<T>::quiet_NaN();
     566             : 
     567           0 :     const int nBandsPerStrile =
     568           0 :         m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     569           0 :     for (int j = 0; j < nActualBlockHeight; ++j)
     570             :     {
     571             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     572             :         static bool bHasWarned = false;
     573             :         if (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1 && !bHasWarned)
     574             :         {
     575             :             for (int i = 0; i < nActualBlockWidth * nBandsPerStrile; ++i)
     576             :             {
     577             :                 if (std::isnan(
     578             :                         pBuffer[j * m_nBlockXSize * nBandsPerStrile + i]))
     579             :                 {
     580             :                     bHasWarned = true;
     581             :                     CPLError(CE_Warning, CPLE_AppDefined,
     582             :                              "libtiff < 4.6.1 does not handle properly NaN "
     583             :                              "values for multi-band PlanarConfig=Contig "
     584             :                              "configuration. As a workaround, you can set the "
     585             :                              "INTERLEAVE=BAND creation option.");
     586             :                     break;
     587             :                 }
     588             :             }
     589             :         }
     590             : #endif
     591           0 :         for (int i = nActualBlockWidth * nBandsPerStrile;
     592           0 :              i < m_nBlockXSize * nBandsPerStrile; ++i)
     593             :         {
     594           0 :             pBuffer[j * m_nBlockXSize * nBandsPerStrile + i] = fPaddingValue;
     595             :         }
     596             :     }
     597           0 :     for (int j = nActualBlockHeight; j < nStrileHeight; ++j)
     598             :     {
     599           0 :         for (int i = 0; i < m_nBlockXSize * nBandsPerStrile; ++i)
     600             :         {
     601           0 :             pBuffer[j * m_nBlockXSize * nBandsPerStrile + i] = fPaddingValue;
     602             :         }
     603             :     }
     604           0 : }
     605             : 
     606             : /************************************************************************/
     607             : /*                        WriteEncodedTile()                            */
     608             : /************************************************************************/
     609             : 
     610       50292 : bool GTiffDataset::WriteEncodedTile(uint32_t tile, GByte *pabyData,
     611             :                                     int bPreserveDataBuffer)
     612             : {
     613       50292 :     const int iColumn = (tile % m_nBlocksPerBand) % m_nBlocksPerRow;
     614       50292 :     const int iRow = (tile % m_nBlocksPerBand) / m_nBlocksPerRow;
     615             : 
     616      100584 :     const int nActualBlockWidth = (iColumn == m_nBlocksPerRow - 1)
     617       50292 :                                       ? nRasterXSize - iColumn * m_nBlockXSize
     618             :                                       : m_nBlockXSize;
     619      100584 :     const int nActualBlockHeight = (iRow == m_nBlocksPerColumn - 1)
     620       50292 :                                        ? nRasterYSize - iRow * m_nBlockYSize
     621             :                                        : m_nBlockYSize;
     622             : 
     623             :     /* -------------------------------------------------------------------- */
     624             :     /*      Don't write empty blocks in some cases.                         */
     625             :     /* -------------------------------------------------------------------- */
     626       50292 :     if (!m_bWriteEmptyTiles && IsFirstPixelEqualToNoData(pabyData))
     627             :     {
     628        1945 :         if (!IsBlockAvailable(tile, nullptr, nullptr, nullptr))
     629             :         {
     630        1945 :             const int nComponents =
     631        1945 :                 m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     632             : 
     633        1945 :             if (HasOnlyNoData(pabyData, nActualBlockWidth, nActualBlockHeight,
     634             :                               m_nBlockXSize, nComponents))
     635             :             {
     636        1171 :                 return true;
     637             :             }
     638             :         }
     639             :     }
     640             : 
     641             :     // Is this a partial right edge or bottom edge tile?
     642       95275 :     const bool bPartialTile = (nActualBlockWidth < m_nBlockXSize) ||
     643       46154 :                               (nActualBlockHeight < m_nBlockYSize);
     644             : 
     645             :     const bool bIsLercFloatingPoint =
     646       49187 :         m_nCompression == COMPRESSION_LERC &&
     647          66 :         (GetRasterBand(1)->GetRasterDataType() == GDT_Float32 ||
     648          64 :          GetRasterBand(1)->GetRasterDataType() == GDT_Float64);
     649             : 
     650             :     // Do we need to spread edge values right or down for a partial
     651             :     // JPEG encoded tile?  We do this to avoid edge artifacts.
     652             :     // We also need to be careful with LERC and NaN values
     653       49121 :     const bool bNeedTempBuffer =
     654       53748 :         bPartialTile &&
     655        4627 :         (m_nCompression == COMPRESSION_JPEG || bIsLercFloatingPoint);
     656             : 
     657             :     // If we need to fill out the tile, or if we want to prevent
     658             :     // TIFFWriteEncodedTile from altering the buffer as part of
     659             :     // byte swapping the data on write then we will need a temporary
     660             :     // working buffer.  If not, we can just do a direct write.
     661       49121 :     const GPtrDiff_t cc = static_cast<GPtrDiff_t>(TIFFTileSize(m_hTIFF));
     662             : 
     663       63418 :     if (bPreserveDataBuffer &&
     664       14297 :         (TIFFIsByteSwapped(m_hTIFF) || bNeedTempBuffer || m_panMaskOffsetLsb))
     665             :     {
     666         158 :         if (m_pabyTempWriteBuffer == nullptr)
     667             :         {
     668          35 :             m_pabyTempWriteBuffer = CPLMalloc(cc);
     669             :         }
     670         158 :         memcpy(m_pabyTempWriteBuffer, pabyData, cc);
     671             : 
     672         158 :         pabyData = static_cast<GByte *>(m_pabyTempWriteBuffer);
     673             :     }
     674             : 
     675             :     // Perform tile fill if needed.
     676             :     // TODO: we should also handle the case of nBitsPerSample == 12
     677             :     // but this is more involved.
     678       49121 :     if (bPartialTile && m_nCompression == COMPRESSION_JPEG &&
     679         134 :         m_nBitsPerSample == 8)
     680             :     {
     681         132 :         const int nComponents =
     682         132 :             m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     683             : 
     684         132 :         CPLDebug("GTiff", "Filling out jpeg edge tile on write.");
     685             : 
     686         132 :         const int nRightPixelsToFill =
     687         132 :             iColumn == m_nBlocksPerRow - 1
     688         132 :                 ? m_nBlockXSize * (iColumn + 1) - nRasterXSize
     689             :                 : 0;
     690         132 :         const int nBottomPixelsToFill =
     691         132 :             iRow == m_nBlocksPerColumn - 1
     692         132 :                 ? m_nBlockYSize * (iRow + 1) - nRasterYSize
     693             :                 : 0;
     694             : 
     695             :         // Fill out to the right.
     696         132 :         const int iSrcX = m_nBlockXSize - nRightPixelsToFill - 1;
     697             : 
     698       12461 :         for (int iX = iSrcX + 1; iX < m_nBlockXSize; ++iX)
     699             :         {
     700     3955880 :             for (int iY = 0; iY < m_nBlockYSize; ++iY)
     701             :             {
     702     3943550 :                 memcpy(pabyData +
     703     3943550 :                            (static_cast<GPtrDiff_t>(m_nBlockXSize) * iY + iX) *
     704     3943550 :                                nComponents,
     705     3943550 :                        pabyData + (static_cast<GPtrDiff_t>(m_nBlockXSize) * iY +
     706     3943550 :                                    iSrcX) *
     707     3943550 :                                       nComponents,
     708             :                        nComponents);
     709             :             }
     710             :         }
     711             : 
     712             :         // Now fill out the bottom.
     713         132 :         const int iSrcY = m_nBlockYSize - nBottomPixelsToFill - 1;
     714       17682 :         for (int iY = iSrcY + 1; iY < m_nBlockYSize; ++iY)
     715             :         {
     716       17550 :             memcpy(pabyData + static_cast<GPtrDiff_t>(m_nBlockXSize) *
     717       17550 :                                   nComponents * iY,
     718       17550 :                    pabyData + static_cast<GPtrDiff_t>(m_nBlockXSize) *
     719       17550 :                                   nComponents * iSrcY,
     720       17550 :                    static_cast<GPtrDiff_t>(m_nBlockXSize) * nComponents);
     721             :         }
     722             :     }
     723             : 
     724       49121 :     if (bIsLercFloatingPoint &&
     725             :         (bPartialTile
     726             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     727             :          /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
     728             :          || (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1)
     729             : #endif
     730             :              ))
     731             :     {
     732           0 :         if (GetRasterBand(1)->GetRasterDataType() == GDT_Float32)
     733           0 :             WriteDealWithLercAndNan(reinterpret_cast<float *>(pabyData),
     734             :                                     nActualBlockWidth, nActualBlockHeight,
     735             :                                     m_nBlockYSize);
     736             :         else
     737           0 :             WriteDealWithLercAndNan(reinterpret_cast<double *>(pabyData),
     738             :                                     nActualBlockWidth, nActualBlockHeight,
     739             :                                     m_nBlockYSize);
     740             :     }
     741             : 
     742       49121 :     if (m_panMaskOffsetLsb)
     743             :     {
     744           0 :         const int iBand = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     745           0 :                               ? static_cast<int>(tile) / m_nBlocksPerBand
     746             :                               : -1;
     747           0 :         DiscardLsb(pabyData, cc, iBand);
     748             :     }
     749             : 
     750       49121 :     if (m_bStreamingOut)
     751             :     {
     752          17 :         if (tile != static_cast<uint32_t>(m_nLastWrittenBlockId + 1))
     753             :         {
     754           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     755             :                         "Attempt to write block %d whereas %d was expected",
     756           1 :                         tile, m_nLastWrittenBlockId + 1);
     757           1 :             return false;
     758             :         }
     759          16 :         if (static_cast<GPtrDiff_t>(VSIFWriteL(pabyData, 1, cc, m_fpToWrite)) !=
     760             :             cc)
     761             :         {
     762           0 :             ReportError(CE_Failure, CPLE_FileIO,
     763             :                         "Could not write " CPL_FRMT_GUIB " bytes",
     764             :                         static_cast<GUIntBig>(cc));
     765           0 :             return false;
     766             :         }
     767          16 :         m_nLastWrittenBlockId = tile;
     768          16 :         return true;
     769             :     }
     770             : 
     771             :     /* -------------------------------------------------------------------- */
     772             :     /*      Should we do compression in a worker thread ?                   */
     773             :     /* -------------------------------------------------------------------- */
     774       49104 :     if (SubmitCompressionJob(tile, pabyData, cc, m_nBlockYSize))
     775       19832 :         return true;
     776             : 
     777       29272 :     return TIFFWriteEncodedTile(m_hTIFF, tile, pabyData, cc) == cc;
     778             : }
     779             : 
     780             : /************************************************************************/
     781             : /*                        WriteEncodedStrip()                           */
     782             : /************************************************************************/
     783             : 
     784      177517 : bool GTiffDataset::WriteEncodedStrip(uint32_t strip, GByte *pabyData,
     785             :                                      int bPreserveDataBuffer)
     786             : {
     787      177517 :     GPtrDiff_t cc = static_cast<GPtrDiff_t>(TIFFStripSize(m_hTIFF));
     788      177517 :     const auto ccFull = cc;
     789             : 
     790             :     /* -------------------------------------------------------------------- */
     791             :     /*      If this is the last strip in the image, and is partial, then    */
     792             :     /*      we need to trim the number of scanlines written to the          */
     793             :     /*      amount of valid data we have. (#2748)                           */
     794             :     /* -------------------------------------------------------------------- */
     795      177517 :     const int nStripWithinBand = strip % m_nBlocksPerBand;
     796      177517 :     int nStripHeight = m_nRowsPerStrip;
     797             : 
     798      177517 :     if (nStripWithinBand * nStripHeight > GetRasterYSize() - nStripHeight)
     799             :     {
     800         379 :         nStripHeight = GetRasterYSize() - nStripWithinBand * m_nRowsPerStrip;
     801         379 :         cc = (cc / m_nRowsPerStrip) * nStripHeight;
     802         758 :         CPLDebug("GTiff",
     803             :                  "Adjusted bytes to write from " CPL_FRMT_GUIB
     804             :                  " to " CPL_FRMT_GUIB ".",
     805         379 :                  static_cast<GUIntBig>(TIFFStripSize(m_hTIFF)),
     806             :                  static_cast<GUIntBig>(cc));
     807             :     }
     808             : 
     809             :     /* -------------------------------------------------------------------- */
     810             :     /*      Don't write empty blocks in some cases.                         */
     811             :     /* -------------------------------------------------------------------- */
     812      177517 :     if (!m_bWriteEmptyTiles && IsFirstPixelEqualToNoData(pabyData))
     813             :     {
     814       40908 :         if (!IsBlockAvailable(strip, nullptr, nullptr, nullptr))
     815             :         {
     816       40738 :             const int nComponents =
     817       40738 :                 m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
     818             : 
     819       40738 :             if (HasOnlyNoData(pabyData, m_nBlockXSize, nStripHeight,
     820             :                               m_nBlockXSize, nComponents))
     821             :             {
     822       28302 :                 return true;
     823             :             }
     824             :         }
     825             :     }
     826             : 
     827             :     /* -------------------------------------------------------------------- */
     828             :     /*      TIFFWriteEncodedStrip can alter the passed buffer if            */
     829             :     /*      byte-swapping is necessary so we use a temporary buffer         */
     830             :     /*      before calling it.                                              */
     831             :     /* -------------------------------------------------------------------- */
     832      238549 :     if (bPreserveDataBuffer &&
     833       89334 :         (TIFFIsByteSwapped(m_hTIFF) || m_panMaskOffsetLsb))
     834             :     {
     835         294 :         if (m_pabyTempWriteBuffer == nullptr)
     836             :         {
     837         126 :             m_pabyTempWriteBuffer = CPLMalloc(ccFull);
     838             :         }
     839         294 :         memcpy(m_pabyTempWriteBuffer, pabyData, cc);
     840         294 :         pabyData = static_cast<GByte *>(m_pabyTempWriteBuffer);
     841             :     }
     842             : 
     843             : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
     844             :     const bool bIsLercFloatingPoint =
     845             :         m_nCompression == COMPRESSION_LERC &&
     846             :         (GetRasterBand(1)->GetRasterDataType() == GDT_Float32 ||
     847             :          GetRasterBand(1)->GetRasterDataType() == GDT_Float64);
     848             :     if (bIsLercFloatingPoint &&
     849             :         /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
     850             :         m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1)
     851             :     {
     852             :         if (GetRasterBand(1)->GetRasterDataType() == GDT_Float32)
     853             :             WriteDealWithLercAndNan(reinterpret_cast<float *>(pabyData),
     854             :                                     m_nBlockXSize, nStripHeight, nStripHeight);
     855             :         else
     856             :             WriteDealWithLercAndNan(reinterpret_cast<double *>(pabyData),
     857             :                                     m_nBlockXSize, nStripHeight, nStripHeight);
     858             :     }
     859             : #endif
     860             : 
     861      149215 :     if (m_panMaskOffsetLsb)
     862             :     {
     863         366 :         int iBand = m_nPlanarConfig == PLANARCONFIG_SEPARATE
     864         183 :                         ? static_cast<int>(strip) / m_nBlocksPerBand
     865             :                         : -1;
     866         183 :         DiscardLsb(pabyData, cc, iBand);
     867             :     }
     868             : 
     869      149215 :     if (m_bStreamingOut)
     870             :     {
     871        1408 :         if (strip != static_cast<uint32_t>(m_nLastWrittenBlockId + 1))
     872             :         {
     873           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     874             :                         "Attempt to write block %d whereas %d was expected",
     875           1 :                         strip, m_nLastWrittenBlockId + 1);
     876           1 :             return false;
     877             :         }
     878        1407 :         if (static_cast<GPtrDiff_t>(VSIFWriteL(pabyData, 1, cc, m_fpToWrite)) !=
     879             :             cc)
     880             :         {
     881           0 :             ReportError(CE_Failure, CPLE_FileIO,
     882             :                         "Could not write " CPL_FRMT_GUIB " bytes",
     883             :                         static_cast<GUIntBig>(cc));
     884           0 :             return false;
     885             :         }
     886        1407 :         m_nLastWrittenBlockId = strip;
     887        1407 :         return true;
     888             :     }
     889             : 
     890             :     /* -------------------------------------------------------------------- */
     891             :     /*      Should we do compression in a worker thread ?                   */
     892             :     /* -------------------------------------------------------------------- */
     893      147807 :     if (SubmitCompressionJob(strip, pabyData, cc, nStripHeight))
     894        6727 :         return true;
     895             : 
     896      141080 :     return TIFFWriteEncodedStrip(m_hTIFF, strip, pabyData, cc) == cc;
     897             : }
     898             : 
     899             : /************************************************************************/
     900             : /*                        InitCompressionThreads()                      */
     901             : /************************************************************************/
     902             : 
     903       31524 : void GTiffDataset::InitCompressionThreads(bool bUpdateMode,
     904             :                                           CSLConstList papszOptions)
     905             : {
     906             :     // Raster == tile, then no need for threads
     907       31524 :     if (m_nBlockXSize == nRasterXSize && m_nBlockYSize == nRasterYSize)
     908       23042 :         return;
     909             : 
     910        8482 :     const char *pszValue = CSLFetchNameValue(papszOptions, "NUM_THREADS");
     911        8482 :     if (pszValue == nullptr)
     912        8417 :         pszValue = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
     913        8482 :     if (pszValue)
     914             :     {
     915             :         int nThreads =
     916         147 :             EQUAL(pszValue, "ALL_CPUS") ? CPLGetNumCPUs() : atoi(pszValue);
     917         147 :         if (nThreads > 1024)
     918           0 :             nThreads = 1024;  // to please Coverity
     919         147 :         if (nThreads > 1)
     920             :         {
     921          98 :             if ((bUpdateMode && m_nCompression != COMPRESSION_NONE) ||
     922          20 :                 (nBands >= 1 && IsMultiThreadedReadCompatible()))
     923             :             {
     924          73 :                 CPLDebug("GTiff",
     925             :                          "Using up to %d threads for compression/decompression",
     926             :                          nThreads);
     927             : 
     928          73 :                 m_poThreadPool = GDALGetGlobalThreadPool(nThreads);
     929          73 :                 if (bUpdateMode && m_poThreadPool)
     930          58 :                     m_poCompressQueue = m_poThreadPool->CreateJobQueue();
     931             : 
     932          73 :                 if (m_poCompressQueue != nullptr)
     933             :                 {
     934             :                     // Add a margin of an extra job w.r.t thread number
     935             :                     // so as to optimize compression time (enables the main
     936             :                     // thread to do boring I/O while all CPUs are working).
     937          58 :                     m_asCompressionJobs.resize(nThreads + 1);
     938          58 :                     memset(&m_asCompressionJobs[0], 0,
     939          58 :                            m_asCompressionJobs.size() *
     940             :                                sizeof(GTiffCompressionJob));
     941          58 :                     for (int i = 0;
     942         280 :                          i < static_cast<int>(m_asCompressionJobs.size()); ++i)
     943             :                     {
     944         444 :                         m_asCompressionJobs[i].pszTmpFilename =
     945         222 :                             CPLStrdup(VSIMemGenerateHiddenFilename(
     946             :                                 CPLSPrintf("thread_job_%d.tif", i)));
     947         222 :                         m_asCompressionJobs[i].nStripOrTile = -1;
     948             :                     }
     949             : 
     950             :                     // This is kind of a hack, but basically using
     951             :                     // TIFFWriteRawStrip/Tile and then TIFFReadEncodedStrip/Tile
     952             :                     // does not work on a newly created file, because
     953             :                     // TIFF_MYBUFFER is not set in tif_flags
     954             :                     // (if using TIFFWriteEncodedStrip/Tile first,
     955             :                     // TIFFWriteBufferSetup() is automatically called).
     956             :                     // This should likely rather fixed in libtiff itself.
     957          58 :                     CPL_IGNORE_RET_VAL(
     958          58 :                         TIFFWriteBufferSetup(m_hTIFF, nullptr, -1));
     959             :                 }
     960             :             }
     961             :         }
     962          69 :         else if (nThreads < 0 ||
     963          69 :                  (!EQUAL(pszValue, "0") && !EQUAL(pszValue, "1") &&
     964           3 :                   !EQUAL(pszValue, "ALL_CPUS")))
     965             :         {
     966           3 :             ReportError(CE_Warning, CPLE_AppDefined,
     967             :                         "Invalid value for NUM_THREADS: %s", pszValue);
     968             :         }
     969             :     }
     970             : }
     971             : 
     972             : /************************************************************************/
     973             : /*                      ThreadCompressionFunc()                         */
     974             : /************************************************************************/
     975             : 
     976       26571 : void GTiffDataset::ThreadCompressionFunc(void *pData)
     977             : {
     978       26571 :     GTiffCompressionJob *psJob = static_cast<GTiffCompressionJob *>(pData);
     979       26571 :     GTiffDataset *poDS = psJob->poDS;
     980             : 
     981       26571 :     VSILFILE *fpTmp = VSIFOpenL(psJob->pszTmpFilename, "wb+");
     982       26571 :     TIFF *hTIFFTmp = VSI_TIFFOpen(
     983       53142 :         psJob->pszTmpFilename, psJob->bTIFFIsBigEndian ? "wb+" : "wl+", fpTmp);
     984       26571 :     CPLAssert(hTIFFTmp != nullptr);
     985       26571 :     TIFFSetField(hTIFFTmp, TIFFTAG_IMAGEWIDTH, poDS->m_nBlockXSize);
     986       26571 :     TIFFSetField(hTIFFTmp, TIFFTAG_IMAGELENGTH, psJob->nHeight);
     987       26571 :     TIFFSetField(hTIFFTmp, TIFFTAG_BITSPERSAMPLE, poDS->m_nBitsPerSample);
     988       26571 :     TIFFSetField(hTIFFTmp, TIFFTAG_COMPRESSION, poDS->m_nCompression);
     989       26571 :     TIFFSetField(hTIFFTmp, TIFFTAG_PHOTOMETRIC, poDS->m_nPhotometric);
     990       26571 :     TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLEFORMAT, poDS->m_nSampleFormat);
     991       26571 :     TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLESPERPIXEL, poDS->m_nSamplesPerPixel);
     992       26571 :     TIFFSetField(hTIFFTmp, TIFFTAG_ROWSPERSTRIP, poDS->m_nBlockYSize);
     993       26571 :     TIFFSetField(hTIFFTmp, TIFFTAG_PLANARCONFIG, poDS->m_nPlanarConfig);
     994       26571 :     if (psJob->nPredictor != PREDICTOR_NONE)
     995         263 :         TIFFSetField(hTIFFTmp, TIFFTAG_PREDICTOR, psJob->nPredictor);
     996       26571 :     if (poDS->m_nCompression == COMPRESSION_LERC)
     997             :     {
     998          24 :         TIFFSetField(hTIFFTmp, TIFFTAG_LERC_PARAMETERS, 2,
     999          24 :                      poDS->m_anLercAddCompressionAndVersion);
    1000             :     }
    1001       26571 :     if (psJob->nExtraSampleCount)
    1002             :     {
    1003         352 :         TIFFSetField(hTIFFTmp, TIFFTAG_EXTRASAMPLES, psJob->nExtraSampleCount,
    1004             :                      psJob->pExtraSamples);
    1005             :     }
    1006             : 
    1007       26571 :     poDS->RestoreVolatileParameters(hTIFFTmp);
    1008             : 
    1009       53142 :     bool bOK = TIFFWriteEncodedStrip(hTIFFTmp, 0, psJob->pabyBuffer,
    1010       26571 :                                      psJob->nBufferSize) == psJob->nBufferSize;
    1011             : 
    1012       26571 :     toff_t nOffset = 0;
    1013       26571 :     if (bOK)
    1014             :     {
    1015       26571 :         toff_t *panOffsets = nullptr;
    1016       26571 :         toff_t *panByteCounts = nullptr;
    1017       26571 :         TIFFGetField(hTIFFTmp, TIFFTAG_STRIPOFFSETS, &panOffsets);
    1018       26571 :         TIFFGetField(hTIFFTmp, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
    1019             : 
    1020       26571 :         nOffset = panOffsets[0];
    1021       26571 :         psJob->nCompressedBufferSize =
    1022       26571 :             static_cast<GPtrDiff_t>(panByteCounts[0]);
    1023             :     }
    1024             :     else
    1025             :     {
    1026           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1027             :                  "Error when compressing strip/tile %d", psJob->nStripOrTile);
    1028             :     }
    1029             : 
    1030       26571 :     XTIFFClose(hTIFFTmp);
    1031       26571 :     if (VSIFCloseL(fpTmp) != 0)
    1032             :     {
    1033           0 :         if (bOK)
    1034             :         {
    1035           0 :             bOK = false;
    1036           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1037             :                      "Error when compressing strip/tile %d",
    1038             :                      psJob->nStripOrTile);
    1039             :         }
    1040             :     }
    1041             : 
    1042       26571 :     if (bOK)
    1043             :     {
    1044       26571 :         vsi_l_offset nFileSize = 0;
    1045             :         GByte *pabyCompressedBuffer =
    1046       26571 :             VSIGetMemFileBuffer(psJob->pszTmpFilename, &nFileSize, FALSE);
    1047       26571 :         CPLAssert(static_cast<vsi_l_offset>(
    1048             :                       nOffset + psJob->nCompressedBufferSize) <= nFileSize);
    1049       26571 :         psJob->pabyCompressedBuffer = pabyCompressedBuffer + nOffset;
    1050             :     }
    1051             :     else
    1052             :     {
    1053           0 :         psJob->pabyCompressedBuffer = nullptr;
    1054           0 :         psJob->nCompressedBufferSize = 0;
    1055             :     }
    1056             : 
    1057       26571 :     auto poMainDS = poDS->m_poBaseDS ? poDS->m_poBaseDS : poDS;
    1058       26571 :     if (poMainDS->m_poCompressQueue)
    1059             :     {
    1060        1576 :         std::lock_guard oLock(poMainDS->m_oCompressThreadPoolMutex);
    1061        1576 :         psJob->bReady = true;
    1062             :     }
    1063       26571 : }
    1064             : 
    1065             : /************************************************************************/
    1066             : /*                        WriteRawStripOrTile()                         */
    1067             : /************************************************************************/
    1068             : 
    1069       33874 : void GTiffDataset::WriteRawStripOrTile(int nStripOrTile,
    1070             :                                        GByte *pabyCompressedBuffer,
    1071             :                                        GPtrDiff_t nCompressedBufferSize)
    1072             : {
    1073             : #ifdef DEBUG_VERBOSE
    1074             :     CPLDebug("GTIFF", "Writing raw strip/tile %d, size " CPL_FRMT_GUIB,
    1075             :              nStripOrTile, static_cast<GUIntBig>(nCompressedBufferSize));
    1076             : #endif
    1077       33874 :     toff_t *panOffsets = nullptr;
    1078       33874 :     toff_t *panByteCounts = nullptr;
    1079       33874 :     bool bWriteAtEnd = true;
    1080       33874 :     bool bWriteLeader = m_bLeaderSizeAsUInt4;
    1081       33874 :     bool bWriteTrailer = m_bTrailerRepeatedLast4BytesRepeated;
    1082       33874 :     if (TIFFGetField(m_hTIFF,
    1083       33874 :                      TIFFIsTiled(m_hTIFF) ? TIFFTAG_TILEOFFSETS
    1084             :                                           : TIFFTAG_STRIPOFFSETS,
    1085       33874 :                      &panOffsets) &&
    1086       33874 :         panOffsets != nullptr && panOffsets[nStripOrTile] != 0)
    1087             :     {
    1088             :         // Forces TIFFAppendStrip() to consider if the location of the
    1089             :         // tile/strip can be reused or if the strile should be written at end of
    1090             :         // file.
    1091         360 :         TIFFSetWriteOffset(m_hTIFF, 0);
    1092             : 
    1093         360 :         if (m_bBlockOrderRowMajor)
    1094             :         {
    1095         264 :             if (TIFFGetField(m_hTIFF,
    1096         264 :                              TIFFIsTiled(m_hTIFF) ? TIFFTAG_TILEBYTECOUNTS
    1097             :                                                   : TIFFTAG_STRIPBYTECOUNTS,
    1098         528 :                              &panByteCounts) &&
    1099         264 :                 panByteCounts != nullptr)
    1100             :             {
    1101         264 :                 if (static_cast<GUIntBig>(nCompressedBufferSize) >
    1102         264 :                     panByteCounts[nStripOrTile])
    1103             :                 {
    1104           8 :                     GTiffDataset *poRootDS = m_poBaseDS ? m_poBaseDS : this;
    1105           8 :                     if (!poRootDS->m_bKnownIncompatibleEdition &&
    1106           8 :                         !poRootDS->m_bWriteKnownIncompatibleEdition)
    1107             :                     {
    1108           8 :                         ReportError(
    1109             :                             CE_Warning, CPLE_AppDefined,
    1110             :                             "A strile cannot be rewritten in place, which "
    1111             :                             "invalidates the BLOCK_ORDER optimization.");
    1112           8 :                         poRootDS->m_bKnownIncompatibleEdition = true;
    1113           8 :                         poRootDS->m_bWriteKnownIncompatibleEdition = true;
    1114             :                     }
    1115             :                 }
    1116             :                 // For mask interleaving, if the size is not exactly the same,
    1117             :                 // completely give up (we could potentially move the mask in
    1118             :                 // case the imagery is smaller)
    1119         256 :                 else if (m_poMaskDS && m_bMaskInterleavedWithImagery &&
    1120           0 :                          static_cast<GUIntBig>(nCompressedBufferSize) !=
    1121           0 :                              panByteCounts[nStripOrTile])
    1122             :                 {
    1123           0 :                     GTiffDataset *poRootDS = m_poBaseDS ? m_poBaseDS : this;
    1124           0 :                     if (!poRootDS->m_bKnownIncompatibleEdition &&
    1125           0 :                         !poRootDS->m_bWriteKnownIncompatibleEdition)
    1126             :                     {
    1127           0 :                         ReportError(
    1128             :                             CE_Warning, CPLE_AppDefined,
    1129             :                             "A strile cannot be rewritten in place, which "
    1130             :                             "invalidates the MASK_INTERLEAVED_WITH_IMAGERY "
    1131             :                             "optimization.");
    1132           0 :                         poRootDS->m_bKnownIncompatibleEdition = true;
    1133           0 :                         poRootDS->m_bWriteKnownIncompatibleEdition = true;
    1134             :                     }
    1135           0 :                     bWriteLeader = false;
    1136           0 :                     bWriteTrailer = false;
    1137           0 :                     if (m_bLeaderSizeAsUInt4)
    1138             :                     {
    1139             :                         // If there was a valid leader, invalidat it
    1140           0 :                         VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4,
    1141             :                                      SEEK_SET);
    1142             :                         uint32_t nOldSize;
    1143           0 :                         VSIFReadL(&nOldSize, 1, 4,
    1144             :                                   VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF)));
    1145           0 :                         CPL_LSBPTR32(&nOldSize);
    1146           0 :                         if (nOldSize == panByteCounts[nStripOrTile])
    1147             :                         {
    1148           0 :                             uint32_t nInvalidatedSize = 0;
    1149           0 :                             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4,
    1150             :                                          SEEK_SET);
    1151           0 :                             VSI_TIFFWrite(m_hTIFF, &nInvalidatedSize,
    1152             :                                           sizeof(nInvalidatedSize));
    1153             :                         }
    1154             :                     }
    1155             :                 }
    1156             :                 else
    1157             :                 {
    1158         256 :                     bWriteAtEnd = false;
    1159             :                 }
    1160             :             }
    1161             :         }
    1162             :     }
    1163       33874 :     if (bWriteLeader &&
    1164       25000 :         static_cast<GUIntBig>(nCompressedBufferSize) <= 0xFFFFFFFFU)
    1165             :     {
    1166             :         // cppcheck-suppress knownConditionTrueFalse
    1167       25000 :         if (bWriteAtEnd)
    1168             :         {
    1169       24744 :             VSI_TIFFSeek(m_hTIFF, 0, SEEK_END);
    1170             :         }
    1171             :         else
    1172             :         {
    1173             :             // If we rewrite an existing strile in place with an existing
    1174             :             // leader, check that the leader is valid, before rewriting it. And
    1175             :             // if it is not valid, then do not write the trailer, as we could
    1176             :             // corrupt other data.
    1177         256 :             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4, SEEK_SET);
    1178             :             uint32_t nOldSize;
    1179         256 :             VSIFReadL(&nOldSize, 1, 4,
    1180             :                       VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF)));
    1181         256 :             CPL_LSBPTR32(&nOldSize);
    1182         256 :             bWriteLeader =
    1183         256 :                 panByteCounts && nOldSize == panByteCounts[nStripOrTile];
    1184         256 :             bWriteTrailer = bWriteLeader;
    1185         256 :             VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4, SEEK_SET);
    1186             :         }
    1187             :         // cppcheck-suppress knownConditionTrueFalse
    1188       25000 :         if (bWriteLeader)
    1189             :         {
    1190       25000 :             uint32_t nSize = static_cast<uint32_t>(nCompressedBufferSize);
    1191       25000 :             CPL_LSBPTR32(&nSize);
    1192       25000 :             if (!VSI_TIFFWrite(m_hTIFF, &nSize, sizeof(nSize)))
    1193           0 :                 m_bWriteError = true;
    1194             :         }
    1195             :     }
    1196             :     tmsize_t written;
    1197       33874 :     if (TIFFIsTiled(m_hTIFF))
    1198       26221 :         written = TIFFWriteRawTile(m_hTIFF, nStripOrTile, pabyCompressedBuffer,
    1199             :                                    nCompressedBufferSize);
    1200             :     else
    1201        7653 :         written = TIFFWriteRawStrip(m_hTIFF, nStripOrTile, pabyCompressedBuffer,
    1202             :                                     nCompressedBufferSize);
    1203       33874 :     if (written != nCompressedBufferSize)
    1204          12 :         m_bWriteError = true;
    1205       33874 :     if (bWriteTrailer &&
    1206       25000 :         static_cast<GUIntBig>(nCompressedBufferSize) <= 0xFFFFFFFFU)
    1207             :     {
    1208       25000 :         GByte abyLastBytes[4] = {};
    1209       25000 :         if (nCompressedBufferSize >= 4)
    1210       25000 :             memcpy(abyLastBytes,
    1211       25000 :                    pabyCompressedBuffer + nCompressedBufferSize - 4, 4);
    1212             :         else
    1213           0 :             memcpy(abyLastBytes, pabyCompressedBuffer, nCompressedBufferSize);
    1214       25000 :         if (!VSI_TIFFWrite(m_hTIFF, abyLastBytes, 4))
    1215           0 :             m_bWriteError = true;
    1216             :     }
    1217       33874 : }
    1218             : 
    1219             : /************************************************************************/
    1220             : /*                        WaitCompletionForJobIdx()                     */
    1221             : /************************************************************************/
    1222             : 
    1223        1576 : void GTiffDataset::WaitCompletionForJobIdx(int i)
    1224             : {
    1225        1576 :     auto poMainDS = m_poBaseDS ? m_poBaseDS : this;
    1226        1576 :     auto poQueue = poMainDS->m_poCompressQueue.get();
    1227        1576 :     auto &oQueue = poMainDS->m_asQueueJobIdx;
    1228        1576 :     auto &asJobs = poMainDS->m_asCompressionJobs;
    1229        1576 :     auto &mutex = poMainDS->m_oCompressThreadPoolMutex;
    1230             : 
    1231        1576 :     CPLAssert(i >= 0 && static_cast<size_t>(i) < asJobs.size());
    1232        1576 :     CPLAssert(asJobs[i].nStripOrTile >= 0);
    1233        1576 :     CPLAssert(!oQueue.empty());
    1234             : 
    1235        1576 :     bool bHasWarned = false;
    1236             :     while (true)
    1237             :     {
    1238             :         bool bReady;
    1239             :         {
    1240        2272 :             std::lock_guard oLock(mutex);
    1241        2272 :             bReady = asJobs[i].bReady;
    1242             :         }
    1243        2272 :         if (!bReady)
    1244             :         {
    1245         696 :             if (!bHasWarned)
    1246             :             {
    1247         417 :                 CPLDebug("GTIFF",
    1248             :                          "Waiting for worker job to finish handling block %d",
    1249         417 :                          asJobs[i].nStripOrTile);
    1250         417 :                 bHasWarned = true;
    1251             :             }
    1252         696 :             poQueue->GetPool()->WaitEvent();
    1253             :         }
    1254             :         else
    1255             :         {
    1256        1576 :             break;
    1257             :         }
    1258         696 :     }
    1259             : 
    1260        1576 :     if (asJobs[i].nCompressedBufferSize)
    1261             :     {
    1262        3152 :         asJobs[i].poDS->WriteRawStripOrTile(asJobs[i].nStripOrTile,
    1263        1576 :                                             asJobs[i].pabyCompressedBuffer,
    1264        1576 :                                             asJobs[i].nCompressedBufferSize);
    1265             :     }
    1266        1576 :     asJobs[i].pabyCompressedBuffer = nullptr;
    1267        1576 :     asJobs[i].nBufferSize = 0;
    1268             :     {
    1269             :         // Likely useless, but makes Coverity happy
    1270        1576 :         std::lock_guard oLock(mutex);
    1271        1576 :         asJobs[i].bReady = false;
    1272             :     }
    1273        1576 :     asJobs[i].nStripOrTile = -1;
    1274        1576 :     oQueue.pop();
    1275        1576 : }
    1276             : 
    1277             : /************************************************************************/
    1278             : /*                        WaitCompletionForBlock()                      */
    1279             : /************************************************************************/
    1280             : 
    1281     2317630 : void GTiffDataset::WaitCompletionForBlock(int nBlockId)
    1282             : {
    1283     2317630 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    1284     2298420 :                               : m_poCompressQueue.get();
    1285             :     // cppcheck-suppress constVariableReference
    1286     2317630 :     auto &oQueue = m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    1287             :     // cppcheck-suppress constVariableReference
    1288     2298420 :     auto &asJobs =
    1289     2317630 :         m_poBaseDS ? m_poBaseDS->m_asCompressionJobs : m_asCompressionJobs;
    1290             : 
    1291     2317630 :     if (poQueue != nullptr && !oQueue.empty())
    1292             :     {
    1293        1066 :         for (int i = 0; i < static_cast<int>(asJobs.size()); ++i)
    1294             :         {
    1295         888 :             if (asJobs[i].poDS == this && asJobs[i].nStripOrTile == nBlockId)
    1296             :             {
    1297         128 :                 while (!oQueue.empty() &&
    1298          64 :                        !(asJobs[oQueue.front()].poDS == this &&
    1299          64 :                          asJobs[oQueue.front()].nStripOrTile == nBlockId))
    1300             :                 {
    1301           0 :                     WaitCompletionForJobIdx(oQueue.front());
    1302             :                 }
    1303          64 :                 CPLAssert(!oQueue.empty() &&
    1304             :                           asJobs[oQueue.front()].poDS == this &&
    1305             :                           asJobs[oQueue.front()].nStripOrTile == nBlockId);
    1306          64 :                 WaitCompletionForJobIdx(oQueue.front());
    1307             :             }
    1308             :         }
    1309             :     }
    1310     2317630 : }
    1311             : 
    1312             : /************************************************************************/
    1313             : /*                      SubmitCompressionJob()                          */
    1314             : /************************************************************************/
    1315             : 
    1316      196911 : bool GTiffDataset::SubmitCompressionJob(int nStripOrTile, GByte *pabyData,
    1317             :                                         GPtrDiff_t cc, int nHeight)
    1318             : {
    1319             :     /* -------------------------------------------------------------------- */
    1320             :     /*      Should we do compression in a worker thread ?                   */
    1321             :     /* -------------------------------------------------------------------- */
    1322      196911 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    1323      182889 :                               : m_poCompressQueue.get();
    1324             : 
    1325      196911 :     if (poQueue && m_nCompression == COMPRESSION_NONE)
    1326             :     {
    1327             :         // We don't do multi-threaded compression for uncompressed...
    1328             :         // but we must wait for other related compression tasks (e.g mask)
    1329             :         // to be completed
    1330           0 :         poQueue->WaitCompletion();
    1331             : 
    1332             :         // Flush remaining data
    1333             :         // cppcheck-suppress constVariableReference
    1334           0 :         auto &oQueue =
    1335           0 :             m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    1336           0 :         while (!oQueue.empty())
    1337             :         {
    1338           0 :             WaitCompletionForJobIdx(oQueue.front());
    1339             :         }
    1340             :     }
    1341             : 
    1342             :     const auto SetupJob =
    1343      123008 :         [this, pabyData, cc, nHeight, nStripOrTile](GTiffCompressionJob &sJob)
    1344             :     {
    1345       26571 :         sJob.poDS = this;
    1346       26571 :         sJob.bTIFFIsBigEndian = CPL_TO_BOOL(TIFFIsBigEndian(m_hTIFF));
    1347             :         GByte *pabyBuffer =
    1348       26571 :             static_cast<GByte *>(VSI_REALLOC_VERBOSE(sJob.pabyBuffer, cc));
    1349       26571 :         if (!pabyBuffer)
    1350           0 :             return false;
    1351       26571 :         sJob.pabyBuffer = pabyBuffer;
    1352       26571 :         memcpy(sJob.pabyBuffer, pabyData, cc);
    1353       26571 :         sJob.nBufferSize = cc;
    1354       26571 :         sJob.nHeight = nHeight;
    1355       26571 :         sJob.nStripOrTile = nStripOrTile;
    1356       26571 :         sJob.nPredictor = PREDICTOR_NONE;
    1357       26571 :         if (GTIFFSupportsPredictor(m_nCompression))
    1358             :         {
    1359       16724 :             TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &sJob.nPredictor);
    1360             :         }
    1361             : 
    1362       26571 :         sJob.pExtraSamples = nullptr;
    1363       26571 :         sJob.nExtraSampleCount = 0;
    1364       26571 :         TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &sJob.nExtraSampleCount,
    1365             :                      &sJob.pExtraSamples);
    1366       26571 :         return true;
    1367      196911 :     };
    1368             : 
    1369      196911 :     if (poQueue == nullptr || !(m_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    1370         806 :                                 m_nCompression == COMPRESSION_LZW ||
    1371          78 :                                 m_nCompression == COMPRESSION_PACKBITS ||
    1372          72 :                                 m_nCompression == COMPRESSION_LZMA ||
    1373          62 :                                 m_nCompression == COMPRESSION_ZSTD ||
    1374          52 :                                 m_nCompression == COMPRESSION_LERC ||
    1375          46 :                                 m_nCompression == COMPRESSION_JXL ||
    1376          46 :                                 m_nCompression == COMPRESSION_JXL_DNG_1_7 ||
    1377          28 :                                 m_nCompression == COMPRESSION_WEBP ||
    1378          18 :                                 m_nCompression == COMPRESSION_JPEG))
    1379             :     {
    1380      195335 :         if (m_bBlockOrderRowMajor || m_bLeaderSizeAsUInt4 ||
    1381      170340 :             m_bTrailerRepeatedLast4BytesRepeated)
    1382             :         {
    1383             :             GTiffCompressionJob sJob;
    1384       24995 :             memset(&sJob, 0, sizeof(sJob));
    1385       24995 :             if (SetupJob(sJob))
    1386             :             {
    1387       24995 :                 sJob.pszTmpFilename =
    1388       24995 :                     CPLStrdup(VSIMemGenerateHiddenFilename("temp.tif"));
    1389             : 
    1390       24995 :                 ThreadCompressionFunc(&sJob);
    1391             : 
    1392       24995 :                 if (sJob.nCompressedBufferSize)
    1393             :                 {
    1394       24995 :                     sJob.poDS->WriteRawStripOrTile(sJob.nStripOrTile,
    1395             :                                                    sJob.pabyCompressedBuffer,
    1396             :                                                    sJob.nCompressedBufferSize);
    1397             :                 }
    1398             : 
    1399       24995 :                 CPLFree(sJob.pabyBuffer);
    1400       24995 :                 VSIUnlink(sJob.pszTmpFilename);
    1401       24995 :                 CPLFree(sJob.pszTmpFilename);
    1402       24995 :                 return sJob.nCompressedBufferSize > 0 && !m_bWriteError;
    1403             :             }
    1404             :         }
    1405             : 
    1406      170340 :         return false;
    1407             :     }
    1408             : 
    1409        1576 :     auto poMainDS = m_poBaseDS ? m_poBaseDS : this;
    1410        1576 :     auto &oQueue = poMainDS->m_asQueueJobIdx;
    1411        1576 :     auto &asJobs = poMainDS->m_asCompressionJobs;
    1412             : 
    1413        1576 :     int nNextCompressionJobAvail = -1;
    1414             : 
    1415        1576 :     if (oQueue.size() == asJobs.size())
    1416             :     {
    1417        1443 :         CPLAssert(!oQueue.empty());
    1418        1443 :         nNextCompressionJobAvail = oQueue.front();
    1419        1443 :         WaitCompletionForJobIdx(nNextCompressionJobAvail);
    1420             :     }
    1421             :     else
    1422             :     {
    1423         133 :         const int nJobs = static_cast<int>(asJobs.size());
    1424         324 :         for (int i = 0; i < nJobs; ++i)
    1425             :         {
    1426         324 :             if (asJobs[i].nBufferSize == 0)
    1427             :             {
    1428         133 :                 nNextCompressionJobAvail = i;
    1429         133 :                 break;
    1430             :             }
    1431             :         }
    1432             :     }
    1433        1576 :     CPLAssert(nNextCompressionJobAvail >= 0);
    1434             : 
    1435        1576 :     GTiffCompressionJob *psJob = &asJobs[nNextCompressionJobAvail];
    1436        1576 :     bool bOK = SetupJob(*psJob);
    1437        1576 :     if (bOK)
    1438             :     {
    1439        1576 :         poQueue->SubmitJob(ThreadCompressionFunc, psJob);
    1440        1576 :         oQueue.push(nNextCompressionJobAvail);
    1441             :     }
    1442             : 
    1443        1576 :     return bOK;
    1444             : }
    1445             : 
    1446             : /************************************************************************/
    1447             : /*                          DiscardLsb()                                */
    1448             : /************************************************************************/
    1449             : 
    1450         272 : template <class T> bool MustNotDiscardLsb(T value, bool bHasNoData, T nodata)
    1451             : {
    1452         272 :     return bHasNoData && value == nodata;
    1453             : }
    1454             : 
    1455             : template <>
    1456          44 : bool MustNotDiscardLsb<float>(float value, bool bHasNoData, float nodata)
    1457             : {
    1458          44 :     return (bHasNoData && value == nodata) || !std::isfinite(value);
    1459             : }
    1460             : 
    1461             : template <>
    1462          44 : bool MustNotDiscardLsb<double>(double value, bool bHasNoData, double nodata)
    1463             : {
    1464          44 :     return (bHasNoData && value == nodata) || !std::isfinite(value);
    1465             : }
    1466             : 
    1467             : template <class T> T AdjustValue(T value, uint64_t nRoundUpBitTest);
    1468             : 
    1469          10 : template <class T> T AdjustValueInt(T value, uint64_t nRoundUpBitTest)
    1470             : {
    1471          10 :     if (value >=
    1472          10 :         static_cast<T>(std::numeric_limits<T>::max() - (nRoundUpBitTest << 1)))
    1473           0 :         return static_cast<T>(value - (nRoundUpBitTest << 1));
    1474          10 :     return static_cast<T>(value + (nRoundUpBitTest << 1));
    1475             : }
    1476             : 
    1477           0 : template <> int8_t AdjustValue<int8_t>(int8_t value, uint64_t nRoundUpBitTest)
    1478             : {
    1479           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1480             : }
    1481             : 
    1482             : template <>
    1483           2 : uint8_t AdjustValue<uint8_t>(uint8_t value, uint64_t nRoundUpBitTest)
    1484             : {
    1485           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1486             : }
    1487             : 
    1488             : template <>
    1489           2 : int16_t AdjustValue<int16_t>(int16_t value, uint64_t nRoundUpBitTest)
    1490             : {
    1491           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1492             : }
    1493             : 
    1494             : template <>
    1495           2 : uint16_t AdjustValue<uint16_t>(uint16_t value, uint64_t nRoundUpBitTest)
    1496             : {
    1497           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1498             : }
    1499             : 
    1500             : template <>
    1501           2 : int32_t AdjustValue<int32_t>(int32_t value, uint64_t nRoundUpBitTest)
    1502             : {
    1503           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1504             : }
    1505             : 
    1506             : template <>
    1507           2 : uint32_t AdjustValue<uint32_t>(uint32_t value, uint64_t nRoundUpBitTest)
    1508             : {
    1509           2 :     return AdjustValueInt(value, nRoundUpBitTest);
    1510             : }
    1511             : 
    1512             : template <>
    1513           0 : int64_t AdjustValue<int64_t>(int64_t value, uint64_t nRoundUpBitTest)
    1514             : {
    1515           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1516             : }
    1517             : 
    1518             : template <>
    1519           0 : uint64_t AdjustValue<uint64_t>(uint64_t value, uint64_t nRoundUpBitTest)
    1520             : {
    1521           0 :     return AdjustValueInt(value, nRoundUpBitTest);
    1522             : }
    1523             : 
    1524           0 : template <> GFloat16 AdjustValue<GFloat16>(GFloat16 value, uint64_t)
    1525             : {
    1526             :     using std::nextafter;
    1527           0 :     return nextafter(value, cpl::NumericLimits<GFloat16>::max());
    1528             : }
    1529             : 
    1530           0 : template <> float AdjustValue<float>(float value, uint64_t)
    1531             : {
    1532           0 :     return std::nextafter(value, std::numeric_limits<float>::max());
    1533             : }
    1534             : 
    1535           0 : template <> double AdjustValue<double>(double value, uint64_t)
    1536             : {
    1537           0 :     return std::nextafter(value, std::numeric_limits<double>::max());
    1538             : }
    1539             : 
    1540             : template <class Teffective, class T>
    1541             : T RoundValueDiscardLsb(const void *ptr, uint64_t nMask,
    1542             :                        uint64_t nRoundUpBitTest);
    1543             : 
    1544             : template <class T>
    1545          16 : T RoundValueDiscardLsbUnsigned(const void *ptr, uint64_t nMask,
    1546             :                                uint64_t nRoundUpBitTest)
    1547             : {
    1548          32 :     if ((*reinterpret_cast<const T *>(ptr) & nMask) >
    1549          16 :         static_cast<uint64_t>(std::numeric_limits<T>::max()) -
    1550          16 :             (nRoundUpBitTest << 1U))
    1551             :     {
    1552           4 :         return static_cast<T>(std::numeric_limits<T>::max() & nMask);
    1553             :     }
    1554          12 :     const uint64_t newval =
    1555          12 :         (*reinterpret_cast<const T *>(ptr) & nMask) + (nRoundUpBitTest << 1U);
    1556          12 :     return static_cast<T>(newval);
    1557             : }
    1558             : 
    1559             : template <class T>
    1560          18 : T RoundValueDiscardLsbSigned(const void *ptr, uint64_t nMask,
    1561             :                              uint64_t nRoundUpBitTest)
    1562             : {
    1563          18 :     T oldval = *reinterpret_cast<const T *>(ptr);
    1564          18 :     if (oldval < 0)
    1565             :     {
    1566           4 :         return static_cast<T>(oldval & nMask);
    1567             :     }
    1568          14 :     const uint64_t newval =
    1569          14 :         (*reinterpret_cast<const T *>(ptr) & nMask) + (nRoundUpBitTest << 1U);
    1570          14 :     if (newval > static_cast<uint64_t>(std::numeric_limits<T>::max()))
    1571           4 :         return static_cast<T>(std::numeric_limits<T>::max() & nMask);
    1572          10 :     return static_cast<T>(newval);
    1573             : }
    1574             : 
    1575             : template <>
    1576          11 : uint16_t RoundValueDiscardLsb<uint16_t, uint16_t>(const void *ptr,
    1577             :                                                   uint64_t nMask,
    1578             :                                                   uint64_t nRoundUpBitTest)
    1579             : {
    1580          11 :     return RoundValueDiscardLsbUnsigned<uint16_t>(ptr, nMask, nRoundUpBitTest);
    1581             : }
    1582             : 
    1583             : template <>
    1584           5 : uint32_t RoundValueDiscardLsb<uint32_t, uint32_t>(const void *ptr,
    1585             :                                                   uint64_t nMask,
    1586             :                                                   uint64_t nRoundUpBitTest)
    1587             : {
    1588           5 :     return RoundValueDiscardLsbUnsigned<uint32_t>(ptr, nMask, nRoundUpBitTest);
    1589             : }
    1590             : 
    1591             : template <>
    1592           0 : uint64_t RoundValueDiscardLsb<uint64_t, uint64_t>(const void *ptr,
    1593             :                                                   uint64_t nMask,
    1594             :                                                   uint64_t nRoundUpBitTest)
    1595             : {
    1596           0 :     return RoundValueDiscardLsbUnsigned<uint64_t>(ptr, nMask, nRoundUpBitTest);
    1597             : }
    1598             : 
    1599             : template <>
    1600           0 : int8_t RoundValueDiscardLsb<int8_t, int8_t>(const void *ptr, uint64_t nMask,
    1601             :                                             uint64_t nRoundUpBitTest)
    1602             : {
    1603           0 :     return RoundValueDiscardLsbSigned<int8_t>(ptr, nMask, nRoundUpBitTest);
    1604             : }
    1605             : 
    1606             : template <>
    1607          13 : int16_t RoundValueDiscardLsb<int16_t, int16_t>(const void *ptr, uint64_t nMask,
    1608             :                                                uint64_t nRoundUpBitTest)
    1609             : {
    1610          13 :     return RoundValueDiscardLsbSigned<int16_t>(ptr, nMask, nRoundUpBitTest);
    1611             : }
    1612             : 
    1613             : template <>
    1614           5 : int32_t RoundValueDiscardLsb<int32_t, int32_t>(const void *ptr, uint64_t nMask,
    1615             :                                                uint64_t nRoundUpBitTest)
    1616             : {
    1617           5 :     return RoundValueDiscardLsbSigned<int32_t>(ptr, nMask, nRoundUpBitTest);
    1618             : }
    1619             : 
    1620             : template <>
    1621           0 : int64_t RoundValueDiscardLsb<int64_t, int64_t>(const void *ptr, uint64_t nMask,
    1622             :                                                uint64_t nRoundUpBitTest)
    1623             : {
    1624           0 :     return RoundValueDiscardLsbSigned<int64_t>(ptr, nMask, nRoundUpBitTest);
    1625             : }
    1626             : 
    1627             : template <>
    1628           0 : uint16_t RoundValueDiscardLsb<GFloat16, uint16_t>(const void *ptr,
    1629             :                                                   uint64_t nMask,
    1630             :                                                   uint64_t nRoundUpBitTest)
    1631             : {
    1632           0 :     return RoundValueDiscardLsbUnsigned<uint16_t>(ptr, nMask, nRoundUpBitTest);
    1633             : }
    1634             : 
    1635             : template <>
    1636           0 : uint32_t RoundValueDiscardLsb<float, uint32_t>(const void *ptr, uint64_t nMask,
    1637             :                                                uint64_t nRoundUpBitTest)
    1638             : {
    1639           0 :     return RoundValueDiscardLsbUnsigned<uint32_t>(ptr, nMask, nRoundUpBitTest);
    1640             : }
    1641             : 
    1642             : template <>
    1643           0 : uint64_t RoundValueDiscardLsb<double, uint64_t>(const void *ptr, uint64_t nMask,
    1644             :                                                 uint64_t nRoundUpBitTest)
    1645             : {
    1646           0 :     return RoundValueDiscardLsbUnsigned<uint64_t>(ptr, nMask, nRoundUpBitTest);
    1647             : }
    1648             : 
    1649             : template <class Teffective, class T>
    1650         145 : static void DiscardLsbT(GByte *pabyBuffer, size_t nBytes, int iBand, int nBands,
    1651             :                         uint16_t nPlanarConfig,
    1652             :                         const GTiffDataset::MaskOffset *panMaskOffsetLsb,
    1653             :                         bool bHasNoData, Teffective nNoDataValue)
    1654             : {
    1655             :     static_assert(sizeof(Teffective) == sizeof(T),
    1656             :                   "sizeof(Teffective) == sizeof(T)");
    1657         145 :     if (nPlanarConfig == PLANARCONFIG_SEPARATE)
    1658             :     {
    1659          98 :         const auto nMask = panMaskOffsetLsb[iBand].nMask;
    1660          98 :         const auto nRoundUpBitTest = panMaskOffsetLsb[iBand].nRoundUpBitTest;
    1661         196 :         for (size_t i = 0; i < nBytes / sizeof(T); ++i)
    1662             :         {
    1663          98 :             if (MustNotDiscardLsb(reinterpret_cast<Teffective *>(pabyBuffer)[i],
    1664             :                                   bHasNoData, nNoDataValue))
    1665             :             {
    1666          22 :                 continue;
    1667             :             }
    1668             : 
    1669          76 :             if (reinterpret_cast<T *>(pabyBuffer)[i] & nRoundUpBitTest)
    1670             :             {
    1671          30 :                 reinterpret_cast<T *>(pabyBuffer)[i] =
    1672          15 :                     RoundValueDiscardLsb<Teffective, T>(
    1673          15 :                         &(reinterpret_cast<T *>(pabyBuffer)[i]), nMask,
    1674             :                         nRoundUpBitTest);
    1675             :             }
    1676             :             else
    1677             :             {
    1678          61 :                 reinterpret_cast<T *>(pabyBuffer)[i] = static_cast<T>(
    1679          61 :                     reinterpret_cast<T *>(pabyBuffer)[i] & nMask);
    1680             :             }
    1681             : 
    1682             :             // Make sure that by discarding LSB we don't end up to a value
    1683             :             // that is no the nodata value
    1684          76 :             if (MustNotDiscardLsb(reinterpret_cast<Teffective *>(pabyBuffer)[i],
    1685             :                                   bHasNoData, nNoDataValue))
    1686             :             {
    1687           8 :                 reinterpret_cast<Teffective *>(pabyBuffer)[i] =
    1688           4 :                     AdjustValue(nNoDataValue, nRoundUpBitTest);
    1689             :             }
    1690             :         }
    1691             :     }
    1692             :     else
    1693             :     {
    1694          94 :         for (size_t i = 0; i < nBytes / sizeof(T); i += nBands)
    1695             :         {
    1696         147 :             for (int j = 0; j < nBands; ++j)
    1697             :             {
    1698         100 :                 if (MustNotDiscardLsb(
    1699         100 :                         reinterpret_cast<Teffective *>(pabyBuffer)[i + j],
    1700             :                         bHasNoData, nNoDataValue))
    1701             :                 {
    1702          14 :                     continue;
    1703             :                 }
    1704             : 
    1705          86 :                 if (reinterpret_cast<T *>(pabyBuffer)[i + j] &
    1706          86 :                     panMaskOffsetLsb[j].nRoundUpBitTest)
    1707             :                 {
    1708          38 :                     reinterpret_cast<T *>(pabyBuffer)[i + j] =
    1709          19 :                         RoundValueDiscardLsb<Teffective, T>(
    1710          19 :                             &(reinterpret_cast<T *>(pabyBuffer)[i + j]),
    1711          19 :                             panMaskOffsetLsb[j].nMask,
    1712          19 :                             panMaskOffsetLsb[j].nRoundUpBitTest);
    1713             :                 }
    1714             :                 else
    1715             :                 {
    1716          67 :                     reinterpret_cast<T *>(pabyBuffer)[i + j] = static_cast<T>(
    1717          67 :                         (reinterpret_cast<T *>(pabyBuffer)[i + j] &
    1718          67 :                          panMaskOffsetLsb[j].nMask));
    1719             :                 }
    1720             : 
    1721             :                 // Make sure that by discarding LSB we don't end up to a value
    1722             :                 // that is no the nodata value
    1723          86 :                 if (MustNotDiscardLsb(
    1724          86 :                         reinterpret_cast<Teffective *>(pabyBuffer)[i + j],
    1725             :                         bHasNoData, nNoDataValue))
    1726             :                 {
    1727           8 :                     reinterpret_cast<Teffective *>(pabyBuffer)[i + j] =
    1728           4 :                         AdjustValue(nNoDataValue,
    1729           4 :                                     panMaskOffsetLsb[j].nRoundUpBitTest);
    1730             :                 }
    1731             :             }
    1732             :         }
    1733             :     }
    1734         145 : }
    1735             : 
    1736         183 : static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand,
    1737             :                        int nBands, uint16_t nSampleFormat,
    1738             :                        uint16_t nBitsPerSample, uint16_t nPlanarConfig,
    1739             :                        const GTiffDataset::MaskOffset *panMaskOffsetLsb,
    1740             :                        bool bHasNoData, double dfNoDataValue)
    1741             : {
    1742         183 :     if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_UINT)
    1743             :     {
    1744          38 :         uint8_t nNoDataValue = 0;
    1745          38 :         if (bHasNoData && GDALIsValueExactAs<uint8_t>(dfNoDataValue))
    1746             :         {
    1747           6 :             nNoDataValue = static_cast<uint8_t>(dfNoDataValue);
    1748             :         }
    1749             :         else
    1750             :         {
    1751          32 :             bHasNoData = false;
    1752             :         }
    1753          38 :         if (nPlanarConfig == PLANARCONFIG_SEPARATE)
    1754             :         {
    1755          25 :             const auto nMask =
    1756          25 :                 static_cast<unsigned>(panMaskOffsetLsb[iBand].nMask);
    1757          25 :             const auto nRoundUpBitTest =
    1758          25 :                 static_cast<unsigned>(panMaskOffsetLsb[iBand].nRoundUpBitTest);
    1759          50 :             for (decltype(nBytes) i = 0; i < nBytes; ++i)
    1760             :             {
    1761          25 :                 if (bHasNoData && pabyBuffer[i] == nNoDataValue)
    1762           3 :                     continue;
    1763             : 
    1764             :                 // Keep 255 in case it is alpha.
    1765          22 :                 if (pabyBuffer[i] != 255)
    1766             :                 {
    1767          21 :                     if (pabyBuffer[i] & nRoundUpBitTest)
    1768           5 :                         pabyBuffer[i] = static_cast<GByte>(
    1769           5 :                             std::min(255U, (pabyBuffer[i] & nMask) +
    1770           5 :                                                (nRoundUpBitTest << 1U)));
    1771             :                     else
    1772          16 :                         pabyBuffer[i] =
    1773          16 :                             static_cast<GByte>(pabyBuffer[i] & nMask);
    1774             : 
    1775             :                     // Make sure that by discarding LSB we don't end up to a
    1776             :                     // value that is no the nodata value
    1777          21 :                     if (bHasNoData && pabyBuffer[i] == nNoDataValue)
    1778           2 :                         pabyBuffer[i] =
    1779           1 :                             AdjustValue(nNoDataValue, nRoundUpBitTest);
    1780             :                 }
    1781             :             }
    1782             :         }
    1783             :         else
    1784             :         {
    1785          26 :             for (decltype(nBytes) i = 0; i < nBytes; i += nBands)
    1786             :             {
    1787          42 :                 for (int j = 0; j < nBands; ++j)
    1788             :                 {
    1789          29 :                     if (bHasNoData && pabyBuffer[i + j] == nNoDataValue)
    1790           2 :                         continue;
    1791             : 
    1792             :                     // Keep 255 in case it is alpha.
    1793          27 :                     if (pabyBuffer[i + j] != 255)
    1794             :                     {
    1795          25 :                         if (pabyBuffer[i + j] &
    1796          25 :                             panMaskOffsetLsb[j].nRoundUpBitTest)
    1797             :                         {
    1798           6 :                             pabyBuffer[i + j] = static_cast<GByte>(std::min(
    1799          12 :                                 255U,
    1800           6 :                                 (pabyBuffer[i + j] &
    1801             :                                  static_cast<unsigned>(
    1802           6 :                                      panMaskOffsetLsb[j].nMask)) +
    1803             :                                     (static_cast<unsigned>(
    1804           6 :                                          panMaskOffsetLsb[j].nRoundUpBitTest)
    1805           6 :                                      << 1U)));
    1806             :                         }
    1807             :                         else
    1808             :                         {
    1809          19 :                             pabyBuffer[i + j] = static_cast<GByte>(
    1810          19 :                                 pabyBuffer[i + j] & panMaskOffsetLsb[j].nMask);
    1811             :                         }
    1812             : 
    1813             :                         // Make sure that by discarding LSB we don't end up to a
    1814             :                         // value that is no the nodata value
    1815          25 :                         if (bHasNoData && pabyBuffer[i + j] == nNoDataValue)
    1816           1 :                             pabyBuffer[i + j] = AdjustValue(
    1817             :                                 nNoDataValue,
    1818           1 :                                 panMaskOffsetLsb[j].nRoundUpBitTest);
    1819             :                     }
    1820             :                 }
    1821             :             }
    1822          38 :         }
    1823             :     }
    1824         145 :     else if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_INT)
    1825             :     {
    1826           0 :         int8_t nNoDataValue = 0;
    1827           0 :         if (bHasNoData && GDALIsValueExactAs<int8_t>(dfNoDataValue))
    1828             :         {
    1829           0 :             nNoDataValue = static_cast<int8_t>(dfNoDataValue);
    1830             :         }
    1831             :         else
    1832             :         {
    1833           0 :             bHasNoData = false;
    1834             :         }
    1835           0 :         DiscardLsbT<int8_t, int8_t>(pabyBuffer, nBytes, iBand, nBands,
    1836             :                                     nPlanarConfig, panMaskOffsetLsb, bHasNoData,
    1837           0 :                                     nNoDataValue);
    1838             :     }
    1839         145 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_INT)
    1840             :     {
    1841          48 :         int16_t nNoDataValue = 0;
    1842          48 :         if (bHasNoData && GDALIsValueExactAs<int16_t>(dfNoDataValue))
    1843             :         {
    1844           6 :             nNoDataValue = static_cast<int16_t>(dfNoDataValue);
    1845             :         }
    1846             :         else
    1847             :         {
    1848          42 :             bHasNoData = false;
    1849             :         }
    1850          48 :         DiscardLsbT<int16_t, int16_t>(pabyBuffer, nBytes, iBand, nBands,
    1851             :                                       nPlanarConfig, panMaskOffsetLsb,
    1852          48 :                                       bHasNoData, nNoDataValue);
    1853             :     }
    1854          97 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_UINT)
    1855             :     {
    1856          33 :         uint16_t nNoDataValue = 0;
    1857          33 :         if (bHasNoData && GDALIsValueExactAs<uint16_t>(dfNoDataValue))
    1858             :         {
    1859           6 :             nNoDataValue = static_cast<uint16_t>(dfNoDataValue);
    1860             :         }
    1861             :         else
    1862             :         {
    1863          27 :             bHasNoData = false;
    1864             :         }
    1865          33 :         DiscardLsbT<uint16_t, uint16_t>(pabyBuffer, nBytes, iBand, nBands,
    1866             :                                         nPlanarConfig, panMaskOffsetLsb,
    1867          33 :                                         bHasNoData, nNoDataValue);
    1868             :     }
    1869          64 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_INT)
    1870             :     {
    1871          13 :         int32_t nNoDataValue = 0;
    1872          13 :         if (bHasNoData && GDALIsValueExactAs<int32_t>(dfNoDataValue))
    1873             :         {
    1874           6 :             nNoDataValue = static_cast<int32_t>(dfNoDataValue);
    1875             :         }
    1876             :         else
    1877             :         {
    1878           7 :             bHasNoData = false;
    1879             :         }
    1880          13 :         DiscardLsbT<int32_t, int32_t>(pabyBuffer, nBytes, iBand, nBands,
    1881             :                                       nPlanarConfig, panMaskOffsetLsb,
    1882          13 :                                       bHasNoData, nNoDataValue);
    1883             :     }
    1884          51 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_UINT)
    1885             :     {
    1886          13 :         uint32_t nNoDataValue = 0;
    1887          13 :         if (bHasNoData && GDALIsValueExactAs<uint32_t>(dfNoDataValue))
    1888             :         {
    1889           6 :             nNoDataValue = static_cast<uint32_t>(dfNoDataValue);
    1890             :         }
    1891             :         else
    1892             :         {
    1893           7 :             bHasNoData = false;
    1894             :         }
    1895          13 :         DiscardLsbT<uint32_t, uint32_t>(pabyBuffer, nBytes, iBand, nBands,
    1896             :                                         nPlanarConfig, panMaskOffsetLsb,
    1897          13 :                                         bHasNoData, nNoDataValue);
    1898             :     }
    1899          38 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_INT)
    1900             :     {
    1901             :         // FIXME: we should not rely on dfNoDataValue when we support native
    1902             :         // data type for nodata
    1903           0 :         int64_t nNoDataValue = 0;
    1904           0 :         if (bHasNoData && GDALIsValueExactAs<int64_t>(dfNoDataValue))
    1905             :         {
    1906           0 :             nNoDataValue = static_cast<int64_t>(dfNoDataValue);
    1907             :         }
    1908             :         else
    1909             :         {
    1910           0 :             bHasNoData = false;
    1911             :         }
    1912           0 :         DiscardLsbT<int64_t, int64_t>(pabyBuffer, nBytes, iBand, nBands,
    1913             :                                       nPlanarConfig, panMaskOffsetLsb,
    1914           0 :                                       bHasNoData, nNoDataValue);
    1915             :     }
    1916          38 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_UINT)
    1917             :     {
    1918             :         // FIXME: we should not rely on dfNoDataValue when we support native
    1919             :         // data type for nodata
    1920           0 :         uint64_t nNoDataValue = 0;
    1921           0 :         if (bHasNoData && GDALIsValueExactAs<uint64_t>(dfNoDataValue))
    1922             :         {
    1923           0 :             nNoDataValue = static_cast<uint64_t>(dfNoDataValue);
    1924             :         }
    1925             :         else
    1926             :         {
    1927           0 :             bHasNoData = false;
    1928             :         }
    1929           0 :         DiscardLsbT<uint64_t, uint64_t>(pabyBuffer, nBytes, iBand, nBands,
    1930             :                                         nPlanarConfig, panMaskOffsetLsb,
    1931           0 :                                         bHasNoData, nNoDataValue);
    1932             :     }
    1933          38 :     else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1934             :     {
    1935           0 :         const GFloat16 fNoDataValue = static_cast<GFloat16>(dfNoDataValue);
    1936           0 :         DiscardLsbT<GFloat16, uint16_t>(pabyBuffer, nBytes, iBand, nBands,
    1937             :                                         nPlanarConfig, panMaskOffsetLsb,
    1938           0 :                                         bHasNoData, fNoDataValue);
    1939             :     }
    1940          38 :     else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1941             :     {
    1942          19 :         const float fNoDataValue = static_cast<float>(dfNoDataValue);
    1943          19 :         DiscardLsbT<float, uint32_t>(pabyBuffer, nBytes, iBand, nBands,
    1944             :                                      nPlanarConfig, panMaskOffsetLsb,
    1945          19 :                                      bHasNoData, fNoDataValue);
    1946             :     }
    1947          19 :     else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
    1948             :     {
    1949          19 :         DiscardLsbT<double, uint64_t>(pabyBuffer, nBytes, iBand, nBands,
    1950             :                                       nPlanarConfig, panMaskOffsetLsb,
    1951             :                                       bHasNoData, dfNoDataValue);
    1952             :     }
    1953         183 : }
    1954             : 
    1955         183 : void GTiffDataset::DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes,
    1956             :                               int iBand) const
    1957             : {
    1958         183 :     ::DiscardLsb(pabyBuffer, nBytes, iBand, nBands, m_nSampleFormat,
    1959         183 :                  m_nBitsPerSample, m_nPlanarConfig, m_panMaskOffsetLsb,
    1960         183 :                  m_bNoDataSet, m_dfNoDataValue);
    1961         183 : }
    1962             : 
    1963             : /************************************************************************/
    1964             : /*                  WriteEncodedTileOrStrip()                           */
    1965             : /************************************************************************/
    1966             : 
    1967      227809 : CPLErr GTiffDataset::WriteEncodedTileOrStrip(uint32_t tile_or_strip, void *data,
    1968             :                                              int bPreserveDataBuffer)
    1969             : {
    1970      227809 :     CPLErr eErr = CE_None;
    1971             : 
    1972      227809 :     if (TIFFIsTiled(m_hTIFF))
    1973             :     {
    1974       50292 :         if (!(WriteEncodedTile(tile_or_strip, static_cast<GByte *>(data),
    1975             :                                bPreserveDataBuffer)))
    1976             :         {
    1977          14 :             eErr = CE_Failure;
    1978             :         }
    1979             :     }
    1980             :     else
    1981             :     {
    1982      177517 :         if (!(WriteEncodedStrip(tile_or_strip, static_cast<GByte *>(data),
    1983             :                                 bPreserveDataBuffer)))
    1984             :         {
    1985           8 :             eErr = CE_Failure;
    1986             :         }
    1987             :     }
    1988             : 
    1989      227809 :     return eErr;
    1990             : }
    1991             : 
    1992             : /************************************************************************/
    1993             : /*                           FlushBlockBuf()                            */
    1994             : /************************************************************************/
    1995             : 
    1996        9602 : CPLErr GTiffDataset::FlushBlockBuf()
    1997             : 
    1998             : {
    1999        9602 :     if (m_nLoadedBlock < 0 || !m_bLoadedBlockDirty)
    2000           0 :         return CE_None;
    2001             : 
    2002        9602 :     m_bLoadedBlockDirty = false;
    2003             : 
    2004             :     const CPLErr eErr =
    2005        9602 :         WriteEncodedTileOrStrip(m_nLoadedBlock, m_pabyBlockBuf, true);
    2006        9602 :     if (eErr != CE_None)
    2007             :     {
    2008           0 :         ReportError(CE_Failure, CPLE_AppDefined,
    2009             :                     "WriteEncodedTile/Strip() failed.");
    2010           0 :         m_bWriteError = true;
    2011             :     }
    2012             : 
    2013        9602 :     return eErr;
    2014             : }
    2015             : 
    2016             : /************************************************************************/
    2017             : /*                   GTiffFillStreamableOffsetAndCount()                */
    2018             : /************************************************************************/
    2019             : 
    2020           8 : static void GTiffFillStreamableOffsetAndCount(TIFF *hTIFF, int nSize)
    2021             : {
    2022           8 :     uint32_t nXSize = 0;
    2023           8 :     uint32_t nYSize = 0;
    2024           8 :     TIFFGetField(hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize);
    2025           8 :     TIFFGetField(hTIFF, TIFFTAG_IMAGELENGTH, &nYSize);
    2026           8 :     const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(hTIFF));
    2027             :     const int nBlockCount =
    2028           8 :         bIsTiled ? TIFFNumberOfTiles(hTIFF) : TIFFNumberOfStrips(hTIFF);
    2029             : 
    2030           8 :     toff_t *panOffset = nullptr;
    2031           8 :     TIFFGetField(hTIFF, bIsTiled ? TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS,
    2032             :                  &panOffset);
    2033           8 :     toff_t *panSize = nullptr;
    2034           8 :     TIFFGetField(hTIFF,
    2035             :                  bIsTiled ? TIFFTAG_TILEBYTECOUNTS : TIFFTAG_STRIPBYTECOUNTS,
    2036             :                  &panSize);
    2037           8 :     toff_t nOffset = nSize;
    2038             :     // Trick to avoid clang static analyzer raising false positive about
    2039             :     // divide by zero later.
    2040           8 :     int nBlocksPerBand = 1;
    2041           8 :     uint32_t nRowsPerStrip = 0;
    2042           8 :     if (!bIsTiled)
    2043             :     {
    2044           6 :         TIFFGetField(hTIFF, TIFFTAG_ROWSPERSTRIP, &nRowsPerStrip);
    2045           6 :         if (nRowsPerStrip > static_cast<uint32_t>(nYSize))
    2046           0 :             nRowsPerStrip = nYSize;
    2047           6 :         nBlocksPerBand = DIV_ROUND_UP(nYSize, nRowsPerStrip);
    2048             :     }
    2049        2947 :     for (int i = 0; i < nBlockCount; ++i)
    2050             :     {
    2051             :         GPtrDiff_t cc = bIsTiled
    2052        2939 :                             ? static_cast<GPtrDiff_t>(TIFFTileSize(hTIFF))
    2053        2907 :                             : static_cast<GPtrDiff_t>(TIFFStripSize(hTIFF));
    2054        2939 :         if (!bIsTiled)
    2055             :         {
    2056             :             /* --------------------------------------------------------------------
    2057             :              */
    2058             :             /*      If this is the last strip in the image, and is partial, then
    2059             :              */
    2060             :             /*      we need to trim the number of scanlines written to the */
    2061             :             /*      amount of valid data we have. (#2748) */
    2062             :             /* --------------------------------------------------------------------
    2063             :              */
    2064        2907 :             int nStripWithinBand = i % nBlocksPerBand;
    2065        2907 :             if (nStripWithinBand * nRowsPerStrip > nYSize - nRowsPerStrip)
    2066             :             {
    2067           1 :                 cc = (cc / nRowsPerStrip) *
    2068           1 :                      (nYSize - nStripWithinBand * nRowsPerStrip);
    2069             :             }
    2070             :         }
    2071        2939 :         panOffset[i] = nOffset;
    2072        2939 :         panSize[i] = cc;
    2073        2939 :         nOffset += cc;
    2074             :     }
    2075           8 : }
    2076             : 
    2077             : /************************************************************************/
    2078             : /*                             Crystalize()                             */
    2079             : /*                                                                      */
    2080             : /*      Make sure that the directory information is written out for     */
    2081             : /*      a new file, require before writing any imagery data.            */
    2082             : /************************************************************************/
    2083             : 
    2084     2650880 : void GTiffDataset::Crystalize()
    2085             : 
    2086             : {
    2087     2650880 :     if (m_bCrystalized)
    2088     2645270 :         return;
    2089             : 
    2090             :     // TODO: libtiff writes extended tags in the order they are specified
    2091             :     // and not in increasing order.
    2092        5611 :     WriteMetadata(this, m_hTIFF, true, m_eProfile, m_osFilename.c_str(),
    2093        5611 :                   m_papszCreationOptions);
    2094        5611 :     WriteGeoTIFFInfo();
    2095        5611 :     if (m_bNoDataSet)
    2096         312 :         WriteNoDataValue(m_hTIFF, m_dfNoDataValue);
    2097        5299 :     else if (m_bNoDataSetAsInt64)
    2098           2 :         WriteNoDataValue(m_hTIFF, m_nNoDataValueInt64);
    2099        5297 :     else if (m_bNoDataSetAsUInt64)
    2100           2 :         WriteNoDataValue(m_hTIFF, m_nNoDataValueUInt64);
    2101             : 
    2102        5611 :     m_bMetadataChanged = false;
    2103        5611 :     m_bGeoTIFFInfoChanged = false;
    2104        5611 :     m_bNoDataChanged = false;
    2105        5611 :     m_bNeedsRewrite = false;
    2106             : 
    2107        5611 :     m_bCrystalized = true;
    2108             : 
    2109        5611 :     TIFFWriteCheck(m_hTIFF, TIFFIsTiled(m_hTIFF), "GTiffDataset::Crystalize");
    2110             : 
    2111        5611 :     TIFFWriteDirectory(m_hTIFF);
    2112        5611 :     if (m_bStreamingOut)
    2113             :     {
    2114             :         // We need to write twice the directory to be sure that custom
    2115             :         // TIFF tags are correctly sorted and that padding bytes have been
    2116             :         // added.
    2117           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2118           3 :         TIFFWriteDirectory(m_hTIFF);
    2119             : 
    2120           3 :         if (VSIFSeekL(m_fpL, 0, SEEK_END) != 0)
    2121             :         {
    2122           0 :             ReportError(CE_Failure, CPLE_FileIO, "Could not seek");
    2123             :         }
    2124           3 :         const int nSize = static_cast<int>(VSIFTellL(m_fpL));
    2125             : 
    2126           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2127           3 :         GTiffFillStreamableOffsetAndCount(m_hTIFF, nSize);
    2128           3 :         TIFFWriteDirectory(m_hTIFF);
    2129             : 
    2130           3 :         vsi_l_offset nDataLength = 0;
    2131             :         void *pabyBuffer =
    2132           3 :             VSIGetMemFileBuffer(m_pszTmpFilename, &nDataLength, FALSE);
    2133           3 :         if (static_cast<int>(VSIFWriteL(
    2134           3 :                 pabyBuffer, 1, static_cast<int>(nDataLength), m_fpToWrite)) !=
    2135             :             static_cast<int>(nDataLength))
    2136             :         {
    2137           0 :             ReportError(CE_Failure, CPLE_FileIO, "Could not write %d bytes",
    2138             :                         static_cast<int>(nDataLength));
    2139             :         }
    2140             :         // In case of single strip file, there's a libtiff check that would
    2141             :         // issue a warning since the file hasn't the required size.
    2142           3 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2143           3 :         TIFFSetDirectory(m_hTIFF, 0);
    2144           3 :         CPLPopErrorHandler();
    2145             :     }
    2146             :     else
    2147             :     {
    2148        5608 :         const tdir_t nNumberOfDirs = TIFFNumberOfDirectories(m_hTIFF);
    2149        5608 :         if (nNumberOfDirs > 0)
    2150             :         {
    2151        5608 :             TIFFSetDirectory(m_hTIFF, static_cast<tdir_t>(nNumberOfDirs - 1));
    2152             :         }
    2153             :     }
    2154             : 
    2155        5611 :     RestoreVolatileParameters(m_hTIFF);
    2156             : 
    2157        5611 :     m_nDirOffset = TIFFCurrentDirOffset(m_hTIFF);
    2158             : }
    2159             : 
    2160             : /************************************************************************/
    2161             : /*                             FlushCache()                             */
    2162             : /*                                                                      */
    2163             : /*      We override this so we can also flush out local tiff strip      */
    2164             : /*      cache if need be.                                               */
    2165             : /************************************************************************/
    2166             : 
    2167        4506 : CPLErr GTiffDataset::FlushCache(bool bAtClosing)
    2168             : 
    2169             : {
    2170        4506 :     return FlushCacheInternal(bAtClosing, true);
    2171             : }
    2172             : 
    2173       45822 : CPLErr GTiffDataset::FlushCacheInternal(bool bAtClosing, bool bFlushDirectory)
    2174             : {
    2175       45822 :     if (m_bIsFinalized)
    2176           1 :         return CE_None;
    2177             : 
    2178       45821 :     CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
    2179             : 
    2180       45821 :     if (m_bLoadedBlockDirty && m_nLoadedBlock != -1)
    2181             :     {
    2182         262 :         if (FlushBlockBuf() != CE_None)
    2183           0 :             eErr = CE_Failure;
    2184             :     }
    2185             : 
    2186       45821 :     CPLFree(m_pabyBlockBuf);
    2187       45821 :     m_pabyBlockBuf = nullptr;
    2188       45821 :     m_nLoadedBlock = -1;
    2189       45821 :     m_bLoadedBlockDirty = false;
    2190             : 
    2191             :     // Finish compression
    2192       45821 :     auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
    2193       43507 :                               : m_poCompressQueue.get();
    2194       45821 :     if (poQueue)
    2195             :     {
    2196         161 :         poQueue->WaitCompletion();
    2197             : 
    2198             :         // Flush remaining data
    2199             :         // cppcheck-suppress constVariableReference
    2200             : 
    2201         161 :         auto &oQueue =
    2202         161 :             m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
    2203         230 :         while (!oQueue.empty())
    2204             :         {
    2205          69 :             WaitCompletionForJobIdx(oQueue.front());
    2206             :         }
    2207             :     }
    2208             : 
    2209       45821 :     if (bFlushDirectory && GetAccess() == GA_Update)
    2210             :     {
    2211       13677 :         if (FlushDirectory() != CE_None)
    2212          12 :             eErr = CE_Failure;
    2213             :     }
    2214       45821 :     return eErr;
    2215             : }
    2216             : 
    2217             : /************************************************************************/
    2218             : /*                           FlushDirectory()                           */
    2219             : /************************************************************************/
    2220             : 
    2221       21433 : CPLErr GTiffDataset::FlushDirectory()
    2222             : 
    2223             : {
    2224       21433 :     CPLErr eErr = CE_None;
    2225             : 
    2226         620 :     const auto ReloadAllOtherDirectories = [this]()
    2227             :     {
    2228         305 :         const auto poBaseDS = m_poBaseDS ? m_poBaseDS : this;
    2229         308 :         for (auto &poOvrDS : poBaseDS->m_apoOverviewDS)
    2230             :         {
    2231           3 :             if (poOvrDS->m_bCrystalized && poOvrDS.get() != this)
    2232             :             {
    2233           3 :                 poOvrDS->ReloadDirectory(true);
    2234             :             }
    2235             : 
    2236           3 :             if (poOvrDS->m_poMaskDS && poOvrDS->m_poMaskDS.get() != this &&
    2237           0 :                 poOvrDS->m_poMaskDS->m_bCrystalized)
    2238             :             {
    2239           0 :                 poOvrDS->m_poMaskDS->ReloadDirectory(true);
    2240             :             }
    2241             :         }
    2242         305 :         if (poBaseDS->m_poMaskDS && poBaseDS->m_poMaskDS.get() != this &&
    2243           0 :             poBaseDS->m_poMaskDS->m_bCrystalized)
    2244             :         {
    2245           0 :             poBaseDS->m_poMaskDS->ReloadDirectory(true);
    2246             :         }
    2247         305 :         if (poBaseDS->m_bCrystalized && poBaseDS != this)
    2248             :         {
    2249           7 :             poBaseDS->ReloadDirectory(true);
    2250             :         }
    2251         305 :     };
    2252             : 
    2253       21433 :     if (eAccess == GA_Update)
    2254             :     {
    2255       15327 :         if (m_bMetadataChanged)
    2256             :         {
    2257         171 :             m_bNeedsRewrite =
    2258         171 :                 WriteMetadata(this, m_hTIFF, true, m_eProfile,
    2259         171 :                               m_osFilename.c_str(), m_papszCreationOptions);
    2260         171 :             m_bMetadataChanged = false;
    2261             : 
    2262         171 :             if (m_bForceUnsetRPC)
    2263             :             {
    2264           5 :                 double *padfRPCTag = nullptr;
    2265             :                 uint16_t nCount;
    2266           5 :                 if (TIFFGetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT, &nCount,
    2267           5 :                                  &padfRPCTag))
    2268             :                 {
    2269           3 :                     std::vector<double> zeroes(92);
    2270           3 :                     TIFFSetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT, 92,
    2271             :                                  zeroes.data());
    2272           3 :                     TIFFUnsetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT);
    2273           3 :                     m_bNeedsRewrite = true;
    2274             :                 }
    2275             : 
    2276           5 :                 if (m_poBaseDS == nullptr)
    2277             :                 {
    2278           5 :                     GDALWriteRPCTXTFile(m_osFilename.c_str(), nullptr);
    2279           5 :                     GDALWriteRPBFile(m_osFilename.c_str(), nullptr);
    2280             :                 }
    2281             :             }
    2282             :         }
    2283             : 
    2284       15327 :         if (m_bGeoTIFFInfoChanged)
    2285             :         {
    2286         145 :             WriteGeoTIFFInfo();
    2287         145 :             m_bGeoTIFFInfoChanged = false;
    2288             :         }
    2289             : 
    2290       15327 :         if (m_bNoDataChanged)
    2291             :         {
    2292          50 :             if (m_bNoDataSet)
    2293             :             {
    2294          36 :                 WriteNoDataValue(m_hTIFF, m_dfNoDataValue);
    2295             :             }
    2296          14 :             else if (m_bNoDataSetAsInt64)
    2297             :             {
    2298           0 :                 WriteNoDataValue(m_hTIFF, m_nNoDataValueInt64);
    2299             :             }
    2300          14 :             else if (m_bNoDataSetAsUInt64)
    2301             :             {
    2302           0 :                 WriteNoDataValue(m_hTIFF, m_nNoDataValueUInt64);
    2303             :             }
    2304             :             else
    2305             :             {
    2306          14 :                 UnsetNoDataValue(m_hTIFF);
    2307             :             }
    2308          50 :             m_bNeedsRewrite = true;
    2309          50 :             m_bNoDataChanged = false;
    2310             :         }
    2311             : 
    2312       15327 :         if (m_bNeedsRewrite)
    2313             :         {
    2314         330 :             if (!m_bCrystalized)
    2315             :             {
    2316          28 :                 Crystalize();
    2317             :             }
    2318             :             else
    2319             :             {
    2320         302 :                 const TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(m_hTIFF);
    2321             : 
    2322         302 :                 m_nDirOffset = pfnSizeProc(TIFFClientdata(m_hTIFF));
    2323         302 :                 if ((m_nDirOffset % 2) == 1)
    2324          67 :                     ++m_nDirOffset;
    2325             : 
    2326         302 :                 if (TIFFRewriteDirectory(m_hTIFF) == 0)
    2327           0 :                     eErr = CE_Failure;
    2328             : 
    2329         302 :                 TIFFSetSubDirectory(m_hTIFF, m_nDirOffset);
    2330             : 
    2331         302 :                 ReloadAllOtherDirectories();
    2332             : 
    2333         302 :                 if (m_bLayoutIFDSBeforeData && m_bBlockOrderRowMajor &&
    2334           1 :                     m_bLeaderSizeAsUInt4 &&
    2335           1 :                     m_bTrailerRepeatedLast4BytesRepeated &&
    2336           1 :                     !m_bKnownIncompatibleEdition &&
    2337           1 :                     !m_bWriteKnownIncompatibleEdition)
    2338             :                 {
    2339           1 :                     ReportError(CE_Warning, CPLE_AppDefined,
    2340             :                                 "The IFD has been rewritten at the end of "
    2341             :                                 "the file, which breaks COG layout.");
    2342           1 :                     m_bKnownIncompatibleEdition = true;
    2343           1 :                     m_bWriteKnownIncompatibleEdition = true;
    2344             :                 }
    2345             :             }
    2346             : 
    2347         330 :             m_bNeedsRewrite = false;
    2348             :         }
    2349             :     }
    2350             : 
    2351             :     // There are some circumstances in which we can reach this point
    2352             :     // without having made this our directory (SetDirectory()) in which
    2353             :     // case we should not risk a flush.
    2354       36760 :     if (GetAccess() == GA_Update &&
    2355       15327 :         TIFFCurrentDirOffset(m_hTIFF) == m_nDirOffset)
    2356             :     {
    2357       15327 :         const TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(m_hTIFF);
    2358             : 
    2359       15327 :         toff_t nNewDirOffset = pfnSizeProc(TIFFClientdata(m_hTIFF));
    2360       15327 :         if ((nNewDirOffset % 2) == 1)
    2361        3317 :             ++nNewDirOffset;
    2362             : 
    2363       15327 :         if (TIFFFlush(m_hTIFF) == 0)
    2364          12 :             eErr = CE_Failure;
    2365             : 
    2366       15327 :         if (m_nDirOffset != TIFFCurrentDirOffset(m_hTIFF))
    2367             :         {
    2368           3 :             m_nDirOffset = nNewDirOffset;
    2369           3 :             ReloadAllOtherDirectories();
    2370           3 :             CPLDebug("GTiff",
    2371             :                      "directory moved during flush in FlushDirectory()");
    2372             :         }
    2373             :     }
    2374             : 
    2375       21433 :     SetDirectory();
    2376       21433 :     return eErr;
    2377             : }
    2378             : 
    2379             : /************************************************************************/
    2380             : /*                           CleanOverviews()                           */
    2381             : /************************************************************************/
    2382             : 
    2383           5 : CPLErr GTiffDataset::CleanOverviews()
    2384             : 
    2385             : {
    2386           5 :     CPLAssert(!m_poBaseDS);
    2387             : 
    2388           5 :     ScanDirectories();
    2389             : 
    2390           5 :     FlushDirectory();
    2391             : 
    2392             :     /* -------------------------------------------------------------------- */
    2393             :     /*      Cleanup overviews objects, and get offsets to all overview      */
    2394             :     /*      directories.                                                    */
    2395             :     /* -------------------------------------------------------------------- */
    2396          10 :     std::vector<toff_t> anOvDirOffsets;
    2397             : 
    2398          10 :     for (auto &poOvrDS : m_apoOverviewDS)
    2399             :     {
    2400           5 :         anOvDirOffsets.push_back(poOvrDS->m_nDirOffset);
    2401           5 :         if (poOvrDS->m_poMaskDS)
    2402           1 :             anOvDirOffsets.push_back(poOvrDS->m_poMaskDS->m_nDirOffset);
    2403             :     }
    2404           5 :     m_apoOverviewDS.clear();
    2405             : 
    2406             :     /* -------------------------------------------------------------------- */
    2407             :     /*      Loop through all the directories, translating the offsets       */
    2408             :     /*      into indexes we can use with TIFFUnlinkDirectory().             */
    2409             :     /* -------------------------------------------------------------------- */
    2410          10 :     std::vector<uint16_t> anOvDirIndexes;
    2411           5 :     int iThisOffset = 1;
    2412             : 
    2413           5 :     TIFFSetDirectory(m_hTIFF, 0);
    2414             : 
    2415             :     while (true)
    2416             :     {
    2417          28 :         for (toff_t nOffset : anOvDirOffsets)
    2418             :         {
    2419          16 :             if (nOffset == TIFFCurrentDirOffset(m_hTIFF))
    2420             :             {
    2421           6 :                 anOvDirIndexes.push_back(static_cast<uint16_t>(iThisOffset));
    2422             :             }
    2423             :         }
    2424             : 
    2425          12 :         if (TIFFLastDirectory(m_hTIFF))
    2426           5 :             break;
    2427             : 
    2428           7 :         TIFFReadDirectory(m_hTIFF);
    2429           7 :         ++iThisOffset;
    2430           7 :     }
    2431             : 
    2432             :     /* -------------------------------------------------------------------- */
    2433             :     /*      Actually unlink the target directories.  Note that we do        */
    2434             :     /*      this from last to first so as to avoid renumbering any of       */
    2435             :     /*      the earlier directories we need to remove.                      */
    2436             :     /* -------------------------------------------------------------------- */
    2437          11 :     while (!anOvDirIndexes.empty())
    2438             :     {
    2439           6 :         TIFFUnlinkDirectory(m_hTIFF, anOvDirIndexes.back());
    2440           6 :         anOvDirIndexes.pop_back();
    2441             :     }
    2442             : 
    2443           5 :     if (m_poMaskDS)
    2444             :     {
    2445           1 :         m_poMaskDS->m_apoOverviewDS.clear();
    2446             :     }
    2447             : 
    2448           5 :     if (!SetDirectory())
    2449           0 :         return CE_Failure;
    2450             : 
    2451           5 :     return CE_None;
    2452             : }
    2453             : 
    2454             : /************************************************************************/
    2455             : /*                   RegisterNewOverviewDataset()                       */
    2456             : /************************************************************************/
    2457             : 
    2458         509 : CPLErr GTiffDataset::RegisterNewOverviewDataset(toff_t nOverviewOffset,
    2459             :                                                 int l_nJpegQuality,
    2460             :                                                 CSLConstList papszOptions)
    2461             : {
    2462             :     const auto GetOptionValue =
    2463        5599 :         [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
    2464       11197 :                        const char **ppszKeyUsed = nullptr)
    2465             :     {
    2466        5599 :         const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
    2467        5599 :         if (pszVal)
    2468             :         {
    2469           1 :             if (ppszKeyUsed)
    2470           1 :                 *ppszKeyUsed = pszOptionKey;
    2471           1 :             return pszVal;
    2472             :         }
    2473        5598 :         pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey);
    2474        5598 :         if (pszVal)
    2475             :         {
    2476           0 :             if (ppszKeyUsed)
    2477           0 :                 *ppszKeyUsed = pszConfigOptionKey;
    2478           0 :             return pszVal;
    2479             :         }
    2480        5598 :         if (pszConfigOptionKey)
    2481             :         {
    2482        5598 :             pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
    2483        5598 :             if (pszVal && ppszKeyUsed)
    2484          13 :                 *ppszKeyUsed = pszConfigOptionKey;
    2485             :         }
    2486        5598 :         return pszVal;
    2487         509 :     };
    2488             : 
    2489         509 :     int nZLevel = m_nZLevel;
    2490         509 :     if (const char *opt = GetOptionValue("ZLEVEL", "ZLEVEL_OVERVIEW"))
    2491             :     {
    2492           4 :         nZLevel = atoi(opt);
    2493             :     }
    2494             : 
    2495         509 :     int nZSTDLevel = m_nZSTDLevel;
    2496         509 :     if (const char *opt = GetOptionValue("ZSTD_LEVEL", "ZSTD_LEVEL_OVERVIEW"))
    2497             :     {
    2498           4 :         nZSTDLevel = atoi(opt);
    2499             :     }
    2500             : 
    2501         509 :     bool bWebpLossless = m_bWebPLossless;
    2502             :     const char *pszWebPLosslessOverview =
    2503         509 :         GetOptionValue("WEBP_LOSSLESS", "WEBP_LOSSLESS_OVERVIEW");
    2504         509 :     if (pszWebPLosslessOverview)
    2505             :     {
    2506           2 :         bWebpLossless = CPLTestBool(pszWebPLosslessOverview);
    2507             :     }
    2508             : 
    2509         509 :     int nWebpLevel = m_nWebPLevel;
    2510         509 :     const char *pszKeyWebpLevel = "";
    2511         509 :     if (const char *opt = GetOptionValue("WEBP_LEVEL", "WEBP_LEVEL_OVERVIEW",
    2512             :                                          &pszKeyWebpLevel))
    2513             :     {
    2514          14 :         if (pszWebPLosslessOverview == nullptr && m_bWebPLossless)
    2515             :         {
    2516           1 :             CPLDebug("GTiff",
    2517             :                      "%s specified, but not WEBP_LOSSLESS_OVERVIEW. "
    2518             :                      "Assuming WEBP_LOSSLESS_OVERVIEW=NO",
    2519             :                      pszKeyWebpLevel);
    2520           1 :             bWebpLossless = false;
    2521             :         }
    2522          13 :         else if (bWebpLossless)
    2523             :         {
    2524           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2525             :                      "%s is specified, but WEBP_LOSSLESS_OVERVIEW=YES. "
    2526             :                      "%s will be ignored.",
    2527             :                      pszKeyWebpLevel, pszKeyWebpLevel);
    2528             :         }
    2529          14 :         nWebpLevel = atoi(opt);
    2530             :     }
    2531             : 
    2532         509 :     double dfMaxZError = m_dfMaxZErrorOverview;
    2533         509 :     if (const char *opt = GetOptionValue("MAX_Z_ERROR", "MAX_Z_ERROR_OVERVIEW"))
    2534             :     {
    2535          20 :         dfMaxZError = CPLAtof(opt);
    2536             :     }
    2537             : 
    2538         509 :     signed char nJpegTablesMode = m_nJpegTablesMode;
    2539         509 :     if (const char *opt =
    2540         509 :             GetOptionValue("JPEG_TABLESMODE", "JPEG_TABLESMODE_OVERVIEW"))
    2541             :     {
    2542           0 :         nJpegTablesMode = static_cast<signed char>(atoi(opt));
    2543             :     }
    2544             : 
    2545             : #ifdef HAVE_JXL
    2546         509 :     bool bJXLLossless = m_bJXLLossless;
    2547         509 :     if (const char *opt =
    2548         509 :             GetOptionValue("JXL_LOSSLESS", "JXL_LOSSLESS_OVERVIEW"))
    2549             :     {
    2550           0 :         bJXLLossless = CPLTestBool(opt);
    2551             :     }
    2552             : 
    2553         509 :     float fJXLDistance = m_fJXLDistance;
    2554         509 :     if (const char *opt =
    2555         509 :             GetOptionValue("JXL_DISTANCE", "JXL_DISTANCE_OVERVIEW"))
    2556             :     {
    2557           0 :         fJXLDistance = static_cast<float>(CPLAtof(opt));
    2558             :     }
    2559             : 
    2560         509 :     float fJXLAlphaDistance = m_fJXLAlphaDistance;
    2561         509 :     if (const char *opt =
    2562         509 :             GetOptionValue("JXL_ALPHA_DISTANCE", "JXL_ALPHA_DISTANCE_OVERVIEW"))
    2563             :     {
    2564           0 :         fJXLAlphaDistance = static_cast<float>(CPLAtof(opt));
    2565             :     }
    2566             : 
    2567         509 :     int nJXLEffort = m_nJXLEffort;
    2568         509 :     if (const char *opt = GetOptionValue("JXL_EFFORT", "JXL_EFFORT_OVERVIEW"))
    2569             :     {
    2570           0 :         nJXLEffort = atoi(opt);
    2571             :     }
    2572             : #endif
    2573             : 
    2574        1018 :     auto poODS = std::make_shared<GTiffDataset>();
    2575         509 :     poODS->ShareLockWithParentDataset(this);
    2576         509 :     poODS->eAccess = GA_Update;
    2577         509 :     poODS->m_osFilename = m_osFilename;
    2578         509 :     const char *pszSparseOK = GetOptionValue("SPARSE_OK", "SPARSE_OK_OVERVIEW");
    2579         509 :     if (pszSparseOK && CPLTestBool(pszSparseOK))
    2580             :     {
    2581           1 :         poODS->m_bWriteEmptyTiles = false;
    2582           1 :         poODS->m_bFillEmptyTilesAtClosing = false;
    2583             :     }
    2584             :     else
    2585             :     {
    2586         508 :         poODS->m_bWriteEmptyTiles = m_bWriteEmptyTiles;
    2587         508 :         poODS->m_bFillEmptyTilesAtClosing = m_bFillEmptyTilesAtClosing;
    2588             :     }
    2589         509 :     poODS->m_nJpegQuality = static_cast<signed char>(l_nJpegQuality);
    2590         509 :     poODS->m_nWebPLevel = static_cast<signed char>(nWebpLevel);
    2591         509 :     poODS->m_nZLevel = static_cast<signed char>(nZLevel);
    2592         509 :     poODS->m_nLZMAPreset = m_nLZMAPreset;
    2593         509 :     poODS->m_nZSTDLevel = static_cast<signed char>(nZSTDLevel);
    2594         509 :     poODS->m_bWebPLossless = bWebpLossless;
    2595         509 :     poODS->m_nJpegTablesMode = nJpegTablesMode;
    2596         509 :     poODS->m_dfMaxZError = dfMaxZError;
    2597         509 :     poODS->m_dfMaxZErrorOverview = dfMaxZError;
    2598        1018 :     memcpy(poODS->m_anLercAddCompressionAndVersion,
    2599         509 :            m_anLercAddCompressionAndVersion,
    2600             :            sizeof(m_anLercAddCompressionAndVersion));
    2601             : #ifdef HAVE_JXL
    2602         509 :     poODS->m_bJXLLossless = bJXLLossless;
    2603         509 :     poODS->m_fJXLDistance = fJXLDistance;
    2604         509 :     poODS->m_fJXLAlphaDistance = fJXLAlphaDistance;
    2605         509 :     poODS->m_nJXLEffort = nJXLEffort;
    2606             : #endif
    2607             : 
    2608         509 :     if (poODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nOverviewOffset,
    2609         509 :                           GA_Update) != CE_None)
    2610             :     {
    2611           0 :         return CE_Failure;
    2612             :     }
    2613             : 
    2614             :     // Assign color interpretation from main dataset
    2615         509 :     const int l_nBands = GetRasterCount();
    2616        1522 :     for (int i = 1; i <= l_nBands; i++)
    2617             :     {
    2618        1013 :         auto poBand = dynamic_cast<GTiffRasterBand *>(poODS->GetRasterBand(i));
    2619        1013 :         if (poBand)
    2620        1013 :             poBand->m_eBandInterp = GetRasterBand(i)->GetColorInterpretation();
    2621             :     }
    2622             : 
    2623             :     // Do that now that m_nCompression is set
    2624         509 :     poODS->RestoreVolatileParameters(poODS->m_hTIFF);
    2625             : 
    2626         509 :     poODS->m_poBaseDS = this;
    2627         509 :     poODS->m_bIsOverview = true;
    2628             : 
    2629         509 :     m_apoOverviewDS.push_back(std::move(poODS));
    2630         509 :     return CE_None;
    2631             : }
    2632             : 
    2633             : /************************************************************************/
    2634             : /*                     CreateTIFFColorTable()                           */
    2635             : /************************************************************************/
    2636             : 
    2637          12 : static void CreateTIFFColorTable(
    2638             :     GDALColorTable *poColorTable, int nBits, int nColorTableMultiplier,
    2639             :     std::vector<unsigned short> &anTRed, std::vector<unsigned short> &anTGreen,
    2640             :     std::vector<unsigned short> &anTBlue, unsigned short *&panRed,
    2641             :     unsigned short *&panGreen, unsigned short *&panBlue)
    2642             : {
    2643             :     int nColors;
    2644             : 
    2645          12 :     if (nBits == 8)
    2646          12 :         nColors = 256;
    2647           0 :     else if (nBits < 8)
    2648           0 :         nColors = 1 << nBits;
    2649             :     else
    2650           0 :         nColors = 65536;
    2651             : 
    2652          12 :     anTRed.resize(nColors, 0);
    2653          12 :     anTGreen.resize(nColors, 0);
    2654          12 :     anTBlue.resize(nColors, 0);
    2655             : 
    2656        3084 :     for (int iColor = 0; iColor < nColors; ++iColor)
    2657             :     {
    2658        3072 :         if (iColor < poColorTable->GetColorEntryCount())
    2659             :         {
    2660             :             GDALColorEntry sRGB;
    2661             : 
    2662        3072 :             poColorTable->GetColorEntryAsRGB(iColor, &sRGB);
    2663             : 
    2664        3072 :             anTRed[iColor] = GTiffDataset::ClampCTEntry(iColor, 1, sRGB.c1,
    2665             :                                                         nColorTableMultiplier);
    2666        3072 :             anTGreen[iColor] = GTiffDataset::ClampCTEntry(
    2667        3072 :                 iColor, 2, sRGB.c2, nColorTableMultiplier);
    2668        3072 :             anTBlue[iColor] = GTiffDataset::ClampCTEntry(iColor, 3, sRGB.c3,
    2669             :                                                          nColorTableMultiplier);
    2670             :         }
    2671             :         else
    2672             :         {
    2673           0 :             anTRed[iColor] = 0;
    2674           0 :             anTGreen[iColor] = 0;
    2675           0 :             anTBlue[iColor] = 0;
    2676             :         }
    2677             :     }
    2678             : 
    2679          12 :     panRed = &(anTRed[0]);
    2680          12 :     panGreen = &(anTGreen[0]);
    2681          12 :     panBlue = &(anTBlue[0]);
    2682          12 : }
    2683             : 
    2684             : /************************************************************************/
    2685             : /*                        GetOverviewParameters()                       */
    2686             : /************************************************************************/
    2687             : 
    2688         326 : bool GTiffDataset::GetOverviewParameters(
    2689             :     int &nCompression, uint16_t &nPlanarConfig, uint16_t &nPredictor,
    2690             :     uint16_t &nPhotometric, int &nOvrJpegQuality, std::string &osNoData,
    2691             :     uint16_t *&panExtraSampleValues, uint16_t &nExtraSamples,
    2692             :     CSLConstList papszOptions) const
    2693             : {
    2694             :     const auto GetOptionValue =
    2695        1080 :         [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
    2696        2152 :                        const char **ppszKeyUsed = nullptr)
    2697             :     {
    2698        1080 :         const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
    2699        1080 :         if (pszVal)
    2700             :         {
    2701           8 :             if (ppszKeyUsed)
    2702           8 :                 *ppszKeyUsed = pszOptionKey;
    2703           8 :             return pszVal;
    2704             :         }
    2705        1072 :         pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey);
    2706        1072 :         if (pszVal)
    2707             :         {
    2708           0 :             if (ppszKeyUsed)
    2709           0 :                 *ppszKeyUsed = pszConfigOptionKey;
    2710           0 :             return pszVal;
    2711             :         }
    2712        1072 :         pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
    2713        1072 :         if (pszVal && ppszKeyUsed)
    2714          58 :             *ppszKeyUsed = pszConfigOptionKey;
    2715        1072 :         return pszVal;
    2716         326 :     };
    2717             : 
    2718             :     /* -------------------------------------------------------------------- */
    2719             :     /*      Determine compression method.                                   */
    2720             :     /* -------------------------------------------------------------------- */
    2721         326 :     nCompression = m_nCompression;
    2722         326 :     const char *pszOptionKey = "";
    2723             :     const char *pszCompressValue =
    2724         326 :         GetOptionValue("COMPRESS", "COMPRESS_OVERVIEW", &pszOptionKey);
    2725         326 :     if (pszCompressValue != nullptr)
    2726             :     {
    2727          56 :         nCompression =
    2728          56 :             GTIFFGetCompressionMethod(pszCompressValue, pszOptionKey);
    2729          56 :         if (nCompression < 0)
    2730             :         {
    2731           0 :             nCompression = m_nCompression;
    2732             :         }
    2733             :     }
    2734             : 
    2735             :     /* -------------------------------------------------------------------- */
    2736             :     /*      Determine planar configuration.                                 */
    2737             :     /* -------------------------------------------------------------------- */
    2738         326 :     nPlanarConfig = m_nPlanarConfig;
    2739         326 :     if (nCompression == COMPRESSION_WEBP)
    2740             :     {
    2741          11 :         nPlanarConfig = PLANARCONFIG_CONTIG;
    2742             :     }
    2743             :     const char *pszInterleave =
    2744         326 :         GetOptionValue("INTERLEAVE", "INTERLEAVE_OVERVIEW", &pszOptionKey);
    2745         326 :     if (pszInterleave != nullptr && pszInterleave[0] != '\0')
    2746             :     {
    2747           2 :         if (EQUAL(pszInterleave, "PIXEL"))
    2748           1 :             nPlanarConfig = PLANARCONFIG_CONTIG;
    2749           1 :         else if (EQUAL(pszInterleave, "BAND"))
    2750           1 :             nPlanarConfig = PLANARCONFIG_SEPARATE;
    2751             :         else
    2752             :         {
    2753           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2754             :                      "%s=%s unsupported, "
    2755             :                      "value must be PIXEL or BAND. ignoring",
    2756             :                      pszOptionKey, pszInterleave);
    2757             :         }
    2758             :     }
    2759             : 
    2760             :     /* -------------------------------------------------------------------- */
    2761             :     /*      Determine predictor tag                                         */
    2762             :     /* -------------------------------------------------------------------- */
    2763         326 :     nPredictor = PREDICTOR_NONE;
    2764         326 :     if (GTIFFSupportsPredictor(nCompression))
    2765             :     {
    2766             :         const char *pszPredictor =
    2767          75 :             GetOptionValue("PREDICTOR", "PREDICTOR_OVERVIEW");
    2768          75 :         if (pszPredictor != nullptr)
    2769             :         {
    2770           1 :             nPredictor = static_cast<uint16_t>(atoi(pszPredictor));
    2771             :         }
    2772          74 :         else if (GTIFFSupportsPredictor(m_nCompression))
    2773          73 :             TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &nPredictor);
    2774             :     }
    2775             : 
    2776             :     /* -------------------------------------------------------------------- */
    2777             :     /*      Determine photometric tag                                       */
    2778             :     /* -------------------------------------------------------------------- */
    2779         326 :     if (m_nPhotometric == PHOTOMETRIC_YCBCR && nCompression != COMPRESSION_JPEG)
    2780           1 :         nPhotometric = PHOTOMETRIC_RGB;
    2781             :     else
    2782         325 :         nPhotometric = m_nPhotometric;
    2783             :     const char *pszPhotometric =
    2784         326 :         GetOptionValue("PHOTOMETRIC", "PHOTOMETRIC_OVERVIEW", &pszOptionKey);
    2785         326 :     if (!GTIFFUpdatePhotometric(pszPhotometric, pszOptionKey, nCompression,
    2786         326 :                                 pszInterleave, nBands, nPhotometric,
    2787             :                                 nPlanarConfig))
    2788             :     {
    2789           0 :         return false;
    2790             :     }
    2791             : 
    2792             :     /* -------------------------------------------------------------------- */
    2793             :     /*      Determine JPEG quality                                          */
    2794             :     /* -------------------------------------------------------------------- */
    2795         326 :     nOvrJpegQuality = m_nJpegQuality;
    2796         326 :     if (nCompression == COMPRESSION_JPEG)
    2797             :     {
    2798             :         const char *pszJPEGQuality =
    2799          27 :             GetOptionValue("JPEG_QUALITY", "JPEG_QUALITY_OVERVIEW");
    2800          27 :         if (pszJPEGQuality != nullptr)
    2801             :         {
    2802           9 :             nOvrJpegQuality = atoi(pszJPEGQuality);
    2803             :         }
    2804             :     }
    2805             : 
    2806             :     /* -------------------------------------------------------------------- */
    2807             :     /*      Set nodata.                                                     */
    2808             :     /* -------------------------------------------------------------------- */
    2809         326 :     if (m_bNoDataSet)
    2810             :     {
    2811          17 :         osNoData = GTiffFormatGDALNoDataTagValue(m_dfNoDataValue);
    2812             :     }
    2813             : 
    2814             :     /* -------------------------------------------------------------------- */
    2815             :     /*      Fetch extra sample tag                                          */
    2816             :     /* -------------------------------------------------------------------- */
    2817         326 :     panExtraSampleValues = nullptr;
    2818         326 :     nExtraSamples = 0;
    2819         326 :     if (TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples,
    2820         326 :                      &panExtraSampleValues))
    2821             :     {
    2822             :         uint16_t *panExtraSampleValuesNew = static_cast<uint16_t *>(
    2823          40 :             CPLMalloc(nExtraSamples * sizeof(uint16_t)));
    2824          40 :         memcpy(panExtraSampleValuesNew, panExtraSampleValues,
    2825          40 :                nExtraSamples * sizeof(uint16_t));
    2826          40 :         panExtraSampleValues = panExtraSampleValuesNew;
    2827             :     }
    2828             :     else
    2829             :     {
    2830         286 :         panExtraSampleValues = nullptr;
    2831         286 :         nExtraSamples = 0;
    2832             :     }
    2833             : 
    2834         326 :     return true;
    2835             : }
    2836             : 
    2837             : /************************************************************************/
    2838             : /*                  CreateOverviewsFromSrcOverviews()                   */
    2839             : /************************************************************************/
    2840             : 
    2841             : // If poOvrDS is not null, it is used and poSrcDS is ignored.
    2842             : 
    2843          69 : CPLErr GTiffDataset::CreateOverviewsFromSrcOverviews(GDALDataset *poSrcDS,
    2844             :                                                      GDALDataset *poOvrDS,
    2845             :                                                      int nOverviews)
    2846             : {
    2847          69 :     CPLAssert(poSrcDS->GetRasterCount() != 0);
    2848          69 :     CPLAssert(m_apoOverviewDS.empty());
    2849             : 
    2850          69 :     ScanDirectories();
    2851             : 
    2852          69 :     FlushDirectory();
    2853             : 
    2854          69 :     int nOvBitsPerSample = m_nBitsPerSample;
    2855             : 
    2856             :     /* -------------------------------------------------------------------- */
    2857             :     /*      Do we need some metadata for the overviews?                     */
    2858             :     /* -------------------------------------------------------------------- */
    2859         138 :     CPLString osMetadata;
    2860             : 
    2861          69 :     GTIFFBuildOverviewMetadata("NONE", this, false, osMetadata);
    2862             : 
    2863             :     int nCompression;
    2864             :     uint16_t nPlanarConfig;
    2865             :     uint16_t nPredictor;
    2866             :     uint16_t nPhotometric;
    2867             :     int nOvrJpegQuality;
    2868         138 :     std::string osNoData;
    2869          69 :     uint16_t *panExtraSampleValues = nullptr;
    2870          69 :     uint16_t nExtraSamples = 0;
    2871          69 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    2872             :                                nPhotometric, nOvrJpegQuality, osNoData,
    2873             :                                panExtraSampleValues, nExtraSamples,
    2874             :                                /*papszOptions=*/nullptr))
    2875             :     {
    2876           0 :         return CE_Failure;
    2877             :     }
    2878             : 
    2879             :     /* -------------------------------------------------------------------- */
    2880             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    2881             :     /* -------------------------------------------------------------------- */
    2882         138 :     std::vector<unsigned short> anTRed;
    2883         138 :     std::vector<unsigned short> anTGreen;
    2884          69 :     std::vector<unsigned short> anTBlue;
    2885          69 :     unsigned short *panRed = nullptr;
    2886          69 :     unsigned short *panGreen = nullptr;
    2887          69 :     unsigned short *panBlue = nullptr;
    2888             : 
    2889          69 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    2890             :     {
    2891           0 :         if (m_nColorTableMultiplier == 0)
    2892           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    2893             : 
    2894           0 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    2895             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    2896             :                              panRed, panGreen, panBlue);
    2897             :     }
    2898             : 
    2899          69 :     int nOvrBlockXSize = 0;
    2900          69 :     int nOvrBlockYSize = 0;
    2901          69 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    2902             :                               &nOvrBlockXSize, &nOvrBlockYSize, nullptr,
    2903             :                               nullptr);
    2904             : 
    2905          69 :     CPLErr eErr = CE_None;
    2906             : 
    2907         195 :     for (int i = 0; i < nOverviews && eErr == CE_None; ++i)
    2908             :     {
    2909             :         GDALRasterBand *poOvrBand =
    2910         163 :             poOvrDS ? ((i == 0) ? poOvrDS->GetRasterBand(1)
    2911          37 :                                 : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    2912          55 :                     : poSrcDS->GetRasterBand(1)->GetOverview(i);
    2913             : 
    2914         126 :         int nOXSize = poOvrBand->GetXSize();
    2915         126 :         int nOYSize = poOvrBand->GetYSize();
    2916             : 
    2917         252 :         toff_t nOverviewOffset = GTIFFWriteDirectory(
    2918             :             m_hTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize, nOvBitsPerSample,
    2919         126 :             nPlanarConfig, m_nSamplesPerPixel, nOvrBlockXSize, nOvrBlockYSize,
    2920         126 :             TRUE, nCompression, nPhotometric, m_nSampleFormat, nPredictor,
    2921             :             panRed, panGreen, panBlue, nExtraSamples, panExtraSampleValues,
    2922             :             osMetadata,
    2923         126 :             nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality) : nullptr,
    2924         126 :             CPLSPrintf("%d", m_nJpegTablesMode),
    2925           2 :             osNoData.empty() ? nullptr : osNoData.c_str(),
    2926         126 :             m_anLercAddCompressionAndVersion, m_bWriteCOGLayout);
    2927             : 
    2928         126 :         if (nOverviewOffset == 0)
    2929           0 :             eErr = CE_Failure;
    2930             :         else
    2931         126 :             eErr = RegisterNewOverviewDataset(nOverviewOffset, nOvrJpegQuality,
    2932             :                                               nullptr);
    2933             :     }
    2934             : 
    2935             :     // For directory reloading, so that the chaining to the next directory is
    2936             :     // reloaded, as well as compression parameters.
    2937          69 :     ReloadDirectory();
    2938             : 
    2939          69 :     CPLFree(panExtraSampleValues);
    2940          69 :     panExtraSampleValues = nullptr;
    2941             : 
    2942          69 :     return eErr;
    2943             : }
    2944             : 
    2945             : /************************************************************************/
    2946             : /*                       CreateInternalMaskOverviews()                  */
    2947             : /************************************************************************/
    2948             : 
    2949         272 : CPLErr GTiffDataset::CreateInternalMaskOverviews(int nOvrBlockXSize,
    2950             :                                                  int nOvrBlockYSize)
    2951             : {
    2952         272 :     ScanDirectories();
    2953             : 
    2954             :     /* -------------------------------------------------------------------- */
    2955             :     /*      Create overviews for the mask.                                  */
    2956             :     /* -------------------------------------------------------------------- */
    2957         272 :     CPLErr eErr = CE_None;
    2958             : 
    2959         272 :     if (m_poMaskDS != nullptr && m_poMaskDS->GetRasterCount() == 1)
    2960             :     {
    2961             :         int nMaskOvrCompression;
    2962          43 :         if (strstr(GDALGetMetadataItem(GDALGetDriverByName("GTiff"),
    2963             :                                        GDAL_DMD_CREATIONOPTIONLIST, nullptr),
    2964          43 :                    "<Value>DEFLATE</Value>") != nullptr)
    2965          43 :             nMaskOvrCompression = COMPRESSION_ADOBE_DEFLATE;
    2966             :         else
    2967           0 :             nMaskOvrCompression = COMPRESSION_PACKBITS;
    2968             : 
    2969         115 :         for (auto &poOvrDS : m_apoOverviewDS)
    2970             :         {
    2971          72 :             if (poOvrDS->m_poMaskDS == nullptr)
    2972             :             {
    2973          60 :                 const toff_t nOverviewOffset = GTIFFWriteDirectory(
    2974             :                     m_hTIFF, FILETYPE_REDUCEDIMAGE | FILETYPE_MASK,
    2975          60 :                     poOvrDS->nRasterXSize, poOvrDS->nRasterYSize, 1,
    2976             :                     PLANARCONFIG_CONTIG, 1, nOvrBlockXSize, nOvrBlockYSize,
    2977             :                     TRUE, nMaskOvrCompression, PHOTOMETRIC_MASK,
    2978             :                     SAMPLEFORMAT_UINT, PREDICTOR_NONE, nullptr, nullptr,
    2979             :                     nullptr, 0, nullptr, "", nullptr, nullptr, nullptr, nullptr,
    2980          60 :                     m_bWriteCOGLayout);
    2981             : 
    2982          60 :                 if (nOverviewOffset == 0)
    2983             :                 {
    2984           0 :                     eErr = CE_Failure;
    2985           0 :                     continue;
    2986             :                 }
    2987             : 
    2988         120 :                 auto poMaskODS = std::make_shared<GTiffDataset>();
    2989          60 :                 poMaskODS->eAccess = GA_Update;
    2990          60 :                 poMaskODS->ShareLockWithParentDataset(this);
    2991          60 :                 poMaskODS->m_osFilename = m_osFilename;
    2992          60 :                 if (poMaskODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF),
    2993             :                                           nOverviewOffset,
    2994          60 :                                           GA_Update) != CE_None)
    2995             :                 {
    2996           0 :                     eErr = CE_Failure;
    2997             :                 }
    2998             :                 else
    2999             :                 {
    3000         120 :                     poMaskODS->m_bPromoteTo8Bits =
    3001          60 :                         CPLTestBool(CPLGetConfigOption(
    3002             :                             "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
    3003          60 :                     poMaskODS->m_poBaseDS = this;
    3004          60 :                     poMaskODS->m_poImageryDS = poOvrDS.get();
    3005          60 :                     poOvrDS->m_poMaskDS = poMaskODS;
    3006          60 :                     m_poMaskDS->m_apoOverviewDS.push_back(std::move(poMaskODS));
    3007             :                 }
    3008             :             }
    3009             :         }
    3010             :     }
    3011             : 
    3012         272 :     ReloadDirectory();
    3013             : 
    3014         272 :     return eErr;
    3015             : }
    3016             : 
    3017             : /************************************************************************/
    3018             : /*                            AddOverviews()                            */
    3019             : /************************************************************************/
    3020             : 
    3021             : CPLErr
    3022          13 : GTiffDataset::AddOverviews(const std::vector<GDALDataset *> &apoSrcOvrDSIn,
    3023             :                            GDALProgressFunc pfnProgress, void *pProgressData,
    3024             :                            CSLConstList papszOptions)
    3025             : {
    3026             :     /* -------------------------------------------------------------------- */
    3027             :     /*      If we don't have read access, then create the overviews         */
    3028             :     /*      externally.                                                     */
    3029             :     /* -------------------------------------------------------------------- */
    3030          13 :     if (GetAccess() != GA_Update)
    3031             :     {
    3032           4 :         CPLDebug("GTiff", "File open for read-only accessing, "
    3033             :                           "creating overviews externally.");
    3034             : 
    3035           4 :         CPLErr eErr = GDALDataset::AddOverviews(apoSrcOvrDSIn, pfnProgress,
    3036             :                                                 pProgressData, papszOptions);
    3037           4 :         if (eErr == CE_None && m_poMaskDS)
    3038             :         {
    3039           0 :             ReportError(
    3040             :                 CE_Warning, CPLE_NotSupported,
    3041             :                 "Building external overviews whereas there is an internal "
    3042             :                 "mask is not fully supported. "
    3043             :                 "The overviews of the non-mask bands will be created, "
    3044             :                 "but not the overviews of the mask band.");
    3045             :         }
    3046           4 :         return eErr;
    3047             :     }
    3048             : 
    3049          18 :     std::vector<GDALDataset *> apoSrcOvrDS = apoSrcOvrDSIn;
    3050             :     // Sort overviews by descending size
    3051           9 :     std::sort(apoSrcOvrDS.begin(), apoSrcOvrDS.end(),
    3052           0 :               [](const GDALDataset *poDS1, const GDALDataset *poDS2)
    3053           0 :               { return poDS1->GetRasterXSize() > poDS2->GetRasterXSize(); });
    3054             : 
    3055           9 :     if (!GDALDefaultOverviews::CheckSrcOverviewsConsistencyWithBase(
    3056             :             this, apoSrcOvrDS))
    3057           5 :         return CE_Failure;
    3058             : 
    3059           4 :     ScanDirectories();
    3060             : 
    3061             :     // Make implicit JPEG overviews invisible, but do not destroy
    3062             :     // them in case they are already used (not sure that the client
    3063             :     // has the right to do that). Behavior maybe undefined in GDAL API.
    3064           4 :     std::swap(m_apoJPEGOverviewDSOld, m_apoJPEGOverviewDS);
    3065           4 :     m_apoJPEGOverviewDS.clear();
    3066             : 
    3067           4 :     FlushDirectory();
    3068             : 
    3069             :     /* -------------------------------------------------------------------- */
    3070             :     /*      If we are averaging bit data to grayscale we need to create     */
    3071             :     /*      8bit overviews.                                                 */
    3072             :     /* -------------------------------------------------------------------- */
    3073           4 :     int nOvBitsPerSample = m_nBitsPerSample;
    3074             : 
    3075             :     /* -------------------------------------------------------------------- */
    3076             :     /*      Do we need some metadata for the overviews?                     */
    3077             :     /* -------------------------------------------------------------------- */
    3078           8 :     CPLString osMetadata;
    3079             : 
    3080           4 :     const bool bIsForMaskBand = nBands == 1 && GetRasterBand(1)->IsMaskBand();
    3081           4 :     GTIFFBuildOverviewMetadata(/* resampling = */ "", this, bIsForMaskBand,
    3082             :                                osMetadata);
    3083             : 
    3084             :     int nCompression;
    3085             :     uint16_t nPlanarConfig;
    3086             :     uint16_t nPredictor;
    3087             :     uint16_t nPhotometric;
    3088             :     int nOvrJpegQuality;
    3089           8 :     std::string osNoData;
    3090           4 :     uint16_t *panExtraSampleValues = nullptr;
    3091           4 :     uint16_t nExtraSamples = 0;
    3092           4 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    3093             :                                nPhotometric, nOvrJpegQuality, osNoData,
    3094             :                                panExtraSampleValues, nExtraSamples,
    3095             :                                papszOptions))
    3096             :     {
    3097           0 :         return CE_Failure;
    3098             :     }
    3099             : 
    3100             :     /* -------------------------------------------------------------------- */
    3101             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    3102             :     /* -------------------------------------------------------------------- */
    3103           8 :     std::vector<unsigned short> anTRed;
    3104           8 :     std::vector<unsigned short> anTGreen;
    3105           4 :     std::vector<unsigned short> anTBlue;
    3106           4 :     unsigned short *panRed = nullptr;
    3107           4 :     unsigned short *panGreen = nullptr;
    3108           4 :     unsigned short *panBlue = nullptr;
    3109             : 
    3110           4 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    3111             :     {
    3112           0 :         if (m_nColorTableMultiplier == 0)
    3113           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    3114             : 
    3115           0 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    3116             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    3117             :                              panRed, panGreen, panBlue);
    3118             :     }
    3119             : 
    3120             :     /* -------------------------------------------------------------------- */
    3121             :     /*      Establish which of the overview levels we already have, and     */
    3122             :     /*      which are new.  We assume that band 1 of the file is            */
    3123             :     /*      representative.                                                 */
    3124             :     /* -------------------------------------------------------------------- */
    3125           4 :     int nOvrBlockXSize = 0;
    3126           4 :     int nOvrBlockYSize = 0;
    3127           4 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    3128             :                               &nOvrBlockXSize, &nOvrBlockYSize, papszOptions,
    3129             :                               "BLOCKSIZE");
    3130             : 
    3131           4 :     CPLErr eErr = CE_None;
    3132           8 :     for (const auto *poSrcOvrDS : apoSrcOvrDS)
    3133             :     {
    3134           4 :         bool bFound = false;
    3135           4 :         for (auto &poOvrDS : m_apoOverviewDS)
    3136             :         {
    3137           4 :             if (poOvrDS->GetRasterXSize() == poSrcOvrDS->GetRasterXSize() &&
    3138           2 :                 poOvrDS->GetRasterYSize() == poSrcOvrDS->GetRasterYSize())
    3139             :             {
    3140           2 :                 bFound = true;
    3141           2 :                 break;
    3142             :             }
    3143             :         }
    3144           4 :         if (!bFound && eErr == CE_None)
    3145             :         {
    3146           2 :             if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    3147           0 :                 !m_bWriteKnownIncompatibleEdition)
    3148             :             {
    3149           0 :                 ReportError(CE_Warning, CPLE_AppDefined,
    3150             :                             "Adding new overviews invalidates the "
    3151             :                             "LAYOUT=IFDS_BEFORE_DATA property");
    3152           0 :                 m_bKnownIncompatibleEdition = true;
    3153           0 :                 m_bWriteKnownIncompatibleEdition = true;
    3154             :             }
    3155             : 
    3156           6 :             const toff_t nOverviewOffset = GTIFFWriteDirectory(
    3157             :                 m_hTIFF, FILETYPE_REDUCEDIMAGE, poSrcOvrDS->GetRasterXSize(),
    3158             :                 poSrcOvrDS->GetRasterYSize(), nOvBitsPerSample, nPlanarConfig,
    3159           2 :                 m_nSamplesPerPixel, nOvrBlockXSize, nOvrBlockYSize, TRUE,
    3160           2 :                 nCompression, nPhotometric, m_nSampleFormat, nPredictor, panRed,
    3161             :                 panGreen, panBlue, nExtraSamples, panExtraSampleValues,
    3162             :                 osMetadata,
    3163           2 :                 nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality)
    3164             :                                      : nullptr,
    3165           2 :                 CPLSPrintf("%d", m_nJpegTablesMode),
    3166           0 :                 osNoData.empty() ? nullptr : osNoData.c_str(),
    3167           2 :                 m_anLercAddCompressionAndVersion, false);
    3168             : 
    3169           2 :             if (nOverviewOffset == 0)
    3170           0 :                 eErr = CE_Failure;
    3171             :             else
    3172           2 :                 eErr = RegisterNewOverviewDataset(
    3173             :                     nOverviewOffset, nOvrJpegQuality, papszOptions);
    3174             :         }
    3175             :     }
    3176             : 
    3177           4 :     CPLFree(panExtraSampleValues);
    3178           4 :     panExtraSampleValues = nullptr;
    3179             : 
    3180           4 :     ReloadDirectory();
    3181             : 
    3182           4 :     if (!pfnProgress)
    3183           2 :         pfnProgress = GDALDummyProgress;
    3184             : 
    3185             :     // almost 0, but not 0 to please Coverity Scan
    3186           4 :     double dfTotalPixels = std::numeric_limits<double>::min();
    3187           8 :     for (const auto *poSrcOvrDS : apoSrcOvrDS)
    3188             :     {
    3189           4 :         dfTotalPixels += static_cast<double>(poSrcOvrDS->GetRasterXSize()) *
    3190           4 :                          poSrcOvrDS->GetRasterYSize();
    3191             :     }
    3192             : 
    3193             :     // Copy source datasets into target overview datasets
    3194           4 :     double dfCurPixels = 0;
    3195           8 :     for (auto *poSrcOvrDS : apoSrcOvrDS)
    3196             :     {
    3197           4 :         GDALDataset *poDstOvrDS = nullptr;
    3198           4 :         for (auto &poOvrDS : m_apoOverviewDS)
    3199             :         {
    3200           8 :             if (poOvrDS->GetRasterXSize() == poSrcOvrDS->GetRasterXSize() &&
    3201           4 :                 poOvrDS->GetRasterYSize() == poSrcOvrDS->GetRasterYSize())
    3202             :             {
    3203           4 :                 poDstOvrDS = poOvrDS.get();
    3204           4 :                 break;
    3205             :             }
    3206             :         }
    3207           4 :         if (eErr == CE_None && poDstOvrDS)
    3208             :         {
    3209             :             const double dfThisPixels =
    3210           4 :                 static_cast<double>(poSrcOvrDS->GetRasterXSize()) *
    3211           4 :                 poSrcOvrDS->GetRasterYSize();
    3212           8 :             void *pScaledProgressData = GDALCreateScaledProgress(
    3213             :                 dfCurPixels / dfTotalPixels,
    3214           4 :                 (dfCurPixels + dfThisPixels) / dfTotalPixels, pfnProgress,
    3215             :                 pProgressData);
    3216           4 :             dfCurPixels += dfThisPixels;
    3217           4 :             eErr = GDALDatasetCopyWholeRaster(GDALDataset::ToHandle(poSrcOvrDS),
    3218             :                                               GDALDataset::ToHandle(poDstOvrDS),
    3219             :                                               nullptr, GDALScaledProgress,
    3220             :                                               pScaledProgressData);
    3221           4 :             GDALDestroyScaledProgress(pScaledProgressData);
    3222             :         }
    3223             :     }
    3224             : 
    3225           4 :     return eErr;
    3226             : }
    3227             : 
    3228             : /************************************************************************/
    3229             : /*                          IBuildOverviews()                           */
    3230             : /************************************************************************/
    3231             : 
    3232         407 : CPLErr GTiffDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
    3233             :                                      const int *panOverviewList, int nBandsIn,
    3234             :                                      const int *panBandList,
    3235             :                                      GDALProgressFunc pfnProgress,
    3236             :                                      void *pProgressData,
    3237             :                                      CSLConstList papszOptions)
    3238             : 
    3239             : {
    3240         407 :     ScanDirectories();
    3241             : 
    3242             :     // Make implicit JPEG overviews invisible, but do not destroy
    3243             :     // them in case they are already used (not sure that the client
    3244             :     // has the right to do that.  Behavior maybe undefined in GDAL API.
    3245         407 :     std::swap(m_apoJPEGOverviewDSOld, m_apoJPEGOverviewDS);
    3246         407 :     m_apoJPEGOverviewDS.clear();
    3247             : 
    3248             :     /* -------------------------------------------------------------------- */
    3249             :     /*      If RRD or external OVR overviews requested, then invoke         */
    3250             :     /*      generic handling.                                               */
    3251             :     /* -------------------------------------------------------------------- */
    3252         407 :     bool bUseGenericHandling = false;
    3253         407 :     bool bUseRRD = false;
    3254         814 :     CPLStringList aosOptions(papszOptions);
    3255             : 
    3256         407 :     const char *pszLocation = CSLFetchNameValue(papszOptions, "LOCATION");
    3257         407 :     if (pszLocation && EQUAL(pszLocation, "EXTERNAL"))
    3258             :     {
    3259           1 :         bUseGenericHandling = true;
    3260             :     }
    3261         406 :     else if (pszLocation && EQUAL(pszLocation, "INTERNAL"))
    3262             :     {
    3263           0 :         if (GetAccess() != GA_Update)
    3264             :         {
    3265           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3266             :                      "Cannot create internal overviews on file opened in "
    3267             :                      "read-only mode");
    3268           0 :             return CE_Failure;
    3269             :         }
    3270             :     }
    3271         406 :     else if (pszLocation && EQUAL(pszLocation, "RRD"))
    3272             :     {
    3273           3 :         bUseGenericHandling = true;
    3274           3 :         bUseRRD = true;
    3275           3 :         aosOptions.SetNameValue("USE_RRD", "YES");
    3276             :     }
    3277             :     // Legacy
    3278         403 :     else if ((bUseRRD = CPLTestBool(
    3279             :                   CSLFetchNameValueDef(papszOptions, "USE_RRD",
    3280         806 :                                        CPLGetConfigOption("USE_RRD", "NO")))) ||
    3281         403 :              CPLTestBool(CSLFetchNameValueDef(
    3282             :                  papszOptions, "TIFF_USE_OVR",
    3283             :                  CPLGetConfigOption("TIFF_USE_OVR", "NO"))))
    3284             :     {
    3285           0 :         bUseGenericHandling = true;
    3286             :     }
    3287             : 
    3288             :     /* -------------------------------------------------------------------- */
    3289             :     /*      If we don't have read access, then create the overviews         */
    3290             :     /*      externally.                                                     */
    3291             :     /* -------------------------------------------------------------------- */
    3292         407 :     if (GetAccess() != GA_Update)
    3293             :     {
    3294         143 :         CPLDebug("GTiff", "File open for read-only accessing, "
    3295             :                           "creating overviews externally.");
    3296             : 
    3297         143 :         bUseGenericHandling = true;
    3298             :     }
    3299             : 
    3300         407 :     if (bUseGenericHandling)
    3301             :     {
    3302         146 :         if (!m_apoOverviewDS.empty())
    3303             :         {
    3304           0 :             ReportError(CE_Failure, CPLE_NotSupported,
    3305             :                         "Cannot add external overviews when there are already "
    3306             :                         "internal overviews");
    3307           0 :             return CE_Failure;
    3308             :         }
    3309             : 
    3310         146 :         if (!m_bWriteEmptyTiles && !bUseRRD)
    3311             :         {
    3312           1 :             aosOptions.SetNameValue("SPARSE_OK", "YES");
    3313             :         }
    3314             : 
    3315         146 :         CPLErr eErr = GDALDataset::IBuildOverviews(
    3316             :             pszResampling, nOverviews, panOverviewList, nBandsIn, panBandList,
    3317         146 :             pfnProgress, pProgressData, aosOptions);
    3318         146 :         if (eErr == CE_None && m_poMaskDS)
    3319             :         {
    3320           1 :             ReportError(
    3321             :                 CE_Warning, CPLE_NotSupported,
    3322             :                 "Building external overviews whereas there is an internal "
    3323             :                 "mask is not fully supported. "
    3324             :                 "The overviews of the non-mask bands will be created, "
    3325             :                 "but not the overviews of the mask band.");
    3326             :         }
    3327         146 :         return eErr;
    3328             :     }
    3329             : 
    3330             :     /* -------------------------------------------------------------------- */
    3331             :     /*      Our TIFF overview support currently only works safely if all    */
    3332             :     /*      bands are handled at the same time.                             */
    3333             :     /* -------------------------------------------------------------------- */
    3334         261 :     if (nBandsIn != GetRasterCount())
    3335             :     {
    3336           0 :         ReportError(CE_Failure, CPLE_NotSupported,
    3337             :                     "Generation of overviews in TIFF currently only "
    3338             :                     "supported when operating on all bands.  "
    3339             :                     "Operation failed.");
    3340           0 :         return CE_Failure;
    3341             :     }
    3342             : 
    3343             :     /* -------------------------------------------------------------------- */
    3344             :     /*      If zero overviews were requested, we need to clear all          */
    3345             :     /*      existing overviews.                                             */
    3346             :     /* -------------------------------------------------------------------- */
    3347         261 :     if (nOverviews == 0)
    3348             :     {
    3349           8 :         if (m_apoOverviewDS.empty())
    3350           3 :             return GDALDataset::IBuildOverviews(
    3351             :                 pszResampling, nOverviews, panOverviewList, nBandsIn,
    3352           3 :                 panBandList, pfnProgress, pProgressData, papszOptions);
    3353             : 
    3354           5 :         return CleanOverviews();
    3355             :     }
    3356             : 
    3357         253 :     CPLErr eErr = CE_None;
    3358             : 
    3359             :     /* -------------------------------------------------------------------- */
    3360             :     /*      Initialize progress counter.                                    */
    3361             :     /* -------------------------------------------------------------------- */
    3362         253 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    3363             :     {
    3364           0 :         ReportError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    3365           0 :         return CE_Failure;
    3366             :     }
    3367             : 
    3368         253 :     FlushDirectory();
    3369             : 
    3370             :     /* -------------------------------------------------------------------- */
    3371             :     /*      If we are averaging bit data to grayscale we need to create     */
    3372             :     /*      8bit overviews.                                                 */
    3373             :     /* -------------------------------------------------------------------- */
    3374         253 :     int nOvBitsPerSample = m_nBitsPerSample;
    3375             : 
    3376         253 :     if (STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
    3377           2 :         nOvBitsPerSample = 8;
    3378             : 
    3379             :     /* -------------------------------------------------------------------- */
    3380             :     /*      Do we need some metadata for the overviews?                     */
    3381             :     /* -------------------------------------------------------------------- */
    3382         506 :     CPLString osMetadata;
    3383             : 
    3384         253 :     const bool bIsForMaskBand = nBands == 1 && GetRasterBand(1)->IsMaskBand();
    3385         253 :     GTIFFBuildOverviewMetadata(pszResampling, this, bIsForMaskBand, osMetadata);
    3386             : 
    3387             :     int nCompression;
    3388             :     uint16_t nPlanarConfig;
    3389             :     uint16_t nPredictor;
    3390             :     uint16_t nPhotometric;
    3391             :     int nOvrJpegQuality;
    3392         506 :     std::string osNoData;
    3393         253 :     uint16_t *panExtraSampleValues = nullptr;
    3394         253 :     uint16_t nExtraSamples = 0;
    3395         253 :     if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
    3396             :                                nPhotometric, nOvrJpegQuality, osNoData,
    3397             :                                panExtraSampleValues, nExtraSamples,
    3398             :                                papszOptions))
    3399             :     {
    3400           0 :         return CE_Failure;
    3401             :     }
    3402             : 
    3403             :     /* -------------------------------------------------------------------- */
    3404             :     /*      Do we have a palette?  If so, create a TIFF compatible version. */
    3405             :     /* -------------------------------------------------------------------- */
    3406         506 :     std::vector<unsigned short> anTRed;
    3407         506 :     std::vector<unsigned short> anTGreen;
    3408         506 :     std::vector<unsigned short> anTBlue;
    3409         253 :     unsigned short *panRed = nullptr;
    3410         253 :     unsigned short *panGreen = nullptr;
    3411         253 :     unsigned short *panBlue = nullptr;
    3412             : 
    3413         253 :     if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
    3414             :     {
    3415          12 :         if (m_nColorTableMultiplier == 0)
    3416           0 :             m_nColorTableMultiplier = DEFAULT_COLOR_TABLE_MULTIPLIER_257;
    3417             : 
    3418          12 :         CreateTIFFColorTable(m_poColorTable.get(), nOvBitsPerSample,
    3419             :                              m_nColorTableMultiplier, anTRed, anTGreen, anTBlue,
    3420             :                              panRed, panGreen, panBlue);
    3421             :     }
    3422             : 
    3423             :     /* -------------------------------------------------------------------- */
    3424             :     /*      Establish which of the overview levels we already have, and     */
    3425             :     /*      which are new.  We assume that band 1 of the file is            */
    3426             :     /*      representative.                                                 */
    3427             :     /* -------------------------------------------------------------------- */
    3428         253 :     int nOvrBlockXSize = 0;
    3429         253 :     int nOvrBlockYSize = 0;
    3430         253 :     GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
    3431             :                               &nOvrBlockXSize, &nOvrBlockYSize, papszOptions,
    3432             :                               "BLOCKSIZE");
    3433         506 :     std::vector<bool> abRequireNewOverview(nOverviews, true);
    3434         690 :     for (int i = 0; i < nOverviews && eErr == CE_None; ++i)
    3435             :     {
    3436         770 :         for (auto &poODS : m_apoOverviewDS)
    3437             :         {
    3438             :             const int nOvFactor =
    3439         778 :                 GDALComputeOvFactor(poODS->GetRasterXSize(), GetRasterXSize(),
    3440         389 :                                     poODS->GetRasterYSize(), GetRasterYSize());
    3441             : 
    3442             :             // If we already have a 1x1 overview and this new one would result
    3443             :             // in it too, then don't create it.
    3444         449 :             if (poODS->GetRasterXSize() == 1 && poODS->GetRasterYSize() == 1 &&
    3445         449 :                 DIV_ROUND_UP(GetRasterXSize(), panOverviewList[i]) == 1 &&
    3446          21 :                 DIV_ROUND_UP(GetRasterYSize(), panOverviewList[i]) == 1)
    3447             :             {
    3448          21 :                 abRequireNewOverview[i] = false;
    3449          21 :                 break;
    3450             :             }
    3451             : 
    3452         701 :             if (nOvFactor == panOverviewList[i] ||
    3453         333 :                 nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3454             :                                                 GetRasterXSize(),
    3455             :                                                 GetRasterYSize()))
    3456             :             {
    3457          35 :                 abRequireNewOverview[i] = false;
    3458          35 :                 break;
    3459             :             }
    3460             :         }
    3461             : 
    3462         437 :         if (abRequireNewOverview[i])
    3463             :         {
    3464         381 :             if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    3465           2 :                 !m_bWriteKnownIncompatibleEdition)
    3466             :             {
    3467           2 :                 ReportError(CE_Warning, CPLE_AppDefined,
    3468             :                             "Adding new overviews invalidates the "
    3469             :                             "LAYOUT=IFDS_BEFORE_DATA property");
    3470           2 :                 m_bKnownIncompatibleEdition = true;
    3471           2 :                 m_bWriteKnownIncompatibleEdition = true;
    3472             :             }
    3473             : 
    3474             :             const int nOXSize =
    3475         381 :                 DIV_ROUND_UP(GetRasterXSize(), panOverviewList[i]);
    3476             :             const int nOYSize =
    3477         381 :                 DIV_ROUND_UP(GetRasterYSize(), panOverviewList[i]);
    3478             : 
    3479         762 :             const toff_t nOverviewOffset = GTIFFWriteDirectory(
    3480             :                 m_hTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize,
    3481         381 :                 nOvBitsPerSample, nPlanarConfig, m_nSamplesPerPixel,
    3482             :                 nOvrBlockXSize, nOvrBlockYSize, TRUE, nCompression,
    3483         381 :                 nPhotometric, m_nSampleFormat, nPredictor, panRed, panGreen,
    3484             :                 panBlue, nExtraSamples, panExtraSampleValues, osMetadata,
    3485         381 :                 nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality)
    3486             :                                      : nullptr,
    3487         381 :                 CPLSPrintf("%d", m_nJpegTablesMode),
    3488          25 :                 osNoData.empty() ? nullptr : osNoData.c_str(),
    3489         381 :                 m_anLercAddCompressionAndVersion, false);
    3490             : 
    3491         381 :             if (nOverviewOffset == 0)
    3492           0 :                 eErr = CE_Failure;
    3493             :             else
    3494         381 :                 eErr = RegisterNewOverviewDataset(
    3495             :                     nOverviewOffset, nOvrJpegQuality, papszOptions);
    3496             :         }
    3497             :     }
    3498             : 
    3499         253 :     CPLFree(panExtraSampleValues);
    3500         253 :     panExtraSampleValues = nullptr;
    3501             : 
    3502         253 :     ReloadDirectory();
    3503             : 
    3504             :     /* -------------------------------------------------------------------- */
    3505             :     /*      Create overviews for the mask.                                  */
    3506             :     /* -------------------------------------------------------------------- */
    3507         253 :     if (eErr != CE_None)
    3508           0 :         return eErr;
    3509             : 
    3510         253 :     eErr = CreateInternalMaskOverviews(nOvrBlockXSize, nOvrBlockYSize);
    3511             : 
    3512             :     /* -------------------------------------------------------------------- */
    3513             :     /*      Refresh overviews for the mask                                  */
    3514             :     /* -------------------------------------------------------------------- */
    3515             :     const bool bHasInternalMask =
    3516         253 :         m_poMaskDS != nullptr && m_poMaskDS->GetRasterCount() == 1;
    3517             :     const bool bHasExternalMask =
    3518         253 :         !bHasInternalMask && oOvManager.HaveMaskFile();
    3519         253 :     const bool bHasMask = bHasInternalMask || bHasExternalMask;
    3520             : 
    3521         253 :     if (bHasInternalMask)
    3522             :     {
    3523          48 :         std::vector<GDALRasterBandH> ahOverviewBands;
    3524          64 :         for (auto &poOvrDS : m_apoOverviewDS)
    3525             :         {
    3526          40 :             if (poOvrDS->m_poMaskDS != nullptr)
    3527             :             {
    3528          40 :                 ahOverviewBands.push_back(GDALRasterBand::ToHandle(
    3529          40 :                     poOvrDS->m_poMaskDS->GetRasterBand(1)));
    3530             :             }
    3531             :         }
    3532             : 
    3533          48 :         void *pScaledProgressData = GDALCreateScaledProgress(
    3534          24 :             0, 1.0 / (nBands + 1), pfnProgress, pProgressData);
    3535          24 :         eErr = GDALRegenerateOverviewsEx(
    3536          24 :             m_poMaskDS->GetRasterBand(1),
    3537          24 :             static_cast<int>(ahOverviewBands.size()), ahOverviewBands.data(),
    3538             :             pszResampling, GDALScaledProgress, pScaledProgressData,
    3539             :             papszOptions);
    3540          24 :         GDALDestroyScaledProgress(pScaledProgressData);
    3541             :     }
    3542         229 :     else if (bHasExternalMask)
    3543             :     {
    3544           4 :         void *pScaledProgressData = GDALCreateScaledProgress(
    3545           2 :             0, 1.0 / (nBands + 1), pfnProgress, pProgressData);
    3546           2 :         eErr = oOvManager.BuildOverviewsMask(
    3547             :             pszResampling, nOverviews, panOverviewList, GDALScaledProgress,
    3548             :             pScaledProgressData, papszOptions);
    3549           2 :         GDALDestroyScaledProgress(pScaledProgressData);
    3550             :     }
    3551             : 
    3552             :     // If we have an alpha band, we want it to be generated before downsampling
    3553             :     // other bands
    3554         253 :     bool bHasAlphaBand = false;
    3555       66253 :     for (int iBand = 0; iBand < nBands; iBand++)
    3556             :     {
    3557       66000 :         if (papoBands[iBand]->GetColorInterpretation() == GCI_AlphaBand)
    3558          18 :             bHasAlphaBand = true;
    3559             :     }
    3560             : 
    3561             :     /* -------------------------------------------------------------------- */
    3562             :     /*      Refresh old overviews that were listed.                         */
    3563             :     /* -------------------------------------------------------------------- */
    3564         253 :     const auto poColorTable = GetRasterBand(panBandList[0])->GetColorTable();
    3565          21 :     if ((m_nPlanarConfig == PLANARCONFIG_CONTIG || bHasAlphaBand) &&
    3566         234 :         GDALDataTypeIsComplex(
    3567         234 :             GetRasterBand(panBandList[0])->GetRasterDataType()) == FALSE &&
    3568          12 :         (poColorTable == nullptr || STARTS_WITH_CI(pszResampling, "NEAR") ||
    3569         507 :          poColorTable->IsIdentity()) &&
    3570         226 :         (STARTS_WITH_CI(pszResampling, "NEAR") ||
    3571         117 :          EQUAL(pszResampling, "AVERAGE") || EQUAL(pszResampling, "RMS") ||
    3572          47 :          EQUAL(pszResampling, "GAUSS") || EQUAL(pszResampling, "CUBIC") ||
    3573          29 :          EQUAL(pszResampling, "CUBICSPLINE") ||
    3574          28 :          EQUAL(pszResampling, "LANCZOS") || EQUAL(pszResampling, "BILINEAR") ||
    3575          24 :          EQUAL(pszResampling, "MODE")))
    3576             :     {
    3577             :         // In the case of pixel interleaved compressed overviews, we want to
    3578             :         // generate the overviews for all the bands block by block, and not
    3579             :         // band after band, in order to write the block once and not loose
    3580             :         // space in the TIFF file.  We also use that logic for uncompressed
    3581             :         // overviews, since GDALRegenerateOverviewsMultiBand() will be able to
    3582             :         // trigger cascading overview regeneration even in the presence
    3583             :         // of an alpha band.
    3584             : 
    3585         205 :         int nNewOverviews = 0;
    3586             : 
    3587             :         GDALRasterBand ***papapoOverviewBands = static_cast<GDALRasterBand ***>(
    3588         205 :             CPLCalloc(sizeof(void *), nBandsIn));
    3589             :         GDALRasterBand **papoBandList =
    3590         205 :             static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nBandsIn));
    3591       66109 :         for (int iBand = 0; iBand < nBandsIn; ++iBand)
    3592             :         {
    3593       65904 :             GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
    3594             : 
    3595       65904 :             papoBandList[iBand] = poBand;
    3596      131808 :             papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
    3597       65904 :                 CPLCalloc(sizeof(void *), poBand->GetOverviewCount()));
    3598             : 
    3599       65904 :             int iCurOverview = 0;
    3600             :             std::vector<bool> abAlreadyUsedOverviewBand(
    3601       65904 :                 poBand->GetOverviewCount(), false);
    3602             : 
    3603      132094 :             for (int i = 0; i < nOverviews; ++i)
    3604             :             {
    3605       66650 :                 for (int j = 0; j < poBand->GetOverviewCount(); ++j)
    3606             :                 {
    3607       66635 :                     if (abAlreadyUsedOverviewBand[j])
    3608         459 :                         continue;
    3609             : 
    3610             :                     int nOvFactor;
    3611       66176 :                     GDALRasterBand *poOverview = poBand->GetOverview(j);
    3612             : 
    3613       66176 :                     nOvFactor = GDALComputeOvFactor(
    3614             :                         poOverview->GetXSize(), poBand->GetXSize(),
    3615             :                         poOverview->GetYSize(), poBand->GetYSize());
    3616             : 
    3617       66176 :                     GDALCopyNoDataValue(poOverview, poBand);
    3618             : 
    3619       66177 :                     if (nOvFactor == panOverviewList[i] ||
    3620           1 :                         nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3621             :                                                         poBand->GetXSize(),
    3622             :                                                         poBand->GetYSize()))
    3623             :                     {
    3624       66175 :                         if (iBand == 0)
    3625             :                         {
    3626             :                             const auto osNewResampling =
    3627         664 :                                 GDALGetNormalizedOvrResampling(pszResampling);
    3628             :                             const char *pszExistingResampling =
    3629         332 :                                 poOverview->GetMetadataItem("RESAMPLING");
    3630         664 :                             if (pszExistingResampling &&
    3631         332 :                                 pszExistingResampling != osNewResampling)
    3632             :                             {
    3633           2 :                                 poOverview->SetMetadataItem(
    3634           2 :                                     "RESAMPLING", osNewResampling.c_str());
    3635             :                             }
    3636             :                         }
    3637             : 
    3638       66175 :                         abAlreadyUsedOverviewBand[j] = true;
    3639       66175 :                         CPLAssert(iCurOverview < poBand->GetOverviewCount());
    3640       66175 :                         papapoOverviewBands[iBand][iCurOverview] = poOverview;
    3641       66175 :                         ++iCurOverview;
    3642       66175 :                         break;
    3643             :                     }
    3644             :                 }
    3645             :             }
    3646             : 
    3647       65904 :             if (nNewOverviews == 0)
    3648             :             {
    3649         205 :                 nNewOverviews = iCurOverview;
    3650             :             }
    3651       65699 :             else if (nNewOverviews != iCurOverview)
    3652             :             {
    3653           0 :                 CPLAssert(false);
    3654             :                 return CE_Failure;
    3655             :             }
    3656             :         }
    3657             : 
    3658             :         void *pScaledProgressData =
    3659         205 :             bHasMask ? GDALCreateScaledProgress(1.0 / (nBands + 1), 1.0,
    3660             :                                                 pfnProgress, pProgressData)
    3661         179 :                      : GDALCreateScaledProgress(0.0, 1.0, pfnProgress,
    3662         205 :                                                 pProgressData);
    3663         205 :         GDALRegenerateOverviewsMultiBand(nBandsIn, papoBandList, nNewOverviews,
    3664             :                                          papapoOverviewBands, pszResampling,
    3665             :                                          GDALScaledProgress,
    3666             :                                          pScaledProgressData, papszOptions);
    3667         205 :         GDALDestroyScaledProgress(pScaledProgressData);
    3668             : 
    3669       66109 :         for (int iBand = 0; iBand < nBandsIn; ++iBand)
    3670             :         {
    3671       65904 :             CPLFree(papapoOverviewBands[iBand]);
    3672             :         }
    3673         205 :         CPLFree(papapoOverviewBands);
    3674         205 :         CPLFree(papoBandList);
    3675             :     }
    3676             :     else
    3677             :     {
    3678             :         GDALRasterBand **papoOverviewBands = static_cast<GDALRasterBand **>(
    3679          48 :             CPLCalloc(sizeof(void *), nOverviews));
    3680             : 
    3681          48 :         const int iBandOffset = bHasMask ? 1 : 0;
    3682             : 
    3683         144 :         for (int iBand = 0; iBand < nBandsIn && eErr == CE_None; ++iBand)
    3684             :         {
    3685          96 :             GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
    3686          96 :             if (poBand == nullptr)
    3687             :             {
    3688           0 :                 eErr = CE_Failure;
    3689           0 :                 break;
    3690             :             }
    3691             : 
    3692             :             std::vector<bool> abAlreadyUsedOverviewBand(
    3693         192 :                 poBand->GetOverviewCount(), false);
    3694             : 
    3695          96 :             int nNewOverviews = 0;
    3696         288 :             for (int i = 0; i < nOverviews; ++i)
    3697             :             {
    3698         450 :                 for (int j = 0; j < poBand->GetOverviewCount(); ++j)
    3699             :                 {
    3700         432 :                     if (abAlreadyUsedOverviewBand[j])
    3701         257 :                         continue;
    3702             : 
    3703         175 :                     GDALRasterBand *poOverview = poBand->GetOverview(j);
    3704             : 
    3705         175 :                     GDALCopyNoDataValue(poOverview, poBand);
    3706             : 
    3707         175 :                     const int nOvFactor = GDALComputeOvFactor(
    3708             :                         poOverview->GetXSize(), poBand->GetXSize(),
    3709             :                         poOverview->GetYSize(), poBand->GetYSize());
    3710             : 
    3711         176 :                     if (nOvFactor == panOverviewList[i] ||
    3712           1 :                         nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3713             :                                                         poBand->GetXSize(),
    3714             :                                                         poBand->GetYSize()))
    3715             :                     {
    3716         174 :                         if (iBand == 0)
    3717             :                         {
    3718             :                             const auto osNewResampling =
    3719         168 :                                 GDALGetNormalizedOvrResampling(pszResampling);
    3720             :                             const char *pszExistingResampling =
    3721          84 :                                 poOverview->GetMetadataItem("RESAMPLING");
    3722         136 :                             if (pszExistingResampling &&
    3723          52 :                                 pszExistingResampling != osNewResampling)
    3724             :                             {
    3725           1 :                                 poOverview->SetMetadataItem(
    3726           1 :                                     "RESAMPLING", osNewResampling.c_str());
    3727             :                             }
    3728             :                         }
    3729             : 
    3730         174 :                         abAlreadyUsedOverviewBand[j] = true;
    3731         174 :                         CPLAssert(nNewOverviews < poBand->GetOverviewCount());
    3732         174 :                         papoOverviewBands[nNewOverviews++] = poOverview;
    3733         174 :                         break;
    3734             :                     }
    3735             :                 }
    3736             :             }
    3737             : 
    3738         192 :             void *pScaledProgressData = GDALCreateScaledProgress(
    3739          96 :                 (iBand + iBandOffset) /
    3740          96 :                     static_cast<double>(nBandsIn + iBandOffset),
    3741          96 :                 (iBand + iBandOffset + 1) /
    3742          96 :                     static_cast<double>(nBandsIn + iBandOffset),
    3743             :                 pfnProgress, pProgressData);
    3744             : 
    3745          96 :             eErr = GDALRegenerateOverviewsEx(
    3746             :                 poBand, nNewOverviews,
    3747             :                 reinterpret_cast<GDALRasterBandH *>(papoOverviewBands),
    3748             :                 pszResampling, GDALScaledProgress, pScaledProgressData,
    3749             :                 papszOptions);
    3750             : 
    3751          96 :             GDALDestroyScaledProgress(pScaledProgressData);
    3752             :         }
    3753             : 
    3754             :         /* --------------------------------------------------------------------
    3755             :          */
    3756             :         /*      Cleanup */
    3757             :         /* --------------------------------------------------------------------
    3758             :          */
    3759          48 :         CPLFree(papoOverviewBands);
    3760             :     }
    3761             : 
    3762         253 :     pfnProgress(1.0, nullptr, pProgressData);
    3763             : 
    3764         253 :     return eErr;
    3765             : }
    3766             : 
    3767             : /************************************************************************/
    3768             : /*                      GTiffWriteDummyGeokeyDirectory()                */
    3769             : /************************************************************************/
    3770             : 
    3771        1485 : static void GTiffWriteDummyGeokeyDirectory(TIFF *hTIFF)
    3772             : {
    3773             :     // If we have existing geokeys, try to wipe them
    3774             :     // by writing a dummy geokey directory. (#2546)
    3775        1485 :     uint16_t *panVI = nullptr;
    3776        1485 :     uint16_t nKeyCount = 0;
    3777             : 
    3778        1485 :     if (TIFFGetField(hTIFF, TIFFTAG_GEOKEYDIRECTORY, &nKeyCount, &panVI))
    3779             :     {
    3780          24 :         GUInt16 anGKVersionInfo[4] = {1, 1, 0, 0};
    3781          24 :         double adfDummyDoubleParams[1] = {0.0};
    3782          24 :         TIFFSetField(hTIFF, TIFFTAG_GEOKEYDIRECTORY, 4, anGKVersionInfo);
    3783          24 :         TIFFSetField(hTIFF, TIFFTAG_GEODOUBLEPARAMS, 1, adfDummyDoubleParams);
    3784          24 :         TIFFSetField(hTIFF, TIFFTAG_GEOASCIIPARAMS, "");
    3785             :     }
    3786        1485 : }
    3787             : 
    3788             : /************************************************************************/
    3789             : /*                    IsSRSCompatibleOfGeoTIFF()                        */
    3790             : /************************************************************************/
    3791             : 
    3792        3130 : static bool IsSRSCompatibleOfGeoTIFF(const OGRSpatialReference *poSRS,
    3793             :                                      GTIFFKeysFlavorEnum eGeoTIFFKeysFlavor)
    3794             : {
    3795        3130 :     char *pszWKT = nullptr;
    3796        3130 :     if ((poSRS->IsGeographic() || poSRS->IsProjected()) && !poSRS->IsCompound())
    3797             :     {
    3798        3112 :         const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
    3799        3112 :         const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
    3800        3112 :         if (pszAuthName && pszAuthCode && EQUAL(pszAuthName, "EPSG"))
    3801        2543 :             return true;
    3802             :     }
    3803             :     OGRErr eErr;
    3804             :     {
    3805        1174 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    3806        1174 :         if (poSRS->IsDerivedGeographic() ||
    3807         587 :             (poSRS->IsProjected() && !poSRS->IsCompound() &&
    3808          70 :              poSRS->GetAxesCount() == 3))
    3809             :         {
    3810           0 :             eErr = OGRERR_FAILURE;
    3811             :         }
    3812             :         else
    3813             :         {
    3814             :             // Geographic3D CRS can't be exported to WKT1, but are
    3815             :             // valid GeoTIFF 1.1
    3816         587 :             const char *const apszOptions[] = {
    3817         587 :                 poSRS->IsGeographic() ? nullptr : "FORMAT=WKT1", nullptr};
    3818         587 :             eErr = poSRS->exportToWkt(&pszWKT, apszOptions);
    3819         587 :             if (eErr == OGRERR_FAILURE && poSRS->IsProjected() &&
    3820             :                 eGeoTIFFKeysFlavor == GEOTIFF_KEYS_ESRI_PE)
    3821             :             {
    3822           0 :                 CPLFree(pszWKT);
    3823           0 :                 const char *const apszOptionsESRIWKT[] = {"FORMAT=WKT1_ESRI",
    3824             :                                                           nullptr};
    3825           0 :                 eErr = poSRS->exportToWkt(&pszWKT, apszOptionsESRIWKT);
    3826             :             }
    3827             :         }
    3828             :     }
    3829         587 :     const bool bCompatibleOfGeoTIFF =
    3830        1173 :         (eErr == OGRERR_NONE && pszWKT != nullptr &&
    3831         586 :          strstr(pszWKT, "custom_proj4") == nullptr);
    3832         587 :     CPLFree(pszWKT);
    3833         587 :     return bCompatibleOfGeoTIFF;
    3834             : }
    3835             : 
    3836             : /************************************************************************/
    3837             : /*                          WriteGeoTIFFInfo()                          */
    3838             : /************************************************************************/
    3839             : 
    3840        5756 : void GTiffDataset::WriteGeoTIFFInfo()
    3841             : 
    3842             : {
    3843        5756 :     bool bPixelIsPoint = false;
    3844        5756 :     bool bPointGeoIgnore = false;
    3845             : 
    3846             :     const char *pszAreaOrPoint =
    3847        5756 :         GTiffDataset::GetMetadataItem(GDALMD_AREA_OR_POINT);
    3848        5756 :     if (pszAreaOrPoint && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT))
    3849             :     {
    3850          17 :         bPixelIsPoint = true;
    3851             :         bPointGeoIgnore =
    3852          17 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    3853             :     }
    3854             : 
    3855        5756 :     if (m_bForceUnsetGTOrGCPs)
    3856             :     {
    3857          11 :         m_bNeedsRewrite = true;
    3858          11 :         m_bForceUnsetGTOrGCPs = false;
    3859             : 
    3860          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE);
    3861          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS);
    3862          11 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX);
    3863             :     }
    3864             : 
    3865        5756 :     if (m_bForceUnsetProjection)
    3866             :     {
    3867           8 :         m_bNeedsRewrite = true;
    3868           8 :         m_bForceUnsetProjection = false;
    3869             : 
    3870           8 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOKEYDIRECTORY);
    3871           8 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEODOUBLEPARAMS);
    3872           8 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOASCIIPARAMS);
    3873             :     }
    3874             : 
    3875             :     /* -------------------------------------------------------------------- */
    3876             :     /*      Write geotransform if valid.                                    */
    3877             :     /* -------------------------------------------------------------------- */
    3878        5756 :     if (m_bGeoTransformValid)
    3879             :     {
    3880        1786 :         m_bNeedsRewrite = true;
    3881             : 
    3882             :         /* --------------------------------------------------------------------
    3883             :          */
    3884             :         /*      Clear old tags to ensure we don't end up with conflicting */
    3885             :         /*      information. (#2625) */
    3886             :         /* --------------------------------------------------------------------
    3887             :          */
    3888        1786 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE);
    3889        1786 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS);
    3890        1786 :         TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX);
    3891             : 
    3892             :         /* --------------------------------------------------------------------
    3893             :          */
    3894             :         /*      Write the transform.  If we have a normal north-up image we */
    3895             :         /*      use the tiepoint plus pixelscale otherwise we use a matrix. */
    3896             :         /* --------------------------------------------------------------------
    3897             :          */
    3898        1786 :         if (m_gt[2] == 0.0 && m_gt[4] == 0.0 && m_gt[5] < 0.0)
    3899             :         {
    3900        1695 :             double dfOffset = 0.0;
    3901        1695 :             if (m_eProfile != GTiffProfile::BASELINE)
    3902             :             {
    3903             :                 // In the case the SRS has a vertical component and we have
    3904             :                 // a single band, encode its scale/offset in the GeoTIFF tags
    3905        1689 :                 int bHasScale = FALSE;
    3906        1689 :                 double dfScale = GetRasterBand(1)->GetScale(&bHasScale);
    3907        1689 :                 int bHasOffset = FALSE;
    3908        1689 :                 dfOffset = GetRasterBand(1)->GetOffset(&bHasOffset);
    3909             :                 const bool bApplyScaleOffset =
    3910        1689 :                     m_oSRS.IsVertical() && GetRasterCount() == 1;
    3911        1689 :                 if (bApplyScaleOffset && !bHasScale)
    3912           0 :                     dfScale = 1.0;
    3913        1689 :                 if (!bApplyScaleOffset || !bHasOffset)
    3914        1686 :                     dfOffset = 0.0;
    3915             :                 const double adfPixelScale[3] = {
    3916        1689 :                     m_gt[1], fabs(m_gt[5]), bApplyScaleOffset ? dfScale : 0.0};
    3917        1689 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale);
    3918             :             }
    3919             : 
    3920        1695 :             double adfTiePoints[6] = {0.0,     0.0,     0.0,
    3921        1695 :                                       m_gt[0], m_gt[3], dfOffset};
    3922             : 
    3923        1695 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3924             :             {
    3925          13 :                 adfTiePoints[3] += m_gt[1] * 0.5 + m_gt[2] * 0.5;
    3926          13 :                 adfTiePoints[4] += m_gt[4] * 0.5 + m_gt[5] * 0.5;
    3927             :             }
    3928             : 
    3929        1695 :             if (m_eProfile != GTiffProfile::BASELINE)
    3930        1689 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
    3931             :         }
    3932             :         else
    3933             :         {
    3934          91 :             double adfMatrix[16] = {};
    3935             : 
    3936          91 :             adfMatrix[0] = m_gt[1];
    3937          91 :             adfMatrix[1] = m_gt[2];
    3938          91 :             adfMatrix[3] = m_gt[0];
    3939          91 :             adfMatrix[4] = m_gt[4];
    3940          91 :             adfMatrix[5] = m_gt[5];
    3941          91 :             adfMatrix[7] = m_gt[3];
    3942          91 :             adfMatrix[15] = 1.0;
    3943             : 
    3944          91 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3945             :             {
    3946           0 :                 adfMatrix[3] += m_gt[1] * 0.5 + m_gt[2] * 0.5;
    3947           0 :                 adfMatrix[7] += m_gt[4] * 0.5 + m_gt[5] * 0.5;
    3948             :             }
    3949             : 
    3950          91 :             if (m_eProfile != GTiffProfile::BASELINE)
    3951          91 :                 TIFFSetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
    3952             :         }
    3953             : 
    3954        1786 :         if (m_poBaseDS == nullptr)
    3955             :         {
    3956             :             // Do we need a world file?
    3957        1786 :             if (CPLFetchBool(m_papszCreationOptions, "TFW", false))
    3958           7 :                 GDALWriteWorldFile(m_osFilename.c_str(), "tfw", m_gt.data());
    3959        1779 :             else if (CPLFetchBool(m_papszCreationOptions, "WORLDFILE", false))
    3960           2 :                 GDALWriteWorldFile(m_osFilename.c_str(), "wld", m_gt.data());
    3961             :         }
    3962             :     }
    3963        3984 :     else if (GetGCPCount() > 0 && GetGCPCount() <= knMAX_GCP_COUNT &&
    3964          14 :              m_eProfile != GTiffProfile::BASELINE)
    3965             :     {
    3966          14 :         m_bNeedsRewrite = true;
    3967             : 
    3968             :         double *padfTiePoints = static_cast<double *>(
    3969          14 :             CPLMalloc(6 * sizeof(double) * GetGCPCount()));
    3970             : 
    3971          74 :         for (size_t iGCP = 0; iGCP < m_aoGCPs.size(); ++iGCP)
    3972             :         {
    3973             : 
    3974          60 :             padfTiePoints[iGCP * 6 + 0] = m_aoGCPs[iGCP].Pixel();
    3975          60 :             padfTiePoints[iGCP * 6 + 1] = m_aoGCPs[iGCP].Line();
    3976          60 :             padfTiePoints[iGCP * 6 + 2] = 0;
    3977          60 :             padfTiePoints[iGCP * 6 + 3] = m_aoGCPs[iGCP].X();
    3978          60 :             padfTiePoints[iGCP * 6 + 4] = m_aoGCPs[iGCP].Y();
    3979          60 :             padfTiePoints[iGCP * 6 + 5] = m_aoGCPs[iGCP].Z();
    3980             : 
    3981          60 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3982             :             {
    3983           0 :                 padfTiePoints[iGCP * 6 + 0] += 0.5;
    3984           0 :                 padfTiePoints[iGCP * 6 + 1] += 0.5;
    3985             :             }
    3986             :         }
    3987             : 
    3988          14 :         TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * GetGCPCount(),
    3989             :                      padfTiePoints);
    3990          14 :         CPLFree(padfTiePoints);
    3991             :     }
    3992             : 
    3993             :     /* -------------------------------------------------------------------- */
    3994             :     /*      Write out projection definition.                                */
    3995             :     /* -------------------------------------------------------------------- */
    3996        5756 :     const bool bHasProjection = !m_oSRS.IsEmpty();
    3997        5756 :     if ((bHasProjection || bPixelIsPoint) &&
    3998        1489 :         m_eProfile != GTiffProfile::BASELINE)
    3999             :     {
    4000        1485 :         m_bNeedsRewrite = true;
    4001             : 
    4002             :         // If we have existing geokeys, try to wipe them
    4003             :         // by writing a dummy geokey directory. (#2546)
    4004        1485 :         GTiffWriteDummyGeokeyDirectory(m_hTIFF);
    4005             : 
    4006        1485 :         GTIF *psGTIF = GTiffDataset::GTIFNew(m_hTIFF);
    4007             : 
    4008             :         // Set according to coordinate system.
    4009        1485 :         if (bHasProjection)
    4010             :         {
    4011        1484 :             if (IsSRSCompatibleOfGeoTIFF(&m_oSRS, m_eGeoTIFFKeysFlavor))
    4012             :             {
    4013        1482 :                 GTIFSetFromOGISDefnEx(psGTIF,
    4014             :                                       OGRSpatialReference::ToHandle(&m_oSRS),
    4015             :                                       m_eGeoTIFFKeysFlavor, m_eGeoTIFFVersion);
    4016             :             }
    4017             :             else
    4018             :             {
    4019           2 :                 GDALPamDataset::SetSpatialRef(&m_oSRS);
    4020             :             }
    4021             :         }
    4022             : 
    4023        1485 :         if (bPixelIsPoint)
    4024             :         {
    4025          17 :             GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
    4026             :                        RasterPixelIsPoint);
    4027             :         }
    4028             : 
    4029        1485 :         GTIFWriteKeys(psGTIF);
    4030        1485 :         GTIFFree(psGTIF);
    4031             :     }
    4032        5756 : }
    4033             : 
    4034             : /************************************************************************/
    4035             : /*                         AppendMetadataItem()                         */
    4036             : /************************************************************************/
    4037             : 
    4038        3701 : static void AppendMetadataItem(CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
    4039             :                                const char *pszKey, const char *pszValue,
    4040             :                                CPLXMLNode *psValueNode, int nBand,
    4041             :                                const char *pszRole, const char *pszDomain)
    4042             : 
    4043             : {
    4044        3701 :     CPLAssert(pszValue || psValueNode);
    4045        3701 :     CPLAssert(!(pszValue && psValueNode));
    4046             : 
    4047             :     /* -------------------------------------------------------------------- */
    4048             :     /*      Create the Item element, and subcomponents.                     */
    4049             :     /* -------------------------------------------------------------------- */
    4050        3701 :     CPLXMLNode *psItem = CPLCreateXMLNode(nullptr, CXT_Element, "Item");
    4051        3701 :     CPLAddXMLAttributeAndValue(psItem, "name", pszKey);
    4052             : 
    4053        3701 :     if (nBand > 0)
    4054             :     {
    4055        1022 :         char szBandId[32] = {};
    4056        1022 :         snprintf(szBandId, sizeof(szBandId), "%d", nBand - 1);
    4057        1022 :         CPLAddXMLAttributeAndValue(psItem, "sample", szBandId);
    4058             :     }
    4059             : 
    4060        3701 :     if (pszRole != nullptr)
    4061         380 :         CPLAddXMLAttributeAndValue(psItem, "role", pszRole);
    4062             : 
    4063        3701 :     if (pszDomain != nullptr && strlen(pszDomain) > 0)
    4064        1009 :         CPLAddXMLAttributeAndValue(psItem, "domain", pszDomain);
    4065             : 
    4066        3701 :     if (pszValue)
    4067             :     {
    4068             :         // Note: this escaping should not normally be done, as the serialization
    4069             :         // of the tree to XML also does it, so we end up width double XML escaping,
    4070             :         // but keep it for backward compatibility.
    4071        3681 :         char *pszEscapedItemValue = CPLEscapeString(pszValue, -1, CPLES_XML);
    4072        3681 :         CPLCreateXMLNode(psItem, CXT_Text, pszEscapedItemValue);
    4073        3681 :         CPLFree(pszEscapedItemValue);
    4074             :     }
    4075             :     else
    4076             :     {
    4077          20 :         CPLAddXMLChild(psItem, psValueNode);
    4078             :     }
    4079             : 
    4080             :     /* -------------------------------------------------------------------- */
    4081             :     /*      Create root, if missing.                                        */
    4082             :     /* -------------------------------------------------------------------- */
    4083        3701 :     if (*ppsRoot == nullptr)
    4084         729 :         *ppsRoot = CPLCreateXMLNode(nullptr, CXT_Element, "GDALMetadata");
    4085             : 
    4086             :     /* -------------------------------------------------------------------- */
    4087             :     /*      Append item to tail.  We keep track of the tail to avoid        */
    4088             :     /*      O(nsquared) time as the list gets longer.                       */
    4089             :     /* -------------------------------------------------------------------- */
    4090        3701 :     if (*ppsTail == nullptr)
    4091         729 :         CPLAddXMLChild(*ppsRoot, psItem);
    4092             :     else
    4093        2972 :         CPLAddXMLSibling(*ppsTail, psItem);
    4094             : 
    4095        3701 :     *ppsTail = psItem;
    4096        3701 : }
    4097             : 
    4098             : /************************************************************************/
    4099             : /*                         AppendMetadataItem()                         */
    4100             : /************************************************************************/
    4101             : 
    4102        3681 : static void AppendMetadataItem(CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
    4103             :                                const char *pszKey, const char *pszValue,
    4104             :                                int nBand, const char *pszRole,
    4105             :                                const char *pszDomain)
    4106             : 
    4107             : {
    4108        3681 :     AppendMetadataItem(ppsRoot, ppsTail, pszKey, pszValue, nullptr, nBand,
    4109             :                        pszRole, pszDomain);
    4110        3681 : }
    4111             : 
    4112             : /************************************************************************/
    4113             : /*                         WriteMDMetadata()                            */
    4114             : /************************************************************************/
    4115             : 
    4116      310713 : static void WriteMDMetadata(GDALMultiDomainMetadata *poMDMD, TIFF *hTIFF,
    4117             :                             CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
    4118             :                             int nBand, GTiffProfile eProfile)
    4119             : 
    4120             : {
    4121             : 
    4122             :     /* ==================================================================== */
    4123             :     /*      Process each domain.                                            */
    4124             :     /* ==================================================================== */
    4125      310713 :     CSLConstList papszDomainList = poMDMD->GetDomainList();
    4126      319002 :     for (int iDomain = 0; papszDomainList && papszDomainList[iDomain];
    4127             :          ++iDomain)
    4128             :     {
    4129        8289 :         CSLConstList papszMD = poMDMD->GetMetadata(papszDomainList[iDomain]);
    4130        8289 :         bool bIsXMLOrJSON = false;
    4131             : 
    4132        8289 :         if (EQUAL(papszDomainList[iDomain], "IMAGE_STRUCTURE") ||
    4133        2427 :             EQUAL(papszDomainList[iDomain], "DERIVED_SUBDATASETS"))
    4134        5865 :             continue;  // Ignored.
    4135        2424 :         if (EQUAL(papszDomainList[iDomain], "COLOR_PROFILE"))
    4136           3 :             continue;  // Handled elsewhere.
    4137        2421 :         if (EQUAL(papszDomainList[iDomain], MD_DOMAIN_RPC))
    4138           7 :             continue;  // Handled elsewhere.
    4139        2415 :         if (EQUAL(papszDomainList[iDomain], "xml:ESRI") &&
    4140           1 :             CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
    4141           1 :             continue;  // Handled elsewhere.
    4142        2413 :         if (EQUAL(papszDomainList[iDomain], "xml:XMP"))
    4143           2 :             continue;  // Handled in SetMetadata.
    4144             : 
    4145        2411 :         if (STARTS_WITH_CI(papszDomainList[iDomain], "xml:") ||
    4146        2409 :             STARTS_WITH_CI(papszDomainList[iDomain], "json:"))
    4147             :         {
    4148          11 :             bIsXMLOrJSON = true;
    4149             :         }
    4150             : 
    4151             :         /* --------------------------------------------------------------------
    4152             :          */
    4153             :         /*      Process each item in this domain. */
    4154             :         /* --------------------------------------------------------------------
    4155             :          */
    4156        7293 :         for (int iItem = 0; papszMD && papszMD[iItem]; ++iItem)
    4157             :         {
    4158        4882 :             const char *pszItemValue = nullptr;
    4159        4882 :             char *pszItemName = nullptr;
    4160             : 
    4161        4882 :             if (bIsXMLOrJSON)
    4162             :             {
    4163          10 :                 pszItemName = CPLStrdup("doc");
    4164          10 :                 pszItemValue = papszMD[iItem];
    4165             :             }
    4166             :             else
    4167             :             {
    4168        4872 :                 pszItemValue = CPLParseNameValue(papszMD[iItem], &pszItemName);
    4169        4872 :                 if (pszItemName == nullptr)
    4170             :                 {
    4171          49 :                     CPLDebug("GTiff", "Invalid metadata item : %s",
    4172          49 :                              papszMD[iItem]);
    4173          49 :                     continue;
    4174             :                 }
    4175             :             }
    4176             : 
    4177             :             /* --------------------------------------------------------------------
    4178             :              */
    4179             :             /*      Convert into XML item or handle as a special TIFF tag. */
    4180             :             /* --------------------------------------------------------------------
    4181             :              */
    4182        4833 :             if (strlen(papszDomainList[iDomain]) == 0 && nBand == 0 &&
    4183        3619 :                 (STARTS_WITH_CI(pszItemName, "TIFFTAG_") ||
    4184        3558 :                  (EQUAL(pszItemName, "GEO_METADATA") &&
    4185        3557 :                   eProfile == GTiffProfile::GDALGEOTIFF) ||
    4186        3557 :                  (EQUAL(pszItemName, "TIFF_RSID") &&
    4187             :                   eProfile == GTiffProfile::GDALGEOTIFF)))
    4188             :             {
    4189          63 :                 if (EQUAL(pszItemName, "TIFFTAG_RESOLUTIONUNIT"))
    4190             :                 {
    4191             :                     // ResolutionUnit can't be 0, which is the default if
    4192             :                     // atoi() fails.  Set to 1=Unknown.
    4193           9 :                     int v = atoi(pszItemValue);
    4194           9 :                     if (!v)
    4195           1 :                         v = RESUNIT_NONE;
    4196           9 :                     TIFFSetField(hTIFF, TIFFTAG_RESOLUTIONUNIT, v);
    4197             :                 }
    4198             :                 else
    4199             :                 {
    4200          54 :                     bool bFoundTag = false;
    4201          54 :                     size_t iTag = 0;  // Used after for.
    4202          54 :                     const auto *pasTIFFTags = GTiffDataset::GetTIFFTags();
    4203         286 :                     for (; pasTIFFTags[iTag].pszTagName; ++iTag)
    4204             :                     {
    4205         286 :                         if (EQUAL(pszItemName, pasTIFFTags[iTag].pszTagName))
    4206             :                         {
    4207          54 :                             bFoundTag = true;
    4208          54 :                             break;
    4209             :                         }
    4210             :                     }
    4211             : 
    4212          54 :                     if (bFoundTag &&
    4213          54 :                         pasTIFFTags[iTag].eType == GTIFFTAGTYPE_STRING)
    4214          33 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4215             :                                      pszItemValue);
    4216          21 :                     else if (bFoundTag &&
    4217          21 :                              pasTIFFTags[iTag].eType == GTIFFTAGTYPE_FLOAT)
    4218          16 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4219             :                                      CPLAtof(pszItemValue));
    4220           5 :                     else if (bFoundTag &&
    4221           5 :                              pasTIFFTags[iTag].eType == GTIFFTAGTYPE_SHORT)
    4222           4 :                         TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4223             :                                      atoi(pszItemValue));
    4224           1 :                     else if (bFoundTag && pasTIFFTags[iTag].eType ==
    4225             :                                               GTIFFTAGTYPE_BYTE_STRING)
    4226             :                     {
    4227           1 :                         uint32_t nLen =
    4228           1 :                             static_cast<uint32_t>(strlen(pszItemValue));
    4229           1 :                         if (nLen)
    4230             :                         {
    4231           1 :                             TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal, nLen,
    4232             :                                          pszItemValue);
    4233           1 :                         }
    4234             :                     }
    4235             :                     else
    4236           0 :                         CPLError(CE_Warning, CPLE_NotSupported,
    4237             :                                  "%s metadata item is unhandled and "
    4238             :                                  "will not be written",
    4239             :                                  pszItemName);
    4240          63 :                 }
    4241             :             }
    4242        4770 :             else if (nBand == 0 && EQUAL(pszItemName, GDALMD_AREA_OR_POINT))
    4243             :             {
    4244             :                 /* Do nothing, handled elsewhere. */;
    4245             :             }
    4246             :             else
    4247             :             {
    4248        2890 :                 AppendMetadataItem(ppsRoot, ppsTail, pszItemName, pszItemValue,
    4249        2890 :                                    nBand, nullptr, papszDomainList[iDomain]);
    4250             :             }
    4251             : 
    4252        4833 :             CPLFree(pszItemName);
    4253             :         }
    4254             : 
    4255             :         /* --------------------------------------------------------------------
    4256             :          */
    4257             :         /*      Remove TIFFTAG_xxxxxx that are already set but no longer in */
    4258             :         /*      the metadata list (#5619) */
    4259             :         /* --------------------------------------------------------------------
    4260             :          */
    4261        2411 :         if (strlen(papszDomainList[iDomain]) == 0 && nBand == 0)
    4262             :         {
    4263        2154 :             const auto *pasTIFFTags = GTiffDataset::GetTIFFTags();
    4264       32310 :             for (size_t iTag = 0; pasTIFFTags[iTag].pszTagName; ++iTag)
    4265             :             {
    4266       30156 :                 uint32_t nCount = 0;
    4267       30156 :                 char *pszText = nullptr;
    4268       30156 :                 int16_t nVal = 0;
    4269       30156 :                 float fVal = 0.0f;
    4270             :                 const char *pszVal =
    4271       30156 :                     CSLFetchNameValue(papszMD, pasTIFFTags[iTag].pszTagName);
    4272       60249 :                 if (pszVal == nullptr &&
    4273       30093 :                     ((pasTIFFTags[iTag].eType == GTIFFTAGTYPE_STRING &&
    4274       17199 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal,
    4275       30085 :                                    &pszText)) ||
    4276       30085 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_SHORT &&
    4277        6449 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &nVal)) ||
    4278       30082 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_FLOAT &&
    4279        4292 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &fVal)) ||
    4280       30081 :                      (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_BYTE_STRING &&
    4281        2153 :                       TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &nCount,
    4282             :                                    &pszText))))
    4283             :                 {
    4284          13 :                     TIFFUnsetField(hTIFF, pasTIFFTags[iTag].nTagVal);
    4285             :                 }
    4286             :             }
    4287             :         }
    4288             :     }
    4289      310713 : }
    4290             : 
    4291             : /************************************************************************/
    4292             : /*                           WriteRPC()                                 */
    4293             : /************************************************************************/
    4294             : 
    4295        9977 : void GTiffDataset::WriteRPC(GDALDataset *poSrcDS, TIFF *l_hTIFF,
    4296             :                             int bSrcIsGeoTIFF, GTiffProfile eProfile,
    4297             :                             const char *pszTIFFFilename,
    4298             :                             CSLConstList papszCreationOptions,
    4299             :                             bool bWriteOnlyInPAMIfNeeded)
    4300             : {
    4301             :     /* -------------------------------------------------------------------- */
    4302             :     /*      Handle RPC data written to TIFF RPCCoefficient tag, RPB file,   */
    4303             :     /*      RPCTEXT file or PAM.                                            */
    4304             :     /* -------------------------------------------------------------------- */
    4305        9977 :     char **papszRPCMD = poSrcDS->GetMetadata(MD_DOMAIN_RPC);
    4306        9977 :     if (papszRPCMD != nullptr)
    4307             :     {
    4308          32 :         bool bRPCSerializedOtherWay = false;
    4309             : 
    4310          32 :         if (eProfile == GTiffProfile::GDALGEOTIFF)
    4311             :         {
    4312          20 :             if (!bWriteOnlyInPAMIfNeeded)
    4313          11 :                 GTiffDatasetWriteRPCTag(l_hTIFF, papszRPCMD);
    4314          20 :             bRPCSerializedOtherWay = true;
    4315             :         }
    4316             : 
    4317             :         // Write RPB file if explicitly asked, or if a non GDAL specific
    4318             :         // profile is selected and RPCTXT is not asked.
    4319             :         bool bRPBExplicitlyAsked =
    4320          32 :             CPLFetchBool(papszCreationOptions, "RPB", false);
    4321             :         bool bRPBExplicitlyDenied =
    4322          32 :             !CPLFetchBool(papszCreationOptions, "RPB", true);
    4323          44 :         if ((eProfile != GTiffProfile::GDALGEOTIFF &&
    4324          12 :              !CPLFetchBool(papszCreationOptions, "RPCTXT", false) &&
    4325          44 :              !bRPBExplicitlyDenied) ||
    4326             :             bRPBExplicitlyAsked)
    4327             :         {
    4328           8 :             if (!bWriteOnlyInPAMIfNeeded)
    4329           4 :                 GDALWriteRPBFile(pszTIFFFilename, papszRPCMD);
    4330           8 :             bRPCSerializedOtherWay = true;
    4331             :         }
    4332             : 
    4333          32 :         if (CPLFetchBool(papszCreationOptions, "RPCTXT", false))
    4334             :         {
    4335           2 :             if (!bWriteOnlyInPAMIfNeeded)
    4336           1 :                 GDALWriteRPCTXTFile(pszTIFFFilename, papszRPCMD);
    4337           2 :             bRPCSerializedOtherWay = true;
    4338             :         }
    4339             : 
    4340          32 :         if (!bRPCSerializedOtherWay && bWriteOnlyInPAMIfNeeded && bSrcIsGeoTIFF)
    4341           1 :             cpl::down_cast<GTiffDataset *>(poSrcDS)
    4342           1 :                 ->GDALPamDataset::SetMetadata(papszRPCMD, MD_DOMAIN_RPC);
    4343             :     }
    4344        9977 : }
    4345             : 
    4346             : /************************************************************************/
    4347             : /*                           WriteMetadata()                            */
    4348             : /************************************************************************/
    4349             : 
    4350        7896 : bool GTiffDataset::WriteMetadata(GDALDataset *poSrcDS, TIFF *l_hTIFF,
    4351             :                                  bool bSrcIsGeoTIFF, GTiffProfile eProfile,
    4352             :                                  const char *pszTIFFFilename,
    4353             :                                  CSLConstList papszCreationOptions,
    4354             :                                  bool bExcludeRPBandIMGFileWriting)
    4355             : 
    4356             : {
    4357             :     /* -------------------------------------------------------------------- */
    4358             :     /*      Convert all the remaining metadata into a simple XML            */
    4359             :     /*      format.                                                         */
    4360             :     /* -------------------------------------------------------------------- */
    4361        7896 :     CPLXMLNode *psRoot = nullptr;
    4362        7896 :     CPLXMLNode *psTail = nullptr;
    4363             : 
    4364             :     const char *pszCopySrcMDD =
    4365        7896 :         CSLFetchNameValueDef(papszCreationOptions, "COPY_SRC_MDD", "AUTO");
    4366             :     char **papszSrcMDD =
    4367        7896 :         CSLFetchNameValueMultiple(papszCreationOptions, "SRC_MDD");
    4368             : 
    4369             :     GTiffDataset *poSrcDSGTiff =
    4370        7896 :         bSrcIsGeoTIFF ? cpl::down_cast<GTiffDataset *>(poSrcDS) : nullptr;
    4371             : 
    4372        7896 :     if (poSrcDSGTiff)
    4373             :     {
    4374        5788 :         WriteMDMetadata(&poSrcDSGTiff->m_oGTiffMDMD, l_hTIFF, &psRoot, &psTail,
    4375             :                         0, eProfile);
    4376             :     }
    4377             :     else
    4378             :     {
    4379        2108 :         if (EQUAL(pszCopySrcMDD, "AUTO") || CPLTestBool(pszCopySrcMDD) ||
    4380             :             papszSrcMDD)
    4381             :         {
    4382        4210 :             GDALMultiDomainMetadata l_oMDMD;
    4383             :             {
    4384        2105 :                 CSLConstList papszMD = poSrcDS->GetMetadata();
    4385        2109 :                 if (CSLCount(papszMD) > 0 &&
    4386           4 :                     (!papszSrcMDD || CSLFindString(papszSrcMDD, "") >= 0 ||
    4387           2 :                      CSLFindString(papszSrcMDD, "_DEFAULT_") >= 0))
    4388             :                 {
    4389        1594 :                     l_oMDMD.SetMetadata(papszMD);
    4390             :                 }
    4391             :             }
    4392             : 
    4393        2105 :             if (EQUAL(pszCopySrcMDD, "AUTO") && !papszSrcMDD)
    4394             :             {
    4395             :                 // Propagate ISIS3 or VICAR metadata
    4396        6288 :                 for (const char *pszMDD : {"json:ISIS3", "json:VICAR"})
    4397             :                 {
    4398        4192 :                     char **papszMD = poSrcDS->GetMetadata(pszMDD);
    4399        4192 :                     if (papszMD)
    4400             :                     {
    4401           5 :                         l_oMDMD.SetMetadata(papszMD, pszMDD);
    4402             :                     }
    4403             :                 }
    4404             :             }
    4405             : 
    4406        2105 :             if ((!EQUAL(pszCopySrcMDD, "AUTO") && CPLTestBool(pszCopySrcMDD)) ||
    4407             :                 papszSrcMDD)
    4408             :             {
    4409           9 :                 char **papszDomainList = poSrcDS->GetMetadataDomainList();
    4410          39 :                 for (CSLConstList papszIter = papszDomainList;
    4411          39 :                      papszIter && *papszIter; ++papszIter)
    4412             :                 {
    4413          30 :                     const char *pszDomain = *papszIter;
    4414          46 :                     if (pszDomain[0] != 0 &&
    4415          16 :                         (!papszSrcMDD ||
    4416          16 :                          CSLFindString(papszSrcMDD, pszDomain) >= 0))
    4417             :                     {
    4418          12 :                         l_oMDMD.SetMetadata(poSrcDS->GetMetadata(pszDomain),
    4419             :                                             pszDomain);
    4420             :                     }
    4421             :                 }
    4422           9 :                 CSLDestroy(papszDomainList);
    4423             :             }
    4424             : 
    4425        2105 :             WriteMDMetadata(&l_oMDMD, l_hTIFF, &psRoot, &psTail, 0, eProfile);
    4426             :         }
    4427             :     }
    4428             : 
    4429        7896 :     if (!bExcludeRPBandIMGFileWriting &&
    4430        5782 :         (!poSrcDSGTiff || poSrcDSGTiff->m_poBaseDS == nullptr))
    4431             :     {
    4432        7885 :         WriteRPC(poSrcDS, l_hTIFF, bSrcIsGeoTIFF, eProfile, pszTIFFFilename,
    4433             :                  papszCreationOptions);
    4434             : 
    4435             :         /* --------------------------------------------------------------------
    4436             :          */
    4437             :         /*      Handle metadata data written to an IMD file. */
    4438             :         /* --------------------------------------------------------------------
    4439             :          */
    4440        7885 :         char **papszIMDMD = poSrcDS->GetMetadata(MD_DOMAIN_IMD);
    4441        7885 :         if (papszIMDMD != nullptr)
    4442             :         {
    4443          20 :             GDALWriteIMDFile(pszTIFFFilename, papszIMDMD);
    4444             :         }
    4445             :     }
    4446             : 
    4447        7896 :     uint16_t nPhotometric = 0;
    4448        7896 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric)))
    4449           1 :         nPhotometric = PHOTOMETRIC_MINISBLACK;
    4450             : 
    4451        7896 :     const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
    4452             :         GDALDataset::ToHandle(poSrcDS), nPhotometric, papszCreationOptions);
    4453             : 
    4454             :     /* -------------------------------------------------------------------- */
    4455             :     /*      We also need to address band specific metadata, and special     */
    4456             :     /*      "role" metadata.                                                */
    4457             :     /* -------------------------------------------------------------------- */
    4458      315648 :     for (int nBand = 1; nBand <= poSrcDS->GetRasterCount(); ++nBand)
    4459             :     {
    4460      307752 :         GDALRasterBand *poBand = poSrcDS->GetRasterBand(nBand);
    4461             : 
    4462      307752 :         if (bSrcIsGeoTIFF)
    4463             :         {
    4464             :             GTiffRasterBand *poSrcBandGTiff =
    4465      302729 :                 cpl::down_cast<GTiffRasterBand *>(poBand);
    4466      302729 :             assert(poSrcBandGTiff);
    4467      302729 :             WriteMDMetadata(&poSrcBandGTiff->m_oGTiffMDMD, l_hTIFF, &psRoot,
    4468             :                             &psTail, nBand, eProfile);
    4469             :         }
    4470             :         else
    4471             :         {
    4472       10046 :             GDALMultiDomainMetadata l_oMDMD;
    4473        5023 :             bool bOMDMDSet = false;
    4474             : 
    4475        5023 :             if (EQUAL(pszCopySrcMDD, "AUTO") && !papszSrcMDD)
    4476             :             {
    4477       15033 :                 for (const char *pszDomain : {"", "IMAGERY"})
    4478             :                 {
    4479       10022 :                     if (CSLConstList papszMD = poBand->GetMetadata(pszDomain))
    4480             :                     {
    4481          89 :                         if (papszMD[0])
    4482             :                         {
    4483          89 :                             bOMDMDSet = true;
    4484          89 :                             l_oMDMD.SetMetadata(papszMD, pszDomain);
    4485             :                         }
    4486             :                     }
    4487        5011 :                 }
    4488             :             }
    4489          12 :             else if (CPLTestBool(pszCopySrcMDD) || papszSrcMDD)
    4490             :             {
    4491           9 :                 char **papszDomainList = poBand->GetMetadataDomainList();
    4492           3 :                 for (const char *pszDomain :
    4493          15 :                      cpl::Iterate(CSLConstList(papszDomainList)))
    4494             :                 {
    4495           9 :                     if (pszDomain[0] != 0 &&
    4496           5 :                         !EQUAL(pszDomain, "IMAGE_STRUCTURE") &&
    4497           2 :                         (!papszSrcMDD ||
    4498           2 :                          CSLFindString(papszSrcMDD, pszDomain) >= 0))
    4499             :                     {
    4500           2 :                         bOMDMDSet = true;
    4501           2 :                         l_oMDMD.SetMetadata(poBand->GetMetadata(pszDomain),
    4502             :                                             pszDomain);
    4503             :                     }
    4504             :                 }
    4505           9 :                 CSLDestroy(papszDomainList);
    4506             :             }
    4507             : 
    4508        5023 :             if (bOMDMDSet)
    4509             :             {
    4510          91 :                 WriteMDMetadata(&l_oMDMD, l_hTIFF, &psRoot, &psTail, nBand,
    4511             :                                 eProfile);
    4512             :             }
    4513             :         }
    4514             : 
    4515      307752 :         const double dfOffset = poBand->GetOffset();
    4516      307752 :         const double dfScale = poBand->GetScale();
    4517      307752 :         bool bGeoTIFFScaleOffsetInZ = false;
    4518      307752 :         GDALGeoTransform gt;
    4519             :         // Check if we have already encoded scale/offset in the GeoTIFF tags
    4520      313813 :         if (poSrcDS->GetGeoTransform(gt) == CE_None && gt[2] == 0.0 &&
    4521        6046 :             gt[4] == 0.0 && gt[5] < 0.0 && poSrcDS->GetSpatialRef() &&
    4522      313820 :             poSrcDS->GetSpatialRef()->IsVertical() &&
    4523           7 :             poSrcDS->GetRasterCount() == 1)
    4524             :         {
    4525           7 :             bGeoTIFFScaleOffsetInZ = true;
    4526             :         }
    4527             : 
    4528      307752 :         if ((dfOffset != 0.0 || dfScale != 1.0) && !bGeoTIFFScaleOffsetInZ)
    4529             :         {
    4530          25 :             char szValue[128] = {};
    4531             : 
    4532          25 :             CPLsnprintf(szValue, sizeof(szValue), "%.17g", dfOffset);
    4533          25 :             AppendMetadataItem(&psRoot, &psTail, "OFFSET", szValue, nBand,
    4534             :                                "offset", "");
    4535          25 :             CPLsnprintf(szValue, sizeof(szValue), "%.17g", dfScale);
    4536          25 :             AppendMetadataItem(&psRoot, &psTail, "SCALE", szValue, nBand,
    4537             :                                "scale", "");
    4538             :         }
    4539             : 
    4540      307752 :         const char *pszUnitType = poBand->GetUnitType();
    4541      307752 :         if (pszUnitType != nullptr && pszUnitType[0] != '\0')
    4542             :         {
    4543          38 :             bool bWriteUnit = true;
    4544          38 :             auto poSRS = poSrcDS->GetSpatialRef();
    4545          38 :             if (poSRS && poSRS->IsCompound())
    4546             :             {
    4547           2 :                 const char *pszVertUnit = nullptr;
    4548           2 :                 poSRS->GetTargetLinearUnits("COMPD_CS|VERT_CS", &pszVertUnit);
    4549           2 :                 if (pszVertUnit && EQUAL(pszVertUnit, pszUnitType))
    4550             :                 {
    4551           2 :                     bWriteUnit = false;
    4552             :                 }
    4553             :             }
    4554          38 :             if (bWriteUnit)
    4555             :             {
    4556          36 :                 AppendMetadataItem(&psRoot, &psTail, "UNITTYPE", pszUnitType,
    4557             :                                    nBand, "unittype", "");
    4558             :             }
    4559             :         }
    4560             : 
    4561      307752 :         if (strlen(poBand->GetDescription()) > 0)
    4562             :         {
    4563          24 :             AppendMetadataItem(&psRoot, &psTail, "DESCRIPTION",
    4564          24 :                                poBand->GetDescription(), nBand, "description",
    4565             :                                "");
    4566             :         }
    4567             : 
    4568      307969 :         if (!bStandardColorInterp &&
    4569         217 :             !(nBand <= 3 && EQUAL(CSLFetchNameValueDef(papszCreationOptions,
    4570             :                                                        "PHOTOMETRIC", ""),
    4571             :                                   "RGB")))
    4572             :         {
    4573         250 :             AppendMetadataItem(&psRoot, &psTail, "COLORINTERP",
    4574             :                                GDALGetColorInterpretationName(
    4575         250 :                                    poBand->GetColorInterpretation()),
    4576             :                                nBand, "colorinterp", "");
    4577             :         }
    4578             :     }
    4579             : 
    4580        7896 :     CSLDestroy(papszSrcMDD);
    4581             : 
    4582             :     const char *pszTilingSchemeName =
    4583        7896 :         CSLFetchNameValue(papszCreationOptions, "@TILING_SCHEME_NAME");
    4584        7896 :     if (pszTilingSchemeName)
    4585             :     {
    4586          23 :         AppendMetadataItem(&psRoot, &psTail, "NAME", pszTilingSchemeName, 0,
    4587             :                            nullptr, "TILING_SCHEME");
    4588             : 
    4589          23 :         const char *pszZoomLevel = CSLFetchNameValue(
    4590             :             papszCreationOptions, "@TILING_SCHEME_ZOOM_LEVEL");
    4591          23 :         if (pszZoomLevel)
    4592             :         {
    4593          23 :             AppendMetadataItem(&psRoot, &psTail, "ZOOM_LEVEL", pszZoomLevel, 0,
    4594             :                                nullptr, "TILING_SCHEME");
    4595             :         }
    4596             : 
    4597          23 :         const char *pszAlignedLevels = CSLFetchNameValue(
    4598             :             papszCreationOptions, "@TILING_SCHEME_ALIGNED_LEVELS");
    4599          23 :         if (pszAlignedLevels)
    4600             :         {
    4601           4 :             AppendMetadataItem(&psRoot, &psTail, "ALIGNED_LEVELS",
    4602             :                                pszAlignedLevels, 0, nullptr, "TILING_SCHEME");
    4603             :         }
    4604             :     }
    4605             : 
    4606        7896 :     if (const char *pszOverviewResampling =
    4607        7896 :             CSLFetchNameValue(papszCreationOptions, "@OVERVIEW_RESAMPLING"))
    4608             :     {
    4609          39 :         AppendMetadataItem(&psRoot, &psTail, "OVERVIEW_RESAMPLING",
    4610             :                            pszOverviewResampling, 0, nullptr,
    4611             :                            "IMAGE_STRUCTURE");
    4612             :     }
    4613             : 
    4614             :     /* -------------------------------------------------------------------- */
    4615             :     /*      Write information about some codecs.                            */
    4616             :     /* -------------------------------------------------------------------- */
    4617        7896 :     if (CPLTestBool(
    4618             :             CPLGetConfigOption("GTIFF_WRITE_IMAGE_STRUCTURE_METADATA", "YES")))
    4619             :     {
    4620             :         const char *pszTileInterleave =
    4621        7891 :             CSLFetchNameValue(papszCreationOptions, "@TILE_INTERLEAVE");
    4622        7891 :         if (pszTileInterleave && CPLTestBool(pszTileInterleave))
    4623             :         {
    4624           7 :             AppendMetadataItem(&psRoot, &psTail, "INTERLEAVE", "TILE", 0,
    4625             :                                nullptr, "IMAGE_STRUCTURE");
    4626             :         }
    4627             : 
    4628             :         const char *pszCompress =
    4629        7891 :             CSLFetchNameValue(papszCreationOptions, "COMPRESS");
    4630        7891 :         if (pszCompress && EQUAL(pszCompress, "WEBP"))
    4631             :         {
    4632          31 :             if (GTiffGetWebPLossless(papszCreationOptions))
    4633             :             {
    4634           6 :                 AppendMetadataItem(&psRoot, &psTail,
    4635             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4636             :                                    nullptr, "IMAGE_STRUCTURE");
    4637             :             }
    4638             :             else
    4639             :             {
    4640          25 :                 AppendMetadataItem(
    4641             :                     &psRoot, &psTail, "WEBP_LEVEL",
    4642          25 :                     CPLSPrintf("%d", GTiffGetWebPLevel(papszCreationOptions)),
    4643             :                     0, nullptr, "IMAGE_STRUCTURE");
    4644             :             }
    4645             :         }
    4646        7860 :         else if (pszCompress && STARTS_WITH_CI(pszCompress, "LERC"))
    4647             :         {
    4648             :             const double dfMaxZError =
    4649          97 :                 GTiffGetLERCMaxZError(papszCreationOptions);
    4650             :             const double dfMaxZErrorOverview =
    4651          97 :                 GTiffGetLERCMaxZErrorOverview(papszCreationOptions);
    4652          97 :             if (dfMaxZError == 0.0 && dfMaxZErrorOverview == 0.0)
    4653             :             {
    4654          83 :                 AppendMetadataItem(&psRoot, &psTail,
    4655             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4656             :                                    nullptr, "IMAGE_STRUCTURE");
    4657             :             }
    4658             :             else
    4659             :             {
    4660          14 :                 AppendMetadataItem(&psRoot, &psTail, "MAX_Z_ERROR",
    4661             :                                    CSLFetchNameValueDef(papszCreationOptions,
    4662             :                                                         "MAX_Z_ERROR", ""),
    4663             :                                    0, nullptr, "IMAGE_STRUCTURE");
    4664          14 :                 if (dfMaxZError != dfMaxZErrorOverview)
    4665             :                 {
    4666           3 :                     AppendMetadataItem(
    4667             :                         &psRoot, &psTail, "MAX_Z_ERROR_OVERVIEW",
    4668             :                         CSLFetchNameValueDef(papszCreationOptions,
    4669             :                                              "MAX_Z_ERROR_OVERVIEW", ""),
    4670             :                         0, nullptr, "IMAGE_STRUCTURE");
    4671             :                 }
    4672          97 :             }
    4673             :         }
    4674             : #if HAVE_JXL
    4675        7763 :         else if (pszCompress && EQUAL(pszCompress, "JXL"))
    4676             :         {
    4677         101 :             float fDistance = 0.0f;
    4678         101 :             if (GTiffGetJXLLossless(papszCreationOptions))
    4679             :             {
    4680          82 :                 AppendMetadataItem(&psRoot, &psTail,
    4681             :                                    "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
    4682             :                                    nullptr, "IMAGE_STRUCTURE");
    4683             :             }
    4684             :             else
    4685             :             {
    4686          19 :                 fDistance = GTiffGetJXLDistance(papszCreationOptions);
    4687          19 :                 AppendMetadataItem(
    4688             :                     &psRoot, &psTail, "JXL_DISTANCE",
    4689             :                     CPLSPrintf("%f", static_cast<double>(fDistance)), 0,
    4690             :                     nullptr, "IMAGE_STRUCTURE");
    4691             :             }
    4692             :             const float fAlphaDistance =
    4693         101 :                 GTiffGetJXLAlphaDistance(papszCreationOptions);
    4694         101 :             if (fAlphaDistance >= 0.0f && fAlphaDistance != fDistance)
    4695             :             {
    4696           2 :                 AppendMetadataItem(
    4697             :                     &psRoot, &psTail, "JXL_ALPHA_DISTANCE",
    4698             :                     CPLSPrintf("%f", static_cast<double>(fAlphaDistance)), 0,
    4699             :                     nullptr, "IMAGE_STRUCTURE");
    4700             :             }
    4701         101 :             AppendMetadataItem(
    4702             :                 &psRoot, &psTail, "JXL_EFFORT",
    4703             :                 CPLSPrintf("%d", GTiffGetJXLEffort(papszCreationOptions)), 0,
    4704             :                 nullptr, "IMAGE_STRUCTURE");
    4705             :         }
    4706             : #endif
    4707             :     }
    4708             : 
    4709        7896 :     if (!CPLTestBool(CPLGetConfigOption("GTIFF_WRITE_RAT_TO_PAM", "NO")))
    4710             :     {
    4711      315644 :         for (int nBand = 1; nBand <= poSrcDS->GetRasterCount(); ++nBand)
    4712             :         {
    4713      307750 :             GDALRasterBand *poBand = poSrcDS->GetRasterBand(nBand);
    4714      307750 :             const auto poRAT = poBand->GetDefaultRAT();
    4715      307750 :             if (poRAT)
    4716             :             {
    4717          21 :                 auto psSerializedRAT = poRAT->Serialize();
    4718          21 :                 if (psSerializedRAT)
    4719             :                 {
    4720          20 :                     AppendMetadataItem(
    4721             :                         &psRoot, &psTail, DEFAULT_RASTER_ATTRIBUTE_TABLE,
    4722             :                         nullptr, psSerializedRAT, nBand, RAT_ROLE, nullptr);
    4723             :                 }
    4724             :             }
    4725             :         }
    4726             :     }
    4727             : 
    4728             :     /* -------------------------------------------------------------------- */
    4729             :     /*      Write out the generic XML metadata if there is any.             */
    4730             :     /* -------------------------------------------------------------------- */
    4731        7896 :     if (psRoot != nullptr)
    4732             :     {
    4733         729 :         bool bRet = true;
    4734             : 
    4735         729 :         if (eProfile == GTiffProfile::GDALGEOTIFF)
    4736             :         {
    4737         712 :             char *pszXML_MD = CPLSerializeXMLTree(psRoot);
    4738         712 :             TIFFSetField(l_hTIFF, TIFFTAG_GDAL_METADATA, pszXML_MD);
    4739         712 :             CPLFree(pszXML_MD);
    4740             :         }
    4741             :         else
    4742             :         {
    4743          17 :             if (bSrcIsGeoTIFF)
    4744          11 :                 cpl::down_cast<GTiffDataset *>(poSrcDS)->PushMetadataToPam();
    4745             :             else
    4746           6 :                 bRet = false;
    4747             :         }
    4748             : 
    4749         729 :         CPLDestroyXMLNode(psRoot);
    4750             : 
    4751         729 :         return bRet;
    4752             :     }
    4753             : 
    4754             :     // If we have no more metadata but it existed before,
    4755             :     // remove the GDAL_METADATA tag.
    4756        7167 :     if (eProfile == GTiffProfile::GDALGEOTIFF)
    4757             :     {
    4758        7143 :         char *pszText = nullptr;
    4759        7143 :         if (TIFFGetField(l_hTIFF, TIFFTAG_GDAL_METADATA, &pszText))
    4760             :         {
    4761           7 :             TIFFUnsetField(l_hTIFF, TIFFTAG_GDAL_METADATA);
    4762             :         }
    4763             :     }
    4764             : 
    4765        7167 :     return true;
    4766             : }
    4767             : 
    4768             : /************************************************************************/
    4769             : /*                         PushMetadataToPam()                          */
    4770             : /*                                                                      */
    4771             : /*      When producing a strict profile TIFF or if our aggregate        */
    4772             : /*      metadata is too big for a single tiff tag we may end up         */
    4773             : /*      needing to write it via the PAM mechanisms.  This method        */
    4774             : /*      copies all the appropriate metadata into the PAM level          */
    4775             : /*      metadata object but with special care to avoid copying          */
    4776             : /*      metadata handled in other ways in TIFF format.                  */
    4777             : /************************************************************************/
    4778             : 
    4779          17 : void GTiffDataset::PushMetadataToPam()
    4780             : 
    4781             : {
    4782          17 :     if (GetPamFlags() & GPF_DISABLED)
    4783           0 :         return;
    4784             : 
    4785          17 :     const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
    4786          17 :         GDALDataset::ToHandle(this), m_nPhotometric, m_papszCreationOptions);
    4787             : 
    4788          55 :     for (int nBand = 0; nBand <= GetRasterCount(); ++nBand)
    4789             :     {
    4790          38 :         GDALMultiDomainMetadata *poSrcMDMD = nullptr;
    4791          38 :         GTiffRasterBand *poBand = nullptr;
    4792             : 
    4793          38 :         if (nBand == 0)
    4794             :         {
    4795          17 :             poSrcMDMD = &(this->m_oGTiffMDMD);
    4796             :         }
    4797             :         else
    4798             :         {
    4799          21 :             poBand = cpl::down_cast<GTiffRasterBand *>(GetRasterBand(nBand));
    4800          21 :             poSrcMDMD = &(poBand->m_oGTiffMDMD);
    4801             :         }
    4802             : 
    4803             :         /* --------------------------------------------------------------------
    4804             :          */
    4805             :         /*      Loop over the available domains. */
    4806             :         /* --------------------------------------------------------------------
    4807             :          */
    4808          38 :         CSLConstList papszDomainList = poSrcMDMD->GetDomainList();
    4809          74 :         for (int iDomain = 0; papszDomainList && papszDomainList[iDomain];
    4810             :              ++iDomain)
    4811             :         {
    4812          36 :             char **papszMD = poSrcMDMD->GetMetadata(papszDomainList[iDomain]);
    4813             : 
    4814          36 :             if (EQUAL(papszDomainList[iDomain], MD_DOMAIN_RPC) ||
    4815          36 :                 EQUAL(papszDomainList[iDomain], MD_DOMAIN_IMD) ||
    4816          36 :                 EQUAL(papszDomainList[iDomain], "_temporary_") ||
    4817          36 :                 EQUAL(papszDomainList[iDomain], "IMAGE_STRUCTURE") ||
    4818          19 :                 EQUAL(papszDomainList[iDomain], "COLOR_PROFILE"))
    4819          17 :                 continue;
    4820             : 
    4821          19 :             papszMD = CSLDuplicate(papszMD);
    4822             : 
    4823          69 :             for (int i = CSLCount(papszMD) - 1; i >= 0; --i)
    4824             :             {
    4825          50 :                 if (STARTS_WITH_CI(papszMD[i], "TIFFTAG_") ||
    4826          50 :                     EQUALN(papszMD[i], GDALMD_AREA_OR_POINT,
    4827             :                            strlen(GDALMD_AREA_OR_POINT)))
    4828           4 :                     papszMD = CSLRemoveStrings(papszMD, i, 1, nullptr);
    4829             :             }
    4830             : 
    4831          19 :             if (nBand == 0)
    4832          10 :                 GDALPamDataset::SetMetadata(papszMD, papszDomainList[iDomain]);
    4833             :             else
    4834           9 :                 poBand->GDALPamRasterBand::SetMetadata(
    4835           9 :                     papszMD, papszDomainList[iDomain]);
    4836             : 
    4837          19 :             CSLDestroy(papszMD);
    4838             :         }
    4839             : 
    4840             :         /* --------------------------------------------------------------------
    4841             :          */
    4842             :         /*      Handle some "special domain" stuff. */
    4843             :         /* --------------------------------------------------------------------
    4844             :          */
    4845          38 :         if (poBand != nullptr)
    4846             :         {
    4847          21 :             poBand->GDALPamRasterBand::SetOffset(poBand->GetOffset());
    4848          21 :             poBand->GDALPamRasterBand::SetScale(poBand->GetScale());
    4849          21 :             poBand->GDALPamRasterBand::SetUnitType(poBand->GetUnitType());
    4850          21 :             poBand->GDALPamRasterBand::SetDescription(poBand->GetDescription());
    4851          21 :             if (!bStandardColorInterp)
    4852             :             {
    4853           3 :                 poBand->GDALPamRasterBand::SetColorInterpretation(
    4854           3 :                     poBand->GetColorInterpretation());
    4855             :             }
    4856             :         }
    4857             :     }
    4858          17 :     MarkPamDirty();
    4859             : }
    4860             : 
    4861             : /************************************************************************/
    4862             : /*                         WriteNoDataValue()                           */
    4863             : /************************************************************************/
    4864             : 
    4865         489 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, double dfNoData)
    4866             : 
    4867             : {
    4868         978 :     CPLString osVal(GTiffFormatGDALNoDataTagValue(dfNoData));
    4869         489 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA, osVal.c_str());
    4870         489 : }
    4871             : 
    4872           3 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, int64_t nNoData)
    4873             : 
    4874             : {
    4875           3 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA,
    4876             :                  CPLSPrintf(CPL_FRMT_GIB, static_cast<GIntBig>(nNoData)));
    4877           3 : }
    4878             : 
    4879           3 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, uint64_t nNoData)
    4880             : 
    4881             : {
    4882           3 :     TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA,
    4883             :                  CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoData)));
    4884           3 : }
    4885             : 
    4886             : /************************************************************************/
    4887             : /*                         UnsetNoDataValue()                           */
    4888             : /************************************************************************/
    4889             : 
    4890          14 : void GTiffDataset::UnsetNoDataValue(TIFF *l_hTIFF)
    4891             : 
    4892             : {
    4893          14 :     TIFFUnsetField(l_hTIFF, TIFFTAG_GDAL_NODATA);
    4894          14 : }
    4895             : 
    4896             : /************************************************************************/
    4897             : /*                             SaveICCProfile()                         */
    4898             : /*                                                                      */
    4899             : /*      Save ICC Profile or colorimetric data into file                 */
    4900             : /* pDS:                                                                 */
    4901             : /*      Dataset that contains the metadata with the ICC or colorimetric */
    4902             : /*      data. If this argument is specified, all other arguments are    */
    4903             : /*      ignored. Set them to NULL or 0.                                 */
    4904             : /* hTIFF:                                                               */
    4905             : /*      Pointer to TIFF handle. Only needed if pDS is NULL or           */
    4906             : /*      pDS->m_hTIFF is NULL.                                             */
    4907             : /* papszParamList:                                                       */
    4908             : /*      Options containing the ICC profile or colorimetric metadata.    */
    4909             : /*      Ignored if pDS is not NULL.                                     */
    4910             : /* nBitsPerSample:                                                      */
    4911             : /*      Bits per sample. Ignored if pDS is not NULL.                    */
    4912             : /************************************************************************/
    4913             : 
    4914        9671 : void GTiffDataset::SaveICCProfile(GTiffDataset *pDS, TIFF *l_hTIFF,
    4915             :                                   char **papszParamList,
    4916             :                                   uint32_t l_nBitsPerSample)
    4917             : {
    4918        9671 :     if ((pDS != nullptr) && (pDS->eAccess != GA_Update))
    4919           0 :         return;
    4920             : 
    4921        9671 :     if (l_hTIFF == nullptr)
    4922             :     {
    4923           2 :         if (pDS == nullptr)
    4924           0 :             return;
    4925             : 
    4926           2 :         l_hTIFF = pDS->m_hTIFF;
    4927           2 :         if (l_hTIFF == nullptr)
    4928           0 :             return;
    4929             :     }
    4930             : 
    4931        9671 :     if ((papszParamList == nullptr) && (pDS == nullptr))
    4932        4768 :         return;
    4933             : 
    4934             :     const char *pszICCProfile =
    4935             :         (pDS != nullptr)
    4936        4903 :             ? pDS->GetMetadataItem("SOURCE_ICC_PROFILE", "COLOR_PROFILE")
    4937        4901 :             : CSLFetchNameValue(papszParamList, "SOURCE_ICC_PROFILE");
    4938        4903 :     if (pszICCProfile != nullptr)
    4939             :     {
    4940           8 :         char *pEmbedBuffer = CPLStrdup(pszICCProfile);
    4941             :         int32_t nEmbedLen =
    4942           8 :             CPLBase64DecodeInPlace(reinterpret_cast<GByte *>(pEmbedBuffer));
    4943             : 
    4944           8 :         TIFFSetField(l_hTIFF, TIFFTAG_ICCPROFILE, nEmbedLen, pEmbedBuffer);
    4945             : 
    4946           8 :         CPLFree(pEmbedBuffer);
    4947             :     }
    4948             :     else
    4949             :     {
    4950             :         // Output colorimetric data.
    4951        4895 :         float pCHR[6] = {};     // Primaries.
    4952        4895 :         uint16_t pTXR[6] = {};  // Transfer range.
    4953        4895 :         const char *pszCHRNames[] = {"SOURCE_PRIMARIES_RED",
    4954             :                                      "SOURCE_PRIMARIES_GREEN",
    4955             :                                      "SOURCE_PRIMARIES_BLUE"};
    4956        4895 :         const char *pszTXRNames[] = {"TIFFTAG_TRANSFERRANGE_BLACK",
    4957             :                                      "TIFFTAG_TRANSFERRANGE_WHITE"};
    4958             : 
    4959             :         // Output chromacities.
    4960        4895 :         bool bOutputCHR = true;
    4961        4910 :         for (int i = 0; i < 3 && bOutputCHR; ++i)
    4962             :         {
    4963             :             const char *pszColorProfile =
    4964             :                 (pDS != nullptr)
    4965        4905 :                     ? pDS->GetMetadataItem(pszCHRNames[i], "COLOR_PROFILE")
    4966        4902 :                     : CSLFetchNameValue(papszParamList, pszCHRNames[i]);
    4967        4905 :             if (pszColorProfile == nullptr)
    4968             :             {
    4969        4890 :                 bOutputCHR = false;
    4970        4890 :                 break;
    4971             :             }
    4972             : 
    4973             :             const CPLStringList aosTokens(CSLTokenizeString2(
    4974             :                 pszColorProfile, ",",
    4975             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    4976          15 :                     CSLT_STRIPENDSPACES));
    4977             : 
    4978          15 :             if (aosTokens.size() != 3)
    4979             :             {
    4980           0 :                 bOutputCHR = false;
    4981           0 :                 break;
    4982             :             }
    4983             : 
    4984          60 :             for (int j = 0; j < 3; ++j)
    4985             :             {
    4986          45 :                 float v = static_cast<float>(CPLAtof(aosTokens[j]));
    4987             : 
    4988          45 :                 if (j == 2)
    4989             :                 {
    4990             :                     // Last term of xyY color must be 1.0.
    4991          15 :                     if (v != 1.0f)
    4992             :                     {
    4993           0 :                         bOutputCHR = false;
    4994           0 :                         break;
    4995             :                     }
    4996             :                 }
    4997             :                 else
    4998             :                 {
    4999          30 :                     pCHR[i * 2 + j] = v;
    5000             :                 }
    5001             :             }
    5002             :         }
    5003             : 
    5004        4895 :         if (bOutputCHR)
    5005             :         {
    5006           5 :             TIFFSetField(l_hTIFF, TIFFTAG_PRIMARYCHROMATICITIES, pCHR);
    5007             :         }
    5008             : 
    5009             :         // Output whitepoint.
    5010             :         const char *pszSourceWhitePoint =
    5011             :             (pDS != nullptr)
    5012        4895 :                 ? pDS->GetMetadataItem("SOURCE_WHITEPOINT", "COLOR_PROFILE")
    5013        4894 :                 : CSLFetchNameValue(papszParamList, "SOURCE_WHITEPOINT");
    5014        4895 :         if (pszSourceWhitePoint != nullptr)
    5015             :         {
    5016             :             const CPLStringList aosTokens(CSLTokenizeString2(
    5017             :                 pszSourceWhitePoint, ",",
    5018             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5019          10 :                     CSLT_STRIPENDSPACES));
    5020             : 
    5021           5 :             bool bOutputWhitepoint = true;
    5022           5 :             float pWP[2] = {0.0f, 0.0f};  // Whitepoint
    5023           5 :             if (aosTokens.size() != 3)
    5024             :             {
    5025           0 :                 bOutputWhitepoint = false;
    5026             :             }
    5027             :             else
    5028             :             {
    5029          20 :                 for (int j = 0; j < 3; ++j)
    5030             :                 {
    5031          15 :                     const float v = static_cast<float>(CPLAtof(aosTokens[j]));
    5032             : 
    5033          15 :                     if (j == 2)
    5034             :                     {
    5035             :                         // Last term of xyY color must be 1.0.
    5036           5 :                         if (v != 1.0f)
    5037             :                         {
    5038           0 :                             bOutputWhitepoint = false;
    5039           0 :                             break;
    5040             :                         }
    5041             :                     }
    5042             :                     else
    5043             :                     {
    5044          10 :                         pWP[j] = v;
    5045             :                     }
    5046             :                 }
    5047             :             }
    5048             : 
    5049           5 :             if (bOutputWhitepoint)
    5050             :             {
    5051           5 :                 TIFFSetField(l_hTIFF, TIFFTAG_WHITEPOINT, pWP);
    5052             :             }
    5053             :         }
    5054             : 
    5055             :         // Set transfer function metadata.
    5056             :         char const *pszTFRed =
    5057             :             (pDS != nullptr)
    5058        4895 :                 ? pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_RED",
    5059             :                                        "COLOR_PROFILE")
    5060        4894 :                 : CSLFetchNameValue(papszParamList,
    5061        4895 :                                     "TIFFTAG_TRANSFERFUNCTION_RED");
    5062             : 
    5063             :         char const *pszTFGreen =
    5064             :             (pDS != nullptr)
    5065        4895 :                 ? pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_GREEN",
    5066             :                                        "COLOR_PROFILE")
    5067        4894 :                 : CSLFetchNameValue(papszParamList,
    5068        4895 :                                     "TIFFTAG_TRANSFERFUNCTION_GREEN");
    5069             : 
    5070             :         char const *pszTFBlue =
    5071             :             (pDS != nullptr)
    5072        4895 :                 ? pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_BLUE",
    5073             :                                        "COLOR_PROFILE")
    5074        4894 :                 : CSLFetchNameValue(papszParamList,
    5075        4895 :                                     "TIFFTAG_TRANSFERFUNCTION_BLUE");
    5076             : 
    5077        4895 :         if ((pszTFRed != nullptr) && (pszTFGreen != nullptr) &&
    5078             :             (pszTFBlue != nullptr))
    5079             :         {
    5080             :             // Get length of table.
    5081           4 :             const int nTransferFunctionLength =
    5082           4 :                 1 << ((pDS != nullptr) ? pDS->m_nBitsPerSample
    5083             :                                        : l_nBitsPerSample);
    5084             : 
    5085             :             const CPLStringList aosTokensRed(CSLTokenizeString2(
    5086             :                 pszTFRed, ",",
    5087             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5088           8 :                     CSLT_STRIPENDSPACES));
    5089             :             const CPLStringList aosTokensGreen(CSLTokenizeString2(
    5090             :                 pszTFGreen, ",",
    5091             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5092           8 :                     CSLT_STRIPENDSPACES));
    5093             :             const CPLStringList aosTokensBlue(CSLTokenizeString2(
    5094             :                 pszTFBlue, ",",
    5095             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5096           8 :                     CSLT_STRIPENDSPACES));
    5097             : 
    5098           4 :             if ((aosTokensRed.size() == nTransferFunctionLength) &&
    5099           8 :                 (aosTokensGreen.size() == nTransferFunctionLength) &&
    5100           4 :                 (aosTokensBlue.size() == nTransferFunctionLength))
    5101             :             {
    5102             :                 std::vector<uint16_t> anTransferFuncRed(
    5103           8 :                     nTransferFunctionLength);
    5104             :                 std::vector<uint16_t> anTransferFuncGreen(
    5105           8 :                     nTransferFunctionLength);
    5106             :                 std::vector<uint16_t> anTransferFuncBlue(
    5107           8 :                     nTransferFunctionLength);
    5108             : 
    5109             :                 // Convert our table in string format into int16_t format.
    5110        1028 :                 for (int i = 0; i < nTransferFunctionLength; ++i)
    5111             :                 {
    5112        2048 :                     anTransferFuncRed[i] =
    5113        1024 :                         static_cast<uint16_t>(atoi(aosTokensRed[i]));
    5114        2048 :                     anTransferFuncGreen[i] =
    5115        1024 :                         static_cast<uint16_t>(atoi(aosTokensGreen[i]));
    5116        1024 :                     anTransferFuncBlue[i] =
    5117        1024 :                         static_cast<uint16_t>(atoi(aosTokensBlue[i]));
    5118             :                 }
    5119             : 
    5120           4 :                 TIFFSetField(
    5121             :                     l_hTIFF, TIFFTAG_TRANSFERFUNCTION, anTransferFuncRed.data(),
    5122             :                     anTransferFuncGreen.data(), anTransferFuncBlue.data());
    5123             :             }
    5124             :         }
    5125             : 
    5126             :         // Output transfer range.
    5127        4895 :         bool bOutputTransferRange = true;
    5128        4895 :         for (int i = 0; (i < 2) && bOutputTransferRange; ++i)
    5129             :         {
    5130             :             const char *pszTXRVal =
    5131             :                 (pDS != nullptr)
    5132        4895 :                     ? pDS->GetMetadataItem(pszTXRNames[i], "COLOR_PROFILE")
    5133        4894 :                     : CSLFetchNameValue(papszParamList, pszTXRNames[i]);
    5134        4895 :             if (pszTXRVal == nullptr)
    5135             :             {
    5136        4895 :                 bOutputTransferRange = false;
    5137        4895 :                 break;
    5138             :             }
    5139             : 
    5140             :             const CPLStringList aosTokens(CSLTokenizeString2(
    5141             :                 pszTXRVal, ",",
    5142             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    5143           0 :                     CSLT_STRIPENDSPACES));
    5144             : 
    5145           0 :             if (aosTokens.size() != 3)
    5146             :             {
    5147           0 :                 bOutputTransferRange = false;
    5148           0 :                 break;
    5149             :             }
    5150             : 
    5151           0 :             for (int j = 0; j < 3; ++j)
    5152             :             {
    5153           0 :                 pTXR[i + j * 2] = static_cast<uint16_t>(atoi(aosTokens[j]));
    5154             :             }
    5155             :         }
    5156             : 
    5157        4895 :         if (bOutputTransferRange)
    5158             :         {
    5159           0 :             const int TIFFTAG_TRANSFERRANGE = 0x0156;
    5160           0 :             TIFFSetField(l_hTIFF, TIFFTAG_TRANSFERRANGE, pTXR);
    5161             :         }
    5162             :     }
    5163             : }
    5164             : 
    5165       17437 : static signed char GTiffGetLZMAPreset(char **papszOptions)
    5166             : {
    5167       17437 :     int nLZMAPreset = -1;
    5168       17437 :     const char *pszValue = CSLFetchNameValue(papszOptions, "LZMA_PRESET");
    5169       17437 :     if (pszValue != nullptr)
    5170             :     {
    5171          20 :         nLZMAPreset = atoi(pszValue);
    5172          20 :         if (!(nLZMAPreset >= 0 && nLZMAPreset <= 9))
    5173             :         {
    5174           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5175             :                      "LZMA_PRESET=%s value not recognised, ignoring.",
    5176             :                      pszValue);
    5177           0 :             nLZMAPreset = -1;
    5178             :         }
    5179             :     }
    5180       17437 :     return static_cast<signed char>(nLZMAPreset);
    5181             : }
    5182             : 
    5183       17437 : static signed char GTiffGetZSTDPreset(char **papszOptions)
    5184             : {
    5185       17437 :     int nZSTDLevel = -1;
    5186       17437 :     const char *pszValue = CSLFetchNameValue(papszOptions, "ZSTD_LEVEL");
    5187       17437 :     if (pszValue != nullptr)
    5188             :     {
    5189          24 :         nZSTDLevel = atoi(pszValue);
    5190          24 :         if (!(nZSTDLevel >= 1 && nZSTDLevel <= 22))
    5191             :         {
    5192           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5193             :                      "ZSTD_LEVEL=%s value not recognised, ignoring.", pszValue);
    5194           0 :             nZSTDLevel = -1;
    5195             :         }
    5196             :     }
    5197       17437 :     return static_cast<signed char>(nZSTDLevel);
    5198             : }
    5199             : 
    5200       17437 : static signed char GTiffGetZLevel(char **papszOptions)
    5201             : {
    5202       17437 :     int nZLevel = -1;
    5203       17437 :     const char *pszValue = CSLFetchNameValue(papszOptions, "ZLEVEL");
    5204       17437 :     if (pszValue != nullptr)
    5205             :     {
    5206          44 :         nZLevel = atoi(pszValue);
    5207             : #ifdef TIFFTAG_DEFLATE_SUBCODEC
    5208          44 :         constexpr int nMaxLevel = 12;
    5209             : #ifndef LIBDEFLATE_SUPPORT
    5210             :         if (nZLevel > 9 && nZLevel <= nMaxLevel)
    5211             :         {
    5212             :             CPLDebug("GTiff",
    5213             :                      "ZLEVEL=%d not supported in a non-libdeflate enabled "
    5214             :                      "libtiff build. Capping to 9",
    5215             :                      nZLevel);
    5216             :             nZLevel = 9;
    5217             :         }
    5218             : #endif
    5219             : #else
    5220             :         constexpr int nMaxLevel = 9;
    5221             : #endif
    5222          44 :         if (nZLevel < 1 || nZLevel > nMaxLevel)
    5223             :         {
    5224           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5225             :                      "ZLEVEL=%s value not recognised, ignoring.", pszValue);
    5226           0 :             nZLevel = -1;
    5227             :         }
    5228             :     }
    5229       17437 :     return static_cast<signed char>(nZLevel);
    5230             : }
    5231             : 
    5232       17437 : static signed char GTiffGetJpegQuality(char **papszOptions)
    5233             : {
    5234       17437 :     int nJpegQuality = -1;
    5235       17437 :     const char *pszValue = CSLFetchNameValue(papszOptions, "JPEG_QUALITY");
    5236       17437 :     if (pszValue != nullptr)
    5237             :     {
    5238        1939 :         nJpegQuality = atoi(pszValue);
    5239        1939 :         if (nJpegQuality < 1 || nJpegQuality > 100)
    5240             :         {
    5241           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
    5242             :                      "JPEG_QUALITY=%s value not recognised, ignoring.",
    5243             :                      pszValue);
    5244           0 :             nJpegQuality = -1;
    5245             :         }
    5246             :     }
    5247       17437 :     return static_cast<signed char>(nJpegQuality);
    5248             : }
    5249             : 
    5250       17437 : static signed char GTiffGetJpegTablesMode(char **papszOptions)
    5251             : {
    5252       17437 :     return static_cast<signed char>(atoi(
    5253             :         CSLFetchNameValueDef(papszOptions, "JPEGTABLESMODE",
    5254       17437 :                              CPLSPrintf("%d", knGTIFFJpegTablesModeDefault))));
    5255             : }
    5256             : 
    5257             : /************************************************************************/
    5258             : /*                        GetDiscardLsbOption()                         */
    5259             : /************************************************************************/
    5260             : 
    5261        7707 : static GTiffDataset::MaskOffset *GetDiscardLsbOption(TIFF *hTIFF,
    5262             :                                                      char **papszOptions)
    5263             : {
    5264        7707 :     const char *pszBits = CSLFetchNameValue(papszOptions, "DISCARD_LSB");
    5265        7707 :     if (pszBits == nullptr)
    5266        7585 :         return nullptr;
    5267             : 
    5268         122 :     uint16_t nPhotometric = 0;
    5269         122 :     TIFFGetFieldDefaulted(hTIFF, TIFFTAG_PHOTOMETRIC, &nPhotometric);
    5270             : 
    5271         122 :     uint16_t nBitsPerSample = 0;
    5272         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &nBitsPerSample))
    5273           0 :         nBitsPerSample = 1;
    5274             : 
    5275         122 :     uint16_t nSamplesPerPixel = 0;
    5276         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSamplesPerPixel))
    5277           0 :         nSamplesPerPixel = 1;
    5278             : 
    5279         122 :     uint16_t nSampleFormat = 0;
    5280         122 :     if (!TIFFGetField(hTIFF, TIFFTAG_SAMPLEFORMAT, &nSampleFormat))
    5281           0 :         nSampleFormat = SAMPLEFORMAT_UINT;
    5282             : 
    5283         122 :     if (nPhotometric == PHOTOMETRIC_PALETTE)
    5284             :     {
    5285           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    5286             :                  "DISCARD_LSB ignored on a paletted image");
    5287           1 :         return nullptr;
    5288             :     }
    5289         121 :     if (!(nBitsPerSample == 8 || nBitsPerSample == 16 || nBitsPerSample == 32 ||
    5290          13 :           nBitsPerSample == 64))
    5291             :     {
    5292           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    5293             :                  "DISCARD_LSB ignored on non 8, 16, 32 or 64 bits images");
    5294           1 :         return nullptr;
    5295             :     }
    5296             : 
    5297         240 :     const CPLStringList aosTokens(CSLTokenizeString2(pszBits, ",", 0));
    5298         120 :     const int nTokens = aosTokens.size();
    5299         120 :     GTiffDataset::MaskOffset *panMaskOffsetLsb = nullptr;
    5300         120 :     if (nTokens == 1 || nTokens == nSamplesPerPixel)
    5301             :     {
    5302             :         panMaskOffsetLsb = static_cast<GTiffDataset::MaskOffset *>(
    5303         119 :             CPLCalloc(nSamplesPerPixel, sizeof(GTiffDataset::MaskOffset)));
    5304         374 :         for (int i = 0; i < nSamplesPerPixel; ++i)
    5305             :         {
    5306         255 :             const int nBits = atoi(aosTokens[nTokens == 1 ? 0 : i]);
    5307         510 :             const int nMaxBits = (nSampleFormat == SAMPLEFORMAT_IEEEFP)
    5308         510 :                                      ? ((nBitsPerSample == 16)   ? 11 - 1
    5309          78 :                                         : (nBitsPerSample == 32) ? 23 - 1
    5310          26 :                                         : (nBitsPerSample == 64) ? 53 - 1
    5311             :                                                                  : 0)
    5312         203 :                                  : nSampleFormat == SAMPLEFORMAT_INT
    5313         203 :                                      ? nBitsPerSample - 2
    5314         119 :                                      : nBitsPerSample - 1;
    5315             : 
    5316         255 :             if (nBits < 0 || nBits > nMaxBits)
    5317             :             {
    5318           0 :                 CPLError(
    5319             :                     CE_Warning, CPLE_AppDefined,
    5320             :                     "DISCARD_LSB ignored: values should be in [0,%d] range",
    5321             :                     nMaxBits);
    5322           0 :                 VSIFree(panMaskOffsetLsb);
    5323           0 :                 return nullptr;
    5324             :             }
    5325         255 :             panMaskOffsetLsb[i].nMask =
    5326         255 :                 ~((static_cast<uint64_t>(1) << nBits) - 1);
    5327         255 :             if (nBits > 1)
    5328             :             {
    5329         249 :                 panMaskOffsetLsb[i].nRoundUpBitTest = static_cast<uint64_t>(1)
    5330         249 :                                                       << (nBits - 1);
    5331             :             }
    5332         119 :         }
    5333             :     }
    5334             :     else
    5335             :     {
    5336           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    5337             :                  "DISCARD_LSB ignored: wrong number of components");
    5338             :     }
    5339         120 :     return panMaskOffsetLsb;
    5340             : }
    5341             : 
    5342        7707 : void GTiffDataset::GetDiscardLsbOption(char **papszOptions)
    5343             : {
    5344        7707 :     m_panMaskOffsetLsb = ::GetDiscardLsbOption(m_hTIFF, papszOptions);
    5345        7707 : }
    5346             : 
    5347             : /************************************************************************/
    5348             : /*                             GetProfile()                             */
    5349             : /************************************************************************/
    5350             : 
    5351       17488 : static GTiffProfile GetProfile(const char *pszProfile)
    5352             : {
    5353       17488 :     GTiffProfile eProfile = GTiffProfile::GDALGEOTIFF;
    5354       17488 :     if (pszProfile != nullptr)
    5355             :     {
    5356          70 :         if (EQUAL(pszProfile, szPROFILE_BASELINE))
    5357          50 :             eProfile = GTiffProfile::BASELINE;
    5358          20 :         else if (EQUAL(pszProfile, szPROFILE_GeoTIFF))
    5359          18 :             eProfile = GTiffProfile::GEOTIFF;
    5360           2 :         else if (!EQUAL(pszProfile, szPROFILE_GDALGeoTIFF))
    5361             :         {
    5362           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    5363             :                      "Unsupported value for PROFILE: %s", pszProfile);
    5364             :         }
    5365             :     }
    5366       17488 :     return eProfile;
    5367             : }
    5368             : 
    5369             : /************************************************************************/
    5370             : /*                            GTiffCreate()                             */
    5371             : /*                                                                      */
    5372             : /*      Shared functionality between GTiffDataset::Create() and         */
    5373             : /*      GTiffCreateCopy() for creating TIFF file based on a set of      */
    5374             : /*      options and a configuration.                                    */
    5375             : /************************************************************************/
    5376             : 
    5377        9750 : TIFF *GTiffDataset::CreateLL(const char *pszFilename, int nXSize, int nYSize,
    5378             :                              int l_nBands, GDALDataType eType,
    5379             :                              double dfExtraSpaceForOverviews,
    5380             :                              int nColorTableMultiplier, char **papszParamList,
    5381             :                              VSILFILE **pfpL, CPLString &l_osTmpFilename,
    5382             :                              bool bCreateCopy, bool &bTileInterleavingOut)
    5383             : 
    5384             : {
    5385        9750 :     bTileInterleavingOut = false;
    5386             : 
    5387        9750 :     GTiffOneTimeInit();
    5388             : 
    5389             :     /* -------------------------------------------------------------------- */
    5390             :     /*      Blow on a few errors.                                           */
    5391             :     /* -------------------------------------------------------------------- */
    5392        9750 :     if (nXSize < 1 || nYSize < 1 || l_nBands < 1)
    5393             :     {
    5394           2 :         ReportError(
    5395             :             pszFilename, CE_Failure, CPLE_AppDefined,
    5396             :             "Attempt to create %dx%dx%d TIFF file, but width, height and bands"
    5397             :             "must be positive.",
    5398             :             nXSize, nYSize, l_nBands);
    5399             : 
    5400           2 :         return nullptr;
    5401             :     }
    5402             : 
    5403        9748 :     if (l_nBands > 65535)
    5404             :     {
    5405           1 :         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5406             :                     "Attempt to create %dx%dx%d TIFF file, but bands "
    5407             :                     "must be lesser or equal to 65535.",
    5408             :                     nXSize, nYSize, l_nBands);
    5409             : 
    5410           1 :         return nullptr;
    5411             :     }
    5412             : 
    5413             :     /* -------------------------------------------------------------------- */
    5414             :     /*      Setup values based on options.                                  */
    5415             :     /* -------------------------------------------------------------------- */
    5416             :     const GTiffProfile eProfile =
    5417        9747 :         GetProfile(CSLFetchNameValue(papszParamList, "PROFILE"));
    5418             : 
    5419        9747 :     const bool bTiled = CPLFetchBool(papszParamList, "TILED", false);
    5420             : 
    5421        9747 :     int l_nBlockXSize = 0;
    5422        9747 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKXSIZE"))
    5423             :     {
    5424         458 :         l_nBlockXSize = atoi(pszValue);
    5425         458 :         if (l_nBlockXSize < 0)
    5426             :         {
    5427           0 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5428             :                         "Invalid value for BLOCKXSIZE");
    5429           0 :             return nullptr;
    5430             :         }
    5431         458 :         if (!bTiled)
    5432             :         {
    5433          10 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    5434             :                         "BLOCKXSIZE can only be used with TILED=YES");
    5435             :         }
    5436         448 :         else if (l_nBlockXSize % 16 != 0)
    5437             :         {
    5438           1 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5439             :                         "BLOCKXSIZE must be a multiple of 16");
    5440           1 :             return nullptr;
    5441             :         }
    5442             :     }
    5443             : 
    5444        9746 :     int l_nBlockYSize = 0;
    5445        9746 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKYSIZE"))
    5446             :     {
    5447        2569 :         l_nBlockYSize = atoi(pszValue);
    5448        2569 :         if (l_nBlockYSize < 0)
    5449             :         {
    5450           0 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5451             :                         "Invalid value for BLOCKYSIZE");
    5452           0 :             return nullptr;
    5453             :         }
    5454        2569 :         if (bTiled && (l_nBlockYSize % 16 != 0))
    5455             :         {
    5456           2 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5457             :                         "BLOCKYSIZE must be a multiple of 16");
    5458           2 :             return nullptr;
    5459             :         }
    5460             :     }
    5461             : 
    5462        9744 :     if (bTiled)
    5463             :     {
    5464         793 :         if (l_nBlockXSize == 0)
    5465         347 :             l_nBlockXSize = 256;
    5466             : 
    5467         793 :         if (l_nBlockYSize == 0)
    5468         347 :             l_nBlockYSize = 256;
    5469             :     }
    5470             : 
    5471        9744 :     int nPlanar = 0;
    5472             : 
    5473             :     // Hidden @TILE_INTERLEAVE=YES parameter used by the COG driver
    5474        9744 :     if (bCreateCopy && CPLTestBool(CSLFetchNameValueDef(
    5475             :                            papszParamList, "@TILE_INTERLEAVE", "NO")))
    5476             :     {
    5477           7 :         bTileInterleavingOut = true;
    5478           7 :         nPlanar = PLANARCONFIG_SEPARATE;
    5479             :     }
    5480             :     else
    5481             :     {
    5482        9737 :         if (const char *pszValue =
    5483        9737 :                 CSLFetchNameValue(papszParamList, "INTERLEAVE"))
    5484             :         {
    5485        1568 :             if (EQUAL(pszValue, "PIXEL"))
    5486         401 :                 nPlanar = PLANARCONFIG_CONTIG;
    5487        1167 :             else if (EQUAL(pszValue, "BAND"))
    5488             :             {
    5489        1166 :                 nPlanar = PLANARCONFIG_SEPARATE;
    5490             :             }
    5491           1 :             else if (EQUAL(pszValue, "BAND"))
    5492             :             {
    5493           0 :                 nPlanar = PLANARCONFIG_SEPARATE;
    5494             :             }
    5495             :             else
    5496             :             {
    5497           1 :                 ReportError(
    5498             :                     pszFilename, CE_Failure, CPLE_IllegalArg,
    5499             :                     "INTERLEAVE=%s unsupported, value must be PIXEL or BAND.",
    5500             :                     pszValue);
    5501           1 :                 return nullptr;
    5502             :             }
    5503             :         }
    5504             :         else
    5505             :         {
    5506        8169 :             nPlanar = PLANARCONFIG_CONTIG;
    5507             :         }
    5508             :     }
    5509             : 
    5510        9743 :     int l_nCompression = COMPRESSION_NONE;
    5511        9743 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "COMPRESS"))
    5512             :     {
    5513        3318 :         l_nCompression = GTIFFGetCompressionMethod(pszValue, "COMPRESS");
    5514        3318 :         if (l_nCompression < 0)
    5515           0 :             return nullptr;
    5516             :     }
    5517             : 
    5518        9743 :     constexpr int JPEG_MAX_DIMENSION = 65500;  // Defined in jpeglib.h
    5519        9743 :     constexpr int WEBP_MAX_DIMENSION = 16383;
    5520             : 
    5521             :     const struct
    5522             :     {
    5523             :         int nCodecID;
    5524             :         const char *pszCodecName;
    5525             :         int nMaxDim;
    5526        9743 :     } asLimitations[] = {
    5527             :         {COMPRESSION_JPEG, "JPEG", JPEG_MAX_DIMENSION},
    5528             :         {COMPRESSION_WEBP, "WEBP", WEBP_MAX_DIMENSION},
    5529             :     };
    5530             : 
    5531       29217 :     for (const auto &sLimitation : asLimitations)
    5532             :     {
    5533       19482 :         if (l_nCompression == sLimitation.nCodecID && !bTiled &&
    5534        2074 :             nXSize > sLimitation.nMaxDim)
    5535             :         {
    5536           2 :             ReportError(
    5537             :                 pszFilename, CE_Failure, CPLE_IllegalArg,
    5538             :                 "COMPRESS=%s is only compatible of un-tiled images whose "
    5539             :                 "width is lesser or equal to %d pixels. "
    5540             :                 "To overcome this limitation, set the TILED=YES creation "
    5541             :                 "option.",
    5542           2 :                 sLimitation.pszCodecName, sLimitation.nMaxDim);
    5543           2 :             return nullptr;
    5544             :         }
    5545       19480 :         else if (l_nCompression == sLimitation.nCodecID && bTiled &&
    5546          52 :                  l_nBlockXSize > sLimitation.nMaxDim)
    5547             :         {
    5548           2 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5549             :                         "COMPRESS=%s is only compatible of tiled images whose "
    5550             :                         "BLOCKXSIZE is lesser or equal to %d pixels.",
    5551           2 :                         sLimitation.pszCodecName, sLimitation.nMaxDim);
    5552           2 :             return nullptr;
    5553             :         }
    5554       19478 :         else if (l_nCompression == sLimitation.nCodecID &&
    5555        2122 :                  l_nBlockYSize > sLimitation.nMaxDim)
    5556             :         {
    5557           4 :             ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
    5558             :                         "COMPRESS=%s is only compatible of images whose "
    5559             :                         "BLOCKYSIZE is lesser or equal to %d pixels. "
    5560             :                         "To overcome this limitation, set the TILED=YES "
    5561             :                         "creation option",
    5562           4 :                         sLimitation.pszCodecName, sLimitation.nMaxDim);
    5563           4 :             return nullptr;
    5564             :         }
    5565             :     }
    5566             : 
    5567             :     /* -------------------------------------------------------------------- */
    5568             :     /*      How many bits per sample?  We have a special case if NBITS      */
    5569             :     /*      specified for GDT_UInt8, GDT_UInt16, GDT_UInt32.                 */
    5570             :     /* -------------------------------------------------------------------- */
    5571        9735 :     int l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5572        9735 :     if (CSLFetchNameValue(papszParamList, "NBITS") != nullptr)
    5573             :     {
    5574        1747 :         int nMinBits = 0;
    5575        1747 :         int nMaxBits = 0;
    5576        1747 :         l_nBitsPerSample = atoi(CSLFetchNameValue(papszParamList, "NBITS"));
    5577        1747 :         if (eType == GDT_UInt8)
    5578             :         {
    5579         527 :             nMinBits = 1;
    5580         527 :             nMaxBits = 8;
    5581             :         }
    5582        1220 :         else if (eType == GDT_UInt16)
    5583             :         {
    5584        1202 :             nMinBits = 9;
    5585        1202 :             nMaxBits = 16;
    5586             :         }
    5587          18 :         else if (eType == GDT_UInt32)
    5588             :         {
    5589          14 :             nMinBits = 17;
    5590          14 :             nMaxBits = 32;
    5591             :         }
    5592           4 :         else if (eType == GDT_Float32)
    5593             :         {
    5594           4 :             if (l_nBitsPerSample != 16 && l_nBitsPerSample != 32)
    5595             :             {
    5596           1 :                 ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5597             :                             "Only NBITS=16 is supported for data type Float32");
    5598           1 :                 l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5599             :             }
    5600             :         }
    5601             :         else
    5602             :         {
    5603           0 :             ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5604             :                         "NBITS is not supported for data type %s",
    5605             :                         GDALGetDataTypeName(eType));
    5606           0 :             l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
    5607             :         }
    5608             : 
    5609        1747 :         if (nMinBits != 0)
    5610             :         {
    5611        1743 :             if (l_nBitsPerSample < nMinBits)
    5612             :             {
    5613           2 :                 ReportError(
    5614             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    5615             :                     "NBITS=%d is invalid for data type %s. Using NBITS=%d",
    5616             :                     l_nBitsPerSample, GDALGetDataTypeName(eType), nMinBits);
    5617           2 :                 l_nBitsPerSample = nMinBits;
    5618             :             }
    5619        1741 :             else if (l_nBitsPerSample > nMaxBits)
    5620             :             {
    5621           3 :                 ReportError(
    5622             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    5623             :                     "NBITS=%d is invalid for data type %s. Using NBITS=%d",
    5624             :                     l_nBitsPerSample, GDALGetDataTypeName(eType), nMaxBits);
    5625           3 :                 l_nBitsPerSample = nMaxBits;
    5626             :             }
    5627             :         }
    5628             :     }
    5629             : 
    5630             : #ifdef HAVE_JXL
    5631        9735 :     if ((l_nCompression == COMPRESSION_JXL ||
    5632         106 :          l_nCompression == COMPRESSION_JXL_DNG_1_7) &&
    5633         105 :         eType != GDT_Float16 && eType != GDT_Float32)
    5634             :     {
    5635             :         // Reflects tif_jxl's GetJXLDataType()
    5636          85 :         if (eType != GDT_UInt8 && eType != GDT_UInt16)
    5637             :         {
    5638           1 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5639             :                         "Data type %s not supported for JXL compression. Only "
    5640             :                         "Byte, UInt16, Float16, Float32 are supported",
    5641             :                         GDALGetDataTypeName(eType));
    5642           2 :             return nullptr;
    5643             :         }
    5644             : 
    5645             :         const struct
    5646             :         {
    5647             :             GDALDataType eDT;
    5648             :             int nBitsPerSample;
    5649          84 :         } asSupportedDTBitsPerSample[] = {
    5650             :             {GDT_UInt8, 8},
    5651             :             {GDT_UInt16, 16},
    5652             :         };
    5653             : 
    5654         250 :         for (const auto &sSupportedDTBitsPerSample : asSupportedDTBitsPerSample)
    5655             :         {
    5656         167 :             if (eType == sSupportedDTBitsPerSample.eDT &&
    5657          84 :                 l_nBitsPerSample != sSupportedDTBitsPerSample.nBitsPerSample)
    5658             :             {
    5659           1 :                 ReportError(
    5660             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    5661             :                     "Bits per sample=%d not supported for JXL compression. "
    5662             :                     "Only %d is supported for %s data type.",
    5663           1 :                     l_nBitsPerSample, sSupportedDTBitsPerSample.nBitsPerSample,
    5664             :                     GDALGetDataTypeName(eType));
    5665           1 :                 return nullptr;
    5666             :             }
    5667             :         }
    5668             :     }
    5669             : #endif
    5670             : 
    5671        9733 :     int nPredictor = PREDICTOR_NONE;
    5672        9733 :     const char *pszPredictor = CSLFetchNameValue(papszParamList, "PREDICTOR");
    5673        9733 :     if (pszPredictor)
    5674             :     {
    5675          30 :         nPredictor = atoi(pszPredictor);
    5676             :     }
    5677             : 
    5678        9733 :     if (nPredictor != PREDICTOR_NONE &&
    5679          17 :         l_nCompression != COMPRESSION_ADOBE_DEFLATE &&
    5680           2 :         l_nCompression != COMPRESSION_LZW &&
    5681           2 :         l_nCompression != COMPRESSION_LZMA &&
    5682             :         l_nCompression != COMPRESSION_ZSTD)
    5683             :     {
    5684           1 :         ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5685             :                     "PREDICTOR option is ignored for COMPRESS=%s. "
    5686             :                     "Only valid for DEFLATE, LZW, LZMA or ZSTD",
    5687             :                     CSLFetchNameValueDef(papszParamList, "COMPRESS", "NONE"));
    5688             :     }
    5689             : 
    5690             :     // Do early checks as libtiff will only error out when starting to write.
    5691        9761 :     else if (nPredictor != PREDICTOR_NONE &&
    5692          29 :              CPLTestBool(
    5693             :                  CPLGetConfigOption("GDAL_GTIFF_PREDICTOR_CHECKS", "YES")))
    5694             :     {
    5695             : #if (TIFFLIB_VERSION > 20210416) || defined(INTERNAL_LIBTIFF)
    5696             : #define HAVE_PREDICTOR_2_FOR_64BIT
    5697             : #endif
    5698          29 :         if (nPredictor == 2)
    5699             :         {
    5700          24 :             if (l_nBitsPerSample != 8 && l_nBitsPerSample != 16 &&
    5701             :                 l_nBitsPerSample != 32
    5702             : #ifdef HAVE_PREDICTOR_2_FOR_64BIT
    5703           2 :                 && l_nBitsPerSample != 64
    5704             : #endif
    5705             :             )
    5706             :             {
    5707             : #if !defined(HAVE_PREDICTOR_2_FOR_64BIT)
    5708             :                 if (l_nBitsPerSample == 64)
    5709             :                 {
    5710             :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5711             :                                 "PREDICTOR=2 is supported on 64 bit samples "
    5712             :                                 "starting with libtiff > 4.3.0.");
    5713             :                 }
    5714             :                 else
    5715             : #endif
    5716             :                 {
    5717           2 :                     const int nBITSHint = (l_nBitsPerSample < 8)    ? 8
    5718           1 :                                           : (l_nBitsPerSample < 16) ? 16
    5719           0 :                                           : (l_nBitsPerSample < 32) ? 32
    5720             :                                                                     : 64;
    5721           1 :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5722             : #ifdef HAVE_PREDICTOR_2_FOR_64BIT
    5723             :                                 "PREDICTOR=2 is only supported with 8/16/32/64 "
    5724             :                                 "bit samples. You can specify the NBITS=%d "
    5725             :                                 "creation option to promote to the closest "
    5726             :                                 "supported bits per sample value.",
    5727             : #else
    5728             :                                 "PREDICTOR=2 is only supported with 8/16/32 "
    5729             :                                 "bit samples. You can specify the NBITS=%d "
    5730             :                                 "creation option to promote to the closest "
    5731             :                                 "supported bits per sample value.",
    5732             : #endif
    5733             :                                 nBITSHint);
    5734             :                 }
    5735           1 :                 return nullptr;
    5736             :             }
    5737             :         }
    5738           5 :         else if (nPredictor == 3)
    5739             :         {
    5740           4 :             if (eType != GDT_Float32 && eType != GDT_Float64)
    5741             :             {
    5742           1 :                 ReportError(
    5743             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    5744             :                     "PREDICTOR=3 is only supported with Float32 or Float64.");
    5745           1 :                 return nullptr;
    5746             :             }
    5747             :         }
    5748             :         else
    5749             :         {
    5750           1 :             ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    5751             :                         "PREDICTOR=%s is not supported.", pszPredictor);
    5752           1 :             return nullptr;
    5753             :         }
    5754             :     }
    5755             : 
    5756        9730 :     const int l_nZLevel = GTiffGetZLevel(papszParamList);
    5757        9730 :     const int l_nLZMAPreset = GTiffGetLZMAPreset(papszParamList);
    5758        9730 :     const int l_nZSTDLevel = GTiffGetZSTDPreset(papszParamList);
    5759        9730 :     const int l_nWebPLevel = GTiffGetWebPLevel(papszParamList);
    5760        9730 :     const bool l_bWebPLossless = GTiffGetWebPLossless(papszParamList);
    5761        9730 :     const int l_nJpegQuality = GTiffGetJpegQuality(papszParamList);
    5762        9730 :     const int l_nJpegTablesMode = GTiffGetJpegTablesMode(papszParamList);
    5763        9730 :     const double l_dfMaxZError = GTiffGetLERCMaxZError(papszParamList);
    5764             : #if HAVE_JXL
    5765        9730 :     bool bJXLLosslessSpecified = false;
    5766             :     const bool l_bJXLLossless =
    5767        9730 :         GTiffGetJXLLossless(papszParamList, &bJXLLosslessSpecified);
    5768        9730 :     const uint32_t l_nJXLEffort = GTiffGetJXLEffort(papszParamList);
    5769        9730 :     bool bJXLDistanceSpecified = false;
    5770             :     const float l_fJXLDistance =
    5771        9730 :         GTiffGetJXLDistance(papszParamList, &bJXLDistanceSpecified);
    5772        9730 :     if (bJXLDistanceSpecified && l_bJXLLossless)
    5773             :     {
    5774           1 :         ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
    5775             :                     "JXL_DISTANCE creation option is ignored, given %s "
    5776             :                     "JXL_LOSSLESS=YES",
    5777             :                     bJXLLosslessSpecified ? "(explicit)" : "(implicit)");
    5778             :     }
    5779        9730 :     bool bJXLAlphaDistanceSpecified = false;
    5780             :     const float l_fJXLAlphaDistance =
    5781        9730 :         GTiffGetJXLAlphaDistance(papszParamList, &bJXLAlphaDistanceSpecified);
    5782        9730 :     if (bJXLAlphaDistanceSpecified && l_bJXLLossless)
    5783             :     {
    5784           1 :         ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
    5785             :                     "JXL_ALPHA_DISTANCE creation option is ignored, given %s "
    5786             :                     "JXL_LOSSLESS=YES",
    5787             :                     bJXLLosslessSpecified ? "(explicit)" : "(implicit)");
    5788             :     }
    5789             : #endif
    5790             :     /* -------------------------------------------------------------------- */
    5791             :     /*      Streaming related code                                          */
    5792             :     /* -------------------------------------------------------------------- */
    5793       19460 :     const CPLString osOriFilename(pszFilename);
    5794       19460 :     bool bStreaming = strcmp(pszFilename, "/vsistdout/") == 0 ||
    5795        9730 :                       CPLFetchBool(papszParamList, "STREAMABLE_OUTPUT", false);
    5796             : #ifdef S_ISFIFO
    5797        9730 :     if (!bStreaming)
    5798             :     {
    5799             :         VSIStatBufL sStat;
    5800        9718 :         if (VSIStatExL(pszFilename, &sStat,
    5801       10598 :                        VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
    5802         880 :             S_ISFIFO(sStat.st_mode))
    5803             :         {
    5804           0 :             bStreaming = true;
    5805             :         }
    5806             :     }
    5807             : #endif
    5808        9730 :     if (bStreaming && !EQUAL("NONE", CSLFetchNameValueDef(papszParamList,
    5809             :                                                           "COMPRESS", "NONE")))
    5810             :     {
    5811           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5812             :                     "Streaming only supported to uncompressed TIFF");
    5813           1 :         return nullptr;
    5814             :     }
    5815        9729 :     if (bStreaming && CPLFetchBool(papszParamList, "SPARSE_OK", false))
    5816             :     {
    5817           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5818             :                     "Streaming not supported with SPARSE_OK");
    5819           1 :         return nullptr;
    5820             :     }
    5821             :     const bool bCopySrcOverviews =
    5822        9728 :         CPLFetchBool(papszParamList, "COPY_SRC_OVERVIEWS", false);
    5823        9728 :     if (bStreaming && bCopySrcOverviews)
    5824             :     {
    5825           1 :         ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5826             :                     "Streaming not supported with COPY_SRC_OVERVIEWS");
    5827           1 :         return nullptr;
    5828             :     }
    5829        9727 :     if (bStreaming)
    5830             :     {
    5831           9 :         l_osTmpFilename = VSIMemGenerateHiddenFilename("vsistdout.tif");
    5832           9 :         pszFilename = l_osTmpFilename.c_str();
    5833             :     }
    5834             : 
    5835             :     /* -------------------------------------------------------------------- */
    5836             :     /*      Compute the uncompressed size.                                  */
    5837             :     /* -------------------------------------------------------------------- */
    5838        9727 :     const unsigned nTileXCount =
    5839        9727 :         bTiled ? DIV_ROUND_UP(nXSize, l_nBlockXSize) : 0;
    5840        9727 :     const unsigned nTileYCount =
    5841        9727 :         bTiled ? DIV_ROUND_UP(nYSize, l_nBlockYSize) : 0;
    5842             :     const double dfUncompressedImageSize =
    5843        9727 :         (bTiled ? (static_cast<double>(nTileXCount) * nTileYCount *
    5844         789 :                    l_nBlockXSize * l_nBlockYSize)
    5845        8938 :                 : (nXSize * static_cast<double>(nYSize))) *
    5846        9727 :             l_nBands * GDALGetDataTypeSizeBytes(eType) +
    5847        9727 :         dfExtraSpaceForOverviews;
    5848             : 
    5849             :     /* -------------------------------------------------------------------- */
    5850             :     /*      Should the file be created as a bigtiff file?                   */
    5851             :     /* -------------------------------------------------------------------- */
    5852        9727 :     const char *pszBIGTIFF = CSLFetchNameValue(papszParamList, "BIGTIFF");
    5853             : 
    5854        9727 :     if (pszBIGTIFF == nullptr)
    5855        9304 :         pszBIGTIFF = "IF_NEEDED";
    5856             : 
    5857        9727 :     bool bCreateBigTIFF = false;
    5858        9727 :     if (EQUAL(pszBIGTIFF, "IF_NEEDED"))
    5859             :     {
    5860        9305 :         if (l_nCompression == COMPRESSION_NONE &&
    5861             :             dfUncompressedImageSize > 4200000000.0)
    5862          17 :             bCreateBigTIFF = true;
    5863             :     }
    5864         422 :     else if (EQUAL(pszBIGTIFF, "IF_SAFER"))
    5865             :     {
    5866         401 :         if (dfUncompressedImageSize > 2000000000.0)
    5867           1 :             bCreateBigTIFF = true;
    5868             :     }
    5869             :     else
    5870             :     {
    5871          21 :         bCreateBigTIFF = CPLTestBool(pszBIGTIFF);
    5872          21 :         if (!bCreateBigTIFF && l_nCompression == COMPRESSION_NONE &&
    5873             :             dfUncompressedImageSize > 4200000000.0)
    5874             :         {
    5875           2 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5876             :                         "The TIFF file will be larger than 4GB, so BigTIFF is "
    5877             :                         "necessary.  Creation failed.");
    5878           2 :             return nullptr;
    5879             :         }
    5880             :     }
    5881             : 
    5882        9725 :     if (bCreateBigTIFF)
    5883          35 :         CPLDebug("GTiff", "File being created as a BigTIFF.");
    5884             : 
    5885             :     /* -------------------------------------------------------------------- */
    5886             :     /*      Sanity check.                                                   */
    5887             :     /* -------------------------------------------------------------------- */
    5888        9725 :     if (bTiled)
    5889             :     {
    5890             :         // libtiff implementation limitation
    5891         789 :         if (nTileXCount > 0x80000000U / (bCreateBigTIFF ? 8 : 4) / nTileYCount)
    5892             :         {
    5893           3 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    5894             :                         "File too large regarding tile size. This would result "
    5895             :                         "in a file with tile arrays larger than 2GB");
    5896           3 :             return nullptr;
    5897             :         }
    5898             :     }
    5899             : 
    5900             :     /* -------------------------------------------------------------------- */
    5901             :     /*      Check free space (only for big, non sparse)                     */
    5902             :     /* -------------------------------------------------------------------- */
    5903        9722 :     const double dfLikelyFloorOfFinalSize =
    5904             :         l_nCompression == COMPRESSION_NONE
    5905        9722 :             ? dfUncompressedImageSize
    5906             :             :
    5907             :             /* For compressed, we target 1% as the most optimistic reduction factor! */
    5908             :             0.01 * dfUncompressedImageSize;
    5909        9744 :     if (dfLikelyFloorOfFinalSize >= 1e9 &&
    5910          22 :         !CPLFetchBool(papszParamList, "SPARSE_OK", false) &&
    5911           5 :         osOriFilename != "/vsistdout/" &&
    5912        9749 :         osOriFilename != "/vsistdout_redirect/" &&
    5913           5 :         CPLTestBool(CPLGetConfigOption("CHECK_DISK_FREE_SPACE", "TRUE")))
    5914             :     {
    5915             :         const GIntBig nFreeDiskSpace =
    5916           4 :             VSIGetDiskFreeSpace(CPLGetDirnameSafe(pszFilename).c_str());
    5917           4 :         if (nFreeDiskSpace >= 0 && nFreeDiskSpace < dfLikelyFloorOfFinalSize)
    5918             :         {
    5919           6 :             ReportError(
    5920             :                 pszFilename, CE_Failure, CPLE_FileIO,
    5921             :                 "Free disk space available is %s, "
    5922             :                 "whereas %s are %s necessary. "
    5923             :                 "You can disable this check by defining the "
    5924             :                 "CHECK_DISK_FREE_SPACE configuration option to FALSE.",
    5925           4 :                 CPLFormatReadableFileSize(static_cast<uint64_t>(nFreeDiskSpace))
    5926             :                     .c_str(),
    5927           4 :                 CPLFormatReadableFileSize(dfLikelyFloorOfFinalSize).c_str(),
    5928             :                 l_nCompression == COMPRESSION_NONE
    5929             :                     ? "at least"
    5930             :                     : "likely at least (probably more)");
    5931           2 :             return nullptr;
    5932             :         }
    5933             :     }
    5934             : 
    5935             :     /* -------------------------------------------------------------------- */
    5936             :     /*      Check if the user wishes a particular endianness                */
    5937             :     /* -------------------------------------------------------------------- */
    5938             : 
    5939        9720 :     int eEndianness = ENDIANNESS_NATIVE;
    5940        9720 :     const char *pszEndianness = CSLFetchNameValue(papszParamList, "ENDIANNESS");
    5941        9720 :     if (pszEndianness == nullptr)
    5942        9657 :         pszEndianness = CPLGetConfigOption("GDAL_TIFF_ENDIANNESS", nullptr);
    5943        9720 :     if (pszEndianness != nullptr)
    5944             :     {
    5945         123 :         if (EQUAL(pszEndianness, "LITTLE"))
    5946             :         {
    5947          36 :             eEndianness = ENDIANNESS_LITTLE;
    5948             :         }
    5949          87 :         else if (EQUAL(pszEndianness, "BIG"))
    5950             :         {
    5951           1 :             eEndianness = ENDIANNESS_BIG;
    5952             :         }
    5953          86 :         else if (EQUAL(pszEndianness, "INVERTED"))
    5954             :         {
    5955             : #ifdef CPL_LSB
    5956          82 :             eEndianness = ENDIANNESS_BIG;
    5957             : #else
    5958             :             eEndianness = ENDIANNESS_LITTLE;
    5959             : #endif
    5960             :         }
    5961           4 :         else if (!EQUAL(pszEndianness, "NATIVE"))
    5962             :         {
    5963           1 :             ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
    5964             :                         "ENDIANNESS=%s not supported. Defaulting to NATIVE",
    5965             :                         pszEndianness);
    5966             :         }
    5967             :     }
    5968             : 
    5969             :     /* -------------------------------------------------------------------- */
    5970             :     /*      Try opening the dataset.                                        */
    5971             :     /* -------------------------------------------------------------------- */
    5972             : 
    5973             :     const bool bAppend =
    5974        9720 :         CPLFetchBool(papszParamList, "APPEND_SUBDATASET", false);
    5975             : 
    5976        9720 :     char szOpeningFlag[5] = {};
    5977        9720 :     strcpy(szOpeningFlag, bAppend ? "r+" : "w+");
    5978        9720 :     if (bCreateBigTIFF)
    5979          32 :         strcat(szOpeningFlag, "8");
    5980        9720 :     if (eEndianness == ENDIANNESS_BIG)
    5981          83 :         strcat(szOpeningFlag, "b");
    5982        9637 :     else if (eEndianness == ENDIANNESS_LITTLE)
    5983          36 :         strcat(szOpeningFlag, "l");
    5984             : 
    5985        9720 :     VSIErrorReset();
    5986        9720 :     const bool bOnlyVisibleAtCloseTime = CPLTestBool(CSLFetchNameValueDef(
    5987             :         papszParamList, "@CREATE_ONLY_VISIBLE_AT_CLOSE_TIME", "NO"));
    5988        9720 :     const bool bSuppressASAP = CPLTestBool(
    5989             :         CSLFetchNameValueDef(papszParamList, "@SUPPRESS_ASAP", "NO"));
    5990             :     auto l_fpL =
    5991        9720 :         (bOnlyVisibleAtCloseTime || bSuppressASAP) && !bAppend
    5992        9788 :             ? VSIFileManager::GetHandler(pszFilename)
    5993         136 :                   ->CreateOnlyVisibleAtCloseTime(pszFilename, true, nullptr)
    5994          68 :                   .release()
    5995       19372 :             : VSIFilesystemHandler::OpenStatic(pszFilename,
    5996             :                                                bAppend ? "r+b" : "w+b", true)
    5997        9720 :                   .release();
    5998        9720 :     if (l_fpL == nullptr)
    5999             :     {
    6000          21 :         VSIToCPLErrorWithMsg(CE_Failure, CPLE_OpenFailed,
    6001          42 :                              std::string("Attempt to create new tiff file `")
    6002          21 :                                  .append(pszFilename)
    6003          21 :                                  .append("' failed")
    6004             :                                  .c_str());
    6005          21 :         return nullptr;
    6006             :     }
    6007             : 
    6008        9699 :     if (bSuppressASAP)
    6009             :     {
    6010          38 :         l_fpL->CancelCreation();
    6011             :     }
    6012             : 
    6013        9699 :     TIFF *l_hTIFF = VSI_TIFFOpen(pszFilename, szOpeningFlag, l_fpL);
    6014        9699 :     if (l_hTIFF == nullptr)
    6015             :     {
    6016           2 :         if (CPLGetLastErrorNo() == 0)
    6017           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    6018             :                      "Attempt to create new tiff file `%s' "
    6019             :                      "failed in XTIFFOpen().",
    6020             :                      pszFilename);
    6021           2 :         l_fpL->CancelCreation();
    6022           2 :         CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6023           2 :         return nullptr;
    6024             :     }
    6025             : 
    6026        9697 :     if (bAppend)
    6027             :     {
    6028             : #if !(defined(INTERNAL_LIBTIFF) || TIFFLIB_VERSION > 20240911)
    6029             :         // This is a bit of a hack to cause (*tif->tif_cleanup)(tif); to be
    6030             :         // called. See https://trac.osgeo.org/gdal/ticket/2055
    6031             :         // Fixed in libtiff > 4.7.0
    6032             :         TIFFSetField(l_hTIFF, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
    6033             :         TIFFFreeDirectory(l_hTIFF);
    6034             : #endif
    6035           6 :         TIFFCreateDirectory(l_hTIFF);
    6036             :     }
    6037             : 
    6038             :     /* -------------------------------------------------------------------- */
    6039             :     /*      Do we have a custom pixel type (just used for signed byte now). */
    6040             :     /* -------------------------------------------------------------------- */
    6041        9697 :     const char *pszPixelType = CSLFetchNameValue(papszParamList, "PIXELTYPE");
    6042        9697 :     if (pszPixelType == nullptr)
    6043        9689 :         pszPixelType = "";
    6044        9697 :     if (eType == GDT_UInt8 && EQUAL(pszPixelType, "SIGNEDBYTE"))
    6045             :     {
    6046           8 :         CPLError(CE_Warning, CPLE_AppDefined,
    6047             :                  "Using PIXELTYPE=SIGNEDBYTE with Byte data type is deprecated "
    6048             :                  "(but still works). "
    6049             :                  "Using Int8 data type instead is now recommended.");
    6050             :     }
    6051             : 
    6052             :     /* -------------------------------------------------------------------- */
    6053             :     /*      Setup some standard flags.                                      */
    6054             :     /* -------------------------------------------------------------------- */
    6055        9697 :     TIFFSetField(l_hTIFF, TIFFTAG_IMAGEWIDTH, nXSize);
    6056        9697 :     TIFFSetField(l_hTIFF, TIFFTAG_IMAGELENGTH, nYSize);
    6057        9697 :     TIFFSetField(l_hTIFF, TIFFTAG_BITSPERSAMPLE, l_nBitsPerSample);
    6058             : 
    6059        9697 :     uint16_t l_nSampleFormat = 0;
    6060        9697 :     if ((eType == GDT_UInt8 && EQUAL(pszPixelType, "SIGNEDBYTE")) ||
    6061        9550 :         eType == GDT_Int8 || eType == GDT_Int16 || eType == GDT_Int32 ||
    6062             :         eType == GDT_Int64)
    6063         790 :         l_nSampleFormat = SAMPLEFORMAT_INT;
    6064        8907 :     else if (eType == GDT_CInt16 || eType == GDT_CInt32)
    6065         363 :         l_nSampleFormat = SAMPLEFORMAT_COMPLEXINT;
    6066        8544 :     else if (eType == GDT_Float16 || eType == GDT_Float32 ||
    6067             :              eType == GDT_Float64)
    6068        1110 :         l_nSampleFormat = SAMPLEFORMAT_IEEEFP;
    6069        7434 :     else if (eType == GDT_CFloat16 || eType == GDT_CFloat32 ||
    6070             :              eType == GDT_CFloat64)
    6071         471 :         l_nSampleFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
    6072             :     else
    6073        6963 :         l_nSampleFormat = SAMPLEFORMAT_UINT;
    6074             : 
    6075        9697 :     TIFFSetField(l_hTIFF, TIFFTAG_SAMPLEFORMAT, l_nSampleFormat);
    6076        9697 :     TIFFSetField(l_hTIFF, TIFFTAG_SAMPLESPERPIXEL, l_nBands);
    6077        9697 :     TIFFSetField(l_hTIFF, TIFFTAG_PLANARCONFIG, nPlanar);
    6078             : 
    6079             :     /* -------------------------------------------------------------------- */
    6080             :     /*      Setup Photometric Interpretation. Take this value from the user */
    6081             :     /*      passed option or guess correct value otherwise.                 */
    6082             :     /* -------------------------------------------------------------------- */
    6083        9697 :     int nSamplesAccountedFor = 1;
    6084        9697 :     bool bForceColorTable = false;
    6085             : 
    6086        9697 :     if (const char *pszValue = CSLFetchNameValue(papszParamList, "PHOTOMETRIC"))
    6087             :     {
    6088        1905 :         if (EQUAL(pszValue, "MINISBLACK"))
    6089          14 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6090        1891 :         else if (EQUAL(pszValue, "MINISWHITE"))
    6091             :         {
    6092           2 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
    6093             :         }
    6094        1889 :         else if (EQUAL(pszValue, "PALETTE"))
    6095             :         {
    6096           5 :             if (eType == GDT_UInt8 || eType == GDT_UInt16)
    6097             :             {
    6098           4 :                 TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    6099           4 :                 nSamplesAccountedFor = 1;
    6100           4 :                 bForceColorTable = true;
    6101             :             }
    6102             :             else
    6103             :             {
    6104           1 :                 ReportError(
    6105             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    6106             :                     "PHOTOMETRIC=PALETTE only compatible with Byte or UInt16");
    6107             :             }
    6108             :         }
    6109        1884 :         else if (EQUAL(pszValue, "RGB"))
    6110             :         {
    6111        1144 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    6112        1144 :             nSamplesAccountedFor = 3;
    6113             :         }
    6114         740 :         else if (EQUAL(pszValue, "CMYK"))
    6115             :         {
    6116          10 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
    6117          10 :             nSamplesAccountedFor = 4;
    6118             :         }
    6119         730 :         else if (EQUAL(pszValue, "YCBCR"))
    6120             :         {
    6121             :             // Because of subsampling, setting YCBCR without JPEG compression
    6122             :             // leads to a crash currently. Would need to make
    6123             :             // GTiffRasterBand::IWriteBlock() aware of subsampling so that it
    6124             :             // doesn't overrun buffer size returned by libtiff.
    6125         729 :             if (l_nCompression != COMPRESSION_JPEG)
    6126             :             {
    6127           1 :                 ReportError(
    6128             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    6129             :                     "Currently, PHOTOMETRIC=YCBCR requires COMPRESS=JPEG");
    6130           1 :                 XTIFFClose(l_hTIFF);
    6131           1 :                 l_fpL->CancelCreation();
    6132           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6133           1 :                 return nullptr;
    6134             :             }
    6135             : 
    6136         728 :             if (nPlanar == PLANARCONFIG_SEPARATE)
    6137             :             {
    6138           1 :                 ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    6139             :                             "PHOTOMETRIC=YCBCR requires INTERLEAVE=PIXEL");
    6140           1 :                 XTIFFClose(l_hTIFF);
    6141           1 :                 l_fpL->CancelCreation();
    6142           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6143           1 :                 return nullptr;
    6144             :             }
    6145             : 
    6146             :             // YCBCR strictly requires 3 bands. Not less, not more Issue an
    6147             :             // explicit error message as libtiff one is a bit cryptic:
    6148             :             // TIFFVStripSize64:Invalid td_samplesperpixel value.
    6149         727 :             if (l_nBands != 3)
    6150             :             {
    6151           1 :                 ReportError(
    6152             :                     pszFilename, CE_Failure, CPLE_NotSupported,
    6153             :                     "PHOTOMETRIC=YCBCR not supported on a %d-band raster: "
    6154             :                     "only compatible of a 3-band (RGB) raster",
    6155             :                     l_nBands);
    6156           1 :                 XTIFFClose(l_hTIFF);
    6157           1 :                 l_fpL->CancelCreation();
    6158           1 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6159           1 :                 return nullptr;
    6160             :             }
    6161             : 
    6162         726 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
    6163         726 :             nSamplesAccountedFor = 3;
    6164             : 
    6165             :             // Explicitly register the subsampling so that JPEGFixupTags
    6166             :             // is a no-op (helps for cloud optimized geotiffs)
    6167         726 :             TIFFSetField(l_hTIFF, TIFFTAG_YCBCRSUBSAMPLING, 2, 2);
    6168             :         }
    6169           1 :         else if (EQUAL(pszValue, "CIELAB"))
    6170             :         {
    6171           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB);
    6172           0 :             nSamplesAccountedFor = 3;
    6173             :         }
    6174           1 :         else if (EQUAL(pszValue, "ICCLAB"))
    6175             :         {
    6176           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ICCLAB);
    6177           0 :             nSamplesAccountedFor = 3;
    6178             :         }
    6179           1 :         else if (EQUAL(pszValue, "ITULAB"))
    6180             :         {
    6181           0 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ITULAB);
    6182           0 :             nSamplesAccountedFor = 3;
    6183             :         }
    6184             :         else
    6185             :         {
    6186           1 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    6187             :                         "PHOTOMETRIC=%s value not recognised, ignoring.  "
    6188             :                         "Set the Photometric Interpretation as MINISBLACK.",
    6189             :                         pszValue);
    6190           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6191             :         }
    6192             : 
    6193        1902 :         if (l_nBands < nSamplesAccountedFor)
    6194             :         {
    6195           1 :             ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
    6196             :                         "PHOTOMETRIC=%s value does not correspond to number "
    6197             :                         "of bands (%d), ignoring.  "
    6198             :                         "Set the Photometric Interpretation as MINISBLACK.",
    6199             :                         pszValue, l_nBands);
    6200           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6201             :         }
    6202             :     }
    6203             :     else
    6204             :     {
    6205             :         // If image contains 3 or 4 bands and datatype is Byte then we will
    6206             :         // assume it is RGB. In all other cases assume it is MINISBLACK.
    6207        7792 :         if (l_nBands == 3 && eType == GDT_UInt8)
    6208             :         {
    6209         317 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    6210         317 :             nSamplesAccountedFor = 3;
    6211             :         }
    6212        7475 :         else if (l_nBands == 4 && eType == GDT_UInt8)
    6213             :         {
    6214             :             uint16_t v[1] = {
    6215         718 :                 GTiffGetAlphaValue(CSLFetchNameValue(papszParamList, "ALPHA"),
    6216         718 :                                    DEFAULT_ALPHA_TYPE)};
    6217             : 
    6218         718 :             TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
    6219         718 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    6220         718 :             nSamplesAccountedFor = 4;
    6221             :         }
    6222             :         else
    6223             :         {
    6224        6757 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    6225        6757 :             nSamplesAccountedFor = 1;
    6226             :         }
    6227             :     }
    6228             : 
    6229             :     /* -------------------------------------------------------------------- */
    6230             :     /*      If there are extra samples, we need to mark them with an        */
    6231             :     /*      appropriate extrasamples definition here.                       */
    6232             :     /* -------------------------------------------------------------------- */
    6233        9694 :     if (l_nBands > nSamplesAccountedFor)
    6234             :     {
    6235        1369 :         const int nExtraSamples = l_nBands - nSamplesAccountedFor;
    6236             : 
    6237             :         uint16_t *v = static_cast<uint16_t *>(
    6238        1369 :             CPLMalloc(sizeof(uint16_t) * nExtraSamples));
    6239             : 
    6240        1369 :         v[0] = GTiffGetAlphaValue(CSLFetchNameValue(papszParamList, "ALPHA"),
    6241             :                                   EXTRASAMPLE_UNSPECIFIED);
    6242             : 
    6243      297668 :         for (int i = 1; i < nExtraSamples; ++i)
    6244      296299 :             v[i] = EXTRASAMPLE_UNSPECIFIED;
    6245             : 
    6246        1369 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples, v);
    6247             : 
    6248        1369 :         CPLFree(v);
    6249             :     }
    6250             : 
    6251             :     // Set the ICC color profile.
    6252        9694 :     if (eProfile != GTiffProfile::BASELINE)
    6253             :     {
    6254        9669 :         SaveICCProfile(nullptr, l_hTIFF, papszParamList, l_nBitsPerSample);
    6255             :     }
    6256             : 
    6257             :     // Set the compression method before asking the default strip size
    6258             :     // This is useful when translating to a JPEG-In-TIFF file where
    6259             :     // the default strip size is 8 or 16 depending on the photometric value.
    6260        9694 :     TIFFSetField(l_hTIFF, TIFFTAG_COMPRESSION, l_nCompression);
    6261             : 
    6262        9694 :     if (l_nCompression == COMPRESSION_LERC)
    6263             :     {
    6264             :         const char *pszCompress =
    6265          97 :             CSLFetchNameValueDef(papszParamList, "COMPRESS", "");
    6266          97 :         if (EQUAL(pszCompress, "LERC_DEFLATE"))
    6267             :         {
    6268          16 :             TIFFSetField(l_hTIFF, TIFFTAG_LERC_ADD_COMPRESSION,
    6269             :                          LERC_ADD_COMPRESSION_DEFLATE);
    6270             :         }
    6271          81 :         else if (EQUAL(pszCompress, "LERC_ZSTD"))
    6272             :         {
    6273          14 :             if (TIFFSetField(l_hTIFF, TIFFTAG_LERC_ADD_COMPRESSION,
    6274          14 :                              LERC_ADD_COMPRESSION_ZSTD) != 1)
    6275             :             {
    6276           0 :                 XTIFFClose(l_hTIFF);
    6277           0 :                 l_fpL->CancelCreation();
    6278           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6279           0 :                 return nullptr;
    6280             :             }
    6281             :         }
    6282             :     }
    6283             :     // TODO later: take into account LERC version
    6284             : 
    6285             :     /* -------------------------------------------------------------------- */
    6286             :     /*      Setup tiling/stripping flags.                                   */
    6287             :     /* -------------------------------------------------------------------- */
    6288        9694 :     if (bTiled)
    6289             :     {
    6290        1558 :         if (!TIFFSetField(l_hTIFF, TIFFTAG_TILEWIDTH, l_nBlockXSize) ||
    6291         779 :             !TIFFSetField(l_hTIFF, TIFFTAG_TILELENGTH, l_nBlockYSize))
    6292             :         {
    6293           0 :             XTIFFClose(l_hTIFF);
    6294           0 :             l_fpL->CancelCreation();
    6295           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    6296           0 :             return nullptr;
    6297             :         }
    6298             :     }
    6299             :     else
    6300             :     {
    6301        8915 :         const uint32_t l_nRowsPerStrip = std::min(
    6302             :             nYSize, l_nBlockYSize == 0
    6303        8915 :                         ? static_cast<int>(TIFFDefaultStripSize(l_hTIFF, 0))
    6304        8915 :                         : l_nBlockYSize);
    6305             : 
    6306        8915 :         TIFFSetField(l_hTIFF, TIFFTAG_ROWSPERSTRIP, l_nRowsPerStrip);
    6307             :     }
    6308             : 
    6309             :     /* -------------------------------------------------------------------- */
    6310             :     /*      Set compression related tags.                                   */
    6311             :     /* -------------------------------------------------------------------- */
    6312        9694 :     if (GTIFFSupportsPredictor(l_nCompression))
    6313         937 :         TIFFSetField(l_hTIFF, TIFFTAG_PREDICTOR, nPredictor);
    6314        9694 :     if (l_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    6315             :         l_nCompression == COMPRESSION_LERC)
    6316             :     {
    6317         280 :         GTiffSetDeflateSubCodec(l_hTIFF);
    6318             : 
    6319         280 :         if (l_nZLevel != -1)
    6320          22 :             TIFFSetField(l_hTIFF, TIFFTAG_ZIPQUALITY, l_nZLevel);
    6321             :     }
    6322        9694 :     if (l_nCompression == COMPRESSION_JPEG && l_nJpegQuality != -1)
    6323        1905 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGQUALITY, l_nJpegQuality);
    6324        9694 :     if (l_nCompression == COMPRESSION_LZMA && l_nLZMAPreset != -1)
    6325          10 :         TIFFSetField(l_hTIFF, TIFFTAG_LZMAPRESET, l_nLZMAPreset);
    6326        9694 :     if ((l_nCompression == COMPRESSION_ZSTD ||
    6327         190 :          l_nCompression == COMPRESSION_LERC) &&
    6328             :         l_nZSTDLevel != -1)
    6329          12 :         TIFFSetField(l_hTIFF, TIFFTAG_ZSTD_LEVEL, l_nZSTDLevel);
    6330        9694 :     if (l_nCompression == COMPRESSION_LERC)
    6331             :     {
    6332          97 :         TIFFSetField(l_hTIFF, TIFFTAG_LERC_MAXZERROR, l_dfMaxZError);
    6333             :     }
    6334             : #if HAVE_JXL
    6335        9694 :     if (l_nCompression == COMPRESSION_JXL ||
    6336             :         l_nCompression == COMPRESSION_JXL_DNG_1_7)
    6337             :     {
    6338         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_LOSSYNESS,
    6339             :                      l_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
    6340         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_EFFORT, l_nJXLEffort);
    6341         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_DISTANCE,
    6342             :                      static_cast<double>(l_fJXLDistance));
    6343         104 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE,
    6344             :                      static_cast<double>(l_fJXLAlphaDistance));
    6345             :     }
    6346             : #endif
    6347        9694 :     if (l_nCompression == COMPRESSION_WEBP)
    6348          33 :         TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LEVEL, l_nWebPLevel);
    6349        9694 :     if (l_nCompression == COMPRESSION_WEBP && l_bWebPLossless)
    6350           7 :         TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LOSSLESS, 1);
    6351             : 
    6352        9694 :     if (l_nCompression == COMPRESSION_JPEG)
    6353        2083 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGTABLESMODE, l_nJpegTablesMode);
    6354             : 
    6355             :     /* -------------------------------------------------------------------- */
    6356             :     /*      If we forced production of a file with photometric=palette,     */
    6357             :     /*      we need to push out a default color table.                      */
    6358             :     /* -------------------------------------------------------------------- */
    6359        9694 :     if (bForceColorTable)
    6360             :     {
    6361           4 :         const int nColors = eType == GDT_UInt8 ? 256 : 65536;
    6362             : 
    6363             :         unsigned short *panTRed = static_cast<unsigned short *>(
    6364           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    6365             :         unsigned short *panTGreen = static_cast<unsigned short *>(
    6366           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    6367             :         unsigned short *panTBlue = static_cast<unsigned short *>(
    6368           4 :             CPLMalloc(sizeof(unsigned short) * nColors));
    6369             : 
    6370        1028 :         for (int iColor = 0; iColor < nColors; ++iColor)
    6371             :         {
    6372        1024 :             if (eType == GDT_UInt8)
    6373             :             {
    6374        1024 :                 panTRed[iColor] = GTiffDataset::ClampCTEntry(
    6375             :                     iColor, 1, iColor, nColorTableMultiplier);
    6376        1024 :                 panTGreen[iColor] = GTiffDataset::ClampCTEntry(
    6377             :                     iColor, 2, iColor, nColorTableMultiplier);
    6378        1024 :                 panTBlue[iColor] = GTiffDataset::ClampCTEntry(
    6379             :                     iColor, 3, iColor, nColorTableMultiplier);
    6380             :             }
    6381             :             else
    6382             :             {
    6383           0 :                 panTRed[iColor] = static_cast<unsigned short>(iColor);
    6384           0 :                 panTGreen[iColor] = static_cast<unsigned short>(iColor);
    6385           0 :                 panTBlue[iColor] = static_cast<unsigned short>(iColor);
    6386             :             }
    6387             :         }
    6388             : 
    6389           4 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue);
    6390             : 
    6391           4 :         CPLFree(panTRed);
    6392           4 :         CPLFree(panTGreen);
    6393           4 :         CPLFree(panTBlue);
    6394             :     }
    6395             : 
    6396             :     // This trick
    6397             :     // creates a temporary in-memory file and fetches its JPEG tables so that
    6398             :     // we can directly set them, before tif_jpeg.c compute them at the first
    6399             :     // strip/tile writing, which is too late, since we have already crystalized
    6400             :     // the directory. This way we avoid a directory rewriting.
    6401       11777 :     if (l_nCompression == COMPRESSION_JPEG &&
    6402        2083 :         CPLTestBool(
    6403             :             CSLFetchNameValueDef(papszParamList, "WRITE_JPEGTABLE_TAG", "YES")))
    6404             :     {
    6405        1014 :         GTiffWriteJPEGTables(
    6406             :             l_hTIFF, CSLFetchNameValue(papszParamList, "PHOTOMETRIC"),
    6407             :             CSLFetchNameValue(papszParamList, "JPEG_QUALITY"),
    6408             :             CSLFetchNameValue(papszParamList, "JPEGTABLESMODE"));
    6409             :     }
    6410             : 
    6411        9694 :     *pfpL = l_fpL;
    6412             : 
    6413        9694 :     return l_hTIFF;
    6414             : }
    6415             : 
    6416             : /************************************************************************/
    6417             : /*                            GuessJPEGQuality()                        */
    6418             : /*                                                                      */
    6419             : /*      Guess JPEG quality from JPEGTABLES tag.                         */
    6420             : /************************************************************************/
    6421             : 
    6422        3850 : static const GByte *GTIFFFindNextTable(const GByte *paby, GByte byMarker,
    6423             :                                        int nLen, int *pnLenTable)
    6424             : {
    6425        7967 :     for (int i = 0; i + 1 < nLen;)
    6426             :     {
    6427        7967 :         if (paby[i] != 0xFF)
    6428           0 :             return nullptr;
    6429        7967 :         ++i;
    6430        7967 :         if (paby[i] == 0xD8)
    6431             :         {
    6432        3117 :             ++i;
    6433        3117 :             continue;
    6434             :         }
    6435        4850 :         if (i + 2 >= nLen)
    6436         833 :             return nullptr;
    6437        4017 :         int nMarkerLen = paby[i + 1] * 256 + paby[i + 2];
    6438        4017 :         if (i + 1 + nMarkerLen >= nLen)
    6439           0 :             return nullptr;
    6440        4017 :         if (paby[i] == byMarker)
    6441             :         {
    6442        3017 :             if (pnLenTable)
    6443        2473 :                 *pnLenTable = nMarkerLen;
    6444        3017 :             return paby + i + 1;
    6445             :         }
    6446        1000 :         i += 1 + nMarkerLen;
    6447             :     }
    6448           0 :     return nullptr;
    6449             : }
    6450             : 
    6451             : constexpr GByte MARKER_HUFFMAN_TABLE = 0xC4;
    6452             : constexpr GByte MARKER_QUANT_TABLE = 0xDB;
    6453             : 
    6454             : // We assume that if there are several quantization tables, they are
    6455             : // in the same order. Which is a reasonable assumption for updating
    6456             : // a file generated by ourselves.
    6457         904 : static bool GTIFFQuantizationTablesEqual(const GByte *paby1, int nLen1,
    6458             :                                          const GByte *paby2, int nLen2)
    6459             : {
    6460         904 :     bool bFound = false;
    6461             :     while (true)
    6462             :     {
    6463         945 :         int nLenTable1 = 0;
    6464         945 :         int nLenTable2 = 0;
    6465             :         const GByte *paby1New =
    6466         945 :             GTIFFFindNextTable(paby1, MARKER_QUANT_TABLE, nLen1, &nLenTable1);
    6467             :         const GByte *paby2New =
    6468         945 :             GTIFFFindNextTable(paby2, MARKER_QUANT_TABLE, nLen2, &nLenTable2);
    6469         945 :         if (paby1New == nullptr && paby2New == nullptr)
    6470         904 :             return bFound;
    6471         911 :         if (paby1New == nullptr || paby2New == nullptr)
    6472           0 :             return false;
    6473         911 :         if (nLenTable1 != nLenTable2)
    6474         207 :             return false;
    6475         704 :         if (memcmp(paby1New, paby2New, nLenTable1) != 0)
    6476         663 :             return false;
    6477          41 :         paby1New += nLenTable1;
    6478          41 :         paby2New += nLenTable2;
    6479          41 :         nLen1 -= static_cast<int>(paby1New - paby1);
    6480          41 :         nLen2 -= static_cast<int>(paby2New - paby2);
    6481          41 :         paby1 = paby1New;
    6482          41 :         paby2 = paby2New;
    6483          41 :         bFound = true;
    6484          41 :     }
    6485             : }
    6486             : 
    6487             : // Guess the JPEG quality by comparing against the MD5Sum of precomputed
    6488             : // quantization tables
    6489         409 : static int GuessJPEGQualityFromMD5(const uint8_t md5JPEGQuantTable[][16],
    6490             :                                    const GByte *const pabyJPEGTable,
    6491             :                                    int nJPEGTableSize)
    6492             : {
    6493         409 :     int nRemainingLen = nJPEGTableSize;
    6494         409 :     const GByte *pabyCur = pabyJPEGTable;
    6495             : 
    6496             :     struct CPLMD5Context context;
    6497         409 :     CPLMD5Init(&context);
    6498             : 
    6499             :     while (true)
    6500             :     {
    6501        1060 :         int nLenTable = 0;
    6502        1060 :         const GByte *pabyNew = GTIFFFindNextTable(pabyCur, MARKER_QUANT_TABLE,
    6503             :                                                   nRemainingLen, &nLenTable);
    6504        1060 :         if (pabyNew == nullptr)
    6505         409 :             break;
    6506         651 :         CPLMD5Update(&context, pabyNew, nLenTable);
    6507         651 :         pabyNew += nLenTable;
    6508         651 :         nRemainingLen -= static_cast<int>(pabyNew - pabyCur);
    6509         651 :         pabyCur = pabyNew;
    6510         651 :     }
    6511             : 
    6512             :     GByte digest[16];
    6513         409 :     CPLMD5Final(digest, &context);
    6514             : 
    6515       28846 :     for (int i = 0; i < 100; i++)
    6516             :     {
    6517       28843 :         if (memcmp(md5JPEGQuantTable[i], digest, 16) == 0)
    6518             :         {
    6519         406 :             return i + 1;
    6520             :         }
    6521             :     }
    6522           3 :     return -1;
    6523             : }
    6524             : 
    6525         464 : int GTiffDataset::GuessJPEGQuality(bool &bOutHasQuantizationTable,
    6526             :                                    bool &bOutHasHuffmanTable)
    6527             : {
    6528         464 :     CPLAssert(m_nCompression == COMPRESSION_JPEG);
    6529         464 :     uint32_t nJPEGTableSize = 0;
    6530         464 :     void *pJPEGTable = nullptr;
    6531         464 :     if (!TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize,
    6532             :                       &pJPEGTable))
    6533             :     {
    6534          14 :         bOutHasQuantizationTable = false;
    6535          14 :         bOutHasHuffmanTable = false;
    6536          14 :         return -1;
    6537             :     }
    6538             : 
    6539         450 :     bOutHasQuantizationTable =
    6540         450 :         GTIFFFindNextTable(static_cast<const GByte *>(pJPEGTable),
    6541             :                            MARKER_QUANT_TABLE, nJPEGTableSize,
    6542         450 :                            nullptr) != nullptr;
    6543         450 :     bOutHasHuffmanTable =
    6544         450 :         GTIFFFindNextTable(static_cast<const GByte *>(pJPEGTable),
    6545             :                            MARKER_HUFFMAN_TABLE, nJPEGTableSize,
    6546         450 :                            nullptr) != nullptr;
    6547         450 :     if (!bOutHasQuantizationTable)
    6548           7 :         return -1;
    6549             : 
    6550         443 :     if ((nBands == 1 && m_nBitsPerSample == 8) ||
    6551         382 :         (nBands == 3 && m_nBitsPerSample == 8 &&
    6552         336 :          m_nPhotometric == PHOTOMETRIC_RGB) ||
    6553         288 :         (nBands == 4 && m_nBitsPerSample == 8 &&
    6554          27 :          m_nPhotometric == PHOTOMETRIC_SEPARATED))
    6555             :     {
    6556         167 :         return GuessJPEGQualityFromMD5(md5JPEGQuantTable_generic_8bit,
    6557             :                                        static_cast<const GByte *>(pJPEGTable),
    6558         167 :                                        static_cast<int>(nJPEGTableSize));
    6559             :     }
    6560             : 
    6561         276 :     if (nBands == 3 && m_nBitsPerSample == 8 &&
    6562         242 :         m_nPhotometric == PHOTOMETRIC_YCBCR)
    6563             :     {
    6564             :         int nRet =
    6565         242 :             GuessJPEGQualityFromMD5(md5JPEGQuantTable_3_YCBCR_8bit,
    6566             :                                     static_cast<const GByte *>(pJPEGTable),
    6567             :                                     static_cast<int>(nJPEGTableSize));
    6568         242 :         if (nRet < 0)
    6569             :         {
    6570             :             // libjpeg 9e has modified the YCbCr quantization tables.
    6571             :             nRet =
    6572           0 :                 GuessJPEGQualityFromMD5(md5JPEGQuantTable_3_YCBCR_8bit_jpeg9e,
    6573             :                                         static_cast<const GByte *>(pJPEGTable),
    6574             :                                         static_cast<int>(nJPEGTableSize));
    6575             :         }
    6576         242 :         return nRet;
    6577             :     }
    6578             : 
    6579          34 :     char **papszLocalParameters = nullptr;
    6580             :     papszLocalParameters =
    6581          34 :         CSLSetNameValue(papszLocalParameters, "COMPRESS", "JPEG");
    6582          34 :     if (m_nPhotometric == PHOTOMETRIC_YCBCR)
    6583             :         papszLocalParameters =
    6584           7 :             CSLSetNameValue(papszLocalParameters, "PHOTOMETRIC", "YCBCR");
    6585          27 :     else if (m_nPhotometric == PHOTOMETRIC_SEPARATED)
    6586             :         papszLocalParameters =
    6587           0 :             CSLSetNameValue(papszLocalParameters, "PHOTOMETRIC", "CMYK");
    6588             :     papszLocalParameters =
    6589          34 :         CSLSetNameValue(papszLocalParameters, "BLOCKYSIZE", "16");
    6590          34 :     if (m_nBitsPerSample == 12)
    6591             :         papszLocalParameters =
    6592          16 :             CSLSetNameValue(papszLocalParameters, "NBITS", "12");
    6593             : 
    6594             :     const CPLString osTmpFilenameIn(
    6595          34 :         VSIMemGenerateHiddenFilename("gtiffdataset_guess_jpeg_quality_tmp"));
    6596             : 
    6597          34 :     int nRet = -1;
    6598         938 :     for (int nQuality = 0; nQuality <= 100 && nRet < 0; ++nQuality)
    6599             :     {
    6600         904 :         VSILFILE *fpTmp = nullptr;
    6601         904 :         if (nQuality == 0)
    6602             :             papszLocalParameters =
    6603          34 :                 CSLSetNameValue(papszLocalParameters, "JPEG_QUALITY", "75");
    6604             :         else
    6605             :             papszLocalParameters =
    6606         870 :                 CSLSetNameValue(papszLocalParameters, "JPEG_QUALITY",
    6607             :                                 CPLSPrintf("%d", nQuality));
    6608             : 
    6609         904 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    6610         904 :         CPLString osTmp;
    6611             :         bool bTileInterleaving;
    6612        1808 :         TIFF *hTIFFTmp = CreateLL(
    6613         904 :             osTmpFilenameIn, 16, 16, (nBands <= 4) ? nBands : 1,
    6614             :             GetRasterBand(1)->GetRasterDataType(), 0.0, 0, papszLocalParameters,
    6615             :             &fpTmp, osTmp, /* bCreateCopy=*/false, bTileInterleaving);
    6616         904 :         CPLPopErrorHandler();
    6617         904 :         if (!hTIFFTmp)
    6618             :         {
    6619           0 :             break;
    6620             :         }
    6621             : 
    6622         904 :         TIFFWriteCheck(hTIFFTmp, FALSE, "CreateLL");
    6623         904 :         TIFFWriteDirectory(hTIFFTmp);
    6624         904 :         TIFFSetDirectory(hTIFFTmp, 0);
    6625             :         // Now reset jpegcolormode.
    6626        1196 :         if (m_nPhotometric == PHOTOMETRIC_YCBCR &&
    6627         292 :             CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
    6628             :         {
    6629         292 :             TIFFSetField(hTIFFTmp, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    6630             :         }
    6631             : 
    6632         904 :         GByte abyZeroData[(16 * 16 * 4 * 3) / 2] = {};
    6633         904 :         const int nBlockSize =
    6634         904 :             (16 * 16 * ((nBands <= 4) ? nBands : 1) * m_nBitsPerSample) / 8;
    6635         904 :         TIFFWriteEncodedStrip(hTIFFTmp, 0, abyZeroData, nBlockSize);
    6636             : 
    6637         904 :         uint32_t nJPEGTableSizeTry = 0;
    6638         904 :         void *pJPEGTableTry = nullptr;
    6639         904 :         if (TIFFGetField(hTIFFTmp, TIFFTAG_JPEGTABLES, &nJPEGTableSizeTry,
    6640         904 :                          &pJPEGTableTry))
    6641             :         {
    6642         904 :             if (GTIFFQuantizationTablesEqual(
    6643             :                     static_cast<GByte *>(pJPEGTable), nJPEGTableSize,
    6644             :                     static_cast<GByte *>(pJPEGTableTry), nJPEGTableSizeTry))
    6645             :             {
    6646          34 :                 nRet = (nQuality == 0) ? 75 : nQuality;
    6647             :             }
    6648             :         }
    6649             : 
    6650         904 :         XTIFFClose(hTIFFTmp);
    6651         904 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpTmp));
    6652             :     }
    6653             : 
    6654          34 :     CSLDestroy(papszLocalParameters);
    6655          34 :     VSIUnlink(osTmpFilenameIn);
    6656             : 
    6657          34 :     return nRet;
    6658             : }
    6659             : 
    6660             : /************************************************************************/
    6661             : /*               SetJPEGQualityAndTablesModeFromFile()                  */
    6662             : /************************************************************************/
    6663             : 
    6664         161 : void GTiffDataset::SetJPEGQualityAndTablesModeFromFile(
    6665             :     int nQuality, bool bHasQuantizationTable, bool bHasHuffmanTable)
    6666             : {
    6667         161 :     if (nQuality > 0)
    6668             :     {
    6669         154 :         CPLDebug("GTiff", "Guessed JPEG quality to be %d", nQuality);
    6670         154 :         m_nJpegQuality = static_cast<signed char>(nQuality);
    6671         154 :         TIFFSetField(m_hTIFF, TIFFTAG_JPEGQUALITY, nQuality);
    6672             : 
    6673             :         // This means we will use the quantization tables from the
    6674             :         // JpegTables tag.
    6675         154 :         m_nJpegTablesMode = JPEGTABLESMODE_QUANT;
    6676             :     }
    6677             :     else
    6678             :     {
    6679           7 :         uint32_t nJPEGTableSize = 0;
    6680           7 :         void *pJPEGTable = nullptr;
    6681           7 :         if (!TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize,
    6682             :                           &pJPEGTable))
    6683             :         {
    6684           4 :             toff_t *panByteCounts = nullptr;
    6685           8 :             const int nBlockCount = m_nPlanarConfig == PLANARCONFIG_SEPARATE
    6686           4 :                                         ? m_nBlocksPerBand * nBands
    6687             :                                         : m_nBlocksPerBand;
    6688           4 :             if (TIFFIsTiled(m_hTIFF))
    6689           1 :                 TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts);
    6690             :             else
    6691           3 :                 TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
    6692             : 
    6693           4 :             bool bFoundNonEmptyBlock = false;
    6694           4 :             if (panByteCounts != nullptr)
    6695             :             {
    6696          56 :                 for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
    6697             :                 {
    6698          53 :                     if (panByteCounts[iBlock] != 0)
    6699             :                     {
    6700           1 :                         bFoundNonEmptyBlock = true;
    6701           1 :                         break;
    6702             :                     }
    6703             :                 }
    6704             :             }
    6705           4 :             if (bFoundNonEmptyBlock)
    6706             :             {
    6707           1 :                 CPLDebug("GTiff", "Could not guess JPEG quality. "
    6708             :                                   "JPEG tables are missing, so going in "
    6709             :                                   "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6710             :                 // Write quantization tables in each strile.
    6711           1 :                 m_nJpegTablesMode = 0;
    6712             :             }
    6713             :         }
    6714             :         else
    6715             :         {
    6716           3 :             if (bHasQuantizationTable)
    6717             :             {
    6718             :                 // FIXME in libtiff: this is likely going to cause issues
    6719             :                 // since libtiff will reuse in each strile the number of
    6720             :                 // the global quantization table, which is invalid.
    6721           1 :                 CPLDebug("GTiff",
    6722             :                          "Could not guess JPEG quality although JPEG "
    6723             :                          "quantization tables are present, so going in "
    6724             :                          "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6725             :             }
    6726             :             else
    6727             :             {
    6728           2 :                 CPLDebug("GTiff",
    6729             :                          "Could not guess JPEG quality since JPEG "
    6730             :                          "quantization tables are not present, so going in "
    6731             :                          "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
    6732             :             }
    6733             : 
    6734             :             // Write quantization tables in each strile.
    6735           3 :             m_nJpegTablesMode = 0;
    6736             :         }
    6737             :     }
    6738         161 :     if (bHasHuffmanTable)
    6739             :     {
    6740             :         // If there are Huffman tables in header use them, otherwise
    6741             :         // if we use optimized tables, libtiff will currently reuse
    6742             :         // the number of the Huffman tables of the header for the
    6743             :         // optimized version of each strile, which is illegal.
    6744          23 :         m_nJpegTablesMode |= JPEGTABLESMODE_HUFF;
    6745             :     }
    6746         161 :     if (m_nJpegTablesMode >= 0)
    6747         159 :         TIFFSetField(m_hTIFF, TIFFTAG_JPEGTABLESMODE, m_nJpegTablesMode);
    6748         161 : }
    6749             : 
    6750             : /************************************************************************/
    6751             : /*                               Create()                               */
    6752             : /*                                                                      */
    6753             : /*      Create a new GeoTIFF or TIFF file.                              */
    6754             : /************************************************************************/
    6755             : 
    6756        5650 : GDALDataset *GTiffDataset::Create(const char *pszFilename, int nXSize,
    6757             :                                   int nYSize, int l_nBands, GDALDataType eType,
    6758             :                                   char **papszParamList)
    6759             : 
    6760             : {
    6761        5650 :     VSILFILE *l_fpL = nullptr;
    6762       11300 :     CPLString l_osTmpFilename;
    6763             : 
    6764             :     const int nColorTableMultiplier = std::max(
    6765       11300 :         1,
    6766       11300 :         std::min(257,
    6767        5650 :                  atoi(CSLFetchNameValueDef(
    6768             :                      papszParamList, "COLOR_TABLE_MULTIPLIER",
    6769        5650 :                      CPLSPrintf("%d", DEFAULT_COLOR_TABLE_MULTIPLIER_257)))));
    6770             : 
    6771             :     /* -------------------------------------------------------------------- */
    6772             :     /*      Create the underlying TIFF file.                                */
    6773             :     /* -------------------------------------------------------------------- */
    6774             :     bool bTileInterleaving;
    6775             :     TIFF *l_hTIFF =
    6776        5650 :         CreateLL(pszFilename, nXSize, nYSize, l_nBands, eType, 0,
    6777             :                  nColorTableMultiplier, papszParamList, &l_fpL, l_osTmpFilename,
    6778             :                  /* bCreateCopy=*/false, bTileInterleaving);
    6779        5650 :     const bool bStreaming = !l_osTmpFilename.empty();
    6780             : 
    6781        5650 :     if (l_hTIFF == nullptr)
    6782          38 :         return nullptr;
    6783             : 
    6784             :     /* -------------------------------------------------------------------- */
    6785             :     /*      Create the new GTiffDataset object.                             */
    6786             :     /* -------------------------------------------------------------------- */
    6787       11224 :     auto poDS = std::make_unique<GTiffDataset>();
    6788        5612 :     poDS->m_hTIFF = l_hTIFF;
    6789        5612 :     poDS->m_fpL = l_fpL;
    6790        5612 :     const bool bSuppressASAP = CPLTestBool(
    6791             :         CSLFetchNameValueDef(papszParamList, "@SUPPRESS_ASAP", "NO"));
    6792        5612 :     if (bSuppressASAP)
    6793          34 :         poDS->MarkSuppressOnClose();
    6794        5612 :     if (bStreaming)
    6795             :     {
    6796           4 :         poDS->m_bStreamingOut = true;
    6797           4 :         poDS->m_pszTmpFilename = CPLStrdup(l_osTmpFilename);
    6798           4 :         poDS->m_fpToWrite = VSIFOpenL(pszFilename, "wb");
    6799           4 :         if (poDS->m_fpToWrite == nullptr)
    6800             :         {
    6801           1 :             VSIUnlink(l_osTmpFilename);
    6802           1 :             return nullptr;
    6803             :         }
    6804             :     }
    6805        5611 :     poDS->nRasterXSize = nXSize;
    6806        5611 :     poDS->nRasterYSize = nYSize;
    6807        5611 :     poDS->eAccess = GA_Update;
    6808             : 
    6809        5611 :     poDS->m_nColorTableMultiplier = nColorTableMultiplier;
    6810             : 
    6811        5611 :     poDS->m_bCrystalized = false;
    6812        5611 :     poDS->m_nSamplesPerPixel = static_cast<uint16_t>(l_nBands);
    6813        5611 :     poDS->m_osFilename = pszFilename;
    6814             : 
    6815             :     // Don't try to load external metadata files (#6597).
    6816        5611 :     poDS->m_bIMDRPCMetadataLoaded = true;
    6817             : 
    6818             :     // Avoid premature crystalization that will cause directory re-writing if
    6819             :     // GetProjectionRef() or GetGeoTransform() are called on the newly created
    6820             :     // GeoTIFF.
    6821        5611 :     poDS->m_bLookedForProjection = true;
    6822             : 
    6823        5611 :     TIFFGetField(l_hTIFF, TIFFTAG_SAMPLEFORMAT, &(poDS->m_nSampleFormat));
    6824        5611 :     TIFFGetField(l_hTIFF, TIFFTAG_PLANARCONFIG, &(poDS->m_nPlanarConfig));
    6825             :     // Weird that we need this, but otherwise we get a Valgrind warning on
    6826             :     // tiff_write_124.
    6827        5611 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &(poDS->m_nPhotometric)))
    6828           1 :         poDS->m_nPhotometric = PHOTOMETRIC_MINISBLACK;
    6829        5611 :     TIFFGetField(l_hTIFF, TIFFTAG_BITSPERSAMPLE, &(poDS->m_nBitsPerSample));
    6830        5611 :     TIFFGetField(l_hTIFF, TIFFTAG_COMPRESSION, &(poDS->m_nCompression));
    6831             : 
    6832        5611 :     if (TIFFIsTiled(l_hTIFF))
    6833             :     {
    6834         393 :         TIFFGetField(l_hTIFF, TIFFTAG_TILEWIDTH, &(poDS->m_nBlockXSize));
    6835         393 :         TIFFGetField(l_hTIFF, TIFFTAG_TILELENGTH, &(poDS->m_nBlockYSize));
    6836             :     }
    6837             :     else
    6838             :     {
    6839        5218 :         if (!TIFFGetField(l_hTIFF, TIFFTAG_ROWSPERSTRIP,
    6840        5218 :                           &(poDS->m_nRowsPerStrip)))
    6841           0 :             poDS->m_nRowsPerStrip = 1;  // Dummy value.
    6842             : 
    6843        5218 :         poDS->m_nBlockXSize = nXSize;
    6844       10436 :         poDS->m_nBlockYSize =
    6845        5218 :             std::min(static_cast<int>(poDS->m_nRowsPerStrip), nYSize);
    6846             :     }
    6847             : 
    6848        5611 :     if (!poDS->ComputeBlocksPerColRowAndBand(l_nBands))
    6849             :     {
    6850           0 :         poDS->m_fpL->CancelCreation();
    6851           0 :         return nullptr;
    6852             :     }
    6853             : 
    6854        5611 :     poDS->m_eProfile = GetProfile(CSLFetchNameValue(papszParamList, "PROFILE"));
    6855             : 
    6856             :     /* -------------------------------------------------------------------- */
    6857             :     /*      YCbCr JPEG compressed images should be translated on the fly    */
    6858             :     /*      to RGB by libtiff/libjpeg unless specifically requested         */
    6859             :     /*      otherwise.                                                      */
    6860             :     /* -------------------------------------------------------------------- */
    6861        5611 :     if (poDS->m_nCompression == COMPRESSION_JPEG &&
    6862        5632 :         poDS->m_nPhotometric == PHOTOMETRIC_YCBCR &&
    6863          21 :         CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
    6864             :     {
    6865          21 :         int nColorMode = 0;
    6866             : 
    6867          21 :         poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr", "IMAGE_STRUCTURE");
    6868          42 :         if (!TIFFGetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode) ||
    6869          21 :             nColorMode != JPEGCOLORMODE_RGB)
    6870          21 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    6871             :     }
    6872             : 
    6873        5611 :     if (poDS->m_nCompression == COMPRESSION_LERC)
    6874             :     {
    6875          26 :         uint32_t nLercParamCount = 0;
    6876          26 :         uint32_t *panLercParams = nullptr;
    6877          26 :         if (TIFFGetField(l_hTIFF, TIFFTAG_LERC_PARAMETERS, &nLercParamCount,
    6878          52 :                          &panLercParams) &&
    6879          26 :             nLercParamCount == 2)
    6880             :         {
    6881          26 :             memcpy(poDS->m_anLercAddCompressionAndVersion, panLercParams,
    6882             :                    sizeof(poDS->m_anLercAddCompressionAndVersion));
    6883             :         }
    6884             :     }
    6885             : 
    6886             :     /* -------------------------------------------------------------------- */
    6887             :     /*      Read palette back as a color table if it has one.               */
    6888             :     /* -------------------------------------------------------------------- */
    6889        5611 :     unsigned short *panRed = nullptr;
    6890        5611 :     unsigned short *panGreen = nullptr;
    6891        5611 :     unsigned short *panBlue = nullptr;
    6892             : 
    6893        5615 :     if (poDS->m_nPhotometric == PHOTOMETRIC_PALETTE &&
    6894           4 :         TIFFGetField(l_hTIFF, TIFFTAG_COLORMAP, &panRed, &panGreen, &panBlue))
    6895             :     {
    6896             : 
    6897           4 :         poDS->m_poColorTable = std::make_unique<GDALColorTable>();
    6898             : 
    6899           4 :         const int nColorCount = 1 << poDS->m_nBitsPerSample;
    6900             : 
    6901        1028 :         for (int iColor = nColorCount - 1; iColor >= 0; iColor--)
    6902             :         {
    6903        1024 :             const GDALColorEntry oEntry = {
    6904        1024 :                 static_cast<short>(panRed[iColor] / nColorTableMultiplier),
    6905        1024 :                 static_cast<short>(panGreen[iColor] / nColorTableMultiplier),
    6906        1024 :                 static_cast<short>(panBlue[iColor] / nColorTableMultiplier),
    6907        1024 :                 static_cast<short>(255)};
    6908             : 
    6909        1024 :             poDS->m_poColorTable->SetColorEntry(iColor, &oEntry);
    6910             :         }
    6911             :     }
    6912             : 
    6913             :     /* -------------------------------------------------------------------- */
    6914             :     /*      Do we want to ensure all blocks get written out on close to     */
    6915             :     /*      avoid sparse files?                                             */
    6916             :     /* -------------------------------------------------------------------- */
    6917        5611 :     if (!CPLFetchBool(papszParamList, "SPARSE_OK", false))
    6918        5501 :         poDS->m_bFillEmptyTilesAtClosing = true;
    6919             : 
    6920        5611 :     poDS->m_bWriteEmptyTiles =
    6921        6410 :         bStreaming || (poDS->m_nCompression != COMPRESSION_NONE &&
    6922         799 :                        poDS->m_bFillEmptyTilesAtClosing);
    6923             :     // Only required for people writing non-compressed striped files in the
    6924             :     // right order and wanting all tstrips to be written in the same order
    6925             :     // so that the end result can be memory mapped without knowledge of each
    6926             :     // strip offset.
    6927        5611 :     if (CPLTestBool(CSLFetchNameValueDef(
    6928       11222 :             papszParamList, "WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")) ||
    6929        5611 :         CPLTestBool(CSLFetchNameValueDef(
    6930             :             papszParamList, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")))
    6931             :     {
    6932          26 :         poDS->m_bWriteEmptyTiles = true;
    6933             :     }
    6934             : 
    6935             :     /* -------------------------------------------------------------------- */
    6936             :     /*      Preserve creation options for consulting later (for instance    */
    6937             :     /*      to decide if a TFW file should be written).                     */
    6938             :     /* -------------------------------------------------------------------- */
    6939        5611 :     poDS->m_papszCreationOptions = CSLDuplicate(papszParamList);
    6940             : 
    6941        5611 :     poDS->m_nZLevel = GTiffGetZLevel(papszParamList);
    6942        5611 :     poDS->m_nLZMAPreset = GTiffGetLZMAPreset(papszParamList);
    6943        5611 :     poDS->m_nZSTDLevel = GTiffGetZSTDPreset(papszParamList);
    6944        5611 :     poDS->m_nWebPLevel = GTiffGetWebPLevel(papszParamList);
    6945        5611 :     poDS->m_bWebPLossless = GTiffGetWebPLossless(papszParamList);
    6946        5613 :     if (poDS->m_nWebPLevel != 100 && poDS->m_bWebPLossless &&
    6947           2 :         CSLFetchNameValue(papszParamList, "WEBP_LEVEL"))
    6948             :     {
    6949           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    6950             :                  "WEBP_LEVEL is specified, but WEBP_LOSSLESS=YES. "
    6951             :                  "WEBP_LEVEL will be ignored.");
    6952             :     }
    6953        5611 :     poDS->m_nJpegQuality = GTiffGetJpegQuality(papszParamList);
    6954        5611 :     poDS->m_nJpegTablesMode = GTiffGetJpegTablesMode(papszParamList);
    6955        5611 :     poDS->m_dfMaxZError = GTiffGetLERCMaxZError(papszParamList);
    6956        5611 :     poDS->m_dfMaxZErrorOverview = GTiffGetLERCMaxZErrorOverview(papszParamList);
    6957             : #if HAVE_JXL
    6958        5611 :     poDS->m_bJXLLossless = GTiffGetJXLLossless(papszParamList);
    6959        5611 :     poDS->m_nJXLEffort = GTiffGetJXLEffort(papszParamList);
    6960        5611 :     poDS->m_fJXLDistance = GTiffGetJXLDistance(papszParamList);
    6961        5611 :     poDS->m_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszParamList);
    6962             : #endif
    6963        5611 :     poDS->InitCreationOrOpenOptions(true, papszParamList);
    6964             : 
    6965             :     /* -------------------------------------------------------------------- */
    6966             :     /*      Create band information objects.                                */
    6967             :     /* -------------------------------------------------------------------- */
    6968      308034 :     for (int iBand = 0; iBand < l_nBands; ++iBand)
    6969             :     {
    6970      371543 :         if (poDS->m_nBitsPerSample == 8 || poDS->m_nBitsPerSample == 16 ||
    6971      371784 :             poDS->m_nBitsPerSample == 32 || poDS->m_nBitsPerSample == 64 ||
    6972         241 :             poDS->m_nBitsPerSample == 128)
    6973             :         {
    6974      604716 :             poDS->SetBand(iBand + 1, std::make_unique<GTiffRasterBand>(
    6975      604716 :                                          poDS.get(), iBand + 1));
    6976             :         }
    6977             :         else
    6978             :         {
    6979         130 :             poDS->SetBand(iBand + 1, std::make_unique<GTiffOddBitsBand>(
    6980          65 :                                          poDS.get(), iBand + 1));
    6981         130 :             poDS->GetRasterBand(iBand + 1)->SetMetadataItem(
    6982         130 :                 "NBITS", CPLString().Printf("%d", poDS->m_nBitsPerSample),
    6983          65 :                 "IMAGE_STRUCTURE");
    6984             :         }
    6985             :     }
    6986             : 
    6987        5611 :     poDS->GetDiscardLsbOption(papszParamList);
    6988             : 
    6989        5611 :     if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG && l_nBands != 1)
    6990         823 :         poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
    6991             :     else
    6992        4788 :         poDS->SetMetadataItem("INTERLEAVE", "BAND", "IMAGE_STRUCTURE");
    6993             : 
    6994        5611 :     poDS->oOvManager.Initialize(poDS.get(), pszFilename);
    6995             : 
    6996        5611 :     return poDS.release();
    6997             : }
    6998             : 
    6999             : /************************************************************************/
    7000             : /*                           CopyImageryAndMask()                       */
    7001             : /************************************************************************/
    7002             : 
    7003         344 : CPLErr GTiffDataset::CopyImageryAndMask(GTiffDataset *poDstDS,
    7004             :                                         GDALDataset *poSrcDS,
    7005             :                                         GDALRasterBand *poSrcMaskBand,
    7006             :                                         GDALProgressFunc pfnProgress,
    7007             :                                         void *pProgressData)
    7008             : {
    7009         344 :     CPLErr eErr = CE_None;
    7010             : 
    7011         344 :     const auto eType = poDstDS->GetRasterBand(1)->GetRasterDataType();
    7012         344 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
    7013         344 :     const int l_nBands = poDstDS->GetRasterCount();
    7014             :     GByte *pBlockBuffer = static_cast<GByte *>(
    7015         344 :         VSI_MALLOC3_VERBOSE(poDstDS->m_nBlockXSize, poDstDS->m_nBlockYSize,
    7016             :                             cpl::fits_on<int>(l_nBands * nDataTypeSize)));
    7017         344 :     if (pBlockBuffer == nullptr)
    7018             :     {
    7019           0 :         eErr = CE_Failure;
    7020             :     }
    7021         344 :     const int nYSize = poDstDS->nRasterYSize;
    7022         344 :     const int nXSize = poDstDS->nRasterXSize;
    7023             :     const bool bIsOddBand =
    7024         344 :         dynamic_cast<GTiffOddBitsBand *>(poDstDS->GetRasterBand(1)) != nullptr;
    7025             : 
    7026         344 :     if (poDstDS->m_poMaskDS)
    7027             :     {
    7028          59 :         CPLAssert(poDstDS->m_poMaskDS->m_nBlockXSize == poDstDS->m_nBlockXSize);
    7029          59 :         CPLAssert(poDstDS->m_poMaskDS->m_nBlockYSize == poDstDS->m_nBlockYSize);
    7030             :     }
    7031             : 
    7032         344 :     if (poDstDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE &&
    7033          58 :         !poDstDS->m_bTileInterleave)
    7034             :     {
    7035          45 :         int iBlock = 0;
    7036          45 :         const int nBlocks = poDstDS->m_nBlocksPerBand *
    7037          45 :                             (l_nBands + (poDstDS->m_poMaskDS ? 1 : 0));
    7038         195 :         for (int i = 0; eErr == CE_None && i < l_nBands; i++)
    7039             :         {
    7040         345 :             for (int iY = 0, nYBlock = 0; iY < nYSize && eErr == CE_None;
    7041         195 :                  iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
    7042         195 :                            ? nYSize
    7043          59 :                            : iY + poDstDS->m_nBlockYSize),
    7044             :                      nYBlock++)
    7045             :             {
    7046             :                 const int nReqYSize =
    7047         195 :                     std::min(nYSize - iY, poDstDS->m_nBlockYSize);
    7048         495 :                 for (int iX = 0, nXBlock = 0; iX < nXSize && eErr == CE_None;
    7049         300 :                      iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
    7050         300 :                                ? nXSize
    7051         155 :                                : iX + poDstDS->m_nBlockXSize),
    7052             :                          nXBlock++)
    7053             :                 {
    7054             :                     const int nReqXSize =
    7055         300 :                         std::min(nXSize - iX, poDstDS->m_nBlockXSize);
    7056         300 :                     if (nReqXSize < poDstDS->m_nBlockXSize ||
    7057         155 :                         nReqYSize < poDstDS->m_nBlockYSize)
    7058             :                     {
    7059         190 :                         memset(pBlockBuffer, 0,
    7060         190 :                                static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7061         190 :                                    poDstDS->m_nBlockYSize * nDataTypeSize);
    7062             :                     }
    7063         300 :                     eErr = poSrcDS->GetRasterBand(i + 1)->RasterIO(
    7064             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7065             :                         nReqXSize, nReqYSize, eType, nDataTypeSize,
    7066         300 :                         static_cast<GSpacing>(nDataTypeSize) *
    7067         300 :                             poDstDS->m_nBlockXSize,
    7068             :                         nullptr);
    7069         300 :                     if (eErr == CE_None)
    7070             :                     {
    7071         300 :                         eErr = poDstDS->WriteEncodedTileOrStrip(
    7072             :                             iBlock, pBlockBuffer, false);
    7073             :                     }
    7074             : 
    7075         300 :                     iBlock++;
    7076         600 :                     if (pfnProgress &&
    7077         300 :                         !pfnProgress(static_cast<double>(iBlock) / nBlocks,
    7078             :                                      nullptr, pProgressData))
    7079             :                     {
    7080           0 :                         eErr = CE_Failure;
    7081             :                     }
    7082             : 
    7083         300 :                     if (poDstDS->m_bWriteError)
    7084           0 :                         eErr = CE_Failure;
    7085             :                 }
    7086             :             }
    7087             :         }
    7088          45 :         if (poDstDS->m_poMaskDS && eErr == CE_None)
    7089             :         {
    7090           6 :             int iBlockMask = 0;
    7091          17 :             for (int iY = 0, nYBlock = 0; iY < nYSize && eErr == CE_None;
    7092          11 :                  iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
    7093          11 :                            ? nYSize
    7094           5 :                            : iY + poDstDS->m_nBlockYSize),
    7095             :                      nYBlock++)
    7096             :             {
    7097             :                 const int nReqYSize =
    7098          11 :                     std::min(nYSize - iY, poDstDS->m_nBlockYSize);
    7099          49 :                 for (int iX = 0, nXBlock = 0; iX < nXSize && eErr == CE_None;
    7100          38 :                      iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
    7101          38 :                                ? nXSize
    7102          30 :                                : iX + poDstDS->m_nBlockXSize),
    7103             :                          nXBlock++)
    7104             :                 {
    7105             :                     const int nReqXSize =
    7106          38 :                         std::min(nXSize - iX, poDstDS->m_nBlockXSize);
    7107          38 :                     if (nReqXSize < poDstDS->m_nBlockXSize ||
    7108          30 :                         nReqYSize < poDstDS->m_nBlockYSize)
    7109             :                     {
    7110          16 :                         memset(pBlockBuffer, 0,
    7111          16 :                                static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7112          16 :                                    poDstDS->m_nBlockYSize);
    7113             :                     }
    7114          76 :                     eErr = poSrcMaskBand->RasterIO(
    7115             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7116             :                         nReqXSize, nReqYSize, GDT_UInt8, 1,
    7117          38 :                         poDstDS->m_nBlockXSize, nullptr);
    7118          38 :                     if (eErr == CE_None)
    7119             :                     {
    7120             :                         // Avoid any attempt to load from disk
    7121          38 :                         poDstDS->m_poMaskDS->m_nLoadedBlock = iBlockMask;
    7122             :                         eErr =
    7123          38 :                             poDstDS->m_poMaskDS->GetRasterBand(1)->WriteBlock(
    7124             :                                 nXBlock, nYBlock, pBlockBuffer);
    7125          38 :                         if (eErr == CE_None)
    7126          38 :                             eErr = poDstDS->m_poMaskDS->FlushBlockBuf();
    7127             :                     }
    7128             : 
    7129          38 :                     iBlockMask++;
    7130          76 :                     if (pfnProgress &&
    7131          38 :                         !pfnProgress(static_cast<double>(iBlock + iBlockMask) /
    7132             :                                          nBlocks,
    7133             :                                      nullptr, pProgressData))
    7134             :                     {
    7135           0 :                         eErr = CE_Failure;
    7136             :                     }
    7137             : 
    7138          38 :                     if (poDstDS->m_poMaskDS->m_bWriteError)
    7139           0 :                         eErr = CE_Failure;
    7140             :                 }
    7141             :             }
    7142          45 :         }
    7143             :     }
    7144             :     else
    7145             :     {
    7146         299 :         int iBlock = 0;
    7147         299 :         const int nBlocks = poDstDS->m_nBlocksPerBand;
    7148        7092 :         for (int iY = 0, nYBlock = 0; iY < nYSize && eErr == CE_None;
    7149        6793 :              iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
    7150        6793 :                        ? nYSize
    7151        6569 :                        : iY + poDstDS->m_nBlockYSize),
    7152             :                  nYBlock++)
    7153             :         {
    7154        6793 :             const int nReqYSize = std::min(nYSize - iY, poDstDS->m_nBlockYSize);
    7155       26509 :             for (int iX = 0, nXBlock = 0; iX < nXSize && eErr == CE_None;
    7156       19716 :                  iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
    7157       19716 :                            ? nXSize
    7158       19420 :                            : iX + poDstDS->m_nBlockXSize),
    7159             :                      nXBlock++)
    7160             :             {
    7161             :                 const int nReqXSize =
    7162       19716 :                     std::min(nXSize - iX, poDstDS->m_nBlockXSize);
    7163       19716 :                 if (nReqXSize < poDstDS->m_nBlockXSize ||
    7164       19420 :                     nReqYSize < poDstDS->m_nBlockYSize)
    7165             :                 {
    7166         465 :                     memset(pBlockBuffer, 0,
    7167         465 :                            static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7168         465 :                                poDstDS->m_nBlockYSize * l_nBands *
    7169         465 :                                nDataTypeSize);
    7170             :                 }
    7171             : 
    7172       19716 :                 if (poDstDS->m_bTileInterleave)
    7173             :                 {
    7174         114 :                     eErr = poSrcDS->RasterIO(
    7175             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7176             :                         nReqXSize, nReqYSize, eType, l_nBands, nullptr,
    7177             :                         nDataTypeSize,
    7178          57 :                         static_cast<GSpacing>(nDataTypeSize) *
    7179          57 :                             poDstDS->m_nBlockXSize,
    7180          57 :                         static_cast<GSpacing>(nDataTypeSize) *
    7181          57 :                             poDstDS->m_nBlockXSize * poDstDS->m_nBlockYSize,
    7182             :                         nullptr);
    7183          57 :                     if (eErr == CE_None)
    7184             :                     {
    7185         228 :                         for (int i = 0; eErr == CE_None && i < l_nBands; i++)
    7186             :                         {
    7187         171 :                             eErr = poDstDS->WriteEncodedTileOrStrip(
    7188         171 :                                 iBlock + i * poDstDS->m_nBlocksPerBand,
    7189         171 :                                 pBlockBuffer + static_cast<size_t>(i) *
    7190         171 :                                                    poDstDS->m_nBlockXSize *
    7191         171 :                                                    poDstDS->m_nBlockYSize *
    7192         171 :                                                    nDataTypeSize,
    7193             :                                 false);
    7194             :                         }
    7195             :                     }
    7196             :                 }
    7197       19659 :                 else if (!bIsOddBand)
    7198             :                 {
    7199       39196 :                     eErr = poSrcDS->RasterIO(
    7200             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7201             :                         nReqXSize, nReqYSize, eType, l_nBands, nullptr,
    7202       19598 :                         static_cast<GSpacing>(nDataTypeSize) * l_nBands,
    7203       19598 :                         static_cast<GSpacing>(nDataTypeSize) * l_nBands *
    7204       19598 :                             poDstDS->m_nBlockXSize,
    7205             :                         nDataTypeSize, nullptr);
    7206       19598 :                     if (eErr == CE_None)
    7207             :                     {
    7208       19597 :                         eErr = poDstDS->WriteEncodedTileOrStrip(
    7209             :                             iBlock, pBlockBuffer, false);
    7210             :                     }
    7211             :                 }
    7212             :                 else
    7213             :                 {
    7214             :                     // In the odd bit case, this is a bit messy to ensure
    7215             :                     // the strile gets written synchronously.
    7216             :                     // We load the content of the n-1 bands in the cache,
    7217             :                     // and for the last band we invoke WriteBlock() directly
    7218             :                     // We also force FlushBlockBuf()
    7219         122 :                     std::vector<GDALRasterBlock *> apoLockedBlocks;
    7220          91 :                     for (int i = 0; eErr == CE_None && i < l_nBands - 1; i++)
    7221             :                     {
    7222             :                         auto poBlock =
    7223          30 :                             poDstDS->GetRasterBand(i + 1)->GetLockedBlockRef(
    7224          30 :                                 nXBlock, nYBlock, TRUE);
    7225          30 :                         if (poBlock)
    7226             :                         {
    7227          60 :                             eErr = poSrcDS->GetRasterBand(i + 1)->RasterIO(
    7228             :                                 GF_Read, iX, iY, nReqXSize, nReqYSize,
    7229             :                                 poBlock->GetDataRef(), nReqXSize, nReqYSize,
    7230             :                                 eType, nDataTypeSize,
    7231          30 :                                 static_cast<GSpacing>(nDataTypeSize) *
    7232          30 :                                     poDstDS->m_nBlockXSize,
    7233             :                                 nullptr);
    7234          30 :                             poBlock->MarkDirty();
    7235          30 :                             apoLockedBlocks.emplace_back(poBlock);
    7236             :                         }
    7237             :                         else
    7238             :                         {
    7239           0 :                             eErr = CE_Failure;
    7240             :                         }
    7241             :                     }
    7242          61 :                     if (eErr == CE_None)
    7243             :                     {
    7244         122 :                         eErr = poSrcDS->GetRasterBand(l_nBands)->RasterIO(
    7245             :                             GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7246             :                             nReqXSize, nReqYSize, eType, nDataTypeSize,
    7247          61 :                             static_cast<GSpacing>(nDataTypeSize) *
    7248          61 :                                 poDstDS->m_nBlockXSize,
    7249             :                             nullptr);
    7250             :                     }
    7251          61 :                     if (eErr == CE_None)
    7252             :                     {
    7253             :                         // Avoid any attempt to load from disk
    7254          61 :                         poDstDS->m_nLoadedBlock = iBlock;
    7255          61 :                         eErr = poDstDS->GetRasterBand(l_nBands)->WriteBlock(
    7256             :                             nXBlock, nYBlock, pBlockBuffer);
    7257          61 :                         if (eErr == CE_None)
    7258          61 :                             eErr = poDstDS->FlushBlockBuf();
    7259             :                     }
    7260          91 :                     for (auto poBlock : apoLockedBlocks)
    7261             :                     {
    7262          30 :                         poBlock->MarkClean();
    7263          30 :                         poBlock->DropLock();
    7264             :                     }
    7265             :                 }
    7266             : 
    7267       19716 :                 if (eErr == CE_None && poDstDS->m_poMaskDS)
    7268             :                 {
    7269        4664 :                     if (nReqXSize < poDstDS->m_nBlockXSize ||
    7270        4621 :                         nReqYSize < poDstDS->m_nBlockYSize)
    7271             :                     {
    7272          81 :                         memset(pBlockBuffer, 0,
    7273          81 :                                static_cast<size_t>(poDstDS->m_nBlockXSize) *
    7274          81 :                                    poDstDS->m_nBlockYSize);
    7275             :                     }
    7276        9328 :                     eErr = poSrcMaskBand->RasterIO(
    7277             :                         GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
    7278             :                         nReqXSize, nReqYSize, GDT_UInt8, 1,
    7279        4664 :                         poDstDS->m_nBlockXSize, nullptr);
    7280        4664 :                     if (eErr == CE_None)
    7281             :                     {
    7282             :                         // Avoid any attempt to load from disk
    7283        4664 :                         poDstDS->m_poMaskDS->m_nLoadedBlock = iBlock;
    7284             :                         eErr =
    7285        4664 :                             poDstDS->m_poMaskDS->GetRasterBand(1)->WriteBlock(
    7286             :                                 nXBlock, nYBlock, pBlockBuffer);
    7287        4664 :                         if (eErr == CE_None)
    7288        4664 :                             eErr = poDstDS->m_poMaskDS->FlushBlockBuf();
    7289             :                     }
    7290             :                 }
    7291       19716 :                 if (poDstDS->m_bWriteError)
    7292           6 :                     eErr = CE_Failure;
    7293             : 
    7294       19716 :                 iBlock++;
    7295       39432 :                 if (pfnProgress &&
    7296       19716 :                     !pfnProgress(static_cast<double>(iBlock) / nBlocks, nullptr,
    7297             :                                  pProgressData))
    7298             :                 {
    7299           0 :                     eErr = CE_Failure;
    7300             :                 }
    7301             :             }
    7302             :         }
    7303             :     }
    7304             : 
    7305         344 :     poDstDS->FlushCache(false);  // mostly to wait for thread completion
    7306         344 :     VSIFree(pBlockBuffer);
    7307             : 
    7308         344 :     return eErr;
    7309             : }
    7310             : 
    7311             : /************************************************************************/
    7312             : /*                             CreateCopy()                             */
    7313             : /************************************************************************/
    7314             : 
    7315        2132 : GDALDataset *GTiffDataset::CreateCopy(const char *pszFilename,
    7316             :                                       GDALDataset *poSrcDS, int bStrict,
    7317             :                                       char **papszOptions,
    7318             :                                       GDALProgressFunc pfnProgress,
    7319             :                                       void *pProgressData)
    7320             : 
    7321             : {
    7322        2132 :     if (poSrcDS->GetRasterCount() == 0)
    7323             :     {
    7324           2 :         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    7325             :                     "Unable to export GeoTIFF files with zero bands.");
    7326           2 :         return nullptr;
    7327             :     }
    7328             : 
    7329        2130 :     GDALRasterBand *const poPBand = poSrcDS->GetRasterBand(1);
    7330        2130 :     GDALDataType eType = poPBand->GetRasterDataType();
    7331             : 
    7332             :     /* -------------------------------------------------------------------- */
    7333             :     /*      Check, whether all bands in input dataset has the same type.    */
    7334             :     /* -------------------------------------------------------------------- */
    7335        2130 :     const int l_nBands = poSrcDS->GetRasterCount();
    7336        5049 :     for (int iBand = 2; iBand <= l_nBands; ++iBand)
    7337             :     {
    7338        2919 :         if (eType != poSrcDS->GetRasterBand(iBand)->GetRasterDataType())
    7339             :         {
    7340           0 :             if (bStrict)
    7341             :             {
    7342           0 :                 ReportError(
    7343             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    7344             :                     "Unable to export GeoTIFF file with different datatypes "
    7345             :                     "per different bands. All bands should have the same "
    7346             :                     "types in TIFF.");
    7347           0 :                 return nullptr;
    7348             :             }
    7349             :             else
    7350             :             {
    7351           0 :                 ReportError(
    7352             :                     pszFilename, CE_Warning, CPLE_AppDefined,
    7353             :                     "Unable to export GeoTIFF file with different datatypes "
    7354             :                     "per different bands. All bands should have the same "
    7355             :                     "types in TIFF.");
    7356             :             }
    7357             :         }
    7358             :     }
    7359             : 
    7360             :     /* -------------------------------------------------------------------- */
    7361             :     /*      Capture the profile.                                            */
    7362             :     /* -------------------------------------------------------------------- */
    7363             :     const GTiffProfile eProfile =
    7364        2130 :         GetProfile(CSLFetchNameValue(papszOptions, "PROFILE"));
    7365             : 
    7366        2130 :     const bool bGeoTIFF = eProfile != GTiffProfile::BASELINE;
    7367             : 
    7368             :     /* -------------------------------------------------------------------- */
    7369             :     /*      Special handling for NBITS.  Copy from band metadata if found.  */
    7370             :     /* -------------------------------------------------------------------- */
    7371        2130 :     char **papszCreateOptions = CSLDuplicate(papszOptions);
    7372             : 
    7373        2130 :     if (poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE") != nullptr &&
    7374        2147 :         atoi(poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE")) > 0 &&
    7375          17 :         CSLFetchNameValue(papszCreateOptions, "NBITS") == nullptr)
    7376             :     {
    7377           3 :         papszCreateOptions = CSLSetNameValue(
    7378             :             papszCreateOptions, "NBITS",
    7379           3 :             poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE"));
    7380             :     }
    7381             : 
    7382        2130 :     if (CSLFetchNameValue(papszOptions, "PIXELTYPE") == nullptr &&
    7383             :         eType == GDT_UInt8)
    7384             :     {
    7385        1769 :         poPBand->EnablePixelTypeSignedByteWarning(false);
    7386             :         const char *pszPixelType =
    7387        1769 :             poPBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
    7388        1769 :         poPBand->EnablePixelTypeSignedByteWarning(true);
    7389        1769 :         if (pszPixelType)
    7390             :         {
    7391           1 :             papszCreateOptions =
    7392           1 :                 CSLSetNameValue(papszCreateOptions, "PIXELTYPE", pszPixelType);
    7393             :         }
    7394             :     }
    7395             : 
    7396             :     /* -------------------------------------------------------------------- */
    7397             :     /*      Color profile.  Copy from band metadata if found.              */
    7398             :     /* -------------------------------------------------------------------- */
    7399        2130 :     if (bGeoTIFF)
    7400             :     {
    7401        2113 :         const char *pszOptionsMD[] = {"SOURCE_ICC_PROFILE",
    7402             :                                       "SOURCE_PRIMARIES_RED",
    7403             :                                       "SOURCE_PRIMARIES_GREEN",
    7404             :                                       "SOURCE_PRIMARIES_BLUE",
    7405             :                                       "SOURCE_WHITEPOINT",
    7406             :                                       "TIFFTAG_TRANSFERFUNCTION_RED",
    7407             :                                       "TIFFTAG_TRANSFERFUNCTION_GREEN",
    7408             :                                       "TIFFTAG_TRANSFERFUNCTION_BLUE",
    7409             :                                       "TIFFTAG_TRANSFERRANGE_BLACK",
    7410             :                                       "TIFFTAG_TRANSFERRANGE_WHITE",
    7411             :                                       nullptr};
    7412             : 
    7413             :         // Copy all the tags.  Options will override tags in the source.
    7414        2113 :         int i = 0;
    7415       23223 :         while (pszOptionsMD[i] != nullptr)
    7416             :         {
    7417             :             char const *pszMD =
    7418       21112 :                 CSLFetchNameValue(papszOptions, pszOptionsMD[i]);
    7419       21112 :             if (pszMD == nullptr)
    7420             :                 pszMD =
    7421       21104 :                     poSrcDS->GetMetadataItem(pszOptionsMD[i], "COLOR_PROFILE");
    7422             : 
    7423       21112 :             if ((pszMD != nullptr) && !EQUAL(pszMD, ""))
    7424             :             {
    7425          16 :                 papszCreateOptions =
    7426          16 :                     CSLSetNameValue(papszCreateOptions, pszOptionsMD[i], pszMD);
    7427             : 
    7428             :                 // If an ICC profile exists, other tags are not needed.
    7429          16 :                 if (EQUAL(pszOptionsMD[i], "SOURCE_ICC_PROFILE"))
    7430           2 :                     break;
    7431             :             }
    7432             : 
    7433       21110 :             ++i;
    7434             :         }
    7435             :     }
    7436             : 
    7437        2130 :     double dfExtraSpaceForOverviews = 0;
    7438             :     const bool bCopySrcOverviews =
    7439        2130 :         CPLFetchBool(papszCreateOptions, "COPY_SRC_OVERVIEWS", false);
    7440        2130 :     std::unique_ptr<GDALDataset> poOvrDS;
    7441        2130 :     int nSrcOverviews = 0;
    7442        2130 :     if (bCopySrcOverviews)
    7443             :     {
    7444             :         const char *pszOvrDS =
    7445         228 :             CSLFetchNameValue(papszCreateOptions, "@OVERVIEW_DATASET");
    7446         228 :         if (pszOvrDS)
    7447             :         {
    7448             :             // Empty string is used by COG driver to indicate that we want
    7449             :             // to ignore source overviews.
    7450          37 :             if (!EQUAL(pszOvrDS, ""))
    7451             :             {
    7452          35 :                 poOvrDS.reset(GDALDataset::Open(pszOvrDS));
    7453          35 :                 if (!poOvrDS)
    7454             :                 {
    7455           0 :                     CSLDestroy(papszCreateOptions);
    7456           0 :                     return nullptr;
    7457             :                 }
    7458          35 :                 if (poOvrDS->GetRasterCount() != l_nBands)
    7459             :                 {
    7460           0 :                     CSLDestroy(papszCreateOptions);
    7461           0 :                     return nullptr;
    7462             :                 }
    7463          35 :                 nSrcOverviews =
    7464          35 :                     poOvrDS->GetRasterBand(1)->GetOverviewCount() + 1;
    7465             :             }
    7466             :         }
    7467             :         else
    7468             :         {
    7469         191 :             nSrcOverviews = poSrcDS->GetRasterBand(1)->GetOverviewCount();
    7470             :         }
    7471             : 
    7472             :         // Limit number of overviews if specified
    7473             :         const char *pszOverviewCount =
    7474         228 :             CSLFetchNameValue(papszCreateOptions, "@OVERVIEW_COUNT");
    7475         228 :         if (pszOverviewCount)
    7476           8 :             nSrcOverviews =
    7477           8 :                 std::max(0, std::min(nSrcOverviews, atoi(pszOverviewCount)));
    7478             : 
    7479         228 :         if (nSrcOverviews)
    7480             :         {
    7481         204 :             for (int j = 1; j <= l_nBands; ++j)
    7482             :             {
    7483             :                 const int nOtherBandOverviewCount =
    7484         134 :                     poOvrDS ? poOvrDS->GetRasterBand(j)->GetOverviewCount() + 1
    7485         198 :                             : poSrcDS->GetRasterBand(j)->GetOverviewCount();
    7486         134 :                 if (nOtherBandOverviewCount < nSrcOverviews)
    7487             :                 {
    7488           1 :                     ReportError(
    7489             :                         pszFilename, CE_Failure, CPLE_NotSupported,
    7490             :                         "COPY_SRC_OVERVIEWS cannot be used when the bands have "
    7491             :                         "not the same number of overview levels.");
    7492           1 :                     CSLDestroy(papszCreateOptions);
    7493           1 :                     return nullptr;
    7494             :                 }
    7495         388 :                 for (int i = 0; i < nSrcOverviews; ++i)
    7496             :                 {
    7497             :                     GDALRasterBand *poOvrBand =
    7498             :                         poOvrDS
    7499         353 :                             ? (i == 0 ? poOvrDS->GetRasterBand(j)
    7500         192 :                                       : poOvrDS->GetRasterBand(j)->GetOverview(
    7501          96 :                                             i - 1))
    7502         348 :                             : poSrcDS->GetRasterBand(j)->GetOverview(i);
    7503         257 :                     if (poOvrBand == nullptr)
    7504             :                     {
    7505           1 :                         ReportError(
    7506             :                             pszFilename, CE_Failure, CPLE_NotSupported,
    7507             :                             "COPY_SRC_OVERVIEWS cannot be used when one "
    7508             :                             "overview band is NULL.");
    7509           1 :                         CSLDestroy(papszCreateOptions);
    7510           1 :                         return nullptr;
    7511             :                     }
    7512             :                     GDALRasterBand *poOvrFirstBand =
    7513             :                         poOvrDS
    7514         352 :                             ? (i == 0 ? poOvrDS->GetRasterBand(1)
    7515         192 :                                       : poOvrDS->GetRasterBand(1)->GetOverview(
    7516          96 :                                             i - 1))
    7517         346 :                             : poSrcDS->GetRasterBand(1)->GetOverview(i);
    7518         511 :                     if (poOvrBand->GetXSize() != poOvrFirstBand->GetXSize() ||
    7519         255 :                         poOvrBand->GetYSize() != poOvrFirstBand->GetYSize())
    7520             :                     {
    7521           1 :                         ReportError(
    7522             :                             pszFilename, CE_Failure, CPLE_NotSupported,
    7523             :                             "COPY_SRC_OVERVIEWS cannot be used when the "
    7524             :                             "overview bands have not the same dimensions "
    7525             :                             "among bands.");
    7526           1 :                         CSLDestroy(papszCreateOptions);
    7527           1 :                         return nullptr;
    7528             :                     }
    7529             :                 }
    7530             :             }
    7531             : 
    7532         198 :             for (int i = 0; i < nSrcOverviews; ++i)
    7533             :             {
    7534             :                 GDALRasterBand *poOvrFirstBand =
    7535             :                     poOvrDS
    7536         201 :                         ? (i == 0
    7537          73 :                                ? poOvrDS->GetRasterBand(1)
    7538          38 :                                : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    7539         183 :                         : poSrcDS->GetRasterBand(1)->GetOverview(i);
    7540         128 :                 dfExtraSpaceForOverviews +=
    7541         128 :                     static_cast<double>(poOvrFirstBand->GetXSize()) *
    7542         128 :                     poOvrFirstBand->GetYSize();
    7543             :             }
    7544          70 :             dfExtraSpaceForOverviews *=
    7545          70 :                 l_nBands * GDALGetDataTypeSizeBytes(eType);
    7546             :         }
    7547             :         else
    7548             :         {
    7549         155 :             CPLDebug("GTiff", "No source overviews to copy");
    7550             :         }
    7551             :     }
    7552             : 
    7553             : /* -------------------------------------------------------------------- */
    7554             : /*      Should we use optimized way of copying from an input JPEG       */
    7555             : /*      dataset?                                                        */
    7556             : /* -------------------------------------------------------------------- */
    7557             : 
    7558             : // TODO(schwehr): Refactor bDirectCopyFromJPEG to be a const.
    7559             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    7560        2127 :     bool bDirectCopyFromJPEG = false;
    7561             : #endif
    7562             : 
    7563             :     // Note: JPEG_DIRECT_COPY is not defined by default, because it is mainly
    7564             :     // useful for debugging purposes.
    7565             : #ifdef JPEG_DIRECT_COPY
    7566             :     if (CPLFetchBool(papszCreateOptions, "JPEG_DIRECT_COPY", false) &&
    7567             :         GTIFF_CanDirectCopyFromJPEG(poSrcDS, papszCreateOptions))
    7568             :     {
    7569             :         CPLDebug("GTiff", "Using special direct copy mode from a JPEG dataset");
    7570             : 
    7571             :         bDirectCopyFromJPEG = true;
    7572             :     }
    7573             : #endif
    7574             : 
    7575             : #ifdef HAVE_LIBJPEG
    7576        2127 :     bool bCopyFromJPEG = false;
    7577             : 
    7578             :     // When CreateCopy'ing() from a JPEG dataset, and asking for COMPRESS=JPEG,
    7579             :     // use DCT coefficients (unless other options are incompatible, like
    7580             :     // strip/tile dimensions, specifying JPEG_QUALITY option, incompatible
    7581             :     // PHOTOMETRIC with the source colorspace, etc.) to avoid the lossy steps
    7582             :     // involved by decompression/recompression.
    7583        4254 :     if (!bDirectCopyFromJPEG &&
    7584        2127 :         GTIFF_CanCopyFromJPEG(poSrcDS, papszCreateOptions))
    7585             :     {
    7586          12 :         CPLDebug("GTiff", "Using special copy mode from a JPEG dataset");
    7587             : 
    7588          12 :         bCopyFromJPEG = true;
    7589             :     }
    7590             : #endif
    7591             : 
    7592             :     /* -------------------------------------------------------------------- */
    7593             :     /*      If the source is RGB, then set the PHOTOMETRIC=RGB value        */
    7594             :     /* -------------------------------------------------------------------- */
    7595             : 
    7596             :     const bool bForcePhotometric =
    7597        2127 :         CSLFetchNameValue(papszOptions, "PHOTOMETRIC") != nullptr;
    7598             : 
    7599        1224 :     if (l_nBands >= 3 && !bForcePhotometric &&
    7600             : #ifdef HAVE_LIBJPEG
    7601        1186 :         !bCopyFromJPEG &&
    7602             : #endif
    7603        1180 :         poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_RedBand &&
    7604        4419 :         poSrcDS->GetRasterBand(2)->GetColorInterpretation() == GCI_GreenBand &&
    7605        1068 :         poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_BlueBand)
    7606             :     {
    7607        1062 :         papszCreateOptions =
    7608        1062 :             CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "RGB");
    7609             :     }
    7610             : 
    7611             :     /* -------------------------------------------------------------------- */
    7612             :     /*      Create the file.                                                */
    7613             :     /* -------------------------------------------------------------------- */
    7614        2127 :     VSILFILE *l_fpL = nullptr;
    7615        4254 :     CPLString l_osTmpFilename;
    7616             : 
    7617        2127 :     const int nXSize = poSrcDS->GetRasterXSize();
    7618        2127 :     const int nYSize = poSrcDS->GetRasterYSize();
    7619             : 
    7620             :     const int nColorTableMultiplier = std::max(
    7621        4254 :         1,
    7622        4254 :         std::min(257,
    7623        2127 :                  atoi(CSLFetchNameValueDef(
    7624             :                      papszOptions, "COLOR_TABLE_MULTIPLIER",
    7625        2127 :                      CPLSPrintf("%d", DEFAULT_COLOR_TABLE_MULTIPLIER_257)))));
    7626             : 
    7627        2127 :     bool bTileInterleaving = false;
    7628        2127 :     TIFF *l_hTIFF = CreateLL(pszFilename, nXSize, nYSize, l_nBands, eType,
    7629             :                              dfExtraSpaceForOverviews, nColorTableMultiplier,
    7630             :                              papszCreateOptions, &l_fpL, l_osTmpFilename,
    7631             :                              /* bCreateCopy = */ true, bTileInterleaving);
    7632        2127 :     const bool bStreaming = !l_osTmpFilename.empty();
    7633             : 
    7634        2127 :     CSLDestroy(papszCreateOptions);
    7635        2127 :     papszCreateOptions = nullptr;
    7636             : 
    7637        2127 :     if (l_hTIFF == nullptr)
    7638             :     {
    7639          18 :         if (bStreaming)
    7640           0 :             VSIUnlink(l_osTmpFilename);
    7641          18 :         return nullptr;
    7642             :     }
    7643             : 
    7644        2109 :     uint16_t l_nPlanarConfig = 0;
    7645        2109 :     TIFFGetField(l_hTIFF, TIFFTAG_PLANARCONFIG, &l_nPlanarConfig);
    7646             : 
    7647        2109 :     uint16_t l_nCompression = 0;
    7648             : 
    7649        2109 :     if (!TIFFGetField(l_hTIFF, TIFFTAG_COMPRESSION, &(l_nCompression)))
    7650           0 :         l_nCompression = COMPRESSION_NONE;
    7651             : 
    7652             :     /* -------------------------------------------------------------------- */
    7653             :     /*      Set the alpha channel if we find one.                           */
    7654             :     /* -------------------------------------------------------------------- */
    7655        2109 :     uint16_t *extraSamples = nullptr;
    7656        2109 :     uint16_t nExtraSamples = 0;
    7657        2109 :     if (TIFFGetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples,
    7658        2374 :                      &extraSamples) &&
    7659         265 :         nExtraSamples > 0)
    7660             :     {
    7661             :         // We need to allocate a new array as (current) libtiff
    7662             :         // versions will not like that we reuse the array we got from
    7663             :         // TIFFGetField().
    7664             :         uint16_t *pasNewExtraSamples = static_cast<uint16_t *>(
    7665         265 :             CPLMalloc(nExtraSamples * sizeof(uint16_t)));
    7666         265 :         memcpy(pasNewExtraSamples, extraSamples,
    7667         265 :                nExtraSamples * sizeof(uint16_t));
    7668         265 :         const char *pszAlpha = CPLGetConfigOption(
    7669             :             "GTIFF_ALPHA", CSLFetchNameValue(papszOptions, "ALPHA"));
    7670             :         const uint16_t nAlpha =
    7671         265 :             GTiffGetAlphaValue(pszAlpha, DEFAULT_ALPHA_TYPE);
    7672         265 :         const int nBaseSamples = l_nBands - nExtraSamples;
    7673         895 :         for (int iExtraBand = nBaseSamples + 1; iExtraBand <= l_nBands;
    7674             :              iExtraBand++)
    7675             :         {
    7676         630 :             if (poSrcDS->GetRasterBand(iExtraBand)->GetColorInterpretation() ==
    7677             :                 GCI_AlphaBand)
    7678             :             {
    7679         145 :                 pasNewExtraSamples[iExtraBand - nBaseSamples - 1] = nAlpha;
    7680         145 :                 if (!pszAlpha)
    7681             :                 {
    7682             :                     // Use the ALPHA metadata item from the source band, when
    7683             :                     // present, if no explicit ALPHA creation option
    7684         286 :                     pasNewExtraSamples[iExtraBand - nBaseSamples - 1] =
    7685         143 :                         GTiffGetAlphaValue(
    7686         143 :                             poSrcDS->GetRasterBand(iExtraBand)
    7687         143 :                                 ->GetMetadataItem("ALPHA", "IMAGE_STRUCTURE"),
    7688             :                             nAlpha);
    7689             :                 }
    7690             :             }
    7691             :         }
    7692         265 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples,
    7693             :                      pasNewExtraSamples);
    7694             : 
    7695         265 :         CPLFree(pasNewExtraSamples);
    7696             :     }
    7697             : 
    7698             :     /* -------------------------------------------------------------------- */
    7699             :     /*      If the output is jpeg compressed, and the input is RGB make     */
    7700             :     /*      sure we note that.                                              */
    7701             :     /* -------------------------------------------------------------------- */
    7702             : 
    7703        2109 :     if (l_nCompression == COMPRESSION_JPEG)
    7704             :     {
    7705         134 :         if (l_nBands >= 3 &&
    7706          58 :             (poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
    7707           0 :              GCI_YCbCr_YBand) &&
    7708           0 :             (poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
    7709         134 :              GCI_YCbCr_CbBand) &&
    7710           0 :             (poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
    7711             :              GCI_YCbCr_CrBand))
    7712             :         {
    7713             :             // Do nothing.
    7714             :         }
    7715             :         else
    7716             :         {
    7717             :             // Assume RGB if it is not explicitly YCbCr.
    7718          76 :             CPLDebug("GTiff", "Setting JPEGCOLORMODE_RGB");
    7719          76 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    7720             :         }
    7721             :     }
    7722             : 
    7723             :     /* -------------------------------------------------------------------- */
    7724             :     /*      Does the source image consist of one band, with a palette?      */
    7725             :     /*      If so, copy over.                                               */
    7726             :     /* -------------------------------------------------------------------- */
    7727        1310 :     if ((l_nBands == 1 || l_nBands == 2) &&
    7728        3419 :         poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7729             :         eType == GDT_UInt8)
    7730             :     {
    7731          16 :         unsigned short anTRed[256] = {0};
    7732          16 :         unsigned short anTGreen[256] = {0};
    7733          16 :         unsigned short anTBlue[256] = {0};
    7734          16 :         GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    7735             : 
    7736        4112 :         for (int iColor = 0; iColor < 256; ++iColor)
    7737             :         {
    7738        4096 :             if (iColor < poCT->GetColorEntryCount())
    7739             :             {
    7740        3041 :                 GDALColorEntry sRGB = {0, 0, 0, 0};
    7741             : 
    7742        3041 :                 poCT->GetColorEntryAsRGB(iColor, &sRGB);
    7743             : 
    7744        6082 :                 anTRed[iColor] = GTiffDataset::ClampCTEntry(
    7745        3041 :                     iColor, 1, sRGB.c1, nColorTableMultiplier);
    7746        6082 :                 anTGreen[iColor] = GTiffDataset::ClampCTEntry(
    7747        3041 :                     iColor, 2, sRGB.c2, nColorTableMultiplier);
    7748        3041 :                 anTBlue[iColor] = GTiffDataset::ClampCTEntry(
    7749        3041 :                     iColor, 3, sRGB.c3, nColorTableMultiplier);
    7750             :             }
    7751             :             else
    7752             :             {
    7753        1055 :                 anTRed[iColor] = 0;
    7754        1055 :                 anTGreen[iColor] = 0;
    7755        1055 :                 anTBlue[iColor] = 0;
    7756             :             }
    7757             :         }
    7758             : 
    7759          16 :         if (!bForcePhotometric)
    7760          16 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    7761          16 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, anTRed, anTGreen, anTBlue);
    7762             :     }
    7763        1309 :     else if ((l_nBands == 1 || l_nBands == 2) &&
    7764        3402 :              poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7765             :              eType == GDT_UInt16)
    7766             :     {
    7767             :         unsigned short *panTRed = static_cast<unsigned short *>(
    7768           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7769             :         unsigned short *panTGreen = static_cast<unsigned short *>(
    7770           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7771             :         unsigned short *panTBlue = static_cast<unsigned short *>(
    7772           1 :             CPLMalloc(65536 * sizeof(unsigned short)));
    7773             : 
    7774           1 :         GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    7775             : 
    7776       65537 :         for (int iColor = 0; iColor < 65536; ++iColor)
    7777             :         {
    7778       65536 :             if (iColor < poCT->GetColorEntryCount())
    7779             :             {
    7780       65536 :                 GDALColorEntry sRGB = {0, 0, 0, 0};
    7781             : 
    7782       65536 :                 poCT->GetColorEntryAsRGB(iColor, &sRGB);
    7783             : 
    7784      131072 :                 panTRed[iColor] = GTiffDataset::ClampCTEntry(
    7785       65536 :                     iColor, 1, sRGB.c1, nColorTableMultiplier);
    7786      131072 :                 panTGreen[iColor] = GTiffDataset::ClampCTEntry(
    7787       65536 :                     iColor, 2, sRGB.c2, nColorTableMultiplier);
    7788       65536 :                 panTBlue[iColor] = GTiffDataset::ClampCTEntry(
    7789       65536 :                     iColor, 3, sRGB.c3, nColorTableMultiplier);
    7790             :             }
    7791             :             else
    7792             :             {
    7793           0 :                 panTRed[iColor] = 0;
    7794           0 :                 panTGreen[iColor] = 0;
    7795           0 :                 panTBlue[iColor] = 0;
    7796             :             }
    7797             :         }
    7798             : 
    7799           1 :         if (!bForcePhotometric)
    7800           1 :             TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
    7801           1 :         TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue);
    7802             : 
    7803           1 :         CPLFree(panTRed);
    7804           1 :         CPLFree(panTGreen);
    7805           1 :         CPLFree(panTBlue);
    7806             :     }
    7807        2092 :     else if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
    7808           1 :         ReportError(
    7809             :             pszFilename, CE_Failure, CPLE_AppDefined,
    7810             :             "Unable to export color table to GeoTIFF file.  Color tables "
    7811             :             "can only be written to 1 band or 2 bands Byte or "
    7812             :             "UInt16 GeoTIFF files.");
    7813             : 
    7814        2109 :     if (l_nCompression == COMPRESSION_JPEG)
    7815             :     {
    7816          76 :         uint16_t l_nPhotometric = 0;
    7817          76 :         TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &l_nPhotometric);
    7818             :         // Check done in tif_jpeg.c later, but not with a very clear error
    7819             :         // message
    7820          76 :         if (l_nPhotometric == PHOTOMETRIC_PALETTE)
    7821             :         {
    7822           1 :             ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
    7823             :                         "JPEG compression not supported with paletted image");
    7824           1 :             XTIFFClose(l_hTIFF);
    7825           1 :             VSIUnlink(l_osTmpFilename);
    7826           1 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    7827           1 :             return nullptr;
    7828             :         }
    7829             :     }
    7830             : 
    7831        2195 :     if (l_nBands == 2 &&
    7832        2108 :         poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    7833           0 :         (eType == GDT_UInt8 || eType == GDT_UInt16))
    7834             :     {
    7835           1 :         uint16_t v[1] = {EXTRASAMPLE_UNASSALPHA};
    7836             : 
    7837           1 :         TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
    7838             :     }
    7839             : 
    7840        2108 :     const int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags();
    7841        2108 :     bool bCreateMask = false;
    7842        4216 :     CPLString osHiddenStructuralMD;
    7843             :     const char *pszInterleave =
    7844        2108 :         CSLFetchNameValueDef(papszOptions, "INTERLEAVE", "PIXEL");
    7845        2330 :     if (bCopySrcOverviews &&
    7846         222 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "TILED", "NO")))
    7847             :     {
    7848         210 :         osHiddenStructuralMD += "LAYOUT=IFDS_BEFORE_DATA\n";
    7849         210 :         osHiddenStructuralMD += "BLOCK_ORDER=ROW_MAJOR\n";
    7850         210 :         osHiddenStructuralMD += "BLOCK_LEADER=SIZE_AS_UINT4\n";
    7851         210 :         osHiddenStructuralMD += "BLOCK_TRAILER=LAST_4_BYTES_REPEATED\n";
    7852         210 :         if (l_nBands > 1 && !EQUAL(pszInterleave, "PIXEL"))
    7853             :         {
    7854          21 :             osHiddenStructuralMD += "INTERLEAVE=";
    7855          21 :             osHiddenStructuralMD += CPLString(pszInterleave).toupper();
    7856          21 :             osHiddenStructuralMD += "\n";
    7857             :         }
    7858             :         osHiddenStructuralMD +=
    7859         210 :             "KNOWN_INCOMPATIBLE_EDITION=NO\n ";  // Final space intended, so
    7860             :                                                  // this can be replaced by YES
    7861             :     }
    7862        2108 :     if (!(nMaskFlags & (GMF_ALL_VALID | GMF_ALPHA | GMF_NODATA)) &&
    7863          42 :         (nMaskFlags & GMF_PER_DATASET) && !bStreaming)
    7864             :     {
    7865          38 :         bCreateMask = true;
    7866          38 :         if (GTiffDataset::MustCreateInternalMask() &&
    7867          38 :             !osHiddenStructuralMD.empty() && EQUAL(pszInterleave, "PIXEL"))
    7868             :         {
    7869          21 :             osHiddenStructuralMD += "MASK_INTERLEAVED_WITH_IMAGERY=YES\n";
    7870             :         }
    7871             :     }
    7872        2318 :     if (!osHiddenStructuralMD.empty() &&
    7873         210 :         CPLTestBool(CPLGetConfigOption("GTIFF_WRITE_COG_GHOST_AREA", "YES")))
    7874             :     {
    7875         209 :         const int nHiddenMDSize = static_cast<int>(osHiddenStructuralMD.size());
    7876             :         osHiddenStructuralMD =
    7877         209 :             CPLOPrintf("GDAL_STRUCTURAL_METADATA_SIZE=%06d bytes\n",
    7878         418 :                        nHiddenMDSize) +
    7879         209 :             osHiddenStructuralMD;
    7880         209 :         VSI_TIFFWrite(l_hTIFF, osHiddenStructuralMD.c_str(),
    7881             :                       osHiddenStructuralMD.size());
    7882             :     }
    7883             : 
    7884             :     // FIXME? libtiff writes extended tags in the order they are specified
    7885             :     // and not in increasing order.
    7886             : 
    7887             :     /* -------------------------------------------------------------------- */
    7888             :     /*      Transfer some TIFF specific metadata, if available.             */
    7889             :     /*      The return value will tell us if we need to try again later with*/
    7890             :     /*      PAM because the profile doesn't allow to write some metadata    */
    7891             :     /*      as TIFF tag                                                     */
    7892             :     /* -------------------------------------------------------------------- */
    7893        2108 :     const bool bHasWrittenMDInGeotiffTAG = GTiffDataset::WriteMetadata(
    7894             :         poSrcDS, l_hTIFF, false, eProfile, pszFilename, papszOptions);
    7895             : 
    7896             :     /* -------------------------------------------------------------------- */
    7897             :     /*      Write NoData value, if exist.                                   */
    7898             :     /* -------------------------------------------------------------------- */
    7899        2108 :     if (eProfile == GTiffProfile::GDALGEOTIFF)
    7900             :     {
    7901        2087 :         int bSuccess = FALSE;
    7902        2087 :         GDALRasterBand *poFirstBand = poSrcDS->GetRasterBand(1);
    7903        2087 :         if (poFirstBand->GetRasterDataType() == GDT_Int64)
    7904             :         {
    7905           4 :             const auto nNoData = poFirstBand->GetNoDataValueAsInt64(&bSuccess);
    7906           4 :             if (bSuccess)
    7907           1 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, nNoData);
    7908             :         }
    7909        2083 :         else if (poFirstBand->GetRasterDataType() == GDT_UInt64)
    7910             :         {
    7911           4 :             const auto nNoData = poFirstBand->GetNoDataValueAsUInt64(&bSuccess);
    7912           4 :             if (bSuccess)
    7913           1 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, nNoData);
    7914             :         }
    7915             :         else
    7916             :         {
    7917        2079 :             const auto dfNoData = poFirstBand->GetNoDataValue(&bSuccess);
    7918        2079 :             if (bSuccess)
    7919         141 :                 GTiffDataset::WriteNoDataValue(l_hTIFF, dfNoData);
    7920             :         }
    7921             :     }
    7922             : 
    7923             :     /* -------------------------------------------------------------------- */
    7924             :     /*      Are we addressing PixelIsPoint mode?                            */
    7925             :     /* -------------------------------------------------------------------- */
    7926        2108 :     bool bPixelIsPoint = false;
    7927        2108 :     bool bPointGeoIgnore = false;
    7928             : 
    7929        3535 :     if (poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT) &&
    7930        1427 :         EQUAL(poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT), GDALMD_AOP_POINT))
    7931             :     {
    7932          10 :         bPixelIsPoint = true;
    7933             :         bPointGeoIgnore =
    7934          10 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    7935             :     }
    7936             : 
    7937             :     /* -------------------------------------------------------------------- */
    7938             :     /*      Write affine transform if it is meaningful.                     */
    7939             :     /* -------------------------------------------------------------------- */
    7940        2108 :     const OGRSpatialReference *l_poSRS = nullptr;
    7941        2108 :     GDALGeoTransform l_gt;
    7942        2108 :     if (poSrcDS->GetGeoTransform(l_gt) == CE_None)
    7943             :     {
    7944        1681 :         if (bGeoTIFF)
    7945             :         {
    7946        1676 :             l_poSRS = poSrcDS->GetSpatialRef();
    7947             : 
    7948        1676 :             if (l_gt[2] == 0.0 && l_gt[4] == 0.0 && l_gt[5] < 0.0)
    7949             :             {
    7950        1660 :                 double dfOffset = 0.0;
    7951             :                 {
    7952             :                     // In the case the SRS has a vertical component and we have
    7953             :                     // a single band, encode its scale/offset in the GeoTIFF
    7954             :                     // tags
    7955        1660 :                     int bHasScale = FALSE;
    7956             :                     double dfScale =
    7957        1660 :                         poSrcDS->GetRasterBand(1)->GetScale(&bHasScale);
    7958        1660 :                     int bHasOffset = FALSE;
    7959             :                     dfOffset =
    7960        1660 :                         poSrcDS->GetRasterBand(1)->GetOffset(&bHasOffset);
    7961             :                     const bool bApplyScaleOffset =
    7962        1664 :                         l_poSRS && l_poSRS->IsVertical() &&
    7963           4 :                         poSrcDS->GetRasterCount() == 1;
    7964        1660 :                     if (bApplyScaleOffset && !bHasScale)
    7965           0 :                         dfScale = 1.0;
    7966        1660 :                     if (!bApplyScaleOffset || !bHasOffset)
    7967        1656 :                         dfOffset = 0.0;
    7968        1660 :                     const double adfPixelScale[3] = {l_gt[1], fabs(l_gt[5]),
    7969        1660 :                                                      bApplyScaleOffset ? dfScale
    7970        1660 :                                                                        : 0.0};
    7971             : 
    7972        1660 :                     TIFFSetField(l_hTIFF, TIFFTAG_GEOPIXELSCALE, 3,
    7973             :                                  adfPixelScale);
    7974             :                 }
    7975             : 
    7976        1660 :                 double adfTiePoints[6] = {0.0,     0.0,     0.0,
    7977        1660 :                                           l_gt[0], l_gt[3], dfOffset};
    7978             : 
    7979        1660 :                 if (bPixelIsPoint && !bPointGeoIgnore)
    7980             :                 {
    7981           6 :                     adfTiePoints[3] += l_gt[1] * 0.5 + l_gt[2] * 0.5;
    7982           6 :                     adfTiePoints[4] += l_gt[4] * 0.5 + l_gt[5] * 0.5;
    7983             :                 }
    7984             : 
    7985        1660 :                 TIFFSetField(l_hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
    7986             :             }
    7987             :             else
    7988             :             {
    7989          16 :                 double adfMatrix[16] = {0.0};
    7990             : 
    7991          16 :                 adfMatrix[0] = l_gt[1];
    7992          16 :                 adfMatrix[1] = l_gt[2];
    7993          16 :                 adfMatrix[3] = l_gt[0];
    7994          16 :                 adfMatrix[4] = l_gt[4];
    7995          16 :                 adfMatrix[5] = l_gt[5];
    7996          16 :                 adfMatrix[7] = l_gt[3];
    7997          16 :                 adfMatrix[15] = 1.0;
    7998             : 
    7999          16 :                 if (bPixelIsPoint && !bPointGeoIgnore)
    8000             :                 {
    8001           0 :                     adfMatrix[3] += l_gt[1] * 0.5 + l_gt[2] * 0.5;
    8002           0 :                     adfMatrix[7] += l_gt[4] * 0.5 + l_gt[5] * 0.5;
    8003             :                 }
    8004             : 
    8005          16 :                 TIFFSetField(l_hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
    8006             :             }
    8007             :         }
    8008             : 
    8009             :         /* --------------------------------------------------------------------
    8010             :          */
    8011             :         /*      Do we need a TFW file? */
    8012             :         /* --------------------------------------------------------------------
    8013             :          */
    8014        1681 :         if (CPLFetchBool(papszOptions, "TFW", false))
    8015           2 :             GDALWriteWorldFile(pszFilename, "tfw", l_gt.data());
    8016        1679 :         else if (CPLFetchBool(papszOptions, "WORLDFILE", false))
    8017           1 :             GDALWriteWorldFile(pszFilename, "wld", l_gt.data());
    8018             :     }
    8019             : 
    8020             :     /* -------------------------------------------------------------------- */
    8021             :     /*      Otherwise write tiepoints if they are available.                */
    8022             :     /* -------------------------------------------------------------------- */
    8023         427 :     else if (poSrcDS->GetGCPCount() > 0 && bGeoTIFF)
    8024             :     {
    8025          12 :         const GDAL_GCP *pasGCPs = poSrcDS->GetGCPs();
    8026             :         double *padfTiePoints = static_cast<double *>(
    8027          12 :             CPLMalloc(6 * sizeof(double) * poSrcDS->GetGCPCount()));
    8028             : 
    8029          60 :         for (int iGCP = 0; iGCP < poSrcDS->GetGCPCount(); ++iGCP)
    8030             :         {
    8031             : 
    8032          48 :             padfTiePoints[iGCP * 6 + 0] = pasGCPs[iGCP].dfGCPPixel;
    8033          48 :             padfTiePoints[iGCP * 6 + 1] = pasGCPs[iGCP].dfGCPLine;
    8034          48 :             padfTiePoints[iGCP * 6 + 2] = 0;
    8035          48 :             padfTiePoints[iGCP * 6 + 3] = pasGCPs[iGCP].dfGCPX;
    8036          48 :             padfTiePoints[iGCP * 6 + 4] = pasGCPs[iGCP].dfGCPY;
    8037          48 :             padfTiePoints[iGCP * 6 + 5] = pasGCPs[iGCP].dfGCPZ;
    8038             : 
    8039          48 :             if (bPixelIsPoint && !bPointGeoIgnore)
    8040             :             {
    8041           4 :                 padfTiePoints[iGCP * 6 + 0] -= 0.5;
    8042           4 :                 padfTiePoints[iGCP * 6 + 1] -= 0.5;
    8043             :             }
    8044             :         }
    8045             : 
    8046          12 :         TIFFSetField(l_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * poSrcDS->GetGCPCount(),
    8047             :                      padfTiePoints);
    8048          12 :         CPLFree(padfTiePoints);
    8049             : 
    8050          12 :         l_poSRS = poSrcDS->GetGCPSpatialRef();
    8051             : 
    8052          24 :         if (CPLFetchBool(papszOptions, "TFW", false) ||
    8053          12 :             CPLFetchBool(papszOptions, "WORLDFILE", false))
    8054             :         {
    8055           0 :             ReportError(
    8056             :                 pszFilename, CE_Warning, CPLE_AppDefined,
    8057             :                 "TFW=ON or WORLDFILE=ON creation options are ignored when "
    8058             :                 "GCPs are available");
    8059             :         }
    8060             :     }
    8061             :     else
    8062             :     {
    8063         415 :         l_poSRS = poSrcDS->GetSpatialRef();
    8064             :     }
    8065             : 
    8066             :     /* -------------------------------------------------------------------- */
    8067             :     /*      Copy xml:XMP data                                               */
    8068             :     /* -------------------------------------------------------------------- */
    8069        2108 :     char **papszXMP = poSrcDS->GetMetadata("xml:XMP");
    8070        2108 :     if (papszXMP != nullptr && *papszXMP != nullptr)
    8071             :     {
    8072           9 :         int nTagSize = static_cast<int>(strlen(*papszXMP));
    8073           9 :         TIFFSetField(l_hTIFF, TIFFTAG_XMLPACKET, nTagSize, *papszXMP);
    8074             :     }
    8075             : 
    8076             :     /* -------------------------------------------------------------------- */
    8077             :     /*      Write the projection information, if possible.                  */
    8078             :     /* -------------------------------------------------------------------- */
    8079        2108 :     const bool bHasProjection = l_poSRS != nullptr;
    8080        2108 :     bool bExportSRSToPAM = false;
    8081        2108 :     if ((bHasProjection || bPixelIsPoint) && bGeoTIFF)
    8082             :     {
    8083        1646 :         GTIF *psGTIF = GTiffDataset::GTIFNew(l_hTIFF);
    8084             : 
    8085        1646 :         if (bHasProjection)
    8086             :         {
    8087        1646 :             const auto eGeoTIFFKeysFlavor = GetGTIFFKeysFlavor(papszOptions);
    8088        1646 :             if (IsSRSCompatibleOfGeoTIFF(l_poSRS, eGeoTIFFKeysFlavor))
    8089             :             {
    8090        1646 :                 GTIFSetFromOGISDefnEx(
    8091             :                     psGTIF,
    8092             :                     OGRSpatialReference::ToHandle(
    8093             :                         const_cast<OGRSpatialReference *>(l_poSRS)),
    8094             :                     eGeoTIFFKeysFlavor, GetGeoTIFFVersion(papszOptions));
    8095             :             }
    8096             :             else
    8097             :             {
    8098           0 :                 bExportSRSToPAM = true;
    8099             :             }
    8100             :         }
    8101             : 
    8102        1646 :         if (bPixelIsPoint)
    8103             :         {
    8104          10 :             GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
    8105             :                        RasterPixelIsPoint);
    8106             :         }
    8107             : 
    8108        1646 :         GTIFWriteKeys(psGTIF);
    8109        1646 :         GTIFFree(psGTIF);
    8110             :     }
    8111             : 
    8112        2108 :     bool l_bDontReloadFirstBlock = false;
    8113             : 
    8114             : #ifdef HAVE_LIBJPEG
    8115        2108 :     if (bCopyFromJPEG)
    8116             :     {
    8117          12 :         GTIFF_CopyFromJPEG_WriteAdditionalTags(l_hTIFF, poSrcDS);
    8118             :     }
    8119             : #endif
    8120             : 
    8121             :     /* -------------------------------------------------------------------- */
    8122             :     /*      Cleanup                                                         */
    8123             :     /* -------------------------------------------------------------------- */
    8124        2108 :     if (bCopySrcOverviews)
    8125             :     {
    8126         222 :         TIFFDeferStrileArrayWriting(l_hTIFF);
    8127             :     }
    8128        2108 :     TIFFWriteCheck(l_hTIFF, TIFFIsTiled(l_hTIFF), "GTiffCreateCopy()");
    8129        2108 :     TIFFWriteDirectory(l_hTIFF);
    8130        2108 :     if (bStreaming)
    8131             :     {
    8132             :         // We need to write twice the directory to be sure that custom
    8133             :         // TIFF tags are correctly sorted and that padding bytes have been
    8134             :         // added.
    8135           5 :         TIFFSetDirectory(l_hTIFF, 0);
    8136           5 :         TIFFWriteDirectory(l_hTIFF);
    8137             : 
    8138           5 :         if (VSIFSeekL(l_fpL, 0, SEEK_END) != 0)
    8139           0 :             ReportError(pszFilename, CE_Failure, CPLE_FileIO, "Cannot seek");
    8140           5 :         const int nSize = static_cast<int>(VSIFTellL(l_fpL));
    8141             : 
    8142           5 :         vsi_l_offset nDataLength = 0;
    8143           5 :         VSIGetMemFileBuffer(l_osTmpFilename, &nDataLength, FALSE);
    8144           5 :         TIFFSetDirectory(l_hTIFF, 0);
    8145           5 :         GTiffFillStreamableOffsetAndCount(l_hTIFF, nSize);
    8146           5 :         TIFFWriteDirectory(l_hTIFF);
    8147             :     }
    8148        2108 :     const auto nDirCount = TIFFNumberOfDirectories(l_hTIFF);
    8149        2108 :     if (nDirCount >= 1)
    8150             :     {
    8151        2101 :         TIFFSetDirectory(l_hTIFF, static_cast<tdir_t>(nDirCount - 1));
    8152             :     }
    8153        2108 :     const toff_t l_nDirOffset = TIFFCurrentDirOffset(l_hTIFF);
    8154        2108 :     TIFFFlush(l_hTIFF);
    8155        2108 :     XTIFFClose(l_hTIFF);
    8156             : 
    8157        2108 :     VSIFSeekL(l_fpL, 0, SEEK_SET);
    8158             : 
    8159             :     // fpStreaming will assigned to the instance and not closed here.
    8160        2108 :     VSILFILE *fpStreaming = nullptr;
    8161        2108 :     if (bStreaming)
    8162             :     {
    8163           5 :         vsi_l_offset nDataLength = 0;
    8164             :         void *pabyBuffer =
    8165           5 :             VSIGetMemFileBuffer(l_osTmpFilename, &nDataLength, FALSE);
    8166           5 :         fpStreaming = VSIFOpenL(pszFilename, "wb");
    8167           5 :         if (fpStreaming == nullptr)
    8168             :         {
    8169           1 :             VSIUnlink(l_osTmpFilename);
    8170           1 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    8171           1 :             return nullptr;
    8172             :         }
    8173           4 :         if (static_cast<vsi_l_offset>(VSIFWriteL(pabyBuffer, 1,
    8174             :                                                  static_cast<int>(nDataLength),
    8175           4 :                                                  fpStreaming)) != nDataLength)
    8176             :         {
    8177           0 :             ReportError(pszFilename, CE_Failure, CPLE_FileIO,
    8178             :                         "Could not write %d bytes",
    8179             :                         static_cast<int>(nDataLength));
    8180           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fpStreaming));
    8181           0 :             VSIUnlink(l_osTmpFilename);
    8182           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    8183           0 :             return nullptr;
    8184             :         }
    8185             :     }
    8186             : 
    8187             :     /* -------------------------------------------------------------------- */
    8188             :     /*      Re-open as a dataset and copy over missing metadata using       */
    8189             :     /*      PAM facilities.                                                 */
    8190             :     /* -------------------------------------------------------------------- */
    8191        2107 :     l_hTIFF = VSI_TIFFOpen(bStreaming ? l_osTmpFilename.c_str() : pszFilename,
    8192             :                            "r+", l_fpL);
    8193        2107 :     if (l_hTIFF == nullptr)
    8194             :     {
    8195          11 :         if (bStreaming)
    8196           0 :             VSIUnlink(l_osTmpFilename);
    8197          11 :         l_fpL->CancelCreation();
    8198          11 :         CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
    8199          11 :         return nullptr;
    8200             :     }
    8201             : 
    8202             :     /* -------------------------------------------------------------------- */
    8203             :     /*      Create a corresponding GDALDataset.                             */
    8204             :     /* -------------------------------------------------------------------- */
    8205        4192 :     auto poDS = std::make_unique<GTiffDataset>();
    8206             :     const bool bSuppressASAP =
    8207        2096 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "@SUPPRESS_ASAP", "NO"));
    8208        2096 :     if (bSuppressASAP)
    8209           4 :         poDS->MarkSuppressOnClose();
    8210        2096 :     poDS->SetDescription(pszFilename);
    8211        2096 :     poDS->eAccess = GA_Update;
    8212        2096 :     poDS->m_osFilename = pszFilename;
    8213        2096 :     poDS->m_fpL = l_fpL;
    8214        2096 :     poDS->m_bIMDRPCMetadataLoaded = true;
    8215        2096 :     poDS->m_nColorTableMultiplier = nColorTableMultiplier;
    8216        2096 :     poDS->m_bTileInterleave = bTileInterleaving;
    8217             : 
    8218        2096 :     if (bTileInterleaving)
    8219             :     {
    8220           7 :         poDS->m_oGTiffMDMD.SetMetadataItem("INTERLEAVE", "TILE",
    8221             :                                            "IMAGE_STRUCTURE");
    8222             :     }
    8223             : 
    8224        2096 :     const bool bAppend = CPLFetchBool(papszOptions, "APPEND_SUBDATASET", false);
    8225        4191 :     if (poDS->OpenOffset(l_hTIFF,
    8226        2095 :                          bAppend ? l_nDirOffset : TIFFCurrentDirOffset(l_hTIFF),
    8227             :                          GA_Update,
    8228             :                          false,  // bAllowRGBAInterface
    8229             :                          true    // bReadGeoTransform
    8230        2096 :                          ) != CE_None)
    8231             :     {
    8232           0 :         l_fpL->CancelCreation();
    8233           0 :         poDS.reset();
    8234           0 :         if (bStreaming)
    8235           0 :             VSIUnlink(l_osTmpFilename);
    8236           0 :         return nullptr;
    8237             :     }
    8238             : 
    8239             :     // Legacy... Patch back GDT_Int8 type to GDT_UInt8 if the user used
    8240             :     // PIXELTYPE=SIGNEDBYTE
    8241        2096 :     const char *pszPixelType = CSLFetchNameValue(papszOptions, "PIXELTYPE");
    8242        2096 :     if (pszPixelType == nullptr)
    8243        2091 :         pszPixelType = "";
    8244        2096 :     if (eType == GDT_UInt8 && EQUAL(pszPixelType, "SIGNEDBYTE"))
    8245             :     {
    8246          10 :         for (int i = 0; i < poDS->nBands; ++i)
    8247             :         {
    8248           5 :             auto poBand = static_cast<GTiffRasterBand *>(poDS->papoBands[i]);
    8249           5 :             poBand->eDataType = GDT_UInt8;
    8250           5 :             poBand->EnablePixelTypeSignedByteWarning(false);
    8251           5 :             poBand->SetMetadataItem("PIXELTYPE", "SIGNEDBYTE",
    8252             :                                     "IMAGE_STRUCTURE");
    8253           5 :             poBand->EnablePixelTypeSignedByteWarning(true);
    8254             :         }
    8255             :     }
    8256             : 
    8257        2096 :     poDS->oOvManager.Initialize(poDS.get(), pszFilename);
    8258             : 
    8259        2096 :     if (bStreaming)
    8260             :     {
    8261           4 :         VSIUnlink(l_osTmpFilename);
    8262           4 :         poDS->m_fpToWrite = fpStreaming;
    8263             :     }
    8264        2096 :     poDS->m_eProfile = eProfile;
    8265             : 
    8266        2096 :     int nCloneInfoFlags = GCIF_PAM_DEFAULT & ~GCIF_MASK;
    8267             : 
    8268             :     // If we explicitly asked not to tag the alpha band as such, do not
    8269             :     // reintroduce this alpha color interpretation in PAM.
    8270        2096 :     if (poSrcDS->GetRasterBand(l_nBands)->GetColorInterpretation() ==
    8271        2224 :             GCI_AlphaBand &&
    8272         128 :         GTiffGetAlphaValue(
    8273             :             CPLGetConfigOption("GTIFF_ALPHA",
    8274             :                                CSLFetchNameValue(papszOptions, "ALPHA")),
    8275             :             DEFAULT_ALPHA_TYPE) == EXTRASAMPLE_UNSPECIFIED)
    8276             :     {
    8277           1 :         nCloneInfoFlags &= ~GCIF_COLORINTERP;
    8278             :     }
    8279             :     // Ignore source band color interpretation if requesting PHOTOMETRIC=RGB
    8280        3316 :     else if (l_nBands >= 3 &&
    8281        1221 :              EQUAL(CSLFetchNameValueDef(papszOptions, "PHOTOMETRIC", ""),
    8282             :                    "RGB"))
    8283             :     {
    8284          28 :         for (int i = 1; i <= 3; i++)
    8285             :         {
    8286          21 :             poDS->GetRasterBand(i)->SetColorInterpretation(
    8287          21 :                 static_cast<GDALColorInterp>(GCI_RedBand + (i - 1)));
    8288             :         }
    8289           7 :         nCloneInfoFlags &= ~GCIF_COLORINTERP;
    8290           9 :         if (!(l_nBands == 4 &&
    8291           2 :               CSLFetchNameValue(papszOptions, "ALPHA") != nullptr))
    8292             :         {
    8293          15 :             for (int i = 4; i <= l_nBands; i++)
    8294             :             {
    8295          18 :                 poDS->GetRasterBand(i)->SetColorInterpretation(
    8296           9 :                     poSrcDS->GetRasterBand(i)->GetColorInterpretation());
    8297             :             }
    8298             :         }
    8299             :     }
    8300             : 
    8301             :     CPLString osOldGTIFF_REPORT_COMPD_CSVal(
    8302        4192 :         CPLGetConfigOption("GTIFF_REPORT_COMPD_CS", ""));
    8303        2096 :     CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS", "YES");
    8304        2096 :     poDS->CloneInfo(poSrcDS, nCloneInfoFlags);
    8305        2096 :     CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS",
    8306        2096 :                                   osOldGTIFF_REPORT_COMPD_CSVal.empty()
    8307             :                                       ? nullptr
    8308           0 :                                       : osOldGTIFF_REPORT_COMPD_CSVal.c_str());
    8309             : 
    8310        2113 :     if ((!bGeoTIFF || bExportSRSToPAM) &&
    8311          17 :         (poDS->GetPamFlags() & GPF_DISABLED) == 0)
    8312             :     {
    8313             :         // Copy georeferencing info to PAM if the profile is not GeoTIFF
    8314          16 :         poDS->GDALPamDataset::SetSpatialRef(poDS->GetSpatialRef());
    8315          16 :         GDALGeoTransform gt;
    8316          16 :         if (poDS->GetGeoTransform(gt) == CE_None)
    8317             :         {
    8318           5 :             poDS->GDALPamDataset::SetGeoTransform(gt);
    8319             :         }
    8320          16 :         poDS->GDALPamDataset::SetGCPs(poDS->GetGCPCount(), poDS->GetGCPs(),
    8321             :                                       poDS->GetGCPSpatialRef());
    8322             :     }
    8323             : 
    8324        2096 :     poDS->m_papszCreationOptions = CSLDuplicate(papszOptions);
    8325        2096 :     poDS->m_bDontReloadFirstBlock = l_bDontReloadFirstBlock;
    8326             : 
    8327             :     /* -------------------------------------------------------------------- */
    8328             :     /*      CloneInfo() does not merge metadata, it just replaces it        */
    8329             :     /*      totally.  So we have to merge it.                               */
    8330             :     /* -------------------------------------------------------------------- */
    8331             : 
    8332        2096 :     char **papszSRC_MD = poSrcDS->GetMetadata();
    8333        2096 :     char **papszDST_MD = CSLDuplicate(poDS->GetMetadata());
    8334             : 
    8335        2096 :     papszDST_MD = CSLMerge(papszDST_MD, papszSRC_MD);
    8336             : 
    8337        2096 :     poDS->SetMetadata(papszDST_MD);
    8338        2096 :     CSLDestroy(papszDST_MD);
    8339             : 
    8340             :     // Depending on the PHOTOMETRIC tag, the TIFF file may not have the same
    8341             :     // band count as the source. Will fail later in GDALDatasetCopyWholeRaster
    8342             :     // anyway.
    8343        7105 :     for (int nBand = 1;
    8344        7105 :          nBand <= std::min(poDS->GetRasterCount(), poSrcDS->GetRasterCount());
    8345             :          ++nBand)
    8346             :     {
    8347        5009 :         GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(nBand);
    8348        5009 :         GDALRasterBand *poDstBand = poDS->GetRasterBand(nBand);
    8349        5009 :         papszSRC_MD = poSrcBand->GetMetadata();
    8350        5009 :         papszDST_MD = CSLDuplicate(poDstBand->GetMetadata());
    8351             : 
    8352        5009 :         papszDST_MD = CSLMerge(papszDST_MD, papszSRC_MD);
    8353             : 
    8354        5009 :         poDstBand->SetMetadata(papszDST_MD);
    8355        5009 :         CSLDestroy(papszDST_MD);
    8356             : 
    8357        5009 :         char **papszCatNames = poSrcBand->GetCategoryNames();
    8358        5009 :         if (nullptr != papszCatNames)
    8359           0 :             poDstBand->SetCategoryNames(papszCatNames);
    8360             :     }
    8361             : 
    8362        2096 :     l_hTIFF = static_cast<TIFF *>(poDS->GetInternalHandle("TIFF_HANDLE"));
    8363             : 
    8364             :     /* -------------------------------------------------------------------- */
    8365             :     /*      Handle forcing xml:ESRI data to be written to PAM.              */
    8366             :     /* -------------------------------------------------------------------- */
    8367        2096 :     if (CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
    8368             :     {
    8369           1 :         char **papszESRIMD = poSrcDS->GetMetadata("xml:ESRI");
    8370           1 :         if (papszESRIMD)
    8371             :         {
    8372           1 :             poDS->SetMetadata(papszESRIMD, "xml:ESRI");
    8373             :         }
    8374             :     }
    8375             : 
    8376             :     /* -------------------------------------------------------------------- */
    8377             :     /*      Second chance: now that we have a PAM dataset, it is possible   */
    8378             :     /*      to write metadata that we could not write as a TIFF tag.        */
    8379             :     /* -------------------------------------------------------------------- */
    8380        2096 :     if (!bHasWrittenMDInGeotiffTAG && !bStreaming)
    8381             :     {
    8382           6 :         GTiffDataset::WriteMetadata(
    8383           6 :             poDS.get(), l_hTIFF, true, eProfile, pszFilename, papszOptions,
    8384             :             true /* don't write RPC and IMD file again */);
    8385             :     }
    8386             : 
    8387        2096 :     if (!bStreaming)
    8388        2092 :         GTiffDataset::WriteRPC(poDS.get(), l_hTIFF, true, eProfile, pszFilename,
    8389             :                                papszOptions,
    8390             :                                true /* write only in PAM AND if needed */);
    8391             : 
    8392        2096 :     poDS->m_bWriteCOGLayout = bCopySrcOverviews;
    8393             : 
    8394             :     // To avoid unnecessary directory rewriting.
    8395        2096 :     poDS->m_bMetadataChanged = false;
    8396        2096 :     poDS->m_bGeoTIFFInfoChanged = false;
    8397        2096 :     poDS->m_bNoDataChanged = false;
    8398        2096 :     poDS->m_bForceUnsetGTOrGCPs = false;
    8399        2096 :     poDS->m_bForceUnsetProjection = false;
    8400        2096 :     poDS->m_bStreamingOut = bStreaming;
    8401             : 
    8402             :     // Don't try to load external metadata files (#6597).
    8403        2096 :     poDS->m_bIMDRPCMetadataLoaded = true;
    8404             : 
    8405             :     // We must re-set the compression level at this point, since it has been
    8406             :     // lost a few lines above when closing the newly create TIFF file The
    8407             :     // TIFFTAG_ZIPQUALITY & TIFFTAG_JPEGQUALITY are not store in the TIFF file.
    8408             :     // They are just TIFF session parameters.
    8409             : 
    8410        2096 :     poDS->m_nZLevel = GTiffGetZLevel(papszOptions);
    8411        2096 :     poDS->m_nLZMAPreset = GTiffGetLZMAPreset(papszOptions);
    8412        2096 :     poDS->m_nZSTDLevel = GTiffGetZSTDPreset(papszOptions);
    8413        2096 :     poDS->m_nWebPLevel = GTiffGetWebPLevel(papszOptions);
    8414        2096 :     poDS->m_bWebPLossless = GTiffGetWebPLossless(papszOptions);
    8415        2099 :     if (poDS->m_nWebPLevel != 100 && poDS->m_bWebPLossless &&
    8416           3 :         CSLFetchNameValue(papszOptions, "WEBP_LEVEL"))
    8417             :     {
    8418           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    8419             :                  "WEBP_LEVEL is specified, but WEBP_LOSSLESS=YES. "
    8420             :                  "WEBP_LEVEL will be ignored.");
    8421             :     }
    8422        2096 :     poDS->m_nJpegQuality = GTiffGetJpegQuality(papszOptions);
    8423        2096 :     poDS->m_nJpegTablesMode = GTiffGetJpegTablesMode(papszOptions);
    8424        2096 :     poDS->GetDiscardLsbOption(papszOptions);
    8425        2096 :     poDS->m_dfMaxZError = GTiffGetLERCMaxZError(papszOptions);
    8426        2096 :     poDS->m_dfMaxZErrorOverview = GTiffGetLERCMaxZErrorOverview(papszOptions);
    8427             : #if HAVE_JXL
    8428        2096 :     poDS->m_bJXLLossless = GTiffGetJXLLossless(papszOptions);
    8429        2096 :     poDS->m_nJXLEffort = GTiffGetJXLEffort(papszOptions);
    8430        2096 :     poDS->m_fJXLDistance = GTiffGetJXLDistance(papszOptions);
    8431        2096 :     poDS->m_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszOptions);
    8432             : #endif
    8433        2096 :     poDS->InitCreationOrOpenOptions(true, papszOptions);
    8434             : 
    8435        2096 :     if (l_nCompression == COMPRESSION_ADOBE_DEFLATE ||
    8436        2068 :         l_nCompression == COMPRESSION_LERC)
    8437             :     {
    8438          99 :         GTiffSetDeflateSubCodec(l_hTIFF);
    8439             : 
    8440          99 :         if (poDS->m_nZLevel != -1)
    8441             :         {
    8442          12 :             TIFFSetField(l_hTIFF, TIFFTAG_ZIPQUALITY, poDS->m_nZLevel);
    8443             :         }
    8444             :     }
    8445        2096 :     if (l_nCompression == COMPRESSION_JPEG)
    8446             :     {
    8447          75 :         if (poDS->m_nJpegQuality != -1)
    8448             :         {
    8449           9 :             TIFFSetField(l_hTIFF, TIFFTAG_JPEGQUALITY, poDS->m_nJpegQuality);
    8450             :         }
    8451          75 :         TIFFSetField(l_hTIFF, TIFFTAG_JPEGTABLESMODE, poDS->m_nJpegTablesMode);
    8452             :     }
    8453        2096 :     if (l_nCompression == COMPRESSION_LZMA)
    8454             :     {
    8455           7 :         if (poDS->m_nLZMAPreset != -1)
    8456             :         {
    8457           6 :             TIFFSetField(l_hTIFF, TIFFTAG_LZMAPRESET, poDS->m_nLZMAPreset);
    8458             :         }
    8459             :     }
    8460        2096 :     if (l_nCompression == COMPRESSION_ZSTD ||
    8461        2085 :         l_nCompression == COMPRESSION_LERC)
    8462             :     {
    8463          82 :         if (poDS->m_nZSTDLevel != -1)
    8464             :         {
    8465           8 :             TIFFSetField(l_hTIFF, TIFFTAG_ZSTD_LEVEL, poDS->m_nZSTDLevel);
    8466             :         }
    8467             :     }
    8468        2096 :     if (l_nCompression == COMPRESSION_LERC)
    8469             :     {
    8470          71 :         TIFFSetField(l_hTIFF, TIFFTAG_LERC_MAXZERROR, poDS->m_dfMaxZError);
    8471             :     }
    8472             : #if HAVE_JXL
    8473        2096 :     if (l_nCompression == COMPRESSION_JXL ||
    8474        2096 :         l_nCompression == COMPRESSION_JXL_DNG_1_7)
    8475             :     {
    8476          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_LOSSYNESS,
    8477          91 :                      poDS->m_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
    8478          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_EFFORT, poDS->m_nJXLEffort);
    8479          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_DISTANCE,
    8480          91 :                      static_cast<double>(poDS->m_fJXLDistance));
    8481          91 :         TIFFSetField(l_hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE,
    8482          91 :                      static_cast<double>(poDS->m_fJXLAlphaDistance));
    8483             :     }
    8484             : #endif
    8485        2096 :     if (l_nCompression == COMPRESSION_WEBP)
    8486             :     {
    8487          14 :         if (poDS->m_nWebPLevel != -1)
    8488             :         {
    8489          14 :             TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LEVEL, poDS->m_nWebPLevel);
    8490             :         }
    8491             : 
    8492          14 :         if (poDS->m_bWebPLossless)
    8493             :         {
    8494           5 :             TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LOSSLESS, poDS->m_bWebPLossless);
    8495             :         }
    8496             :     }
    8497             : 
    8498             :     /* -------------------------------------------------------------------- */
    8499             :     /*      Do we want to ensure all blocks get written out on close to     */
    8500             :     /*      avoid sparse files?                                             */
    8501             :     /* -------------------------------------------------------------------- */
    8502        2096 :     if (!CPLFetchBool(papszOptions, "SPARSE_OK", false))
    8503        2068 :         poDS->m_bFillEmptyTilesAtClosing = true;
    8504             : 
    8505        2096 :     poDS->m_bWriteEmptyTiles =
    8506        3974 :         (bCopySrcOverviews && poDS->m_bFillEmptyTilesAtClosing) || bStreaming ||
    8507        1878 :         (poDS->m_nCompression != COMPRESSION_NONE &&
    8508         306 :          poDS->m_bFillEmptyTilesAtClosing);
    8509             :     // Only required for people writing non-compressed striped files in the
    8510             :     // rightorder and wanting all tstrips to be written in the same order
    8511             :     // so that the end result can be memory mapped without knowledge of each
    8512             :     // strip offset
    8513        2096 :     if (CPLTestBool(CSLFetchNameValueDef(
    8514        4192 :             papszOptions, "WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")) ||
    8515        2096 :         CPLTestBool(CSLFetchNameValueDef(
    8516             :             papszOptions, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")))
    8517             :     {
    8518           0 :         poDS->m_bWriteEmptyTiles = true;
    8519             :     }
    8520             : 
    8521             :     // Precreate (internal) mask, so that the IBuildOverviews() below
    8522             :     // has a chance to create also the overviews of the mask.
    8523        2096 :     CPLErr eErr = CE_None;
    8524             : 
    8525        2096 :     if (bCreateMask)
    8526             :     {
    8527          38 :         eErr = poDS->CreateMaskBand(nMaskFlags);
    8528          38 :         if (poDS->m_poMaskDS)
    8529             :         {
    8530          37 :             poDS->m_poMaskDS->m_bFillEmptyTilesAtClosing =
    8531          37 :                 poDS->m_bFillEmptyTilesAtClosing;
    8532          37 :             poDS->m_poMaskDS->m_bWriteEmptyTiles = poDS->m_bWriteEmptyTiles;
    8533             :         }
    8534             :     }
    8535             : 
    8536             :     /* -------------------------------------------------------------------- */
    8537             :     /*      Create and then copy existing overviews if requested            */
    8538             :     /*  We do it such that all the IFDs are at the beginning of the file,   */
    8539             :     /*  and that the imagery data for the smallest overview is written      */
    8540             :     /*  first, that way the file is more usable when embedded in a          */
    8541             :     /*  compressed stream.                                                  */
    8542             :     /* -------------------------------------------------------------------- */
    8543             : 
    8544             :     // For scaled progress due to overview copying.
    8545        2096 :     const int nBandsWidthMask = l_nBands + (bCreateMask ? 1 : 0);
    8546        2096 :     double dfTotalPixels =
    8547        2096 :         static_cast<double>(nXSize) * nYSize * nBandsWidthMask;
    8548        2096 :     double dfCurPixels = 0;
    8549             : 
    8550        2096 :     if (eErr == CE_None && bCopySrcOverviews)
    8551             :     {
    8552           0 :         std::unique_ptr<GDALDataset> poMaskOvrDS;
    8553             :         const char *pszMaskOvrDS =
    8554         219 :             CSLFetchNameValue(papszOptions, "@MASK_OVERVIEW_DATASET");
    8555         219 :         if (pszMaskOvrDS)
    8556             :         {
    8557           6 :             poMaskOvrDS.reset(GDALDataset::Open(pszMaskOvrDS));
    8558           6 :             if (!poMaskOvrDS)
    8559             :             {
    8560           0 :                 l_fpL->CancelCreation();
    8561           0 :                 return nullptr;
    8562             :             }
    8563           6 :             if (poMaskOvrDS->GetRasterCount() != 1)
    8564             :             {
    8565           0 :                 l_fpL->CancelCreation();
    8566           0 :                 return nullptr;
    8567             :             }
    8568             :         }
    8569         219 :         if (nSrcOverviews)
    8570             :         {
    8571          69 :             eErr = poDS->CreateOverviewsFromSrcOverviews(poSrcDS, poOvrDS.get(),
    8572             :                                                          nSrcOverviews);
    8573             : 
    8574         201 :             if (eErr == CE_None &&
    8575          69 :                 (poMaskOvrDS != nullptr ||
    8576          63 :                  (poSrcDS->GetRasterBand(1)->GetOverview(0) &&
    8577          35 :                   poSrcDS->GetRasterBand(1)->GetOverview(0)->GetMaskFlags() ==
    8578             :                       GMF_PER_DATASET)))
    8579             :             {
    8580          19 :                 int nOvrBlockXSize = 0;
    8581          19 :                 int nOvrBlockYSize = 0;
    8582          19 :                 GTIFFGetOverviewBlockSize(
    8583          19 :                     GDALRasterBand::ToHandle(poDS->GetRasterBand(1)),
    8584             :                     &nOvrBlockXSize, &nOvrBlockYSize, nullptr, nullptr);
    8585          19 :                 eErr = poDS->CreateInternalMaskOverviews(nOvrBlockXSize,
    8586             :                                                          nOvrBlockYSize);
    8587             :             }
    8588             :         }
    8589             : 
    8590         219 :         TIFFForceStrileArrayWriting(poDS->m_hTIFF);
    8591             : 
    8592         219 :         if (poDS->m_poMaskDS)
    8593             :         {
    8594          27 :             TIFFForceStrileArrayWriting(poDS->m_poMaskDS->m_hTIFF);
    8595             :         }
    8596             : 
    8597         345 :         for (auto &poIterOvrDS : poDS->m_apoOverviewDS)
    8598             :         {
    8599         126 :             TIFFForceStrileArrayWriting(poIterOvrDS->m_hTIFF);
    8600             : 
    8601         126 :             if (poIterOvrDS->m_poMaskDS)
    8602             :             {
    8603          32 :                 TIFFForceStrileArrayWriting(poIterOvrDS->m_poMaskDS->m_hTIFF);
    8604             :             }
    8605             :         }
    8606             : 
    8607         219 :         if (eErr == CE_None && nSrcOverviews)
    8608             :         {
    8609          69 :             if (poDS->m_apoOverviewDS.size() !=
    8610          69 :                 static_cast<size_t>(nSrcOverviews))
    8611             :             {
    8612           0 :                 ReportError(
    8613             :                     pszFilename, CE_Failure, CPLE_AppDefined,
    8614             :                     "Did only manage to instantiate %d overview levels, "
    8615             :                     "whereas source contains %d",
    8616           0 :                     static_cast<int>(poDS->m_apoOverviewDS.size()),
    8617             :                     nSrcOverviews);
    8618           0 :                 eErr = CE_Failure;
    8619             :             }
    8620             : 
    8621         195 :             for (int i = 0; eErr == CE_None && i < nSrcOverviews; ++i)
    8622             :             {
    8623             :                 GDALRasterBand *poOvrBand =
    8624             :                     poOvrDS
    8625         197 :                         ? (i == 0
    8626          71 :                                ? poOvrDS->GetRasterBand(1)
    8627          37 :                                : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
    8628         181 :                         : poSrcDS->GetRasterBand(1)->GetOverview(i);
    8629             :                 const double dfOvrPixels =
    8630         126 :                     static_cast<double>(poOvrBand->GetXSize()) *
    8631         126 :                     poOvrBand->GetYSize();
    8632         126 :                 dfTotalPixels += dfOvrPixels * l_nBands;
    8633         234 :                 if (poOvrBand->GetMaskFlags() == GMF_PER_DATASET ||
    8634         108 :                     poMaskOvrDS != nullptr)
    8635             :                 {
    8636          32 :                     dfTotalPixels += dfOvrPixels;
    8637             :                 }
    8638          94 :                 else if (i == 0 && poDS->GetRasterBand(1)->GetMaskFlags() ==
    8639             :                                        GMF_PER_DATASET)
    8640             :                 {
    8641           1 :                     ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
    8642             :                                 "Source dataset has a mask band on full "
    8643             :                                 "resolution, overviews on the regular bands, "
    8644             :                                 "but lacks overviews on the mask band.");
    8645             :                 }
    8646             :             }
    8647             : 
    8648             :             // Now copy the imagery.
    8649             :             // Begin with the smallest overview.
    8650          69 :             for (int iOvrLevel = nSrcOverviews - 1;
    8651         194 :                  eErr == CE_None && iOvrLevel >= 0; --iOvrLevel)
    8652             :             {
    8653         125 :                 auto poDstDS = poDS->m_apoOverviewDS[iOvrLevel].get();
    8654             : 
    8655             :                 // Create a fake dataset with the source overview level so that
    8656             :                 // GDALDatasetCopyWholeRaster can cope with it.
    8657             :                 GDALDataset *poSrcOvrDS =
    8658             :                     poOvrDS
    8659         162 :                         ? (iOvrLevel == 0 ? poOvrDS.get()
    8660          37 :                                           : GDALCreateOverviewDataset(
    8661             :                                                 poOvrDS.get(), iOvrLevel - 1,
    8662             :                                                 /* bThisLevelOnly = */ true))
    8663          54 :                         : GDALCreateOverviewDataset(
    8664             :                               poSrcDS, iOvrLevel,
    8665         125 :                               /* bThisLevelOnly = */ true);
    8666             :                 GDALRasterBand *poSrcOvrBand =
    8667         196 :                     poOvrDS ? (iOvrLevel == 0
    8668          71 :                                    ? poOvrDS->GetRasterBand(1)
    8669          74 :                                    : poOvrDS->GetRasterBand(1)->GetOverview(
    8670          37 :                                          iOvrLevel - 1))
    8671         179 :                             : poSrcDS->GetRasterBand(1)->GetOverview(iOvrLevel);
    8672             :                 double dfNextCurPixels =
    8673             :                     dfCurPixels +
    8674         125 :                     static_cast<double>(poSrcOvrBand->GetXSize()) *
    8675         125 :                         poSrcOvrBand->GetYSize() * l_nBands;
    8676             : 
    8677         125 :                 poDstDS->m_bBlockOrderRowMajor = true;
    8678         125 :                 poDstDS->m_bLeaderSizeAsUInt4 = true;
    8679         125 :                 poDstDS->m_bTrailerRepeatedLast4BytesRepeated = true;
    8680         125 :                 poDstDS->m_bFillEmptyTilesAtClosing =
    8681         125 :                     poDS->m_bFillEmptyTilesAtClosing;
    8682         125 :                 poDstDS->m_bWriteEmptyTiles = poDS->m_bWriteEmptyTiles;
    8683         125 :                 poDstDS->m_bTileInterleave = poDS->m_bTileInterleave;
    8684         125 :                 GDALRasterBand *poSrcMaskBand = nullptr;
    8685         125 :                 if (poDstDS->m_poMaskDS)
    8686             :                 {
    8687          32 :                     poDstDS->m_poMaskDS->m_bBlockOrderRowMajor = true;
    8688          32 :                     poDstDS->m_poMaskDS->m_bLeaderSizeAsUInt4 = true;
    8689          32 :                     poDstDS->m_poMaskDS->m_bTrailerRepeatedLast4BytesRepeated =
    8690             :                         true;
    8691          64 :                     poDstDS->m_poMaskDS->m_bFillEmptyTilesAtClosing =
    8692          32 :                         poDS->m_bFillEmptyTilesAtClosing;
    8693          64 :                     poDstDS->m_poMaskDS->m_bWriteEmptyTiles =
    8694          32 :                         poDS->m_bWriteEmptyTiles;
    8695             : 
    8696          32 :                     poSrcMaskBand =
    8697             :                         poMaskOvrDS
    8698          46 :                             ? (iOvrLevel == 0
    8699          14 :                                    ? poMaskOvrDS->GetRasterBand(1)
    8700          16 :                                    : poMaskOvrDS->GetRasterBand(1)->GetOverview(
    8701           8 :                                          iOvrLevel - 1))
    8702          50 :                             : poSrcOvrBand->GetMaskBand();
    8703             :                 }
    8704             : 
    8705         125 :                 if (poDstDS->m_poMaskDS)
    8706             :                 {
    8707          32 :                     dfNextCurPixels +=
    8708          32 :                         static_cast<double>(poSrcOvrBand->GetXSize()) *
    8709          32 :                         poSrcOvrBand->GetYSize();
    8710             :                 }
    8711             :                 void *pScaledData =
    8712         125 :                     GDALCreateScaledProgress(dfCurPixels / dfTotalPixels,
    8713             :                                              dfNextCurPixels / dfTotalPixels,
    8714             :                                              pfnProgress, pProgressData);
    8715             : 
    8716         125 :                 eErr = CopyImageryAndMask(poDstDS, poSrcOvrDS, poSrcMaskBand,
    8717             :                                           GDALScaledProgress, pScaledData);
    8718             : 
    8719         125 :                 dfCurPixels = dfNextCurPixels;
    8720         125 :                 GDALDestroyScaledProgress(pScaledData);
    8721             : 
    8722         125 :                 if (poSrcOvrDS != poOvrDS.get())
    8723          91 :                     delete poSrcOvrDS;
    8724         125 :                 poSrcOvrDS = nullptr;
    8725             :             }
    8726             :         }
    8727             :     }
    8728             : 
    8729             :     /* -------------------------------------------------------------------- */
    8730             :     /*      Copy actual imagery.                                            */
    8731             :     /* -------------------------------------------------------------------- */
    8732        2096 :     double dfNextCurPixels =
    8733        2096 :         dfCurPixels + static_cast<double>(nXSize) * nYSize * l_nBands;
    8734        2096 :     void *pScaledData = GDALCreateScaledProgress(
    8735             :         dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
    8736             :         pfnProgress, pProgressData);
    8737             : 
    8738             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8739        2096 :     bool bTryCopy = true;
    8740             : #endif
    8741             : 
    8742             : #ifdef HAVE_LIBJPEG
    8743        2096 :     if (bCopyFromJPEG)
    8744             :     {
    8745          12 :         eErr = GTIFF_CopyFromJPEG(poDS.get(), poSrcDS, pfnProgress,
    8746             :                                   pProgressData, bTryCopy);
    8747             : 
    8748             :         // In case of failure in the decompression step, try normal copy.
    8749          12 :         if (bTryCopy)
    8750           0 :             eErr = CE_None;
    8751             :     }
    8752             : #endif
    8753             : 
    8754             : #ifdef JPEG_DIRECT_COPY
    8755             :     if (bDirectCopyFromJPEG)
    8756             :     {
    8757             :         eErr = GTIFF_DirectCopyFromJPEG(poDS.get(), poSrcDS, pfnProgress,
    8758             :                                         pProgressData, bTryCopy);
    8759             : 
    8760             :         // In case of failure in the reading step, try normal copy.
    8761             :         if (bTryCopy)
    8762             :             eErr = CE_None;
    8763             :     }
    8764             : #endif
    8765             : 
    8766        2096 :     bool bWriteMask = true;
    8767        2096 :     if (
    8768             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8769        4180 :         bTryCopy &&
    8770             : #endif
    8771        2084 :         (poDS->m_bTreatAsSplit || poDS->m_bTreatAsSplitBitmap))
    8772             :     {
    8773             :         // For split bands, we use TIFFWriteScanline() interface.
    8774           9 :         CPLAssert(poDS->m_nBitsPerSample == 8 || poDS->m_nBitsPerSample == 1);
    8775             : 
    8776           9 :         if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG && poDS->nBands > 1)
    8777             :         {
    8778             :             GByte *pabyScanline = static_cast<GByte *>(
    8779           3 :                 VSI_MALLOC_VERBOSE(TIFFScanlineSize(l_hTIFF)));
    8780           3 :             if (pabyScanline == nullptr)
    8781           0 :                 eErr = CE_Failure;
    8782        9052 :             for (int j = 0; j < nYSize && eErr == CE_None; ++j)
    8783             :             {
    8784       18098 :                 eErr = poSrcDS->RasterIO(GF_Read, 0, j, nXSize, 1, pabyScanline,
    8785             :                                          nXSize, 1, GDT_UInt8, l_nBands,
    8786        9049 :                                          nullptr, poDS->nBands, 0, 1, nullptr);
    8787       18098 :                 if (eErr == CE_None &&
    8788        9049 :                     TIFFWriteScanline(l_hTIFF, pabyScanline, j, 0) == -1)
    8789             :                 {
    8790           0 :                     ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    8791             :                                 "TIFFWriteScanline() failed.");
    8792           0 :                     eErr = CE_Failure;
    8793             :                 }
    8794        9049 :                 if (!GDALScaledProgress((j + 1) * 1.0 / nYSize, nullptr,
    8795             :                                         pScaledData))
    8796           0 :                     eErr = CE_Failure;
    8797             :             }
    8798           3 :             CPLFree(pabyScanline);
    8799             :         }
    8800             :         else
    8801             :         {
    8802             :             GByte *pabyScanline =
    8803           6 :                 static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
    8804           6 :             if (pabyScanline == nullptr)
    8805           0 :                 eErr = CE_Failure;
    8806             :             else
    8807           6 :                 eErr = CE_None;
    8808          14 :             for (int iBand = 1; iBand <= l_nBands && eErr == CE_None; ++iBand)
    8809             :             {
    8810       48211 :                 for (int j = 0; j < nYSize && eErr == CE_None; ++j)
    8811             :                 {
    8812       48203 :                     eErr = poSrcDS->GetRasterBand(iBand)->RasterIO(
    8813             :                         GF_Read, 0, j, nXSize, 1, pabyScanline, nXSize, 1,
    8814             :                         GDT_UInt8, 0, 0, nullptr);
    8815       48203 :                     if (poDS->m_bTreatAsSplitBitmap)
    8816             :                     {
    8817     7225210 :                         for (int i = 0; i < nXSize; ++i)
    8818             :                         {
    8819     7216010 :                             const GByte byVal = pabyScanline[i];
    8820     7216010 :                             if ((i & 0x7) == 0)
    8821      902001 :                                 pabyScanline[i >> 3] = 0;
    8822     7216010 :                             if (byVal)
    8823     7097220 :                                 pabyScanline[i >> 3] |= 0x80 >> (i & 0x7);
    8824             :                         }
    8825             :                     }
    8826       96406 :                     if (eErr == CE_None &&
    8827       48203 :                         TIFFWriteScanline(l_hTIFF, pabyScanline, j,
    8828       48203 :                                           static_cast<uint16_t>(iBand - 1)) ==
    8829             :                             -1)
    8830             :                     {
    8831           0 :                         ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
    8832             :                                     "TIFFWriteScanline() failed.");
    8833           0 :                         eErr = CE_Failure;
    8834             :                     }
    8835       48203 :                     if (!GDALScaledProgress((j + 1 + (iBand - 1) * nYSize) *
    8836       48203 :                                                 1.0 / (l_nBands * nYSize),
    8837             :                                             nullptr, pScaledData))
    8838           0 :                         eErr = CE_Failure;
    8839             :                 }
    8840             :             }
    8841           6 :             CPLFree(pabyScanline);
    8842             :         }
    8843             : 
    8844             :         // Necessary to be able to read the file without re-opening.
    8845           9 :         TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(l_hTIFF);
    8846             : 
    8847           9 :         TIFFFlushData(l_hTIFF);
    8848             : 
    8849           9 :         toff_t nNewDirOffset = pfnSizeProc(TIFFClientdata(l_hTIFF));
    8850           9 :         if ((nNewDirOffset % 2) == 1)
    8851           5 :             ++nNewDirOffset;
    8852             : 
    8853           9 :         TIFFFlush(l_hTIFF);
    8854             : 
    8855           9 :         if (poDS->m_nDirOffset != TIFFCurrentDirOffset(l_hTIFF))
    8856             :         {
    8857           0 :             poDS->m_nDirOffset = nNewDirOffset;
    8858           0 :             CPLDebug("GTiff", "directory moved during flush.");
    8859             :         }
    8860             :     }
    8861        2087 :     else if (
    8862             : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
    8863        2075 :         bTryCopy &&
    8864             : #endif
    8865             :         eErr == CE_None)
    8866             :     {
    8867        2074 :         const char *papszCopyWholeRasterOptions[3] = {nullptr, nullptr,
    8868             :                                                       nullptr};
    8869        2074 :         int iNextOption = 0;
    8870        2074 :         papszCopyWholeRasterOptions[iNextOption++] = "SKIP_HOLES=YES";
    8871        2074 :         if (l_nCompression != COMPRESSION_NONE)
    8872             :         {
    8873         490 :             papszCopyWholeRasterOptions[iNextOption++] = "COMPRESSED=YES";
    8874             :         }
    8875             : 
    8876             :         // For streaming with separate, we really want that bands are written
    8877             :         // after each other, even if the source is pixel interleaved.
    8878        1584 :         else if (bStreaming && poDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE)
    8879             :         {
    8880           1 :             papszCopyWholeRasterOptions[iNextOption++] = "INTERLEAVE=BAND";
    8881             :         }
    8882             : 
    8883        2074 :         if (bCopySrcOverviews || bTileInterleaving)
    8884             :         {
    8885         219 :             poDS->m_bBlockOrderRowMajor = true;
    8886         219 :             poDS->m_bLeaderSizeAsUInt4 = bCopySrcOverviews;
    8887         219 :             poDS->m_bTrailerRepeatedLast4BytesRepeated = bCopySrcOverviews;
    8888         219 :             if (poDS->m_poMaskDS)
    8889             :             {
    8890          27 :                 poDS->m_poMaskDS->m_bBlockOrderRowMajor = true;
    8891          27 :                 poDS->m_poMaskDS->m_bLeaderSizeAsUInt4 = bCopySrcOverviews;
    8892          27 :                 poDS->m_poMaskDS->m_bTrailerRepeatedLast4BytesRepeated =
    8893             :                     bCopySrcOverviews;
    8894          27 :                 GDALDestroyScaledProgress(pScaledData);
    8895             :                 pScaledData =
    8896          27 :                     GDALCreateScaledProgress(dfCurPixels / dfTotalPixels, 1.0,
    8897             :                                              pfnProgress, pProgressData);
    8898             :             }
    8899             : 
    8900         219 :             eErr = CopyImageryAndMask(poDS.get(), poSrcDS,
    8901         219 :                                       poSrcDS->GetRasterBand(1)->GetMaskBand(),
    8902             :                                       GDALScaledProgress, pScaledData);
    8903         219 :             if (poDS->m_poMaskDS)
    8904             :             {
    8905          27 :                 bWriteMask = false;
    8906             :             }
    8907             :         }
    8908             :         else
    8909             :         {
    8910        1855 :             eErr = GDALDatasetCopyWholeRaster(GDALDataset::ToHandle(poSrcDS),
    8911        1855 :                                               GDALDataset::ToHandle(poDS.get()),
    8912             :                                               papszCopyWholeRasterOptions,
    8913             :                                               GDALScaledProgress, pScaledData);
    8914             :         }
    8915             :     }
    8916             : 
    8917        2096 :     GDALDestroyScaledProgress(pScaledData);
    8918             : 
    8919        2096 :     if (eErr == CE_None && !bStreaming && bWriteMask)
    8920             :     {
    8921        2047 :         pScaledData = GDALCreateScaledProgress(dfNextCurPixels / dfTotalPixels,
    8922             :                                                1.0, pfnProgress, pProgressData);
    8923        2047 :         if (poDS->m_poMaskDS)
    8924             :         {
    8925          10 :             const char *l_papszOptions[2] = {"COMPRESSED=YES", nullptr};
    8926          10 :             eErr = GDALRasterBandCopyWholeRaster(
    8927          10 :                 poSrcDS->GetRasterBand(1)->GetMaskBand(),
    8928          10 :                 poDS->GetRasterBand(1)->GetMaskBand(),
    8929             :                 const_cast<char **>(l_papszOptions), GDALScaledProgress,
    8930             :                 pScaledData);
    8931             :         }
    8932             :         else
    8933             :         {
    8934        2037 :             eErr = GDALDriver::DefaultCopyMasks(poSrcDS, poDS.get(), bStrict,
    8935             :                                                 nullptr, GDALScaledProgress,
    8936             :                                                 pScaledData);
    8937             :         }
    8938        2047 :         GDALDestroyScaledProgress(pScaledData);
    8939             :     }
    8940             : 
    8941        2096 :     poDS->m_bWriteCOGLayout = false;
    8942             : 
    8943        4174 :     if (eErr == CE_None &&
    8944        2078 :         CPLTestBool(CSLFetchNameValueDef(poDS->m_papszCreationOptions,
    8945             :                                          "@FLUSHCACHE", "NO")))
    8946             :     {
    8947         172 :         if (poDS->FlushCache(false) != CE_None)
    8948             :         {
    8949           0 :             eErr = CE_Failure;
    8950             :         }
    8951             :     }
    8952             : 
    8953        2096 :     if (eErr == CE_Failure)
    8954             :     {
    8955          18 :         if (CPLTestBool(CPLGetConfigOption("GTIFF_DELETE_ON_ERROR", "YES")))
    8956             :         {
    8957          17 :             l_fpL->CancelCreation();
    8958          17 :             poDS.reset();
    8959             : 
    8960          17 :             if (!bStreaming)
    8961             :             {
    8962             :                 // Should really delete more carefully.
    8963          17 :                 VSIUnlink(pszFilename);
    8964             :             }
    8965             :         }
    8966             :         else
    8967             :         {
    8968           1 :             poDS.reset();
    8969             :         }
    8970             :     }
    8971             : 
    8972        2096 :     return poDS.release();
    8973             : }
    8974             : 
    8975             : /************************************************************************/
    8976             : /*                           SetSpatialRef()                            */
    8977             : /************************************************************************/
    8978             : 
    8979        1488 : CPLErr GTiffDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    8980             : 
    8981             : {
    8982        1488 :     if (m_bStreamingOut && m_bCrystalized)
    8983             :     {
    8984           1 :         ReportError(CE_Failure, CPLE_NotSupported,
    8985             :                     "Cannot modify projection at that point in "
    8986             :                     "a streamed output file");
    8987           1 :         return CE_Failure;
    8988             :     }
    8989             : 
    8990        1487 :     LoadGeoreferencingAndPamIfNeeded();
    8991        1487 :     LookForProjection();
    8992             : 
    8993        1487 :     CPLErr eErr = CE_None;
    8994        1487 :     if (eAccess == GA_Update)
    8995             :     {
    8996        1489 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    8997           7 :             (GetPamFlags() & GPF_DISABLED) == 0)
    8998             :         {
    8999           7 :             eErr = GDALPamDataset::SetSpatialRef(poSRS);
    9000             :         }
    9001             :         else
    9002             :         {
    9003        1475 :             if (GDALPamDataset::GetSpatialRef() != nullptr)
    9004             :             {
    9005             :                 // Cancel any existing SRS from PAM file.
    9006           1 :                 GDALPamDataset::SetSpatialRef(nullptr);
    9007             :             }
    9008        1475 :             m_bGeoTIFFInfoChanged = true;
    9009             :         }
    9010             :     }
    9011             :     else
    9012             :     {
    9013           5 :         CPLDebug("GTIFF", "SetSpatialRef() goes to PAM instead of TIFF tags");
    9014           5 :         eErr = GDALPamDataset::SetSpatialRef(poSRS);
    9015             :     }
    9016             : 
    9017        1487 :     if (eErr == CE_None)
    9018             :     {
    9019        1487 :         if (poSRS == nullptr || poSRS->IsEmpty())
    9020             :         {
    9021          14 :             if (!m_oSRS.IsEmpty())
    9022             :             {
    9023           4 :                 m_bForceUnsetProjection = true;
    9024             :             }
    9025          14 :             m_oSRS.Clear();
    9026             :         }
    9027             :         else
    9028             :         {
    9029        1473 :             m_oSRS = *poSRS;
    9030        1473 :             m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    9031             :         }
    9032             :     }
    9033             : 
    9034        1487 :     return eErr;
    9035             : }
    9036             : 
    9037             : /************************************************************************/
    9038             : /*                          SetGeoTransform()                           */
    9039             : /************************************************************************/
    9040             : 
    9041        1791 : CPLErr GTiffDataset::SetGeoTransform(const GDALGeoTransform &gt)
    9042             : 
    9043             : {
    9044        1791 :     if (m_bStreamingOut && m_bCrystalized)
    9045             :     {
    9046           1 :         ReportError(CE_Failure, CPLE_NotSupported,
    9047             :                     "Cannot modify geotransform at that point in a "
    9048             :                     "streamed output file");
    9049           1 :         return CE_Failure;
    9050             :     }
    9051             : 
    9052        1790 :     LoadGeoreferencingAndPamIfNeeded();
    9053             : 
    9054        1790 :     CPLErr eErr = CE_None;
    9055        1790 :     if (eAccess == GA_Update)
    9056             :     {
    9057        1784 :         if (!m_aoGCPs.empty())
    9058             :         {
    9059           1 :             ReportError(CE_Warning, CPLE_AppDefined,
    9060             :                         "GCPs previously set are going to be cleared "
    9061             :                         "due to the setting of a geotransform.");
    9062           1 :             m_bForceUnsetGTOrGCPs = true;
    9063           1 :             m_aoGCPs.clear();
    9064             :         }
    9065        1991 :         else if (gt[0] == 0.0 && gt[1] == 0.0 && gt[2] == 0.0 && gt[3] == 0.0 &&
    9066        1991 :                  gt[4] == 0.0 && gt[5] == 0.0)
    9067             :         {
    9068           2 :             if (m_bGeoTransformValid)
    9069             :             {
    9070           2 :                 m_bForceUnsetGTOrGCPs = true;
    9071           2 :                 m_bGeoTIFFInfoChanged = true;
    9072             :             }
    9073           2 :             m_bGeoTransformValid = false;
    9074           2 :             m_gt = gt;
    9075           2 :             return CE_None;
    9076             :         }
    9077             : 
    9078        3573 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    9079           9 :             !CPLFetchBool(m_papszCreationOptions, "TFW", false) &&
    9080        1796 :             !CPLFetchBool(m_papszCreationOptions, "WORLDFILE", false) &&
    9081           5 :             (GetPamFlags() & GPF_DISABLED) == 0)
    9082             :         {
    9083           5 :             eErr = GDALPamDataset::SetGeoTransform(gt);
    9084             :         }
    9085             :         else
    9086             :         {
    9087             :             // Cancel any existing geotransform from PAM file.
    9088        1777 :             GDALPamDataset::DeleteGeoTransform();
    9089        1777 :             m_bGeoTIFFInfoChanged = true;
    9090             :         }
    9091             :     }
    9092             :     else
    9093             :     {
    9094           6 :         CPLDebug("GTIFF", "SetGeoTransform() goes to PAM instead of TIFF tags");
    9095           6 :         eErr = GDALPamDataset::SetGeoTransform(gt);
    9096             :     }
    9097             : 
    9098        1788 :     if (eErr == CE_None)
    9099             :     {
    9100        1788 :         m_gt = gt;
    9101        1788 :         m_bGeoTransformValid = true;
    9102             :     }
    9103             : 
    9104        1788 :     return eErr;
    9105             : }
    9106             : 
    9107             : /************************************************************************/
    9108             : /*                               SetGCPs()                              */
    9109             : /************************************************************************/
    9110             : 
    9111          23 : CPLErr GTiffDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
    9112             :                              const OGRSpatialReference *poGCPSRS)
    9113             : {
    9114          23 :     CPLErr eErr = CE_None;
    9115          23 :     LoadGeoreferencingAndPamIfNeeded();
    9116          23 :     LookForProjection();
    9117             : 
    9118          23 :     if (eAccess == GA_Update)
    9119             :     {
    9120          21 :         if (!m_aoGCPs.empty() && nGCPCountIn == 0)
    9121             :         {
    9122           3 :             m_bForceUnsetGTOrGCPs = true;
    9123             :         }
    9124          18 :         else if (nGCPCountIn > 0 && m_bGeoTransformValid)
    9125             :         {
    9126           5 :             ReportError(CE_Warning, CPLE_AppDefined,
    9127             :                         "A geotransform previously set is going to be cleared "
    9128             :                         "due to the setting of GCPs.");
    9129           5 :             m_gt = GDALGeoTransform();
    9130           5 :             m_bGeoTransformValid = false;
    9131           5 :             m_bForceUnsetGTOrGCPs = true;
    9132             :         }
    9133          21 :         if ((m_eProfile == GTiffProfile::BASELINE) &&
    9134           0 :             (GetPamFlags() & GPF_DISABLED) == 0)
    9135             :         {
    9136           0 :             eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn, poGCPSRS);
    9137             :         }
    9138             :         else
    9139             :         {
    9140          21 :             if (nGCPCountIn > knMAX_GCP_COUNT)
    9141             :             {
    9142           2 :                 if (GDALPamDataset::GetGCPCount() == 0 && !m_aoGCPs.empty())
    9143             :                 {
    9144           1 :                     m_bForceUnsetGTOrGCPs = true;
    9145             :                 }
    9146           2 :                 ReportError(CE_Warning, CPLE_AppDefined,
    9147             :                             "Trying to write %d GCPs, whereas the maximum "
    9148             :                             "supported in GeoTIFF tag is %d. "
    9149             :                             "Falling back to writing them to PAM",
    9150             :                             nGCPCountIn, knMAX_GCP_COUNT);
    9151           2 :                 eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn,
    9152             :                                                poGCPSRS);
    9153             :             }
    9154          19 :             else if (GDALPamDataset::GetGCPCount() > 0)
    9155             :             {
    9156             :                 // Cancel any existing GCPs from PAM file.
    9157           1 :                 GDALPamDataset::SetGCPs(
    9158             :                     0, nullptr,
    9159             :                     static_cast<const OGRSpatialReference *>(nullptr));
    9160             :             }
    9161          21 :             m_bGeoTIFFInfoChanged = true;
    9162             :         }
    9163             :     }
    9164             :     else
    9165             :     {
    9166           2 :         CPLDebug("GTIFF", "SetGCPs() goes to PAM instead of TIFF tags");
    9167           2 :         eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn, poGCPSRS);
    9168             :     }
    9169             : 
    9170          23 :     if (eErr == CE_None)
    9171             :     {
    9172          23 :         if (poGCPSRS == nullptr || poGCPSRS->IsEmpty())
    9173             :         {
    9174          12 :             if (!m_oSRS.IsEmpty())
    9175             :             {
    9176           5 :                 m_bForceUnsetProjection = true;
    9177             :             }
    9178          12 :             m_oSRS.Clear();
    9179             :         }
    9180             :         else
    9181             :         {
    9182          11 :             m_oSRS = *poGCPSRS;
    9183          11 :             m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    9184             :         }
    9185             : 
    9186          23 :         m_aoGCPs = gdal::GCP::fromC(pasGCPListIn, nGCPCountIn);
    9187             :     }
    9188             : 
    9189          23 :     return eErr;
    9190             : }
    9191             : 
    9192             : /************************************************************************/
    9193             : /*                            SetMetadata()                             */
    9194             : /************************************************************************/
    9195        2680 : CPLErr GTiffDataset::SetMetadata(char **papszMD, const char *pszDomain)
    9196             : 
    9197             : {
    9198        2680 :     LoadGeoreferencingAndPamIfNeeded();
    9199             : 
    9200        2680 :     if (m_bStreamingOut && m_bCrystalized)
    9201             :     {
    9202           1 :         ReportError(
    9203             :             CE_Failure, CPLE_NotSupported,
    9204             :             "Cannot modify metadata at that point in a streamed output file");
    9205           1 :         return CE_Failure;
    9206             :     }
    9207             : 
    9208        2679 :     CPLErr eErr = CE_None;
    9209        2679 :     if (eAccess == GA_Update)
    9210             :     {
    9211        2677 :         if (pszDomain != nullptr && EQUAL(pszDomain, MD_DOMAIN_RPC))
    9212             :         {
    9213             :             // So that a subsequent GetMetadata() wouldn't override our new
    9214             :             // values
    9215          22 :             LoadMetadata();
    9216          22 :             m_bForceUnsetRPC = (CSLCount(papszMD) == 0);
    9217             :         }
    9218             : 
    9219        2677 :         if ((papszMD != nullptr) && (pszDomain != nullptr) &&
    9220        1849 :             EQUAL(pszDomain, "COLOR_PROFILE"))
    9221             :         {
    9222           0 :             m_bColorProfileMetadataChanged = true;
    9223             :         }
    9224        2677 :         else if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
    9225             :         {
    9226        2677 :             m_bMetadataChanged = true;
    9227             :             // Cancel any existing metadata from PAM file.
    9228        2677 :             if (GDALPamDataset::GetMetadata(pszDomain) != nullptr)
    9229           1 :                 GDALPamDataset::SetMetadata(nullptr, pszDomain);
    9230             :         }
    9231             : 
    9232        5319 :         if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    9233        2642 :             CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT) != nullptr)
    9234             :         {
    9235        2011 :             const char *pszPrevValue = GetMetadataItem(GDALMD_AREA_OR_POINT);
    9236             :             const char *pszNewValue =
    9237        2011 :                 CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT);
    9238        2011 :             if (pszPrevValue == nullptr || pszNewValue == nullptr ||
    9239        1592 :                 !EQUAL(pszPrevValue, pszNewValue))
    9240             :             {
    9241         423 :                 LookForProjection();
    9242         423 :                 m_bGeoTIFFInfoChanged = true;
    9243             :             }
    9244             :         }
    9245             : 
    9246        2677 :         if (pszDomain != nullptr && EQUAL(pszDomain, "xml:XMP"))
    9247             :         {
    9248           2 :             if (papszMD != nullptr && *papszMD != nullptr)
    9249             :             {
    9250           1 :                 int nTagSize = static_cast<int>(strlen(*papszMD));
    9251           1 :                 TIFFSetField(m_hTIFF, TIFFTAG_XMLPACKET, nTagSize, *papszMD);
    9252             :             }
    9253             :             else
    9254             :             {
    9255           1 :                 TIFFUnsetField(m_hTIFF, TIFFTAG_XMLPACKET);
    9256             :             }
    9257             :         }
    9258             :     }
    9259             :     else
    9260             :     {
    9261           2 :         CPLDebug(
    9262             :             "GTIFF",
    9263             :             "GTiffDataset::SetMetadata() goes to PAM instead of TIFF tags");
    9264           2 :         eErr = GDALPamDataset::SetMetadata(papszMD, pszDomain);
    9265             :     }
    9266             : 
    9267        2679 :     if (eErr == CE_None)
    9268             :     {
    9269        2679 :         eErr = m_oGTiffMDMD.SetMetadata(papszMD, pszDomain);
    9270             :     }
    9271        2679 :     return eErr;
    9272             : }
    9273             : 
    9274             : /************************************************************************/
    9275             : /*                          SetMetadataItem()                           */
    9276             : /************************************************************************/
    9277             : 
    9278        5738 : CPLErr GTiffDataset::SetMetadataItem(const char *pszName, const char *pszValue,
    9279             :                                      const char *pszDomain)
    9280             : 
    9281             : {
    9282        5738 :     LoadGeoreferencingAndPamIfNeeded();
    9283             : 
    9284        5738 :     if (m_bStreamingOut && m_bCrystalized)
    9285             :     {
    9286           1 :         ReportError(
    9287             :             CE_Failure, CPLE_NotSupported,
    9288             :             "Cannot modify metadata at that point in a streamed output file");
    9289           1 :         return CE_Failure;
    9290             :     }
    9291             : 
    9292        5737 :     CPLErr eErr = CE_None;
    9293        5737 :     if (eAccess == GA_Update)
    9294             :     {
    9295        5730 :         if ((pszDomain != nullptr) && EQUAL(pszDomain, "COLOR_PROFILE"))
    9296             :         {
    9297           8 :             m_bColorProfileMetadataChanged = true;
    9298             :         }
    9299        5722 :         else if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
    9300             :         {
    9301        5722 :             m_bMetadataChanged = true;
    9302             :             // Cancel any existing metadata from PAM file.
    9303        5722 :             if (GDALPamDataset::GetMetadataItem(pszName, pszDomain) != nullptr)
    9304           1 :                 GDALPamDataset::SetMetadataItem(pszName, nullptr, pszDomain);
    9305             :         }
    9306             : 
    9307        5730 :         if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    9308          82 :             pszName != nullptr && EQUAL(pszName, GDALMD_AREA_OR_POINT))
    9309             :         {
    9310           7 :             LookForProjection();
    9311           7 :             m_bGeoTIFFInfoChanged = true;
    9312             :         }
    9313             :     }
    9314             :     else
    9315             :     {
    9316           7 :         CPLDebug(
    9317             :             "GTIFF",
    9318             :             "GTiffDataset::SetMetadataItem() goes to PAM instead of TIFF tags");
    9319           7 :         eErr = GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    9320             :     }
    9321             : 
    9322        5737 :     if (eErr == CE_None)
    9323             :     {
    9324        5737 :         eErr = m_oGTiffMDMD.SetMetadataItem(pszName, pszValue, pszDomain);
    9325             :     }
    9326             : 
    9327        5737 :     return eErr;
    9328             : }
    9329             : 
    9330             : /************************************************************************/
    9331             : /*                         CreateMaskBand()                             */
    9332             : /************************************************************************/
    9333             : 
    9334          98 : CPLErr GTiffDataset::CreateMaskBand(int nFlagsIn)
    9335             : {
    9336          98 :     ScanDirectories();
    9337             : 
    9338          98 :     if (m_poMaskDS != nullptr)
    9339             :     {
    9340           1 :         ReportError(CE_Failure, CPLE_AppDefined,
    9341             :                     "This TIFF dataset has already an internal mask band");
    9342           1 :         return CE_Failure;
    9343             :     }
    9344          97 :     else if (MustCreateInternalMask())
    9345             :     {
    9346          84 :         if (nFlagsIn != GMF_PER_DATASET)
    9347             :         {
    9348           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    9349             :                         "The only flag value supported for internal mask is "
    9350             :                         "GMF_PER_DATASET");
    9351           1 :             return CE_Failure;
    9352             :         }
    9353             : 
    9354          83 :         int l_nCompression = COMPRESSION_PACKBITS;
    9355          83 :         if (strstr(GDALGetMetadataItem(GDALGetDriverByName("GTiff"),
    9356             :                                        GDAL_DMD_CREATIONOPTIONLIST, nullptr),
    9357          83 :                    "<Value>DEFLATE</Value>") != nullptr)
    9358          83 :             l_nCompression = COMPRESSION_ADOBE_DEFLATE;
    9359             : 
    9360             :         /* --------------------------------------------------------------------
    9361             :          */
    9362             :         /*      If we don't have read access, then create the mask externally.
    9363             :          */
    9364             :         /* --------------------------------------------------------------------
    9365             :          */
    9366          83 :         if (GetAccess() != GA_Update)
    9367             :         {
    9368           1 :             ReportError(CE_Warning, CPLE_AppDefined,
    9369             :                         "File open for read-only accessing, "
    9370             :                         "creating mask externally.");
    9371             : 
    9372           1 :             return GDALPamDataset::CreateMaskBand(nFlagsIn);
    9373             :         }
    9374             : 
    9375          82 :         if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
    9376           0 :             !m_bWriteKnownIncompatibleEdition)
    9377             :         {
    9378           0 :             ReportError(CE_Warning, CPLE_AppDefined,
    9379             :                         "Adding a mask invalidates the "
    9380             :                         "LAYOUT=IFDS_BEFORE_DATA property");
    9381           0 :             m_bKnownIncompatibleEdition = true;
    9382           0 :             m_bWriteKnownIncompatibleEdition = true;
    9383             :         }
    9384             : 
    9385          82 :         bool bIsOverview = false;
    9386          82 :         uint32_t nSubType = 0;
    9387          82 :         if (TIFFGetField(m_hTIFF, TIFFTAG_SUBFILETYPE, &nSubType))
    9388             :         {
    9389           8 :             bIsOverview = (nSubType & FILETYPE_REDUCEDIMAGE) != 0;
    9390             : 
    9391           8 :             if ((nSubType & FILETYPE_MASK) != 0)
    9392             :             {
    9393           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
    9394             :                             "Cannot create a mask on a TIFF mask IFD !");
    9395           0 :                 return CE_Failure;
    9396             :             }
    9397             :         }
    9398             : 
    9399          82 :         const int bIsTiled = TIFFIsTiled(m_hTIFF);
    9400             : 
    9401          82 :         FlushDirectory();
    9402             : 
    9403          82 :         const toff_t nOffset = GTIFFWriteDirectory(
    9404             :             m_hTIFF,
    9405             :             bIsOverview ? FILETYPE_REDUCEDIMAGE | FILETYPE_MASK : FILETYPE_MASK,
    9406             :             nRasterXSize, nRasterYSize, 1, PLANARCONFIG_CONTIG, 1,
    9407             :             m_nBlockXSize, m_nBlockYSize, bIsTiled, l_nCompression,
    9408             :             PHOTOMETRIC_MASK, PREDICTOR_NONE, SAMPLEFORMAT_UINT, nullptr,
    9409             :             nullptr, nullptr, 0, nullptr, "", nullptr, nullptr, nullptr,
    9410          82 :             nullptr, m_bWriteCOGLayout);
    9411             : 
    9412          82 :         ReloadDirectory();
    9413             : 
    9414          82 :         if (nOffset == 0)
    9415           0 :             return CE_Failure;
    9416             : 
    9417          82 :         m_poMaskDS = std::make_shared<GTiffDataset>();
    9418          82 :         m_poMaskDS->eAccess = GA_Update;
    9419          82 :         m_poMaskDS->m_poBaseDS = this;
    9420          82 :         m_poMaskDS->m_poImageryDS = this;
    9421          82 :         m_poMaskDS->ShareLockWithParentDataset(this);
    9422          82 :         m_poMaskDS->m_osFilename = m_osFilename;
    9423          82 :         m_poMaskDS->m_bPromoteTo8Bits = CPLTestBool(
    9424             :             CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
    9425          82 :         return m_poMaskDS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nOffset,
    9426          82 :                                       GA_Update);
    9427             :     }
    9428             : 
    9429          13 :     return GDALPamDataset::CreateMaskBand(nFlagsIn);
    9430             : }
    9431             : 
    9432             : /************************************************************************/
    9433             : /*                        MustCreateInternalMask()                      */
    9434             : /************************************************************************/
    9435             : 
    9436         135 : bool GTiffDataset::MustCreateInternalMask()
    9437             : {
    9438         135 :     return CPLTestBool(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", "YES"));
    9439             : }
    9440             : 
    9441             : /************************************************************************/
    9442             : /*                         CreateMaskBand()                             */
    9443             : /************************************************************************/
    9444             : 
    9445          29 : CPLErr GTiffRasterBand::CreateMaskBand(int nFlagsIn)
    9446             : {
    9447          29 :     m_poGDS->ScanDirectories();
    9448             : 
    9449          29 :     if (m_poGDS->m_poMaskDS != nullptr)
    9450             :     {
    9451           5 :         ReportError(CE_Failure, CPLE_AppDefined,
    9452             :                     "This TIFF dataset has already an internal mask band");
    9453           5 :         return CE_Failure;
    9454             :     }
    9455             : 
    9456             :     const char *pszGDAL_TIFF_INTERNAL_MASK =
    9457          24 :         CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", nullptr);
    9458          27 :     if ((pszGDAL_TIFF_INTERNAL_MASK &&
    9459          24 :          CPLTestBool(pszGDAL_TIFF_INTERNAL_MASK)) ||
    9460             :         nFlagsIn == GMF_PER_DATASET)
    9461             :     {
    9462          16 :         return m_poGDS->CreateMaskBand(nFlagsIn);
    9463             :     }
    9464             : 
    9465           8 :     return GDALPamRasterBand::CreateMaskBand(nFlagsIn);
    9466             : }
    9467             : 
    9468             : /************************************************************************/
    9469             : /*                          ClampCTEntry()                              */
    9470             : /************************************************************************/
    9471             : 
    9472      232815 : /* static */ unsigned short GTiffDataset::ClampCTEntry(int iColor, int iComp,
    9473             :                                                        int nCTEntryVal,
    9474             :                                                        int nMultFactor)
    9475             : {
    9476      232815 :     const int nVal = nCTEntryVal * nMultFactor;
    9477      232815 :     if (nVal < 0)
    9478             :     {
    9479           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    9480             :                  "Color table entry [%d][%d] = %d, clamped to 0", iColor, iComp,
    9481             :                  nCTEntryVal);
    9482           0 :         return 0;
    9483             :     }
    9484      232815 :     if (nVal > 65535)
    9485             :     {
    9486           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    9487             :                  "Color table entry [%d][%d] = %d, clamped to 65535", iColor,
    9488             :                  iComp, nCTEntryVal);
    9489           2 :         return 65535;
    9490             :     }
    9491      232813 :     return static_cast<unsigned short>(nVal);
    9492             : }

Generated by: LCOV version 1.14