Coverage Report

Created: 2025-12-31 06:48

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