Coverage Report

Created: 2025-12-31 06:48

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