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