• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15899162844

26 Jun 2025 10:14AM UTC coverage: 71.088% (+0.004%) from 71.084%
15899162844

Pull #12623

github

web-flow
Merge c704a8392 into f5cb024d4
Pull Request #12623: gdal raster overview add: add a --overview-src option

209 of 244 new or added lines in 5 files covered. (85.66%)

96 existing lines in 44 files now uncovered.

574014 of 807474 relevant lines covered (71.09%)

250815.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.41
/gcore/gdaldefaultoverviews.cpp
1
/******************************************************************************
2
 *
3
 * Project:  GDAL Core
4
 * Purpose:  Helper code to implement overview and mask support for many
5
 *           drivers with no inherent format support.
6
 * Author:   Frank Warmerdam, warmerdam@pobox.com
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2000, 2007, Frank Warmerdam
10
 * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14

15
#include "cpl_port.h"
16
#include "cpl_multiproc.h"
17
#include "gdal_priv.h"
18

19
#include <cstdlib>
20
#include <cstring>
21

22
#include <algorithm>
23
#include <limits>
24
#include <set>
25
#include <string>
26
#include <vector>
27

28
#include "cpl_conv.h"
29
#include "cpl_error.h"
30
#include "cpl_progress.h"
31
#include "cpl_string.h"
32
#include "cpl_vsi.h"
33
#include "gdal.h"
34

35
//! @cond Doxygen_Suppress
36
/************************************************************************/
37
/*                        GDALDefaultOverviews()                        */
38
/************************************************************************/
39

40
GDALDefaultOverviews::GDALDefaultOverviews()
146,351✔
41
    : poDS(nullptr), poODS(nullptr), bOvrIsAux(false), bCheckedForMask(false),
42
      bOwnMaskDS(false), poMaskDS(nullptr), poBaseDS(nullptr),
43
      bCheckedForOverviews(FALSE), pszInitName(nullptr), bInitNameIsOVR(false),
44
      papszInitSiblingFiles(nullptr)
146,351✔
45
{
46
}
146,345✔
47

48
/************************************************************************/
49
/*                       ~GDALDefaultOverviews()                        */
50
/************************************************************************/
51

52
GDALDefaultOverviews::~GDALDefaultOverviews()
146,312✔
53

54
{
55
    CPLFree(pszInitName);
146,313✔
56
    CSLDestroy(papszInitSiblingFiles);
146,312✔
57

58
    CloseDependentDatasets();
146,313✔
59
}
146,312✔
60

61
/************************************************************************/
62
/*                       CloseDependentDatasets()                       */
63
/************************************************************************/
64

65
int GDALDefaultOverviews::CloseDependentDatasets()
165,179✔
66
{
67
    bool bHasDroppedRef = false;
165,179✔
68
    if (poODS != nullptr)
165,179✔
69
    {
70
        bHasDroppedRef = true;
395✔
71
        poODS->FlushCache(true);
395✔
72
        GDALClose(poODS);
395✔
73
        poODS = nullptr;
395✔
74
    }
75

76
    if (poMaskDS != nullptr)
165,179✔
77
    {
78
        if (bOwnMaskDS)
57✔
79
        {
80
            bHasDroppedRef = true;
53✔
81
            poMaskDS->FlushCache(true);
53✔
82
            GDALClose(poMaskDS);
53✔
83
        }
84
        poMaskDS = nullptr;
57✔
85
    }
86

87
    return bHasDroppedRef;
165,179✔
88
}
89

90
/************************************************************************/
91
/*                           IsInitialized()                            */
92
/*                                                                      */
93
/*      Returns TRUE if we are initialized.                             */
94
/************************************************************************/
95

96
int GDALDefaultOverviews::IsInitialized()
1,071,270✔
97

98
{
99
    OverviewScan();
1,071,270✔
100
    return poDS != nullptr;
1,071,270✔
101
}
102

103
/************************************************************************/
104
/*                             Initialize()                             */
105
/************************************************************************/
106

107
void GDALDefaultOverviews::Initialize(GDALDataset *poDSIn,
51,876✔
108
                                      const char *pszBasename,
109
                                      CSLConstList papszSiblingFiles,
110
                                      bool bNameIsOVR)
111

112
{
113
    poDS = poDSIn;
51,876✔
114

115
    /* -------------------------------------------------------------------- */
116
    /*      If we were already initialized, destroy the old overview        */
117
    /*      file handle.                                                    */
118
    /* -------------------------------------------------------------------- */
119
    if (poODS != nullptr)
51,876✔
120
    {
121
        GDALClose(poODS);
×
122
        poODS = nullptr;
×
123

124
        CPLDebug("GDAL", "GDALDefaultOverviews::Initialize() called twice - "
×
125
                         "this is odd and perhaps dangerous!");
126
    }
127

128
    /* -------------------------------------------------------------------- */
129
    /*      Store the initialization information for later use in           */
130
    /*      OverviewScan()                                                  */
131
    /* -------------------------------------------------------------------- */
132
    bCheckedForOverviews = FALSE;
51,876✔
133

134
    CPLFree(pszInitName);
51,876✔
135
    pszInitName = nullptr;
51,874✔
136
    if (pszBasename != nullptr)
51,874✔
137
        pszInitName = CPLStrdup(pszBasename);
51,681✔
138
    bInitNameIsOVR = bNameIsOVR;
51,895✔
139

140
    CSLDestroy(papszInitSiblingFiles);
51,895✔
141
    papszInitSiblingFiles = nullptr;
51,881✔
142
    if (papszSiblingFiles != nullptr)
51,881✔
143
        papszInitSiblingFiles = CSLDuplicate(papszSiblingFiles);
5,649✔
144
}
51,881✔
145

146
/************************************************************************/
147
/*                             Initialize()                             */
148
/************************************************************************/
149

150
/** Initialize the GDALDefaultOverviews instance.
151
 *
152
 * @param poDSIn Base dataset.
153
 * @param poOpenInfo Open info instance. Must not be NULL.
154
 * @param pszName Base dataset name. If set to NULL, poOpenInfo->pszFilename is
155
 *                used.
156
 * @param bTransferSiblingFilesIfLoaded Whether sibling files of poOpenInfo
157
 *                                      should be transferred to this
158
 *                                      GDALDefaultOverviews instance, if they
159
 *                                      have bean already loaded.
160
 * @since 3.10
161
 */
162
void GDALDefaultOverviews::Initialize(GDALDataset *poDSIn,
32,257✔
163
                                      GDALOpenInfo *poOpenInfo,
164
                                      const char *pszName,
165
                                      bool bTransferSiblingFilesIfLoaded)
166
{
167
    Initialize(poDSIn, pszName ? pszName : poOpenInfo->pszFilename);
32,257✔
168

169
    if (bTransferSiblingFilesIfLoaded && poOpenInfo->AreSiblingFilesLoaded())
32,248✔
170
        TransferSiblingFiles(poOpenInfo->StealSiblingFiles());
9,150✔
171
}
32,248✔
172

173
/************************************************************************/
174
/*                         TransferSiblingFiles()                       */
175
/*                                                                      */
176
/*      Contrary to Initialize(), this sets papszInitSiblingFiles but   */
177
/*      without duplicating the passed list. Which must be              */
178
/*      "de-allocatable" with CSLDestroy()                              */
179
/************************************************************************/
180

181
void GDALDefaultOverviews::TransferSiblingFiles(char **papszSiblingFiles)
25,911✔
182
{
183
    CSLDestroy(papszInitSiblingFiles);
25,911✔
184
    papszInitSiblingFiles = papszSiblingFiles;
25,910✔
185
}
25,910✔
186

187
namespace
188
{
189
// Prevent infinite recursion.
190
struct AntiRecursionStructDefaultOvr
191
{
192
    int nRecLevel = 0;
193
    std::set<CPLString> oSetFiles{};
194
};
195
}  // namespace
196

197
static void FreeAntiRecursionDefaultOvr(void *pData)
349✔
198
{
199
    delete static_cast<AntiRecursionStructDefaultOvr *>(pData);
349✔
200
}
349✔
201

202
static AntiRecursionStructDefaultOvr &GetAntiRecursionDefaultOvr()
11,639✔
203
{
204
    static AntiRecursionStructDefaultOvr dummy;
11,639✔
205
    int bMemoryErrorOccurred = false;
11,639✔
206
    void *pData =
207
        CPLGetTLSEx(CTLS_GDALDEFAULTOVR_ANTIREC, &bMemoryErrorOccurred);
11,639✔
208
    if (bMemoryErrorOccurred)
11,639✔
209
    {
210
        return dummy;
×
211
    }
212
    if (pData == nullptr)
11,639✔
213
    {
214
        auto pAntiRecursion = new AntiRecursionStructDefaultOvr();
424✔
215
        CPLSetTLSWithFreeFuncEx(CTLS_GDALDEFAULTOVR_ANTIREC, pAntiRecursion,
424✔
216
                                FreeAntiRecursionDefaultOvr,
217
                                &bMemoryErrorOccurred);
218
        if (bMemoryErrorOccurred)
424✔
219
        {
220
            delete pAntiRecursion;
×
221
            return dummy;
×
222
        }
223
        return *pAntiRecursion;
424✔
224
    }
225
    return *static_cast<AntiRecursionStructDefaultOvr *>(pData);
11,215✔
226
}
227

228
/************************************************************************/
229
/*                            OverviewScan()                            */
230
/*                                                                      */
231
/*      This is called to scan for overview files when a first          */
232
/*      request is made with regard to overviews.  It uses the          */
233
/*      pszInitName, bInitNameIsOVR and papszInitSiblingFiles           */
234
/*      information that was stored at Initialization() time.           */
235
/************************************************************************/
236

237
void GDALDefaultOverviews::OverviewScan()
1,071,270✔
238

239
{
240
    if (bCheckedForOverviews || poDS == nullptr)
1,071,270✔
241
        return;
1,059,630✔
242

243
    bCheckedForOverviews = true;
11,639✔
244
    if (pszInitName == nullptr)
11,639✔
245
        pszInitName = CPLStrdup(poDS->GetDescription());
14✔
246

247
    AntiRecursionStructDefaultOvr &antiRec = GetAntiRecursionDefaultOvr();
11,639✔
248
    // 32 should be enough to handle a .ovr.ovr.ovr...
249
    if (antiRec.nRecLevel == 32)
11,639✔
250
        return;
×
251
    if (antiRec.oSetFiles.find(pszInitName) != antiRec.oSetFiles.end())
11,639✔
252
        return;
×
253
    antiRec.oSetFiles.insert(pszInitName);
11,639✔
254
    ++antiRec.nRecLevel;
11,639✔
255

256
    CPLDebug("GDAL", "GDALDefaultOverviews::OverviewScan()");
11,639✔
257

258
    /* -------------------------------------------------------------------- */
259
    /*      Open overview dataset if it exists.                             */
260
    /* -------------------------------------------------------------------- */
261
    if (!EQUAL(pszInitName, ":::VIRTUAL:::") &&
23,098✔
262
        GDALCanFileAcceptSidecarFile(pszInitName))
11,459✔
263
    {
264
        if (bInitNameIsOVR)
11,457✔
265
            osOvrFilename = pszInitName;
×
266
        else
267
            osOvrFilename.Printf("%s.ovr", pszInitName);
11,457✔
268

269
        std::vector<char> achOvrFilename;
22,914✔
270
        achOvrFilename.resize(osOvrFilename.size() + 1);
11,457✔
271
        memcpy(&(achOvrFilename[0]), osOvrFilename.c_str(),
11,457✔
272
               osOvrFilename.size() + 1);
11,457✔
273
        bool bExists = CPL_TO_BOOL(
11,457✔
274
            CPLCheckForFile(&achOvrFilename[0], papszInitSiblingFiles));
11,457✔
275
        osOvrFilename = &achOvrFilename[0];
11,457✔
276

277
#if !defined(_WIN32)
278
        if (!bInitNameIsOVR && !bExists && !papszInitSiblingFiles)
11,457✔
279
        {
280
            osOvrFilename.Printf("%s.OVR", pszInitName);
2,892✔
281
            memcpy(&(achOvrFilename[0]), osOvrFilename.c_str(),
2,892✔
282
                   osOvrFilename.size() + 1);
2,892✔
283
            bExists = CPL_TO_BOOL(
2,892✔
284
                CPLCheckForFile(&achOvrFilename[0], papszInitSiblingFiles));
2,892✔
285
            osOvrFilename = &achOvrFilename[0];
2,892✔
286
            if (!bExists)
2,892✔
287
                osOvrFilename.Printf("%s.ovr", pszInitName);
2,892✔
288
        }
289
#endif
290

291
        if (bExists)
11,457✔
292
        {
293
            poODS = GDALDataset::Open(
201✔
294
                osOvrFilename,
295
                GDAL_OF_RASTER |
296
                    (poDS->GetAccess() == GA_Update ? GDAL_OF_UPDATE : 0),
201✔
297
                nullptr, nullptr, papszInitSiblingFiles);
201✔
298
        }
299
    }
300

301
    /* -------------------------------------------------------------------- */
302
    /*      We didn't find that, so try and find a corresponding aux        */
303
    /*      file.  Check that we are the dependent file of the aux          */
304
    /*      file.                                                           */
305
    /*                                                                      */
306
    /*      We only use the .aux file for overviews if they already have    */
307
    /*      overviews existing, or if USE_RRD is set true.                  */
308
    /* -------------------------------------------------------------------- */
309
    if (!poODS && !EQUAL(pszInitName, ":::VIRTUAL:::") &&
22,898✔
310
        GDALCanFileAcceptSidecarFile(pszInitName))
11,259✔
311
    {
312
        bool bTryFindAssociatedAuxFile = true;
11,257✔
313
        if (papszInitSiblingFiles)
11,257✔
314
        {
315
            CPLString osAuxFilename = CPLResetExtensionSafe(pszInitName, "aux");
16,730✔
316
            int iSibling = CSLFindString(papszInitSiblingFiles,
8,365✔
317
                                         CPLGetFilename(osAuxFilename));
318
            if (iSibling < 0)
8,365✔
319
            {
320
                osAuxFilename = pszInitName;
8,361✔
321
                osAuxFilename += ".aux";
8,361✔
322
                iSibling = CSLFindString(papszInitSiblingFiles,
8,361✔
323
                                         CPLGetFilename(osAuxFilename));
324
                if (iSibling < 0)
8,361✔
325
                    bTryFindAssociatedAuxFile = false;
8,361✔
326
            }
327
        }
328

329
        if (bTryFindAssociatedAuxFile)
11,257✔
330
        {
331
            poODS =
2,896✔
332
                GDALFindAssociatedAuxFile(pszInitName, poDS->GetAccess(), poDS);
2,896✔
333
        }
334

335
        if (poODS)
11,257✔
336
        {
337
            const bool bUseRRD =
338
                CPLTestBool(CPLGetConfigOption("USE_RRD", "NO"));
7✔
339

340
            bOvrIsAux = true;
7✔
341
            if (GetOverviewCount(1) == 0 && !bUseRRD)
7✔
342
            {
343
                bOvrIsAux = false;
1✔
344
                GDALClose(poODS);
1✔
345
                poODS = nullptr;
1✔
346
            }
347
            else
348
            {
349
                osOvrFilename = poODS->GetDescription();
6✔
350
            }
351
        }
352
    }
353

354
    /* -------------------------------------------------------------------- */
355
    /*      If we still don't have an overview, check to see if we have     */
356
    /*      overview metadata referencing a remote (i.e. proxy) or local    */
357
    /*      subdataset overview dataset.                                    */
358
    /* -------------------------------------------------------------------- */
359
    if (poODS == nullptr)
11,639✔
360
    {
361
        const char *pszProxyOvrFilename =
362
            poDS->GetMetadataItem("OVERVIEW_FILE", "OVERVIEWS");
11,433✔
363

364
        if (pszProxyOvrFilename != nullptr)
11,433✔
365
        {
366
            if (STARTS_WITH_CI(pszProxyOvrFilename, ":::BASE:::"))
33✔
367
            {
368
                const CPLString osPath = CPLGetPathSafe(poDS->GetDescription());
×
369

370
                osOvrFilename = CPLFormFilenameSafe(
×
371
                    osPath, pszProxyOvrFilename + 10, nullptr);
×
372
            }
373
            else
374
            {
375
                osOvrFilename = pszProxyOvrFilename;
33✔
376
            }
377

378
            CPLPushErrorHandler(CPLQuietErrorHandler);
33✔
379
            poODS = GDALDataset::Open(
33✔
380
                osOvrFilename,
381
                GDAL_OF_RASTER |
382
                    (poDS->GetAccess() == GA_Update ? GDAL_OF_UPDATE : 0));
33✔
383
            CPLPopErrorHandler();
33✔
384
        }
385
    }
386

387
    /* -------------------------------------------------------------------- */
388
    /*      If we have an overview dataset, then mark all the overviews     */
389
    /*      with the base dataset  Used later for finding overviews         */
390
    /*      masks.  Uggg.                                                   */
391
    /* -------------------------------------------------------------------- */
392
    if (poODS)
11,639✔
393
    {
394
        const int nOverviewCount = GetOverviewCount(1);
224✔
395

396
        for (int iOver = 0; iOver < nOverviewCount; iOver++)
534✔
397
        {
398
            GDALRasterBand *const poBand = GetOverview(1, iOver);
310✔
399
            GDALDataset *const poOverDS =
400
                poBand != nullptr ? poBand->GetDataset() : nullptr;
310✔
401

402
            if (poOverDS != nullptr)
310✔
403
            {
404
                poOverDS->oOvManager.poBaseDS = poDS;
299✔
405
                poOverDS->oOvManager.poDS = poOverDS;
299✔
406
            }
407
        }
408
    }
409

410
    // Undo anti recursion protection
411
    antiRec.oSetFiles.erase(pszInitName);
11,639✔
412
    --antiRec.nRecLevel;
11,639✔
413
}
414

415
/************************************************************************/
416
/*                          GetOverviewCount()                          */
417
/************************************************************************/
418

419
int GDALDefaultOverviews::GetOverviewCount(int nBand)
656,976✔
420

421
{
422
    if (poODS == nullptr || nBand < 1 || nBand > poODS->GetRasterCount())
656,976✔
423
        return 0;
655,938✔
424

425
    GDALRasterBand *poBand = poODS->GetRasterBand(nBand);
1,038✔
426
    if (poBand == nullptr)
1,038✔
427
        return 0;
×
428

429
    if (bOvrIsAux)
1,038✔
430
        return poBand->GetOverviewCount();
72✔
431

432
    return poBand->GetOverviewCount() + 1;
966✔
433
}
434

435
/************************************************************************/
436
/*                            GetOverview()                             */
437
/************************************************************************/
438

439
GDALRasterBand *GDALDefaultOverviews::GetOverview(int nBand, int iOverview)
1,364✔
440

441
{
442
    if (poODS == nullptr || nBand < 1 || nBand > poODS->GetRasterCount())
1,364✔
443
        return nullptr;
261✔
444

445
    GDALRasterBand *const poBand = poODS->GetRasterBand(nBand);
1,103✔
446
    if (poBand == nullptr)
1,103✔
447
        return nullptr;
×
448

449
    if (bOvrIsAux)
1,103✔
450
        return poBand->GetOverview(iOverview);
60✔
451

452
    // TIFF case, base is overview 0.
453
    if (iOverview == 0)
1,043✔
454
        return poBand;
792✔
455

456
    if (iOverview - 1 >= poBand->GetOverviewCount())
251✔
457
        return nullptr;
×
458

459
    return poBand->GetOverview(iOverview - 1);
251✔
460
}
461

462
/************************************************************************/
463
/*                         GDALOvLevelAdjust()                          */
464
/*                                                                      */
465
/*      Some overview levels cannot be achieved closely enough to be    */
466
/*      recognised as the desired overview level.  This function        */
467
/*      will adjust an overview level to one that is achievable on      */
468
/*      the given raster size.                                          */
469
/*                                                                      */
470
/*      For instance a 1200x1200 image on which a 256 level overview    */
471
/*      is request will end up generating a 5x5 overview.  However,     */
472
/*      this will appear to the system be a level 240 overview.         */
473
/*      This function will adjust 256 to 240 based on knowledge of      */
474
/*      the image size.                                                 */
475
/************************************************************************/
476

477
int GDALOvLevelAdjust(int nOvLevel, int nXSize)
×
478

479
{
480
    int nOXSize = DIV_ROUND_UP(nXSize, nOvLevel);
×
481

482
    return static_cast<int>(0.5 + nXSize / static_cast<double>(nOXSize));
×
483
}
484

485
int GDALOvLevelAdjust2(int nOvLevel, int nXSize, int nYSize)
472✔
486

487
{
488
    // Select the larger dimension to have increased accuracy, but
489
    // with a slight preference to x even if (a bit) smaller than y
490
    // in an attempt to behave closer as previous behavior.
491
    if (nXSize >= nYSize / 2 && !(nXSize < nYSize && nXSize < nOvLevel))
472✔
492
    {
493
        const int nOXSize = DIV_ROUND_UP(nXSize, nOvLevel);
397✔
494

495
        return static_cast<int>(0.5 + nXSize / static_cast<double>(nOXSize));
397✔
496
    }
497

498
    const int nOYSize = DIV_ROUND_UP(nYSize, nOvLevel);
75✔
499

500
    return static_cast<int>(0.5 + nYSize / static_cast<double>(nOYSize));
75✔
501
}
502

503
/************************************************************************/
504
/*                         GetFloorPowerOfTwo()                         */
505
/************************************************************************/
506

507
static int GetFloorPowerOfTwo(int n)
67,446✔
508
{
509
    int p2 = 1;
67,446✔
510
    while ((n = n >> 1) > 0)
136,081✔
511
    {
512
        p2 <<= 1;
68,635✔
513
    }
514
    return p2;
67,446✔
515
}
516

517
/************************************************************************/
518
/*                         GDALComputeOvFactor()                        */
519
/************************************************************************/
520

521
int GDALComputeOvFactor(int nOvrXSize, int nRasterXSize, int nOvrYSize,
67,446✔
522
                        int nRasterYSize)
523
{
524
    // Select the larger dimension to have increased accuracy, but
525
    // with a slight preference to x even if (a bit) smaller than y
526
    // in an attempt to behave closer as previous behavior.
527
    if (nRasterXSize != 1 && nRasterXSize >= nRasterYSize / 2)
67,446✔
528
    {
529
        const int nVal = static_cast<int>(
67,332✔
530
            0.5 + nRasterXSize / static_cast<double>(nOvrXSize));
67,332✔
531
        // Try to return a power-of-two value
532
        const int nValPowerOfTwo = GetFloorPowerOfTwo(nVal);
67,332✔
533
        for (int fact = 1; fact <= 2 && nValPowerOfTwo <= INT_MAX / fact;
67,520✔
534
             ++fact)
535
        {
536
            if (DIV_ROUND_UP(nRasterXSize, fact * nValPowerOfTwo) == nOvrXSize)
67,487✔
537
                return fact * nValPowerOfTwo;
67,299✔
538
        }
539
        return nVal;
33✔
540
    }
541

542
    const int nVal =
114✔
543
        static_cast<int>(0.5 + nRasterYSize / static_cast<double>(nOvrYSize));
114✔
544
    // Try to return a power-of-two value
545
    const int nValPowerOfTwo = GetFloorPowerOfTwo(nVal);
114✔
546
    for (int fact = 1; fact <= 2 && nValPowerOfTwo <= INT_MAX / fact; ++fact)
116✔
547
    {
548
        if (DIV_ROUND_UP(nRasterYSize, fact * nValPowerOfTwo) == nOvrYSize)
115✔
549
            return fact * nValPowerOfTwo;
113✔
550
    }
551
    return nVal;
1✔
552
}
553

554
/************************************************************************/
555
/*                           CleanOverviews()                           */
556
/*                                                                      */
557
/*      Remove all existing overviews.                                  */
558
/************************************************************************/
559

560
CPLErr GDALDefaultOverviews::CleanOverviews()
10✔
561

562
{
563
    // Anything to do?
564
    if (poODS == nullptr)
10✔
565
        return CE_None;
1✔
566

567
    // Delete the overview file(s).
568
    GDALDriver *poOvrDriver = poODS->GetDriver();
9✔
569
    GDALClose(poODS);
9✔
570
    poODS = nullptr;
9✔
571

572
    CPLErr eErr =
573
        poOvrDriver != nullptr ? poOvrDriver->Delete(osOvrFilename) : CE_None;
9✔
574

575
    // Reset the saved overview filename.
576
    if (!EQUAL(poDS->GetDescription(), ":::VIRTUAL:::"))
9✔
577
    {
578
        const bool bUseRRD = CPLTestBool(CPLGetConfigOption("USE_RRD", "NO"));
9✔
579

580
        if (bUseRRD)
9✔
581
            osOvrFilename =
582
                CPLResetExtensionSafe(poDS->GetDescription(), "aux");
×
583
        else
584
            osOvrFilename = std::string(poDS->GetDescription()).append(".ovr");
9✔
585
    }
586
    else
587
    {
588
        osOvrFilename = "";
×
589
    }
590

591
    if (HaveMaskFile() && poMaskDS)
9✔
592
    {
593
        const CPLErr eErr2 = poMaskDS->BuildOverviews(
1✔
594
            nullptr, 0, nullptr, 0, nullptr, nullptr, nullptr, nullptr);
595
        if (eErr2 != CE_None)
1✔
596
            return eErr2;
×
597
    }
598

599
    return eErr;
9✔
600
}
601

602
/************************************************************************/
603
/*                      BuildOverviewsSubDataset()                      */
604
/************************************************************************/
605

606
CPLErr GDALDefaultOverviews::BuildOverviewsSubDataset(
9✔
607
    const char *pszPhysicalFile, const char *pszResampling, int nOverviews,
608
    const int *panOverviewList, int nBands, const int *panBandList,
609
    GDALProgressFunc pfnProgress, void *pProgressData,
610
    CSLConstList papszOptions)
611

612
{
613
    if (osOvrFilename.length() == 0 && nOverviews > 0)
9✔
614
    {
615
        VSIStatBufL sStatBuf;
616

617
        int iSequence = 0;  // Used after for.
6✔
618
        for (iSequence = 0; iSequence < 100; iSequence++)
6✔
619
        {
620
            osOvrFilename.Printf("%s_%d.ovr", pszPhysicalFile, iSequence);
6✔
621
            if (VSIStatExL(osOvrFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
6✔
622
            {
623
                CPLString osAdjustedOvrFilename;
12✔
624

625
                if (poDS->GetMOFlags() & GMO_PAM_CLASS)
6✔
626
                {
627
                    osAdjustedOvrFilename.Printf(
628
                        ":::BASE:::%s_%d.ovr", CPLGetFilename(pszPhysicalFile),
629
                        iSequence);
6✔
630
                }
631
                else
632
                {
633
                    osAdjustedOvrFilename = osOvrFilename;
×
634
                }
635

636
                poDS->SetMetadataItem("OVERVIEW_FILE", osAdjustedOvrFilename,
6✔
637
                                      "OVERVIEWS");
6✔
638
                break;
6✔
639
            }
640
        }
641

642
        if (iSequence == 100)
6✔
643
            osOvrFilename = "";
×
644
    }
645

646
    return BuildOverviews(nullptr, pszResampling, nOverviews, panOverviewList,
9✔
647
                          nBands, panBandList, pfnProgress, pProgressData,
648
                          papszOptions);
9✔
649
}
650

651
/************************************************************************/
652
/*                           GetOptionValue()                           */
653
/************************************************************************/
654

655
static const char *GetOptionValue(CSLConstList papszOptions,
192✔
656
                                  const char *pszOptionKey,
657
                                  const char *pszConfigOptionKey)
658
{
659
    const char *pszVal =
660
        pszOptionKey ? CSLFetchNameValue(papszOptions, pszOptionKey) : nullptr;
192✔
661
    if (pszVal)
192✔
662
    {
663
        return pszVal;
×
664
    }
665
    pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey);
192✔
666
    if (pszVal)
192✔
667
    {
668
        return pszVal;
2✔
669
    }
670
    pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
190✔
671
    return pszVal;
190✔
672
}
673

674
/************************************************************************/
675
/*                CheckSrcOverviewsConsistencyWithBase()                */
676
/************************************************************************/
677

678
/*static */ bool GDALDefaultOverviews::CheckSrcOverviewsConsistencyWithBase(
13✔
679
    GDALDataset *poFullResDS, const std::vector<GDALDataset *> &apoSrcOvrDS)
680
{
681
    const auto poThisCRS = poFullResDS->GetSpatialRef();
13✔
682
    GDALGeoTransform thisGT;
13✔
683
    const bool bThisHasGT = poFullResDS->GetGeoTransform(thisGT) == CE_None;
13✔
684
    for (auto *poSrcOvrDS : apoSrcOvrDS)
21✔
685
    {
686
        if (poSrcOvrDS->GetRasterXSize() > poFullResDS->GetRasterXSize() ||
25✔
687
            poSrcOvrDS->GetRasterYSize() > poFullResDS->GetRasterYSize())
12✔
688
        {
689
            CPLError(
1✔
690
                CE_Failure, CPLE_AppDefined,
691
                "AddOverviews(): at least one input dataset has dimensions "
692
                "larger than the full resolution dataset.");
693
            return false;
5✔
694
        }
695
        if (poSrcOvrDS->GetRasterXSize() == 0 ||
23✔
696
            poSrcOvrDS->GetRasterYSize() == 0)
11✔
697
        {
698
            CPLError(
1✔
699
                CE_Failure, CPLE_AppDefined,
700
                "AddOverviews(): at least one input dataset has one of its "
701
                "dimensions equal to 0.");
702
            return false;
1✔
703
        }
704
        if (poSrcOvrDS->GetRasterCount() != poFullResDS->GetRasterCount())
11✔
705
        {
706
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
707
                     "AddOverviews(): at least one input dataset not the same "
708
                     "number of bands than the full resolution dataset.");
709
            return false;
1✔
710
        }
711
        if (poThisCRS)
10✔
712
        {
713
            if (const auto poOvrCRS = poSrcOvrDS->GetSpatialRef())
9✔
714
            {
715
                if (!poOvrCRS->IsSame(poThisCRS))
9✔
716
                {
717
                    CPLError(CE_Failure, CPLE_AppDefined,
1✔
718
                             "AddOverviews(): at least one input dataset has "
719
                             "its CRS "
720
                             "different from the one of the full resolution "
721
                             "dataset.");
722
                    return false;
1✔
723
                }
724
            }
725
        }
726
        if (bThisHasGT)
9✔
727
        {
728
            GDALGeoTransform ovrGT;
9✔
729
            const bool bOvrHasGT =
730
                poSrcOvrDS->GetGeoTransform(ovrGT) == CE_None;
9✔
731
            const double dfOvrXRatio =
732
                static_cast<double>(poFullResDS->GetRasterXSize()) /
9✔
733
                poSrcOvrDS->GetRasterXSize();
9✔
734
            const double dfOvrYRatio =
735
                static_cast<double>(poFullResDS->GetRasterYSize()) /
9✔
736
                poSrcOvrDS->GetRasterYSize();
9✔
737
            if (bOvrHasGT && !(std::fabs(thisGT[0] - ovrGT[0]) <=
18✔
738
                                   0.5 * std::fabs(ovrGT[1]) &&
9✔
739
                               std::fabs(thisGT[1] - ovrGT[1] / dfOvrXRatio) <=
9✔
740
                                   0.1 * std::fabs(ovrGT[1]) &&
9✔
741
                               std::fabs(thisGT[2] - ovrGT[2] / dfOvrYRatio) <=
8✔
742
                                   0.1 * std::fabs(ovrGT[2]) &&
8✔
743
                               std::fabs(thisGT[3] - ovrGT[3]) <=
8✔
744
                                   0.5 * std::fabs(ovrGT[5]) &&
8✔
745
                               std::fabs(thisGT[4] - ovrGT[4] / dfOvrXRatio) <=
8✔
746
                                   0.1 * std::fabs(ovrGT[4]) &&
8✔
747
                               std::fabs(thisGT[5] - ovrGT[5] / dfOvrYRatio) <=
8✔
748
                                   0.1 * std::fabs(ovrGT[5])))
8✔
749
            {
750
                CPLError(
1✔
751
                    CE_Failure, CPLE_AppDefined,
752
                    "AddOverviews(): at least one input dataset has its "
753
                    "geospatial extent "
754
                    "different from the one of the full resolution dataset.");
755
                return false;
1✔
756
            }
757
        }
758
    }
759
    return true;
8✔
760
}
761

762
/************************************************************************/
763
/*                           AddOverviews()                             */
764
/************************************************************************/
765

766
CPLErr GDALDefaultOverviews::AddOverviews(
4✔
767
    [[maybe_unused]] const char *pszBasename,
768
    [[maybe_unused]] const std::vector<GDALDataset *> &apoSrcOvrDSIn,
769
    [[maybe_unused]] GDALProgressFunc pfnProgress,
770
    [[maybe_unused]] void *pProgressData,
771
    [[maybe_unused]] CSLConstList papszOptions)
772
{
773
#ifdef HAVE_TIFF
774
    if (pfnProgress == nullptr)
4✔
775
        pfnProgress = GDALDummyProgress;
2✔
776

777
    if (CreateOrOpenOverviewFile(pszBasename, papszOptions) != CE_None)
4✔
NEW
778
        return CE_Failure;
×
779

780
    if (bOvrIsAux)
4✔
781
    {
NEW
782
        CPLError(CE_Failure, CPLE_NotSupported,
×
783
                 "AddOverviews() not supported for .aux overviews");
NEW
784
        return CE_Failure;
×
785
    }
786

787
    if (!GDALDefaultOverviews::CheckSrcOverviewsConsistencyWithBase(
4✔
788
            poDS, apoSrcOvrDSIn))
NEW
789
        return CE_Failure;
×
790

791
    std::vector<GDALDataset *> apoSrcOvrDS = apoSrcOvrDSIn;
8✔
792
    // Sort overviews by descending size
793
    std::sort(apoSrcOvrDS.begin(), apoSrcOvrDS.end(),
4✔
NEW
794
              [](const GDALDataset *poDS1, const GDALDataset *poDS2)
×
NEW
795
              { return poDS1->GetRasterXSize() > poDS2->GetRasterXSize(); });
×
796

797
    auto poBand = poDS->GetRasterBand(1);
4✔
798
    if (!poBand)
4✔
NEW
799
        return CE_Failure;
×
800

801
    // Determine which overview levels must be created
802
    std::vector<std::pair<int, int>> anOverviewSizes;
4✔
803
    for (auto *poSrcOvrDS : apoSrcOvrDS)
8✔
804
    {
805
        bool bFound = false;
4✔
806
        for (int j = 0; j < poBand->GetOverviewCount(); j++)
4✔
807
        {
808
            GDALRasterBand *poOverview = poBand->GetOverview(j);
2✔
809
            if (poOverview && poOverview->GetDataset() &&
2✔
810
                poOverview->GetDataset() != poDS &&
2✔
811
                poOverview->GetXSize() == poSrcOvrDS->GetRasterXSize() &&
6✔
812
                poOverview->GetYSize() == poSrcOvrDS->GetRasterYSize())
2✔
813
            {
814
                bFound = true;
2✔
815
                break;
2✔
816
            }
817
        }
818
        if (!bFound)
4✔
819
        {
NEW
820
            anOverviewSizes.emplace_back(poSrcOvrDS->GetRasterXSize(),
×
821
                                         poSrcOvrDS->GetRasterYSize());
2✔
822
        }
823
    }
824

825
    CPLErr eErr = CE_None;
4✔
826

827
    if (!anOverviewSizes.empty())
4✔
828
    {
829
        if (poODS != nullptr)
2✔
830
        {
NEW
831
            delete poODS;
×
NEW
832
            poODS = nullptr;
×
833
        }
834

835
        const int nBands = poDS->GetRasterCount();
2✔
836
        std::vector<GDALRasterBand *> apoBands;
4✔
837
        for (int i = 0; i < nBands; ++i)
4✔
838
            apoBands.push_back(poDS->GetRasterBand(i + 1));
2✔
839

840
        eErr = GTIFFBuildOverviewsEx(osOvrFilename, nBands, apoBands.data(),
2✔
841
                                     static_cast<int>(apoSrcOvrDS.size()),
2✔
842
                                     nullptr, anOverviewSizes.data(), "NONE",
2✔
843
                                     nullptr, GDALDummyProgress, nullptr);
844

845
        // Probe for proxy overview filename.
846
        if (eErr == CE_Failure)
2✔
847
        {
848
            const char *pszProxyOvrFilename =
NEW
849
                poDS->GetMetadataItem("FILENAME", "ProxyOverviewRequest");
×
850

NEW
851
            if (pszProxyOvrFilename != nullptr)
×
852
            {
NEW
853
                osOvrFilename = pszProxyOvrFilename;
×
NEW
854
                eErr = GTIFFBuildOverviewsEx(
×
NEW
855
                    osOvrFilename, nBands, apoBands.data(),
×
NEW
856
                    static_cast<int>(apoSrcOvrDS.size()), nullptr,
×
NEW
857
                    anOverviewSizes.data(), "NONE", nullptr, GDALDummyProgress,
×
858
                    nullptr);
859
            }
860
        }
861

862
        if (eErr == CE_None)
2✔
863
        {
864
            poODS = GDALDataset::Open(osOvrFilename,
2✔
865
                                      GDAL_OF_RASTER | GDAL_OF_UPDATE);
866
            if (poODS == nullptr)
2✔
NEW
867
                eErr = CE_Failure;
×
868
        }
869
    }
870

871
    // almost 0, but not 0 to please Coverity Scan
872
    double dfTotalPixels = std::numeric_limits<double>::min();
4✔
873
    for (const auto *poSrcOvrDS : apoSrcOvrDS)
8✔
874
    {
875
        dfTotalPixels += static_cast<double>(poSrcOvrDS->GetRasterXSize()) *
4✔
876
                         poSrcOvrDS->GetRasterYSize();
4✔
877
    }
878

879
    // Copy source datasets into target overview datasets
880
    double dfCurPixels = 0;
4✔
881
    for (auto *poSrcOvrDS : apoSrcOvrDS)
8✔
882
    {
883
        GDALDataset *poDstOvrDS = nullptr;
4✔
884
        for (int j = 0; eErr == CE_None && j < poBand->GetOverviewCount(); j++)
4✔
885
        {
886
            GDALRasterBand *poOverview = poBand->GetOverview(j);
4✔
887
            if (poOverview &&
8✔
888
                poOverview->GetXSize() == poSrcOvrDS->GetRasterXSize() &&
8✔
889
                poOverview->GetYSize() == poSrcOvrDS->GetRasterYSize())
4✔
890
            {
891
                poDstOvrDS = poOverview->GetDataset();
4✔
892
                break;
4✔
893
            }
894
        }
895
        if (poDstOvrDS)
4✔
896
        {
897
            const double dfThisPixels =
898
                static_cast<double>(poSrcOvrDS->GetRasterXSize()) *
4✔
899
                poSrcOvrDS->GetRasterYSize();
4✔
900
            void *pScaledProgressData = GDALCreateScaledProgress(
8✔
901
                dfCurPixels / dfTotalPixels,
902
                (dfCurPixels + dfThisPixels) / dfTotalPixels, pfnProgress,
4✔
903
                pProgressData);
904
            dfCurPixels += dfThisPixels;
4✔
905
            eErr = GDALDatasetCopyWholeRaster(GDALDataset::ToHandle(poSrcOvrDS),
4✔
906
                                              GDALDataset::ToHandle(poDstOvrDS),
907
                                              nullptr, GDALScaledProgress,
908
                                              pScaledProgressData);
909
            GDALDestroyScaledProgress(pScaledProgressData);
4✔
910
        }
911
    }
912

913
    return eErr;
4✔
914
#else
915
    CPLError(CE_Failure, CPLE_NotSupported,
916
             "AddOverviews() not supported due to GeoTIFF driver missing");
917
    return CE_Failure;
918
#endif
919
}
920

921
/************************************************************************/
922
/*                      CreateOrOpenOverviewFile()                      */
923
/************************************************************************/
924

925
CPLErr GDALDefaultOverviews::CreateOrOpenOverviewFile(const char *pszBasename,
199✔
926
                                                      CSLConstList papszOptions)
927
{
928

929
    /* -------------------------------------------------------------------- */
930
    /*      If we don't already have an overview file, we need to decide    */
931
    /*      what format to use.                                             */
932
    /* -------------------------------------------------------------------- */
933
    if (poODS == nullptr)
199✔
934
    {
935
        const char *pszUseRRD =
936
            GetOptionValue(papszOptions, nullptr, "USE_RRD");
184✔
937
        bOvrIsAux = pszUseRRD && CPLTestBool(pszUseRRD);
184✔
938
        if (bOvrIsAux)
184✔
939
        {
940
            osOvrFilename =
941
                CPLResetExtensionSafe(poDS->GetDescription(), "aux");
4✔
942

943
            VSIStatBufL sStatBuf;
944
            if (VSIStatExL(osOvrFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
4✔
945
                osOvrFilename.Printf("%s.aux", poDS->GetDescription());
×
946
        }
947
    }
948
    /* -------------------------------------------------------------------- */
949
    /*      If we already have the overviews open, but they are             */
950
    /*      read-only, then try and reopen them read-write.                 */
951
    /* -------------------------------------------------------------------- */
952
    else if (poODS->GetAccess() == GA_ReadOnly)
15✔
953
    {
954
        GDALClose(poODS);
13✔
955
        poODS =
13✔
956
            GDALDataset::Open(osOvrFilename, GDAL_OF_RASTER | GDAL_OF_UPDATE);
13✔
957
        if (poODS == nullptr)
13✔
958
            return CE_Failure;
×
959
    }
960

961
    /* -------------------------------------------------------------------- */
962
    /*      If a basename is provided, use it to override the internal      */
963
    /*      overview filename.                                              */
964
    /* -------------------------------------------------------------------- */
965
    if (pszBasename == nullptr && osOvrFilename.length() == 0)
199✔
966
        pszBasename = poDS->GetDescription();
×
967

968
    if (pszBasename != nullptr)
199✔
969
    {
970
        if (bOvrIsAux)
1✔
971
            osOvrFilename.Printf("%s.aux", pszBasename);
×
972
        else
973
            osOvrFilename.Printf("%s.ovr", pszBasename);
1✔
974
    }
975

976
    return CE_None;
199✔
977
}
978

979
/************************************************************************/
980
/*                           BuildOverviews()                           */
981
/************************************************************************/
982

983
CPLErr GDALDefaultOverviews::BuildOverviews(
204✔
984
    const char *pszBasename, const char *pszResampling, int nOverviews,
985
    const int *panOverviewList, int nBands, const int *panBandList,
986
    GDALProgressFunc pfnProgress, void *pProgressData,
987
    CSLConstList papszOptions)
988

989
{
990
    if (pfnProgress == nullptr)
204✔
NEW
991
        pfnProgress = GDALDummyProgress;
×
992

993
    if (nOverviews == 0)
204✔
994
        return CleanOverviews();
9✔
995

996
    if (CreateOrOpenOverviewFile(pszBasename, papszOptions) != CE_None)
195✔
NEW
997
        return CE_Failure;
×
998

999
    /* -------------------------------------------------------------------- */
1000
    /*      Our TIFF overview support currently only works safely if all    */
1001
    /*      bands are handled at the same time.                             */
1002
    /* -------------------------------------------------------------------- */
1003
    if (!bOvrIsAux && nBands != poDS->GetRasterCount())
195✔
1004
    {
NEW
1005
        CPLError(CE_Failure, CPLE_NotSupported,
×
1006
                 "Generation of overviews in external TIFF currently only "
1007
                 "supported when operating on all bands.  "
1008
                 "Operation failed.");
NEW
1009
        return CE_Failure;
×
1010
    }
1011

1012
    /* -------------------------------------------------------------------- */
1013
    /*      Establish which of the overview levels we already have, and     */
1014
    /*      which are new.  We assume that band 1 of the file is            */
1015
    /*      representative.                                                 */
1016
    /* -------------------------------------------------------------------- */
1017
    GDALRasterBand *poBand = poDS->GetRasterBand(1);
195✔
1018

1019
    int nNewOverviews = 0;
195✔
1020
    int *panNewOverviewList =
1021
        static_cast<int *>(CPLCalloc(sizeof(int), nOverviews));
195✔
1022
    double dfAreaNewOverviews = 0;
195✔
1023
    double dfAreaRefreshedOverviews = 0;
195✔
1024
    std::vector<bool> abValidLevel(nOverviews, true);
390✔
1025
    std::vector<bool> abRequireRefresh(nOverviews, false);
195✔
1026
    bool bFoundSinglePixelOverview = false;
195✔
1027
    for (int i = 0; i < nOverviews && poBand != nullptr; i++)
480✔
1028
    {
1029
        // If we already have a 1x1 overview and this new one would result
1030
        // in it too, then don't create it.
1031
        if (bFoundSinglePixelOverview &&
16✔
1032
            DIV_ROUND_UP(poBand->GetXSize(), panOverviewList[i]) == 1 &&
301✔
1033
            DIV_ROUND_UP(poBand->GetYSize(), panOverviewList[i]) == 1)
16✔
1034
        {
1035
            abValidLevel[i] = false;
16✔
1036
            continue;
16✔
1037
        }
1038

1039
        for (int j = 0; j < poBand->GetOverviewCount(); j++)
290✔
1040
        {
1041
            GDALRasterBand *poOverview = poBand->GetOverview(j);
38✔
1042
            if (poOverview == nullptr)
38✔
1043
                continue;
×
1044

1045
            int nOvFactor =
1046
                GDALComputeOvFactor(poOverview->GetXSize(), poBand->GetXSize(),
38✔
1047
                                    poOverview->GetYSize(), poBand->GetYSize());
1048

1049
            if (nOvFactor == panOverviewList[i] ||
59✔
1050
                nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
21✔
1051
                                                poBand->GetXSize(),
1052
                                                poBand->GetYSize()))
1053
            {
1054
                const auto osNewResampling =
1055
                    GDALGetNormalizedOvrResampling(pszResampling);
34✔
1056
                const char *pszExistingResampling =
1057
                    poOverview->GetMetadataItem("RESAMPLING");
17✔
1058
                if (pszExistingResampling &&
28✔
1059
                    pszExistingResampling != osNewResampling)
11✔
1060
                {
1061
                    if (auto l_poODS = poOverview->GetDataset())
3✔
1062
                    {
1063
                        if (auto poDriver = l_poODS->GetDriver())
3✔
1064
                        {
1065
                            if (EQUAL(poDriver->GetDescription(), "GTiff"))
1✔
1066
                            {
1067
                                poOverview->SetMetadataItem(
1✔
1068
                                    "RESAMPLING", osNewResampling.c_str());
1✔
1069
                            }
1070
                        }
1071
                    }
1072
                }
1073

1074
                abRequireRefresh[i] = true;
17✔
1075
                break;
17✔
1076
            }
1077
        }
1078

1079
        if (abValidLevel[i])
269✔
1080
        {
1081
            const double dfArea =
269✔
1082
                1.0 /
1083
                (static_cast<double>(panOverviewList[i]) * panOverviewList[i]);
269✔
1084
            dfAreaRefreshedOverviews += dfArea;
269✔
1085
            if (!abRequireRefresh[i])
269✔
1086
            {
1087
                dfAreaNewOverviews += dfArea;
252✔
1088
                panNewOverviewList[nNewOverviews++] = panOverviewList[i];
252✔
1089
            }
1090

1091
            if (DIV_ROUND_UP(poBand->GetXSize(), panOverviewList[i]) == 1 &&
290✔
1092
                DIV_ROUND_UP(poBand->GetYSize(), panOverviewList[i]) == 1)
21✔
1093
            {
1094
                bFoundSinglePixelOverview = true;
19✔
1095
            }
1096
        }
1097
    }
1098

1099
    /* -------------------------------------------------------------------- */
1100
    /*      Build band list.                                                */
1101
    /* -------------------------------------------------------------------- */
1102
    GDALRasterBand **pahBands = static_cast<GDALRasterBand **>(
1103
        CPLCalloc(sizeof(GDALRasterBand *), nBands));
195✔
1104
    for (int i = 0; i < nBands; i++)
505✔
1105
        pahBands[i] = poDS->GetRasterBand(panBandList[i]);
310✔
1106

1107
    /* -------------------------------------------------------------------- */
1108
    /*      Build new overviews - Imagine.  Keep existing file open if      */
1109
    /*      we have it.  But mark all overviews as in need of               */
1110
    /*      regeneration, since HFAAuxBuildOverviews() doesn't actually     */
1111
    /*      produce the imagery.                                            */
1112
    /* -------------------------------------------------------------------- */
1113

1114
    CPLErr eErr = CE_None;
195✔
1115

1116
    void *pScaledOverviewWithoutMask = GDALCreateScaledProgress(
195✔
1117
        0, (HaveMaskFile() && poMaskDS) ? double(nBands) / (nBands + 1) : 1,
195✔
1118
        pfnProgress, pProgressData);
1119

1120
    const auto AvoidZero = [](double x)
214✔
1121
    {
1122
        if (x == 0)
214✔
1123
            return 1.0;
×
1124
        return x;
214✔
1125
    };
1126

1127
    void *pScaledProgress = GDALCreateScaledProgress(
195✔
1128
        0, dfAreaNewOverviews / AvoidZero(dfAreaRefreshedOverviews),
195✔
1129
        GDALScaledProgress, pScaledOverviewWithoutMask);
1130
    if (bOvrIsAux)
195✔
1131
    {
1132
#ifdef NO_HFA_SUPPORT
1133
        CPLError(CE_Failure, CPLE_NotSupported,
1134
                 "This build does not support creating .aux overviews");
1135
        eErr = CE_Failure;
1136
#else
1137
        if (nNewOverviews == 0)
8✔
1138
        {
1139
            /* if we call HFAAuxBuildOverviews() with nNewOverviews == 0 */
1140
            /* because that there's no new, this will wipe existing */
1141
            /* overviews (#4831) */
1142
            // eErr = CE_None;
1143
        }
1144
        else
1145
        {
1146
            eErr = HFAAuxBuildOverviews(
5✔
1147
                osOvrFilename, poDS, &poODS, nBands, panBandList, nNewOverviews,
1148
                panNewOverviewList, pszResampling, GDALScaledProgress,
1149
                pScaledProgress, papszOptions);
1150
        }
1151

1152
        // HFAAuxBuildOverviews doesn't actually generate overviews
1153
        dfAreaNewOverviews = 0.0;
8✔
1154
        for (int j = 0; j < nOverviews; j++)
20✔
1155
        {
1156
            if (abValidLevel[j])
12✔
1157
                abRequireRefresh[j] = true;
12✔
1158
        }
1159
#endif
1160
    }
1161

1162
    /* -------------------------------------------------------------------- */
1163
    /*      Build new overviews - TIFF.  Close TIFF files while we          */
1164
    /*      operate on it.                                                  */
1165
    /* -------------------------------------------------------------------- */
1166
    else
1167
    {
1168
        if (poODS != nullptr)
187✔
1169
        {
1170
            delete poODS;
9✔
1171
            poODS = nullptr;
9✔
1172
        }
1173

1174
#ifdef HAVE_TIFF
1175
        eErr = GTIFFBuildOverviews(
187✔
1176
            osOvrFilename, nBands, pahBands, nNewOverviews, panNewOverviewList,
1177
            pszResampling, GDALScaledProgress, pScaledProgress, papszOptions);
1178

1179
        // Probe for proxy overview filename.
1180
        if (eErr == CE_Failure)
187✔
1181
        {
1182
            const char *pszProxyOvrFilename =
1183
                poDS->GetMetadataItem("FILENAME", "ProxyOverviewRequest");
5✔
1184

1185
            if (pszProxyOvrFilename != nullptr)
5✔
1186
            {
1187
                osOvrFilename = pszProxyOvrFilename;
1✔
1188
                eErr = GTIFFBuildOverviews(osOvrFilename, nBands, pahBands,
1✔
1189
                                           nNewOverviews, panNewOverviewList,
1190
                                           pszResampling, GDALScaledProgress,
1191
                                           pScaledProgress, papszOptions);
1192
            }
1193
        }
1194

1195
        if (eErr == CE_None)
187✔
1196
        {
1197
            poODS = GDALDataset::Open(osOvrFilename,
183✔
1198
                                      GDAL_OF_RASTER | GDAL_OF_UPDATE);
1199
            if (poODS == nullptr)
183✔
1200
                eErr = CE_Failure;
×
1201
        }
1202
#else
1203
        CPLError(CE_Failure, CPLE_NotSupported,
1204
                 "Cannot build TIFF overviews due to GeoTIFF driver missing");
1205
        eErr = CE_Failure;
1206
#endif
1207
    }
1208

1209
    GDALDestroyScaledProgress(pScaledProgress);
195✔
1210

1211
    /* -------------------------------------------------------------------- */
1212
    /*      Refresh old overviews that were listed.                         */
1213
    /* -------------------------------------------------------------------- */
1214
    GDALRasterBand **papoOverviewBands =
1215
        static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nOverviews));
195✔
1216

1217
    for (int iBand = 0; iBand < nBands && eErr == CE_None; iBand++)
501✔
1218
    {
1219
        poBand = poDS->GetRasterBand(panBandList[iBand]);
306✔
1220
        if (poBand == nullptr)
306✔
1221
        {
1222
            eErr = CE_Failure;
×
1223
            break;
×
1224
        }
1225

1226
        nNewOverviews = 0;
306✔
1227
        std::vector<bool> abAlreadyUsedOverviewBand(poBand->GetOverviewCount(),
612✔
1228
                                                    false);
612✔
1229

1230
        for (int i = 0; i < nOverviews; i++)
769✔
1231
        {
1232
            if (!abValidLevel[i] || !abRequireRefresh[i])
463✔
1233
                continue;
434✔
1234

1235
            for (int j = 0; j < poBand->GetOverviewCount(); j++)
47✔
1236
            {
1237
                if (abAlreadyUsedOverviewBand[j])
47✔
1238
                    continue;
16✔
1239

1240
                GDALRasterBand *poOverview = poBand->GetOverview(j);
31✔
1241
                if (poOverview == nullptr)
31✔
1242
                    continue;
×
1243

1244
                int bHasNoData = FALSE;
31✔
1245
                double noDataValue = poBand->GetNoDataValue(&bHasNoData);
31✔
1246

1247
                if (bHasNoData)
31✔
1248
                    poOverview->SetNoDataValue(noDataValue);
2✔
1249

1250
                const int nOvFactor = GDALComputeOvFactor(
31✔
1251
                    poOverview->GetXSize(), poBand->GetXSize(),
1252
                    poOverview->GetYSize(), poBand->GetYSize());
1253

1254
                if (nOvFactor == panOverviewList[i] ||
33✔
1255
                    nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
2✔
1256
                                                    poBand->GetXSize(),
1257
                                                    poBand->GetYSize()))
1258
                {
1259
                    abAlreadyUsedOverviewBand[j] = true;
29✔
1260
                    CPLAssert(nNewOverviews < poBand->GetOverviewCount());
29✔
1261
                    papoOverviewBands[nNewOverviews++] = poOverview;
29✔
1262
                    break;
29✔
1263
                }
1264
            }
1265
        }
1266

1267
        if (nNewOverviews > 0)
306✔
1268
        {
1269
            const double dfOffset =
1270
                dfAreaNewOverviews / AvoidZero(dfAreaRefreshedOverviews);
19✔
1271
            const double dfScale = 1.0 - dfOffset;
19✔
1272
            pScaledProgress = GDALCreateScaledProgress(
38✔
1273
                dfOffset + dfScale * iBand / nBands,
19✔
1274
                dfOffset + dfScale * (iBand + 1) / nBands, GDALScaledProgress,
19✔
1275
                pScaledOverviewWithoutMask);
1276
            eErr = GDALRegenerateOverviewsEx(
19✔
1277
                GDALRasterBand::ToHandle(poBand), nNewOverviews,
1278
                reinterpret_cast<GDALRasterBandH *>(papoOverviewBands),
1279
                pszResampling, GDALScaledProgress, pScaledProgress,
1280
                papszOptions);
1281
            GDALDestroyScaledProgress(pScaledProgress);
19✔
1282
        }
1283
    }
1284

1285
    /* -------------------------------------------------------------------- */
1286
    /*      Cleanup                                                         */
1287
    /* -------------------------------------------------------------------- */
1288
    CPLFree(papoOverviewBands);
195✔
1289
    CPLFree(panNewOverviewList);
195✔
1290
    CPLFree(pahBands);
195✔
1291
    GDALDestroyScaledProgress(pScaledOverviewWithoutMask);
195✔
1292

1293
    /* -------------------------------------------------------------------- */
1294
    /*      If we have a mask file, we need to build its overviews too.     */
1295
    /* -------------------------------------------------------------------- */
1296
    if (HaveMaskFile() && eErr == CE_None)
195✔
1297
    {
1298
        pScaledProgress = GDALCreateScaledProgress(
4✔
1299
            double(nBands) / (nBands + 1), 1.0, pfnProgress, pProgressData);
2✔
1300
        eErr = BuildOverviewsMask(pszResampling, nOverviews, panOverviewList,
2✔
1301
                                  GDALScaledProgress, pScaledProgress,
1302
                                  papszOptions);
1303
        GDALDestroyScaledProgress(pScaledProgress);
2✔
1304
    }
1305

1306
    /* -------------------------------------------------------------------- */
1307
    /*      If we have an overview dataset, then mark all the overviews     */
1308
    /*      with the base dataset  Used later for finding overviews         */
1309
    /*      masks.  Uggg.                                                   */
1310
    /* -------------------------------------------------------------------- */
1311
    if (poODS)
195✔
1312
    {
1313
        const int nOverviewCount = GetOverviewCount(1);
191✔
1314

1315
        for (int iOver = 0; iOver < nOverviewCount; iOver++)
462✔
1316
        {
1317
            GDALRasterBand *poOtherBand = GetOverview(1, iOver);
271✔
1318
            GDALDataset *poOverDS =
1319
                poOtherBand != nullptr ? poOtherBand->GetDataset() : nullptr;
271✔
1320

1321
            if (poOverDS != nullptr)
271✔
1322
            {
1323
                poOverDS->oOvManager.poBaseDS = poDS;
257✔
1324
                poOverDS->oOvManager.poDS = poOverDS;
257✔
1325
            }
1326
        }
1327
    }
1328

1329
    return eErr;
195✔
1330
}
1331

1332
/************************************************************************/
1333
/*                          BuildOverviewsMask()                        */
1334
/************************************************************************/
1335

1336
CPLErr GDALDefaultOverviews::BuildOverviewsMask(const char *pszResampling,
4✔
1337
                                                int nOverviews,
1338
                                                const int *panOverviewList,
1339
                                                GDALProgressFunc pfnProgress,
1340
                                                void *pProgressData,
1341
                                                CSLConstList papszOptions)
1342
{
1343
    CPLErr eErr = CE_None;
4✔
1344
    if (HaveMaskFile() && poMaskDS)
4✔
1345
    {
1346
        // Some options are not compatible with mask overviews
1347
        // so unset them, and define more sensible values.
1348
        CPLStringList aosMaskOptions(papszOptions);
4✔
1349
        const char *pszCompress =
1350
            GetOptionValue(papszOptions, "COMPRESS", "COMPRESS_OVERVIEW");
4✔
1351
        const bool bJPEG = pszCompress && EQUAL(pszCompress, "JPEG");
4✔
1352
        const char *pszPhotometric =
1353
            GetOptionValue(papszOptions, "PHOTOMETRIC", "PHOTOMETRIC_OVERVIEW");
4✔
1354
        const bool bPHOTOMETRIC_YCBCR =
4✔
1355
            pszPhotometric && EQUAL(pszPhotometric, "YCBCR");
4✔
1356
        if (bJPEG)
4✔
1357
            aosMaskOptions.SetNameValue("COMPRESS", "DEFLATE");
×
1358
        if (bPHOTOMETRIC_YCBCR)
4✔
1359
            aosMaskOptions.SetNameValue("PHOTOMETRIC", "MINISBLACK");
×
1360

1361
        eErr = poMaskDS->BuildOverviews(
4✔
1362
            pszResampling, nOverviews, panOverviewList, 0, nullptr, pfnProgress,
1363
            pProgressData, aosMaskOptions.List());
4✔
1364

1365
        if (bOwnMaskDS)
4✔
1366
        {
1367
            // Reset the poMask member of main dataset bands, since it
1368
            // will become invalid after poMaskDS closing.
1369
            for (int iBand = 1; iBand <= poDS->GetRasterCount(); iBand++)
10✔
1370
            {
1371
                GDALRasterBand *poOtherBand = poDS->GetRasterBand(iBand);
6✔
1372
                if (poOtherBand != nullptr)
6✔
1373
                    poOtherBand->InvalidateMaskBand();
6✔
1374
            }
1375

1376
            GDALClose(poMaskDS);
4✔
1377
        }
1378

1379
        // force next request to reread mask file.
1380
        poMaskDS = nullptr;
4✔
1381
        bOwnMaskDS = false;
4✔
1382
        bCheckedForMask = false;
4✔
1383
    }
1384

1385
    return eErr;
4✔
1386
}
1387

1388
/************************************************************************/
1389
/*                           CreateMaskBand()                           */
1390
/************************************************************************/
1391

1392
CPLErr GDALDefaultOverviews::CreateMaskBand(int nFlags, int nBand)
26✔
1393

1394
{
1395
    if (nBand < 1)
26✔
1396
        nFlags |= GMF_PER_DATASET;
17✔
1397

1398
    /* -------------------------------------------------------------------- */
1399
    /*      ensure existing file gets opened if there is one.               */
1400
    /* -------------------------------------------------------------------- */
1401
    CPL_IGNORE_RET_VAL(HaveMaskFile());
26✔
1402

1403
    /* -------------------------------------------------------------------- */
1404
    /*      Try creating the mask file.                                     */
1405
    /* -------------------------------------------------------------------- */
1406
    if (poMaskDS == nullptr)
26✔
1407
    {
1408
        GDALDriver *const poDr =
1409
            static_cast<GDALDriver *>(GDALGetDriverByName("GTiff"));
21✔
1410

1411
        if (poDr == nullptr)
21✔
1412
            return CE_Failure;
×
1413

1414
        GDALRasterBand *const poTBand = poDS->GetRasterBand(1);
21✔
1415
        if (poTBand == nullptr)
21✔
1416
            return CE_Failure;
×
1417

1418
        const int nBands =
1419
            (nFlags & GMF_PER_DATASET) ? 1 : poDS->GetRasterCount();
21✔
1420

1421
        char **papszOpt = CSLSetNameValue(nullptr, "COMPRESS", "DEFLATE");
21✔
1422
        papszOpt = CSLSetNameValue(papszOpt, "INTERLEAVE", "BAND");
21✔
1423

1424
        int nBX = 0;
21✔
1425
        int nBY = 0;
21✔
1426
        poTBand->GetBlockSize(&nBX, &nBY);
21✔
1427

1428
        // Try to create matching tile size if legal in TIFF.
1429
        if ((nBX % 16) == 0 && (nBY % 16) == 0)
21✔
1430
        {
1431
            papszOpt = CSLSetNameValue(papszOpt, "TILED", "YES");
2✔
1432
            papszOpt = CSLSetNameValue(papszOpt, "BLOCKXSIZE",
2✔
1433
                                       CPLString().Printf("%d", nBX));
4✔
1434
            papszOpt = CSLSetNameValue(papszOpt, "BLOCKYSIZE",
2✔
1435
                                       CPLString().Printf("%d", nBY));
4✔
1436
        }
1437

1438
        CPLString osMskFilename;
21✔
1439
        osMskFilename.Printf("%s.msk", poDS->GetDescription());
21✔
1440
        poMaskDS =
21✔
1441
            poDr->Create(osMskFilename, poDS->GetRasterXSize(),
21✔
1442
                         poDS->GetRasterYSize(), nBands, GDT_Byte, papszOpt);
21✔
1443
        CSLDestroy(papszOpt);
21✔
1444

1445
        if (poMaskDS == nullptr)  // Presumably error already issued.
21✔
1446
            return CE_Failure;
×
1447

1448
        bOwnMaskDS = true;
21✔
1449
    }
1450

1451
    /* -------------------------------------------------------------------- */
1452
    /*      Save the mask flags for this band.                              */
1453
    /* -------------------------------------------------------------------- */
1454
    if (nBand > poMaskDS->GetRasterCount())
26✔
1455
    {
1456
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
1457
                 "Attempt to create a mask band for band %d of %s, "
1458
                 "but the .msk file has a PER_DATASET mask.",
1459
                 nBand, poDS->GetDescription());
1✔
1460
        return CE_Failure;
1✔
1461
    }
1462

1463
    for (int iBand = 0; iBand < poDS->GetRasterCount(); iBand++)
67✔
1464
    {
1465
        // we write only the info for this band, unless we are
1466
        // using PER_DATASET in which case we write for all.
1467
        if (nBand != iBand + 1 && !(nFlags & GMF_PER_DATASET))
42✔
1468
            continue;
6✔
1469

1470
        poMaskDS->SetMetadataItem(
36✔
1471
            CPLString().Printf("INTERNAL_MASK_FLAGS_%d", iBand + 1),
72✔
1472
            CPLString().Printf("%d", nFlags));
72✔
1473
    }
1474

1475
    return CE_None;
25✔
1476
}
1477

1478
/************************************************************************/
1479
/*                            GetMaskBand()                             */
1480
/************************************************************************/
1481

1482
// Secret code meaning we don't handle this band.
1483
constexpr int MISSING_FLAGS = 0x8000;
1484

1485
GDALRasterBand *GDALDefaultOverviews::GetMaskBand(int nBand)
46✔
1486

1487
{
1488
    const int nFlags = GetMaskFlags(nBand);
46✔
1489

1490
    if (poMaskDS == nullptr || nFlags == MISSING_FLAGS)
46✔
1491
        return nullptr;
1✔
1492

1493
    if (nFlags & GMF_PER_DATASET)
45✔
1494
        return poMaskDS->GetRasterBand(1);
36✔
1495

1496
    if (nBand > 0)
9✔
1497
        return poMaskDS->GetRasterBand(nBand);
9✔
1498

1499
    return nullptr;
×
1500
}
1501

1502
/************************************************************************/
1503
/*                            GetMaskFlags()                            */
1504
/************************************************************************/
1505

1506
int GDALDefaultOverviews::GetMaskFlags(int nBand)
90✔
1507

1508
{
1509
    /* -------------------------------------------------------------------- */
1510
    /*      Fetch this band's metadata entry.  They are of the form:        */
1511
    /*        INTERNAL_MASK_FLAGS_n: flags                                  */
1512
    /* -------------------------------------------------------------------- */
1513
    if (!HaveMaskFile())
90✔
1514
        return 0;
×
1515

1516
    const char *pszValue = poMaskDS->GetMetadataItem(
90✔
1517
        CPLString().Printf("INTERNAL_MASK_FLAGS_%d", std::max(nBand, 1)));
180✔
1518

1519
    if (pszValue == nullptr)
90✔
1520
        return MISSING_FLAGS;
1✔
1521

1522
    return atoi(pszValue);
89✔
1523
}
1524

1525
/************************************************************************/
1526
/*                            HaveMaskFile()                            */
1527
/*                                                                      */
1528
/*      Check for a mask file if we haven't already done so.            */
1529
/*      Returns TRUE if we have one, otherwise FALSE.                   */
1530
/************************************************************************/
1531

1532
int GDALDefaultOverviews::HaveMaskFile(char **papszSiblingFiles,
101,124✔
1533
                                       const char *pszBasename)
1534

1535
{
1536
    /* -------------------------------------------------------------------- */
1537
    /*      Have we already checked for masks?                              */
1538
    /* -------------------------------------------------------------------- */
1539
    if (bCheckedForMask)
101,124✔
1540
        return poMaskDS != nullptr;
3,776✔
1541

1542
    if (papszSiblingFiles == nullptr)
97,348✔
1543
        papszSiblingFiles = papszInitSiblingFiles;
97,348✔
1544

1545
    /* -------------------------------------------------------------------- */
1546
    /*      Are we an overview?  If so we need to find the corresponding    */
1547
    /*      overview in the base files mask file (if there is one).         */
1548
    /* -------------------------------------------------------------------- */
1549
    if (poBaseDS != nullptr && poBaseDS->oOvManager.HaveMaskFile())
97,348✔
1550
    {
1551
        GDALRasterBand *const poBaseBand = poBaseDS->GetRasterBand(1);
4✔
1552
        GDALDataset *poMaskDSTemp = nullptr;
4✔
1553
        if (poBaseBand != nullptr)
4✔
1554
        {
1555
            GDALRasterBand *poBaseMask = poBaseBand->GetMaskBand();
4✔
1556
            if (poBaseMask != nullptr)
4✔
1557
            {
1558
                const int nOverviewCount = poBaseMask->GetOverviewCount();
4✔
1559
                for (int iOver = 0; iOver < nOverviewCount; iOver++)
6✔
1560
                {
1561
                    GDALRasterBand *const poOverBand =
1562
                        poBaseMask->GetOverview(iOver);
6✔
1563
                    if (poOverBand == nullptr)
6✔
1564
                        continue;
×
1565

1566
                    if (poOverBand->GetXSize() == poDS->GetRasterXSize() &&
10✔
1567
                        poOverBand->GetYSize() == poDS->GetRasterYSize())
4✔
1568
                    {
1569
                        poMaskDSTemp = poOverBand->GetDataset();
4✔
1570
                        break;
4✔
1571
                    }
1572
                }
1573
            }
1574
        }
1575

1576
        if (poMaskDSTemp != poDS)
4✔
1577
        {
1578
            poMaskDS = poMaskDSTemp;
4✔
1579
            bCheckedForMask = true;
4✔
1580
            bOwnMaskDS = false;
4✔
1581

1582
            return poMaskDS != nullptr;
4✔
1583
        }
1584
    }
1585

1586
    /* -------------------------------------------------------------------- */
1587
    /*      Are we even initialized?  If not, we apparently don't want      */
1588
    /*      to support overviews and masks.                                 */
1589
    /* -------------------------------------------------------------------- */
1590
    if (poDS == nullptr)
97,344✔
1591
        return FALSE;
86,984✔
1592

1593
    /* -------------------------------------------------------------------- */
1594
    /*      Check for .msk file.                                            */
1595
    /* -------------------------------------------------------------------- */
1596
    bCheckedForMask = true;
10,360✔
1597

1598
    if (pszBasename == nullptr)
10,360✔
1599
        pszBasename = poDS->GetDescription();
10,360✔
1600

1601
    // Don't bother checking for masks of masks.
1602
    if (EQUAL(CPLGetExtensionSafe(pszBasename).c_str(), "msk"))
10,360✔
1603
        return FALSE;
20✔
1604

1605
    if (!GDALCanFileAcceptSidecarFile(pszBasename))
10,340✔
1606
        return FALSE;
1✔
1607
    CPLString osMskFilename;
20,678✔
1608
    osMskFilename.Printf("%s.msk", pszBasename);
10,339✔
1609

1610
    std::vector<char> achMskFilename;
20,678✔
1611
    achMskFilename.resize(osMskFilename.size() + 1);
10,339✔
1612
    memcpy(&(achMskFilename[0]), osMskFilename.c_str(),
10,339✔
1613
           osMskFilename.size() + 1);
10,339✔
1614
    bool bExists =
1615
        CPL_TO_BOOL(CPLCheckForFile(&achMskFilename[0], papszSiblingFiles));
10,339✔
1616
    osMskFilename = &achMskFilename[0];
10,339✔
1617

1618
#if !defined(_WIN32)
1619
    if (!bExists && !papszSiblingFiles)
10,339✔
1620
    {
1621
        osMskFilename.Printf("%s.MSK", pszBasename);
2,302✔
1622
        memcpy(&(achMskFilename[0]), osMskFilename.c_str(),
2,302✔
1623
               osMskFilename.size() + 1);
2,302✔
1624
        bExists =
1625
            CPL_TO_BOOL(CPLCheckForFile(&achMskFilename[0], papszSiblingFiles));
2,302✔
1626
        osMskFilename = &achMskFilename[0];
2,302✔
1627
    }
1628
#endif
1629

1630
    if (!bExists)
10,339✔
1631
        return FALSE;
10,302✔
1632

1633
    /* -------------------------------------------------------------------- */
1634
    /*      Open the file.                                                  */
1635
    /* -------------------------------------------------------------------- */
1636
    poMaskDS = GDALDataset::Open(
37✔
1637
        osMskFilename,
1638
        GDAL_OF_RASTER | (poDS->GetAccess() == GA_Update ? GDAL_OF_UPDATE : 0),
37✔
1639
        nullptr, nullptr, papszInitSiblingFiles);
37✔
1640
    CPLAssert(poMaskDS != poDS);
37✔
1641

1642
    if (poMaskDS == nullptr)
37✔
1643
        return FALSE;
1✔
1644

1645
    bOwnMaskDS = true;
36✔
1646

1647
    return TRUE;
36✔
1648
}
1649

1650
/************************************************************************/
1651
/*                    GDALGetNormalizedOvrResampling()                  */
1652
/************************************************************************/
1653

1654
std::string GDALGetNormalizedOvrResampling(const char *pszResampling)
979✔
1655
{
1656
    if (pszResampling &&
979✔
1657
        EQUAL(pszResampling, "AVERAGE_BIT2GRAYSCALE_MINISWHITE"))
979✔
1658
        return "AVERAGE_BIT2GRAYSCALE_MINISWHITE";
×
1659
    else if (pszResampling && STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
979✔
1660
        return "AVERAGE_BIT2GRAYSCALE";
8✔
1661
    else if (pszResampling && STARTS_WITH_CI(pszResampling, "NEAR"))
971✔
1662
        return "NEAREST";
417✔
1663
    else if (pszResampling && EQUAL(pszResampling, "AVERAGE_MAGPHASE"))
554✔
1664
        return "AVERAGE_MAGPHASE";
×
1665
    else if (pszResampling && STARTS_WITH_CI(pszResampling, "AVER"))
554✔
1666
        return "AVERAGE";
254✔
1667
    else if (pszResampling && !EQUAL(pszResampling, "NONE"))
300✔
1668
    {
1669
        return CPLString(pszResampling).toupper();
342✔
1670
    }
1671
    return std::string();
129✔
1672
}
1673

1674
//! @endcond
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc