Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/gtiff/gtiffdataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GeoTIFF Driver
4
 * Purpose:  GDAL GeoTIFF support.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1998, 2002, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2007-2015, Even Rouault <even dot rouault at spatialys dot com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "gtiffdataset.h"
15
#include "gtiffrasterband.h"
16
#include "gtiffjpegoverviewds.h"
17
18
#include <cassert>
19
20
#include <algorithm>
21
#include <limits>
22
#include <memory>
23
#include <set>
24
#include <string>
25
#include <tuple>
26
#include <utility>
27
28
#include "cpl_error.h"
29
#include "cpl_vsi.h"
30
#include "cpl_vsi_virtual.h"
31
#include "cpl_worker_thread_pool.h"
32
#include "gdal_priv.h"
33
#include "ogr_proj_p.h"  // OSRGetProjTLSContext()
34
#include "tif_jxl.h"
35
#include "tifvsi.h"
36
#include "xtiffio.h"
37
38
static const GTIFFTag asTIFFTags[] = {
39
    {"TIFFTAG_DOCUMENTNAME", TIFFTAG_DOCUMENTNAME, GTIFFTAGTYPE_STRING},
40
    {"TIFFTAG_IMAGEDESCRIPTION", TIFFTAG_IMAGEDESCRIPTION, GTIFFTAGTYPE_STRING},
41
    {"TIFFTAG_SOFTWARE", TIFFTAG_SOFTWARE, GTIFFTAGTYPE_STRING},
42
    {"TIFFTAG_DATETIME", TIFFTAG_DATETIME, GTIFFTAGTYPE_STRING},
43
    {"TIFFTAG_ARTIST", TIFFTAG_ARTIST, GTIFFTAGTYPE_STRING},
44
    {"TIFFTAG_HOSTCOMPUTER", TIFFTAG_HOSTCOMPUTER, GTIFFTAGTYPE_STRING},
45
    {"TIFFTAG_COPYRIGHT", TIFFTAG_COPYRIGHT, GTIFFTAGTYPE_STRING},
46
    {"TIFFTAG_XRESOLUTION", TIFFTAG_XRESOLUTION, GTIFFTAGTYPE_FLOAT},
47
    {"TIFFTAG_YRESOLUTION", TIFFTAG_YRESOLUTION, GTIFFTAGTYPE_FLOAT},
48
    // Dealt as special case.
49
    {"TIFFTAG_RESOLUTIONUNIT", TIFFTAG_RESOLUTIONUNIT, GTIFFTAGTYPE_SHORT},
50
    {"TIFFTAG_MINSAMPLEVALUE", TIFFTAG_MINSAMPLEVALUE, GTIFFTAGTYPE_SHORT},
51
    {"TIFFTAG_MAXSAMPLEVALUE", TIFFTAG_MAXSAMPLEVALUE, GTIFFTAGTYPE_SHORT},
52
53
    // GeoTIFF DGIWG tags
54
    {"GEO_METADATA", TIFFTAG_GEO_METADATA, GTIFFTAGTYPE_BYTE_STRING},
55
    {"TIFF_RSID", TIFFTAG_TIFF_RSID, GTIFFTAGTYPE_STRING},
56
    {nullptr, 0, GTIFFTAGTYPE_STRING},
57
};
58
59
/************************************************************************/
60
/*                            GetTIFFTags()                             */
61
/************************************************************************/
62
63
const GTIFFTag *GTiffDataset::GetTIFFTags()
64
198k
{
65
198k
    return asTIFFTags;
66
198k
}
67
68
/************************************************************************/
69
/*                            GTiffDataset()                            */
70
/************************************************************************/
71
72
GTiffDataset::GTiffDataset()
73
213k
    : m_bStreamingIn(false), m_bStreamingOut(false), m_bScanDeferred(true),
74
213k
      m_bSingleIFDOpened(false), m_bLoadedBlockDirty(false),
75
213k
      m_bWriteError(false), m_bLookedForProjection(false),
76
213k
      m_bLookedForMDAreaOrPoint(false), m_bGeoTransformValid(false),
77
213k
      m_bCrystalized(true), m_bGeoTIFFInfoChanged(false),
78
213k
      m_bForceUnsetGTOrGCPs(false), m_bForceUnsetProjection(false),
79
213k
      m_bNoDataChanged(false), m_bNoDataSet(false), m_bNoDataSetAsInt64(false),
80
213k
      m_bNoDataSetAsUInt64(false), m_bMetadataChanged(false),
81
213k
      m_bColorProfileMetadataChanged(false), m_bForceUnsetRPC(false),
82
213k
      m_bNeedsRewrite(false), m_bLoadingOtherBands(false), m_bIsOverview(false),
83
213k
      m_bWriteEmptyTiles(true), m_bFillEmptyTilesAtClosing(false),
84
213k
      m_bTreatAsSplit(false), m_bTreatAsSplitBitmap(false), m_bClipWarn(false),
85
213k
      m_bIMDRPCMetadataLoaded(false), m_bEXIFMetadataLoaded(false),
86
213k
      m_bICCMetadataLoaded(false),
87
213k
      m_bHasWarnedDisableAggressiveBandCaching(false),
88
213k
      m_bDontReloadFirstBlock(false), m_bWebPLossless(false),
89
213k
      m_bPromoteTo8Bits(false),
90
      m_bDebugDontWriteBlocks(
91
213k
          CPLTestBool(CPLGetConfigOption("GTIFF_DONT_WRITE_BLOCKS", "NO"))),
92
213k
      m_bIsFinalized(false),
93
      m_bIgnoreReadErrors(
94
213k
          CPLTestBool(CPLGetConfigOption("GTIFF_IGNORE_READ_ERRORS", "NO"))),
95
213k
      m_bDirectIO(CPLTestBool(CPLGetConfigOption("GTIFF_DIRECT_IO", "NO"))),
96
213k
      m_bReadGeoTransform(false), m_bLoadPam(false), m_bENVIHdrTried(false),
97
213k
      m_bENVIHdrFound(false), m_bHasGotSiblingFiles(false),
98
213k
      m_bHasIdentifiedAuthorizedGeoreferencingSources(false),
99
213k
      m_bLayoutIFDSBeforeData(false), m_bBlockOrderRowMajor(false),
100
213k
      m_bLeaderSizeAsUInt4(false), m_bTrailerRepeatedLast4BytesRepeated(false),
101
213k
      m_bMaskInterleavedWithImagery(false), m_bKnownIncompatibleEdition(false),
102
213k
      m_bWriteKnownIncompatibleEdition(false), m_bHasUsedReadEncodedAPI(false),
103
213k
      m_bWriteCOGLayout(false), m_bTileInterleave(false),
104
213k
      m_bLayoutChecked(false)
105
213k
{
106
    // CPLDebug("GDAL", "sizeof(GTiffDataset) = %d bytes", static_cast<int>(
107
    //     sizeof(GTiffDataset)));
108
109
213k
    const char *pszVirtualMemIO =
110
213k
        CPLGetConfigOption("GTIFF_VIRTUAL_MEM_IO", "NO");
111
213k
    if (EQUAL(pszVirtualMemIO, "IF_ENOUGH_RAM"))
112
0
        m_eVirtualMemIOUsage = VirtualMemIOEnum::IF_ENOUGH_RAM;
113
213k
    else if (CPLTestBool(pszVirtualMemIO))
114
0
        m_eVirtualMemIOUsage = VirtualMemIOEnum::YES;
115
116
213k
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
117
213k
    m_oISIS3Metadata.Deinit();
118
213k
}
119
120
/************************************************************************/
121
/*                           ~GTiffDataset()                            */
122
/************************************************************************/
123
124
GTiffDataset::~GTiffDataset()
125
126
213k
{
127
213k
    GTiffDataset::Close();
128
213k
}
129
130
/************************************************************************/
131
/*                               Close()                                */
132
/************************************************************************/
133
134
CPLErr GTiffDataset::Close(GDALProgressFunc, void *)
135
316k
{
136
316k
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
137
213k
    {
138
213k
        auto [eErr, bDroppedRef] = Finalize();
139
140
213k
        if (m_pszTmpFilename)
141
0
        {
142
0
            VSIUnlink(m_pszTmpFilename);
143
0
            CPLFree(m_pszTmpFilename);
144
0
        }
145
146
213k
        if (GDALPamDataset::Close() != CE_None)
147
0
            eErr = CE_Failure;
148
213k
        return eErr;
149
213k
    }
150
103k
    return CE_None;
151
316k
}
152
153
/************************************************************************/
154
/*                              Finalize()                              */
155
/************************************************************************/
156
157
// Return a tuple (CPLErr, bool) to indicate respectively if an I/O error has
158
// occurred and if a reference to an auxiliary dataset has been dropped.
159
std::tuple<CPLErr, bool> GTiffDataset::Finalize()
160
213k
{
161
213k
    bool bDroppedRef = false;
162
213k
    if (m_bIsFinalized)
163
0
        return std::tuple(CE_None, bDroppedRef);
164
165
213k
    CPLErr eErr = CE_None;
166
213k
    Crystalize();
167
168
213k
    if (m_bColorProfileMetadataChanged)
169
0
    {
170
0
        SaveICCProfile(this, nullptr, nullptr, 0);
171
0
        m_bColorProfileMetadataChanged = false;
172
0
    }
173
174
    /* -------------------------------------------------------------------- */
175
    /*      Handle forcing xml:ESRI data to be written to PAM.              */
176
    /* -------------------------------------------------------------------- */
177
213k
    if (CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
178
0
    {
179
0
        CSLConstList papszESRIMD = GTiffDataset::GetMetadata("xml:ESRI");
180
0
        if (papszESRIMD)
181
0
        {
182
0
            GDALPamDataset::SetMetadata(papszESRIMD, "xml:ESRI");
183
0
        }
184
0
    }
185
186
213k
    if (m_psVirtualMemIOMapping)
187
0
        CPLVirtualMemFree(m_psVirtualMemIOMapping);
188
213k
    m_psVirtualMemIOMapping = nullptr;
189
190
    /* -------------------------------------------------------------------- */
191
    /*      Fill in missing blocks with empty data.                         */
192
    /* -------------------------------------------------------------------- */
193
213k
    if (m_bFillEmptyTilesAtClosing)
194
529
    {
195
        /* --------------------------------------------------------------------
196
         */
197
        /*  Ensure any blocks write cached by GDAL gets pushed through libtiff.
198
         */
199
        /* --------------------------------------------------------------------
200
         */
201
529
        if (FlushCacheInternal(true, /* at closing */
202
529
                               false /* do not call FlushDirectory */) !=
203
529
            CE_None)
204
0
        {
205
0
            eErr = CE_Failure;
206
0
        }
207
208
529
        if (FillEmptyTiles() != CE_None)
209
0
        {
210
0
            eErr = CE_Failure;
211
0
        }
212
529
        m_bFillEmptyTilesAtClosing = false;
213
529
    }
214
215
    /* -------------------------------------------------------------------- */
216
    /*      Force a complete flush, including either rewriting(moving)      */
217
    /*      of writing in place the current directory.                      */
218
    /* -------------------------------------------------------------------- */
219
213k
    if (FlushCacheInternal(true /* at closing */, true) != CE_None)
220
5
    {
221
5
        eErr = CE_Failure;
222
5
    }
223
224
    // Destroy compression queue
225
213k
    if (m_poCompressQueue)
226
4
    {
227
4
        m_poCompressQueue->WaitCompletion();
228
229
276
        for (int i = 0; i < static_cast<int>(m_asCompressionJobs.size()); ++i)
230
272
        {
231
272
            CPLFree(m_asCompressionJobs[i].pabyBuffer);
232
272
            if (m_asCompressionJobs[i].pszTmpFilename)
233
272
            {
234
272
                VSIUnlink(m_asCompressionJobs[i].pszTmpFilename);
235
272
                CPLFree(m_asCompressionJobs[i].pszTmpFilename);
236
272
            }
237
272
        }
238
4
        m_poCompressQueue.reset();
239
4
    }
240
241
    /* -------------------------------------------------------------------- */
242
    /*      If there is still changed metadata, then presumably we want     */
243
    /*      to push it into PAM.                                            */
244
    /* -------------------------------------------------------------------- */
245
213k
    if (m_bMetadataChanged)
246
0
    {
247
0
        PushMetadataToPam();
248
0
        m_bMetadataChanged = false;
249
0
        GDALPamDataset::FlushCache(false);
250
0
    }
251
252
    /* -------------------------------------------------------------------- */
253
    /*      Cleanup overviews.                                              */
254
    /* -------------------------------------------------------------------- */
255
213k
    if (!m_poBaseDS)
256
208k
    {
257
208k
        if (!m_apoOverviewDS.empty())
258
695
            bDroppedRef = true;
259
        // Move m_apoOverviewDS to a temporary variable, otherwise
260
        // GTiffDataset::FlushDirectory() might try to access an overview
261
        // that is being deleted (#5580)
262
208k
        auto apoTmpDS = std::move(m_apoOverviewDS);
263
208k
        apoTmpDS.clear();
264
265
208k
        if (!m_apoJPEGOverviewDS.empty())
266
51
            bDroppedRef = true;
267
208k
        m_apoJPEGOverviewDS.clear();
268
208k
    }
269
5.47k
    else
270
5.47k
    {
271
5.47k
        m_apoOverviewDS.clear();
272
5.47k
    }
273
274
213k
    if (m_poMaskDS)
275
600
    {
276
        // Move m_poMaskDS to a temporary variable, otherwise
277
        // GTiffDataset::FlushDirectory() might try to access it while being
278
        // deleted. (#5580)
279
600
        auto poTmpDS = std::move(m_poMaskDS);
280
600
        poTmpDS.reset();
281
600
        bDroppedRef = true;
282
600
    }
283
284
213k
    m_poColorTable.reset();
285
286
213k
    if (m_hTIFF)
287
213k
    {
288
213k
        XTIFFClose(m_hTIFF);
289
213k
        m_hTIFF = nullptr;
290
213k
    }
291
292
213k
    if (!m_poBaseDS)
293
208k
    {
294
208k
        if (m_fpL != nullptr)
295
105k
        {
296
105k
            if (m_bWriteKnownIncompatibleEdition)
297
0
            {
298
0
                GByte abyHeader[4096];
299
0
                VSIFSeekL(m_fpL, 0, SEEK_SET);
300
0
                VSIFReadL(abyHeader, 1, sizeof(abyHeader), m_fpL);
301
0
                const char *szKeyToLook =
302
0
                    "KNOWN_INCOMPATIBLE_EDITION=NO\n ";  // trailing space
303
                                                         // intended
304
0
                for (size_t i = 0; i < sizeof(abyHeader) - strlen(szKeyToLook);
305
0
                     i++)
306
0
                {
307
0
                    if (memcmp(abyHeader + i, szKeyToLook,
308
0
                               strlen(szKeyToLook)) == 0)
309
0
                    {
310
0
                        const char *szNewKey =
311
0
                            "KNOWN_INCOMPATIBLE_EDITION=YES\n";
312
0
                        CPLAssert(strlen(szKeyToLook) == strlen(szNewKey));
313
0
                        memcpy(abyHeader + i, szNewKey, strlen(szNewKey));
314
0
                        VSIFSeekL(m_fpL, 0, SEEK_SET);
315
0
                        VSIFWriteL(abyHeader, 1, sizeof(abyHeader), m_fpL);
316
0
                        break;
317
0
                    }
318
0
                }
319
0
            }
320
321
105k
            if (IsMarkedSuppressOnClose())
322
0
                m_fpL->CancelCreation();
323
324
105k
            if (VSIFCloseL(m_fpL) != 0)
325
0
            {
326
0
                eErr = CE_Failure;
327
0
                ReportError(CE_Failure, CPLE_FileIO, "I/O error");
328
0
            }
329
105k
            m_fpL = nullptr;
330
105k
        }
331
208k
    }
332
333
213k
    if (m_fpToWrite != nullptr)
334
0
    {
335
0
        if (VSIFCloseL(m_fpToWrite) != 0)
336
0
        {
337
0
            eErr = CE_Failure;
338
0
            ReportError(CE_Failure, CPLE_FileIO, "I/O error");
339
0
        }
340
0
        m_fpToWrite = nullptr;
341
0
    }
342
343
213k
    m_aoGCPs.clear();
344
345
213k
    CSLDestroy(m_papszCreationOptions);
346
213k
    m_papszCreationOptions = nullptr;
347
348
213k
    CPLFree(m_pabyTempWriteBuffer);
349
213k
    m_pabyTempWriteBuffer = nullptr;
350
351
213k
    m_bIMDRPCMetadataLoaded = false;
352
213k
    CSLDestroy(m_papszMetadataFiles);
353
213k
    m_papszMetadataFiles = nullptr;
354
355
213k
    VSIFree(m_pTempBufferForCommonDirectIO);
356
213k
    m_pTempBufferForCommonDirectIO = nullptr;
357
358
213k
    CPLFree(m_panMaskOffsetLsb);
359
213k
    m_panMaskOffsetLsb = nullptr;
360
361
213k
    CPLFree(m_pszVertUnit);
362
213k
    m_pszVertUnit = nullptr;
363
364
213k
    m_osFilename.clear();
365
366
213k
    CPLFree(m_pszGeorefFilename);
367
213k
    m_pszGeorefFilename = nullptr;
368
369
213k
    CPLFree(m_pszXMLFilename);
370
213k
    m_pszXMLFilename = nullptr;
371
372
213k
    m_bIsFinalized = true;
373
374
213k
    return std::tuple(eErr, bDroppedRef);
375
213k
}
376
377
/************************************************************************/
378
/*                       CloseDependentDatasets()                       */
379
/************************************************************************/
380
381
int GTiffDataset::CloseDependentDatasets()
382
0
{
383
0
    if (m_poBaseDS)
384
0
        return FALSE;
385
386
0
    int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
387
388
    // We ignore eErr as it is not relevant for CloseDependentDatasets(),
389
    // which is called in a "garbage collection" context.
390
0
    auto [eErr, bHasDroppedRefInFinalize] = Finalize();
391
0
    if (bHasDroppedRefInFinalize)
392
0
        bHasDroppedRef = true;
393
394
0
    return bHasDroppedRef;
395
0
}
396
397
/************************************************************************/
398
/*                            IsWholeBlock()                            */
399
/************************************************************************/
400
401
bool GTiffDataset::IsWholeBlock(int nXOff, int nYOff, int nXSize,
402
                                int nYSize) const
403
0
{
404
0
    if ((nXOff % m_nBlockXSize) != 0 || (nYOff % m_nBlockYSize) != 0)
405
0
    {
406
0
        return false;
407
0
    }
408
0
    if (TIFFIsTiled(m_hTIFF))
409
0
    {
410
0
        return nXSize == m_nBlockXSize && nYSize == m_nBlockYSize;
411
0
    }
412
0
    else
413
0
    {
414
0
        return nXSize == m_nBlockXSize &&
415
0
               (nYSize == m_nBlockYSize || nYOff + nYSize == nRasterYSize);
416
0
    }
417
0
}
418
419
/************************************************************************/
420
/*                             IRasterIO()                              */
421
/************************************************************************/
422
423
CPLErr GTiffDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
424
                               int nXSize, int nYSize, void *pData,
425
                               int nBufXSize, int nBufYSize,
426
                               GDALDataType eBufType, int nBandCount,
427
                               BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
428
                               GSpacing nLineSpace, GSpacing nBandSpace,
429
                               GDALRasterIOExtraArg *psExtraArg)
430
431
22.7k
{
432
    // Try to pass the request to the most appropriate overview dataset.
433
22.7k
    if (nBufXSize < nXSize && nBufYSize < nYSize)
434
42
    {
435
42
        int bTried = FALSE;
436
42
        std::unique_ptr<JPEGOverviewVisibilitySetter> setter;
437
42
        if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
438
39
        {
439
39
            setter = MakeJPEGOverviewVisible();
440
39
            CPL_IGNORE_RET_VAL(setter);
441
39
        }
442
42
        const CPLErr eErr = TryOverviewRasterIO(
443
42
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
444
42
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
445
42
            nBandSpace, psExtraArg, &bTried);
446
42
        if (bTried)
447
38
            return eErr;
448
42
    }
449
450
22.7k
    if (m_eVirtualMemIOUsage != VirtualMemIOEnum::NO)
451
0
    {
452
0
        const int nErr =
453
0
            VirtualMemIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
454
0
                         nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
455
0
                         nPixelSpace, nLineSpace, nBandSpace, psExtraArg);
456
0
        if (nErr >= 0)
457
0
            return static_cast<CPLErr>(nErr);
458
0
    }
459
22.7k
    if (m_bDirectIO)
460
0
    {
461
0
        const int nErr =
462
0
            DirectIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
463
0
                     nBufYSize, eBufType, nBandCount, panBandMap, nPixelSpace,
464
0
                     nLineSpace, nBandSpace, psExtraArg);
465
0
        if (nErr >= 0)
466
0
            return static_cast<CPLErr>(nErr);
467
0
    }
468
469
22.7k
    bool bCanUseMultiThreadedRead = false;
470
22.7k
    if (m_nDisableMultiThreadedRead == 0 && m_poThreadPool &&
471
1
        eRWFlag == GF_Read && nBufXSize == nXSize && nBufYSize == nYSize &&
472
0
        IsMultiThreadedReadCompatible())
473
0
    {
474
0
        const int nBlockX1 = nXOff / m_nBlockXSize;
475
0
        const int nBlockY1 = nYOff / m_nBlockYSize;
476
0
        const int nBlockX2 = (nXOff + nXSize - 1) / m_nBlockXSize;
477
0
        const int nBlockY2 = (nYOff + nYSize - 1) / m_nBlockYSize;
478
0
        const int nXBlocks = nBlockX2 - nBlockX1 + 1;
479
0
        const int nYBlocks = nBlockY2 - nBlockY1 + 1;
480
0
        const size_t nBlocks =
481
0
            static_cast<size_t>(nXBlocks) * nYBlocks *
482
0
            (m_nPlanarConfig == PLANARCONFIG_CONTIG ? 1 : nBandCount);
483
0
        if (nBlocks > 1)
484
0
        {
485
0
            bCanUseMultiThreadedRead = true;
486
0
        }
487
0
    }
488
489
22.7k
    struct ReleaseCachedRanges
490
22.7k
    {
491
22.7k
        TIFF *hTIFF_ = nullptr;
492
22.7k
        void *pBufferedData_ = nullptr;
493
494
22.7k
        explicit ReleaseCachedRanges(TIFF *hTIFFIn, void *pBufferedData)
495
22.7k
            : hTIFF_(hTIFFIn), pBufferedData_(pBufferedData)
496
22.7k
        {
497
0
        }
498
499
22.7k
        ~ReleaseCachedRanges()
500
22.7k
        {
501
0
            VSIFree(pBufferedData_);
502
0
            VSI_TIFFSetCachedRanges(TIFFClientdata(hTIFF_), 0, nullptr, nullptr,
503
0
                                    nullptr);
504
0
        }
505
22.7k
        CPL_DISALLOW_COPY_ASSIGN(ReleaseCachedRanges)
506
22.7k
    };
507
508
22.7k
    std::unique_ptr<ReleaseCachedRanges>
509
22.7k
        cachedRangesReleaser;  // keep in this scope
510
511
22.7k
    const auto poFirstBand = cpl::down_cast<GTiffRasterBand *>(papoBands[0]);
512
22.7k
    const auto eDataType = poFirstBand->GetRasterDataType();
513
514
22.7k
    if (eAccess == GA_ReadOnly && eRWFlag == GF_Read &&
515
263
        HasOptimizedReadMultiRange() &&
516
0
        !(bCanUseMultiThreadedRead &&
517
0
          VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF))->HasPRead()))
518
0
    {
519
0
        void *pBufferedData = nullptr;
520
0
        if (nBands == 1 || m_nPlanarConfig == PLANARCONFIG_CONTIG)
521
0
        {
522
0
            const int nBandOne = 1;
523
0
            pBufferedData =
524
0
                CacheMultiRange(nXOff, nYOff, nXSize, nYSize, nBufXSize,
525
0
                                nBufYSize, &nBandOne, 1, psExtraArg);
526
0
        }
527
0
        else
528
0
        {
529
0
            pBufferedData =
530
0
                CacheMultiRange(nXOff, nYOff, nXSize, nYSize, nBufXSize,
531
0
                                nBufYSize, panBandMap, nBandCount, psExtraArg);
532
0
        }
533
0
        if (pBufferedData)
534
0
            cachedRangesReleaser =
535
0
                std::make_unique<ReleaseCachedRanges>(m_hTIFF, pBufferedData);
536
0
        CPL_IGNORE_RET_VAL(cachedRangesReleaser);
537
0
    }
538
22.7k
    else if (bCanUseMultiThreadedRead)
539
0
    {
540
0
        return MultiThreadedRead(nXOff, nYOff, nXSize, nYSize, pData, eBufType,
541
0
                                 nBandCount, panBandMap, nPixelSpace,
542
0
                                 nLineSpace, nBandSpace);
543
0
    }
544
545
    // Write optimization when writing whole blocks, by-passing the block cache.
546
    // We require the block cache to be non instantiated to simplify things
547
    // (otherwise we might need to evict corresponding existing blocks from the
548
    // block cache).
549
22.7k
    else if (eRWFlag == GF_Write && nBands > 1 &&
550
1.59k
             m_nPlanarConfig == PLANARCONFIG_CONTIG &&
551
             // Could be extended to "odd bit" case, but more work
552
209
             m_nBitsPerSample == GDALGetDataTypeSizeBits(eDataType) &&
553
207
             nXSize == nBufXSize && nYSize == nBufYSize &&
554
207
             nBandCount == nBands && !m_bLoadedBlockDirty &&
555
207
             (nXOff % m_nBlockXSize) == 0 && (nYOff % m_nBlockYSize) == 0 &&
556
207
             (nXOff + nXSize == nRasterXSize ||
557
0
              (nXSize % m_nBlockXSize) == 0) &&
558
207
             (nYOff + nYSize == nRasterYSize || (nYSize % m_nBlockYSize) == 0))
559
207
    {
560
207
        bool bOptimOK = true;
561
207
        bool bOrderedBands = true;
562
24.5k
        for (int i = 0; i < nBands; ++i)
563
24.3k
        {
564
24.3k
            if (panBandMap[i] != i + 1)
565
0
            {
566
0
                bOrderedBands = false;
567
0
            }
568
24.3k
            if (cpl::down_cast<GTiffRasterBand *>(papoBands[panBandMap[i] - 1])
569
24.3k
                    ->HasBlockCache())
570
0
            {
571
0
                bOptimOK = false;
572
0
                break;
573
0
            }
574
24.3k
        }
575
207
        if (bOptimOK)
576
207
        {
577
207
            Crystalize();
578
579
207
            if (m_bDebugDontWriteBlocks)
580
0
                return CE_None;
581
582
207
            const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
583
207
            if (bOrderedBands && nXSize == m_nBlockXSize &&
584
207
                nYSize == m_nBlockYSize && eBufType == eDataType &&
585
27
                nBandSpace == nDTSize &&
586
0
                nPixelSpace == static_cast<GSpacing>(nDTSize) * nBands &&
587
0
                nLineSpace == nPixelSpace * m_nBlockXSize)
588
0
            {
589
                // If writing one single block with the right data type and
590
                // layout (interleaved per pixel), we don't need a temporary
591
                // buffer
592
0
                const int nBlockId = poFirstBand->ComputeBlockId(
593
0
                    nXOff / m_nBlockXSize, nYOff / m_nBlockYSize);
594
0
                return WriteEncodedTileOrStrip(nBlockId, pData,
595
0
                                               /* bPreserveDataBuffer= */ true);
596
0
            }
597
598
            // Make sure m_poGDS->m_pabyBlockBuf is allocated.
599
            // We could actually use any temporary buffer
600
207
            if (LoadBlockBuf(/* nBlockId = */ -1,
601
207
                             /* bReadFromDisk = */ false) != CE_None)
602
0
            {
603
0
                return CE_Failure;
604
0
            }
605
606
            // Iterate over all blocks defined by
607
            // [nXOff, nXOff+nXSize[ * [nYOff, nYOff+nYSize[
608
            // and write their content as a nBlockXSize x nBlockYSize strile
609
            // in a temporary buffer, before calling WriteEncodedTileOrStrip()
610
            // on it
611
207
            const int nYBlockStart = nYOff / m_nBlockYSize;
612
207
            const int nYBlockEnd = 1 + (nYOff + nYSize - 1) / m_nBlockYSize;
613
207
            const int nXBlockStart = nXOff / m_nBlockXSize;
614
207
            const int nXBlockEnd = 1 + (nXOff + nXSize - 1) / m_nBlockXSize;
615
2.31k
            for (int nYBlock = nYBlockStart; nYBlock < nYBlockEnd; ++nYBlock)
616
2.11k
            {
617
2.11k
                const int nValidY = std::min(
618
2.11k
                    m_nBlockYSize, nRasterYSize - nYBlock * m_nBlockYSize);
619
4.22k
                for (int nXBlock = nXBlockStart; nXBlock < nXBlockEnd;
620
2.11k
                     ++nXBlock)
621
2.11k
                {
622
2.11k
                    const int nValidX = std::min(
623
2.11k
                        m_nBlockXSize, nRasterXSize - nXBlock * m_nBlockXSize);
624
2.11k
                    if (nValidY < m_nBlockYSize || nValidX < m_nBlockXSize)
625
43
                    {
626
                        // Make sure padding bytes at the right/bottom of the
627
                        // tile are initialized to zero.
628
43
                        memset(m_pabyBlockBuf, 0,
629
43
                               static_cast<size_t>(m_nBlockXSize) *
630
43
                                   m_nBlockYSize * nBands * nDTSize);
631
43
                    }
632
2.11k
                    const auto nBufDTSize = GDALGetDataTypeSizeBytes(eBufType);
633
2.11k
                    const GByte *pabySrcData =
634
2.11k
                        static_cast<const GByte *>(pData) +
635
2.11k
                        static_cast<size_t>(nYBlock - nYBlockStart) *
636
2.11k
                            m_nBlockYSize * nLineSpace +
637
2.11k
                        static_cast<size_t>(nXBlock - nXBlockStart) *
638
2.11k
                            m_nBlockXSize * nPixelSpace;
639
2.11k
                    if (bOrderedBands && nBandSpace == nBufDTSize &&
640
1
                        nPixelSpace == nBands * nBandSpace)
641
0
                    {
642
                        // Input buffer is pixel interleaved
643
0
                        for (int iY = 0; iY < nValidY; ++iY)
644
0
                        {
645
0
                            GDALCopyWords64(
646
0
                                pabySrcData +
647
0
                                    static_cast<size_t>(iY) * nLineSpace,
648
0
                                eBufType, nBufDTSize,
649
0
                                m_pabyBlockBuf + static_cast<size_t>(iY) *
650
0
                                                     m_nBlockXSize * nBands *
651
0
                                                     nDTSize,
652
0
                                eDataType, nDTSize,
653
0
                                static_cast<GPtrDiff_t>(nValidX) * nBands);
654
0
                        }
655
0
                    }
656
2.11k
                    else
657
2.11k
                    {
658
                        // "Random" spacing for input buffer
659
316k
                        for (int iBand = 0; iBand < nBands; ++iBand)
660
314k
                        {
661
1.53M
                            for (int iY = 0; iY < nValidY; ++iY)
662
1.21M
                            {
663
1.21M
                                GDALCopyWords64(
664
1.21M
                                    pabySrcData +
665
1.21M
                                        static_cast<size_t>(iY) * nLineSpace,
666
1.21M
                                    eBufType, static_cast<int>(nPixelSpace),
667
1.21M
                                    m_pabyBlockBuf +
668
1.21M
                                        (panBandMap[iBand] - 1 +
669
1.21M
                                         static_cast<size_t>(iY) *
670
1.21M
                                             m_nBlockXSize * nBands) *
671
1.21M
                                            nDTSize,
672
1.21M
                                    eDataType, nDTSize * nBands, nValidX);
673
1.21M
                            }
674
314k
                            pabySrcData += nBandSpace;
675
314k
                        }
676
2.11k
                    }
677
678
2.11k
                    const int nBlockId =
679
2.11k
                        poFirstBand->ComputeBlockId(nXBlock, nYBlock);
680
2.11k
                    if (WriteEncodedTileOrStrip(
681
2.11k
                            nBlockId, m_pabyBlockBuf,
682
2.11k
                            /* bPreserveDataBuffer= */ false) != CE_None)
683
0
                    {
684
0
                        return CE_Failure;
685
0
                    }
686
2.11k
                }
687
2.11k
            }
688
207
            return CE_None;
689
207
        }
690
207
    }
691
692
22.5k
    std::unique_ptr<JPEGOverviewVisibilitySetter> setter;
693
22.5k
    if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
694
22.5k
    {
695
22.5k
        setter = MakeJPEGOverviewVisible();
696
22.5k
        CPL_IGNORE_RET_VAL(setter);
697
22.5k
    }
698
22.5k
    return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
699
22.5k
                                     pData, nBufXSize, nBufYSize, eBufType,
700
22.5k
                                     nBandCount, panBandMap, nPixelSpace,
701
22.5k
                                     nLineSpace, nBandSpace, psExtraArg);
702
22.7k
}
703
704
/************************************************************************/
705
/*                         GetGTIFFKeysFlavor()                         */
706
/************************************************************************/
707
708
GTIFFKeysFlavorEnum GetGTIFFKeysFlavor(CSLConstList papszOptions)
709
103k
{
710
103k
    const char *pszGeoTIFFKeysFlavor =
711
103k
        CSLFetchNameValueDef(papszOptions, "GEOTIFF_KEYS_FLAVOR", "STANDARD");
712
103k
    if (EQUAL(pszGeoTIFFKeysFlavor, "ESRI_PE"))
713
0
        return GEOTIFF_KEYS_ESRI_PE;
714
103k
    return GEOTIFF_KEYS_STANDARD;
715
103k
}
716
717
/************************************************************************/
718
/*                         GetGeoTIFFVersion()                          */
719
/************************************************************************/
720
721
GeoTIFFVersionEnum GetGeoTIFFVersion(CSLConstList papszOptions)
722
103k
{
723
103k
    const char *pszVersion =
724
103k
        CSLFetchNameValueDef(papszOptions, "GEOTIFF_VERSION", "AUTO");
725
103k
    if (EQUAL(pszVersion, "1.0"))
726
0
        return GEOTIFF_VERSION_1_0;
727
103k
    if (EQUAL(pszVersion, "1.1"))
728
0
        return GEOTIFF_VERSION_1_1;
729
103k
    return GEOTIFF_VERSION_AUTO;
730
103k
}
731
732
/************************************************************************/
733
/*                     InitCreationOrOpenOptions()                      */
734
/************************************************************************/
735
736
void GTiffDataset::InitCreationOrOpenOptions(bool bUpdateMode,
737
                                             CSLConstList papszOptions)
738
103k
{
739
103k
    InitCompressionThreads(bUpdateMode, papszOptions);
740
741
103k
    m_eGeoTIFFKeysFlavor = GetGTIFFKeysFlavor(papszOptions);
742
103k
    m_eGeoTIFFVersion = GetGeoTIFFVersion(papszOptions);
743
103k
}
744
745
/************************************************************************/
746
/*                          IsBlockAvailable()                          */
747
/*                                                                      */
748
/*      Return true if the indicated strip/tile is available.  We       */
749
/*      establish this by testing if the stripbytecount is zero.  If    */
750
/*      zero then the block has never been committed to disk.           */
751
/************************************************************************/
752
753
bool GTiffDataset::IsBlockAvailable(int nBlockId, vsi_l_offset *pnOffset,
754
                                    vsi_l_offset *pnSize, bool *pbErrOccurred)
755
756
1.87M
{
757
1.87M
    if (pbErrOccurred)
758
1.49M
        *pbErrOccurred = false;
759
760
1.87M
    std::pair<vsi_l_offset, vsi_l_offset> oPair;
761
1.87M
    if (m_oCacheStrileToOffsetByteCount.tryGet(nBlockId, oPair))
762
0
    {
763
0
        if (pnOffset)
764
0
            *pnOffset = oPair.first;
765
0
        if (pnSize)
766
0
            *pnSize = oPair.second;
767
0
        return oPair.first != 0;
768
0
    }
769
770
1.87M
    WaitCompletionForBlock(nBlockId);
771
772
    // Optimization to avoid fetching the whole Strip/TileCounts and
773
    // Strip/TileOffsets arrays.
774
1.87M
    if (eAccess == GA_ReadOnly && !m_bStreamingIn)
775
1.86M
    {
776
1.86M
        int nErrOccurred = 0;
777
1.86M
        auto bytecount =
778
1.86M
            TIFFGetStrileByteCountWithErr(m_hTIFF, nBlockId, &nErrOccurred);
779
1.86M
        if (nErrOccurred && pbErrOccurred)
780
19.0k
            *pbErrOccurred = true;
781
1.86M
        if (pnOffset)
782
871k
        {
783
871k
            *pnOffset =
784
871k
                TIFFGetStrileOffsetWithErr(m_hTIFF, nBlockId, &nErrOccurred);
785
871k
            if (nErrOccurred && pbErrOccurred)
786
14.3k
                *pbErrOccurred = true;
787
871k
        }
788
1.86M
        if (pnSize)
789
827
            *pnSize = bytecount;
790
1.86M
        return bytecount != 0;
791
1.86M
    }
792
793
6.31k
    if (!m_bCrystalized)
794
0
    {
795
        // If this is a fresh new file not yet crystalized, do not try to
796
        // read the [Strip|Tile][ByteCounts|Offsets] tags as they do not yet
797
        // exist. Trying would set *pbErrOccurred=true, which is not desirable.
798
0
        if (pnOffset)
799
0
            *pnOffset = 0;
800
0
        if (pnSize)
801
0
            *pnSize = 0;
802
0
        return false;
803
0
    }
804
805
6.31k
    toff_t *panByteCounts = nullptr;
806
6.31k
    toff_t *panOffsets = nullptr;
807
6.31k
    const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(m_hTIFF));
808
809
6.31k
    if ((bIsTiled &&
810
0
         TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts) &&
811
0
         (pnOffset == nullptr ||
812
0
          TIFFGetField(m_hTIFF, TIFFTAG_TILEOFFSETS, &panOffsets))) ||
813
6.31k
        (!bIsTiled &&
814
6.31k
         TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts) &&
815
6.31k
         (pnOffset == nullptr ||
816
1.04k
          TIFFGetField(m_hTIFF, TIFFTAG_STRIPOFFSETS, &panOffsets))))
817
6.31k
    {
818
6.31k
        if (panByteCounts == nullptr ||
819
6.31k
            (pnOffset != nullptr && panOffsets == nullptr))
820
0
        {
821
0
            if (pbErrOccurred)
822
0
                *pbErrOccurred = true;
823
0
            return false;
824
0
        }
825
6.31k
        const int nBlockCount =
826
6.31k
            bIsTiled ? TIFFNumberOfTiles(m_hTIFF) : TIFFNumberOfStrips(m_hTIFF);
827
6.31k
        if (nBlockId >= nBlockCount)
828
0
        {
829
0
            if (pbErrOccurred)
830
0
                *pbErrOccurred = true;
831
0
            return false;
832
0
        }
833
834
6.31k
        if (pnOffset)
835
1.04k
            *pnOffset = panOffsets[nBlockId];
836
6.31k
        if (pnSize)
837
546
            *pnSize = panByteCounts[nBlockId];
838
6.31k
        return panByteCounts[nBlockId] != 0;
839
6.31k
    }
840
0
    else
841
0
    {
842
0
        if (pbErrOccurred)
843
0
            *pbErrOccurred = true;
844
0
    }
845
846
0
    return false;
847
6.31k
}
848
849
/************************************************************************/
850
/*                          ReloadDirectory()                           */
851
/************************************************************************/
852
853
void GTiffDataset::ReloadDirectory(bool bReopenHandle)
854
95.0k
{
855
95.0k
    bool bNeedSetInvalidDir = true;
856
95.0k
    if (bReopenHandle)
857
0
    {
858
        // When issuing a TIFFRewriteDirectory() or when a TIFFFlush() has
859
        // caused a move of the directory, we would need to invalidate the
860
        // tif_lastdiroff member, but it is not possible to do so without
861
        // re-opening the TIFF handle.
862
0
        auto hTIFFNew = VSI_TIFFReOpen(m_hTIFF);
863
0
        if (hTIFFNew != nullptr)
864
0
        {
865
0
            m_hTIFF = hTIFFNew;
866
0
            bNeedSetInvalidDir = false;  // we could do it, but not needed
867
0
        }
868
0
        else
869
0
        {
870
0
            CPLError(CE_Failure, CPLE_AppDefined,
871
0
                     "Cannot re-open TIFF handle for file %s. "
872
0
                     "Directory chaining may be corrupted !",
873
0
                     m_osFilename.c_str());
874
0
        }
875
0
    }
876
95.0k
    if (bNeedSetInvalidDir)
877
95.0k
    {
878
95.0k
        TIFFSetSubDirectory(m_hTIFF, 0);
879
95.0k
    }
880
95.0k
    CPL_IGNORE_RET_VAL(SetDirectory());
881
95.0k
}
882
883
/************************************************************************/
884
/*                            SetDirectory()                            */
885
/************************************************************************/
886
887
bool GTiffDataset::SetDirectory()
888
889
404k
{
890
404k
    Crystalize();
891
892
404k
    if (TIFFCurrentDirOffset(m_hTIFF) == m_nDirOffset)
893
202k
    {
894
202k
        return true;
895
202k
    }
896
897
202k
    const int nSetDirResult = TIFFSetSubDirectory(m_hTIFF, m_nDirOffset);
898
202k
    if (!nSetDirResult)
899
368
        return false;
900
901
202k
    RestoreVolatileParameters(m_hTIFF);
902
903
202k
    return true;
904
202k
}
905
906
/************************************************************************/
907
/*                      GTiffSetDeflateSubCodec()                       */
908
/************************************************************************/
909
910
void GTiffSetDeflateSubCodec(TIFF *hTIFF)
911
3.89k
{
912
3.89k
    (void)hTIFF;
913
914
3.89k
#if defined(TIFFTAG_DEFLATE_SUBCODEC) && defined(LIBDEFLATE_SUPPORT)
915
    // Mostly for strict reproducibility purposes
916
3.89k
    if (EQUAL(CPLGetConfigOption("GDAL_TIFF_DEFLATE_SUBCODEC", ""), "ZLIB"))
917
0
    {
918
0
        TIFFSetField(hTIFF, TIFFTAG_DEFLATE_SUBCODEC, DEFLATE_SUBCODEC_ZLIB);
919
0
    }
920
3.89k
#endif
921
3.89k
}
922
923
/************************************************************************/
924
/*                     RestoreVolatileParameters()                      */
925
/************************************************************************/
926
927
void GTiffDataset::RestoreVolatileParameters(TIFF *hTIFF)
928
206k
{
929
930
    /* -------------------------------------------------------------------- */
931
    /*      YCbCr JPEG compressed images should be translated on the fly    */
932
    /*      to RGB by libtiff/libjpeg unless specifically requested         */
933
    /*      otherwise.                                                      */
934
    /* -------------------------------------------------------------------- */
935
206k
    if (m_nCompression == COMPRESSION_JPEG &&
936
7.61k
        m_nPhotometric == PHOTOMETRIC_YCBCR &&
937
1.46k
        CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
938
1.46k
    {
939
1.46k
        int nColorMode = JPEGCOLORMODE_RAW;  // Initialize to 0;
940
941
1.46k
        TIFFGetField(hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode);
942
1.46k
        if (nColorMode != JPEGCOLORMODE_RGB)
943
1.46k
        {
944
1.46k
            TIFFSetField(hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
945
1.46k
        }
946
1.46k
    }
947
948
206k
    if (m_nCompression == COMPRESSION_ADOBE_DEFLATE ||
949
205k
        m_nCompression == COMPRESSION_LERC)
950
3.88k
    {
951
3.88k
        GTiffSetDeflateSubCodec(hTIFF);
952
3.88k
    }
953
954
    /* -------------------------------------------------------------------- */
955
    /*      Propagate any quality settings.                                 */
956
    /* -------------------------------------------------------------------- */
957
206k
    if (eAccess == GA_Update)
958
4.55k
    {
959
        // Now, reset zip and jpeg quality.
960
4.55k
        if (m_nJpegQuality > 0 && m_nCompression == COMPRESSION_JPEG)
961
0
        {
962
#ifdef DEBUG_VERBOSE
963
            CPLDebug("GTiff", "Propagate JPEG_QUALITY(%d) in SetDirectory()",
964
                     m_nJpegQuality);
965
#endif
966
0
            TIFFSetField(hTIFF, TIFFTAG_JPEGQUALITY, m_nJpegQuality);
967
0
        }
968
4.55k
        if (m_nJpegTablesMode >= 0 && m_nCompression == COMPRESSION_JPEG)
969
0
            TIFFSetField(hTIFF, TIFFTAG_JPEGTABLESMODE, m_nJpegTablesMode);
970
4.55k
        if (m_nZLevel > 0 && (m_nCompression == COMPRESSION_ADOBE_DEFLATE ||
971
0
                              m_nCompression == COMPRESSION_LERC))
972
0
            TIFFSetField(hTIFF, TIFFTAG_ZIPQUALITY, m_nZLevel);
973
4.55k
        if (m_nLZMAPreset > 0 && m_nCompression == COMPRESSION_LZMA)
974
0
            TIFFSetField(hTIFF, TIFFTAG_LZMAPRESET, m_nLZMAPreset);
975
4.55k
        if (m_nZSTDLevel > 0 && (m_nCompression == COMPRESSION_ZSTD ||
976
0
                                 m_nCompression == COMPRESSION_LERC))
977
0
            TIFFSetField(hTIFF, TIFFTAG_ZSTD_LEVEL, m_nZSTDLevel);
978
4.55k
        if (m_nCompression == COMPRESSION_LERC)
979
0
        {
980
0
            TIFFSetField(hTIFF, TIFFTAG_LERC_MAXZERROR, m_dfMaxZError);
981
0
        }
982
4.55k
        if (m_nWebPLevel > 0 && m_nCompression == COMPRESSION_WEBP)
983
0
            TIFFSetField(hTIFF, TIFFTAG_WEBP_LEVEL, m_nWebPLevel);
984
4.55k
        if (m_bWebPLossless && m_nCompression == COMPRESSION_WEBP)
985
0
            TIFFSetField(hTIFF, TIFFTAG_WEBP_LOSSLESS, 1);
986
#ifdef HAVE_JXL
987
        if (m_nCompression == COMPRESSION_JXL ||
988
            m_nCompression == COMPRESSION_JXL_DNG_1_7)
989
        {
990
            TIFFSetField(hTIFF, TIFFTAG_JXL_LOSSYNESS,
991
                         m_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
992
            TIFFSetField(hTIFF, TIFFTAG_JXL_EFFORT, m_nJXLEffort);
993
            TIFFSetField(hTIFF, TIFFTAG_JXL_DISTANCE,
994
                         static_cast<double>(m_fJXLDistance));
995
            TIFFSetField(hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE,
996
                         static_cast<double>(m_fJXLAlphaDistance));
997
        }
998
#endif
999
4.55k
    }
1000
206k
}
1001
1002
/************************************************************************/
1003
/*                   ComputeBlocksPerColRowAndBand()                    */
1004
/************************************************************************/
1005
1006
bool GTiffDataset::ComputeBlocksPerColRowAndBand(int l_nBands)
1007
209k
{
1008
209k
    m_nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, m_nBlockYSize);
1009
209k
    m_nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, m_nBlockXSize);
1010
209k
    if (m_nBlocksPerColumn > INT_MAX / m_nBlocksPerRow)
1011
126
    {
1012
126
        ReportError(CE_Failure, CPLE_AppDefined, "Too many blocks: %d x %d",
1013
126
                    m_nBlocksPerRow, m_nBlocksPerColumn);
1014
126
        return false;
1015
126
    }
1016
1017
    // Note: we could potentially go up to UINT_MAX blocks, but currently
1018
    // we use a int nBlockId
1019
209k
    m_nBlocksPerBand = m_nBlocksPerColumn * m_nBlocksPerRow;
1020
209k
    if (m_nPlanarConfig == PLANARCONFIG_SEPARATE &&
1021
6.32k
        m_nBlocksPerBand > INT_MAX / l_nBands)
1022
125
    {
1023
125
        ReportError(CE_Failure, CPLE_AppDefined,
1024
125
                    "Too many blocks: %d x %d x %d bands", m_nBlocksPerRow,
1025
125
                    m_nBlocksPerColumn, l_nBands);
1026
125
        return false;
1027
125
    }
1028
209k
    return true;
1029
209k
}
1030
1031
/************************************************************************/
1032
/*                     SetStructuralMDFromParent()                      */
1033
/************************************************************************/
1034
1035
void GTiffDataset::SetStructuralMDFromParent(GTiffDataset *poParentDS)
1036
108k
{
1037
108k
    m_bBlockOrderRowMajor = poParentDS->m_bBlockOrderRowMajor;
1038
108k
    m_bLeaderSizeAsUInt4 = poParentDS->m_bLeaderSizeAsUInt4;
1039
108k
    m_bTrailerRepeatedLast4BytesRepeated =
1040
108k
        poParentDS->m_bTrailerRepeatedLast4BytesRepeated;
1041
108k
    m_bMaskInterleavedWithImagery = poParentDS->m_bMaskInterleavedWithImagery;
1042
108k
    m_bWriteEmptyTiles = poParentDS->m_bWriteEmptyTiles;
1043
108k
    m_bTileInterleave = poParentDS->m_bTileInterleave;
1044
108k
}
1045
1046
/************************************************************************/
1047
/*                          ScanDirectories()                           */
1048
/*                                                                      */
1049
/*      Scan through all the directories finding overviews, masks       */
1050
/*      and subdatasets.                                                */
1051
/************************************************************************/
1052
1053
void GTiffDataset::ScanDirectories()
1054
1055
599k
{
1056
    /* -------------------------------------------------------------------- */
1057
    /*      We only scan once.  We do not scan for non-base datasets.       */
1058
    /* -------------------------------------------------------------------- */
1059
599k
    if (!m_bScanDeferred)
1060
504k
        return;
1061
1062
95.0k
    m_bScanDeferred = false;
1063
1064
95.0k
    if (m_poBaseDS)
1065
12
        return;
1066
1067
94.9k
    Crystalize();
1068
1069
94.9k
    CPLDebug("GTiff", "ScanDirectories()");
1070
1071
    /* ==================================================================== */
1072
    /*      Scan all directories.                                           */
1073
    /* ==================================================================== */
1074
94.9k
    CPLStringList aosSubdatasets;
1075
94.9k
    int iDirIndex = 0;
1076
1077
94.9k
    FlushDirectory();
1078
1079
94.9k
    do
1080
97.5k
    {
1081
97.5k
        toff_t nTopDir = TIFFCurrentDirOffset(m_hTIFF);
1082
97.5k
        uint32_t nSubType = 0;
1083
1084
97.5k
        ++iDirIndex;
1085
1086
97.5k
        toff_t *tmpSubIFDOffsets = nullptr;
1087
97.5k
        toff_t *subIFDOffsets = nullptr;
1088
97.5k
        uint16_t nSubIFDs = 0;
1089
97.5k
        if (TIFFGetField(m_hTIFF, TIFFTAG_SUBIFD, &nSubIFDs,
1090
97.5k
                         &tmpSubIFDOffsets) &&
1091
12.0k
            iDirIndex == 1)
1092
11.7k
        {
1093
11.7k
            subIFDOffsets =
1094
11.7k
                static_cast<toff_t *>(CPLMalloc(nSubIFDs * sizeof(toff_t)));
1095
1.22M
            for (uint16_t iSubIFD = 0; iSubIFD < nSubIFDs; iSubIFD++)
1096
1.20M
            {
1097
1.20M
                subIFDOffsets[iSubIFD] = tmpSubIFDOffsets[iSubIFD];
1098
1.20M
            }
1099
11.7k
        }
1100
1101
        // early break for backwards compatibility: if the first directory read
1102
        // is also the last, and there are no subIFDs, no use continuing
1103
97.5k
        if (iDirIndex == 1 && nSubIFDs == 0 && TIFFLastDirectory(m_hTIFF))
1104
36.2k
        {
1105
36.2k
            CPLFree(subIFDOffsets);
1106
36.2k
            break;
1107
36.2k
        }
1108
1109
254k
        for (uint16_t iSubIFD = 0; iSubIFD <= nSubIFDs; iSubIFD++)
1110
204k
        {
1111
204k
            toff_t nThisDir = nTopDir;
1112
204k
            if (iSubIFD > 0 && iDirIndex > 1)  // don't read subIFDs if we are
1113
                                               // not in the original directory
1114
243
                break;
1115
204k
            if (iSubIFD > 0)
1116
142k
            {
1117
                // make static analyzer happy. subIFDOffsets cannot be null if
1118
                // iSubIFD>0
1119
142k
                assert(subIFDOffsets != nullptr);
1120
142k
                nThisDir = subIFDOffsets[iSubIFD - 1];
1121
                // CPLDebug("GTiff", "Opened subIFD %d/%d at offset %llu.",
1122
                // iSubIFD, nSubIFDs, nThisDir);
1123
142k
                if (!TIFFSetSubDirectory(m_hTIFF, nThisDir))
1124
10.5k
                    break;
1125
142k
            }
1126
1127
193k
            if (!TIFFGetField(m_hTIFF, TIFFTAG_SUBFILETYPE, &nSubType))
1128
77.7k
                nSubType = 0;
1129
1130
            /* Embedded overview of the main image */
1131
193k
            if ((nSubType & FILETYPE_REDUCEDIMAGE) != 0 &&
1132
74.4k
                (nSubType & FILETYPE_MASK) == 0 &&
1133
25.9k
                ((nSubIFDs == 0 && iDirIndex != 1) || iSubIFD > 0) &&
1134
25.6k
                m_apoOverviewDS.size() < 30 /* to avoid DoS */)
1135
23.7k
            {
1136
23.7k
                auto poODS = std::make_shared<GTiffDataset>();
1137
23.7k
                poODS->ShareLockWithParentDataset(this);
1138
23.7k
                poODS->SetStructuralMDFromParent(this);
1139
23.7k
                if (m_bHasGotSiblingFiles)
1140
931
                    poODS->oOvManager.TransferSiblingFiles(
1141
931
                        CSLDuplicate(GetSiblingFiles()));
1142
23.7k
                poODS->m_osFilename = m_osFilename;
1143
23.7k
                poODS->m_nColorTableMultiplier = m_nColorTableMultiplier;
1144
23.7k
                if (poODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nThisDir,
1145
23.7k
                                      eAccess) == CE_None &&
1146
21.3k
                    poODS->GetRasterCount() == GetRasterCount())
1147
4.87k
                {
1148
4.87k
                    CPLDebug("GTiff", "Opened %dx%d overview.",
1149
4.87k
                             poODS->GetRasterXSize(), poODS->GetRasterYSize());
1150
4.87k
                    m_apoOverviewDS.push_back(poODS);
1151
4.87k
                    poODS->m_poBaseDS = this;
1152
4.87k
                    poODS->m_bIsOverview = true;
1153
1154
                    // Propagate a few compression related settings that are
1155
                    // no preserved at the TIFF tag level, but may be set in
1156
                    // the GDAL_METADATA tag in the IMAGE_STRUCTURE domain
1157
                    // Note: this might not be totally reflecting the reality
1158
                    // if users have created overviews with different settings
1159
                    // but this is probably better than the default ones
1160
4.87k
                    poODS->m_nWebPLevel = m_nWebPLevel;
1161
                    // below is not a copy & paste error: we transfer the
1162
                    // m_dfMaxZErrorOverview overview of the parent to
1163
                    // m_dfMaxZError of the overview
1164
4.87k
                    poODS->m_dfMaxZError = m_dfMaxZErrorOverview;
1165
4.87k
                    poODS->m_dfMaxZErrorOverview = m_dfMaxZErrorOverview;
1166
#if HAVE_JXL
1167
                    poODS->m_bJXLLossless = m_bJXLLossless;
1168
                    poODS->m_fJXLDistance = m_fJXLDistance;
1169
                    poODS->m_fJXLAlphaDistance = m_fJXLAlphaDistance;
1170
                    poODS->m_nJXLEffort = m_nJXLEffort;
1171
#endif
1172
                    // Those ones are not serialized currently..
1173
                    // poODS->m_nZLevel = m_nZLevel;
1174
                    // poODS->m_nLZMAPreset = m_nLZMAPreset;
1175
                    // poODS->m_nZSTDLevel = m_nZSTDLevel;
1176
1177
4.87k
                    if (const char *pszOverviewResampling =
1178
4.87k
                            m_oGTiffMDMD.GetMetadataItem("OVERVIEW_RESAMPLING",
1179
4.87k
                                                         "IMAGE_STRUCTURE"))
1180
0
                    {
1181
0
                        for (int iBand = 1; iBand <= poODS->GetRasterCount();
1182
0
                             ++iBand)
1183
0
                        {
1184
0
                            auto poOBand = cpl::down_cast<GTiffRasterBand *>(
1185
0
                                poODS->GetRasterBand(iBand));
1186
0
                            if (poOBand->GetMetadataItem("RESAMPLING") ==
1187
0
                                nullptr)
1188
0
                            {
1189
0
                                poOBand->m_oGTiffMDMD.SetMetadataItem(
1190
0
                                    "RESAMPLING", pszOverviewResampling);
1191
0
                            }
1192
0
                        }
1193
0
                    }
1194
4.87k
                }
1195
23.7k
            }
1196
            // Embedded mask of the main image.
1197
169k
            else if ((nSubType & FILETYPE_MASK) != 0 &&
1198
87.9k
                     (nSubType & FILETYPE_REDUCEDIMAGE) == 0 &&
1199
39.4k
                     ((nSubIFDs == 0 && iDirIndex != 1) || iSubIFD > 0) &&
1200
36.3k
                     m_poMaskDS == nullptr)
1201
36.1k
            {
1202
36.1k
                m_poMaskDS = std::make_shared<GTiffDataset>();
1203
36.1k
                m_poMaskDS->ShareLockWithParentDataset(this);
1204
36.1k
                m_poMaskDS->SetStructuralMDFromParent(this);
1205
36.1k
                m_poMaskDS->m_osFilename = m_osFilename;
1206
1207
                // The TIFF6 specification - page 37 - only allows 1
1208
                // SamplesPerPixel and 1 BitsPerSample Here we support either 1
1209
                // or 8 bit per sample and we support either 1 sample per pixel
1210
                // or as many samples as in the main image We don't check the
1211
                // value of the PhotometricInterpretation tag, which should be
1212
                // set to "Transparency mask" (4) according to the specification
1213
                // (page 36).  However, the TIFF6 specification allows image
1214
                // masks to have a higher resolution than the main image, what
1215
                // we don't support here.
1216
1217
36.1k
                if (m_poMaskDS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nThisDir,
1218
36.1k
                                           eAccess) != CE_None ||
1219
31.8k
                    m_poMaskDS->GetRasterCount() == 0 ||
1220
31.8k
                    !(m_poMaskDS->GetRasterCount() == 1 ||
1221
7.83k
                      m_poMaskDS->GetRasterCount() == GetRasterCount()) ||
1222
24.7k
                    m_poMaskDS->GetRasterXSize() != GetRasterXSize() ||
1223
929
                    m_poMaskDS->GetRasterYSize() != GetRasterYSize() ||
1224
670
                    m_poMaskDS->GetRasterBand(1)->GetRasterDataType() !=
1225
670
                        GDT_UInt8)
1226
35.5k
                {
1227
35.5k
                    m_poMaskDS.reset();
1228
35.5k
                }
1229
557
                else
1230
557
                {
1231
557
                    CPLDebug("GTiff", "Opened band mask.");
1232
557
                    m_poMaskDS->m_poBaseDS = this;
1233
557
                    m_poMaskDS->m_poImageryDS = this;
1234
1235
557
                    m_poMaskDS->m_bPromoteTo8Bits =
1236
557
                        CPLTestBool(CPLGetConfigOption(
1237
557
                            "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
1238
557
                }
1239
36.1k
            }
1240
1241
            // Embedded mask of an overview.  The TIFF6 specification allows the
1242
            // combination of the FILETYPE_xxxx masks.
1243
133k
            else if ((nSubType & FILETYPE_REDUCEDIMAGE) != 0 &&
1244
50.6k
                     (nSubType & FILETYPE_MASK) != 0 &&
1245
48.5k
                     ((nSubIFDs == 0 && iDirIndex != 1) || iSubIFD > 0))
1246
48.2k
            {
1247
48.2k
                auto poDS = std::make_shared<GTiffDataset>();
1248
48.2k
                poDS->ShareLockWithParentDataset(this);
1249
48.2k
                poDS->SetStructuralMDFromParent(this);
1250
48.2k
                poDS->m_osFilename = m_osFilename;
1251
48.2k
                if (poDS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nThisDir,
1252
48.2k
                                     eAccess) == CE_None &&
1253
41.1k
                    poDS->GetRasterCount() != 0 &&
1254
41.1k
                    poDS->GetRasterBand(1)->GetRasterDataType() == GDT_UInt8)
1255
35.1k
                {
1256
35.1k
                    for (auto &poOvrDS : m_apoOverviewDS)
1257
152
                    {
1258
152
                        if (poOvrDS->m_poMaskDS == nullptr &&
1259
146
                            poDS->GetRasterXSize() ==
1260
146
                                poOvrDS->GetRasterXSize() &&
1261
55
                            poDS->GetRasterYSize() ==
1262
55
                                poOvrDS->GetRasterYSize() &&
1263
32
                            (poDS->GetRasterCount() == 1 ||
1264
22
                             poDS->GetRasterCount() == GetRasterCount()))
1265
10
                        {
1266
10
                            CPLDebug(
1267
10
                                "GTiff", "Opened band mask for %dx%d overview.",
1268
10
                                poDS->GetRasterXSize(), poDS->GetRasterYSize());
1269
10
                            poDS->m_poImageryDS = poOvrDS.get();
1270
10
                            poDS->m_bPromoteTo8Bits =
1271
10
                                CPLTestBool(CPLGetConfigOption(
1272
10
                                    "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
1273
10
                            poDS->m_poBaseDS = this;
1274
10
                            poOvrDS->m_poMaskDS = std::move(poDS);
1275
10
                            break;
1276
10
                        }
1277
152
                    }
1278
35.1k
                }
1279
48.2k
            }
1280
85.5k
            else if (!m_bSingleIFDOpened &&
1281
85.5k
                     (nSubType == 0 || nSubType == FILETYPE_PAGE))
1282
79.1k
            {
1283
79.1k
                uint32_t nXSize = 0;
1284
79.1k
                uint32_t nYSize = 0;
1285
1286
79.1k
                TIFFGetField(m_hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize);
1287
79.1k
                TIFFGetField(m_hTIFF, TIFFTAG_IMAGELENGTH, &nYSize);
1288
1289
                // For Geodetic TIFF grids (GTG)
1290
                // (https://proj.org/specifications/geodetictiffgrids.html)
1291
                // extract the grid_name to put it in the description
1292
79.1k
                std::string osFriendlyName;
1293
79.1k
                char *pszText = nullptr;
1294
79.1k
                if (TIFFGetField(m_hTIFF, TIFFTAG_GDAL_METADATA, &pszText) &&
1295
4.76k
                    strstr(pszText, "grid_name") != nullptr)
1296
3.47k
                {
1297
3.47k
                    CPLXMLNode *psRoot = CPLParseXMLString(pszText);
1298
3.47k
                    const CPLXMLNode *psItem =
1299
3.47k
                        psRoot ? CPLGetXMLNode(psRoot, "=GDALMetadata")
1300
3.47k
                               : nullptr;
1301
3.47k
                    if (psItem)
1302
116
                        psItem = psItem->psChild;
1303
4.95k
                    for (; psItem != nullptr; psItem = psItem->psNext)
1304
1.49k
                    {
1305
1306
1.49k
                        if (psItem->eType != CXT_Element ||
1307
1.36k
                            !EQUAL(psItem->pszValue, "Item"))
1308
199
                            continue;
1309
1310
1.29k
                        const char *pszKey =
1311
1.29k
                            CPLGetXMLValue(psItem, "name", nullptr);
1312
1.29k
                        const char *pszValue =
1313
1.29k
                            CPLGetXMLValue(psItem, nullptr, nullptr);
1314
1.29k
                        int nBand =
1315
1.29k
                            atoi(CPLGetXMLValue(psItem, "sample", "-1"));
1316
1.29k
                        if (pszKey && pszValue && nBand <= 0 &&
1317
787
                            EQUAL(pszKey, "grid_name"))
1318
21
                        {
1319
21
                            osFriendlyName = ": ";
1320
21
                            osFriendlyName += pszValue;
1321
21
                            break;
1322
21
                        }
1323
1.29k
                    }
1324
1325
3.47k
                    CPLDestroyXMLNode(psRoot);
1326
3.47k
                }
1327
1328
79.1k
                if (nXSize > INT_MAX || nYSize > INT_MAX)
1329
1.21k
                {
1330
1.21k
                    CPLDebug("GTiff",
1331
1.21k
                             "Skipping directory with too large image: %u x %u",
1332
1.21k
                             nXSize, nYSize);
1333
1.21k
                }
1334
77.9k
                else
1335
77.9k
                {
1336
77.9k
                    uint16_t nSPP = 0;
1337
77.9k
                    if (!TIFFGetField(m_hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSPP))
1338
47.2k
                        nSPP = 1;
1339
1340
77.9k
                    CPLString osName, osDesc;
1341
77.9k
                    osName.Printf("SUBDATASET_%d_NAME=GTIFF_DIR:%d:%s",
1342
77.9k
                                  iDirIndex, iDirIndex, m_osFilename.c_str());
1343
77.9k
                    osDesc.Printf(
1344
77.9k
                        "SUBDATASET_%d_DESC=Page %d (%dP x %dL x %dB)",
1345
77.9k
                        iDirIndex, iDirIndex, static_cast<int>(nXSize),
1346
77.9k
                        static_cast<int>(nYSize), nSPP);
1347
77.9k
                    osDesc += osFriendlyName;
1348
1349
77.9k
                    aosSubdatasets.AddString(osName);
1350
77.9k
                    aosSubdatasets.AddString(osDesc);
1351
77.9k
                }
1352
79.1k
            }
1353
193k
        }
1354
61.3k
        CPLFree(subIFDOffsets);
1355
1356
        // Make sure we are stepping from the expected directory regardless
1357
        // of churn done processing the above.
1358
61.3k
        if (TIFFCurrentDirOffset(m_hTIFF) != nTopDir)
1359
10.6k
            TIFFSetSubDirectory(m_hTIFF, nTopDir);
1360
61.3k
    } while (!m_bSingleIFDOpened && !TIFFLastDirectory(m_hTIFF) &&
1361
59.7k
             TIFFReadDirectory(m_hTIFF) != 0);
1362
1363
94.9k
    ReloadDirectory();
1364
1365
    // If we have a mask for the main image, loop over the overviews, and if
1366
    // they have a mask, let's set this mask as an overview of the main mask.
1367
94.9k
    if (m_poMaskDS)
1368
557
    {
1369
557
        for (auto &poOvrDS : m_apoOverviewDS)
1370
61
        {
1371
61
            if (poOvrDS->m_poMaskDS)
1372
2
            {
1373
2
                m_poMaskDS->m_apoOverviewDS.push_back(poOvrDS->m_poMaskDS);
1374
2
            }
1375
61
        }
1376
557
    }
1377
1378
    // Assign color interpretation from main dataset
1379
94.9k
    const int l_nBands = GetRasterCount();
1380
94.9k
    for (auto &poOvrDS : m_apoOverviewDS)
1381
4.87k
    {
1382
309k
        for (int i = 1; i <= l_nBands; i++)
1383
304k
        {
1384
304k
            auto poBand =
1385
304k
                dynamic_cast<GTiffRasterBand *>(poOvrDS->GetRasterBand(i));
1386
304k
            if (poBand)
1387
304k
                poBand->m_eBandInterp =
1388
304k
                    GetRasterBand(i)->GetColorInterpretation();
1389
304k
        }
1390
4.87k
    }
1391
1392
    /* -------------------------------------------------------------------- */
1393
    /*      Only keep track of subdatasets if we have more than one         */
1394
    /*      subdataset (pair).                                              */
1395
    /* -------------------------------------------------------------------- */
1396
94.9k
    if (aosSubdatasets.size() > 2)
1397
2.18k
    {
1398
2.18k
        m_oGTiffMDMD.SetMetadata(aosSubdatasets.List(), "SUBDATASETS");
1399
2.18k
    }
1400
94.9k
}
1401
1402
/************************************************************************/
1403
/*                         GetInternalHandle()                          */
1404
/************************************************************************/
1405
1406
void *GTiffDataset::GetInternalHandle(const char *pszHandleName)
1407
1408
49.0k
{
1409
49.0k
    if (pszHandleName && EQUAL(pszHandleName, "TIFF_HANDLE"))
1410
484
        return m_hTIFF;
1411
48.5k
    return nullptr;
1412
49.0k
}
1413
1414
/************************************************************************/
1415
/*                            GetFileList()                             */
1416
/************************************************************************/
1417
1418
char **GTiffDataset::GetFileList()
1419
1420
93.1k
{
1421
93.1k
    if (m_poBaseDS != nullptr)
1422
6
        return nullptr;
1423
1424
93.1k
    LoadGeoreferencingAndPamIfNeeded();
1425
1426
93.1k
    CPLStringList aosFiles(GDALPamDataset::GetFileList());
1427
1428
93.1k
    LoadMetadata();
1429
93.1k
    if (nullptr != m_papszMetadataFiles)
1430
2.95k
    {
1431
6.27k
        for (int i = 0; m_papszMetadataFiles[i] != nullptr; ++i)
1432
3.32k
        {
1433
3.32k
            if (aosFiles.FindString(m_papszMetadataFiles[i]) < 0)
1434
3.10k
            {
1435
3.10k
                aosFiles.AddString(m_papszMetadataFiles[i]);
1436
3.10k
            }
1437
3.32k
        }
1438
2.95k
    }
1439
1440
93.1k
    if (m_pszGeorefFilename && aosFiles.FindString(m_pszGeorefFilename) == -1)
1441
16
    {
1442
16
        aosFiles.AddString(m_pszGeorefFilename);
1443
16
    }
1444
1445
93.1k
    if (m_nXMLGeorefSrcIndex >= 0)
1446
93.1k
        LookForProjection();
1447
1448
93.1k
    if (m_pszXMLFilename && aosFiles.FindString(m_pszXMLFilename) == -1)
1449
0
    {
1450
0
        aosFiles.AddString(m_pszXMLFilename);
1451
0
    }
1452
1453
93.1k
    const std::string osVATDBF = m_osFilename + ".vat.dbf";
1454
93.1k
    VSIStatBufL sStat;
1455
93.1k
    if (VSIStatL(osVATDBF.c_str(), &sStat) == 0)
1456
2
    {
1457
2
        aosFiles.AddString(osVATDBF.c_str());
1458
2
    }
1459
1460
93.1k
    LoadENVIHdrIfNeeded();
1461
93.1k
    if (m_bENVIHdrFound)
1462
0
    {
1463
0
        aosFiles.AddString(
1464
0
            GetSidecarFilenameWithReplacedExtension("hdr").c_str());
1465
0
    }
1466
1467
93.1k
    return aosFiles.StealList();
1468
93.1k
}
1469
1470
/************************************************************************/
1471
/*                         GetRawBinaryLayout()                         */
1472
/************************************************************************/
1473
1474
bool GTiffDataset::GetRawBinaryLayout(GDALDataset::RawBinaryLayout &sLayout)
1475
0
{
1476
0
    if (eAccess == GA_Update)
1477
0
    {
1478
0
        FlushCache(false);
1479
0
        Crystalize();
1480
0
    }
1481
1482
0
    if (m_nCompression != COMPRESSION_NONE)
1483
0
        return false;
1484
0
    if (!CPLIsPowerOfTwo(m_nBitsPerSample) || m_nBitsPerSample < 8)
1485
0
        return false;
1486
0
    const auto eDT = GetRasterBand(1)->GetRasterDataType();
1487
0
    if (GDALDataTypeIsComplex(eDT))
1488
0
        return false;
1489
1490
0
    toff_t *panByteCounts = nullptr;
1491
0
    toff_t *panOffsets = nullptr;
1492
0
    const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(m_hTIFF));
1493
1494
0
    if (!((bIsTiled &&
1495
0
           TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts) &&
1496
0
           TIFFGetField(m_hTIFF, TIFFTAG_TILEOFFSETS, &panOffsets)) ||
1497
0
          (!bIsTiled &&
1498
0
           TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts) &&
1499
0
           TIFFGetField(m_hTIFF, TIFFTAG_STRIPOFFSETS, &panOffsets))))
1500
0
    {
1501
0
        return false;
1502
0
    }
1503
1504
0
    const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
1505
0
    vsi_l_offset nImgOffset = panOffsets[0];
1506
0
    GIntBig nPixelOffset = (m_nPlanarConfig == PLANARCONFIG_CONTIG)
1507
0
                               ? static_cast<GIntBig>(nDTSize) * nBands
1508
0
                               : nDTSize;
1509
0
    GIntBig nLineOffset = nPixelOffset * nRasterXSize;
1510
0
    GIntBig nBandOffset =
1511
0
        (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1) ? nDTSize : 0;
1512
0
    RawBinaryLayout::Interleaving eInterleaving =
1513
0
        (nBands == 1) ? RawBinaryLayout::Interleaving::UNKNOWN
1514
0
        : (m_nPlanarConfig == PLANARCONFIG_CONTIG)
1515
0
            ? RawBinaryLayout::Interleaving::BIP
1516
0
            : RawBinaryLayout::Interleaving::BSQ;
1517
0
    if (bIsTiled)
1518
0
    {
1519
        // Only a single block tiled file with same dimension as the raster
1520
        // might be acceptable
1521
0
        if (m_nBlockXSize != nRasterXSize || m_nBlockYSize != nRasterYSize)
1522
0
            return false;
1523
0
        if (nBands > 1 && m_nPlanarConfig != PLANARCONFIG_CONTIG)
1524
0
        {
1525
0
            nBandOffset = static_cast<GIntBig>(panOffsets[1]) -
1526
0
                          static_cast<GIntBig>(panOffsets[0]);
1527
0
            for (int i = 2; i < nBands; i++)
1528
0
            {
1529
0
                if (static_cast<GIntBig>(panOffsets[i]) -
1530
0
                        static_cast<GIntBig>(panOffsets[i - 1]) !=
1531
0
                    nBandOffset)
1532
0
                    return false;
1533
0
            }
1534
0
        }
1535
0
    }
1536
0
    else
1537
0
    {
1538
0
        const int nStrips = DIV_ROUND_UP(nRasterYSize, m_nRowsPerStrip);
1539
0
        if (nBands == 1 || m_nPlanarConfig == PLANARCONFIG_CONTIG)
1540
0
        {
1541
0
            vsi_l_offset nLastStripEnd = panOffsets[0] + panByteCounts[0];
1542
0
            for (int iStrip = 1; iStrip < nStrips; iStrip++)
1543
0
            {
1544
0
                if (nLastStripEnd != panOffsets[iStrip])
1545
0
                    return false;
1546
0
                nLastStripEnd = panOffsets[iStrip] + panByteCounts[iStrip];
1547
0
            }
1548
0
        }
1549
0
        else
1550
0
        {
1551
            // Note: we could potentially have BIL order with m_nRowsPerStrip ==
1552
            // 1 and if strips are ordered strip_line_1_band_1, ...,
1553
            // strip_line_1_band_N, strip_line2_band1, ... strip_line2_band_N,
1554
            // etc.... but that'd be faily exotic ! So only detect BSQ layout
1555
            // here
1556
0
            nBandOffset = static_cast<GIntBig>(panOffsets[nStrips]) -
1557
0
                          static_cast<GIntBig>(panOffsets[0]);
1558
0
            for (int i = 0; i < nBands; i++)
1559
0
            {
1560
0
                uint32_t iStripOffset = nStrips * i;
1561
0
                vsi_l_offset nLastStripEnd =
1562
0
                    panOffsets[iStripOffset] + panByteCounts[iStripOffset];
1563
0
                for (int iStrip = 1; iStrip < nStrips; iStrip++)
1564
0
                {
1565
0
                    if (nLastStripEnd != panOffsets[iStripOffset + iStrip])
1566
0
                        return false;
1567
0
                    nLastStripEnd = panOffsets[iStripOffset + iStrip] +
1568
0
                                    panByteCounts[iStripOffset + iStrip];
1569
0
                }
1570
0
                if (i >= 2 && static_cast<GIntBig>(panOffsets[iStripOffset]) -
1571
0
                                      static_cast<GIntBig>(
1572
0
                                          panOffsets[iStripOffset - nStrips]) !=
1573
0
                                  nBandOffset)
1574
0
                {
1575
0
                    return false;
1576
0
                }
1577
0
            }
1578
0
        }
1579
0
    }
1580
1581
0
    sLayout.osRawFilename = m_osFilename;
1582
0
    sLayout.eInterleaving = eInterleaving;
1583
0
    sLayout.eDataType = eDT;
1584
0
#ifdef CPL_LSB
1585
0
    sLayout.bLittleEndianOrder = !TIFFIsByteSwapped(m_hTIFF);
1586
#else
1587
    sLayout.bLittleEndianOrder = TIFFIsByteSwapped(m_hTIFF);
1588
#endif
1589
0
    sLayout.nImageOffset = nImgOffset;
1590
0
    sLayout.nPixelOffset = nPixelOffset;
1591
0
    sLayout.nLineOffset = nLineOffset;
1592
0
    sLayout.nBandOffset = nBandOffset;
1593
1594
0
    return true;
1595
0
}
1596
1597
/************************************************************************/
1598
/*                GTiffDatasetLibGeotiffErrorCallback()                 */
1599
/************************************************************************/
1600
1601
static void GTiffDatasetLibGeotiffErrorCallback(GTIF *, int level,
1602
                                                const char *pszMsg, ...)
1603
127k
{
1604
127k
    va_list ap;
1605
127k
    va_start(ap, pszMsg);
1606
127k
    CPLErrorV((level == LIBGEOTIFF_WARNING) ? CE_Warning : CE_Failure,
1607
127k
              CPLE_AppDefined, pszMsg, ap);
1608
127k
    va_end(ap);
1609
127k
}
1610
1611
/************************************************************************/
1612
/*                              GTIFNew()                               */
1613
/************************************************************************/
1614
1615
/* static */ GTIF *GTiffDataset::GTIFNew(TIFF *hTIFF)
1616
201k
{
1617
201k
    GTIF *gtif = GTIFNewEx(hTIFF, GTiffDatasetLibGeotiffErrorCallback, nullptr);
1618
201k
    if (gtif)
1619
189k
    {
1620
189k
        GTIFAttachPROJContext(gtif, OSRGetProjTLSContext());
1621
189k
    }
1622
201k
    return gtif;
1623
201k
}