Coverage Report

Created: 2026-07-14 06:21

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