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