Coverage Report

Created: 2025-06-13 06:18

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