Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GeoTIFF Driver
4 : * Purpose: GDAL GeoTIFF support.
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 :
34 : #include <cassert>
35 :
36 : #include <algorithm>
37 : #include <limits>
38 : #include <memory>
39 : #include <set>
40 : #include <string>
41 : #include <tuple>
42 : #include <utility>
43 :
44 : #include "cpl_error.h"
45 : #include "cpl_vsi.h"
46 : #include "cpl_vsi_virtual.h"
47 : #include "cpl_worker_thread_pool.h"
48 : #include "ogr_proj_p.h" // OSRGetProjTLSContext()
49 : #include "tif_jxl.h"
50 : #include "tifvsi.h"
51 : #include "xtiffio.h"
52 :
53 : static const GTIFFTag asTIFFTags[] = {
54 : {"TIFFTAG_DOCUMENTNAME", TIFFTAG_DOCUMENTNAME, GTIFFTAGTYPE_STRING},
55 : {"TIFFTAG_IMAGEDESCRIPTION", TIFFTAG_IMAGEDESCRIPTION, GTIFFTAGTYPE_STRING},
56 : {"TIFFTAG_SOFTWARE", TIFFTAG_SOFTWARE, GTIFFTAGTYPE_STRING},
57 : {"TIFFTAG_DATETIME", TIFFTAG_DATETIME, GTIFFTAGTYPE_STRING},
58 : {"TIFFTAG_ARTIST", TIFFTAG_ARTIST, GTIFFTAGTYPE_STRING},
59 : {"TIFFTAG_HOSTCOMPUTER", TIFFTAG_HOSTCOMPUTER, GTIFFTAGTYPE_STRING},
60 : {"TIFFTAG_COPYRIGHT", TIFFTAG_COPYRIGHT, GTIFFTAGTYPE_STRING},
61 : {"TIFFTAG_XRESOLUTION", TIFFTAG_XRESOLUTION, GTIFFTAGTYPE_FLOAT},
62 : {"TIFFTAG_YRESOLUTION", TIFFTAG_YRESOLUTION, GTIFFTAGTYPE_FLOAT},
63 : // Dealt as special case.
64 : {"TIFFTAG_RESOLUTIONUNIT", TIFFTAG_RESOLUTIONUNIT, GTIFFTAGTYPE_SHORT},
65 : {"TIFFTAG_MINSAMPLEVALUE", TIFFTAG_MINSAMPLEVALUE, GTIFFTAGTYPE_SHORT},
66 : {"TIFFTAG_MAXSAMPLEVALUE", TIFFTAG_MAXSAMPLEVALUE, GTIFFTAGTYPE_SHORT},
67 :
68 : // GeoTIFF DGIWG tags
69 : {"GEO_METADATA", TIFFTAG_GEO_METADATA, GTIFFTAGTYPE_BYTE_STRING},
70 : {"TIFF_RSID", TIFFTAG_TIFF_RSID, GTIFFTAGTYPE_STRING},
71 : {nullptr, 0, GTIFFTAGTYPE_STRING},
72 : };
73 :
74 : /************************************************************************/
75 : /* GetTIFFTags() */
76 : /************************************************************************/
77 :
78 19609 : const GTIFFTag *GTiffDataset::GetTIFFTags()
79 : {
80 19609 : return asTIFFTags;
81 : }
82 :
83 : /************************************************************************/
84 : /* GTiffDataset() */
85 : /************************************************************************/
86 :
87 21456 : GTiffDataset::GTiffDataset()
88 : : m_bStreamingIn(false), m_bStreamingOut(false), m_bScanDeferred(true),
89 : m_bSingleIFDOpened(false), m_bLoadedBlockDirty(false),
90 : m_bWriteError(false), m_bLookedForProjection(false),
91 : m_bLookedForMDAreaOrPoint(false), m_bGeoTransformValid(false),
92 : m_bCrystalized(true), m_bGeoTIFFInfoChanged(false),
93 : m_bForceUnsetGTOrGCPs(false), m_bForceUnsetProjection(false),
94 : m_bNoDataChanged(false), m_bNoDataSet(false), m_bNoDataSetAsInt64(false),
95 : m_bNoDataSetAsUInt64(false), m_bMetadataChanged(false),
96 : m_bColorProfileMetadataChanged(false), m_bForceUnsetRPC(false),
97 : m_bNeedsRewrite(false), m_bLoadingOtherBands(false), m_bIsOverview(false),
98 : m_bWriteEmptyTiles(true), m_bFillEmptyTilesAtClosing(false),
99 : m_bTreatAsSplit(false), m_bTreatAsSplitBitmap(false), m_bClipWarn(false),
100 : m_bIMDRPCMetadataLoaded(false), m_bEXIFMetadataLoaded(false),
101 : m_bICCMetadataLoaded(false),
102 : m_bHasWarnedDisableAggressiveBandCaching(false),
103 : m_bDontReloadFirstBlock(false), m_bWebPLossless(false),
104 : m_bPromoteTo8Bits(false),
105 : m_bDebugDontWriteBlocks(
106 21461 : CPLTestBool(CPLGetConfigOption("GTIFF_DONT_WRITE_BLOCKS", "NO"))),
107 : m_bIsFinalized(false),
108 : m_bIgnoreReadErrors(
109 21461 : CPLTestBool(CPLGetConfigOption("GTIFF_IGNORE_READ_ERRORS", "NO"))),
110 21460 : m_bDirectIO(CPLTestBool(CPLGetConfigOption("GTIFF_DIRECT_IO", "NO"))),
111 : m_bReadGeoTransform(false), m_bLoadPam(false),
112 : m_bHasGotSiblingFiles(false),
113 : m_bHasIdentifiedAuthorizedGeoreferencingSources(false),
114 : m_bLayoutIFDSBeforeData(false), m_bBlockOrderRowMajor(false),
115 : m_bLeaderSizeAsUInt4(false), m_bTrailerRepeatedLast4BytesRepeated(false),
116 : m_bMaskInterleavedWithImagery(false), m_bKnownIncompatibleEdition(false),
117 : m_bWriteKnownIncompatibleEdition(false), m_bHasUsedReadEncodedAPI(false),
118 64378 : m_bWriteCOGLayout(false)
119 : {
120 : // CPLDebug("GDAL", "sizeof(GTiffDataset) = %d bytes", static_cast<int>(
121 : // sizeof(GTiffDataset)));
122 :
123 : const char *pszVirtualMemIO =
124 21460 : CPLGetConfigOption("GTIFF_VIRTUAL_MEM_IO", "NO");
125 21461 : if (EQUAL(pszVirtualMemIO, "IF_ENOUGH_RAM"))
126 0 : m_eVirtualMemIOUsage = VirtualMemIOEnum::IF_ENOUGH_RAM;
127 21461 : else if (CPLTestBool(pszVirtualMemIO))
128 41 : m_eVirtualMemIOUsage = VirtualMemIOEnum::YES;
129 :
130 21461 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
131 21442 : }
132 :
133 : /************************************************************************/
134 : /* ~GTiffDataset() */
135 : /************************************************************************/
136 :
137 42920 : GTiffDataset::~GTiffDataset()
138 :
139 : {
140 21460 : GTiffDataset::Close();
141 42919 : }
142 :
143 : /************************************************************************/
144 : /* Close() */
145 : /************************************************************************/
146 :
147 39435 : CPLErr GTiffDataset::Close()
148 : {
149 39435 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
150 : {
151 21460 : auto [eErr, bDroppedRef] = Finalize();
152 :
153 21460 : if (m_pszTmpFilename)
154 : {
155 4 : VSIUnlink(m_pszTmpFilename);
156 4 : CPLFree(m_pszTmpFilename);
157 : }
158 :
159 21460 : if (GDALPamDataset::Close() != CE_None)
160 0 : eErr = CE_Failure;
161 21460 : return eErr;
162 : }
163 17975 : return CE_None;
164 : }
165 :
166 : /************************************************************************/
167 : /* Finalize() */
168 : /************************************************************************/
169 :
170 : // Return a tuple (CPLErr, bool) to indicate respectively if an I/O error has
171 : // occurred and if a reference to an auxiliary dataset has been dropped.
172 21540 : std::tuple<CPLErr, bool> GTiffDataset::Finalize()
173 : {
174 21540 : bool bDroppedRef = false;
175 21540 : if (m_bIsFinalized)
176 160 : return std::tuple(CE_None, bDroppedRef);
177 :
178 21460 : CPLErr eErr = CE_None;
179 21460 : Crystalize();
180 :
181 21460 : if (m_bColorProfileMetadataChanged)
182 : {
183 2 : SaveICCProfile(this, nullptr, nullptr, 0);
184 2 : m_bColorProfileMetadataChanged = false;
185 : }
186 :
187 : /* -------------------------------------------------------------------- */
188 : /* Handle forcing xml:ESRI data to be written to PAM. */
189 : /* -------------------------------------------------------------------- */
190 21460 : if (CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
191 : {
192 7 : char **papszESRIMD = GTiffDataset::GetMetadata("xml:ESRI");
193 7 : if (papszESRIMD)
194 : {
195 5 : GDALPamDataset::SetMetadata(papszESRIMD, "xml:ESRI");
196 : }
197 : }
198 :
199 21460 : if (m_psVirtualMemIOMapping)
200 11 : CPLVirtualMemFree(m_psVirtualMemIOMapping);
201 21460 : m_psVirtualMemIOMapping = nullptr;
202 :
203 : /* -------------------------------------------------------------------- */
204 : /* Fill in missing blocks with empty data. */
205 : /* -------------------------------------------------------------------- */
206 21460 : if (m_bFillEmptyTilesAtClosing)
207 : {
208 : /* --------------------------------------------------------------------
209 : */
210 : /* Ensure any blocks write cached by GDAL gets pushed through libtiff.
211 : */
212 : /* --------------------------------------------------------------------
213 : */
214 4272 : if (FlushCacheInternal(true, /* at closing */
215 4272 : false /* do not call FlushDirectory */) !=
216 : CE_None)
217 : {
218 0 : eErr = CE_Failure;
219 : }
220 :
221 4272 : if (FillEmptyTiles() != CE_None)
222 : {
223 9 : eErr = CE_Failure;
224 : }
225 4272 : m_bFillEmptyTilesAtClosing = false;
226 : }
227 :
228 : /* -------------------------------------------------------------------- */
229 : /* Force a complete flush, including either rewriting(moving) */
230 : /* of writing in place the current directory. */
231 : /* -------------------------------------------------------------------- */
232 21460 : if (FlushCacheInternal(true /* at closing */, true) != CE_None)
233 : {
234 7 : eErr = CE_Failure;
235 : }
236 :
237 : // Destroy compression queue
238 21460 : if (m_poCompressQueue)
239 : {
240 57 : m_poCompressQueue->WaitCompletion();
241 :
242 276 : for (int i = 0; i < static_cast<int>(m_asCompressionJobs.size()); ++i)
243 : {
244 219 : CPLFree(m_asCompressionJobs[i].pabyBuffer);
245 219 : if (m_asCompressionJobs[i].pszTmpFilename)
246 : {
247 219 : VSIUnlink(m_asCompressionJobs[i].pszTmpFilename);
248 219 : CPLFree(m_asCompressionJobs[i].pszTmpFilename);
249 : }
250 : }
251 57 : m_poCompressQueue.reset();
252 : }
253 :
254 : /* -------------------------------------------------------------------- */
255 : /* If there is still changed metadata, then presumably we want */
256 : /* to push it into PAM. */
257 : /* -------------------------------------------------------------------- */
258 21460 : if (m_bMetadataChanged)
259 : {
260 6 : PushMetadataToPam();
261 6 : m_bMetadataChanged = false;
262 6 : GDALPamDataset::FlushCache(false);
263 : }
264 :
265 : /* -------------------------------------------------------------------- */
266 : /* Cleanup overviews. */
267 : /* -------------------------------------------------------------------- */
268 21460 : if (!m_poBaseDS)
269 : {
270 : // Nullify m_nOverviewCount before deleting overviews, otherwise
271 : // GTiffDataset::FlushDirectory() might try to access an overview
272 : // that is being deleted (#5580)
273 19973 : const int nOldOverviewCount = m_nOverviewCount;
274 19973 : m_nOverviewCount = 0;
275 21179 : for (int i = 0; i < nOldOverviewCount; ++i)
276 : {
277 1206 : delete m_papoOverviewDS[i];
278 1206 : bDroppedRef = true;
279 : }
280 :
281 20033 : for (int i = 0; i < m_nJPEGOverviewCountOri; ++i)
282 : {
283 60 : delete m_papoJPEGOverviewDS[i];
284 60 : bDroppedRef = true;
285 : }
286 19973 : m_nJPEGOverviewCount = 0;
287 19973 : m_nJPEGOverviewCountOri = 0;
288 19973 : CPLFree(m_papoJPEGOverviewDS);
289 19973 : m_papoJPEGOverviewDS = nullptr;
290 : }
291 :
292 : // If we are a mask dataset, we can have overviews, but we don't
293 : // own them. We can only free the array, not the overviews themselves.
294 21460 : CPLFree(m_papoOverviewDS);
295 21460 : m_papoOverviewDS = nullptr;
296 :
297 : // m_poMaskDS is owned by the main image and the overviews
298 : // so because of the latter case, we can delete it even if
299 : // we are not the base image.
300 21460 : if (m_poMaskDS)
301 : {
302 : // Nullify m_nOverviewCount before deleting overviews, otherwise
303 : // GTiffDataset::FlushDirectory() might try to access it while being
304 : // deleted. (#5580)
305 278 : auto poMaskDS = m_poMaskDS;
306 278 : m_poMaskDS = nullptr;
307 278 : delete poMaskDS;
308 278 : bDroppedRef = true;
309 : }
310 :
311 21460 : if (m_poColorTable != nullptr)
312 184 : delete m_poColorTable;
313 21460 : m_poColorTable = nullptr;
314 :
315 21460 : if (m_hTIFF)
316 : {
317 21459 : XTIFFClose(m_hTIFF);
318 21459 : m_hTIFF = nullptr;
319 : }
320 :
321 21460 : if (!m_poBaseDS)
322 : {
323 19973 : if (m_fpL != nullptr)
324 : {
325 19973 : if (m_bWriteKnownIncompatibleEdition)
326 : {
327 : GByte abyHeader[4096];
328 9 : VSIFSeekL(m_fpL, 0, SEEK_SET);
329 9 : VSIFReadL(abyHeader, 1, sizeof(abyHeader), m_fpL);
330 9 : const char *szKeyToLook =
331 : "KNOWN_INCOMPATIBLE_EDITION=NO\n "; // trailing space
332 : // intended
333 1449 : for (size_t i = 0; i < sizeof(abyHeader) - strlen(szKeyToLook);
334 : i++)
335 : {
336 1449 : if (memcmp(abyHeader + i, szKeyToLook,
337 : strlen(szKeyToLook)) == 0)
338 : {
339 9 : const char *szNewKey =
340 : "KNOWN_INCOMPATIBLE_EDITION=YES\n";
341 9 : CPLAssert(strlen(szKeyToLook) == strlen(szNewKey));
342 9 : memcpy(abyHeader + i, szNewKey, strlen(szNewKey));
343 9 : VSIFSeekL(m_fpL, 0, SEEK_SET);
344 9 : VSIFWriteL(abyHeader, 1, sizeof(abyHeader), m_fpL);
345 9 : break;
346 : }
347 : }
348 : }
349 19973 : if (VSIFCloseL(m_fpL) != 0)
350 : {
351 0 : eErr = CE_Failure;
352 0 : ReportError(CE_Failure, CPLE_FileIO, "I/O error");
353 : }
354 19973 : m_fpL = nullptr;
355 : }
356 : }
357 :
358 21460 : if (m_fpToWrite != nullptr)
359 : {
360 7 : if (VSIFCloseL(m_fpToWrite) != 0)
361 : {
362 0 : eErr = CE_Failure;
363 0 : ReportError(CE_Failure, CPLE_FileIO, "I/O error");
364 : }
365 7 : m_fpToWrite = nullptr;
366 : }
367 :
368 21460 : m_aoGCPs.clear();
369 :
370 21460 : CSLDestroy(m_papszCreationOptions);
371 21460 : m_papszCreationOptions = nullptr;
372 :
373 21460 : CPLFree(m_pabyTempWriteBuffer);
374 21460 : m_pabyTempWriteBuffer = nullptr;
375 :
376 21460 : m_bIMDRPCMetadataLoaded = false;
377 21460 : CSLDestroy(m_papszMetadataFiles);
378 21460 : m_papszMetadataFiles = nullptr;
379 :
380 21460 : VSIFree(m_pTempBufferForCommonDirectIO);
381 21460 : m_pTempBufferForCommonDirectIO = nullptr;
382 :
383 21460 : CPLFree(m_panMaskOffsetLsb);
384 21460 : m_panMaskOffsetLsb = nullptr;
385 :
386 21460 : CPLFree(m_pszVertUnit);
387 21460 : m_pszVertUnit = nullptr;
388 :
389 21460 : CPLFree(m_pszFilename);
390 21460 : m_pszFilename = nullptr;
391 :
392 21460 : CPLFree(m_pszGeorefFilename);
393 21460 : m_pszGeorefFilename = nullptr;
394 :
395 21460 : CPLFree(m_pszXMLFilename);
396 21460 : m_pszXMLFilename = nullptr;
397 :
398 21460 : m_bIsFinalized = true;
399 :
400 21460 : return std::tuple(eErr, bDroppedRef);
401 : }
402 :
403 : /************************************************************************/
404 : /* CloseDependentDatasets() */
405 : /************************************************************************/
406 :
407 80 : int GTiffDataset::CloseDependentDatasets()
408 : {
409 80 : if (m_poBaseDS)
410 0 : return FALSE;
411 :
412 80 : int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
413 :
414 : // We ignore eErr as it is not relevant for CloseDependentDatasets(),
415 : // which is called in a "garbage collection" context.
416 80 : auto [eErr, bHasDroppedRefInFinalize] = Finalize();
417 80 : if (bHasDroppedRefInFinalize)
418 1 : bHasDroppedRef = true;
419 :
420 80 : return bHasDroppedRef;
421 : }
422 :
423 : /************************************************************************/
424 : /* IsWholeBlock() */
425 : /************************************************************************/
426 :
427 27 : bool GTiffDataset::IsWholeBlock(int nXOff, int nYOff, int nXSize,
428 : int nYSize) const
429 : {
430 27 : if ((nXOff % m_nBlockXSize) != 0 || (nYOff % m_nBlockYSize) != 0)
431 : {
432 0 : return false;
433 : }
434 27 : if (TIFFIsTiled(m_hTIFF))
435 : {
436 3 : return nXSize == m_nBlockXSize && nYSize == m_nBlockYSize;
437 : }
438 : else
439 : {
440 46 : return nXSize == m_nBlockXSize &&
441 46 : (nYSize == m_nBlockYSize || nYOff + nYSize == nRasterYSize);
442 : }
443 : }
444 :
445 : /************************************************************************/
446 : /* IRasterIO() */
447 : /************************************************************************/
448 :
449 432140 : CPLErr GTiffDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
450 : int nXSize, int nYSize, void *pData,
451 : int nBufXSize, int nBufYSize,
452 : GDALDataType eBufType, int nBandCount,
453 : int *panBandMap, GSpacing nPixelSpace,
454 : GSpacing nLineSpace, GSpacing nBandSpace,
455 : GDALRasterIOExtraArg *psExtraArg)
456 :
457 : {
458 : // Try to pass the request to the most appropriate overview dataset.
459 432140 : if (nBufXSize < nXSize && nBufYSize < nYSize)
460 : {
461 157877 : int bTried = FALSE;
462 157877 : if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
463 157717 : ++m_nJPEGOverviewVisibilityCounter;
464 157877 : const CPLErr eErr = TryOverviewRasterIO(
465 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
466 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
467 : nBandSpace, psExtraArg, &bTried);
468 157877 : if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
469 157717 : --m_nJPEGOverviewVisibilityCounter;
470 157877 : if (bTried)
471 25 : return eErr;
472 : }
473 :
474 432115 : if (m_eVirtualMemIOUsage != VirtualMemIOEnum::NO)
475 : {
476 : const int nErr =
477 649 : VirtualMemIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
478 : nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
479 : nPixelSpace, nLineSpace, nBandSpace, psExtraArg);
480 649 : if (nErr >= 0)
481 608 : return static_cast<CPLErr>(nErr);
482 : }
483 431507 : if (m_bDirectIO)
484 : {
485 : const int nErr =
486 471 : DirectIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
487 : nBufYSize, eBufType, nBandCount, panBandMap, nPixelSpace,
488 : nLineSpace, nBandSpace, psExtraArg);
489 471 : if (nErr >= 0)
490 414 : return static_cast<CPLErr>(nErr);
491 : }
492 :
493 431093 : bool bCanUseMultiThreadedRead = false;
494 431092 : if (m_nDisableMultiThreadedRead == 0 && m_poThreadPool &&
495 862330 : eRWFlag == GF_Read && nBufXSize == nXSize && nBufYSize == nYSize &&
496 145 : IsMultiThreadedReadCompatible())
497 : {
498 145 : const int nBlockX1 = nXOff / m_nBlockXSize;
499 145 : const int nBlockY1 = nYOff / m_nBlockYSize;
500 145 : const int nBlockX2 = (nXOff + nXSize - 1) / m_nBlockXSize;
501 145 : const int nBlockY2 = (nYOff + nYSize - 1) / m_nBlockYSize;
502 145 : const int nXBlocks = nBlockX2 - nBlockX1 + 1;
503 145 : const int nYBlocks = nBlockY2 - nBlockY1 + 1;
504 145 : const int nBlocks =
505 145 : nXBlocks * nYBlocks *
506 145 : (m_nPlanarConfig == PLANARCONFIG_CONTIG ? 1 : nBandCount);
507 145 : if (nBlocks > 1)
508 : {
509 142 : bCanUseMultiThreadedRead = true;
510 : }
511 : }
512 :
513 431093 : void *pBufferedData = nullptr;
514 431093 : const auto poFirstBand = cpl::down_cast<GTiffRasterBand *>(papoBands[0]);
515 431091 : const auto eDataType = poFirstBand->GetRasterDataType();
516 :
517 37768 : if (eAccess == GA_ReadOnly && eRWFlag == GF_Read &&
518 37768 : (nBands == 1 || m_nPlanarConfig == PLANARCONFIG_CONTIG) &&
519 468874 : HasOptimizedReadMultiRange() &&
520 14 : !(bCanUseMultiThreadedRead &&
521 3 : VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF))->HasPRead()))
522 : {
523 11 : pBufferedData = poFirstBand->CacheMultiRange(
524 : nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, psExtraArg);
525 : }
526 431082 : else if (bCanUseMultiThreadedRead)
527 : {
528 142 : return MultiThreadedRead(nXOff, nYOff, nXSize, nYSize, pData, eBufType,
529 : nBandCount, panBandMap, nPixelSpace,
530 142 : nLineSpace, nBandSpace);
531 : }
532 :
533 : // Write optimization when writing whole blocks, by-passing the block cache.
534 : // We require the block cache to be non instantiated to simplify things
535 : // (otherwise we might need to evict corresponding existing blocks from the
536 : // block cache).
537 199985 : else if (eRWFlag == GF_Write && nBands > 1 &&
538 27941 : m_nPlanarConfig == PLANARCONFIG_CONTIG &&
539 : // Could be extended to "odd bit" case, but more work
540 27452 : m_nBitsPerSample == GDALGetDataTypeSize(eDataType) &&
541 27441 : nXSize == nBufXSize && nYSize == nBufYSize &&
542 27439 : nBandCount == nBands && !m_bLoadedBlockDirty &&
543 27418 : (nXOff % m_nBlockXSize) == 0 && (nYOff % m_nBlockYSize) == 0 &&
544 1677 : (nXOff + nXSize == nRasterXSize ||
545 630965 : (nXSize % m_nBlockXSize) == 0) &&
546 1674 : (nYOff + nYSize == nRasterYSize || (nYSize % m_nBlockYSize) == 0))
547 : {
548 1552 : bool bOptimOK = true;
549 1552 : bool bOrderedBands = true;
550 6513 : for (int i = 0; i < nBands; ++i)
551 : {
552 4970 : if (panBandMap[i] != i + 1)
553 : {
554 14 : bOrderedBands = false;
555 : }
556 9940 : if (cpl::down_cast<GTiffRasterBand *>(papoBands[panBandMap[i] - 1])
557 4970 : ->HasBlockCache())
558 : {
559 9 : bOptimOK = false;
560 9 : break;
561 : }
562 : }
563 1552 : if (bOptimOK)
564 : {
565 1543 : Crystalize();
566 :
567 1543 : if (m_bDebugDontWriteBlocks)
568 0 : return CE_None;
569 :
570 1543 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
571 1543 : if (bOrderedBands && nXSize == m_nBlockXSize &&
572 1315 : nYSize == m_nBlockYSize && eBufType == eDataType &&
573 1157 : nBandSpace == nDTSize &&
574 23 : nPixelSpace == static_cast<GSpacing>(nDTSize) * nBands &&
575 5 : nLineSpace == nPixelSpace * m_nBlockXSize)
576 : {
577 : // If writing one single block with the right data type and
578 : // layout (interleaved per pixel), we don't need a temporary
579 : // buffer
580 10 : const int nBlockId = poFirstBand->ComputeBlockId(
581 5 : nXOff / m_nBlockXSize, nYOff / m_nBlockYSize);
582 5 : return WriteEncodedTileOrStrip(nBlockId, pData,
583 5 : /* bPreserveDataBuffer= */ true);
584 : }
585 :
586 : // Make sure m_poGDS->m_pabyBlockBuf is allocated.
587 : // We could actually use any temporary buffer
588 1538 : if (LoadBlockBuf(/* nBlockId = */ -1,
589 1538 : /* bReadFromDisk = */ false) != CE_None)
590 : {
591 0 : return CE_Failure;
592 : }
593 :
594 : // Iterate over all blocks defined by
595 : // [nXOff, nXOff+nXSize[ * [nYOff, nYOff+nYSize[
596 : // and write their content as a nBlockXSize x nBlockYSize strile
597 : // in a temporary buffer, before calling WriteEncodedTileOrStrip()
598 : // on it
599 1538 : const int nYBlockStart = nYOff / m_nBlockYSize;
600 1538 : const int nYBlockEnd = 1 + (nYOff + nYSize - 1) / m_nBlockYSize;
601 1538 : const int nXBlockStart = nXOff / m_nBlockXSize;
602 1538 : const int nXBlockEnd = 1 + (nXOff + nXSize - 1) / m_nBlockXSize;
603 17056 : for (int nYBlock = nYBlockStart; nYBlock < nYBlockEnd; ++nYBlock)
604 : {
605 : const int nValidY = std::min(
606 15522 : m_nBlockYSize, nRasterYSize - nYBlock * m_nBlockYSize);
607 33885 : for (int nXBlock = nXBlockStart; nXBlock < nXBlockEnd;
608 : ++nXBlock)
609 : {
610 : const int nValidX = std::min(
611 18367 : m_nBlockXSize, nRasterXSize - nXBlock * m_nBlockXSize);
612 18367 : if (nValidY < m_nBlockYSize || nValidX < m_nBlockXSize)
613 : {
614 : // Make sure padding bytes at the right/bottom of the
615 : // tile are initialized to zero.
616 500 : memset(m_pabyBlockBuf, 0,
617 500 : static_cast<size_t>(m_nBlockXSize) *
618 500 : m_nBlockYSize * nBands * nDTSize);
619 : }
620 18367 : const auto nBufDTSize = GDALGetDataTypeSizeBytes(eBufType);
621 18367 : const GByte *pabySrcData =
622 : static_cast<const GByte *>(pData) +
623 18367 : static_cast<size_t>(nYBlock - nYBlockStart) *
624 18367 : m_nBlockYSize * nLineSpace +
625 18367 : static_cast<size_t>(nXBlock - nXBlockStart) *
626 18367 : m_nBlockXSize * nPixelSpace;
627 18367 : if (bOrderedBands && nBandSpace == nBufDTSize &&
628 20 : nPixelSpace == nBands * nBandSpace)
629 : {
630 : // Input buffer is pixel interleaved
631 17 : for (int iY = 0; iY < nValidY; ++iY)
632 : {
633 16 : GDALCopyWords64(
634 16 : pabySrcData +
635 16 : static_cast<size_t>(iY) * nLineSpace,
636 : eBufType, nBufDTSize,
637 16 : m_pabyBlockBuf + static_cast<size_t>(iY) *
638 16 : m_nBlockXSize * nBands *
639 16 : nDTSize,
640 : eDataType, nDTSize,
641 16 : static_cast<GPtrDiff_t>(nValidX) * nBands);
642 1 : }
643 : }
644 : else
645 : {
646 : // "Random" spacing for input buffer
647 76075 : for (int iBand = 0; iBand < nBands; ++iBand)
648 : {
649 1837780 : for (int iY = 0; iY < nValidY; ++iY)
650 : {
651 1780070 : GDALCopyWords64(
652 1780070 : pabySrcData +
653 1780070 : static_cast<size_t>(iY) * nLineSpace,
654 : eBufType, static_cast<int>(nPixelSpace),
655 1780070 : m_pabyBlockBuf +
656 1780070 : (panBandMap[iBand] - 1 +
657 1780070 : static_cast<size_t>(iY) *
658 1780070 : m_nBlockXSize * nBands) *
659 1780070 : nDTSize,
660 1780070 : eDataType, nDTSize * nBands, nValidX);
661 : }
662 57709 : pabySrcData += nBandSpace;
663 : }
664 : }
665 :
666 : const int nBlockId =
667 18367 : poFirstBand->ComputeBlockId(nXBlock, nYBlock);
668 36734 : if (WriteEncodedTileOrStrip(
669 18367 : nBlockId, m_pabyBlockBuf,
670 18367 : /* bPreserveDataBuffer= */ false) != CE_None)
671 : {
672 4 : return CE_Failure;
673 : }
674 : }
675 : }
676 1534 : return CE_None;
677 : }
678 : }
679 :
680 429408 : if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
681 429232 : ++m_nJPEGOverviewVisibilityCounter;
682 429408 : const CPLErr eErr = GDALPamDataset::IRasterIO(
683 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
684 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace,
685 : psExtraArg);
686 429408 : if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
687 429232 : m_nJPEGOverviewVisibilityCounter--;
688 :
689 429408 : if (pBufferedData)
690 : {
691 5 : VSIFree(pBufferedData);
692 5 : VSI_TIFFSetCachedRanges(TIFFClientdata(m_hTIFF), 0, nullptr, nullptr,
693 : nullptr);
694 : }
695 :
696 429408 : return eErr;
697 : }
698 :
699 : /************************************************************************/
700 : /* GetGTIFFKeysFlavor() */
701 : /************************************************************************/
702 :
703 20530 : GTIFFKeysFlavorEnum GetGTIFFKeysFlavor(CSLConstList papszOptions)
704 : {
705 : const char *pszGeoTIFFKeysFlavor =
706 20530 : CSLFetchNameValueDef(papszOptions, "GEOTIFF_KEYS_FLAVOR", "STANDARD");
707 20520 : if (EQUAL(pszGeoTIFFKeysFlavor, "ESRI_PE"))
708 1 : return GEOTIFF_KEYS_ESRI_PE;
709 20519 : return GEOTIFF_KEYS_STANDARD;
710 : }
711 :
712 : /************************************************************************/
713 : /* GetGeoTIFFVersion() */
714 : /************************************************************************/
715 :
716 20508 : GeoTIFFVersionEnum GetGeoTIFFVersion(CSLConstList papszOptions)
717 : {
718 : const char *pszVersion =
719 20508 : CSLFetchNameValueDef(papszOptions, "GEOTIFF_VERSION", "AUTO");
720 20516 : if (EQUAL(pszVersion, "1.0"))
721 3 : return GEOTIFF_VERSION_1_0;
722 20513 : if (EQUAL(pszVersion, "1.1"))
723 5 : return GEOTIFF_VERSION_1_1;
724 20508 : return GEOTIFF_VERSION_AUTO;
725 : }
726 :
727 : /************************************************************************/
728 : /* InitCreationOrOpenOptions() */
729 : /************************************************************************/
730 :
731 19943 : void GTiffDataset::InitCreationOrOpenOptions(bool bUpdateMode,
732 : CSLConstList papszOptions)
733 : {
734 19943 : InitCompressionThreads(bUpdateMode, papszOptions);
735 :
736 19932 : m_eGeoTIFFKeysFlavor = GetGTIFFKeysFlavor(papszOptions);
737 19907 : m_eGeoTIFFVersion = GetGeoTIFFVersion(papszOptions);
738 19898 : }
739 :
740 : /************************************************************************/
741 : /* IsBlockAvailable() */
742 : /* */
743 : /* Return true if the indicated strip/tile is available. We */
744 : /* establish this by testing if the stripbytecount is zero. If */
745 : /* zero then the block has never been committed to disk. */
746 : /************************************************************************/
747 :
748 2242720 : bool GTiffDataset::IsBlockAvailable(int nBlockId, vsi_l_offset *pnOffset,
749 : vsi_l_offset *pnSize, bool *pbErrOccurred)
750 :
751 : {
752 2242720 : if (pbErrOccurred)
753 2206720 : *pbErrOccurred = false;
754 :
755 2242720 : std::pair<vsi_l_offset, vsi_l_offset> oPair;
756 2242720 : if (m_oCacheStrileToOffsetByteCount.tryGet(nBlockId, oPair))
757 : {
758 428 : if (pnOffset)
759 87 : *pnOffset = oPair.first;
760 428 : if (pnSize)
761 0 : *pnSize = oPair.second;
762 428 : return oPair.first != 0;
763 : }
764 :
765 2242290 : WaitCompletionForBlock(nBlockId);
766 :
767 : // Optimization to avoid fetching the whole Strip/TileCounts and
768 : // Strip/TileOffsets arrays.
769 2242290 : if (eAccess == GA_ReadOnly && !m_bStreamingIn)
770 : {
771 2140920 : int nErrOccurred = 0;
772 : auto bytecount =
773 2140920 : TIFFGetStrileByteCountWithErr(m_hTIFF, nBlockId, &nErrOccurred);
774 2140920 : if (nErrOccurred && pbErrOccurred)
775 0 : *pbErrOccurred = true;
776 2140920 : if (pnOffset)
777 : {
778 2102640 : *pnOffset =
779 2102640 : TIFFGetStrileOffsetWithErr(m_hTIFF, nBlockId, &nErrOccurred);
780 2102640 : if (nErrOccurred && pbErrOccurred)
781 1 : *pbErrOccurred = true;
782 : }
783 2140920 : if (pnSize)
784 4026 : *pnSize = bytecount;
785 2140920 : return bytecount != 0;
786 : }
787 :
788 101372 : toff_t *panByteCounts = nullptr;
789 101372 : toff_t *panOffsets = nullptr;
790 101372 : const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(m_hTIFF));
791 :
792 134449 : if ((bIsTiled &&
793 33078 : TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts) &&
794 25048 : (pnOffset == nullptr ||
795 202742 : TIFFGetField(m_hTIFF, TIFFTAG_TILEOFFSETS, &panOffsets))) ||
796 68293 : (!bIsTiled &&
797 68293 : TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts) &&
798 37277 : (pnOffset == nullptr ||
799 37277 : TIFFGetField(m_hTIFF, TIFFTAG_STRIPOFFSETS, &panOffsets))))
800 : {
801 101371 : if (panByteCounts == nullptr ||
802 62325 : (pnOffset != nullptr && panOffsets == nullptr))
803 : {
804 0 : if (pbErrOccurred)
805 0 : *pbErrOccurred = true;
806 0 : return false;
807 : }
808 : const int nBlockCount =
809 101371 : bIsTiled ? TIFFNumberOfTiles(m_hTIFF) : TIFFNumberOfStrips(m_hTIFF);
810 101371 : if (nBlockId >= nBlockCount)
811 : {
812 0 : if (pbErrOccurred)
813 0 : *pbErrOccurred = true;
814 0 : return false;
815 : }
816 :
817 101371 : if (pnOffset)
818 62325 : *pnOffset = panOffsets[nBlockId];
819 101371 : if (pnSize)
820 5771 : *pnSize = panByteCounts[nBlockId];
821 101371 : return panByteCounts[nBlockId] != 0;
822 : }
823 : else
824 : {
825 0 : if (pbErrOccurred)
826 0 : *pbErrOccurred = true;
827 : }
828 :
829 0 : return false;
830 : }
831 :
832 : /************************************************************************/
833 : /* ReloadDirectory() */
834 : /************************************************************************/
835 :
836 5698 : void GTiffDataset::ReloadDirectory(bool bReopenHandle)
837 : {
838 5698 : bool bNeedSetInvalidDir = true;
839 5698 : if (bReopenHandle)
840 : {
841 : // When issuing a TIFFRewriteDirectory() or when a TIFFFlush() has
842 : // caused a move of the directory, we would need to invalidate the
843 : // tif_lastdiroff member, but it is not possible to do so without
844 : // re-opening the TIFF handle.
845 9 : auto hTIFFNew = VSI_TIFFReOpen(m_hTIFF);
846 9 : if (hTIFFNew != nullptr)
847 : {
848 9 : m_hTIFF = hTIFFNew;
849 9 : bNeedSetInvalidDir = false; // we could do it, but not needed
850 : }
851 : else
852 : {
853 0 : CPLError(CE_Failure, CPLE_AppDefined,
854 : "Cannot re-open TIFF handle for file %s. "
855 : "Directory chaining may be corrupted !",
856 : m_pszFilename);
857 : }
858 : }
859 5698 : if (bNeedSetInvalidDir)
860 : {
861 5689 : TIFFSetSubDirectory(m_hTIFF, 0);
862 : }
863 5698 : CPL_IGNORE_RET_VAL(SetDirectory());
864 5698 : }
865 :
866 : /************************************************************************/
867 : /* SetDirectory() */
868 : /************************************************************************/
869 :
870 38142 : bool GTiffDataset::SetDirectory()
871 :
872 : {
873 38142 : Crystalize();
874 :
875 38135 : if (TIFFCurrentDirOffset(m_hTIFF) == m_nDirOffset)
876 : {
877 30933 : return true;
878 : }
879 :
880 7186 : const int nSetDirResult = TIFFSetSubDirectory(m_hTIFF, m_nDirOffset);
881 7186 : if (!nSetDirResult)
882 0 : return false;
883 :
884 7186 : RestoreVolatileParameters(m_hTIFF);
885 :
886 7186 : return true;
887 : }
888 :
889 : /************************************************************************/
890 : /* GTiffSetDeflateSubCodec() */
891 : /************************************************************************/
892 :
893 6299 : void GTiffSetDeflateSubCodec(TIFF *hTIFF)
894 : {
895 : (void)hTIFF;
896 :
897 : #if defined(TIFFTAG_DEFLATE_SUBCODEC) && defined(LIBDEFLATE_SUPPORT)
898 : // Mostly for strict reproducibility purposes
899 6299 : if (EQUAL(CPLGetConfigOption("GDAL_TIFF_DEFLATE_SUBCODEC", ""), "ZLIB"))
900 : {
901 4097 : TIFFSetField(hTIFF, TIFFTAG_DEFLATE_SUBCODEC, DEFLATE_SUBCODEC_ZLIB);
902 : }
903 : #endif
904 6300 : }
905 :
906 : /************************************************************************/
907 : /* RestoreVolatileParameters() */
908 : /************************************************************************/
909 :
910 39158 : void GTiffDataset::RestoreVolatileParameters(TIFF *hTIFF)
911 : {
912 :
913 : /* -------------------------------------------------------------------- */
914 : /* YCbCr JPEG compressed images should be translated on the fly */
915 : /* to RGB by libtiff/libjpeg unless specifically requested */
916 : /* otherwise. */
917 : /* -------------------------------------------------------------------- */
918 78733 : if (m_nCompression == COMPRESSION_JPEG &&
919 39434 : m_nPhotometric == PHOTOMETRIC_YCBCR &&
920 278 : CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
921 : {
922 280 : int nColorMode = JPEGCOLORMODE_RAW; // Initialize to 0;
923 :
924 280 : TIFFGetField(hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode);
925 280 : if (nColorMode != JPEGCOLORMODE_RGB)
926 : {
927 246 : TIFFSetField(hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
928 : }
929 : }
930 :
931 39156 : if (m_nCompression == COMPRESSION_ADOBE_DEFLATE ||
932 33442 : m_nCompression == COMPRESSION_LERC)
933 : {
934 5950 : GTiffSetDeflateSubCodec(hTIFF);
935 : }
936 :
937 : /* -------------------------------------------------------------------- */
938 : /* Propagate any quality settings. */
939 : /* -------------------------------------------------------------------- */
940 39154 : if (eAccess == GA_Update)
941 : {
942 : // Now, reset zip and jpeg quality.
943 34230 : if (m_nJpegQuality > 0 && m_nCompression == COMPRESSION_JPEG)
944 : {
945 : #ifdef DEBUG_VERBOSE
946 : CPLDebug("GTiff", "Propagate JPEG_QUALITY(%d) in SetDirectory()",
947 : m_nJpegQuality);
948 : #endif
949 182 : TIFFSetField(hTIFF, TIFFTAG_JPEGQUALITY, m_nJpegQuality);
950 : }
951 34230 : if (m_nJpegTablesMode >= 0 && m_nCompression == COMPRESSION_JPEG)
952 318 : TIFFSetField(hTIFF, TIFFTAG_JPEGTABLESMODE, m_nJpegTablesMode);
953 34230 : if (m_nZLevel > 0 && (m_nCompression == COMPRESSION_ADOBE_DEFLATE ||
954 12 : m_nCompression == COMPRESSION_LERC))
955 25 : TIFFSetField(hTIFF, TIFFTAG_ZIPQUALITY, m_nZLevel);
956 34230 : if (m_nLZMAPreset > 0 && m_nCompression == COMPRESSION_LZMA)
957 8 : TIFFSetField(hTIFF, TIFFTAG_LZMAPRESET, m_nLZMAPreset);
958 34230 : if (m_nZSTDLevel > 0 && (m_nCompression == COMPRESSION_ZSTD ||
959 12 : m_nCompression == COMPRESSION_LERC))
960 18 : TIFFSetField(hTIFF, TIFFTAG_ZSTD_LEVEL, m_nZSTDLevel);
961 34230 : if (m_nCompression == COMPRESSION_LERC)
962 : {
963 185 : TIFFSetField(hTIFF, TIFFTAG_LERC_MAXZERROR, m_dfMaxZError);
964 : }
965 34230 : if (m_nWebPLevel > 0 && m_nCompression == COMPRESSION_WEBP)
966 124 : TIFFSetField(hTIFF, TIFFTAG_WEBP_LEVEL, m_nWebPLevel);
967 34229 : if (m_bWebPLossless && m_nCompression == COMPRESSION_WEBP)
968 49 : TIFFSetField(hTIFF, TIFFTAG_WEBP_LOSSLESS, 1);
969 : #ifdef HAVE_JXL
970 34230 : if (m_nCompression == COMPRESSION_JXL)
971 : {
972 76 : TIFFSetField(hTIFF, TIFFTAG_JXL_LOSSYNESS,
973 76 : m_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
974 76 : TIFFSetField(hTIFF, TIFFTAG_JXL_EFFORT, m_nJXLEffort);
975 76 : TIFFSetField(hTIFF, TIFFTAG_JXL_DISTANCE, m_fJXLDistance);
976 76 : TIFFSetField(hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE,
977 76 : m_fJXLAlphaDistance);
978 : }
979 : #endif
980 : }
981 39154 : }
982 :
983 : /************************************************************************/
984 : /* ComputeBlocksPerColRowAndBand() */
985 : /************************************************************************/
986 :
987 21451 : bool GTiffDataset::ComputeBlocksPerColRowAndBand(int l_nBands)
988 : {
989 21451 : m_nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, m_nBlockYSize);
990 21451 : m_nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, m_nBlockXSize);
991 21451 : if (m_nBlocksPerColumn > INT_MAX / m_nBlocksPerRow)
992 : {
993 1 : ReportError(CE_Failure, CPLE_AppDefined, "Too many blocks: %d x %d",
994 : m_nBlocksPerRow, m_nBlocksPerColumn);
995 1 : return false;
996 : }
997 :
998 : // Note: we could potentially go up to UINT_MAX blocks, but currently
999 : // we use a int nBlockId
1000 21450 : m_nBlocksPerBand = m_nBlocksPerColumn * m_nBlocksPerRow;
1001 21450 : if (m_nPlanarConfig == PLANARCONFIG_SEPARATE &&
1002 1505 : m_nBlocksPerBand > INT_MAX / l_nBands)
1003 : {
1004 1 : ReportError(CE_Failure, CPLE_AppDefined,
1005 : "Too many blocks: %d x %d x %d bands", m_nBlocksPerRow,
1006 : m_nBlocksPerColumn, l_nBands);
1007 1 : return false;
1008 : }
1009 21449 : return true;
1010 : }
1011 :
1012 : /************************************************************************/
1013 : /* SetStructuralMDFromParent() */
1014 : /************************************************************************/
1015 :
1016 926 : void GTiffDataset::SetStructuralMDFromParent(GTiffDataset *poParentDS)
1017 : {
1018 926 : m_bBlockOrderRowMajor = poParentDS->m_bBlockOrderRowMajor;
1019 926 : m_bLeaderSizeAsUInt4 = poParentDS->m_bLeaderSizeAsUInt4;
1020 926 : m_bTrailerRepeatedLast4BytesRepeated =
1021 926 : poParentDS->m_bTrailerRepeatedLast4BytesRepeated;
1022 926 : m_bMaskInterleavedWithImagery = poParentDS->m_bMaskInterleavedWithImagery;
1023 926 : m_bWriteEmptyTiles = poParentDS->m_bWriteEmptyTiles;
1024 926 : }
1025 :
1026 : /************************************************************************/
1027 : /* ScanDirectories() */
1028 : /* */
1029 : /* Scan through all the directories finding overviews, masks */
1030 : /* and subdatasets. */
1031 : /************************************************************************/
1032 :
1033 683850 : void GTiffDataset::ScanDirectories()
1034 :
1035 : {
1036 : /* -------------------------------------------------------------------- */
1037 : /* We only scan once. We do not scan for non-base datasets. */
1038 : /* -------------------------------------------------------------------- */
1039 683850 : if (!m_bScanDeferred)
1040 678735 : return;
1041 :
1042 5378 : m_bScanDeferred = false;
1043 :
1044 5378 : if (m_poBaseDS)
1045 263 : return;
1046 :
1047 5115 : Crystalize();
1048 :
1049 5115 : CPLDebug("GTiff", "ScanDirectories()");
1050 :
1051 : /* ==================================================================== */
1052 : /* Scan all directories. */
1053 : /* ==================================================================== */
1054 10230 : CPLStringList aosSubdatasets;
1055 5115 : int iDirIndex = 0;
1056 :
1057 5115 : FlushDirectory();
1058 :
1059 917 : do
1060 : {
1061 6032 : toff_t nTopDir = TIFFCurrentDirOffset(m_hTIFF);
1062 6032 : uint32_t nSubType = 0;
1063 :
1064 6032 : ++iDirIndex;
1065 :
1066 6032 : toff_t *tmpSubIFDOffsets = nullptr;
1067 6032 : toff_t *subIFDOffsets = nullptr;
1068 6032 : uint16_t nSubIFDs = 0;
1069 6032 : if (TIFFGetField(m_hTIFF, TIFFTAG_SUBIFD, &nSubIFDs,
1070 6032 : &tmpSubIFDOffsets) &&
1071 : iDirIndex == 1)
1072 : {
1073 : subIFDOffsets =
1074 10 : static_cast<toff_t *>(CPLMalloc(nSubIFDs * sizeof(toff_t)));
1075 30 : for (uint16_t iSubIFD = 0; iSubIFD < nSubIFDs; iSubIFD++)
1076 : {
1077 20 : subIFDOffsets[iSubIFD] = tmpSubIFDOffsets[iSubIFD];
1078 : }
1079 : }
1080 :
1081 : // early break for backwards compatibility: if the first directory read
1082 : // is also the last, and there are no subIFDs, no use continuing
1083 6032 : if (iDirIndex == 1 && nSubIFDs == 0 && TIFFLastDirectory(m_hTIFF))
1084 : {
1085 4558 : CPLFree(subIFDOffsets);
1086 4558 : break;
1087 : }
1088 :
1089 2968 : for (uint16_t iSubIFD = 0; iSubIFD <= nSubIFDs; iSubIFD++)
1090 : {
1091 1500 : toff_t nThisDir = nTopDir;
1092 1500 : if (iSubIFD > 0 && iDirIndex > 1) // don't read subIFDs if we are
1093 : // not in the original directory
1094 6 : break;
1095 1494 : if (iSubIFD > 0)
1096 : {
1097 : // make static analyzer happy. subIFDOffsets cannot be null if
1098 : // iSubIFD>0
1099 20 : assert(subIFDOffsets != nullptr);
1100 20 : nThisDir = subIFDOffsets[iSubIFD - 1];
1101 : // CPLDebug("GTiff", "Opened subIFD %d/%d at offset %llu.",
1102 : // iSubIFD, nSubIFDs, nThisDir);
1103 20 : if (!TIFFSetSubDirectory(m_hTIFF, nThisDir))
1104 0 : break;
1105 : }
1106 :
1107 1494 : if (!TIFFGetField(m_hTIFF, TIFFTAG_SUBFILETYPE, &nSubType))
1108 326 : nSubType = 0;
1109 :
1110 : /* Embedded overview of the main image */
1111 1494 : if ((nSubType & FILETYPE_REDUCEDIMAGE) != 0 &&
1112 1079 : (nSubType & FILETYPE_MASK) == 0 &&
1113 1001 : ((nSubIFDs == 0 && iDirIndex != 1) || iSubIFD > 0) &&
1114 759 : m_nOverviewCount < 30 /* to avoid DoS */)
1115 : {
1116 759 : GTiffDataset *poODS = new GTiffDataset();
1117 759 : poODS->ShareLockWithParentDataset(this);
1118 759 : poODS->SetStructuralMDFromParent(this);
1119 759 : poODS->m_pszFilename = CPLStrdup(m_pszFilename);
1120 759 : if (poODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nThisDir,
1121 1518 : eAccess) != CE_None ||
1122 759 : poODS->GetRasterCount() != GetRasterCount())
1123 : {
1124 0 : delete poODS;
1125 : }
1126 : else
1127 : {
1128 759 : CPLDebug("GTiff", "Opened %dx%d overview.",
1129 : poODS->GetRasterXSize(), poODS->GetRasterYSize());
1130 759 : ++m_nOverviewCount;
1131 1518 : m_papoOverviewDS = static_cast<GTiffDataset **>(CPLRealloc(
1132 759 : m_papoOverviewDS, m_nOverviewCount * (sizeof(void *))));
1133 759 : m_papoOverviewDS[m_nOverviewCount - 1] = poODS;
1134 759 : poODS->m_poBaseDS = this;
1135 759 : poODS->m_bIsOverview = true;
1136 :
1137 : // Propagate a few compression related settings that are
1138 : // no preserved at the TIFF tag level, but may be set in
1139 : // the GDAL_METADATA tag in the IMAGE_STRUCTURE domain
1140 : // Note: this might not be totally reflecting the reality
1141 : // if users have created overviews with different settings
1142 : // but this is probably better than the default ones
1143 759 : poODS->m_nWebPLevel = m_nWebPLevel;
1144 : // below is not a copy & paste error: we transfer the
1145 : // m_dfMaxZErrorOverview overview of the parent to
1146 : // m_dfMaxZError of the overview
1147 759 : poODS->m_dfMaxZError = m_dfMaxZErrorOverview;
1148 759 : poODS->m_dfMaxZErrorOverview = m_dfMaxZErrorOverview;
1149 : #if HAVE_JXL
1150 759 : poODS->m_bJXLLossless = m_bJXLLossless;
1151 759 : poODS->m_fJXLDistance = m_fJXLDistance;
1152 759 : poODS->m_fJXLAlphaDistance = m_fJXLAlphaDistance;
1153 759 : poODS->m_nJXLEffort = m_nJXLEffort;
1154 : #endif
1155 : // Those ones are not serialized currently..
1156 : // poODS->m_nZLevel = m_nZLevel;
1157 : // poODS->m_nLZMAPreset = m_nLZMAPreset;
1158 : // poODS->m_nZSTDLevel = m_nZSTDLevel;
1159 759 : }
1160 : }
1161 : // Embedded mask of the main image.
1162 735 : else if ((nSubType & FILETYPE_MASK) != 0 &&
1163 167 : (nSubType & FILETYPE_REDUCEDIMAGE) == 0 &&
1164 89 : ((nSubIFDs == 0 && iDirIndex != 1) || iSubIFD > 0) &&
1165 89 : m_poMaskDS == nullptr)
1166 : {
1167 89 : m_poMaskDS = new GTiffDataset();
1168 89 : m_poMaskDS->ShareLockWithParentDataset(this);
1169 89 : m_poMaskDS->SetStructuralMDFromParent(this);
1170 89 : m_poMaskDS->m_pszFilename = CPLStrdup(m_pszFilename);
1171 :
1172 : // The TIFF6 specification - page 37 - only allows 1
1173 : // SamplesPerPixel and 1 BitsPerSample Here we support either 1
1174 : // or 8 bit per sample and we support either 1 sample per pixel
1175 : // or as many samples as in the main image We don't check the
1176 : // value of the PhotometricInterpretation tag, which should be
1177 : // set to "Transparency mask" (4) according to the specification
1178 : // (page 36). However, the TIFF6 specification allows image
1179 : // masks to have a higher resolution than the main image, what
1180 : // we don't support here.
1181 :
1182 89 : if (m_poMaskDS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nThisDir,
1183 89 : eAccess) != CE_None ||
1184 89 : m_poMaskDS->GetRasterCount() == 0 ||
1185 89 : !(m_poMaskDS->GetRasterCount() == 1 ||
1186 3 : m_poMaskDS->GetRasterCount() == GetRasterCount()) ||
1187 89 : m_poMaskDS->GetRasterXSize() != GetRasterXSize() ||
1188 267 : m_poMaskDS->GetRasterYSize() != GetRasterYSize() ||
1189 89 : m_poMaskDS->GetRasterBand(1)->GetRasterDataType() !=
1190 : GDT_Byte)
1191 : {
1192 0 : delete m_poMaskDS;
1193 0 : m_poMaskDS = nullptr;
1194 : }
1195 : else
1196 : {
1197 89 : CPLDebug("GTiff", "Opened band mask.");
1198 89 : m_poMaskDS->m_poBaseDS = this;
1199 89 : m_poMaskDS->m_poImageryDS = this;
1200 :
1201 178 : m_poMaskDS->m_bPromoteTo8Bits =
1202 89 : CPLTestBool(CPLGetConfigOption(
1203 : "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
1204 : }
1205 : }
1206 :
1207 : // Embedded mask of an overview. The TIFF6 specification allows the
1208 : // combination of the FILETYPE_xxxx masks.
1209 646 : else if ((nSubType & FILETYPE_REDUCEDIMAGE) != 0 &&
1210 320 : (nSubType & FILETYPE_MASK) != 0 &&
1211 78 : ((nSubIFDs == 0 && iDirIndex != 1) || iSubIFD > 0))
1212 : {
1213 78 : GTiffDataset *poDS = new GTiffDataset();
1214 78 : poDS->ShareLockWithParentDataset(this);
1215 78 : poDS->SetStructuralMDFromParent(this);
1216 78 : poDS->m_pszFilename = CPLStrdup(m_pszFilename);
1217 78 : if (poDS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nThisDir,
1218 78 : eAccess) != CE_None ||
1219 156 : poDS->GetRasterCount() == 0 ||
1220 78 : poDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte)
1221 : {
1222 0 : delete poDS;
1223 : }
1224 : else
1225 : {
1226 78 : int i = 0; // Used after for.
1227 115 : for (; i < m_nOverviewCount; ++i)
1228 : {
1229 115 : auto poOvrDS = cpl::down_cast<GTiffDataset *>(
1230 115 : GDALDataset::FromHandle(m_papoOverviewDS[i]));
1231 308 : if (poOvrDS->m_poMaskDS == nullptr &&
1232 78 : poDS->GetRasterXSize() ==
1233 78 : m_papoOverviewDS[i]->GetRasterXSize() &&
1234 78 : poDS->GetRasterYSize() ==
1235 271 : m_papoOverviewDS[i]->GetRasterYSize() &&
1236 78 : (poDS->GetRasterCount() == 1 ||
1237 0 : poDS->GetRasterCount() == GetRasterCount()))
1238 : {
1239 78 : CPLDebug(
1240 : "GTiff", "Opened band mask for %dx%d overview.",
1241 : poDS->GetRasterXSize(), poDS->GetRasterYSize());
1242 78 : poDS->m_poImageryDS = poOvrDS;
1243 78 : poOvrDS->m_poMaskDS = poDS;
1244 78 : poDS->m_bPromoteTo8Bits =
1245 78 : CPLTestBool(CPLGetConfigOption(
1246 : "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
1247 78 : poDS->m_poBaseDS = this;
1248 78 : break;
1249 : }
1250 : }
1251 78 : if (i == m_nOverviewCount)
1252 : {
1253 0 : delete poDS;
1254 : }
1255 78 : }
1256 : }
1257 568 : else if (!m_bSingleIFDOpened &&
1258 564 : (nSubType == 0 || nSubType == FILETYPE_PAGE))
1259 : {
1260 322 : uint32_t nXSize = 0;
1261 322 : uint32_t nYSize = 0;
1262 :
1263 322 : TIFFGetField(m_hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize);
1264 322 : TIFFGetField(m_hTIFF, TIFFTAG_IMAGELENGTH, &nYSize);
1265 :
1266 : // For Geodetic TIFF grids (GTG)
1267 : // (https://proj.org/specifications/geodetictiffgrids.html)
1268 : // extract the grid_name to put it in the description
1269 644 : std::string osFriendlyName;
1270 322 : char *pszText = nullptr;
1271 377 : if (TIFFGetField(m_hTIFF, TIFFTAG_GDAL_METADATA, &pszText) &&
1272 55 : strstr(pszText, "grid_name") != nullptr)
1273 : {
1274 4 : CPLXMLNode *psRoot = CPLParseXMLString(pszText);
1275 : const CPLXMLNode *psItem =
1276 4 : psRoot ? CPLGetXMLNode(psRoot, "=GDALMetadata")
1277 4 : : nullptr;
1278 4 : if (psItem)
1279 4 : psItem = psItem->psChild;
1280 4 : for (; psItem != nullptr; psItem = psItem->psNext)
1281 : {
1282 :
1283 4 : if (psItem->eType != CXT_Element ||
1284 4 : !EQUAL(psItem->pszValue, "Item"))
1285 0 : continue;
1286 :
1287 : const char *pszKey =
1288 4 : CPLGetXMLValue(psItem, "name", nullptr);
1289 : const char *pszValue =
1290 4 : CPLGetXMLValue(psItem, nullptr, nullptr);
1291 : int nBand =
1292 4 : atoi(CPLGetXMLValue(psItem, "sample", "-1"));
1293 4 : if (pszKey && pszValue && nBand <= 0 &&
1294 4 : EQUAL(pszKey, "grid_name"))
1295 : {
1296 4 : osFriendlyName = ": ";
1297 4 : osFriendlyName += pszValue;
1298 4 : break;
1299 : }
1300 : }
1301 :
1302 4 : CPLDestroyXMLNode(psRoot);
1303 : }
1304 :
1305 322 : if (nXSize > INT_MAX || nYSize > INT_MAX)
1306 : {
1307 1 : CPLDebug("GTiff",
1308 : "Skipping directory with too large image: %u x %u",
1309 : nXSize, nYSize);
1310 : }
1311 : else
1312 : {
1313 321 : uint16_t nSPP = 0;
1314 321 : if (!TIFFGetField(m_hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSPP))
1315 0 : nSPP = 1;
1316 :
1317 642 : CPLString osName, osDesc;
1318 : osName.Printf("SUBDATASET_%d_NAME=GTIFF_DIR:%d:%s",
1319 321 : iDirIndex, iDirIndex, m_pszFilename);
1320 : osDesc.Printf(
1321 : "SUBDATASET_%d_DESC=Page %d (%dP x %dL x %dB)",
1322 : iDirIndex, iDirIndex, static_cast<int>(nXSize),
1323 321 : static_cast<int>(nYSize), nSPP);
1324 321 : osDesc += osFriendlyName;
1325 :
1326 321 : aosSubdatasets.AddString(osName);
1327 321 : aosSubdatasets.AddString(osDesc);
1328 : }
1329 : }
1330 : }
1331 1474 : CPLFree(subIFDOffsets);
1332 :
1333 : // Make sure we are stepping from the expected directory regardless
1334 : // of churn done processing the above.
1335 1474 : if (TIFFCurrentDirOffset(m_hTIFF) != nTopDir)
1336 10 : TIFFSetSubDirectory(m_hTIFF, nTopDir);
1337 2391 : } while (!m_bSingleIFDOpened && !TIFFLastDirectory(m_hTIFF) &&
1338 917 : TIFFReadDirectory(m_hTIFF) != 0);
1339 :
1340 5115 : ReloadDirectory();
1341 :
1342 : // If we have a mask for the main image, loop over the overviews, and if
1343 : // they have a mask, let's set this mask as an overview of the main mask.
1344 5115 : if (m_poMaskDS != nullptr)
1345 : {
1346 167 : for (int i = 0; i < m_nOverviewCount; ++i)
1347 : {
1348 78 : if (cpl::down_cast<GTiffDataset *>(
1349 78 : GDALDataset::FromHandle(m_papoOverviewDS[i]))
1350 78 : ->m_poMaskDS != nullptr)
1351 : {
1352 78 : ++m_poMaskDS->m_nOverviewCount;
1353 156 : m_poMaskDS->m_papoOverviewDS =
1354 156 : static_cast<GTiffDataset **>(CPLRealloc(
1355 78 : m_poMaskDS->m_papoOverviewDS,
1356 78 : m_poMaskDS->m_nOverviewCount * (sizeof(void *))));
1357 78 : m_poMaskDS->m_papoOverviewDS[m_poMaskDS->m_nOverviewCount - 1] =
1358 78 : cpl::down_cast<GTiffDataset *>(
1359 78 : GDALDataset::FromHandle(m_papoOverviewDS[i]))
1360 78 : ->m_poMaskDS;
1361 : }
1362 : }
1363 : }
1364 :
1365 : // Assign color interpretation from main dataset
1366 5115 : const int l_nBands = GetRasterCount();
1367 5874 : for (int iOvr = 0; iOvr < m_nOverviewCount; ++iOvr)
1368 : {
1369 2134 : for (int i = 1; i <= l_nBands; i++)
1370 : {
1371 0 : auto poBand = dynamic_cast<GTiffRasterBand *>(
1372 1375 : m_papoOverviewDS[iOvr]->GetRasterBand(i));
1373 1375 : if (poBand)
1374 1375 : poBand->m_eBandInterp =
1375 1375 : GetRasterBand(i)->GetColorInterpretation();
1376 : }
1377 : }
1378 :
1379 : /* -------------------------------------------------------------------- */
1380 : /* Only keep track of subdatasets if we have more than one */
1381 : /* subdataset (pair). */
1382 : /* -------------------------------------------------------------------- */
1383 5115 : if (aosSubdatasets.size() > 2)
1384 : {
1385 8 : m_oGTiffMDMD.SetMetadata(aosSubdatasets.List(), "SUBDATASETS");
1386 : }
1387 : }
1388 :
1389 : /************************************************************************/
1390 : /* GetInternalHandle() */
1391 : /************************************************************************/
1392 :
1393 1249 : void *GTiffDataset::GetInternalHandle(const char * /* pszHandleName */)
1394 :
1395 : {
1396 1249 : return m_hTIFF;
1397 : }
1398 :
1399 : /************************************************************************/
1400 : /* GetFileList() */
1401 : /************************************************************************/
1402 :
1403 2372 : char **GTiffDataset::GetFileList()
1404 :
1405 : {
1406 2372 : LoadGeoreferencingAndPamIfNeeded();
1407 :
1408 2372 : char **papszFileList = GDALPamDataset::GetFileList();
1409 :
1410 2372 : LoadMetadata();
1411 2372 : if (nullptr != m_papszMetadataFiles)
1412 : {
1413 77 : for (int i = 0; m_papszMetadataFiles[i] != nullptr; ++i)
1414 : {
1415 44 : if (CSLFindString(papszFileList, m_papszMetadataFiles[i]) < 0)
1416 : {
1417 : papszFileList =
1418 44 : CSLAddString(papszFileList, m_papszMetadataFiles[i]);
1419 : }
1420 : }
1421 : }
1422 :
1423 2378 : if (m_pszGeorefFilename &&
1424 6 : CSLFindString(papszFileList, m_pszGeorefFilename) == -1)
1425 : {
1426 6 : papszFileList = CSLAddString(papszFileList, m_pszGeorefFilename);
1427 : }
1428 :
1429 2372 : if (m_nXMLGeorefSrcIndex >= 0)
1430 2372 : LookForProjection();
1431 :
1432 2373 : if (m_pszXMLFilename &&
1433 1 : CSLFindString(papszFileList, m_pszXMLFilename) == -1)
1434 : {
1435 1 : papszFileList = CSLAddString(papszFileList, m_pszXMLFilename);
1436 : }
1437 :
1438 2372 : return papszFileList;
1439 : }
1440 :
1441 : /************************************************************************/
1442 : /* GetRawBinaryLayout() */
1443 : /************************************************************************/
1444 :
1445 9 : bool GTiffDataset::GetRawBinaryLayout(GDALDataset::RawBinaryLayout &sLayout)
1446 : {
1447 9 : if (eAccess == GA_Update)
1448 : {
1449 3 : FlushCache(false);
1450 3 : Crystalize();
1451 : }
1452 :
1453 9 : if (m_nCompression != COMPRESSION_NONE)
1454 1 : return false;
1455 8 : if (!CPLIsPowerOfTwo(m_nBitsPerSample) || m_nBitsPerSample < 8)
1456 0 : return false;
1457 8 : const auto eDT = GetRasterBand(1)->GetRasterDataType();
1458 8 : if (GDALDataTypeIsComplex(eDT))
1459 0 : return false;
1460 :
1461 8 : toff_t *panByteCounts = nullptr;
1462 8 : toff_t *panOffsets = nullptr;
1463 8 : const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(m_hTIFF));
1464 :
1465 16 : if (!((bIsTiled &&
1466 3 : TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts) &&
1467 3 : TIFFGetField(m_hTIFF, TIFFTAG_TILEOFFSETS, &panOffsets)) ||
1468 5 : (!bIsTiled &&
1469 5 : TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts) &&
1470 5 : TIFFGetField(m_hTIFF, TIFFTAG_STRIPOFFSETS, &panOffsets))))
1471 : {
1472 0 : return false;
1473 : }
1474 :
1475 8 : const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
1476 8 : vsi_l_offset nImgOffset = panOffsets[0];
1477 16 : GIntBig nPixelOffset = (m_nPlanarConfig == PLANARCONFIG_CONTIG)
1478 8 : ? static_cast<GIntBig>(nDTSize) * nBands
1479 : : nDTSize;
1480 8 : GIntBig nLineOffset = nPixelOffset * nRasterXSize;
1481 8 : GIntBig nBandOffset =
1482 8 : (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1) ? nDTSize : 0;
1483 8 : RawBinaryLayout::Interleaving eInterleaving =
1484 12 : (nBands == 1) ? RawBinaryLayout::Interleaving::UNKNOWN
1485 4 : : (m_nPlanarConfig == PLANARCONFIG_CONTIG)
1486 4 : ? RawBinaryLayout::Interleaving::BIP
1487 : : RawBinaryLayout::Interleaving::BSQ;
1488 8 : if (bIsTiled)
1489 : {
1490 : // Only a single block tiled file with same dimension as the raster
1491 : // might be acceptable
1492 3 : if (m_nBlockXSize != nRasterXSize || m_nBlockYSize != nRasterYSize)
1493 1 : return false;
1494 2 : if (nBands > 1 && m_nPlanarConfig != PLANARCONFIG_CONTIG)
1495 : {
1496 1 : nBandOffset = static_cast<GIntBig>(panOffsets[1]) -
1497 1 : static_cast<GIntBig>(panOffsets[0]);
1498 2 : for (int i = 2; i < nBands; i++)
1499 : {
1500 1 : if (static_cast<GIntBig>(panOffsets[i]) -
1501 1 : static_cast<GIntBig>(panOffsets[i - 1]) !=
1502 : nBandOffset)
1503 0 : return false;
1504 : }
1505 : }
1506 : }
1507 : else
1508 : {
1509 5 : const int nStrips = DIV_ROUND_UP(nRasterYSize, m_nRowsPerStrip);
1510 5 : if (nBands == 1 || m_nPlanarConfig == PLANARCONFIG_CONTIG)
1511 : {
1512 4 : vsi_l_offset nLastStripEnd = panOffsets[0] + panByteCounts[0];
1513 16 : for (int iStrip = 1; iStrip < nStrips; iStrip++)
1514 : {
1515 12 : if (nLastStripEnd != panOffsets[iStrip])
1516 0 : return false;
1517 12 : nLastStripEnd = panOffsets[iStrip] + panByteCounts[iStrip];
1518 4 : }
1519 : }
1520 : else
1521 : {
1522 : // Note: we could potentially have BIL order with m_nRowsPerStrip ==
1523 : // 1 and if strips are ordered strip_line_1_band_1, ...,
1524 : // strip_line_1_band_N, strip_line2_band1, ... strip_line2_band_N,
1525 : // etc.... but that'd be faily exotic ! So only detect BSQ layout
1526 : // here
1527 1 : nBandOffset = static_cast<GIntBig>(panOffsets[nStrips]) -
1528 1 : static_cast<GIntBig>(panOffsets[0]);
1529 4 : for (int i = 0; i < nBands; i++)
1530 : {
1531 3 : uint32_t iStripOffset = nStrips * i;
1532 3 : vsi_l_offset nLastStripEnd =
1533 3 : panOffsets[iStripOffset] + panByteCounts[iStripOffset];
1534 3 : for (int iStrip = 1; iStrip < nStrips; iStrip++)
1535 : {
1536 0 : if (nLastStripEnd != panOffsets[iStripOffset + iStrip])
1537 0 : return false;
1538 0 : nLastStripEnd = panOffsets[iStripOffset + iStrip] +
1539 0 : panByteCounts[iStripOffset + iStrip];
1540 : }
1541 3 : if (i >= 2 && static_cast<GIntBig>(panOffsets[iStripOffset]) -
1542 1 : static_cast<GIntBig>(
1543 1 : panOffsets[iStripOffset - nStrips]) !=
1544 : nBandOffset)
1545 : {
1546 0 : return false;
1547 : }
1548 : }
1549 : }
1550 : }
1551 :
1552 7 : sLayout.osRawFilename = m_pszFilename;
1553 7 : sLayout.eInterleaving = eInterleaving;
1554 7 : sLayout.eDataType = eDT;
1555 : #ifdef CPL_LSB
1556 7 : sLayout.bLittleEndianOrder = !TIFFIsByteSwapped(m_hTIFF);
1557 : #else
1558 : sLayout.bLittleEndianOrder = TIFFIsByteSwapped(m_hTIFF);
1559 : #endif
1560 7 : sLayout.nImageOffset = nImgOffset;
1561 7 : sLayout.nPixelOffset = nPixelOffset;
1562 7 : sLayout.nLineOffset = nLineOffset;
1563 7 : sLayout.nBandOffset = nBandOffset;
1564 :
1565 7 : return true;
1566 : }
1567 :
1568 : /************************************************************************/
1569 : /* GTiffDatasetLibGeotiffErrorCallback() */
1570 : /************************************************************************/
1571 :
1572 0 : static void GTiffDatasetLibGeotiffErrorCallback(GTIF *, int level,
1573 : const char *pszMsg, ...)
1574 : {
1575 : va_list ap;
1576 0 : va_start(ap, pszMsg);
1577 0 : CPLErrorV((level == LIBGEOTIFF_WARNING) ? CE_Warning : CE_Failure,
1578 : CPLE_AppDefined, pszMsg, ap);
1579 0 : va_end(ap);
1580 0 : }
1581 :
1582 : /************************************************************************/
1583 : /* GTIFNew() */
1584 : /************************************************************************/
1585 :
1586 20516 : /* static */ GTIF *GTiffDataset::GTIFNew(TIFF *hTIFF)
1587 : {
1588 20516 : GTIF *gtif = GTIFNewEx(hTIFF, GTiffDatasetLibGeotiffErrorCallback, nullptr);
1589 20516 : if (gtif)
1590 : {
1591 20516 : GTIFAttachPROJContext(gtif, OSRGetProjTLSContext());
1592 : }
1593 20516 : return gtif;
1594 : }
|