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