Coverage Report

Created: 2025-06-13 06:18

/src/gdal/frmts/gtiff/geotiff.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  GeoTIFF Driver
4
 * Purpose:  GDAL GeoTIFF support.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1998, 2002, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2007-2015, Even Rouault <even dot rouault at spatialys dot com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"  // Must be first.
15
16
#include "gtiff.h"
17
18
#include "tiff_common.h"
19
20
#include "cpl_conv.h"
21
#include "cpl_error.h"
22
#include "gdal.h"
23
#include "gdal_mdreader.h"  // RPC_xxx
24
#include "gtiffdataset.h"
25
#include "tiffio.h"
26
#include "tif_jxl.h"
27
#include "xtiffio.h"
28
#include <cctype>
29
#include <cmath>
30
31
// Needed to expose WEBP_LOSSLESS option
32
#ifdef WEBP_SUPPORT
33
#include "webp/encode.h"
34
#endif
35
36
#ifdef LERC_SUPPORT
37
#include "Lerc_c_api.h"
38
#endif
39
40
0
#define STRINGIFY(x) #x
41
0
#define XSTRINGIFY(x) STRINGIFY(x)
42
43
static thread_local bool bThreadLocalInExternalOvr = false;
44
45
static thread_local int gnThreadLocalLibtiffError = 0;
46
47
int &GTIFFGetThreadLocalLibtiffError()
48
0
{
49
0
    return gnThreadLocalLibtiffError;
50
0
}
51
52
/************************************************************************/
53
/*                         GTIFFSupportsPredictor()                     */
54
/************************************************************************/
55
56
bool GTIFFSupportsPredictor(int nCompression)
57
0
{
58
0
    return nCompression == COMPRESSION_LZW ||
59
0
           nCompression == COMPRESSION_ADOBE_DEFLATE ||
60
0
           nCompression == COMPRESSION_ZSTD;
61
0
}
62
63
/************************************************************************/
64
/*                     GTIFFSetThreadLocalInExternalOvr()               */
65
/************************************************************************/
66
67
void GTIFFSetThreadLocalInExternalOvr(bool b)
68
0
{
69
0
    bThreadLocalInExternalOvr = b;
70
0
}
71
72
/************************************************************************/
73
/*                     GTIFFGetOverviewBlockSize()                      */
74
/************************************************************************/
75
76
void GTIFFGetOverviewBlockSize(GDALRasterBandH hBand, int *pnBlockXSize,
77
                               int *pnBlockYSize)
78
0
{
79
0
    const char *pszVal = CPLGetConfigOption("GDAL_TIFF_OVR_BLOCKSIZE", nullptr);
80
0
    if (!pszVal)
81
0
    {
82
0
        GDALRasterBand *const poBand = GDALRasterBand::FromHandle(hBand);
83
0
        poBand->GetBlockSize(pnBlockXSize, pnBlockYSize);
84
0
        if (*pnBlockXSize != *pnBlockYSize || *pnBlockXSize < 64 ||
85
0
            *pnBlockXSize > 4096 || !CPLIsPowerOfTwo(*pnBlockXSize))
86
0
        {
87
0
            *pnBlockXSize = *pnBlockYSize = 128;
88
0
        }
89
0
    }
90
0
    else
91
0
    {
92
0
        int nOvrBlockSize = atoi(pszVal);
93
0
        if (nOvrBlockSize < 64 || nOvrBlockSize > 4096 ||
94
0
            !CPLIsPowerOfTwo(nOvrBlockSize))
95
0
        {
96
0
            CPLErrorOnce(CE_Warning, CPLE_NotSupported,
97
0
                         "Wrong value for GDAL_TIFF_OVR_BLOCKSIZE : %s. "
98
0
                         "Should be a power of 2 between 64 and 4096. "
99
0
                         "Defaulting to 128",
100
0
                         pszVal);
101
0
            nOvrBlockSize = 128;
102
0
        }
103
104
0
        *pnBlockXSize = nOvrBlockSize;
105
0
        *pnBlockYSize = nOvrBlockSize;
106
0
    }
107
0
}
108
109
/************************************************************************/
110
/*                        GTIFFSetJpegQuality()                         */
111
/* Called by GTIFFBuildOverviews() to set the jpeg quality on the IFD   */
112
/* of the .ovr file.                                                    */
113
/************************************************************************/
114
115
void GTIFFSetJpegQuality(GDALDatasetH hGTIFFDS, int nJpegQuality)
116
0
{
117
0
    CPLAssert(
118
0
        EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hGTIFFDS)), "GTIFF"));
119
120
0
    GTiffDataset *const poDS = static_cast<GTiffDataset *>(hGTIFFDS);
121
0
    poDS->m_nJpegQuality = static_cast<signed char>(nJpegQuality);
122
123
0
    poDS->ScanDirectories();
124
125
0
    for (int i = 0; i < poDS->m_nOverviewCount; ++i)
126
0
        poDS->m_papoOverviewDS[i]->m_nJpegQuality = poDS->m_nJpegQuality;
127
0
}
128
129
/************************************************************************/
130
/*                        GTIFFSetWebPLevel()                         */
131
/* Called by GTIFFBuildOverviews() to set the jpeg quality on the IFD   */
132
/* of the .ovr file.                                                    */
133
/************************************************************************/
134
135
void GTIFFSetWebPLevel(GDALDatasetH hGTIFFDS, int nWebpLevel)
136
0
{
137
0
    CPLAssert(
138
0
        EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hGTIFFDS)), "GTIFF"));
139
140
0
    GTiffDataset *const poDS = static_cast<GTiffDataset *>(hGTIFFDS);
141
0
    poDS->m_nWebPLevel = static_cast<signed char>(nWebpLevel);
142
143
0
    poDS->ScanDirectories();
144
145
0
    for (int i = 0; i < poDS->m_nOverviewCount; ++i)
146
0
        poDS->m_papoOverviewDS[i]->m_nWebPLevel = poDS->m_nWebPLevel;
147
0
}
148
149
/************************************************************************/
150
/*                       GTIFFSetWebPLossless()                         */
151
/* Called by GTIFFBuildOverviews() to set webp lossless on the IFD      */
152
/* of the .ovr file.                                                    */
153
/************************************************************************/
154
155
void GTIFFSetWebPLossless(GDALDatasetH hGTIFFDS, bool bWebpLossless)
156
0
{
157
0
    CPLAssert(
158
0
        EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hGTIFFDS)), "GTIFF"));
159
160
0
    GTiffDataset *const poDS = static_cast<GTiffDataset *>(hGTIFFDS);
161
0
    poDS->m_bWebPLossless = bWebpLossless;
162
163
0
    poDS->ScanDirectories();
164
165
0
    for (int i = 0; i < poDS->m_nOverviewCount; ++i)
166
0
        poDS->m_papoOverviewDS[i]->m_bWebPLossless = poDS->m_bWebPLossless;
167
0
}
168
169
/************************************************************************/
170
/*                     GTIFFSetJpegTablesMode()                         */
171
/* Called by GTIFFBuildOverviews() to set the jpeg tables mode on the   */
172
/* of the .ovr file.                                                    */
173
/************************************************************************/
174
175
void GTIFFSetJpegTablesMode(GDALDatasetH hGTIFFDS, int nJpegTablesMode)
176
0
{
177
0
    CPLAssert(
178
0
        EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hGTIFFDS)), "GTIFF"));
179
180
0
    GTiffDataset *const poDS = static_cast<GTiffDataset *>(hGTIFFDS);
181
0
    poDS->m_nJpegTablesMode = static_cast<signed char>(nJpegTablesMode);
182
183
0
    poDS->ScanDirectories();
184
185
0
    for (int i = 0; i < poDS->m_nOverviewCount; ++i)
186
0
        poDS->m_papoOverviewDS[i]->m_nJpegTablesMode = poDS->m_nJpegTablesMode;
187
0
}
188
189
/************************************************************************/
190
/*                        GTIFFSetZLevel()                              */
191
/* Called by GTIFFBuildOverviews() to set the deflate level on the IFD  */
192
/* of the .ovr file.                                                    */
193
/************************************************************************/
194
195
void GTIFFSetZLevel(GDALDatasetH hGTIFFDS, int nZLevel)
196
0
{
197
0
    CPLAssert(
198
0
        EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hGTIFFDS)), "GTIFF"));
199
200
0
    GTiffDataset *const poDS = static_cast<GTiffDataset *>(hGTIFFDS);
201
0
    poDS->m_nZLevel = static_cast<signed char>(nZLevel);
202
203
0
    poDS->ScanDirectories();
204
205
0
    for (int i = 0; i < poDS->m_nOverviewCount; ++i)
206
0
        poDS->m_papoOverviewDS[i]->m_nZLevel = poDS->m_nZLevel;
207
0
}
208
209
/************************************************************************/
210
/*                        GTIFFSetZSTDLevel()                           */
211
/* Called by GTIFFBuildOverviews() to set the ZSTD level on the IFD     */
212
/* of the .ovr file.                                                    */
213
/************************************************************************/
214
215
void GTIFFSetZSTDLevel(GDALDatasetH hGTIFFDS, int nZSTDLevel)
216
0
{
217
0
    CPLAssert(
218
0
        EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hGTIFFDS)), "GTIFF"));
219
220
0
    GTiffDataset *const poDS = static_cast<GTiffDataset *>(hGTIFFDS);
221
0
    poDS->m_nZSTDLevel = static_cast<signed char>(nZSTDLevel);
222
223
0
    poDS->ScanDirectories();
224
225
0
    for (int i = 0; i < poDS->m_nOverviewCount; ++i)
226
0
        poDS->m_papoOverviewDS[i]->m_nZSTDLevel = poDS->m_nZSTDLevel;
227
0
}
228
229
/************************************************************************/
230
/*                        GTIFFSetMaxZError()                           */
231
/* Called by GTIFFBuildOverviews() to set the Lerc max error on the IFD */
232
/* of the .ovr file.                                                    */
233
/************************************************************************/
234
235
void GTIFFSetMaxZError(GDALDatasetH hGTIFFDS, double dfMaxZError)
236
0
{
237
0
    CPLAssert(
238
0
        EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hGTIFFDS)), "GTIFF"));
239
240
0
    GTiffDataset *const poDS = static_cast<GTiffDataset *>(hGTIFFDS);
241
0
    poDS->m_dfMaxZError = dfMaxZError;
242
0
    poDS->m_dfMaxZErrorOverview = dfMaxZError;
243
244
0
    poDS->ScanDirectories();
245
246
0
    for (int i = 0; i < poDS->m_nOverviewCount; ++i)
247
0
    {
248
0
        poDS->m_papoOverviewDS[i]->m_dfMaxZError = poDS->m_dfMaxZError;
249
0
        poDS->m_papoOverviewDS[i]->m_dfMaxZErrorOverview =
250
0
            poDS->m_dfMaxZErrorOverview;
251
0
    }
252
0
}
253
254
#if HAVE_JXL
255
256
/************************************************************************/
257
/*                       GTIFFSetJXLLossless()                          */
258
/* Called by GTIFFBuildOverviews() to set the JXL lossyness on the IFD  */
259
/* of the .ovr file.                                                    */
260
/************************************************************************/
261
262
void GTIFFSetJXLLossless(GDALDatasetH hGTIFFDS, bool bIsLossless)
263
{
264
    CPLAssert(
265
        EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hGTIFFDS)), "GTIFF"));
266
267
    GTiffDataset *const poDS = static_cast<GTiffDataset *>(hGTIFFDS);
268
    poDS->m_bJXLLossless = bIsLossless;
269
270
    poDS->ScanDirectories();
271
272
    for (int i = 0; i < poDS->m_nOverviewCount; ++i)
273
    {
274
        poDS->m_papoOverviewDS[i]->m_bJXLLossless = poDS->m_bJXLLossless;
275
    }
276
}
277
278
/************************************************************************/
279
/*                       GTIFFSetJXLEffort()                            */
280
/* Called by GTIFFBuildOverviews() to set the JXL effort on the IFD     */
281
/* of the .ovr file.                                                    */
282
/************************************************************************/
283
284
void GTIFFSetJXLEffort(GDALDatasetH hGTIFFDS, int nEffort)
285
{
286
    CPLAssert(
287
        EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hGTIFFDS)), "GTIFF"));
288
289
    GTiffDataset *const poDS = static_cast<GTiffDataset *>(hGTIFFDS);
290
    poDS->m_nJXLEffort = nEffort;
291
292
    poDS->ScanDirectories();
293
294
    for (int i = 0; i < poDS->m_nOverviewCount; ++i)
295
    {
296
        poDS->m_papoOverviewDS[i]->m_nJXLEffort = poDS->m_nJXLEffort;
297
    }
298
}
299
300
/************************************************************************/
301
/*                       GTIFFSetJXLDistance()                          */
302
/* Called by GTIFFBuildOverviews() to set the JXL distance on the IFD   */
303
/* of the .ovr file.                                                    */
304
/************************************************************************/
305
306
void GTIFFSetJXLDistance(GDALDatasetH hGTIFFDS, float fDistance)
307
{
308
    CPLAssert(
309
        EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hGTIFFDS)), "GTIFF"));
310
311
    GTiffDataset *const poDS = static_cast<GTiffDataset *>(hGTIFFDS);
312
    poDS->m_fJXLDistance = fDistance;
313
314
    poDS->ScanDirectories();
315
316
    for (int i = 0; i < poDS->m_nOverviewCount; ++i)
317
    {
318
        poDS->m_papoOverviewDS[i]->m_fJXLDistance = poDS->m_fJXLDistance;
319
    }
320
}
321
322
/************************************************************************/
323
/*                     GTIFFSetJXLAlphaDistance()                       */
324
/* Called by GTIFFBuildOverviews() to set the JXL alpha distance on the */
325
/* IFD of the .ovr file.                                                */
326
/************************************************************************/
327
328
void GTIFFSetJXLAlphaDistance(GDALDatasetH hGTIFFDS, float fAlphaDistance)
329
{
330
    CPLAssert(
331
        EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hGTIFFDS)), "GTIFF"));
332
333
    GTiffDataset *const poDS = static_cast<GTiffDataset *>(hGTIFFDS);
334
    poDS->m_fJXLAlphaDistance = fAlphaDistance;
335
336
    poDS->ScanDirectories();
337
338
    for (int i = 0; i < poDS->m_nOverviewCount; ++i)
339
    {
340
        poDS->m_papoOverviewDS[i]->m_fJXLAlphaDistance =
341
            poDS->m_fJXLAlphaDistance;
342
    }
343
}
344
345
#endif  // HAVE_JXL
346
347
/************************************************************************/
348
/*                         GTiffGetAlphaValue()                         */
349
/************************************************************************/
350
351
uint16_t GTiffGetAlphaValue(const char *pszValue, uint16_t nDefault)
352
0
{
353
0
    if (pszValue == nullptr)
354
0
        return nDefault;
355
0
    if (EQUAL(pszValue, "YES"))
356
0
        return DEFAULT_ALPHA_TYPE;
357
0
    if (EQUAL(pszValue, "PREMULTIPLIED"))
358
0
        return EXTRASAMPLE_ASSOCALPHA;
359
0
    if (EQUAL(pszValue, "NON-PREMULTIPLIED"))
360
0
        return EXTRASAMPLE_UNASSALPHA;
361
0
    if (EQUAL(pszValue, "NO") || EQUAL(pszValue, "UNSPECIFIED"))
362
0
        return EXTRASAMPLE_UNSPECIFIED;
363
364
0
    return nDefault;
365
0
}
366
367
/************************************************************************/
368
/*                 GTIFFIsStandardColorInterpretation()                 */
369
/************************************************************************/
370
371
bool GTIFFIsStandardColorInterpretation(GDALDatasetH hSrcDS,
372
                                        uint16_t nPhotometric,
373
                                        CSLConstList papszCreationOptions)
374
0
{
375
0
    GDALDataset *poSrcDS = GDALDataset::FromHandle(hSrcDS);
376
0
    bool bStandardColorInterp = true;
377
0
    if (nPhotometric == PHOTOMETRIC_MINISBLACK)
378
0
    {
379
0
        for (int i = 0; i < poSrcDS->GetRasterCount(); ++i)
380
0
        {
381
0
            const GDALColorInterp eInterp =
382
0
                poSrcDS->GetRasterBand(i + 1)->GetColorInterpretation();
383
0
            if (!(eInterp == GCI_GrayIndex || eInterp == GCI_Undefined ||
384
0
                  (i > 0 && eInterp == GCI_AlphaBand)))
385
0
            {
386
0
                bStandardColorInterp = false;
387
0
                break;
388
0
            }
389
0
        }
390
0
    }
391
0
    else if (nPhotometric == PHOTOMETRIC_PALETTE)
392
0
    {
393
0
        bStandardColorInterp =
394
0
            poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
395
0
            GCI_PaletteIndex;
396
0
    }
397
0
    else if (nPhotometric == PHOTOMETRIC_RGB)
398
0
    {
399
0
        int iStart = 0;
400
0
        if (EQUAL(CSLFetchNameValueDef(papszCreationOptions, "PHOTOMETRIC", ""),
401
0
                  "RGB"))
402
0
        {
403
0
            iStart = 3;
404
0
            if (poSrcDS->GetRasterCount() == 4 &&
405
0
                CSLFetchNameValue(papszCreationOptions, "ALPHA") != nullptr)
406
0
            {
407
0
                iStart = 4;
408
0
            }
409
0
        }
410
0
        for (int i = iStart; i < poSrcDS->GetRasterCount(); ++i)
411
0
        {
412
0
            const GDALColorInterp eInterp =
413
0
                poSrcDS->GetRasterBand(i + 1)->GetColorInterpretation();
414
0
            if (!((i == 0 && eInterp == GCI_RedBand) ||
415
0
                  (i == 1 && eInterp == GCI_GreenBand) ||
416
0
                  (i == 2 && eInterp == GCI_BlueBand) ||
417
0
                  (i >= 3 &&
418
0
                   (eInterp == GCI_Undefined || eInterp == GCI_AlphaBand))))
419
0
            {
420
0
                bStandardColorInterp = false;
421
0
                break;
422
0
            }
423
0
        }
424
0
    }
425
0
    else if (nPhotometric == PHOTOMETRIC_YCBCR &&
426
0
             poSrcDS->GetRasterCount() == 3)
427
0
    {
428
        // do nothing
429
0
    }
430
0
    else
431
0
    {
432
0
        bStandardColorInterp = false;
433
0
    }
434
0
    return bStandardColorInterp;
435
0
}
436
437
/************************************************************************/
438
/*                     GTiffDatasetWriteRPCTag()                        */
439
/*                                                                      */
440
/*      Format a TAG according to:                                      */
441
/*                                                                      */
442
/*      http://geotiff.maptools.org/rpc_prop.html                       */
443
/************************************************************************/
444
445
void GTiffDatasetWriteRPCTag(TIFF *hTIFF, char **papszRPCMD)
446
447
0
{
448
0
    GDALRPCInfoV2 sRPC;
449
450
0
    if (!GDALExtractRPCInfoV2(papszRPCMD, &sRPC))
451
0
        return;
452
453
0
    double adfRPCTag[92] = {};
454
0
    adfRPCTag[0] = sRPC.dfERR_BIAS;  // Error Bias
455
0
    adfRPCTag[1] = sRPC.dfERR_RAND;  // Error Random
456
457
0
    adfRPCTag[2] = sRPC.dfLINE_OFF;
458
0
    adfRPCTag[3] = sRPC.dfSAMP_OFF;
459
0
    adfRPCTag[4] = sRPC.dfLAT_OFF;
460
0
    adfRPCTag[5] = sRPC.dfLONG_OFF;
461
0
    adfRPCTag[6] = sRPC.dfHEIGHT_OFF;
462
0
    adfRPCTag[7] = sRPC.dfLINE_SCALE;
463
0
    adfRPCTag[8] = sRPC.dfSAMP_SCALE;
464
0
    adfRPCTag[9] = sRPC.dfLAT_SCALE;
465
0
    adfRPCTag[10] = sRPC.dfLONG_SCALE;
466
0
    adfRPCTag[11] = sRPC.dfHEIGHT_SCALE;
467
468
0
    memcpy(adfRPCTag + 12, sRPC.adfLINE_NUM_COEFF, sizeof(double) * 20);
469
0
    memcpy(adfRPCTag + 32, sRPC.adfLINE_DEN_COEFF, sizeof(double) * 20);
470
0
    memcpy(adfRPCTag + 52, sRPC.adfSAMP_NUM_COEFF, sizeof(double) * 20);
471
0
    memcpy(adfRPCTag + 72, sRPC.adfSAMP_DEN_COEFF, sizeof(double) * 20);
472
473
0
    TIFFSetField(hTIFF, TIFFTAG_RPCCOEFFICIENT, 92, adfRPCTag);
474
0
}
475
476
/************************************************************************/
477
/*                             ReadRPCTag()                             */
478
/*                                                                      */
479
/*      Format a TAG according to:                                      */
480
/*                                                                      */
481
/*      http://geotiff.maptools.org/rpc_prop.html                       */
482
/************************************************************************/
483
484
char **GTiffDatasetReadRPCTag(TIFF *hTIFF)
485
486
0
{
487
0
    double *padfRPCTag = nullptr;
488
0
    uint16_t nCount;
489
490
0
    if (!TIFFGetField(hTIFF, TIFFTAG_RPCCOEFFICIENT, &nCount, &padfRPCTag) ||
491
0
        nCount != 92)
492
0
        return nullptr;
493
494
0
    return gdal::tiff_common::TIFFRPCTagToRPCMetadata(padfRPCTag).StealList();
495
0
}
496
497
/************************************************************************/
498
/*                  GTiffFormatGDALNoDataTagValue()                     */
499
/************************************************************************/
500
501
CPLString GTiffFormatGDALNoDataTagValue(double dfNoData)
502
0
{
503
0
    CPLString osVal;
504
0
    if (std::isnan(dfNoData))
505
0
        osVal = "nan";
506
0
    else
507
0
        osVal.Printf("%.17g", dfNoData);
508
0
    return osVal;
509
0
}
510
511
/************************************************************************/
512
/*                       GTIFFUpdatePhotometric()                      */
513
/************************************************************************/
514
515
bool GTIFFUpdatePhotometric(const char *pszPhotometric,
516
                            const char *pszOptionKey, int nCompression,
517
                            const char *pszInterleave, int nBands,
518
                            uint16_t &nPhotometric, uint16_t &nPlanarConfig)
519
0
{
520
0
    if (pszPhotometric != nullptr && pszPhotometric[0] != '\0')
521
0
    {
522
0
        if (EQUAL(pszPhotometric, "MINISBLACK"))
523
0
            nPhotometric = PHOTOMETRIC_MINISBLACK;
524
0
        else if (EQUAL(pszPhotometric, "MINISWHITE"))
525
0
            nPhotometric = PHOTOMETRIC_MINISWHITE;
526
0
        else if (EQUAL(pszPhotometric, "RGB"))
527
0
        {
528
0
            nPhotometric = PHOTOMETRIC_RGB;
529
0
        }
530
0
        else if (EQUAL(pszPhotometric, "CMYK"))
531
0
        {
532
0
            nPhotometric = PHOTOMETRIC_SEPARATED;
533
0
        }
534
0
        else if (EQUAL(pszPhotometric, "YCBCR"))
535
0
        {
536
0
            nPhotometric = PHOTOMETRIC_YCBCR;
537
538
            // Because of subsampling, setting YCBCR without JPEG compression
539
            // leads to a crash currently. Would need to make
540
            // GTiffRasterBand::IWriteBlock() aware of subsampling so that it
541
            // doesn't overrun buffer size returned by libtiff.
542
0
            if (nCompression != COMPRESSION_JPEG)
543
0
            {
544
0
                CPLError(CE_Failure, CPLE_NotSupported,
545
0
                         "Currently, %s=YCBCR requires JPEG compression",
546
0
                         pszOptionKey);
547
0
                return false;
548
0
            }
549
550
0
            if (pszInterleave != nullptr && pszInterleave[0] != '\0' &&
551
0
                nPlanarConfig == PLANARCONFIG_SEPARATE)
552
0
            {
553
0
                CPLError(CE_Failure, CPLE_NotSupported,
554
0
                         "%s=YCBCR requires PIXEL interleaving", pszOptionKey);
555
0
                return false;
556
0
            }
557
0
            else
558
0
            {
559
0
                nPlanarConfig = PLANARCONFIG_CONTIG;
560
0
            }
561
562
            // YCBCR strictly requires 3 bands. Not less, not more
563
            // Issue an explicit error message as libtiff one is a bit cryptic:
564
            // JPEGLib:Bogus input colorspace.
565
0
            if (nBands != 3)
566
0
            {
567
0
                CPLError(CE_Failure, CPLE_NotSupported,
568
0
                         "%s=YCBCR requires a source raster "
569
0
                         "with only 3 bands (RGB)",
570
0
                         pszOptionKey);
571
0
                return false;
572
0
            }
573
0
        }
574
0
        else if (EQUAL(pszPhotometric, "CIELAB"))
575
0
        {
576
0
            nPhotometric = PHOTOMETRIC_CIELAB;
577
0
        }
578
0
        else if (EQUAL(pszPhotometric, "ICCLAB"))
579
0
        {
580
0
            nPhotometric = PHOTOMETRIC_ICCLAB;
581
0
        }
582
0
        else if (EQUAL(pszPhotometric, "ITULAB"))
583
0
        {
584
0
            nPhotometric = PHOTOMETRIC_ITULAB;
585
0
        }
586
0
        else
587
0
        {
588
0
            CPLError(CE_Warning, CPLE_IllegalArg,
589
0
                     "%s=%s value not recognised, ignoring.", pszOptionKey,
590
0
                     pszPhotometric);
591
0
        }
592
0
    }
593
0
    return true;
594
0
}
595
596
/************************************************************************/
597
/*                      GTiffWriteJPEGTables()                          */
598
/*                                                                      */
599
/*      Sets the TIFFTAG_JPEGTABLES (and TIFFTAG_REFERENCEBLACKWHITE)   */
600
/*      tags immediately, instead of relying on the TIFF JPEG codec     */
601
/*      to write them when it starts compressing imagery. This avoids   */
602
/*      an IFD rewrite at the end of the file.                          */
603
/*      Must be used after having set TIFFTAG_SAMPLESPERPIXEL,          */
604
/*      TIFFTAG_BITSPERSAMPLE.                                          */
605
/************************************************************************/
606
607
void GTiffWriteJPEGTables(TIFF *hTIFF, const char *pszPhotometric,
608
                          const char *pszJPEGQuality,
609
                          const char *pszJPEGTablesMode)
610
0
{
611
    // This trick
612
    // creates a temporary in-memory file and fetches its JPEG tables so that
613
    // we can directly set them, before tif_jpeg.c compute them at the first
614
    // strip/tile writing, which is too late, since we have already crystalized
615
    // the directory. This way we avoid a directory rewriting.
616
0
    uint16_t nBands = 0;
617
0
    if (!TIFFGetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nBands))
618
0
        nBands = 1;
619
620
0
    uint16_t l_nBitsPerSample = 0;
621
0
    if (!TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &(l_nBitsPerSample)))
622
0
        l_nBitsPerSample = 1;
623
624
0
    const CPLString osTmpFilenameIn(
625
0
        VSIMemGenerateHiddenFilename("gtiffdataset_jpg_tmp"));
626
0
    VSILFILE *fpTmp = nullptr;
627
0
    CPLString osTmp;
628
0
    char **papszLocalParameters = nullptr;
629
0
    const int nInMemImageWidth = 16;
630
0
    const int nInMemImageHeight = 16;
631
0
    papszLocalParameters =
632
0
        CSLSetNameValue(papszLocalParameters, "COMPRESS", "JPEG");
633
0
    papszLocalParameters =
634
0
        CSLSetNameValue(papszLocalParameters, "JPEG_QUALITY", pszJPEGQuality);
635
0
    if (nBands <= 4)
636
0
    {
637
0
        papszLocalParameters = CSLSetNameValue(papszLocalParameters,
638
0
                                               "PHOTOMETRIC", pszPhotometric);
639
0
    }
640
0
    papszLocalParameters = CSLSetNameValue(papszLocalParameters, "BLOCKYSIZE",
641
0
                                           CPLSPrintf("%u", nInMemImageHeight));
642
0
    papszLocalParameters = CSLSetNameValue(papszLocalParameters, "NBITS",
643
0
                                           CPLSPrintf("%u", l_nBitsPerSample));
644
0
    papszLocalParameters = CSLSetNameValue(papszLocalParameters,
645
0
                                           "JPEGTABLESMODE", pszJPEGTablesMode);
646
0
    papszLocalParameters =
647
0
        CSLSetNameValue(papszLocalParameters, "WRITE_JPEGTABLE_TAG", "NO");
648
649
0
    bool bTileInterleaving;
650
0
    TIFF *hTIFFTmp =
651
0
        GTiffDataset::CreateLL(osTmpFilenameIn, nInMemImageWidth,
652
0
                               nInMemImageHeight, (nBands <= 4) ? nBands : 1,
653
0
                               (l_nBitsPerSample <= 8) ? GDT_Byte : GDT_UInt16,
654
0
                               0.0, 0, papszLocalParameters, &fpTmp, osTmp,
655
0
                               /* bCreateCopy=*/false, bTileInterleaving);
656
0
    CSLDestroy(papszLocalParameters);
657
0
    if (hTIFFTmp)
658
0
    {
659
0
        uint16_t l_nPhotometric = 0;
660
0
        int nJpegTablesModeIn = 0;
661
0
        TIFFGetField(hTIFFTmp, TIFFTAG_PHOTOMETRIC, &(l_nPhotometric));
662
0
        TIFFGetField(hTIFFTmp, TIFFTAG_JPEGTABLESMODE, &nJpegTablesModeIn);
663
0
        TIFFWriteCheck(hTIFFTmp, FALSE, "CreateLL");
664
0
        TIFFWriteDirectory(hTIFFTmp);
665
0
        TIFFSetDirectory(hTIFFTmp, 0);
666
        // Now, reset quality and jpegcolormode.
667
0
        const int l_nJpegQuality = pszJPEGQuality ? atoi(pszJPEGQuality) : 0;
668
0
        if (l_nJpegQuality > 0)
669
0
            TIFFSetField(hTIFFTmp, TIFFTAG_JPEGQUALITY, l_nJpegQuality);
670
0
        if (l_nPhotometric == PHOTOMETRIC_YCBCR &&
671
0
            CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
672
0
        {
673
0
            TIFFSetField(hTIFFTmp, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
674
0
        }
675
0
        if (nJpegTablesModeIn >= 0)
676
0
            TIFFSetField(hTIFFTmp, TIFFTAG_JPEGTABLESMODE, nJpegTablesModeIn);
677
678
0
        GPtrDiff_t nBlockSize = static_cast<GPtrDiff_t>(nInMemImageWidth) *
679
0
                                nInMemImageHeight *
680
0
                                ((nBands <= 4) ? nBands : 1);
681
0
        if (l_nBitsPerSample == 12)
682
0
            nBlockSize = (nBlockSize * 3) / 2;
683
0
        std::vector<GByte> abyZeroData(nBlockSize, 0);
684
0
        TIFFWriteEncodedStrip(hTIFFTmp, 0, &abyZeroData[0], nBlockSize);
685
686
0
        uint32_t nJPEGTableSize = 0;
687
0
        void *pJPEGTable = nullptr;
688
0
        if (TIFFGetField(hTIFFTmp, TIFFTAG_JPEGTABLES, &nJPEGTableSize,
689
0
                         &pJPEGTable))
690
0
            TIFFSetField(hTIFF, TIFFTAG_JPEGTABLES, nJPEGTableSize, pJPEGTable);
691
692
0
        float *ref = nullptr;
693
0
        if (TIFFGetField(hTIFFTmp, TIFFTAG_REFERENCEBLACKWHITE, &ref))
694
0
            TIFFSetField(hTIFF, TIFFTAG_REFERENCEBLACKWHITE, ref);
695
696
0
        XTIFFClose(hTIFFTmp);
697
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpTmp));
698
0
    }
699
0
    VSIUnlink(osTmpFilenameIn);
700
0
}
701
702
#if !defined(SUPPORTS_LIBTIFF_OPEN_OPTIONS)
703
704
/************************************************************************/
705
/*                        GTiffWarningHandler()                         */
706
/************************************************************************/
707
static void GTiffWarningHandler(const char *module, const char *fmt, va_list ap)
708
{
709
    if (GTIFFGetThreadLocalLibtiffError() > 0)
710
    {
711
        GTIFFGetThreadLocalLibtiffError()++;
712
        if (GTIFFGetThreadLocalLibtiffError() > 10)
713
            return;
714
    }
715
716
    if (strstr(fmt, "nknown field") != nullptr)
717
        return;
718
719
    char *pszModFmt = gdal::tiff_common::PrepareTIFFErrorFormat(module, fmt);
720
    if (strstr(fmt, "does not end in null byte") != nullptr)
721
    {
722
        CPLString osMsg;
723
        osMsg.vPrintf(pszModFmt, ap);
724
        CPLDebug("GTiff", "%s", osMsg.c_str());
725
    }
726
    else
727
    {
728
        CPLErrorV(CE_Warning, CPLE_AppDefined, pszModFmt, ap);
729
    }
730
    CPLFree(pszModFmt);
731
    return;
732
}
733
734
/************************************************************************/
735
/*                         GTiffErrorHandler()                          */
736
/************************************************************************/
737
static void GTiffErrorHandler(const char *module, const char *fmt, va_list ap)
738
{
739
    if (GTIFFGetThreadLocalLibtiffError() > 0)
740
    {
741
        GTIFFGetThreadLocalLibtiffError()++;
742
        if (GTIFFGetThreadLocalLibtiffError() > 10)
743
            return;
744
    }
745
746
    if (strcmp(fmt, "Maximum TIFF file size exceeded") == 0)
747
    {
748
        if (bThreadLocalInExternalOvr)
749
            fmt = "Maximum TIFF file size exceeded. "
750
                  "Use --config BIGTIFF_OVERVIEW YES configuration option.";
751
        else
752
            fmt = "Maximum TIFF file size exceeded. "
753
                  "Use BIGTIFF=YES creation option.";
754
    }
755
756
    char *pszModFmt = gdal::tiff_common::PrepareTIFFErrorFormat(module, fmt);
757
    CPLErrorV(CE_Failure, CPLE_AppDefined, pszModFmt, ap);
758
    CPLFree(pszModFmt);
759
    return;
760
}
761
#else
762
763
/************************************************************************/
764
/*                      GTiffWarningHandlerExt()                        */
765
/************************************************************************/
766
extern int GTiffWarningHandlerExt(TIFF *tif, void *user_data,
767
                                  const char *module, const char *fmt,
768
                                  va_list ap);
769
770
int GTiffWarningHandlerExt(TIFF *tif, void *user_data, const char *module,
771
                           const char *fmt, va_list ap)
772
0
{
773
0
    (void)tif;
774
0
    (void)user_data;
775
0
    auto &nLibtiffErrors = GTIFFGetThreadLocalLibtiffError();
776
    // cppcheck-suppress knownConditionTrueFalse
777
0
    if (nLibtiffErrors > 0)
778
0
    {
779
0
        nLibtiffErrors++;
780
        // cppcheck-suppress knownConditionTrueFalse
781
0
        if (nLibtiffErrors > 10)
782
0
            return 1;
783
0
    }
784
785
0
    if (strstr(fmt, "nknown field") != nullptr)
786
0
        return 1;
787
788
0
    char *pszModFmt = gdal::tiff_common::PrepareTIFFErrorFormat(module, fmt);
789
0
    if (strstr(fmt, "does not end in null byte") != nullptr)
790
0
    {
791
0
        CPLString osMsg;
792
0
        osMsg.vPrintf(pszModFmt, ap);
793
0
        CPLDebug("GTiff", "%s", osMsg.c_str());
794
0
    }
795
0
    else
796
0
    {
797
0
        CPLErrorV(CE_Warning, CPLE_AppDefined, pszModFmt, ap);
798
0
    }
799
0
    CPLFree(pszModFmt);
800
0
    return 1;
801
0
}
802
803
/************************************************************************/
804
/*                       GTiffErrorHandlerExt()                         */
805
/************************************************************************/
806
extern int GTiffErrorHandlerExt(TIFF *tif, void *user_data, const char *module,
807
                                const char *fmt, va_list ap);
808
809
int GTiffErrorHandlerExt(TIFF *tif, void *user_data, const char *module,
810
                         const char *fmt, va_list ap)
811
0
{
812
0
    (void)tif;
813
0
    (void)user_data;
814
0
    auto &nLibtiffErrors = GTIFFGetThreadLocalLibtiffError();
815
    // cppcheck-suppress knownConditionTrueFalse
816
0
    if (nLibtiffErrors > 0)
817
0
    {
818
0
        nLibtiffErrors++;
819
        // cppcheck-suppress knownConditionTrueFalse
820
0
        if (nLibtiffErrors > 10)
821
0
            return 1;
822
0
    }
823
824
0
    if (strcmp(fmt, "Maximum TIFF file size exceeded") == 0)
825
0
    {
826
0
        if (bThreadLocalInExternalOvr)
827
0
            fmt = "Maximum TIFF file size exceeded. "
828
0
                  "Use --config BIGTIFF_OVERVIEW YES configuration option.";
829
0
        else
830
0
            fmt = "Maximum TIFF file size exceeded. "
831
0
                  "Use BIGTIFF=YES creation option.";
832
0
    }
833
834
0
    char *pszModFmt = gdal::tiff_common::PrepareTIFFErrorFormat(module, fmt);
835
0
    CPLErrorV(CE_Failure, CPLE_AppDefined, pszModFmt, ap);
836
0
    CPLFree(pszModFmt);
837
0
    return 1;
838
0
}
839
840
#endif
841
842
/************************************************************************/
843
/*                          GTiffTagExtender()                          */
844
/*                                                                      */
845
/*      Install tags specially known to GDAL.                           */
846
/************************************************************************/
847
848
static TIFFExtendProc _ParentExtender = nullptr;
849
850
static void GTiffTagExtender(TIFF *tif)
851
852
0
{
853
0
    const TIFFFieldInfo xtiffFieldInfo[] = {
854
0
        {TIFFTAG_GDAL_METADATA, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE,
855
0
         const_cast<char *>("GDALMetadata")},
856
0
        {TIFFTAG_GDAL_NODATA, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE,
857
0
         const_cast<char *>("GDALNoDataValue")},
858
0
        {TIFFTAG_RPCCOEFFICIENT, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE,
859
0
         const_cast<char *>("RPCCoefficient")},
860
0
        {TIFFTAG_TIFF_RSID, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE,
861
0
         const_cast<char *>("TIFF_RSID")},
862
0
        {TIFFTAG_GEO_METADATA, TIFF_VARIABLE2, TIFF_VARIABLE2, TIFF_BYTE,
863
0
         FIELD_CUSTOM, TRUE, TRUE, const_cast<char *>("GEO_METADATA")}};
864
865
0
    if (_ParentExtender)
866
0
        (*_ParentExtender)(tif);
867
868
0
    TIFFMergeFieldInfo(tif, xtiffFieldInfo,
869
0
                       sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]));
870
0
}
871
872
/************************************************************************/
873
/*                          GTiffOneTimeInit()                          */
874
/*                                                                      */
875
/*      This is stuff that is initialized for the TIFF library just     */
876
/*      once.  We deliberately defer the initialization till the        */
877
/*      first time we are likely to call into libtiff to avoid          */
878
/*      unnecessary paging in of the library for GDAL apps that         */
879
/*      don't use it.                                                   */
880
/************************************************************************/
881
882
static std::mutex oDeleteMutex;
883
#ifdef HAVE_JXL
884
static TIFFCodec *pJXLCodec = nullptr;
885
static TIFFCodec *pJXLCodecDNG17 = nullptr;
886
#endif
887
888
void GTiffOneTimeInit()
889
890
0
{
891
0
    std::lock_guard<std::mutex> oLock(oDeleteMutex);
892
893
0
    static bool bOneTimeInitDone = false;
894
0
    if (bOneTimeInitDone)
895
0
        return;
896
897
0
    bOneTimeInitDone = true;
898
899
#ifdef HAVE_JXL
900
    if (pJXLCodec == nullptr)
901
    {
902
        pJXLCodec = TIFFRegisterCODEC(COMPRESSION_JXL, "JXL", TIFFInitJXL);
903
        pJXLCodecDNG17 =
904
            TIFFRegisterCODEC(COMPRESSION_JXL_DNG_1_7, "JXL", TIFFInitJXL);
905
    }
906
#endif
907
908
0
    _ParentExtender = TIFFSetTagExtender(GTiffTagExtender);
909
910
#if !defined(SUPPORTS_LIBTIFF_OPEN_OPTIONS)
911
    TIFFSetWarningHandler(GTiffWarningHandler);
912
    TIFFSetErrorHandler(GTiffErrorHandler);
913
#endif
914
915
0
    LibgeotiffOneTimeInit();
916
0
}
917
918
/************************************************************************/
919
/*                        GDALDeregister_GTiff()                        */
920
/************************************************************************/
921
922
static void GDALDeregister_GTiff(GDALDriver *)
923
924
0
{
925
#ifdef HAVE_JXL
926
    if (pJXLCodec)
927
        TIFFUnRegisterCODEC(pJXLCodec);
928
    pJXLCodec = nullptr;
929
    if (pJXLCodecDNG17)
930
        TIFFUnRegisterCODEC(pJXLCodecDNG17);
931
    pJXLCodecDNG17 = nullptr;
932
#endif
933
0
}
934
935
#define COMPRESSION_ENTRY(x, bWriteSupported)                                  \
936
    {                                                                          \
937
        COMPRESSION_##x, STRINGIFY(x), bWriteSupported                         \
938
    }
939
940
static const struct
941
{
942
    int nCode;
943
    const char *pszText;
944
    bool bWriteSupported;
945
} asCompressionNames[] = {
946
    // Compression methods in read/write mode
947
    COMPRESSION_ENTRY(NONE, true),
948
    COMPRESSION_ENTRY(CCITTRLE, true),
949
    COMPRESSION_ENTRY(CCITTFAX3, true),
950
    {COMPRESSION_CCITTFAX3, "FAX3", true},  // alternate name for write side
951
    COMPRESSION_ENTRY(CCITTFAX4, true),
952
    {COMPRESSION_CCITTFAX4, "FAX4", true},  // alternate name for write side
953
    COMPRESSION_ENTRY(LZW, true),
954
    COMPRESSION_ENTRY(JPEG, true),
955
    COMPRESSION_ENTRY(PACKBITS, true),
956
    {COMPRESSION_ADOBE_DEFLATE, "DEFLATE",
957
     true},  // manual entry since we want the user friendly name to be DEFLATE
958
    {COMPRESSION_ADOBE_DEFLATE, "ZIP", true},  // alternate name for write side
959
    COMPRESSION_ENTRY(LZMA, true),
960
    COMPRESSION_ENTRY(ZSTD, true),
961
    COMPRESSION_ENTRY(LERC, true),
962
    {COMPRESSION_LERC, "LERC_DEFLATE", true},
963
    {COMPRESSION_LERC, "LERC_ZSTD", true},
964
    COMPRESSION_ENTRY(WEBP, true),
965
    // COMPRESSION_JXL_DNG_1_7 must be *before* COMPRESSION_JXL
966
    {COMPRESSION_JXL_DNG_1_7, "JXL", true},
967
    {COMPRESSION_JXL, "JXL",
968
     true},  // deprecated. No longer used for writing since GDAL 3.11
969
970
    // Compression methods in read-only
971
    COMPRESSION_ENTRY(OJPEG, false),
972
    COMPRESSION_ENTRY(NEXT, false),
973
    COMPRESSION_ENTRY(CCITTRLEW, false),
974
    COMPRESSION_ENTRY(THUNDERSCAN, false),
975
    COMPRESSION_ENTRY(PIXARFILM, false),
976
    COMPRESSION_ENTRY(PIXARLOG, false),
977
    COMPRESSION_ENTRY(DEFLATE, false),  // COMPRESSION_DEFLATE is deprecated
978
    COMPRESSION_ENTRY(DCS, false),
979
    COMPRESSION_ENTRY(JBIG, false),
980
    COMPRESSION_ENTRY(SGILOG, false),
981
    COMPRESSION_ENTRY(SGILOG24, false),
982
    COMPRESSION_ENTRY(JP2000, false),
983
};
984
985
/************************************************************************/
986
/*                    GTIFFGetCompressionMethodName()                   */
987
/************************************************************************/
988
989
const char *GTIFFGetCompressionMethodName(int nCompressionCode)
990
0
{
991
0
    for (const auto &entry : asCompressionNames)
992
0
    {
993
0
        if (entry.nCode == nCompressionCode)
994
0
        {
995
0
            return entry.pszText;
996
0
        }
997
0
    }
998
0
    return nullptr;
999
0
}
1000
1001
/************************************************************************/
1002
/*                   GTIFFGetCompressionMethod()                        */
1003
/************************************************************************/
1004
1005
int GTIFFGetCompressionMethod(const char *pszValue, const char *pszVariableName)
1006
0
{
1007
0
    int nCompression = COMPRESSION_NONE;
1008
0
    bool bFoundMatch = false;
1009
0
    for (const auto &entry : asCompressionNames)
1010
0
    {
1011
0
        if (entry.bWriteSupported && EQUAL(entry.pszText, pszValue))
1012
0
        {
1013
0
            bFoundMatch = true;
1014
0
            nCompression = entry.nCode;
1015
0
            break;
1016
0
        }
1017
0
    }
1018
1019
0
    if (!bFoundMatch)
1020
0
    {
1021
0
        CPLError(CE_Warning, CPLE_IllegalArg,
1022
0
                 "%s=%s value not recognised, ignoring.", pszVariableName,
1023
0
                 pszValue);
1024
0
    }
1025
1026
0
    if (nCompression != COMPRESSION_NONE &&
1027
0
        !TIFFIsCODECConfigured(static_cast<uint16_t>(nCompression)))
1028
0
    {
1029
0
        CPLError(CE_Failure, CPLE_AppDefined,
1030
0
                 "Cannot create TIFF file due to missing codec for %s.",
1031
0
                 pszValue);
1032
0
        return -1;
1033
0
    }
1034
1035
0
    return nCompression;
1036
0
}
1037
1038
/************************************************************************/
1039
/*                     GTiffGetCompressValues()                         */
1040
/************************************************************************/
1041
1042
CPLString GTiffGetCompressValues(bool &bHasLZW, bool &bHasDEFLATE,
1043
                                 bool &bHasLZMA, bool &bHasZSTD, bool &bHasJPEG,
1044
                                 bool &bHasWebP, bool &bHasLERC, bool bForCOG)
1045
0
{
1046
0
    bHasLZW = false;
1047
0
    bHasDEFLATE = false;
1048
0
    bHasLZMA = false;
1049
0
    bHasZSTD = false;
1050
0
    bHasJPEG = false;
1051
0
    bHasWebP = false;
1052
0
    bHasLERC = false;
1053
1054
    /* -------------------------------------------------------------------- */
1055
    /*      Determine which compression codecs are available that we        */
1056
    /*      want to advertise.  If we are using an old libtiff we won't     */
1057
    /*      be able to find out so we just assume all are available.        */
1058
    /* -------------------------------------------------------------------- */
1059
0
    CPLString osCompressValues = "       <Value>NONE</Value>";
1060
1061
0
    TIFFCodec *codecs = TIFFGetConfiguredCODECs();
1062
1063
0
    for (TIFFCodec *c = codecs; c->name; ++c)
1064
0
    {
1065
0
        if (c->scheme == COMPRESSION_PACKBITS && !bForCOG)
1066
0
        {
1067
0
            osCompressValues += "       <Value>PACKBITS</Value>";
1068
0
        }
1069
0
        else if (c->scheme == COMPRESSION_JPEG)
1070
0
        {
1071
0
            bHasJPEG = true;
1072
0
            osCompressValues += "       <Value>JPEG</Value>";
1073
0
        }
1074
0
        else if (c->scheme == COMPRESSION_LZW)
1075
0
        {
1076
0
            bHasLZW = true;
1077
0
            osCompressValues += "       <Value>LZW</Value>";
1078
0
        }
1079
0
        else if (c->scheme == COMPRESSION_ADOBE_DEFLATE)
1080
0
        {
1081
0
            bHasDEFLATE = true;
1082
0
            osCompressValues += "       <Value>DEFLATE</Value>";
1083
0
        }
1084
0
        else if (c->scheme == COMPRESSION_CCITTRLE && !bForCOG)
1085
0
        {
1086
0
            osCompressValues += "       <Value>CCITTRLE</Value>";
1087
0
        }
1088
0
        else if (c->scheme == COMPRESSION_CCITTFAX3 && !bForCOG)
1089
0
        {
1090
0
            osCompressValues += "       <Value>CCITTFAX3</Value>";
1091
0
        }
1092
0
        else if (c->scheme == COMPRESSION_CCITTFAX4 && !bForCOG)
1093
0
        {
1094
0
            osCompressValues += "       <Value>CCITTFAX4</Value>";
1095
0
        }
1096
0
        else if (c->scheme == COMPRESSION_LZMA)
1097
0
        {
1098
0
            bHasLZMA = true;
1099
0
            osCompressValues += "       <Value>LZMA</Value>";
1100
0
        }
1101
0
        else if (c->scheme == COMPRESSION_ZSTD)
1102
0
        {
1103
0
            bHasZSTD = true;
1104
0
            osCompressValues += "       <Value>ZSTD</Value>";
1105
0
        }
1106
0
        else if (c->scheme == COMPRESSION_WEBP)
1107
0
        {
1108
0
            bHasWebP = true;
1109
0
            osCompressValues += "       <Value>WEBP</Value>";
1110
0
        }
1111
0
        else if (c->scheme == COMPRESSION_LERC)
1112
0
        {
1113
0
            bHasLERC = true;
1114
0
        }
1115
0
    }
1116
0
    if (bHasLERC)
1117
0
    {
1118
0
        osCompressValues += "       <Value>LERC</Value>"
1119
0
                            "       <Value>LERC_DEFLATE</Value>";
1120
0
        if (bHasZSTD)
1121
0
        {
1122
0
            osCompressValues += "       <Value>LERC_ZSTD</Value>";
1123
0
        }
1124
0
    }
1125
#ifdef HAVE_JXL
1126
    osCompressValues += "       <Value>JXL</Value>";
1127
#endif
1128
0
    _TIFFfree(codecs);
1129
1130
0
    return osCompressValues;
1131
0
}
1132
1133
/************************************************************************/
1134
/*                    OGRGTiffDriverGetSubdatasetInfo()                 */
1135
/************************************************************************/
1136
1137
struct GTiffDriverSubdatasetInfo : public GDALSubdatasetInfo
1138
{
1139
  public:
1140
    explicit GTiffDriverSubdatasetInfo(const std::string &fileName)
1141
0
        : GDALSubdatasetInfo(fileName)
1142
0
    {
1143
0
    }
1144
1145
    // GDALSubdatasetInfo interface
1146
  private:
1147
    void parseFileName() override;
1148
};
1149
1150
void GTiffDriverSubdatasetInfo::parseFileName()
1151
0
{
1152
0
    if (!STARTS_WITH_CI(m_fileName.c_str(), "GTIFF_DIR:"))
1153
0
    {
1154
0
        return;
1155
0
    }
1156
1157
0
    CPLStringList aosParts{CSLTokenizeString2(m_fileName.c_str(), ":", 0)};
1158
0
    const int iPartsCount{CSLCount(aosParts)};
1159
1160
0
    if (iPartsCount == 3 || iPartsCount == 4)
1161
0
    {
1162
1163
0
        m_driverPrefixComponent = aosParts[0];
1164
1165
0
        const bool hasDriveLetter{
1166
0
            strlen(aosParts[2]) == 1 &&
1167
0
            std::isalpha(static_cast<unsigned char>(aosParts[2][0]))};
1168
1169
        // Check for drive letter
1170
0
        if (iPartsCount == 4)
1171
0
        {
1172
            // Invalid
1173
0
            if (!hasDriveLetter)
1174
0
            {
1175
0
                return;
1176
0
            }
1177
0
            m_pathComponent = aosParts[2];
1178
0
            m_pathComponent.append(":");
1179
0
            m_pathComponent.append(aosParts[3]);
1180
0
        }
1181
0
        else  // count is 3
1182
0
        {
1183
0
            if (hasDriveLetter)
1184
0
            {
1185
0
                return;
1186
0
            }
1187
0
            m_pathComponent = aosParts[2];
1188
0
        }
1189
1190
0
        m_subdatasetComponent = aosParts[1];
1191
0
    }
1192
0
}
1193
1194
static GDALSubdatasetInfo *GTiffDriverGetSubdatasetInfo(const char *pszFileName)
1195
0
{
1196
0
    if (STARTS_WITH_CI(pszFileName, "GTIFF_DIR:"))
1197
0
    {
1198
0
        std::unique_ptr<GDALSubdatasetInfo> info =
1199
0
            std::make_unique<GTiffDriverSubdatasetInfo>(pszFileName);
1200
0
        if (!info->GetSubdatasetComponent().empty() &&
1201
0
            !info->GetPathComponent().empty())
1202
0
        {
1203
0
            return info.release();
1204
0
        }
1205
0
    }
1206
0
    return nullptr;
1207
0
}
1208
1209
/************************************************************************/
1210
/*                          GDALRegister_GTiff()                        */
1211
/************************************************************************/
1212
1213
void GDALRegister_GTiff()
1214
1215
0
{
1216
0
    if (GDALGetDriverByName("GTiff") != nullptr)
1217
0
        return;
1218
1219
0
    CPLString osOptions;
1220
1221
0
    bool bHasLZW = false;
1222
0
    bool bHasDEFLATE = false;
1223
0
    bool bHasLZMA = false;
1224
0
    bool bHasZSTD = false;
1225
0
    bool bHasJPEG = false;
1226
0
    bool bHasWebP = false;
1227
0
    bool bHasLERC = false;
1228
0
    CPLString osCompressValues(GTiffGetCompressValues(
1229
0
        bHasLZW, bHasDEFLATE, bHasLZMA, bHasZSTD, bHasJPEG, bHasWebP, bHasLERC,
1230
0
        false /* bForCOG */));
1231
1232
0
    GDALDriver *poDriver = new GDALDriver();
1233
1234
    /* -------------------------------------------------------------------- */
1235
    /*      Build full creation option list.                                */
1236
    /* -------------------------------------------------------------------- */
1237
0
    osOptions = "<CreationOptionList>"
1238
0
                "   <Option name='COMPRESS' type='string-select'>";
1239
0
    osOptions += osCompressValues;
1240
0
    osOptions += "   </Option>";
1241
0
    if (bHasLZW || bHasDEFLATE || bHasZSTD)
1242
0
        osOptions += ""
1243
0
                     "   <Option name='PREDICTOR' type='int' "
1244
0
                     "description='Predictor Type (1=default, 2=horizontal "
1245
0
                     "differencing, 3=floating point prediction)' "
1246
0
                     "default='1'/>";
1247
0
    osOptions +=
1248
0
        ""
1249
0
        "   <Option name='DISCARD_LSB' type='string' description='Number of "
1250
0
        "least-significant bits to set to clear as a single value or "
1251
0
        "comma-separated list of values for per-band values'/>";
1252
0
    if (bHasJPEG)
1253
0
    {
1254
0
        osOptions +=
1255
0
            ""
1256
0
            "   <Option name='JPEG_QUALITY' type='int' description='JPEG "
1257
0
            "quality 1-100' min='1' max='100' default='75'/>"
1258
0
            "   <Option name='JPEGTABLESMODE' type='int' description='Content "
1259
0
            "of JPEGTABLES tag. 0=no JPEGTABLES tag, 1=Quantization tables "
1260
0
            "only, 2=Huffman tables only, 3=Both' default='1'/>";
1261
#ifdef JPEG_DIRECT_COPY
1262
        osOptions +=
1263
            ""
1264
            "   <Option name='JPEG_DIRECT_COPY' type='boolean' description='To "
1265
            "copy without any decompression/recompression a JPEG source file' "
1266
            "default='NO'/>";
1267
#endif
1268
0
    }
1269
0
    if (bHasDEFLATE)
1270
0
    {
1271
#ifdef LIBDEFLATE_SUPPORT
1272
        osOptions += ""
1273
                     "   <Option name='ZLEVEL' type='int' description='DEFLATE "
1274
                     "compression level 1-12' min='1' max='12' default='6'/>";
1275
#else
1276
0
        osOptions += ""
1277
0
                     "   <Option name='ZLEVEL' type='int' description='DEFLATE "
1278
0
                     "compression level 1-9' min='1' max='9' default='6'/>";
1279
0
#endif
1280
0
    }
1281
0
    if (bHasLZMA)
1282
0
        osOptions +=
1283
0
            ""
1284
0
            "   <Option name='LZMA_PRESET' type='int' description='LZMA "
1285
0
            "compression level 0(fast)-9(slow)' min='0' max='9' default='6'/>";
1286
0
    if (bHasZSTD)
1287
0
        osOptions +=
1288
0
            ""
1289
0
            "   <Option name='ZSTD_LEVEL' type='int' description='ZSTD "
1290
0
            "compression level 1(fast)-22(slow)' min='1' max='22' "
1291
0
            "default='9'/>";
1292
0
    if (bHasLERC)
1293
0
    {
1294
0
        osOptions +=
1295
0
            ""
1296
0
            "   <Option name='MAX_Z_ERROR' type='float' description='Maximum "
1297
0
            "error for LERC compression' default='0'/>"
1298
0
            "   <Option name='MAX_Z_ERROR_OVERVIEW' type='float' "
1299
0
            "description='Maximum error for LERC compression in overviews' "
1300
0
            "default='0'/>";
1301
0
    }
1302
0
    if (bHasWebP)
1303
0
    {
1304
#ifndef DEFAULT_WEBP_LEVEL
1305
#error "DEFAULT_WEBP_LEVEL should be defined"
1306
#endif
1307
0
        osOptions +=
1308
0
            ""
1309
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
1310
            "   <Option name='WEBP_LOSSLESS' type='boolean' "
1311
            "description='Whether lossless compression should be used' "
1312
            "default='FALSE'/>"
1313
#endif
1314
0
            "   <Option name='WEBP_LEVEL' type='int' description='WEBP quality "
1315
0
            "level. Low values result in higher compression ratios' "
1316
0
            "default='" XSTRINGIFY(DEFAULT_WEBP_LEVEL) "'/>";
1317
0
    }
1318
#ifdef HAVE_JXL
1319
    osOptions +=
1320
        ""
1321
        "   <Option name='JXL_LOSSLESS' type='boolean' description='Whether "
1322
        "JPEGXL compression should be lossless' default='YES'/>"
1323
        "   <Option name='JXL_EFFORT' type='int' description='Level of effort "
1324
        "1(fast)-9(slow)' min='1' max='9' default='5'/>"
1325
        "   <Option name='JXL_DISTANCE' type='float' description='Distance "
1326
        "level for lossy compression (0=mathematically lossless, 1.0=visually "
1327
        "lossless, usual range [0.5,3])' default='1.0' min='0.01' max='25.0'/>";
1328
#ifdef HAVE_JxlEncoderSetExtraChannelDistance
1329
    osOptions += "   <Option name='JXL_ALPHA_DISTANCE' type='float' "
1330
                 "description='Distance level for alpha channel "
1331
                 "(-1=same as non-alpha channels, "
1332
                 "0=mathematically lossless, 1.0=visually lossless, "
1333
                 "usual range [0.5,3])' default='-1' min='-1' max='25.0'/>";
1334
#endif
1335
#endif
1336
0
    osOptions +=
1337
0
        ""
1338
0
        "   <Option name='NUM_THREADS' type='string' description='Number of "
1339
0
        "worker threads for compression. Can be set to ALL_CPUS' default='1'/>"
1340
0
        "   <Option name='NBITS' type='int' description='BITS for sub-byte "
1341
0
        "files (1-7), sub-uint16_t (9-15), sub-uint32_t (17-31), or float32 "
1342
0
        "(16)'/>"
1343
0
        "   <Option name='INTERLEAVE' type='string-select' default='PIXEL'>"
1344
0
        "       <Value>BAND</Value>"
1345
0
        "       <Value>PIXEL</Value>"
1346
0
        "   </Option>"
1347
0
        "   <Option name='TILED' type='boolean' description='Switch to tiled "
1348
0
        "format' default='NO'/>"
1349
0
        "   <Option name='TFW' type='boolean' description='Write out world "
1350
0
        "file'/>"
1351
0
        "   <Option name='RPB' type='boolean' description='Write out .RPB "
1352
0
        "(RPC) file'/>"
1353
0
        "   <Option name='RPCTXT' type='boolean' description='Write out "
1354
0
        "_RPC.TXT file'/>"
1355
0
        "   <Option name='BLOCKXSIZE' type='int' description='Tile Width' "
1356
0
        "default='256'/>"
1357
0
        "   <Option name='BLOCKYSIZE' type='int' description='Tile/Strip "
1358
0
        "Height'/>"
1359
0
        "   <Option name='PHOTOMETRIC' type='string-select'>"
1360
0
        "       <Value>MINISBLACK</Value>"
1361
0
        "       <Value>MINISWHITE</Value>"
1362
0
        "       <Value>PALETTE</Value>"
1363
0
        "       <Value>RGB</Value>"
1364
0
        "       <Value>CMYK</Value>"
1365
0
        "       <Value>YCBCR</Value>"
1366
0
        "       <Value>CIELAB</Value>"
1367
0
        "       <Value>ICCLAB</Value>"
1368
0
        "       <Value>ITULAB</Value>"
1369
0
        "   </Option>"
1370
0
        "   <Option name='SPARSE_OK' type='boolean' description='Should empty "
1371
0
        "blocks be omitted on disk?' default='FALSE'/>"
1372
0
        "   <Option name='ALPHA' type='string-select' description='Mark first "
1373
0
        "extrasample as being alpha'>"
1374
0
        "       <Value>NON-PREMULTIPLIED</Value>"
1375
0
        "       <Value>PREMULTIPLIED</Value>"
1376
0
        "       <Value>UNSPECIFIED</Value>"
1377
0
        "       <Value aliasOf='NON-PREMULTIPLIED'>YES</Value>"
1378
0
        "       <Value aliasOf='UNSPECIFIED'>NO</Value>"
1379
0
        "   </Option>"
1380
0
        "   <Option name='PROFILE' type='string-select' default='GDALGeoTIFF'>"
1381
0
        "       <Value>GDALGeoTIFF</Value>"
1382
0
        "       <Value>GeoTIFF</Value>"
1383
0
        "       <Value>BASELINE</Value>"
1384
0
        "   </Option>"
1385
0
        "   <Option name='PIXELTYPE' type='string-select' "
1386
0
        "description='(deprecated, use Int8 datatype)'>"
1387
0
        "       <Value>DEFAULT</Value>"
1388
0
        "       <Value>SIGNEDBYTE</Value>"
1389
0
        "   </Option>"
1390
0
        "   <Option name='BIGTIFF' type='string-select' description='Force "
1391
0
        "creation of BigTIFF file' default='IF_NEEDED'>"
1392
0
        "     <Value>YES</Value>"
1393
0
        "     <Value>NO</Value>"
1394
0
        "     <Value>IF_NEEDED</Value>"
1395
0
        "     <Value>IF_SAFER</Value>"
1396
0
        "   </Option>"
1397
0
        "   <Option name='ENDIANNESS' type='string-select' default='NATIVE' "
1398
0
        "description='Force endianness of created file. For DEBUG purpose "
1399
0
        "mostly'>"
1400
0
        "       <Value>NATIVE</Value>"
1401
0
        "       <Value>INVERTED</Value>"
1402
0
        "       <Value>LITTLE</Value>"
1403
0
        "       <Value>BIG</Value>"
1404
0
        "   </Option>"
1405
0
        "   <Option name='COPY_SRC_OVERVIEWS' type='boolean' default='NO' "
1406
0
        "description='Force copy of overviews of source dataset "
1407
0
        "(CreateCopy())'/>"
1408
0
        "   <Option name='SOURCE_ICC_PROFILE' type='string' description='ICC "
1409
0
        "profile'/>"
1410
0
        "   <Option name='SOURCE_PRIMARIES_RED' type='string' "
1411
0
        "description='x,y,1.0 (xyY) red chromaticity'/>"
1412
0
        "   <Option name='SOURCE_PRIMARIES_GREEN' type='string' "
1413
0
        "description='x,y,1.0 (xyY) green chromaticity'/>"
1414
0
        "   <Option name='SOURCE_PRIMARIES_BLUE' type='string' "
1415
0
        "description='x,y,1.0 (xyY) blue chromaticity'/>"
1416
0
        "   <Option name='SOURCE_WHITEPOINT' type='string' "
1417
0
        "description='x,y,1.0 (xyY) whitepoint'/>"
1418
0
        "   <Option name='TIFFTAG_TRANSFERFUNCTION_RED' type='string' "
1419
0
        "description='Transfer function for red'/>"
1420
0
        "   <Option name='TIFFTAG_TRANSFERFUNCTION_GREEN' type='string' "
1421
0
        "description='Transfer function for green'/>"
1422
0
        "   <Option name='TIFFTAG_TRANSFERFUNCTION_BLUE' type='string' "
1423
0
        "description='Transfer function for blue'/>"
1424
0
        "   <Option name='TIFFTAG_TRANSFERRANGE_BLACK' type='string' "
1425
0
        "description='Transfer range for black'/>"
1426
0
        "   <Option name='TIFFTAG_TRANSFERRANGE_WHITE' type='string' "
1427
0
        "description='Transfer range for white'/>"
1428
0
        "   <Option name='STREAMABLE_OUTPUT' type='boolean' default='NO' "
1429
0
        "description='Enforce a mode compatible with a streamable file'/>"
1430
0
        "   <Option name='GEOTIFF_KEYS_FLAVOR' type='string-select' "
1431
0
        "default='STANDARD' description='Which flavor of GeoTIFF keys must be "
1432
0
        "used'>"
1433
0
        "       <Value>STANDARD</Value>"
1434
0
        "       <Value>ESRI_PE</Value>"
1435
0
        "   </Option>"
1436
0
#if LIBGEOTIFF_VERSION >= 1600
1437
0
        "   <Option name='GEOTIFF_VERSION' type='string-select' default='AUTO' "
1438
0
        "description='Which version of GeoTIFF must be used'>"
1439
0
        "       <Value>AUTO</Value>"
1440
0
        "       <Value>1.0</Value>"
1441
0
        "       <Value>1.1</Value>"
1442
0
        "   </Option>"
1443
0
#endif
1444
0
        "   <Option name='COLOR_TABLE_MULTIPLIER' type='string-select' "
1445
0
        "description='Multiplication factor to apply to go from GDAL color "
1446
0
        "table to TIFF color table' "
1447
0
        "default='257'>"
1448
0
        "       <Value>1</Value>"
1449
0
        "       <Value>256</Value>"
1450
0
        "       <Value>257</Value>"
1451
0
        "   </Option>"
1452
0
        "</CreationOptionList>";
1453
1454
    /* -------------------------------------------------------------------- */
1455
    /*      Set the driver details.                                         */
1456
    /* -------------------------------------------------------------------- */
1457
0
    poDriver->SetDescription("GTiff");
1458
0
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1459
0
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GeoTIFF");
1460
0
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gtiff.html");
1461
0
    poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/tiff");
1462
0
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "tif");
1463
0
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "tif tiff");
1464
0
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
1465
0
                              "Byte Int8 UInt16 Int16 UInt32 Int32 Float32 "
1466
0
                              "Float64 CInt16 CInt32 CFloat32 CFloat64");
1467
0
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST, osOptions);
1468
0
    poDriver->SetMetadataItem(
1469
0
        GDAL_DMD_OPENOPTIONLIST,
1470
0
        "<OpenOptionList>"
1471
0
        "   <Option name='NUM_THREADS' type='string' description='Number of "
1472
0
        "worker threads for compression. Can be set to ALL_CPUS' default='1'/>"
1473
0
        "   <Option name='GEOTIFF_KEYS_FLAVOR' type='string-select' "
1474
0
        "default='STANDARD' description='Which flavor of GeoTIFF keys must be "
1475
0
        "used (for writing)'>"
1476
0
        "       <Value>STANDARD</Value>"
1477
0
        "       <Value>ESRI_PE</Value>"
1478
0
        "   </Option>"
1479
0
        "   <Option name='GEOREF_SOURCES' type='string' description='Comma "
1480
0
        "separated list made with values "
1481
0
        "INTERNAL/TABFILE/WORLDFILE/PAM/XML/NONE "
1482
0
        "that describe the priority order for georeferencing' "
1483
0
        "default='PAM,INTERNAL,TABFILE,WORLDFILE,XML'/>"
1484
0
        "   <Option name='SPARSE_OK' type='boolean' description='Should empty "
1485
0
        "blocks be omitted on disk?' default='FALSE'/>"
1486
0
        "   <Option name='IGNORE_COG_LAYOUT_BREAK' type='boolean' "
1487
0
        "description='Allow update mode on files with COG structure' "
1488
0
        "default='FALSE'/>"
1489
0
        "   <Option name='COLOR_TABLE_MULTIPLIER' type='string-select' "
1490
0
        "description='Multiplication factor to apply to go from GDAL color "
1491
0
        "table to TIFF color table' "
1492
0
        "default='AUTO'>"
1493
0
        "       <Value>AUTO</Value>"
1494
0
        "       <Value>1</Value>"
1495
0
        "       <Value>256</Value>"
1496
0
        "       <Value>257</Value>"
1497
0
        "   </Option>"
1498
0
        "</OpenOptionList>");
1499
0
    poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
1500
0
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_SUBDATASETS, "YES");
1501
0
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1502
1503
0
    poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
1504
0
    poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS,
1505
0
                              "GeoTransform SRS GCPs NoData "
1506
0
                              "ColorInterpretation RasterValues "
1507
0
                              "DatasetMetadata BandMetadata");
1508
1509
0
#ifdef INTERNAL_LIBTIFF
1510
0
    poDriver->SetMetadataItem("LIBTIFF", "INTERNAL");
1511
#else
1512
    poDriver->SetMetadataItem("LIBTIFF", TIFFLIB_VERSION_STR);
1513
#endif
1514
1515
0
    poDriver->SetMetadataItem("LIBGEOTIFF", XSTRINGIFY(LIBGEOTIFF_VERSION));
1516
1517
#if defined(LERC_SUPPORT) && defined(LERC_VERSION_MAJOR)
1518
    poDriver->SetMetadataItem("LERC_VERSION_MAJOR",
1519
                              XSTRINGIFY(LERC_VERSION_MAJOR), "LERC");
1520
    poDriver->SetMetadataItem("LERC_VERSION_MINOR",
1521
                              XSTRINGIFY(LERC_VERSION_MINOR), "LERC");
1522
    poDriver->SetMetadataItem("LERC_VERSION_PATCH",
1523
                              XSTRINGIFY(LERC_VERSION_PATCH), "LERC");
1524
#endif
1525
1526
0
    poDriver->SetMetadataItem(GDAL_DCAP_COORDINATE_EPOCH, "YES");
1527
1528
0
    poDriver->pfnOpen = GTiffDataset::Open;
1529
0
    poDriver->pfnCreate = GTiffDataset::Create;
1530
0
    poDriver->pfnCreateCopy = GTiffDataset::CreateCopy;
1531
0
    poDriver->pfnUnloadDriver = GDALDeregister_GTiff;
1532
0
    poDriver->pfnIdentify = GTiffDataset::Identify;
1533
0
    poDriver->pfnGetSubdatasetInfoFunc = GTiffDriverGetSubdatasetInfo;
1534
1535
0
    GetGDALDriverManager()->RegisterDriver(poDriver);
1536
0
}