Coverage Report

Created: 2025-11-16 06:25

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