Coverage Report

Created: 2025-08-28 06:57

/src/gdal/frmts/gtiff/gtiffdataset.cpp
Line
Count
Source (jump to first uncovered line)
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 "ogr_proj_p.h"  // OSRGetProjTLSContext()
33
#include "tif_jxl.h"
34
#include "tifvsi.h"
35
#include "xtiffio.h"
36
37
static const GTIFFTag asTIFFTags[] = {
38
    {"TIFFTAG_DOCUMENTNAME", TIFFTAG_DOCUMENTNAME, GTIFFTAGTYPE_STRING},
39
    {"TIFFTAG_IMAGEDESCRIPTION", TIFFTAG_IMAGEDESCRIPTION, GTIFFTAGTYPE_STRING},
40
    {"TIFFTAG_SOFTWARE", TIFFTAG_SOFTWARE, GTIFFTAGTYPE_STRING},
41
    {"TIFFTAG_DATETIME", TIFFTAG_DATETIME, GTIFFTAGTYPE_STRING},
42
    {"TIFFTAG_ARTIST", TIFFTAG_ARTIST, GTIFFTAGTYPE_STRING},
43
    {"TIFFTAG_HOSTCOMPUTER", TIFFTAG_HOSTCOMPUTER, GTIFFTAGTYPE_STRING},
44
    {"TIFFTAG_COPYRIGHT", TIFFTAG_COPYRIGHT, GTIFFTAGTYPE_STRING},
45
    {"TIFFTAG_XRESOLUTION", TIFFTAG_XRESOLUTION, GTIFFTAGTYPE_FLOAT},
46
    {"TIFFTAG_YRESOLUTION", TIFFTAG_YRESOLUTION, GTIFFTAGTYPE_FLOAT},
47
    // Dealt as special case.
48
    {"TIFFTAG_RESOLUTIONUNIT", TIFFTAG_RESOLUTIONUNIT, GTIFFTAGTYPE_SHORT},
49
    {"TIFFTAG_MINSAMPLEVALUE", TIFFTAG_MINSAMPLEVALUE, GTIFFTAGTYPE_SHORT},
50
    {"TIFFTAG_MAXSAMPLEVALUE", TIFFTAG_MAXSAMPLEVALUE, GTIFFTAGTYPE_SHORT},
51
52
    // GeoTIFF DGIWG tags
53
    {"GEO_METADATA", TIFFTAG_GEO_METADATA, GTIFFTAGTYPE_BYTE_STRING},
54
    {"TIFF_RSID", TIFFTAG_TIFF_RSID, GTIFFTAGTYPE_STRING},
55
    {nullptr, 0, GTIFFTAGTYPE_STRING},
56
};
57
58
/************************************************************************/
59
/*                            GetTIFFTags()                             */
60
/************************************************************************/
61
62
const GTIFFTag *GTiffDataset::GetTIFFTags()
63
0
{
64
0
    return asTIFFTags;
65
0
}
66
67
/************************************************************************/
68
/*                            GTiffDataset()                            */
69
/************************************************************************/
70
71
GTiffDataset::GTiffDataset()
72
0
    : m_bStreamingIn(false), m_bStreamingOut(false), m_bScanDeferred(true),
73
0
      m_bSingleIFDOpened(false), m_bLoadedBlockDirty(false),
74
0
      m_bWriteError(false), m_bLookedForProjection(false),
75
0
      m_bLookedForMDAreaOrPoint(false), m_bGeoTransformValid(false),
76
0
      m_bCrystalized(true), m_bGeoTIFFInfoChanged(false),
77
0
      m_bForceUnsetGTOrGCPs(false), m_bForceUnsetProjection(false),
78
0
      m_bNoDataChanged(false), m_bNoDataSet(false), m_bNoDataSetAsInt64(false),
79
0
      m_bNoDataSetAsUInt64(false), m_bMetadataChanged(false),
80
0
      m_bColorProfileMetadataChanged(false), m_bForceUnsetRPC(false),
81
0
      m_bNeedsRewrite(false), m_bLoadingOtherBands(false), m_bIsOverview(false),
82
0
      m_bWriteEmptyTiles(true), m_bFillEmptyTilesAtClosing(false),
83
0
      m_bTreatAsSplit(false), m_bTreatAsSplitBitmap(false), m_bClipWarn(false),
84
0
      m_bIMDRPCMetadataLoaded(false), m_bEXIFMetadataLoaded(false),
85
0
      m_bICCMetadataLoaded(false),
86
0
      m_bHasWarnedDisableAggressiveBandCaching(false),
87
0
      m_bDontReloadFirstBlock(false), m_bWebPLossless(false),
88
0
      m_bPromoteTo8Bits(false),
89
      m_bDebugDontWriteBlocks(
90
0
          CPLTestBool(CPLGetConfigOption("GTIFF_DONT_WRITE_BLOCKS", "NO"))),
91
0
      m_bIsFinalized(false),
92
      m_bIgnoreReadErrors(
93
0
          CPLTestBool(CPLGetConfigOption("GTIFF_IGNORE_READ_ERRORS", "NO"))),
94
0
      m_bDirectIO(CPLTestBool(CPLGetConfigOption("GTIFF_DIRECT_IO", "NO"))),
95
0
      m_bReadGeoTransform(false), m_bLoadPam(false),
96
0
      m_bHasGotSiblingFiles(false),
97
0
      m_bHasIdentifiedAuthorizedGeoreferencingSources(false),
98
0
      m_bLayoutIFDSBeforeData(false), m_bBlockOrderRowMajor(false),
99
0
      m_bLeaderSizeAsUInt4(false), m_bTrailerRepeatedLast4BytesRepeated(false),
100
0
      m_bMaskInterleavedWithImagery(false), m_bKnownIncompatibleEdition(false),
101
0
      m_bWriteKnownIncompatibleEdition(false), m_bHasUsedReadEncodedAPI(false),
102
0
      m_bWriteCOGLayout(false), m_bTileInterleave(false)
103
0
{
104
    // CPLDebug("GDAL", "sizeof(GTiffDataset) = %d bytes", static_cast<int>(
105
    //     sizeof(GTiffDataset)));
106
107
0
    const char *pszVirtualMemIO =
108
0
        CPLGetConfigOption("GTIFF_VIRTUAL_MEM_IO", "NO");
109
0
    if (EQUAL(pszVirtualMemIO, "IF_ENOUGH_RAM"))
110
0
        m_eVirtualMemIOUsage = VirtualMemIOEnum::IF_ENOUGH_RAM;
111
0
    else if (CPLTestBool(pszVirtualMemIO))
112
0
        m_eVirtualMemIOUsage = VirtualMemIOEnum::YES;
113
114
0
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
115
0
}
116
117
/************************************************************************/
118
/*                           ~GTiffDataset()                            */
119
/************************************************************************/
120
121
GTiffDataset::~GTiffDataset()
122
123
0
{
124
0
    GTiffDataset::Close();
125
0
}
126
127
/************************************************************************/
128
/*                              Close()                                 */
129
/************************************************************************/
130
131
CPLErr GTiffDataset::Close()
132
0
{
133
0
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
134
0
    {
135
0
        auto [eErr, bDroppedRef] = Finalize();
136
137
0
        if (m_pszTmpFilename)
138
0
        {
139
0
            VSIUnlink(m_pszTmpFilename);
140
0
            CPLFree(m_pszTmpFilename);
141
0
        }
142
143
0
        if (GDALPamDataset::Close() != CE_None)
144
0
            eErr = CE_Failure;
145
0
        return eErr;
146
0
    }
147
0
    return CE_None;
148
0
}
149
150
/************************************************************************/
151
/*                             Finalize()                               */
152
/************************************************************************/
153
154
// Return a tuple (CPLErr, bool) to indicate respectively if an I/O error has
155
// occurred and if a reference to an auxiliary dataset has been dropped.
156
std::tuple<CPLErr, bool> GTiffDataset::Finalize()
157
0
{
158
0
    bool bDroppedRef = false;
159
0
    if (m_bIsFinalized)
160
0
        return std::tuple(CE_None, bDroppedRef);
161
162
0
    CPLErr eErr = CE_None;
163
0
    Crystalize();
164
165
0
    if (m_bColorProfileMetadataChanged)
166
0
    {
167
0
        SaveICCProfile(this, nullptr, nullptr, 0);
168
0
        m_bColorProfileMetadataChanged = false;
169
0
    }
170
171
    /* -------------------------------------------------------------------- */
172
    /*      Handle forcing xml:ESRI data to be written to PAM.              */
173
    /* -------------------------------------------------------------------- */
174
0
    if (CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
175
0
    {
176
0
        char **papszESRIMD = GTiffDataset::GetMetadata("xml:ESRI");
177
0
        if (papszESRIMD)
178
0
        {
179
0
            GDALPamDataset::SetMetadata(papszESRIMD, "xml:ESRI");
180
0
        }
181
0
    }
182
183
0
    if (m_psVirtualMemIOMapping)
184
0
        CPLVirtualMemFree(m_psVirtualMemIOMapping);
185
0
    m_psVirtualMemIOMapping = nullptr;
186
187
    /* -------------------------------------------------------------------- */
188
    /*      Fill in missing blocks with empty data.                         */
189
    /* -------------------------------------------------------------------- */
190
0
    if (m_bFillEmptyTilesAtClosing)
191
0
    {
192
        /* --------------------------------------------------------------------
193
         */
194
        /*  Ensure any blocks write cached by GDAL gets pushed through libtiff.
195
         */
196
        /* --------------------------------------------------------------------
197
         */
198
0
        if (FlushCacheInternal(true, /* at closing */
199
0
                               false /* do not call FlushDirectory */) !=
200
0
            CE_None)
201
0
        {
202
0
            eErr = CE_Failure;
203
0
        }
204
205
0
        if (FillEmptyTiles() != CE_None)
206
0
        {
207
0
            eErr = CE_Failure;
208
0
        }
209
0
        m_bFillEmptyTilesAtClosing = false;
210
0
    }
211
212
    /* -------------------------------------------------------------------- */
213
    /*      Force a complete flush, including either rewriting(moving)      */
214
    /*      of writing in place the current directory.                      */
215
    /* -------------------------------------------------------------------- */
216
0
    if (FlushCacheInternal(true /* at closing */, true) != CE_None)
217
0
    {
218
0
        eErr = CE_Failure;
219
0
    }
220
221
    // Destroy compression queue
222
0
    if (m_poCompressQueue)
223
0
    {
224
0
        m_poCompressQueue->WaitCompletion();
225
226
0
        for (int i = 0; i < static_cast<int>(m_asCompressionJobs.size()); ++i)
227
0
        {
228
0
            CPLFree(m_asCompressionJobs[i].pabyBuffer);
229
0
            if (m_asCompressionJobs[i].pszTmpFilename)
230
0
            {
231
0
                VSIUnlink(m_asCompressionJobs[i].pszTmpFilename);
232
0
                CPLFree(m_asCompressionJobs[i].pszTmpFilename);
233
0
            }
234
0
        }
235
0
        m_poCompressQueue.reset();
236
0
    }
237
238
    /* -------------------------------------------------------------------- */
239
    /*      If there is still changed metadata, then presumably we want     */
240
    /*      to push it into PAM.                                            */
241
    /* -------------------------------------------------------------------- */
242
0
    if (m_bMetadataChanged)
243
0
    {
244
0
        PushMetadataToPam();
245
0
        m_bMetadataChanged = false;
246
0
        GDALPamDataset::FlushCache(false);
247
0
    }
248
249
    /* -------------------------------------------------------------------- */
250
    /*      Cleanup overviews.                                              */
251
    /* -------------------------------------------------------------------- */
252
0
    if (!m_poBaseDS)
253
0
    {
254
        // Nullify m_nOverviewCount before deleting overviews, otherwise
255
        // GTiffDataset::FlushDirectory() might try to access an overview
256
        // that is being deleted (#5580)
257
0
        const int nOldOverviewCount = m_nOverviewCount;
258
0
        m_nOverviewCount = 0;
259
0
        for (int i = 0; i < nOldOverviewCount; ++i)
260
0
        {
261
0
            delete m_papoOverviewDS[i];
262
0
            bDroppedRef = true;
263
0
        }
264
265
0
        for (int i = 0; i < m_nJPEGOverviewCountOri; ++i)
266
0
        {
267
0
            delete m_papoJPEGOverviewDS[i];
268
0
            bDroppedRef = true;
269
0
        }
270
0
        m_nJPEGOverviewCount = 0;
271
0
        m_nJPEGOverviewCountOri = 0;
272
0
        CPLFree(m_papoJPEGOverviewDS);
273
0
        m_papoJPEGOverviewDS = nullptr;
274
0
    }
275
276
    // If we are a mask dataset, we can have overviews, but we don't
277
    // own them. We can only free the array, not the overviews themselves.
278
0
    CPLFree(m_papoOverviewDS);
279
0
    m_papoOverviewDS = nullptr;
280
281
    // m_poMaskDS is owned by the main image and the overviews
282
    // so because of the latter case, we can delete it even if
283
    // we are not the base image.
284
0
    if (m_poMaskDS)
285
0
    {
286
        // Nullify m_nOverviewCount before deleting overviews, otherwise
287
        // GTiffDataset::FlushDirectory() might try to access it while being
288
        // deleted. (#5580)
289
0
        auto poMaskDS = m_poMaskDS;
290
0
        m_poMaskDS = nullptr;
291
0
        delete poMaskDS;
292
0
        bDroppedRef = true;
293
0
    }
294
295
0
    m_poColorTable.reset();
296
297
0
    if (m_hTIFF)
298
0
    {
299
0
        XTIFFClose(m_hTIFF);
300
0
        m_hTIFF = nullptr;
301
0
    }
302
303
0
    if (!m_poBaseDS)
304
0
    {
305
0
        if (m_fpL != nullptr)
306
0
        {
307
0
            if (m_bWriteKnownIncompatibleEdition)
308
0
            {
309
0
                GByte abyHeader[4096];
310
0
                VSIFSeekL(m_fpL, 0, SEEK_SET);
311
0
                VSIFReadL(abyHeader, 1, sizeof(abyHeader), m_fpL);
312
0
                const char *szKeyToLook =
313
0
                    "KNOWN_INCOMPATIBLE_EDITION=NO\n ";  // trailing space
314
                                                         // intended
315
0
                for (size_t i = 0; i < sizeof(abyHeader) - strlen(szKeyToLook);
316
0
                     i++)
317
0
                {
318
0
                    if (memcmp(abyHeader + i, szKeyToLook,
319
0
                               strlen(szKeyToLook)) == 0)
320
0
                    {
321
0
                        const char *szNewKey =
322
0
                            "KNOWN_INCOMPATIBLE_EDITION=YES\n";
323
0
                        CPLAssert(strlen(szKeyToLook) == strlen(szNewKey));
324
0
                        memcpy(abyHeader + i, szNewKey, strlen(szNewKey));
325
0
                        VSIFSeekL(m_fpL, 0, SEEK_SET);
326
0
                        VSIFWriteL(abyHeader, 1, sizeof(abyHeader), m_fpL);
327
0
                        break;
328
0
                    }
329
0
                }
330
0
            }
331
332
0
            if (IsMarkedSuppressOnClose())
333
0
                m_fpL->CancelCreation();
334
335
0
            if (VSIFCloseL(m_fpL) != 0)
336
0
            {
337
0
                eErr = CE_Failure;
338
0
                ReportError(CE_Failure, CPLE_FileIO, "I/O error");
339
0
            }
340
0
            m_fpL = nullptr;
341
0
        }
342
0
    }
343
344
0
    if (m_fpToWrite != nullptr)
345
0
    {
346
0
        if (VSIFCloseL(m_fpToWrite) != 0)
347
0
        {
348
0
            eErr = CE_Failure;
349
0
            ReportError(CE_Failure, CPLE_FileIO, "I/O error");
350
0
        }
351
0
        m_fpToWrite = nullptr;
352
0
    }
353
354
0
    m_aoGCPs.clear();
355
356
0
    CSLDestroy(m_papszCreationOptions);
357
0
    m_papszCreationOptions = nullptr;
358
359
0
    CPLFree(m_pabyTempWriteBuffer);
360
0
    m_pabyTempWriteBuffer = nullptr;
361
362
0
    m_bIMDRPCMetadataLoaded = false;
363
0
    CSLDestroy(m_papszMetadataFiles);
364
0
    m_papszMetadataFiles = nullptr;
365
366
0
    VSIFree(m_pTempBufferForCommonDirectIO);
367
0
    m_pTempBufferForCommonDirectIO = nullptr;
368
369
0
    CPLFree(m_panMaskOffsetLsb);
370
0
    m_panMaskOffsetLsb = nullptr;
371
372
0
    CPLFree(m_pszVertUnit);
373
0
    m_pszVertUnit = nullptr;
374
375
0
    CPLFree(m_pszFilename);
376
0
    m_pszFilename = nullptr;
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_pszFilename);
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, m_fJXLDistance);
985
            TIFFSetField(hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE,
986
                         m_fJXLAlphaDistance);
987
        }
988
#endif
989
0
    }
990
0
}
991
992
/************************************************************************/
993
/*                     ComputeBlocksPerColRowAndBand()                  */
994
/************************************************************************/
995
996
bool GTiffDataset::ComputeBlocksPerColRowAndBand(int l_nBands)
997
0
{
998
0
    m_nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, m_nBlockYSize);
999
0
    m_nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, m_nBlockXSize);
1000
0
    if (m_nBlocksPerColumn > INT_MAX / m_nBlocksPerRow)
1001
0
    {
1002
0
        ReportError(CE_Failure, CPLE_AppDefined, "Too many blocks: %d x %d",
1003
0
                    m_nBlocksPerRow, m_nBlocksPerColumn);
1004
0
        return false;
1005
0
    }
1006
1007
    // Note: we could potentially go up to UINT_MAX blocks, but currently
1008
    // we use a int nBlockId
1009
0
    m_nBlocksPerBand = m_nBlocksPerColumn * m_nBlocksPerRow;
1010
0
    if (m_nPlanarConfig == PLANARCONFIG_SEPARATE &&
1011
0
        m_nBlocksPerBand > INT_MAX / l_nBands)
1012
0
    {
1013
0
        ReportError(CE_Failure, CPLE_AppDefined,
1014
0
                    "Too many blocks: %d x %d x %d bands", m_nBlocksPerRow,
1015
0
                    m_nBlocksPerColumn, l_nBands);
1016
0
        return false;
1017
0
    }
1018
0
    return true;
1019
0
}
1020
1021
/************************************************************************/
1022
/*                   SetStructuralMDFromParent()                        */
1023
/************************************************************************/
1024
1025
void GTiffDataset::SetStructuralMDFromParent(GTiffDataset *poParentDS)
1026
0
{
1027
0
    m_bBlockOrderRowMajor = poParentDS->m_bBlockOrderRowMajor;
1028
0
    m_bLeaderSizeAsUInt4 = poParentDS->m_bLeaderSizeAsUInt4;
1029
0
    m_bTrailerRepeatedLast4BytesRepeated =
1030
0
        poParentDS->m_bTrailerRepeatedLast4BytesRepeated;
1031
0
    m_bMaskInterleavedWithImagery = poParentDS->m_bMaskInterleavedWithImagery;
1032
0
    m_bWriteEmptyTiles = poParentDS->m_bWriteEmptyTiles;
1033
0
    m_bTileInterleave = poParentDS->m_bTileInterleave;
1034
0
}
1035
1036
/************************************************************************/
1037
/*                          ScanDirectories()                           */
1038
/*                                                                      */
1039
/*      Scan through all the directories finding overviews, masks       */
1040
/*      and subdatasets.                                                */
1041
/************************************************************************/
1042
1043
void GTiffDataset::ScanDirectories()
1044
1045
0
{
1046
    /* -------------------------------------------------------------------- */
1047
    /*      We only scan once.  We do not scan for non-base datasets.       */
1048
    /* -------------------------------------------------------------------- */
1049
0
    if (!m_bScanDeferred)
1050
0
        return;
1051
1052
0
    m_bScanDeferred = false;
1053
1054
0
    if (m_poBaseDS)
1055
0
        return;
1056
1057
0
    Crystalize();
1058
1059
0
    CPLDebug("GTiff", "ScanDirectories()");
1060
1061
    /* ==================================================================== */
1062
    /*      Scan all directories.                                           */
1063
    /* ==================================================================== */
1064
0
    CPLStringList aosSubdatasets;
1065
0
    int iDirIndex = 0;
1066
1067
0
    FlushDirectory();
1068
1069
0
    do
1070
0
    {
1071
0
        toff_t nTopDir = TIFFCurrentDirOffset(m_hTIFF);
1072
0
        uint32_t nSubType = 0;
1073
1074
0
        ++iDirIndex;
1075
1076
0
        toff_t *tmpSubIFDOffsets = nullptr;
1077
0
        toff_t *subIFDOffsets = nullptr;
1078
0
        uint16_t nSubIFDs = 0;
1079
0
        if (TIFFGetField(m_hTIFF, TIFFTAG_SUBIFD, &nSubIFDs,
1080
0
                         &tmpSubIFDOffsets) &&
1081
0
            iDirIndex == 1)
1082
0
        {
1083
0
            subIFDOffsets =
1084
0
                static_cast<toff_t *>(CPLMalloc(nSubIFDs * sizeof(toff_t)));
1085
0
            for (uint16_t iSubIFD = 0; iSubIFD < nSubIFDs; iSubIFD++)
1086
0
            {
1087
0
                subIFDOffsets[iSubIFD] = tmpSubIFDOffsets[iSubIFD];
1088
0
            }
1089
0
        }
1090
1091
        // early break for backwards compatibility: if the first directory read
1092
        // is also the last, and there are no subIFDs, no use continuing
1093
0
        if (iDirIndex == 1 && nSubIFDs == 0 && TIFFLastDirectory(m_hTIFF))
1094
0
        {
1095
0
            CPLFree(subIFDOffsets);
1096
0
            break;
1097
0
        }
1098
1099
0
        for (uint16_t iSubIFD = 0; iSubIFD <= nSubIFDs; iSubIFD++)
1100
0
        {
1101
0
            toff_t nThisDir = nTopDir;
1102
0
            if (iSubIFD > 0 && iDirIndex > 1)  // don't read subIFDs if we are
1103
                                               // not in the original directory
1104
0
                break;
1105
0
            if (iSubIFD > 0)
1106
0
            {
1107
                // make static analyzer happy. subIFDOffsets cannot be null if
1108
                // iSubIFD>0
1109
0
                assert(subIFDOffsets != nullptr);
1110
0
                nThisDir = subIFDOffsets[iSubIFD - 1];
1111
                // CPLDebug("GTiff", "Opened subIFD %d/%d at offset %llu.",
1112
                // iSubIFD, nSubIFDs, nThisDir);
1113
0
                if (!TIFFSetSubDirectory(m_hTIFF, nThisDir))
1114
0
                    break;
1115
0
            }
1116
1117
0
            if (!TIFFGetField(m_hTIFF, TIFFTAG_SUBFILETYPE, &nSubType))
1118
0
                nSubType = 0;
1119
1120
            /* Embedded overview of the main image */
1121
0
            if ((nSubType & FILETYPE_REDUCEDIMAGE) != 0 &&
1122
0
                (nSubType & FILETYPE_MASK) == 0 &&
1123
0
                ((nSubIFDs == 0 && iDirIndex != 1) || iSubIFD > 0) &&
1124
0
                m_nOverviewCount < 30 /* to avoid DoS */)
1125
0
            {
1126
0
                GTiffDataset *poODS = new GTiffDataset();
1127
0
                poODS->ShareLockWithParentDataset(this);
1128
0
                poODS->SetStructuralMDFromParent(this);
1129
0
                if (m_bHasGotSiblingFiles)
1130
0
                    poODS->oOvManager.TransferSiblingFiles(
1131
0
                        CSLDuplicate(GetSiblingFiles()));
1132
0
                poODS->m_pszFilename = CPLStrdup(m_pszFilename);
1133
0
                poODS->m_nColorTableMultiplier = m_nColorTableMultiplier;
1134
0
                if (poODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nThisDir,
1135
0
                                      eAccess) != CE_None ||
1136
0
                    poODS->GetRasterCount() != GetRasterCount())
1137
0
                {
1138
0
                    delete poODS;
1139
0
                }
1140
0
                else
1141
0
                {
1142
0
                    CPLDebug("GTiff", "Opened %dx%d overview.",
1143
0
                             poODS->GetRasterXSize(), poODS->GetRasterYSize());
1144
0
                    ++m_nOverviewCount;
1145
0
                    m_papoOverviewDS = static_cast<GTiffDataset **>(CPLRealloc(
1146
0
                        m_papoOverviewDS, m_nOverviewCount * (sizeof(void *))));
1147
0
                    m_papoOverviewDS[m_nOverviewCount - 1] = poODS;
1148
0
                    poODS->m_poBaseDS = this;
1149
0
                    poODS->m_bIsOverview = true;
1150
1151
                    // Propagate a few compression related settings that are
1152
                    // no preserved at the TIFF tag level, but may be set in
1153
                    // the GDAL_METADATA tag in the IMAGE_STRUCTURE domain
1154
                    // Note: this might not be totally reflecting the reality
1155
                    // if users have created overviews with different settings
1156
                    // but this is probably better than the default ones
1157
0
                    poODS->m_nWebPLevel = m_nWebPLevel;
1158
                    // below is not a copy & paste error: we transfer the
1159
                    // m_dfMaxZErrorOverview overview of the parent to
1160
                    // m_dfMaxZError of the overview
1161
0
                    poODS->m_dfMaxZError = m_dfMaxZErrorOverview;
1162
0
                    poODS->m_dfMaxZErrorOverview = m_dfMaxZErrorOverview;
1163
#if HAVE_JXL
1164
                    poODS->m_bJXLLossless = m_bJXLLossless;
1165
                    poODS->m_fJXLDistance = m_fJXLDistance;
1166
                    poODS->m_fJXLAlphaDistance = m_fJXLAlphaDistance;
1167
                    poODS->m_nJXLEffort = m_nJXLEffort;
1168
#endif
1169
                    // Those ones are not serialized currently..
1170
                    // poODS->m_nZLevel = m_nZLevel;
1171
                    // poODS->m_nLZMAPreset = m_nLZMAPreset;
1172
                    // poODS->m_nZSTDLevel = m_nZSTDLevel;
1173
0
                }
1174
0
            }
1175
            // Embedded mask of the main image.
1176
0
            else if ((nSubType & FILETYPE_MASK) != 0 &&
1177
0
                     (nSubType & FILETYPE_REDUCEDIMAGE) == 0 &&
1178
0
                     ((nSubIFDs == 0 && iDirIndex != 1) || iSubIFD > 0) &&
1179
0
                     m_poMaskDS == nullptr)
1180
0
            {
1181
0
                m_poMaskDS = new GTiffDataset();
1182
0
                m_poMaskDS->ShareLockWithParentDataset(this);
1183
0
                m_poMaskDS->SetStructuralMDFromParent(this);
1184
0
                m_poMaskDS->m_pszFilename = CPLStrdup(m_pszFilename);
1185
1186
                // The TIFF6 specification - page 37 - only allows 1
1187
                // SamplesPerPixel and 1 BitsPerSample Here we support either 1
1188
                // or 8 bit per sample and we support either 1 sample per pixel
1189
                // or as many samples as in the main image We don't check the
1190
                // value of the PhotometricInterpretation tag, which should be
1191
                // set to "Transparency mask" (4) according to the specification
1192
                // (page 36).  However, the TIFF6 specification allows image
1193
                // masks to have a higher resolution than the main image, what
1194
                // we don't support here.
1195
1196
0
                if (m_poMaskDS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nThisDir,
1197
0
                                           eAccess) != CE_None ||
1198
0
                    m_poMaskDS->GetRasterCount() == 0 ||
1199
0
                    !(m_poMaskDS->GetRasterCount() == 1 ||
1200
0
                      m_poMaskDS->GetRasterCount() == GetRasterCount()) ||
1201
0
                    m_poMaskDS->GetRasterXSize() != GetRasterXSize() ||
1202
0
                    m_poMaskDS->GetRasterYSize() != GetRasterYSize() ||
1203
0
                    m_poMaskDS->GetRasterBand(1)->GetRasterDataType() !=
1204
0
                        GDT_Byte)
1205
0
                {
1206
0
                    delete m_poMaskDS;
1207
0
                    m_poMaskDS = nullptr;
1208
0
                }
1209
0
                else
1210
0
                {
1211
0
                    CPLDebug("GTiff", "Opened band mask.");
1212
0
                    m_poMaskDS->m_poBaseDS = this;
1213
0
                    m_poMaskDS->m_poImageryDS = this;
1214
1215
0
                    m_poMaskDS->m_bPromoteTo8Bits =
1216
0
                        CPLTestBool(CPLGetConfigOption(
1217
0
                            "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
1218
0
                }
1219
0
            }
1220
1221
            // Embedded mask of an overview.  The TIFF6 specification allows the
1222
            // combination of the FILETYPE_xxxx masks.
1223
0
            else if ((nSubType & FILETYPE_REDUCEDIMAGE) != 0 &&
1224
0
                     (nSubType & FILETYPE_MASK) != 0 &&
1225
0
                     ((nSubIFDs == 0 && iDirIndex != 1) || iSubIFD > 0))
1226
0
            {
1227
0
                GTiffDataset *poDS = new GTiffDataset();
1228
0
                poDS->ShareLockWithParentDataset(this);
1229
0
                poDS->SetStructuralMDFromParent(this);
1230
0
                poDS->m_pszFilename = CPLStrdup(m_pszFilename);
1231
0
                if (poDS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nThisDir,
1232
0
                                     eAccess) != CE_None ||
1233
0
                    poDS->GetRasterCount() == 0 ||
1234
0
                    poDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte)
1235
0
                {
1236
0
                    delete poDS;
1237
0
                }
1238
0
                else
1239
0
                {
1240
0
                    int i = 0;  // Used after for.
1241
0
                    for (; i < m_nOverviewCount; ++i)
1242
0
                    {
1243
0
                        auto poOvrDS = cpl::down_cast<GTiffDataset *>(
1244
0
                            GDALDataset::FromHandle(m_papoOverviewDS[i]));
1245
0
                        if (poOvrDS->m_poMaskDS == nullptr &&
1246
0
                            poDS->GetRasterXSize() ==
1247
0
                                m_papoOverviewDS[i]->GetRasterXSize() &&
1248
0
                            poDS->GetRasterYSize() ==
1249
0
                                m_papoOverviewDS[i]->GetRasterYSize() &&
1250
0
                            (poDS->GetRasterCount() == 1 ||
1251
0
                             poDS->GetRasterCount() == GetRasterCount()))
1252
0
                        {
1253
0
                            CPLDebug(
1254
0
                                "GTiff", "Opened band mask for %dx%d overview.",
1255
0
                                poDS->GetRasterXSize(), poDS->GetRasterYSize());
1256
0
                            poDS->m_poImageryDS = poOvrDS;
1257
0
                            poOvrDS->m_poMaskDS = poDS;
1258
0
                            poDS->m_bPromoteTo8Bits =
1259
0
                                CPLTestBool(CPLGetConfigOption(
1260
0
                                    "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
1261
0
                            poDS->m_poBaseDS = this;
1262
0
                            break;
1263
0
                        }
1264
0
                    }
1265
0
                    if (i == m_nOverviewCount)
1266
0
                    {
1267
0
                        delete poDS;
1268
0
                    }
1269
0
                }
1270
0
            }
1271
0
            else if (!m_bSingleIFDOpened &&
1272
0
                     (nSubType == 0 || nSubType == FILETYPE_PAGE))
1273
0
            {
1274
0
                uint32_t nXSize = 0;
1275
0
                uint32_t nYSize = 0;
1276
1277
0
                TIFFGetField(m_hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize);
1278
0
                TIFFGetField(m_hTIFF, TIFFTAG_IMAGELENGTH, &nYSize);
1279
1280
                // For Geodetic TIFF grids (GTG)
1281
                // (https://proj.org/specifications/geodetictiffgrids.html)
1282
                // extract the grid_name to put it in the description
1283
0
                std::string osFriendlyName;
1284
0
                char *pszText = nullptr;
1285
0
                if (TIFFGetField(m_hTIFF, TIFFTAG_GDAL_METADATA, &pszText) &&
1286
0
                    strstr(pszText, "grid_name") != nullptr)
1287
0
                {
1288
0
                    CPLXMLNode *psRoot = CPLParseXMLString(pszText);
1289
0
                    const CPLXMLNode *psItem =
1290
0
                        psRoot ? CPLGetXMLNode(psRoot, "=GDALMetadata")
1291
0
                               : nullptr;
1292
0
                    if (psItem)
1293
0
                        psItem = psItem->psChild;
1294
0
                    for (; psItem != nullptr; psItem = psItem->psNext)
1295
0
                    {
1296
1297
0
                        if (psItem->eType != CXT_Element ||
1298
0
                            !EQUAL(psItem->pszValue, "Item"))
1299
0
                            continue;
1300
1301
0
                        const char *pszKey =
1302
0
                            CPLGetXMLValue(psItem, "name", nullptr);
1303
0
                        const char *pszValue =
1304
0
                            CPLGetXMLValue(psItem, nullptr, nullptr);
1305
0
                        int nBand =
1306
0
                            atoi(CPLGetXMLValue(psItem, "sample", "-1"));
1307
0
                        if (pszKey && pszValue && nBand <= 0 &&
1308
0
                            EQUAL(pszKey, "grid_name"))
1309
0
                        {
1310
0
                            osFriendlyName = ": ";
1311
0
                            osFriendlyName += pszValue;
1312
0
                            break;
1313
0
                        }
1314
0
                    }
1315
1316
0
                    CPLDestroyXMLNode(psRoot);
1317
0
                }
1318
1319
0
                if (nXSize > INT_MAX || nYSize > INT_MAX)
1320
0
                {
1321
0
                    CPLDebug("GTiff",
1322
0
                             "Skipping directory with too large image: %u x %u",
1323
0
                             nXSize, nYSize);
1324
0
                }
1325
0
                else
1326
0
                {
1327
0
                    uint16_t nSPP = 0;
1328
0
                    if (!TIFFGetField(m_hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSPP))
1329
0
                        nSPP = 1;
1330
1331
0
                    CPLString osName, osDesc;
1332
0
                    osName.Printf("SUBDATASET_%d_NAME=GTIFF_DIR:%d:%s",
1333
0
                                  iDirIndex, iDirIndex, m_pszFilename);
1334
0
                    osDesc.Printf(
1335
0
                        "SUBDATASET_%d_DESC=Page %d (%dP x %dL x %dB)",
1336
0
                        iDirIndex, iDirIndex, static_cast<int>(nXSize),
1337
0
                        static_cast<int>(nYSize), nSPP);
1338
0
                    osDesc += osFriendlyName;
1339
1340
0
                    aosSubdatasets.AddString(osName);
1341
0
                    aosSubdatasets.AddString(osDesc);
1342
0
                }
1343
0
            }
1344
0
        }
1345
0
        CPLFree(subIFDOffsets);
1346
1347
        // Make sure we are stepping from the expected directory regardless
1348
        // of churn done processing the above.
1349
0
        if (TIFFCurrentDirOffset(m_hTIFF) != nTopDir)
1350
0
            TIFFSetSubDirectory(m_hTIFF, nTopDir);
1351
0
    } while (!m_bSingleIFDOpened && !TIFFLastDirectory(m_hTIFF) &&
1352
0
             TIFFReadDirectory(m_hTIFF) != 0);
1353
1354
0
    ReloadDirectory();
1355
1356
    // If we have a mask for the main image, loop over the overviews, and if
1357
    // they have a mask, let's set this mask as an overview of the main mask.
1358
0
    if (m_poMaskDS != nullptr)
1359
0
    {
1360
0
        for (int i = 0; i < m_nOverviewCount; ++i)
1361
0
        {
1362
0
            if (cpl::down_cast<GTiffDataset *>(
1363
0
                    GDALDataset::FromHandle(m_papoOverviewDS[i]))
1364
0
                    ->m_poMaskDS != nullptr)
1365
0
            {
1366
0
                ++m_poMaskDS->m_nOverviewCount;
1367
0
                m_poMaskDS->m_papoOverviewDS =
1368
0
                    static_cast<GTiffDataset **>(CPLRealloc(
1369
0
                        m_poMaskDS->m_papoOverviewDS,
1370
0
                        m_poMaskDS->m_nOverviewCount * (sizeof(void *))));
1371
0
                m_poMaskDS->m_papoOverviewDS[m_poMaskDS->m_nOverviewCount - 1] =
1372
0
                    cpl::down_cast<GTiffDataset *>(
1373
0
                        GDALDataset::FromHandle(m_papoOverviewDS[i]))
1374
0
                        ->m_poMaskDS;
1375
0
            }
1376
0
        }
1377
0
    }
1378
1379
    // Assign color interpretation from main dataset
1380
0
    const int l_nBands = GetRasterCount();
1381
0
    for (int iOvr = 0; iOvr < m_nOverviewCount; ++iOvr)
1382
0
    {
1383
0
        for (int i = 1; i <= l_nBands; i++)
1384
0
        {
1385
0
            auto poBand = dynamic_cast<GTiffRasterBand *>(
1386
0
                m_papoOverviewDS[iOvr]->GetRasterBand(i));
1387
0
            if (poBand)
1388
0
                poBand->m_eBandInterp =
1389
0
                    GetRasterBand(i)->GetColorInterpretation();
1390
0
        }
1391
0
    }
1392
1393
    /* -------------------------------------------------------------------- */
1394
    /*      Only keep track of subdatasets if we have more than one         */
1395
    /*      subdataset (pair).                                              */
1396
    /* -------------------------------------------------------------------- */
1397
0
    if (aosSubdatasets.size() > 2)
1398
0
    {
1399
0
        m_oGTiffMDMD.SetMetadata(aosSubdatasets.List(), "SUBDATASETS");
1400
0
    }
1401
0
}
1402
1403
/************************************************************************/
1404
/*                         GetInternalHandle()                          */
1405
/************************************************************************/
1406
1407
void *GTiffDataset::GetInternalHandle(const char *pszHandleName)
1408
1409
0
{
1410
0
    if (pszHandleName && EQUAL(pszHandleName, "TIFF_HANDLE"))
1411
0
        return m_hTIFF;
1412
0
    return nullptr;
1413
0
}
1414
1415
/************************************************************************/
1416
/*                            GetFileList()                             */
1417
/************************************************************************/
1418
1419
char **GTiffDataset::GetFileList()
1420
1421
0
{
1422
0
    LoadGeoreferencingAndPamIfNeeded();
1423
1424
0
    char **papszFileList = GDALPamDataset::GetFileList();
1425
1426
0
    LoadMetadata();
1427
0
    if (nullptr != m_papszMetadataFiles)
1428
0
    {
1429
0
        for (int i = 0; m_papszMetadataFiles[i] != nullptr; ++i)
1430
0
        {
1431
0
            if (CSLFindString(papszFileList, m_papszMetadataFiles[i]) < 0)
1432
0
            {
1433
0
                papszFileList =
1434
0
                    CSLAddString(papszFileList, m_papszMetadataFiles[i]);
1435
0
            }
1436
0
        }
1437
0
    }
1438
1439
0
    if (m_pszGeorefFilename &&
1440
0
        CSLFindString(papszFileList, m_pszGeorefFilename) == -1)
1441
0
    {
1442
0
        papszFileList = CSLAddString(papszFileList, m_pszGeorefFilename);
1443
0
    }
1444
1445
0
    if (m_nXMLGeorefSrcIndex >= 0)
1446
0
        LookForProjection();
1447
1448
0
    if (m_pszXMLFilename &&
1449
0
        CSLFindString(papszFileList, m_pszXMLFilename) == -1)
1450
0
    {
1451
0
        papszFileList = CSLAddString(papszFileList, m_pszXMLFilename);
1452
0
    }
1453
1454
0
    const std::string osVATDBF = std::string(m_pszFilename) + ".vat.dbf";
1455
0
    VSIStatBufL sStat;
1456
0
    if (VSIStatL(osVATDBF.c_str(), &sStat) == 0)
1457
0
    {
1458
0
        papszFileList = CSLAddString(papszFileList, osVATDBF.c_str());
1459
0
    }
1460
1461
0
    return papszFileList;
1462
0
}
1463
1464
/************************************************************************/
1465
/*                        GetRawBinaryLayout()                          */
1466
/************************************************************************/
1467
1468
bool GTiffDataset::GetRawBinaryLayout(GDALDataset::RawBinaryLayout &sLayout)
1469
0
{
1470
0
    if (eAccess == GA_Update)
1471
0
    {
1472
0
        FlushCache(false);
1473
0
        Crystalize();
1474
0
    }
1475
1476
0
    if (m_nCompression != COMPRESSION_NONE)
1477
0
        return false;
1478
0
    if (!CPLIsPowerOfTwo(m_nBitsPerSample) || m_nBitsPerSample < 8)
1479
0
        return false;
1480
0
    const auto eDT = GetRasterBand(1)->GetRasterDataType();
1481
0
    if (GDALDataTypeIsComplex(eDT))
1482
0
        return false;
1483
1484
0
    toff_t *panByteCounts = nullptr;
1485
0
    toff_t *panOffsets = nullptr;
1486
0
    const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(m_hTIFF));
1487
1488
0
    if (!((bIsTiled &&
1489
0
           TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts) &&
1490
0
           TIFFGetField(m_hTIFF, TIFFTAG_TILEOFFSETS, &panOffsets)) ||
1491
0
          (!bIsTiled &&
1492
0
           TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts) &&
1493
0
           TIFFGetField(m_hTIFF, TIFFTAG_STRIPOFFSETS, &panOffsets))))
1494
0
    {
1495
0
        return false;
1496
0
    }
1497
1498
0
    const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
1499
0
    vsi_l_offset nImgOffset = panOffsets[0];
1500
0
    GIntBig nPixelOffset = (m_nPlanarConfig == PLANARCONFIG_CONTIG)
1501
0
                               ? static_cast<GIntBig>(nDTSize) * nBands
1502
0
                               : nDTSize;
1503
0
    GIntBig nLineOffset = nPixelOffset * nRasterXSize;
1504
0
    GIntBig nBandOffset =
1505
0
        (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1) ? nDTSize : 0;
1506
0
    RawBinaryLayout::Interleaving eInterleaving =
1507
0
        (nBands == 1) ? RawBinaryLayout::Interleaving::UNKNOWN
1508
0
        : (m_nPlanarConfig == PLANARCONFIG_CONTIG)
1509
0
            ? RawBinaryLayout::Interleaving::BIP
1510
0
            : RawBinaryLayout::Interleaving::BSQ;
1511
0
    if (bIsTiled)
1512
0
    {
1513
        // Only a single block tiled file with same dimension as the raster
1514
        // might be acceptable
1515
0
        if (m_nBlockXSize != nRasterXSize || m_nBlockYSize != nRasterYSize)
1516
0
            return false;
1517
0
        if (nBands > 1 && m_nPlanarConfig != PLANARCONFIG_CONTIG)
1518
0
        {
1519
0
            nBandOffset = static_cast<GIntBig>(panOffsets[1]) -
1520
0
                          static_cast<GIntBig>(panOffsets[0]);
1521
0
            for (int i = 2; i < nBands; i++)
1522
0
            {
1523
0
                if (static_cast<GIntBig>(panOffsets[i]) -
1524
0
                        static_cast<GIntBig>(panOffsets[i - 1]) !=
1525
0
                    nBandOffset)
1526
0
                    return false;
1527
0
            }
1528
0
        }
1529
0
    }
1530
0
    else
1531
0
    {
1532
0
        const int nStrips = DIV_ROUND_UP(nRasterYSize, m_nRowsPerStrip);
1533
0
        if (nBands == 1 || m_nPlanarConfig == PLANARCONFIG_CONTIG)
1534
0
        {
1535
0
            vsi_l_offset nLastStripEnd = panOffsets[0] + panByteCounts[0];
1536
0
            for (int iStrip = 1; iStrip < nStrips; iStrip++)
1537
0
            {
1538
0
                if (nLastStripEnd != panOffsets[iStrip])
1539
0
                    return false;
1540
0
                nLastStripEnd = panOffsets[iStrip] + panByteCounts[iStrip];
1541
0
            }
1542
0
        }
1543
0
        else
1544
0
        {
1545
            // Note: we could potentially have BIL order with m_nRowsPerStrip ==
1546
            // 1 and if strips are ordered strip_line_1_band_1, ...,
1547
            // strip_line_1_band_N, strip_line2_band1, ... strip_line2_band_N,
1548
            // etc.... but that'd be faily exotic ! So only detect BSQ layout
1549
            // here
1550
0
            nBandOffset = static_cast<GIntBig>(panOffsets[nStrips]) -
1551
0
                          static_cast<GIntBig>(panOffsets[0]);
1552
0
            for (int i = 0; i < nBands; i++)
1553
0
            {
1554
0
                uint32_t iStripOffset = nStrips * i;
1555
0
                vsi_l_offset nLastStripEnd =
1556
0
                    panOffsets[iStripOffset] + panByteCounts[iStripOffset];
1557
0
                for (int iStrip = 1; iStrip < nStrips; iStrip++)
1558
0
                {
1559
0
                    if (nLastStripEnd != panOffsets[iStripOffset + iStrip])
1560
0
                        return false;
1561
0
                    nLastStripEnd = panOffsets[iStripOffset + iStrip] +
1562
0
                                    panByteCounts[iStripOffset + iStrip];
1563
0
                }
1564
0
                if (i >= 2 && static_cast<GIntBig>(panOffsets[iStripOffset]) -
1565
0
                                      static_cast<GIntBig>(
1566
0
                                          panOffsets[iStripOffset - nStrips]) !=
1567
0
                                  nBandOffset)
1568
0
                {
1569
0
                    return false;
1570
0
                }
1571
0
            }
1572
0
        }
1573
0
    }
1574
1575
0
    sLayout.osRawFilename = m_pszFilename;
1576
0
    sLayout.eInterleaving = eInterleaving;
1577
0
    sLayout.eDataType = eDT;
1578
0
#ifdef CPL_LSB
1579
0
    sLayout.bLittleEndianOrder = !TIFFIsByteSwapped(m_hTIFF);
1580
#else
1581
    sLayout.bLittleEndianOrder = TIFFIsByteSwapped(m_hTIFF);
1582
#endif
1583
0
    sLayout.nImageOffset = nImgOffset;
1584
0
    sLayout.nPixelOffset = nPixelOffset;
1585
0
    sLayout.nLineOffset = nLineOffset;
1586
0
    sLayout.nBandOffset = nBandOffset;
1587
1588
0
    return true;
1589
0
}
1590
1591
/************************************************************************/
1592
/*               GTiffDatasetLibGeotiffErrorCallback()                  */
1593
/************************************************************************/
1594
1595
static void GTiffDatasetLibGeotiffErrorCallback(GTIF *, int level,
1596
                                                const char *pszMsg, ...)
1597
0
{
1598
0
    va_list ap;
1599
0
    va_start(ap, pszMsg);
1600
0
    CPLErrorV((level == LIBGEOTIFF_WARNING) ? CE_Warning : CE_Failure,
1601
0
              CPLE_AppDefined, pszMsg, ap);
1602
0
    va_end(ap);
1603
0
}
1604
1605
/************************************************************************/
1606
/*                           GTIFNew()                                  */
1607
/************************************************************************/
1608
1609
/* static */ GTIF *GTiffDataset::GTIFNew(TIFF *hTIFF)
1610
0
{
1611
0
    GTIF *gtif = GTIFNewEx(hTIFF, GTiffDatasetLibGeotiffErrorCallback, nullptr);
1612
0
    if (gtif)
1613
0
    {
1614
0
        GTIFAttachPROJContext(gtif, OSRGetProjTLSContext());
1615
0
    }
1616
0
    return gtif;
1617
0
}