Coverage Report

Created: 2025-06-13 06:18

/src/gdal/frmts/gtiff/gtiffrasterband_write.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  GeoTIFF Driver
4
 * Purpose:  Write/set operations on GTiffRasterBand
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 "gtiffrasterband.h"
15
#include "gtiffdataset.h"
16
17
#include <algorithm>
18
#include <cmath>
19
#include <limits>
20
21
#include "cpl_vsi_virtual.h"
22
#include "gdal_priv_templates.hpp"
23
#include "gtiff.h"
24
#include "tifvsi.h"
25
26
/************************************************************************/
27
/*                           SetDefaultRAT()                            */
28
/************************************************************************/
29
30
CPLErr GTiffRasterBand::SetDefaultRAT(const GDALRasterAttributeTable *poRAT)
31
0
{
32
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
33
0
    return GDALPamRasterBand::SetDefaultRAT(poRAT);
34
0
}
35
36
/************************************************************************/
37
/*                            IWriteBlock()                             */
38
/************************************************************************/
39
40
CPLErr GTiffRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
41
                                    void *pImage)
42
43
0
{
44
0
    m_poGDS->Crystalize();
45
46
0
    if (m_poGDS->m_bDebugDontWriteBlocks)
47
0
        return CE_None;
48
49
0
    if (m_poGDS->m_bWriteError)
50
0
    {
51
        // Report as an error if a previously loaded block couldn't be written
52
        // correctly.
53
0
        return CE_Failure;
54
0
    }
55
56
0
    const int nBlockId = ComputeBlockId(nBlockXOff, nBlockYOff);
57
58
    /* -------------------------------------------------------------------- */
59
    /*      Handle case of "separate" images                                */
60
    /* -------------------------------------------------------------------- */
61
0
    if (m_poGDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE ||
62
0
        m_poGDS->nBands == 1)
63
0
    {
64
0
        const CPLErr eErr =
65
0
            m_poGDS->WriteEncodedTileOrStrip(nBlockId, pImage, true);
66
67
0
        return eErr;
68
0
    }
69
70
    /* -------------------------------------------------------------------- */
71
    /*      Handle case of pixel interleaved (PLANARCONFIG_CONTIG) images.  */
72
    /* -------------------------------------------------------------------- */
73
    // Why 10 ? Somewhat arbitrary
74
0
    constexpr int MAX_BANDS_FOR_DIRTY_CHECK = 10;
75
0
    GDALRasterBlock *apoBlocks[MAX_BANDS_FOR_DIRTY_CHECK] = {};
76
0
    const int nBands = m_poGDS->nBands;
77
0
    bool bAllBlocksDirty = false;
78
79
    /* -------------------------------------------------------------------- */
80
    /*     If all blocks are cached and dirty then we do not need to reload */
81
    /*     the tile/strip from disk                                         */
82
    /* -------------------------------------------------------------------- */
83
0
    if (nBands <= MAX_BANDS_FOR_DIRTY_CHECK)
84
0
    {
85
0
        bAllBlocksDirty = true;
86
0
        for (int iBand = 0; iBand < nBands; ++iBand)
87
0
        {
88
0
            if (iBand + 1 != nBand)
89
0
            {
90
0
                apoBlocks[iBand] =
91
0
                    cpl::down_cast<GTiffRasterBand *>(
92
0
                        m_poGDS->GetRasterBand(iBand + 1))
93
0
                        ->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
94
95
0
                if (apoBlocks[iBand] == nullptr)
96
0
                {
97
0
                    bAllBlocksDirty = false;
98
0
                }
99
0
                else if (!apoBlocks[iBand]->GetDirty())
100
0
                {
101
0
                    apoBlocks[iBand]->DropLock();
102
0
                    apoBlocks[iBand] = nullptr;
103
0
                    bAllBlocksDirty = false;
104
0
                }
105
0
            }
106
0
            else
107
0
                apoBlocks[iBand] = nullptr;
108
0
        }
109
#if DEBUG_VERBOSE
110
        if (bAllBlocksDirty)
111
            CPLDebug("GTIFF", "Saved reloading block %d", nBlockId);
112
        else
113
            CPLDebug("GTIFF", "Must reload block %d", nBlockId);
114
#endif
115
0
    }
116
117
0
    {
118
0
        const CPLErr eErr = m_poGDS->LoadBlockBuf(nBlockId, !bAllBlocksDirty);
119
0
        if (eErr != CE_None)
120
0
        {
121
0
            if (nBands <= MAX_BANDS_FOR_DIRTY_CHECK)
122
0
            {
123
0
                for (int iBand = 0; iBand < nBands; ++iBand)
124
0
                {
125
0
                    if (apoBlocks[iBand] != nullptr)
126
0
                        apoBlocks[iBand]->DropLock();
127
0
                }
128
0
            }
129
0
            return eErr;
130
0
        }
131
0
    }
132
133
    /* -------------------------------------------------------------------- */
134
    /*      On write of pixel interleaved data, we might as well flush      */
135
    /*      out any other bands that are dirty in our cache.  This is       */
136
    /*      especially helpful when writing compressed blocks.              */
137
    /* -------------------------------------------------------------------- */
138
0
    const int nWordBytes = m_poGDS->m_nBitsPerSample / 8;
139
140
0
    for (int iBand = 0; iBand < nBands; ++iBand)
141
0
    {
142
0
        const GByte *pabyThisImage = nullptr;
143
0
        GDALRasterBlock *poBlock = nullptr;
144
145
0
        if (iBand + 1 == nBand)
146
0
        {
147
0
            pabyThisImage = static_cast<GByte *>(pImage);
148
0
        }
149
0
        else
150
0
        {
151
0
            if (nBands <= MAX_BANDS_FOR_DIRTY_CHECK)
152
0
                poBlock = apoBlocks[iBand];
153
0
            else
154
0
                poBlock = cpl::down_cast<GTiffRasterBand *>(
155
0
                              m_poGDS->GetRasterBand(iBand + 1))
156
0
                              ->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
157
158
0
            if (poBlock == nullptr)
159
0
                continue;
160
161
0
            if (!poBlock->GetDirty())
162
0
            {
163
0
                poBlock->DropLock();
164
0
                continue;
165
0
            }
166
167
0
            pabyThisImage = static_cast<GByte *>(poBlock->GetDataRef());
168
0
        }
169
170
0
        GByte *pabyOut = m_poGDS->m_pabyBlockBuf + iBand * nWordBytes;
171
172
0
        GDALCopyWords64(pabyThisImage, eDataType, nWordBytes, pabyOut,
173
0
                        eDataType, nWordBytes * nBands,
174
0
                        static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize);
175
176
0
        if (poBlock != nullptr)
177
0
        {
178
0
            poBlock->MarkClean();
179
0
            poBlock->DropLock();
180
0
        }
181
0
    }
182
183
0
    if (bAllBlocksDirty)
184
0
    {
185
        // We can synchronously write the block now.
186
0
        const CPLErr eErr = m_poGDS->WriteEncodedTileOrStrip(
187
0
            nBlockId, m_poGDS->m_pabyBlockBuf, true);
188
0
        m_poGDS->m_bLoadedBlockDirty = false;
189
0
        return eErr;
190
0
    }
191
192
0
    m_poGDS->m_bLoadedBlockDirty = true;
193
194
0
    return CE_None;
195
0
}
196
197
/************************************************************************/
198
/*                           SetDescription()                           */
199
/************************************************************************/
200
201
void GTiffRasterBand::SetDescription(const char *pszDescription)
202
203
0
{
204
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
205
206
0
    if (pszDescription == nullptr)
207
0
        pszDescription = "";
208
209
0
    if (m_osDescription != pszDescription)
210
0
        m_poGDS->m_bMetadataChanged = true;
211
212
0
    m_osDescription = pszDescription;
213
0
}
214
215
/************************************************************************/
216
/*                             SetOffset()                              */
217
/************************************************************************/
218
219
CPLErr GTiffRasterBand::SetOffset(double dfNewValue)
220
221
0
{
222
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
223
224
0
    if (!m_bHaveOffsetScale || dfNewValue != m_dfOffset)
225
0
        m_poGDS->m_bMetadataChanged = true;
226
227
0
    m_bHaveOffsetScale = true;
228
0
    m_dfOffset = dfNewValue;
229
0
    return CE_None;
230
0
}
231
232
/************************************************************************/
233
/*                              SetScale()                              */
234
/************************************************************************/
235
236
CPLErr GTiffRasterBand::SetScale(double dfNewValue)
237
238
0
{
239
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
240
241
0
    if (!m_bHaveOffsetScale || dfNewValue != m_dfScale)
242
0
        m_poGDS->m_bMetadataChanged = true;
243
244
0
    m_bHaveOffsetScale = true;
245
0
    m_dfScale = dfNewValue;
246
0
    return CE_None;
247
0
}
248
249
/************************************************************************/
250
/*                           SetUnitType()                              */
251
/************************************************************************/
252
253
CPLErr GTiffRasterBand::SetUnitType(const char *pszNewValue)
254
255
0
{
256
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
257
258
0
    CPLString osNewValue(pszNewValue ? pszNewValue : "");
259
0
    if (osNewValue.compare(m_osUnitType) != 0)
260
0
        m_poGDS->m_bMetadataChanged = true;
261
262
0
    m_osUnitType = std::move(osNewValue);
263
0
    return CE_None;
264
0
}
265
266
/************************************************************************/
267
/*                            SetMetadata()                             */
268
/************************************************************************/
269
270
CPLErr GTiffRasterBand::SetMetadata(char **papszMD, const char *pszDomain)
271
272
0
{
273
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
274
275
0
    if (m_poGDS->m_bStreamingOut && m_poGDS->m_bCrystalized)
276
0
    {
277
0
        ReportError(CE_Failure, CPLE_NotSupported,
278
0
                    "Cannot modify metadata at that point in a streamed "
279
0
                    "output file");
280
0
        return CE_Failure;
281
0
    }
282
283
0
    CPLErr eErr = CE_None;
284
0
    if (eAccess == GA_Update)
285
0
    {
286
0
        if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
287
0
        {
288
0
            if (papszMD != nullptr || GetMetadata(pszDomain) != nullptr)
289
0
            {
290
0
                m_poGDS->m_bMetadataChanged = true;
291
                // Cancel any existing metadata from PAM file.
292
0
                if (GDALPamRasterBand::GetMetadata(pszDomain) != nullptr)
293
0
                    GDALPamRasterBand::SetMetadata(nullptr, pszDomain);
294
0
            }
295
0
        }
296
0
    }
297
0
    else
298
0
    {
299
0
        CPLDebug(
300
0
            "GTIFF",
301
0
            "GTiffRasterBand::SetMetadata() goes to PAM instead of TIFF tags");
302
0
        eErr = GDALPamRasterBand::SetMetadata(papszMD, pszDomain);
303
0
    }
304
305
0
    if (eErr == CE_None)
306
0
    {
307
0
        eErr = m_oGTiffMDMD.SetMetadata(papszMD, pszDomain);
308
0
    }
309
0
    return eErr;
310
0
}
311
312
/************************************************************************/
313
/*                          SetMetadataItem()                           */
314
/************************************************************************/
315
316
CPLErr GTiffRasterBand::SetMetadataItem(const char *pszName,
317
                                        const char *pszValue,
318
                                        const char *pszDomain)
319
320
0
{
321
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
322
323
0
    if (m_poGDS->m_bStreamingOut && m_poGDS->m_bCrystalized)
324
0
    {
325
0
        ReportError(CE_Failure, CPLE_NotSupported,
326
0
                    "Cannot modify metadata at that point in a streamed "
327
0
                    "output file");
328
0
        return CE_Failure;
329
0
    }
330
331
0
    CPLErr eErr = CE_None;
332
0
    if (eAccess == GA_Update)
333
0
    {
334
0
        if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
335
0
        {
336
0
            m_poGDS->m_bMetadataChanged = true;
337
            // Cancel any existing metadata from PAM file.
338
0
            if (GDALPamRasterBand::GetMetadataItem(pszName, pszDomain) !=
339
0
                nullptr)
340
0
                GDALPamRasterBand::SetMetadataItem(pszName, nullptr, pszDomain);
341
0
        }
342
0
    }
343
0
    else
344
0
    {
345
0
        CPLDebug("GTIFF", "GTiffRasterBand::SetMetadataItem() goes to PAM "
346
0
                          "instead of TIFF tags");
347
0
        eErr = GDALPamRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
348
0
    }
349
350
0
    if (eErr == CE_None)
351
0
    {
352
0
        eErr = m_oGTiffMDMD.SetMetadataItem(pszName, pszValue, pszDomain);
353
0
    }
354
0
    return eErr;
355
0
}
356
357
/************************************************************************/
358
/*                       SetColorInterpretation()                       */
359
/************************************************************************/
360
361
CPLErr GTiffRasterBand::SetColorInterpretation(GDALColorInterp eInterp)
362
363
0
{
364
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
365
366
0
    if (eInterp == m_eBandInterp)
367
0
        return CE_None;
368
369
0
    m_eBandInterp = eInterp;
370
371
0
    if (eAccess != GA_Update)
372
0
    {
373
0
        CPLDebug("GTIFF",
374
0
                 "ColorInterpretation %s for band %d goes to PAM "
375
0
                 "instead of TIFF tag",
376
0
                 GDALGetColorInterpretationName(eInterp), nBand);
377
0
        return GDALPamRasterBand::SetColorInterpretation(eInterp);
378
0
    }
379
380
0
    m_poGDS->m_bNeedsRewrite = true;
381
0
    m_poGDS->m_bMetadataChanged = true;
382
383
    // Try to autoset TIFFTAG_PHOTOMETRIC = PHOTOMETRIC_RGB if possible.
384
0
    if (m_poGDS->nBands >= 3 && m_poGDS->m_nCompression != COMPRESSION_JPEG &&
385
0
        m_poGDS->m_nPhotometric != PHOTOMETRIC_RGB &&
386
0
        CSLFetchNameValue(m_poGDS->m_papszCreationOptions, "PHOTOMETRIC") ==
387
0
            nullptr &&
388
0
        ((nBand == 1 && eInterp == GCI_RedBand) ||
389
0
         (nBand == 2 && eInterp == GCI_GreenBand) ||
390
0
         (nBand == 3 && eInterp == GCI_BlueBand)))
391
0
    {
392
0
        if (m_poGDS->GetRasterBand(1)->GetColorInterpretation() ==
393
0
                GCI_RedBand &&
394
0
            m_poGDS->GetRasterBand(2)->GetColorInterpretation() ==
395
0
                GCI_GreenBand &&
396
0
            m_poGDS->GetRasterBand(3)->GetColorInterpretation() == GCI_BlueBand)
397
0
        {
398
0
            m_poGDS->m_nPhotometric = PHOTOMETRIC_RGB;
399
0
            TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_PHOTOMETRIC,
400
0
                         m_poGDS->m_nPhotometric);
401
402
            // We need to update the number of extra samples.
403
0
            uint16_t *v = nullptr;
404
0
            uint16_t count = 0;
405
0
            const uint16_t nNewExtraSamplesCount =
406
0
                static_cast<uint16_t>(m_poGDS->nBands - 3);
407
0
            if (m_poGDS->nBands >= 4 &&
408
0
                TIFFGetField(m_poGDS->m_hTIFF, TIFFTAG_EXTRASAMPLES, &count,
409
0
                             &v) &&
410
0
                count > nNewExtraSamplesCount)
411
0
            {
412
0
                uint16_t *const pasNewExtraSamples = static_cast<uint16_t *>(
413
0
                    CPLMalloc(nNewExtraSamplesCount * sizeof(uint16_t)));
414
0
                memcpy(pasNewExtraSamples, v + count - nNewExtraSamplesCount,
415
0
                       nNewExtraSamplesCount * sizeof(uint16_t));
416
417
0
                TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_EXTRASAMPLES,
418
0
                             nNewExtraSamplesCount, pasNewExtraSamples);
419
420
0
                CPLFree(pasNewExtraSamples);
421
0
            }
422
0
        }
423
0
        return CE_None;
424
0
    }
425
426
    // On the contrary, cancel the above if needed
427
0
    if (m_poGDS->m_nCompression != COMPRESSION_JPEG &&
428
0
        m_poGDS->m_nPhotometric == PHOTOMETRIC_RGB &&
429
0
        CSLFetchNameValue(m_poGDS->m_papszCreationOptions, "PHOTOMETRIC") ==
430
0
            nullptr &&
431
0
        ((nBand == 1 && eInterp != GCI_RedBand) ||
432
0
         (nBand == 2 && eInterp != GCI_GreenBand) ||
433
0
         (nBand == 3 && eInterp != GCI_BlueBand)))
434
0
    {
435
0
        m_poGDS->m_nPhotometric = PHOTOMETRIC_MINISBLACK;
436
0
        TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_PHOTOMETRIC,
437
0
                     m_poGDS->m_nPhotometric);
438
439
        // We need to update the number of extra samples.
440
0
        uint16_t *v = nullptr;
441
0
        uint16_t count = 0;
442
0
        const uint16_t nNewExtraSamplesCount =
443
0
            static_cast<uint16_t>(m_poGDS->nBands - 1);
444
0
        if (m_poGDS->nBands >= 2)
445
0
        {
446
0
            TIFFGetField(m_poGDS->m_hTIFF, TIFFTAG_EXTRASAMPLES, &count, &v);
447
0
            if (nNewExtraSamplesCount > count)
448
0
            {
449
0
                uint16_t *const pasNewExtraSamples = static_cast<uint16_t *>(
450
0
                    CPLMalloc(nNewExtraSamplesCount * sizeof(uint16_t)));
451
0
                for (int i = 0;
452
0
                     i < static_cast<int>(nNewExtraSamplesCount - count); ++i)
453
0
                    pasNewExtraSamples[i] = EXTRASAMPLE_UNSPECIFIED;
454
0
                if (count > 0)
455
0
                {
456
0
                    memcpy(pasNewExtraSamples + nNewExtraSamplesCount - count,
457
0
                           v, count * sizeof(uint16_t));
458
0
                }
459
460
0
                TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_EXTRASAMPLES,
461
0
                             nNewExtraSamplesCount, pasNewExtraSamples);
462
463
0
                CPLFree(pasNewExtraSamples);
464
0
            }
465
0
        }
466
0
    }
467
468
    // Mark alpha band / undefined in extrasamples.
469
0
    if (eInterp == GCI_AlphaBand || eInterp == GCI_Undefined)
470
0
    {
471
0
        uint16_t *v = nullptr;
472
0
        uint16_t count = 0;
473
0
        if (TIFFGetField(m_poGDS->m_hTIFF, TIFFTAG_EXTRASAMPLES, &count, &v))
474
0
        {
475
0
            const int nBaseSamples = m_poGDS->m_nSamplesPerPixel - count;
476
477
0
            if (eInterp == GCI_AlphaBand)
478
0
            {
479
0
                for (int i = 1; i <= m_poGDS->nBands; ++i)
480
0
                {
481
0
                    if (i != nBand &&
482
0
                        m_poGDS->GetRasterBand(i)->GetColorInterpretation() ==
483
0
                            GCI_AlphaBand)
484
0
                    {
485
0
                        if (i == nBaseSamples + 1 &&
486
0
                            CSLFetchNameValue(m_poGDS->m_papszCreationOptions,
487
0
                                              "ALPHA") != nullptr)
488
0
                        {
489
0
                            ReportError(
490
0
                                CE_Warning, CPLE_AppDefined,
491
0
                                "Band %d was already identified as alpha band, "
492
0
                                "and band %d is now marked as alpha too. "
493
0
                                "Presumably ALPHA creation option is not "
494
0
                                "needed",
495
0
                                i, nBand);
496
0
                        }
497
0
                        else
498
0
                        {
499
0
                            ReportError(
500
0
                                CE_Warning, CPLE_AppDefined,
501
0
                                "Band %d was already identified as alpha band, "
502
0
                                "and band %d is now marked as alpha too",
503
0
                                i, nBand);
504
0
                        }
505
0
                    }
506
0
                }
507
0
            }
508
509
0
            if (nBand > nBaseSamples && nBand - nBaseSamples - 1 < count)
510
0
            {
511
                // We need to allocate a new array as (current) libtiff
512
                // versions will not like that we reuse the array we got from
513
                // TIFFGetField().
514
515
0
                uint16_t *pasNewExtraSamples = static_cast<uint16_t *>(
516
0
                    CPLMalloc(count * sizeof(uint16_t)));
517
0
                memcpy(pasNewExtraSamples, v, count * sizeof(uint16_t));
518
0
                if (eInterp == GCI_AlphaBand)
519
0
                {
520
0
                    pasNewExtraSamples[nBand - nBaseSamples - 1] =
521
0
                        GTiffGetAlphaValue(
522
0
                            CPLGetConfigOption("GTIFF_ALPHA", nullptr),
523
0
                            DEFAULT_ALPHA_TYPE);
524
0
                }
525
0
                else
526
0
                {
527
0
                    pasNewExtraSamples[nBand - nBaseSamples - 1] =
528
0
                        EXTRASAMPLE_UNSPECIFIED;
529
0
                }
530
531
0
                TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_EXTRASAMPLES, count,
532
0
                             pasNewExtraSamples);
533
534
0
                CPLFree(pasNewExtraSamples);
535
536
0
                return CE_None;
537
0
            }
538
0
        }
539
0
    }
540
541
0
    if (m_poGDS->m_nPhotometric != PHOTOMETRIC_MINISBLACK &&
542
0
        CSLFetchNameValue(m_poGDS->m_papszCreationOptions, "PHOTOMETRIC") ==
543
0
            nullptr)
544
0
    {
545
0
        m_poGDS->m_nPhotometric = PHOTOMETRIC_MINISBLACK;
546
0
        TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_PHOTOMETRIC,
547
0
                     m_poGDS->m_nPhotometric);
548
0
    }
549
550
0
    return CE_None;
551
0
}
552
553
/************************************************************************/
554
/*                           SetColorTable()                            */
555
/************************************************************************/
556
557
CPLErr GTiffRasterBand::SetColorTable(GDALColorTable *poCT)
558
559
0
{
560
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
561
562
    /* -------------------------------------------------------------------- */
563
    /*      Check if this is even a candidate for applying a PCT.           */
564
    /* -------------------------------------------------------------------- */
565
0
    if (eAccess == GA_Update)
566
0
    {
567
0
        if (nBand != 1)
568
0
        {
569
0
            ReportError(CE_Failure, CPLE_NotSupported,
570
0
                        "SetColorTable() can only be called on band 1.");
571
0
            return CE_Failure;
572
0
        }
573
574
0
        if (m_poGDS->m_nSamplesPerPixel != 1 &&
575
0
            m_poGDS->m_nSamplesPerPixel != 2)
576
0
        {
577
0
            ReportError(CE_Failure, CPLE_NotSupported,
578
0
                        "SetColorTable() not supported for multi-sample TIFF "
579
0
                        "files.");
580
0
            return CE_Failure;
581
0
        }
582
583
0
        if (eDataType != GDT_Byte && eDataType != GDT_UInt16)
584
0
        {
585
0
            ReportError(
586
0
                CE_Failure, CPLE_NotSupported,
587
0
                "SetColorTable() only supported for Byte or UInt16 bands "
588
0
                "in TIFF format.");
589
0
            return CE_Failure;
590
0
        }
591
592
        // Clear any existing PAM color table
593
0
        if (GDALPamRasterBand::GetColorTable() != nullptr)
594
0
        {
595
0
            GDALPamRasterBand::SetColorTable(nullptr);
596
0
            GDALPamRasterBand::SetColorInterpretation(GCI_Undefined);
597
0
        }
598
0
    }
599
600
    /* -------------------------------------------------------------------- */
601
    /*      Is this really a request to clear the color table?              */
602
    /* -------------------------------------------------------------------- */
603
0
    if (poCT == nullptr || poCT->GetColorEntryCount() == 0)
604
0
    {
605
0
        if (eAccess == GA_Update)
606
0
        {
607
0
            TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_PHOTOMETRIC,
608
0
                         PHOTOMETRIC_MINISBLACK);
609
610
0
            TIFFUnsetField(m_poGDS->m_hTIFF, TIFFTAG_COLORMAP);
611
0
        }
612
613
0
        m_poGDS->m_poColorTable.reset();
614
615
0
        return CE_None;
616
0
    }
617
618
    /* -------------------------------------------------------------------- */
619
    /*      Write out the colortable, and update the configuration.         */
620
    /* -------------------------------------------------------------------- */
621
0
    CPLErr eErr = CE_None;
622
0
    if (eAccess == GA_Update)
623
0
    {
624
0
        int nColors = 65536;
625
626
0
        if (eDataType == GDT_Byte)
627
0
            nColors = 256;
628
629
0
        unsigned short *panTRed = static_cast<unsigned short *>(
630
0
            CPLMalloc(sizeof(unsigned short) * nColors));
631
0
        unsigned short *panTGreen = static_cast<unsigned short *>(
632
0
            CPLMalloc(sizeof(unsigned short) * nColors));
633
0
        unsigned short *panTBlue = static_cast<unsigned short *>(
634
0
            CPLMalloc(sizeof(unsigned short) * nColors));
635
636
0
        if (m_poGDS->m_nColorTableMultiplier == 0)
637
0
            m_poGDS->m_nColorTableMultiplier =
638
0
                GTiffDataset::DEFAULT_COLOR_TABLE_MULTIPLIER_257;
639
640
0
        for (int iColor = 0; iColor < nColors; ++iColor)
641
0
        {
642
0
            if (iColor < poCT->GetColorEntryCount())
643
0
            {
644
0
                GDALColorEntry sRGB;
645
0
                poCT->GetColorEntryAsRGB(iColor, &sRGB);
646
647
0
                panTRed[iColor] = GTiffDataset::ClampCTEntry(
648
0
                    iColor, 1, sRGB.c1, m_poGDS->m_nColorTableMultiplier);
649
0
                panTGreen[iColor] = GTiffDataset::ClampCTEntry(
650
0
                    iColor, 2, sRGB.c2, m_poGDS->m_nColorTableMultiplier);
651
0
                panTBlue[iColor] = GTiffDataset::ClampCTEntry(
652
0
                    iColor, 3, sRGB.c3, m_poGDS->m_nColorTableMultiplier);
653
0
            }
654
0
            else
655
0
            {
656
0
                panTRed[iColor] = 0;
657
0
                panTGreen[iColor] = 0;
658
0
                panTBlue[iColor] = 0;
659
0
            }
660
0
        }
661
662
0
        TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_PHOTOMETRIC,
663
0
                     PHOTOMETRIC_PALETTE);
664
0
        TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen,
665
0
                     panTBlue);
666
667
0
        CPLFree(panTRed);
668
0
        CPLFree(panTGreen);
669
0
        CPLFree(panTBlue);
670
671
        // libtiff 3.X needs setting this in all cases (creation or update)
672
        // whereas libtiff 4.X would just need it if there
673
        // was no color table before.
674
0
        m_poGDS->m_bNeedsRewrite = true;
675
0
    }
676
0
    else
677
0
    {
678
0
        eErr = GDALPamRasterBand::SetColorTable(poCT);
679
0
    }
680
681
0
    m_poGDS->m_poColorTable.reset(poCT->Clone());
682
0
    m_eBandInterp = GCI_PaletteIndex;
683
684
0
    return eErr;
685
0
}
686
687
/************************************************************************/
688
/*                           SetNoDataValue()                           */
689
/************************************************************************/
690
691
CPLErr GTiffRasterBand::SetNoDataValue(double dfNoData)
692
693
0
{
694
0
    const auto SetNoDataMembers = [this, dfNoData]()
695
0
    {
696
0
        m_bNoDataSet = true;
697
0
        m_dfNoDataValue = dfNoData;
698
699
0
        m_poGDS->m_bNoDataSet = true;
700
0
        m_poGDS->m_dfNoDataValue = dfNoData;
701
702
0
        if (eDataType == GDT_Int64 && GDALIsValueExactAs<int64_t>(dfNoData))
703
0
        {
704
0
            m_bNoDataSetAsInt64 = true;
705
0
            m_nNoDataValueInt64 = static_cast<int64_t>(dfNoData);
706
707
0
            m_poGDS->m_bNoDataSetAsInt64 = true;
708
0
            m_poGDS->m_nNoDataValueInt64 = static_cast<int64_t>(dfNoData);
709
0
        }
710
0
        else if (eDataType == GDT_UInt64 &&
711
0
                 GDALIsValueExactAs<uint64_t>(dfNoData))
712
0
        {
713
0
            m_bNoDataSetAsUInt64 = true;
714
0
            m_nNoDataValueUInt64 = static_cast<uint64_t>(dfNoData);
715
716
0
            m_poGDS->m_bNoDataSetAsUInt64 = true;
717
0
            m_poGDS->m_nNoDataValueUInt64 = static_cast<uint64_t>(dfNoData);
718
0
        }
719
0
    };
720
721
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
722
723
0
    if (m_poGDS->m_bNoDataSet &&
724
0
        (m_poGDS->m_dfNoDataValue == dfNoData ||
725
0
         (std::isnan(m_poGDS->m_dfNoDataValue) && std::isnan(dfNoData))))
726
0
    {
727
0
        ResetNoDataValues(false);
728
729
0
        SetNoDataMembers();
730
731
0
        return CE_None;
732
0
    }
733
734
0
    if (m_poGDS->nBands > 1 && m_poGDS->m_eProfile == GTiffProfile::GDALGEOTIFF)
735
0
    {
736
0
        int bOtherBandHasNoData = FALSE;
737
0
        const int nOtherBand = nBand > 1 ? 1 : 2;
738
0
        double dfOtherNoData = m_poGDS->GetRasterBand(nOtherBand)
739
0
                                   ->GetNoDataValue(&bOtherBandHasNoData);
740
0
        if (bOtherBandHasNoData && dfOtherNoData != dfNoData)
741
0
        {
742
0
            ReportError(
743
0
                CE_Warning, CPLE_AppDefined,
744
0
                "Setting nodata to %.17g on band %d, but band %d has nodata "
745
0
                "at %.17g. The TIFFTAG_GDAL_NODATA only support one value "
746
0
                "per dataset. This value of %.17g will be used for all bands "
747
0
                "on re-opening",
748
0
                dfNoData, nBand, nOtherBand, dfOtherNoData, dfNoData);
749
0
        }
750
0
    }
751
752
0
    if (m_poGDS->m_bStreamingOut && m_poGDS->m_bCrystalized)
753
0
    {
754
0
        ReportError(
755
0
            CE_Failure, CPLE_NotSupported,
756
0
            "Cannot modify nodata at that point in a streamed output file");
757
0
        return CE_Failure;
758
0
    }
759
760
0
    CPLErr eErr = CE_None;
761
0
    if (eAccess == GA_Update)
762
0
    {
763
0
        m_poGDS->m_bNoDataChanged = true;
764
0
        int bSuccess = FALSE;
765
0
        CPL_IGNORE_RET_VAL(GDALPamRasterBand::GetNoDataValue(&bSuccess));
766
0
        if (bSuccess)
767
0
        {
768
            // Cancel any existing nodata from PAM file.
769
0
            eErr = GDALPamRasterBand::DeleteNoDataValue();
770
0
        }
771
0
    }
772
0
    else
773
0
    {
774
0
        CPLDebug("GTIFF", "SetNoDataValue() goes to PAM instead of TIFF tags");
775
0
        eErr = GDALPamRasterBand::SetNoDataValue(dfNoData);
776
0
    }
777
778
0
    if (eErr == CE_None)
779
0
    {
780
0
        ResetNoDataValues(true);
781
782
0
        SetNoDataMembers();
783
0
    }
784
785
0
    return eErr;
786
0
}
787
788
/************************************************************************/
789
/*                       SetNoDataValueAsInt64()                        */
790
/************************************************************************/
791
792
CPLErr GTiffRasterBand::SetNoDataValueAsInt64(int64_t nNoData)
793
794
0
{
795
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
796
797
0
    if (m_poGDS->m_bNoDataSetAsInt64 && m_poGDS->m_nNoDataValueInt64 == nNoData)
798
0
    {
799
0
        ResetNoDataValues(false);
800
801
0
        m_bNoDataSetAsInt64 = true;
802
0
        m_nNoDataValueInt64 = nNoData;
803
804
0
        return CE_None;
805
0
    }
806
807
0
    if (m_poGDS->nBands > 1 && m_poGDS->m_eProfile == GTiffProfile::GDALGEOTIFF)
808
0
    {
809
0
        int bOtherBandHasNoData = FALSE;
810
0
        const int nOtherBand = nBand > 1 ? 1 : 2;
811
0
        const auto nOtherNoData =
812
0
            m_poGDS->GetRasterBand(nOtherBand)
813
0
                ->GetNoDataValueAsInt64(&bOtherBandHasNoData);
814
0
        if (bOtherBandHasNoData && nOtherNoData != nNoData)
815
0
        {
816
0
            ReportError(CE_Warning, CPLE_AppDefined,
817
0
                        "Setting nodata to " CPL_FRMT_GIB
818
0
                        " on band %d, but band %d has nodata "
819
0
                        "at " CPL_FRMT_GIB
820
0
                        ". The TIFFTAG_GDAL_NODATA only support one value "
821
0
                        "per dataset. This value of " CPL_FRMT_GIB
822
0
                        " will be used for all bands "
823
0
                        "on re-opening",
824
0
                        static_cast<GIntBig>(nNoData), nBand, nOtherBand,
825
0
                        static_cast<GIntBig>(nOtherNoData),
826
0
                        static_cast<GIntBig>(nNoData));
827
0
        }
828
0
    }
829
830
0
    if (m_poGDS->m_bStreamingOut && m_poGDS->m_bCrystalized)
831
0
    {
832
0
        ReportError(
833
0
            CE_Failure, CPLE_NotSupported,
834
0
            "Cannot modify nodata at that point in a streamed output file");
835
0
        return CE_Failure;
836
0
    }
837
838
0
    CPLErr eErr = CE_None;
839
0
    if (eAccess == GA_Update)
840
0
    {
841
0
        m_poGDS->m_bNoDataChanged = true;
842
0
        int bSuccess = FALSE;
843
0
        CPL_IGNORE_RET_VAL(GDALPamRasterBand::GetNoDataValueAsInt64(&bSuccess));
844
0
        if (bSuccess)
845
0
        {
846
            // Cancel any existing nodata from PAM file.
847
0
            eErr = GDALPamRasterBand::DeleteNoDataValue();
848
0
        }
849
0
    }
850
0
    else
851
0
    {
852
0
        CPLDebug("GTIFF", "SetNoDataValue() goes to PAM instead of TIFF tags");
853
0
        eErr = GDALPamRasterBand::SetNoDataValueAsInt64(nNoData);
854
0
    }
855
856
0
    if (eErr == CE_None)
857
0
    {
858
0
        ResetNoDataValues(true);
859
860
0
        m_poGDS->m_bNoDataSetAsInt64 = true;
861
0
        m_poGDS->m_nNoDataValueInt64 = nNoData;
862
0
    }
863
864
0
    return eErr;
865
0
}
866
867
/************************************************************************/
868
/*                      SetNoDataValueAsUInt64()                        */
869
/************************************************************************/
870
871
CPLErr GTiffRasterBand::SetNoDataValueAsUInt64(uint64_t nNoData)
872
873
0
{
874
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
875
876
0
    if (m_poGDS->m_bNoDataSetAsUInt64 &&
877
0
        m_poGDS->m_nNoDataValueUInt64 == nNoData)
878
0
    {
879
0
        ResetNoDataValues(false);
880
881
0
        m_bNoDataSetAsUInt64 = true;
882
0
        m_nNoDataValueUInt64 = nNoData;
883
884
0
        return CE_None;
885
0
    }
886
887
0
    if (m_poGDS->nBands > 1 && m_poGDS->m_eProfile == GTiffProfile::GDALGEOTIFF)
888
0
    {
889
0
        int bOtherBandHasNoData = FALSE;
890
0
        const int nOtherBand = nBand > 1 ? 1 : 2;
891
0
        const auto nOtherNoData =
892
0
            m_poGDS->GetRasterBand(nOtherBand)
893
0
                ->GetNoDataValueAsUInt64(&bOtherBandHasNoData);
894
0
        if (bOtherBandHasNoData && nOtherNoData != nNoData)
895
0
        {
896
0
            ReportError(CE_Warning, CPLE_AppDefined,
897
0
                        "Setting nodata to " CPL_FRMT_GUIB
898
0
                        " on band %d, but band %d has nodata "
899
0
                        "at " CPL_FRMT_GUIB
900
0
                        ". The TIFFTAG_GDAL_NODATA only support one value "
901
0
                        "per dataset. This value of " CPL_FRMT_GUIB
902
0
                        " will be used for all bands "
903
0
                        "on re-opening",
904
0
                        static_cast<GUIntBig>(nNoData), nBand, nOtherBand,
905
0
                        static_cast<GUIntBig>(nOtherNoData),
906
0
                        static_cast<GUIntBig>(nNoData));
907
0
        }
908
0
    }
909
910
0
    if (m_poGDS->m_bStreamingOut && m_poGDS->m_bCrystalized)
911
0
    {
912
0
        ReportError(
913
0
            CE_Failure, CPLE_NotSupported,
914
0
            "Cannot modify nodata at that point in a streamed output file");
915
0
        return CE_Failure;
916
0
    }
917
918
0
    CPLErr eErr = CE_None;
919
0
    if (eAccess == GA_Update)
920
0
    {
921
0
        m_poGDS->m_bNoDataChanged = true;
922
0
        int bSuccess = FALSE;
923
0
        CPL_IGNORE_RET_VAL(
924
0
            GDALPamRasterBand::GetNoDataValueAsUInt64(&bSuccess));
925
0
        if (bSuccess)
926
0
        {
927
            // Cancel any existing nodata from PAM file.
928
0
            eErr = GDALPamRasterBand::DeleteNoDataValue();
929
0
        }
930
0
    }
931
0
    else
932
0
    {
933
0
        CPLDebug("GTIFF", "SetNoDataValue() goes to PAM instead of TIFF tags");
934
0
        eErr = GDALPamRasterBand::SetNoDataValueAsUInt64(nNoData);
935
0
    }
936
937
0
    if (eErr == CE_None)
938
0
    {
939
0
        ResetNoDataValues(true);
940
941
0
        m_poGDS->m_bNoDataSetAsUInt64 = true;
942
0
        m_poGDS->m_nNoDataValueUInt64 = nNoData;
943
944
0
        m_bNoDataSetAsUInt64 = true;
945
0
        m_nNoDataValueUInt64 = nNoData;
946
0
    }
947
948
0
    return eErr;
949
0
}
950
951
/************************************************************************/
952
/*                        ResetNoDataValues()                           */
953
/************************************************************************/
954
955
void GTiffRasterBand::ResetNoDataValues(bool bResetDatasetToo)
956
0
{
957
0
    if (bResetDatasetToo)
958
0
    {
959
0
        m_poGDS->m_bNoDataSet = false;
960
0
        m_poGDS->m_dfNoDataValue = DEFAULT_NODATA_VALUE;
961
0
    }
962
963
0
    m_bNoDataSet = false;
964
0
    m_dfNoDataValue = DEFAULT_NODATA_VALUE;
965
966
0
    if (bResetDatasetToo)
967
0
    {
968
0
        m_poGDS->m_bNoDataSetAsInt64 = false;
969
0
        m_poGDS->m_nNoDataValueInt64 = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
970
0
    }
971
972
0
    m_bNoDataSetAsInt64 = false;
973
0
    m_nNoDataValueInt64 = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
974
975
0
    if (bResetDatasetToo)
976
0
    {
977
0
        m_poGDS->m_bNoDataSetAsUInt64 = false;
978
0
        m_poGDS->m_nNoDataValueUInt64 = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
979
0
    }
980
981
0
    m_bNoDataSetAsUInt64 = false;
982
0
    m_nNoDataValueUInt64 = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
983
0
}
984
985
/************************************************************************/
986
/*                        DeleteNoDataValue()                           */
987
/************************************************************************/
988
989
CPLErr GTiffRasterBand::DeleteNoDataValue()
990
991
0
{
992
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
993
994
0
    if (m_poGDS->m_bStreamingOut && m_poGDS->m_bCrystalized)
995
0
    {
996
0
        ReportError(
997
0
            CE_Failure, CPLE_NotSupported,
998
0
            "Cannot modify nodata at that point in a streamed output file");
999
0
        return CE_Failure;
1000
0
    }
1001
1002
0
    if (eAccess == GA_Update)
1003
0
    {
1004
0
        if (m_poGDS->m_bNoDataSet)
1005
0
            m_poGDS->m_bNoDataChanged = true;
1006
0
    }
1007
0
    else
1008
0
    {
1009
0
        CPLDebug("GTIFF",
1010
0
                 "DeleteNoDataValue() goes to PAM instead of TIFF tags");
1011
0
    }
1012
1013
0
    CPLErr eErr = GDALPamRasterBand::DeleteNoDataValue();
1014
0
    if (eErr == CE_None)
1015
0
    {
1016
0
        ResetNoDataValues(true);
1017
0
    }
1018
1019
0
    return eErr;
1020
0
}
1021
1022
/************************************************************************/
1023
/*                             NullBlock()                              */
1024
/*                                                                      */
1025
/*      Set the block data to the null value if it is set, or zero      */
1026
/*      if there is no null data value.                                 */
1027
/************************************************************************/
1028
1029
void GTiffRasterBand::NullBlock(void *pData)
1030
1031
0
{
1032
0
    const GPtrDiff_t nWords =
1033
0
        static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
1034
0
    const int nChunkSize = std::max(1, GDALGetDataTypeSizeBytes(eDataType));
1035
1036
0
    int l_bNoDataSet = FALSE;
1037
0
    if (eDataType == GDT_Int64)
1038
0
    {
1039
0
        const auto nVal = GetNoDataValueAsInt64(&l_bNoDataSet);
1040
0
        if (!l_bNoDataSet)
1041
0
        {
1042
0
            memset(pData, 0, nWords * nChunkSize);
1043
0
        }
1044
0
        else
1045
0
        {
1046
0
            GDALCopyWords64(&nVal, GDT_Int64, 0, pData, eDataType, nChunkSize,
1047
0
                            nWords);
1048
0
        }
1049
0
    }
1050
0
    else if (eDataType == GDT_UInt64)
1051
0
    {
1052
0
        const auto nVal = GetNoDataValueAsUInt64(&l_bNoDataSet);
1053
0
        if (!l_bNoDataSet)
1054
0
        {
1055
0
            memset(pData, 0, nWords * nChunkSize);
1056
0
        }
1057
0
        else
1058
0
        {
1059
0
            GDALCopyWords64(&nVal, GDT_UInt64, 0, pData, eDataType, nChunkSize,
1060
0
                            nWords);
1061
0
        }
1062
0
    }
1063
0
    else
1064
0
    {
1065
0
        double dfNoData = GetNoDataValue(&l_bNoDataSet);
1066
0
        if (!l_bNoDataSet)
1067
0
        {
1068
#ifdef ESRI_BUILD
1069
            if (m_poGDS->m_nBitsPerSample >= 2)
1070
                memset(pData, 0, nWords * nChunkSize);
1071
            else
1072
                memset(pData, 1, nWords * nChunkSize);
1073
#else
1074
0
            memset(pData, 0, nWords * nChunkSize);
1075
0
#endif
1076
0
        }
1077
0
        else
1078
0
        {
1079
            // Will convert nodata value to the right type and copy efficiently.
1080
0
            GDALCopyWords64(&dfNoData, GDT_Float64, 0, pData, eDataType,
1081
0
                            nChunkSize, nWords);
1082
0
        }
1083
0
    }
1084
0
}