LCOV - code coverage report
Current view: top level - frmts/gtiff - gtiffdataset_write.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3610 3963 91.1 %
Date: 2024-05-18 15:15:27 Functions: 107 131 81.7 %

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

Generated by: LCOV version 1.14