/src/gdal/gcore/gdaldefaultoverviews.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GDAL Core |
4 | | * Purpose: Helper code to implement overview and mask support for many |
5 | | * drivers with no inherent format support. |
6 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 2000, 2007, Frank Warmerdam |
10 | | * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com> |
11 | | * |
12 | | * SPDX-License-Identifier: MIT |
13 | | ****************************************************************************/ |
14 | | |
15 | | #include "cpl_port.h" |
16 | | #include "cpl_multiproc.h" |
17 | | #include "gdal_priv.h" |
18 | | |
19 | | #include <cstdlib> |
20 | | #include <cstring> |
21 | | |
22 | | #include <algorithm> |
23 | | #include <set> |
24 | | #include <string> |
25 | | #include <vector> |
26 | | |
27 | | #include "cpl_conv.h" |
28 | | #include "cpl_error.h" |
29 | | #include "cpl_progress.h" |
30 | | #include "cpl_string.h" |
31 | | #include "cpl_vsi.h" |
32 | | #include "gdal.h" |
33 | | |
34 | | //! @cond Doxygen_Suppress |
35 | | /************************************************************************/ |
36 | | /* GDALDefaultOverviews() */ |
37 | | /************************************************************************/ |
38 | | |
39 | | GDALDefaultOverviews::GDALDefaultOverviews() |
40 | 0 | : poDS(nullptr), poODS(nullptr), bOvrIsAux(false), bCheckedForMask(false), |
41 | 0 | bOwnMaskDS(false), poMaskDS(nullptr), poBaseDS(nullptr), |
42 | 0 | bCheckedForOverviews(FALSE), pszInitName(nullptr), bInitNameIsOVR(false), |
43 | 0 | papszInitSiblingFiles(nullptr) |
44 | 0 | { |
45 | 0 | } |
46 | | |
47 | | /************************************************************************/ |
48 | | /* ~GDALDefaultOverviews() */ |
49 | | /************************************************************************/ |
50 | | |
51 | | GDALDefaultOverviews::~GDALDefaultOverviews() |
52 | | |
53 | 0 | { |
54 | 0 | CPLFree(pszInitName); |
55 | 0 | CSLDestroy(papszInitSiblingFiles); |
56 | |
|
57 | 0 | CloseDependentDatasets(); |
58 | 0 | } |
59 | | |
60 | | /************************************************************************/ |
61 | | /* CloseDependentDatasets() */ |
62 | | /************************************************************************/ |
63 | | |
64 | | int GDALDefaultOverviews::CloseDependentDatasets() |
65 | 0 | { |
66 | 0 | bool bHasDroppedRef = false; |
67 | 0 | if (poODS != nullptr) |
68 | 0 | { |
69 | 0 | bHasDroppedRef = true; |
70 | 0 | poODS->FlushCache(true); |
71 | 0 | GDALClose(poODS); |
72 | 0 | poODS = nullptr; |
73 | 0 | } |
74 | |
|
75 | 0 | if (poMaskDS != nullptr) |
76 | 0 | { |
77 | 0 | if (bOwnMaskDS) |
78 | 0 | { |
79 | 0 | bHasDroppedRef = true; |
80 | 0 | poMaskDS->FlushCache(true); |
81 | 0 | GDALClose(poMaskDS); |
82 | 0 | } |
83 | 0 | poMaskDS = nullptr; |
84 | 0 | } |
85 | |
|
86 | 0 | return bHasDroppedRef; |
87 | 0 | } |
88 | | |
89 | | /************************************************************************/ |
90 | | /* IsInitialized() */ |
91 | | /* */ |
92 | | /* Returns TRUE if we are initialized. */ |
93 | | /************************************************************************/ |
94 | | |
95 | | int GDALDefaultOverviews::IsInitialized() |
96 | | |
97 | 0 | { |
98 | 0 | OverviewScan(); |
99 | 0 | return poDS != nullptr; |
100 | 0 | } |
101 | | |
102 | | /************************************************************************/ |
103 | | /* Initialize() */ |
104 | | /************************************************************************/ |
105 | | |
106 | | void GDALDefaultOverviews::Initialize(GDALDataset *poDSIn, |
107 | | const char *pszBasename, |
108 | | CSLConstList papszSiblingFiles, |
109 | | bool bNameIsOVR) |
110 | | |
111 | 0 | { |
112 | 0 | poDS = poDSIn; |
113 | | |
114 | | /* -------------------------------------------------------------------- */ |
115 | | /* If we were already initialized, destroy the old overview */ |
116 | | /* file handle. */ |
117 | | /* -------------------------------------------------------------------- */ |
118 | 0 | if (poODS != nullptr) |
119 | 0 | { |
120 | 0 | GDALClose(poODS); |
121 | 0 | poODS = nullptr; |
122 | |
|
123 | 0 | CPLDebug("GDAL", "GDALDefaultOverviews::Initialize() called twice - " |
124 | 0 | "this is odd and perhaps dangerous!"); |
125 | 0 | } |
126 | | |
127 | | /* -------------------------------------------------------------------- */ |
128 | | /* Store the initialization information for later use in */ |
129 | | /* OverviewScan() */ |
130 | | /* -------------------------------------------------------------------- */ |
131 | 0 | bCheckedForOverviews = FALSE; |
132 | |
|
133 | 0 | CPLFree(pszInitName); |
134 | 0 | pszInitName = nullptr; |
135 | 0 | if (pszBasename != nullptr) |
136 | 0 | pszInitName = CPLStrdup(pszBasename); |
137 | 0 | bInitNameIsOVR = bNameIsOVR; |
138 | |
|
139 | 0 | CSLDestroy(papszInitSiblingFiles); |
140 | 0 | papszInitSiblingFiles = nullptr; |
141 | 0 | if (papszSiblingFiles != nullptr) |
142 | 0 | papszInitSiblingFiles = CSLDuplicate(papszSiblingFiles); |
143 | 0 | } |
144 | | |
145 | | /************************************************************************/ |
146 | | /* Initialize() */ |
147 | | /************************************************************************/ |
148 | | |
149 | | /** Initialize the GDALDefaultOverviews instance. |
150 | | * |
151 | | * @param poDSIn Base dataset. |
152 | | * @param poOpenInfo Open info instance. Must not be NULL. |
153 | | * @param pszName Base dataset name. If set to NULL, poOpenInfo->pszFilename is |
154 | | * used. |
155 | | * @param bTransferSiblingFilesIfLoaded Whether sibling files of poOpenInfo |
156 | | * should be transferred to this |
157 | | * GDALDefaultOverviews instance, if they |
158 | | * have bean already loaded. |
159 | | * @since 3.10 |
160 | | */ |
161 | | void GDALDefaultOverviews::Initialize(GDALDataset *poDSIn, |
162 | | GDALOpenInfo *poOpenInfo, |
163 | | const char *pszName, |
164 | | bool bTransferSiblingFilesIfLoaded) |
165 | 0 | { |
166 | 0 | Initialize(poDSIn, pszName ? pszName : poOpenInfo->pszFilename); |
167 | |
|
168 | 0 | if (bTransferSiblingFilesIfLoaded && poOpenInfo->AreSiblingFilesLoaded()) |
169 | 0 | TransferSiblingFiles(poOpenInfo->StealSiblingFiles()); |
170 | 0 | } |
171 | | |
172 | | /************************************************************************/ |
173 | | /* TransferSiblingFiles() */ |
174 | | /* */ |
175 | | /* Contrary to Initialize(), this sets papszInitSiblingFiles but */ |
176 | | /* without duplicating the passed list. Which must be */ |
177 | | /* "de-allocatable" with CSLDestroy() */ |
178 | | /************************************************************************/ |
179 | | |
180 | | void GDALDefaultOverviews::TransferSiblingFiles(char **papszSiblingFiles) |
181 | 0 | { |
182 | 0 | CSLDestroy(papszInitSiblingFiles); |
183 | 0 | papszInitSiblingFiles = papszSiblingFiles; |
184 | 0 | } |
185 | | |
186 | | namespace |
187 | | { |
188 | | // Prevent infinite recursion. |
189 | | struct AntiRecursionStructDefaultOvr |
190 | | { |
191 | | int nRecLevel = 0; |
192 | | std::set<CPLString> oSetFiles{}; |
193 | | }; |
194 | | } // namespace |
195 | | |
196 | | static void FreeAntiRecursionDefaultOvr(void *pData) |
197 | 0 | { |
198 | 0 | delete static_cast<AntiRecursionStructDefaultOvr *>(pData); |
199 | 0 | } |
200 | | |
201 | | static AntiRecursionStructDefaultOvr &GetAntiRecursionDefaultOvr() |
202 | 0 | { |
203 | 0 | static AntiRecursionStructDefaultOvr dummy; |
204 | 0 | int bMemoryErrorOccurred = false; |
205 | 0 | void *pData = |
206 | 0 | CPLGetTLSEx(CTLS_GDALDEFAULTOVR_ANTIREC, &bMemoryErrorOccurred); |
207 | 0 | if (bMemoryErrorOccurred) |
208 | 0 | { |
209 | 0 | return dummy; |
210 | 0 | } |
211 | 0 | if (pData == nullptr) |
212 | 0 | { |
213 | 0 | auto pAntiRecursion = new AntiRecursionStructDefaultOvr(); |
214 | 0 | CPLSetTLSWithFreeFuncEx(CTLS_GDALDEFAULTOVR_ANTIREC, pAntiRecursion, |
215 | 0 | FreeAntiRecursionDefaultOvr, |
216 | 0 | &bMemoryErrorOccurred); |
217 | 0 | if (bMemoryErrorOccurred) |
218 | 0 | { |
219 | 0 | delete pAntiRecursion; |
220 | 0 | return dummy; |
221 | 0 | } |
222 | 0 | return *pAntiRecursion; |
223 | 0 | } |
224 | 0 | return *static_cast<AntiRecursionStructDefaultOvr *>(pData); |
225 | 0 | } |
226 | | |
227 | | /************************************************************************/ |
228 | | /* OverviewScan() */ |
229 | | /* */ |
230 | | /* This is called to scan for overview files when a first */ |
231 | | /* request is made with regard to overviews. It uses the */ |
232 | | /* pszInitName, bInitNameIsOVR and papszInitSiblingFiles */ |
233 | | /* information that was stored at Initialization() time. */ |
234 | | /************************************************************************/ |
235 | | |
236 | | void GDALDefaultOverviews::OverviewScan() |
237 | | |
238 | 0 | { |
239 | 0 | if (bCheckedForOverviews || poDS == nullptr) |
240 | 0 | return; |
241 | | |
242 | 0 | bCheckedForOverviews = true; |
243 | 0 | if (pszInitName == nullptr) |
244 | 0 | pszInitName = CPLStrdup(poDS->GetDescription()); |
245 | |
|
246 | 0 | AntiRecursionStructDefaultOvr &antiRec = GetAntiRecursionDefaultOvr(); |
247 | | // 32 should be enough to handle a .ovr.ovr.ovr... |
248 | 0 | if (antiRec.nRecLevel == 32) |
249 | 0 | return; |
250 | 0 | if (antiRec.oSetFiles.find(pszInitName) != antiRec.oSetFiles.end()) |
251 | 0 | return; |
252 | 0 | antiRec.oSetFiles.insert(pszInitName); |
253 | 0 | ++antiRec.nRecLevel; |
254 | |
|
255 | 0 | CPLDebug("GDAL", "GDALDefaultOverviews::OverviewScan()"); |
256 | | |
257 | | /* -------------------------------------------------------------------- */ |
258 | | /* Open overview dataset if it exists. */ |
259 | | /* -------------------------------------------------------------------- */ |
260 | 0 | if (!EQUAL(pszInitName, ":::VIRTUAL:::") && |
261 | 0 | GDALCanFileAcceptSidecarFile(pszInitName)) |
262 | 0 | { |
263 | 0 | if (bInitNameIsOVR) |
264 | 0 | osOvrFilename = pszInitName; |
265 | 0 | else |
266 | 0 | osOvrFilename.Printf("%s.ovr", pszInitName); |
267 | |
|
268 | 0 | std::vector<char> achOvrFilename; |
269 | 0 | achOvrFilename.resize(osOvrFilename.size() + 1); |
270 | 0 | memcpy(&(achOvrFilename[0]), osOvrFilename.c_str(), |
271 | 0 | osOvrFilename.size() + 1); |
272 | 0 | bool bExists = CPL_TO_BOOL( |
273 | 0 | CPLCheckForFile(&achOvrFilename[0], papszInitSiblingFiles)); |
274 | 0 | osOvrFilename = &achOvrFilename[0]; |
275 | |
|
276 | 0 | #if !defined(_WIN32) |
277 | 0 | if (!bInitNameIsOVR && !bExists && !papszInitSiblingFiles) |
278 | 0 | { |
279 | 0 | osOvrFilename.Printf("%s.OVR", pszInitName); |
280 | 0 | memcpy(&(achOvrFilename[0]), osOvrFilename.c_str(), |
281 | 0 | osOvrFilename.size() + 1); |
282 | 0 | bExists = CPL_TO_BOOL( |
283 | 0 | CPLCheckForFile(&achOvrFilename[0], papszInitSiblingFiles)); |
284 | 0 | osOvrFilename = &achOvrFilename[0]; |
285 | 0 | if (!bExists) |
286 | 0 | osOvrFilename.Printf("%s.ovr", pszInitName); |
287 | 0 | } |
288 | 0 | #endif |
289 | |
|
290 | 0 | if (bExists) |
291 | 0 | { |
292 | 0 | poODS = GDALDataset::Open( |
293 | 0 | osOvrFilename, |
294 | 0 | GDAL_OF_RASTER | |
295 | 0 | (poDS->GetAccess() == GA_Update ? GDAL_OF_UPDATE : 0), |
296 | 0 | nullptr, nullptr, papszInitSiblingFiles); |
297 | 0 | } |
298 | 0 | } |
299 | | |
300 | | /* -------------------------------------------------------------------- */ |
301 | | /* We didn't find that, so try and find a corresponding aux */ |
302 | | /* file. Check that we are the dependent file of the aux */ |
303 | | /* file. */ |
304 | | /* */ |
305 | | /* We only use the .aux file for overviews if they already have */ |
306 | | /* overviews existing, or if USE_RRD is set true. */ |
307 | | /* -------------------------------------------------------------------- */ |
308 | 0 | if (!poODS && !EQUAL(pszInitName, ":::VIRTUAL:::") && |
309 | 0 | GDALCanFileAcceptSidecarFile(pszInitName)) |
310 | 0 | { |
311 | 0 | bool bTryFindAssociatedAuxFile = true; |
312 | 0 | if (papszInitSiblingFiles) |
313 | 0 | { |
314 | 0 | CPLString osAuxFilename = CPLResetExtensionSafe(pszInitName, "aux"); |
315 | 0 | int iSibling = CSLFindString(papszInitSiblingFiles, |
316 | 0 | CPLGetFilename(osAuxFilename)); |
317 | 0 | if (iSibling < 0) |
318 | 0 | { |
319 | 0 | osAuxFilename = pszInitName; |
320 | 0 | osAuxFilename += ".aux"; |
321 | 0 | iSibling = CSLFindString(papszInitSiblingFiles, |
322 | 0 | CPLGetFilename(osAuxFilename)); |
323 | 0 | if (iSibling < 0) |
324 | 0 | bTryFindAssociatedAuxFile = false; |
325 | 0 | } |
326 | 0 | } |
327 | |
|
328 | 0 | if (bTryFindAssociatedAuxFile) |
329 | 0 | { |
330 | 0 | poODS = |
331 | 0 | GDALFindAssociatedAuxFile(pszInitName, poDS->GetAccess(), poDS); |
332 | 0 | } |
333 | |
|
334 | 0 | if (poODS) |
335 | 0 | { |
336 | 0 | const bool bUseRRD = |
337 | 0 | CPLTestBool(CPLGetConfigOption("USE_RRD", "NO")); |
338 | |
|
339 | 0 | bOvrIsAux = true; |
340 | 0 | if (GetOverviewCount(1) == 0 && !bUseRRD) |
341 | 0 | { |
342 | 0 | bOvrIsAux = false; |
343 | 0 | GDALClose(poODS); |
344 | 0 | poODS = nullptr; |
345 | 0 | } |
346 | 0 | else |
347 | 0 | { |
348 | 0 | osOvrFilename = poODS->GetDescription(); |
349 | 0 | } |
350 | 0 | } |
351 | 0 | } |
352 | | |
353 | | /* -------------------------------------------------------------------- */ |
354 | | /* If we still don't have an overview, check to see if we have */ |
355 | | /* overview metadata referencing a remote (i.e. proxy) or local */ |
356 | | /* subdataset overview dataset. */ |
357 | | /* -------------------------------------------------------------------- */ |
358 | 0 | if (poODS == nullptr) |
359 | 0 | { |
360 | 0 | const char *pszProxyOvrFilename = |
361 | 0 | poDS->GetMetadataItem("OVERVIEW_FILE", "OVERVIEWS"); |
362 | |
|
363 | 0 | if (pszProxyOvrFilename != nullptr) |
364 | 0 | { |
365 | 0 | if (STARTS_WITH_CI(pszProxyOvrFilename, ":::BASE:::")) |
366 | 0 | { |
367 | 0 | const CPLString osPath = CPLGetPathSafe(poDS->GetDescription()); |
368 | |
|
369 | 0 | osOvrFilename = CPLFormFilenameSafe( |
370 | 0 | osPath, pszProxyOvrFilename + 10, nullptr); |
371 | 0 | } |
372 | 0 | else |
373 | 0 | { |
374 | 0 | osOvrFilename = pszProxyOvrFilename; |
375 | 0 | } |
376 | |
|
377 | 0 | CPLPushErrorHandler(CPLQuietErrorHandler); |
378 | 0 | poODS = GDALDataset::Open( |
379 | 0 | osOvrFilename, |
380 | 0 | GDAL_OF_RASTER | |
381 | 0 | (poDS->GetAccess() == GA_Update ? GDAL_OF_UPDATE : 0)); |
382 | 0 | CPLPopErrorHandler(); |
383 | 0 | } |
384 | 0 | } |
385 | | |
386 | | /* -------------------------------------------------------------------- */ |
387 | | /* If we have an overview dataset, then mark all the overviews */ |
388 | | /* with the base dataset Used later for finding overviews */ |
389 | | /* masks. Uggg. */ |
390 | | /* -------------------------------------------------------------------- */ |
391 | 0 | if (poODS) |
392 | 0 | { |
393 | 0 | const int nOverviewCount = GetOverviewCount(1); |
394 | |
|
395 | 0 | for (int iOver = 0; iOver < nOverviewCount; iOver++) |
396 | 0 | { |
397 | 0 | GDALRasterBand *const poBand = GetOverview(1, iOver); |
398 | 0 | GDALDataset *const poOverDS = |
399 | 0 | poBand != nullptr ? poBand->GetDataset() : nullptr; |
400 | |
|
401 | 0 | if (poOverDS != nullptr) |
402 | 0 | { |
403 | 0 | poOverDS->oOvManager.poBaseDS = poDS; |
404 | 0 | poOverDS->oOvManager.poDS = poOverDS; |
405 | 0 | } |
406 | 0 | } |
407 | 0 | } |
408 | | |
409 | | // Undo anti recursion protection |
410 | 0 | antiRec.oSetFiles.erase(pszInitName); |
411 | 0 | --antiRec.nRecLevel; |
412 | 0 | } |
413 | | |
414 | | /************************************************************************/ |
415 | | /* GetOverviewCount() */ |
416 | | /************************************************************************/ |
417 | | |
418 | | int GDALDefaultOverviews::GetOverviewCount(int nBand) |
419 | | |
420 | 0 | { |
421 | 0 | if (poODS == nullptr || nBand < 1 || nBand > poODS->GetRasterCount()) |
422 | 0 | return 0; |
423 | | |
424 | 0 | GDALRasterBand *poBand = poODS->GetRasterBand(nBand); |
425 | 0 | if (poBand == nullptr) |
426 | 0 | return 0; |
427 | | |
428 | 0 | if (bOvrIsAux) |
429 | 0 | return poBand->GetOverviewCount(); |
430 | | |
431 | 0 | return poBand->GetOverviewCount() + 1; |
432 | 0 | } |
433 | | |
434 | | /************************************************************************/ |
435 | | /* GetOverview() */ |
436 | | /************************************************************************/ |
437 | | |
438 | | GDALRasterBand *GDALDefaultOverviews::GetOverview(int nBand, int iOverview) |
439 | | |
440 | 0 | { |
441 | 0 | if (poODS == nullptr || nBand < 1 || nBand > poODS->GetRasterCount()) |
442 | 0 | return nullptr; |
443 | | |
444 | 0 | GDALRasterBand *const poBand = poODS->GetRasterBand(nBand); |
445 | 0 | if (poBand == nullptr) |
446 | 0 | return nullptr; |
447 | | |
448 | 0 | if (bOvrIsAux) |
449 | 0 | return poBand->GetOverview(iOverview); |
450 | | |
451 | | // TIFF case, base is overview 0. |
452 | 0 | if (iOverview == 0) |
453 | 0 | return poBand; |
454 | | |
455 | 0 | if (iOverview - 1 >= poBand->GetOverviewCount()) |
456 | 0 | return nullptr; |
457 | | |
458 | 0 | return poBand->GetOverview(iOverview - 1); |
459 | 0 | } |
460 | | |
461 | | /************************************************************************/ |
462 | | /* GDALOvLevelAdjust() */ |
463 | | /* */ |
464 | | /* Some overview levels cannot be achieved closely enough to be */ |
465 | | /* recognised as the desired overview level. This function */ |
466 | | /* will adjust an overview level to one that is achievable on */ |
467 | | /* the given raster size. */ |
468 | | /* */ |
469 | | /* For instance a 1200x1200 image on which a 256 level overview */ |
470 | | /* is request will end up generating a 5x5 overview. However, */ |
471 | | /* this will appear to the system be a level 240 overview. */ |
472 | | /* This function will adjust 256 to 240 based on knowledge of */ |
473 | | /* the image size. */ |
474 | | /************************************************************************/ |
475 | | |
476 | | int GDALOvLevelAdjust(int nOvLevel, int nXSize) |
477 | | |
478 | 0 | { |
479 | 0 | int nOXSize = DIV_ROUND_UP(nXSize, nOvLevel); |
480 | |
|
481 | 0 | return static_cast<int>(0.5 + nXSize / static_cast<double>(nOXSize)); |
482 | 0 | } |
483 | | |
484 | | int GDALOvLevelAdjust2(int nOvLevel, int nXSize, int nYSize) |
485 | | |
486 | 0 | { |
487 | | // Select the larger dimension to have increased accuracy, but |
488 | | // with a slight preference to x even if (a bit) smaller than y |
489 | | // in an attempt to behave closer as previous behavior. |
490 | 0 | if (nXSize >= nYSize / 2 && !(nXSize < nYSize && nXSize < nOvLevel)) |
491 | 0 | { |
492 | 0 | const int nOXSize = DIV_ROUND_UP(nXSize, nOvLevel); |
493 | |
|
494 | 0 | return static_cast<int>(0.5 + nXSize / static_cast<double>(nOXSize)); |
495 | 0 | } |
496 | | |
497 | 0 | const int nOYSize = DIV_ROUND_UP(nYSize, nOvLevel); |
498 | |
|
499 | 0 | return static_cast<int>(0.5 + nYSize / static_cast<double>(nOYSize)); |
500 | 0 | } |
501 | | |
502 | | /************************************************************************/ |
503 | | /* GetFloorPowerOfTwo() */ |
504 | | /************************************************************************/ |
505 | | |
506 | | static int GetFloorPowerOfTwo(int n) |
507 | 0 | { |
508 | 0 | int p2 = 1; |
509 | 0 | while ((n = n >> 1) > 0) |
510 | 0 | { |
511 | 0 | p2 <<= 1; |
512 | 0 | } |
513 | 0 | return p2; |
514 | 0 | } |
515 | | |
516 | | /************************************************************************/ |
517 | | /* GDALComputeOvFactor() */ |
518 | | /************************************************************************/ |
519 | | |
520 | | int GDALComputeOvFactor(int nOvrXSize, int nRasterXSize, int nOvrYSize, |
521 | | int nRasterYSize) |
522 | 0 | { |
523 | | // Select the larger dimension to have increased accuracy, but |
524 | | // with a slight preference to x even if (a bit) smaller than y |
525 | | // in an attempt to behave closer as previous behavior. |
526 | 0 | if (nRasterXSize != 1 && nRasterXSize >= nRasterYSize / 2) |
527 | 0 | { |
528 | 0 | const int nVal = static_cast<int>( |
529 | 0 | 0.5 + nRasterXSize / static_cast<double>(nOvrXSize)); |
530 | | // Try to return a power-of-two value |
531 | 0 | const int nValPowerOfTwo = GetFloorPowerOfTwo(nVal); |
532 | 0 | for (int fact = 1; fact <= 2 && nValPowerOfTwo <= INT_MAX / fact; |
533 | 0 | ++fact) |
534 | 0 | { |
535 | 0 | if (DIV_ROUND_UP(nRasterXSize, fact * nValPowerOfTwo) == nOvrXSize) |
536 | 0 | return fact * nValPowerOfTwo; |
537 | 0 | } |
538 | 0 | return nVal; |
539 | 0 | } |
540 | | |
541 | 0 | const int nVal = |
542 | 0 | static_cast<int>(0.5 + nRasterYSize / static_cast<double>(nOvrYSize)); |
543 | | // Try to return a power-of-two value |
544 | 0 | const int nValPowerOfTwo = GetFloorPowerOfTwo(nVal); |
545 | 0 | for (int fact = 1; fact <= 2 && nValPowerOfTwo <= INT_MAX / fact; ++fact) |
546 | 0 | { |
547 | 0 | if (DIV_ROUND_UP(nRasterYSize, fact * nValPowerOfTwo) == nOvrYSize) |
548 | 0 | return fact * nValPowerOfTwo; |
549 | 0 | } |
550 | 0 | return nVal; |
551 | 0 | } |
552 | | |
553 | | /************************************************************************/ |
554 | | /* CleanOverviews() */ |
555 | | /* */ |
556 | | /* Remove all existing overviews. */ |
557 | | /************************************************************************/ |
558 | | |
559 | | CPLErr GDALDefaultOverviews::CleanOverviews() |
560 | | |
561 | 0 | { |
562 | | // Anything to do? |
563 | 0 | if (poODS == nullptr) |
564 | 0 | return CE_None; |
565 | | |
566 | | // Delete the overview file(s). |
567 | 0 | GDALDriver *poOvrDriver = poODS->GetDriver(); |
568 | 0 | GDALClose(poODS); |
569 | 0 | poODS = nullptr; |
570 | |
|
571 | 0 | CPLErr eErr = |
572 | 0 | poOvrDriver != nullptr ? poOvrDriver->Delete(osOvrFilename) : CE_None; |
573 | | |
574 | | // Reset the saved overview filename. |
575 | 0 | if (!EQUAL(poDS->GetDescription(), ":::VIRTUAL:::")) |
576 | 0 | { |
577 | 0 | const bool bUseRRD = CPLTestBool(CPLGetConfigOption("USE_RRD", "NO")); |
578 | |
|
579 | 0 | if (bUseRRD) |
580 | 0 | osOvrFilename = |
581 | 0 | CPLResetExtensionSafe(poDS->GetDescription(), "aux"); |
582 | 0 | else |
583 | 0 | osOvrFilename = std::string(poDS->GetDescription()).append(".ovr"); |
584 | 0 | } |
585 | 0 | else |
586 | 0 | { |
587 | 0 | osOvrFilename = ""; |
588 | 0 | } |
589 | |
|
590 | 0 | if (HaveMaskFile() && poMaskDS) |
591 | 0 | { |
592 | 0 | const CPLErr eErr2 = poMaskDS->BuildOverviews( |
593 | 0 | nullptr, 0, nullptr, 0, nullptr, nullptr, nullptr, nullptr); |
594 | 0 | if (eErr2 != CE_None) |
595 | 0 | return eErr2; |
596 | 0 | } |
597 | | |
598 | 0 | return eErr; |
599 | 0 | } |
600 | | |
601 | | /************************************************************************/ |
602 | | /* BuildOverviewsSubDataset() */ |
603 | | /************************************************************************/ |
604 | | |
605 | | CPLErr GDALDefaultOverviews::BuildOverviewsSubDataset( |
606 | | const char *pszPhysicalFile, const char *pszResampling, int nOverviews, |
607 | | const int *panOverviewList, int nBands, const int *panBandList, |
608 | | GDALProgressFunc pfnProgress, void *pProgressData, |
609 | | CSLConstList papszOptions) |
610 | | |
611 | 0 | { |
612 | 0 | if (osOvrFilename.length() == 0 && nOverviews > 0) |
613 | 0 | { |
614 | 0 | VSIStatBufL sStatBuf; |
615 | |
|
616 | 0 | int iSequence = 0; // Used after for. |
617 | 0 | for (iSequence = 0; iSequence < 100; iSequence++) |
618 | 0 | { |
619 | 0 | osOvrFilename.Printf("%s_%d.ovr", pszPhysicalFile, iSequence); |
620 | 0 | if (VSIStatExL(osOvrFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0) |
621 | 0 | { |
622 | 0 | CPLString osAdjustedOvrFilename; |
623 | |
|
624 | 0 | if (poDS->GetMOFlags() & GMO_PAM_CLASS) |
625 | 0 | { |
626 | 0 | osAdjustedOvrFilename.Printf( |
627 | 0 | ":::BASE:::%s_%d.ovr", CPLGetFilename(pszPhysicalFile), |
628 | 0 | iSequence); |
629 | 0 | } |
630 | 0 | else |
631 | 0 | { |
632 | 0 | osAdjustedOvrFilename = osOvrFilename; |
633 | 0 | } |
634 | |
|
635 | 0 | poDS->SetMetadataItem("OVERVIEW_FILE", osAdjustedOvrFilename, |
636 | 0 | "OVERVIEWS"); |
637 | 0 | break; |
638 | 0 | } |
639 | 0 | } |
640 | |
|
641 | 0 | if (iSequence == 100) |
642 | 0 | osOvrFilename = ""; |
643 | 0 | } |
644 | |
|
645 | 0 | return BuildOverviews(nullptr, pszResampling, nOverviews, panOverviewList, |
646 | 0 | nBands, panBandList, pfnProgress, pProgressData, |
647 | 0 | papszOptions); |
648 | 0 | } |
649 | | |
650 | | /************************************************************************/ |
651 | | /* GetOptionValue() */ |
652 | | /************************************************************************/ |
653 | | |
654 | | static const char *GetOptionValue(CSLConstList papszOptions, |
655 | | const char *pszOptionKey, |
656 | | const char *pszConfigOptionKey) |
657 | 0 | { |
658 | 0 | const char *pszVal = |
659 | 0 | pszOptionKey ? CSLFetchNameValue(papszOptions, pszOptionKey) : nullptr; |
660 | 0 | if (pszVal) |
661 | 0 | { |
662 | 0 | return pszVal; |
663 | 0 | } |
664 | 0 | pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey); |
665 | 0 | if (pszVal) |
666 | 0 | { |
667 | 0 | return pszVal; |
668 | 0 | } |
669 | 0 | pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr); |
670 | 0 | return pszVal; |
671 | 0 | } |
672 | | |
673 | | /************************************************************************/ |
674 | | /* BuildOverviews() */ |
675 | | /************************************************************************/ |
676 | | |
677 | | CPLErr GDALDefaultOverviews::BuildOverviews( |
678 | | const char *pszBasename, const char *pszResampling, int nOverviews, |
679 | | const int *panOverviewList, int nBands, const int *panBandList, |
680 | | GDALProgressFunc pfnProgress, void *pProgressData, |
681 | | CSLConstList papszOptions) |
682 | | |
683 | 0 | { |
684 | 0 | if (pfnProgress == nullptr) |
685 | 0 | pfnProgress = GDALDummyProgress; |
686 | |
|
687 | 0 | if (nOverviews == 0) |
688 | 0 | return CleanOverviews(); |
689 | | |
690 | | /* -------------------------------------------------------------------- */ |
691 | | /* If we don't already have an overview file, we need to decide */ |
692 | | /* what format to use. */ |
693 | | /* -------------------------------------------------------------------- */ |
694 | 0 | if (poODS == nullptr) |
695 | 0 | { |
696 | 0 | const char *pszUseRRD = |
697 | 0 | GetOptionValue(papszOptions, nullptr, "USE_RRD"); |
698 | 0 | bOvrIsAux = pszUseRRD && CPLTestBool(pszUseRRD); |
699 | 0 | if (bOvrIsAux) |
700 | 0 | { |
701 | 0 | osOvrFilename = |
702 | 0 | CPLResetExtensionSafe(poDS->GetDescription(), "aux"); |
703 | |
|
704 | 0 | VSIStatBufL sStatBuf; |
705 | 0 | if (VSIStatExL(osOvrFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0) |
706 | 0 | osOvrFilename.Printf("%s.aux", poDS->GetDescription()); |
707 | 0 | } |
708 | 0 | } |
709 | | /* -------------------------------------------------------------------- */ |
710 | | /* If we already have the overviews open, but they are */ |
711 | | /* read-only, then try and reopen them read-write. */ |
712 | | /* -------------------------------------------------------------------- */ |
713 | 0 | else if (poODS->GetAccess() == GA_ReadOnly) |
714 | 0 | { |
715 | 0 | GDALClose(poODS); |
716 | 0 | poODS = |
717 | 0 | GDALDataset::Open(osOvrFilename, GDAL_OF_RASTER | GDAL_OF_UPDATE); |
718 | 0 | if (poODS == nullptr) |
719 | 0 | return CE_Failure; |
720 | 0 | } |
721 | | |
722 | | /* -------------------------------------------------------------------- */ |
723 | | /* Our TIFF overview support currently only works safely if all */ |
724 | | /* bands are handled at the same time. */ |
725 | | /* -------------------------------------------------------------------- */ |
726 | 0 | if (!bOvrIsAux && nBands != poDS->GetRasterCount()) |
727 | 0 | { |
728 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
729 | 0 | "Generation of overviews in external TIFF currently only " |
730 | 0 | "supported when operating on all bands. " |
731 | 0 | "Operation failed."); |
732 | 0 | return CE_Failure; |
733 | 0 | } |
734 | | |
735 | | /* -------------------------------------------------------------------- */ |
736 | | /* If a basename is provided, use it to override the internal */ |
737 | | /* overview filename. */ |
738 | | /* -------------------------------------------------------------------- */ |
739 | 0 | if (pszBasename == nullptr && osOvrFilename.length() == 0) |
740 | 0 | pszBasename = poDS->GetDescription(); |
741 | |
|
742 | 0 | if (pszBasename != nullptr) |
743 | 0 | { |
744 | 0 | if (bOvrIsAux) |
745 | 0 | osOvrFilename.Printf("%s.aux", pszBasename); |
746 | 0 | else |
747 | 0 | osOvrFilename.Printf("%s.ovr", pszBasename); |
748 | 0 | } |
749 | | |
750 | | /* -------------------------------------------------------------------- */ |
751 | | /* Establish which of the overview levels we already have, and */ |
752 | | /* which are new. We assume that band 1 of the file is */ |
753 | | /* representative. */ |
754 | | /* -------------------------------------------------------------------- */ |
755 | 0 | GDALRasterBand *poBand = poDS->GetRasterBand(1); |
756 | |
|
757 | 0 | int nNewOverviews = 0; |
758 | 0 | int *panNewOverviewList = |
759 | 0 | static_cast<int *>(CPLCalloc(sizeof(int), nOverviews)); |
760 | 0 | double dfAreaNewOverviews = 0; |
761 | 0 | double dfAreaRefreshedOverviews = 0; |
762 | 0 | std::vector<bool> abValidLevel(nOverviews, true); |
763 | 0 | std::vector<bool> abRequireRefresh(nOverviews, false); |
764 | 0 | bool bFoundSinglePixelOverview = false; |
765 | 0 | for (int i = 0; i < nOverviews && poBand != nullptr; i++) |
766 | 0 | { |
767 | | // If we already have a 1x1 overview and this new one would result |
768 | | // in it too, then don't create it. |
769 | 0 | if (bFoundSinglePixelOverview && |
770 | 0 | DIV_ROUND_UP(poBand->GetXSize(), panOverviewList[i]) == 1 && |
771 | 0 | DIV_ROUND_UP(poBand->GetYSize(), panOverviewList[i]) == 1) |
772 | 0 | { |
773 | 0 | abValidLevel[i] = false; |
774 | 0 | continue; |
775 | 0 | } |
776 | | |
777 | 0 | for (int j = 0; j < poBand->GetOverviewCount(); j++) |
778 | 0 | { |
779 | 0 | GDALRasterBand *poOverview = poBand->GetOverview(j); |
780 | 0 | if (poOverview == nullptr) |
781 | 0 | continue; |
782 | | |
783 | 0 | int nOvFactor = |
784 | 0 | GDALComputeOvFactor(poOverview->GetXSize(), poBand->GetXSize(), |
785 | 0 | poOverview->GetYSize(), poBand->GetYSize()); |
786 | |
|
787 | 0 | if (nOvFactor == panOverviewList[i] || |
788 | 0 | nOvFactor == GDALOvLevelAdjust2(panOverviewList[i], |
789 | 0 | poBand->GetXSize(), |
790 | 0 | poBand->GetYSize())) |
791 | 0 | { |
792 | 0 | const auto osNewResampling = |
793 | 0 | GDALGetNormalizedOvrResampling(pszResampling); |
794 | 0 | const char *pszExistingResampling = |
795 | 0 | poOverview->GetMetadataItem("RESAMPLING"); |
796 | 0 | if (pszExistingResampling && |
797 | 0 | pszExistingResampling != osNewResampling) |
798 | 0 | { |
799 | 0 | if (auto l_poODS = poOverview->GetDataset()) |
800 | 0 | { |
801 | 0 | if (auto poDriver = l_poODS->GetDriver()) |
802 | 0 | { |
803 | 0 | if (EQUAL(poDriver->GetDescription(), "GTiff")) |
804 | 0 | { |
805 | 0 | poOverview->SetMetadataItem( |
806 | 0 | "RESAMPLING", osNewResampling.c_str()); |
807 | 0 | } |
808 | 0 | } |
809 | 0 | } |
810 | 0 | } |
811 | |
|
812 | 0 | abRequireRefresh[i] = true; |
813 | 0 | break; |
814 | 0 | } |
815 | 0 | } |
816 | |
|
817 | 0 | if (abValidLevel[i]) |
818 | 0 | { |
819 | 0 | const double dfArea = |
820 | 0 | 1.0 / |
821 | 0 | (static_cast<double>(panOverviewList[i]) * panOverviewList[i]); |
822 | 0 | dfAreaRefreshedOverviews += dfArea; |
823 | 0 | if (!abRequireRefresh[i]) |
824 | 0 | { |
825 | 0 | dfAreaNewOverviews += dfArea; |
826 | 0 | panNewOverviewList[nNewOverviews++] = panOverviewList[i]; |
827 | 0 | } |
828 | |
|
829 | 0 | if (DIV_ROUND_UP(poBand->GetXSize(), panOverviewList[i]) == 1 && |
830 | 0 | DIV_ROUND_UP(poBand->GetYSize(), panOverviewList[i]) == 1) |
831 | 0 | { |
832 | 0 | bFoundSinglePixelOverview = true; |
833 | 0 | } |
834 | 0 | } |
835 | 0 | } |
836 | | |
837 | | /* -------------------------------------------------------------------- */ |
838 | | /* Build band list. */ |
839 | | /* -------------------------------------------------------------------- */ |
840 | 0 | GDALRasterBand **pahBands = static_cast<GDALRasterBand **>( |
841 | 0 | CPLCalloc(sizeof(GDALRasterBand *), nBands)); |
842 | 0 | for (int i = 0; i < nBands; i++) |
843 | 0 | pahBands[i] = poDS->GetRasterBand(panBandList[i]); |
844 | | |
845 | | /* -------------------------------------------------------------------- */ |
846 | | /* Build new overviews - Imagine. Keep existing file open if */ |
847 | | /* we have it. But mark all overviews as in need of */ |
848 | | /* regeneration, since HFAAuxBuildOverviews() doesn't actually */ |
849 | | /* produce the imagery. */ |
850 | | /* -------------------------------------------------------------------- */ |
851 | |
|
852 | 0 | CPLErr eErr = CE_None; |
853 | |
|
854 | 0 | void *pScaledOverviewWithoutMask = GDALCreateScaledProgress( |
855 | 0 | 0, (HaveMaskFile() && poMaskDS) ? double(nBands) / (nBands + 1) : 1, |
856 | 0 | pfnProgress, pProgressData); |
857 | |
|
858 | 0 | const auto AvoidZero = [](double x) |
859 | 0 | { |
860 | 0 | if (x == 0) |
861 | 0 | return 1.0; |
862 | 0 | return x; |
863 | 0 | }; |
864 | |
|
865 | 0 | void *pScaledProgress = GDALCreateScaledProgress( |
866 | 0 | 0, dfAreaNewOverviews / AvoidZero(dfAreaRefreshedOverviews), |
867 | 0 | GDALScaledProgress, pScaledOverviewWithoutMask); |
868 | 0 | if (bOvrIsAux) |
869 | 0 | { |
870 | 0 | #ifdef NO_HFA_SUPPORT |
871 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
872 | 0 | "This build does not support creating .aux overviews"); |
873 | 0 | eErr = CE_Failure; |
874 | | #else |
875 | | if (nNewOverviews == 0) |
876 | | { |
877 | | /* if we call HFAAuxBuildOverviews() with nNewOverviews == 0 */ |
878 | | /* because that there's no new, this will wipe existing */ |
879 | | /* overviews (#4831) */ |
880 | | // eErr = CE_None; |
881 | | } |
882 | | else |
883 | | { |
884 | | eErr = HFAAuxBuildOverviews( |
885 | | osOvrFilename, poDS, &poODS, nBands, panBandList, nNewOverviews, |
886 | | panNewOverviewList, pszResampling, GDALScaledProgress, |
887 | | pScaledProgress, papszOptions); |
888 | | } |
889 | | |
890 | | // HFAAuxBuildOverviews doesn't actually generate overviews |
891 | | dfAreaNewOverviews = 0.0; |
892 | | for (int j = 0; j < nOverviews; j++) |
893 | | { |
894 | | if (abValidLevel[j]) |
895 | | abRequireRefresh[j] = true; |
896 | | } |
897 | | #endif |
898 | 0 | } |
899 | | |
900 | | /* -------------------------------------------------------------------- */ |
901 | | /* Build new overviews - TIFF. Close TIFF files while we */ |
902 | | /* operate on it. */ |
903 | | /* -------------------------------------------------------------------- */ |
904 | 0 | else |
905 | 0 | { |
906 | 0 | if (poODS != nullptr) |
907 | 0 | { |
908 | 0 | delete poODS; |
909 | 0 | poODS = nullptr; |
910 | 0 | } |
911 | |
|
912 | 0 | #ifdef HAVE_TIFF |
913 | 0 | eErr = GTIFFBuildOverviews( |
914 | 0 | osOvrFilename, nBands, pahBands, nNewOverviews, panNewOverviewList, |
915 | 0 | pszResampling, GDALScaledProgress, pScaledProgress, papszOptions); |
916 | | |
917 | | // Probe for proxy overview filename. |
918 | 0 | if (eErr == CE_Failure) |
919 | 0 | { |
920 | 0 | const char *pszProxyOvrFilename = |
921 | 0 | poDS->GetMetadataItem("FILENAME", "ProxyOverviewRequest"); |
922 | |
|
923 | 0 | if (pszProxyOvrFilename != nullptr) |
924 | 0 | { |
925 | 0 | osOvrFilename = pszProxyOvrFilename; |
926 | 0 | eErr = GTIFFBuildOverviews(osOvrFilename, nBands, pahBands, |
927 | 0 | nNewOverviews, panNewOverviewList, |
928 | 0 | pszResampling, GDALScaledProgress, |
929 | 0 | pScaledProgress, papszOptions); |
930 | 0 | } |
931 | 0 | } |
932 | |
|
933 | 0 | if (eErr == CE_None) |
934 | 0 | { |
935 | 0 | poODS = GDALDataset::Open(osOvrFilename, |
936 | 0 | GDAL_OF_RASTER | GDAL_OF_UPDATE); |
937 | 0 | if (poODS == nullptr) |
938 | 0 | eErr = CE_Failure; |
939 | 0 | } |
940 | | #else |
941 | | CPLError(CE_Failure, CPLE_NotSupported, |
942 | | "Cannot build TIFF overviews due to GeoTIFF driver missing"); |
943 | | eErr = CE_Failure; |
944 | | #endif |
945 | 0 | } |
946 | |
|
947 | 0 | GDALDestroyScaledProgress(pScaledProgress); |
948 | | |
949 | | /* -------------------------------------------------------------------- */ |
950 | | /* Refresh old overviews that were listed. */ |
951 | | /* -------------------------------------------------------------------- */ |
952 | 0 | GDALRasterBand **papoOverviewBands = |
953 | 0 | static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nOverviews)); |
954 | |
|
955 | 0 | for (int iBand = 0; iBand < nBands && eErr == CE_None; iBand++) |
956 | 0 | { |
957 | 0 | poBand = poDS->GetRasterBand(panBandList[iBand]); |
958 | 0 | if (poBand == nullptr) |
959 | 0 | { |
960 | 0 | eErr = CE_Failure; |
961 | 0 | break; |
962 | 0 | } |
963 | | |
964 | 0 | nNewOverviews = 0; |
965 | 0 | std::vector<bool> abAlreadyUsedOverviewBand(poBand->GetOverviewCount(), |
966 | 0 | false); |
967 | |
|
968 | 0 | for (int i = 0; i < nOverviews; i++) |
969 | 0 | { |
970 | 0 | if (!abValidLevel[i] || !abRequireRefresh[i]) |
971 | 0 | continue; |
972 | | |
973 | 0 | for (int j = 0; j < poBand->GetOverviewCount(); j++) |
974 | 0 | { |
975 | 0 | if (abAlreadyUsedOverviewBand[j]) |
976 | 0 | continue; |
977 | | |
978 | 0 | GDALRasterBand *poOverview = poBand->GetOverview(j); |
979 | 0 | if (poOverview == nullptr) |
980 | 0 | continue; |
981 | | |
982 | 0 | int bHasNoData = FALSE; |
983 | 0 | double noDataValue = poBand->GetNoDataValue(&bHasNoData); |
984 | |
|
985 | 0 | if (bHasNoData) |
986 | 0 | poOverview->SetNoDataValue(noDataValue); |
987 | |
|
988 | 0 | const int nOvFactor = GDALComputeOvFactor( |
989 | 0 | poOverview->GetXSize(), poBand->GetXSize(), |
990 | 0 | poOverview->GetYSize(), poBand->GetYSize()); |
991 | |
|
992 | 0 | if (nOvFactor == panOverviewList[i] || |
993 | 0 | nOvFactor == GDALOvLevelAdjust2(panOverviewList[i], |
994 | 0 | poBand->GetXSize(), |
995 | 0 | poBand->GetYSize())) |
996 | 0 | { |
997 | 0 | abAlreadyUsedOverviewBand[j] = true; |
998 | 0 | CPLAssert(nNewOverviews < poBand->GetOverviewCount()); |
999 | 0 | papoOverviewBands[nNewOverviews++] = poOverview; |
1000 | 0 | break; |
1001 | 0 | } |
1002 | 0 | } |
1003 | 0 | } |
1004 | | |
1005 | 0 | if (nNewOverviews > 0) |
1006 | 0 | { |
1007 | 0 | const double dfOffset = |
1008 | 0 | dfAreaNewOverviews / AvoidZero(dfAreaRefreshedOverviews); |
1009 | 0 | const double dfScale = 1.0 - dfOffset; |
1010 | 0 | pScaledProgress = GDALCreateScaledProgress( |
1011 | 0 | dfOffset + dfScale * iBand / nBands, |
1012 | 0 | dfOffset + dfScale * (iBand + 1) / nBands, GDALScaledProgress, |
1013 | 0 | pScaledOverviewWithoutMask); |
1014 | 0 | eErr = GDALRegenerateOverviewsEx( |
1015 | 0 | GDALRasterBand::ToHandle(poBand), nNewOverviews, |
1016 | 0 | reinterpret_cast<GDALRasterBandH *>(papoOverviewBands), |
1017 | 0 | pszResampling, GDALScaledProgress, pScaledProgress, |
1018 | 0 | papszOptions); |
1019 | 0 | GDALDestroyScaledProgress(pScaledProgress); |
1020 | 0 | } |
1021 | 0 | } |
1022 | | |
1023 | | /* -------------------------------------------------------------------- */ |
1024 | | /* Cleanup */ |
1025 | | /* -------------------------------------------------------------------- */ |
1026 | 0 | CPLFree(papoOverviewBands); |
1027 | 0 | CPLFree(panNewOverviewList); |
1028 | 0 | CPLFree(pahBands); |
1029 | 0 | GDALDestroyScaledProgress(pScaledOverviewWithoutMask); |
1030 | | |
1031 | | /* -------------------------------------------------------------------- */ |
1032 | | /* If we have a mask file, we need to build its overviews too. */ |
1033 | | /* -------------------------------------------------------------------- */ |
1034 | 0 | if (HaveMaskFile() && eErr == CE_None) |
1035 | 0 | { |
1036 | 0 | pScaledProgress = GDALCreateScaledProgress( |
1037 | 0 | double(nBands) / (nBands + 1), 1.0, pfnProgress, pProgressData); |
1038 | 0 | eErr = BuildOverviewsMask(pszResampling, nOverviews, panOverviewList, |
1039 | 0 | GDALScaledProgress, pScaledProgress, |
1040 | 0 | papszOptions); |
1041 | 0 | GDALDestroyScaledProgress(pScaledProgress); |
1042 | 0 | } |
1043 | | |
1044 | | /* -------------------------------------------------------------------- */ |
1045 | | /* If we have an overview dataset, then mark all the overviews */ |
1046 | | /* with the base dataset Used later for finding overviews */ |
1047 | | /* masks. Uggg. */ |
1048 | | /* -------------------------------------------------------------------- */ |
1049 | 0 | if (poODS) |
1050 | 0 | { |
1051 | 0 | const int nOverviewCount = GetOverviewCount(1); |
1052 | |
|
1053 | 0 | for (int iOver = 0; iOver < nOverviewCount; iOver++) |
1054 | 0 | { |
1055 | 0 | GDALRasterBand *poOtherBand = GetOverview(1, iOver); |
1056 | 0 | GDALDataset *poOverDS = |
1057 | 0 | poOtherBand != nullptr ? poOtherBand->GetDataset() : nullptr; |
1058 | |
|
1059 | 0 | if (poOverDS != nullptr) |
1060 | 0 | { |
1061 | 0 | poOverDS->oOvManager.poBaseDS = poDS; |
1062 | 0 | poOverDS->oOvManager.poDS = poOverDS; |
1063 | 0 | } |
1064 | 0 | } |
1065 | 0 | } |
1066 | |
|
1067 | 0 | return eErr; |
1068 | 0 | } |
1069 | | |
1070 | | /************************************************************************/ |
1071 | | /* BuildOverviewsMask() */ |
1072 | | /************************************************************************/ |
1073 | | |
1074 | | CPLErr GDALDefaultOverviews::BuildOverviewsMask(const char *pszResampling, |
1075 | | int nOverviews, |
1076 | | const int *panOverviewList, |
1077 | | GDALProgressFunc pfnProgress, |
1078 | | void *pProgressData, |
1079 | | CSLConstList papszOptions) |
1080 | 0 | { |
1081 | 0 | CPLErr eErr = CE_None; |
1082 | 0 | if (HaveMaskFile() && poMaskDS) |
1083 | 0 | { |
1084 | | // Some options are not compatible with mask overviews |
1085 | | // so unset them, and define more sensible values. |
1086 | 0 | CPLStringList aosMaskOptions(papszOptions); |
1087 | 0 | const char *pszCompress = |
1088 | 0 | GetOptionValue(papszOptions, "COMPRESS", "COMPRESS_OVERVIEW"); |
1089 | 0 | const bool bJPEG = pszCompress && EQUAL(pszCompress, "JPEG"); |
1090 | 0 | const char *pszPhotometric = |
1091 | 0 | GetOptionValue(papszOptions, "PHOTOMETRIC", "PHOTOMETRIC_OVERVIEW"); |
1092 | 0 | const bool bPHOTOMETRIC_YCBCR = |
1093 | 0 | pszPhotometric && EQUAL(pszPhotometric, "YCBCR"); |
1094 | 0 | if (bJPEG) |
1095 | 0 | aosMaskOptions.SetNameValue("COMPRESS", "DEFLATE"); |
1096 | 0 | if (bPHOTOMETRIC_YCBCR) |
1097 | 0 | aosMaskOptions.SetNameValue("PHOTOMETRIC", "MINISBLACK"); |
1098 | |
|
1099 | 0 | eErr = poMaskDS->BuildOverviews( |
1100 | 0 | pszResampling, nOverviews, panOverviewList, 0, nullptr, pfnProgress, |
1101 | 0 | pProgressData, aosMaskOptions.List()); |
1102 | |
|
1103 | 0 | if (bOwnMaskDS) |
1104 | 0 | { |
1105 | | // Reset the poMask member of main dataset bands, since it |
1106 | | // will become invalid after poMaskDS closing. |
1107 | 0 | for (int iBand = 1; iBand <= poDS->GetRasterCount(); iBand++) |
1108 | 0 | { |
1109 | 0 | GDALRasterBand *poOtherBand = poDS->GetRasterBand(iBand); |
1110 | 0 | if (poOtherBand != nullptr) |
1111 | 0 | poOtherBand->InvalidateMaskBand(); |
1112 | 0 | } |
1113 | |
|
1114 | 0 | GDALClose(poMaskDS); |
1115 | 0 | } |
1116 | | |
1117 | | // force next request to reread mask file. |
1118 | 0 | poMaskDS = nullptr; |
1119 | 0 | bOwnMaskDS = false; |
1120 | 0 | bCheckedForMask = false; |
1121 | 0 | } |
1122 | |
|
1123 | 0 | return eErr; |
1124 | 0 | } |
1125 | | |
1126 | | /************************************************************************/ |
1127 | | /* CreateMaskBand() */ |
1128 | | /************************************************************************/ |
1129 | | |
1130 | | CPLErr GDALDefaultOverviews::CreateMaskBand(int nFlags, int nBand) |
1131 | | |
1132 | 0 | { |
1133 | 0 | if (nBand < 1) |
1134 | 0 | nFlags |= GMF_PER_DATASET; |
1135 | | |
1136 | | /* -------------------------------------------------------------------- */ |
1137 | | /* ensure existing file gets opened if there is one. */ |
1138 | | /* -------------------------------------------------------------------- */ |
1139 | 0 | CPL_IGNORE_RET_VAL(HaveMaskFile()); |
1140 | | |
1141 | | /* -------------------------------------------------------------------- */ |
1142 | | /* Try creating the mask file. */ |
1143 | | /* -------------------------------------------------------------------- */ |
1144 | 0 | if (poMaskDS == nullptr) |
1145 | 0 | { |
1146 | 0 | GDALDriver *const poDr = |
1147 | 0 | static_cast<GDALDriver *>(GDALGetDriverByName("GTiff")); |
1148 | |
|
1149 | 0 | if (poDr == nullptr) |
1150 | 0 | return CE_Failure; |
1151 | | |
1152 | 0 | GDALRasterBand *const poTBand = poDS->GetRasterBand(1); |
1153 | 0 | if (poTBand == nullptr) |
1154 | 0 | return CE_Failure; |
1155 | | |
1156 | 0 | const int nBands = |
1157 | 0 | (nFlags & GMF_PER_DATASET) ? 1 : poDS->GetRasterCount(); |
1158 | |
|
1159 | 0 | char **papszOpt = CSLSetNameValue(nullptr, "COMPRESS", "DEFLATE"); |
1160 | 0 | papszOpt = CSLSetNameValue(papszOpt, "INTERLEAVE", "BAND"); |
1161 | |
|
1162 | 0 | int nBX = 0; |
1163 | 0 | int nBY = 0; |
1164 | 0 | poTBand->GetBlockSize(&nBX, &nBY); |
1165 | | |
1166 | | // Try to create matching tile size if legal in TIFF. |
1167 | 0 | if ((nBX % 16) == 0 && (nBY % 16) == 0) |
1168 | 0 | { |
1169 | 0 | papszOpt = CSLSetNameValue(papszOpt, "TILED", "YES"); |
1170 | 0 | papszOpt = CSLSetNameValue(papszOpt, "BLOCKXSIZE", |
1171 | 0 | CPLString().Printf("%d", nBX)); |
1172 | 0 | papszOpt = CSLSetNameValue(papszOpt, "BLOCKYSIZE", |
1173 | 0 | CPLString().Printf("%d", nBY)); |
1174 | 0 | } |
1175 | |
|
1176 | 0 | CPLString osMskFilename; |
1177 | 0 | osMskFilename.Printf("%s.msk", poDS->GetDescription()); |
1178 | 0 | poMaskDS = |
1179 | 0 | poDr->Create(osMskFilename, poDS->GetRasterXSize(), |
1180 | 0 | poDS->GetRasterYSize(), nBands, GDT_Byte, papszOpt); |
1181 | 0 | CSLDestroy(papszOpt); |
1182 | |
|
1183 | 0 | if (poMaskDS == nullptr) // Presumably error already issued. |
1184 | 0 | return CE_Failure; |
1185 | | |
1186 | 0 | bOwnMaskDS = true; |
1187 | 0 | } |
1188 | | |
1189 | | /* -------------------------------------------------------------------- */ |
1190 | | /* Save the mask flags for this band. */ |
1191 | | /* -------------------------------------------------------------------- */ |
1192 | 0 | if (nBand > poMaskDS->GetRasterCount()) |
1193 | 0 | { |
1194 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1195 | 0 | "Attempt to create a mask band for band %d of %s, " |
1196 | 0 | "but the .msk file has a PER_DATASET mask.", |
1197 | 0 | nBand, poDS->GetDescription()); |
1198 | 0 | return CE_Failure; |
1199 | 0 | } |
1200 | | |
1201 | 0 | for (int iBand = 0; iBand < poDS->GetRasterCount(); iBand++) |
1202 | 0 | { |
1203 | | // we write only the info for this band, unless we are |
1204 | | // using PER_DATASET in which case we write for all. |
1205 | 0 | if (nBand != iBand + 1 && !(nFlags & GMF_PER_DATASET)) |
1206 | 0 | continue; |
1207 | | |
1208 | 0 | poMaskDS->SetMetadataItem( |
1209 | 0 | CPLString().Printf("INTERNAL_MASK_FLAGS_%d", iBand + 1), |
1210 | 0 | CPLString().Printf("%d", nFlags)); |
1211 | 0 | } |
1212 | |
|
1213 | 0 | return CE_None; |
1214 | 0 | } |
1215 | | |
1216 | | /************************************************************************/ |
1217 | | /* GetMaskBand() */ |
1218 | | /************************************************************************/ |
1219 | | |
1220 | | // Secret code meaning we don't handle this band. |
1221 | | constexpr int MISSING_FLAGS = 0x8000; |
1222 | | |
1223 | | GDALRasterBand *GDALDefaultOverviews::GetMaskBand(int nBand) |
1224 | | |
1225 | 0 | { |
1226 | 0 | const int nFlags = GetMaskFlags(nBand); |
1227 | |
|
1228 | 0 | if (poMaskDS == nullptr || nFlags == MISSING_FLAGS) |
1229 | 0 | return nullptr; |
1230 | | |
1231 | 0 | if (nFlags & GMF_PER_DATASET) |
1232 | 0 | return poMaskDS->GetRasterBand(1); |
1233 | | |
1234 | 0 | if (nBand > 0) |
1235 | 0 | return poMaskDS->GetRasterBand(nBand); |
1236 | | |
1237 | 0 | return nullptr; |
1238 | 0 | } |
1239 | | |
1240 | | /************************************************************************/ |
1241 | | /* GetMaskFlags() */ |
1242 | | /************************************************************************/ |
1243 | | |
1244 | | int GDALDefaultOverviews::GetMaskFlags(int nBand) |
1245 | | |
1246 | 0 | { |
1247 | | /* -------------------------------------------------------------------- */ |
1248 | | /* Fetch this band's metadata entry. They are of the form: */ |
1249 | | /* INTERNAL_MASK_FLAGS_n: flags */ |
1250 | | /* -------------------------------------------------------------------- */ |
1251 | 0 | if (!HaveMaskFile()) |
1252 | 0 | return 0; |
1253 | | |
1254 | 0 | const char *pszValue = poMaskDS->GetMetadataItem( |
1255 | 0 | CPLString().Printf("INTERNAL_MASK_FLAGS_%d", std::max(nBand, 1))); |
1256 | |
|
1257 | 0 | if (pszValue == nullptr) |
1258 | 0 | return MISSING_FLAGS; |
1259 | | |
1260 | 0 | return atoi(pszValue); |
1261 | 0 | } |
1262 | | |
1263 | | /************************************************************************/ |
1264 | | /* HaveMaskFile() */ |
1265 | | /* */ |
1266 | | /* Check for a mask file if we haven't already done so. */ |
1267 | | /* Returns TRUE if we have one, otherwise FALSE. */ |
1268 | | /************************************************************************/ |
1269 | | |
1270 | | int GDALDefaultOverviews::HaveMaskFile(char **papszSiblingFiles, |
1271 | | const char *pszBasename) |
1272 | | |
1273 | 0 | { |
1274 | | /* -------------------------------------------------------------------- */ |
1275 | | /* Have we already checked for masks? */ |
1276 | | /* -------------------------------------------------------------------- */ |
1277 | 0 | if (bCheckedForMask) |
1278 | 0 | return poMaskDS != nullptr; |
1279 | | |
1280 | 0 | if (papszSiblingFiles == nullptr) |
1281 | 0 | papszSiblingFiles = papszInitSiblingFiles; |
1282 | | |
1283 | | /* -------------------------------------------------------------------- */ |
1284 | | /* Are we an overview? If so we need to find the corresponding */ |
1285 | | /* overview in the base files mask file (if there is one). */ |
1286 | | /* -------------------------------------------------------------------- */ |
1287 | 0 | if (poBaseDS != nullptr && poBaseDS->oOvManager.HaveMaskFile()) |
1288 | 0 | { |
1289 | 0 | GDALRasterBand *const poBaseBand = poBaseDS->GetRasterBand(1); |
1290 | 0 | GDALDataset *poMaskDSTemp = nullptr; |
1291 | 0 | if (poBaseBand != nullptr) |
1292 | 0 | { |
1293 | 0 | GDALRasterBand *poBaseMask = poBaseBand->GetMaskBand(); |
1294 | 0 | if (poBaseMask != nullptr) |
1295 | 0 | { |
1296 | 0 | const int nOverviewCount = poBaseMask->GetOverviewCount(); |
1297 | 0 | for (int iOver = 0; iOver < nOverviewCount; iOver++) |
1298 | 0 | { |
1299 | 0 | GDALRasterBand *const poOverBand = |
1300 | 0 | poBaseMask->GetOverview(iOver); |
1301 | 0 | if (poOverBand == nullptr) |
1302 | 0 | continue; |
1303 | | |
1304 | 0 | if (poOverBand->GetXSize() == poDS->GetRasterXSize() && |
1305 | 0 | poOverBand->GetYSize() == poDS->GetRasterYSize()) |
1306 | 0 | { |
1307 | 0 | poMaskDSTemp = poOverBand->GetDataset(); |
1308 | 0 | break; |
1309 | 0 | } |
1310 | 0 | } |
1311 | 0 | } |
1312 | 0 | } |
1313 | |
|
1314 | 0 | if (poMaskDSTemp != poDS) |
1315 | 0 | { |
1316 | 0 | poMaskDS = poMaskDSTemp; |
1317 | 0 | bCheckedForMask = true; |
1318 | 0 | bOwnMaskDS = false; |
1319 | |
|
1320 | 0 | return poMaskDS != nullptr; |
1321 | 0 | } |
1322 | 0 | } |
1323 | | |
1324 | | /* -------------------------------------------------------------------- */ |
1325 | | /* Are we even initialized? If not, we apparently don't want */ |
1326 | | /* to support overviews and masks. */ |
1327 | | /* -------------------------------------------------------------------- */ |
1328 | 0 | if (poDS == nullptr) |
1329 | 0 | return FALSE; |
1330 | | |
1331 | | /* -------------------------------------------------------------------- */ |
1332 | | /* Check for .msk file. */ |
1333 | | /* -------------------------------------------------------------------- */ |
1334 | 0 | bCheckedForMask = true; |
1335 | |
|
1336 | 0 | if (pszBasename == nullptr) |
1337 | 0 | pszBasename = poDS->GetDescription(); |
1338 | | |
1339 | | // Don't bother checking for masks of masks. |
1340 | 0 | if (EQUAL(CPLGetExtensionSafe(pszBasename).c_str(), "msk")) |
1341 | 0 | return FALSE; |
1342 | | |
1343 | 0 | if (!GDALCanFileAcceptSidecarFile(pszBasename)) |
1344 | 0 | return FALSE; |
1345 | 0 | CPLString osMskFilename; |
1346 | 0 | osMskFilename.Printf("%s.msk", pszBasename); |
1347 | |
|
1348 | 0 | std::vector<char> achMskFilename; |
1349 | 0 | achMskFilename.resize(osMskFilename.size() + 1); |
1350 | 0 | memcpy(&(achMskFilename[0]), osMskFilename.c_str(), |
1351 | 0 | osMskFilename.size() + 1); |
1352 | 0 | bool bExists = |
1353 | 0 | CPL_TO_BOOL(CPLCheckForFile(&achMskFilename[0], papszSiblingFiles)); |
1354 | 0 | osMskFilename = &achMskFilename[0]; |
1355 | |
|
1356 | 0 | #if !defined(_WIN32) |
1357 | 0 | if (!bExists && !papszSiblingFiles) |
1358 | 0 | { |
1359 | 0 | osMskFilename.Printf("%s.MSK", pszBasename); |
1360 | 0 | memcpy(&(achMskFilename[0]), osMskFilename.c_str(), |
1361 | 0 | osMskFilename.size() + 1); |
1362 | 0 | bExists = |
1363 | 0 | CPL_TO_BOOL(CPLCheckForFile(&achMskFilename[0], papszSiblingFiles)); |
1364 | 0 | osMskFilename = &achMskFilename[0]; |
1365 | 0 | } |
1366 | 0 | #endif |
1367 | |
|
1368 | 0 | if (!bExists) |
1369 | 0 | return FALSE; |
1370 | | |
1371 | | /* -------------------------------------------------------------------- */ |
1372 | | /* Open the file. */ |
1373 | | /* -------------------------------------------------------------------- */ |
1374 | 0 | poMaskDS = GDALDataset::Open( |
1375 | 0 | osMskFilename, |
1376 | 0 | GDAL_OF_RASTER | (poDS->GetAccess() == GA_Update ? GDAL_OF_UPDATE : 0), |
1377 | 0 | nullptr, nullptr, papszInitSiblingFiles); |
1378 | 0 | CPLAssert(poMaskDS != poDS); |
1379 | | |
1380 | 0 | if (poMaskDS == nullptr) |
1381 | 0 | return FALSE; |
1382 | | |
1383 | 0 | bOwnMaskDS = true; |
1384 | |
|
1385 | 0 | return TRUE; |
1386 | 0 | } |
1387 | | |
1388 | | /************************************************************************/ |
1389 | | /* GDALGetNormalizedOvrResampling() */ |
1390 | | /************************************************************************/ |
1391 | | |
1392 | | std::string GDALGetNormalizedOvrResampling(const char *pszResampling) |
1393 | 0 | { |
1394 | 0 | if (pszResampling && |
1395 | 0 | EQUAL(pszResampling, "AVERAGE_BIT2GRAYSCALE_MINISWHITE")) |
1396 | 0 | return "AVERAGE_BIT2GRAYSCALE_MINISWHITE"; |
1397 | 0 | else if (pszResampling && STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2")) |
1398 | 0 | return "AVERAGE_BIT2GRAYSCALE"; |
1399 | 0 | else if (pszResampling && STARTS_WITH_CI(pszResampling, "NEAR")) |
1400 | 0 | return "NEAREST"; |
1401 | 0 | else if (pszResampling && EQUAL(pszResampling, "AVERAGE_MAGPHASE")) |
1402 | 0 | return "AVERAGE_MAGPHASE"; |
1403 | 0 | else if (pszResampling && STARTS_WITH_CI(pszResampling, "AVER")) |
1404 | 0 | return "AVERAGE"; |
1405 | 0 | else if (pszResampling && !EQUAL(pszResampling, "NONE")) |
1406 | 0 | { |
1407 | 0 | return CPLString(pszResampling).toupper(); |
1408 | 0 | } |
1409 | 0 | return std::string(); |
1410 | 0 | } |
1411 | | |
1412 | | //! @endcond |