Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/gtiff/gtiffdataset_read.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GeoTIFF Driver
4
 * Purpose:  Read/get operations on GTiffDataset
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
#include "gtiffrgbaband.h"
18
#include "gtiffbitmapband.h"
19
#include "gtiffsplitband.h"
20
#include "gtiffsplitbitmapband.h"
21
22
#include <algorithm>
23
#include <cassert>
24
#include <limits>
25
#include <memory>
26
#include <mutex>
27
#include <set>
28
#include <string>
29
#include <queue>
30
#include <tuple>
31
#include <utility>
32
33
#include "cpl_error.h"
34
#include "cpl_error_internal.h"  // CPLErrorHandlerAccumulatorStruct
35
#include "cpl_vsi.h"
36
#include "cpl_vsi_virtual.h"
37
#include "cpl_worker_thread_pool.h"
38
#include "fetchbufferdirectio.h"
39
#include "gdal_mdreader.h"  // MD_DOMAIN_RPC
40
#include "gdal_priv.h"
41
#include "geovalues.h"        // RasterPixelIsPoint
42
#include "gt_wkt_srs_priv.h"  // GDALGTIFKeyGetSHORT()
43
#include "tif_jxl.h"
44
#include "tifvsi.h"
45
#include "xtiffio.h"
46
47
#include "tiff_common.h"
48
49
/************************************************************************/
50
/*                        GetJPEGOverviewCount()                        */
51
/************************************************************************/
52
53
int GTiffDataset::GetJPEGOverviewCount()
54
0
{
55
0
    if (!m_apoJPEGOverviewDS.empty())
56
0
        return static_cast<int>(m_apoJPEGOverviewDS.size());
57
58
0
    if (m_poBaseDS || eAccess != GA_ReadOnly ||
59
0
        m_nCompression != COMPRESSION_JPEG ||
60
0
        (nRasterXSize < 256 && nRasterYSize < 256) ||
61
0
        !CPLTestBool(CPLGetConfigOption("GTIFF_IMPLICIT_JPEG_OVR", "YES")) ||
62
0
        GDALGetDriverByName("JPEG") == nullptr)
63
0
    {
64
0
        return 0;
65
0
    }
66
0
    const char *pszSourceColorSpace =
67
0
        m_oGTiffMDMD.GetMetadataItem("SOURCE_COLOR_SPACE", "IMAGE_STRUCTURE");
68
0
    if (pszSourceColorSpace != nullptr && EQUAL(pszSourceColorSpace, "CMYK"))
69
0
    {
70
        // We cannot handle implicit overviews on JPEG CMYK datasets converted
71
        // to RGBA This would imply doing the conversion in
72
        // GTiffJPEGOverviewBand.
73
0
        return 0;
74
0
    }
75
76
    // libjpeg-6b only supports 2, 4 and 8 scale denominators.
77
    // TODO: Later versions support more.
78
0
    int nJPEGOverviewCount = 0;
79
0
    for (signed char i = 2; i >= 0; i--)
80
0
    {
81
0
        if (nRasterXSize >= (256 << i) || nRasterYSize >= (256 << i))
82
0
        {
83
0
            nJPEGOverviewCount = i + 1;
84
0
            break;
85
0
        }
86
0
    }
87
0
    if (nJPEGOverviewCount == 0)
88
0
        return 0;
89
90
    // Get JPEG tables.
91
0
    uint32_t nJPEGTableSize = 0;
92
0
    void *pJPEGTable = nullptr;
93
0
    GByte abyFFD8[] = {0xFF, 0xD8};
94
0
    if (TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize, &pJPEGTable))
95
0
    {
96
0
        if (pJPEGTable == nullptr || nJPEGTableSize < 2 ||
97
0
            nJPEGTableSize > INT_MAX ||
98
0
            static_cast<GByte *>(pJPEGTable)[nJPEGTableSize - 1] != 0xD9)
99
0
        {
100
0
            return 0;
101
0
        }
102
0
        nJPEGTableSize--;  // Remove final 0xD9.
103
0
    }
104
0
    else
105
0
    {
106
0
        pJPEGTable = abyFFD8;
107
0
        nJPEGTableSize = 2;
108
0
    }
109
110
0
    for (int i = 0; i < nJPEGOverviewCount; ++i)
111
0
    {
112
0
        m_apoJPEGOverviewDS.emplace_back(std::make_unique<GTiffJPEGOverviewDS>(
113
0
            this, i + 1, pJPEGTable, static_cast<int>(nJPEGTableSize)));
114
0
    }
115
116
0
    return nJPEGOverviewCount;
117
0
}
118
119
/************************************************************************/
120
/*                       GetCompressionFormats()                        */
121
/************************************************************************/
122
123
CPLStringList GTiffDataset::GetCompressionFormats(int nXOff, int nYOff,
124
                                                  int nXSize, int nYSize,
125
                                                  int nBandCount,
126
                                                  const int *panBandList)
127
0
{
128
0
    if (m_nCompression != COMPRESSION_NONE &&
129
0
        IsWholeBlock(nXOff, nYOff, nXSize, nYSize) &&
130
0
        ((nBandCount == 1 && (panBandList || nBands == 1) &&
131
0
          m_nPlanarConfig == PLANARCONFIG_SEPARATE) ||
132
0
         (IsAllBands(nBandCount, panBandList) &&
133
0
          m_nPlanarConfig == PLANARCONFIG_CONTIG)))
134
0
    {
135
0
        CPLStringList aosList;
136
0
        int nBlockId =
137
0
            (nXOff / m_nBlockXSize) + (nYOff / m_nBlockYSize) * m_nBlocksPerRow;
138
0
        if (m_nPlanarConfig == PLANARCONFIG_SEPARATE && panBandList != nullptr)
139
0
            nBlockId += panBandList[0] * m_nBlocksPerBand;
140
141
0
        vsi_l_offset nOffset = 0;
142
0
        vsi_l_offset nSize = 0;
143
0
        if (IsBlockAvailable(nBlockId, &nOffset, &nSize, nullptr) &&
144
0
            nSize <
145
0
                static_cast<vsi_l_offset>(std::numeric_limits<tmsize_t>::max()))
146
0
        {
147
0
            switch (m_nCompression)
148
0
            {
149
0
                case COMPRESSION_JPEG:
150
0
                {
151
0
                    if (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands == 4 &&
152
0
                        m_nPhotometric == PHOTOMETRIC_RGB &&
153
0
                        GetRasterBand(4)->GetColorInterpretation() ==
154
0
                            GCI_AlphaBand)
155
0
                    {
156
                        // as a hint for the JPEG and JPEGXL drivers to not use it!
157
0
                        aosList.AddString("JPEG;colorspace=RGBA");
158
0
                    }
159
0
                    else
160
0
                    {
161
0
                        aosList.AddString("JPEG");
162
0
                    }
163
0
                    break;
164
0
                }
165
166
0
                case COMPRESSION_WEBP:
167
0
                    aosList.AddString("WEBP");
168
0
                    break;
169
170
0
                case COMPRESSION_JXL:
171
0
                    aosList.AddString("JXL");
172
0
                    break;
173
174
0
                default:
175
0
                    break;
176
0
            }
177
0
        }
178
0
        return aosList;
179
0
    }
180
0
    return CPLStringList();
181
0
}
182
183
/************************************************************************/
184
/*                         ReadCompressedData()                         */
185
/************************************************************************/
186
187
CPLErr GTiffDataset::ReadCompressedData(const char *pszFormat, int nXOff,
188
                                        int nYOff, int nXSize, int nYSize,
189
                                        int nBandCount, const int *panBandList,
190
                                        void **ppBuffer, size_t *pnBufferSize,
191
                                        char **ppszDetailedFormat)
192
0
{
193
0
    if (m_nCompression != COMPRESSION_NONE &&
194
0
        IsWholeBlock(nXOff, nYOff, nXSize, nYSize) &&
195
0
        ((nBandCount == 1 && (panBandList != nullptr || nBands == 1) &&
196
0
          m_nPlanarConfig == PLANARCONFIG_SEPARATE) ||
197
0
         (IsAllBands(nBandCount, panBandList) &&
198
0
          m_nPlanarConfig == PLANARCONFIG_CONTIG)))
199
0
    {
200
0
        const CPLStringList aosTokens(CSLTokenizeString2(pszFormat, ";", 0));
201
0
        if (aosTokens.size() != 1)
202
0
            return CE_Failure;
203
204
        // We don't want to handle CMYK JPEG for now
205
0
        if ((m_nCompression == COMPRESSION_JPEG &&
206
0
             EQUAL(aosTokens[0], "JPEG") &&
207
0
             (m_nPlanarConfig == PLANARCONFIG_SEPARATE ||
208
0
              m_nPhotometric != PHOTOMETRIC_SEPARATED)) ||
209
0
            (m_nCompression == COMPRESSION_WEBP &&
210
0
             EQUAL(aosTokens[0], "WEBP")) ||
211
0
            ((m_nCompression == COMPRESSION_JXL ||
212
0
              m_nCompression == COMPRESSION_JXL_DNG_1_7) &&
213
0
             EQUAL(aosTokens[0], "JXL")))
214
0
        {
215
0
            std::string osDetailedFormat = aosTokens[0];
216
217
0
            int nBlockId = (nXOff / m_nBlockXSize) +
218
0
                           (nYOff / m_nBlockYSize) * m_nBlocksPerRow;
219
0
            if (m_nPlanarConfig == PLANARCONFIG_SEPARATE &&
220
0
                panBandList != nullptr)
221
0
                nBlockId += panBandList[0] * m_nBlocksPerBand;
222
223
0
            vsi_l_offset nOffset = 0;
224
0
            vsi_l_offset nSize = 0;
225
0
            if (IsBlockAvailable(nBlockId, &nOffset, &nSize, nullptr) &&
226
0
                nSize < static_cast<vsi_l_offset>(
227
0
                            std::numeric_limits<tmsize_t>::max()))
228
0
            {
229
0
                uint32_t nJPEGTableSize = 0;
230
0
                void *pJPEGTable = nullptr;
231
0
                if (m_nCompression == COMPRESSION_JPEG)
232
0
                {
233
0
                    if (TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES,
234
0
                                     &nJPEGTableSize, &pJPEGTable) &&
235
0
                        pJPEGTable != nullptr && nJPEGTableSize > 4 &&
236
0
                        static_cast<GByte *>(pJPEGTable)[0] == 0xFF &&
237
0
                        static_cast<GByte *>(pJPEGTable)[1] == 0xD8 &&
238
0
                        static_cast<GByte *>(pJPEGTable)[nJPEGTableSize - 2] ==
239
0
                            0xFF &&
240
0
                        static_cast<GByte *>(pJPEGTable)[nJPEGTableSize - 1] ==
241
0
                            0xD9)
242
0
                    {
243
0
                        pJPEGTable = static_cast<GByte *>(pJPEGTable) + 2;
244
0
                        nJPEGTableSize -= 4;
245
0
                    }
246
0
                    else
247
0
                    {
248
0
                        nJPEGTableSize = 0;
249
0
                    }
250
0
                }
251
252
0
                size_t nSizeSize = static_cast<size_t>(nSize + nJPEGTableSize);
253
0
                if (ppBuffer)
254
0
                {
255
0
                    if (!pnBufferSize)
256
0
                        return CE_Failure;
257
0
                    bool bFreeOnError = false;
258
0
                    if (*ppBuffer)
259
0
                    {
260
0
                        if (*pnBufferSize < nSizeSize)
261
0
                            return CE_Failure;
262
0
                    }
263
0
                    else
264
0
                    {
265
0
                        *ppBuffer = VSI_MALLOC_VERBOSE(nSizeSize);
266
0
                        if (*ppBuffer == nullptr)
267
0
                            return CE_Failure;
268
0
                        bFreeOnError = true;
269
0
                    }
270
0
                    const auto nTileSize = static_cast<tmsize_t>(nSize);
271
0
                    bool bOK;
272
0
                    if (TIFFIsTiled(m_hTIFF))
273
0
                    {
274
0
                        bOK = TIFFReadRawTile(m_hTIFF, nBlockId, *ppBuffer,
275
0
                                              nTileSize) == nTileSize;
276
0
                    }
277
0
                    else
278
0
                    {
279
0
                        bOK = TIFFReadRawStrip(m_hTIFF, nBlockId, *ppBuffer,
280
0
                                               nTileSize) == nTileSize;
281
0
                    }
282
0
                    if (!bOK)
283
0
                    {
284
0
                        if (bFreeOnError)
285
0
                        {
286
0
                            VSIFree(*ppBuffer);
287
0
                            *ppBuffer = nullptr;
288
0
                        }
289
0
                        return CE_Failure;
290
0
                    }
291
0
                    if (nJPEGTableSize > 0)
292
0
                    {
293
0
                        GByte *pabyBuffer = static_cast<GByte *>(*ppBuffer);
294
0
                        memmove(pabyBuffer + 2 + nJPEGTableSize, pabyBuffer + 2,
295
0
                                static_cast<size_t>(nSize) - 2);
296
0
                        memcpy(pabyBuffer + 2, pJPEGTable, nJPEGTableSize);
297
0
                    }
298
299
0
                    if (m_nCompression == COMPRESSION_JPEG)
300
0
                    {
301
0
                        osDetailedFormat = GDALGetCompressionFormatForJPEG(
302
0
                            *ppBuffer, nSizeSize);
303
0
                        const CPLStringList aosTokens2(CSLTokenizeString2(
304
0
                            osDetailedFormat.c_str(), ";", 0));
305
0
                        if (m_nPlanarConfig == PLANARCONFIG_CONTIG &&
306
0
                            nBands == 4 && m_nPhotometric == PHOTOMETRIC_RGB &&
307
0
                            GetRasterBand(4)->GetColorInterpretation() ==
308
0
                                GCI_AlphaBand)
309
0
                        {
310
0
                            osDetailedFormat = aosTokens2[0];
311
0
                            for (int i = 1; i < aosTokens2.size(); ++i)
312
0
                            {
313
0
                                if (!STARTS_WITH_CI(aosTokens2[i],
314
0
                                                    "colorspace="))
315
0
                                {
316
0
                                    osDetailedFormat += ';';
317
0
                                    osDetailedFormat += aosTokens2[i];
318
0
                                }
319
0
                            }
320
0
                            osDetailedFormat += ";colorspace=RGBA";
321
0
                        }
322
0
                    }
323
0
                }
324
0
                if (ppszDetailedFormat)
325
0
                    *ppszDetailedFormat = VSIStrdup(osDetailedFormat.c_str());
326
0
                if (pnBufferSize)
327
0
                    *pnBufferSize = nSizeSize;
328
0
                return CE_None;
329
0
            }
330
0
        }
331
0
    }
332
0
    return CE_Failure;
333
0
}
334
335
struct GTiffDecompressContext
336
{
337
    // The mutex must be recursive because ThreadDecompressionFuncErrorHandler()
338
    // which acquires the mutex can be called from a section where the mutex is
339
    // already acquired.
340
    std::recursive_mutex oMutex{};
341
    bool bSuccess = true;
342
    CPLErrorAccumulator oErrorAccumulator{};
343
344
    VSIVirtualHandle *poHandle = nullptr;
345
    GTiffDataset *poDS = nullptr;
346
    GDALDataType eDT = GDT_Unknown;
347
    int nXOff = 0;
348
    int nYOff = 0;
349
    int nXSize = 0;
350
    int nYSize = 0;
351
    int nBlockXStart = 0;
352
    int nBlockYStart = 0;
353
    int nBlockXEnd = 0;
354
    int nBlockYEnd = 0;
355
    GByte *pabyData = nullptr;
356
    GDALDataType eBufType = GDT_Unknown;
357
    int nBufDTSize = 0;
358
    int nBandCount = 0;
359
    const int *panBandMap = nullptr;
360
    GSpacing nPixelSpace = 0;
361
    GSpacing nLineSpace = 0;
362
    GSpacing nBandSpace = 0;
363
    bool bHasPRead = false;
364
    bool bCacheAllBands = false;
365
    bool bSkipBlockCache = false;
366
    bool bUseBIPOptim = false;
367
    bool bUseDeinterleaveOptimNoBlockCache = false;
368
    bool bUseDeinterleaveOptimBlockCache = false;
369
    bool bIsTiled = false;
370
    bool bTIFFIsBigEndian = false;
371
    int nBlocksPerRow = 0;
372
373
    uint16_t nPredictor = 0;
374
375
    uint32_t nJPEGTableSize = 0;
376
    void *pJPEGTable = nullptr;
377
    uint16_t nYCrbCrSubSampling0 = 2;
378
    uint16_t nYCrbCrSubSampling1 = 2;
379
380
    uint16_t *pExtraSamples = nullptr;
381
    uint16_t nExtraSampleCount = 0;
382
};
383
384
struct GTiffDecompressJob
385
{
386
    GTiffDecompressContext *psContext = nullptr;
387
    int iSrcBandIdxSeparate =
388
        0;  // in [0, GetRasterCount()-1] in PLANARCONFIG_SEPARATE, or -1 in PLANARCONFIG_CONTIG
389
    int iDstBandIdxSeparate =
390
        0;  // in [0, nBandCount-1] in PLANARCONFIG_SEPARATE, or -1 in PLANARCONFIG_CONTIG
391
    int nXBlock = 0;
392
    int nYBlock = 0;
393
    vsi_l_offset nOffset = 0;
394
    vsi_l_offset nSize = 0;
395
};
396
397
/************************************************************************/
398
/*                      ThreadDecompressionFunc()                       */
399
/************************************************************************/
400
401
/* static */ void GTiffDataset::ThreadDecompressionFunc(void *pData)
402
0
{
403
0
    const auto psJob = static_cast<const GTiffDecompressJob *>(pData);
404
0
    auto psContext = psJob->psContext;
405
0
    auto poDS = psContext->poDS;
406
407
0
    auto oAccumulator = psContext->oErrorAccumulator.InstallForCurrentScope();
408
409
0
    const int nBandsPerStrile =
410
0
        poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG ? poDS->nBands : 1;
411
0
    const int nBandsToWrite = poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG
412
0
                                  ? psContext->nBandCount
413
0
                                  : 1;
414
415
0
    const int nXOffsetInBlock = psJob->nXBlock == psContext->nBlockXStart
416
0
                                    ? psContext->nXOff % poDS->m_nBlockXSize
417
0
                                    : 0;
418
0
    const int nXOffsetInData =
419
0
        psJob->nXBlock == psContext->nBlockXStart
420
0
            ? 0
421
0
            : (psJob->nXBlock - psContext->nBlockXStart) * poDS->m_nBlockXSize -
422
0
                  (psContext->nXOff % poDS->m_nBlockXSize);
423
0
    const int nXSize =
424
0
        psJob->nXBlock == psContext->nBlockXStart
425
0
            ? (psJob->nXBlock == psContext->nBlockXEnd
426
0
                   ? psContext->nXSize
427
0
                   : poDS->m_nBlockXSize -
428
0
                         (psContext->nXOff % poDS->m_nBlockXSize))
429
0
        : psJob->nXBlock == psContext->nBlockXEnd
430
0
            ? (((psContext->nXOff + psContext->nXSize) % poDS->m_nBlockXSize) ==
431
0
                       0
432
0
                   ? poDS->m_nBlockXSize
433
0
                   : ((psContext->nXOff + psContext->nXSize) %
434
0
                      poDS->m_nBlockXSize))
435
0
            : poDS->m_nBlockXSize;
436
437
0
    const int nYOffsetInBlock = psJob->nYBlock == psContext->nBlockYStart
438
0
                                    ? psContext->nYOff % poDS->m_nBlockYSize
439
0
                                    : 0;
440
0
    const int nYOffsetInData =
441
0
        psJob->nYBlock == psContext->nBlockYStart
442
0
            ? 0
443
0
            : (psJob->nYBlock - psContext->nBlockYStart) * poDS->m_nBlockYSize -
444
0
                  (psContext->nYOff % poDS->m_nBlockYSize);
445
0
    const int nYSize =
446
0
        psJob->nYBlock == psContext->nBlockYStart
447
0
            ? (psJob->nYBlock == psContext->nBlockYEnd
448
0
                   ? psContext->nYSize
449
0
                   : poDS->m_nBlockYSize -
450
0
                         (psContext->nYOff % poDS->m_nBlockYSize))
451
0
        : psJob->nYBlock == psContext->nBlockYEnd
452
0
            ? (((psContext->nYOff + psContext->nYSize) % poDS->m_nBlockYSize) ==
453
0
                       0
454
0
                   ? poDS->m_nBlockYSize
455
0
                   : ((psContext->nYOff + psContext->nYSize) %
456
0
                      poDS->m_nBlockYSize))
457
0
            : poDS->m_nBlockYSize;
458
#if 0
459
    CPLDebug("GTiff",
460
             "nXBlock = %d, nYBlock = %d, "
461
             "nXOffsetInBlock = %d, nXOffsetInData = %d, nXSize = %d, "
462
             "nYOffsetInBlock = %d, nYOffsetInData = %d, nYSize = %d\n",
463
             psJob->nXBlock, psJob->nYBlock,
464
             nXOffsetInBlock, nXOffsetInData, nXSize,
465
             nYOffsetInBlock, nYOffsetInData, nYSize);
466
#endif
467
468
0
    if (psJob->nSize == 0)
469
0
    {
470
0
        {
471
0
            std::lock_guard<std::recursive_mutex> oLock(psContext->oMutex);
472
0
            if (!psContext->bSuccess)
473
0
                return;
474
0
        }
475
0
        const double dfNoDataValue =
476
0
            poDS->m_bNoDataSet ? poDS->m_dfNoDataValue : 0;
477
0
        for (int y = 0; y < nYSize; ++y)
478
0
        {
479
0
            for (int i = 0; i < nBandsToWrite; ++i)
480
0
            {
481
0
                const int iDstBandIdx =
482
0
                    poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG
483
0
                        ? i
484
0
                        : psJob->iDstBandIdxSeparate;
485
0
                GDALCopyWords64(
486
0
                    &dfNoDataValue, GDT_Float64, 0,
487
0
                    psContext->pabyData + iDstBandIdx * psContext->nBandSpace +
488
0
                        (y + nYOffsetInData) * psContext->nLineSpace +
489
0
                        nXOffsetInData * psContext->nPixelSpace,
490
0
                    psContext->eBufType,
491
0
                    static_cast<int>(psContext->nPixelSpace), nXSize);
492
0
            }
493
0
        }
494
0
        return;
495
0
    }
496
497
0
    const int nBandsToCache =
498
0
        psContext->bCacheAllBands ? poDS->nBands : nBandsToWrite;
499
0
    std::vector<GDALRasterBlock *> apoBlocks(nBandsToCache);
500
0
    std::vector<bool> abAlreadyLoadedBlocks(nBandsToCache);
501
0
    int nAlreadyLoadedBlocks = 0;
502
0
    std::vector<GByte> abyInput;
503
504
0
    struct FreeBlocks
505
0
    {
506
0
        std::vector<GDALRasterBlock *> &m_apoBlocks;
507
508
0
        explicit FreeBlocks(std::vector<GDALRasterBlock *> &apoBlocksIn)
509
0
            : m_apoBlocks(apoBlocksIn)
510
0
        {
511
0
        }
512
513
0
        ~FreeBlocks()
514
0
        {
515
0
            for (auto *poBlock : m_apoBlocks)
516
0
            {
517
0
                if (poBlock)
518
0
                    poBlock->DropLock();
519
0
            }
520
0
        }
521
0
    };
522
523
0
    FreeBlocks oFreeBlocks(apoBlocks);
524
525
0
    const auto LoadBlocks = [&]()
526
0
    {
527
0
        for (int i = 0; i < nBandsToCache; ++i)
528
0
        {
529
0
            const int iBand = psContext->bCacheAllBands ? i + 1
530
0
                              : poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG
531
0
                                  ? psContext->panBandMap[i]
532
0
                                  : psJob->iSrcBandIdxSeparate + 1;
533
0
            apoBlocks[i] = poDS->GetRasterBand(iBand)->TryGetLockedBlockRef(
534
0
                psJob->nXBlock, psJob->nYBlock);
535
0
            if (apoBlocks[i] == nullptr)
536
0
            {
537
                // Temporary disabling of dirty block flushing, otherwise
538
                // we can be in a deadlock situation, where the
539
                // GTiffDataset::SubmitCompressionJob() method waits for jobs
540
                // to be finished, that can't finish (actually be started)
541
                // because this task and its siblings are taking all the
542
                // available workers allowed by the global thread pool.
543
0
                GDALRasterBlock::EnterDisableDirtyBlockFlush();
544
0
                apoBlocks[i] = poDS->GetRasterBand(iBand)->GetLockedBlockRef(
545
0
                    psJob->nXBlock, psJob->nYBlock, TRUE);
546
0
                GDALRasterBlock::LeaveDisableDirtyBlockFlush();
547
0
                if (apoBlocks[i] == nullptr)
548
0
                    return false;
549
0
            }
550
0
            else
551
0
            {
552
0
                abAlreadyLoadedBlocks[i] = true;
553
0
                nAlreadyLoadedBlocks++;
554
0
            }
555
0
        }
556
0
        return true;
557
0
    };
558
559
0
    const auto AllocInputBuffer = [&]()
560
0
    {
561
0
        bool bError = false;
562
#if SIZEOF_VOIDP == 4
563
        if (psJob->nSize != static_cast<size_t>(psJob->nSize))
564
        {
565
            bError = true;
566
        }
567
        else
568
#endif
569
0
        {
570
0
            try
571
0
            {
572
0
                abyInput.resize(static_cast<size_t>(psJob->nSize));
573
0
            }
574
0
            catch (const std::exception &)
575
0
            {
576
0
                bError = true;
577
0
            }
578
0
        }
579
0
        if (bError)
580
0
        {
581
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
582
0
                     "Cannot allocate working buffer of size " CPL_FRMT_GUIB,
583
0
                     static_cast<GUIntBig>(psJob->nSize));
584
0
            return false;
585
0
        }
586
0
        return true;
587
0
    };
588
589
0
    if (psContext->bHasPRead)
590
0
    {
591
0
        {
592
0
            std::lock_guard<std::recursive_mutex> oLock(psContext->oMutex);
593
0
            if (!psContext->bSuccess)
594
0
                return;
595
596
            // Coverity Scan notices that GDALRasterBlock::Internalize() calls
597
            // CPLSleep() in a debug code path, and warns about that while
598
            // holding the above mutex.
599
            // coverity[sleep]
600
0
            if (!psContext->bSkipBlockCache && !LoadBlocks())
601
0
            {
602
0
                psContext->bSuccess = false;
603
0
                return;
604
0
            }
605
0
        }
606
0
        if (nAlreadyLoadedBlocks != nBandsToCache)
607
0
        {
608
0
            if (!AllocInputBuffer())
609
0
            {
610
0
                std::lock_guard<std::recursive_mutex> oLock(psContext->oMutex);
611
0
                psContext->bSuccess = false;
612
0
                return;
613
0
            }
614
0
            if (psContext->poHandle->PRead(abyInput.data(), abyInput.size(),
615
0
                                           psJob->nOffset) != abyInput.size())
616
0
            {
617
0
                CPLError(CE_Failure, CPLE_AppDefined,
618
0
                         "Cannot read " CPL_FRMT_GUIB
619
0
                         " bytes at offset " CPL_FRMT_GUIB,
620
0
                         static_cast<GUIntBig>(psJob->nSize),
621
0
                         static_cast<GUIntBig>(psJob->nOffset));
622
623
0
                std::lock_guard<std::recursive_mutex> oLock(psContext->oMutex);
624
0
                psContext->bSuccess = false;
625
0
                return;
626
0
            }
627
0
        }
628
0
    }
629
0
    else
630
0
    {
631
0
        std::lock_guard<std::recursive_mutex> oLock(psContext->oMutex);
632
0
        if (!psContext->bSuccess)
633
0
            return;
634
635
        // Coverity Scan notices that GDALRasterBlock::Internalize() calls
636
        // CPLSleep() in a debug code path, and warns about that while
637
        // holding the above mutex.
638
        // coverity[sleep]
639
0
        if (!psContext->bSkipBlockCache && !LoadBlocks())
640
0
        {
641
0
            psContext->bSuccess = false;
642
0
            return;
643
0
        }
644
645
0
        if (nAlreadyLoadedBlocks != nBandsToCache)
646
0
        {
647
0
            if (!AllocInputBuffer())
648
0
            {
649
0
                psContext->bSuccess = false;
650
0
                return;
651
0
            }
652
0
            if (psContext->poHandle->Seek(psJob->nOffset, SEEK_SET) != 0 ||
653
0
                psContext->poHandle->Read(abyInput.data(), abyInput.size(),
654
0
                                          1) != 1)
655
0
            {
656
0
                CPLError(CE_Failure, CPLE_AppDefined,
657
0
                         "Cannot read " CPL_FRMT_GUIB
658
0
                         " bytes at offset " CPL_FRMT_GUIB,
659
0
                         static_cast<GUIntBig>(psJob->nSize),
660
0
                         static_cast<GUIntBig>(psJob->nOffset));
661
0
                psContext->bSuccess = false;
662
0
                return;
663
0
            }
664
0
        }
665
0
    }
666
667
0
    const int nDTSize = GDALGetDataTypeSizeBytes(psContext->eDT);
668
0
    GByte *pDstPtr = psContext->pabyData +
669
0
                     nYOffsetInData * psContext->nLineSpace +
670
0
                     nXOffsetInData * psContext->nPixelSpace;
671
672
0
    if (nAlreadyLoadedBlocks != nBandsToCache)
673
0
    {
674
        // Generate a dummy in-memory TIFF file that has all the needed tags
675
        // from the original file
676
0
        const CPLString osTmpFilename(
677
0
            VSIMemGenerateHiddenFilename("decompress.tif"));
678
0
        VSILFILE *fpTmp = VSIFOpenL(osTmpFilename.c_str(), "wb+");
679
0
        TIFF *hTIFFTmp =
680
0
            VSI_TIFFOpen(osTmpFilename.c_str(),
681
0
                         psContext->bTIFFIsBigEndian ? "wb+" : "wl+", fpTmp);
682
0
        CPLAssert(hTIFFTmp != nullptr);
683
0
        const int nBlockYSize =
684
0
            (psContext->bIsTiled ||
685
0
             psJob->nYBlock < poDS->m_nBlocksPerColumn - 1)
686
0
                ? poDS->m_nBlockYSize
687
0
            : (poDS->nRasterYSize % poDS->m_nBlockYSize) == 0
688
0
                ? poDS->m_nBlockYSize
689
0
                : poDS->nRasterYSize % poDS->m_nBlockYSize;
690
0
        TIFFSetField(hTIFFTmp, TIFFTAG_IMAGEWIDTH, poDS->m_nBlockXSize);
691
0
        TIFFSetField(hTIFFTmp, TIFFTAG_IMAGELENGTH, nBlockYSize);
692
0
        TIFFSetField(hTIFFTmp, TIFFTAG_BITSPERSAMPLE, poDS->m_nBitsPerSample);
693
0
        TIFFSetField(hTIFFTmp, TIFFTAG_COMPRESSION, poDS->m_nCompression);
694
0
        TIFFSetField(hTIFFTmp, TIFFTAG_PHOTOMETRIC, poDS->m_nPhotometric);
695
0
        TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLEFORMAT, poDS->m_nSampleFormat);
696
0
        TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLESPERPIXEL,
697
0
                     poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG
698
0
                         ? poDS->m_nSamplesPerPixel
699
0
                         : 1);
700
0
        TIFFSetField(hTIFFTmp, TIFFTAG_ROWSPERSTRIP, nBlockYSize);
701
0
        TIFFSetField(hTIFFTmp, TIFFTAG_PLANARCONFIG, poDS->m_nPlanarConfig);
702
0
        if (psContext->nPredictor != PREDICTOR_NONE)
703
0
            TIFFSetField(hTIFFTmp, TIFFTAG_PREDICTOR, psContext->nPredictor);
704
0
        if (poDS->m_nCompression == COMPRESSION_LERC)
705
0
        {
706
0
            TIFFSetField(hTIFFTmp, TIFFTAG_LERC_PARAMETERS, 2,
707
0
                         poDS->m_anLercAddCompressionAndVersion);
708
0
        }
709
0
        else if (poDS->m_nCompression == COMPRESSION_JPEG)
710
0
        {
711
0
            if (psContext->pJPEGTable)
712
0
            {
713
0
                TIFFSetField(hTIFFTmp, TIFFTAG_JPEGTABLES,
714
0
                             psContext->nJPEGTableSize, psContext->pJPEGTable);
715
0
            }
716
0
            if (poDS->m_nPhotometric == PHOTOMETRIC_YCBCR)
717
0
            {
718
0
                TIFFSetField(hTIFFTmp, TIFFTAG_YCBCRSUBSAMPLING,
719
0
                             psContext->nYCrbCrSubSampling0,
720
0
                             psContext->nYCrbCrSubSampling1);
721
0
            }
722
0
        }
723
0
        if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG)
724
0
        {
725
0
            if (psContext->pExtraSamples)
726
0
            {
727
0
                TIFFSetField(hTIFFTmp, TIFFTAG_EXTRASAMPLES,
728
0
                             psContext->nExtraSampleCount,
729
0
                             psContext->pExtraSamples);
730
0
            }
731
0
            else
732
0
            {
733
0
                const int nSamplesAccountedFor =
734
0
                    poDS->m_nPhotometric == PHOTOMETRIC_RGB          ? 3
735
0
                    : poDS->m_nPhotometric == PHOTOMETRIC_MINISBLACK ? 1
736
0
                                                                     : 0;
737
0
                if (nSamplesAccountedFor > 0 &&
738
0
                    poDS->m_nSamplesPerPixel > nSamplesAccountedFor)
739
0
                {
740
                    // If the input image is not compliant regarndig ExtraSamples,
741
                    // generate a synthetic one to avoid gazillons of warnings
742
0
                    const auto nExtraSampleCount = static_cast<uint16_t>(
743
0
                        poDS->m_nSamplesPerPixel - nSamplesAccountedFor);
744
0
                    std::vector<uint16_t> anExtraSamples(
745
0
                        nExtraSampleCount, EXTRASAMPLE_UNSPECIFIED);
746
0
                    TIFFSetField(hTIFFTmp, TIFFTAG_EXTRASAMPLES,
747
0
                                 nExtraSampleCount, anExtraSamples.data());
748
0
                }
749
0
            }
750
0
        }
751
0
        TIFFWriteCheck(hTIFFTmp, FALSE, "ThreadDecompressionFunc");
752
0
        TIFFWriteDirectory(hTIFFTmp);
753
0
        XTIFFClose(hTIFFTmp);
754
755
        // Re-open file
756
0
        hTIFFTmp = VSI_TIFFOpen(osTmpFilename.c_str(), "r", fpTmp);
757
0
        CPLAssert(hTIFFTmp != nullptr);
758
0
        poDS->RestoreVolatileParameters(hTIFFTmp);
759
760
0
        bool bRet = true;
761
        // Request m_nBlockYSize line in the block, except on the bottom-most
762
        // tile/strip.
763
0
        const int nBlockReqYSize =
764
0
            (psJob->nYBlock < poDS->m_nBlocksPerColumn - 1)
765
0
                ? poDS->m_nBlockYSize
766
0
            : (poDS->nRasterYSize % poDS->m_nBlockYSize) == 0
767
0
                ? poDS->m_nBlockYSize
768
0
                : poDS->nRasterYSize % poDS->m_nBlockYSize;
769
770
0
        const size_t nReqSize = static_cast<size_t>(poDS->m_nBlockXSize) *
771
0
                                nBlockReqYSize * nBandsPerStrile * nDTSize;
772
773
0
        GByte *pabyOutput;
774
0
        std::vector<GByte> abyOutput;
775
0
        if (poDS->m_nCompression == COMPRESSION_NONE &&
776
0
            !TIFFIsByteSwapped(poDS->m_hTIFF) && abyInput.size() >= nReqSize &&
777
0
            (psContext->bSkipBlockCache || nBandsPerStrile > 1))
778
0
        {
779
0
            pabyOutput = abyInput.data();
780
0
        }
781
0
        else
782
0
        {
783
0
            if (psContext->bSkipBlockCache || nBandsPerStrile > 1)
784
0
            {
785
0
                abyOutput.resize(nReqSize);
786
0
                pabyOutput = abyOutput.data();
787
0
            }
788
0
            else
789
0
            {
790
0
                pabyOutput = static_cast<GByte *>(apoBlocks[0]->GetDataRef());
791
0
            }
792
0
            if (!TIFFReadFromUserBuffer(hTIFFTmp, 0, abyInput.data(),
793
0
                                        abyInput.size(), pabyOutput,
794
0
                                        nReqSize) &&
795
0
                !poDS->m_bIgnoreReadErrors)
796
0
            {
797
0
                bRet = false;
798
0
            }
799
0
        }
800
0
        XTIFFClose(hTIFFTmp);
801
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpTmp));
802
0
        VSIUnlink(osTmpFilename.c_str());
803
804
0
        if (!bRet)
805
0
        {
806
0
            std::lock_guard<std::recursive_mutex> oLock(psContext->oMutex);
807
0
            psContext->bSuccess = false;
808
0
            return;
809
0
        }
810
811
0
        if (!psContext->bSkipBlockCache && nBandsPerStrile > 1)
812
0
        {
813
            // Copy pixel-interleaved all-band buffer to cached blocks
814
815
0
            if (psContext->bUseDeinterleaveOptimBlockCache)
816
0
            {
817
                // Optimization
818
0
                std::vector<void *> ppDestBuffers(poDS->nBands);
819
0
                for (int i = 0; i < poDS->nBands; ++i)
820
0
                {
821
0
                    ppDestBuffers[i] = apoBlocks[i]->GetDataRef();
822
0
                }
823
0
                GDALDeinterleave(pabyOutput, psContext->eDT, poDS->nBands,
824
0
                                 ppDestBuffers.data(), psContext->eDT,
825
0
                                 static_cast<size_t>(nBlockReqYSize) *
826
0
                                     poDS->m_nBlockXSize);
827
0
            }
828
0
            else
829
0
            {
830
                // General case
831
0
                for (int i = 0; i < nBandsToCache; ++i)
832
0
                {
833
0
                    if (!abAlreadyLoadedBlocks[i])
834
0
                    {
835
0
                        const int iBand = psContext->bCacheAllBands
836
0
                                              ? i
837
0
                                              : psContext->panBandMap[i] - 1;
838
0
                        GDALCopyWords64(pabyOutput + iBand * nDTSize,
839
0
                                        psContext->eDT, nDTSize * poDS->nBands,
840
0
                                        apoBlocks[i]->GetDataRef(),
841
0
                                        psContext->eDT, nDTSize,
842
0
                                        static_cast<size_t>(nBlockReqYSize) *
843
0
                                            poDS->m_nBlockXSize);
844
0
                    }
845
0
                }
846
0
            }
847
0
        }
848
849
0
        const GByte *pSrcPtr =
850
0
            pabyOutput +
851
0
            (static_cast<size_t>(nYOffsetInBlock) * poDS->m_nBlockXSize +
852
0
             nXOffsetInBlock) *
853
0
                nDTSize * nBandsPerStrile;
854
0
        const size_t nSrcLineInc = static_cast<size_t>(poDS->m_nBlockXSize) *
855
0
                                   nDTSize * nBandsPerStrile;
856
857
        // Optimization when writing to BIP buffer.
858
0
        if (psContext->bUseBIPOptim)
859
0
        {
860
0
            for (int y = 0; y < nYSize; ++y)
861
0
            {
862
0
                GDALCopyWords64(pSrcPtr, psContext->eDT, nDTSize, pDstPtr,
863
0
                                psContext->eBufType, psContext->nBufDTSize,
864
0
                                static_cast<size_t>(nXSize) * poDS->nBands);
865
0
                pSrcPtr += nSrcLineInc;
866
0
                pDstPtr += psContext->nLineSpace;
867
0
            }
868
0
            return;
869
0
        }
870
871
0
        if (psContext->bSkipBlockCache)
872
0
        {
873
            // Copy from pixel-interleaved all-band buffer (or temporary buffer
874
            // for single-band/separate case) into final buffer
875
0
            if (psContext->bUseDeinterleaveOptimNoBlockCache)
876
0
            {
877
                // Optimization
878
0
                std::vector<void *> ppDestBuffers(psContext->nBandCount);
879
0
                for (int i = 0; i < psContext->nBandCount; ++i)
880
0
                {
881
0
                    ppDestBuffers[i] =
882
0
                        pDstPtr +
883
0
                        (psContext->panBandMap[i] - 1) * psContext->nBandSpace;
884
0
                }
885
0
                for (int y = 0; y < nYSize; ++y)
886
0
                {
887
0
                    GDALDeinterleave(
888
0
                        pSrcPtr, psContext->eDT, psContext->nBandCount,
889
0
                        ppDestBuffers.data(), psContext->eDT, nXSize);
890
0
                    pSrcPtr += nSrcLineInc;
891
0
                    for (int i = 0; i < psContext->nBandCount; ++i)
892
0
                    {
893
0
                        ppDestBuffers[i] =
894
0
                            static_cast<GByte *>(ppDestBuffers[i]) +
895
0
                            psContext->nLineSpace;
896
0
                    }
897
0
                }
898
0
                return;
899
0
            }
900
901
            // General case
902
0
            for (int y = 0; y < nYSize; ++y)
903
0
            {
904
0
                for (int i = 0; i < nBandsToWrite; ++i)
905
0
                {
906
0
                    const int iSrcBandIdx =
907
0
                        poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG
908
0
                            ? psContext->panBandMap[i] - 1
909
0
                            : 0;
910
0
                    const int iDstBandIdx =
911
0
                        poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG
912
0
                            ? i
913
0
                            : psJob->iDstBandIdxSeparate;
914
0
                    GDALCopyWords64(
915
0
                        pSrcPtr + iSrcBandIdx * nDTSize + y * nSrcLineInc,
916
0
                        psContext->eDT, nDTSize * nBandsPerStrile,
917
0
                        pDstPtr + iDstBandIdx * psContext->nBandSpace +
918
0
                            y * psContext->nLineSpace,
919
0
                        psContext->eBufType,
920
0
                        static_cast<int>(psContext->nPixelSpace), nXSize);
921
0
                }
922
0
            }
923
0
            return;
924
0
        }
925
0
    }
926
927
0
    CPLAssert(!psContext->bSkipBlockCache);
928
929
    // Compose cached blocks into final buffer
930
0
    for (int i = 0; i < nBandsToWrite; ++i)
931
0
    {
932
0
        const int iSrcBandIdx =
933
0
            psContext->bCacheAllBands ? psContext->panBandMap[i] - 1
934
0
            : poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG ? i
935
0
                                                           : 0;
936
0
        assert(iSrcBandIdx >= 0);
937
0
        const int iDstBandIdx = poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG
938
0
                                    ? i
939
0
                                    : psJob->iDstBandIdxSeparate;
940
0
        const GByte *pSrcPtr =
941
0
            static_cast<GByte *>(apoBlocks[iSrcBandIdx]->GetDataRef()) +
942
0
            (static_cast<size_t>(nYOffsetInBlock) * poDS->m_nBlockXSize +
943
0
             nXOffsetInBlock) *
944
0
                nDTSize;
945
0
        for (int y = 0; y < nYSize; ++y)
946
0
        {
947
0
            GDALCopyWords64(pSrcPtr + static_cast<size_t>(y) *
948
0
                                          poDS->m_nBlockXSize * nDTSize,
949
0
                            psContext->eDT, nDTSize,
950
0
                            pDstPtr + iDstBandIdx * psContext->nBandSpace +
951
0
                                y * psContext->nLineSpace,
952
0
                            psContext->eBufType,
953
0
                            static_cast<int>(psContext->nPixelSpace), nXSize);
954
0
        }
955
0
    }
956
0
}
957
958
/************************************************************************/
959
/*                   IsMultiThreadedReadCompatible()                    */
960
/************************************************************************/
961
962
bool GTiffDataset::IsMultiThreadedReadCompatible() const
963
0
{
964
0
    return cpl::down_cast<GTiffRasterBand *>(papoBands[0])
965
0
               ->IsBaseGTiffClass() &&
966
0
           !m_bStreamingIn && !m_bStreamingOut &&
967
0
           (m_nCompression == COMPRESSION_NONE ||
968
0
            m_nCompression == COMPRESSION_ADOBE_DEFLATE ||
969
0
            m_nCompression == COMPRESSION_LZW ||
970
0
            m_nCompression == COMPRESSION_PACKBITS ||
971
0
            m_nCompression == COMPRESSION_LZMA ||
972
0
            m_nCompression == COMPRESSION_ZSTD ||
973
0
            m_nCompression == COMPRESSION_LERC ||
974
0
            m_nCompression == COMPRESSION_JXL ||
975
0
            m_nCompression == COMPRESSION_JXL_DNG_1_7 ||
976
0
            m_nCompression == COMPRESSION_WEBP ||
977
0
            m_nCompression == COMPRESSION_JPEG);
978
0
}
979
980
/************************************************************************/
981
/*                         MultiThreadedRead()                          */
982
/************************************************************************/
983
984
CPLErr GTiffDataset::MultiThreadedRead(int nXOff, int nYOff, int nXSize,
985
                                       int nYSize, void *pData,
986
                                       GDALDataType eBufType, int nBandCount,
987
                                       const int *panBandMap,
988
                                       GSpacing nPixelSpace,
989
                                       GSpacing nLineSpace, GSpacing nBandSpace)
990
0
{
991
0
    auto poQueue = m_poThreadPool->CreateJobQueue();
992
0
    if (poQueue == nullptr)
993
0
    {
994
0
        return CE_Failure;
995
0
    }
996
997
0
    const int nBlockXStart = nXOff / m_nBlockXSize;
998
0
    const int nBlockYStart = nYOff / m_nBlockYSize;
999
0
    const int nBlockXEnd = (nXOff + nXSize - 1) / m_nBlockXSize;
1000
0
    const int nBlockYEnd = (nYOff + nYSize - 1) / m_nBlockYSize;
1001
0
    const int nXBlocks = nBlockXEnd - nBlockXStart + 1;
1002
0
    const int nYBlocks = nBlockYEnd - nBlockYStart + 1;
1003
0
    const int nStrilePerBlock =
1004
0
        m_nPlanarConfig == PLANARCONFIG_CONTIG ? 1 : nBandCount;
1005
0
    const int nBlocks = nXBlocks * nYBlocks * nStrilePerBlock;
1006
1007
0
    GTiffDecompressContext sContext;
1008
0
    sContext.poHandle = VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
1009
0
    sContext.bHasPRead =
1010
0
        sContext.poHandle->HasPRead()
1011
0
#ifdef DEBUG
1012
0
        && CPLTestBool(CPLGetConfigOption("GTIFF_ALLOW_PREAD", "YES"))
1013
0
#endif
1014
0
        ;
1015
0
    sContext.poDS = this;
1016
0
    sContext.eDT = GetRasterBand(1)->GetRasterDataType();
1017
0
    sContext.nXOff = nXOff;
1018
0
    sContext.nYOff = nYOff;
1019
0
    sContext.nXSize = nXSize;
1020
0
    sContext.nYSize = nYSize;
1021
0
    sContext.nBlockXStart = nBlockXStart;
1022
0
    sContext.nBlockXEnd = nBlockXEnd;
1023
0
    sContext.nBlockYStart = nBlockYStart;
1024
0
    sContext.nBlockYEnd = nBlockYEnd;
1025
0
    sContext.pabyData = static_cast<GByte *>(pData);
1026
0
    sContext.eBufType = eBufType;
1027
0
    sContext.nBufDTSize = GDALGetDataTypeSizeBytes(eBufType);
1028
0
    sContext.nBandCount = nBandCount;
1029
0
    sContext.panBandMap = panBandMap;
1030
0
    sContext.nPixelSpace = nPixelSpace;
1031
0
    sContext.nLineSpace = nLineSpace;
1032
    // Setting nBandSpace to a dummy value when nBandCount == 1 helps detecting
1033
    // bad computations of target buffer address
1034
    // (https://github.com/rasterio/rasterio/issues/2847)
1035
0
    sContext.nBandSpace = nBandCount == 1 ? 0xDEADBEEF : nBandSpace;
1036
0
    sContext.bIsTiled = CPL_TO_BOOL(TIFFIsTiled(m_hTIFF));
1037
0
    sContext.bTIFFIsBigEndian = CPL_TO_BOOL(TIFFIsBigEndian(m_hTIFF));
1038
0
    sContext.nPredictor = PREDICTOR_NONE;
1039
0
    sContext.nBlocksPerRow = m_nBlocksPerRow;
1040
1041
0
    if (m_bDirectIO)
1042
0
    {
1043
0
        sContext.bSkipBlockCache = true;
1044
0
    }
1045
0
    else if (nXOff == 0 && nYOff == 0 && nXSize == nRasterXSize &&
1046
0
             nYSize == nRasterYSize)
1047
0
    {
1048
0
        if (m_nPlanarConfig == PLANARCONFIG_SEPARATE)
1049
0
        {
1050
0
            sContext.bSkipBlockCache = true;
1051
0
        }
1052
0
        else if (nBandCount == nBands)
1053
0
        {
1054
0
            sContext.bSkipBlockCache = true;
1055
0
            for (int i = 0; i < nBands; ++i)
1056
0
            {
1057
0
                if (panBandMap[i] != i + 1)
1058
0
                {
1059
0
                    sContext.bSkipBlockCache = false;
1060
0
                    break;
1061
0
                }
1062
0
            }
1063
0
        }
1064
0
    }
1065
1066
0
    if (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBandCount == nBands &&
1067
0
        nPixelSpace == nBands * static_cast<GSpacing>(sContext.nBufDTSize))
1068
0
    {
1069
0
        sContext.bUseBIPOptim = true;
1070
0
        for (int i = 0; i < nBands; ++i)
1071
0
        {
1072
0
            if (panBandMap[i] != i + 1)
1073
0
            {
1074
0
                sContext.bUseBIPOptim = false;
1075
0
                break;
1076
0
            }
1077
0
        }
1078
0
    }
1079
1080
0
    if (m_nPlanarConfig == PLANARCONFIG_CONTIG &&
1081
0
        (nBands == 3 || nBands == 4) && nBands == nBandCount &&
1082
0
        (sContext.eDT == GDT_UInt8 || sContext.eDT == GDT_Int16 ||
1083
0
         sContext.eDT == GDT_UInt16))
1084
0
    {
1085
0
        if (sContext.bSkipBlockCache)
1086
0
        {
1087
0
            if (sContext.eBufType == sContext.eDT &&
1088
0
                nPixelSpace == sContext.nBufDTSize)
1089
0
            {
1090
0
                sContext.bUseDeinterleaveOptimNoBlockCache = true;
1091
0
            }
1092
0
        }
1093
0
        else
1094
0
        {
1095
0
            sContext.bUseDeinterleaveOptimBlockCache = true;
1096
0
            for (int i = 0; i < nBands; ++i)
1097
0
            {
1098
0
                if (panBandMap[i] != i + 1)
1099
0
                {
1100
0
                    sContext.bUseDeinterleaveOptimBlockCache = false;
1101
0
                    break;
1102
0
                }
1103
0
            }
1104
0
        }
1105
0
    }
1106
1107
    // In contig mode, if only one band is requested, check if we have
1108
    // enough cache to cache all bands.
1109
0
    if (!sContext.bSkipBlockCache && nBands != 1 &&
1110
0
        m_nPlanarConfig == PLANARCONFIG_CONTIG && nBandCount == 1)
1111
0
    {
1112
0
        const GIntBig nRequiredMem = static_cast<GIntBig>(nBands) * nXBlocks *
1113
0
                                     nYBlocks * m_nBlockXSize * m_nBlockYSize *
1114
0
                                     GDALGetDataTypeSizeBytes(sContext.eDT);
1115
0
        if (nRequiredMem > GDALGetCacheMax64())
1116
0
        {
1117
0
            if (!m_bHasWarnedDisableAggressiveBandCaching)
1118
0
            {
1119
0
                CPLDebug("GTiff",
1120
0
                         "Disable aggressive band caching. "
1121
0
                         "Cache not big enough. "
1122
0
                         "At least " CPL_FRMT_GIB " bytes necessary",
1123
0
                         nRequiredMem);
1124
0
                m_bHasWarnedDisableAggressiveBandCaching = true;
1125
0
            }
1126
0
        }
1127
0
        else
1128
0
        {
1129
0
            sContext.bCacheAllBands = true;
1130
0
            if ((nBands == 3 || nBands == 4) &&
1131
0
                (sContext.eDT == GDT_UInt8 || sContext.eDT == GDT_Int16 ||
1132
0
                 sContext.eDT == GDT_UInt16))
1133
0
            {
1134
0
                sContext.bUseDeinterleaveOptimBlockCache = true;
1135
0
            }
1136
0
        }
1137
0
    }
1138
1139
0
    if (eAccess == GA_Update)
1140
0
    {
1141
0
        std::vector<int> anBandsToCheck;
1142
0
        if (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1)
1143
0
        {
1144
0
            for (int i = 0; i < nBands; ++i)
1145
0
            {
1146
0
                anBandsToCheck.push_back(i);
1147
0
            }
1148
0
        }
1149
0
        else
1150
0
        {
1151
0
            for (int i = 0; i < nBandCount; ++i)
1152
0
            {
1153
0
                anBandsToCheck.push_back(panBandMap[i] - 1);
1154
0
            }
1155
0
        }
1156
0
        if (!anBandsToCheck.empty())
1157
0
        {
1158
            // If at least one block in the region of intersest is dirty,
1159
            // fallback to normal reading code path to be able to retrieve
1160
            // content partly from the block cache.
1161
            // An alternative that was implemented in GDAL 3.6 to 3.8.0 was
1162
            // to flush dirty blocks, but this could cause many write&read&write
1163
            // cycles in some gdalwarp scenarios.
1164
            // Cf https://github.com/OSGeo/gdal/issues/8729
1165
0
            bool bUseBaseImplementation = false;
1166
0
            for (int y = 0; y < nYBlocks; ++y)
1167
0
            {
1168
0
                for (int x = 0; x < nXBlocks; ++x)
1169
0
                {
1170
0
                    for (const int iBand : anBandsToCheck)
1171
0
                    {
1172
0
                        if (m_nLoadedBlock >= 0 && m_bLoadedBlockDirty &&
1173
0
                            cpl::down_cast<GTiffRasterBand *>(papoBands[iBand])
1174
0
                                    ->ComputeBlockId(nBlockXStart + x,
1175
0
                                                     nBlockYStart + y) ==
1176
0
                                m_nLoadedBlock)
1177
0
                        {
1178
0
                            bUseBaseImplementation = true;
1179
0
                            goto after_loop;
1180
0
                        }
1181
0
                        auto poBlock = papoBands[iBand]->TryGetLockedBlockRef(
1182
0
                            nBlockXStart + x, nBlockYStart + y);
1183
0
                        if (poBlock)
1184
0
                        {
1185
0
                            if (poBlock->GetDirty())
1186
0
                            {
1187
0
                                poBlock->DropLock();
1188
0
                                bUseBaseImplementation = true;
1189
0
                                goto after_loop;
1190
0
                            }
1191
0
                            poBlock->DropLock();
1192
0
                        }
1193
0
                    }
1194
0
                }
1195
0
            }
1196
0
        after_loop:
1197
0
            if (bUseBaseImplementation)
1198
0
            {
1199
0
                ++m_nDisableMultiThreadedRead;
1200
0
                GDALRasterIOExtraArg sExtraArg;
1201
0
                INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1202
0
                const CPLErr eErr = GDALDataset::IRasterIO(
1203
0
                    GF_Read, nXOff, nYOff, nXSize, nYSize, pData, nXSize,
1204
0
                    nYSize, eBufType, nBandCount, const_cast<int *>(panBandMap),
1205
0
                    nPixelSpace, nLineSpace, nBandSpace, &sExtraArg);
1206
0
                --m_nDisableMultiThreadedRead;
1207
0
                return eErr;
1208
0
            }
1209
0
        }
1210
1211
        // Make sure that all blocks that we are going to read and that are
1212
        // being written by a worker thread are completed.
1213
        // cppcheck-suppress constVariableReference
1214
0
        auto &oQueue =
1215
0
            m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
1216
0
        if (!oQueue.empty())
1217
0
        {
1218
0
            for (int y = 0; y < nYBlocks; ++y)
1219
0
            {
1220
0
                for (int x = 0; x < nXBlocks; ++x)
1221
0
                {
1222
0
                    for (int i = 0; i < nStrilePerBlock; ++i)
1223
0
                    {
1224
0
                        int nBlockId =
1225
0
                            nBlockXStart + x +
1226
0
                            (nBlockYStart + y) * sContext.nBlocksPerRow;
1227
0
                        if (m_nPlanarConfig == PLANARCONFIG_SEPARATE)
1228
0
                            nBlockId += (panBandMap[i] - 1) * m_nBlocksPerBand;
1229
1230
0
                        WaitCompletionForBlock(nBlockId);
1231
0
                    }
1232
0
                }
1233
0
            }
1234
0
        }
1235
1236
        // Flush to file, and then to disk if using pread() interface
1237
0
        VSI_TIFFFlushBufferedWrite(TIFFClientdata(m_hTIFF));
1238
0
        if (sContext.bHasPRead)
1239
0
            sContext.poHandle->Flush();
1240
0
    }
1241
1242
0
    if (GTIFFSupportsPredictor(m_nCompression))
1243
0
    {
1244
0
        TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &sContext.nPredictor);
1245
0
    }
1246
0
    else if (m_nCompression == COMPRESSION_JPEG)
1247
0
    {
1248
0
        TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &sContext.nJPEGTableSize,
1249
0
                     &sContext.pJPEGTable);
1250
0
        if (m_nPhotometric == PHOTOMETRIC_YCBCR)
1251
0
        {
1252
0
            TIFFGetFieldDefaulted(m_hTIFF, TIFFTAG_YCBCRSUBSAMPLING,
1253
0
                                  &sContext.nYCrbCrSubSampling0,
1254
0
                                  &sContext.nYCrbCrSubSampling1);
1255
0
        }
1256
0
    }
1257
0
    if (m_nPlanarConfig == PLANARCONFIG_CONTIG)
1258
0
    {
1259
0
        TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &sContext.nExtraSampleCount,
1260
0
                     &sContext.pExtraSamples);
1261
0
    }
1262
1263
    // Create one job per tile/strip
1264
0
    vsi_l_offset nFileSize = 0;
1265
0
    std::vector<GTiffDecompressJob> asJobs(nBlocks);
1266
0
    std::vector<vsi_l_offset> anOffsets(nBlocks);
1267
0
    std::vector<size_t> anSizes(nBlocks);
1268
0
    int iJob = 0;
1269
0
    int nAdviseReadRanges = 0;
1270
0
    const size_t nAdviseReadTotalBytesLimit =
1271
0
        sContext.poHandle->GetAdviseReadTotalBytesLimit();
1272
0
    size_t nAdviseReadAccBytes = 0;
1273
0
    for (int y = 0; y < nYBlocks; ++y)
1274
0
    {
1275
0
        for (int x = 0; x < nXBlocks; ++x)
1276
0
        {
1277
0
            for (int i = 0; i < nStrilePerBlock; ++i)
1278
0
            {
1279
0
                asJobs[iJob].psContext = &sContext;
1280
0
                asJobs[iJob].iSrcBandIdxSeparate =
1281
0
                    m_nPlanarConfig == PLANARCONFIG_CONTIG ? -1
1282
0
                                                           : panBandMap[i] - 1;
1283
0
                asJobs[iJob].iDstBandIdxSeparate =
1284
0
                    m_nPlanarConfig == PLANARCONFIG_CONTIG ? -1 : i;
1285
0
                asJobs[iJob].nXBlock = nBlockXStart + x;
1286
0
                asJobs[iJob].nYBlock = nBlockYStart + y;
1287
1288
0
                int nBlockId = asJobs[iJob].nXBlock +
1289
0
                               asJobs[iJob].nYBlock * sContext.nBlocksPerRow;
1290
0
                if (m_nPlanarConfig == PLANARCONFIG_SEPARATE)
1291
0
                    nBlockId +=
1292
0
                        asJobs[iJob].iSrcBandIdxSeparate * m_nBlocksPerBand;
1293
1294
0
                bool bErrorInIsBlockAvailable = false;
1295
0
                if (!sContext.bHasPRead)
1296
0
                {
1297
                    // Taking the mutex here is only needed when bHasPRead ==
1298
                    // false since we could have concurrent uses of the handle,
1299
                    // when when reading the TIFF TileOffsets / TileByteCounts
1300
                    // array
1301
0
                    std::lock_guard<std::recursive_mutex> oLock(
1302
0
                        sContext.oMutex);
1303
1304
0
                    CPL_IGNORE_RET_VAL(IsBlockAvailable(
1305
0
                        nBlockId, &asJobs[iJob].nOffset, &asJobs[iJob].nSize,
1306
0
                        &bErrorInIsBlockAvailable));
1307
0
                }
1308
0
                else
1309
0
                {
1310
0
                    CPL_IGNORE_RET_VAL(IsBlockAvailable(
1311
0
                        nBlockId, &asJobs[iJob].nOffset, &asJobs[iJob].nSize,
1312
0
                        &bErrorInIsBlockAvailable));
1313
0
                }
1314
0
                if (bErrorInIsBlockAvailable)
1315
0
                {
1316
0
                    ReportError(CE_Failure, CPLE_AppDefined,
1317
0
                                "Error while getting location of block %d",
1318
0
                                nBlockId);
1319
0
                    std::lock_guard<std::recursive_mutex> oLock(
1320
0
                        sContext.oMutex);
1321
0
                    sContext.bSuccess = false;
1322
0
                    return CE_Failure;
1323
0
                }
1324
1325
                // Sanity check on block size
1326
0
                if (asJobs[iJob].nSize > 100U * 1024 * 1024)
1327
0
                {
1328
0
                    if (nFileSize == 0)
1329
0
                    {
1330
0
                        std::lock_guard<std::recursive_mutex> oLock(
1331
0
                            sContext.oMutex);
1332
0
                        sContext.poHandle->Seek(0, SEEK_END);
1333
0
                        nFileSize = sContext.poHandle->Tell();
1334
0
                    }
1335
0
                    if (asJobs[iJob].nSize > nFileSize)
1336
0
                    {
1337
0
                        CPLError(CE_Failure, CPLE_AppDefined,
1338
0
                                 "Cannot read " CPL_FRMT_GUIB
1339
0
                                 " bytes at offset " CPL_FRMT_GUIB,
1340
0
                                 static_cast<GUIntBig>(asJobs[iJob].nSize),
1341
0
                                 static_cast<GUIntBig>(asJobs[iJob].nOffset));
1342
1343
0
                        std::lock_guard<std::recursive_mutex> oLock(
1344
0
                            sContext.oMutex);
1345
0
                        sContext.bSuccess = false;
1346
0
                        return CE_Failure;
1347
0
                    }
1348
0
                }
1349
1350
                // Only request in AdviseRead() ranges for blocks we don't
1351
                // have in cache.
1352
0
                bool bAddToAdviseRead = true;
1353
0
                if (m_nPlanarConfig == PLANARCONFIG_SEPARATE)
1354
0
                {
1355
0
                    auto poBlock =
1356
0
                        GetRasterBand(panBandMap[i])
1357
0
                            ->TryGetLockedBlockRef(asJobs[iJob].nXBlock,
1358
0
                                                   asJobs[iJob].nYBlock);
1359
0
                    if (poBlock)
1360
0
                    {
1361
0
                        poBlock->DropLock();
1362
0
                        bAddToAdviseRead = false;
1363
0
                    }
1364
0
                }
1365
0
                else
1366
0
                {
1367
0
                    bool bAllCached = true;
1368
0
                    for (int iBand = 0; iBand < nBandCount; ++iBand)
1369
0
                    {
1370
0
                        auto poBlock =
1371
0
                            GetRasterBand(panBandMap[iBand])
1372
0
                                ->TryGetLockedBlockRef(asJobs[iJob].nXBlock,
1373
0
                                                       asJobs[iJob].nYBlock);
1374
0
                        if (poBlock)
1375
0
                        {
1376
0
                            poBlock->DropLock();
1377
0
                        }
1378
0
                        else
1379
0
                        {
1380
0
                            bAllCached = false;
1381
0
                            break;
1382
0
                        }
1383
0
                    }
1384
0
                    if (bAllCached)
1385
0
                        bAddToAdviseRead = false;
1386
0
                }
1387
1388
0
                if (bAddToAdviseRead)
1389
0
                {
1390
0
                    anOffsets[nAdviseReadRanges] = asJobs[iJob].nOffset;
1391
0
                    anSizes[nAdviseReadRanges] =
1392
0
                        static_cast<size_t>(std::min<vsi_l_offset>(
1393
0
                            std::numeric_limits<size_t>::max(),
1394
0
                            asJobs[iJob].nSize));
1395
1396
                    // If the total number of bytes we must read excess the
1397
                    // capacity of AdviseRead(), then split the RasterIO()
1398
                    // request in 2 halves.
1399
0
                    if (nAdviseReadTotalBytesLimit > 0 &&
1400
0
                        anSizes[nAdviseReadRanges] <
1401
0
                            nAdviseReadTotalBytesLimit &&
1402
0
                        anSizes[nAdviseReadRanges] >
1403
0
                            nAdviseReadTotalBytesLimit - nAdviseReadAccBytes &&
1404
0
                        nYBlocks >= 2)
1405
0
                    {
1406
0
                        const int nYOff2 =
1407
0
                            (nBlockYStart + nYBlocks / 2) * m_nBlockYSize;
1408
0
                        CPLDebugOnly("GTiff",
1409
0
                                     "Splitting request (%d,%d,%dx%d) into "
1410
0
                                     "(%d,%d,%dx%d) and (%d,%d,%dx%d)",
1411
0
                                     nXOff, nYOff, nXSize, nYSize, nXOff, nYOff,
1412
0
                                     nXSize, nYOff2 - nYOff, nXOff, nYOff2,
1413
0
                                     nXSize, nYOff + nYSize - nYOff2);
1414
1415
0
                        asJobs.clear();
1416
0
                        anOffsets.clear();
1417
0
                        anSizes.clear();
1418
0
                        poQueue.reset();
1419
1420
0
                        CPLErr eErr = MultiThreadedRead(
1421
0
                            nXOff, nYOff, nXSize, nYOff2 - nYOff, pData,
1422
0
                            eBufType, nBandCount, panBandMap, nPixelSpace,
1423
0
                            nLineSpace, nBandSpace);
1424
0
                        if (eErr == CE_None)
1425
0
                        {
1426
0
                            eErr = MultiThreadedRead(
1427
0
                                nXOff, nYOff2, nXSize, nYOff + nYSize - nYOff2,
1428
0
                                static_cast<GByte *>(pData) +
1429
0
                                    (nYOff2 - nYOff) * nLineSpace,
1430
0
                                eBufType, nBandCount, panBandMap, nPixelSpace,
1431
0
                                nLineSpace, nBandSpace);
1432
0
                        }
1433
0
                        return eErr;
1434
0
                    }
1435
0
                    nAdviseReadAccBytes += anSizes[nAdviseReadRanges];
1436
1437
0
                    ++nAdviseReadRanges;
1438
0
                }
1439
1440
0
                ++iJob;
1441
0
            }
1442
0
        }
1443
0
    }
1444
1445
0
    if (sContext.bSuccess)
1446
0
    {
1447
        // Potentially start asynchronous fetching of ranges depending on file
1448
        // implementation
1449
0
        if (nAdviseReadRanges > 0)
1450
0
        {
1451
0
            sContext.poHandle->AdviseRead(nAdviseReadRanges, anOffsets.data(),
1452
0
                                          anSizes.data());
1453
0
        }
1454
1455
        // We need to do that as threads will access the block cache
1456
0
        TemporarilyDropReadWriteLock();
1457
1458
0
        for (auto &sJob : asJobs)
1459
0
        {
1460
0
            poQueue->SubmitJob(ThreadDecompressionFunc, &sJob);
1461
0
        }
1462
1463
        // Wait for all jobs to have been completed
1464
0
        poQueue->WaitCompletion();
1465
1466
        // Undo effect of above TemporarilyDropReadWriteLock()
1467
0
        ReacquireReadWriteLock();
1468
1469
0
        sContext.oErrorAccumulator.ReplayErrors();
1470
0
    }
1471
1472
0
    return sContext.bSuccess ? CE_None : CE_Failure;
1473
0
}
1474
1475
/************************************************************************/
1476
/*                       FetchBufferVirtualMemIO                        */
1477
/************************************************************************/
1478
1479
class FetchBufferVirtualMemIO final
1480
{
1481
    const GByte *pabySrcData;
1482
    size_t nMappingSize;
1483
    GByte *pTempBuffer;
1484
1485
  public:
1486
    FetchBufferVirtualMemIO(const GByte *pabySrcDataIn, size_t nMappingSizeIn,
1487
                            GByte *pTempBufferIn)
1488
0
        : pabySrcData(pabySrcDataIn), nMappingSize(nMappingSizeIn),
1489
0
          pTempBuffer(pTempBufferIn)
1490
0
    {
1491
0
    }
1492
1493
    const GByte *FetchBytes(vsi_l_offset nOffset, int nPixels, int nDTSize,
1494
                            bool bIsByteSwapped, bool bIsComplex, int nBlockId)
1495
0
    {
1496
0
        if (nOffset + static_cast<size_t>(nPixels) * nDTSize > nMappingSize)
1497
0
        {
1498
0
            CPLError(CE_Failure, CPLE_FileIO, "Missing data for block %d",
1499
0
                     nBlockId);
1500
0
            return nullptr;
1501
0
        }
1502
0
        if (!bIsByteSwapped)
1503
0
            return pabySrcData + nOffset;
1504
0
        memcpy(pTempBuffer, pabySrcData + nOffset,
1505
0
               static_cast<size_t>(nPixels) * nDTSize);
1506
0
        if (bIsComplex)
1507
0
            GDALSwapWords(pTempBuffer, nDTSize / 2, 2 * nPixels, nDTSize / 2);
1508
0
        else
1509
0
            GDALSwapWords(pTempBuffer, nDTSize, nPixels, nDTSize);
1510
0
        return pTempBuffer;
1511
0
    }
1512
1513
    bool FetchBytes(GByte *pabyDstBuffer, vsi_l_offset nOffset, int nPixels,
1514
                    int nDTSize, bool bIsByteSwapped, bool bIsComplex,
1515
                    int nBlockId)
1516
0
    {
1517
0
        if (nOffset + static_cast<size_t>(nPixels) * nDTSize > nMappingSize)
1518
0
        {
1519
0
            CPLError(CE_Failure, CPLE_FileIO, "Missing data for block %d",
1520
0
                     nBlockId);
1521
0
            return false;
1522
0
        }
1523
0
        memcpy(pabyDstBuffer, pabySrcData + nOffset,
1524
0
               static_cast<size_t>(nPixels) * nDTSize);
1525
0
        if (bIsByteSwapped)
1526
0
        {
1527
0
            if (bIsComplex)
1528
0
                GDALSwapWords(pabyDstBuffer, nDTSize / 2, 2 * nPixels,
1529
0
                              nDTSize / 2);
1530
0
            else
1531
0
                GDALSwapWords(pabyDstBuffer, nDTSize, nPixels, nDTSize);
1532
0
        }
1533
0
        return true;
1534
0
    }
1535
1536
    // cppcheck-suppress unusedStructMember
1537
    static const bool bMinimizeIO = false;
1538
};
1539
1540
/************************************************************************/
1541
/*                            VirtualMemIO()                            */
1542
/************************************************************************/
1543
1544
int GTiffDataset::VirtualMemIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1545
                               int nXSize, int nYSize, void *pData,
1546
                               int nBufXSize, int nBufYSize,
1547
                               GDALDataType eBufType, int nBandCount,
1548
                               const int *panBandMap, GSpacing nPixelSpace,
1549
                               GSpacing nLineSpace, GSpacing nBandSpace,
1550
                               GDALRasterIOExtraArg *psExtraArg)
1551
0
{
1552
0
    if (eAccess == GA_Update || eRWFlag == GF_Write || m_bStreamingIn)
1553
0
        return -1;
1554
1555
    // Only know how to deal with nearest neighbour in this optimized routine.
1556
0
    if ((nXSize != nBufXSize || nYSize != nBufYSize) && psExtraArg != nullptr &&
1557
0
        psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
1558
0
    {
1559
0
        return -1;
1560
0
    }
1561
1562
0
    const GDALDataType eDataType = GetRasterBand(1)->GetRasterDataType();
1563
0
    const int nDTSizeBits = GDALGetDataTypeSizeBits(eDataType);
1564
0
    if (!(m_nCompression == COMPRESSION_NONE &&
1565
0
          (m_nPhotometric == PHOTOMETRIC_MINISBLACK ||
1566
0
           m_nPhotometric == PHOTOMETRIC_RGB ||
1567
0
           m_nPhotometric == PHOTOMETRIC_PALETTE) &&
1568
0
          m_nBitsPerSample == nDTSizeBits))
1569
0
    {
1570
0
        m_eVirtualMemIOUsage = VirtualMemIOEnum::NO;
1571
0
        return -1;
1572
0
    }
1573
1574
0
    size_t nMappingSize = 0;
1575
0
    GByte *pabySrcData = nullptr;
1576
0
    if (STARTS_WITH(m_osFilename.c_str(), "/vsimem/"))
1577
0
    {
1578
0
        vsi_l_offset nDataLength = 0;
1579
0
        pabySrcData =
1580
0
            VSIGetMemFileBuffer(m_osFilename.c_str(), &nDataLength, FALSE);
1581
0
        nMappingSize = static_cast<size_t>(nDataLength);
1582
0
        if (pabySrcData == nullptr)
1583
0
            return -1;
1584
0
    }
1585
0
    else if (m_psVirtualMemIOMapping == nullptr)
1586
0
    {
1587
0
        VSILFILE *fp = VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
1588
0
        if (!CPLIsVirtualMemFileMapAvailable() ||
1589
0
            VSIFGetNativeFileDescriptorL(fp) == nullptr)
1590
0
        {
1591
0
            m_eVirtualMemIOUsage = VirtualMemIOEnum::NO;
1592
0
            return -1;
1593
0
        }
1594
0
        if (VSIFSeekL(fp, 0, SEEK_END) != 0)
1595
0
        {
1596
0
            m_eVirtualMemIOUsage = VirtualMemIOEnum::NO;
1597
0
            return -1;
1598
0
        }
1599
0
        const vsi_l_offset nLength = VSIFTellL(fp);
1600
0
        if (static_cast<size_t>(nLength) != nLength)
1601
0
        {
1602
0
            m_eVirtualMemIOUsage = VirtualMemIOEnum::NO;
1603
0
            return -1;
1604
0
        }
1605
0
        if (m_eVirtualMemIOUsage == VirtualMemIOEnum::IF_ENOUGH_RAM)
1606
0
        {
1607
0
            GIntBig nRAM = CPLGetUsablePhysicalRAM();
1608
0
            if (static_cast<GIntBig>(nLength) > nRAM)
1609
0
            {
1610
0
                CPLDebug("GTiff",
1611
0
                         "Not enough RAM to map whole file into memory.");
1612
0
                m_eVirtualMemIOUsage = VirtualMemIOEnum::NO;
1613
0
                return -1;
1614
0
            }
1615
0
        }
1616
0
        m_psVirtualMemIOMapping = CPLVirtualMemFileMapNew(
1617
0
            fp, 0, nLength, VIRTUALMEM_READONLY, nullptr, nullptr);
1618
0
        if (m_psVirtualMemIOMapping == nullptr)
1619
0
        {
1620
0
            m_eVirtualMemIOUsage = VirtualMemIOEnum::NO;
1621
0
            return -1;
1622
0
        }
1623
0
        m_eVirtualMemIOUsage = VirtualMemIOEnum::YES;
1624
0
    }
1625
1626
0
    if (m_psVirtualMemIOMapping)
1627
0
    {
1628
0
#ifdef DEBUG
1629
0
        CPLDebug("GTiff", "Using VirtualMemIO");
1630
0
#endif
1631
0
        nMappingSize = CPLVirtualMemGetSize(m_psVirtualMemIOMapping);
1632
0
        pabySrcData =
1633
0
            static_cast<GByte *>(CPLVirtualMemGetAddr(m_psVirtualMemIOMapping));
1634
0
    }
1635
1636
0
    if (TIFFIsByteSwapped(m_hTIFF) && m_pTempBufferForCommonDirectIO == nullptr)
1637
0
    {
1638
0
        const int nDTSize = nDTSizeBits / 8;
1639
0
        size_t nTempBufferForCommonDirectIOSize = static_cast<size_t>(
1640
0
            m_nBlockXSize * nDTSize *
1641
0
            (m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1));
1642
0
        if (TIFFIsTiled(m_hTIFF))
1643
0
            nTempBufferForCommonDirectIOSize *= m_nBlockYSize;
1644
1645
0
        m_pTempBufferForCommonDirectIO = static_cast<GByte *>(
1646
0
            VSI_MALLOC_VERBOSE(nTempBufferForCommonDirectIOSize));
1647
0
        if (m_pTempBufferForCommonDirectIO == nullptr)
1648
0
            return CE_Failure;
1649
0
    }
1650
0
    FetchBufferVirtualMemIO oFetcher(pabySrcData, nMappingSize,
1651
0
                                     m_pTempBufferForCommonDirectIO);
1652
1653
0
    return CommonDirectIO(oFetcher, nXOff, nYOff, nXSize, nYSize, pData,
1654
0
                          nBufXSize, nBufYSize, eBufType, nBandCount,
1655
0
                          panBandMap, nPixelSpace, nLineSpace, nBandSpace);
1656
0
}
1657
1658
/************************************************************************/
1659
/*                      CopyContigByteMultiBand()                       */
1660
/************************************************************************/
1661
1662
static inline void CopyContigByteMultiBand(const GByte *CPL_RESTRICT pabySrc,
1663
                                           int nSrcStride,
1664
                                           GByte *CPL_RESTRICT pabyDest,
1665
                                           int nDestStride, int nIters,
1666
                                           int nBandCount)
1667
0
{
1668
0
    if (nBandCount == 3)
1669
0
    {
1670
0
        if (nSrcStride == 3 && nDestStride == 4)
1671
0
        {
1672
0
            while (nIters >= 8)
1673
0
            {
1674
0
                pabyDest[4 * 0 + 0] = pabySrc[3 * 0 + 0];
1675
0
                pabyDest[4 * 0 + 1] = pabySrc[3 * 0 + 1];
1676
0
                pabyDest[4 * 0 + 2] = pabySrc[3 * 0 + 2];
1677
0
                pabyDest[4 * 1 + 0] = pabySrc[3 * 1 + 0];
1678
0
                pabyDest[4 * 1 + 1] = pabySrc[3 * 1 + 1];
1679
0
                pabyDest[4 * 1 + 2] = pabySrc[3 * 1 + 2];
1680
0
                pabyDest[4 * 2 + 0] = pabySrc[3 * 2 + 0];
1681
0
                pabyDest[4 * 2 + 1] = pabySrc[3 * 2 + 1];
1682
0
                pabyDest[4 * 2 + 2] = pabySrc[3 * 2 + 2];
1683
0
                pabyDest[4 * 3 + 0] = pabySrc[3 * 3 + 0];
1684
0
                pabyDest[4 * 3 + 1] = pabySrc[3 * 3 + 1];
1685
0
                pabyDest[4 * 3 + 2] = pabySrc[3 * 3 + 2];
1686
0
                pabyDest[4 * 4 + 0] = pabySrc[3 * 4 + 0];
1687
0
                pabyDest[4 * 4 + 1] = pabySrc[3 * 4 + 1];
1688
0
                pabyDest[4 * 4 + 2] = pabySrc[3 * 4 + 2];
1689
0
                pabyDest[4 * 5 + 0] = pabySrc[3 * 5 + 0];
1690
0
                pabyDest[4 * 5 + 1] = pabySrc[3 * 5 + 1];
1691
0
                pabyDest[4 * 5 + 2] = pabySrc[3 * 5 + 2];
1692
0
                pabyDest[4 * 6 + 0] = pabySrc[3 * 6 + 0];
1693
0
                pabyDest[4 * 6 + 1] = pabySrc[3 * 6 + 1];
1694
0
                pabyDest[4 * 6 + 2] = pabySrc[3 * 6 + 2];
1695
0
                pabyDest[4 * 7 + 0] = pabySrc[3 * 7 + 0];
1696
0
                pabyDest[4 * 7 + 1] = pabySrc[3 * 7 + 1];
1697
0
                pabyDest[4 * 7 + 2] = pabySrc[3 * 7 + 2];
1698
0
                pabySrc += 3 * 8;
1699
0
                pabyDest += 4 * 8;
1700
0
                nIters -= 8;
1701
0
            }
1702
0
            while (nIters-- > 0)
1703
0
            {
1704
0
                pabyDest[0] = pabySrc[0];
1705
0
                pabyDest[1] = pabySrc[1];
1706
0
                pabyDest[2] = pabySrc[2];
1707
0
                pabySrc += 3;
1708
0
                pabyDest += 4;
1709
0
            }
1710
0
        }
1711
0
        else
1712
0
        {
1713
0
            while (nIters-- > 0)
1714
0
            {
1715
0
                pabyDest[0] = pabySrc[0];
1716
0
                pabyDest[1] = pabySrc[1];
1717
0
                pabyDest[2] = pabySrc[2];
1718
0
                pabySrc += nSrcStride;
1719
0
                pabyDest += nDestStride;
1720
0
            }
1721
0
        }
1722
0
    }
1723
0
    else
1724
0
    {
1725
0
        while (nIters-- > 0)
1726
0
        {
1727
0
            for (int iBand = 0; iBand < nBandCount; ++iBand)
1728
0
                pabyDest[iBand] = pabySrc[iBand];
1729
0
            pabySrc += nSrcStride;
1730
0
            pabyDest += nDestStride;
1731
0
        }
1732
0
    }
1733
0
}
1734
1735
/************************************************************************/
1736
/*                           CommonDirectIO()                           */
1737
/************************************************************************/
1738
1739
// #define DEBUG_REACHED_VIRTUAL_MEM_IO
1740
#ifdef DEBUG_REACHED_VIRTUAL_MEM_IO
1741
static int anReachedVirtualMemIO[52] = {0};
1742
#define REACHED(x) anReachedVirtualMemIO[x] = 1
1743
#else
1744
#define REACHED(x)
1745
#endif
1746
1747
template <class FetchBuffer>
1748
CPLErr GTiffDataset::CommonDirectIO(FetchBuffer &oFetcher, int nXOff, int nYOff,
1749
                                    int nXSize, int nYSize, void *pData,
1750
                                    int nBufXSize, int nBufYSize,
1751
                                    GDALDataType eBufType, int nBandCount,
1752
                                    const int *panBandMap, GSpacing nPixelSpace,
1753
                                    GSpacing nLineSpace, GSpacing nBandSpace)
1754
0
{
1755
0
    const auto poFirstBand =
1756
0
        cpl::down_cast<GTiffRasterBand *>(GetRasterBand(1));
1757
0
    const GDALDataType eDataType = poFirstBand->GetRasterDataType();
1758
0
    const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
1759
0
    const bool bIsComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eDataType));
1760
0
    const int nBufDTSize = GDALGetDataTypeSizeBytes(eBufType);
1761
1762
    // Get strip offsets.
1763
0
    toff_t *panOffsets = nullptr;
1764
0
    if (!TIFFGetField(m_hTIFF,
1765
0
                      (TIFFIsTiled(m_hTIFF)) ? TIFFTAG_TILEOFFSETS
1766
0
                                             : TIFFTAG_STRIPOFFSETS,
1767
0
                      &panOffsets) ||
1768
0
        panOffsets == nullptr)
1769
0
    {
1770
0
        return CE_Failure;
1771
0
    }
1772
1773
0
    bool bUseContigImplementation = m_nPlanarConfig == PLANARCONFIG_CONTIG &&
1774
0
                                    nBandCount > 1 && nBandSpace == nBufDTSize;
1775
0
    if (bUseContigImplementation)
1776
0
    {
1777
0
        for (int iBand = 0; iBand < nBandCount; ++iBand)
1778
0
        {
1779
0
            const int nBand = panBandMap[iBand];
1780
0
            if (nBand != iBand + 1)
1781
0
            {
1782
0
                bUseContigImplementation = false;
1783
0
                break;
1784
0
            }
1785
0
        }
1786
0
    }
1787
1788
0
    const int nBandsPerBlock =
1789
0
        m_nPlanarConfig == PLANARCONFIG_SEPARATE ? 1 : nBands;
1790
0
    const int nBandsPerBlockDTSize = nBandsPerBlock * nDTSize;
1791
0
    const bool bNoTypeChange = (eDataType == eBufType);
1792
0
    const bool bNoXResampling = (nXSize == nBufXSize);
1793
0
    const bool bNoXResamplingNoTypeChange = (bNoTypeChange && bNoXResampling);
1794
0
    const bool bByteOnly = (bNoTypeChange && nDTSize == 1);
1795
0
    const bool bByteNoXResampling = (bByteOnly && bNoXResamplingNoTypeChange);
1796
0
    const bool bIsByteSwapped = CPL_TO_BOOL(TIFFIsByteSwapped(m_hTIFF));
1797
0
    const double dfSrcXInc = nXSize / static_cast<double>(nBufXSize);
1798
0
    const double dfSrcYInc = nYSize / static_cast<double>(nBufYSize);
1799
1800
0
    int bNoDataSetIn = FALSE;
1801
0
    double dfNoData = poFirstBand->GetNoDataValue(&bNoDataSetIn);
1802
0
    GByte abyNoData = 0;
1803
0
    if (!bNoDataSetIn)
1804
0
        dfNoData = 0;
1805
0
    else if (dfNoData >= 0 && dfNoData <= 255)
1806
0
        abyNoData = static_cast<GByte>(dfNoData + 0.5);
1807
1808
    // cppcheck-suppress knownConditionTrueFalse
1809
0
    if (FetchBuffer::bMinimizeIO && TIFFIsTiled(m_hTIFF) && bNoXResampling &&
1810
0
        (nYSize == nBufYSize) && m_nPlanarConfig == PLANARCONFIG_CONTIG &&
1811
0
        nBandCount > 1)
1812
0
    {
1813
0
        GByte *pabyData = static_cast<GByte *>(pData);
1814
0
        for (int y = 0; y < nBufYSize;)
1815
0
        {
1816
0
            const int nSrcLine = nYOff + y;
1817
0
            const int nBlockYOff = nSrcLine / m_nBlockYSize;
1818
0
            const int nYOffsetInBlock = nSrcLine % m_nBlockYSize;
1819
0
            const int nUsedBlockHeight =
1820
0
                std::min(nBufYSize - y, m_nBlockYSize - nYOffsetInBlock);
1821
1822
0
            int nBlockXOff = nXOff / m_nBlockXSize;
1823
0
            int nXOffsetInBlock = nXOff % m_nBlockXSize;
1824
0
            int nBlockId = poFirstBand->ComputeBlockId(nBlockXOff, nBlockYOff);
1825
1826
0
            int x = 0;
1827
0
            while (x < nBufXSize)
1828
0
            {
1829
0
                const toff_t nCurOffset = panOffsets[nBlockId];
1830
0
                const int nUsedBlockWidth =
1831
0
                    std::min(m_nBlockXSize - nXOffsetInBlock, nBufXSize - x);
1832
1833
0
                if (nCurOffset == 0)
1834
0
                {
1835
0
                    REACHED(30);
1836
0
                    for (int k = 0; k < nUsedBlockHeight; ++k)
1837
0
                    {
1838
0
                        GByte *pabyLocalData =
1839
0
                            pabyData + (y + k) * nLineSpace + x * nPixelSpace;
1840
0
                        for (int iBand = 0; iBand < nBandCount; ++iBand)
1841
0
                        {
1842
0
                            GByte *pabyLocalDataBand =
1843
0
                                pabyLocalData + iBand * nBandSpace;
1844
1845
0
                            GDALCopyWords(&dfNoData, GDT_Float64, 0,
1846
0
                                          pabyLocalDataBand, eBufType,
1847
0
                                          static_cast<int>(nPixelSpace),
1848
0
                                          nUsedBlockWidth);
1849
0
                        }
1850
0
                    }
1851
0
                }
1852
0
                else
1853
0
                {
1854
0
                    const int nByteOffsetInBlock =
1855
0
                        nYOffsetInBlock * m_nBlockXSize * nBandsPerBlockDTSize;
1856
0
                    const GByte *pabyLocalSrcDataK0 = oFetcher.FetchBytes(
1857
0
                        nCurOffset + nByteOffsetInBlock,
1858
0
                        m_nBlockXSize * nUsedBlockHeight * nBandsPerBlock,
1859
0
                        nDTSize, bIsByteSwapped, bIsComplex, nBlockId);
1860
0
                    if (pabyLocalSrcDataK0 == nullptr)
1861
0
                        return CE_Failure;
1862
1863
0
                    for (int k = 0; k < nUsedBlockHeight; ++k)
1864
0
                    {
1865
0
                        GByte *pabyLocalData =
1866
0
                            pabyData + (y + k) * nLineSpace + x * nPixelSpace;
1867
0
                        const GByte *pabyLocalSrcData =
1868
0
                            pabyLocalSrcDataK0 +
1869
0
                            (k * m_nBlockXSize + nXOffsetInBlock) *
1870
0
                                nBandsPerBlockDTSize;
1871
1872
0
                        if (bUseContigImplementation && nBands == nBandCount &&
1873
0
                            nPixelSpace == nBandsPerBlockDTSize)
1874
0
                        {
1875
0
                            REACHED(31);
1876
0
                            GDALCopyWords(pabyLocalSrcData, eDataType, nDTSize,
1877
0
                                          pabyLocalData, eBufType, nBufDTSize,
1878
0
                                          nUsedBlockWidth * nBands);
1879
0
                        }
1880
0
                        else
1881
0
                        {
1882
0
                            REACHED(32);
1883
0
                            for (int iBand = 0; iBand < nBandCount; ++iBand)
1884
0
                            {
1885
0
                                GByte *pabyLocalDataBand =
1886
0
                                    pabyLocalData + iBand * nBandSpace;
1887
0
                                const GByte *pabyLocalSrcDataBand =
1888
0
                                    pabyLocalSrcData +
1889
0
                                    (panBandMap[iBand] - 1) * nDTSize;
1890
1891
0
                                GDALCopyWords(pabyLocalSrcDataBand, eDataType,
1892
0
                                              nBandsPerBlockDTSize,
1893
0
                                              pabyLocalDataBand, eBufType,
1894
0
                                              static_cast<int>(nPixelSpace),
1895
0
                                              nUsedBlockWidth);
1896
0
                            }
1897
0
                        }
1898
0
                    }
1899
0
                }
1900
1901
0
                nXOffsetInBlock = 0;
1902
0
                ++nBlockXOff;
1903
0
                ++nBlockId;
1904
0
                x += nUsedBlockWidth;
1905
0
            }
1906
1907
0
            y += nUsedBlockHeight;
1908
0
        }
1909
0
    }
1910
    // cppcheck-suppress knownConditionTrueFalse
1911
0
    else if (FetchBuffer::bMinimizeIO && TIFFIsTiled(m_hTIFF) &&
1912
0
             bNoXResampling && (nYSize == nBufYSize))
1913
    // && (m_nPlanarConfig == PLANARCONFIG_SEPARATE || nBandCount == 1) )
1914
0
    {
1915
0
        for (int iBand = 0; iBand < nBandCount; ++iBand)
1916
0
        {
1917
0
            GByte *pabyData = static_cast<GByte *>(pData) + iBand * nBandSpace;
1918
0
            const int nBand = panBandMap[iBand];
1919
0
            auto poCurBand =
1920
0
                cpl::down_cast<GTiffRasterBand *>(GetRasterBand(nBand));
1921
0
            for (int y = 0; y < nBufYSize;)
1922
0
            {
1923
0
                const int nSrcLine = nYOff + y;
1924
0
                const int m_nBlockYOff = nSrcLine / m_nBlockYSize;
1925
0
                const int nYOffsetIm_nBlock = nSrcLine % m_nBlockYSize;
1926
0
                const int nUsedBlockHeight =
1927
0
                    std::min(nBufYSize - y, m_nBlockYSize - nYOffsetIm_nBlock);
1928
1929
0
                int nBlockXOff = nXOff / m_nBlockXSize;
1930
0
                int nXOffsetInBlock = nXOff % m_nBlockXSize;
1931
0
                int nBlockId =
1932
0
                    poCurBand->ComputeBlockId(nBlockXOff, m_nBlockYOff);
1933
1934
0
                int x = 0;
1935
0
                while (x < nBufXSize)
1936
0
                {
1937
0
                    const toff_t nCurOffset = panOffsets[nBlockId];
1938
0
                    const int nUsedBlockWidth = std::min(
1939
0
                        m_nBlockXSize - nXOffsetInBlock, nBufXSize - x);
1940
1941
0
                    if (nCurOffset == 0)
1942
0
                    {
1943
0
                        REACHED(35);
1944
0
                        for (int k = 0; k < nUsedBlockHeight; ++k)
1945
0
                        {
1946
0
                            GByte *pabyLocalData = pabyData +
1947
0
                                                   (y + k) * nLineSpace +
1948
0
                                                   x * nPixelSpace;
1949
1950
0
                            GDALCopyWords(&dfNoData, GDT_Float64, 0,
1951
0
                                          pabyLocalData, eBufType,
1952
0
                                          static_cast<int>(nPixelSpace),
1953
0
                                          nUsedBlockWidth);
1954
0
                        }
1955
0
                    }
1956
0
                    else
1957
0
                    {
1958
0
                        const int nByteOffsetIm_nBlock = nYOffsetIm_nBlock *
1959
0
                                                         m_nBlockXSize *
1960
0
                                                         nBandsPerBlockDTSize;
1961
0
                        const GByte *pabyLocalSrcDataK0 = oFetcher.FetchBytes(
1962
0
                            nCurOffset + nByteOffsetIm_nBlock,
1963
0
                            m_nBlockXSize * nUsedBlockHeight * nBandsPerBlock,
1964
0
                            nDTSize, bIsByteSwapped, bIsComplex, nBlockId);
1965
0
                        if (pabyLocalSrcDataK0 == nullptr)
1966
0
                            return CE_Failure;
1967
1968
0
                        if (m_nPlanarConfig == PLANARCONFIG_CONTIG)
1969
0
                        {
1970
0
                            REACHED(36);
1971
0
                            pabyLocalSrcDataK0 += (nBand - 1) * nDTSize;
1972
0
                        }
1973
0
                        else
1974
0
                        {
1975
0
                            REACHED(37);
1976
0
                        }
1977
1978
0
                        for (int k = 0; k < nUsedBlockHeight; ++k)
1979
0
                        {
1980
0
                            GByte *pabyLocalData = pabyData +
1981
0
                                                   (y + k) * nLineSpace +
1982
0
                                                   x * nPixelSpace;
1983
0
                            const GByte *pabyLocalSrcData =
1984
0
                                pabyLocalSrcDataK0 +
1985
0
                                (k * m_nBlockXSize + nXOffsetInBlock) *
1986
0
                                    nBandsPerBlockDTSize;
1987
1988
0
                            GDALCopyWords(
1989
0
                                pabyLocalSrcData, eDataType,
1990
0
                                nBandsPerBlockDTSize, pabyLocalData, eBufType,
1991
0
                                static_cast<int>(nPixelSpace), nUsedBlockWidth);
1992
0
                        }
1993
0
                    }
1994
1995
0
                    nXOffsetInBlock = 0;
1996
0
                    ++nBlockXOff;
1997
0
                    ++nBlockId;
1998
0
                    x += nUsedBlockWidth;
1999
0
                }
2000
2001
0
                y += nUsedBlockHeight;
2002
0
            }
2003
0
        }
2004
0
    }
2005
    // cppcheck-suppress knownConditionTrueFalse
2006
0
    else if (FetchBuffer::bMinimizeIO && TIFFIsTiled(m_hTIFF) &&
2007
0
             m_nPlanarConfig == PLANARCONFIG_CONTIG && nBandCount > 1)
2008
0
    {
2009
0
        GByte *pabyData = static_cast<GByte *>(pData);
2010
0
        int anSrcYOffset[256] = {0};
2011
0
        for (int y = 0; y < nBufYSize;)
2012
0
        {
2013
0
            const double dfYOffStart = nYOff + (y + 0.5) * dfSrcYInc;
2014
0
            const int nSrcLine = static_cast<int>(dfYOffStart);
2015
0
            const int nYOffsetIm_nBlock = nSrcLine % m_nBlockYSize;
2016
0
            const int m_nBlockYOff = nSrcLine / m_nBlockYSize;
2017
0
            const int nBaseByteOffsetIm_nBlock =
2018
0
                nYOffsetIm_nBlock * m_nBlockXSize * nBandsPerBlockDTSize;
2019
0
            int ychunk = 1;
2020
0
            int nLastSrcLineK = nSrcLine;
2021
0
            anSrcYOffset[0] = 0;
2022
0
            for (int k = 1; k < nBufYSize - y; ++k)
2023
0
            {
2024
0
                int nSrcLineK =
2025
0
                    nYOff + static_cast<int>((y + k + 0.5) * dfSrcYInc);
2026
0
                const int nBlockYOffK = nSrcLineK / m_nBlockYSize;
2027
0
                if (k < 256)
2028
0
                    anSrcYOffset[k] =
2029
0
                        ((nSrcLineK % m_nBlockYSize) - nYOffsetIm_nBlock) *
2030
0
                        m_nBlockXSize * nBandsPerBlockDTSize;
2031
0
                if (nBlockYOffK != m_nBlockYOff)
2032
0
                {
2033
0
                    break;
2034
0
                }
2035
0
                ++ychunk;
2036
0
                nLastSrcLineK = nSrcLineK;
2037
0
            }
2038
0
            const int nUsedBlockHeight = nLastSrcLineK - nSrcLine + 1;
2039
            // CPLAssert(nUsedBlockHeight <= m_nBlockYSize);
2040
2041
0
            double dfSrcX = nXOff + 0.5 * dfSrcXInc;
2042
0
            int nCurBlockXOff = 0;
2043
0
            int nNextBlockXOff = 0;
2044
0
            toff_t nCurOffset = 0;
2045
0
            const GByte *pabyLocalSrcDataStartLine = nullptr;
2046
0
            for (int x = 0; x < nBufXSize; ++x, dfSrcX += dfSrcXInc)
2047
0
            {
2048
0
                const int nSrcPixel = static_cast<int>(dfSrcX);
2049
0
                if (nSrcPixel >= nNextBlockXOff)
2050
0
                {
2051
0
                    const int nBlockXOff = nSrcPixel / m_nBlockXSize;
2052
0
                    nCurBlockXOff = nBlockXOff * m_nBlockXSize;
2053
0
                    nNextBlockXOff = nCurBlockXOff + m_nBlockXSize;
2054
0
                    const int nBlockId =
2055
0
                        poFirstBand->ComputeBlockId(nBlockXOff, m_nBlockYOff);
2056
0
                    nCurOffset = panOffsets[nBlockId];
2057
0
                    if (nCurOffset != 0)
2058
0
                    {
2059
0
                        pabyLocalSrcDataStartLine = oFetcher.FetchBytes(
2060
0
                            nCurOffset + nBaseByteOffsetIm_nBlock,
2061
0
                            m_nBlockXSize * nBandsPerBlock * nUsedBlockHeight,
2062
0
                            nDTSize, bIsByteSwapped, bIsComplex, nBlockId);
2063
0
                        if (pabyLocalSrcDataStartLine == nullptr)
2064
0
                            return CE_Failure;
2065
0
                    }
2066
0
                }
2067
2068
0
                if (nCurOffset == 0)
2069
0
                {
2070
0
                    REACHED(38);
2071
2072
0
                    for (int k = 0; k < ychunk; ++k)
2073
0
                    {
2074
0
                        GByte *const pabyLocalData =
2075
0
                            pabyData + (y + k) * nLineSpace + x * nPixelSpace;
2076
0
                        for (int iBand = 0; iBand < nBandCount; ++iBand)
2077
0
                        {
2078
0
                            GDALCopyWords(&dfNoData, GDT_Float64, 0,
2079
0
                                          pabyLocalData + nBandSpace * iBand,
2080
0
                                          eBufType, 0, 1);
2081
0
                        }
2082
0
                    }
2083
0
                }
2084
0
                else
2085
0
                {
2086
0
                    const int nXOffsetInBlock = nSrcPixel - nCurBlockXOff;
2087
0
                    double dfYOff = dfYOffStart;
2088
0
                    const GByte *const pabyLocalSrcDataK0 =
2089
0
                        pabyLocalSrcDataStartLine +
2090
0
                        nXOffsetInBlock * nBandsPerBlockDTSize;
2091
0
                    GByte *pabyLocalData =
2092
0
                        pabyData + y * nLineSpace + x * nPixelSpace;
2093
0
                    for (int k = 0; k < ychunk;
2094
0
                         ++k, pabyLocalData += nLineSpace)
2095
0
                    {
2096
0
                        const GByte *pabyLocalSrcData = nullptr;
2097
0
                        if (ychunk <= 256)
2098
0
                        {
2099
0
                            REACHED(39);
2100
0
                            pabyLocalSrcData =
2101
0
                                pabyLocalSrcDataK0 + anSrcYOffset[k];
2102
0
                        }
2103
0
                        else
2104
0
                        {
2105
0
                            REACHED(40);
2106
0
                            const int nYOffsetIm_nBlockK =
2107
0
                                static_cast<int>(dfYOff) % m_nBlockYSize;
2108
                            // CPLAssert(
2109
                            //     nYOffsetIm_nBlockK - nYOffsetIm_nBlock <=
2110
                            //     nUsedBlockHeight);
2111
0
                            pabyLocalSrcData =
2112
0
                                pabyLocalSrcDataK0 +
2113
0
                                (nYOffsetIm_nBlockK - nYOffsetIm_nBlock) *
2114
0
                                    m_nBlockXSize * nBandsPerBlockDTSize;
2115
0
                            dfYOff += dfSrcYInc;
2116
0
                        }
2117
2118
0
                        if (bByteOnly)
2119
0
                        {
2120
0
                            REACHED(41);
2121
0
                            for (int iBand = 0; iBand < nBandCount; ++iBand)
2122
0
                            {
2123
0
                                GByte *pabyLocalDataBand =
2124
0
                                    pabyLocalData + iBand * nBandSpace;
2125
0
                                const GByte *pabyLocalSrcDataBand =
2126
0
                                    pabyLocalSrcData + (panBandMap[iBand] - 1);
2127
0
                                *pabyLocalDataBand = *pabyLocalSrcDataBand;
2128
0
                            }
2129
0
                        }
2130
0
                        else
2131
0
                        {
2132
0
                            REACHED(42);
2133
0
                            for (int iBand = 0; iBand < nBandCount; ++iBand)
2134
0
                            {
2135
0
                                GByte *pabyLocalDataBand =
2136
0
                                    pabyLocalData + iBand * nBandSpace;
2137
0
                                const GByte *pabyLocalSrcDataBand =
2138
0
                                    pabyLocalSrcData +
2139
0
                                    (panBandMap[iBand] - 1) * nDTSize;
2140
2141
0
                                GDALCopyWords(pabyLocalSrcDataBand, eDataType,
2142
0
                                              0, pabyLocalDataBand, eBufType, 0,
2143
0
                                              1);
2144
0
                            }
2145
0
                        }
2146
0
                    }
2147
0
                }
2148
0
            }
2149
2150
0
            y += ychunk;
2151
0
        }
2152
0
    }
2153
    // cppcheck-suppress knownConditionTrueFalse
2154
0
    else if (FetchBuffer::bMinimizeIO && TIFFIsTiled(m_hTIFF))
2155
    // && (m_nPlanarConfig == PLANARCONFIG_SEPARATE || nBandCount == 1) )
2156
0
    {
2157
0
        for (int iBand = 0; iBand < nBandCount; ++iBand)
2158
0
        {
2159
0
            GByte *pabyData = static_cast<GByte *>(pData) + iBand * nBandSpace;
2160
0
            const int nBand = panBandMap[iBand];
2161
0
            auto poCurBand =
2162
0
                cpl::down_cast<GTiffRasterBand *>(GetRasterBand(nBand));
2163
0
            int anSrcYOffset[256] = {0};
2164
0
            for (int y = 0; y < nBufYSize;)
2165
0
            {
2166
0
                const double dfYOffStart = nYOff + (y + 0.5) * dfSrcYInc;
2167
0
                const int nSrcLine = static_cast<int>(dfYOffStart);
2168
0
                const int nYOffsetIm_nBlock = nSrcLine % m_nBlockYSize;
2169
0
                const int m_nBlockYOff = nSrcLine / m_nBlockYSize;
2170
0
                const int nBaseByteOffsetIm_nBlock =
2171
0
                    nYOffsetIm_nBlock * m_nBlockXSize * nBandsPerBlockDTSize;
2172
0
                int ychunk = 1;
2173
0
                int nLastSrcLineK = nSrcLine;
2174
0
                anSrcYOffset[0] = 0;
2175
0
                for (int k = 1; k < nBufYSize - y; ++k)
2176
0
                {
2177
0
                    const int nSrcLineK =
2178
0
                        nYOff + static_cast<int>((y + k + 0.5) * dfSrcYInc);
2179
0
                    const int nBlockYOffK = nSrcLineK / m_nBlockYSize;
2180
0
                    if (k < 256)
2181
0
                        anSrcYOffset[k] =
2182
0
                            ((nSrcLineK % m_nBlockYSize) - nYOffsetIm_nBlock) *
2183
0
                            m_nBlockXSize * nBandsPerBlockDTSize;
2184
0
                    if (nBlockYOffK != m_nBlockYOff)
2185
0
                    {
2186
0
                        break;
2187
0
                    }
2188
0
                    ++ychunk;
2189
0
                    nLastSrcLineK = nSrcLineK;
2190
0
                }
2191
0
                const int nUsedBlockHeight = nLastSrcLineK - nSrcLine + 1;
2192
                // CPLAssert(nUsedBlockHeight <= m_nBlockYSize);
2193
2194
0
                double dfSrcX = nXOff + 0.5 * dfSrcXInc;
2195
0
                int nCurBlockXOff = 0;
2196
0
                int nNextBlockXOff = 0;
2197
0
                toff_t nCurOffset = 0;
2198
0
                const GByte *pabyLocalSrcDataStartLine = nullptr;
2199
0
                for (int x = 0; x < nBufXSize; ++x, dfSrcX += dfSrcXInc)
2200
0
                {
2201
0
                    int nSrcPixel = static_cast<int>(dfSrcX);
2202
0
                    if (nSrcPixel >= nNextBlockXOff)
2203
0
                    {
2204
0
                        const int nBlockXOff = nSrcPixel / m_nBlockXSize;
2205
0
                        nCurBlockXOff = nBlockXOff * m_nBlockXSize;
2206
0
                        nNextBlockXOff = nCurBlockXOff + m_nBlockXSize;
2207
0
                        const int nBlockId =
2208
0
                            poCurBand->ComputeBlockId(nBlockXOff, m_nBlockYOff);
2209
0
                        nCurOffset = panOffsets[nBlockId];
2210
0
                        if (nCurOffset != 0)
2211
0
                        {
2212
0
                            pabyLocalSrcDataStartLine = oFetcher.FetchBytes(
2213
0
                                nCurOffset + nBaseByteOffsetIm_nBlock,
2214
0
                                m_nBlockXSize * nBandsPerBlock *
2215
0
                                    nUsedBlockHeight,
2216
0
                                nDTSize, bIsByteSwapped, bIsComplex, nBlockId);
2217
0
                            if (pabyLocalSrcDataStartLine == nullptr)
2218
0
                                return CE_Failure;
2219
2220
0
                            if (m_nPlanarConfig == PLANARCONFIG_CONTIG)
2221
0
                            {
2222
0
                                REACHED(45);
2223
0
                                pabyLocalSrcDataStartLine +=
2224
0
                                    (nBand - 1) * nDTSize;
2225
0
                            }
2226
0
                            else
2227
0
                            {
2228
0
                                REACHED(46);
2229
0
                            }
2230
0
                        }
2231
0
                    }
2232
2233
0
                    if (nCurOffset == 0)
2234
0
                    {
2235
0
                        REACHED(47);
2236
2237
0
                        for (int k = 0; k < ychunk; ++k)
2238
0
                        {
2239
0
                            GByte *const pabyLocalData = pabyData +
2240
0
                                                         (y + k) * nLineSpace +
2241
0
                                                         x * nPixelSpace;
2242
2243
0
                            GDALCopyWords(&dfNoData, GDT_Float64, 0,
2244
0
                                          pabyLocalData, eBufType, 0, 1);
2245
0
                        }
2246
0
                    }
2247
0
                    else
2248
0
                    {
2249
0
                        const int nXOffsetInBlock = nSrcPixel - nCurBlockXOff;
2250
0
                        double dfYOff = dfYOffStart;
2251
0
                        const GByte *const pabyLocalSrcDataK0 =
2252
0
                            pabyLocalSrcDataStartLine +
2253
0
                            nXOffsetInBlock * nBandsPerBlockDTSize;
2254
0
                        GByte *pabyLocalData =
2255
0
                            pabyData + y * nLineSpace + x * nPixelSpace;
2256
0
                        for (int k = 0; k < ychunk;
2257
0
                             ++k, pabyLocalData += nLineSpace)
2258
0
                        {
2259
0
                            const GByte *pabyLocalSrcData = nullptr;
2260
0
                            if (ychunk <= 256)
2261
0
                            {
2262
0
                                REACHED(48);
2263
0
                                pabyLocalSrcData =
2264
0
                                    pabyLocalSrcDataK0 + anSrcYOffset[k];
2265
0
                            }
2266
0
                            else
2267
0
                            {
2268
0
                                REACHED(49);
2269
0
                                const int nYOffsetIm_nBlockK =
2270
0
                                    static_cast<int>(dfYOff) % m_nBlockYSize;
2271
                                // CPLAssert(
2272
                                //     nYOffsetIm_nBlockK - nYOffsetIm_nBlock <=
2273
                                //     nUsedBlockHeight);
2274
0
                                pabyLocalSrcData =
2275
0
                                    pabyLocalSrcDataK0 +
2276
0
                                    (nYOffsetIm_nBlockK - nYOffsetIm_nBlock) *
2277
0
                                        m_nBlockXSize * nBandsPerBlockDTSize;
2278
0
                                dfYOff += dfSrcYInc;
2279
0
                            }
2280
2281
0
                            if (bByteOnly)
2282
0
                            {
2283
0
                                REACHED(50);
2284
2285
0
                                *pabyLocalData = *pabyLocalSrcData;
2286
0
                            }
2287
0
                            else
2288
0
                            {
2289
0
                                REACHED(51);
2290
2291
0
                                GDALCopyWords(pabyLocalSrcData, eDataType, 0,
2292
0
                                              pabyLocalData, eBufType, 0, 1);
2293
0
                            }
2294
0
                        }
2295
0
                    }
2296
0
                }
2297
2298
0
                y += ychunk;
2299
0
            }
2300
0
        }
2301
0
    }
2302
0
    else if (bUseContigImplementation)
2303
0
    {
2304
        // cppcheck-suppress knownConditionTrueFalse
2305
0
        if (!FetchBuffer::bMinimizeIO && TIFFIsTiled(m_hTIFF))
2306
0
        {
2307
0
            GByte *pabyData = static_cast<GByte *>(pData);
2308
0
            for (int y = 0; y < nBufYSize; ++y)
2309
0
            {
2310
0
                const int nSrcLine =
2311
0
                    nYOff + static_cast<int>((y + 0.5) * dfSrcYInc);
2312
0
                const int m_nBlockYOff = nSrcLine / m_nBlockYSize;
2313
0
                const int nYOffsetIm_nBlock = nSrcLine % m_nBlockYSize;
2314
0
                const int nBaseByteOffsetIm_nBlock =
2315
0
                    nYOffsetIm_nBlock * m_nBlockXSize * nBandsPerBlockDTSize;
2316
2317
0
                if (bNoXResampling)
2318
0
                {
2319
0
                    GByte *pabyLocalData = pabyData + y * nLineSpace;
2320
0
                    int nBlockXOff = nXOff / m_nBlockXSize;
2321
0
                    int nXOffsetInBlock = nXOff % m_nBlockXSize;
2322
0
                    int nBlockId =
2323
0
                        poFirstBand->ComputeBlockId(nBlockXOff, m_nBlockYOff);
2324
2325
0
                    int x = 0;
2326
0
                    while (x < nBufXSize)
2327
0
                    {
2328
0
                        const int nByteOffsetIm_nBlock =
2329
0
                            nBaseByteOffsetIm_nBlock +
2330
0
                            nXOffsetInBlock * nBandsPerBlockDTSize;
2331
0
                        const toff_t nCurOffset = panOffsets[nBlockId];
2332
0
                        const int nUsedBlockWidth = std::min(
2333
0
                            m_nBlockXSize - nXOffsetInBlock, nBufXSize - x);
2334
2335
0
                        int nIters = nUsedBlockWidth;
2336
0
                        if (nCurOffset == 0)
2337
0
                        {
2338
0
                            if (bByteNoXResampling)
2339
0
                            {
2340
0
                                REACHED(0);
2341
0
                                while (nIters-- > 0)
2342
0
                                {
2343
0
                                    for (int iBand = 0; iBand < nBandCount;
2344
0
                                         ++iBand)
2345
0
                                    {
2346
0
                                        pabyLocalData[iBand] = abyNoData;
2347
0
                                    }
2348
0
                                    pabyLocalData += nPixelSpace;
2349
0
                                }
2350
0
                            }
2351
0
                            else
2352
0
                            {
2353
0
                                REACHED(1);
2354
0
                                while (nIters-- > 0)
2355
0
                                {
2356
0
                                    GDALCopyWords(&dfNoData, GDT_Float64, 0,
2357
0
                                                  pabyLocalData, eBufType,
2358
0
                                                  static_cast<int>(nBandSpace),
2359
0
                                                  nBandCount);
2360
0
                                    pabyLocalData += nPixelSpace;
2361
0
                                }
2362
0
                            }
2363
0
                        }
2364
0
                        else
2365
0
                        {
2366
0
                            if (bNoTypeChange && nBands == nBandCount &&
2367
0
                                nPixelSpace == nBandsPerBlockDTSize)
2368
0
                            {
2369
0
                                REACHED(2);
2370
0
                                if (!oFetcher.FetchBytes(
2371
0
                                        pabyLocalData,
2372
0
                                        nCurOffset + nByteOffsetIm_nBlock,
2373
0
                                        nIters * nBandsPerBlock, nDTSize,
2374
0
                                        bIsByteSwapped, bIsComplex, nBlockId))
2375
0
                                {
2376
0
                                    return CE_Failure;
2377
0
                                }
2378
0
                                pabyLocalData +=
2379
0
                                    nIters * nBandsPerBlock * nDTSize;
2380
0
                            }
2381
0
                            else
2382
0
                            {
2383
0
                                const GByte *pabyLocalSrcData =
2384
0
                                    oFetcher.FetchBytes(
2385
0
                                        nCurOffset + nByteOffsetIm_nBlock,
2386
0
                                        nIters * nBandsPerBlock, nDTSize,
2387
0
                                        bIsByteSwapped, bIsComplex, nBlockId);
2388
0
                                if (pabyLocalSrcData == nullptr)
2389
0
                                    return CE_Failure;
2390
0
                                if (bByteNoXResampling)
2391
0
                                {
2392
0
                                    REACHED(3);
2393
0
                                    CopyContigByteMultiBand(
2394
0
                                        pabyLocalSrcData, nBandsPerBlockDTSize,
2395
0
                                        pabyLocalData,
2396
0
                                        static_cast<int>(nPixelSpace), nIters,
2397
0
                                        nBandCount);
2398
0
                                    pabyLocalData += nIters * nPixelSpace;
2399
0
                                }
2400
0
                                else
2401
0
                                {
2402
0
                                    REACHED(4);
2403
0
                                    while (nIters-- > 0)
2404
0
                                    {
2405
0
                                        GDALCopyWords(
2406
0
                                            pabyLocalSrcData, eDataType,
2407
0
                                            nDTSize, pabyLocalData, eBufType,
2408
0
                                            static_cast<int>(nBandSpace),
2409
0
                                            nBandCount);
2410
0
                                        pabyLocalSrcData +=
2411
0
                                            nBandsPerBlockDTSize;
2412
0
                                        pabyLocalData += nPixelSpace;
2413
0
                                    }
2414
0
                                }
2415
0
                            }
2416
0
                        }
2417
2418
0
                        nXOffsetInBlock = 0;
2419
0
                        ++nBlockXOff;
2420
0
                        ++nBlockId;
2421
0
                        x += nUsedBlockWidth;
2422
0
                    }
2423
0
                }
2424
0
                else  // Contig, tiled, potential resampling & data type change.
2425
0
                {
2426
0
                    const GByte *pabyLocalSrcDataStartLine = nullptr;
2427
0
                    GByte *pabyLocalData = pabyData + y * nLineSpace;
2428
0
                    double dfSrcX = nXOff + 0.5 * dfSrcXInc;
2429
0
                    int nCurBlockXOff = 0;
2430
0
                    int nNextBlockXOff = 0;
2431
0
                    toff_t nCurOffset = 0;
2432
0
                    for (int x = 0; x < nBufXSize; ++x, dfSrcX += dfSrcXInc)
2433
0
                    {
2434
0
                        int nSrcPixel = static_cast<int>(dfSrcX);
2435
0
                        if (nSrcPixel >= nNextBlockXOff)
2436
0
                        {
2437
0
                            const int nBlockXOff = nSrcPixel / m_nBlockXSize;
2438
0
                            nCurBlockXOff = nBlockXOff * m_nBlockXSize;
2439
0
                            nNextBlockXOff = nCurBlockXOff + m_nBlockXSize;
2440
0
                            const int nBlockId = poFirstBand->ComputeBlockId(
2441
0
                                nBlockXOff, m_nBlockYOff);
2442
0
                            nCurOffset = panOffsets[nBlockId];
2443
0
                            if (nCurOffset != 0)
2444
0
                            {
2445
0
                                pabyLocalSrcDataStartLine = oFetcher.FetchBytes(
2446
0
                                    nCurOffset + nBaseByteOffsetIm_nBlock,
2447
0
                                    m_nBlockXSize * nBandsPerBlock, nDTSize,
2448
0
                                    bIsByteSwapped, bIsComplex, nBlockId);
2449
0
                                if (pabyLocalSrcDataStartLine == nullptr)
2450
0
                                    return CE_Failure;
2451
0
                            }
2452
0
                        }
2453
0
                        const int nXOffsetInBlock = nSrcPixel - nCurBlockXOff;
2454
2455
0
                        if (nCurOffset == 0)
2456
0
                        {
2457
0
                            REACHED(5);
2458
0
                            GDALCopyWords(&dfNoData, GDT_Float64, 0,
2459
0
                                          pabyLocalData, eBufType,
2460
0
                                          static_cast<int>(nBandSpace),
2461
0
                                          nBandCount);
2462
0
                            pabyLocalData += nPixelSpace;
2463
0
                        }
2464
0
                        else
2465
0
                        {
2466
0
                            const GByte *pabyLocalSrcData =
2467
0
                                pabyLocalSrcDataStartLine +
2468
0
                                nXOffsetInBlock * nBandsPerBlockDTSize;
2469
2470
0
                            REACHED(6);
2471
0
                            if (bByteOnly)
2472
0
                            {
2473
0
                                for (int iBand = 0; iBand < nBands; ++iBand)
2474
0
                                    pabyLocalData[iBand] =
2475
0
                                        pabyLocalSrcData[iBand];
2476
0
                            }
2477
0
                            else
2478
0
                            {
2479
0
                                GDALCopyWords(pabyLocalSrcData, eDataType,
2480
0
                                              nDTSize, pabyLocalData, eBufType,
2481
0
                                              static_cast<int>(nBandSpace),
2482
0
                                              nBandCount);
2483
0
                            }
2484
0
                            pabyLocalData += nPixelSpace;
2485
0
                        }
2486
0
                    }
2487
0
                }
2488
0
            }
2489
0
        }
2490
0
        else  // Contig, striped organized.
2491
0
        {
2492
0
            GByte *pabyData = static_cast<GByte *>(pData);
2493
0
            for (int y = 0; y < nBufYSize; ++y)
2494
0
            {
2495
0
                const int nSrcLine =
2496
0
                    nYOff + static_cast<int>((y + 0.5) * dfSrcYInc);
2497
0
                const int m_nBlockYOff = nSrcLine / m_nBlockYSize;
2498
0
                const int nYOffsetIm_nBlock = nSrcLine % m_nBlockYSize;
2499
0
                const int nBlockId = m_nBlockYOff;
2500
0
                const toff_t nCurOffset = panOffsets[nBlockId];
2501
0
                if (nCurOffset == 0)
2502
0
                {
2503
0
                    REACHED(7);
2504
0
                    for (int x = 0; x < nBufXSize; ++x)
2505
0
                    {
2506
0
                        GDALCopyWords(
2507
0
                            &dfNoData, GDT_Float64, 0,
2508
0
                            pabyData + y * nLineSpace + x * nPixelSpace,
2509
0
                            eBufType, static_cast<int>(nBandSpace), nBandCount);
2510
0
                    }
2511
0
                }
2512
0
                else
2513
0
                {
2514
0
                    GByte *pabyLocalData = pabyData + y * nLineSpace;
2515
0
                    const int nBaseByteOffsetIm_nBlock =
2516
0
                        (nYOffsetIm_nBlock * m_nBlockXSize + nXOff) *
2517
0
                        nBandsPerBlockDTSize;
2518
2519
0
                    if (bNoXResamplingNoTypeChange && nBands == nBandCount &&
2520
0
                        nPixelSpace == nBandsPerBlockDTSize)
2521
0
                    {
2522
0
                        REACHED(8);
2523
0
                        if (!oFetcher.FetchBytes(
2524
0
                                pabyLocalData,
2525
0
                                nCurOffset + nBaseByteOffsetIm_nBlock,
2526
0
                                nXSize * nBandsPerBlock, nDTSize,
2527
0
                                bIsByteSwapped, bIsComplex, nBlockId))
2528
0
                        {
2529
0
                            return CE_Failure;
2530
0
                        }
2531
0
                    }
2532
0
                    else
2533
0
                    {
2534
0
                        const GByte *pabyLocalSrcData = oFetcher.FetchBytes(
2535
0
                            nCurOffset + nBaseByteOffsetIm_nBlock,
2536
0
                            nXSize * nBandsPerBlock, nDTSize, bIsByteSwapped,
2537
0
                            bIsComplex, nBlockId);
2538
0
                        if (pabyLocalSrcData == nullptr)
2539
0
                            return CE_Failure;
2540
2541
0
                        if (bByteNoXResampling)
2542
0
                        {
2543
0
                            REACHED(9);
2544
0
                            CopyContigByteMultiBand(
2545
0
                                pabyLocalSrcData, nBandsPerBlockDTSize,
2546
0
                                pabyLocalData, static_cast<int>(nPixelSpace),
2547
0
                                nBufXSize, nBandCount);
2548
0
                        }
2549
0
                        else if (bByteOnly)
2550
0
                        {
2551
0
                            REACHED(10);
2552
0
                            double dfSrcX = 0.5 * dfSrcXInc;
2553
0
                            for (int x = 0; x < nBufXSize;
2554
0
                                 ++x, dfSrcX += dfSrcXInc)
2555
0
                            {
2556
0
                                const int nSrcPixelMinusXOff =
2557
0
                                    static_cast<int>(dfSrcX);
2558
0
                                for (int iBand = 0; iBand < nBandCount; ++iBand)
2559
0
                                {
2560
0
                                    pabyLocalData[x * nPixelSpace + iBand] =
2561
0
                                        pabyLocalSrcData
2562
0
                                            [nSrcPixelMinusXOff *
2563
0
                                                 nBandsPerBlockDTSize +
2564
0
                                             iBand];
2565
0
                                }
2566
0
                            }
2567
0
                        }
2568
0
                        else
2569
0
                        {
2570
0
                            REACHED(11);
2571
0
                            double dfSrcX = 0.5 * dfSrcXInc;
2572
0
                            for (int x = 0; x < nBufXSize;
2573
0
                                 ++x, dfSrcX += dfSrcXInc)
2574
0
                            {
2575
0
                                int nSrcPixelMinusXOff =
2576
0
                                    static_cast<int>(dfSrcX);
2577
0
                                GDALCopyWords(
2578
0
                                    pabyLocalSrcData + nSrcPixelMinusXOff *
2579
0
                                                           nBandsPerBlockDTSize,
2580
0
                                    eDataType, nDTSize,
2581
0
                                    pabyLocalData + x * nPixelSpace, eBufType,
2582
0
                                    static_cast<int>(nBandSpace), nBandCount);
2583
0
                            }
2584
0
                        }
2585
0
                    }
2586
0
                }
2587
0
            }
2588
0
        }
2589
0
    }
2590
0
    else  // Non-contig reading case.
2591
0
    {
2592
        // cppcheck-suppress knownConditionTrueFalse
2593
0
        if (!FetchBuffer::bMinimizeIO && TIFFIsTiled(m_hTIFF))
2594
0
        {
2595
0
            for (int iBand = 0; iBand < nBandCount; ++iBand)
2596
0
            {
2597
0
                const int nBand = panBandMap[iBand];
2598
0
                auto poCurBand =
2599
0
                    cpl::down_cast<GTiffRasterBand *>(GetRasterBand(nBand));
2600
0
                GByte *const pabyData =
2601
0
                    static_cast<GByte *>(pData) + iBand * nBandSpace;
2602
0
                for (int y = 0; y < nBufYSize; ++y)
2603
0
                {
2604
0
                    const int nSrcLine =
2605
0
                        nYOff + static_cast<int>((y + 0.5) * dfSrcYInc);
2606
0
                    const int m_nBlockYOff = nSrcLine / m_nBlockYSize;
2607
0
                    const int nYOffsetIm_nBlock = nSrcLine % m_nBlockYSize;
2608
2609
0
                    int nBaseByteOffsetIm_nBlock = nYOffsetIm_nBlock *
2610
0
                                                   m_nBlockXSize *
2611
0
                                                   nBandsPerBlockDTSize;
2612
0
                    if (m_nPlanarConfig == PLANARCONFIG_CONTIG)
2613
0
                    {
2614
0
                        REACHED(12);
2615
0
                        nBaseByteOffsetIm_nBlock += (nBand - 1) * nDTSize;
2616
0
                    }
2617
0
                    else
2618
0
                    {
2619
0
                        REACHED(13);
2620
0
                    }
2621
2622
0
                    if (bNoXResampling)
2623
0
                    {
2624
0
                        GByte *pabyLocalData = pabyData + y * nLineSpace;
2625
0
                        int nBlockXOff = nXOff / m_nBlockXSize;
2626
0
                        int nBlockId =
2627
0
                            poCurBand->ComputeBlockId(nBlockXOff, m_nBlockYOff);
2628
0
                        int nXOffsetInBlock = nXOff % m_nBlockXSize;
2629
2630
0
                        int x = 0;
2631
0
                        while (x < nBufXSize)
2632
0
                        {
2633
0
                            const int nByteOffsetIm_nBlock =
2634
0
                                nBaseByteOffsetIm_nBlock +
2635
0
                                nXOffsetInBlock * nBandsPerBlockDTSize;
2636
0
                            const toff_t nCurOffset = panOffsets[nBlockId];
2637
0
                            const int nUsedBlockWidth = std::min(
2638
0
                                m_nBlockXSize - nXOffsetInBlock, nBufXSize - x);
2639
0
                            int nIters = nUsedBlockWidth;
2640
2641
0
                            if (nCurOffset == 0)
2642
0
                            {
2643
0
                                REACHED(16);
2644
0
                                GDALCopyWords(&dfNoData, GDT_Float64, 0,
2645
0
                                              pabyLocalData, eBufType,
2646
0
                                              static_cast<int>(nPixelSpace),
2647
0
                                              nIters);
2648
0
                                pabyLocalData += nIters * nPixelSpace;
2649
0
                            }
2650
0
                            else
2651
0
                            {
2652
0
                                if (bNoTypeChange &&
2653
0
                                    nPixelSpace == nBandsPerBlockDTSize)
2654
0
                                {
2655
0
                                    REACHED(17);
2656
0
                                    if (!oFetcher.FetchBytes(
2657
0
                                            pabyLocalData,
2658
0
                                            nCurOffset + nByteOffsetIm_nBlock,
2659
0
                                            (nIters - 1) * nBandsPerBlock + 1,
2660
0
                                            nDTSize, bIsByteSwapped, bIsComplex,
2661
0
                                            nBlockId))
2662
0
                                    {
2663
0
                                        return CE_Failure;
2664
0
                                    }
2665
0
                                    pabyLocalData += nIters * nPixelSpace;
2666
0
                                }
2667
0
                                else
2668
0
                                {
2669
0
                                    const GByte *pabyLocalSrcData =
2670
0
                                        oFetcher.FetchBytes(
2671
0
                                            nCurOffset + nByteOffsetIm_nBlock,
2672
0
                                            (nIters - 1) * nBandsPerBlock + 1,
2673
0
                                            nDTSize, bIsByteSwapped, bIsComplex,
2674
0
                                            nBlockId);
2675
0
                                    if (pabyLocalSrcData == nullptr)
2676
0
                                        return CE_Failure;
2677
2678
0
                                    REACHED(18);
2679
0
                                    GDALCopyWords(pabyLocalSrcData, eDataType,
2680
0
                                                  nBandsPerBlockDTSize,
2681
0
                                                  pabyLocalData, eBufType,
2682
0
                                                  static_cast<int>(nPixelSpace),
2683
0
                                                  nIters);
2684
0
                                    pabyLocalData += nIters * nPixelSpace;
2685
0
                                }
2686
0
                            }
2687
2688
0
                            nXOffsetInBlock = 0;
2689
0
                            ++nBlockXOff;
2690
0
                            ++nBlockId;
2691
0
                            x += nUsedBlockWidth;
2692
0
                        }
2693
0
                    }
2694
0
                    else
2695
0
                    {
2696
                        // Non-contig reading, tiled, potential resampling and
2697
                        // data type change.
2698
2699
0
                        const GByte *pabyLocalSrcDataStartLine = nullptr;
2700
0
                        GByte *pabyLocalData = pabyData + y * nLineSpace;
2701
0
                        double dfSrcX = nXOff + 0.5 * dfSrcXInc;
2702
0
                        int nCurBlockXOff = 0;
2703
0
                        int nNextBlockXOff = 0;
2704
0
                        toff_t nCurOffset = 0;
2705
0
                        for (int x = 0; x < nBufXSize; ++x, dfSrcX += dfSrcXInc)
2706
0
                        {
2707
0
                            const int nSrcPixel = static_cast<int>(dfSrcX);
2708
0
                            if (nSrcPixel >= nNextBlockXOff)
2709
0
                            {
2710
0
                                const int nBlockXOff =
2711
0
                                    nSrcPixel / m_nBlockXSize;
2712
0
                                nCurBlockXOff = nBlockXOff * m_nBlockXSize;
2713
0
                                nNextBlockXOff = nCurBlockXOff + m_nBlockXSize;
2714
0
                                const int nBlockId = poCurBand->ComputeBlockId(
2715
0
                                    nBlockXOff, m_nBlockYOff);
2716
0
                                nCurOffset = panOffsets[nBlockId];
2717
0
                                if (nCurOffset != 0)
2718
0
                                {
2719
0
                                    pabyLocalSrcDataStartLine =
2720
0
                                        oFetcher.FetchBytes(
2721
0
                                            nCurOffset +
2722
0
                                                nBaseByteOffsetIm_nBlock,
2723
0
                                            m_nBlockXSize * nBandsPerBlock,
2724
0
                                            nDTSize, bIsByteSwapped, bIsComplex,
2725
0
                                            nBlockId);
2726
0
                                    if (pabyLocalSrcDataStartLine == nullptr)
2727
0
                                        return CE_Failure;
2728
0
                                }
2729
0
                            }
2730
0
                            const int nXOffsetInBlock =
2731
0
                                nSrcPixel - nCurBlockXOff;
2732
2733
0
                            if (nCurOffset == 0)
2734
0
                            {
2735
0
                                REACHED(21);
2736
0
                                GDALCopyWords(&dfNoData, GDT_Float64, 0,
2737
0
                                              pabyLocalData, eBufType, 0, 1);
2738
0
                                pabyLocalData += nPixelSpace;
2739
0
                            }
2740
0
                            else
2741
0
                            {
2742
0
                                const GByte *pabyLocalSrcData =
2743
0
                                    pabyLocalSrcDataStartLine +
2744
0
                                    nXOffsetInBlock * nBandsPerBlockDTSize;
2745
2746
0
                                REACHED(22);
2747
0
                                if (bByteOnly)
2748
0
                                {
2749
0
                                    *pabyLocalData = *pabyLocalSrcData;
2750
0
                                }
2751
0
                                else
2752
0
                                {
2753
0
                                    GDALCopyWords(pabyLocalSrcData, eDataType,
2754
0
                                                  0, pabyLocalData, eBufType, 0,
2755
0
                                                  1);
2756
0
                                }
2757
0
                                pabyLocalData += nPixelSpace;
2758
0
                            }
2759
0
                        }
2760
0
                    }
2761
0
                }
2762
0
            }
2763
0
        }
2764
0
        else  // Non-contig reading, striped.
2765
0
        {
2766
0
            for (int iBand = 0; iBand < nBandCount; ++iBand)
2767
0
            {
2768
0
                const int nBand = panBandMap[iBand];
2769
0
                GByte *pabyData =
2770
0
                    static_cast<GByte *>(pData) + iBand * nBandSpace;
2771
0
                for (int y = 0; y < nBufYSize; ++y)
2772
0
                {
2773
0
                    const int nSrcLine =
2774
0
                        nYOff + static_cast<int>((y + 0.5) * dfSrcYInc);
2775
0
                    const int m_nBlockYOff = nSrcLine / m_nBlockYSize;
2776
0
                    const int nYOffsetIm_nBlock = nSrcLine % m_nBlockYSize;
2777
0
                    int nBlockId = m_nBlockYOff;
2778
0
                    if (m_nPlanarConfig == PLANARCONFIG_SEPARATE)
2779
0
                    {
2780
0
                        REACHED(23);
2781
0
                        nBlockId += m_nBlocksPerBand * (nBand - 1);
2782
0
                    }
2783
0
                    else
2784
0
                    {
2785
0
                        REACHED(24);
2786
0
                    }
2787
0
                    const toff_t nCurOffset = panOffsets[nBlockId];
2788
0
                    if (nCurOffset == 0)
2789
0
                    {
2790
0
                        REACHED(25);
2791
0
                        GDALCopyWords(&dfNoData, GDT_Float64, 0,
2792
0
                                      pabyData + y * nLineSpace, eBufType,
2793
0
                                      static_cast<int>(nPixelSpace), nBufXSize);
2794
0
                    }
2795
0
                    else
2796
0
                    {
2797
0
                        int nBaseByteOffsetIm_nBlock =
2798
0
                            (nYOffsetIm_nBlock * m_nBlockXSize + nXOff) *
2799
0
                            nBandsPerBlockDTSize;
2800
0
                        if (m_nPlanarConfig == PLANARCONFIG_CONTIG)
2801
0
                            nBaseByteOffsetIm_nBlock += (nBand - 1) * nDTSize;
2802
2803
0
                        GByte *pabyLocalData = pabyData + y * nLineSpace;
2804
0
                        if (bNoXResamplingNoTypeChange &&
2805
0
                            nPixelSpace == nBandsPerBlockDTSize)
2806
0
                        {
2807
0
                            REACHED(26);
2808
0
                            if (!oFetcher.FetchBytes(
2809
0
                                    pabyLocalData,
2810
0
                                    nCurOffset + nBaseByteOffsetIm_nBlock,
2811
0
                                    (nXSize - 1) * nBandsPerBlock + 1, nDTSize,
2812
0
                                    bIsByteSwapped, bIsComplex, nBlockId))
2813
0
                            {
2814
0
                                return CE_Failure;
2815
0
                            }
2816
0
                        }
2817
0
                        else
2818
0
                        {
2819
0
                            const GByte *pabyLocalSrcData = oFetcher.FetchBytes(
2820
0
                                nCurOffset + nBaseByteOffsetIm_nBlock,
2821
0
                                (nXSize - 1) * nBandsPerBlock + 1, nDTSize,
2822
0
                                bIsByteSwapped, bIsComplex, nBlockId);
2823
0
                            if (pabyLocalSrcData == nullptr)
2824
0
                                return CE_Failure;
2825
2826
0
                            if (bNoXResamplingNoTypeChange)
2827
0
                            {
2828
0
                                REACHED(27);
2829
0
                                GDALCopyWords(pabyLocalSrcData, eDataType,
2830
0
                                              nBandsPerBlockDTSize,
2831
0
                                              pabyLocalData, eBufType,
2832
0
                                              static_cast<int>(nPixelSpace),
2833
0
                                              nBufXSize);
2834
0
                            }
2835
0
                            else if (bByteOnly)
2836
0
                            {
2837
0
                                REACHED(28);
2838
0
                                double dfSrcX = 0.5 * dfSrcXInc;
2839
0
                                for (int x = 0; x < nBufXSize;
2840
0
                                     ++x, dfSrcX += dfSrcXInc)
2841
0
                                {
2842
0
                                    const int nSrcPixelMinusXOff =
2843
0
                                        static_cast<int>(dfSrcX);
2844
0
                                    pabyLocalData[x * nPixelSpace] =
2845
0
                                        pabyLocalSrcData[nSrcPixelMinusXOff *
2846
0
                                                         nBandsPerBlockDTSize];
2847
0
                                }
2848
0
                            }
2849
0
                            else
2850
0
                            {
2851
0
                                REACHED(29);
2852
0
                                double dfSrcX = 0.5 * dfSrcXInc;
2853
0
                                for (int x = 0; x < nBufXSize;
2854
0
                                     ++x, dfSrcX += dfSrcXInc)
2855
0
                                {
2856
0
                                    const int nSrcPixelMinusXOff =
2857
0
                                        static_cast<int>(dfSrcX);
2858
0
                                    GDALCopyWords(pabyLocalSrcData +
2859
0
                                                      nSrcPixelMinusXOff *
2860
0
                                                          nBandsPerBlockDTSize,
2861
0
                                                  eDataType, 0,
2862
0
                                                  pabyLocalData +
2863
0
                                                      x * nPixelSpace,
2864
0
                                                  eBufType, 0, 1);
2865
0
                                }
2866
0
                            }
2867
0
                        }
2868
0
                    }
2869
0
                }
2870
0
            }
2871
0
        }
2872
0
    }
2873
2874
0
    return CE_None;
2875
0
}
Unexecuted instantiation: CPLErr GTiffDataset::CommonDirectIO<FetchBufferVirtualMemIO>(FetchBufferVirtualMemIO&, int, int, int, int, void*, int, int, GDALDataType, int, int const*, long long, long long, long long)
Unexecuted instantiation: CPLErr GTiffDataset::CommonDirectIO<FetchBufferDirectIO>(FetchBufferDirectIO&, int, int, int, int, void*, int, int, GDALDataType, int, int const*, long long, long long, long long)
2876
2877
/************************************************************************/
2878
/*                              DirectIO()                              */
2879
/************************************************************************/
2880
2881
CPLErr GTiffDataset::CommonDirectIOClassic(
2882
    FetchBufferDirectIO &oFetcher, int nXOff, int nYOff, int nXSize, int nYSize,
2883
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
2884
    int nBandCount, const int *panBandMap, GSpacing nPixelSpace,
2885
    GSpacing nLineSpace, GSpacing nBandSpace)
2886
0
{
2887
0
    return CommonDirectIO<FetchBufferDirectIO>(
2888
0
        oFetcher, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2889
0
        eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace);
2890
0
}
2891
2892
/************************************************************************/
2893
/*                              DirectIO()                              */
2894
/************************************************************************/
2895
2896
// Reads directly bytes from the file using ReadMultiRange(), and by-pass
2897
// block reading. Restricted to simple TIFF configurations
2898
// (uncompressed data, standard data types). Particularly useful to extract
2899
// sub-windows of data on a large /vsicurl dataset).
2900
// Returns -1 if DirectIO() can't be supported on that file.
2901
2902
int GTiffDataset::DirectIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
2903
                           int nYSize, void *pData, int nBufXSize,
2904
                           int nBufYSize, GDALDataType eBufType, int nBandCount,
2905
                           const int *panBandMap, GSpacing nPixelSpace,
2906
                           GSpacing nLineSpace, GSpacing nBandSpace,
2907
                           GDALRasterIOExtraArg *psExtraArg)
2908
0
{
2909
0
    auto poProtoBand = cpl::down_cast<GTiffRasterBand *>(papoBands[0]);
2910
0
    const GDALDataType eDataType = poProtoBand->GetRasterDataType();
2911
0
    const int nDTSizeBits = GDALGetDataTypeSizeBits(eDataType);
2912
0
    if (!(eRWFlag == GF_Read && m_nCompression == COMPRESSION_NONE &&
2913
0
          (m_nPhotometric == PHOTOMETRIC_MINISBLACK ||
2914
0
           m_nPhotometric == PHOTOMETRIC_RGB ||
2915
0
           m_nPhotometric == PHOTOMETRIC_PALETTE) &&
2916
0
          poProtoBand->IsBaseGTiffClass()))
2917
0
    {
2918
0
        return -1;
2919
0
    }
2920
0
    Crystalize();
2921
2922
    // Only know how to deal with nearest neighbour in this optimized routine.
2923
0
    if ((nXSize != nBufXSize || nYSize != nBufYSize) && psExtraArg != nullptr &&
2924
0
        psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
2925
0
    {
2926
0
        return -1;
2927
0
    }
2928
2929
    // If the file is band interleave or only one band is requested, then
2930
    // fallback to band DirectIO.
2931
0
    bool bUseBandRasterIO = false;
2932
0
    if (m_nPlanarConfig == PLANARCONFIG_SEPARATE || nBandCount == 1)
2933
0
    {
2934
0
        bUseBandRasterIO = true;
2935
0
    }
2936
0
    else
2937
0
    {
2938
        // For simplicity, only deals with "naturally ordered" bands.
2939
0
        for (int iBand = 0; iBand < nBandCount; ++iBand)
2940
0
        {
2941
0
            if (panBandMap[iBand] != iBand + 1)
2942
0
            {
2943
0
                bUseBandRasterIO = true;
2944
0
                break;
2945
0
            }
2946
0
        }
2947
0
    }
2948
0
    if (bUseBandRasterIO)
2949
0
    {
2950
0
        CPLErr eErr = CE_None;
2951
0
        for (int iBand = 0; eErr == CE_None && iBand < nBandCount; ++iBand)
2952
0
        {
2953
0
            eErr =
2954
0
                GetRasterBand(panBandMap[iBand])
2955
0
                    ->RasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2956
0
                               static_cast<GByte *>(pData) + iBand * nBandSpace,
2957
0
                               nBufXSize, nBufYSize, eBufType, nPixelSpace,
2958
0
                               nLineSpace, psExtraArg);
2959
0
        }
2960
0
        return eErr;
2961
0
    }
2962
2963
#if DEBUG_VERBOSE
2964
    CPLDebug("GTiff", "DirectIO(%d,%d,%d,%d -> %dx%d)", nXOff, nYOff, nXSize,
2965
             nYSize, nBufXSize, nBufYSize);
2966
#endif
2967
2968
    // No need to look if overviews can satisfy the request as it has already */
2969
    // been done in GTiffDataset::IRasterIO().
2970
2971
    // Make sure that TIFFTAG_STRIPOFFSETS is up-to-date.
2972
0
    if (eAccess == GA_Update)
2973
0
    {
2974
0
        FlushCache(false);
2975
0
        VSI_TIFFFlushBufferedWrite(TIFFClientdata(m_hTIFF));
2976
0
    }
2977
2978
0
    if (TIFFIsTiled(m_hTIFF))
2979
0
    {
2980
0
        const int nDTSize = nDTSizeBits / 8;
2981
0
        const size_t nTempBufferForCommonDirectIOSize = static_cast<size_t>(
2982
0
            static_cast<GPtrDiff_t>(m_nBlockXSize) * m_nBlockYSize * nDTSize *
2983
0
            ((m_nPlanarConfig == PLANARCONFIG_CONTIG) ? nBands : 1));
2984
0
        if (m_pTempBufferForCommonDirectIO == nullptr)
2985
0
        {
2986
0
            m_pTempBufferForCommonDirectIO = static_cast<GByte *>(
2987
0
                VSI_MALLOC_VERBOSE(nTempBufferForCommonDirectIOSize));
2988
0
            if (m_pTempBufferForCommonDirectIO == nullptr)
2989
0
                return CE_Failure;
2990
0
        }
2991
2992
0
        VSILFILE *fp = VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
2993
0
        FetchBufferDirectIO oFetcher(fp, m_pTempBufferForCommonDirectIO,
2994
0
                                     nTempBufferForCommonDirectIOSize);
2995
2996
0
        return CommonDirectIOClassic(oFetcher, nXOff, nYOff, nXSize, nYSize,
2997
0
                                     pData, nBufXSize, nBufYSize, eBufType,
2998
0
                                     nBandCount, panBandMap, nPixelSpace,
2999
0
                                     nLineSpace, nBandSpace);
3000
0
    }
3001
3002
    // Get strip offsets.
3003
0
    toff_t *panTIFFOffsets = nullptr;
3004
0
    if (!TIFFGetField(m_hTIFF, TIFFTAG_STRIPOFFSETS, &panTIFFOffsets) ||
3005
0
        panTIFFOffsets == nullptr)
3006
0
    {
3007
0
        return CE_Failure;
3008
0
    }
3009
3010
    // Sub-sampling or over-sampling can only be done at last stage.
3011
0
    int nReqXSize = nXSize;
3012
    // Can do sub-sampling at the extraction stage.
3013
0
    const int nReqYSize = std::min(nBufYSize, nYSize);
3014
0
    void **ppData =
3015
0
        static_cast<void **>(VSI_MALLOC_VERBOSE(nReqYSize * sizeof(void *)));
3016
0
    vsi_l_offset *panOffsets = static_cast<vsi_l_offset *>(
3017
0
        VSI_MALLOC_VERBOSE(nReqYSize * sizeof(vsi_l_offset)));
3018
0
    size_t *panSizes =
3019
0
        static_cast<size_t *>(VSI_MALLOC_VERBOSE(nReqYSize * sizeof(size_t)));
3020
0
    const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
3021
0
    void *pTmpBuffer = nullptr;
3022
0
    int eErr = CE_None;
3023
0
    int nContigBands = nBands;
3024
0
    const int nSrcPixelSize = nDTSize * nContigBands;
3025
3026
0
    if (ppData == nullptr || panOffsets == nullptr || panSizes == nullptr)
3027
0
    {
3028
0
        eErr = CE_Failure;
3029
0
    }
3030
    // For now we always allocate a temp buffer as it is easier.
3031
0
    else
3032
    // if( nXSize != nBufXSize || nYSize != nBufYSize ||
3033
    //   eBufType != eDataType ||
3034
    //   nPixelSpace != GDALGetDataTypeSizeBytes(eBufType) ||
3035
    //   check if the user buffer is large enough )
3036
0
    {
3037
        // We need a temporary buffer for over-sampling/sub-sampling
3038
        // and/or data type conversion.
3039
0
        pTmpBuffer = VSI_MALLOC3_VERBOSE(nReqXSize, nReqYSize, nSrcPixelSize);
3040
0
        if (pTmpBuffer == nullptr)
3041
0
            eErr = CE_Failure;
3042
0
    }
3043
3044
    // Prepare data extraction.
3045
0
    const double dfSrcYInc = nYSize / static_cast<double>(nBufYSize);
3046
3047
0
    for (int iLine = 0; eErr == CE_None && iLine < nReqYSize; ++iLine)
3048
0
    {
3049
0
        ppData[iLine] = static_cast<GByte *>(pTmpBuffer) +
3050
0
                        static_cast<size_t>(iLine) * nReqXSize * nSrcPixelSize;
3051
0
        int nSrcLine = 0;
3052
0
        if (nBufYSize < nYSize)  // Sub-sampling in y.
3053
0
            nSrcLine = nYOff + static_cast<int>((iLine + 0.5) * dfSrcYInc);
3054
0
        else
3055
0
            nSrcLine = nYOff + iLine;
3056
3057
0
        const int nBlockXOff = 0;
3058
0
        const int nBlockYOff = nSrcLine / m_nBlockYSize;
3059
0
        const int nYOffsetInBlock = nSrcLine % m_nBlockYSize;
3060
0
        const int nBlockId =
3061
0
            poProtoBand->ComputeBlockId(nBlockXOff, nBlockYOff);
3062
3063
0
        panOffsets[iLine] = panTIFFOffsets[nBlockId];
3064
0
        if (panOffsets[iLine] == 0)  // We don't support sparse files.
3065
0
            eErr = -1;
3066
3067
0
        panOffsets[iLine] +=
3068
0
            (nXOff +
3069
0
             static_cast<vsi_l_offset>(nYOffsetInBlock) * m_nBlockXSize) *
3070
0
            nSrcPixelSize;
3071
0
        panSizes[iLine] = static_cast<size_t>(nReqXSize) * nSrcPixelSize;
3072
0
    }
3073
3074
    // Extract data from the file.
3075
0
    if (eErr == CE_None)
3076
0
    {
3077
0
        VSILFILE *fp = VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
3078
0
        const int nRet =
3079
0
            VSIFReadMultiRangeL(nReqYSize, ppData, panOffsets, panSizes, fp);
3080
0
        if (nRet != 0)
3081
0
            eErr = CE_Failure;
3082
0
    }
3083
3084
    // Byte-swap if necessary.
3085
0
    if (eErr == CE_None && TIFFIsByteSwapped(m_hTIFF))
3086
0
    {
3087
0
        for (int iLine = 0; iLine < nReqYSize; ++iLine)
3088
0
        {
3089
0
            if (GDALDataTypeIsComplex(eDataType))
3090
0
                GDALSwapWords(ppData[iLine], nDTSize / 2,
3091
0
                              2 * nReqXSize * nContigBands, nDTSize / 2);
3092
0
            else
3093
0
                GDALSwapWords(ppData[iLine], nDTSize, nReqXSize * nContigBands,
3094
0
                              nDTSize);
3095
0
        }
3096
0
    }
3097
3098
    // Over-sampling/sub-sampling and/or data type conversion.
3099
0
    const double dfSrcXInc = nXSize / static_cast<double>(nBufXSize);
3100
0
    if (eErr == CE_None && pTmpBuffer != nullptr)
3101
0
    {
3102
0
        for (int iY = 0; iY < nBufYSize; ++iY)
3103
0
        {
3104
0
            const int iSrcY = nBufYSize <= nYSize
3105
0
                                  ? iY
3106
0
                                  : static_cast<int>((iY + 0.5) * dfSrcYInc);
3107
            // Optimization: no resampling, no data type change, number of
3108
            // bands requested == number of bands and buffer is packed
3109
            // pixel-interleaved.
3110
0
            if (nBufXSize == nXSize && nContigBands == nBandCount &&
3111
0
                eDataType == eBufType && nBandSpace == nDTSize &&
3112
0
                nPixelSpace == nBandCount * nBandSpace)
3113
0
            {
3114
0
                memcpy(static_cast<GByte *>(pData) + iY * nLineSpace,
3115
0
                       ppData[iSrcY],
3116
0
                       static_cast<size_t>(nReqXSize * nPixelSpace));
3117
0
            }
3118
            // Other optimization: no resampling, no data type change,
3119
            // data type is Byte/Int8.
3120
0
            else if (nBufXSize == nXSize && eDataType == eBufType &&
3121
0
                     (eDataType == GDT_UInt8 || eDataType == GDT_Int8))
3122
0
            {
3123
0
                GByte *pabySrcData = static_cast<GByte *>(ppData[iSrcY]);
3124
0
                GByte *pabyDstData =
3125
0
                    static_cast<GByte *>(pData) + iY * nLineSpace;
3126
0
                if (nBandSpace == 1 && nPixelSpace > nBandCount)
3127
0
                {
3128
                    // Buffer is pixel-interleaved (with some stridding
3129
                    // between pixels).
3130
0
                    CopyContigByteMultiBand(
3131
0
                        pabySrcData, nSrcPixelSize, pabyDstData,
3132
0
                        static_cast<int>(nPixelSpace), nBufXSize, nBandCount);
3133
0
                }
3134
0
                else
3135
0
                {
3136
0
                    for (int iBand = 0; iBand < nBandCount; ++iBand)
3137
0
                    {
3138
0
                        GDALCopyWords(
3139
0
                            pabySrcData + iBand, GDT_UInt8, nSrcPixelSize,
3140
0
                            pabyDstData + iBand * nBandSpace, GDT_UInt8,
3141
0
                            static_cast<int>(nPixelSpace), nBufXSize);
3142
0
                    }
3143
0
                }
3144
0
            }
3145
0
            else  // General case.
3146
0
            {
3147
0
                for (int iBand = 0; iBand < nBandCount; ++iBand)
3148
0
                {
3149
0
                    GByte *pabySrcData =
3150
0
                        static_cast<GByte *>(ppData[iSrcY]) + iBand * nDTSize;
3151
0
                    GByte *pabyDstData = static_cast<GByte *>(pData) +
3152
0
                                         iBand * nBandSpace + iY * nLineSpace;
3153
0
                    if ((eDataType == GDT_UInt8 && eBufType == GDT_UInt8) ||
3154
0
                        (eDataType == GDT_Int8 && eBufType == GDT_Int8))
3155
0
                    {
3156
0
                        double dfSrcX = 0.5 * dfSrcXInc;
3157
0
                        for (int iX = 0; iX < nBufXSize;
3158
0
                             ++iX, dfSrcX += dfSrcXInc)
3159
0
                        {
3160
0
                            int iSrcX = static_cast<int>(dfSrcX);
3161
0
                            pabyDstData[iX * nPixelSpace] =
3162
0
                                pabySrcData[iSrcX * nSrcPixelSize];
3163
0
                        }
3164
0
                    }
3165
0
                    else
3166
0
                    {
3167
0
                        double dfSrcX = 0.5 * dfSrcXInc;
3168
0
                        for (int iX = 0; iX < nBufXSize;
3169
0
                             ++iX, dfSrcX += dfSrcXInc)
3170
0
                        {
3171
0
                            int iSrcX = static_cast<int>(dfSrcX);
3172
0
                            GDALCopyWords(pabySrcData + iSrcX * nSrcPixelSize,
3173
0
                                          eDataType, 0,
3174
0
                                          pabyDstData + iX * nPixelSpace,
3175
0
                                          eBufType, 0, 1);
3176
0
                        }
3177
0
                    }
3178
0
                }
3179
0
            }
3180
0
        }
3181
0
    }
3182
3183
0
    CPLFree(pTmpBuffer);
3184
0
    CPLFree(ppData);
3185
0
    CPLFree(panOffsets);
3186
0
    CPLFree(panSizes);
3187
3188
0
    return eErr;
3189
0
}
3190
3191
/************************************************************************/
3192
/*                             ReadStrile()                             */
3193
/************************************************************************/
3194
3195
bool GTiffDataset::ReadStrile(int nBlockId, void *pOutputBuffer,
3196
                              GPtrDiff_t nBlockReqSize)
3197
0
{
3198
    // Optimization by which we can save some libtiff buffer copy
3199
0
    std::pair<vsi_l_offset, vsi_l_offset> oPair;
3200
0
    if (
3201
#if TIFFLIB_VERSION <= 20220520 && !defined(INTERNAL_LIBTIFF)
3202
        // There's a bug, up to libtiff 4.4.0, in TIFFReadFromUserBuffer()
3203
        // which clears the TIFF_CODERSETUP flag of tif->tif_flags, which
3204
        // causes the codec SetupDecode method to be called for each strile,
3205
        // whereas it should normally be called only for the first decoded one.
3206
        // For JPEG, that causes TIFFjpeg_read_header() to be called. Most
3207
        // of the time, that works. But for some files, at some point, the
3208
        // libjpeg machinery is not in the appropriate state for that.
3209
        m_nCompression != COMPRESSION_JPEG &&
3210
#endif
3211
0
        m_oCacheStrileToOffsetByteCount.tryGet(nBlockId, oPair))
3212
0
    {
3213
        // For the mask, use the parent TIFF handle to get cached ranges
3214
0
        auto th = TIFFClientdata(m_poImageryDS && m_bMaskInterleavedWithImagery
3215
0
                                     ? m_poImageryDS->m_hTIFF
3216
0
                                     : m_hTIFF);
3217
0
        void *pInputBuffer = VSI_TIFFGetCachedRange(
3218
0
            th, oPair.first, static_cast<size_t>(oPair.second));
3219
0
        if (pInputBuffer &&
3220
0
            TIFFReadFromUserBuffer(m_hTIFF, nBlockId, pInputBuffer,
3221
0
                                   static_cast<size_t>(oPair.second),
3222
0
                                   pOutputBuffer, nBlockReqSize))
3223
0
        {
3224
0
            return true;
3225
0
        }
3226
0
    }
3227
3228
    // For debugging
3229
0
    if (m_poBaseDS)
3230
0
        m_poBaseDS->m_bHasUsedReadEncodedAPI = true;
3231
0
    else
3232
0
        m_bHasUsedReadEncodedAPI = true;
3233
3234
#if 0
3235
    // Can be useful to test TIFFReadFromUserBuffer() for local files
3236
    VSILFILE* fpTIF = VSI_TIFFGetVSILFile(TIFFClientdata( m_hTIFF ));
3237
    std::vector<GByte> tmp(TIFFGetStrileByteCount(m_hTIFF, nBlockId));
3238
    VSIFSeekL(fpTIF, TIFFGetStrileOffset(m_hTIFF, nBlockId), SEEK_SET);
3239
    VSIFReadL(&tmp[0], 1, TIFFGetStrileByteCount(m_hTIFF, nBlockId), fpTIF);
3240
    if( !TIFFReadFromUserBuffer( m_hTIFF, nBlockId,
3241
                                &tmp[0], tmp.size(),
3242
                                pOutputBuffer, nBlockReqSize ) )
3243
    {
3244
        return false;
3245
    }
3246
#else
3247
    // Set to 1 to allow GTiffErrorHandler to implement limitation on error
3248
    // messages
3249
0
    GTIFFGetThreadLocalLibtiffError() = 1;
3250
0
    if (TIFFIsTiled(m_hTIFF))
3251
0
    {
3252
0
        if (TIFFReadEncodedTile(m_hTIFF, nBlockId, pOutputBuffer,
3253
0
                                nBlockReqSize) == -1 &&
3254
0
            !m_bIgnoreReadErrors)
3255
0
        {
3256
0
            CPLError(CE_Failure, CPLE_AppDefined,
3257
0
                     "TIFFReadEncodedTile() failed.");
3258
0
            GTIFFGetThreadLocalLibtiffError() = 0;
3259
0
            return false;
3260
0
        }
3261
0
    }
3262
0
    else
3263
0
    {
3264
0
        if (TIFFReadEncodedStrip(m_hTIFF, nBlockId, pOutputBuffer,
3265
0
                                 nBlockReqSize) == -1 &&
3266
0
            !m_bIgnoreReadErrors)
3267
0
        {
3268
0
            CPLError(CE_Failure, CPLE_AppDefined,
3269
0
                     "TIFFReadEncodedStrip() failed.");
3270
0
            GTIFFGetThreadLocalLibtiffError() = 0;
3271
0
            return false;
3272
0
        }
3273
0
    }
3274
0
    GTIFFGetThreadLocalLibtiffError() = 0;
3275
0
#endif
3276
0
    return true;
3277
0
}
3278
3279
/************************************************************************/
3280
/*                            LoadBlockBuf()                            */
3281
/*                                                                      */
3282
/*      Load working block buffer with request block (tile/strip).      */
3283
/************************************************************************/
3284
3285
CPLErr GTiffDataset::LoadBlockBuf(int nBlockId, bool bReadFromDisk)
3286
3287
0
{
3288
0
    if (m_nLoadedBlock == nBlockId && m_pabyBlockBuf != nullptr)
3289
0
        return CE_None;
3290
3291
    /* -------------------------------------------------------------------- */
3292
    /*      If we have a dirty loaded block, flush it out first.            */
3293
    /* -------------------------------------------------------------------- */
3294
0
    if (m_nLoadedBlock != -1 && m_bLoadedBlockDirty)
3295
0
    {
3296
0
        const CPLErr eErr = FlushBlockBuf();
3297
0
        if (eErr != CE_None)
3298
0
            return eErr;
3299
0
    }
3300
3301
    /* -------------------------------------------------------------------- */
3302
    /*      Get block size.                                                 */
3303
    /* -------------------------------------------------------------------- */
3304
0
    const GPtrDiff_t nBlockBufSize = static_cast<GPtrDiff_t>(
3305
0
        TIFFIsTiled(m_hTIFF) ? TIFFTileSize(m_hTIFF) : TIFFStripSize(m_hTIFF));
3306
0
    if (!nBlockBufSize)
3307
0
    {
3308
0
        ReportError(CE_Failure, CPLE_AppDefined,
3309
0
                    "Bogus block size; unable to allocate a buffer.");
3310
0
        return CE_Failure;
3311
0
    }
3312
3313
    /* -------------------------------------------------------------------- */
3314
    /*      Allocate a temporary buffer for this strip.                     */
3315
    /* -------------------------------------------------------------------- */
3316
0
    if (m_pabyBlockBuf == nullptr)
3317
0
    {
3318
0
        m_pabyBlockBuf =
3319
0
            static_cast<GByte *>(VSI_CALLOC_VERBOSE(1, nBlockBufSize));
3320
0
        if (m_pabyBlockBuf == nullptr)
3321
0
        {
3322
0
            return CE_Failure;
3323
0
        }
3324
0
    }
3325
3326
0
    if (m_nLoadedBlock == nBlockId)
3327
0
        return CE_None;
3328
3329
    /* -------------------------------------------------------------------- */
3330
    /*  When called from ::IWriteBlock in separate cases (or in single band */
3331
    /*  geotiffs), the ::IWriteBlock will override the content of the buffer*/
3332
    /*  with pImage, so we don't need to read data from disk                */
3333
    /* -------------------------------------------------------------------- */
3334
0
    if (!bReadFromDisk || m_bStreamingOut)
3335
0
    {
3336
0
        m_nLoadedBlock = nBlockId;
3337
0
        return CE_None;
3338
0
    }
3339
3340
    // libtiff 3.X doesn't like mixing read&write of JPEG compressed blocks
3341
    // The below hack is necessary due to another hack that consist in
3342
    // writing zero block to force creation of JPEG tables.
3343
0
    if (nBlockId == 0 && m_bDontReloadFirstBlock)
3344
0
    {
3345
0
        m_bDontReloadFirstBlock = false;
3346
0
        memset(m_pabyBlockBuf, 0, nBlockBufSize);
3347
0
        m_nLoadedBlock = nBlockId;
3348
0
        return CE_None;
3349
0
    }
3350
3351
    /* -------------------------------------------------------------------- */
3352
    /*      The bottom most partial tiles and strips are sometimes only     */
3353
    /*      partially encoded.  This code reduces the requested data so     */
3354
    /*      an error won't be reported in this case. (#1179)                */
3355
    /*      We exclude tiled WEBP, because as it is a new codec, whole tiles*/
3356
    /*      are written by libtiff. This helps avoiding creating a temporary*/
3357
    /*      decode buffer.                                                  */
3358
    /* -------------------------------------------------------------------- */
3359
0
    auto nBlockReqSize = nBlockBufSize;
3360
0
    const int nBlockYOff = (nBlockId % m_nBlocksPerBand) / m_nBlocksPerRow;
3361
3362
0
    if (nBlockYOff * m_nBlockYSize > nRasterYSize - m_nBlockYSize &&
3363
0
        !(m_nCompression == COMPRESSION_WEBP && TIFFIsTiled(m_hTIFF)))
3364
0
    {
3365
0
        nBlockReqSize =
3366
0
            (nBlockBufSize / m_nBlockYSize) *
3367
0
            (m_nBlockYSize -
3368
0
             static_cast<int>(
3369
0
                 (static_cast<GIntBig>(nBlockYOff + 1) * m_nBlockYSize) %
3370
0
                 nRasterYSize));
3371
0
        memset(m_pabyBlockBuf, 0, nBlockBufSize);
3372
0
    }
3373
3374
    /* -------------------------------------------------------------------- */
3375
    /*      If we don't have this block already loaded, and we know it      */
3376
    /*      doesn't yet exist on disk, just zero the memory buffer and      */
3377
    /*      pretend we loaded it.                                           */
3378
    /* -------------------------------------------------------------------- */
3379
0
    bool bErrOccurred = false;
3380
0
    if (!IsBlockAvailable(nBlockId, nullptr, nullptr, &bErrOccurred))
3381
0
    {
3382
0
        memset(m_pabyBlockBuf, 0, nBlockBufSize);
3383
0
        m_nLoadedBlock = nBlockId;
3384
0
        if (bErrOccurred)
3385
0
            return CE_Failure;
3386
0
        return CE_None;
3387
0
    }
3388
3389
    /* -------------------------------------------------------------------- */
3390
    /*      Load the block, if it isn't our current block.                  */
3391
    /* -------------------------------------------------------------------- */
3392
0
    CPLErr eErr = CE_None;
3393
3394
0
    if (!ReadStrile(nBlockId, m_pabyBlockBuf, nBlockReqSize))
3395
0
    {
3396
0
        memset(m_pabyBlockBuf, 0, nBlockBufSize);
3397
0
        eErr = CE_Failure;
3398
0
    }
3399
3400
0
    if (eErr == CE_None)
3401
0
    {
3402
0
        if (m_nCompression == COMPRESSION_WEBP && TIFFIsTiled(m_hTIFF) &&
3403
0
            nBlockYOff * m_nBlockYSize > nRasterYSize - m_nBlockYSize)
3404
0
        {
3405
0
            const auto nValidBytes =
3406
0
                (nBlockBufSize / m_nBlockYSize) *
3407
0
                (m_nBlockYSize -
3408
0
                 static_cast<int>(
3409
0
                     (static_cast<GIntBig>(nBlockYOff + 1) * m_nBlockYSize) %
3410
0
                     nRasterYSize));
3411
            // Zero-out unused area
3412
0
            memset(m_pabyBlockBuf + nValidBytes, 0,
3413
0
                   nBlockBufSize - nValidBytes);
3414
0
        }
3415
3416
0
        m_nLoadedBlock = nBlockId;
3417
0
    }
3418
0
    else
3419
0
    {
3420
0
        m_nLoadedBlock = -1;
3421
0
    }
3422
0
    m_bLoadedBlockDirty = false;
3423
3424
0
    return eErr;
3425
0
}
3426
3427
/************************************************************************/
3428
/*                              Identify()                              */
3429
/************************************************************************/
3430
3431
int GTiffDataset::Identify(GDALOpenInfo *poOpenInfo)
3432
3433
0
{
3434
0
    const char *pszFilename = poOpenInfo->pszFilename;
3435
0
    if (STARTS_WITH_CI(pszFilename, "GTIFF_RAW:"))
3436
0
    {
3437
0
        pszFilename += strlen("GTIFF_RAW:");
3438
0
        GDALOpenInfo oOpenInfo(pszFilename, poOpenInfo->eAccess);
3439
0
        return Identify(&oOpenInfo);
3440
0
    }
3441
3442
    /* -------------------------------------------------------------------- */
3443
    /*      We have a special hook for handling opening a specific          */
3444
    /*      directory of a TIFF file.                                       */
3445
    /* -------------------------------------------------------------------- */
3446
0
    if (STARTS_WITH_CI(pszFilename, "GTIFF_DIR:"))
3447
0
        return TRUE;
3448
3449
    /* -------------------------------------------------------------------- */
3450
    /*      First we check to see if the file has the expected header       */
3451
    /*      bytes.                                                          */
3452
    /* -------------------------------------------------------------------- */
3453
0
    if (poOpenInfo->fpL == nullptr || poOpenInfo->nHeaderBytes < 2)
3454
0
        return FALSE;
3455
3456
0
    if ((poOpenInfo->pabyHeader[0] != 'I' ||
3457
0
         poOpenInfo->pabyHeader[1] != 'I') &&
3458
0
        (poOpenInfo->pabyHeader[0] != 'M' || poOpenInfo->pabyHeader[1] != 'M'))
3459
0
        return FALSE;
3460
3461
0
    if ((poOpenInfo->pabyHeader[2] != 0x2A || poOpenInfo->pabyHeader[3] != 0) &&
3462
0
        (poOpenInfo->pabyHeader[3] != 0x2A || poOpenInfo->pabyHeader[2] != 0) &&
3463
0
        (poOpenInfo->pabyHeader[2] != 0x2B || poOpenInfo->pabyHeader[3] != 0) &&
3464
0
        (poOpenInfo->pabyHeader[3] != 0x2B || poOpenInfo->pabyHeader[2] != 0))
3465
0
        return FALSE;
3466
3467
0
    return TRUE;
3468
0
}
3469
3470
/************************************************************************/
3471
/*                       GTIFFExtendMemoryFile()                        */
3472
/************************************************************************/
3473
3474
static bool GTIFFExtendMemoryFile(const CPLString &osTmpFilename,
3475
                                  VSILFILE *fpTemp, VSILFILE *fpL,
3476
                                  int nNewLength, GByte *&pabyBuffer,
3477
                                  vsi_l_offset &nDataLength)
3478
0
{
3479
0
    if (nNewLength <= static_cast<int>(nDataLength))
3480
0
        return true;
3481
0
    if (VSIFSeekL(fpTemp, nNewLength - 1, SEEK_SET) != 0)
3482
0
        return false;
3483
0
    char ch = 0;
3484
0
    if (VSIFWriteL(&ch, 1, 1, fpTemp) != 1)
3485
0
        return false;
3486
0
    const int nOldDataLength = static_cast<int>(nDataLength);
3487
0
    pabyBuffer = static_cast<GByte *>(
3488
0
        VSIGetMemFileBuffer(osTmpFilename, &nDataLength, FALSE));
3489
0
    const int nToRead = nNewLength - nOldDataLength;
3490
0
    const int nRead = static_cast<int>(
3491
0
        VSIFReadL(pabyBuffer + nOldDataLength, 1, nToRead, fpL));
3492
0
    if (nRead != nToRead)
3493
0
    {
3494
0
        CPLError(CE_Failure, CPLE_FileIO,
3495
0
                 "Needed to read %d bytes. Only %d got", nToRead, nRead);
3496
0
        return false;
3497
0
    }
3498
0
    return true;
3499
0
}
3500
3501
/************************************************************************/
3502
/*                      GTIFFMakeBufferedStream()                       */
3503
/************************************************************************/
3504
3505
static bool GTIFFMakeBufferedStream(GDALOpenInfo *poOpenInfo)
3506
0
{
3507
0
    const CPLString osTmpFilename(
3508
0
        VSIMemGenerateHiddenFilename("GTIFFMakeBufferedStream.tif"));
3509
0
    VSILFILE *fpTemp = VSIFOpenL(osTmpFilename, "wb+");
3510
0
    if (fpTemp == nullptr)
3511
0
        return false;
3512
    // The seek is needed for /vsistdin/ that has some rewind capabilities.
3513
0
    if (VSIFSeekL(poOpenInfo->fpL,
3514
0
                  static_cast<vsi_l_offset>(poOpenInfo->nHeaderBytes),
3515
0
                  SEEK_SET) != 0)
3516
0
    {
3517
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3518
0
        return false;
3519
0
    }
3520
0
    CPLAssert(static_cast<int>(VSIFTellL(poOpenInfo->fpL)) ==
3521
0
              poOpenInfo->nHeaderBytes);
3522
0
    if (VSIFWriteL(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes, 1,
3523
0
                   fpTemp) != 1)
3524
0
    {
3525
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3526
0
        return false;
3527
0
    }
3528
0
    vsi_l_offset nDataLength = 0;
3529
0
    GByte *pabyBuffer = static_cast<GByte *>(
3530
0
        VSIGetMemFileBuffer(osTmpFilename, &nDataLength, FALSE));
3531
0
    const bool bLittleEndian = (pabyBuffer[0] == 'I');
3532
0
    const bool bSwap = CPL_IS_LSB ^ bLittleEndian;
3533
0
    const bool bBigTIFF = pabyBuffer[2] == 43 || pabyBuffer[3] == 43;
3534
0
    vsi_l_offset nMaxOffset = 0;
3535
0
    if (bBigTIFF)
3536
0
    {
3537
0
        GUInt64 nTmp = 0;
3538
0
        memcpy(&nTmp, pabyBuffer + 8, 8);
3539
0
        if (bSwap)
3540
0
            CPL_SWAP64PTR(&nTmp);
3541
0
        if (nTmp != 16)
3542
0
        {
3543
0
            CPLError(CE_Failure, CPLE_NotSupported,
3544
0
                     "IFD start should be at offset 16 for a streamed BigTIFF");
3545
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3546
0
            VSIUnlink(osTmpFilename);
3547
0
            return false;
3548
0
        }
3549
0
        memcpy(&nTmp, pabyBuffer + 16, 8);
3550
0
        if (bSwap)
3551
0
            CPL_SWAP64PTR(&nTmp);
3552
0
        if (nTmp > 1024)
3553
0
        {
3554
0
            CPLError(CE_Failure, CPLE_NotSupported,
3555
0
                     "Too many tags : " CPL_FRMT_GIB, nTmp);
3556
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3557
0
            VSIUnlink(osTmpFilename);
3558
0
            return false;
3559
0
        }
3560
0
        const int nTags = static_cast<int>(nTmp);
3561
0
        const int nSpaceForTags = nTags * 20;
3562
0
        if (!GTIFFExtendMemoryFile(osTmpFilename, fpTemp, poOpenInfo->fpL,
3563
0
                                   24 + nSpaceForTags, pabyBuffer, nDataLength))
3564
0
        {
3565
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3566
0
            VSIUnlink(osTmpFilename);
3567
0
            return false;
3568
0
        }
3569
0
        nMaxOffset = 24 + nSpaceForTags + 8;
3570
0
        for (int i = 0; i < nTags; ++i)
3571
0
        {
3572
0
            GUInt16 nTmp16 = 0;
3573
0
            memcpy(&nTmp16, pabyBuffer + 24 + i * 20, 2);
3574
0
            if (bSwap)
3575
0
                CPL_SWAP16PTR(&nTmp16);
3576
0
            const int nTag = nTmp16;
3577
0
            memcpy(&nTmp16, pabyBuffer + 24 + i * 20 + 2, 2);
3578
0
            if (bSwap)
3579
0
                CPL_SWAP16PTR(&nTmp16);
3580
0
            const int nDataType = nTmp16;
3581
0
            memcpy(&nTmp, pabyBuffer + 24 + i * 20 + 4, 8);
3582
0
            if (bSwap)
3583
0
                CPL_SWAP64PTR(&nTmp);
3584
0
            if (nTmp >= 16 * 1024 * 1024)
3585
0
            {
3586
0
                CPLError(CE_Failure, CPLE_NotSupported,
3587
0
                         "Too many elements for tag %d : " CPL_FRMT_GUIB, nTag,
3588
0
                         nTmp);
3589
0
                CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3590
0
                VSIUnlink(osTmpFilename);
3591
0
                return false;
3592
0
            }
3593
0
            const GUInt32 nCount = static_cast<GUInt32>(nTmp);
3594
0
            const GUInt32 nTagSize =
3595
0
                TIFFDataWidth(static_cast<TIFFDataType>(nDataType)) * nCount;
3596
0
            if (nTagSize > 8)
3597
0
            {
3598
0
                memcpy(&nTmp, pabyBuffer + 24 + i * 20 + 12, 8);
3599
0
                if (bSwap)
3600
0
                    CPL_SWAP64PTR(&nTmp);
3601
0
                if (nTmp > GUINT64_MAX - nTagSize)
3602
0
                {
3603
0
                    CPLError(CE_Failure, CPLE_NotSupported,
3604
0
                             "Overflow with tag %d", nTag);
3605
0
                    CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3606
0
                    VSIUnlink(osTmpFilename);
3607
0
                    return false;
3608
0
                }
3609
0
                if (static_cast<vsi_l_offset>(nTmp + nTagSize) > nMaxOffset)
3610
0
                    nMaxOffset = nTmp + nTagSize;
3611
0
            }
3612
0
        }
3613
0
    }
3614
0
    else
3615
0
    {
3616
0
        GUInt32 nTmp = 0;
3617
0
        memcpy(&nTmp, pabyBuffer + 4, 4);
3618
0
        if (bSwap)
3619
0
            CPL_SWAP32PTR(&nTmp);
3620
0
        if (nTmp != 8)
3621
0
        {
3622
0
            CPLError(CE_Failure, CPLE_NotSupported,
3623
0
                     "IFD start should be at offset 8 for a streamed TIFF");
3624
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3625
0
            VSIUnlink(osTmpFilename);
3626
0
            return false;
3627
0
        }
3628
0
        GUInt16 nTmp16 = 0;
3629
0
        memcpy(&nTmp16, pabyBuffer + 8, 2);
3630
0
        if (bSwap)
3631
0
            CPL_SWAP16PTR(&nTmp16);
3632
0
        if (nTmp16 > 1024)
3633
0
        {
3634
0
            CPLError(CE_Failure, CPLE_NotSupported, "Too many tags : %d",
3635
0
                     nTmp16);
3636
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3637
0
            VSIUnlink(osTmpFilename);
3638
0
            return false;
3639
0
        }
3640
0
        const int nTags = nTmp16;
3641
0
        const int nSpaceForTags = nTags * 12;
3642
0
        if (!GTIFFExtendMemoryFile(osTmpFilename, fpTemp, poOpenInfo->fpL,
3643
0
                                   10 + nSpaceForTags, pabyBuffer, nDataLength))
3644
0
        {
3645
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3646
0
            VSIUnlink(osTmpFilename);
3647
0
            return false;
3648
0
        }
3649
0
        nMaxOffset = 10 + nSpaceForTags + 4;
3650
0
        for (int i = 0; i < nTags; ++i)
3651
0
        {
3652
0
            memcpy(&nTmp16, pabyBuffer + 10 + i * 12, 2);
3653
0
            if (bSwap)
3654
0
                CPL_SWAP16PTR(&nTmp16);
3655
0
            const int nTag = nTmp16;
3656
0
            memcpy(&nTmp16, pabyBuffer + 10 + i * 12 + 2, 2);
3657
0
            if (bSwap)
3658
0
                CPL_SWAP16PTR(&nTmp16);
3659
0
            const int nDataType = nTmp16;
3660
0
            memcpy(&nTmp, pabyBuffer + 10 + i * 12 + 4, 4);
3661
0
            if (bSwap)
3662
0
                CPL_SWAP32PTR(&nTmp);
3663
0
            if (nTmp >= 16 * 1024 * 1024)
3664
0
            {
3665
0
                CPLError(CE_Failure, CPLE_NotSupported,
3666
0
                         "Too many elements for tag %d : %u", nTag, nTmp);
3667
0
                CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3668
0
                VSIUnlink(osTmpFilename);
3669
0
                return false;
3670
0
            }
3671
0
            const GUInt32 nCount = nTmp;
3672
0
            const GUInt32 nTagSize =
3673
0
                TIFFDataWidth(static_cast<TIFFDataType>(nDataType)) * nCount;
3674
0
            if (nTagSize > 4)
3675
0
            {
3676
0
                memcpy(&nTmp, pabyBuffer + 10 + i * 12 + 8, 4);
3677
0
                if (bSwap)
3678
0
                    CPL_SWAP32PTR(&nTmp);
3679
0
                if (nTmp > static_cast<GUInt32>(UINT_MAX - nTagSize))
3680
0
                {
3681
0
                    CPLError(CE_Failure, CPLE_NotSupported,
3682
0
                             "Overflow with tag %d", nTag);
3683
0
                    CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3684
0
                    VSIUnlink(osTmpFilename);
3685
0
                    return false;
3686
0
                }
3687
0
                if (nTmp + nTagSize > nMaxOffset)
3688
0
                    nMaxOffset = nTmp + nTagSize;
3689
0
            }
3690
0
        }
3691
0
    }
3692
0
    if (nMaxOffset > 10 * 1024 * 1024)
3693
0
    {
3694
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3695
0
        VSIUnlink(osTmpFilename);
3696
0
        return false;
3697
0
    }
3698
0
    if (!GTIFFExtendMemoryFile(osTmpFilename, fpTemp, poOpenInfo->fpL,
3699
0
                               static_cast<int>(nMaxOffset), pabyBuffer,
3700
0
                               nDataLength))
3701
0
    {
3702
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpTemp));
3703
0
        VSIUnlink(osTmpFilename);
3704
0
        return false;
3705
0
    }
3706
0
    CPLAssert(nDataLength == VSIFTellL(poOpenInfo->fpL));
3707
0
    poOpenInfo->fpL = VSICreateBufferedReaderHandle(
3708
0
        poOpenInfo->fpL, pabyBuffer, static_cast<vsi_l_offset>(INT_MAX) << 32);
3709
0
    if (VSIFCloseL(fpTemp) != 0)
3710
0
        return false;
3711
0
    VSIUnlink(osTmpFilename);
3712
3713
0
    return true;
3714
0
}
3715
3716
/************************************************************************/
3717
/*                       AssociateExternalMask()                        */
3718
/************************************************************************/
3719
3720
// Used by GTIFFBuildOverviewsEx() for the COG driver
3721
bool GTiffDataset::AssociateExternalMask()
3722
0
{
3723
0
    if (m_poMaskExtOvrDS->GetRasterBand(1)->GetOverviewCount() !=
3724
0
        GetRasterBand(1)->GetOverviewCount())
3725
0
        return false;
3726
0
    if (m_apoOverviewDS.empty())
3727
0
        return false;
3728
0
    if (m_poMaskDS)
3729
0
        return false;
3730
0
    if (m_poMaskExtOvrDS->GetRasterXSize() != nRasterXSize ||
3731
0
        m_poMaskExtOvrDS->GetRasterYSize() != nRasterYSize)
3732
0
        return false;
3733
0
    m_poExternalMaskDS = m_poMaskExtOvrDS.get();
3734
0
    int i = 0;
3735
0
    for (auto &poOvrDS : m_apoOverviewDS)
3736
0
    {
3737
0
        if (poOvrDS->m_poMaskDS)
3738
0
            return false;
3739
0
        poOvrDS->m_poExternalMaskDS =
3740
0
            m_poMaskExtOvrDS->GetRasterBand(1)->GetOverview(i)->GetDataset();
3741
0
        if (!poOvrDS->m_poExternalMaskDS)
3742
0
            return false;
3743
0
        auto poOvrBand = poOvrDS->GetRasterBand(1);
3744
0
        if (poOvrDS->m_poExternalMaskDS->GetRasterXSize() !=
3745
0
                poOvrBand->GetXSize() ||
3746
0
            poOvrDS->m_poExternalMaskDS->GetRasterYSize() !=
3747
0
                poOvrBand->GetYSize())
3748
0
            return false;
3749
0
        ++i;
3750
0
    }
3751
0
    return true;
3752
0
}
3753
3754
/************************************************************************/
3755
/*                                Open()                                */
3756
/************************************************************************/
3757
3758
GDALDataset *GTiffDataset::Open(GDALOpenInfo *poOpenInfo)
3759
3760
0
{
3761
0
    const char *pszFilename = poOpenInfo->pszFilename;
3762
3763
    /* -------------------------------------------------------------------- */
3764
    /*      Check if it looks like a TIFF file.                             */
3765
    /* -------------------------------------------------------------------- */
3766
0
    if (!Identify(poOpenInfo))
3767
0
        return nullptr;
3768
3769
0
    bool bAllowRGBAInterface = true;
3770
0
    if (STARTS_WITH_CI(pszFilename, "GTIFF_RAW:"))
3771
0
    {
3772
0
        bAllowRGBAInterface = false;
3773
0
        pszFilename += strlen("GTIFF_RAW:");
3774
0
    }
3775
3776
    /* -------------------------------------------------------------------- */
3777
    /*      We have a special hook for handling opening a specific          */
3778
    /*      directory of a TIFF file.                                       */
3779
    /* -------------------------------------------------------------------- */
3780
0
    if (STARTS_WITH_CI(pszFilename, "GTIFF_DIR:"))
3781
0
        return OpenDir(poOpenInfo);
3782
3783
0
    GTiffOneTimeInit();
3784
3785
    /* -------------------------------------------------------------------- */
3786
    /*      Try opening the dataset.                                        */
3787
    /* -------------------------------------------------------------------- */
3788
0
    bool bStreaming = false;
3789
0
    const char *pszReadStreaming =
3790
0
        CPLGetConfigOption("TIFF_READ_STREAMING", nullptr);
3791
0
    if (poOpenInfo->fpL == nullptr)
3792
0
    {
3793
0
        poOpenInfo->fpL = VSIFOpenL(
3794
0
            pszFilename, poOpenInfo->eAccess == GA_ReadOnly ? "rb" : "r+b");
3795
0
        if (poOpenInfo->fpL == nullptr)
3796
0
            return nullptr;
3797
0
    }
3798
0
    else if (!(pszReadStreaming && !CPLTestBool(pszReadStreaming)) &&
3799
0
             poOpenInfo->nHeaderBytes >= 24 &&
3800
             // A pipe has no seeking capability, so its position is 0 despite
3801
             // having read bytes.
3802
0
             (static_cast<int>(VSIFTellL(poOpenInfo->fpL)) ==
3803
0
                  poOpenInfo->nHeaderBytes ||
3804
0
              strcmp(pszFilename, "/vsistdin/") == 0 ||
3805
              // STARTS_WITH(pszFilename, "/vsicurl_streaming/") ||
3806
0
              (pszReadStreaming && CPLTestBool(pszReadStreaming))))
3807
0
    {
3808
0
        bStreaming = true;
3809
0
        if (!GTIFFMakeBufferedStream(poOpenInfo))
3810
0
            return nullptr;
3811
0
    }
3812
3813
    // Store errors/warnings and emit them later.
3814
0
    TIFF *l_hTIFF;
3815
0
    CPLErrorAccumulator oErrorAccumulator;
3816
0
    {
3817
0
        auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
3818
0
        CPL_IGNORE_RET_VAL(oAccumulator);
3819
0
        CPLSetCurrentErrorHandlerCatchDebug(FALSE);
3820
0
        const bool bDeferStrileLoading = CPLTestBool(
3821
0
            CPLGetConfigOption("GTIFF_USE_DEFER_STRILE_LOADING", "YES"));
3822
0
        l_hTIFF = VSI_TIFFOpen(
3823
0
            pszFilename,
3824
0
            poOpenInfo->eAccess == GA_ReadOnly
3825
0
                ? ((bStreaming || !bDeferStrileLoading) ? "rC" : "rDOC")
3826
0
                : (!bDeferStrileLoading ? "r+C" : "r+DC"),
3827
0
            poOpenInfo->fpL);
3828
0
    };
3829
3830
    // Now emit errors and change their criticality if needed
3831
    // We only emit failures if we didn't manage to open the file.
3832
    // Otherwise it makes Python bindings unhappy (#5616).
3833
0
    for (const auto &oError : oErrorAccumulator.GetErrors())
3834
0
    {
3835
0
        ReportError(pszFilename,
3836
0
                    (l_hTIFF == nullptr && oError.type == CE_Failure)
3837
0
                        ? CE_Failure
3838
0
                        : CE_Warning,
3839
0
                    oError.no, "%s", oError.msg.c_str());
3840
0
    }
3841
3842
0
    if (l_hTIFF == nullptr)
3843
0
        return nullptr;
3844
3845
0
    uint32_t nXSize = 0;
3846
0
    TIFFGetField(l_hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize);
3847
0
    uint32_t nYSize = 0;
3848
0
    TIFFGetField(l_hTIFF, TIFFTAG_IMAGELENGTH, &nYSize);
3849
3850
0
    if (nXSize > INT_MAX || nYSize > INT_MAX)
3851
0
    {
3852
        // GDAL only supports signed 32bit dimensions.
3853
0
        ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
3854
0
                    "Too large image size: %u x %u", nXSize, nYSize);
3855
0
        XTIFFClose(l_hTIFF);
3856
0
        return nullptr;
3857
0
    }
3858
3859
0
    uint16_t l_nCompression = 0;
3860
0
    if (!TIFFGetField(l_hTIFF, TIFFTAG_COMPRESSION, &(l_nCompression)))
3861
0
        l_nCompression = COMPRESSION_NONE;
3862
3863
    /* -------------------------------------------------------------------- */
3864
    /*      Create a corresponding GDALDataset.                             */
3865
    /* -------------------------------------------------------------------- */
3866
0
    auto poDS = std::make_unique<GTiffDataset>();
3867
0
    poDS->SetDescription(pszFilename);
3868
0
    poDS->m_osFilename = pszFilename;
3869
0
    poDS->m_fpL = poOpenInfo->fpL;
3870
0
    poOpenInfo->fpL = nullptr;
3871
0
    poDS->m_bStreamingIn = bStreaming;
3872
0
    poDS->m_nCompression = l_nCompression;
3873
3874
    // Check structural metadata (for COG)
3875
0
    const int nOffsetOfStructuralMetadata =
3876
0
        poOpenInfo->nHeaderBytes && ((poOpenInfo->pabyHeader[2] == 0x2B ||
3877
0
                                      poOpenInfo->pabyHeader[3] == 0x2B))
3878
0
            ? 16
3879
0
            : 8;
3880
0
    if (poOpenInfo->nHeaderBytes >
3881
0
            nOffsetOfStructuralMetadata +
3882
0
                static_cast<int>(strlen("GDAL_STRUCTURAL_METADATA_SIZE=")) &&
3883
0
        memcmp(poOpenInfo->pabyHeader + nOffsetOfStructuralMetadata,
3884
0
               "GDAL_STRUCTURAL_METADATA_SIZE=",
3885
0
               strlen("GDAL_STRUCTURAL_METADATA_SIZE=")) == 0)
3886
0
    {
3887
0
        const char *pszStructuralMD = reinterpret_cast<const char *>(
3888
0
            poOpenInfo->pabyHeader + nOffsetOfStructuralMetadata);
3889
0
        poDS->m_bLayoutIFDSBeforeData =
3890
0
            strstr(pszStructuralMD, "LAYOUT=IFDS_BEFORE_DATA") != nullptr;
3891
0
        poDS->m_bBlockOrderRowMajor =
3892
0
            strstr(pszStructuralMD, "BLOCK_ORDER=ROW_MAJOR") != nullptr;
3893
0
        poDS->m_bLeaderSizeAsUInt4 =
3894
0
            strstr(pszStructuralMD, "BLOCK_LEADER=SIZE_AS_UINT4") != nullptr &&
3895
0
            (strstr(pszStructuralMD, "INTERLEAVE=") == nullptr ||
3896
0
             strstr(pszStructuralMD, "INTERLEAVE=BAND") != nullptr ||
3897
0
             strstr(pszStructuralMD, "INTERLEAVE=TILE") != nullptr);
3898
0
        poDS->m_bTrailerRepeatedLast4BytesRepeated =
3899
0
            strstr(pszStructuralMD, "BLOCK_TRAILER=LAST_4_BYTES_REPEATED") !=
3900
0
                nullptr &&
3901
0
            (strstr(pszStructuralMD, "INTERLEAVE=") == nullptr ||
3902
0
             strstr(pszStructuralMD, "INTERLEAVE=BAND") != nullptr ||
3903
0
             strstr(pszStructuralMD, "INTERLEAVE=TILE") != nullptr);
3904
0
        poDS->m_bMaskInterleavedWithImagery =
3905
0
            strstr(pszStructuralMD, "MASK_INTERLEAVED_WITH_IMAGERY=YES") !=
3906
0
                nullptr &&
3907
0
            strstr(pszStructuralMD, "INTERLEAVE=") == nullptr;
3908
0
        poDS->m_bKnownIncompatibleEdition =
3909
0
            strstr(pszStructuralMD, "KNOWN_INCOMPATIBLE_EDITION=YES") !=
3910
0
            nullptr;
3911
0
        if (poDS->m_bKnownIncompatibleEdition)
3912
0
        {
3913
0
            poDS->ReportError(
3914
0
                CE_Warning, CPLE_AppDefined,
3915
0
                "This file used to have optimizations in its layout, "
3916
0
                "but those have been, at least partly, invalidated by "
3917
0
                "later changes");
3918
0
        }
3919
0
        else if (poDS->m_bLayoutIFDSBeforeData && poDS->m_bBlockOrderRowMajor &&
3920
0
                 poDS->m_bLeaderSizeAsUInt4 &&
3921
0
                 poDS->m_bTrailerRepeatedLast4BytesRepeated)
3922
0
        {
3923
0
            if (poOpenInfo->eAccess == GA_Update &&
3924
0
                !CPLTestBool(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
3925
0
                                                  "IGNORE_COG_LAYOUT_BREAK",
3926
0
                                                  "FALSE")))
3927
0
            {
3928
0
                CPLError(CE_Failure, CPLE_AppDefined,
3929
0
                         "File %s has C(loud) O(ptimized) G(eoTIFF) layout. "
3930
0
                         "Updating it will generally result in losing part of "
3931
0
                         "the optimizations (but will still produce a valid "
3932
0
                         "GeoTIFF file). If this is acceptable, open the file "
3933
0
                         "with the IGNORE_COG_LAYOUT_BREAK open option set "
3934
0
                         "to YES.",
3935
0
                         pszFilename);
3936
0
                XTIFFClose(l_hTIFF);
3937
0
                return nullptr;
3938
0
            }
3939
0
            poDS->m_oGTiffMDMD.SetMetadataItem("LAYOUT", "COG",
3940
0
                                               "IMAGE_STRUCTURE");
3941
0
        }
3942
0
    }
3943
3944
    // In the case of GDAL_DISABLE_READDIR_ON_OPEN = NO / EMPTY_DIR
3945
0
    if (poOpenInfo->AreSiblingFilesLoaded() &&
3946
0
        CSLCount(poOpenInfo->GetSiblingFiles()) <= 1)
3947
0
    {
3948
0
        poDS->oOvManager.TransferSiblingFiles(
3949
0
            CSLDuplicate(poOpenInfo->GetSiblingFiles()));
3950
0
        poDS->m_bHasGotSiblingFiles = true;
3951
0
    }
3952
3953
    // Should be capped by 257, to avoid 65535 / m_nColorTableMultiplier to overflow 255
3954
0
    poDS->m_nColorTableMultiplier = std::max(
3955
0
        0, std::min(257,
3956
0
                    atoi(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
3957
0
                                              "COLOR_TABLE_MULTIPLIER", "0"))));
3958
3959
0
    if (poDS->OpenOffset(l_hTIFF, TIFFCurrentDirOffset(l_hTIFF),
3960
0
                         poOpenInfo->eAccess, bAllowRGBAInterface,
3961
0
                         true) != CE_None)
3962
0
    {
3963
0
        return nullptr;
3964
0
    }
3965
3966
    // Do we want blocks that are set to zero and that haven't yet being
3967
    // allocated as tile/strip to remain implicit?
3968
0
    if (CPLFetchBool(poOpenInfo->papszOpenOptions, "SPARSE_OK", false))
3969
0
        poDS->m_bWriteEmptyTiles = false;
3970
3971
0
    poDS->InitCreationOrOpenOptions(poOpenInfo->eAccess == GA_Update,
3972
0
                                    poOpenInfo->papszOpenOptions);
3973
3974
0
    poDS->m_bLoadPam = true;
3975
0
    poDS->m_bColorProfileMetadataChanged = false;
3976
0
    poDS->m_bMetadataChanged = false;
3977
0
    poDS->m_bGeoTIFFInfoChanged = false;
3978
0
    poDS->m_bNoDataChanged = false;
3979
0
    poDS->m_bForceUnsetGTOrGCPs = false;
3980
0
    poDS->m_bForceUnsetProjection = false;
3981
3982
    // Used by GTIFFBuildOverviewsEx() for the COG driver
3983
0
    const char *pszMaskOverviewDS = CSLFetchNameValue(
3984
0
        poOpenInfo->papszOpenOptions, "MASK_OVERVIEW_DATASET");
3985
0
    if (pszMaskOverviewDS)
3986
0
    {
3987
0
        poDS->m_poMaskExtOvrDS.reset(GDALDataset::Open(
3988
0
            pszMaskOverviewDS, GDAL_OF_RASTER | GDAL_OF_INTERNAL));
3989
0
        if (!poDS->m_poMaskExtOvrDS || !poDS->AssociateExternalMask())
3990
0
        {
3991
0
            CPLDebug("GTiff",
3992
0
                     "Association with external mask overview file failed");
3993
0
        }
3994
0
    }
3995
3996
    /* -------------------------------------------------------------------- */
3997
    /*      Initialize info for external overviews.                         */
3998
    /* -------------------------------------------------------------------- */
3999
0
    poDS->oOvManager.Initialize(poDS.get(), poOpenInfo, pszFilename);
4000
4001
    // For backward compatibility, in case GTIFF_POINT_GEO_IGNORE is defined
4002
    // load georeferencing right now so as to not require it to be defined
4003
    // at the GetGeoTransform() time.
4004
0
    if (CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", nullptr) != nullptr)
4005
0
    {
4006
0
        poDS->LoadGeoreferencingAndPamIfNeeded();
4007
0
    }
4008
4009
0
    return poDS.release();
4010
0
}
4011
4012
/************************************************************************/
4013
/*                    GTiffDatasetSetAreaOrPointMD()                    */
4014
/************************************************************************/
4015
4016
static void GTiffDatasetSetAreaOrPointMD(GTIF *hGTIF,
4017
                                         GDALMultiDomainMetadata &m_oGTiffMDMD)
4018
0
{
4019
    // Is this a pixel-is-point dataset?
4020
0
    unsigned short nRasterType = 0;
4021
4022
0
    if (GDALGTIFKeyGetSHORT(hGTIF, GTRasterTypeGeoKey, &nRasterType, 0, 1) == 1)
4023
0
    {
4024
0
        if (nRasterType == static_cast<short>(RasterPixelIsPoint))
4025
0
            m_oGTiffMDMD.SetMetadataItem(GDALMD_AREA_OR_POINT,
4026
0
                                         GDALMD_AOP_POINT);
4027
0
        else
4028
0
            m_oGTiffMDMD.SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_AREA);
4029
0
    }
4030
0
}
4031
4032
/************************************************************************/
4033
/*                         LoadMDAreaOrPoint()                          */
4034
/************************************************************************/
4035
4036
// This is a light version of LookForProjection(), which saves the
4037
// potential costly cost of GTIFGetOGISDefn(), since we just need to
4038
// access to a raw GeoTIFF key, and not build the full projection object.
4039
4040
void GTiffDataset::LoadMDAreaOrPoint()
4041
0
{
4042
0
    if (m_bLookedForProjection || m_bLookedForMDAreaOrPoint ||
4043
0
        m_oGTiffMDMD.GetMetadataItem(GDALMD_AREA_OR_POINT) != nullptr)
4044
0
        return;
4045
4046
0
    m_bLookedForMDAreaOrPoint = true;
4047
4048
0
    GTIF *hGTIF = GTiffDataset::GTIFNew(m_hTIFF);
4049
4050
0
    if (!hGTIF)
4051
0
    {
4052
0
        ReportError(CE_Warning, CPLE_AppDefined,
4053
0
                    "GeoTIFF tags apparently corrupt, they are being ignored.");
4054
0
    }
4055
0
    else
4056
0
    {
4057
0
        GTiffDatasetSetAreaOrPointMD(hGTIF, m_oGTiffMDMD);
4058
4059
0
        GTIFFree(hGTIF);
4060
0
    }
4061
0
}
4062
4063
/************************************************************************/
4064
/*                         LookForProjection()                          */
4065
/************************************************************************/
4066
4067
void GTiffDataset::LookForProjection()
4068
4069
0
{
4070
0
    if (m_bLookedForProjection)
4071
0
        return;
4072
4073
0
    m_bLookedForProjection = true;
4074
4075
0
    IdentifyAuthorizedGeoreferencingSources();
4076
4077
0
    m_oSRS.Clear();
4078
4079
0
    std::set<signed char> aoSetPriorities;
4080
0
    if (m_nINTERNALGeorefSrcIndex >= 0)
4081
0
        aoSetPriorities.insert(m_nINTERNALGeorefSrcIndex);
4082
0
    if (m_nXMLGeorefSrcIndex >= 0)
4083
0
        aoSetPriorities.insert(m_nXMLGeorefSrcIndex);
4084
0
    for (const auto nIndex : aoSetPriorities)
4085
0
    {
4086
0
        if (m_nINTERNALGeorefSrcIndex == nIndex)
4087
0
        {
4088
0
            LookForProjectionFromGeoTIFF();
4089
0
        }
4090
0
        else if (m_nXMLGeorefSrcIndex == nIndex)
4091
0
        {
4092
0
            LookForProjectionFromXML();
4093
0
        }
4094
0
    }
4095
0
}
4096
4097
/************************************************************************/
4098
/*                    LookForProjectionFromGeoTIFF()                    */
4099
/************************************************************************/
4100
4101
void GTiffDataset::LookForProjectionFromGeoTIFF()
4102
0
{
4103
    /* -------------------------------------------------------------------- */
4104
    /*      Capture the GeoTIFF projection, if available.                   */
4105
    /* -------------------------------------------------------------------- */
4106
4107
0
    GTIF *hGTIF = GTiffDataset::GTIFNew(m_hTIFF);
4108
4109
0
    if (!hGTIF)
4110
0
    {
4111
0
        ReportError(CE_Warning, CPLE_AppDefined,
4112
0
                    "GeoTIFF tags apparently corrupt, they are being ignored.");
4113
0
    }
4114
0
    else
4115
0
    {
4116
0
        GTIFDefn *psGTIFDefn = GTIFAllocDefn();
4117
4118
0
        bool bHasErrorBefore = CPLGetLastErrorType() != 0;
4119
        // Collect (PROJ) error messages and remit them later as warnings
4120
0
        int ret;
4121
0
        CPLErrorAccumulator oErrorAccumulator;
4122
0
        {
4123
0
            auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
4124
0
            ret = GTIFGetDefn(hGTIF, psGTIFDefn);
4125
0
        }
4126
4127
0
        bool bWarnAboutEllipsoid = true;
4128
4129
0
        if (ret)
4130
0
        {
4131
0
            auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
4132
4133
0
            if (psGTIFDefn->Ellipsoid == 4326 &&
4134
0
                psGTIFDefn->SemiMajor == 6378137 &&
4135
0
                psGTIFDefn->SemiMinor == 6356752.314245)
4136
0
            {
4137
                // Buggy Sentinel1 geotiff files use a wrong 4326 code for the
4138
                // ellipsoid instead of 7030.
4139
0
                psGTIFDefn->Ellipsoid = 7030;
4140
0
                bWarnAboutEllipsoid = false;
4141
0
            }
4142
4143
0
            OGRSpatialReferenceH hSRS = GTIFGetOGISDefnAsOSR(hGTIF, psGTIFDefn);
4144
4145
0
            if (hSRS)
4146
0
            {
4147
0
                CPLFree(m_pszXMLFilename);
4148
0
                m_pszXMLFilename = nullptr;
4149
4150
0
                m_oSRS = *(OGRSpatialReference::FromHandle(hSRS));
4151
0
                OSRDestroySpatialReference(hSRS);
4152
0
            }
4153
0
        }
4154
4155
0
        std::set<std::string> oSetErrorMsg;
4156
0
        for (const auto &oError : oErrorAccumulator.GetErrors())
4157
0
        {
4158
0
            if (!bWarnAboutEllipsoid &&
4159
0
                oError.msg.find("ellipsoid not found") != std::string::npos)
4160
0
            {
4161
0
                continue;
4162
0
            }
4163
4164
            // Some error messages might be duplicated in GTIFGetDefn()
4165
            // and GTIFGetOGISDefnAsOSR(). Emit them just once.
4166
0
            if (oSetErrorMsg.find(oError.msg) == oSetErrorMsg.end())
4167
0
            {
4168
0
                oSetErrorMsg.insert(oError.msg);
4169
0
                CPLError(oError.type == CE_Failure ? CE_Warning : oError.type,
4170
0
                         oError.no, "%s", oError.msg.c_str());
4171
0
            }
4172
0
        }
4173
4174
0
        if (!bHasErrorBefore && oSetErrorMsg.empty())
4175
0
        {
4176
0
            CPLErrorReset();
4177
0
        }
4178
4179
0
        if (ret && m_oSRS.IsCompound())
4180
0
        {
4181
0
            const char *pszVertUnit = nullptr;
4182
0
            m_oSRS.GetTargetLinearUnits("COMPD_CS|VERT_CS", &pszVertUnit);
4183
0
            if (pszVertUnit && !EQUAL(pszVertUnit, "unknown"))
4184
0
            {
4185
0
                CPLFree(m_pszVertUnit);
4186
0
                m_pszVertUnit = CPLStrdup(pszVertUnit);
4187
0
            }
4188
4189
0
            int versions[3];
4190
0
            GTIFDirectoryInfo(hGTIF, versions, nullptr);
4191
4192
            // If GeoTIFF 1.0, strip vertical by default
4193
0
            const char *pszDefaultReportCompdCS =
4194
0
                (versions[0] == 1 && versions[1] == 1 && versions[2] == 0)
4195
0
                    ? "NO"
4196
0
                    : "YES";
4197
4198
            // Should we simplify away vertical CS stuff?
4199
0
            if (!CPLTestBool(CPLGetConfigOption("GTIFF_REPORT_COMPD_CS",
4200
0
                                                pszDefaultReportCompdCS)))
4201
0
            {
4202
0
                CPLDebug("GTiff", "Got COMPD_CS, but stripping it.");
4203
4204
0
                m_oSRS.StripVertical();
4205
0
            }
4206
0
        }
4207
4208
0
        GTIFFreeDefn(psGTIFDefn);
4209
4210
0
        GTiffDatasetSetAreaOrPointMD(hGTIF, m_oGTiffMDMD);
4211
4212
0
        GTIFFree(hGTIF);
4213
0
    }
4214
0
}
4215
4216
/************************************************************************/
4217
/*            GetSidecarFilenameWithReplacedExtension()    ¨            */
4218
/************************************************************************/
4219
4220
std::string
4221
GTiffDataset::GetSidecarFilenameWithReplacedExtension(const char *pszExt)
4222
0
{
4223
0
    if (!GDALCanFileAcceptSidecarFile(m_osFilename.c_str()))
4224
0
        return std::string();
4225
4226
0
    CSLConstList papszSiblingFiles = GetSiblingFiles();
4227
0
    const std::string osSidecarFilenameLowerCaseExt = CPLResetExtensionSafe(
4228
0
        m_osFilename.c_str(), CPLString(pszExt).tolower());
4229
4230
0
    if (papszSiblingFiles && GDALCanReliablyUseSiblingFileList(
4231
0
                                 osSidecarFilenameLowerCaseExt.c_str()))
4232
0
    {
4233
0
        const int iSibling = CSLFindString(
4234
0
            papszSiblingFiles,
4235
0
            CPLGetFilename(osSidecarFilenameLowerCaseExt.c_str()));
4236
0
        if (iSibling >= 0)
4237
0
        {
4238
0
            std::string osRet = m_osFilename.c_str();
4239
0
            osRet.resize(m_osFilename.size() -
4240
0
                         strlen(CPLGetFilename(m_osFilename.c_str())));
4241
0
            osRet += papszSiblingFiles[iSibling];
4242
0
            return osRet;
4243
0
        }
4244
0
        else
4245
0
        {
4246
0
            return std::string();
4247
0
        }
4248
0
    }
4249
4250
0
    VSIStatBufL sStatBuf;
4251
0
    bool bGotSidecar = VSIStatExL(osSidecarFilenameLowerCaseExt.c_str(),
4252
0
                                  &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
4253
4254
0
    if (bGotSidecar)
4255
0
    {
4256
0
        return osSidecarFilenameLowerCaseExt;
4257
0
    }
4258
0
    else if (VSIIsCaseSensitiveFS(osSidecarFilenameLowerCaseExt.c_str()))
4259
0
    {
4260
0
        const std::string osSidecarFilenameUppercaseExt = CPLResetExtensionSafe(
4261
0
            m_osFilename.c_str(), CPLString(pszExt).toupper());
4262
0
        bGotSidecar = VSIStatExL(osSidecarFilenameUppercaseExt.c_str(),
4263
0
                                 &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
4264
0
        if (bGotSidecar)
4265
0
        {
4266
0
            return osSidecarFilenameUppercaseExt;
4267
0
        }
4268
0
    }
4269
4270
0
    return std::string();
4271
0
}
4272
4273
/************************************************************************/
4274
/*                      LookForProjectionFromXML()                      */
4275
/************************************************************************/
4276
4277
void GTiffDataset::LookForProjectionFromXML()
4278
0
{
4279
0
    if (m_poBaseDS != nullptr)
4280
0
        return;
4281
4282
0
    const std::string osXMLFilename =
4283
0
        GetSidecarFilenameWithReplacedExtension("xml");
4284
0
    if (osXMLFilename.empty())
4285
0
        return;
4286
4287
0
    GByte *pabyRet = nullptr;
4288
0
    vsi_l_offset nSize = 0;
4289
0
    constexpr int nMaxSize = 10 * 1024 * 1024;
4290
0
    if (!VSIIngestFile(nullptr, osXMLFilename.c_str(), &pabyRet, &nSize,
4291
0
                       nMaxSize))
4292
0
        return;
4293
0
    CPLXMLTreeCloser oXML(
4294
0
        CPLParseXMLString(reinterpret_cast<const char *>(pabyRet)));
4295
0
    VSIFree(pabyRet);
4296
0
    if (!oXML.get())
4297
0
        return;
4298
0
    const char *pszCode = CPLGetXMLValue(
4299
0
        oXML.get(), "=metadata.refSysInfo.RefSystem.refSysID.identCode.code",
4300
0
        "0");
4301
0
    const int nCode = atoi(pszCode);
4302
0
    if (nCode <= 0)
4303
0
        return;
4304
0
    if (nCode <= 32767)
4305
0
        m_oSRS.importFromEPSG(nCode);
4306
0
    else
4307
0
        m_oSRS.SetFromUserInput(CPLSPrintf("ESRI:%d", nCode));
4308
4309
0
    CPLFree(m_pszXMLFilename);
4310
0
    m_pszXMLFilename = CPLStrdup(osXMLFilename.c_str());
4311
0
}
4312
4313
/************************************************************************/
4314
/*                            ApplyPamInfo()                            */
4315
/*                                                                      */
4316
/*      PAM Information, if available, overrides the GeoTIFF            */
4317
/*      geotransform and projection definition.  Check for them         */
4318
/*      now.                                                            */
4319
/************************************************************************/
4320
4321
void GTiffDataset::ApplyPamInfo()
4322
4323
0
{
4324
0
    bool bGotGTFromPAM = false;
4325
4326
0
    if (m_nPAMGeorefSrcIndex >= 0 &&
4327
0
        ((m_bGeoTransformValid &&
4328
0
          m_nPAMGeorefSrcIndex < m_nGeoTransformGeorefSrcIndex) ||
4329
0
         m_nGeoTransformGeorefSrcIndex < 0 || !m_bGeoTransformValid))
4330
0
    {
4331
0
        GDALGeoTransform pamGT;
4332
0
        if (GDALPamDataset::GetGeoTransform(pamGT) == CE_None)
4333
0
        {
4334
0
            if (m_nGeoTransformGeorefSrcIndex == m_nWORLDFILEGeorefSrcIndex)
4335
0
            {
4336
0
                CPLFree(m_pszGeorefFilename);
4337
0
                m_pszGeorefFilename = nullptr;
4338
0
            }
4339
0
            m_gt = pamGT;
4340
0
            m_bGeoTransformValid = true;
4341
0
            bGotGTFromPAM = true;
4342
0
        }
4343
0
    }
4344
4345
0
    if (m_nPAMGeorefSrcIndex >= 0)
4346
0
    {
4347
0
        if ((m_nTABFILEGeorefSrcIndex < 0 ||
4348
0
             m_nPAMGeorefSrcIndex < m_nTABFILEGeorefSrcIndex) &&
4349
0
            (m_nINTERNALGeorefSrcIndex < 0 ||
4350
0
             m_nPAMGeorefSrcIndex < m_nINTERNALGeorefSrcIndex))
4351
0
        {
4352
0
            const auto *poPamSRS = GDALPamDataset::GetSpatialRef();
4353
0
            if (poPamSRS)
4354
0
            {
4355
0
                m_oSRS = *poPamSRS;
4356
0
                m_bLookedForProjection = true;
4357
                // m_nProjectionGeorefSrcIndex = m_nPAMGeorefSrcIndex;
4358
0
            }
4359
0
        }
4360
0
        else
4361
0
        {
4362
0
            if (m_nINTERNALGeorefSrcIndex >= 0)
4363
0
                LookForProjection();
4364
0
            if (m_oSRS.IsEmpty())
4365
0
            {
4366
0
                const auto *poPamSRS = GDALPamDataset::GetSpatialRef();
4367
0
                if (poPamSRS)
4368
0
                {
4369
0
                    m_oSRS = *poPamSRS;
4370
0
                    m_bLookedForProjection = true;
4371
                    // m_nProjectionGeorefSrcIndex = m_nPAMGeorefSrcIndex;
4372
0
                }
4373
0
            }
4374
0
        }
4375
0
    }
4376
4377
0
    int nPamGCPCount;
4378
0
    if (m_nPAMGeorefSrcIndex >= 0 && !oMDMD.GetMetadata("xml:ESRI") &&
4379
0
        (nPamGCPCount = GDALPamDataset::GetGCPCount()) > 0 &&
4380
0
        ((!m_aoGCPs.empty() &&
4381
0
          m_nPAMGeorefSrcIndex < m_nGeoTransformGeorefSrcIndex) ||
4382
0
         m_nGeoTransformGeorefSrcIndex < 0 || m_aoGCPs.empty()))
4383
0
    {
4384
0
        m_aoGCPs = gdal::GCP::fromC(GDALPamDataset::GetGCPs(), nPamGCPCount);
4385
4386
        // Invalidate Geotransorm got from less priority sources
4387
0
        if (!m_aoGCPs.empty() && m_bGeoTransformValid && !bGotGTFromPAM &&
4388
0
            m_nPAMGeorefSrcIndex == 0)
4389
0
        {
4390
0
            m_bGeoTransformValid = false;
4391
0
        }
4392
4393
        // m_nProjectionGeorefSrcIndex = m_nPAMGeorefSrcIndex;
4394
4395
0
        const auto *poPamGCPSRS = GDALPamDataset::GetGCPSpatialRef();
4396
0
        if (poPamGCPSRS)
4397
0
            m_oSRS = *poPamGCPSRS;
4398
0
        else
4399
0
            m_oSRS.Clear();
4400
4401
0
        m_bLookedForProjection = true;
4402
0
    }
4403
4404
0
    if (m_nPAMGeorefSrcIndex >= 0)
4405
0
    {
4406
0
        CPLXMLNode *psValueAsXML = nullptr;
4407
0
        CPLXMLNode *psGeodataXform = nullptr;
4408
0
        char **papszXML = oMDMD.GetMetadata("xml:ESRI");
4409
0
        if (CSLCount(papszXML) == 1)
4410
0
        {
4411
0
            psValueAsXML = CPLParseXMLString(papszXML[0]);
4412
0
            if (psValueAsXML)
4413
0
                psGeodataXform = CPLGetXMLNode(psValueAsXML, "=GeodataXform");
4414
0
        }
4415
4416
0
        const char *pszTIFFTagResUnit =
4417
0
            GetMetadataItem("TIFFTAG_RESOLUTIONUNIT");
4418
0
        const char *pszTIFFTagXRes = GetMetadataItem("TIFFTAG_XRESOLUTION");
4419
0
        const char *pszTIFFTagYRes = GetMetadataItem("TIFFTAG_YRESOLUTION");
4420
0
        if (psGeodataXform && pszTIFFTagXRes && pszTIFFTagYRes &&
4421
0
            pszTIFFTagResUnit && atoi(pszTIFFTagResUnit) == 2)
4422
0
        {
4423
0
            CPLXMLNode *psSourceGCPs =
4424
0
                CPLGetXMLNode(psGeodataXform, "SourceGCPs");
4425
0
            CPLXMLNode *psTargetGCPs =
4426
0
                CPLGetXMLNode(psGeodataXform, "TargetGCPs");
4427
0
            if (psSourceGCPs && psTargetGCPs)
4428
0
            {
4429
0
                std::vector<double> adfSourceGCPs, adfTargetGCPs;
4430
0
                for (CPLXMLNode *psIter = psSourceGCPs->psChild;
4431
0
                     psIter != nullptr; psIter = psIter->psNext)
4432
0
                {
4433
0
                    if (psIter->eType == CXT_Element &&
4434
0
                        EQUAL(psIter->pszValue, "Double"))
4435
0
                    {
4436
0
                        adfSourceGCPs.push_back(
4437
0
                            CPLAtof(CPLGetXMLValue(psIter, nullptr, "")));
4438
0
                    }
4439
0
                }
4440
0
                for (CPLXMLNode *psIter = psTargetGCPs->psChild;
4441
0
                     psIter != nullptr; psIter = psIter->psNext)
4442
0
                {
4443
0
                    if (psIter->eType == CXT_Element &&
4444
0
                        EQUAL(psIter->pszValue, "Double"))
4445
0
                    {
4446
0
                        adfTargetGCPs.push_back(
4447
0
                            CPLAtof(CPLGetXMLValue(psIter, nullptr, "")));
4448
0
                    }
4449
0
                }
4450
0
                if (adfSourceGCPs.size() == adfTargetGCPs.size() &&
4451
0
                    (adfSourceGCPs.size() % 2) == 0)
4452
0
                {
4453
0
                    const char *pszESRI_WKT = CPLGetXMLValue(
4454
0
                        psGeodataXform, "SpatialReference.WKT", nullptr);
4455
0
                    if (pszESRI_WKT)
4456
0
                    {
4457
0
                        m_bLookedForProjection = true;
4458
0
                        m_oSRS.SetAxisMappingStrategy(
4459
0
                            OAMS_TRADITIONAL_GIS_ORDER);
4460
0
                        if (m_oSRS.importFromWkt(pszESRI_WKT) != OGRERR_NONE)
4461
0
                        {
4462
0
                            m_oSRS.Clear();
4463
0
                        }
4464
0
                    }
4465
4466
0
                    m_aoGCPs.clear();
4467
0
                    const size_t nNewGCPCount = adfSourceGCPs.size() / 2;
4468
0
                    for (size_t i = 0; i < nNewGCPCount; ++i)
4469
0
                    {
4470
0
                        m_aoGCPs.emplace_back(
4471
0
                            "", "",
4472
                            // The origin used is the bottom left corner,
4473
                            // and raw values to be multiplied by the
4474
                            // TIFFTAG_XRESOLUTION/TIFFTAG_YRESOLUTION
4475
                            /* pixel  = */
4476
0
                            adfSourceGCPs[2 * i] * CPLAtof(pszTIFFTagXRes),
4477
                            /* line = */
4478
0
                            nRasterYSize - adfSourceGCPs[2 * i + 1] *
4479
0
                                               CPLAtof(pszTIFFTagYRes),
4480
0
                            /* X = */ adfTargetGCPs[2 * i],
4481
0
                            /* Y = */ adfTargetGCPs[2 * i + 1]);
4482
0
                    }
4483
4484
                    // Invalidate Geotransform got from less priority sources
4485
0
                    if (!m_aoGCPs.empty() && m_bGeoTransformValid &&
4486
0
                        !bGotGTFromPAM && m_nPAMGeorefSrcIndex == 0)
4487
0
                    {
4488
0
                        m_bGeoTransformValid = false;
4489
0
                    }
4490
0
                }
4491
0
            }
4492
0
        }
4493
4494
0
        if (psValueAsXML)
4495
0
            CPLDestroyXMLNode(psValueAsXML);
4496
0
    }
4497
4498
    /* -------------------------------------------------------------------- */
4499
    /*      Copy any PAM metadata into our GeoTIFF context, and with        */
4500
    /*      the PAM info overriding the GeoTIFF context.                    */
4501
    /* -------------------------------------------------------------------- */
4502
0
    CSLConstList papszPamDomains = oMDMD.GetDomainList();
4503
4504
0
    for (int iDomain = 0;
4505
0
         papszPamDomains && papszPamDomains[iDomain] != nullptr; ++iDomain)
4506
0
    {
4507
0
        const char *pszDomain = papszPamDomains[iDomain];
4508
        // We skip IMAGE_STRUCTURE as PAM content is not supposed to modify
4509
        // it and it could cause consistency issues if doing GetMetadata("IMAGE_STRUCTURE")
4510
        // which doesn't load PAM, then GetMetadata(other_domain) and
4511
        // GetMetadata("IMAGE_STRUCTURE")
4512
0
        if (!EQUAL(pszDomain, "IMAGE_STRUCTURE"))
4513
0
        {
4514
0
            char **papszGT_MD =
4515
0
                CSLDuplicate(m_oGTiffMDMD.GetMetadata(pszDomain));
4516
0
            char **papszPAM_MD = oMDMD.GetMetadata(pszDomain);
4517
4518
0
            papszGT_MD = CSLMerge(papszGT_MD, papszPAM_MD);
4519
4520
0
            m_oGTiffMDMD.SetMetadata(papszGT_MD, pszDomain);
4521
0
            CSLDestroy(papszGT_MD);
4522
0
        }
4523
0
    }
4524
4525
0
    for (int i = 1; i <= GetRasterCount(); ++i)
4526
0
    {
4527
0
        GTiffRasterBand *poBand =
4528
0
            cpl::down_cast<GTiffRasterBand *>(GetRasterBand(i));
4529
0
        papszPamDomains = poBand->oMDMD.GetDomainList();
4530
4531
0
        for (int iDomain = 0;
4532
0
             papszPamDomains && papszPamDomains[iDomain] != nullptr; ++iDomain)
4533
0
        {
4534
0
            const char *pszDomain = papszPamDomains[iDomain];
4535
0
            char **papszGT_MD =
4536
0
                CSLDuplicate(poBand->m_oGTiffMDMD.GetMetadata(pszDomain));
4537
0
            char **papszPAM_MD = poBand->oMDMD.GetMetadata(pszDomain);
4538
4539
0
            papszGT_MD = CSLMerge(papszGT_MD, papszPAM_MD);
4540
4541
0
            poBand->m_oGTiffMDMD.SetMetadata(papszGT_MD, pszDomain);
4542
0
            CSLDestroy(papszGT_MD);
4543
0
        }
4544
0
    }
4545
4546
0
    for (int i = 1; i <= nBands; ++i)
4547
0
    {
4548
0
        GTiffRasterBand *poBand =
4549
0
            cpl::down_cast<GTiffRasterBand *>(GetRasterBand(i));
4550
4551
        /* Load scale, offset and unittype from PAM if available */
4552
0
        int nHaveOffsetScale = false;
4553
0
        double dfScale = poBand->GDALPamRasterBand::GetScale(&nHaveOffsetScale);
4554
0
        if (nHaveOffsetScale)
4555
0
        {
4556
0
            poBand->m_bHaveOffsetScale = true;
4557
0
            poBand->m_dfScale = dfScale;
4558
0
            poBand->m_dfOffset = poBand->GDALPamRasterBand::GetOffset();
4559
0
        }
4560
4561
0
        const char *pszUnitType = poBand->GDALPamRasterBand::GetUnitType();
4562
0
        if (pszUnitType && pszUnitType[0])
4563
0
            poBand->m_osUnitType = pszUnitType;
4564
4565
0
        const char *pszDescription =
4566
0
            poBand->GDALPamRasterBand::GetDescription();
4567
0
        if (pszDescription && pszDescription[0])
4568
0
            poBand->m_osDescription = pszDescription;
4569
4570
0
        GDALColorInterp ePAMColorInterp =
4571
0
            poBand->GDALPamRasterBand::GetColorInterpretation();
4572
0
        if (ePAMColorInterp != GCI_Undefined)
4573
0
            poBand->m_eBandInterp = ePAMColorInterp;
4574
4575
0
        if (i == 1)
4576
0
        {
4577
0
            const auto poCT = poBand->GDALPamRasterBand::GetColorTable();
4578
0
            if (poCT)
4579
0
            {
4580
0
                m_poColorTable.reset(poCT->Clone());
4581
0
            }
4582
0
        }
4583
0
    }
4584
0
}
4585
4586
/************************************************************************/
4587
/*                              OpenDir()                               */
4588
/*                                                                      */
4589
/*      Open a specific directory as encoded into a filename.           */
4590
/************************************************************************/
4591
4592
GDALDataset *GTiffDataset::OpenDir(GDALOpenInfo *poOpenInfo)
4593
4594
0
{
4595
0
    bool bAllowRGBAInterface = true;
4596
0
    const char *pszFilename = poOpenInfo->pszFilename;
4597
0
    if (STARTS_WITH_CI(pszFilename, "GTIFF_RAW:"))
4598
0
    {
4599
0
        bAllowRGBAInterface = false;
4600
0
        pszFilename += strlen("GTIFF_RAW:");
4601
0
    }
4602
4603
0
    if (!STARTS_WITH_CI(pszFilename, "GTIFF_DIR:") ||
4604
0
        pszFilename[strlen("GTIFF_DIR:")] == '\0')
4605
0
    {
4606
0
        return nullptr;
4607
0
    }
4608
4609
    /* -------------------------------------------------------------------- */
4610
    /*      Split out filename, and dir#/offset.                            */
4611
    /* -------------------------------------------------------------------- */
4612
0
    pszFilename += strlen("GTIFF_DIR:");
4613
0
    bool bAbsolute = false;
4614
4615
0
    if (STARTS_WITH_CI(pszFilename, "off:"))
4616
0
    {
4617
0
        bAbsolute = true;
4618
0
        pszFilename += 4;
4619
0
    }
4620
4621
0
    toff_t nOffset = atol(pszFilename);
4622
0
    pszFilename += 1;
4623
4624
0
    while (*pszFilename != '\0' && pszFilename[-1] != ':')
4625
0
        ++pszFilename;
4626
4627
0
    if (*pszFilename == '\0' || nOffset == 0)
4628
0
    {
4629
0
        ReportError(
4630
0
            pszFilename, CE_Failure, CPLE_OpenFailed,
4631
0
            "Unable to extract offset or filename, should take the form:\n"
4632
0
            "GTIFF_DIR:<dir>:filename or GTIFF_DIR:off:<dir_offset>:filename");
4633
0
        return nullptr;
4634
0
    }
4635
4636
0
    if (poOpenInfo->eAccess == GA_Update)
4637
0
    {
4638
0
        ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
4639
0
                    "Opening a specific TIFF directory is not supported in "
4640
0
                    "update mode. Switching to read-only");
4641
0
    }
4642
4643
    /* -------------------------------------------------------------------- */
4644
    /*      Try opening the dataset.                                        */
4645
    /* -------------------------------------------------------------------- */
4646
0
    GTiffOneTimeInit();
4647
4648
0
    const char *pszFlag = poOpenInfo->eAccess == GA_Update ? "r+DC" : "rDOC";
4649
0
    VSILFILE *l_fpL = VSIFOpenL(pszFilename, pszFlag);
4650
0
    if (l_fpL == nullptr)
4651
0
        return nullptr;
4652
0
    TIFF *l_hTIFF = VSI_TIFFOpen(pszFilename, pszFlag, l_fpL);
4653
0
    if (l_hTIFF == nullptr)
4654
0
    {
4655
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
4656
0
        return nullptr;
4657
0
    }
4658
4659
    /* -------------------------------------------------------------------- */
4660
    /*      If a directory was requested by index, advance to it now.       */
4661
    /* -------------------------------------------------------------------- */
4662
0
    if (!bAbsolute)
4663
0
    {
4664
0
        const toff_t nOffsetRequested = nOffset;
4665
0
        while (nOffset > 1)
4666
0
        {
4667
0
            if (TIFFReadDirectory(l_hTIFF) == 0)
4668
0
            {
4669
0
                XTIFFClose(l_hTIFF);
4670
0
                ReportError(pszFilename, CE_Failure, CPLE_OpenFailed,
4671
0
                            "Requested directory %lu not found.",
4672
0
                            static_cast<long unsigned int>(nOffsetRequested));
4673
0
                CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
4674
0
                return nullptr;
4675
0
            }
4676
0
            nOffset--;
4677
0
        }
4678
4679
0
        nOffset = TIFFCurrentDirOffset(l_hTIFF);
4680
0
    }
4681
4682
    /* -------------------------------------------------------------------- */
4683
    /*      Create a corresponding GDALDataset.                             */
4684
    /* -------------------------------------------------------------------- */
4685
0
    auto poDS = std::make_unique<GTiffDataset>();
4686
0
    poDS->SetDescription(poOpenInfo->pszFilename);
4687
0
    poDS->m_osFilename = pszFilename;
4688
0
    poDS->m_fpL = l_fpL;
4689
0
    poDS->m_hTIFF = l_hTIFF;
4690
0
    poDS->m_bSingleIFDOpened = true;
4691
4692
0
    if (!EQUAL(pszFilename, poOpenInfo->pszFilename) &&
4693
0
        !STARTS_WITH_CI(poOpenInfo->pszFilename, "GTIFF_RAW:"))
4694
0
    {
4695
0
        poDS->SetPhysicalFilename(pszFilename);
4696
0
        poDS->SetSubdatasetName(poOpenInfo->pszFilename);
4697
0
    }
4698
4699
0
    if (poOpenInfo->AreSiblingFilesLoaded())
4700
0
        poDS->oOvManager.TransferSiblingFiles(poOpenInfo->StealSiblingFiles());
4701
4702
0
    if (poDS->OpenOffset(l_hTIFF, nOffset, poOpenInfo->eAccess,
4703
0
                         bAllowRGBAInterface, true) != CE_None)
4704
0
    {
4705
0
        return nullptr;
4706
0
    }
4707
4708
0
    return poDS.release();
4709
0
}
4710
4711
/************************************************************************/
4712
/*                   ConvertTransferFunctionToString()                  */
4713
/*                                                                      */
4714
/*      Convert a transfer function table into a string.                */
4715
/*      Used by LoadICCProfile().                                       */
4716
/************************************************************************/
4717
static CPLString ConvertTransferFunctionToString(const uint16_t *pTable,
4718
                                                 uint32_t nTableEntries)
4719
0
{
4720
0
    CPLString sValue;
4721
4722
0
    for (uint32_t i = 0; i < nTableEntries; ++i)
4723
0
    {
4724
0
        if (i > 0)
4725
0
            sValue += ", ";
4726
0
        sValue += CPLSPrintf("%d", static_cast<uint32_t>(pTable[i]));
4727
0
    }
4728
4729
0
    return sValue;
4730
0
}
4731
4732
/************************************************************************/
4733
/*                             LoadICCProfile()                         */
4734
/*                                                                      */
4735
/*      Load ICC Profile or colorimetric data into metadata             */
4736
/************************************************************************/
4737
4738
void GTiffDataset::LoadICCProfile()
4739
0
{
4740
0
    if (m_bICCMetadataLoaded)
4741
0
        return;
4742
0
    m_bICCMetadataLoaded = true;
4743
4744
0
    uint32_t nEmbedLen = 0;
4745
0
    uint8_t *pEmbedBuffer = nullptr;
4746
4747
0
    if (TIFFGetField(m_hTIFF, TIFFTAG_ICCPROFILE, &nEmbedLen, &pEmbedBuffer))
4748
0
    {
4749
0
        char *pszBase64Profile = CPLBase64Encode(
4750
0
            nEmbedLen, reinterpret_cast<const GByte *>(pEmbedBuffer));
4751
4752
0
        m_oGTiffMDMD.SetMetadataItem("SOURCE_ICC_PROFILE", pszBase64Profile,
4753
0
                                     "COLOR_PROFILE");
4754
4755
0
        CPLFree(pszBase64Profile);
4756
4757
0
        return;
4758
0
    }
4759
4760
    // Check for colorimetric tiff.
4761
0
    float *pCHR = nullptr;
4762
0
    float *pWP = nullptr;
4763
0
    uint16_t *pTFR = nullptr;
4764
0
    uint16_t *pTFG = nullptr;
4765
0
    uint16_t *pTFB = nullptr;
4766
0
    uint16_t *pTransferRange = nullptr;
4767
4768
0
    if (TIFFGetField(m_hTIFF, TIFFTAG_PRIMARYCHROMATICITIES, &pCHR))
4769
0
    {
4770
0
        if (TIFFGetField(m_hTIFF, TIFFTAG_WHITEPOINT, &pWP))
4771
0
        {
4772
0
            if (m_nBitsPerSample > 24 ||
4773
0
                !TIFFGetFieldDefaulted(m_hTIFF, TIFFTAG_TRANSFERFUNCTION, &pTFR,
4774
0
                                       &pTFG, &pTFB) ||
4775
0
                pTFR == nullptr || pTFG == nullptr || pTFB == nullptr)
4776
0
            {
4777
0
                return;
4778
0
            }
4779
4780
0
            const int TIFFTAG_TRANSFERRANGE = 0x0156;
4781
0
            TIFFGetFieldDefaulted(m_hTIFF, TIFFTAG_TRANSFERRANGE,
4782
0
                                  &pTransferRange);
4783
4784
            // Set all the colorimetric metadata.
4785
0
            m_oGTiffMDMD.SetMetadataItem(
4786
0
                "SOURCE_PRIMARIES_RED",
4787
0
                CPLString().Printf("%.9f, %.9f, 1.0",
4788
0
                                   static_cast<double>(pCHR[0]),
4789
0
                                   static_cast<double>(pCHR[1])),
4790
0
                "COLOR_PROFILE");
4791
0
            m_oGTiffMDMD.SetMetadataItem(
4792
0
                "SOURCE_PRIMARIES_GREEN",
4793
0
                CPLString().Printf("%.9f, %.9f, 1.0",
4794
0
                                   static_cast<double>(pCHR[2]),
4795
0
                                   static_cast<double>(pCHR[3])),
4796
0
                "COLOR_PROFILE");
4797
0
            m_oGTiffMDMD.SetMetadataItem(
4798
0
                "SOURCE_PRIMARIES_BLUE",
4799
0
                CPLString().Printf("%.9f, %.9f, 1.0",
4800
0
                                   static_cast<double>(pCHR[4]),
4801
0
                                   static_cast<double>(pCHR[5])),
4802
0
                "COLOR_PROFILE");
4803
4804
0
            m_oGTiffMDMD.SetMetadataItem(
4805
0
                "SOURCE_WHITEPOINT",
4806
0
                CPLString().Printf("%.9f, %.9f, 1.0",
4807
0
                                   static_cast<double>(pWP[0]),
4808
0
                                   static_cast<double>(pWP[1])),
4809
0
                "COLOR_PROFILE");
4810
4811
            // Set transfer function metadata.
4812
4813
            // Get length of table.
4814
0
            const uint32_t nTransferFunctionLength = 1 << m_nBitsPerSample;
4815
4816
0
            m_oGTiffMDMD.SetMetadataItem(
4817
0
                "TIFFTAG_TRANSFERFUNCTION_RED",
4818
0
                ConvertTransferFunctionToString(pTFR, nTransferFunctionLength),
4819
0
                "COLOR_PROFILE");
4820
4821
0
            m_oGTiffMDMD.SetMetadataItem(
4822
0
                "TIFFTAG_TRANSFERFUNCTION_GREEN",
4823
0
                ConvertTransferFunctionToString(pTFG, nTransferFunctionLength),
4824
0
                "COLOR_PROFILE");
4825
4826
0
            m_oGTiffMDMD.SetMetadataItem(
4827
0
                "TIFFTAG_TRANSFERFUNCTION_BLUE",
4828
0
                ConvertTransferFunctionToString(pTFB, nTransferFunctionLength),
4829
0
                "COLOR_PROFILE");
4830
4831
            // Set transfer range.
4832
0
            if (pTransferRange)
4833
0
            {
4834
0
                m_oGTiffMDMD.SetMetadataItem(
4835
0
                    "TIFFTAG_TRANSFERRANGE_BLACK",
4836
0
                    CPLString().Printf("%d, %d, %d",
4837
0
                                       static_cast<int>(pTransferRange[0]),
4838
0
                                       static_cast<int>(pTransferRange[2]),
4839
0
                                       static_cast<int>(pTransferRange[4])),
4840
0
                    "COLOR_PROFILE");
4841
0
                m_oGTiffMDMD.SetMetadataItem(
4842
0
                    "TIFFTAG_TRANSFERRANGE_WHITE",
4843
0
                    CPLString().Printf("%d, %d, %d",
4844
0
                                       static_cast<int>(pTransferRange[1]),
4845
0
                                       static_cast<int>(pTransferRange[3]),
4846
0
                                       static_cast<int>(pTransferRange[5])),
4847
0
                    "COLOR_PROFILE");
4848
0
            }
4849
0
        }
4850
0
    }
4851
0
}
4852
4853
/************************************************************************/
4854
/*                             OpenOffset()                             */
4855
/*                                                                      */
4856
/*      Initialize the GTiffDataset based on a passed in file           */
4857
/*      handle, and directory offset to utilize.  This is called for    */
4858
/*      full res, and overview pages.                                   */
4859
/************************************************************************/
4860
4861
CPLErr GTiffDataset::OpenOffset(TIFF *hTIFFIn, toff_t nDirOffsetIn,
4862
                                GDALAccess eAccessIn, bool bAllowRGBAInterface,
4863
                                bool bReadGeoTransform)
4864
4865
0
{
4866
0
    if (!hTIFFIn)
4867
0
        return CE_Failure;
4868
4869
0
    eAccess = eAccessIn;
4870
4871
0
    m_hTIFF = hTIFFIn;
4872
4873
0
    m_nDirOffset = nDirOffsetIn;
4874
4875
0
    if (!SetDirectory())
4876
0
        return CE_Failure;
4877
4878
    /* -------------------------------------------------------------------- */
4879
    /*      Capture some information from the file that is of interest.     */
4880
    /* -------------------------------------------------------------------- */
4881
0
    uint32_t nXSize = 0;
4882
0
    uint32_t nYSize = 0;
4883
0
    TIFFGetField(m_hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize);
4884
0
    TIFFGetField(m_hTIFF, TIFFTAG_IMAGELENGTH, &nYSize);
4885
4886
    // Unlikely to occur, but could happen on a disk full situation.
4887
0
    if (nXSize == 0 || nYSize == 0)
4888
0
        return CE_Failure;
4889
4890
0
    if (nXSize > INT_MAX || nYSize > INT_MAX)
4891
0
    {
4892
        // GDAL only supports signed 32bit dimensions.
4893
0
        ReportError(CE_Failure, CPLE_NotSupported,
4894
0
                    "Too large image size: %u x %u", nXSize, nYSize);
4895
0
        return CE_Failure;
4896
0
    }
4897
0
    nRasterXSize = nXSize;
4898
0
    nRasterYSize = nYSize;
4899
4900
0
    if (!TIFFGetField(m_hTIFF, TIFFTAG_SAMPLESPERPIXEL, &m_nSamplesPerPixel))
4901
0
        nBands = 1;
4902
0
    else
4903
0
        nBands = m_nSamplesPerPixel;
4904
4905
0
    if (!TIFFGetField(m_hTIFF, TIFFTAG_BITSPERSAMPLE, &(m_nBitsPerSample)))
4906
0
        m_nBitsPerSample = 1;
4907
4908
0
    if (!TIFFGetField(m_hTIFF, TIFFTAG_PLANARCONFIG, &(m_nPlanarConfig)))
4909
0
        m_nPlanarConfig = PLANARCONFIG_CONTIG;
4910
4911
0
    if (!TIFFGetField(m_hTIFF, TIFFTAG_PHOTOMETRIC, &(m_nPhotometric)))
4912
0
        m_nPhotometric = PHOTOMETRIC_MINISBLACK;
4913
4914
0
    if (!TIFFGetField(m_hTIFF, TIFFTAG_SAMPLEFORMAT, &(m_nSampleFormat)))
4915
0
        m_nSampleFormat = SAMPLEFORMAT_UINT;
4916
4917
0
    if (!TIFFGetField(m_hTIFF, TIFFTAG_COMPRESSION, &(m_nCompression)))
4918
0
        m_nCompression = COMPRESSION_NONE;
4919
4920
0
    if (m_nCompression != COMPRESSION_NONE &&
4921
0
        !TIFFIsCODECConfigured(m_nCompression))
4922
0
    {
4923
0
        const char *pszCompressionMethodName =
4924
0
            GTIFFGetCompressionMethodName(m_nCompression);
4925
0
        if (pszCompressionMethodName)
4926
0
        {
4927
0
            ReportError(CE_Failure, CPLE_AppDefined,
4928
0
                        "Cannot open TIFF file due to missing codec %s.",
4929
0
                        pszCompressionMethodName);
4930
0
        }
4931
0
        else
4932
0
        {
4933
0
            ReportError(
4934
0
                CE_Failure, CPLE_AppDefined,
4935
0
                "Cannot open TIFF file due to missing codec of code %d.",
4936
0
                m_nCompression);
4937
0
        }
4938
0
        return CE_Failure;
4939
0
    }
4940
4941
    /* -------------------------------------------------------------------- */
4942
    /*      YCbCr JPEG compressed images should be translated on the fly    */
4943
    /*      to RGB by libtiff/libjpeg unless specifically requested         */
4944
    /*      otherwise.                                                      */
4945
    /* -------------------------------------------------------------------- */
4946
0
    if (m_nCompression == COMPRESSION_JPEG &&
4947
0
        m_nPhotometric == PHOTOMETRIC_YCBCR &&
4948
0
        CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
4949
0
    {
4950
0
        m_oGTiffMDMD.SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr",
4951
0
                                     "IMAGE_STRUCTURE");
4952
0
        int nColorMode = 0;
4953
0
        if (!TIFFGetField(m_hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode) ||
4954
0
            nColorMode != JPEGCOLORMODE_RGB)
4955
0
        {
4956
0
            TIFFSetField(m_hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
4957
0
        }
4958
0
    }
4959
4960
    /* -------------------------------------------------------------------- */
4961
    /*      Get strip/tile layout.                                          */
4962
    /* -------------------------------------------------------------------- */
4963
0
    if (TIFFIsTiled(m_hTIFF))
4964
0
    {
4965
0
        uint32_t l_nBlockXSize = 0;
4966
0
        uint32_t l_nBlockYSize = 0;
4967
0
        TIFFGetField(m_hTIFF, TIFFTAG_TILEWIDTH, &(l_nBlockXSize));
4968
0
        TIFFGetField(m_hTIFF, TIFFTAG_TILELENGTH, &(l_nBlockYSize));
4969
0
        if (l_nBlockXSize > INT_MAX || l_nBlockYSize > INT_MAX)
4970
0
        {
4971
0
            ReportError(CE_Failure, CPLE_NotSupported,
4972
0
                        "Too large block size: %u x %u", l_nBlockXSize,
4973
0
                        l_nBlockYSize);
4974
0
            return CE_Failure;
4975
0
        }
4976
0
        m_nBlockXSize = static_cast<int>(l_nBlockXSize);
4977
0
        m_nBlockYSize = static_cast<int>(l_nBlockYSize);
4978
0
    }
4979
0
    else
4980
0
    {
4981
0
        if (!TIFFGetField(m_hTIFF, TIFFTAG_ROWSPERSTRIP, &(m_nRowsPerStrip)))
4982
0
        {
4983
0
            ReportError(CE_Warning, CPLE_AppDefined,
4984
0
                        "RowsPerStrip not defined ... assuming all one strip.");
4985
0
            m_nRowsPerStrip = nYSize;  // Dummy value.
4986
0
        }
4987
4988
        // If the rows per strip is larger than the file we will get
4989
        // confused.  libtiff internally will treat the rowsperstrip as
4990
        // the image height and it is best if we do too. (#4468)
4991
0
        if (m_nRowsPerStrip > static_cast<uint32_t>(nRasterYSize))
4992
0
            m_nRowsPerStrip = nRasterYSize;
4993
4994
0
        m_nBlockXSize = nRasterXSize;
4995
0
        m_nBlockYSize = m_nRowsPerStrip;
4996
0
    }
4997
4998
0
    if (!ComputeBlocksPerColRowAndBand(nBands))
4999
0
        return CE_Failure;
5000
5001
    /* -------------------------------------------------------------------- */
5002
    /*      Should we handle this using the GTiffBitmapBand?                */
5003
    /* -------------------------------------------------------------------- */
5004
0
    bool bTreatAsBitmap = false;
5005
5006
0
    if (m_nBitsPerSample == 1 && nBands == 1)
5007
0
    {
5008
0
        bTreatAsBitmap = true;
5009
5010
        // Lets treat large "one row" bitmaps using the scanline api.
5011
0
        if (!TIFFIsTiled(m_hTIFF) && m_nBlockYSize == nRasterYSize &&
5012
0
            nRasterYSize > 2000
5013
            // libtiff does not support reading JBIG files with
5014
            // TIFFReadScanline().
5015
0
            && m_nCompression != COMPRESSION_JBIG)
5016
0
        {
5017
0
            m_bTreatAsSplitBitmap = true;
5018
0
        }
5019
0
    }
5020
5021
    /* -------------------------------------------------------------------- */
5022
    /*      Should we treat this via the RGBA interface?                    */
5023
    /* -------------------------------------------------------------------- */
5024
0
    bool bTreatAsRGBA = false;
5025
0
    if (
5026
0
#ifdef DEBUG
5027
0
        CPLTestBool(CPLGetConfigOption("GTIFF_FORCE_RGBA", "NO")) ||
5028
0
#endif
5029
0
        (bAllowRGBAInterface && !bTreatAsBitmap && !(m_nBitsPerSample > 8) &&
5030
0
         (m_nPhotometric == PHOTOMETRIC_CIELAB ||
5031
0
          m_nPhotometric == PHOTOMETRIC_LOGL ||
5032
0
          m_nPhotometric == PHOTOMETRIC_LOGLUV ||
5033
0
          m_nPhotometric == PHOTOMETRIC_SEPARATED ||
5034
0
          (m_nPhotometric == PHOTOMETRIC_YCBCR &&
5035
0
           m_nCompression != COMPRESSION_JPEG))))
5036
0
    {
5037
0
        char szMessage[1024] = {};
5038
5039
0
        if (TIFFRGBAImageOK(m_hTIFF, szMessage) == 1)
5040
0
        {
5041
0
            const char *pszSourceColorSpace = nullptr;
5042
0
            nBands = 4;
5043
0
            switch (m_nPhotometric)
5044
0
            {
5045
0
                case PHOTOMETRIC_CIELAB:
5046
0
                    pszSourceColorSpace = "CIELAB";
5047
0
                    break;
5048
0
                case PHOTOMETRIC_LOGL:
5049
0
                    pszSourceColorSpace = "LOGL";
5050
0
                    break;
5051
0
                case PHOTOMETRIC_LOGLUV:
5052
0
                    pszSourceColorSpace = "LOGLUV";
5053
0
                    break;
5054
0
                case PHOTOMETRIC_SEPARATED:
5055
0
                    pszSourceColorSpace = "CMYK";
5056
0
                    break;
5057
0
                case PHOTOMETRIC_YCBCR:
5058
0
                    pszSourceColorSpace = "YCbCr";
5059
0
                    nBands = 3;  // probably true for other photometric values
5060
0
                    break;
5061
0
            }
5062
0
            if (pszSourceColorSpace)
5063
0
                m_oGTiffMDMD.SetMetadataItem("SOURCE_COLOR_SPACE",
5064
0
                                             pszSourceColorSpace,
5065
0
                                             "IMAGE_STRUCTURE");
5066
0
            bTreatAsRGBA = true;
5067
0
        }
5068
0
        else
5069
0
        {
5070
0
            CPLDebug("GTiff", "TIFFRGBAImageOK says:\n%s", szMessage);
5071
0
        }
5072
0
    }
5073
5074
    // libtiff has various issues with OJPEG compression and chunky-strip
5075
    // support with the "classic" scanline/strip/tile interfaces, and that
5076
    // wouldn't work either, so better bail out.
5077
0
    if (m_nCompression == COMPRESSION_OJPEG && !bTreatAsRGBA)
5078
0
    {
5079
0
        ReportError(
5080
0
            CE_Failure, CPLE_NotSupported,
5081
0
            "Old-JPEG compression only supported through RGBA interface, "
5082
0
            "which cannot be used probably because the file is corrupted");
5083
0
        return CE_Failure;
5084
0
    }
5085
5086
    // If photometric is YCbCr, scanline/strip/tile interfaces assumes that
5087
    // we are ready with downsampled data. And we are not.
5088
0
    if (m_nCompression != COMPRESSION_JPEG &&
5089
0
        m_nCompression != COMPRESSION_OJPEG &&
5090
0
        m_nPhotometric == PHOTOMETRIC_YCBCR &&
5091
0
        m_nPlanarConfig == PLANARCONFIG_CONTIG && !bTreatAsRGBA)
5092
0
    {
5093
0
        uint16_t nF1, nF2;
5094
0
        TIFFGetFieldDefaulted(m_hTIFF, TIFFTAG_YCBCRSUBSAMPLING, &nF1, &nF2);
5095
0
        if (nF1 != 1 || nF2 != 1)
5096
0
        {
5097
0
            ReportError(CE_Failure, CPLE_AppDefined,
5098
0
                        "Cannot open TIFF file with YCbCr, subsampling and "
5099
0
                        "BitsPerSample > 8 that is not JPEG compressed");
5100
0
            return CE_Failure;
5101
0
        }
5102
0
    }
5103
5104
    /* -------------------------------------------------------------------- */
5105
    /*      Should we treat this via the split interface?                   */
5106
    /* -------------------------------------------------------------------- */
5107
0
    if (!TIFFIsTiled(m_hTIFF) && m_nBitsPerSample == 8 &&
5108
0
        m_nBlockYSize == nRasterYSize && nRasterYSize > 2000 && !bTreatAsRGBA &&
5109
0
        CPLTestBool(CPLGetConfigOption("GDAL_ENABLE_TIFF_SPLIT", "YES")))
5110
0
    {
5111
0
        m_bTreatAsSplit = true;
5112
0
    }
5113
5114
    /* -------------------------------------------------------------------- */
5115
    /*      Should we treat this via the odd bits interface?                */
5116
    /* -------------------------------------------------------------------- */
5117
0
    bool bTreatAsOdd = false;
5118
0
    if (m_nSampleFormat == SAMPLEFORMAT_IEEEFP)
5119
0
    {
5120
0
        if (m_nBitsPerSample == 24)
5121
0
            bTreatAsOdd = true;
5122
0
        else if (m_nBitsPerSample != 16 && m_nBitsPerSample != 32 &&
5123
0
                 m_nBitsPerSample != 64)
5124
0
        {
5125
0
            ReportError(CE_Failure, CPLE_AppDefined,
5126
0
                        "Cannot open TIFF file with SampleFormat=IEEEFP "
5127
0
                        "and BitsPerSample=%d",
5128
0
                        m_nBitsPerSample);
5129
0
            return CE_Failure;
5130
0
        }
5131
0
    }
5132
0
    else if (!bTreatAsRGBA && !bTreatAsBitmap && m_nBitsPerSample != 8 &&
5133
0
             m_nBitsPerSample != 16 && m_nBitsPerSample != 32 &&
5134
0
             m_nBitsPerSample != 64 && m_nBitsPerSample != 128)
5135
0
    {
5136
0
        bTreatAsOdd = true;
5137
0
    }
5138
5139
/* -------------------------------------------------------------------- */
5140
/*      We can't support 'chunks' bigger than 2GB on 32 bit builds      */
5141
/* -------------------------------------------------------------------- */
5142
#if SIZEOF_VOIDP == 4
5143
    uint64_t nChunkSize = 0;
5144
    if (m_bTreatAsSplit || m_bTreatAsSplitBitmap)
5145
    {
5146
        nChunkSize = TIFFScanlineSize64(m_hTIFF);
5147
    }
5148
    else
5149
    {
5150
        if (TIFFIsTiled(m_hTIFF))
5151
            nChunkSize = TIFFTileSize64(m_hTIFF);
5152
        else
5153
            nChunkSize = TIFFStripSize64(m_hTIFF);
5154
    }
5155
    if (bTreatAsRGBA)
5156
    {
5157
        nChunkSize =
5158
            std::max(nChunkSize,
5159
                     4 * static_cast<uint64_t>(m_nBlockXSize) * m_nBlockYSize);
5160
    }
5161
    if (nChunkSize > static_cast<uint64_t>(INT_MAX))
5162
    {
5163
        ReportError(CE_Failure, CPLE_NotSupported,
5164
                    "Scanline/tile/strip size bigger than 2GB unsupported "
5165
                    "on 32-bit builds.");
5166
        return CE_Failure;
5167
    }
5168
#endif
5169
5170
0
    const bool bMinIsWhite = m_nPhotometric == PHOTOMETRIC_MINISWHITE;
5171
5172
    /* -------------------------------------------------------------------- */
5173
    /*      Check for NODATA                                                */
5174
    /* -------------------------------------------------------------------- */
5175
0
    char *pszText = nullptr;
5176
0
    if (TIFFGetField(m_hTIFF, TIFFTAG_GDAL_NODATA, &pszText) &&
5177
0
        !EQUAL(pszText, ""))
5178
0
    {
5179
0
        if (m_nBitsPerSample > 32 && m_nBitsPerSample <= 64 &&
5180
0
            m_nSampleFormat == SAMPLEFORMAT_INT)
5181
0
        {
5182
0
            m_bNoDataSetAsInt64 = true;
5183
0
            m_nNoDataValueInt64 =
5184
0
                static_cast<int64_t>(std::strtoll(pszText, nullptr, 10));
5185
0
        }
5186
0
        else if (m_nBitsPerSample > 32 && m_nBitsPerSample <= 64 &&
5187
0
                 m_nSampleFormat == SAMPLEFORMAT_UINT)
5188
0
        {
5189
0
            m_bNoDataSetAsUInt64 = true;
5190
0
            m_nNoDataValueUInt64 =
5191
0
                static_cast<uint64_t>(std::strtoull(pszText, nullptr, 10));
5192
0
        }
5193
0
        else
5194
0
        {
5195
0
            m_bNoDataSet = true;
5196
0
            m_dfNoDataValue = CPLAtofM(pszText);
5197
0
            if (m_nBitsPerSample == 32 &&
5198
0
                m_nSampleFormat == SAMPLEFORMAT_IEEEFP)
5199
0
            {
5200
0
                m_dfNoDataValue =
5201
0
                    GDALAdjustNoDataCloseToFloatMax(m_dfNoDataValue);
5202
0
                m_dfNoDataValue =
5203
0
                    static_cast<double>(static_cast<float>(m_dfNoDataValue));
5204
0
            }
5205
0
        }
5206
0
    }
5207
5208
    /* -------------------------------------------------------------------- */
5209
    /*      Capture the color table if there is one.                        */
5210
    /* -------------------------------------------------------------------- */
5211
0
    unsigned short *panRed = nullptr;
5212
0
    unsigned short *panGreen = nullptr;
5213
0
    unsigned short *panBlue = nullptr;
5214
5215
0
    if (bTreatAsRGBA || m_nBitsPerSample > 16 ||
5216
0
        TIFFGetField(m_hTIFF, TIFFTAG_COLORMAP, &panRed, &panGreen, &panBlue) ==
5217
0
            0)
5218
0
    {
5219
        // Build inverted palette if we have inverted photometric.
5220
        // Pixel values remains unchanged.  Avoid doing this for *deep*
5221
        // data types (per #1882)
5222
0
        if (m_nBitsPerSample <= 16 && m_nPhotometric == PHOTOMETRIC_MINISWHITE)
5223
0
        {
5224
0
            m_poColorTable = std::make_unique<GDALColorTable>();
5225
0
            const int nColorCount = 1 << m_nBitsPerSample;
5226
5227
0
            for (int iColor = 0; iColor < nColorCount; ++iColor)
5228
0
            {
5229
0
                const short nValue = static_cast<short>(
5230
0
                    ((255 * (nColorCount - 1 - iColor)) / (nColorCount - 1)));
5231
0
                const GDALColorEntry oEntry = {nValue, nValue, nValue,
5232
0
                                               static_cast<short>(255)};
5233
0
                m_poColorTable->SetColorEntry(iColor, &oEntry);
5234
0
            }
5235
5236
0
            m_nPhotometric = PHOTOMETRIC_PALETTE;
5237
0
        }
5238
0
        else
5239
0
        {
5240
0
            m_poColorTable.reset();
5241
0
        }
5242
0
    }
5243
0
    else
5244
0
    {
5245
0
        const int nColorCount = 1 << m_nBitsPerSample;
5246
0
        m_poColorTable = gdal::tiff_common::TIFFColorMapTagToColorTable(
5247
0
            panRed, panGreen, panBlue, nColorCount, m_nColorTableMultiplier,
5248
0
            DEFAULT_COLOR_TABLE_MULTIPLIER_257, m_bNoDataSet, m_dfNoDataValue);
5249
0
    }
5250
5251
    /* -------------------------------------------------------------------- */
5252
    /*      Create band information objects.                                */
5253
    /* -------------------------------------------------------------------- */
5254
0
    for (int iBand = 0; iBand < nBands; ++iBand)
5255
0
    {
5256
0
        if (bTreatAsRGBA)
5257
0
            SetBand(iBand + 1,
5258
0
                    std::make_unique<GTiffRGBABand>(this, iBand + 1));
5259
0
        else if (m_bTreatAsSplitBitmap)
5260
0
            SetBand(iBand + 1,
5261
0
                    std::make_unique<GTiffSplitBitmapBand>(this, iBand + 1));
5262
0
        else if (m_bTreatAsSplit)
5263
0
            SetBand(iBand + 1,
5264
0
                    std::make_unique<GTiffSplitBand>(this, iBand + 1));
5265
0
        else if (bTreatAsBitmap)
5266
0
            SetBand(iBand + 1,
5267
0
                    std::make_unique<GTiffBitmapBand>(this, iBand + 1));
5268
0
        else if (bTreatAsOdd)
5269
0
            SetBand(iBand + 1,
5270
0
                    std::make_unique<GTiffOddBitsBand>(this, iBand + 1));
5271
0
        else
5272
0
            SetBand(iBand + 1,
5273
0
                    std::make_unique<GTiffRasterBand>(this, iBand + 1));
5274
0
    }
5275
5276
0
    if (GetRasterBand(1)->GetRasterDataType() == GDT_Unknown)
5277
0
    {
5278
0
        ReportError(CE_Failure, CPLE_NotSupported,
5279
0
                    "Unsupported TIFF configuration: BitsPerSample(=%d) and "
5280
0
                    "SampleType(=%d)",
5281
0
                    m_nBitsPerSample, m_nSampleFormat);
5282
0
        return CE_Failure;
5283
0
    }
5284
5285
0
    m_bReadGeoTransform = bReadGeoTransform;
5286
5287
    /* -------------------------------------------------------------------- */
5288
    /*      Capture some other potentially interesting information.         */
5289
    /* -------------------------------------------------------------------- */
5290
0
    char szWorkMDI[200] = {};
5291
0
    uint16_t nShort = 0;
5292
5293
0
    const auto *pasTIFFTags = GetTIFFTags();
5294
0
    for (size_t iTag = 0; pasTIFFTags[iTag].pszTagName; ++iTag)
5295
0
    {
5296
0
        if (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_STRING)
5297
0
        {
5298
0
            if (TIFFGetField(m_hTIFF, pasTIFFTags[iTag].nTagVal, &pszText))
5299
0
                m_oGTiffMDMD.SetMetadataItem(pasTIFFTags[iTag].pszTagName,
5300
0
                                             pszText);
5301
0
        }
5302
0
        else if (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_FLOAT)
5303
0
        {
5304
0
            float fVal = 0.0;
5305
0
            if (TIFFGetField(m_hTIFF, pasTIFFTags[iTag].nTagVal, &fVal))
5306
0
            {
5307
0
                CPLsnprintf(szWorkMDI, sizeof(szWorkMDI), "%.8g",
5308
0
                            static_cast<double>(fVal));
5309
0
                m_oGTiffMDMD.SetMetadataItem(pasTIFFTags[iTag].pszTagName,
5310
0
                                             szWorkMDI);
5311
0
            }
5312
0
        }
5313
0
        else if (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_SHORT &&
5314
0
                 pasTIFFTags[iTag].nTagVal != TIFFTAG_RESOLUTIONUNIT)
5315
0
        {
5316
0
            if (TIFFGetField(m_hTIFF, pasTIFFTags[iTag].nTagVal, &nShort))
5317
0
            {
5318
0
                snprintf(szWorkMDI, sizeof(szWorkMDI), "%d", nShort);
5319
0
                m_oGTiffMDMD.SetMetadataItem(pasTIFFTags[iTag].pszTagName,
5320
0
                                             szWorkMDI);
5321
0
            }
5322
0
        }
5323
0
        else if (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_BYTE_STRING)
5324
0
        {
5325
0
            uint32_t nCount = 0;
5326
0
            if (TIFFGetField(m_hTIFF, pasTIFFTags[iTag].nTagVal, &nCount,
5327
0
                             &pszText))
5328
0
            {
5329
0
                std::string osStr;
5330
0
                osStr.assign(pszText, nCount);
5331
0
                m_oGTiffMDMD.SetMetadataItem(pasTIFFTags[iTag].pszTagName,
5332
0
                                             osStr.c_str());
5333
0
            }
5334
0
        }
5335
0
    }
5336
5337
0
    if (TIFFGetField(m_hTIFF, TIFFTAG_RESOLUTIONUNIT, &nShort))
5338
0
    {
5339
0
        if (nShort == RESUNIT_NONE)
5340
0
            snprintf(szWorkMDI, sizeof(szWorkMDI), "%d (unitless)", nShort);
5341
0
        else if (nShort == RESUNIT_INCH)
5342
0
            snprintf(szWorkMDI, sizeof(szWorkMDI), "%d (pixels/inch)", nShort);
5343
0
        else if (nShort == RESUNIT_CENTIMETER)
5344
0
            snprintf(szWorkMDI, sizeof(szWorkMDI), "%d (pixels/cm)", nShort);
5345
0
        else
5346
0
            snprintf(szWorkMDI, sizeof(szWorkMDI), "%d", nShort);
5347
0
        m_oGTiffMDMD.SetMetadataItem("TIFFTAG_RESOLUTIONUNIT", szWorkMDI);
5348
0
    }
5349
5350
0
    int nTagSize = 0;
5351
0
    void *pData = nullptr;
5352
0
    if (TIFFGetField(m_hTIFF, TIFFTAG_XMLPACKET, &nTagSize, &pData))
5353
0
    {
5354
0
        char *pszXMP = static_cast<char *>(VSI_MALLOC_VERBOSE(nTagSize + 1));
5355
0
        if (pszXMP)
5356
0
        {
5357
0
            memcpy(pszXMP, pData, nTagSize);
5358
0
            pszXMP[nTagSize] = '\0';
5359
5360
0
            char *apszMDList[2] = {pszXMP, nullptr};
5361
0
            m_oGTiffMDMD.SetMetadata(apszMDList, "xml:XMP");
5362
5363
0
            CPLFree(pszXMP);
5364
0
        }
5365
0
    }
5366
5367
0
    if (m_nCompression != COMPRESSION_NONE)
5368
0
    {
5369
0
        const char *pszCompressionMethodName =
5370
0
            GTIFFGetCompressionMethodName(m_nCompression);
5371
0
        if (pszCompressionMethodName)
5372
0
        {
5373
0
            m_oGTiffMDMD.SetMetadataItem(
5374
0
                "COMPRESSION", pszCompressionMethodName, "IMAGE_STRUCTURE");
5375
0
        }
5376
0
        else
5377
0
        {
5378
0
            CPLString oComp;
5379
0
            oComp.Printf("%d", m_nCompression);
5380
0
            m_oGTiffMDMD.SetMetadataItem("COMPRESSION", oComp.c_str());
5381
0
        }
5382
0
    }
5383
5384
0
    if (m_nCompression == COMPRESSION_JPEG &&
5385
0
        m_nPhotometric == PHOTOMETRIC_YCBCR)
5386
0
    {
5387
0
        m_oGTiffMDMD.SetMetadataItem("COMPRESSION", "YCbCr JPEG",
5388
0
                                     "IMAGE_STRUCTURE");
5389
0
    }
5390
0
    else if (m_nCompression == COMPRESSION_LERC)
5391
0
    {
5392
0
        uint32_t nLercParamCount = 0;
5393
0
        uint32_t *panLercParams = nullptr;
5394
0
        if (TIFFGetField(m_hTIFF, TIFFTAG_LERC_PARAMETERS, &nLercParamCount,
5395
0
                         &panLercParams) &&
5396
0
            nLercParamCount == 2)
5397
0
        {
5398
0
            memcpy(m_anLercAddCompressionAndVersion, panLercParams,
5399
0
                   sizeof(m_anLercAddCompressionAndVersion));
5400
0
        }
5401
5402
0
        uint32_t nAddVersion = LERC_ADD_COMPRESSION_NONE;
5403
0
        if (TIFFGetField(m_hTIFF, TIFFTAG_LERC_ADD_COMPRESSION, &nAddVersion) &&
5404
0
            nAddVersion != LERC_ADD_COMPRESSION_NONE)
5405
0
        {
5406
0
            if (nAddVersion == LERC_ADD_COMPRESSION_DEFLATE)
5407
0
            {
5408
0
                m_oGTiffMDMD.SetMetadataItem("COMPRESSION", "LERC_DEFLATE",
5409
0
                                             "IMAGE_STRUCTURE");
5410
0
            }
5411
0
            else if (nAddVersion == LERC_ADD_COMPRESSION_ZSTD)
5412
0
            {
5413
0
                m_oGTiffMDMD.SetMetadataItem("COMPRESSION", "LERC_ZSTD",
5414
0
                                             "IMAGE_STRUCTURE");
5415
0
            }
5416
0
        }
5417
0
        uint32_t nLercVersion = LERC_VERSION_2_4;
5418
0
        if (TIFFGetField(m_hTIFF, TIFFTAG_LERC_VERSION, &nLercVersion))
5419
0
        {
5420
0
            if (nLercVersion == LERC_VERSION_2_4)
5421
0
            {
5422
0
                m_oGTiffMDMD.SetMetadataItem("LERC_VERSION", "2.4",
5423
0
                                             "IMAGE_STRUCTURE");
5424
0
            }
5425
0
            else
5426
0
            {
5427
0
                ReportError(CE_Warning, CPLE_AppDefined,
5428
0
                            "Unknown Lerc version: %d", nLercVersion);
5429
0
            }
5430
0
        }
5431
0
    }
5432
5433
0
    if (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands != 1)
5434
0
        m_oGTiffMDMD.SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
5435
0
    else
5436
0
        m_oGTiffMDMD.SetMetadataItem("INTERLEAVE", "BAND", "IMAGE_STRUCTURE");
5437
5438
0
    if ((GetRasterBand(1)->GetRasterDataType() == GDT_UInt8 &&
5439
0
         m_nBitsPerSample != 8) ||
5440
0
        (GetRasterBand(1)->GetRasterDataType() == GDT_UInt16 &&
5441
0
         m_nBitsPerSample != 16) ||
5442
0
        ((GetRasterBand(1)->GetRasterDataType() == GDT_UInt32 ||
5443
0
          GetRasterBand(1)->GetRasterDataType() == GDT_Float32) &&
5444
0
         m_nBitsPerSample != 32))
5445
0
    {
5446
0
        for (int i = 0; i < nBands; ++i)
5447
0
            cpl::down_cast<GTiffRasterBand *>(GetRasterBand(i + 1))
5448
0
                ->m_oGTiffMDMD.SetMetadataItem(
5449
0
                    "NBITS",
5450
0
                    CPLString().Printf("%d",
5451
0
                                       static_cast<int>(m_nBitsPerSample)),
5452
0
                    "IMAGE_STRUCTURE");
5453
0
    }
5454
5455
0
    if (bMinIsWhite)
5456
0
        m_oGTiffMDMD.SetMetadataItem("MINISWHITE", "YES", "IMAGE_STRUCTURE");
5457
5458
0
    if (TIFFGetField(m_hTIFF, TIFFTAG_GDAL_METADATA, &pszText))
5459
0
    {
5460
0
        auto psRoot = CPLXMLTreeCloser(CPLParseXMLString(pszText));
5461
0
        const CPLXMLNode *psItem =
5462
0
            psRoot.get() ? CPLGetXMLNode(psRoot.get(), "=GDALMetadata")
5463
0
                         : nullptr;
5464
0
        if (psItem)
5465
0
            psItem = psItem->psChild;
5466
0
        bool bMaxZErrorFound = false;
5467
0
        bool bMaxZErrorOverviewFound = false;
5468
0
        for (; psItem != nullptr; psItem = psItem->psNext)
5469
0
        {
5470
5471
0
            if (psItem->eType != CXT_Element ||
5472
0
                !EQUAL(psItem->pszValue, "Item"))
5473
0
                continue;
5474
5475
0
            const char *pszKey = CPLGetXMLValue(psItem, "name", nullptr);
5476
0
            const char *pszValue = CPLGetXMLValue(psItem, nullptr, nullptr);
5477
0
            int nBand = atoi(CPLGetXMLValue(psItem, "sample", "-1"));
5478
0
            if (nBand < -1 || nBand > nBands - 1)
5479
0
                continue;
5480
0
            nBand++;
5481
0
            const char *pszRole = CPLGetXMLValue(psItem, "role", "");
5482
0
            const char *pszDomain = CPLGetXMLValue(psItem, "domain", "");
5483
5484
0
            if (nBand > 0 && pszKey &&
5485
0
                strcmp(pszKey, DEFAULT_RASTER_ATTRIBUTE_TABLE) == 0 &&
5486
0
                strcmp(pszRole, RAT_ROLE) == 0)
5487
0
            {
5488
0
                const CPLXMLNode *psValueNode = psItem->psChild;
5489
0
                while (psValueNode && psValueNode->eType != CXT_Element)
5490
0
                    psValueNode = psValueNode->psNext;
5491
0
                if (psValueNode && psValueNode->eType == CXT_Element)
5492
0
                {
5493
0
                    auto poRAT =
5494
0
                        std::make_unique<GDALDefaultRasterAttributeTable>();
5495
0
                    if (poRAT->XMLInit(psValueNode, nullptr) == CE_None)
5496
0
                    {
5497
0
                        GTiffRasterBand *poBand =
5498
0
                            cpl::down_cast<GTiffRasterBand *>(
5499
0
                                GetRasterBand(nBand));
5500
0
                        if (poBand != nullptr)
5501
0
                        {
5502
0
                            poBand->m_poRAT = std::move(poRAT);
5503
0
                            poBand->m_bRATSet = true;
5504
0
                        }
5505
0
                    }
5506
0
                }
5507
0
            }
5508
5509
0
            if (pszKey == nullptr || pszValue == nullptr)
5510
0
                continue;
5511
0
            if (EQUAL(pszDomain, "IMAGE_STRUCTURE"))
5512
0
            {
5513
0
                if (EQUAL(pszKey, "OVERVIEW_RESAMPLING"))
5514
0
                {
5515
0
                    m_oGTiffMDMD.SetMetadataItem(pszKey, pszValue,
5516
0
                                                 "IMAGE_STRUCTURE");
5517
0
                }
5518
0
                else if (EQUAL(pszKey, "INTERLEAVE"))
5519
0
                {
5520
0
                    if (EQUAL(pszValue, "TILE"))
5521
0
                    {
5522
0
                        m_bTileInterleave = true;
5523
0
                        m_oGTiffMDMD.SetMetadataItem("INTERLEAVE", "TILE",
5524
0
                                                     "IMAGE_STRUCTURE");
5525
0
                    }
5526
0
                    else
5527
0
                    {
5528
0
                        CPLDebug("GTiff",
5529
0
                                 "Unhandled INTERLEAVE=%s found in "
5530
0
                                 "GDAL_METADATA tag",
5531
0
                                 pszValue);
5532
0
                    }
5533
0
                }
5534
0
                else if (m_nCompression == COMPRESSION_WEBP &&
5535
0
                         EQUAL(pszKey, "COMPRESSION_REVERSIBILITY"))
5536
0
                {
5537
0
                    if (EQUAL(pszValue, "LOSSLESS"))
5538
0
                        m_bWebPLossless = true;
5539
0
                    else if (EQUAL(pszValue, "LOSSY"))
5540
0
                        m_bWebPLossless = false;
5541
0
                }
5542
0
                else if (m_nCompression == COMPRESSION_WEBP &&
5543
0
                         EQUAL(pszKey, "WEBP_LEVEL"))
5544
0
                {
5545
0
                    const int nLevel = atoi(pszValue);
5546
0
                    if (nLevel >= 1 && nLevel <= 100)
5547
0
                    {
5548
0
                        m_oGTiffMDMD.SetMetadataItem(
5549
0
                            "COMPRESSION_REVERSIBILITY", "LOSSY",
5550
0
                            "IMAGE_STRUCTURE");
5551
0
                        m_bWebPLossless = false;
5552
0
                        m_nWebPLevel = static_cast<signed char>(nLevel);
5553
0
                    }
5554
0
                }
5555
0
                else if (m_nCompression == COMPRESSION_LERC &&
5556
0
                         EQUAL(pszKey, "MAX_Z_ERROR"))
5557
0
                {
5558
0
                    bMaxZErrorFound = true;
5559
0
                    m_dfMaxZError = CPLAtof(pszValue);
5560
0
                }
5561
0
                else if (m_nCompression == COMPRESSION_LERC &&
5562
0
                         EQUAL(pszKey, "MAX_Z_ERROR_OVERVIEW"))
5563
0
                {
5564
0
                    bMaxZErrorOverviewFound = true;
5565
0
                    m_dfMaxZErrorOverview = CPLAtof(pszValue);
5566
0
                }
5567
#if HAVE_JXL
5568
                else if ((m_nCompression == COMPRESSION_JXL ||
5569
                          m_nCompression == COMPRESSION_JXL_DNG_1_7) &&
5570
                         EQUAL(pszKey, "COMPRESSION_REVERSIBILITY"))
5571
                {
5572
                    if (EQUAL(pszValue, "LOSSLESS"))
5573
                        m_bJXLLossless = true;
5574
                    else if (EQUAL(pszValue, "LOSSY"))
5575
                        m_bJXLLossless = false;
5576
                }
5577
                else if ((m_nCompression == COMPRESSION_JXL ||
5578
                          m_nCompression == COMPRESSION_JXL_DNG_1_7) &&
5579
                         EQUAL(pszKey, "JXL_DISTANCE"))
5580
                {
5581
                    const double dfVal = CPLAtof(pszValue);
5582
                    if (dfVal > 0 && dfVal <= 15)
5583
                    {
5584
                        m_oGTiffMDMD.SetMetadataItem(
5585
                            "COMPRESSION_REVERSIBILITY", "LOSSY",
5586
                            "IMAGE_STRUCTURE");
5587
                        m_bJXLLossless = false;
5588
                        m_fJXLDistance = static_cast<float>(dfVal);
5589
                    }
5590
                }
5591
                else if ((m_nCompression == COMPRESSION_JXL ||
5592
                          m_nCompression == COMPRESSION_JXL_DNG_1_7) &&
5593
                         EQUAL(pszKey, "JXL_ALPHA_DISTANCE"))
5594
                {
5595
                    const double dfVal = CPLAtof(pszValue);
5596
                    if (dfVal > 0 && dfVal <= 15)
5597
                    {
5598
                        m_oGTiffMDMD.SetMetadataItem(
5599
                            "COMPRESSION_REVERSIBILITY", "LOSSY",
5600
                            "IMAGE_STRUCTURE");
5601
                        m_fJXLAlphaDistance = static_cast<float>(dfVal);
5602
                    }
5603
                }
5604
                else if ((m_nCompression == COMPRESSION_JXL ||
5605
                          m_nCompression == COMPRESSION_JXL_DNG_1_7) &&
5606
                         EQUAL(pszKey, "JXL_EFFORT"))
5607
                {
5608
                    const int nEffort = atoi(pszValue);
5609
                    if (nEffort >= 1 && nEffort <= 9)
5610
                    {
5611
                        m_nJXLEffort = nEffort;
5612
                    }
5613
                }
5614
#endif
5615
0
                else
5616
0
                {
5617
0
                    continue;
5618
0
                }
5619
0
            }
5620
5621
0
            bool bIsXMLOrJSON = false;
5622
5623
0
            if (STARTS_WITH_CI(pszDomain, "xml:") ||
5624
0
                STARTS_WITH_CI(pszDomain, "json:"))
5625
0
                bIsXMLOrJSON = true;
5626
5627
            // Note: this un-escaping should not normally be done, as the
5628
            // deserialization of the tree from XML also does it, so we end up
5629
            // width double XML escaping, but keep it for backward
5630
            // compatibility.
5631
0
            char *pszUnescapedValue =
5632
0
                CPLUnescapeString(pszValue, nullptr, CPLES_XML);
5633
0
            if (nBand == 0)
5634
0
            {
5635
0
                if (bIsXMLOrJSON)
5636
0
                {
5637
0
                    char *apszMD[2] = {pszUnescapedValue, nullptr};
5638
0
                    m_oGTiffMDMD.SetMetadata(apszMD, pszDomain);
5639
0
                }
5640
0
                else
5641
0
                {
5642
0
                    m_oGTiffMDMD.SetMetadataItem(pszKey, pszUnescapedValue,
5643
0
                                                 pszDomain);
5644
0
                }
5645
0
            }
5646
0
            else
5647
0
            {
5648
0
                GTiffRasterBand *poBand =
5649
0
                    cpl::down_cast<GTiffRasterBand *>(GetRasterBand(nBand));
5650
0
                if (poBand != nullptr)
5651
0
                {
5652
0
                    if (EQUAL(pszRole, "scale"))
5653
0
                    {
5654
0
                        poBand->m_bHaveOffsetScale = true;
5655
0
                        poBand->m_dfScale = CPLAtofM(pszUnescapedValue);
5656
0
                    }
5657
0
                    else if (EQUAL(pszRole, "offset"))
5658
0
                    {
5659
0
                        poBand->m_bHaveOffsetScale = true;
5660
0
                        poBand->m_dfOffset = CPLAtofM(pszUnescapedValue);
5661
0
                    }
5662
0
                    else if (EQUAL(pszRole, "unittype"))
5663
0
                    {
5664
0
                        poBand->m_osUnitType = pszUnescapedValue;
5665
0
                    }
5666
0
                    else if (EQUAL(pszRole, "description"))
5667
0
                    {
5668
0
                        poBand->m_osDescription = pszUnescapedValue;
5669
0
                    }
5670
0
                    else if (EQUAL(pszRole, "colorinterp"))
5671
0
                    {
5672
0
                        if (EQUAL(pszUnescapedValue, "undefined"))
5673
0
                            poBand->m_eBandInterp = GCI_Undefined;
5674
0
                        else
5675
0
                        {
5676
0
                            poBand->m_eBandInterp =
5677
0
                                GDALGetColorInterpretationByName(
5678
0
                                    pszUnescapedValue);
5679
0
                            if (poBand->m_eBandInterp == GCI_Undefined)
5680
0
                            {
5681
0
                                poBand->m_oGTiffMDMD.SetMetadataItem(
5682
0
                                    "COLOR_INTERPRETATION", pszUnescapedValue);
5683
0
                            }
5684
0
                        }
5685
0
                    }
5686
0
                    else
5687
0
                    {
5688
0
                        if (bIsXMLOrJSON)
5689
0
                        {
5690
0
                            char *apszMD[2] = {pszUnescapedValue, nullptr};
5691
0
                            poBand->m_oGTiffMDMD.SetMetadata(apszMD, pszDomain);
5692
0
                        }
5693
0
                        else
5694
0
                        {
5695
0
                            poBand->m_oGTiffMDMD.SetMetadataItem(
5696
0
                                pszKey, pszUnescapedValue, pszDomain);
5697
0
                        }
5698
0
                    }
5699
0
                }
5700
0
            }
5701
0
            CPLFree(pszUnescapedValue);
5702
0
        }
5703
5704
0
        if (bMaxZErrorFound && !bMaxZErrorOverviewFound)
5705
0
        {
5706
0
            m_dfMaxZErrorOverview = m_dfMaxZError;
5707
0
        }
5708
0
    }
5709
5710
0
    if (m_bStreamingIn)
5711
0
    {
5712
0
        toff_t *panOffsets = nullptr;
5713
0
        TIFFGetField(m_hTIFF,
5714
0
                     TIFFIsTiled(m_hTIFF) ? TIFFTAG_TILEOFFSETS
5715
0
                                          : TIFFTAG_STRIPOFFSETS,
5716
0
                     &panOffsets);
5717
0
        if (panOffsets)
5718
0
        {
5719
0
            int nBlockCount = TIFFIsTiled(m_hTIFF)
5720
0
                                  ? TIFFNumberOfTiles(m_hTIFF)
5721
0
                                  : TIFFNumberOfStrips(m_hTIFF);
5722
0
            for (int i = 1; i < nBlockCount; ++i)
5723
0
            {
5724
0
                if (panOffsets[i] < panOffsets[i - 1])
5725
0
                {
5726
0
                    m_oGTiffMDMD.SetMetadataItem("UNORDERED_BLOCKS", "YES",
5727
0
                                                 "TIFF");
5728
0
                    CPLDebug("GTIFF",
5729
0
                             "Offset of block %d is lower than previous block. "
5730
0
                             "Reader must be careful",
5731
0
                             i);
5732
0
                    break;
5733
0
                }
5734
0
            }
5735
0
        }
5736
0
    }
5737
5738
0
    if (m_nCompression == COMPRESSION_JPEG)
5739
0
    {
5740
0
        bool bHasQuantizationTable = false;
5741
0
        bool bHasHuffmanTable = false;
5742
0
        int nQuality =
5743
0
            GuessJPEGQuality(bHasQuantizationTable, bHasHuffmanTable);
5744
0
        if (nQuality > 0)
5745
0
        {
5746
0
            m_oGTiffMDMD.SetMetadataItem(
5747
0
                "JPEG_QUALITY", CPLSPrintf("%d", nQuality), "IMAGE_STRUCTURE");
5748
0
            int nJpegTablesMode = JPEGTABLESMODE_QUANT;
5749
0
            if (bHasHuffmanTable)
5750
0
            {
5751
0
                nJpegTablesMode |= JPEGTABLESMODE_HUFF;
5752
0
            }
5753
0
            m_oGTiffMDMD.SetMetadataItem("JPEGTABLESMODE",
5754
0
                                         CPLSPrintf("%d", nJpegTablesMode),
5755
0
                                         "IMAGE_STRUCTURE");
5756
0
        }
5757
0
        if (eAccess == GA_Update)
5758
0
        {
5759
0
            SetJPEGQualityAndTablesModeFromFile(nQuality, bHasQuantizationTable,
5760
0
                                                bHasHuffmanTable);
5761
0
        }
5762
0
    }
5763
0
    else if (eAccess == GA_Update &&
5764
0
             m_oGTiffMDMD.GetMetadataItem("COMPRESSION_REVERSIBILITY",
5765
0
                                          "IMAGE_STRUCTURE") == nullptr)
5766
0
    {
5767
0
        if (m_nCompression == COMPRESSION_WEBP)
5768
0
        {
5769
0
            const char *pszReversibility =
5770
0
                GetMetadataItem("COMPRESSION_REVERSIBILITY", "IMAGE_STRUCTURE");
5771
0
            if (pszReversibility && strstr(pszReversibility, "LOSSLESS"))
5772
0
            {
5773
0
                m_bWebPLossless = true;
5774
0
            }
5775
0
            else if (pszReversibility && strstr(pszReversibility, "LOSSY"))
5776
0
            {
5777
0
                m_bWebPLossless = false;
5778
0
            }
5779
0
        }
5780
#ifdef HAVE_JXL
5781
        else if (m_nCompression == COMPRESSION_JXL ||
5782
                 m_nCompression == COMPRESSION_JXL_DNG_1_7)
5783
        {
5784
            const char *pszReversibility =
5785
                GetMetadataItem("COMPRESSION_REVERSIBILITY", "IMAGE_STRUCTURE");
5786
            if (pszReversibility && strstr(pszReversibility, "LOSSLESS"))
5787
            {
5788
                m_bJXLLossless = true;
5789
            }
5790
            else if (pszReversibility && strstr(pszReversibility, "LOSSY"))
5791
            {
5792
                m_bJXLLossless = false;
5793
            }
5794
        }
5795
#endif
5796
0
    }
5797
5798
0
    if (GTIFFSupportsPredictor(m_nCompression))
5799
0
    {
5800
0
        uint16_t nPredictor = 0;
5801
0
        if (TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &nPredictor) &&
5802
0
            nPredictor > 1)
5803
0
        {
5804
0
            m_oGTiffMDMD.SetMetadataItem(
5805
0
                "PREDICTOR", CPLSPrintf("%d", nPredictor), "IMAGE_STRUCTURE");
5806
0
        }
5807
0
    }
5808
5809
0
    CPLAssert(m_bReadGeoTransform == bReadGeoTransform);
5810
0
    CPLAssert(!m_bMetadataChanged);
5811
0
    m_bMetadataChanged = false;
5812
5813
0
    return CE_None;
5814
0
}
5815
5816
/************************************************************************/
5817
/*                          GetSiblingFiles()                           */
5818
/************************************************************************/
5819
5820
CSLConstList GTiffDataset::GetSiblingFiles()
5821
0
{
5822
0
    if (m_poBaseDS != nullptr)
5823
0
        return nullptr;
5824
5825
0
    if (m_bHasGotSiblingFiles)
5826
0
    {
5827
0
        return oOvManager.GetSiblingFiles();
5828
0
    }
5829
5830
0
    m_bHasGotSiblingFiles = true;
5831
0
    const int nMaxFiles =
5832
0
        atoi(CPLGetConfigOption("GDAL_READDIR_LIMIT_ON_OPEN", "1000"));
5833
0
    const std::string osDirname = CPLGetDirnameSafe(m_osFilename.c_str());
5834
0
    CPLStringList aosSiblingFiles(VSIReadDirEx(osDirname.c_str(), nMaxFiles));
5835
0
    if (nMaxFiles > 0 && aosSiblingFiles.size() > nMaxFiles)
5836
0
    {
5837
0
        CPLDebug("GTiff", "GDAL_READDIR_LIMIT_ON_OPEN reached on %s",
5838
0
                 osDirname.c_str());
5839
0
        aosSiblingFiles.clear();
5840
0
    }
5841
0
    oOvManager.TransferSiblingFiles(aosSiblingFiles.StealList());
5842
5843
0
    return oOvManager.GetSiblingFiles();
5844
0
}
5845
5846
/************************************************************************/
5847
/*              IdentifyAuthorizedGeoreferencingSources()               */
5848
/************************************************************************/
5849
5850
void GTiffDataset::IdentifyAuthorizedGeoreferencingSources()
5851
0
{
5852
0
    if (m_bHasIdentifiedAuthorizedGeoreferencingSources)
5853
0
        return;
5854
0
    m_bHasIdentifiedAuthorizedGeoreferencingSources = true;
5855
0
    CPLString osGeorefSources = CSLFetchNameValueDef(
5856
0
        papszOpenOptions, "GEOREF_SOURCES",
5857
0
        CPLGetConfigOption("GDAL_GEOREF_SOURCES",
5858
0
                           "PAM,INTERNAL,TABFILE,WORLDFILE,XML"));
5859
0
    char **papszTokens = CSLTokenizeString2(osGeorefSources, ",", 0);
5860
0
    m_nPAMGeorefSrcIndex =
5861
0
        static_cast<signed char>(CSLFindString(papszTokens, "PAM"));
5862
0
    m_nINTERNALGeorefSrcIndex =
5863
0
        static_cast<signed char>(CSLFindString(papszTokens, "INTERNAL"));
5864
0
    m_nTABFILEGeorefSrcIndex =
5865
0
        static_cast<signed char>(CSLFindString(papszTokens, "TABFILE"));
5866
0
    m_nWORLDFILEGeorefSrcIndex =
5867
0
        static_cast<signed char>(CSLFindString(papszTokens, "WORLDFILE"));
5868
0
    m_nXMLGeorefSrcIndex =
5869
0
        static_cast<signed char>(CSLFindString(papszTokens, "XML"));
5870
0
    CSLDestroy(papszTokens);
5871
0
}
5872
5873
/************************************************************************/
5874
/*                  LoadGeoreferencingAndPamIfNeeded()                  */
5875
/************************************************************************/
5876
5877
void GTiffDataset::LoadGeoreferencingAndPamIfNeeded()
5878
5879
0
{
5880
0
    if (!m_bReadGeoTransform && !m_bLoadPam)
5881
0
        return;
5882
0
    if (m_poBaseDS != nullptr)
5883
0
        return;
5884
5885
0
    IdentifyAuthorizedGeoreferencingSources();
5886
5887
    /* -------------------------------------------------------------------- */
5888
    /*      Get the transform or gcps from the GeoTIFF file.                */
5889
    /* -------------------------------------------------------------------- */
5890
0
    if (m_bReadGeoTransform)
5891
0
    {
5892
0
        m_bReadGeoTransform = false;
5893
5894
0
        char *pszTabWKT = nullptr;
5895
0
        double *padfTiePoints = nullptr;
5896
0
        double *padfScale = nullptr;
5897
0
        double *padfMatrix = nullptr;
5898
0
        uint16_t nCount = 0;
5899
0
        bool bPixelIsPoint = false;
5900
0
        unsigned short nRasterType = 0;
5901
0
        bool bPointGeoIgnore = false;
5902
5903
0
        std::set<signed char> aoSetPriorities;
5904
0
        if (m_nINTERNALGeorefSrcIndex >= 0)
5905
0
            aoSetPriorities.insert(m_nINTERNALGeorefSrcIndex);
5906
0
        if (m_nTABFILEGeorefSrcIndex >= 0)
5907
0
            aoSetPriorities.insert(m_nTABFILEGeorefSrcIndex);
5908
0
        if (m_nWORLDFILEGeorefSrcIndex >= 0)
5909
0
            aoSetPriorities.insert(m_nWORLDFILEGeorefSrcIndex);
5910
0
        for (const auto nIndex : aoSetPriorities)
5911
0
        {
5912
0
            if (m_nINTERNALGeorefSrcIndex == nIndex)
5913
0
            {
5914
0
                GTIF *psGTIF =
5915
0
                    GTiffDataset::GTIFNew(m_hTIFF);  // How expensive this is?
5916
5917
0
                if (psGTIF)
5918
0
                {
5919
0
                    if (GDALGTIFKeyGetSHORT(psGTIF, GTRasterTypeGeoKey,
5920
0
                                            &nRasterType, 0, 1) == 1 &&
5921
0
                        nRasterType == static_cast<short>(RasterPixelIsPoint))
5922
0
                    {
5923
0
                        bPixelIsPoint = true;
5924
0
                        bPointGeoIgnore = CPLTestBool(CPLGetConfigOption(
5925
0
                            "GTIFF_POINT_GEO_IGNORE", "FALSE"));
5926
0
                    }
5927
5928
0
                    GTIFFree(psGTIF);
5929
0
                }
5930
5931
0
                m_gt = GDALGeoTransform();
5932
5933
0
                uint16_t nCountScale = 0;
5934
0
                if (TIFFGetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE, &nCountScale,
5935
0
                                 &padfScale) &&
5936
0
                    nCountScale >= 2 && padfScale[0] != 0.0 &&
5937
0
                    padfScale[1] != 0.0)
5938
0
                {
5939
0
                    m_gt.xscale = padfScale[0];
5940
0
                    if (padfScale[1] < 0)
5941
0
                    {
5942
0
                        const char *pszOptionVal = CPLGetConfigOption(
5943
0
                            "GTIFF_HONOUR_NEGATIVE_SCALEY", nullptr);
5944
0
                        if (pszOptionVal == nullptr)
5945
0
                        {
5946
0
                            ReportError(
5947
0
                                CE_Warning, CPLE_AppDefined,
5948
0
                                "File with negative value for ScaleY in "
5949
0
                                "GeoPixelScale tag. This is rather "
5950
0
                                "unusual. GDAL, contrary to the GeoTIFF "
5951
0
                                "specification, assumes that the file "
5952
0
                                "was intended to be north-up, and will "
5953
0
                                "treat this file as if ScaleY was "
5954
0
                                "positive. You may override this behavior "
5955
0
                                "by setting the GTIFF_HONOUR_NEGATIVE_SCALEY "
5956
0
                                "configuration option to YES");
5957
0
                            m_gt.yscale = padfScale[1];
5958
0
                        }
5959
0
                        else if (CPLTestBool(pszOptionVal))
5960
0
                        {
5961
0
                            m_gt.yscale = -padfScale[1];
5962
0
                        }
5963
0
                        else
5964
0
                        {
5965
0
                            m_gt.yscale = padfScale[1];
5966
0
                        }
5967
0
                    }
5968
0
                    else
5969
0
                    {
5970
0
                        m_gt.yscale = -padfScale[1];
5971
0
                    }
5972
5973
0
                    if (TIFFGetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, &nCount,
5974
0
                                     &padfTiePoints) &&
5975
0
                        nCount >= 6)
5976
0
                    {
5977
0
                        m_gt.xorig =
5978
0
                            padfTiePoints[3] - padfTiePoints[0] * m_gt.xscale;
5979
0
                        m_gt.yorig =
5980
0
                            padfTiePoints[4] - padfTiePoints[1] * m_gt.yscale;
5981
5982
0
                        if (bPixelIsPoint && !bPointGeoIgnore)
5983
0
                        {
5984
0
                            m_gt.xorig -= (m_gt.xscale * 0.5 + m_gt.xrot * 0.5);
5985
0
                            m_gt.yorig -= (m_gt.yrot * 0.5 + m_gt.yscale * 0.5);
5986
0
                        }
5987
5988
0
                        m_bGeoTransformValid = true;
5989
0
                        m_nGeoTransformGeorefSrcIndex = nIndex;
5990
5991
0
                        if (nCountScale >= 3 && GetRasterCount() == 1 &&
5992
0
                            (padfScale[2] != 0.0 || padfTiePoints[2] != 0.0 ||
5993
0
                             padfTiePoints[5] != 0.0))
5994
0
                        {
5995
0
                            LookForProjection();
5996
0
                            if (!m_oSRS.IsEmpty() && m_oSRS.IsVertical())
5997
0
                            {
5998
                                /* modelTiePointTag = (pixel, line, z0, X, Y,
5999
                                 * Z0) */
6000
                                /* thus Z(some_point) = (z(some_point) - z0) *
6001
                                 * scaleZ + Z0 */
6002
                                /* equivalently written as */
6003
                                /* Z(some_point) = z(some_point) * scaleZ +
6004
                                 * offsetZ with */
6005
                                /* offsetZ = - z0 * scaleZ + Z0 */
6006
0
                                double dfScale = padfScale[2];
6007
0
                                double dfOffset = -padfTiePoints[2] * dfScale +
6008
0
                                                  padfTiePoints[5];
6009
0
                                GTiffRasterBand *poBand =
6010
0
                                    cpl::down_cast<GTiffRasterBand *>(
6011
0
                                        GetRasterBand(1));
6012
0
                                poBand->m_bHaveOffsetScale = true;
6013
0
                                poBand->m_dfScale = dfScale;
6014
0
                                poBand->m_dfOffset = dfOffset;
6015
0
                            }
6016
0
                        }
6017
0
                    }
6018
0
                }
6019
6020
0
                else if (TIFFGetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX, &nCount,
6021
0
                                      &padfMatrix) &&
6022
0
                         nCount == 16)
6023
0
                {
6024
0
                    m_gt.xorig = padfMatrix[3];
6025
0
                    m_gt.xscale = padfMatrix[0];
6026
0
                    m_gt.xrot = padfMatrix[1];
6027
0
                    m_gt.yorig = padfMatrix[7];
6028
0
                    m_gt.yrot = padfMatrix[4];
6029
0
                    m_gt.yscale = padfMatrix[5];
6030
6031
0
                    if (bPixelIsPoint && !bPointGeoIgnore)
6032
0
                    {
6033
0
                        m_gt.xorig -= m_gt.xscale * 0.5 + m_gt.xrot * 0.5;
6034
0
                        m_gt.yorig -= m_gt.yrot * 0.5 + m_gt.yscale * 0.5;
6035
0
                    }
6036
6037
0
                    m_bGeoTransformValid = true;
6038
0
                    m_nGeoTransformGeorefSrcIndex = nIndex;
6039
0
                }
6040
0
                if (m_bGeoTransformValid)
6041
0
                    break;
6042
0
            }
6043
6044
            /* --------------------------------------------------------------------
6045
             */
6046
            /*      Otherwise try looking for a .tab, .tfw, .tifw or .wld file.
6047
             */
6048
            /* --------------------------------------------------------------------
6049
             */
6050
0
            if (m_nTABFILEGeorefSrcIndex == nIndex)
6051
0
            {
6052
0
                char *pszGeorefFilename = nullptr;
6053
6054
0
                CSLConstList papszSiblingFiles = GetSiblingFiles();
6055
6056
                // Begin with .tab since it can also have projection info.
6057
0
                int nGCPCount = 0;
6058
0
                GDAL_GCP *pasGCPList = nullptr;
6059
0
                const int bTabFileOK = GDALReadTabFile2(
6060
0
                    m_osFilename.c_str(), m_gt.data(), &pszTabWKT, &nGCPCount,
6061
0
                    &pasGCPList, papszSiblingFiles, &pszGeorefFilename);
6062
6063
0
                if (bTabFileOK)
6064
0
                {
6065
0
                    m_nGeoTransformGeorefSrcIndex = nIndex;
6066
                    // if( pszTabWKT )
6067
                    // {
6068
                    //     m_nProjectionGeorefSrcIndex = nIndex;
6069
                    // }
6070
0
                    m_aoGCPs = gdal::GCP::fromC(pasGCPList, nGCPCount);
6071
0
                    if (m_aoGCPs.empty())
6072
0
                    {
6073
0
                        m_bGeoTransformValid = true;
6074
0
                    }
6075
0
                }
6076
6077
0
                if (nGCPCount)
6078
0
                {
6079
0
                    GDALDeinitGCPs(nGCPCount, pasGCPList);
6080
0
                    CPLFree(pasGCPList);
6081
0
                }
6082
6083
0
                if (pszGeorefFilename)
6084
0
                {
6085
0
                    CPLFree(m_pszGeorefFilename);
6086
0
                    m_pszGeorefFilename = pszGeorefFilename;
6087
0
                    pszGeorefFilename = nullptr;
6088
0
                }
6089
0
                if (m_bGeoTransformValid)
6090
0
                    break;
6091
0
            }
6092
6093
0
            if (m_nWORLDFILEGeorefSrcIndex == nIndex)
6094
0
            {
6095
0
                char *pszGeorefFilename = nullptr;
6096
6097
0
                CSLConstList papszSiblingFiles = GetSiblingFiles();
6098
6099
0
                m_bGeoTransformValid = CPL_TO_BOOL(
6100
0
                    GDALReadWorldFile2(m_osFilename.c_str(), nullptr, m_gt,
6101
0
                                       papszSiblingFiles, &pszGeorefFilename));
6102
6103
0
                if (!m_bGeoTransformValid)
6104
0
                {
6105
0
                    m_bGeoTransformValid = CPL_TO_BOOL(GDALReadWorldFile2(
6106
0
                        m_osFilename.c_str(), "wld", m_gt, papszSiblingFiles,
6107
0
                        &pszGeorefFilename));
6108
0
                }
6109
0
                if (m_bGeoTransformValid)
6110
0
                    m_nGeoTransformGeorefSrcIndex = nIndex;
6111
6112
0
                if (pszGeorefFilename)
6113
0
                {
6114
0
                    CPLFree(m_pszGeorefFilename);
6115
0
                    m_pszGeorefFilename = pszGeorefFilename;
6116
0
                    pszGeorefFilename = nullptr;
6117
0
                }
6118
0
                if (m_bGeoTransformValid)
6119
0
                    break;
6120
0
            }
6121
0
        }
6122
6123
        /* --------------------------------------------------------------------
6124
         */
6125
        /*      Check for GCPs. */
6126
        /* --------------------------------------------------------------------
6127
         */
6128
0
        if (m_nINTERNALGeorefSrcIndex >= 0 &&
6129
0
            TIFFGetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, &nCount,
6130
0
                         &padfTiePoints) &&
6131
0
            !m_bGeoTransformValid)
6132
0
        {
6133
0
            m_aoGCPs.clear();
6134
0
            const int nNewGCPCount = nCount / 6;
6135
0
            for (int iGCP = 0; iGCP < nNewGCPCount; ++iGCP)
6136
0
            {
6137
0
                m_aoGCPs.emplace_back(CPLSPrintf("%d", iGCP + 1), "",
6138
0
                                      /* pixel = */ padfTiePoints[iGCP * 6 + 0],
6139
0
                                      /* line = */ padfTiePoints[iGCP * 6 + 1],
6140
0
                                      /* X = */ padfTiePoints[iGCP * 6 + 3],
6141
0
                                      /* Y = */ padfTiePoints[iGCP * 6 + 4],
6142
0
                                      /* Z = */ padfTiePoints[iGCP * 6 + 5]);
6143
6144
0
                if (bPixelIsPoint && !bPointGeoIgnore)
6145
0
                {
6146
0
                    m_aoGCPs.back().Pixel() += 0.5;
6147
0
                    m_aoGCPs.back().Line() += 0.5;
6148
0
                }
6149
0
            }
6150
0
            m_nGeoTransformGeorefSrcIndex = m_nINTERNALGeorefSrcIndex;
6151
0
        }
6152
6153
        /* --------------------------------------------------------------------
6154
         */
6155
        /*      Did we find a tab file?  If so we will use its coordinate */
6156
        /*      system and give it precedence. */
6157
        /* --------------------------------------------------------------------
6158
         */
6159
0
        if (pszTabWKT != nullptr && m_oSRS.IsEmpty())
6160
0
        {
6161
0
            m_oSRS.importFromWkt(pszTabWKT);
6162
0
            m_bLookedForProjection = true;
6163
0
        }
6164
6165
0
        CPLFree(pszTabWKT);
6166
0
    }
6167
6168
0
    if (m_bLoadPam && m_nPAMGeorefSrcIndex >= 0)
6169
0
    {
6170
        /* --------------------------------------------------------------------
6171
         */
6172
        /*      Initialize any PAM information. */
6173
        /* --------------------------------------------------------------------
6174
         */
6175
0
        CPLAssert(!m_bColorProfileMetadataChanged);
6176
0
        CPLAssert(!m_bMetadataChanged);
6177
0
        CPLAssert(!m_bGeoTIFFInfoChanged);
6178
0
        CPLAssert(!m_bNoDataChanged);
6179
6180
        // We must absolutely unset m_bLoadPam now, otherwise calling
6181
        // GetFileList() on a .tif with a .aux will result in an (almost)
6182
        // endless sequence of calls.
6183
0
        m_bLoadPam = false;
6184
6185
0
        TryLoadXML(GetSiblingFiles());
6186
0
        ApplyPamInfo();
6187
6188
0
        m_bColorProfileMetadataChanged = false;
6189
0
        m_bMetadataChanged = false;
6190
0
        m_bGeoTIFFInfoChanged = false;
6191
0
        m_bNoDataChanged = false;
6192
0
    }
6193
0
    m_bLoadPam = false;
6194
0
}
6195
6196
/************************************************************************/
6197
/*                           GetSpatialRef()                            */
6198
/************************************************************************/
6199
6200
const OGRSpatialReference *GTiffDataset::GetSpatialRef() const
6201
6202
0
{
6203
0
    const_cast<GTiffDataset *>(this)->LoadGeoreferencingAndPamIfNeeded();
6204
0
    if (m_aoGCPs.empty())
6205
0
    {
6206
0
        const_cast<GTiffDataset *>(this)->LookForProjection();
6207
0
    }
6208
6209
0
    return m_aoGCPs.empty() && !m_oSRS.IsEmpty() ? &m_oSRS : nullptr;
6210
0
}
6211
6212
/************************************************************************/
6213
/*                          GetGeoTransform()                           */
6214
/************************************************************************/
6215
6216
CPLErr GTiffDataset::GetGeoTransform(GDALGeoTransform &gt) const
6217
6218
0
{
6219
0
    const_cast<GTiffDataset *>(this)->LoadGeoreferencingAndPamIfNeeded();
6220
6221
0
    gt = m_gt;
6222
6223
0
    if (!m_bGeoTransformValid)
6224
0
        return CE_Failure;
6225
6226
    // Same logic as in the .gtx driver, for the benefit of
6227
    // GDALOpenVerticalShiftGrid() when used with PROJ-data's US geoids.
6228
0
    if (CPLFetchBool(papszOpenOptions, "SHIFT_ORIGIN_IN_MINUS_180_PLUS_180",
6229
0
                     false))
6230
0
    {
6231
0
        if (gt.xorig < -180.0 - gt.xscale)
6232
0
            gt.xorig += 360.0;
6233
0
        else if (gt.xorig > 180.0)
6234
0
            gt.xorig -= 360.0;
6235
0
    }
6236
6237
0
    return CE_None;
6238
0
}
6239
6240
/************************************************************************/
6241
/*                            GetGCPCount()                             */
6242
/************************************************************************/
6243
6244
int GTiffDataset::GetGCPCount()
6245
6246
0
{
6247
0
    LoadGeoreferencingAndPamIfNeeded();
6248
6249
0
    return static_cast<int>(m_aoGCPs.size());
6250
0
}
6251
6252
/************************************************************************/
6253
/*                          GetGCPSpatialRef()                          */
6254
/************************************************************************/
6255
6256
const OGRSpatialReference *GTiffDataset::GetGCPSpatialRef() const
6257
6258
0
{
6259
0
    const_cast<GTiffDataset *>(this)->LoadGeoreferencingAndPamIfNeeded();
6260
6261
0
    if (!m_aoGCPs.empty())
6262
0
    {
6263
0
        const_cast<GTiffDataset *>(this)->LookForProjection();
6264
0
    }
6265
0
    return !m_aoGCPs.empty() && !m_oSRS.IsEmpty() ? &m_oSRS : nullptr;
6266
0
}
6267
6268
/************************************************************************/
6269
/*                              GetGCPs()                               */
6270
/************************************************************************/
6271
6272
const GDAL_GCP *GTiffDataset::GetGCPs()
6273
6274
0
{
6275
0
    LoadGeoreferencingAndPamIfNeeded();
6276
6277
0
    return gdal::GCP::c_ptr(m_aoGCPs);
6278
0
}
6279
6280
/************************************************************************/
6281
/*                       GetMetadataDomainList()                        */
6282
/************************************************************************/
6283
6284
char **GTiffDataset::GetMetadataDomainList()
6285
0
{
6286
0
    LoadGeoreferencingAndPamIfNeeded();
6287
6288
0
    char **papszDomainList = CSLDuplicate(m_oGTiffMDMD.GetDomainList());
6289
0
    char **papszBaseList = GDALDataset::GetMetadataDomainList();
6290
6291
0
    const int nbBaseDomains = CSLCount(papszBaseList);
6292
6293
0
    for (int domainId = 0; domainId < nbBaseDomains; ++domainId)
6294
0
    {
6295
0
        if (CSLFindString(papszDomainList, papszBaseList[domainId]) < 0)
6296
0
        {
6297
0
            papszDomainList =
6298
0
                CSLAddString(papszDomainList, papszBaseList[domainId]);
6299
0
        }
6300
0
    }
6301
6302
0
    CSLDestroy(papszBaseList);
6303
6304
0
    return BuildMetadataDomainList(papszDomainList, TRUE, "",
6305
0
                                   "ProxyOverviewRequest", MD_DOMAIN_RPC,
6306
0
                                   MD_DOMAIN_IMD, "SUBDATASETS", "EXIF",
6307
0
                                   "xml:XMP", "COLOR_PROFILE", nullptr);
6308
0
}
6309
6310
/************************************************************************/
6311
/*                            GetMetadata()                             */
6312
/************************************************************************/
6313
6314
CSLConstList GTiffDataset::GetMetadata(const char *pszDomain)
6315
6316
0
{
6317
0
    if (pszDomain != nullptr && EQUAL(pszDomain, "IMAGE_STRUCTURE"))
6318
0
    {
6319
0
        GTiffDataset::GetMetadataItem("COMPRESSION_REVERSIBILITY", pszDomain);
6320
0
        GTiffDataset::GetMetadataItem("LAYOUT", pszDomain);
6321
0
    }
6322
0
    else
6323
0
    {
6324
0
        LoadGeoreferencingAndPamIfNeeded();
6325
0
    }
6326
6327
0
    if (pszDomain != nullptr && EQUAL(pszDomain, "ProxyOverviewRequest"))
6328
0
        return GDALPamDataset::GetMetadata(pszDomain);
6329
6330
0
    if (pszDomain != nullptr && EQUAL(pszDomain, "DERIVED_SUBDATASETS"))
6331
0
    {
6332
0
        return GDALDataset::GetMetadata(pszDomain);
6333
0
    }
6334
6335
0
    else if (pszDomain != nullptr && (EQUAL(pszDomain, MD_DOMAIN_RPC) ||
6336
0
                                      EQUAL(pszDomain, MD_DOMAIN_IMD) ||
6337
0
                                      EQUAL(pszDomain, MD_DOMAIN_IMAGERY)))
6338
0
        LoadMetadata();
6339
6340
0
    else if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
6341
0
        ScanDirectories();
6342
6343
0
    else if (pszDomain != nullptr && EQUAL(pszDomain, "EXIF"))
6344
0
        LoadEXIFMetadata();
6345
6346
0
    else if (pszDomain != nullptr && EQUAL(pszDomain, "COLOR_PROFILE"))
6347
0
        LoadICCProfile();
6348
6349
0
    else if (pszDomain == nullptr || EQUAL(pszDomain, ""))
6350
0
        LoadMDAreaOrPoint();  // To set GDALMD_AREA_OR_POINT.
6351
6352
0
    return m_oGTiffMDMD.GetMetadata(pszDomain);
6353
0
}
6354
6355
/************************************************************************/
6356
/*                          GetMetadataItem()                           */
6357
/************************************************************************/
6358
6359
const char *GTiffDataset::GetMetadataItem(const char *pszName,
6360
                                          const char *pszDomain)
6361
6362
0
{
6363
0
    if (pszDomain != nullptr && EQUAL(pszDomain, "IMAGE_STRUCTURE"))
6364
0
    {
6365
0
        if ((m_nCompression == COMPRESSION_WEBP ||
6366
0
             m_nCompression == COMPRESSION_JXL ||
6367
0
             m_nCompression == COMPRESSION_JXL_DNG_1_7) &&
6368
0
            EQUAL(pszName, "COMPRESSION_REVERSIBILITY") &&
6369
0
            m_oGTiffMDMD.GetMetadataItem("COMPRESSION_REVERSIBILITY",
6370
0
                                         "IMAGE_STRUCTURE") == nullptr)
6371
0
        {
6372
0
            const char *pszDriverName =
6373
0
                m_nCompression == COMPRESSION_WEBP ? "WEBP" : "JPEGXL";
6374
0
            auto poTileDriver = GDALGetDriverByName(pszDriverName);
6375
0
            if (poTileDriver)
6376
0
            {
6377
0
                vsi_l_offset nOffset = 0;
6378
0
                vsi_l_offset nSize = 0;
6379
0
                CPL_IGNORE_RET_VAL(
6380
0
                    IsBlockAvailable(0, &nOffset, &nSize, nullptr));
6381
0
                if (nSize > 0)
6382
0
                {
6383
0
                    const std::string osSubfile(
6384
0
                        CPLSPrintf("/vsisubfile/" CPL_FRMT_GUIB "_%d,%s",
6385
0
                                   static_cast<GUIntBig>(nOffset),
6386
0
                                   static_cast<int>(std::min(
6387
0
                                       static_cast<vsi_l_offset>(1024), nSize)),
6388
0
                                   m_osFilename.c_str()));
6389
0
                    const char *const apszDrivers[] = {pszDriverName, nullptr};
6390
0
                    auto poWebPDataset =
6391
0
                        std::unique_ptr<GDALDataset>(GDALDataset::Open(
6392
0
                            osSubfile.c_str(), GDAL_OF_RASTER, apszDrivers));
6393
0
                    if (poWebPDataset)
6394
0
                    {
6395
0
                        const char *pszReversibility =
6396
0
                            poWebPDataset->GetMetadataItem(
6397
0
                                "COMPRESSION_REVERSIBILITY", "IMAGE_STRUCTURE");
6398
0
                        if (pszReversibility)
6399
0
                            m_oGTiffMDMD.SetMetadataItem(
6400
0
                                "COMPRESSION_REVERSIBILITY", pszReversibility,
6401
0
                                "IMAGE_STRUCTURE");
6402
0
                    }
6403
0
                }
6404
0
            }
6405
0
        }
6406
6407
0
        if (!m_bLayoutChecked && m_poBaseDS == nullptr)
6408
0
        {
6409
0
            m_bLayoutChecked = true;
6410
0
            const char *pszLayout =
6411
0
                m_oGTiffMDMD.GetMetadataItem("LAYOUT", "IMAGE_STRUCTURE");
6412
0
            if (eAccess == GA_ReadOnly && !pszLayout && CheckCOGLayout())
6413
0
            {
6414
0
                m_oGTiffMDMD.SetMetadataItem("LAYOUT", "COG",
6415
0
                                             "IMAGE_STRUCTURE");
6416
0
            }
6417
0
        }
6418
0
    }
6419
0
    else
6420
0
    {
6421
0
        LoadGeoreferencingAndPamIfNeeded();
6422
0
    }
6423
6424
0
    if (pszDomain != nullptr && EQUAL(pszDomain, "ProxyOverviewRequest"))
6425
0
    {
6426
0
        return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
6427
0
    }
6428
0
    else if (pszDomain != nullptr && (EQUAL(pszDomain, MD_DOMAIN_RPC) ||
6429
0
                                      EQUAL(pszDomain, MD_DOMAIN_IMD) ||
6430
0
                                      EQUAL(pszDomain, MD_DOMAIN_IMAGERY)))
6431
0
    {
6432
0
        LoadMetadata();
6433
0
    }
6434
0
    else if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
6435
0
    {
6436
0
        ScanDirectories();
6437
0
    }
6438
0
    else if (pszDomain != nullptr && EQUAL(pszDomain, "EXIF"))
6439
0
    {
6440
0
        LoadEXIFMetadata();
6441
0
    }
6442
0
    else if (pszDomain != nullptr && EQUAL(pszDomain, "COLOR_PROFILE"))
6443
0
    {
6444
0
        LoadICCProfile();
6445
0
    }
6446
0
    else if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
6447
0
             pszName != nullptr && EQUAL(pszName, GDALMD_AREA_OR_POINT))
6448
0
    {
6449
0
        LoadMDAreaOrPoint();  // To set GDALMD_AREA_OR_POINT.
6450
0
    }
6451
0
    else if (pszDomain != nullptr && EQUAL(pszDomain, "_DEBUG_") &&
6452
0
             pszName != nullptr)
6453
0
    {
6454
#ifdef DEBUG_REACHED_VIRTUAL_MEM_IO
6455
        if (EQUAL(pszName, "UNREACHED_VIRTUALMEMIO_CODE_PATH"))
6456
        {
6457
            CPLString osMissing;
6458
            for (int i = 0;
6459
                 i < static_cast<int>(CPL_ARRAYSIZE(anReachedVirtualMemIO));
6460
                 ++i)
6461
            {
6462
                if (!anReachedVirtualMemIO[i])
6463
                {
6464
                    if (!osMissing.empty())
6465
                        osMissing += ",";
6466
                    osMissing += CPLSPrintf("%d", i);
6467
                }
6468
            }
6469
            return (osMissing.size()) ? CPLSPrintf("%s", osMissing.c_str())
6470
                                      : nullptr;
6471
        }
6472
        else
6473
#endif
6474
0
            if (EQUAL(pszName, "TIFFTAG_EXTRASAMPLES"))
6475
0
        {
6476
0
            CPLString osRet;
6477
0
            uint16_t *v = nullptr;
6478
0
            uint16_t count = 0;
6479
6480
0
            if (TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &count, &v))
6481
0
            {
6482
0
                for (int i = 0; i < static_cast<int>(count); ++i)
6483
0
                {
6484
0
                    if (i > 0)
6485
0
                        osRet += ",";
6486
0
                    osRet += CPLSPrintf("%d", v[i]);
6487
0
                }
6488
0
            }
6489
0
            return (osRet.size()) ? CPLSPrintf("%s", osRet.c_str()) : nullptr;
6490
0
        }
6491
0
        else if (EQUAL(pszName, "TIFFTAG_PHOTOMETRIC"))
6492
0
        {
6493
0
            return CPLSPrintf("%d", m_nPhotometric);
6494
0
        }
6495
6496
0
        else if (EQUAL(pszName, "TIFFTAG_GDAL_METADATA"))
6497
0
        {
6498
0
            char *pszText = nullptr;
6499
0
            if (!TIFFGetField(m_hTIFF, TIFFTAG_GDAL_METADATA, &pszText))
6500
0
                return nullptr;
6501
6502
0
            return pszText;
6503
0
        }
6504
0
        else if (EQUAL(pszName, "HAS_USED_READ_ENCODED_API"))
6505
0
        {
6506
0
            return m_bHasUsedReadEncodedAPI ? "1" : "0";
6507
0
        }
6508
0
        else if (EQUAL(pszName, "WEBP_LOSSLESS"))
6509
0
        {
6510
0
            return m_bWebPLossless ? "1" : "0";
6511
0
        }
6512
0
        else if (EQUAL(pszName, "WEBP_LEVEL"))
6513
0
        {
6514
0
            return CPLSPrintf("%d", m_nWebPLevel);
6515
0
        }
6516
0
        else if (EQUAL(pszName, "MAX_Z_ERROR"))
6517
0
        {
6518
0
            return CPLSPrintf("%f", m_dfMaxZError);
6519
0
        }
6520
0
        else if (EQUAL(pszName, "MAX_Z_ERROR_OVERVIEW"))
6521
0
        {
6522
0
            return CPLSPrintf("%f", m_dfMaxZErrorOverview);
6523
0
        }
6524
#if HAVE_JXL
6525
        else if (EQUAL(pszName, "JXL_LOSSLESS"))
6526
        {
6527
            return m_bJXLLossless ? "1" : "0";
6528
        }
6529
        else if (EQUAL(pszName, "JXL_DISTANCE"))
6530
        {
6531
            return CPLSPrintf("%f", static_cast<double>(m_fJXLDistance));
6532
        }
6533
        else if (EQUAL(pszName, "JXL_ALPHA_DISTANCE"))
6534
        {
6535
            return CPLSPrintf("%f", static_cast<double>(m_fJXLAlphaDistance));
6536
        }
6537
        else if (EQUAL(pszName, "JXL_EFFORT"))
6538
        {
6539
            return CPLSPrintf("%u", m_nJXLEffort);
6540
        }
6541
#endif
6542
0
        return nullptr;
6543
0
    }
6544
6545
0
    else if (pszDomain != nullptr && EQUAL(pszDomain, "TIFF") &&
6546
0
             pszName != nullptr)
6547
0
    {
6548
0
        if (EQUAL(pszName, "GDAL_STRUCTURAL_METADATA"))
6549
0
        {
6550
0
            const auto nOffset = VSIFTellL(m_fpL);
6551
0
            VSIFSeekL(m_fpL, 0, SEEK_SET);
6552
0
            GByte abyData[1024];
6553
0
            size_t nRead = VSIFReadL(abyData, 1, sizeof(abyData) - 1, m_fpL);
6554
0
            abyData[nRead] = 0;
6555
0
            VSIFSeekL(m_fpL, nOffset, SEEK_SET);
6556
0
            if (nRead > 4)
6557
0
            {
6558
0
                const int nOffsetOfStructuralMetadata =
6559
0
                    (abyData[2] == 0x2B || abyData[3] == 0x2B) ? 16 : 8;
6560
0
                const int nSizePatternLen =
6561
0
                    static_cast<int>(strlen("XXXXXX bytes\n"));
6562
0
                if (nRead > nOffsetOfStructuralMetadata +
6563
0
                                strlen("GDAL_STRUCTURAL_METADATA_SIZE=") +
6564
0
                                nSizePatternLen &&
6565
0
                    memcmp(abyData + nOffsetOfStructuralMetadata,
6566
0
                           "GDAL_STRUCTURAL_METADATA_SIZE=",
6567
0
                           strlen("GDAL_STRUCTURAL_METADATA_SIZE=")) == 0)
6568
0
                {
6569
0
                    char *pszStructuralMD = reinterpret_cast<char *>(
6570
0
                        abyData + nOffsetOfStructuralMetadata);
6571
0
                    const int nLenMD =
6572
0
                        atoi(pszStructuralMD +
6573
0
                             strlen("GDAL_STRUCTURAL_METADATA_SIZE="));
6574
0
                    if (nOffsetOfStructuralMetadata +
6575
0
                            strlen("GDAL_STRUCTURAL_METADATA_SIZE=") +
6576
0
                            nSizePatternLen + nLenMD <=
6577
0
                        nRead)
6578
0
                    {
6579
0
                        pszStructuralMD[strlen(
6580
0
                                            "GDAL_STRUCTURAL_METADATA_SIZE=") +
6581
0
                                        nSizePatternLen + nLenMD] = 0;
6582
0
                        return CPLSPrintf("%s", pszStructuralMD);
6583
0
                    }
6584
0
                }
6585
0
            }
6586
0
            return nullptr;
6587
0
        }
6588
0
    }
6589
6590
0
    else if (pszName && pszDomain && EQUAL(pszDomain, "json:ISIS3"))
6591
0
    {
6592
0
        auto oIter = m_oMapISIS3MetadataItems.find(pszName);
6593
0
        if (oIter != m_oMapISIS3MetadataItems.end())
6594
0
        {
6595
0
            return oIter->second.c_str();
6596
0
        }
6597
0
        else
6598
0
        {
6599
0
            if (!m_oISIS3Metadata.IsValid())
6600
0
            {
6601
0
                CSLConstList papszMD = m_oGTiffMDMD.GetMetadata(pszDomain);
6602
0
                if (papszMD && papszMD[0])
6603
0
                {
6604
0
                    CPLJSONDocument oDoc;
6605
0
                    if (oDoc.LoadMemory(papszMD[0]))
6606
0
                    {
6607
0
                        m_oISIS3Metadata = oDoc.GetRoot();
6608
0
                    }
6609
0
                }
6610
0
            }
6611
0
            CPLJSONObject obj = m_oISIS3Metadata[pszName];
6612
0
            if (obj.IsValid())
6613
0
            {
6614
0
                return m_oMapISIS3MetadataItems
6615
0
                    .insert(std::pair<std::string, std::string>(pszName,
6616
0
                                                                obj.ToString()))
6617
0
                    .first->second.c_str();
6618
0
            }
6619
0
            return nullptr;
6620
0
        }
6621
0
    }
6622
6623
0
    return m_oGTiffMDMD.GetMetadataItem(pszName, pszDomain);
6624
0
}
6625
6626
/************************************************************************/
6627
/*                          LoadEXIFMetadata()                          */
6628
/************************************************************************/
6629
6630
void GTiffDataset::LoadEXIFMetadata()
6631
0
{
6632
0
    if (m_bEXIFMetadataLoaded)
6633
0
        return;
6634
0
    m_bEXIFMetadataLoaded = true;
6635
6636
0
    VSILFILE *fp = VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
6637
6638
0
    GByte abyHeader[2] = {0};
6639
0
    if (VSIFSeekL(fp, 0, SEEK_SET) != 0 || VSIFReadL(abyHeader, 1, 2, fp) != 2)
6640
0
        return;
6641
6642
0
    const bool bLittleEndian = abyHeader[0] == 'I' && abyHeader[1] == 'I';
6643
0
    const bool bLeastSignificantBit = CPL_IS_LSB != 0;
6644
0
    const bool bSwabflag = bLittleEndian != bLeastSignificantBit;  // != is XOR.
6645
6646
0
    char **papszMetadata = nullptr;
6647
0
    toff_t nOffset = 0;
6648
6649
0
    if (TIFFGetField(m_hTIFF, TIFFTAG_EXIFIFD, &nOffset))
6650
0
    {
6651
0
        uint32_t nExifOffset = static_cast<uint32_t>(nOffset);
6652
0
        uint32_t nInterOffset = 0;
6653
0
        uint32_t nGPSOffset = 0;
6654
0
        EXIFExtractMetadata(papszMetadata, fp, static_cast<uint32_t>(nOffset),
6655
0
                            bSwabflag, 0, nExifOffset, nInterOffset,
6656
0
                            nGPSOffset);
6657
0
    }
6658
6659
0
    if (TIFFGetField(m_hTIFF, TIFFTAG_GPSIFD, &nOffset))
6660
0
    {
6661
0
        uint32_t nExifOffset = 0;
6662
0
        uint32_t nInterOffset = 0;
6663
0
        uint32_t nGPSOffset = static_cast<uint32_t>(nOffset);
6664
0
        EXIFExtractMetadata(papszMetadata, fp, static_cast<uint32_t>(nOffset),
6665
0
                            bSwabflag, 0, nExifOffset, nInterOffset,
6666
0
                            nGPSOffset);
6667
0
    }
6668
6669
0
    if (papszMetadata)
6670
0
    {
6671
0
        m_oGTiffMDMD.SetMetadata(papszMetadata, "EXIF");
6672
0
        CSLDestroy(papszMetadata);
6673
0
    }
6674
0
}
6675
6676
/************************************************************************/
6677
/*                            LoadMetadata()                            */
6678
/************************************************************************/
6679
void GTiffDataset::LoadMetadata()
6680
0
{
6681
0
    if (m_bIMDRPCMetadataLoaded)
6682
0
        return;
6683
0
    m_bIMDRPCMetadataLoaded = true;
6684
6685
0
    if (EQUAL(CPLGetExtensionSafe(GetDescription()).c_str(), "ovr"))
6686
0
    {
6687
        // Do not attempt to retrieve metadata files on .tif.ovr files.
6688
        // For example the Pleiades metadata reader might wrongly associate a
6689
        // DIM_xxx.XML file that was meant to be associated with the main
6690
        // TIFF file. The consequence of that wrong association is that if
6691
        // one cleans overviews, then the Delete() method would then delete
6692
        // that DIM_xxx.XML file since it would be reported in the GetFileList()
6693
        // of the overview dataset.
6694
0
        return;
6695
0
    }
6696
6697
0
    GDALMDReaderManager mdreadermanager;
6698
0
    GDALMDReaderBase *mdreader =
6699
0
        m_poBaseDS == nullptr
6700
0
            ? mdreadermanager.GetReader(m_osFilename.c_str(),
6701
0
                                        oOvManager.GetSiblingFiles(), MDR_ANY)
6702
0
            : nullptr;
6703
6704
0
    if (nullptr != mdreader)
6705
0
    {
6706
0
        mdreader->FillMetadata(&m_oGTiffMDMD);
6707
6708
0
        if (mdreader->GetMetadataDomain(MD_DOMAIN_RPC) == nullptr)
6709
0
        {
6710
0
            char **papszRPCMD = GTiffDatasetReadRPCTag(m_hTIFF);
6711
0
            if (papszRPCMD)
6712
0
            {
6713
0
                m_oGTiffMDMD.SetMetadata(papszRPCMD, MD_DOMAIN_RPC);
6714
0
                CSLDestroy(papszRPCMD);
6715
0
            }
6716
0
        }
6717
6718
0
        m_papszMetadataFiles = mdreader->GetMetadataFiles();
6719
0
    }
6720
0
    else
6721
0
    {
6722
0
        char **papszRPCMD = GTiffDatasetReadRPCTag(m_hTIFF);
6723
0
        if (papszRPCMD)
6724
0
        {
6725
0
            m_oGTiffMDMD.SetMetadata(papszRPCMD, MD_DOMAIN_RPC);
6726
0
            CSLDestroy(papszRPCMD);
6727
0
        }
6728
0
    }
6729
0
}
6730
6731
/************************************************************************/
6732
/*                        LoadENVIHdrIfNeeded()                         */
6733
/************************************************************************/
6734
6735
void GTiffDataset::LoadENVIHdrIfNeeded()
6736
0
{
6737
0
    if (m_bENVIHdrTried || m_poBaseDS)
6738
0
        return;
6739
0
    m_bENVIHdrTried = true;
6740
6741
0
    const std::string osHdrFilename =
6742
0
        GetSidecarFilenameWithReplacedExtension("hdr");
6743
0
    if (osHdrFilename.empty())
6744
0
        return;
6745
6746
0
    auto fp = VSIFilesystemHandler::OpenStatic(osHdrFilename.c_str(), "rb");
6747
0
    if (!fp)
6748
0
        return;
6749
6750
0
    constexpr int MAX_LINE_SIZE = 10000;
6751
6752
    // Check line is "ENVI"
6753
0
    const char *pszFirstLine = CPLReadLine2L(fp.get(), MAX_LINE_SIZE, nullptr);
6754
0
    if (!pszFirstLine || !EQUAL(pszFirstLine, "ENVI"))
6755
0
        return;
6756
6757
0
    VSIRewindL(fp.get());
6758
6759
0
    const CPLStringList aosHeaders(GDALReadENVIHeader(fp.get()));
6760
6761
    // Basic check to verify the .hdr file is indeed related to the GeoTIFF one
6762
0
    if (atoi(aosHeaders.FetchNameValueDef("samples", "0")) != nRasterXSize ||
6763
0
        atoi(aosHeaders.FetchNameValueDef("lines", "0")) != nRasterYSize ||
6764
0
        !EQUAL(aosHeaders.FetchNameValueDef("file_type", ""), "TIFF"))
6765
0
    {
6766
0
        return;
6767
0
    }
6768
6769
0
    m_bENVIHdrFound = true;
6770
6771
0
    const char *const apszOptions[] = {
6772
0
        "APPLY_DEFAULT_BANDS=NO",        "APPLY_CLASS_LOOKUP=NO",
6773
0
        "APPLY_DATA_IGNORE_VALUE=NO",    "SET_BAND_NAME=NO",
6774
0
        "SET_DATASET_LEVEL_METADATA=NO", nullptr,
6775
0
    };
6776
0
    GDALApplyENVIHeaders(this, aosHeaders, apszOptions);
6777
0
}
6778
6779
/************************************************************************/
6780
/*                     HasOptimizedReadMultiRange()                     */
6781
/************************************************************************/
6782
6783
bool GTiffDataset::HasOptimizedReadMultiRange()
6784
0
{
6785
0
    if (m_nHasOptimizedReadMultiRange >= 0)
6786
0
        return m_nHasOptimizedReadMultiRange != 0;
6787
0
    m_nHasOptimizedReadMultiRange = static_cast<signed char>(
6788
0
        VSIHasOptimizedReadMultiRange(m_osFilename.c_str())
6789
        // Config option for debug and testing purposes only
6790
0
        || CPLTestBool(CPLGetConfigOption(
6791
0
               "GTIFF_HAS_OPTIMIZED_READ_MULTI_RANGE", "NO")));
6792
0
    return m_nHasOptimizedReadMultiRange != 0;
6793
0
}
6794
6795
/************************************************************************/
6796
/*                          CacheMultiRange()                           */
6797
/************************************************************************/
6798
6799
static bool CheckTrailer(const GByte *strileData, vsi_l_offset nStrileSize)
6800
0
{
6801
0
    GByte abyTrailer[4];
6802
0
    memcpy(abyTrailer, strileData + nStrileSize, 4);
6803
0
    GByte abyLastBytes[4] = {};
6804
0
    if (nStrileSize >= 4)
6805
0
        memcpy(abyLastBytes, strileData + nStrileSize - 4, 4);
6806
0
    else
6807
0
    {
6808
        // The last bytes will be zero due to the above {} initialization,
6809
        // and that's what should be in abyTrailer too when the trailer is
6810
        // correct.
6811
0
        memcpy(abyLastBytes, strileData, static_cast<size_t>(nStrileSize));
6812
0
    }
6813
0
    return memcmp(abyTrailer, abyLastBytes, 4) == 0;
6814
0
}
6815
6816
void *GTiffDataset::CacheMultiRange(int nXOff, int nYOff, int nXSize,
6817
                                    int nYSize, int nBufXSize, int nBufYSize,
6818
                                    const int *panBandMap, int nBandCount,
6819
                                    GDALRasterIOExtraArg *psExtraArg)
6820
0
{
6821
0
    void *pBufferedData = nullptr;
6822
    // Same logic as in GDALRasterBand::IRasterIO()
6823
0
    double dfXOff = nXOff;
6824
0
    double dfYOff = nYOff;
6825
0
    double dfXSize = nXSize;
6826
0
    double dfYSize = nYSize;
6827
0
    if (psExtraArg->bFloatingPointWindowValidity)
6828
0
    {
6829
0
        dfXOff = psExtraArg->dfXOff;
6830
0
        dfYOff = psExtraArg->dfYOff;
6831
0
        dfXSize = psExtraArg->dfXSize;
6832
0
        dfYSize = psExtraArg->dfYSize;
6833
0
    }
6834
0
    const double dfSrcXInc = dfXSize / static_cast<double>(nBufXSize);
6835
0
    const double dfSrcYInc = dfYSize / static_cast<double>(nBufYSize);
6836
0
    const double EPS = 1e-10;
6837
0
    const int nBlockX1 =
6838
0
        static_cast<int>(std::max(0.0, (0 + 0.5) * dfSrcXInc + dfXOff + EPS)) /
6839
0
        m_nBlockXSize;
6840
0
    const int nBlockY1 =
6841
0
        static_cast<int>(std::max(0.0, (0 + 0.5) * dfSrcYInc + dfYOff + EPS)) /
6842
0
        m_nBlockYSize;
6843
0
    const int nBlockX2 =
6844
0
        static_cast<int>(
6845
0
            std::min(static_cast<double>(nRasterXSize - 1),
6846
0
                     (nBufXSize - 1 + 0.5) * dfSrcXInc + dfXOff + EPS)) /
6847
0
        m_nBlockXSize;
6848
0
    const int nBlockY2 =
6849
0
        static_cast<int>(
6850
0
            std::min(static_cast<double>(nRasterYSize - 1),
6851
0
                     (nBufYSize - 1 + 0.5) * dfSrcYInc + dfYOff + EPS)) /
6852
0
        m_nBlockYSize;
6853
6854
0
    struct StrileData
6855
0
    {
6856
0
        vsi_l_offset nOffset;
6857
0
        vsi_l_offset nByteCount;
6858
0
        bool bTryMask;
6859
0
    };
6860
6861
0
    std::map<int, StrileData> oMapStrileToOffsetByteCount;
6862
6863
    // Dedicated method to retrieved the offset and size in an efficient way
6864
    // when m_bBlockOrderRowMajor and m_bLeaderSizeAsUInt4 conditions are
6865
    // met.
6866
    // Except for the last block, we just read the offset from the TIFF offset
6867
    // array, and retrieve the size in the leader 4 bytes that come before the
6868
    // payload.
6869
0
    auto OptimizedRetrievalOfOffsetSize =
6870
0
        [&](int nBand, int nBlockId, vsi_l_offset &nOffset, vsi_l_offset &nSize,
6871
0
            size_t nTotalSize, size_t nMaxRawBlockCacheSize)
6872
0
    {
6873
0
        bool bTryMask = m_bMaskInterleavedWithImagery;
6874
0
        nOffset = TIFFGetStrileOffset(m_hTIFF, nBlockId);
6875
0
        if (nOffset >= 4)
6876
0
        {
6877
0
            if ((m_nPlanarConfig == PLANARCONFIG_CONTIG &&
6878
0
                 nBlockId == m_nBlocksPerBand - 1) ||
6879
0
                (m_nPlanarConfig == PLANARCONFIG_SEPARATE &&
6880
0
                 nBlockId == m_nBlocksPerBand * nBands - 1))
6881
0
            {
6882
                // Special case for the last block. As there is no next block
6883
                // from which to retrieve an offset, use the good old method
6884
                // that consists in reading the ByteCount array.
6885
0
                if (m_nPlanarConfig == PLANARCONFIG_CONTIG && bTryMask &&
6886
0
                    GetRasterBand(1)->GetMaskBand() && m_poMaskDS)
6887
0
                {
6888
0
                    auto nMaskOffset =
6889
0
                        TIFFGetStrileOffset(m_poMaskDS->m_hTIFF, nBlockId);
6890
0
                    if (nMaskOffset)
6891
0
                    {
6892
0
                        nSize = nMaskOffset +
6893
0
                                TIFFGetStrileByteCount(m_poMaskDS->m_hTIFF,
6894
0
                                                       nBlockId) -
6895
0
                                nOffset;
6896
0
                    }
6897
0
                    else
6898
0
                    {
6899
0
                        bTryMask = false;
6900
0
                    }
6901
0
                }
6902
0
                if (nSize == 0)
6903
0
                {
6904
0
                    nSize = TIFFGetStrileByteCount(m_hTIFF, nBlockId);
6905
0
                }
6906
0
                if (nSize && m_bTrailerRepeatedLast4BytesRepeated)
6907
0
                {
6908
0
                    nSize += 4;
6909
0
                }
6910
0
            }
6911
0
            else
6912
0
            {
6913
0
                const auto nNextBlockId =
6914
0
                    (m_bTileInterleave && nBand < nBands)
6915
0
                        ? nBlockId + m_nBlocksPerBand
6916
0
                    : (m_bTileInterleave && nBand == nBands)
6917
0
                        ? nBlockId - (nBand - 1) * m_nBlocksPerBand + 1
6918
0
                        : nBlockId + 1;
6919
0
                auto nOffsetNext = TIFFGetStrileOffset(m_hTIFF, nNextBlockId);
6920
0
                if (nOffsetNext > nOffset)
6921
0
                {
6922
0
                    nSize = nOffsetNext - nOffset;
6923
0
                }
6924
0
                else
6925
0
                {
6926
                    // Shouldn't happen for a compliant file
6927
0
                    if (nOffsetNext != 0)
6928
0
                    {
6929
0
                        CPLDebug("GTiff", "Tile %d is not located after %d",
6930
0
                                 nNextBlockId, nBlockId);
6931
0
                    }
6932
0
                    bTryMask = false;
6933
0
                    nSize = TIFFGetStrileByteCount(m_hTIFF, nBlockId);
6934
0
                    if (m_bTrailerRepeatedLast4BytesRepeated)
6935
0
                        nSize += 4;
6936
0
                }
6937
0
            }
6938
0
            if (nSize)
6939
0
            {
6940
0
                nOffset -= 4;
6941
0
                nSize += 4;
6942
0
                if (nTotalSize + nSize < nMaxRawBlockCacheSize)
6943
0
                {
6944
0
                    StrileData data;
6945
0
                    data.nOffset = nOffset;
6946
0
                    data.nByteCount = nSize;
6947
0
                    data.bTryMask = bTryMask;
6948
0
                    oMapStrileToOffsetByteCount[nBlockId] = data;
6949
0
                }
6950
0
            }
6951
0
        }
6952
0
        else
6953
0
        {
6954
            // Sparse tile
6955
0
            StrileData data;
6956
0
            data.nOffset = 0;
6957
0
            data.nByteCount = 0;
6958
0
            data.bTryMask = false;
6959
0
            oMapStrileToOffsetByteCount[nBlockId] = data;
6960
0
        }
6961
0
    };
6962
6963
    // This lambda fills m_poDS->m_oCacheStrileToOffsetByteCount (and
6964
    // m_poDS->m_poMaskDS->m_oCacheStrileToOffsetByteCount, when there is a
6965
    // mask) from the temporary oMapStrileToOffsetByteCount.
6966
0
    auto FillCacheStrileToOffsetByteCount =
6967
0
        [&](const std::vector<vsi_l_offset> &anOffsets,
6968
0
            const std::vector<size_t> &anSizes,
6969
0
            const std::vector<void *> &apData)
6970
0
    {
6971
0
        CPLAssert(m_bLeaderSizeAsUInt4);
6972
0
        size_t i = 0;
6973
0
        vsi_l_offset nLastOffset = 0;
6974
0
        for (const auto &entry : oMapStrileToOffsetByteCount)
6975
0
        {
6976
0
            const auto nBlockId = entry.first;
6977
0
            const auto nOffset = entry.second.nOffset;
6978
0
            const auto nSize = entry.second.nByteCount;
6979
0
            if (nOffset == 0)
6980
0
            {
6981
                // Sparse tile
6982
0
                m_oCacheStrileToOffsetByteCount.insert(nBlockId,
6983
0
                                                       std::pair(0, 0));
6984
0
                continue;
6985
0
            }
6986
6987
0
            if (nOffset < nLastOffset)
6988
0
            {
6989
                // shouldn't happen normally if tiles are sorted
6990
0
                i = 0;
6991
0
            }
6992
0
            nLastOffset = nOffset;
6993
0
            while (i < anOffsets.size() &&
6994
0
                   !(nOffset >= anOffsets[i] &&
6995
0
                     nOffset + nSize <= anOffsets[i] + anSizes[i]))
6996
0
            {
6997
0
                i++;
6998
0
            }
6999
0
            CPLAssert(i < anOffsets.size());
7000
0
            CPLAssert(nOffset >= anOffsets[i]);
7001
0
            CPLAssert(nOffset + nSize <= anOffsets[i] + anSizes[i]);
7002
0
            GUInt32 nSizeFromLeader;
7003
0
            memcpy(&nSizeFromLeader,
7004
                   // cppcheck-suppress containerOutOfBounds
7005
0
                   static_cast<GByte *>(apData[i]) + nOffset - anOffsets[i],
7006
0
                   sizeof(nSizeFromLeader));
7007
0
            CPL_LSBPTR32(&nSizeFromLeader);
7008
0
            bool bOK = true;
7009
0
            constexpr int nLeaderSize = 4;
7010
0
            const int nTrailerSize =
7011
0
                (m_bTrailerRepeatedLast4BytesRepeated ? 4 : 0);
7012
0
            if (nSizeFromLeader > nSize - nLeaderSize - nTrailerSize)
7013
0
            {
7014
0
                CPLDebug("GTiff",
7015
0
                         "Inconsistent block size from in leader of block %d",
7016
0
                         nBlockId);
7017
0
                bOK = false;
7018
0
            }
7019
0
            else if (m_bTrailerRepeatedLast4BytesRepeated)
7020
0
            {
7021
                // Check trailer consistency
7022
0
                const GByte *strileData = static_cast<GByte *>(apData[i]) +
7023
0
                                          nOffset - anOffsets[i] + nLeaderSize;
7024
0
                if (!CheckTrailer(strileData, nSizeFromLeader))
7025
0
                {
7026
0
                    CPLDebug("GTiff", "Inconsistent trailer of block %d",
7027
0
                             nBlockId);
7028
0
                    bOK = false;
7029
0
                }
7030
0
            }
7031
0
            if (!bOK)
7032
0
            {
7033
0
                return false;
7034
0
            }
7035
7036
0
            {
7037
0
                const vsi_l_offset nRealOffset = nOffset + nLeaderSize;
7038
0
                const vsi_l_offset nRealSize = nSizeFromLeader;
7039
#ifdef DEBUG_VERBOSE
7040
                CPLDebug("GTiff",
7041
                         "Block %d found at offset " CPL_FRMT_GUIB
7042
                         " with size " CPL_FRMT_GUIB,
7043
                         nBlockId, nRealOffset, nRealSize);
7044
#endif
7045
0
                m_oCacheStrileToOffsetByteCount.insert(
7046
0
                    nBlockId, std::pair(nRealOffset, nRealSize));
7047
0
            }
7048
7049
            // Processing of mask
7050
0
            if (!(entry.second.bTryMask && m_bMaskInterleavedWithImagery &&
7051
0
                  GetRasterBand(1)->GetMaskBand() && m_poMaskDS))
7052
0
            {
7053
0
                continue;
7054
0
            }
7055
7056
0
            bOK = false;
7057
0
            const vsi_l_offset nMaskOffsetWithLeader =
7058
0
                nOffset + nLeaderSize + nSizeFromLeader + nTrailerSize;
7059
0
            if (nMaskOffsetWithLeader + nLeaderSize <=
7060
0
                anOffsets[i] + anSizes[i])
7061
0
            {
7062
0
                GUInt32 nMaskSizeFromLeader;
7063
0
                memcpy(&nMaskSizeFromLeader,
7064
0
                       static_cast<GByte *>(apData[i]) + nMaskOffsetWithLeader -
7065
0
                           anOffsets[i],
7066
0
                       sizeof(nMaskSizeFromLeader));
7067
0
                CPL_LSBPTR32(&nMaskSizeFromLeader);
7068
0
                if (nMaskOffsetWithLeader + nLeaderSize + nMaskSizeFromLeader +
7069
0
                        nTrailerSize <=
7070
0
                    anOffsets[i] + anSizes[i])
7071
0
                {
7072
0
                    bOK = true;
7073
0
                    if (m_bTrailerRepeatedLast4BytesRepeated)
7074
0
                    {
7075
                        // Check trailer consistency
7076
0
                        const GByte *strileMaskData =
7077
0
                            static_cast<GByte *>(apData[i]) + nOffset -
7078
0
                            anOffsets[i] + nLeaderSize + nSizeFromLeader +
7079
0
                            nTrailerSize + nLeaderSize;
7080
0
                        if (!CheckTrailer(strileMaskData, nMaskSizeFromLeader))
7081
0
                        {
7082
0
                            CPLDebug("GTiff",
7083
0
                                     "Inconsistent trailer of mask of block %d",
7084
0
                                     nBlockId);
7085
0
                            bOK = false;
7086
0
                        }
7087
0
                    }
7088
0
                }
7089
0
                if (bOK)
7090
0
                {
7091
0
                    const vsi_l_offset nRealOffset = nOffset + nLeaderSize +
7092
0
                                                     nSizeFromLeader +
7093
0
                                                     nTrailerSize + nLeaderSize;
7094
0
                    const vsi_l_offset nRealSize = nMaskSizeFromLeader;
7095
#ifdef DEBUG_VERBOSE
7096
                    CPLDebug("GTiff",
7097
                             "Mask of block %d found at offset " CPL_FRMT_GUIB
7098
                             " with size " CPL_FRMT_GUIB,
7099
                             nBlockId, nRealOffset, nRealSize);
7100
#endif
7101
7102
0
                    m_poMaskDS->m_oCacheStrileToOffsetByteCount.insert(
7103
0
                        nBlockId, std::pair(nRealOffset, nRealSize));
7104
0
                }
7105
0
            }
7106
0
            if (!bOK)
7107
0
            {
7108
0
                CPLDebug("GTiff",
7109
0
                         "Mask for block %d is not properly interleaved with "
7110
0
                         "imagery block",
7111
0
                         nBlockId);
7112
0
            }
7113
0
        }
7114
0
        return true;
7115
0
    };
7116
7117
0
    thandle_t th = TIFFClientdata(m_hTIFF);
7118
0
    if (!VSI_TIFFHasCachedRanges(th))
7119
0
    {
7120
0
        std::vector<std::pair<vsi_l_offset, size_t>> aOffsetSize;
7121
0
        size_t nTotalSize = 0;
7122
0
        const unsigned int nMaxRawBlockCacheSize = atoi(
7123
0
            CPLGetConfigOption("GDAL_MAX_RAW_BLOCK_CACHE_SIZE", "10485760"));
7124
0
        bool bGoOn = true;
7125
0
        for (int iBand = 0; iBand < nBandCount; ++iBand)
7126
0
        {
7127
0
            const int nBand = panBandMap[iBand];
7128
0
            GTiffRasterBand *poBand =
7129
0
                cpl::down_cast<GTiffRasterBand *>(papoBands[nBand - 1]);
7130
0
            for (int iY = nBlockY1; bGoOn && iY <= nBlockY2; iY++)
7131
0
            {
7132
0
                for (int iX = nBlockX1; bGoOn && iX <= nBlockX2; iX++)
7133
0
                {
7134
0
                    GDALRasterBlock *poBlock =
7135
0
                        poBand->TryGetLockedBlockRef(iX, iY);
7136
0
                    if (poBlock != nullptr)
7137
0
                    {
7138
0
                        poBlock->DropLock();
7139
0
                        continue;
7140
0
                    }
7141
0
                    int nBlockId = iX + iY * m_nBlocksPerRow;
7142
0
                    if (m_nPlanarConfig == PLANARCONFIG_SEPARATE)
7143
0
                        nBlockId += (nBand - 1) * m_nBlocksPerBand;
7144
0
                    vsi_l_offset nOffset = 0;
7145
0
                    vsi_l_offset nSize = 0;
7146
7147
0
                    if (!m_bStreamingIn && m_bBlockOrderRowMajor &&
7148
0
                        m_bLeaderSizeAsUInt4)
7149
0
                    {
7150
0
                        OptimizedRetrievalOfOffsetSize(nBand, nBlockId, nOffset,
7151
0
                                                       nSize, nTotalSize,
7152
0
                                                       nMaxRawBlockCacheSize);
7153
0
                    }
7154
0
                    else
7155
0
                    {
7156
0
                        CPL_IGNORE_RET_VAL(IsBlockAvailable(nBlockId, &nOffset,
7157
0
                                                            &nSize, nullptr));
7158
0
                    }
7159
0
                    if (nSize)
7160
0
                    {
7161
0
                        if (nTotalSize + nSize < nMaxRawBlockCacheSize)
7162
0
                        {
7163
#ifdef DEBUG_VERBOSE
7164
                            CPLDebug(
7165
                                "GTiff",
7166
                                "Precaching for block (%d, %d), " CPL_FRMT_GUIB
7167
                                "-" CPL_FRMT_GUIB,
7168
                                iX, iY, nOffset,
7169
                                nOffset + static_cast<size_t>(nSize) - 1);
7170
#endif
7171
0
                            aOffsetSize.push_back(
7172
0
                                std::pair(nOffset, static_cast<size_t>(nSize)));
7173
0
                            nTotalSize += static_cast<size_t>(nSize);
7174
0
                        }
7175
0
                        else
7176
0
                        {
7177
0
                            bGoOn = false;
7178
0
                        }
7179
0
                    }
7180
0
                }
7181
0
            }
7182
0
        }
7183
7184
0
        std::sort(aOffsetSize.begin(), aOffsetSize.end());
7185
7186
0
        if (nTotalSize > 0)
7187
0
        {
7188
0
            pBufferedData = VSI_MALLOC_VERBOSE(nTotalSize);
7189
0
            if (pBufferedData)
7190
0
            {
7191
0
                std::vector<vsi_l_offset> anOffsets;
7192
0
                std::vector<size_t> anSizes;
7193
0
                std::vector<void *> apData;
7194
0
                anOffsets.push_back(aOffsetSize[0].first);
7195
0
                apData.push_back(static_cast<GByte *>(pBufferedData));
7196
0
                size_t nChunkSize = aOffsetSize[0].second;
7197
0
                size_t nAccOffset = 0;
7198
                // Try to merge contiguous or slightly overlapping ranges
7199
0
                for (size_t i = 0; i < aOffsetSize.size() - 1; i++)
7200
0
                {
7201
0
                    if (aOffsetSize[i].first < aOffsetSize[i + 1].first &&
7202
0
                        aOffsetSize[i].first + aOffsetSize[i].second >=
7203
0
                            aOffsetSize[i + 1].first)
7204
0
                    {
7205
0
                        const auto overlap = aOffsetSize[i].first +
7206
0
                                             aOffsetSize[i].second -
7207
0
                                             aOffsetSize[i + 1].first;
7208
                        // That should always be the case for well behaved
7209
                        // TIFF files.
7210
0
                        if (aOffsetSize[i + 1].second > overlap)
7211
0
                        {
7212
0
                            nChunkSize += static_cast<size_t>(
7213
0
                                aOffsetSize[i + 1].second - overlap);
7214
0
                        }
7215
0
                    }
7216
0
                    else
7217
0
                    {
7218
                        // terminate current block
7219
0
                        anSizes.push_back(nChunkSize);
7220
#ifdef DEBUG_VERBOSE
7221
                        CPLDebug("GTiff",
7222
                                 "Requesting range [" CPL_FRMT_GUIB
7223
                                 "-" CPL_FRMT_GUIB "]",
7224
                                 anOffsets.back(),
7225
                                 anOffsets.back() + anSizes.back() - 1);
7226
#endif
7227
0
                        nAccOffset += nChunkSize;
7228
                        // start a new range
7229
0
                        anOffsets.push_back(aOffsetSize[i + 1].first);
7230
0
                        apData.push_back(static_cast<GByte *>(pBufferedData) +
7231
0
                                         nAccOffset);
7232
0
                        nChunkSize = aOffsetSize[i + 1].second;
7233
0
                    }
7234
0
                }
7235
                // terminate last block
7236
0
                anSizes.push_back(nChunkSize);
7237
#ifdef DEBUG_VERBOSE
7238
                CPLDebug(
7239
                    "GTiff",
7240
                    "Requesting range [" CPL_FRMT_GUIB "-" CPL_FRMT_GUIB "]",
7241
                    anOffsets.back(), anOffsets.back() + anSizes.back() - 1);
7242
#endif
7243
7244
0
                VSILFILE *fp = VSI_TIFFGetVSILFile(th);
7245
7246
                // An error in VSIFReadMultiRangeL() will not be critical,
7247
                // as this method is an optimization, and if it fails,
7248
                // tile-by-tile data acquisition will be done, so we can
7249
                // temporary turn failures into warnings.
7250
0
                bool ok;
7251
0
                {
7252
0
                    CPLTurnFailureIntoWarningBackuper
7253
0
                        oFailureToWarningBackuper{};
7254
0
                    ok = VSIFReadMultiRangeL(static_cast<int>(anSizes.size()),
7255
0
                                             &apData[0], &anOffsets[0],
7256
0
                                             &anSizes[0], fp) == 0;
7257
0
                }
7258
7259
0
                if (ok)
7260
0
                {
7261
0
                    if (!oMapStrileToOffsetByteCount.empty() &&
7262
0
                        !FillCacheStrileToOffsetByteCount(anOffsets, anSizes,
7263
0
                                                          apData))
7264
0
                    {
7265
                        // Retry without optimization
7266
0
                        CPLFree(pBufferedData);
7267
0
                        m_bLeaderSizeAsUInt4 = false;
7268
0
                        void *pRet = CacheMultiRange(
7269
0
                            nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
7270
0
                            panBandMap, nBandCount, psExtraArg);
7271
0
                        m_bLeaderSizeAsUInt4 = true;
7272
0
                        return pRet;
7273
0
                    }
7274
7275
0
                    VSI_TIFFSetCachedRanges(
7276
0
                        th, static_cast<int>(anSizes.size()), &apData[0],
7277
0
                        &anOffsets[0], &anSizes[0]);
7278
0
                }
7279
0
                else
7280
0
                {
7281
0
                    CPLFree(pBufferedData);
7282
0
                    pBufferedData = nullptr;
7283
0
                }
7284
0
            }
7285
0
        }
7286
0
    }
7287
0
    return pBufferedData;
7288
0
}
7289
7290
/************************************************************************/
7291
/*                           CheckCOGLayout()                           */
7292
/************************************************************************/
7293
7294
bool GTiffDataset::CheckCOGLayout()
7295
0
{
7296
0
    bool bRet = CPL_TO_BOOL(TIFFIsTiled(m_hTIFF));
7297
0
    const bool bNeedsOverviews = (nRasterXSize > 512 || nRasterYSize > 512);
7298
0
    if (bNeedsOverviews)
7299
0
        ScanDirectories();
7300
0
    bRet = bRet && !(m_apoOverviewDS.empty() && bNeedsOverviews);
7301
0
#ifdef TIFFLIB_MAJOR_VERSION
7302
    // TIFFIsBigTIFF() added in libtiff 4.4
7303
0
    const uint64_t nExpectedDirOffset = TIFFIsBigTIFF(m_hTIFF) ? 16 : 8;
7304
#else
7305
    GByte abyData[4] = {0, 0, 0, 0};
7306
    {
7307
        const auto nCurFPOffset = VSIFTellL(m_fpL);
7308
        VSIFSeekL(m_fpL, 0, SEEK_SET);
7309
        CPL_IGNORE_RET_VAL(VSIFReadL(abyData, 1, sizeof(abyData), m_fpL));
7310
        VSIFSeekL(m_fpL, nCurFPOffset, SEEK_SET);
7311
    }
7312
    const uint64_t nExpectedDirOffset =
7313
        (abyData[2] == 0x2B || abyData[3] == 0x2B) ? 16 : 8;
7314
#endif
7315
0
    bRet = bRet && (m_nDirOffset == nExpectedDirOffset);
7316
0
    uint64_t nLastIFDOffset = nExpectedDirOffset;
7317
0
    int nLastXSize = nRasterXSize;
7318
0
    int nLastYSize = nRasterYSize;
7319
0
    for (const auto &poOvrDS : m_apoOverviewDS)
7320
0
    {
7321
0
        bRet = bRet && poOvrDS->m_nDirOffset > nLastIFDOffset;
7322
0
        bRet = bRet && poOvrDS->nRasterXSize <= nLastXSize;
7323
0
        bRet = bRet && poOvrDS->nRasterYSize <= nLastYSize;
7324
0
        bRet = bRet && CPL_TO_BOOL(TIFFIsTiled(poOvrDS->m_hTIFF));
7325
0
        nLastXSize = poOvrDS->nRasterXSize;
7326
0
        nLastYSize = poOvrDS->nRasterYSize;
7327
0
        nLastIFDOffset = poOvrDS->m_nDirOffset;
7328
0
    }
7329
0
    uint64_t nDataOffset = nLastIFDOffset;
7330
0
    for (auto iter = m_apoOverviewDS.rbegin();
7331
0
         bRet && iter != m_apoOverviewDS.rend(); ++iter)
7332
0
    {
7333
0
        bRet = CPL_TO_BOOL(TIFFIsTiled((*iter)->m_hTIFF));
7334
0
        vsi_l_offset nOffset = 0;
7335
0
        vsi_l_offset nSize = 0;
7336
0
        if ((*iter)->IsBlockAvailable(0, &nOffset, &nSize, nullptr))
7337
0
        {
7338
0
            bRet = bRet && nOffset > nDataOffset;
7339
0
            nDataOffset = nOffset;
7340
0
        }
7341
0
    }
7342
0
    if (!m_apoOverviewDS.empty())
7343
0
    {
7344
0
        vsi_l_offset nOffset = 0;
7345
0
        vsi_l_offset nSize = 0;
7346
0
        if (IsBlockAvailable(0, &nOffset, &nSize, nullptr))
7347
0
        {
7348
0
            bRet = bRet && nOffset > nDataOffset;
7349
0
        }
7350
0
    }
7351
0
    return bRet;
7352
0
}