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