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