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