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