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