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