/src/gdal/gcore/gdaldefaultoverviews.cpp
Line | Count | Source |
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 | | // Exclude TILEDB because reading from /vsis3/ can be really slow |
379 | 0 | const char *const apszAllowedDrivers[] = {"-TILEDB", nullptr}; |
380 | 0 | CPLPushErrorHandler(CPLQuietErrorHandler); |
381 | 0 | poODS = GDALDataset::Open( |
382 | 0 | osOvrFilename, |
383 | 0 | GDAL_OF_RASTER | |
384 | 0 | (poDS->GetAccess() == GA_Update ? GDAL_OF_UPDATE : 0), |
385 | 0 | apszAllowedDrivers); |
386 | 0 | CPLPopErrorHandler(); |
387 | 0 | } |
388 | 0 | } |
389 | | |
390 | | /* -------------------------------------------------------------------- */ |
391 | | /* If we have an overview dataset, then mark all the overviews */ |
392 | | /* with the base dataset Used later for finding overviews */ |
393 | | /* masks. Uggg. */ |
394 | | /* -------------------------------------------------------------------- */ |
395 | 0 | if (poODS) |
396 | 0 | { |
397 | 0 | const int nOverviewCount = GetOverviewCount(1); |
398 | |
|
399 | 0 | for (int iOver = 0; iOver < nOverviewCount; iOver++) |
400 | 0 | { |
401 | 0 | GDALRasterBand *const poBand = GetOverview(1, iOver); |
402 | 0 | GDALDataset *const poOverDS = |
403 | 0 | poBand != nullptr ? poBand->GetDataset() : nullptr; |
404 | |
|
405 | 0 | if (poOverDS != nullptr) |
406 | 0 | { |
407 | 0 | poOverDS->oOvManager.poBaseDS = poDS; |
408 | 0 | poOverDS->oOvManager.poDS = poOverDS; |
409 | 0 | } |
410 | 0 | } |
411 | 0 | } |
412 | | |
413 | | // Undo anti recursion protection |
414 | 0 | antiRec.oSetFiles.erase(pszInitName); |
415 | 0 | --antiRec.nRecLevel; |
416 | 0 | } |
417 | | |
418 | | /************************************************************************/ |
419 | | /* GetOverviewCount() */ |
420 | | /************************************************************************/ |
421 | | |
422 | | int GDALDefaultOverviews::GetOverviewCount(int nBand) |
423 | | |
424 | 0 | { |
425 | 0 | if (poODS == nullptr || nBand < 1 || nBand > poODS->GetRasterCount()) |
426 | 0 | return 0; |
427 | | |
428 | 0 | GDALRasterBand *poBand = poODS->GetRasterBand(nBand); |
429 | 0 | if (poBand == nullptr) |
430 | 0 | return 0; |
431 | | |
432 | 0 | if (bOvrIsAux) |
433 | 0 | return poBand->GetOverviewCount(); |
434 | | |
435 | 0 | return poBand->GetOverviewCount() + 1; |
436 | 0 | } |
437 | | |
438 | | /************************************************************************/ |
439 | | /* GetOverview() */ |
440 | | /************************************************************************/ |
441 | | |
442 | | GDALRasterBand *GDALDefaultOverviews::GetOverview(int nBand, int iOverview) |
443 | | |
444 | 0 | { |
445 | 0 | if (poODS == nullptr || nBand < 1 || nBand > poODS->GetRasterCount()) |
446 | 0 | return nullptr; |
447 | | |
448 | 0 | GDALRasterBand *const poBand = poODS->GetRasterBand(nBand); |
449 | 0 | if (poBand == nullptr) |
450 | 0 | return nullptr; |
451 | | |
452 | 0 | if (bOvrIsAux) |
453 | 0 | return poBand->GetOverview(iOverview); |
454 | | |
455 | | // TIFF case, base is overview 0. |
456 | 0 | if (iOverview == 0) |
457 | 0 | return poBand; |
458 | | |
459 | 0 | if (iOverview - 1 >= poBand->GetOverviewCount()) |
460 | 0 | return nullptr; |
461 | | |
462 | 0 | return poBand->GetOverview(iOverview - 1); |
463 | 0 | } |
464 | | |
465 | | /************************************************************************/ |
466 | | /* GDALOvLevelAdjust() */ |
467 | | /* */ |
468 | | /* Some overview levels cannot be achieved closely enough to be */ |
469 | | /* recognised as the desired overview level. This function */ |
470 | | /* will adjust an overview level to one that is achievable on */ |
471 | | /* the given raster size. */ |
472 | | /* */ |
473 | | /* For instance a 1200x1200 image on which a 256 level overview */ |
474 | | /* is request will end up generating a 5x5 overview. However, */ |
475 | | /* this will appear to the system be a level 240 overview. */ |
476 | | /* This function will adjust 256 to 240 based on knowledge of */ |
477 | | /* the image size. */ |
478 | | /************************************************************************/ |
479 | | |
480 | | int GDALOvLevelAdjust(int nOvLevel, int nXSize) |
481 | | |
482 | 0 | { |
483 | 0 | int nOXSize = DIV_ROUND_UP(nXSize, nOvLevel); |
484 | |
|
485 | 0 | return static_cast<int>(0.5 + nXSize / static_cast<double>(nOXSize)); |
486 | 0 | } |
487 | | |
488 | | int GDALOvLevelAdjust2(int nOvLevel, int nXSize, int nYSize) |
489 | | |
490 | 0 | { |
491 | | // Select the larger dimension to have increased accuracy, but |
492 | | // with a slight preference to x even if (a bit) smaller than y |
493 | | // in an attempt to behave closer as previous behavior. |
494 | 0 | if (nXSize >= nYSize / 2 && !(nXSize < nYSize && nXSize < nOvLevel)) |
495 | 0 | { |
496 | 0 | const int nOXSize = DIV_ROUND_UP(nXSize, nOvLevel); |
497 | |
|
498 | 0 | return static_cast<int>(0.5 + nXSize / static_cast<double>(nOXSize)); |
499 | 0 | } |
500 | | |
501 | 0 | const int nOYSize = DIV_ROUND_UP(nYSize, nOvLevel); |
502 | |
|
503 | 0 | return static_cast<int>(0.5 + nYSize / static_cast<double>(nOYSize)); |
504 | 0 | } |
505 | | |
506 | | /************************************************************************/ |
507 | | /* GetFloorPowerOfTwo() */ |
508 | | /************************************************************************/ |
509 | | |
510 | | static int GetFloorPowerOfTwo(int n) |
511 | 0 | { |
512 | 0 | int p2 = 1; |
513 | 0 | while ((n = n >> 1) > 0) |
514 | 0 | { |
515 | 0 | p2 <<= 1; |
516 | 0 | } |
517 | 0 | return p2; |
518 | 0 | } |
519 | | |
520 | | /************************************************************************/ |
521 | | /* GDALComputeOvFactor() */ |
522 | | /************************************************************************/ |
523 | | |
524 | | int GDALComputeOvFactor(int nOvrXSize, int nRasterXSize, int nOvrYSize, |
525 | | int nRasterYSize) |
526 | 0 | { |
527 | | // Select the larger dimension to have increased accuracy, but |
528 | | // with a slight preference to x even if (a bit) smaller than y |
529 | | // in an attempt to behave closer as previous behavior. |
530 | 0 | if (nRasterXSize != 1 && nRasterXSize >= nRasterYSize / 2) |
531 | 0 | { |
532 | 0 | const int nVal = static_cast<int>( |
533 | 0 | 0.5 + nRasterXSize / static_cast<double>(nOvrXSize)); |
534 | | // Try to return a power-of-two value |
535 | 0 | const int nValPowerOfTwo = GetFloorPowerOfTwo(nVal); |
536 | 0 | for (int fact = 1; fact <= 2 && nValPowerOfTwo <= INT_MAX / fact; |
537 | 0 | ++fact) |
538 | 0 | { |
539 | 0 | if (DIV_ROUND_UP(nRasterXSize, fact * nValPowerOfTwo) == nOvrXSize) |
540 | 0 | return fact * nValPowerOfTwo; |
541 | 0 | } |
542 | 0 | return nVal; |
543 | 0 | } |
544 | | |
545 | 0 | const int nVal = |
546 | 0 | static_cast<int>(0.5 + nRasterYSize / static_cast<double>(nOvrYSize)); |
547 | | // Try to return a power-of-two value |
548 | 0 | const int nValPowerOfTwo = GetFloorPowerOfTwo(nVal); |
549 | 0 | for (int fact = 1; fact <= 2 && nValPowerOfTwo <= INT_MAX / fact; ++fact) |
550 | 0 | { |
551 | 0 | if (DIV_ROUND_UP(nRasterYSize, fact * nValPowerOfTwo) == nOvrYSize) |
552 | 0 | return fact * nValPowerOfTwo; |
553 | 0 | } |
554 | 0 | return nVal; |
555 | 0 | } |
556 | | |
557 | | /************************************************************************/ |
558 | | /* CleanOverviews() */ |
559 | | /* */ |
560 | | /* Remove all existing overviews. */ |
561 | | /************************************************************************/ |
562 | | |
563 | | CPLErr GDALDefaultOverviews::CleanOverviews() |
564 | | |
565 | 0 | { |
566 | | // Anything to do? |
567 | 0 | if (poODS == nullptr) |
568 | 0 | return CE_None; |
569 | | |
570 | | // Delete the overview file(s). |
571 | 0 | GDALDriver *poOvrDriver = poODS->GetDriver(); |
572 | 0 | GDALClose(poODS); |
573 | 0 | poODS = nullptr; |
574 | |
|
575 | 0 | CPLErr eErr = |
576 | 0 | poOvrDriver != nullptr ? poOvrDriver->Delete(osOvrFilename) : CE_None; |
577 | | |
578 | | // Reset the saved overview filename. |
579 | 0 | if (!EQUAL(poDS->GetDescription(), ":::VIRTUAL:::")) |
580 | 0 | { |
581 | 0 | const bool bUseRRD = CPLTestBool(CPLGetConfigOption("USE_RRD", "NO")); |
582 | |
|
583 | 0 | if (bUseRRD) |
584 | 0 | osOvrFilename = |
585 | 0 | CPLResetExtensionSafe(poDS->GetDescription(), "aux"); |
586 | 0 | else |
587 | 0 | osOvrFilename = std::string(poDS->GetDescription()).append(".ovr"); |
588 | 0 | } |
589 | 0 | else |
590 | 0 | { |
591 | 0 | osOvrFilename = ""; |
592 | 0 | } |
593 | |
|
594 | 0 | if (HaveMaskFile() && poMaskDS) |
595 | 0 | { |
596 | 0 | const CPLErr eErr2 = poMaskDS->BuildOverviews( |
597 | 0 | nullptr, 0, nullptr, 0, nullptr, nullptr, nullptr, nullptr); |
598 | 0 | if (eErr2 != CE_None) |
599 | 0 | return eErr2; |
600 | 0 | } |
601 | | |
602 | 0 | return eErr; |
603 | 0 | } |
604 | | |
605 | | /************************************************************************/ |
606 | | /* BuildOverviewsSubDataset() */ |
607 | | /************************************************************************/ |
608 | | |
609 | | CPLErr GDALDefaultOverviews::BuildOverviewsSubDataset( |
610 | | const char *pszPhysicalFile, const char *pszResampling, int nOverviews, |
611 | | const int *panOverviewList, int nBands, const int *panBandList, |
612 | | GDALProgressFunc pfnProgress, void *pProgressData, |
613 | | CSLConstList papszOptions) |
614 | | |
615 | 0 | { |
616 | 0 | if (osOvrFilename.length() == 0 && nOverviews > 0) |
617 | 0 | { |
618 | 0 | VSIStatBufL sStatBuf; |
619 | |
|
620 | 0 | int iSequence = 0; // Used after for. |
621 | 0 | for (iSequence = 0; iSequence < 100; iSequence++) |
622 | 0 | { |
623 | 0 | osOvrFilename.Printf("%s_%d.ovr", pszPhysicalFile, iSequence); |
624 | 0 | if (VSIStatExL(osOvrFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0) |
625 | 0 | { |
626 | 0 | CPLString osAdjustedOvrFilename; |
627 | |
|
628 | 0 | if (poDS->GetMOFlags() & GMO_PAM_CLASS) |
629 | 0 | { |
630 | 0 | osAdjustedOvrFilename.Printf( |
631 | 0 | ":::BASE:::%s_%d.ovr", CPLGetFilename(pszPhysicalFile), |
632 | 0 | iSequence); |
633 | 0 | } |
634 | 0 | else |
635 | 0 | { |
636 | 0 | osAdjustedOvrFilename = osOvrFilename; |
637 | 0 | } |
638 | |
|
639 | 0 | poDS->SetMetadataItem("OVERVIEW_FILE", osAdjustedOvrFilename, |
640 | 0 | "OVERVIEWS"); |
641 | 0 | break; |
642 | 0 | } |
643 | 0 | } |
644 | |
|
645 | 0 | if (iSequence == 100) |
646 | 0 | osOvrFilename = ""; |
647 | 0 | } |
648 | |
|
649 | 0 | return BuildOverviews(nullptr, pszResampling, nOverviews, panOverviewList, |
650 | 0 | nBands, panBandList, pfnProgress, pProgressData, |
651 | 0 | papszOptions); |
652 | 0 | } |
653 | | |
654 | | /************************************************************************/ |
655 | | /* GetOptionValue() */ |
656 | | /************************************************************************/ |
657 | | |
658 | | static const char *GetOptionValue(CSLConstList papszOptions, |
659 | | const char *pszOptionKey, |
660 | | const char *pszConfigOptionKey) |
661 | 0 | { |
662 | 0 | const char *pszVal = |
663 | 0 | pszOptionKey ? CSLFetchNameValue(papszOptions, pszOptionKey) : nullptr; |
664 | 0 | if (pszVal) |
665 | 0 | { |
666 | 0 | return pszVal; |
667 | 0 | } |
668 | 0 | pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey); |
669 | 0 | if (pszVal) |
670 | 0 | { |
671 | 0 | return pszVal; |
672 | 0 | } |
673 | 0 | pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr); |
674 | 0 | return pszVal; |
675 | 0 | } |
676 | | |
677 | | /************************************************************************/ |
678 | | /* CheckSrcOverviewsConsistencyWithBase() */ |
679 | | /************************************************************************/ |
680 | | |
681 | | /*static */ bool GDALDefaultOverviews::CheckSrcOverviewsConsistencyWithBase( |
682 | | GDALDataset *poFullResDS, const std::vector<GDALDataset *> &apoSrcOvrDS) |
683 | 0 | { |
684 | 0 | const auto poThisCRS = poFullResDS->GetSpatialRef(); |
685 | 0 | GDALGeoTransform thisGT; |
686 | 0 | const bool bThisHasGT = poFullResDS->GetGeoTransform(thisGT) == CE_None; |
687 | 0 | for (auto *poSrcOvrDS : apoSrcOvrDS) |
688 | 0 | { |
689 | 0 | if (poSrcOvrDS->GetRasterXSize() > poFullResDS->GetRasterXSize() || |
690 | 0 | poSrcOvrDS->GetRasterYSize() > poFullResDS->GetRasterYSize()) |
691 | 0 | { |
692 | 0 | CPLError( |
693 | 0 | CE_Failure, CPLE_AppDefined, |
694 | 0 | "AddOverviews(): at least one input dataset has dimensions " |
695 | 0 | "larger than the full resolution dataset."); |
696 | 0 | return false; |
697 | 0 | } |
698 | 0 | if (poSrcOvrDS->GetRasterXSize() == 0 || |
699 | 0 | poSrcOvrDS->GetRasterYSize() == 0) |
700 | 0 | { |
701 | 0 | CPLError( |
702 | 0 | CE_Failure, CPLE_AppDefined, |
703 | 0 | "AddOverviews(): at least one input dataset has one of its " |
704 | 0 | "dimensions equal to 0."); |
705 | 0 | return false; |
706 | 0 | } |
707 | 0 | if (poSrcOvrDS->GetRasterCount() != poFullResDS->GetRasterCount()) |
708 | 0 | { |
709 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
710 | 0 | "AddOverviews(): at least one input dataset not the same " |
711 | 0 | "number of bands than the full resolution dataset."); |
712 | 0 | return false; |
713 | 0 | } |
714 | 0 | if (poThisCRS) |
715 | 0 | { |
716 | 0 | if (const auto poOvrCRS = poSrcOvrDS->GetSpatialRef()) |
717 | 0 | { |
718 | 0 | if (!poOvrCRS->IsSame(poThisCRS)) |
719 | 0 | { |
720 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
721 | 0 | "AddOverviews(): at least one input dataset has " |
722 | 0 | "its CRS " |
723 | 0 | "different from the one of the full resolution " |
724 | 0 | "dataset."); |
725 | 0 | return false; |
726 | 0 | } |
727 | 0 | } |
728 | 0 | } |
729 | 0 | if (bThisHasGT) |
730 | 0 | { |
731 | 0 | GDALGeoTransform ovrGT; |
732 | 0 | const bool bOvrHasGT = |
733 | 0 | poSrcOvrDS->GetGeoTransform(ovrGT) == CE_None; |
734 | 0 | const double dfOvrXRatio = |
735 | 0 | static_cast<double>(poFullResDS->GetRasterXSize()) / |
736 | 0 | poSrcOvrDS->GetRasterXSize(); |
737 | 0 | const double dfOvrYRatio = |
738 | 0 | static_cast<double>(poFullResDS->GetRasterYSize()) / |
739 | 0 | poSrcOvrDS->GetRasterYSize(); |
740 | 0 | if (bOvrHasGT && !(std::fabs(thisGT[0] - ovrGT[0]) <= |
741 | 0 | 0.5 * std::fabs(ovrGT[1]) && |
742 | 0 | std::fabs(thisGT[1] - ovrGT[1] / dfOvrXRatio) <= |
743 | 0 | 0.1 * std::fabs(ovrGT[1]) && |
744 | 0 | std::fabs(thisGT[2] - ovrGT[2] / dfOvrYRatio) <= |
745 | 0 | 0.1 * std::fabs(ovrGT[2]) && |
746 | 0 | std::fabs(thisGT[3] - ovrGT[3]) <= |
747 | 0 | 0.5 * std::fabs(ovrGT[5]) && |
748 | 0 | std::fabs(thisGT[4] - ovrGT[4] / dfOvrXRatio) <= |
749 | 0 | 0.1 * std::fabs(ovrGT[4]) && |
750 | 0 | std::fabs(thisGT[5] - ovrGT[5] / dfOvrYRatio) <= |
751 | 0 | 0.1 * std::fabs(ovrGT[5]))) |
752 | 0 | { |
753 | 0 | CPLError( |
754 | 0 | CE_Failure, CPLE_AppDefined, |
755 | 0 | "AddOverviews(): at least one input dataset has its " |
756 | 0 | "geospatial extent " |
757 | 0 | "different from the one of the full resolution dataset."); |
758 | 0 | return false; |
759 | 0 | } |
760 | 0 | } |
761 | 0 | } |
762 | 0 | return true; |
763 | 0 | } |
764 | | |
765 | | /************************************************************************/ |
766 | | /* AddOverviews() */ |
767 | | /************************************************************************/ |
768 | | |
769 | | CPLErr GDALDefaultOverviews::AddOverviews( |
770 | | [[maybe_unused]] const char *pszBasename, |
771 | | [[maybe_unused]] const std::vector<GDALDataset *> &apoSrcOvrDSIn, |
772 | | [[maybe_unused]] GDALProgressFunc pfnProgress, |
773 | | [[maybe_unused]] void *pProgressData, |
774 | | [[maybe_unused]] CSLConstList papszOptions) |
775 | 0 | { |
776 | 0 | #ifdef HAVE_TIFF |
777 | 0 | if (pfnProgress == nullptr) |
778 | 0 | pfnProgress = GDALDummyProgress; |
779 | |
|
780 | 0 | if (CreateOrOpenOverviewFile(pszBasename, papszOptions) != CE_None) |
781 | 0 | return CE_Failure; |
782 | | |
783 | 0 | if (bOvrIsAux) |
784 | 0 | { |
785 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
786 | 0 | "AddOverviews() not supported for .aux overviews"); |
787 | 0 | return CE_Failure; |
788 | 0 | } |
789 | | |
790 | 0 | if (!GDALDefaultOverviews::CheckSrcOverviewsConsistencyWithBase( |
791 | 0 | poDS, apoSrcOvrDSIn)) |
792 | 0 | return CE_Failure; |
793 | | |
794 | 0 | std::vector<GDALDataset *> apoSrcOvrDS = apoSrcOvrDSIn; |
795 | | // Sort overviews by descending size |
796 | 0 | std::sort(apoSrcOvrDS.begin(), apoSrcOvrDS.end(), |
797 | 0 | [](const GDALDataset *poDS1, const GDALDataset *poDS2) |
798 | 0 | { return poDS1->GetRasterXSize() > poDS2->GetRasterXSize(); }); |
799 | |
|
800 | 0 | auto poBand = poDS->GetRasterBand(1); |
801 | 0 | if (!poBand) |
802 | 0 | return CE_Failure; |
803 | | |
804 | | // Determine which overview levels must be created |
805 | 0 | std::vector<std::pair<int, int>> anOverviewSizes; |
806 | 0 | for (auto *poSrcOvrDS : apoSrcOvrDS) |
807 | 0 | { |
808 | 0 | bool bFound = false; |
809 | 0 | for (int j = 0; j < poBand->GetOverviewCount(); j++) |
810 | 0 | { |
811 | 0 | GDALRasterBand *poOverview = poBand->GetOverview(j); |
812 | 0 | if (poOverview && poOverview->GetDataset() && |
813 | 0 | poOverview->GetDataset() != poDS && |
814 | 0 | poOverview->GetXSize() == poSrcOvrDS->GetRasterXSize() && |
815 | 0 | poOverview->GetYSize() == poSrcOvrDS->GetRasterYSize()) |
816 | 0 | { |
817 | 0 | bFound = true; |
818 | 0 | break; |
819 | 0 | } |
820 | 0 | } |
821 | 0 | if (!bFound) |
822 | 0 | { |
823 | 0 | anOverviewSizes.emplace_back(poSrcOvrDS->GetRasterXSize(), |
824 | 0 | poSrcOvrDS->GetRasterYSize()); |
825 | 0 | } |
826 | 0 | } |
827 | |
|
828 | 0 | CPLErr eErr = CE_None; |
829 | |
|
830 | 0 | if (!anOverviewSizes.empty()) |
831 | 0 | { |
832 | 0 | if (poODS != nullptr) |
833 | 0 | { |
834 | 0 | delete poODS; |
835 | 0 | poODS = nullptr; |
836 | 0 | } |
837 | |
|
838 | 0 | const int nBands = poDS->GetRasterCount(); |
839 | 0 | std::vector<GDALRasterBand *> apoBands; |
840 | 0 | for (int i = 0; i < nBands; ++i) |
841 | 0 | apoBands.push_back(poDS->GetRasterBand(i + 1)); |
842 | |
|
843 | 0 | eErr = GTIFFBuildOverviewsEx(osOvrFilename, nBands, apoBands.data(), |
844 | 0 | static_cast<int>(apoSrcOvrDS.size()), |
845 | 0 | nullptr, anOverviewSizes.data(), "NONE", |
846 | 0 | nullptr, GDALDummyProgress, nullptr); |
847 | | |
848 | | // Probe for proxy overview filename. |
849 | 0 | if (eErr == CE_Failure) |
850 | 0 | { |
851 | 0 | const char *pszProxyOvrFilename = |
852 | 0 | poDS->GetMetadataItem("FILENAME", "ProxyOverviewRequest"); |
853 | |
|
854 | 0 | if (pszProxyOvrFilename != nullptr) |
855 | 0 | { |
856 | 0 | osOvrFilename = pszProxyOvrFilename; |
857 | 0 | eErr = GTIFFBuildOverviewsEx( |
858 | 0 | osOvrFilename, nBands, apoBands.data(), |
859 | 0 | static_cast<int>(apoSrcOvrDS.size()), nullptr, |
860 | 0 | anOverviewSizes.data(), "NONE", nullptr, GDALDummyProgress, |
861 | 0 | nullptr); |
862 | 0 | } |
863 | 0 | } |
864 | |
|
865 | 0 | if (eErr == CE_None) |
866 | 0 | { |
867 | 0 | poODS = GDALDataset::Open(osOvrFilename, |
868 | 0 | GDAL_OF_RASTER | GDAL_OF_UPDATE); |
869 | 0 | if (poODS == nullptr) |
870 | 0 | eErr = CE_Failure; |
871 | 0 | } |
872 | 0 | } |
873 | | |
874 | | // almost 0, but not 0 to please Coverity Scan |
875 | 0 | double dfTotalPixels = std::numeric_limits<double>::min(); |
876 | 0 | for (const auto *poSrcOvrDS : apoSrcOvrDS) |
877 | 0 | { |
878 | 0 | dfTotalPixels += static_cast<double>(poSrcOvrDS->GetRasterXSize()) * |
879 | 0 | poSrcOvrDS->GetRasterYSize(); |
880 | 0 | } |
881 | | |
882 | | // Copy source datasets into target overview datasets |
883 | 0 | double dfCurPixels = 0; |
884 | 0 | for (auto *poSrcOvrDS : apoSrcOvrDS) |
885 | 0 | { |
886 | 0 | GDALDataset *poDstOvrDS = nullptr; |
887 | 0 | for (int j = 0; eErr == CE_None && j < poBand->GetOverviewCount(); j++) |
888 | 0 | { |
889 | 0 | GDALRasterBand *poOverview = poBand->GetOverview(j); |
890 | 0 | if (poOverview && |
891 | 0 | poOverview->GetXSize() == poSrcOvrDS->GetRasterXSize() && |
892 | 0 | poOverview->GetYSize() == poSrcOvrDS->GetRasterYSize()) |
893 | 0 | { |
894 | 0 | poDstOvrDS = poOverview->GetDataset(); |
895 | 0 | break; |
896 | 0 | } |
897 | 0 | } |
898 | 0 | if (poDstOvrDS) |
899 | 0 | { |
900 | 0 | const double dfThisPixels = |
901 | 0 | static_cast<double>(poSrcOvrDS->GetRasterXSize()) * |
902 | 0 | poSrcOvrDS->GetRasterYSize(); |
903 | 0 | void *pScaledProgressData = GDALCreateScaledProgress( |
904 | 0 | dfCurPixels / dfTotalPixels, |
905 | 0 | (dfCurPixels + dfThisPixels) / dfTotalPixels, pfnProgress, |
906 | 0 | pProgressData); |
907 | 0 | dfCurPixels += dfThisPixels; |
908 | 0 | eErr = GDALDatasetCopyWholeRaster(GDALDataset::ToHandle(poSrcOvrDS), |
909 | 0 | GDALDataset::ToHandle(poDstOvrDS), |
910 | 0 | nullptr, GDALScaledProgress, |
911 | 0 | pScaledProgressData); |
912 | 0 | GDALDestroyScaledProgress(pScaledProgressData); |
913 | 0 | } |
914 | 0 | } |
915 | |
|
916 | 0 | return eErr; |
917 | | #else |
918 | | CPLError(CE_Failure, CPLE_NotSupported, |
919 | | "AddOverviews() not supported due to GeoTIFF driver missing"); |
920 | | return CE_Failure; |
921 | | #endif |
922 | 0 | } |
923 | | |
924 | | /************************************************************************/ |
925 | | /* CreateOrOpenOverviewFile() */ |
926 | | /************************************************************************/ |
927 | | |
928 | | CPLErr GDALDefaultOverviews::CreateOrOpenOverviewFile(const char *pszBasename, |
929 | | CSLConstList papszOptions) |
930 | 0 | { |
931 | | |
932 | | /* -------------------------------------------------------------------- */ |
933 | | /* If we don't already have an overview file, we need to decide */ |
934 | | /* what format to use. */ |
935 | | /* -------------------------------------------------------------------- */ |
936 | 0 | if (poODS == nullptr) |
937 | 0 | { |
938 | 0 | const char *pszUseRRD = |
939 | 0 | GetOptionValue(papszOptions, nullptr, "USE_RRD"); |
940 | 0 | bOvrIsAux = pszUseRRD && CPLTestBool(pszUseRRD); |
941 | 0 | if (bOvrIsAux) |
942 | 0 | { |
943 | 0 | osOvrFilename = |
944 | 0 | CPLResetExtensionSafe(poDS->GetDescription(), "aux"); |
945 | |
|
946 | 0 | VSIStatBufL sStatBuf; |
947 | 0 | if (VSIStatExL(osOvrFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0) |
948 | 0 | osOvrFilename.Printf("%s.aux", poDS->GetDescription()); |
949 | 0 | } |
950 | 0 | } |
951 | | /* -------------------------------------------------------------------- */ |
952 | | /* If we already have the overviews open, but they are */ |
953 | | /* read-only, then try and reopen them read-write. */ |
954 | | /* -------------------------------------------------------------------- */ |
955 | 0 | else if (poODS->GetAccess() == GA_ReadOnly) |
956 | 0 | { |
957 | 0 | GDALClose(poODS); |
958 | 0 | poODS = |
959 | 0 | GDALDataset::Open(osOvrFilename, GDAL_OF_RASTER | GDAL_OF_UPDATE); |
960 | 0 | if (poODS == nullptr) |
961 | 0 | return CE_Failure; |
962 | 0 | } |
963 | | |
964 | | /* -------------------------------------------------------------------- */ |
965 | | /* If a basename is provided, use it to override the internal */ |
966 | | /* overview filename. */ |
967 | | /* -------------------------------------------------------------------- */ |
968 | 0 | if (pszBasename == nullptr && osOvrFilename.length() == 0) |
969 | 0 | pszBasename = poDS->GetDescription(); |
970 | |
|
971 | 0 | if (pszBasename != nullptr) |
972 | 0 | { |
973 | 0 | if (bOvrIsAux) |
974 | 0 | osOvrFilename.Printf("%s.aux", pszBasename); |
975 | 0 | else |
976 | 0 | osOvrFilename.Printf("%s.ovr", pszBasename); |
977 | 0 | } |
978 | |
|
979 | 0 | return CE_None; |
980 | 0 | } |
981 | | |
982 | | /************************************************************************/ |
983 | | /* BuildOverviews() */ |
984 | | /************************************************************************/ |
985 | | |
986 | | CPLErr GDALDefaultOverviews::BuildOverviews( |
987 | | const char *pszBasename, const char *pszResampling, int nOverviews, |
988 | | const int *panOverviewList, int nBands, const int *panBandList, |
989 | | GDALProgressFunc pfnProgress, void *pProgressData, |
990 | | CSLConstList papszOptions) |
991 | | |
992 | 0 | { |
993 | 0 | if (pfnProgress == nullptr) |
994 | 0 | pfnProgress = GDALDummyProgress; |
995 | |
|
996 | 0 | if (nOverviews == 0) |
997 | 0 | return CleanOverviews(); |
998 | | |
999 | 0 | if (CreateOrOpenOverviewFile(pszBasename, papszOptions) != CE_None) |
1000 | 0 | return CE_Failure; |
1001 | | |
1002 | | /* -------------------------------------------------------------------- */ |
1003 | | /* Our TIFF overview support currently only works safely if all */ |
1004 | | /* bands are handled at the same time. */ |
1005 | | /* -------------------------------------------------------------------- */ |
1006 | 0 | if (!bOvrIsAux && nBands != poDS->GetRasterCount()) |
1007 | 0 | { |
1008 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1009 | 0 | "Generation of overviews in external TIFF currently only " |
1010 | 0 | "supported when operating on all bands. " |
1011 | 0 | "Operation failed."); |
1012 | 0 | return CE_Failure; |
1013 | 0 | } |
1014 | | |
1015 | | /* -------------------------------------------------------------------- */ |
1016 | | /* Establish which of the overview levels we already have, and */ |
1017 | | /* which are new. We assume that band 1 of the file is */ |
1018 | | /* representative. */ |
1019 | | /* -------------------------------------------------------------------- */ |
1020 | 0 | GDALRasterBand *poBand = poDS->GetRasterBand(1); |
1021 | |
|
1022 | 0 | int nNewOverviews = 0; |
1023 | 0 | int *panNewOverviewList = |
1024 | 0 | static_cast<int *>(CPLCalloc(sizeof(int), nOverviews)); |
1025 | 0 | double dfAreaNewOverviews = 0; |
1026 | 0 | double dfAreaRefreshedOverviews = 0; |
1027 | 0 | std::vector<bool> abValidLevel(nOverviews, true); |
1028 | 0 | std::vector<bool> abRequireRefresh(nOverviews, false); |
1029 | 0 | bool bFoundSinglePixelOverview = false; |
1030 | 0 | for (int i = 0; i < nOverviews && poBand != nullptr; i++) |
1031 | 0 | { |
1032 | | // If we already have a 1x1 overview and this new one would result |
1033 | | // in it too, then don't create it. |
1034 | 0 | if (bFoundSinglePixelOverview && |
1035 | 0 | DIV_ROUND_UP(poBand->GetXSize(), panOverviewList[i]) == 1 && |
1036 | 0 | DIV_ROUND_UP(poBand->GetYSize(), panOverviewList[i]) == 1) |
1037 | 0 | { |
1038 | 0 | abValidLevel[i] = false; |
1039 | 0 | continue; |
1040 | 0 | } |
1041 | | |
1042 | 0 | for (int j = 0; j < poBand->GetOverviewCount(); j++) |
1043 | 0 | { |
1044 | 0 | GDALRasterBand *poOverview = poBand->GetOverview(j); |
1045 | 0 | if (poOverview == nullptr) |
1046 | 0 | continue; |
1047 | | |
1048 | 0 | int nOvFactor = |
1049 | 0 | GDALComputeOvFactor(poOverview->GetXSize(), poBand->GetXSize(), |
1050 | 0 | poOverview->GetYSize(), poBand->GetYSize()); |
1051 | |
|
1052 | 0 | if (nOvFactor == panOverviewList[i] || |
1053 | 0 | nOvFactor == GDALOvLevelAdjust2(panOverviewList[i], |
1054 | 0 | poBand->GetXSize(), |
1055 | 0 | poBand->GetYSize())) |
1056 | 0 | { |
1057 | 0 | const auto osNewResampling = |
1058 | 0 | GDALGetNormalizedOvrResampling(pszResampling); |
1059 | 0 | const char *pszExistingResampling = |
1060 | 0 | poOverview->GetMetadataItem("RESAMPLING"); |
1061 | 0 | if (pszExistingResampling && |
1062 | 0 | pszExistingResampling != osNewResampling) |
1063 | 0 | { |
1064 | 0 | if (auto l_poODS = poOverview->GetDataset()) |
1065 | 0 | { |
1066 | 0 | if (auto poDriver = l_poODS->GetDriver()) |
1067 | 0 | { |
1068 | 0 | if (EQUAL(poDriver->GetDescription(), "GTiff")) |
1069 | 0 | { |
1070 | 0 | poOverview->SetMetadataItem( |
1071 | 0 | "RESAMPLING", osNewResampling.c_str()); |
1072 | 0 | } |
1073 | 0 | } |
1074 | 0 | } |
1075 | 0 | } |
1076 | |
|
1077 | 0 | abRequireRefresh[i] = true; |
1078 | 0 | break; |
1079 | 0 | } |
1080 | 0 | } |
1081 | |
|
1082 | 0 | if (abValidLevel[i]) |
1083 | 0 | { |
1084 | 0 | const double dfArea = |
1085 | 0 | 1.0 / |
1086 | 0 | (static_cast<double>(panOverviewList[i]) * panOverviewList[i]); |
1087 | 0 | dfAreaRefreshedOverviews += dfArea; |
1088 | 0 | if (!abRequireRefresh[i]) |
1089 | 0 | { |
1090 | 0 | dfAreaNewOverviews += dfArea; |
1091 | 0 | panNewOverviewList[nNewOverviews++] = panOverviewList[i]; |
1092 | 0 | } |
1093 | |
|
1094 | 0 | if (DIV_ROUND_UP(poBand->GetXSize(), panOverviewList[i]) == 1 && |
1095 | 0 | DIV_ROUND_UP(poBand->GetYSize(), panOverviewList[i]) == 1) |
1096 | 0 | { |
1097 | 0 | bFoundSinglePixelOverview = true; |
1098 | 0 | } |
1099 | 0 | } |
1100 | 0 | } |
1101 | | |
1102 | | /* -------------------------------------------------------------------- */ |
1103 | | /* Build band list. */ |
1104 | | /* -------------------------------------------------------------------- */ |
1105 | 0 | GDALRasterBand **pahBands = static_cast<GDALRasterBand **>( |
1106 | 0 | CPLCalloc(sizeof(GDALRasterBand *), nBands)); |
1107 | 0 | for (int i = 0; i < nBands; i++) |
1108 | 0 | pahBands[i] = poDS->GetRasterBand(panBandList[i]); |
1109 | | |
1110 | | /* -------------------------------------------------------------------- */ |
1111 | | /* Build new overviews - Imagine. Keep existing file open if */ |
1112 | | /* we have it. But mark all overviews as in need of */ |
1113 | | /* regeneration, since HFAAuxBuildOverviews() doesn't actually */ |
1114 | | /* produce the imagery. */ |
1115 | | /* -------------------------------------------------------------------- */ |
1116 | |
|
1117 | 0 | CPLErr eErr = CE_None; |
1118 | |
|
1119 | 0 | void *pScaledOverviewWithoutMask = GDALCreateScaledProgress( |
1120 | 0 | 0, (HaveMaskFile() && poMaskDS) ? double(nBands) / (nBands + 1) : 1, |
1121 | 0 | pfnProgress, pProgressData); |
1122 | |
|
1123 | 0 | const auto AvoidZero = [](double x) |
1124 | 0 | { |
1125 | 0 | if (x == 0) |
1126 | 0 | return 1.0; |
1127 | 0 | return x; |
1128 | 0 | }; |
1129 | |
|
1130 | 0 | void *pScaledProgress = GDALCreateScaledProgress( |
1131 | 0 | 0, dfAreaNewOverviews / AvoidZero(dfAreaRefreshedOverviews), |
1132 | 0 | GDALScaledProgress, pScaledOverviewWithoutMask); |
1133 | 0 | if (bOvrIsAux) |
1134 | 0 | { |
1135 | 0 | #ifdef NO_HFA_SUPPORT |
1136 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1137 | 0 | "This build does not support creating .aux overviews"); |
1138 | 0 | eErr = CE_Failure; |
1139 | | #else |
1140 | | if (nNewOverviews == 0) |
1141 | | { |
1142 | | /* if we call HFAAuxBuildOverviews() with nNewOverviews == 0 */ |
1143 | | /* because that there's no new, this will wipe existing */ |
1144 | | /* overviews (#4831) */ |
1145 | | // eErr = CE_None; |
1146 | | } |
1147 | | else |
1148 | | { |
1149 | | CPLStringList aosOptions(papszOptions); |
1150 | | aosOptions.SetNameValue("LOCATION", nullptr); |
1151 | | aosOptions.SetNameValue("USE_RRD", nullptr); |
1152 | | eErr = HFAAuxBuildOverviews( |
1153 | | osOvrFilename, poDS, &poODS, nBands, panBandList, nNewOverviews, |
1154 | | panNewOverviewList, pszResampling, GDALScaledProgress, |
1155 | | pScaledProgress, aosOptions.List()); |
1156 | | } |
1157 | | |
1158 | | // HFAAuxBuildOverviews doesn't actually generate overviews |
1159 | | dfAreaNewOverviews = 0.0; |
1160 | | for (int j = 0; j < nOverviews; j++) |
1161 | | { |
1162 | | if (abValidLevel[j]) |
1163 | | abRequireRefresh[j] = true; |
1164 | | } |
1165 | | #endif |
1166 | 0 | } |
1167 | | |
1168 | | /* -------------------------------------------------------------------- */ |
1169 | | /* Build new overviews - TIFF. Close TIFF files while we */ |
1170 | | /* operate on it. */ |
1171 | | /* -------------------------------------------------------------------- */ |
1172 | 0 | else |
1173 | 0 | { |
1174 | 0 | if (poODS != nullptr) |
1175 | 0 | { |
1176 | 0 | delete poODS; |
1177 | 0 | poODS = nullptr; |
1178 | 0 | } |
1179 | |
|
1180 | 0 | #ifdef HAVE_TIFF |
1181 | 0 | eErr = GTIFFBuildOverviews( |
1182 | 0 | osOvrFilename, nBands, pahBands, nNewOverviews, panNewOverviewList, |
1183 | 0 | pszResampling, GDALScaledProgress, pScaledProgress, papszOptions); |
1184 | | |
1185 | | // Probe for proxy overview filename. |
1186 | 0 | if (eErr == CE_Failure) |
1187 | 0 | { |
1188 | 0 | const char *pszProxyOvrFilename = |
1189 | 0 | poDS->GetMetadataItem("FILENAME", "ProxyOverviewRequest"); |
1190 | |
|
1191 | 0 | if (pszProxyOvrFilename != nullptr) |
1192 | 0 | { |
1193 | 0 | osOvrFilename = pszProxyOvrFilename; |
1194 | 0 | eErr = GTIFFBuildOverviews(osOvrFilename, nBands, pahBands, |
1195 | 0 | nNewOverviews, panNewOverviewList, |
1196 | 0 | pszResampling, GDALScaledProgress, |
1197 | 0 | pScaledProgress, papszOptions); |
1198 | 0 | } |
1199 | 0 | } |
1200 | |
|
1201 | 0 | if (eErr == CE_None) |
1202 | 0 | { |
1203 | 0 | poODS = GDALDataset::Open(osOvrFilename, |
1204 | 0 | GDAL_OF_RASTER | GDAL_OF_UPDATE); |
1205 | 0 | if (poODS == nullptr) |
1206 | 0 | eErr = CE_Failure; |
1207 | 0 | } |
1208 | | #else |
1209 | | CPLError(CE_Failure, CPLE_NotSupported, |
1210 | | "Cannot build TIFF overviews due to GeoTIFF driver missing"); |
1211 | | eErr = CE_Failure; |
1212 | | #endif |
1213 | 0 | } |
1214 | |
|
1215 | 0 | GDALDestroyScaledProgress(pScaledProgress); |
1216 | | |
1217 | | /* -------------------------------------------------------------------- */ |
1218 | | /* Refresh old overviews that were listed. */ |
1219 | | /* -------------------------------------------------------------------- */ |
1220 | 0 | GDALRasterBand **papoOverviewBands = |
1221 | 0 | static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nOverviews)); |
1222 | |
|
1223 | 0 | for (int iBand = 0; iBand < nBands && eErr == CE_None; iBand++) |
1224 | 0 | { |
1225 | 0 | poBand = poDS->GetRasterBand(panBandList[iBand]); |
1226 | 0 | if (poBand == nullptr) |
1227 | 0 | { |
1228 | 0 | eErr = CE_Failure; |
1229 | 0 | break; |
1230 | 0 | } |
1231 | | |
1232 | 0 | nNewOverviews = 0; |
1233 | 0 | std::vector<bool> abAlreadyUsedOverviewBand(poBand->GetOverviewCount(), |
1234 | 0 | false); |
1235 | |
|
1236 | 0 | for (int i = 0; i < nOverviews; i++) |
1237 | 0 | { |
1238 | 0 | if (!abValidLevel[i] || !abRequireRefresh[i]) |
1239 | 0 | continue; |
1240 | | |
1241 | 0 | for (int j = 0; j < poBand->GetOverviewCount(); j++) |
1242 | 0 | { |
1243 | 0 | if (abAlreadyUsedOverviewBand[j]) |
1244 | 0 | continue; |
1245 | | |
1246 | 0 | GDALRasterBand *poOverview = poBand->GetOverview(j); |
1247 | 0 | if (poOverview == nullptr) |
1248 | 0 | continue; |
1249 | | |
1250 | 0 | int bHasNoData = FALSE; |
1251 | 0 | double noDataValue = poBand->GetNoDataValue(&bHasNoData); |
1252 | |
|
1253 | 0 | if (bHasNoData) |
1254 | 0 | poOverview->SetNoDataValue(noDataValue); |
1255 | |
|
1256 | 0 | const int nOvFactor = GDALComputeOvFactor( |
1257 | 0 | poOverview->GetXSize(), poBand->GetXSize(), |
1258 | 0 | poOverview->GetYSize(), poBand->GetYSize()); |
1259 | |
|
1260 | 0 | if (nOvFactor == panOverviewList[i] || |
1261 | 0 | nOvFactor == GDALOvLevelAdjust2(panOverviewList[i], |
1262 | 0 | poBand->GetXSize(), |
1263 | 0 | poBand->GetYSize())) |
1264 | 0 | { |
1265 | 0 | abAlreadyUsedOverviewBand[j] = true; |
1266 | 0 | CPLAssert(nNewOverviews < poBand->GetOverviewCount()); |
1267 | 0 | papoOverviewBands[nNewOverviews++] = poOverview; |
1268 | 0 | break; |
1269 | 0 | } |
1270 | 0 | } |
1271 | 0 | } |
1272 | | |
1273 | 0 | if (nNewOverviews > 0) |
1274 | 0 | { |
1275 | 0 | const double dfOffset = |
1276 | 0 | dfAreaNewOverviews / AvoidZero(dfAreaRefreshedOverviews); |
1277 | 0 | const double dfScale = 1.0 - dfOffset; |
1278 | 0 | pScaledProgress = GDALCreateScaledProgress( |
1279 | 0 | dfOffset + dfScale * iBand / nBands, |
1280 | 0 | dfOffset + dfScale * (iBand + 1) / nBands, GDALScaledProgress, |
1281 | 0 | pScaledOverviewWithoutMask); |
1282 | 0 | eErr = GDALRegenerateOverviewsEx( |
1283 | 0 | GDALRasterBand::ToHandle(poBand), nNewOverviews, |
1284 | 0 | reinterpret_cast<GDALRasterBandH *>(papoOverviewBands), |
1285 | 0 | pszResampling, GDALScaledProgress, pScaledProgress, |
1286 | 0 | papszOptions); |
1287 | 0 | GDALDestroyScaledProgress(pScaledProgress); |
1288 | 0 | } |
1289 | 0 | } |
1290 | | |
1291 | | /* -------------------------------------------------------------------- */ |
1292 | | /* Cleanup */ |
1293 | | /* -------------------------------------------------------------------- */ |
1294 | 0 | CPLFree(papoOverviewBands); |
1295 | 0 | CPLFree(panNewOverviewList); |
1296 | 0 | CPLFree(pahBands); |
1297 | 0 | GDALDestroyScaledProgress(pScaledOverviewWithoutMask); |
1298 | | |
1299 | | /* -------------------------------------------------------------------- */ |
1300 | | /* If we have a mask file, we need to build its overviews too. */ |
1301 | | /* -------------------------------------------------------------------- */ |
1302 | 0 | if (HaveMaskFile() && eErr == CE_None) |
1303 | 0 | { |
1304 | 0 | pScaledProgress = GDALCreateScaledProgress( |
1305 | 0 | double(nBands) / (nBands + 1), 1.0, pfnProgress, pProgressData); |
1306 | 0 | eErr = BuildOverviewsMask(pszResampling, nOverviews, panOverviewList, |
1307 | 0 | GDALScaledProgress, pScaledProgress, |
1308 | 0 | papszOptions); |
1309 | 0 | GDALDestroyScaledProgress(pScaledProgress); |
1310 | 0 | } |
1311 | | |
1312 | | /* -------------------------------------------------------------------- */ |
1313 | | /* If we have an overview dataset, then mark all the overviews */ |
1314 | | /* with the base dataset Used later for finding overviews */ |
1315 | | /* masks. Uggg. */ |
1316 | | /* -------------------------------------------------------------------- */ |
1317 | 0 | if (poODS) |
1318 | 0 | { |
1319 | 0 | const int nOverviewCount = GetOverviewCount(1); |
1320 | |
|
1321 | 0 | for (int iOver = 0; iOver < nOverviewCount; iOver++) |
1322 | 0 | { |
1323 | 0 | GDALRasterBand *poOtherBand = GetOverview(1, iOver); |
1324 | 0 | GDALDataset *poOverDS = |
1325 | 0 | poOtherBand != nullptr ? poOtherBand->GetDataset() : nullptr; |
1326 | |
|
1327 | 0 | if (poOverDS != nullptr) |
1328 | 0 | { |
1329 | 0 | poOverDS->oOvManager.poBaseDS = poDS; |
1330 | 0 | poOverDS->oOvManager.poDS = poOverDS; |
1331 | 0 | } |
1332 | 0 | } |
1333 | 0 | } |
1334 | |
|
1335 | 0 | return eErr; |
1336 | 0 | } |
1337 | | |
1338 | | /************************************************************************/ |
1339 | | /* BuildOverviewsMask() */ |
1340 | | /************************************************************************/ |
1341 | | |
1342 | | CPLErr GDALDefaultOverviews::BuildOverviewsMask(const char *pszResampling, |
1343 | | int nOverviews, |
1344 | | const int *panOverviewList, |
1345 | | GDALProgressFunc pfnProgress, |
1346 | | void *pProgressData, |
1347 | | CSLConstList papszOptions) |
1348 | 0 | { |
1349 | 0 | CPLErr eErr = CE_None; |
1350 | 0 | if (HaveMaskFile() && poMaskDS) |
1351 | 0 | { |
1352 | | // Some options are not compatible with mask overviews |
1353 | | // so unset them, and define more sensible values. |
1354 | 0 | CPLStringList aosMaskOptions(papszOptions); |
1355 | 0 | const char *pszCompress = |
1356 | 0 | GetOptionValue(papszOptions, "COMPRESS", "COMPRESS_OVERVIEW"); |
1357 | 0 | const bool bJPEG = pszCompress && EQUAL(pszCompress, "JPEG"); |
1358 | 0 | const char *pszPhotometric = |
1359 | 0 | GetOptionValue(papszOptions, "PHOTOMETRIC", "PHOTOMETRIC_OVERVIEW"); |
1360 | 0 | const bool bPHOTOMETRIC_YCBCR = |
1361 | 0 | pszPhotometric && EQUAL(pszPhotometric, "YCBCR"); |
1362 | 0 | if (bJPEG) |
1363 | 0 | aosMaskOptions.SetNameValue("COMPRESS", "DEFLATE"); |
1364 | 0 | if (bPHOTOMETRIC_YCBCR) |
1365 | 0 | aosMaskOptions.SetNameValue("PHOTOMETRIC", "MINISBLACK"); |
1366 | |
|
1367 | 0 | eErr = poMaskDS->BuildOverviews( |
1368 | 0 | pszResampling, nOverviews, panOverviewList, 0, nullptr, pfnProgress, |
1369 | 0 | pProgressData, aosMaskOptions.List()); |
1370 | |
|
1371 | 0 | if (bOwnMaskDS) |
1372 | 0 | { |
1373 | | // Reset the poMask member of main dataset bands, since it |
1374 | | // will become invalid after poMaskDS closing. |
1375 | 0 | for (int iBand = 1; iBand <= poDS->GetRasterCount(); iBand++) |
1376 | 0 | { |
1377 | 0 | GDALRasterBand *poOtherBand = poDS->GetRasterBand(iBand); |
1378 | 0 | if (poOtherBand != nullptr) |
1379 | 0 | poOtherBand->InvalidateMaskBand(); |
1380 | 0 | } |
1381 | |
|
1382 | 0 | GDALClose(poMaskDS); |
1383 | 0 | } |
1384 | | |
1385 | | // force next request to reread mask file. |
1386 | 0 | poMaskDS = nullptr; |
1387 | 0 | bOwnMaskDS = false; |
1388 | 0 | bCheckedForMask = false; |
1389 | 0 | } |
1390 | |
|
1391 | 0 | return eErr; |
1392 | 0 | } |
1393 | | |
1394 | | /************************************************************************/ |
1395 | | /* CreateMaskBand() */ |
1396 | | /************************************************************************/ |
1397 | | |
1398 | | CPLErr GDALDefaultOverviews::CreateMaskBand(int nFlags, int nBand) |
1399 | | |
1400 | 0 | { |
1401 | 0 | if (nBand < 1) |
1402 | 0 | nFlags |= GMF_PER_DATASET; |
1403 | | |
1404 | | /* -------------------------------------------------------------------- */ |
1405 | | /* ensure existing file gets opened if there is one. */ |
1406 | | /* -------------------------------------------------------------------- */ |
1407 | 0 | CPL_IGNORE_RET_VAL(HaveMaskFile()); |
1408 | | |
1409 | | /* -------------------------------------------------------------------- */ |
1410 | | /* Try creating the mask file. */ |
1411 | | /* -------------------------------------------------------------------- */ |
1412 | 0 | if (poMaskDS == nullptr) |
1413 | 0 | { |
1414 | 0 | GDALDriver *const poDr = |
1415 | 0 | static_cast<GDALDriver *>(GDALGetDriverByName("GTiff")); |
1416 | |
|
1417 | 0 | if (poDr == nullptr) |
1418 | 0 | return CE_Failure; |
1419 | | |
1420 | 0 | GDALRasterBand *const poTBand = poDS->GetRasterBand(1); |
1421 | 0 | if (poTBand == nullptr) |
1422 | 0 | return CE_Failure; |
1423 | | |
1424 | 0 | const int nBands = |
1425 | 0 | (nFlags & GMF_PER_DATASET) ? 1 : poDS->GetRasterCount(); |
1426 | |
|
1427 | 0 | char **papszOpt = CSLSetNameValue(nullptr, "COMPRESS", "DEFLATE"); |
1428 | 0 | papszOpt = CSLSetNameValue(papszOpt, "INTERLEAVE", "BAND"); |
1429 | |
|
1430 | 0 | int nBX = 0; |
1431 | 0 | int nBY = 0; |
1432 | 0 | poTBand->GetBlockSize(&nBX, &nBY); |
1433 | | |
1434 | | // Try to create matching tile size if legal in TIFF. |
1435 | 0 | if ((nBX % 16) == 0 && (nBY % 16) == 0) |
1436 | 0 | { |
1437 | 0 | papszOpt = CSLSetNameValue(papszOpt, "TILED", "YES"); |
1438 | 0 | papszOpt = CSLSetNameValue(papszOpt, "BLOCKXSIZE", |
1439 | 0 | CPLString().Printf("%d", nBX)); |
1440 | 0 | papszOpt = CSLSetNameValue(papszOpt, "BLOCKYSIZE", |
1441 | 0 | CPLString().Printf("%d", nBY)); |
1442 | 0 | } |
1443 | |
|
1444 | 0 | CPLString osMskFilename; |
1445 | 0 | osMskFilename.Printf("%s.msk", poDS->GetDescription()); |
1446 | 0 | poMaskDS = |
1447 | 0 | poDr->Create(osMskFilename, poDS->GetRasterXSize(), |
1448 | 0 | poDS->GetRasterYSize(), nBands, GDT_UInt8, papszOpt); |
1449 | 0 | CSLDestroy(papszOpt); |
1450 | |
|
1451 | 0 | if (poMaskDS == nullptr) // Presumably error already issued. |
1452 | 0 | return CE_Failure; |
1453 | | |
1454 | 0 | bOwnMaskDS = true; |
1455 | 0 | } |
1456 | | |
1457 | | /* -------------------------------------------------------------------- */ |
1458 | | /* Save the mask flags for this band. */ |
1459 | | /* -------------------------------------------------------------------- */ |
1460 | 0 | if (nBand > poMaskDS->GetRasterCount()) |
1461 | 0 | { |
1462 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1463 | 0 | "Attempt to create a mask band for band %d of %s, " |
1464 | 0 | "but the .msk file has a PER_DATASET mask.", |
1465 | 0 | nBand, poDS->GetDescription()); |
1466 | 0 | return CE_Failure; |
1467 | 0 | } |
1468 | | |
1469 | 0 | for (int iBand = 0; iBand < poDS->GetRasterCount(); iBand++) |
1470 | 0 | { |
1471 | | // we write only the info for this band, unless we are |
1472 | | // using PER_DATASET in which case we write for all. |
1473 | 0 | if (nBand != iBand + 1 && !(nFlags & GMF_PER_DATASET)) |
1474 | 0 | continue; |
1475 | | |
1476 | 0 | poMaskDS->SetMetadataItem( |
1477 | 0 | CPLString().Printf("INTERNAL_MASK_FLAGS_%d", iBand + 1), |
1478 | 0 | CPLString().Printf("%d", nFlags)); |
1479 | 0 | } |
1480 | |
|
1481 | 0 | return CE_None; |
1482 | 0 | } |
1483 | | |
1484 | | /************************************************************************/ |
1485 | | /* GetMaskBand() */ |
1486 | | /************************************************************************/ |
1487 | | |
1488 | | // Secret code meaning we don't handle this band. |
1489 | | constexpr int MISSING_FLAGS = 0x8000; |
1490 | | |
1491 | | GDALRasterBand *GDALDefaultOverviews::GetMaskBand(int nBand) |
1492 | | |
1493 | 0 | { |
1494 | 0 | const int nFlags = GetMaskFlags(nBand); |
1495 | |
|
1496 | 0 | if (poMaskDS == nullptr || nFlags == MISSING_FLAGS) |
1497 | 0 | return nullptr; |
1498 | | |
1499 | 0 | if (nFlags & GMF_PER_DATASET) |
1500 | 0 | return poMaskDS->GetRasterBand(1); |
1501 | | |
1502 | 0 | if (nBand > 0) |
1503 | 0 | return poMaskDS->GetRasterBand(nBand); |
1504 | | |
1505 | 0 | return nullptr; |
1506 | 0 | } |
1507 | | |
1508 | | /************************************************************************/ |
1509 | | /* GetMaskFlags() */ |
1510 | | /************************************************************************/ |
1511 | | |
1512 | | int GDALDefaultOverviews::GetMaskFlags(int nBand) |
1513 | | |
1514 | 0 | { |
1515 | | /* -------------------------------------------------------------------- */ |
1516 | | /* Fetch this band's metadata entry. They are of the form: */ |
1517 | | /* INTERNAL_MASK_FLAGS_n: flags */ |
1518 | | /* -------------------------------------------------------------------- */ |
1519 | 0 | if (!HaveMaskFile()) |
1520 | 0 | return 0; |
1521 | | |
1522 | 0 | const char *pszValue = poMaskDS->GetMetadataItem( |
1523 | 0 | CPLString().Printf("INTERNAL_MASK_FLAGS_%d", std::max(nBand, 1))); |
1524 | |
|
1525 | 0 | if (pszValue == nullptr) |
1526 | 0 | return MISSING_FLAGS; |
1527 | | |
1528 | 0 | return atoi(pszValue); |
1529 | 0 | } |
1530 | | |
1531 | | /************************************************************************/ |
1532 | | /* HaveMaskFile() */ |
1533 | | /* */ |
1534 | | /* Check for a mask file if we haven't already done so. */ |
1535 | | /* Returns TRUE if we have one, otherwise FALSE. */ |
1536 | | /************************************************************************/ |
1537 | | |
1538 | | int GDALDefaultOverviews::HaveMaskFile(char **papszSiblingFiles, |
1539 | | const char *pszBasename) |
1540 | | |
1541 | 0 | { |
1542 | | /* -------------------------------------------------------------------- */ |
1543 | | /* Have we already checked for masks? */ |
1544 | | /* -------------------------------------------------------------------- */ |
1545 | 0 | if (bCheckedForMask) |
1546 | 0 | return poMaskDS != nullptr; |
1547 | | |
1548 | 0 | if (papszSiblingFiles == nullptr) |
1549 | 0 | papszSiblingFiles = papszInitSiblingFiles; |
1550 | | |
1551 | | /* -------------------------------------------------------------------- */ |
1552 | | /* Are we an overview? If so we need to find the corresponding */ |
1553 | | /* overview in the base files mask file (if there is one). */ |
1554 | | /* -------------------------------------------------------------------- */ |
1555 | 0 | if (poBaseDS != nullptr && poBaseDS->oOvManager.HaveMaskFile()) |
1556 | 0 | { |
1557 | 0 | GDALRasterBand *const poBaseBand = poBaseDS->GetRasterBand(1); |
1558 | 0 | GDALDataset *poMaskDSTemp = nullptr; |
1559 | 0 | if (poBaseBand != nullptr) |
1560 | 0 | { |
1561 | 0 | GDALRasterBand *poBaseMask = poBaseBand->GetMaskBand(); |
1562 | 0 | if (poBaseMask != nullptr) |
1563 | 0 | { |
1564 | 0 | const int nOverviewCount = poBaseMask->GetOverviewCount(); |
1565 | 0 | for (int iOver = 0; iOver < nOverviewCount; iOver++) |
1566 | 0 | { |
1567 | 0 | GDALRasterBand *const poOverBand = |
1568 | 0 | poBaseMask->GetOverview(iOver); |
1569 | 0 | if (poOverBand == nullptr) |
1570 | 0 | continue; |
1571 | | |
1572 | 0 | if (poOverBand->GetXSize() == poDS->GetRasterXSize() && |
1573 | 0 | poOverBand->GetYSize() == poDS->GetRasterYSize()) |
1574 | 0 | { |
1575 | 0 | poMaskDSTemp = poOverBand->GetDataset(); |
1576 | 0 | break; |
1577 | 0 | } |
1578 | 0 | } |
1579 | 0 | } |
1580 | 0 | } |
1581 | |
|
1582 | 0 | if (poMaskDSTemp != poDS) |
1583 | 0 | { |
1584 | 0 | poMaskDS = poMaskDSTemp; |
1585 | 0 | bCheckedForMask = true; |
1586 | 0 | bOwnMaskDS = false; |
1587 | |
|
1588 | 0 | return poMaskDS != nullptr; |
1589 | 0 | } |
1590 | 0 | } |
1591 | | |
1592 | | /* -------------------------------------------------------------------- */ |
1593 | | /* Are we even initialized? If not, we apparently don't want */ |
1594 | | /* to support overviews and masks. */ |
1595 | | /* -------------------------------------------------------------------- */ |
1596 | 0 | if (poDS == nullptr) |
1597 | 0 | return FALSE; |
1598 | | |
1599 | | /* -------------------------------------------------------------------- */ |
1600 | | /* Check for .msk file. */ |
1601 | | /* -------------------------------------------------------------------- */ |
1602 | 0 | bCheckedForMask = true; |
1603 | |
|
1604 | 0 | if (pszBasename == nullptr) |
1605 | 0 | pszBasename = poDS->GetDescription(); |
1606 | | |
1607 | | // Don't bother checking for masks of masks. |
1608 | 0 | if (EQUAL(CPLGetExtensionSafe(pszBasename).c_str(), "msk")) |
1609 | 0 | return FALSE; |
1610 | | |
1611 | 0 | if (!GDALCanFileAcceptSidecarFile(pszBasename)) |
1612 | 0 | return FALSE; |
1613 | 0 | CPLString osMskFilename; |
1614 | 0 | osMskFilename.Printf("%s.msk", pszBasename); |
1615 | |
|
1616 | 0 | std::vector<char> achMskFilename; |
1617 | 0 | achMskFilename.resize(osMskFilename.size() + 1); |
1618 | 0 | memcpy(&(achMskFilename[0]), osMskFilename.c_str(), |
1619 | 0 | osMskFilename.size() + 1); |
1620 | 0 | bool bExists = |
1621 | 0 | CPL_TO_BOOL(CPLCheckForFile(&achMskFilename[0], papszSiblingFiles)); |
1622 | 0 | osMskFilename = &achMskFilename[0]; |
1623 | |
|
1624 | 0 | #if !defined(_WIN32) |
1625 | 0 | if (!bExists && !papszSiblingFiles) |
1626 | 0 | { |
1627 | 0 | osMskFilename.Printf("%s.MSK", pszBasename); |
1628 | 0 | memcpy(&(achMskFilename[0]), osMskFilename.c_str(), |
1629 | 0 | osMskFilename.size() + 1); |
1630 | 0 | bExists = |
1631 | 0 | CPL_TO_BOOL(CPLCheckForFile(&achMskFilename[0], papszSiblingFiles)); |
1632 | 0 | osMskFilename = &achMskFilename[0]; |
1633 | 0 | } |
1634 | 0 | #endif |
1635 | |
|
1636 | 0 | if (!bExists) |
1637 | 0 | return FALSE; |
1638 | | |
1639 | | /* -------------------------------------------------------------------- */ |
1640 | | /* Open the file. */ |
1641 | | /* -------------------------------------------------------------------- */ |
1642 | 0 | poMaskDS = GDALDataset::Open( |
1643 | 0 | osMskFilename, |
1644 | 0 | GDAL_OF_RASTER | (poDS->GetAccess() == GA_Update ? GDAL_OF_UPDATE : 0), |
1645 | 0 | nullptr, nullptr, papszInitSiblingFiles); |
1646 | 0 | CPLAssert(poMaskDS != poDS); |
1647 | | |
1648 | 0 | if (poMaskDS == nullptr) |
1649 | 0 | return FALSE; |
1650 | | |
1651 | 0 | bOwnMaskDS = true; |
1652 | |
|
1653 | 0 | return TRUE; |
1654 | 0 | } |
1655 | | |
1656 | | /************************************************************************/ |
1657 | | /* GDALGetNormalizedOvrResampling() */ |
1658 | | /************************************************************************/ |
1659 | | |
1660 | | std::string GDALGetNormalizedOvrResampling(const char *pszResampling) |
1661 | 0 | { |
1662 | 0 | if (pszResampling && |
1663 | 0 | EQUAL(pszResampling, "AVERAGE_BIT2GRAYSCALE_MINISWHITE")) |
1664 | 0 | return "AVERAGE_BIT2GRAYSCALE_MINISWHITE"; |
1665 | 0 | else if (pszResampling && STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2")) |
1666 | 0 | return "AVERAGE_BIT2GRAYSCALE"; |
1667 | 0 | else if (pszResampling && STARTS_WITH_CI(pszResampling, "NEAR")) |
1668 | 0 | return "NEAREST"; |
1669 | 0 | else if (pszResampling && EQUAL(pszResampling, "AVERAGE_MAGPHASE")) |
1670 | 0 | return "AVERAGE_MAGPHASE"; |
1671 | 0 | else if (pszResampling && STARTS_WITH_CI(pszResampling, "AVER")) |
1672 | 0 | return "AVERAGE"; |
1673 | 0 | else if (pszResampling && !EQUAL(pszResampling, "NONE")) |
1674 | 0 | { |
1675 | 0 | return CPLString(pszResampling).toupper(); |
1676 | 0 | } |
1677 | 0 | return std::string(); |
1678 | 0 | } |
1679 | | |
1680 | | //! @endcond |