Coverage Report

Created: 2025-06-13 06:18

/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
0
{
67
0
    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
0
    TIFFCreateDirectory(hTIFF);
78
79
    /* -------------------------------------------------------------------- */
80
    /*      Setup TIFF fields.                                              */
81
    /* -------------------------------------------------------------------- */
82
0
    TIFFSetField(hTIFF, TIFFTAG_IMAGEWIDTH, nXSize);
83
0
    TIFFSetField(hTIFF, TIFFTAG_IMAGELENGTH, nYSize);
84
0
    if (nSamples == 1)
85
0
        TIFFSetField(hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
86
0
    else
87
0
        TIFFSetField(hTIFF, TIFFTAG_PLANARCONFIG, nPlanarConfig);
88
89
0
    TIFFSetField(hTIFF, TIFFTAG_BITSPERSAMPLE, nBitsPerPixel);
90
0
    TIFFSetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, nSamples);
91
0
    TIFFSetField(hTIFF, TIFFTAG_COMPRESSION, nCompressFlag);
92
0
    TIFFSetField(hTIFF, TIFFTAG_PHOTOMETRIC, nPhotometric);
93
0
    TIFFSetField(hTIFF, TIFFTAG_SAMPLEFORMAT, nSampleFormat);
94
95
0
    if (bTiled)
96
0
    {
97
0
        TIFFSetField(hTIFF, TIFFTAG_TILEWIDTH, nBlockXSize);
98
0
        TIFFSetField(hTIFF, TIFFTAG_TILELENGTH, nBlockYSize);
99
0
    }
100
0
    else
101
0
    {
102
0
        TIFFSetField(hTIFF, TIFFTAG_ROWSPERSTRIP, nBlockYSize);
103
0
    }
104
105
0
    TIFFSetField(hTIFF, TIFFTAG_SUBFILETYPE, nSubfileType);
106
107
0
    if (panExtraSampleValues != nullptr)
108
0
    {
109
0
        TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples,
110
0
                     panExtraSampleValues);
111
0
    }
112
113
0
    if (GTIFFSupportsPredictor(nCompressFlag))
114
0
        TIFFSetField(hTIFF, TIFFTAG_PREDICTOR, nPredictor);
115
116
    /* -------------------------------------------------------------------- */
117
    /*      Write color table if one is present.                            */
118
    /* -------------------------------------------------------------------- */
119
0
    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
0
    if (pszMetadata && strlen(pszMetadata) > 0)
128
0
        TIFFSetField(hTIFF, TIFFTAG_GDAL_METADATA, pszMetadata);
129
130
    /* -------------------------------------------------------------------- */
131
    /*      Write JPEG tables if needed.                                    */
132
    /* -------------------------------------------------------------------- */
133
0
    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
0
    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
0
    if (pszNoData != nullptr)
160
0
    {
161
0
        TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA, pszNoData);
162
0
    }
163
164
0
    if (bDeferStrileArrayWriting)
165
0
    {
166
0
        TIFFDeferStrileArrayWriting(hTIFF);
167
0
    }
168
169
    /* -------------------------------------------------------------------- */
170
    /*      Write directory, and return byte offset.                        */
171
    /* -------------------------------------------------------------------- */
172
0
    if (TIFFWriteCheck(hTIFF, bTiled, "GTIFFWriteDirectory") == 0)
173
0
    {
174
0
        TIFFSetSubDirectory(hTIFF, nBaseDirOffset);
175
0
        return 0;
176
0
    }
177
178
0
    TIFFWriteDirectory(hTIFF);
179
0
    const tdir_t nNumberOfDirs = TIFFNumberOfDirectories(hTIFF);
180
0
    if (nNumberOfDirs > 0)  // always true, but to please Coverity
181
0
    {
182
0
        TIFFSetDirectory(hTIFF, static_cast<tdir_t>(nNumberOfDirs - 1));
183
0
    }
184
185
0
    const toff_t nOffset = TIFFCurrentDirOffset(hTIFF);
186
187
0
    TIFFSetSubDirectory(hTIFF, nBaseDirOffset);
188
189
0
    return nOffset;
190
0
}
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
    /* -------------------------------------------------------------------- */
951
    /*      Open the overview dataset so that we can get at the overview    */
952
    /*      bands.                                                          */
953
    /* -------------------------------------------------------------------- */
954
0
    CPLStringList aosOpenOptions;
955
0
    aosOpenOptions.SetNameValue("NUM_THREADS",
956
0
                                CSLFetchNameValue(papszOptions, "NUM_THREADS"));
957
0
    aosOpenOptions.SetNameValue(
958
0
        "SPARSE_OK", GetOptionValue("SPARSE_OK", "SPARSE_OK_OVERVIEW"));
959
0
    aosOpenOptions.SetNameValue(
960
0
        "@MASK_OVERVIEW_DATASET",
961
0
        CSLFetchNameValue(papszOptions, "MASK_OVERVIEW_DATASET"));
962
0
    GDALDataset *hODS =
963
0
        GDALDataset::Open(pszFilename, GDAL_OF_RASTER | GDAL_OF_UPDATE, nullptr,
964
0
                          aosOpenOptions.List());
965
0
    if (hODS == nullptr)
966
0
        return CE_Failure;
967
968
    /* -------------------------------------------------------------------- */
969
    /*      Do we need to set the jpeg quality?                             */
970
    /* -------------------------------------------------------------------- */
971
0
    TIFF *hTIFF = static_cast<TIFF *>(hODS->GetInternalHandle(nullptr));
972
973
0
    const char *pszJPEGQuality =
974
0
        GetOptionValue("JPEG_QUALITY", "JPEG_QUALITY_OVERVIEW");
975
0
    if (nCompression == COMPRESSION_JPEG && pszJPEGQuality != nullptr)
976
0
    {
977
0
        const int nJpegQuality = atoi(pszJPEGQuality);
978
0
        TIFFSetField(hTIFF, TIFFTAG_JPEGQUALITY, nJpegQuality);
979
0
        GTIFFSetJpegQuality(GDALDataset::ToHandle(hODS), nJpegQuality);
980
0
    }
981
982
0
    const char *pszWebpLevel =
983
0
        GetOptionValue("WEBP_LEVEL", "WEBP_LEVEL_OVERVIEW");
984
0
    if (nCompression == COMPRESSION_WEBP && pszWebpLevel != nullptr)
985
0
    {
986
0
        const int nWebpLevel = atoi(pszWebpLevel);
987
0
        if (nWebpLevel >= 1)
988
0
        {
989
0
            TIFFSetField(hTIFF, TIFFTAG_WEBP_LEVEL, nWebpLevel);
990
0
            GTIFFSetWebPLevel(GDALDataset::ToHandle(hODS), nWebpLevel);
991
0
        }
992
0
    }
993
994
0
    const char *pszWebpLossless =
995
0
        GetOptionValue("WEBP_LOSSLESS", "WEBP_LOSSLESS_OVERVIEW");
996
0
    if (nCompression == COMPRESSION_WEBP && pszWebpLossless != nullptr)
997
0
    {
998
0
        const bool bWebpLossless = CPLTestBool(pszWebpLossless);
999
0
        TIFFSetField(hTIFF, TIFFTAG_WEBP_LOSSLESS,
1000
0
                     static_cast<int>(bWebpLossless));
1001
0
        GTIFFSetWebPLossless(GDALDataset::ToHandle(hODS), bWebpLossless);
1002
0
    }
1003
1004
0
    const char *pszJPEGTablesMode =
1005
0
        GetOptionValue("JPEG_TABLESMODE", "JPEG_TABLESMODE_OVERVIEW");
1006
0
    if (nCompression == COMPRESSION_JPEG && pszJPEGTablesMode != nullptr)
1007
0
    {
1008
0
        const int nJpegTablesMode = atoi(pszJPEGTablesMode);
1009
0
        TIFFSetField(hTIFF, TIFFTAG_JPEGTABLESMODE, nJpegTablesMode);
1010
0
        GTIFFSetJpegTablesMode(GDALDataset::ToHandle(hODS), nJpegTablesMode);
1011
0
    }
1012
1013
0
    const char *pszZLevel = GetOptionValue("ZLEVEL", "ZLEVEL_OVERVIEW");
1014
0
    if ((nCompression == COMPRESSION_DEFLATE ||
1015
0
         anLercAddCompressionAndVersion[1] == LERC_ADD_COMPRESSION_DEFLATE) &&
1016
0
        pszZLevel != nullptr)
1017
0
    {
1018
0
        const int nZLevel = atoi(pszZLevel);
1019
0
        if (nZLevel >= 1)
1020
0
        {
1021
0
            TIFFSetField(hTIFF, TIFFTAG_ZIPQUALITY, nZLevel);
1022
0
            GTIFFSetZLevel(GDALDataset::ToHandle(hODS), nZLevel);
1023
0
        }
1024
0
    }
1025
1026
0
    const char *pszZSTDLevel =
1027
0
        GetOptionValue("ZSTD_LEVEL", "ZSTD_LEVEL_OVERVIEW");
1028
0
    if ((nCompression == COMPRESSION_ZSTD ||
1029
0
         anLercAddCompressionAndVersion[1] == LERC_ADD_COMPRESSION_ZSTD) &&
1030
0
        pszZSTDLevel != nullptr)
1031
0
    {
1032
0
        const int nZSTDLevel = atoi(pszZSTDLevel);
1033
0
        if (nZSTDLevel >= 1)
1034
0
        {
1035
0
            TIFFSetField(hTIFF, TIFFTAG_ZSTD_LEVEL, nZSTDLevel);
1036
0
            GTIFFSetZSTDLevel(GDALDataset::ToHandle(hODS), nZSTDLevel);
1037
0
        }
1038
0
    }
1039
1040
0
    const char *pszMaxZError =
1041
0
        GetOptionValue("MAX_Z_ERROR", "MAX_Z_ERROR_OVERVIEW");
1042
0
    if (nCompression == COMPRESSION_LERC && pszMaxZError != nullptr)
1043
0
    {
1044
0
        const double dfMaxZError = CPLAtof(pszMaxZError);
1045
0
        if (dfMaxZError >= 0)
1046
0
        {
1047
0
            TIFFSetField(hTIFF, TIFFTAG_LERC_MAXZERROR, dfMaxZError);
1048
0
            GTIFFSetMaxZError(GDALDataset::ToHandle(hODS), dfMaxZError);
1049
0
        }
1050
0
    }
1051
1052
#if HAVE_JXL
1053
    if (nCompression == COMPRESSION_JXL ||
1054
        nCompression == COMPRESSION_JXL_DNG_1_7)
1055
    {
1056
        if (const char *pszJXLLossLess =
1057
                GetOptionValue("JXL_LOSSLESS", "JXL_LOSSLESS_OVERVIEW"))
1058
        {
1059
            const bool bJXLLossless = CPLTestBool(pszJXLLossLess);
1060
            TIFFSetField(hTIFF, TIFFTAG_JXL_LOSSYNESS,
1061
                         bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
1062
            GTIFFSetJXLLossless(GDALDataset::ToHandle(hODS), bJXLLossless);
1063
        }
1064
        if (const char *pszJXLEffort =
1065
                GetOptionValue("JXL_EFFORT", "JXL_EFFORT_OVERVIEW"))
1066
        {
1067
            const int nJXLEffort = atoi(pszJXLEffort);
1068
            TIFFSetField(hTIFF, TIFFTAG_JXL_EFFORT, nJXLEffort);
1069
            GTIFFSetJXLEffort(GDALDataset::ToHandle(hODS), nJXLEffort);
1070
        }
1071
        if (const char *pszJXLDistance =
1072
                GetOptionValue("JXL_DISTANCE", "JXL_DISTANCE_OVERVIEW"))
1073
        {
1074
            const float fJXLDistance =
1075
                static_cast<float>(CPLAtof(pszJXLDistance));
1076
            TIFFSetField(hTIFF, TIFFTAG_JXL_DISTANCE, fJXLDistance);
1077
            GTIFFSetJXLDistance(GDALDataset::ToHandle(hODS), fJXLDistance);
1078
        }
1079
        if (const char *pszJXLAlphaDistance = GetOptionValue(
1080
                "JXL_ALPHA_DISTANCE", "JXL_ALPHA_DISTANCE_OVERVIEW"))
1081
        {
1082
            const float fJXLAlphaDistance =
1083
                static_cast<float>(CPLAtof(pszJXLAlphaDistance));
1084
            TIFFSetField(hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE, fJXLAlphaDistance);
1085
            GTIFFSetJXLAlphaDistance(GDALDataset::ToHandle(hODS),
1086
                                     fJXLAlphaDistance);
1087
        }
1088
    }
1089
#endif
1090
1091
    /* -------------------------------------------------------------------- */
1092
    /*      Loop writing overview data.                                     */
1093
    /* -------------------------------------------------------------------- */
1094
1095
0
    int *panOverviewListSorted = nullptr;
1096
0
    if (panOverviewList)
1097
0
    {
1098
0
        panOverviewListSorted =
1099
0
            static_cast<int *>(CPLMalloc(sizeof(int) * nOverviews));
1100
0
        memcpy(panOverviewListSorted, panOverviewList,
1101
0
               sizeof(int) * nOverviews);
1102
0
        std::sort(panOverviewListSorted, panOverviewListSorted + nOverviews);
1103
0
    }
1104
1105
0
    GTIFFSetThreadLocalInExternalOvr(true);
1106
1107
0
    CPLErr eErr = CE_None;
1108
1109
    // If we have an alpha band, we want it to be generated before downsampling
1110
    // other bands
1111
0
    bool bHasAlphaBand = false;
1112
0
    for (int iBand = 0; iBand < nBands; iBand++)
1113
0
    {
1114
0
        if (papoBandList[iBand]->GetColorInterpretation() == GCI_AlphaBand)
1115
0
            bHasAlphaBand = true;
1116
0
    }
1117
1118
0
    const auto poColorTable = papoBandList[0]->GetColorTable();
1119
0
    if (((((bSourceIsPixelInterleaved && bSourceIsJPEG2000) ||
1120
0
           (nCompression != COMPRESSION_NONE)) &&
1121
0
          nPlanarConfig == PLANARCONFIG_CONTIG) ||
1122
0
         bHasAlphaBand) &&
1123
0
        !GDALDataTypeIsComplex(papoBandList[0]->GetRasterDataType()) &&
1124
0
        (poColorTable == nullptr || STARTS_WITH_CI(pszResampling, "NEAR") ||
1125
0
         poColorTable->IsIdentity()) &&
1126
0
        (STARTS_WITH_CI(pszResampling, "NEAR") ||
1127
0
         EQUAL(pszResampling, "AVERAGE") || EQUAL(pszResampling, "RMS") ||
1128
0
         EQUAL(pszResampling, "GAUSS") || EQUAL(pszResampling, "CUBIC") ||
1129
0
         EQUAL(pszResampling, "CUBICSPLINE") ||
1130
0
         EQUAL(pszResampling, "LANCZOS") || EQUAL(pszResampling, "BILINEAR") ||
1131
0
         EQUAL(pszResampling, "MODE")))
1132
0
    {
1133
        // In the case of pixel interleaved compressed overviews, we want to
1134
        // generate the overviews for all the bands block by block, and not
1135
        // band after band, in order to write the block once and not loose
1136
        // space in the TIFF file.
1137
0
        GDALRasterBand ***papapoOverviewBands =
1138
0
            static_cast<GDALRasterBand ***>(CPLCalloc(sizeof(void *), nBands));
1139
0
        for (int iBand = 0; iBand < nBands && eErr == CE_None; iBand++)
1140
0
        {
1141
0
            GDALRasterBand *poSrcBand = papoBandList[iBand];
1142
0
            GDALRasterBand *poDstBand = hODS->GetRasterBand(iBand + 1);
1143
0
            papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
1144
0
                CPLCalloc(sizeof(void *), nOverviews));
1145
1146
0
            int bHasNoData = FALSE;
1147
0
            const double noDataValue = poSrcBand->GetNoDataValue(&bHasNoData);
1148
0
            if (bHasNoData)
1149
0
                poDstBand->SetNoDataValue(noDataValue);
1150
0
            std::vector<bool> abDstOverviewAssigned(
1151
0
                1 + poDstBand->GetOverviewCount());
1152
1153
0
            for (int i = 0; i < nOverviews && eErr == CE_None; i++)
1154
0
            {
1155
0
                const bool bDegenerateOverview =
1156
0
                    panOverviewListSorted != nullptr &&
1157
0
                    (poSrcBand->GetXSize() >> panOverviewListSorted[i]) == 0 &&
1158
0
                    (poSrcBand->GetYSize() >> panOverviewListSorted[i]) == 0;
1159
1160
0
                for (int j = -1;
1161
0
                     j < poDstBand->GetOverviewCount() && eErr == CE_None; j++)
1162
0
                {
1163
0
                    if (abDstOverviewAssigned[1 + j])
1164
0
                        continue;
1165
0
                    GDALRasterBand *poOverview =
1166
0
                        (j < 0) ? poDstBand : poDstBand->GetOverview(j);
1167
0
                    if (poOverview == nullptr)
1168
0
                    {
1169
0
                        eErr = CE_Failure;
1170
0
                        continue;
1171
0
                    }
1172
1173
0
                    bool bMatch;
1174
0
                    if (panOverviewListSorted)
1175
0
                    {
1176
0
                        const int nOvFactor = GDALComputeOvFactor(
1177
0
                            poOverview->GetXSize(), poSrcBand->GetXSize(),
1178
0
                            poOverview->GetYSize(), poSrcBand->GetYSize());
1179
1180
0
                        bMatch = nOvFactor == panOverviewListSorted[i] ||
1181
0
                                 nOvFactor == GDALOvLevelAdjust2(
1182
0
                                                  panOverviewListSorted[i],
1183
0
                                                  poSrcBand->GetXSize(),
1184
0
                                                  poSrcBand->GetYSize())
1185
                                 // Deal with edge cases where overview levels
1186
                                 // lead to degenerate 1x1 overviews
1187
0
                                 || (bDegenerateOverview &&
1188
0
                                     poOverview->GetXSize() == 1 &&
1189
0
                                     poOverview->GetYSize() == 1);
1190
0
                    }
1191
0
                    else
1192
0
                    {
1193
0
                        bMatch = (
1194
                            // cppcheck-suppress nullPointer
1195
0
                            poOverview->GetXSize() ==
1196
0
                                pasOverviewSize[i].first &&
1197
                            // cppcheck-suppress nullPointer
1198
0
                            poOverview->GetYSize() ==
1199
0
                                pasOverviewSize[i].second);
1200
0
                    }
1201
0
                    if (bMatch)
1202
0
                    {
1203
0
                        abDstOverviewAssigned[j + 1] = true;
1204
0
                        papapoOverviewBands[iBand][i] = poOverview;
1205
0
                        if (bHasNoData)
1206
0
                            poOverview->SetNoDataValue(noDataValue);
1207
0
                        break;
1208
0
                    }
1209
0
                }
1210
1211
0
                CPLAssert(papapoOverviewBands[iBand][i] != nullptr);
1212
0
            }
1213
0
        }
1214
1215
0
        {
1216
0
            CPLConfigOptionSetter oSetter(
1217
0
                "GDAL_NUM_THREADS",
1218
0
                CSLFetchNameValue(papszOptions, "NUM_THREADS"), true);
1219
1220
0
            if (eErr == CE_None)
1221
0
                eErr = GDALRegenerateOverviewsMultiBand(
1222
0
                    nBands, papoBandList, nOverviews, papapoOverviewBands,
1223
0
                    pszResampling, pfnProgress, pProgressData, papszOptions);
1224
0
        }
1225
1226
0
        for (int iBand = 0; iBand < nBands; iBand++)
1227
0
        {
1228
0
            CPLFree(papapoOverviewBands[iBand]);
1229
0
        }
1230
0
        CPLFree(papapoOverviewBands);
1231
0
    }
1232
0
    else
1233
0
    {
1234
0
        GDALRasterBand **papoOverviews = static_cast<GDALRasterBand **>(
1235
0
            CPLCalloc(sizeof(void *), knMaxOverviews));
1236
1237
0
        for (int iBand = 0; iBand < nBands && eErr == CE_None; iBand++)
1238
0
        {
1239
0
            GDALRasterBand *hSrcBand = papoBandList[iBand];
1240
0
            GDALRasterBand *hDstBand = hODS->GetRasterBand(iBand + 1);
1241
1242
0
            int bHasNoData = FALSE;
1243
0
            const double noDataValue = hSrcBand->GetNoDataValue(&bHasNoData);
1244
0
            if (bHasNoData)
1245
0
                hDstBand->SetNoDataValue(noDataValue);
1246
1247
            // FIXME: this logic regenerates all overview bands, not only the
1248
            // ones requested.
1249
1250
0
            papoOverviews[0] = hDstBand;
1251
0
            int nDstOverviews = hDstBand->GetOverviewCount() + 1;
1252
0
            CPLAssert(nDstOverviews < knMaxOverviews);
1253
0
            nDstOverviews = std::min(knMaxOverviews, nDstOverviews);
1254
1255
            // TODO(schwehr): Convert to starting with i = 1 and remove +1.
1256
0
            for (int i = 0; i < nDstOverviews - 1 && eErr == CE_None; i++)
1257
0
            {
1258
0
                papoOverviews[i + 1] = hDstBand->GetOverview(i);
1259
0
                if (papoOverviews[i + 1] == nullptr)
1260
0
                {
1261
0
                    eErr = CE_Failure;
1262
0
                }
1263
0
                else
1264
0
                {
1265
0
                    if (bHasNoData)
1266
0
                        papoOverviews[i + 1]->SetNoDataValue(noDataValue);
1267
0
                }
1268
0
            }
1269
1270
0
            void *pScaledProgressData = GDALCreateScaledProgress(
1271
0
                iBand / static_cast<double>(nBands),
1272
0
                (iBand + 1) / static_cast<double>(nBands), pfnProgress,
1273
0
                pProgressData);
1274
1275
0
            {
1276
0
                CPLConfigOptionSetter oSetter(
1277
0
                    "GDAL_NUM_THREADS",
1278
0
                    CSLFetchNameValue(papszOptions, "NUM_THREADS"), true);
1279
1280
0
                if (eErr == CE_None)
1281
0
                    eErr = GDALRegenerateOverviewsEx(
1282
0
                        hSrcBand, nDstOverviews,
1283
0
                        reinterpret_cast<GDALRasterBandH *>(papoOverviews),
1284
0
                        pszResampling, GDALScaledProgress, pScaledProgressData,
1285
0
                        papszOptions);
1286
0
            }
1287
1288
0
            GDALDestroyScaledProgress(pScaledProgressData);
1289
0
        }
1290
1291
0
        CPLFree(papoOverviews);
1292
0
    }
1293
1294
    /* -------------------------------------------------------------------- */
1295
    /*      Cleanup                                                         */
1296
    /* -------------------------------------------------------------------- */
1297
0
    if (eErr == CE_None)
1298
0
        hODS->FlushCache(true);
1299
0
    delete hODS;
1300
1301
0
    GTIFFSetThreadLocalInExternalOvr(false);
1302
1303
0
    CPLFree(panOverviewListSorted);
1304
1305
0
    pfnProgress(1.0, nullptr, pProgressData);
1306
1307
0
    return eErr;
1308
0
}