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