Coverage Report

Created: 2025-06-13 06:29

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