Coverage Report

Created: 2025-07-23 09:13

/src/gdal/frmts/gtiff/gt_overview.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  GeoTIFF Driver
4
 * Purpose:  Code to build overviews of external databases as a TIFF file.
5
 *           Only used by the GDALDefaultOverviews::BuildOverviews() method.
6
 * Author:   Frank Warmerdam, warmerdam@pobox.com
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2000, Frank Warmerdam
10
 * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14
15
#include "cpl_port.h"
16
17
#include "gt_overview.h"
18
19
#include <cstdlib>
20
#include <cstring>
21
22
#include <algorithm>
23
#include <string>
24
25
#include "cpl_conv.h"
26
#include "cpl_error.h"
27
#include "cpl_progress.h"
28
#include "cpl_string.h"
29
#include "cpl_vsi.h"
30
#include "gdal.h"
31
#include "gdal_priv.h"
32
#include "gtiff.h"
33
#include "gtiffdataset.h"
34
#include "tiff.h"
35
#include "tiffvers.h"
36
#include "tifvsi.h"
37
#include "tif_jxl.h"
38
#include "xtiffio.h"
39
40
// TODO(schwehr): Explain why 128 and not 127.
41
constexpr int knMaxOverviews = 128;
42
43
/************************************************************************/
44
/*                         GTIFFWriteDirectory()                        */
45
/*                                                                      */
46
/*      Create a new directory, without any image data for an overview  */
47
/*      or a mask                                                       */
48
/*      Returns offset of newly created directory, but the              */
49
/*      current directory is reset to be the one in used when this      */
50
/*      function is called.                                             */
51
/************************************************************************/
52
53
toff_t GTIFFWriteDirectory(TIFF *hTIFF, int nSubfileType, int nXSize,
54
                           int nYSize, int nBitsPerPixel, int nPlanarConfig,
55
                           int nSamples, int nBlockXSize, int nBlockYSize,
56
                           int bTiled, int nCompressFlag, int nPhotometric,
57
                           int nSampleFormat, int nPredictor,
58
                           unsigned short *panRed, unsigned short *panGreen,
59
                           unsigned short *panBlue, int nExtraSamples,
60
                           unsigned short *panExtraSampleValues,
61
                           const char *pszMetadata, const char *pszJPEGQuality,
62
                           const char *pszJPEGTablesMode, const char *pszNoData,
63
                           const uint32_t *panLercAddCompressionAndVersion,
64
                           bool bDeferStrileArrayWriting)
65
66
133
{
67
133
    const toff_t nBaseDirOffset = TIFFCurrentDirOffset(hTIFF);
68
69
#if !(defined(INTERNAL_LIBTIFF) || TIFFLIB_VERSION > 20240911)
70
    // This is a bit of a hack to cause (*tif->tif_cleanup)(tif); to be
71
    // called. See https://trac.osgeo.org/gdal/ticket/2055
72
    // Fixed in libtiff > 4.7.0
73
    TIFFSetField(hTIFF, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
74
    TIFFFreeDirectory(hTIFF);
75
#endif
76
77
133
    TIFFCreateDirectory(hTIFF);
78
79
    /* -------------------------------------------------------------------- */
80
    /*      Setup TIFF fields.                                              */
81
    /* -------------------------------------------------------------------- */
82
133
    TIFFSetField(hTIFF, TIFFTAG_IMAGEWIDTH, nXSize);
83
133
    TIFFSetField(hTIFF, TIFFTAG_IMAGELENGTH, nYSize);
84
133
    if (nSamples == 1)
85
133
        TIFFSetField(hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
86
0
    else
87
0
        TIFFSetField(hTIFF, TIFFTAG_PLANARCONFIG, nPlanarConfig);
88
89
133
    TIFFSetField(hTIFF, TIFFTAG_BITSPERSAMPLE, nBitsPerPixel);
90
133
    TIFFSetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, nSamples);
91
133
    TIFFSetField(hTIFF, TIFFTAG_COMPRESSION, nCompressFlag);
92
133
    TIFFSetField(hTIFF, TIFFTAG_PHOTOMETRIC, nPhotometric);
93
133
    TIFFSetField(hTIFF, TIFFTAG_SAMPLEFORMAT, nSampleFormat);
94
95
133
    if (bTiled)
96
0
    {
97
0
        TIFFSetField(hTIFF, TIFFTAG_TILEWIDTH, nBlockXSize);
98
0
        TIFFSetField(hTIFF, TIFFTAG_TILELENGTH, nBlockYSize);
99
0
    }
100
133
    else
101
133
    {
102
133
        TIFFSetField(hTIFF, TIFFTAG_ROWSPERSTRIP, nBlockYSize);
103
133
    }
104
105
133
    TIFFSetField(hTIFF, TIFFTAG_SUBFILETYPE, nSubfileType);
106
107
133
    if (panExtraSampleValues != nullptr)
108
0
    {
109
0
        TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples,
110
0
                     panExtraSampleValues);
111
0
    }
112
113
133
    if (GTIFFSupportsPredictor(nCompressFlag))
114
133
        TIFFSetField(hTIFF, TIFFTAG_PREDICTOR, nPredictor);
115
116
    /* -------------------------------------------------------------------- */
117
    /*      Write color table if one is present.                            */
118
    /* -------------------------------------------------------------------- */
119
133
    if (panRed != nullptr)
120
0
    {
121
0
        TIFFSetField(hTIFF, TIFFTAG_COLORMAP, panRed, panGreen, panBlue);
122
0
    }
123
124
    /* -------------------------------------------------------------------- */
125
    /*      Write metadata if we have some.                                 */
126
    /* -------------------------------------------------------------------- */
127
133
    if (pszMetadata && strlen(pszMetadata) > 0)
128
0
        TIFFSetField(hTIFF, TIFFTAG_GDAL_METADATA, pszMetadata);
129
130
    /* -------------------------------------------------------------------- */
131
    /*      Write JPEG tables if needed.                                    */
132
    /* -------------------------------------------------------------------- */
133
133
    if (nCompressFlag == COMPRESSION_JPEG)
134
0
    {
135
0
        GTiffWriteJPEGTables(hTIFF,
136
0
                             (nPhotometric == PHOTOMETRIC_RGB) ? "RGB"
137
0
                             : (nPhotometric == PHOTOMETRIC_YCBCR)
138
0
                                 ? "YCBCR"
139
0
                                 : "MINISBLACK",
140
0
                             pszJPEGQuality, pszJPEGTablesMode);
141
142
0
        if (nPhotometric == PHOTOMETRIC_YCBCR)
143
0
        {
144
            // Explicitly register the subsampling so that JPEGFixupTags
145
            // is a no-op (helps for cloud optimized geotiffs)
146
0
            TIFFSetField(hTIFF, TIFFTAG_YCBCRSUBSAMPLING, 2, 2);
147
0
        }
148
0
    }
149
150
133
    if (nCompressFlag == COMPRESSION_LERC && panLercAddCompressionAndVersion)
151
0
    {
152
0
        TIFFSetField(hTIFF, TIFFTAG_LERC_PARAMETERS, 2,
153
0
                     panLercAddCompressionAndVersion);
154
0
    }
155
156
    /* -------------------------------------------------------------------- */
157
    /*      Write no data value if we have one.                             */
158
    /* -------------------------------------------------------------------- */
159
133
    if (pszNoData != nullptr)
160
0
    {
161
0
        TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA, pszNoData);
162
0
    }
163
164
133
    if (bDeferStrileArrayWriting)
165
0
    {
166
0
        TIFFDeferStrileArrayWriting(hTIFF);
167
0
    }
168
169
    /* -------------------------------------------------------------------- */
170
    /*      Write directory, and return byte offset.                        */
171
    /* -------------------------------------------------------------------- */
172
133
    if (TIFFWriteCheck(hTIFF, bTiled, "GTIFFWriteDirectory") == 0)
173
0
    {
174
0
        TIFFSetSubDirectory(hTIFF, nBaseDirOffset);
175
0
        return 0;
176
0
    }
177
178
133
    TIFFWriteDirectory(hTIFF);
179
133
    const tdir_t nNumberOfDirs = TIFFNumberOfDirectories(hTIFF);
180
133
    if (nNumberOfDirs > 0)  // always true, but to please Coverity
181
133
    {
182
133
        TIFFSetDirectory(hTIFF, static_cast<tdir_t>(nNumberOfDirs - 1));
183
133
    }
184
185
133
    const toff_t nOffset = TIFFCurrentDirOffset(hTIFF);
186
187
133
    TIFFSetSubDirectory(hTIFF, nBaseDirOffset);
188
189
133
    return nOffset;
190
133
}
191
192
/************************************************************************/
193
/*                     GTIFFBuildOverviewMetadata()                     */
194
/************************************************************************/
195
196
void GTIFFBuildOverviewMetadata(const char *pszResampling,
197
                                GDALDataset *poBaseDS, bool bIsForMaskBand,
198
                                CPLString &osMetadata)
199
200
0
{
201
0
    osMetadata = "<GDALMetadata>";
202
203
0
    auto osNormalizedResampling = GDALGetNormalizedOvrResampling(pszResampling);
204
0
    if (!osNormalizedResampling.empty())
205
0
    {
206
0
        osMetadata += "<Item name=\"RESAMPLING\" sample=\"0\">";
207
0
        osMetadata += osNormalizedResampling;
208
0
        osMetadata += "</Item>";
209
0
    }
210
211
0
    if (bIsForMaskBand)
212
0
    {
213
0
        osMetadata += "<Item name=\"INTERNAL_MASK_FLAGS_1\">2</Item>";
214
0
    }
215
0
    else if (poBaseDS->GetMetadataItem("INTERNAL_MASK_FLAGS_1"))
216
0
    {
217
0
        for (int iBand = 0; iBand < 200; iBand++)
218
0
        {
219
0
            CPLString osItem;
220
0
            CPLString osName;
221
222
0
            osName.Printf("INTERNAL_MASK_FLAGS_%d", iBand + 1);
223
0
            if (poBaseDS->GetMetadataItem(osName))
224
0
            {
225
0
                osItem.Printf("<Item name=\"%s\">%s</Item>", osName.c_str(),
226
0
                              poBaseDS->GetMetadataItem(osName));
227
0
                osMetadata += osItem;
228
0
            }
229
0
        }
230
0
    }
231
232
0
    const char *pszNoDataValues = poBaseDS->GetMetadataItem("NODATA_VALUES");
233
0
    if (pszNoDataValues)
234
0
    {
235
0
        CPLString osItem;
236
0
        osItem.Printf("<Item name=\"NODATA_VALUES\">%s</Item>",
237
0
                      pszNoDataValues);
238
0
        osMetadata += osItem;
239
0
    }
240
241
0
    if (!EQUAL(osMetadata, "<GDALMetadata>"))
242
0
        osMetadata += "</GDALMetadata>";
243
0
    else
244
0
        osMetadata = "";
245
0
}
246
247
/************************************************************************/
248
/*                      GTIFFGetMaxColorChannels()                      */
249
/************************************************************************/
250
251
/*
252
 * Return the maximum number of color channels specified for a given photometric
253
 * type. 0 is returned if photometric type isn't supported or no default value
254
 * is defined by the specification.
255
 */
256
static int GTIFFGetMaxColorChannels(int photometric)
257
0
{
258
0
    switch (photometric)
259
0
    {
260
0
        case PHOTOMETRIC_PALETTE:
261
0
        case PHOTOMETRIC_MINISWHITE:
262
0
        case PHOTOMETRIC_MINISBLACK:
263
0
            return 1;
264
0
        case PHOTOMETRIC_YCBCR:
265
0
        case PHOTOMETRIC_RGB:
266
0
        case PHOTOMETRIC_CIELAB:
267
0
        case PHOTOMETRIC_LOGLUV:
268
0
        case PHOTOMETRIC_ITULAB:
269
0
        case PHOTOMETRIC_ICCLAB:
270
0
            return 3;
271
0
        case PHOTOMETRIC_SEPARATED:
272
0
        case PHOTOMETRIC_MASK:
273
0
            return 4;
274
0
        case PHOTOMETRIC_LOGL:
275
0
        default:
276
0
            return 0;
277
0
    }
278
0
}
279
280
/************************************************************************/
281
/*                        GTIFFBuildOverviews()                         */
282
/************************************************************************/
283
284
CPLErr GTIFFBuildOverviews(const char *pszFilename, int nBands,
285
                           GDALRasterBand *const *papoBandList, int nOverviews,
286
                           const int *panOverviewList,
287
                           const char *pszResampling,
288
                           GDALProgressFunc pfnProgress, void *pProgressData,
289
                           CSLConstList papszOptions)
290
291
0
{
292
0
    return GTIFFBuildOverviewsEx(pszFilename, nBands, papoBandList, nOverviews,
293
0
                                 panOverviewList, nullptr, pszResampling,
294
0
                                 papszOptions, pfnProgress, pProgressData);
295
0
}
296
297
CPLErr GTIFFBuildOverviewsEx(const char *pszFilename, int nBands,
298
                             GDALRasterBand *const *papoBandList,
299
                             int nOverviews, const int *panOverviewList,
300
                             const std::pair<int, int> *pasOverviewSize,
301
                             const char *pszResampling,
302
                             const char *const *papszOptions,
303
                             GDALProgressFunc pfnProgress, void *pProgressData)
304
0
{
305
0
    if (nBands == 0 || nOverviews == 0)
306
0
        return CE_None;
307
308
0
    CPLAssert((panOverviewList != nullptr) ^ (pasOverviewSize != nullptr));
309
310
0
    GTiffOneTimeInit();
311
312
0
    TIFF *hOTIFF = nullptr;
313
0
    int nBitsPerPixel = 0;
314
0
    int nCompression = COMPRESSION_NONE;
315
0
    uint16_t nPhotometric = 0;
316
0
    int nSampleFormat = 0;
317
0
    uint16_t nPlanarConfig = 0;
318
0
    int iOverview = 0;
319
0
    int nXSize = 0;
320
0
    int nYSize = 0;
321
322
    /* -------------------------------------------------------------------- */
323
    /*      Verify that the list of bands is suitable for emitting in       */
324
    /*      TIFF file.                                                      */
325
    /* -------------------------------------------------------------------- */
326
0
    for (int iBand = 0; iBand < nBands; iBand++)
327
0
    {
328
0
        int nBandBits = 0;
329
0
        int nBandFormat = 0;
330
0
        GDALRasterBand *hBand = papoBandList[iBand];
331
332
0
        switch (hBand->GetRasterDataType())
333
0
        {
334
0
            case GDT_Byte:
335
0
                nBandBits = 8;
336
0
                nBandFormat = SAMPLEFORMAT_UINT;
337
0
                break;
338
339
0
            case GDT_Int8:
340
0
                nBandBits = 8;
341
0
                nBandFormat = SAMPLEFORMAT_INT;
342
0
                break;
343
344
0
            case GDT_UInt16:
345
0
                nBandBits = 16;
346
0
                nBandFormat = SAMPLEFORMAT_UINT;
347
0
                break;
348
349
0
            case GDT_Int16:
350
0
                nBandBits = 16;
351
0
                nBandFormat = SAMPLEFORMAT_INT;
352
0
                break;
353
354
0
            case GDT_UInt32:
355
0
                nBandBits = 32;
356
0
                nBandFormat = SAMPLEFORMAT_UINT;
357
0
                break;
358
359
0
            case GDT_Int32:
360
0
                nBandBits = 32;
361
0
                nBandFormat = SAMPLEFORMAT_INT;
362
0
                break;
363
364
0
            case GDT_UInt64:
365
0
                nBandBits = 64;
366
0
                nBandFormat = SAMPLEFORMAT_UINT;
367
0
                break;
368
369
0
            case GDT_Int64:
370
0
                nBandBits = 64;
371
0
                nBandFormat = SAMPLEFORMAT_INT;
372
0
                break;
373
374
0
            case GDT_Float16:
375
                // Convert Float16 to float.
376
                // TODO: At some point we should support Float16.
377
0
                nBandBits = 32;
378
0
                nBandFormat = SAMPLEFORMAT_IEEEFP;
379
0
                break;
380
381
0
            case GDT_Float32:
382
0
                nBandBits = 32;
383
0
                nBandFormat = SAMPLEFORMAT_IEEEFP;
384
0
                break;
385
386
0
            case GDT_Float64:
387
0
                nBandBits = 64;
388
0
                nBandFormat = SAMPLEFORMAT_IEEEFP;
389
0
                break;
390
391
0
            case GDT_CInt16:
392
0
                nBandBits = 32;
393
0
                nBandFormat = SAMPLEFORMAT_COMPLEXINT;
394
0
                break;
395
396
0
            case GDT_CInt32:
397
0
                nBandBits = 64;
398
0
                nBandFormat = SAMPLEFORMAT_COMPLEXINT;
399
0
                break;
400
401
0
            case GDT_CFloat16:
402
                // Convert Float16 to float.
403
                // TODO: At some point we should support Float16.
404
0
                nBandBits = 64;
405
0
                nBandFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
406
0
                break;
407
408
0
            case GDT_CFloat32:
409
0
                nBandBits = 64;
410
0
                nBandFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
411
0
                break;
412
413
0
            case GDT_CFloat64:
414
0
                nBandBits = 128;
415
0
                nBandFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
416
0
                break;
417
418
0
            case GDT_Unknown:
419
0
            case GDT_TypeCount:
420
0
                CPLAssert(false);
421
0
                return CE_Failure;
422
0
        }
423
424
0
        if (hBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE"))
425
0
        {
426
0
            nBandBits =
427
0
                atoi(hBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE"));
428
429
0
            if (nBandBits == 1 && STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
430
0
                nBandBits = 8;
431
0
        }
432
433
0
        if (iBand == 0)
434
0
        {
435
0
            nBitsPerPixel = nBandBits;
436
0
            nSampleFormat = nBandFormat;
437
0
            nXSize = hBand->GetXSize();
438
0
            nYSize = hBand->GetYSize();
439
0
        }
440
0
        else if (nBitsPerPixel != nBandBits || nSampleFormat != nBandFormat)
441
0
        {
442
0
            CPLError(CE_Failure, CPLE_NotSupported,
443
0
                     "GTIFFBuildOverviews() doesn't support a mixture of band"
444
0
                     " data types.");
445
0
            return CE_Failure;
446
0
        }
447
0
        else if (hBand->GetColorTable() != nullptr)
448
0
        {
449
0
            CPLError(CE_Failure, CPLE_NotSupported,
450
0
                     "GTIFFBuildOverviews() doesn't support building"
451
0
                     " overviews of multiple colormapped bands.");
452
0
            return CE_Failure;
453
0
        }
454
0
        else if (hBand->GetXSize() != nXSize || hBand->GetYSize() != nYSize)
455
0
        {
456
0
            CPLError(CE_Failure, CPLE_NotSupported,
457
0
                     "GTIFFBuildOverviews() doesn't support building"
458
0
                     " overviews of different sized bands.");
459
0
            return CE_Failure;
460
0
        }
461
0
    }
462
463
0
    const auto GetOptionValue =
464
0
        [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
465
0
                       const char **ppszKeyUsed = nullptr)
466
0
    {
467
0
        const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
468
0
        if (pszVal)
469
0
        {
470
0
            if (ppszKeyUsed)
471
0
                *ppszKeyUsed = pszOptionKey;
472
0
            return pszVal;
473
0
        }
474
0
        pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
475
0
        if (pszVal && ppszKeyUsed)
476
0
            *ppszKeyUsed = pszConfigOptionKey;
477
0
        return pszVal;
478
0
    };
479
480
    /* -------------------------------------------------------------------- */
481
    /*      Use specified compression method.                               */
482
    /* -------------------------------------------------------------------- */
483
0
    const char *pszCompressKey = "";
484
0
    const char *pszCompress =
485
0
        GetOptionValue("COMPRESS", "COMPRESS_OVERVIEW", &pszCompressKey);
486
487
0
    if (pszCompress != nullptr && pszCompress[0] != '\0')
488
0
    {
489
0
        nCompression = GTIFFGetCompressionMethod(pszCompress, pszCompressKey);
490
0
        if (nCompression < 0)
491
0
            return CE_Failure;
492
0
    }
493
494
0
    if (nCompression == COMPRESSION_JPEG && nBitsPerPixel > 8)
495
0
    {
496
0
        if (nBitsPerPixel > 16)
497
0
        {
498
0
            CPLError(CE_Failure, CPLE_NotSupported,
499
0
                     "GTIFFBuildOverviews() doesn't support building"
500
0
                     " JPEG compressed overviews of nBitsPerPixel > 16.");
501
0
            return CE_Failure;
502
0
        }
503
504
0
        nBitsPerPixel = 12;
505
0
    }
506
507
    /* -------------------------------------------------------------------- */
508
    /*      Figure out the planar configuration to use.                     */
509
    /* -------------------------------------------------------------------- */
510
0
    if (nBands == 1)
511
0
        nPlanarConfig = PLANARCONFIG_CONTIG;
512
0
    else
513
0
        nPlanarConfig = PLANARCONFIG_SEPARATE;
514
515
0
    bool bSourceIsPixelInterleaved = false;
516
0
    bool bSourceIsJPEG2000 = false;
517
0
    if (nBands > 1)
518
0
    {
519
0
        GDALDataset *poSrcDS = papoBandList[0]->GetDataset();
520
0
        if (poSrcDS)
521
0
        {
522
0
            const char *pszSrcInterleave =
523
0
                poSrcDS->GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE");
524
0
            if (pszSrcInterleave && EQUAL(pszSrcInterleave, "PIXEL"))
525
0
            {
526
0
                bSourceIsPixelInterleaved = true;
527
0
            }
528
0
        }
529
530
0
        const char *pszSrcCompression =
531
0
            papoBandList[0]->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE");
532
0
        if (pszSrcCompression)
533
0
        {
534
0
            bSourceIsJPEG2000 = EQUAL(pszSrcCompression, "JPEG2000");
535
0
        }
536
0
        if (bSourceIsPixelInterleaved && bSourceIsJPEG2000)
537
0
        {
538
0
            nPlanarConfig = PLANARCONFIG_CONTIG;
539
0
        }
540
0
        else if (nCompression == COMPRESSION_WEBP ||
541
0
                 nCompression == COMPRESSION_JXL ||
542
0
                 nCompression == COMPRESSION_JXL_DNG_1_7)
543
0
        {
544
0
            nPlanarConfig = PLANARCONFIG_CONTIG;
545
0
        }
546
0
    }
547
548
0
    const char *pszInterleave =
549
0
        GetOptionValue("INTERLEAVE", "INTERLEAVE_OVERVIEW");
550
0
    if (pszInterleave != nullptr && pszInterleave[0] != '\0')
551
0
    {
552
0
        if (EQUAL(pszInterleave, "PIXEL"))
553
0
            nPlanarConfig = PLANARCONFIG_CONTIG;
554
0
        else if (EQUAL(pszInterleave, "BAND"))
555
0
            nPlanarConfig = PLANARCONFIG_SEPARATE;
556
0
        else
557
0
        {
558
0
            CPLError(CE_Failure, CPLE_AppDefined,
559
0
                     "INTERLEAVE_OVERVIEW=%s unsupported, "
560
0
                     "value must be PIXEL or BAND. ignoring",
561
0
                     pszInterleave);
562
0
        }
563
0
    }
564
565
    /* -------------------------------------------------------------------- */
566
    /*      Figure out the photometric interpretation to use.               */
567
    /* -------------------------------------------------------------------- */
568
0
    if (nBands == 3)
569
0
        nPhotometric = PHOTOMETRIC_RGB;
570
0
    else if (papoBandList[0]->GetColorTable() != nullptr &&
571
0
             (papoBandList[0]->GetRasterDataType() == GDT_Byte ||
572
0
              papoBandList[0]->GetRasterDataType() == GDT_UInt16) &&
573
0
             !STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
574
0
    {
575
        // Would also apply to other lossy compression scheme, but for JPEG,
576
        // this at least avoids a later cryptic error message from libtiff:
577
        // "JPEGSetupEncode:PhotometricInterpretation 3 not allowed for JPEG"
578
0
        if (nCompression == COMPRESSION_JPEG)
579
0
        {
580
0
            CPLError(CE_Failure, CPLE_AppDefined,
581
0
                     "Cannot create JPEG compressed overviews on a raster "
582
0
                     "with a color table");
583
0
            return CE_Failure;
584
0
        }
585
586
0
        nPhotometric = PHOTOMETRIC_PALETTE;
587
        // Color map is set up after
588
0
    }
589
0
    else if (nBands >= 3 &&
590
0
             papoBandList[0]->GetColorInterpretation() == GCI_RedBand &&
591
0
             papoBandList[1]->GetColorInterpretation() == GCI_GreenBand &&
592
0
             papoBandList[2]->GetColorInterpretation() == GCI_BlueBand)
593
0
    {
594
0
        nPhotometric = PHOTOMETRIC_RGB;
595
0
    }
596
0
    else
597
0
        nPhotometric = PHOTOMETRIC_MINISBLACK;
598
599
0
    const char *pszOptionKey = "";
600
0
    const char *pszPhotometric =
601
0
        GetOptionValue("PHOTOMETRIC", "PHOTOMETRIC_OVERVIEW", &pszOptionKey);
602
0
    if (!GTIFFUpdatePhotometric(pszPhotometric, pszOptionKey, nCompression,
603
0
                                pszInterleave, nBands, nPhotometric,
604
0
                                nPlanarConfig))
605
0
    {
606
0
        return CE_Failure;
607
0
    }
608
609
    /* -------------------------------------------------------------------- */
610
    /*      Figure out the predictor value to use.                          */
611
    /* -------------------------------------------------------------------- */
612
0
    int nPredictor = PREDICTOR_NONE;
613
0
    if (GTIFFSupportsPredictor(nCompression))
614
0
    {
615
0
        const char *pszPredictor =
616
0
            GetOptionValue("PREDICTOR", "PREDICTOR_OVERVIEW");
617
0
        if (pszPredictor != nullptr)
618
0
        {
619
0
            nPredictor = atoi(pszPredictor);
620
0
        }
621
0
    }
622
623
    /* -------------------------------------------------------------------- */
624
    /*      Create the file, if it does not already exist.                  */
625
    /* -------------------------------------------------------------------- */
626
0
    VSIStatBufL sStatBuf;
627
0
    VSILFILE *fpL = nullptr;
628
629
0
    bool bCreateBigTIFF = false;
630
0
    if (VSIStatExL(pszFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
631
0
    {
632
        /* --------------------------------------------------------------------
633
         */
634
        /*      Compute the uncompressed size. */
635
        /* --------------------------------------------------------------------
636
         */
637
0
        double dfUncompressedOverviewSize = 0;
638
0
        int nDataTypeSize =
639
0
            GDALGetDataTypeSizeBytes(papoBandList[0]->GetRasterDataType());
640
641
0
        for (iOverview = 0; iOverview < nOverviews; iOverview++)
642
0
        {
643
0
            const int nOXSize =
644
0
                panOverviewList
645
0
                    ? DIV_ROUND_UP(nXSize, panOverviewList[iOverview])
646
0
                    :
647
                    // cppcheck-suppress nullPointer
648
0
                    pasOverviewSize[iOverview].first;
649
0
            const int nOYSize =
650
0
                panOverviewList
651
0
                    ? DIV_ROUND_UP(nYSize, panOverviewList[iOverview])
652
0
                    :
653
                    // cppcheck-suppress nullPointer
654
0
                    pasOverviewSize[iOverview].second;
655
656
0
            dfUncompressedOverviewSize +=
657
0
                nOXSize * static_cast<double>(nOYSize) * nBands * nDataTypeSize;
658
0
        }
659
660
        /* --------------------------------------------------------------------
661
         */
662
        /*      Should the file be created as a bigtiff file? */
663
        /* --------------------------------------------------------------------
664
         */
665
0
        const char *pszBIGTIFF = GetOptionValue("BIGTIFF", "BIGTIFF_OVERVIEW");
666
667
0
        if (pszBIGTIFF == nullptr)
668
0
            pszBIGTIFF = "IF_SAFER";
669
670
0
        if (EQUAL(pszBIGTIFF, "IF_NEEDED"))
671
0
        {
672
0
            if (nCompression == COMPRESSION_NONE &&
673
0
                dfUncompressedOverviewSize > 4200000000.0)
674
0
                bCreateBigTIFF = true;
675
0
        }
676
0
        else if (EQUAL(pszBIGTIFF, "IF_SAFER"))
677
0
        {
678
            // Look at the size of the base image and suppose that
679
            // the added overview levels won't be more than 1/2 of
680
            // the size of the base image. The theory says 1/3 of the
681
            // base image size if the overview levels are 2, 4, 8, 16.
682
            // Thus take 1/2 as the security margin for 1/3.
683
0
            const double dfUncompressedImageSize =
684
0
                nXSize * static_cast<double>(nYSize) * nBands * nDataTypeSize;
685
0
            if (dfUncompressedImageSize * 0.5 > 4200000000.0)
686
0
                bCreateBigTIFF = true;
687
0
        }
688
0
        else
689
0
        {
690
0
            bCreateBigTIFF = CPLTestBool(pszBIGTIFF);
691
0
            if (!bCreateBigTIFF && nCompression == COMPRESSION_NONE &&
692
0
                dfUncompressedOverviewSize > 4200000000.0)
693
0
            {
694
0
                CPLError(CE_Failure, CPLE_NotSupported,
695
0
                         "The overview file will be larger than 4GB, "
696
0
                         "so BigTIFF is necessary.  "
697
0
                         "Creation failed.");
698
0
                return CE_Failure;
699
0
            }
700
0
        }
701
702
0
        if (bCreateBigTIFF)
703
0
            CPLDebug("GTiff", "File being created as a BigTIFF.");
704
705
0
        fpL = VSIFOpenL(pszFilename, "w+");
706
0
        if (fpL == nullptr)
707
0
            hOTIFF = nullptr;
708
0
        else
709
0
            hOTIFF =
710
0
                VSI_TIFFOpen(pszFilename, bCreateBigTIFF ? "w+8" : "w+", fpL);
711
0
        if (hOTIFF == nullptr)
712
0
        {
713
0
            if (CPLGetLastErrorNo() == 0)
714
0
                CPLError(CE_Failure, CPLE_OpenFailed,
715
0
                         "Attempt to create new tiff file `%s' "
716
0
                         "failed in VSI_TIFFOpen().",
717
0
                         pszFilename);
718
0
            if (fpL != nullptr)
719
0
                CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
720
0
            return CE_Failure;
721
0
        }
722
0
    }
723
    /* -------------------------------------------------------------------- */
724
    /*      Otherwise just open it for update access.                       */
725
    /* -------------------------------------------------------------------- */
726
0
    else
727
0
    {
728
0
        fpL = VSIFOpenL(pszFilename, "r+");
729
0
        if (fpL == nullptr)
730
0
            hOTIFF = nullptr;
731
0
        else
732
0
        {
733
0
            GByte abyBuffer[4] = {0};
734
0
            VSIFReadL(abyBuffer, 1, 4, fpL);
735
0
            VSIFSeekL(fpL, 0, SEEK_SET);
736
0
            bCreateBigTIFF = abyBuffer[2] == 43 || abyBuffer[3] == 43;
737
0
            hOTIFF = VSI_TIFFOpen(pszFilename, "r+", fpL);
738
0
        }
739
0
        if (hOTIFF == nullptr)
740
0
        {
741
0
            if (CPLGetLastErrorNo() == 0)
742
0
                CPLError(CE_Failure, CPLE_OpenFailed,
743
0
                         "Attempt to create new tiff file `%s' "
744
0
                         "failed in VSI_TIFFOpen().",
745
0
                         pszFilename);
746
0
            if (fpL != nullptr)
747
0
                CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
748
0
            return CE_Failure;
749
0
        }
750
0
    }
751
752
    /* -------------------------------------------------------------------- */
753
    /*      Do we have a palette?  If so, create a TIFF compatible version. */
754
    /* -------------------------------------------------------------------- */
755
0
    unsigned short *panRed = nullptr;
756
0
    unsigned short *panGreen = nullptr;
757
0
    unsigned short *panBlue = nullptr;
758
759
0
    if (nPhotometric == PHOTOMETRIC_PALETTE)
760
0
    {
761
0
        GDALColorTable *poCT = papoBandList[0]->GetColorTable();
762
0
        int nColorCount = 65536;
763
764
0
        if (nBitsPerPixel <= 8)
765
0
            nColorCount = 256;
766
767
0
        panRed = static_cast<unsigned short *>(
768
0
            CPLCalloc(nColorCount, sizeof(unsigned short)));
769
0
        panGreen = static_cast<unsigned short *>(
770
0
            CPLCalloc(nColorCount, sizeof(unsigned short)));
771
0
        panBlue = static_cast<unsigned short *>(
772
0
            CPLCalloc(nColorCount, sizeof(unsigned short)));
773
774
0
        const int nColorTableMultiplier = std::max(
775
0
            1,
776
0
            std::min(
777
0
                257,
778
0
                atoi(CSLFetchNameValueDef(
779
0
                    papszOptions, "COLOR_TABLE_MULTIPLIER",
780
0
                    CPLSPrintf(
781
0
                        "%d",
782
0
                        GTiffDataset::DEFAULT_COLOR_TABLE_MULTIPLIER_257)))));
783
784
0
        for (int iColor = 0; iColor < nColorCount; iColor++)
785
0
        {
786
0
            GDALColorEntry sRGB = {0, 0, 0, 0};
787
788
0
            if (poCT->GetColorEntryAsRGB(iColor, &sRGB))
789
0
            {
790
0
                panRed[iColor] = GTiffDataset::ClampCTEntry(
791
0
                    iColor, 1, sRGB.c1, nColorTableMultiplier);
792
0
                panGreen[iColor] = GTiffDataset::ClampCTEntry(
793
0
                    iColor, 2, sRGB.c2, nColorTableMultiplier);
794
0
                panBlue[iColor] = GTiffDataset::ClampCTEntry(
795
0
                    iColor, 3, sRGB.c3, nColorTableMultiplier);
796
0
            }
797
0
        }
798
0
    }
799
800
    /* -------------------------------------------------------------------- */
801
    /*      Do we need some metadata for the overviews?                     */
802
    /* -------------------------------------------------------------------- */
803
0
    CPLString osMetadata;
804
0
    GDALDataset *poBaseDS = papoBandList[0]->GetDataset();
805
0
    if (poBaseDS)
806
0
    {
807
0
        const bool bIsForMaskBand =
808
0
            nBands == 1 && papoBandList[0]->IsMaskBand();
809
0
        GTIFFBuildOverviewMetadata(pszResampling, poBaseDS, bIsForMaskBand,
810
0
                                   osMetadata);
811
0
    }
812
813
0
    if (poBaseDS != nullptr && poBaseDS->GetRasterCount() == nBands)
814
0
    {
815
0
        const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
816
0
            GDALDataset::ToHandle(poBaseDS),
817
0
            static_cast<uint16_t>(nPhotometric), nullptr);
818
0
        if (!bStandardColorInterp)
819
0
        {
820
0
            if (osMetadata.size() >= strlen("</GDALMetadata>") &&
821
0
                osMetadata.substr(osMetadata.size() -
822
0
                                  strlen("</GDALMetadata>")) ==
823
0
                    "</GDALMetadata>")
824
0
            {
825
0
                osMetadata.resize(osMetadata.size() -
826
0
                                  strlen("</GDALMetadata>"));
827
0
            }
828
0
            else
829
0
            {
830
0
                CPLAssert(osMetadata.empty());
831
0
                osMetadata = "<GDALMetadata>";
832
0
            }
833
0
            for (int i = 0; i < poBaseDS->GetRasterCount(); ++i)
834
0
            {
835
0
                const GDALColorInterp eInterp =
836
0
                    poBaseDS->GetRasterBand(i + 1)->GetColorInterpretation();
837
0
                osMetadata +=
838
0
                    CPLSPrintf("<Item sample=\"%d\" name=\"COLORINTERP\" "
839
0
                               "role=\"colorinterp\">",
840
0
                               i);
841
0
                osMetadata += GDALGetColorInterpretationName(eInterp);
842
0
                osMetadata += "</Item>";
843
0
            }
844
0
            osMetadata += "</GDALMetadata>";
845
0
        }
846
0
    }
847
848
    /* -------------------------------------------------------------------- */
849
    /*      Loop, creating overviews.                                       */
850
    /* -------------------------------------------------------------------- */
851
0
    int nOvrBlockXSize = 0;
852
0
    int nOvrBlockYSize = 0;
853
0
    GTIFFGetOverviewBlockSize(papoBandList[0], &nOvrBlockXSize,
854
0
                              &nOvrBlockYSize);
855
856
0
    CPLString osNoData;  // don't move this in inner scope
857
0
    const char *pszNoData = nullptr;
858
0
    int bNoDataSet = FALSE;
859
0
    const double dfNoDataValue = papoBandList[0]->GetNoDataValue(&bNoDataSet);
860
0
    if (bNoDataSet)
861
0
    {
862
0
        osNoData = GTiffFormatGDALNoDataTagValue(dfNoDataValue);
863
0
        pszNoData = osNoData.c_str();
864
0
    }
865
866
0
    std::vector<uint16_t> anExtraSamples;
867
0
    for (int i = GTIFFGetMaxColorChannels(nPhotometric) + 1; i <= nBands; i++)
868
0
    {
869
0
        if (papoBandList[i - 1]->GetColorInterpretation() == GCI_AlphaBand)
870
0
        {
871
0
            anExtraSamples.push_back(GTiffGetAlphaValue(
872
0
                GetOptionValue("ALPHA", "GTIFF_ALPHA"), DEFAULT_ALPHA_TYPE));
873
0
        }
874
0
        else
875
0
        {
876
0
            anExtraSamples.push_back(EXTRASAMPLE_UNSPECIFIED);
877
0
        }
878
0
    }
879
880
0
    const uint32_t *panLercAddCompressionAndVersion = nullptr;
881
0
    uint32_t anLercAddCompressionAndVersion[2] = {LERC_VERSION_2_4,
882
0
                                                  LERC_ADD_COMPRESSION_NONE};
883
0
    if (pszCompress && EQUAL(pszCompress, "LERC_DEFLATE"))
884
0
    {
885
0
        anLercAddCompressionAndVersion[1] = LERC_ADD_COMPRESSION_DEFLATE;
886
0
        panLercAddCompressionAndVersion = anLercAddCompressionAndVersion;
887
0
    }
888
0
    else if (pszCompress && EQUAL(pszCompress, "LERC_ZSTD"))
889
0
    {
890
0
        anLercAddCompressionAndVersion[1] = LERC_ADD_COMPRESSION_ZSTD;
891
0
        panLercAddCompressionAndVersion = anLercAddCompressionAndVersion;
892
0
    }
893
894
0
    for (iOverview = 0; iOverview < nOverviews; iOverview++)
895
0
    {
896
0
        const int nOXSize =
897
0
            panOverviewList ? DIV_ROUND_UP(nXSize, panOverviewList[iOverview]) :
898
                            // cppcheck-suppress nullPointer
899
0
                pasOverviewSize[iOverview].first;
900
0
        const int nOYSize =
901
0
            panOverviewList ? DIV_ROUND_UP(nYSize, panOverviewList[iOverview]) :
902
                            // cppcheck-suppress nullPointer
903
0
                pasOverviewSize[iOverview].second;
904
905
0
        const int nTileXCount = DIV_ROUND_UP(nOXSize, nOvrBlockXSize);
906
0
        const int nTileYCount = DIV_ROUND_UP(nOYSize, nOvrBlockYSize);
907
        // libtiff implementation limitation
908
0
        if (nTileXCount > INT_MAX / (bCreateBigTIFF ? 8 : 4) / nTileYCount)
909
0
        {
910
0
            CPLError(CE_Failure, CPLE_NotSupported,
911
0
                     "File too large regarding tile size. This would result "
912
0
                     "in a file with tile arrays larger than 2GB");
913
0
            XTIFFClose(hOTIFF);
914
0
            VSIFCloseL(fpL);
915
0
            return CE_Failure;
916
0
        }
917
918
0
        if (GTIFFWriteDirectory(
919
0
                hOTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize, nBitsPerPixel,
920
0
                nPlanarConfig, nBands, nOvrBlockXSize, nOvrBlockYSize, TRUE,
921
0
                nCompression, nPhotometric, nSampleFormat, nPredictor, panRed,
922
0
                panGreen, panBlue, static_cast<int>(anExtraSamples.size()),
923
0
                anExtraSamples.empty() ? nullptr : anExtraSamples.data(),
924
0
                osMetadata,
925
0
                GetOptionValue("JPEG_QUALITY", "JPEG_QUALITY_OVERVIEW"),
926
0
                GetOptionValue("JPEG_TABLESMODE", "JPEG_TABLESMODE_OVERVIEW"),
927
0
                pszNoData, panLercAddCompressionAndVersion, false) == 0)
928
0
        {
929
0
            XTIFFClose(hOTIFF);
930
0
            VSIFCloseL(fpL);
931
0
            return CE_Failure;
932
0
        }
933
0
    }
934
935
0
    if (panRed)
936
0
    {
937
0
        CPLFree(panRed);
938
0
        CPLFree(panGreen);
939
0
        CPLFree(panBlue);
940
0
        panRed = nullptr;
941
0
        panGreen = nullptr;
942
0
        panBlue = nullptr;
943
0
    }
944
945
0
    XTIFFClose(hOTIFF);
946
0
    if (VSIFCloseL(fpL) != 0)
947
0
        return CE_Failure;
948
0
    fpL = nullptr;
949
950
0
    if (EQUAL(pszResampling, "NONE"))
951
0
    {
952
0
        return CE_None;
953
0
    }
954
955
    /* -------------------------------------------------------------------- */
956
    /*      Open the overview dataset so that we can get at the overview    */
957
    /*      bands.                                                          */
958
    /* -------------------------------------------------------------------- */
959
0
    CPLStringList aosOpenOptions;
960
0
    aosOpenOptions.SetNameValue("NUM_THREADS",
961
0
                                CSLFetchNameValue(papszOptions, "NUM_THREADS"));
962
0
    aosOpenOptions.SetNameValue(
963
0
        "SPARSE_OK", GetOptionValue("SPARSE_OK", "SPARSE_OK_OVERVIEW"));
964
0
    aosOpenOptions.SetNameValue(
965
0
        "@MASK_OVERVIEW_DATASET",
966
0
        CSLFetchNameValue(papszOptions, "MASK_OVERVIEW_DATASET"));
967
0
    GDALDataset *hODS =
968
0
        GDALDataset::Open(pszFilename, GDAL_OF_RASTER | GDAL_OF_UPDATE, nullptr,
969
0
                          aosOpenOptions.List());
970
0
    if (hODS == nullptr)
971
0
        return CE_Failure;
972
973
    /* -------------------------------------------------------------------- */
974
    /*      Do we need to set the jpeg quality?                             */
975
    /* -------------------------------------------------------------------- */
976
0
    TIFF *hTIFF = static_cast<TIFF *>(hODS->GetInternalHandle("TIFF_HANDLE"));
977
978
0
    const char *pszJPEGQuality =
979
0
        GetOptionValue("JPEG_QUALITY", "JPEG_QUALITY_OVERVIEW");
980
0
    if (nCompression == COMPRESSION_JPEG && pszJPEGQuality != nullptr)
981
0
    {
982
0
        const int nJpegQuality = atoi(pszJPEGQuality);
983
0
        TIFFSetField(hTIFF, TIFFTAG_JPEGQUALITY, nJpegQuality);
984
0
        GTIFFSetJpegQuality(GDALDataset::ToHandle(hODS), nJpegQuality);
985
0
    }
986
987
0
    const char *pszWebpLevel =
988
0
        GetOptionValue("WEBP_LEVEL", "WEBP_LEVEL_OVERVIEW");
989
0
    if (nCompression == COMPRESSION_WEBP && pszWebpLevel != nullptr)
990
0
    {
991
0
        const int nWebpLevel = atoi(pszWebpLevel);
992
0
        if (nWebpLevel >= 1)
993
0
        {
994
0
            TIFFSetField(hTIFF, TIFFTAG_WEBP_LEVEL, nWebpLevel);
995
0
            GTIFFSetWebPLevel(GDALDataset::ToHandle(hODS), nWebpLevel);
996
0
        }
997
0
    }
998
999
0
    const char *pszWebpLossless =
1000
0
        GetOptionValue("WEBP_LOSSLESS", "WEBP_LOSSLESS_OVERVIEW");
1001
0
    if (nCompression == COMPRESSION_WEBP && pszWebpLossless != nullptr)
1002
0
    {
1003
0
        const bool bWebpLossless = CPLTestBool(pszWebpLossless);
1004
0
        TIFFSetField(hTIFF, TIFFTAG_WEBP_LOSSLESS,
1005
0
                     static_cast<int>(bWebpLossless));
1006
0
        GTIFFSetWebPLossless(GDALDataset::ToHandle(hODS), bWebpLossless);
1007
0
    }
1008
1009
0
    const char *pszJPEGTablesMode =
1010
0
        GetOptionValue("JPEG_TABLESMODE", "JPEG_TABLESMODE_OVERVIEW");
1011
0
    if (nCompression == COMPRESSION_JPEG && pszJPEGTablesMode != nullptr)
1012
0
    {
1013
0
        const int nJpegTablesMode = atoi(pszJPEGTablesMode);
1014
0
        TIFFSetField(hTIFF, TIFFTAG_JPEGTABLESMODE, nJpegTablesMode);
1015
0
        GTIFFSetJpegTablesMode(GDALDataset::ToHandle(hODS), nJpegTablesMode);
1016
0
    }
1017
1018
0
    const char *pszZLevel = GetOptionValue("ZLEVEL", "ZLEVEL_OVERVIEW");
1019
0
    if ((nCompression == COMPRESSION_DEFLATE ||
1020
0
         anLercAddCompressionAndVersion[1] == LERC_ADD_COMPRESSION_DEFLATE) &&
1021
0
        pszZLevel != nullptr)
1022
0
    {
1023
0
        const int nZLevel = atoi(pszZLevel);
1024
0
        if (nZLevel >= 1)
1025
0
        {
1026
0
            TIFFSetField(hTIFF, TIFFTAG_ZIPQUALITY, nZLevel);
1027
0
            GTIFFSetZLevel(GDALDataset::ToHandle(hODS), nZLevel);
1028
0
        }
1029
0
    }
1030
1031
0
    const char *pszZSTDLevel =
1032
0
        GetOptionValue("ZSTD_LEVEL", "ZSTD_LEVEL_OVERVIEW");
1033
0
    if ((nCompression == COMPRESSION_ZSTD ||
1034
0
         anLercAddCompressionAndVersion[1] == LERC_ADD_COMPRESSION_ZSTD) &&
1035
0
        pszZSTDLevel != nullptr)
1036
0
    {
1037
0
        const int nZSTDLevel = atoi(pszZSTDLevel);
1038
0
        if (nZSTDLevel >= 1)
1039
0
        {
1040
0
            TIFFSetField(hTIFF, TIFFTAG_ZSTD_LEVEL, nZSTDLevel);
1041
0
            GTIFFSetZSTDLevel(GDALDataset::ToHandle(hODS), nZSTDLevel);
1042
0
        }
1043
0
    }
1044
1045
0
    const char *pszMaxZError =
1046
0
        GetOptionValue("MAX_Z_ERROR", "MAX_Z_ERROR_OVERVIEW");
1047
0
    if (nCompression == COMPRESSION_LERC && pszMaxZError != nullptr)
1048
0
    {
1049
0
        const double dfMaxZError = CPLAtof(pszMaxZError);
1050
0
        if (dfMaxZError >= 0)
1051
0
        {
1052
0
            TIFFSetField(hTIFF, TIFFTAG_LERC_MAXZERROR, dfMaxZError);
1053
0
            GTIFFSetMaxZError(GDALDataset::ToHandle(hODS), dfMaxZError);
1054
0
        }
1055
0
    }
1056
1057
#if HAVE_JXL
1058
    if (nCompression == COMPRESSION_JXL ||
1059
        nCompression == COMPRESSION_JXL_DNG_1_7)
1060
    {
1061
        if (const char *pszJXLLossLess =
1062
                GetOptionValue("JXL_LOSSLESS", "JXL_LOSSLESS_OVERVIEW"))
1063
        {
1064
            const bool bJXLLossless = CPLTestBool(pszJXLLossLess);
1065
            TIFFSetField(hTIFF, TIFFTAG_JXL_LOSSYNESS,
1066
                         bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
1067
            GTIFFSetJXLLossless(GDALDataset::ToHandle(hODS), bJXLLossless);
1068
        }
1069
        if (const char *pszJXLEffort =
1070
                GetOptionValue("JXL_EFFORT", "JXL_EFFORT_OVERVIEW"))
1071
        {
1072
            const int nJXLEffort = atoi(pszJXLEffort);
1073
            TIFFSetField(hTIFF, TIFFTAG_JXL_EFFORT, nJXLEffort);
1074
            GTIFFSetJXLEffort(GDALDataset::ToHandle(hODS), nJXLEffort);
1075
        }
1076
        if (const char *pszJXLDistance =
1077
                GetOptionValue("JXL_DISTANCE", "JXL_DISTANCE_OVERVIEW"))
1078
        {
1079
            const float fJXLDistance =
1080
                static_cast<float>(CPLAtof(pszJXLDistance));
1081
            TIFFSetField(hTIFF, TIFFTAG_JXL_DISTANCE, fJXLDistance);
1082
            GTIFFSetJXLDistance(GDALDataset::ToHandle(hODS), fJXLDistance);
1083
        }
1084
        if (const char *pszJXLAlphaDistance = GetOptionValue(
1085
                "JXL_ALPHA_DISTANCE", "JXL_ALPHA_DISTANCE_OVERVIEW"))
1086
        {
1087
            const float fJXLAlphaDistance =
1088
                static_cast<float>(CPLAtof(pszJXLAlphaDistance));
1089
            TIFFSetField(hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE, fJXLAlphaDistance);
1090
            GTIFFSetJXLAlphaDistance(GDALDataset::ToHandle(hODS),
1091
                                     fJXLAlphaDistance);
1092
        }
1093
    }
1094
#endif
1095
1096
    /* -------------------------------------------------------------------- */
1097
    /*      Loop writing overview data.                                     */
1098
    /* -------------------------------------------------------------------- */
1099
1100
0
    int *panOverviewListSorted = nullptr;
1101
0
    if (panOverviewList)
1102
0
    {
1103
0
        panOverviewListSorted =
1104
0
            static_cast<int *>(CPLMalloc(sizeof(int) * nOverviews));
1105
0
        memcpy(panOverviewListSorted, panOverviewList,
1106
0
               sizeof(int) * nOverviews);
1107
0
        std::sort(panOverviewListSorted, panOverviewListSorted + nOverviews);
1108
0
    }
1109
1110
0
    GTIFFSetThreadLocalInExternalOvr(true);
1111
1112
0
    CPLErr eErr = CE_None;
1113
1114
    // If we have an alpha band, we want it to be generated before downsampling
1115
    // other bands
1116
0
    bool bHasAlphaBand = false;
1117
0
    for (int iBand = 0; iBand < nBands; iBand++)
1118
0
    {
1119
0
        if (papoBandList[iBand]->GetColorInterpretation() == GCI_AlphaBand)
1120
0
            bHasAlphaBand = true;
1121
0
    }
1122
1123
0
    const auto poColorTable = papoBandList[0]->GetColorTable();
1124
0
    if (((((bSourceIsPixelInterleaved && bSourceIsJPEG2000) ||
1125
0
           (nCompression != COMPRESSION_NONE)) &&
1126
0
          nPlanarConfig == PLANARCONFIG_CONTIG) ||
1127
0
         bHasAlphaBand) &&
1128
0
        !GDALDataTypeIsComplex(papoBandList[0]->GetRasterDataType()) &&
1129
0
        (poColorTable == nullptr || STARTS_WITH_CI(pszResampling, "NEAR") ||
1130
0
         poColorTable->IsIdentity()) &&
1131
0
        (STARTS_WITH_CI(pszResampling, "NEAR") ||
1132
0
         EQUAL(pszResampling, "AVERAGE") || EQUAL(pszResampling, "RMS") ||
1133
0
         EQUAL(pszResampling, "GAUSS") || EQUAL(pszResampling, "CUBIC") ||
1134
0
         EQUAL(pszResampling, "CUBICSPLINE") ||
1135
0
         EQUAL(pszResampling, "LANCZOS") || EQUAL(pszResampling, "BILINEAR") ||
1136
0
         EQUAL(pszResampling, "MODE")))
1137
0
    {
1138
        // In the case of pixel interleaved compressed overviews, we want to
1139
        // generate the overviews for all the bands block by block, and not
1140
        // band after band, in order to write the block once and not loose
1141
        // space in the TIFF file.
1142
0
        GDALRasterBand ***papapoOverviewBands =
1143
0
            static_cast<GDALRasterBand ***>(CPLCalloc(sizeof(void *), nBands));
1144
0
        for (int iBand = 0; iBand < nBands && eErr == CE_None; iBand++)
1145
0
        {
1146
0
            GDALRasterBand *poSrcBand = papoBandList[iBand];
1147
0
            GDALRasterBand *poDstBand = hODS->GetRasterBand(iBand + 1);
1148
0
            papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
1149
0
                CPLCalloc(sizeof(void *), nOverviews));
1150
1151
0
            int bHasNoData = FALSE;
1152
0
            const double noDataValue = poSrcBand->GetNoDataValue(&bHasNoData);
1153
0
            if (bHasNoData)
1154
0
                poDstBand->SetNoDataValue(noDataValue);
1155
0
            std::vector<bool> abDstOverviewAssigned(
1156
0
                1 + poDstBand->GetOverviewCount());
1157
1158
0
            for (int i = 0; i < nOverviews && eErr == CE_None; i++)
1159
0
            {
1160
0
                const bool bDegenerateOverview =
1161
0
                    panOverviewListSorted != nullptr &&
1162
0
                    (poSrcBand->GetXSize() >> panOverviewListSorted[i]) == 0 &&
1163
0
                    (poSrcBand->GetYSize() >> panOverviewListSorted[i]) == 0;
1164
1165
0
                for (int j = -1;
1166
0
                     j < poDstBand->GetOverviewCount() && eErr == CE_None; j++)
1167
0
                {
1168
0
                    if (abDstOverviewAssigned[1 + j])
1169
0
                        continue;
1170
0
                    GDALRasterBand *poOverview =
1171
0
                        (j < 0) ? poDstBand : poDstBand->GetOverview(j);
1172
0
                    if (poOverview == nullptr)
1173
0
                    {
1174
0
                        eErr = CE_Failure;
1175
0
                        continue;
1176
0
                    }
1177
1178
0
                    bool bMatch;
1179
0
                    if (panOverviewListSorted)
1180
0
                    {
1181
0
                        const int nOvFactor = GDALComputeOvFactor(
1182
0
                            poOverview->GetXSize(), poSrcBand->GetXSize(),
1183
0
                            poOverview->GetYSize(), poSrcBand->GetYSize());
1184
1185
0
                        bMatch = nOvFactor == panOverviewListSorted[i] ||
1186
0
                                 nOvFactor == GDALOvLevelAdjust2(
1187
0
                                                  panOverviewListSorted[i],
1188
0
                                                  poSrcBand->GetXSize(),
1189
0
                                                  poSrcBand->GetYSize())
1190
                                 // Deal with edge cases where overview levels
1191
                                 // lead to degenerate 1x1 overviews
1192
0
                                 || (bDegenerateOverview &&
1193
0
                                     poOverview->GetXSize() == 1 &&
1194
0
                                     poOverview->GetYSize() == 1);
1195
0
                    }
1196
0
                    else
1197
0
                    {
1198
0
                        bMatch = (
1199
                            // cppcheck-suppress nullPointer
1200
0
                            poOverview->GetXSize() ==
1201
0
                                pasOverviewSize[i].first &&
1202
                            // cppcheck-suppress nullPointer
1203
0
                            poOverview->GetYSize() ==
1204
0
                                pasOverviewSize[i].second);
1205
0
                    }
1206
0
                    if (bMatch)
1207
0
                    {
1208
0
                        abDstOverviewAssigned[j + 1] = true;
1209
0
                        papapoOverviewBands[iBand][i] = poOverview;
1210
0
                        if (bHasNoData)
1211
0
                            poOverview->SetNoDataValue(noDataValue);
1212
0
                        break;
1213
0
                    }
1214
0
                }
1215
1216
0
                CPLAssert(papapoOverviewBands[iBand][i] != nullptr);
1217
0
            }
1218
0
        }
1219
1220
0
        {
1221
0
            CPLConfigOptionSetter oSetter(
1222
0
                "GDAL_NUM_THREADS",
1223
0
                CSLFetchNameValue(papszOptions, "NUM_THREADS"), true);
1224
1225
0
            if (eErr == CE_None)
1226
0
                eErr = GDALRegenerateOverviewsMultiBand(
1227
0
                    nBands, papoBandList, nOverviews, papapoOverviewBands,
1228
0
                    pszResampling, pfnProgress, pProgressData, papszOptions);
1229
0
        }
1230
1231
0
        for (int iBand = 0; iBand < nBands; iBand++)
1232
0
        {
1233
0
            CPLFree(papapoOverviewBands[iBand]);
1234
0
        }
1235
0
        CPLFree(papapoOverviewBands);
1236
0
    }
1237
0
    else
1238
0
    {
1239
0
        GDALRasterBand **papoOverviews = static_cast<GDALRasterBand **>(
1240
0
            CPLCalloc(sizeof(void *), knMaxOverviews));
1241
1242
0
        for (int iBand = 0; iBand < nBands && eErr == CE_None; iBand++)
1243
0
        {
1244
0
            GDALRasterBand *hSrcBand = papoBandList[iBand];
1245
0
            GDALRasterBand *hDstBand = hODS->GetRasterBand(iBand + 1);
1246
1247
0
            int bHasNoData = FALSE;
1248
0
            const double noDataValue = hSrcBand->GetNoDataValue(&bHasNoData);
1249
0
            if (bHasNoData)
1250
0
                hDstBand->SetNoDataValue(noDataValue);
1251
1252
            // FIXME: this logic regenerates all overview bands, not only the
1253
            // ones requested.
1254
1255
0
            papoOverviews[0] = hDstBand;
1256
0
            int nDstOverviews = hDstBand->GetOverviewCount() + 1;
1257
0
            CPLAssert(nDstOverviews < knMaxOverviews);
1258
0
            nDstOverviews = std::min(knMaxOverviews, nDstOverviews);
1259
1260
            // TODO(schwehr): Convert to starting with i = 1 and remove +1.
1261
0
            for (int i = 0; i < nDstOverviews - 1 && eErr == CE_None; i++)
1262
0
            {
1263
0
                papoOverviews[i + 1] = hDstBand->GetOverview(i);
1264
0
                if (papoOverviews[i + 1] == nullptr)
1265
0
                {
1266
0
                    eErr = CE_Failure;
1267
0
                }
1268
0
                else
1269
0
                {
1270
0
                    if (bHasNoData)
1271
0
                        papoOverviews[i + 1]->SetNoDataValue(noDataValue);
1272
0
                }
1273
0
            }
1274
1275
0
            void *pScaledProgressData = GDALCreateScaledProgress(
1276
0
                iBand / static_cast<double>(nBands),
1277
0
                (iBand + 1) / static_cast<double>(nBands), pfnProgress,
1278
0
                pProgressData);
1279
1280
0
            {
1281
0
                CPLConfigOptionSetter oSetter(
1282
0
                    "GDAL_NUM_THREADS",
1283
0
                    CSLFetchNameValue(papszOptions, "NUM_THREADS"), true);
1284
1285
0
                if (eErr == CE_None)
1286
0
                    eErr = GDALRegenerateOverviewsEx(
1287
0
                        hSrcBand, nDstOverviews,
1288
0
                        reinterpret_cast<GDALRasterBandH *>(papoOverviews),
1289
0
                        pszResampling, GDALScaledProgress, pScaledProgressData,
1290
0
                        papszOptions);
1291
0
            }
1292
1293
0
            GDALDestroyScaledProgress(pScaledProgressData);
1294
0
        }
1295
1296
0
        CPLFree(papoOverviews);
1297
0
    }
1298
1299
    /* -------------------------------------------------------------------- */
1300
    /*      Cleanup                                                         */
1301
    /* -------------------------------------------------------------------- */
1302
0
    if (eErr == CE_None)
1303
0
        hODS->FlushCache(true);
1304
0
    delete hODS;
1305
1306
0
    GTIFFSetThreadLocalInExternalOvr(false);
1307
1308
0
    CPLFree(panOverviewListSorted);
1309
1310
0
    pfnProgress(1.0, nullptr, pProgressData);
1311
1312
0
    return eErr;
1313
0
}