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