Coverage Report

Created: 2025-11-16 06:25

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