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