Coverage Report

Created: 2025-11-16 06:25

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