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