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