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