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