LCOV - code coverage report
Current view: top level - frmts/gtiff - gtiffdataset_write.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3762 4112 91.5 %
Date: 2025-05-06 02:01:45 Functions: 108 136 79.4 %

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

Generated by: LCOV version 1.14