Coverage Report

Created: 2025-08-28 06:57

/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 non-RGB in extrasamples.
469
0
    if (eInterp != GCI_RedBand && eInterp != GCI_GreenBand &&
470
0
        eInterp != GCI_BlueBand)
471
0
    {
472
0
        uint16_t *v = nullptr;
473
0
        uint16_t count = 0;
474
0
        if (TIFFGetField(m_poGDS->m_hTIFF, TIFFTAG_EXTRASAMPLES, &count, &v) &&
475
0
            count > 0)
476
0
        {
477
0
            const int nBaseSamples = m_poGDS->m_nSamplesPerPixel - count;
478
479
0
            if (eInterp == GCI_AlphaBand)
480
0
            {
481
0
                for (int i = 1; i <= m_poGDS->nBands; ++i)
482
0
                {
483
0
                    if (i != nBand &&
484
0
                        m_poGDS->GetRasterBand(i)->GetColorInterpretation() ==
485
0
                            GCI_AlphaBand)
486
0
                    {
487
0
                        if (i == nBaseSamples + 1 &&
488
0
                            CSLFetchNameValue(m_poGDS->m_papszCreationOptions,
489
0
                                              "ALPHA") != nullptr)
490
0
                        {
491
0
                            ReportError(
492
0
                                CE_Warning, CPLE_AppDefined,
493
0
                                "Band %d was already identified as alpha band, "
494
0
                                "and band %d is now marked as alpha too. "
495
0
                                "Presumably ALPHA creation option is not "
496
0
                                "needed",
497
0
                                i, nBand);
498
0
                        }
499
0
                        else
500
0
                        {
501
0
                            ReportError(
502
0
                                CE_Warning, CPLE_AppDefined,
503
0
                                "Band %d was already identified as alpha band, "
504
0
                                "and band %d is now marked as alpha too",
505
0
                                i, nBand);
506
0
                        }
507
0
                    }
508
0
                }
509
0
            }
510
511
0
            if (nBand > nBaseSamples && nBand - nBaseSamples - 1 < count)
512
0
            {
513
                // We need to allocate a new array as (current) libtiff
514
                // versions will not like that we reuse the array we got from
515
                // TIFFGetField().
516
517
0
                uint16_t *pasNewExtraSamples = static_cast<uint16_t *>(
518
0
                    CPLMalloc(count * sizeof(uint16_t)));
519
0
                memcpy(pasNewExtraSamples, v, count * sizeof(uint16_t));
520
0
                if (eInterp == GCI_AlphaBand)
521
0
                {
522
0
                    pasNewExtraSamples[nBand - nBaseSamples - 1] =
523
0
                        GTiffGetAlphaValue(
524
0
                            CPLGetConfigOption("GTIFF_ALPHA", nullptr),
525
0
                            DEFAULT_ALPHA_TYPE);
526
0
                }
527
0
                else
528
0
                {
529
0
                    pasNewExtraSamples[nBand - nBaseSamples - 1] =
530
0
                        EXTRASAMPLE_UNSPECIFIED;
531
0
                }
532
533
0
                TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_EXTRASAMPLES, count,
534
0
                             pasNewExtraSamples);
535
536
0
                CPLFree(pasNewExtraSamples);
537
538
0
                return CE_None;
539
0
            }
540
0
        }
541
0
    }
542
543
0
    if (m_poGDS->m_nPhotometric != PHOTOMETRIC_MINISBLACK &&
544
0
        CSLFetchNameValue(m_poGDS->m_papszCreationOptions, "PHOTOMETRIC") ==
545
0
            nullptr)
546
0
    {
547
0
        m_poGDS->m_nPhotometric = PHOTOMETRIC_MINISBLACK;
548
0
        TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_PHOTOMETRIC,
549
0
                     m_poGDS->m_nPhotometric);
550
0
    }
551
552
0
    return CE_None;
553
0
}
554
555
/************************************************************************/
556
/*                           SetColorTable()                            */
557
/************************************************************************/
558
559
CPLErr GTiffRasterBand::SetColorTable(GDALColorTable *poCT)
560
561
0
{
562
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
563
564
    /* -------------------------------------------------------------------- */
565
    /*      Check if this is even a candidate for applying a PCT.           */
566
    /* -------------------------------------------------------------------- */
567
0
    if (eAccess == GA_Update)
568
0
    {
569
0
        if (nBand != 1)
570
0
        {
571
0
            ReportError(CE_Failure, CPLE_NotSupported,
572
0
                        "SetColorTable() can only be called on band 1.");
573
0
            return CE_Failure;
574
0
        }
575
576
0
        if (m_poGDS->m_nSamplesPerPixel != 1 &&
577
0
            m_poGDS->m_nSamplesPerPixel != 2)
578
0
        {
579
0
            ReportError(CE_Failure, CPLE_NotSupported,
580
0
                        "SetColorTable() not supported for multi-sample TIFF "
581
0
                        "files.");
582
0
            return CE_Failure;
583
0
        }
584
585
0
        if (eDataType != GDT_Byte && eDataType != GDT_UInt16)
586
0
        {
587
0
            ReportError(
588
0
                CE_Failure, CPLE_NotSupported,
589
0
                "SetColorTable() only supported for Byte or UInt16 bands "
590
0
                "in TIFF format.");
591
0
            return CE_Failure;
592
0
        }
593
594
        // Clear any existing PAM color table
595
0
        if (GDALPamRasterBand::GetColorTable() != nullptr)
596
0
        {
597
0
            GDALPamRasterBand::SetColorTable(nullptr);
598
0
            GDALPamRasterBand::SetColorInterpretation(GCI_Undefined);
599
0
        }
600
0
    }
601
602
    /* -------------------------------------------------------------------- */
603
    /*      Is this really a request to clear the color table?              */
604
    /* -------------------------------------------------------------------- */
605
0
    if (poCT == nullptr || poCT->GetColorEntryCount() == 0)
606
0
    {
607
0
        if (eAccess == GA_Update)
608
0
        {
609
0
            TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_PHOTOMETRIC,
610
0
                         PHOTOMETRIC_MINISBLACK);
611
612
0
            TIFFUnsetField(m_poGDS->m_hTIFF, TIFFTAG_COLORMAP);
613
0
        }
614
615
0
        m_poGDS->m_poColorTable.reset();
616
617
0
        return CE_None;
618
0
    }
619
620
    /* -------------------------------------------------------------------- */
621
    /*      Write out the colortable, and update the configuration.         */
622
    /* -------------------------------------------------------------------- */
623
0
    CPLErr eErr = CE_None;
624
0
    if (eAccess == GA_Update)
625
0
    {
626
0
        int nColors = 65536;
627
628
0
        if (eDataType == GDT_Byte)
629
0
            nColors = 256;
630
631
0
        unsigned short *panTRed = static_cast<unsigned short *>(
632
0
            CPLMalloc(sizeof(unsigned short) * nColors));
633
0
        unsigned short *panTGreen = static_cast<unsigned short *>(
634
0
            CPLMalloc(sizeof(unsigned short) * nColors));
635
0
        unsigned short *panTBlue = static_cast<unsigned short *>(
636
0
            CPLMalloc(sizeof(unsigned short) * nColors));
637
638
0
        if (m_poGDS->m_nColorTableMultiplier == 0)
639
0
            m_poGDS->m_nColorTableMultiplier =
640
0
                GTiffDataset::DEFAULT_COLOR_TABLE_MULTIPLIER_257;
641
642
0
        for (int iColor = 0; iColor < nColors; ++iColor)
643
0
        {
644
0
            if (iColor < poCT->GetColorEntryCount())
645
0
            {
646
0
                GDALColorEntry sRGB;
647
0
                poCT->GetColorEntryAsRGB(iColor, &sRGB);
648
649
0
                panTRed[iColor] = GTiffDataset::ClampCTEntry(
650
0
                    iColor, 1, sRGB.c1, m_poGDS->m_nColorTableMultiplier);
651
0
                panTGreen[iColor] = GTiffDataset::ClampCTEntry(
652
0
                    iColor, 2, sRGB.c2, m_poGDS->m_nColorTableMultiplier);
653
0
                panTBlue[iColor] = GTiffDataset::ClampCTEntry(
654
0
                    iColor, 3, sRGB.c3, m_poGDS->m_nColorTableMultiplier);
655
0
            }
656
0
            else
657
0
            {
658
0
                panTRed[iColor] = 0;
659
0
                panTGreen[iColor] = 0;
660
0
                panTBlue[iColor] = 0;
661
0
            }
662
0
        }
663
664
0
        TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_PHOTOMETRIC,
665
0
                     PHOTOMETRIC_PALETTE);
666
0
        TIFFSetField(m_poGDS->m_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen,
667
0
                     panTBlue);
668
669
0
        CPLFree(panTRed);
670
0
        CPLFree(panTGreen);
671
0
        CPLFree(panTBlue);
672
673
        // libtiff 3.X needs setting this in all cases (creation or update)
674
        // whereas libtiff 4.X would just need it if there
675
        // was no color table before.
676
0
        m_poGDS->m_bNeedsRewrite = true;
677
0
    }
678
0
    else
679
0
    {
680
0
        eErr = GDALPamRasterBand::SetColorTable(poCT);
681
0
    }
682
683
0
    m_poGDS->m_poColorTable.reset(poCT->Clone());
684
0
    m_eBandInterp = GCI_PaletteIndex;
685
686
0
    return eErr;
687
0
}
688
689
/************************************************************************/
690
/*                           SetNoDataValue()                           */
691
/************************************************************************/
692
693
CPLErr GTiffRasterBand::SetNoDataValue(double dfNoData)
694
695
0
{
696
0
    const auto SetNoDataMembers = [this, dfNoData]()
697
0
    {
698
0
        m_bNoDataSet = true;
699
0
        m_dfNoDataValue = dfNoData;
700
701
0
        m_poGDS->m_bNoDataSet = true;
702
0
        m_poGDS->m_dfNoDataValue = dfNoData;
703
704
0
        if (eDataType == GDT_Int64 && GDALIsValueExactAs<int64_t>(dfNoData))
705
0
        {
706
0
            m_bNoDataSetAsInt64 = true;
707
0
            m_nNoDataValueInt64 = static_cast<int64_t>(dfNoData);
708
709
0
            m_poGDS->m_bNoDataSetAsInt64 = true;
710
0
            m_poGDS->m_nNoDataValueInt64 = static_cast<int64_t>(dfNoData);
711
0
        }
712
0
        else if (eDataType == GDT_UInt64 &&
713
0
                 GDALIsValueExactAs<uint64_t>(dfNoData))
714
0
        {
715
0
            m_bNoDataSetAsUInt64 = true;
716
0
            m_nNoDataValueUInt64 = static_cast<uint64_t>(dfNoData);
717
718
0
            m_poGDS->m_bNoDataSetAsUInt64 = true;
719
0
            m_poGDS->m_nNoDataValueUInt64 = static_cast<uint64_t>(dfNoData);
720
0
        }
721
0
    };
722
723
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
724
725
0
    if (m_poGDS->m_bNoDataSet &&
726
0
        (m_poGDS->m_dfNoDataValue == dfNoData ||
727
0
         (std::isnan(m_poGDS->m_dfNoDataValue) && std::isnan(dfNoData))))
728
0
    {
729
0
        ResetNoDataValues(false);
730
731
0
        SetNoDataMembers();
732
733
0
        return CE_None;
734
0
    }
735
736
0
    if (m_poGDS->nBands > 1 && m_poGDS->m_eProfile == GTiffProfile::GDALGEOTIFF)
737
0
    {
738
0
        int bOtherBandHasNoData = FALSE;
739
0
        const int nOtherBand = nBand > 1 ? 1 : 2;
740
0
        double dfOtherNoData = m_poGDS->GetRasterBand(nOtherBand)
741
0
                                   ->GetNoDataValue(&bOtherBandHasNoData);
742
0
        if (bOtherBandHasNoData && dfOtherNoData != dfNoData)
743
0
        {
744
0
            ReportError(
745
0
                CE_Warning, CPLE_AppDefined,
746
0
                "Setting nodata to %.17g on band %d, but band %d has nodata "
747
0
                "at %.17g. The TIFFTAG_GDAL_NODATA only support one value "
748
0
                "per dataset. This value of %.17g will be used for all bands "
749
0
                "on re-opening",
750
0
                dfNoData, nBand, nOtherBand, dfOtherNoData, dfNoData);
751
0
        }
752
0
    }
753
754
0
    if (m_poGDS->m_bStreamingOut && m_poGDS->m_bCrystalized)
755
0
    {
756
0
        ReportError(
757
0
            CE_Failure, CPLE_NotSupported,
758
0
            "Cannot modify nodata at that point in a streamed output file");
759
0
        return CE_Failure;
760
0
    }
761
762
0
    CPLErr eErr = CE_None;
763
0
    if (eAccess == GA_Update)
764
0
    {
765
0
        m_poGDS->m_bNoDataChanged = true;
766
0
        int bSuccess = FALSE;
767
0
        CPL_IGNORE_RET_VAL(GDALPamRasterBand::GetNoDataValue(&bSuccess));
768
0
        if (bSuccess)
769
0
        {
770
            // Cancel any existing nodata from PAM file.
771
0
            eErr = GDALPamRasterBand::DeleteNoDataValue();
772
0
        }
773
0
    }
774
0
    else
775
0
    {
776
0
        CPLDebug("GTIFF", "SetNoDataValue() goes to PAM instead of TIFF tags");
777
0
        eErr = GDALPamRasterBand::SetNoDataValue(dfNoData);
778
0
    }
779
780
0
    if (eErr == CE_None)
781
0
    {
782
0
        ResetNoDataValues(true);
783
784
0
        SetNoDataMembers();
785
0
    }
786
787
0
    return eErr;
788
0
}
789
790
/************************************************************************/
791
/*                       SetNoDataValueAsInt64()                        */
792
/************************************************************************/
793
794
CPLErr GTiffRasterBand::SetNoDataValueAsInt64(int64_t nNoData)
795
796
0
{
797
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
798
799
0
    if (m_poGDS->m_bNoDataSetAsInt64 && m_poGDS->m_nNoDataValueInt64 == nNoData)
800
0
    {
801
0
        ResetNoDataValues(false);
802
803
0
        m_bNoDataSetAsInt64 = true;
804
0
        m_nNoDataValueInt64 = nNoData;
805
806
0
        return CE_None;
807
0
    }
808
809
0
    if (m_poGDS->nBands > 1 && m_poGDS->m_eProfile == GTiffProfile::GDALGEOTIFF)
810
0
    {
811
0
        int bOtherBandHasNoData = FALSE;
812
0
        const int nOtherBand = nBand > 1 ? 1 : 2;
813
0
        const auto nOtherNoData =
814
0
            m_poGDS->GetRasterBand(nOtherBand)
815
0
                ->GetNoDataValueAsInt64(&bOtherBandHasNoData);
816
0
        if (bOtherBandHasNoData && nOtherNoData != nNoData)
817
0
        {
818
0
            ReportError(CE_Warning, CPLE_AppDefined,
819
0
                        "Setting nodata to " CPL_FRMT_GIB
820
0
                        " on band %d, but band %d has nodata "
821
0
                        "at " CPL_FRMT_GIB
822
0
                        ". The TIFFTAG_GDAL_NODATA only support one value "
823
0
                        "per dataset. This value of " CPL_FRMT_GIB
824
0
                        " will be used for all bands "
825
0
                        "on re-opening",
826
0
                        static_cast<GIntBig>(nNoData), nBand, nOtherBand,
827
0
                        static_cast<GIntBig>(nOtherNoData),
828
0
                        static_cast<GIntBig>(nNoData));
829
0
        }
830
0
    }
831
832
0
    if (m_poGDS->m_bStreamingOut && m_poGDS->m_bCrystalized)
833
0
    {
834
0
        ReportError(
835
0
            CE_Failure, CPLE_NotSupported,
836
0
            "Cannot modify nodata at that point in a streamed output file");
837
0
        return CE_Failure;
838
0
    }
839
840
0
    CPLErr eErr = CE_None;
841
0
    if (eAccess == GA_Update)
842
0
    {
843
0
        m_poGDS->m_bNoDataChanged = true;
844
0
        int bSuccess = FALSE;
845
0
        CPL_IGNORE_RET_VAL(GDALPamRasterBand::GetNoDataValueAsInt64(&bSuccess));
846
0
        if (bSuccess)
847
0
        {
848
            // Cancel any existing nodata from PAM file.
849
0
            eErr = GDALPamRasterBand::DeleteNoDataValue();
850
0
        }
851
0
    }
852
0
    else
853
0
    {
854
0
        CPLDebug("GTIFF", "SetNoDataValue() goes to PAM instead of TIFF tags");
855
0
        eErr = GDALPamRasterBand::SetNoDataValueAsInt64(nNoData);
856
0
    }
857
858
0
    if (eErr == CE_None)
859
0
    {
860
0
        ResetNoDataValues(true);
861
862
0
        m_poGDS->m_bNoDataSetAsInt64 = true;
863
0
        m_poGDS->m_nNoDataValueInt64 = nNoData;
864
0
    }
865
866
0
    return eErr;
867
0
}
868
869
/************************************************************************/
870
/*                      SetNoDataValueAsUInt64()                        */
871
/************************************************************************/
872
873
CPLErr GTiffRasterBand::SetNoDataValueAsUInt64(uint64_t nNoData)
874
875
0
{
876
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
877
878
0
    if (m_poGDS->m_bNoDataSetAsUInt64 &&
879
0
        m_poGDS->m_nNoDataValueUInt64 == nNoData)
880
0
    {
881
0
        ResetNoDataValues(false);
882
883
0
        m_bNoDataSetAsUInt64 = true;
884
0
        m_nNoDataValueUInt64 = nNoData;
885
886
0
        return CE_None;
887
0
    }
888
889
0
    if (m_poGDS->nBands > 1 && m_poGDS->m_eProfile == GTiffProfile::GDALGEOTIFF)
890
0
    {
891
0
        int bOtherBandHasNoData = FALSE;
892
0
        const int nOtherBand = nBand > 1 ? 1 : 2;
893
0
        const auto nOtherNoData =
894
0
            m_poGDS->GetRasterBand(nOtherBand)
895
0
                ->GetNoDataValueAsUInt64(&bOtherBandHasNoData);
896
0
        if (bOtherBandHasNoData && nOtherNoData != nNoData)
897
0
        {
898
0
            ReportError(CE_Warning, CPLE_AppDefined,
899
0
                        "Setting nodata to " CPL_FRMT_GUIB
900
0
                        " on band %d, but band %d has nodata "
901
0
                        "at " CPL_FRMT_GUIB
902
0
                        ". The TIFFTAG_GDAL_NODATA only support one value "
903
0
                        "per dataset. This value of " CPL_FRMT_GUIB
904
0
                        " will be used for all bands "
905
0
                        "on re-opening",
906
0
                        static_cast<GUIntBig>(nNoData), nBand, nOtherBand,
907
0
                        static_cast<GUIntBig>(nOtherNoData),
908
0
                        static_cast<GUIntBig>(nNoData));
909
0
        }
910
0
    }
911
912
0
    if (m_poGDS->m_bStreamingOut && m_poGDS->m_bCrystalized)
913
0
    {
914
0
        ReportError(
915
0
            CE_Failure, CPLE_NotSupported,
916
0
            "Cannot modify nodata at that point in a streamed output file");
917
0
        return CE_Failure;
918
0
    }
919
920
0
    CPLErr eErr = CE_None;
921
0
    if (eAccess == GA_Update)
922
0
    {
923
0
        m_poGDS->m_bNoDataChanged = true;
924
0
        int bSuccess = FALSE;
925
0
        CPL_IGNORE_RET_VAL(
926
0
            GDALPamRasterBand::GetNoDataValueAsUInt64(&bSuccess));
927
0
        if (bSuccess)
928
0
        {
929
            // Cancel any existing nodata from PAM file.
930
0
            eErr = GDALPamRasterBand::DeleteNoDataValue();
931
0
        }
932
0
    }
933
0
    else
934
0
    {
935
0
        CPLDebug("GTIFF", "SetNoDataValue() goes to PAM instead of TIFF tags");
936
0
        eErr = GDALPamRasterBand::SetNoDataValueAsUInt64(nNoData);
937
0
    }
938
939
0
    if (eErr == CE_None)
940
0
    {
941
0
        ResetNoDataValues(true);
942
943
0
        m_poGDS->m_bNoDataSetAsUInt64 = true;
944
0
        m_poGDS->m_nNoDataValueUInt64 = nNoData;
945
946
0
        m_bNoDataSetAsUInt64 = true;
947
0
        m_nNoDataValueUInt64 = nNoData;
948
0
    }
949
950
0
    return eErr;
951
0
}
952
953
/************************************************************************/
954
/*                        ResetNoDataValues()                           */
955
/************************************************************************/
956
957
void GTiffRasterBand::ResetNoDataValues(bool bResetDatasetToo)
958
0
{
959
0
    if (bResetDatasetToo)
960
0
    {
961
0
        m_poGDS->m_bNoDataSet = false;
962
0
        m_poGDS->m_dfNoDataValue = DEFAULT_NODATA_VALUE;
963
0
    }
964
965
0
    m_bNoDataSet = false;
966
0
    m_dfNoDataValue = DEFAULT_NODATA_VALUE;
967
968
0
    if (bResetDatasetToo)
969
0
    {
970
0
        m_poGDS->m_bNoDataSetAsInt64 = false;
971
0
        m_poGDS->m_nNoDataValueInt64 = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
972
0
    }
973
974
0
    m_bNoDataSetAsInt64 = false;
975
0
    m_nNoDataValueInt64 = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
976
977
0
    if (bResetDatasetToo)
978
0
    {
979
0
        m_poGDS->m_bNoDataSetAsUInt64 = false;
980
0
        m_poGDS->m_nNoDataValueUInt64 = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
981
0
    }
982
983
0
    m_bNoDataSetAsUInt64 = false;
984
0
    m_nNoDataValueUInt64 = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
985
0
}
986
987
/************************************************************************/
988
/*                        DeleteNoDataValue()                           */
989
/************************************************************************/
990
991
CPLErr GTiffRasterBand::DeleteNoDataValue()
992
993
0
{
994
0
    m_poGDS->LoadGeoreferencingAndPamIfNeeded();
995
996
0
    if (m_poGDS->m_bStreamingOut && m_poGDS->m_bCrystalized)
997
0
    {
998
0
        ReportError(
999
0
            CE_Failure, CPLE_NotSupported,
1000
0
            "Cannot modify nodata at that point in a streamed output file");
1001
0
        return CE_Failure;
1002
0
    }
1003
1004
0
    if (eAccess == GA_Update)
1005
0
    {
1006
0
        if (m_poGDS->m_bNoDataSet)
1007
0
            m_poGDS->m_bNoDataChanged = true;
1008
0
    }
1009
0
    else
1010
0
    {
1011
0
        CPLDebug("GTIFF",
1012
0
                 "DeleteNoDataValue() goes to PAM instead of TIFF tags");
1013
0
    }
1014
1015
0
    CPLErr eErr = GDALPamRasterBand::DeleteNoDataValue();
1016
0
    if (eErr == CE_None)
1017
0
    {
1018
0
        ResetNoDataValues(true);
1019
0
    }
1020
1021
0
    return eErr;
1022
0
}
1023
1024
/************************************************************************/
1025
/*                             NullBlock()                              */
1026
/*                                                                      */
1027
/*      Set the block data to the null value if it is set, or zero      */
1028
/*      if there is no null data value.                                 */
1029
/************************************************************************/
1030
1031
void GTiffRasterBand::NullBlock(void *pData)
1032
1033
0
{
1034
0
    const GPtrDiff_t nWords =
1035
0
        static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
1036
0
    const int nChunkSize = std::max(1, GDALGetDataTypeSizeBytes(eDataType));
1037
1038
0
    int l_bNoDataSet = FALSE;
1039
0
    if (eDataType == GDT_Int64)
1040
0
    {
1041
0
        const auto nVal = GetNoDataValueAsInt64(&l_bNoDataSet);
1042
0
        if (!l_bNoDataSet)
1043
0
        {
1044
0
            memset(pData, 0, nWords * nChunkSize);
1045
0
        }
1046
0
        else
1047
0
        {
1048
0
            GDALCopyWords64(&nVal, GDT_Int64, 0, pData, eDataType, nChunkSize,
1049
0
                            nWords);
1050
0
        }
1051
0
    }
1052
0
    else if (eDataType == GDT_UInt64)
1053
0
    {
1054
0
        const auto nVal = GetNoDataValueAsUInt64(&l_bNoDataSet);
1055
0
        if (!l_bNoDataSet)
1056
0
        {
1057
0
            memset(pData, 0, nWords * nChunkSize);
1058
0
        }
1059
0
        else
1060
0
        {
1061
0
            GDALCopyWords64(&nVal, GDT_UInt64, 0, pData, eDataType, nChunkSize,
1062
0
                            nWords);
1063
0
        }
1064
0
    }
1065
0
    else
1066
0
    {
1067
0
        double dfNoData = GetNoDataValue(&l_bNoDataSet);
1068
0
        if (!l_bNoDataSet)
1069
0
        {
1070
#ifdef ESRI_BUILD
1071
            if (m_poGDS->m_nBitsPerSample >= 2)
1072
                memset(pData, 0, nWords * nChunkSize);
1073
            else
1074
                memset(pData, 1, nWords * nChunkSize);
1075
#else
1076
0
            memset(pData, 0, nWords * nChunkSize);
1077
0
#endif
1078
0
        }
1079
0
        else
1080
0
        {
1081
            // Will convert nodata value to the right type and copy efficiently.
1082
0
            GDALCopyWords64(&dfNoData, GDT_Float64, 0, pData, eDataType,
1083
0
                            nChunkSize, nWords);
1084
0
        }
1085
0
    }
1086
0
}