Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GeoTIFF Driver
4 : * Purpose: Write/set operations on GTiffDataset
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 "gtiffdataset.h"
31 : #include "gtiffrasterband.h"
32 : #include "gtiffoddbitsband.h"
33 :
34 : #include <cassert>
35 : #include <cerrno>
36 :
37 : #include <algorithm>
38 : #include <limits>
39 : #include <memory>
40 : #include <mutex>
41 : #include <set>
42 : #include <string>
43 : #include <tuple>
44 : #include <utility>
45 :
46 : #include "cpl_error.h"
47 : #include "cpl_error_internal.h" // CPLErrorHandlerAccumulatorStruct
48 : #include "cpl_md5.h"
49 : #include "cpl_vsi.h"
50 : #include "cpl_vsi_virtual.h"
51 : #include "cpl_worker_thread_pool.h"
52 : #include "fetchbufferdirectio.h"
53 : #include "gdal_mdreader.h" // GDALWriteRPCTXTFile()
54 : #include "gdal_priv_templates.hpp" // GDALIsValueInRange<>
55 : #include "gdal_thread_pool.h" // GDALGetGlobalThreadPool()
56 : #include "geovalues.h" // RasterPixelIsPoint
57 : #include "gt_jpeg_copy.h"
58 : #include "gt_overview.h" // GTIFFBuildOverviewMetadata()
59 : #include "quant_table_md5sum.h"
60 : #include "quant_table_md5sum_jpeg9e.h"
61 : #include "tif_jxl.h"
62 : #include "tifvsi.h"
63 : #include "xtiffio.h"
64 :
65 : #if LIFFLIB_VERSION > 20230908 || defined(INTERNAL_LIBTIFF)
66 : /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
67 : #define LIBTIFF_MULTIBAND_LERC_NAN_OK
68 : #endif
69 :
70 : static const int knGTIFFJpegTablesModeDefault = JPEGTABLESMODE_QUANT;
71 :
72 : static constexpr const char szPROFILE_BASELINE[] = "BASELINE";
73 : static constexpr const char szPROFILE_GeoTIFF[] = "GeoTIFF";
74 : static constexpr const char szPROFILE_GDALGeoTIFF[] = "GDALGeoTIFF";
75 :
76 : enum
77 : {
78 : ENDIANNESS_NATIVE,
79 : ENDIANNESS_LITTLE,
80 : ENDIANNESS_BIG
81 : };
82 :
83 10162 : static signed char GTiffGetWebPLevel(CSLConstList papszOptions)
84 : {
85 10162 : int nWebPLevel = DEFAULT_WEBP_LEVEL;
86 10162 : const char *pszValue = CSLFetchNameValue(papszOptions, "WEBP_LEVEL");
87 10162 : if (pszValue != nullptr)
88 : {
89 51 : nWebPLevel = atoi(pszValue);
90 51 : if (!(nWebPLevel >= 1 && nWebPLevel <= 100))
91 : {
92 0 : CPLError(CE_Warning, CPLE_IllegalArg,
93 : "WEBP_LEVEL=%s value not recognised, ignoring.", pszValue);
94 0 : nWebPLevel = DEFAULT_WEBP_LEVEL;
95 : }
96 : }
97 10162 : return static_cast<signed char>(nWebPLevel);
98 : }
99 :
100 10168 : static bool GTiffGetWebPLossless(CSLConstList papszOptions)
101 : {
102 10168 : return CPLFetchBool(papszOptions, "WEBP_LOSSLESS", false);
103 : }
104 :
105 10234 : static double GTiffGetLERCMaxZError(CSLConstList papszOptions)
106 : {
107 10234 : return CPLAtof(CSLFetchNameValueDef(papszOptions, "MAX_Z_ERROR", "0.0"));
108 : }
109 :
110 4158 : static double GTiffGetLERCMaxZErrorOverview(CSLConstList papszOptions)
111 : {
112 4158 : return CPLAtof(CSLFetchNameValueDef(
113 : papszOptions, "MAX_Z_ERROR_OVERVIEW",
114 4158 : CSLFetchNameValueDef(papszOptions, "MAX_Z_ERROR", "0.0")));
115 : }
116 :
117 : #if HAVE_JXL
118 10232 : static bool GTiffGetJXLLossless(CSLConstList papszOptions)
119 : {
120 10232 : return CPLTestBool(
121 10232 : CSLFetchNameValueDef(papszOptions, "JXL_LOSSLESS", "TRUE"));
122 : }
123 :
124 10232 : static uint32_t GTiffGetJXLEffort(CSLConstList papszOptions)
125 : {
126 10232 : return atoi(CSLFetchNameValueDef(papszOptions, "JXL_EFFORT", "5"));
127 : }
128 :
129 10154 : static float GTiffGetJXLDistance(CSLConstList papszOptions)
130 : {
131 : return static_cast<float>(
132 10154 : CPLAtof(CSLFetchNameValueDef(papszOptions, "JXL_DISTANCE", "1.0")));
133 : }
134 :
135 10232 : static float GTiffGetJXLAlphaDistance(CSLConstList papszOptions)
136 : {
137 10232 : return static_cast<float>(CPLAtof(
138 10232 : CSLFetchNameValueDef(papszOptions, "JXL_ALPHA_DISTANCE", "-1.0")));
139 : }
140 :
141 : #endif
142 :
143 : /************************************************************************/
144 : /* FillEmptyTiles() */
145 : /************************************************************************/
146 :
147 4272 : CPLErr GTiffDataset::FillEmptyTiles()
148 :
149 : {
150 : /* -------------------------------------------------------------------- */
151 : /* How many blocks are there in this file? */
152 : /* -------------------------------------------------------------------- */
153 8544 : const int nBlockCount = m_nPlanarConfig == PLANARCONFIG_SEPARATE
154 4272 : ? m_nBlocksPerBand * nBands
155 : : m_nBlocksPerBand;
156 :
157 : /* -------------------------------------------------------------------- */
158 : /* Fetch block maps. */
159 : /* -------------------------------------------------------------------- */
160 4272 : toff_t *panByteCounts = nullptr;
161 :
162 4272 : if (TIFFIsTiled(m_hTIFF))
163 888 : TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts);
164 : else
165 3384 : TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
166 :
167 4272 : if (panByteCounts == nullptr)
168 : {
169 : // Got here with libtiff 3.9.3 and tiff_write_8 test.
170 0 : ReportError(CE_Failure, CPLE_AppDefined,
171 : "FillEmptyTiles() failed because panByteCounts == NULL");
172 0 : return CE_Failure;
173 : }
174 :
175 : /* -------------------------------------------------------------------- */
176 : /* Prepare a blank data buffer to write for uninitialized blocks. */
177 : /* -------------------------------------------------------------------- */
178 : const GPtrDiff_t nBlockBytes =
179 4272 : TIFFIsTiled(m_hTIFF) ? static_cast<GPtrDiff_t>(TIFFTileSize(m_hTIFF))
180 3384 : : static_cast<GPtrDiff_t>(TIFFStripSize(m_hTIFF));
181 :
182 4272 : GByte *pabyData = static_cast<GByte *>(VSI_CALLOC_VERBOSE(nBlockBytes, 1));
183 4272 : if (pabyData == nullptr)
184 : {
185 0 : return CE_Failure;
186 : }
187 :
188 : // Force tiles completely filled with the nodata value to be written.
189 4272 : m_bWriteEmptyTiles = true;
190 :
191 : /* -------------------------------------------------------------------- */
192 : /* If set, fill data buffer with no data value. */
193 : /* -------------------------------------------------------------------- */
194 4272 : if ((m_bNoDataSet && m_dfNoDataValue != 0.0) ||
195 4077 : (m_bNoDataSetAsInt64 && m_nNoDataValueInt64 != 0) ||
196 4075 : (m_bNoDataSetAsUInt64 && m_nNoDataValueUInt64 != 0))
197 : {
198 199 : const GDALDataType eDataType = GetRasterBand(1)->GetRasterDataType();
199 199 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
200 199 : if (nDataTypeSize &&
201 199 : nDataTypeSize * 8 == static_cast<int>(m_nBitsPerSample))
202 : {
203 188 : if (m_bNoDataSetAsInt64)
204 : {
205 2 : GDALCopyWords64(&m_nNoDataValueInt64, GDT_Int64, 0, pabyData,
206 : eDataType, nDataTypeSize,
207 2 : nBlockBytes / nDataTypeSize);
208 : }
209 186 : else if (m_bNoDataSetAsUInt64)
210 : {
211 2 : GDALCopyWords64(&m_nNoDataValueUInt64, GDT_UInt64, 0, pabyData,
212 : eDataType, nDataTypeSize,
213 2 : nBlockBytes / nDataTypeSize);
214 : }
215 : else
216 : {
217 184 : double dfNoData = m_dfNoDataValue;
218 184 : GDALCopyWords64(&dfNoData, GDT_Float64, 0, pabyData, eDataType,
219 184 : nDataTypeSize, nBlockBytes / nDataTypeSize);
220 188 : }
221 : }
222 11 : else if (nDataTypeSize)
223 : {
224 : // Handle non power-of-two depths.
225 : // Ideally make a packed buffer, but that is a bit tedious,
226 : // so use the normal I/O interfaces.
227 :
228 11 : CPLFree(pabyData);
229 :
230 11 : pabyData = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
231 : m_nBlockXSize, m_nBlockYSize, nDataTypeSize));
232 11 : if (pabyData == nullptr)
233 0 : return CE_Failure;
234 11 : if (m_bNoDataSetAsInt64)
235 : {
236 0 : GDALCopyWords64(&m_nNoDataValueInt64, GDT_Int64, 0, pabyData,
237 : eDataType, nDataTypeSize,
238 0 : static_cast<GPtrDiff_t>(m_nBlockXSize) *
239 0 : m_nBlockYSize);
240 : }
241 11 : else if (m_bNoDataSetAsUInt64)
242 : {
243 0 : GDALCopyWords64(&m_nNoDataValueUInt64, GDT_UInt64, 0, pabyData,
244 : eDataType, nDataTypeSize,
245 0 : static_cast<GPtrDiff_t>(m_nBlockXSize) *
246 0 : m_nBlockYSize);
247 : }
248 : else
249 : {
250 11 : GDALCopyWords64(&m_dfNoDataValue, GDT_Float64, 0, pabyData,
251 : eDataType, nDataTypeSize,
252 11 : static_cast<GPtrDiff_t>(m_nBlockXSize) *
253 11 : m_nBlockYSize);
254 : }
255 11 : CPLErr eErr = CE_None;
256 46 : for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
257 : {
258 35 : if (panByteCounts[iBlock] == 0)
259 : {
260 18 : if (m_nPlanarConfig == PLANARCONFIG_SEPARATE || nBands == 1)
261 : {
262 24 : if (GetRasterBand(1 + iBlock / m_nBlocksPerBand)
263 12 : ->WriteBlock((iBlock % m_nBlocksPerBand) %
264 12 : m_nBlocksPerRow,
265 12 : (iBlock % m_nBlocksPerBand) /
266 12 : m_nBlocksPerRow,
267 12 : pabyData) != CE_None)
268 : {
269 0 : eErr = CE_Failure;
270 : }
271 : }
272 : else
273 : {
274 : // In contig case, don't directly call WriteBlock(), as
275 : // it could cause useless decompression-recompression.
276 6 : const int nXOff =
277 6 : (iBlock % m_nBlocksPerRow) * m_nBlockXSize;
278 6 : const int nYOff =
279 6 : (iBlock / m_nBlocksPerRow) * m_nBlockYSize;
280 6 : const int nXSize =
281 6 : (nXOff + m_nBlockXSize <= nRasterXSize)
282 6 : ? m_nBlockXSize
283 2 : : nRasterXSize - nXOff;
284 6 : const int nYSize =
285 6 : (nYOff + m_nBlockYSize <= nRasterYSize)
286 6 : ? m_nBlockYSize
287 3 : : nRasterYSize - nYOff;
288 18 : for (int iBand = 1; iBand <= nBands; ++iBand)
289 : {
290 12 : if (GetRasterBand(iBand)->RasterIO(
291 : GF_Write, nXOff, nYOff, nXSize, nYSize,
292 : pabyData, nXSize, nYSize, eDataType, 0, 0,
293 12 : nullptr) != CE_None)
294 : {
295 0 : eErr = CE_Failure;
296 : }
297 : }
298 : }
299 : }
300 : }
301 11 : CPLFree(pabyData);
302 11 : return eErr;
303 188 : }
304 : }
305 :
306 : /* -------------------------------------------------------------------- */
307 : /* When we must fill with zeroes, try to create non-sparse file */
308 : /* w.r.t TIFF spec ... as a sparse file w.r.t filesystem, ie by */
309 : /* seeking to end of file instead of writing zero blocks. */
310 : /* -------------------------------------------------------------------- */
311 4073 : else if (m_nCompression == COMPRESSION_NONE && (m_nBitsPerSample % 8) == 0)
312 : {
313 2827 : CPLErr eErr = CE_None;
314 : // Only use libtiff to write the first sparse block to ensure that it
315 : // will serialize offset and count arrays back to disk.
316 2827 : int nCountBlocksToZero = 0;
317 2258640 : for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
318 : {
319 2255810 : if (panByteCounts[iBlock] == 0)
320 : {
321 2180560 : if (nCountBlocksToZero == 0)
322 : {
323 882 : const bool bWriteEmptyTilesBak = m_bWriteEmptyTiles;
324 882 : m_bWriteEmptyTiles = true;
325 882 : const bool bOK = WriteEncodedTileOrStrip(iBlock, pabyData,
326 882 : FALSE) == CE_None;
327 882 : m_bWriteEmptyTiles = bWriteEmptyTilesBak;
328 882 : if (!bOK)
329 : {
330 2 : eErr = CE_Failure;
331 2 : break;
332 : }
333 : }
334 2180560 : nCountBlocksToZero++;
335 : }
336 : }
337 2827 : CPLFree(pabyData);
338 :
339 2827 : --nCountBlocksToZero;
340 :
341 : // And then seek to end of file for other ones.
342 2827 : if (nCountBlocksToZero > 0)
343 : {
344 302 : toff_t *panByteOffsets = nullptr;
345 :
346 302 : if (TIFFIsTiled(m_hTIFF))
347 84 : TIFFGetField(m_hTIFF, TIFFTAG_TILEOFFSETS, &panByteOffsets);
348 : else
349 218 : TIFFGetField(m_hTIFF, TIFFTAG_STRIPOFFSETS, &panByteOffsets);
350 :
351 302 : if (panByteOffsets == nullptr)
352 : {
353 0 : ReportError(
354 : CE_Failure, CPLE_AppDefined,
355 : "FillEmptyTiles() failed because panByteOffsets == NULL");
356 0 : return CE_Failure;
357 : }
358 :
359 302 : VSILFILE *fpTIF = VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
360 302 : VSIFSeekL(fpTIF, 0, SEEK_END);
361 302 : const vsi_l_offset nOffset = VSIFTellL(fpTIF);
362 :
363 302 : vsi_l_offset iBlockToZero = 0;
364 2187700 : for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
365 : {
366 2187400 : if (panByteCounts[iBlock] == 0)
367 : {
368 2179680 : panByteOffsets[iBlock] = static_cast<toff_t>(
369 2179680 : nOffset + iBlockToZero * nBlockBytes);
370 2179680 : panByteCounts[iBlock] = nBlockBytes;
371 2179680 : iBlockToZero++;
372 : }
373 : }
374 302 : CPLAssert(iBlockToZero ==
375 : static_cast<vsi_l_offset>(nCountBlocksToZero));
376 :
377 302 : if (VSIFTruncateL(fpTIF, nOffset + iBlockToZero * nBlockBytes) != 0)
378 : {
379 0 : eErr = CE_Failure;
380 0 : ReportError(CE_Failure, CPLE_FileIO,
381 : "Cannot initialize empty blocks");
382 : }
383 : }
384 :
385 2827 : return eErr;
386 : }
387 :
388 : /* -------------------------------------------------------------------- */
389 : /* Check all blocks, writing out data for uninitialized blocks. */
390 : /* -------------------------------------------------------------------- */
391 :
392 1434 : GByte *pabyRaw = nullptr;
393 1434 : vsi_l_offset nRawSize = 0;
394 1434 : CPLErr eErr = CE_None;
395 42464 : for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
396 : {
397 41037 : if (panByteCounts[iBlock] == 0)
398 : {
399 8434 : if (pabyRaw == nullptr)
400 : {
401 1527 : if (WriteEncodedTileOrStrip(iBlock, pabyData, FALSE) != CE_None)
402 : {
403 7 : eErr = CE_Failure;
404 7 : break;
405 : }
406 :
407 1520 : vsi_l_offset nOffset = 0;
408 1520 : if (!IsBlockAvailable(iBlock, &nOffset, &nRawSize))
409 0 : break;
410 :
411 : // When using compression, get back the compressed block
412 : // so we can use the raw API to write it faster.
413 1520 : if (m_nCompression != COMPRESSION_NONE)
414 : {
415 : pabyRaw = static_cast<GByte *>(
416 353 : VSI_MALLOC_VERBOSE(static_cast<size_t>(nRawSize)));
417 353 : if (pabyRaw)
418 : {
419 : VSILFILE *fp =
420 353 : VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF));
421 353 : const vsi_l_offset nCurOffset = VSIFTellL(fp);
422 353 : VSIFSeekL(fp, nOffset, SEEK_SET);
423 353 : VSIFReadL(pabyRaw, 1, static_cast<size_t>(nRawSize),
424 : fp);
425 353 : VSIFSeekL(fp, nCurOffset, SEEK_SET);
426 : }
427 : }
428 : }
429 : else
430 : {
431 6907 : WriteRawStripOrTile(iBlock, pabyRaw,
432 : static_cast<GPtrDiff_t>(nRawSize));
433 : }
434 : }
435 : }
436 :
437 1434 : CPLFree(pabyData);
438 1434 : VSIFree(pabyRaw);
439 1434 : return eErr;
440 : }
441 :
442 : /************************************************************************/
443 : /* HasOnlyNoData() */
444 : /************************************************************************/
445 :
446 24953 : bool GTiffDataset::HasOnlyNoData(const void *pBuffer, int nWidth, int nHeight,
447 : int nLineStride, int nComponents)
448 : {
449 24953 : if (m_nSampleFormat == SAMPLEFORMAT_COMPLEXINT ||
450 24953 : m_nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP)
451 0 : return false;
452 24953 : if (m_bNoDataSetAsInt64 || m_bNoDataSetAsUInt64)
453 2 : return false; // FIXME: over pessimistic
454 49902 : return GDALBufferHasOnlyNoData(
455 24951 : pBuffer, m_bNoDataSet ? m_dfNoDataValue : 0.0, nWidth, nHeight,
456 24951 : nLineStride, nComponents, m_nBitsPerSample,
457 24951 : m_nSampleFormat == SAMPLEFORMAT_UINT ? GSF_UNSIGNED_INT
458 705 : : m_nSampleFormat == SAMPLEFORMAT_INT ? GSF_SIGNED_INT
459 24951 : : GSF_FLOATING_POINT);
460 : }
461 :
462 : /************************************************************************/
463 : /* IsFirstPixelEqualToNoData() */
464 : /************************************************************************/
465 :
466 132767 : inline bool GTiffDataset::IsFirstPixelEqualToNoData(const void *pBuffer)
467 : {
468 132767 : const GDALDataType eDT = GetRasterBand(1)->GetRasterDataType();
469 132676 : const double dfEffectiveNoData = (m_bNoDataSet) ? m_dfNoDataValue : 0.0;
470 132676 : if (m_bNoDataSetAsInt64 || m_bNoDataSetAsUInt64)
471 0 : return true; // FIXME: over pessimistic
472 132698 : if (m_nBitsPerSample == 8 ||
473 48950 : (m_nBitsPerSample < 8 && dfEffectiveNoData == 0))
474 : {
475 85828 : if (eDT == GDT_Int8)
476 : {
477 36 : return GDALIsValueInRange<signed char>(dfEffectiveNoData) &&
478 18 : *(static_cast<const signed char *>(pBuffer)) ==
479 36 : static_cast<signed char>(dfEffectiveNoData);
480 : }
481 171593 : return GDALIsValueInRange<GByte>(dfEffectiveNoData) &&
482 85783 : *(static_cast<const GByte *>(pBuffer)) ==
483 171593 : static_cast<GByte>(dfEffectiveNoData);
484 : }
485 46870 : if (m_nBitsPerSample == 16 && eDT == GDT_UInt16)
486 : {
487 3532 : return GDALIsValueInRange<GUInt16>(dfEffectiveNoData) &&
488 1766 : *(static_cast<const GUInt16 *>(pBuffer)) ==
489 3532 : static_cast<GUInt16>(dfEffectiveNoData);
490 : }
491 45104 : if (m_nBitsPerSample == 16 && eDT == GDT_Int16)
492 : {
493 6630 : return GDALIsValueInRange<GInt16>(dfEffectiveNoData) &&
494 3315 : *(static_cast<const GInt16 *>(pBuffer)) ==
495 6630 : static_cast<GInt16>(dfEffectiveNoData);
496 : }
497 41789 : if (m_nBitsPerSample == 32 && eDT == GDT_UInt32)
498 : {
499 146 : return GDALIsValueInRange<GUInt32>(dfEffectiveNoData) &&
500 73 : *(static_cast<const GUInt32 *>(pBuffer)) ==
501 146 : static_cast<GUInt32>(dfEffectiveNoData);
502 : }
503 41716 : if (m_nBitsPerSample == 32 && eDT == GDT_Int32)
504 : {
505 254 : return GDALIsValueInRange<GInt32>(dfEffectiveNoData) &&
506 127 : *(static_cast<const GInt32 *>(pBuffer)) ==
507 254 : static_cast<GInt32>(dfEffectiveNoData);
508 : }
509 41589 : if (m_nBitsPerSample == 64 && eDT == GDT_UInt64)
510 : {
511 6 : return GDALIsValueInRange<std::uint64_t>(dfEffectiveNoData) &&
512 3 : *(static_cast<const std::uint64_t *>(pBuffer)) ==
513 6 : static_cast<std::uint64_t>(dfEffectiveNoData);
514 : }
515 41586 : if (m_nBitsPerSample == 64 && eDT == GDT_Int64)
516 : {
517 6 : return GDALIsValueInRange<std::int64_t>(dfEffectiveNoData) &&
518 3 : *(static_cast<const std::int64_t *>(pBuffer)) ==
519 6 : static_cast<std::int64_t>(dfEffectiveNoData);
520 : }
521 41583 : if (m_nBitsPerSample == 32 && eDT == GDT_Float32)
522 : {
523 40475 : if (CPLIsNan(m_dfNoDataValue))
524 3 : return CPL_TO_BOOL(
525 3 : CPLIsNan(*(static_cast<const float *>(pBuffer))));
526 80693 : return GDALIsValueInRange<float>(dfEffectiveNoData) &&
527 40221 : *(static_cast<const float *>(pBuffer)) ==
528 80420 : static_cast<float>(dfEffectiveNoData);
529 : }
530 1108 : if (m_nBitsPerSample == 64 && eDT == GDT_Float64)
531 : {
532 183 : if (CPLIsNan(dfEffectiveNoData))
533 0 : return CPL_TO_BOOL(
534 0 : CPLIsNan(*(static_cast<const double *>(pBuffer))));
535 183 : return *(static_cast<const double *>(pBuffer)) == dfEffectiveNoData;
536 : }
537 925 : return false;
538 : }
539 :
540 : /************************************************************************/
541 : /* WriteDealWithLercAndNan() */
542 : /************************************************************************/
543 :
544 : template <typename T>
545 0 : void GTiffDataset::WriteDealWithLercAndNan(T *pBuffer, int nActualBlockWidth,
546 : int nActualBlockHeight,
547 : int nStrileHeight)
548 : {
549 : // This method does 2 things:
550 : // - warn the user if he tries to write NaN values with libtiff < 4.6.1
551 : // and multi-band PlanarConfig=Contig configuration
552 : // - and in right-most and bottom-most tiles, replace non accessible
553 : // pixel values by a safe one.
554 :
555 0 : const auto fPaddingValue =
556 : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
557 : m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1
558 : ? 0
559 : :
560 : #endif
561 : std::numeric_limits<T>::quiet_NaN();
562 :
563 0 : const int nBandsPerStrile =
564 0 : m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
565 0 : for (int j = 0; j < nActualBlockHeight; ++j)
566 : {
567 : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
568 : static bool bHasWarned = false;
569 : if (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1 && !bHasWarned)
570 : {
571 : for (int i = 0; i < nActualBlockWidth * nBandsPerStrile; ++i)
572 : {
573 : if (std::isnan(
574 : pBuffer[j * m_nBlockXSize * nBandsPerStrile + i]))
575 : {
576 : bHasWarned = true;
577 : CPLError(CE_Warning, CPLE_AppDefined,
578 : "libtiff < 4.6.1 does not handle properly NaN "
579 : "values for multi-band PlanarConfig=Contig "
580 : "configuration. As a workaround, you can set the "
581 : "INTERLEAVE=BAND creation option.");
582 : break;
583 : }
584 : }
585 : }
586 : #endif
587 0 : for (int i = nActualBlockWidth * nBandsPerStrile;
588 0 : i < m_nBlockXSize * nBandsPerStrile; ++i)
589 : {
590 0 : pBuffer[j * m_nBlockXSize * nBandsPerStrile + i] = fPaddingValue;
591 : }
592 : }
593 0 : for (int j = nActualBlockHeight; j < nStrileHeight; ++j)
594 : {
595 0 : for (int i = 0; i < m_nBlockXSize * nBandsPerStrile; ++i)
596 : {
597 0 : pBuffer[j * m_nBlockXSize * nBandsPerStrile + i] = fPaddingValue;
598 : }
599 : }
600 0 : }
601 :
602 : /************************************************************************/
603 : /* WriteEncodedTile() */
604 : /************************************************************************/
605 :
606 46810 : bool GTiffDataset::WriteEncodedTile(uint32_t tile, GByte *pabyData,
607 : int bPreserveDataBuffer)
608 : {
609 :
610 46810 : const int iColumn = (tile % m_nBlocksPerBand) % m_nBlocksPerRow;
611 46810 : const int iRow = (tile % m_nBlocksPerBand) / m_nBlocksPerRow;
612 :
613 93620 : const int nActualBlockWidth = (iColumn == m_nBlocksPerRow - 1)
614 46810 : ? nRasterXSize - iColumn * m_nBlockXSize
615 : : m_nBlockXSize;
616 93620 : const int nActualBlockHeight = (iRow == m_nBlocksPerColumn - 1)
617 46810 : ? nRasterYSize - iRow * m_nBlockYSize
618 : : m_nBlockYSize;
619 :
620 : /* -------------------------------------------------------------------- */
621 : /* Don't write empty blocks in some cases. */
622 : /* -------------------------------------------------------------------- */
623 46810 : if (!m_bWriteEmptyTiles && IsFirstPixelEqualToNoData(pabyData))
624 : {
625 1805 : if (!IsBlockAvailable(tile))
626 : {
627 1805 : const int nComponents =
628 1805 : m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
629 :
630 1805 : if (HasOnlyNoData(pabyData, nActualBlockWidth, nActualBlockHeight,
631 : m_nBlockXSize, nComponents))
632 : {
633 1150 : return true;
634 : }
635 : }
636 : }
637 :
638 : // Is this a partial right edge or bottom edge tile?
639 89061 : const bool bPartialTile = (nActualBlockWidth < m_nBlockXSize) ||
640 43401 : (nActualBlockHeight < m_nBlockYSize);
641 :
642 : const bool bIsLercFloatingPoint =
643 45725 : m_nCompression == COMPRESSION_LERC &&
644 66 : (GetRasterBand(1)->GetRasterDataType() == GDT_Float32 ||
645 64 : GetRasterBand(1)->GetRasterDataType() == GDT_Float64);
646 :
647 : // Do we need to spread edge values right or down for a partial
648 : // JPEG encoded tile? We do this to avoid edge artifacts.
649 : // We also need to be careful with LERC and NaN values
650 45659 : const bool bNeedTempBuffer =
651 49204 : bPartialTile &&
652 3545 : (m_nCompression == COMPRESSION_JPEG || bIsLercFloatingPoint);
653 :
654 : // If we need to fill out the tile, or if we want to prevent
655 : // TIFFWriteEncodedTile from altering the buffer as part of
656 : // byte swapping the data on write then we will need a temporary
657 : // working buffer. If not, we can just do a direct write.
658 45659 : const GPtrDiff_t cc = static_cast<GPtrDiff_t>(TIFFTileSize(m_hTIFF));
659 :
660 59234 : if (bPreserveDataBuffer &&
661 13579 : (TIFFIsByteSwapped(m_hTIFF) || bNeedTempBuffer || m_panMaskOffsetLsb))
662 : {
663 176 : if (m_pabyTempWriteBuffer == nullptr)
664 : {
665 39 : m_pabyTempWriteBuffer = CPLMalloc(cc);
666 : }
667 176 : memcpy(m_pabyTempWriteBuffer, pabyData, cc);
668 :
669 176 : pabyData = static_cast<GByte *>(m_pabyTempWriteBuffer);
670 : }
671 :
672 : // Perform tile fill if needed.
673 : // TODO: we should also handle the case of nBitsPerSample == 12
674 : // but this is more involved.
675 45655 : if (bPartialTile && m_nCompression == COMPRESSION_JPEG &&
676 150 : m_nBitsPerSample == 8)
677 : {
678 148 : const int nComponents =
679 148 : m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
680 :
681 148 : CPLDebug("GTiff", "Filling out jpeg edge tile on write.");
682 :
683 148 : const int nRightPixelsToFill =
684 148 : iColumn == m_nBlocksPerRow - 1
685 148 : ? m_nBlockXSize * (iColumn + 1) - nRasterXSize
686 : : 0;
687 148 : const int nBottomPixelsToFill =
688 148 : iRow == m_nBlocksPerColumn - 1
689 148 : ? m_nBlockYSize * (iRow + 1) - nRasterYSize
690 : : 0;
691 :
692 : // Fill out to the right.
693 148 : const int iSrcX = m_nBlockXSize - nRightPixelsToFill - 1;
694 :
695 11667 : for (int iX = iSrcX + 1; iX < m_nBlockXSize; ++iX)
696 : {
697 3392900 : for (int iY = 0; iY < m_nBlockYSize; ++iY)
698 : {
699 3381380 : memcpy(pabyData +
700 3381380 : (static_cast<GPtrDiff_t>(m_nBlockXSize) * iY + iX) *
701 3381380 : nComponents,
702 3381380 : pabyData + (static_cast<GPtrDiff_t>(m_nBlockXSize) * iY +
703 3381380 : iSrcX) *
704 3381380 : nComponents,
705 : nComponents);
706 : }
707 : }
708 :
709 : // Now fill out the bottom.
710 148 : const int iSrcY = m_nBlockYSize - nBottomPixelsToFill - 1;
711 16888 : for (int iY = iSrcY + 1; iY < m_nBlockYSize; ++iY)
712 : {
713 16740 : memcpy(pabyData + static_cast<GPtrDiff_t>(m_nBlockXSize) *
714 16740 : nComponents * iY,
715 16740 : pabyData + static_cast<GPtrDiff_t>(m_nBlockXSize) *
716 16740 : nComponents * iSrcY,
717 16740 : static_cast<GPtrDiff_t>(m_nBlockXSize) * nComponents);
718 : }
719 : }
720 :
721 45655 : if (bIsLercFloatingPoint &&
722 : (bPartialTile
723 : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
724 : /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
725 : || (m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1)
726 : #endif
727 : ))
728 : {
729 0 : if (GetRasterBand(1)->GetRasterDataType() == GDT_Float32)
730 0 : WriteDealWithLercAndNan(reinterpret_cast<float *>(pabyData),
731 : nActualBlockWidth, nActualBlockHeight,
732 : m_nBlockYSize);
733 : else
734 0 : WriteDealWithLercAndNan(reinterpret_cast<double *>(pabyData),
735 : nActualBlockWidth, nActualBlockHeight,
736 : m_nBlockYSize);
737 : }
738 :
739 45655 : if (m_panMaskOffsetLsb)
740 : {
741 0 : const int iBand = m_nPlanarConfig == PLANARCONFIG_SEPARATE
742 0 : ? static_cast<int>(tile) / m_nBlocksPerBand
743 : : -1;
744 0 : DiscardLsb(pabyData, cc, iBand);
745 : }
746 :
747 45655 : if (m_bStreamingOut)
748 : {
749 17 : if (tile != static_cast<uint32_t>(m_nLastWrittenBlockId + 1))
750 : {
751 1 : ReportError(CE_Failure, CPLE_NotSupported,
752 : "Attempt to write block %d whereas %d was expected",
753 1 : tile, m_nLastWrittenBlockId + 1);
754 1 : return false;
755 : }
756 16 : if (static_cast<GPtrDiff_t>(VSIFWriteL(pabyData, 1, cc, m_fpToWrite)) !=
757 : cc)
758 : {
759 0 : ReportError(CE_Failure, CPLE_FileIO,
760 : "Could not write " CPL_FRMT_GUIB " bytes",
761 : static_cast<GUIntBig>(cc));
762 0 : return false;
763 : }
764 16 : m_nLastWrittenBlockId = tile;
765 16 : return true;
766 : }
767 :
768 : /* -------------------------------------------------------------------- */
769 : /* Should we do compression in a worker thread ? */
770 : /* -------------------------------------------------------------------- */
771 45638 : if (SubmitCompressionJob(tile, pabyData, cc, m_nBlockYSize))
772 19262 : return true;
773 :
774 26370 : return TIFFWriteEncodedTile(m_hTIFF, tile, pabyData, cc) == cc;
775 : }
776 :
777 : /************************************************************************/
778 : /* WriteEncodedStrip() */
779 : /************************************************************************/
780 :
781 133150 : bool GTiffDataset::WriteEncodedStrip(uint32_t strip, GByte *pabyData,
782 : int bPreserveDataBuffer)
783 : {
784 133150 : GPtrDiff_t cc = static_cast<GPtrDiff_t>(TIFFStripSize(m_hTIFF));
785 133101 : const auto ccFull = cc;
786 :
787 : /* -------------------------------------------------------------------- */
788 : /* If this is the last strip in the image, and is partial, then */
789 : /* we need to trim the number of scanlines written to the */
790 : /* amount of valid data we have. (#2748) */
791 : /* -------------------------------------------------------------------- */
792 133101 : const int nStripWithinBand = strip % m_nBlocksPerBand;
793 133101 : int nStripHeight = m_nRowsPerStrip;
794 :
795 133101 : if (nStripWithinBand * nStripHeight > GetRasterYSize() - nStripHeight)
796 : {
797 324 : nStripHeight = GetRasterYSize() - nStripWithinBand * m_nRowsPerStrip;
798 324 : cc = (cc / m_nRowsPerStrip) * nStripHeight;
799 648 : CPLDebug("GTiff",
800 : "Adjusted bytes to write from " CPL_FRMT_GUIB
801 : " to " CPL_FRMT_GUIB ".",
802 324 : static_cast<GUIntBig>(TIFFStripSize(m_hTIFF)),
803 : static_cast<GUIntBig>(cc));
804 : }
805 :
806 : /* -------------------------------------------------------------------- */
807 : /* Don't write empty blocks in some cases. */
808 : /* -------------------------------------------------------------------- */
809 133232 : if (!m_bWriteEmptyTiles && IsFirstPixelEqualToNoData(pabyData))
810 : {
811 23318 : if (!IsBlockAvailable(strip))
812 : {
813 23148 : const int nComponents =
814 23148 : m_nPlanarConfig == PLANARCONFIG_CONTIG ? nBands : 1;
815 :
816 23148 : if (HasOnlyNoData(pabyData, m_nBlockXSize, nStripHeight,
817 : m_nBlockXSize, nComponents))
818 : {
819 14118 : return true;
820 : }
821 : }
822 : }
823 :
824 : /* -------------------------------------------------------------------- */
825 : /* TIFFWriteEncodedStrip can alter the passed buffer if */
826 : /* byte-swapping is necessary so we use a temporary buffer */
827 : /* before calling it. */
828 : /* -------------------------------------------------------------------- */
829 205716 : if (bPreserveDataBuffer &&
830 86832 : (TIFFIsByteSwapped(m_hTIFF) || m_panMaskOffsetLsb))
831 : {
832 293 : if (m_pabyTempWriteBuffer == nullptr)
833 : {
834 125 : m_pabyTempWriteBuffer = CPLMalloc(ccFull);
835 : }
836 293 : memcpy(m_pabyTempWriteBuffer, pabyData, cc);
837 293 : pabyData = static_cast<GByte *>(m_pabyTempWriteBuffer);
838 : }
839 :
840 : #if !defined(LIBTIFF_MULTIBAND_LERC_NAN_OK)
841 : const bool bIsLercFloatingPoint =
842 : m_nCompression == COMPRESSION_LERC &&
843 : (GetRasterBand(1)->GetRasterDataType() == GDT_Float32 ||
844 : GetRasterBand(1)->GetRasterDataType() == GDT_Float64);
845 : if (bIsLercFloatingPoint &&
846 : /* libtiff < 4.6.1 doesn't generate a LERC mask for multi-band contig configuration */
847 : m_nPlanarConfig == PLANARCONFIG_CONTIG && nBands > 1)
848 : {
849 : if (GetRasterBand(1)->GetRasterDataType() == GDT_Float32)
850 : WriteDealWithLercAndNan(reinterpret_cast<float *>(pabyData),
851 : m_nBlockXSize, nStripHeight, nStripHeight);
852 : else
853 : WriteDealWithLercAndNan(reinterpret_cast<double *>(pabyData),
854 : m_nBlockXSize, nStripHeight, nStripHeight);
855 : }
856 : #endif
857 :
858 118884 : if (m_panMaskOffsetLsb)
859 : {
860 366 : int iBand = m_nPlanarConfig == PLANARCONFIG_SEPARATE
861 183 : ? static_cast<int>(strip) / m_nBlocksPerBand
862 : : -1;
863 183 : DiscardLsb(pabyData, cc, iBand);
864 : }
865 :
866 119133 : if (m_bStreamingOut)
867 : {
868 1408 : if (strip != static_cast<uint32_t>(m_nLastWrittenBlockId + 1))
869 : {
870 1 : ReportError(CE_Failure, CPLE_NotSupported,
871 : "Attempt to write block %d whereas %d was expected",
872 1 : strip, m_nLastWrittenBlockId + 1);
873 1 : return false;
874 : }
875 1407 : if (static_cast<GPtrDiff_t>(VSIFWriteL(pabyData, 1, cc, m_fpToWrite)) !=
876 : cc)
877 : {
878 0 : ReportError(CE_Failure, CPLE_FileIO,
879 : "Could not write " CPL_FRMT_GUIB " bytes",
880 : static_cast<GUIntBig>(cc));
881 0 : return false;
882 : }
883 1407 : m_nLastWrittenBlockId = strip;
884 1407 : return true;
885 : }
886 :
887 : /* -------------------------------------------------------------------- */
888 : /* Should we do compression in a worker thread ? */
889 : /* -------------------------------------------------------------------- */
890 117725 : if (SubmitCompressionJob(strip, pabyData, cc, nStripHeight))
891 6703 : return true;
892 :
893 110855 : return TIFFWriteEncodedStrip(m_hTIFF, strip, pabyData, cc) == cc;
894 : }
895 :
896 : /************************************************************************/
897 : /* InitCompressionThreads() */
898 : /************************************************************************/
899 :
900 19930 : void GTiffDataset::InitCompressionThreads(bool bUpdateMode,
901 : CSLConstList papszOptions)
902 : {
903 : // Raster == tile, then no need for threads
904 19930 : if (m_nBlockXSize == nRasterXSize && m_nBlockYSize == nRasterYSize)
905 15162 : return;
906 :
907 4768 : const char *pszValue = CSLFetchNameValue(papszOptions, "NUM_THREADS");
908 4774 : if (pszValue == nullptr)
909 4711 : pszValue = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
910 4774 : if (pszValue)
911 : {
912 : int nThreads =
913 77 : EQUAL(pszValue, "ALL_CPUS") ? CPLGetNumCPUs() : atoi(pszValue);
914 77 : if (nThreads > 1024)
915 0 : nThreads = 1024; // to please Coverity
916 77 : if (nThreads > 1)
917 : {
918 83 : if ((bUpdateMode && m_nCompression != COMPRESSION_NONE) ||
919 13 : (nBands >= 1 && IsMultiThreadedReadCompatible()))
920 : {
921 65 : CPLDebug("GTiff",
922 : "Using up to %d threads for compression/decompression",
923 : nThreads);
924 :
925 65 : m_poThreadPool = GDALGetGlobalThreadPool(nThreads);
926 65 : if (bUpdateMode && m_poThreadPool)
927 57 : m_poCompressQueue = m_poThreadPool->CreateJobQueue();
928 :
929 65 : if (m_poCompressQueue != nullptr)
930 : {
931 : // Add a margin of an extra job w.r.t thread number
932 : // so as to optimize compression time (enables the main
933 : // thread to do boring I/O while all CPUs are working).
934 57 : m_asCompressionJobs.resize(nThreads + 1);
935 57 : memset(&m_asCompressionJobs[0], 0,
936 57 : m_asCompressionJobs.size() *
937 : sizeof(GTiffCompressionJob));
938 57 : for (int i = 0;
939 276 : i < static_cast<int>(m_asCompressionJobs.size()); ++i)
940 : {
941 438 : m_asCompressionJobs[i].pszTmpFilename =
942 219 : CPLStrdup(CPLSPrintf("/vsimem/gtiff/thread/job/%p",
943 219 : &m_asCompressionJobs[i]));
944 219 : m_asCompressionJobs[i].nStripOrTile = -1;
945 : }
946 :
947 : // This is kind of a hack, but basically using
948 : // TIFFWriteRawStrip/Tile and then TIFFReadEncodedStrip/Tile
949 : // does not work on a newly created file, because
950 : // TIFF_MYBUFFER is not set in tif_flags
951 : // (if using TIFFWriteEncodedStrip/Tile first,
952 : // TIFFWriteBufferSetup() is automatically called).
953 : // This should likely rather fixed in libtiff itself.
954 57 : CPL_IGNORE_RET_VAL(
955 57 : TIFFWriteBufferSetup(m_hTIFF, nullptr, -1));
956 : }
957 : }
958 : }
959 7 : else if (nThreads < 0 ||
960 7 : (!EQUAL(pszValue, "0") && !EQUAL(pszValue, "1") &&
961 3 : !EQUAL(pszValue, "ALL_CPUS")))
962 : {
963 3 : ReportError(CE_Warning, CPLE_AppDefined,
964 : "Invalid value for NUM_THREADS: %s", pszValue);
965 : }
966 : }
967 : }
968 :
969 : /************************************************************************/
970 : /* ThreadCompressionFunc() */
971 : /************************************************************************/
972 :
973 25977 : void GTiffDataset::ThreadCompressionFunc(void *pData)
974 : {
975 25977 : GTiffCompressionJob *psJob = static_cast<GTiffCompressionJob *>(pData);
976 25977 : GTiffDataset *poDS = psJob->poDS;
977 :
978 25977 : VSILFILE *fpTmp = VSIFOpenL(psJob->pszTmpFilename, "wb+");
979 25977 : TIFF *hTIFFTmp = VSI_TIFFOpen(
980 51954 : psJob->pszTmpFilename, psJob->bTIFFIsBigEndian ? "wb+" : "wl+", fpTmp);
981 25977 : CPLAssert(hTIFFTmp != nullptr);
982 25977 : TIFFSetField(hTIFFTmp, TIFFTAG_IMAGEWIDTH, poDS->m_nBlockXSize);
983 25976 : TIFFSetField(hTIFFTmp, TIFFTAG_IMAGELENGTH, psJob->nHeight);
984 25977 : TIFFSetField(hTIFFTmp, TIFFTAG_BITSPERSAMPLE, poDS->m_nBitsPerSample);
985 25976 : TIFFSetField(hTIFFTmp, TIFFTAG_COMPRESSION, poDS->m_nCompression);
986 25977 : TIFFSetField(hTIFFTmp, TIFFTAG_PHOTOMETRIC, poDS->m_nPhotometric);
987 25976 : TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLEFORMAT, poDS->m_nSampleFormat);
988 25976 : TIFFSetField(hTIFFTmp, TIFFTAG_SAMPLESPERPIXEL, poDS->m_nSamplesPerPixel);
989 25976 : TIFFSetField(hTIFFTmp, TIFFTAG_ROWSPERSTRIP, poDS->m_nBlockYSize);
990 25975 : TIFFSetField(hTIFFTmp, TIFFTAG_PLANARCONFIG, poDS->m_nPlanarConfig);
991 25975 : if (psJob->nPredictor != PREDICTOR_NONE)
992 256 : TIFFSetField(hTIFFTmp, TIFFTAG_PREDICTOR, psJob->nPredictor);
993 25975 : if (poDS->m_nCompression == COMPRESSION_LERC)
994 : {
995 24 : TIFFSetField(hTIFFTmp, TIFFTAG_LERC_PARAMETERS, 2,
996 24 : poDS->m_anLercAddCompressionAndVersion);
997 : }
998 25975 : if (psJob->nExtraSampleCount)
999 : {
1000 282 : TIFFSetField(hTIFFTmp, TIFFTAG_EXTRASAMPLES, psJob->nExtraSampleCount,
1001 : psJob->pExtraSamples);
1002 : }
1003 :
1004 25975 : poDS->RestoreVolatileParameters(hTIFFTmp);
1005 :
1006 51953 : bool bOK = TIFFWriteEncodedStrip(hTIFFTmp, 0, psJob->pabyBuffer,
1007 25976 : psJob->nBufferSize) == psJob->nBufferSize;
1008 :
1009 25977 : toff_t nOffset = 0;
1010 25977 : if (bOK)
1011 : {
1012 25977 : toff_t *panOffsets = nullptr;
1013 25977 : toff_t *panByteCounts = nullptr;
1014 25977 : TIFFGetField(hTIFFTmp, TIFFTAG_STRIPOFFSETS, &panOffsets);
1015 25976 : TIFFGetField(hTIFFTmp, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
1016 :
1017 25976 : nOffset = panOffsets[0];
1018 25976 : psJob->nCompressedBufferSize =
1019 25976 : static_cast<GPtrDiff_t>(panByteCounts[0]);
1020 : }
1021 : else
1022 : {
1023 0 : CPLError(CE_Failure, CPLE_AppDefined,
1024 : "Error when compressing strip/tile %d", psJob->nStripOrTile);
1025 : }
1026 :
1027 25976 : XTIFFClose(hTIFFTmp);
1028 25977 : if (VSIFCloseL(fpTmp) != 0)
1029 : {
1030 0 : if (bOK)
1031 : {
1032 0 : bOK = false;
1033 0 : CPLError(CE_Failure, CPLE_AppDefined,
1034 : "Error when compressing strip/tile %d",
1035 : psJob->nStripOrTile);
1036 : }
1037 : }
1038 :
1039 25971 : if (bOK)
1040 : {
1041 25971 : vsi_l_offset nFileSize = 0;
1042 : GByte *pabyCompressedBuffer =
1043 25971 : VSIGetMemFileBuffer(psJob->pszTmpFilename, &nFileSize, FALSE);
1044 25977 : CPLAssert(static_cast<vsi_l_offset>(
1045 : nOffset + psJob->nCompressedBufferSize) <= nFileSize);
1046 25977 : psJob->pabyCompressedBuffer = pabyCompressedBuffer + nOffset;
1047 : }
1048 : else
1049 : {
1050 0 : psJob->pabyCompressedBuffer = nullptr;
1051 0 : psJob->nCompressedBufferSize = 0;
1052 : }
1053 :
1054 25977 : auto poMainDS = poDS->m_poBaseDS ? poDS->m_poBaseDS : poDS;
1055 25977 : if (poMainDS->m_poCompressQueue)
1056 : {
1057 1575 : std::lock_guard oLock(poMainDS->m_oCompressThreadPoolMutex);
1058 1575 : psJob->bReady = true;
1059 : }
1060 25977 : }
1061 :
1062 : /************************************************************************/
1063 : /* WriteRawStripOrTile() */
1064 : /************************************************************************/
1065 :
1066 32884 : void GTiffDataset::WriteRawStripOrTile(int nStripOrTile,
1067 : GByte *pabyCompressedBuffer,
1068 : GPtrDiff_t nCompressedBufferSize)
1069 : {
1070 : #ifdef DEBUG_VERBOSE
1071 : CPLDebug("GTIFF", "Writing raw strip/tile %d, size " CPL_FRMT_GUIB,
1072 : nStripOrTile, static_cast<GUIntBig>(nCompressedBufferSize));
1073 : #endif
1074 32884 : toff_t *panOffsets = nullptr;
1075 32884 : toff_t *panByteCounts = nullptr;
1076 32884 : bool bWriteAtEnd = true;
1077 32884 : bool bWriteLeader = m_bLeaderSizeAsUInt4;
1078 32884 : bool bWriteTrailer = m_bTrailerRepeatedLast4BytesRepeated;
1079 32884 : if (TIFFGetField(m_hTIFF,
1080 32884 : TIFFIsTiled(m_hTIFF) ? TIFFTAG_TILEOFFSETS
1081 : : TIFFTAG_STRIPOFFSETS,
1082 32884 : &panOffsets) &&
1083 32884 : panOffsets != nullptr && panOffsets[nStripOrTile] != 0)
1084 : {
1085 : // Forces TIFFAppendStrip() to consider if the location of the
1086 : // tile/strip can be reused or if the strile should be written at end of
1087 : // file.
1088 360 : TIFFSetWriteOffset(m_hTIFF, 0);
1089 :
1090 360 : if (m_bBlockOrderRowMajor)
1091 : {
1092 264 : if (TIFFGetField(m_hTIFF,
1093 264 : TIFFIsTiled(m_hTIFF) ? TIFFTAG_TILEBYTECOUNTS
1094 : : TIFFTAG_STRIPBYTECOUNTS,
1095 528 : &panByteCounts) &&
1096 264 : panByteCounts != nullptr)
1097 : {
1098 264 : if (static_cast<GUIntBig>(nCompressedBufferSize) >
1099 264 : panByteCounts[nStripOrTile])
1100 : {
1101 8 : GTiffDataset *poRootDS = m_poBaseDS ? m_poBaseDS : this;
1102 8 : if (!poRootDS->m_bKnownIncompatibleEdition &&
1103 8 : !poRootDS->m_bWriteKnownIncompatibleEdition)
1104 : {
1105 8 : ReportError(
1106 : CE_Warning, CPLE_AppDefined,
1107 : "A strile cannot be rewritten in place, which "
1108 : "invalidates the BLOCK_ORDER optimization.");
1109 8 : poRootDS->m_bKnownIncompatibleEdition = true;
1110 8 : poRootDS->m_bWriteKnownIncompatibleEdition = true;
1111 : }
1112 : }
1113 : // For mask interleaving, if the size is not exactly the same,
1114 : // completely give up (we could potentially move the mask in
1115 : // case the imagery is smaller)
1116 256 : else if (m_poMaskDS && m_bMaskInterleavedWithImagery &&
1117 0 : static_cast<GUIntBig>(nCompressedBufferSize) !=
1118 0 : panByteCounts[nStripOrTile])
1119 : {
1120 0 : GTiffDataset *poRootDS = m_poBaseDS ? m_poBaseDS : this;
1121 0 : if (!poRootDS->m_bKnownIncompatibleEdition &&
1122 0 : !poRootDS->m_bWriteKnownIncompatibleEdition)
1123 : {
1124 0 : ReportError(
1125 : CE_Warning, CPLE_AppDefined,
1126 : "A strile cannot be rewritten in place, which "
1127 : "invalidates the MASK_INTERLEAVED_WITH_IMAGERY "
1128 : "optimization.");
1129 0 : poRootDS->m_bKnownIncompatibleEdition = true;
1130 0 : poRootDS->m_bWriteKnownIncompatibleEdition = true;
1131 : }
1132 0 : bWriteLeader = false;
1133 0 : bWriteTrailer = false;
1134 0 : if (m_bLeaderSizeAsUInt4)
1135 : {
1136 : // If there was a valid leader, invalidat it
1137 0 : VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4,
1138 : SEEK_SET);
1139 : uint32_t nOldSize;
1140 0 : VSIFReadL(&nOldSize, 1, 4,
1141 : VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF)));
1142 0 : CPL_LSBPTR32(&nOldSize);
1143 0 : if (nOldSize == panByteCounts[nStripOrTile])
1144 : {
1145 0 : uint32_t nInvalidatedSize = 0;
1146 0 : VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4,
1147 : SEEK_SET);
1148 0 : VSI_TIFFWrite(m_hTIFF, &nInvalidatedSize,
1149 : sizeof(nInvalidatedSize));
1150 : }
1151 0 : }
1152 : }
1153 : else
1154 : {
1155 256 : bWriteAtEnd = false;
1156 : }
1157 : }
1158 : }
1159 : }
1160 32884 : if (bWriteLeader &&
1161 24419 : static_cast<GUIntBig>(nCompressedBufferSize) <= 0xFFFFFFFFU)
1162 : {
1163 : // cppcheck-suppress knownConditionTrueFalse
1164 24419 : if (bWriteAtEnd)
1165 : {
1166 24163 : VSI_TIFFSeek(m_hTIFF, 0, SEEK_END);
1167 : }
1168 : else
1169 : {
1170 : // If we rewrite an existing strile in place with an existing
1171 : // leader, check that the leader is valid, before rewriting it. And
1172 : // if it is not valid, then do not write the trailer, as we could
1173 : // corrupt other data.
1174 256 : VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4, SEEK_SET);
1175 : uint32_t nOldSize;
1176 256 : VSIFReadL(&nOldSize, 1, 4,
1177 : VSI_TIFFGetVSILFile(TIFFClientdata(m_hTIFF)));
1178 256 : CPL_LSBPTR32(&nOldSize);
1179 256 : bWriteLeader =
1180 256 : panByteCounts && nOldSize == panByteCounts[nStripOrTile];
1181 256 : bWriteTrailer = bWriteLeader;
1182 256 : VSI_TIFFSeek(m_hTIFF, panOffsets[nStripOrTile] - 4, SEEK_SET);
1183 : }
1184 : // cppcheck-suppress knownConditionTrueFalse
1185 24419 : if (bWriteLeader)
1186 : {
1187 24419 : uint32_t nSize = static_cast<uint32_t>(nCompressedBufferSize);
1188 24419 : CPL_LSBPTR32(&nSize);
1189 24419 : if (!VSI_TIFFWrite(m_hTIFF, &nSize, sizeof(nSize)))
1190 0 : m_bWriteError = true;
1191 : }
1192 : }
1193 : tmsize_t written;
1194 32884 : if (TIFFIsTiled(m_hTIFF))
1195 25556 : written = TIFFWriteRawTile(m_hTIFF, nStripOrTile, pabyCompressedBuffer,
1196 : nCompressedBufferSize);
1197 : else
1198 7328 : written = TIFFWriteRawStrip(m_hTIFF, nStripOrTile, pabyCompressedBuffer,
1199 : nCompressedBufferSize);
1200 32884 : if (written != nCompressedBufferSize)
1201 12 : m_bWriteError = true;
1202 32884 : if (bWriteTrailer &&
1203 24419 : static_cast<GUIntBig>(nCompressedBufferSize) <= 0xFFFFFFFFU)
1204 : {
1205 24419 : GByte abyLastBytes[4] = {};
1206 24419 : if (nCompressedBufferSize >= 4)
1207 24419 : memcpy(abyLastBytes,
1208 24419 : pabyCompressedBuffer + nCompressedBufferSize - 4, 4);
1209 : else
1210 0 : memcpy(abyLastBytes, pabyCompressedBuffer, nCompressedBufferSize);
1211 24419 : if (!VSI_TIFFWrite(m_hTIFF, abyLastBytes, 4))
1212 0 : m_bWriteError = true;
1213 : }
1214 32884 : }
1215 :
1216 : /************************************************************************/
1217 : /* WaitCompletionForJobIdx() */
1218 : /************************************************************************/
1219 :
1220 1575 : void GTiffDataset::WaitCompletionForJobIdx(int i)
1221 : {
1222 1575 : auto poMainDS = m_poBaseDS ? m_poBaseDS : this;
1223 1575 : auto poQueue = poMainDS->m_poCompressQueue.get();
1224 1575 : auto &oQueue = poMainDS->m_asQueueJobIdx;
1225 1575 : auto &asJobs = poMainDS->m_asCompressionJobs;
1226 1575 : auto &mutex = poMainDS->m_oCompressThreadPoolMutex;
1227 :
1228 1575 : CPLAssert(i >= 0 && static_cast<size_t>(i) < asJobs.size());
1229 1575 : CPLAssert(asJobs[i].nStripOrTile >= 0);
1230 1575 : CPLAssert(!oQueue.empty());
1231 :
1232 1575 : bool bHasWarned = false;
1233 : while (true)
1234 : {
1235 : bool bReady;
1236 : {
1237 2128 : std::lock_guard oLock(mutex);
1238 2128 : bReady = asJobs[i].bReady;
1239 : }
1240 2128 : if (!bReady)
1241 : {
1242 553 : if (!bHasWarned)
1243 : {
1244 261 : CPLDebug("GTIFF",
1245 : "Waiting for worker job to finish handling block %d",
1246 261 : asJobs[i].nStripOrTile);
1247 261 : bHasWarned = true;
1248 : }
1249 553 : poQueue->GetPool()->WaitEvent();
1250 : }
1251 : else
1252 : {
1253 1575 : break;
1254 : }
1255 553 : }
1256 :
1257 1575 : if (asJobs[i].nCompressedBufferSize)
1258 : {
1259 3150 : asJobs[i].poDS->WriteRawStripOrTile(asJobs[i].nStripOrTile,
1260 1575 : asJobs[i].pabyCompressedBuffer,
1261 1575 : asJobs[i].nCompressedBufferSize);
1262 : }
1263 1575 : asJobs[i].pabyCompressedBuffer = nullptr;
1264 1575 : asJobs[i].nBufferSize = 0;
1265 : {
1266 : // Likely useless, but makes Coverity happy
1267 1575 : std::lock_guard oLock(mutex);
1268 1575 : asJobs[i].bReady = false;
1269 : }
1270 1575 : asJobs[i].nStripOrTile = -1;
1271 1575 : oQueue.pop();
1272 1575 : }
1273 :
1274 : /************************************************************************/
1275 : /* WaitCompletionForBlock() */
1276 : /************************************************************************/
1277 :
1278 2242470 : void GTiffDataset::WaitCompletionForBlock(int nBlockId)
1279 : {
1280 2242470 : auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
1281 2228260 : : m_poCompressQueue.get();
1282 : // cppcheck-suppress constVariableReference
1283 2242470 : auto &oQueue = m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
1284 : // cppcheck-suppress constVariableReference
1285 2228260 : auto &asJobs =
1286 2242470 : m_poBaseDS ? m_poBaseDS->m_asCompressionJobs : m_asCompressionJobs;
1287 :
1288 2242470 : if (poQueue != nullptr && !oQueue.empty())
1289 : {
1290 1062 : for (int i = 0; i < static_cast<int>(asJobs.size()); ++i)
1291 : {
1292 885 : if (asJobs[i].poDS == this && asJobs[i].nStripOrTile == nBlockId)
1293 : {
1294 126 : while (!oQueue.empty() &&
1295 63 : !(asJobs[oQueue.front()].poDS == this &&
1296 63 : asJobs[oQueue.front()].nStripOrTile == nBlockId))
1297 : {
1298 0 : WaitCompletionForJobIdx(oQueue.front());
1299 : }
1300 63 : CPLAssert(!oQueue.empty() &&
1301 : asJobs[oQueue.front()].poDS == this &&
1302 : asJobs[oQueue.front()].nStripOrTile == nBlockId);
1303 63 : WaitCompletionForJobIdx(oQueue.front());
1304 : }
1305 : }
1306 : }
1307 2242470 : }
1308 :
1309 : /************************************************************************/
1310 : /* SubmitCompressionJob() */
1311 : /************************************************************************/
1312 :
1313 163449 : bool GTiffDataset::SubmitCompressionJob(int nStripOrTile, GByte *pabyData,
1314 : GPtrDiff_t cc, int nHeight)
1315 : {
1316 : /* -------------------------------------------------------------------- */
1317 : /* Should we do compression in a worker thread ? */
1318 : /* -------------------------------------------------------------------- */
1319 163449 : auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
1320 151164 : : m_poCompressQueue.get();
1321 :
1322 163044 : if (poQueue && m_nCompression == COMPRESSION_NONE)
1323 : {
1324 : // We don't do multi-threaded compression for uncompressed...
1325 : // but we must wait for other related compression tasks (e.g mask)
1326 : // to be completed
1327 0 : poQueue->WaitCompletion();
1328 :
1329 : // Flush remaining data
1330 : // cppcheck-suppress constVariableReference
1331 0 : auto &oQueue =
1332 0 : m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
1333 0 : while (!oQueue.empty())
1334 : {
1335 0 : WaitCompletionForJobIdx(oQueue.front());
1336 : }
1337 : }
1338 :
1339 : const auto SetupJob =
1340 120103 : [this, pabyData, cc, nHeight, nStripOrTile](GTiffCompressionJob &sJob)
1341 : {
1342 25977 : sJob.poDS = this;
1343 25977 : sJob.bTIFFIsBigEndian = CPL_TO_BOOL(TIFFIsBigEndian(m_hTIFF));
1344 25977 : sJob.pabyBuffer = static_cast<GByte *>(CPLRealloc(sJob.pabyBuffer, cc));
1345 25977 : memcpy(sJob.pabyBuffer, pabyData, cc);
1346 25977 : sJob.nBufferSize = cc;
1347 25977 : sJob.nHeight = nHeight;
1348 25977 : sJob.nStripOrTile = nStripOrTile;
1349 25977 : sJob.nPredictor = PREDICTOR_NONE;
1350 25977 : if (GTIFFSupportsPredictor(m_nCompression))
1351 : {
1352 16195 : TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &sJob.nPredictor);
1353 : }
1354 :
1355 25977 : sJob.pExtraSamples = nullptr;
1356 25977 : sJob.nExtraSampleCount = 0;
1357 25977 : TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &sJob.nExtraSampleCount,
1358 : &sJob.pExtraSamples);
1359 189021 : };
1360 :
1361 163044 : if (poQueue == nullptr || !(m_nCompression == COMPRESSION_ADOBE_DEFLATE ||
1362 806 : m_nCompression == COMPRESSION_LZW ||
1363 78 : m_nCompression == COMPRESSION_PACKBITS ||
1364 72 : m_nCompression == COMPRESSION_LZMA ||
1365 62 : m_nCompression == COMPRESSION_ZSTD ||
1366 52 : m_nCompression == COMPRESSION_LERC ||
1367 46 : m_nCompression == COMPRESSION_JXL ||
1368 28 : m_nCompression == COMPRESSION_WEBP ||
1369 18 : m_nCompression == COMPRESSION_JPEG))
1370 : {
1371 161469 : if (m_bBlockOrderRowMajor || m_bLeaderSizeAsUInt4 ||
1372 137367 : m_bTrailerRepeatedLast4BytesRepeated)
1373 : {
1374 : GTiffCompressionJob sJob;
1375 24289 : memset(&sJob, 0, sizeof(sJob));
1376 24289 : SetupJob(sJob);
1377 24402 : sJob.pszTmpFilename =
1378 24402 : CPLStrdup(CPLSPrintf("/vsimem/gtiff/%p", this));
1379 :
1380 24402 : ThreadCompressionFunc(&sJob);
1381 :
1382 24402 : if (sJob.nCompressedBufferSize)
1383 : {
1384 24402 : sJob.poDS->WriteRawStripOrTile(sJob.nStripOrTile,
1385 : sJob.pabyCompressedBuffer,
1386 : sJob.nCompressedBufferSize);
1387 : }
1388 :
1389 24402 : CPLFree(sJob.pabyBuffer);
1390 24402 : VSIUnlink(sJob.pszTmpFilename);
1391 24402 : CPLFree(sJob.pszTmpFilename);
1392 24402 : return sJob.nCompressedBufferSize > 0 && !m_bWriteError;
1393 : }
1394 :
1395 137180 : return false;
1396 : }
1397 :
1398 1575 : auto poMainDS = m_poBaseDS ? m_poBaseDS : this;
1399 1575 : auto &oQueue = poMainDS->m_asQueueJobIdx;
1400 1575 : auto &asJobs = poMainDS->m_asCompressionJobs;
1401 :
1402 1575 : int nNextCompressionJobAvail = -1;
1403 :
1404 1575 : if (oQueue.size() == asJobs.size())
1405 : {
1406 1443 : CPLAssert(!oQueue.empty());
1407 1443 : nNextCompressionJobAvail = oQueue.front();
1408 1443 : WaitCompletionForJobIdx(nNextCompressionJobAvail);
1409 : }
1410 : else
1411 : {
1412 132 : const int nJobs = static_cast<int>(asJobs.size());
1413 323 : for (int i = 0; i < nJobs; ++i)
1414 : {
1415 323 : if (asJobs[i].nBufferSize == 0)
1416 : {
1417 132 : nNextCompressionJobAvail = i;
1418 132 : break;
1419 : }
1420 : }
1421 : }
1422 1575 : CPLAssert(nNextCompressionJobAvail >= 0);
1423 :
1424 1575 : GTiffCompressionJob *psJob = &asJobs[nNextCompressionJobAvail];
1425 1575 : SetupJob(*psJob);
1426 1575 : poQueue->SubmitJob(ThreadCompressionFunc, psJob);
1427 1575 : oQueue.push(nNextCompressionJobAvail);
1428 :
1429 1575 : return true;
1430 : }
1431 :
1432 : /************************************************************************/
1433 : /* DiscardLsb() */
1434 : /************************************************************************/
1435 :
1436 272 : template <class T> bool MustNotDiscardLsb(T value, bool bHasNoData, T nodata)
1437 : {
1438 272 : return bHasNoData && value == nodata;
1439 : }
1440 :
1441 : template <>
1442 44 : bool MustNotDiscardLsb<float>(float value, bool bHasNoData, float nodata)
1443 : {
1444 44 : return (bHasNoData && value == nodata) || !std::isfinite(value);
1445 : }
1446 :
1447 : template <>
1448 44 : bool MustNotDiscardLsb<double>(double value, bool bHasNoData, double nodata)
1449 : {
1450 44 : return (bHasNoData && value == nodata) || !std::isfinite(value);
1451 : }
1452 :
1453 : template <class T> T AdjustValue(T value, uint64_t nRoundUpBitTest);
1454 :
1455 10 : template <class T> T AdjustValueInt(T value, uint64_t nRoundUpBitTest)
1456 : {
1457 10 : if (value >=
1458 10 : static_cast<T>(std::numeric_limits<T>::max() - (nRoundUpBitTest << 1)))
1459 0 : return static_cast<T>(value - (nRoundUpBitTest << 1));
1460 10 : return static_cast<T>(value + (nRoundUpBitTest << 1));
1461 : }
1462 :
1463 0 : template <> int8_t AdjustValue<int8_t>(int8_t value, uint64_t nRoundUpBitTest)
1464 : {
1465 0 : return AdjustValueInt(value, nRoundUpBitTest);
1466 : }
1467 :
1468 : template <>
1469 2 : uint8_t AdjustValue<uint8_t>(uint8_t value, uint64_t nRoundUpBitTest)
1470 : {
1471 2 : return AdjustValueInt(value, nRoundUpBitTest);
1472 : }
1473 :
1474 : template <>
1475 2 : int16_t AdjustValue<int16_t>(int16_t value, uint64_t nRoundUpBitTest)
1476 : {
1477 2 : return AdjustValueInt(value, nRoundUpBitTest);
1478 : }
1479 :
1480 : template <>
1481 2 : uint16_t AdjustValue<uint16_t>(uint16_t value, uint64_t nRoundUpBitTest)
1482 : {
1483 2 : return AdjustValueInt(value, nRoundUpBitTest);
1484 : }
1485 :
1486 : template <>
1487 2 : int32_t AdjustValue<int32_t>(int32_t value, uint64_t nRoundUpBitTest)
1488 : {
1489 2 : return AdjustValueInt(value, nRoundUpBitTest);
1490 : }
1491 :
1492 : template <>
1493 2 : uint32_t AdjustValue<uint32_t>(uint32_t value, uint64_t nRoundUpBitTest)
1494 : {
1495 2 : return AdjustValueInt(value, nRoundUpBitTest);
1496 : }
1497 :
1498 : template <>
1499 0 : int64_t AdjustValue<int64_t>(int64_t value, uint64_t nRoundUpBitTest)
1500 : {
1501 0 : return AdjustValueInt(value, nRoundUpBitTest);
1502 : }
1503 :
1504 : template <>
1505 0 : uint64_t AdjustValue<uint64_t>(uint64_t value, uint64_t nRoundUpBitTest)
1506 : {
1507 0 : return AdjustValueInt(value, nRoundUpBitTest);
1508 : }
1509 :
1510 0 : template <> float AdjustValue<float>(float value, uint64_t)
1511 : {
1512 0 : return std::nextafter(value, std::numeric_limits<float>::max());
1513 : }
1514 :
1515 0 : template <> double AdjustValue<double>(double value, uint64_t)
1516 : {
1517 0 : return std::nextafter(value, std::numeric_limits<double>::max());
1518 : }
1519 :
1520 : template <class Teffective, class T>
1521 : T RoundValueDiscardLsb(const void *ptr, uint64_t nMask,
1522 : uint64_t nRoundUpBitTest);
1523 :
1524 : template <class T>
1525 16 : T RoundValueDiscardLsbUnsigned(const void *ptr, uint64_t nMask,
1526 : uint64_t nRoundUpBitTest)
1527 : {
1528 32 : if ((*reinterpret_cast<const T *>(ptr) & nMask) >
1529 16 : static_cast<uint64_t>(std::numeric_limits<T>::max()) -
1530 16 : (nRoundUpBitTest << 1U))
1531 : {
1532 4 : return static_cast<T>(std::numeric_limits<T>::max() & nMask);
1533 : }
1534 12 : const uint64_t newval =
1535 12 : (*reinterpret_cast<const T *>(ptr) & nMask) + (nRoundUpBitTest << 1U);
1536 12 : return static_cast<T>(newval);
1537 : }
1538 :
1539 : template <class T>
1540 18 : T RoundValueDiscardLsbSigned(const void *ptr, uint64_t nMask,
1541 : uint64_t nRoundUpBitTest)
1542 : {
1543 18 : T oldval = *reinterpret_cast<const T *>(ptr);
1544 18 : if (oldval < 0)
1545 : {
1546 4 : return static_cast<T>(oldval & nMask);
1547 : }
1548 14 : const uint64_t newval =
1549 14 : (*reinterpret_cast<const T *>(ptr) & nMask) + (nRoundUpBitTest << 1U);
1550 14 : if (newval > static_cast<uint64_t>(std::numeric_limits<T>::max()))
1551 4 : return static_cast<T>(std::numeric_limits<T>::max() & nMask);
1552 10 : return static_cast<T>(newval);
1553 : }
1554 :
1555 : template <>
1556 11 : uint16_t RoundValueDiscardLsb<uint16_t, uint16_t>(const void *ptr,
1557 : uint64_t nMask,
1558 : uint64_t nRoundUpBitTest)
1559 : {
1560 11 : return RoundValueDiscardLsbUnsigned<uint16_t>(ptr, nMask, nRoundUpBitTest);
1561 : }
1562 :
1563 : template <>
1564 5 : uint32_t RoundValueDiscardLsb<uint32_t, uint32_t>(const void *ptr,
1565 : uint64_t nMask,
1566 : uint64_t nRoundUpBitTest)
1567 : {
1568 5 : return RoundValueDiscardLsbUnsigned<uint32_t>(ptr, nMask, nRoundUpBitTest);
1569 : }
1570 :
1571 : template <>
1572 0 : uint64_t RoundValueDiscardLsb<uint64_t, uint64_t>(const void *ptr,
1573 : uint64_t nMask,
1574 : uint64_t nRoundUpBitTest)
1575 : {
1576 0 : return RoundValueDiscardLsbUnsigned<uint64_t>(ptr, nMask, nRoundUpBitTest);
1577 : }
1578 :
1579 : template <>
1580 0 : int8_t RoundValueDiscardLsb<int8_t, int8_t>(const void *ptr, uint64_t nMask,
1581 : uint64_t nRoundUpBitTest)
1582 : {
1583 0 : return RoundValueDiscardLsbSigned<int8_t>(ptr, nMask, nRoundUpBitTest);
1584 : }
1585 :
1586 : template <>
1587 13 : int16_t RoundValueDiscardLsb<int16_t, int16_t>(const void *ptr, uint64_t nMask,
1588 : uint64_t nRoundUpBitTest)
1589 : {
1590 13 : return RoundValueDiscardLsbSigned<int16_t>(ptr, nMask, nRoundUpBitTest);
1591 : }
1592 :
1593 : template <>
1594 5 : int32_t RoundValueDiscardLsb<int32_t, int32_t>(const void *ptr, uint64_t nMask,
1595 : uint64_t nRoundUpBitTest)
1596 : {
1597 5 : return RoundValueDiscardLsbSigned<int32_t>(ptr, nMask, nRoundUpBitTest);
1598 : }
1599 :
1600 : template <>
1601 0 : int64_t RoundValueDiscardLsb<int64_t, int64_t>(const void *ptr, uint64_t nMask,
1602 : uint64_t nRoundUpBitTest)
1603 : {
1604 0 : return RoundValueDiscardLsbSigned<int64_t>(ptr, nMask, nRoundUpBitTest);
1605 : }
1606 :
1607 : template <>
1608 0 : uint32_t RoundValueDiscardLsb<float, uint32_t>(const void *ptr, uint64_t nMask,
1609 : uint64_t nRoundUpBitTest)
1610 : {
1611 0 : return RoundValueDiscardLsbUnsigned<uint32_t>(ptr, nMask, nRoundUpBitTest);
1612 : }
1613 :
1614 : template <>
1615 0 : uint64_t RoundValueDiscardLsb<double, uint64_t>(const void *ptr, uint64_t nMask,
1616 : uint64_t nRoundUpBitTest)
1617 : {
1618 0 : return RoundValueDiscardLsbUnsigned<uint64_t>(ptr, nMask, nRoundUpBitTest);
1619 : }
1620 :
1621 : template <class Teffective, class T>
1622 145 : static void DiscardLsbT(GByte *pabyBuffer, size_t nBytes, int iBand, int nBands,
1623 : uint16_t nPlanarConfig,
1624 : const GTiffDataset::MaskOffset *panMaskOffsetLsb,
1625 : bool bHasNoData, Teffective nNoDataValue)
1626 : {
1627 : static_assert(sizeof(Teffective) == sizeof(T),
1628 : "sizeof(Teffective) == sizeof(T)");
1629 145 : if (nPlanarConfig == PLANARCONFIG_SEPARATE)
1630 : {
1631 98 : const auto nMask = panMaskOffsetLsb[iBand].nMask;
1632 98 : const auto nRoundUpBitTest = panMaskOffsetLsb[iBand].nRoundUpBitTest;
1633 196 : for (size_t i = 0; i < nBytes / sizeof(T); ++i)
1634 : {
1635 98 : if (MustNotDiscardLsb(reinterpret_cast<Teffective *>(pabyBuffer)[i],
1636 : bHasNoData, nNoDataValue))
1637 : {
1638 22 : continue;
1639 : }
1640 :
1641 76 : if (reinterpret_cast<T *>(pabyBuffer)[i] & nRoundUpBitTest)
1642 : {
1643 30 : reinterpret_cast<T *>(pabyBuffer)[i] =
1644 15 : RoundValueDiscardLsb<Teffective, T>(
1645 15 : &(reinterpret_cast<T *>(pabyBuffer)[i]), nMask,
1646 : nRoundUpBitTest);
1647 : }
1648 : else
1649 : {
1650 61 : reinterpret_cast<T *>(pabyBuffer)[i] = static_cast<T>(
1651 61 : reinterpret_cast<T *>(pabyBuffer)[i] & nMask);
1652 : }
1653 :
1654 : // Make sure that by discarding LSB we don't end up to a value
1655 : // that is no the nodata value
1656 76 : if (MustNotDiscardLsb(reinterpret_cast<Teffective *>(pabyBuffer)[i],
1657 : bHasNoData, nNoDataValue))
1658 : {
1659 8 : reinterpret_cast<Teffective *>(pabyBuffer)[i] =
1660 4 : AdjustValue(nNoDataValue, nRoundUpBitTest);
1661 : }
1662 : }
1663 : }
1664 : else
1665 : {
1666 94 : for (size_t i = 0; i < nBytes / sizeof(T); i += nBands)
1667 : {
1668 147 : for (int j = 0; j < nBands; ++j)
1669 : {
1670 100 : if (MustNotDiscardLsb(
1671 100 : reinterpret_cast<Teffective *>(pabyBuffer)[i + j],
1672 : bHasNoData, nNoDataValue))
1673 : {
1674 14 : continue;
1675 : }
1676 :
1677 86 : if (reinterpret_cast<T *>(pabyBuffer)[i + j] &
1678 86 : panMaskOffsetLsb[j].nRoundUpBitTest)
1679 : {
1680 38 : reinterpret_cast<T *>(pabyBuffer)[i + j] =
1681 19 : RoundValueDiscardLsb<Teffective, T>(
1682 19 : &(reinterpret_cast<T *>(pabyBuffer)[i + j]),
1683 19 : panMaskOffsetLsb[j].nMask,
1684 19 : panMaskOffsetLsb[j].nRoundUpBitTest);
1685 : }
1686 : else
1687 : {
1688 67 : reinterpret_cast<T *>(pabyBuffer)[i + j] = static_cast<T>(
1689 67 : (reinterpret_cast<T *>(pabyBuffer)[i + j] &
1690 67 : panMaskOffsetLsb[j].nMask));
1691 : }
1692 :
1693 : // Make sure that by discarding LSB we don't end up to a value
1694 : // that is no the nodata value
1695 86 : if (MustNotDiscardLsb(
1696 86 : reinterpret_cast<Teffective *>(pabyBuffer)[i + j],
1697 : bHasNoData, nNoDataValue))
1698 : {
1699 8 : reinterpret_cast<Teffective *>(pabyBuffer)[i + j] =
1700 4 : AdjustValue(nNoDataValue,
1701 4 : panMaskOffsetLsb[j].nRoundUpBitTest);
1702 : }
1703 : }
1704 : }
1705 : }
1706 145 : }
1707 :
1708 183 : static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand,
1709 : int nBands, uint16_t nSampleFormat,
1710 : uint16_t nBitsPerSample, uint16_t nPlanarConfig,
1711 : const GTiffDataset::MaskOffset *panMaskOffsetLsb,
1712 : bool bHasNoData, double dfNoDataValue)
1713 : {
1714 183 : if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_UINT)
1715 : {
1716 38 : uint8_t nNoDataValue = 0;
1717 47 : if (bHasNoData &&
1718 9 : dfNoDataValue >= std::numeric_limits<uint8_t>::min() &&
1719 56 : dfNoDataValue <= std::numeric_limits<uint8_t>::max() &&
1720 9 : dfNoDataValue == static_cast<uint8_t>(dfNoDataValue))
1721 : {
1722 6 : nNoDataValue = static_cast<uint8_t>(dfNoDataValue);
1723 : }
1724 : else
1725 : {
1726 32 : bHasNoData = false;
1727 : }
1728 38 : if (nPlanarConfig == PLANARCONFIG_SEPARATE)
1729 : {
1730 25 : const auto nMask =
1731 25 : static_cast<unsigned>(panMaskOffsetLsb[iBand].nMask);
1732 25 : const auto nRoundUpBitTest =
1733 25 : static_cast<unsigned>(panMaskOffsetLsb[iBand].nRoundUpBitTest);
1734 50 : for (decltype(nBytes) i = 0; i < nBytes; ++i)
1735 : {
1736 25 : if (bHasNoData && pabyBuffer[i] == nNoDataValue)
1737 3 : continue;
1738 :
1739 : // Keep 255 in case it is alpha.
1740 22 : if (pabyBuffer[i] != 255)
1741 : {
1742 21 : if (pabyBuffer[i] & nRoundUpBitTest)
1743 5 : pabyBuffer[i] = static_cast<GByte>(
1744 5 : std::min(255U, (pabyBuffer[i] & nMask) +
1745 5 : (nRoundUpBitTest << 1U)));
1746 : else
1747 16 : pabyBuffer[i] =
1748 16 : static_cast<GByte>(pabyBuffer[i] & nMask);
1749 :
1750 : // Make sure that by discarding LSB we don't end up to a
1751 : // value that is no the nodata value
1752 21 : if (bHasNoData && pabyBuffer[i] == nNoDataValue)
1753 2 : pabyBuffer[i] =
1754 1 : AdjustValue(nNoDataValue, nRoundUpBitTest);
1755 : }
1756 : }
1757 : }
1758 : else
1759 : {
1760 26 : for (decltype(nBytes) i = 0; i < nBytes; i += nBands)
1761 : {
1762 42 : for (int j = 0; j < nBands; ++j)
1763 : {
1764 29 : if (bHasNoData && pabyBuffer[i + j] == nNoDataValue)
1765 2 : continue;
1766 :
1767 : // Keep 255 in case it is alpha.
1768 27 : if (pabyBuffer[i + j] != 255)
1769 : {
1770 25 : if (pabyBuffer[i + j] &
1771 25 : panMaskOffsetLsb[j].nRoundUpBitTest)
1772 : {
1773 6 : pabyBuffer[i + j] = static_cast<GByte>(std::min(
1774 12 : 255U,
1775 6 : (pabyBuffer[i + j] &
1776 : static_cast<unsigned>(
1777 6 : panMaskOffsetLsb[j].nMask)) +
1778 : (static_cast<unsigned>(
1779 6 : panMaskOffsetLsb[j].nRoundUpBitTest)
1780 6 : << 1U)));
1781 : }
1782 : else
1783 : {
1784 19 : pabyBuffer[i + j] = static_cast<GByte>(
1785 19 : pabyBuffer[i + j] & panMaskOffsetLsb[j].nMask);
1786 : }
1787 :
1788 : // Make sure that by discarding LSB we don't end up to a
1789 : // value that is no the nodata value
1790 25 : if (bHasNoData && pabyBuffer[i + j] == nNoDataValue)
1791 1 : pabyBuffer[i + j] = AdjustValue(
1792 : nNoDataValue,
1793 1 : panMaskOffsetLsb[j].nRoundUpBitTest);
1794 : }
1795 : }
1796 : }
1797 38 : }
1798 : }
1799 145 : else if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_INT)
1800 : {
1801 0 : int8_t nNoDataValue = 0;
1802 0 : if (bHasNoData && dfNoDataValue >= std::numeric_limits<int8_t>::min() &&
1803 0 : dfNoDataValue <= std::numeric_limits<int8_t>::max() &&
1804 0 : dfNoDataValue == static_cast<int8_t>(dfNoDataValue))
1805 : {
1806 0 : nNoDataValue = static_cast<int8_t>(dfNoDataValue);
1807 : }
1808 : else
1809 : {
1810 0 : bHasNoData = false;
1811 : }
1812 0 : DiscardLsbT<int8_t, int8_t>(pabyBuffer, nBytes, iBand, nBands,
1813 : nPlanarConfig, panMaskOffsetLsb, bHasNoData,
1814 0 : nNoDataValue);
1815 : }
1816 145 : else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_INT)
1817 : {
1818 48 : int16_t nNoDataValue = 0;
1819 57 : if (bHasNoData &&
1820 9 : dfNoDataValue >= std::numeric_limits<int16_t>::min() &&
1821 66 : dfNoDataValue <= std::numeric_limits<int16_t>::max() &&
1822 9 : dfNoDataValue == static_cast<int16_t>(dfNoDataValue))
1823 : {
1824 6 : nNoDataValue = static_cast<int16_t>(dfNoDataValue);
1825 : }
1826 : else
1827 : {
1828 42 : bHasNoData = false;
1829 : }
1830 48 : DiscardLsbT<int16_t, int16_t>(pabyBuffer, nBytes, iBand, nBands,
1831 : nPlanarConfig, panMaskOffsetLsb,
1832 48 : bHasNoData, nNoDataValue);
1833 : }
1834 97 : else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_UINT)
1835 : {
1836 33 : uint16_t nNoDataValue = 0;
1837 42 : if (bHasNoData &&
1838 9 : dfNoDataValue >= std::numeric_limits<uint16_t>::min() &&
1839 51 : dfNoDataValue <= std::numeric_limits<uint16_t>::max() &&
1840 9 : dfNoDataValue == static_cast<uint16_t>(dfNoDataValue))
1841 : {
1842 6 : nNoDataValue = static_cast<uint16_t>(dfNoDataValue);
1843 : }
1844 : else
1845 : {
1846 27 : bHasNoData = false;
1847 : }
1848 33 : DiscardLsbT<uint16_t, uint16_t>(pabyBuffer, nBytes, iBand, nBands,
1849 : nPlanarConfig, panMaskOffsetLsb,
1850 33 : bHasNoData, nNoDataValue);
1851 : }
1852 64 : else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_INT)
1853 : {
1854 13 : int32_t nNoDataValue = 0;
1855 22 : if (bHasNoData &&
1856 9 : dfNoDataValue >= std::numeric_limits<int32_t>::min() &&
1857 31 : dfNoDataValue <= std::numeric_limits<int32_t>::max() &&
1858 9 : dfNoDataValue == static_cast<int32_t>(dfNoDataValue))
1859 : {
1860 6 : nNoDataValue = static_cast<int32_t>(dfNoDataValue);
1861 : }
1862 : else
1863 : {
1864 7 : bHasNoData = false;
1865 : }
1866 13 : DiscardLsbT<int32_t, int32_t>(pabyBuffer, nBytes, iBand, nBands,
1867 : nPlanarConfig, panMaskOffsetLsb,
1868 13 : bHasNoData, nNoDataValue);
1869 : }
1870 51 : else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_UINT)
1871 : {
1872 13 : uint32_t nNoDataValue = 0;
1873 22 : if (bHasNoData &&
1874 9 : dfNoDataValue >= std::numeric_limits<uint32_t>::min() &&
1875 31 : dfNoDataValue <= std::numeric_limits<uint32_t>::max() &&
1876 9 : dfNoDataValue == static_cast<uint32_t>(dfNoDataValue))
1877 : {
1878 6 : nNoDataValue = static_cast<uint32_t>(dfNoDataValue);
1879 : }
1880 : else
1881 : {
1882 7 : bHasNoData = false;
1883 : }
1884 13 : DiscardLsbT<uint32_t, uint32_t>(pabyBuffer, nBytes, iBand, nBands,
1885 : nPlanarConfig, panMaskOffsetLsb,
1886 13 : bHasNoData, nNoDataValue);
1887 : }
1888 38 : else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_INT)
1889 : {
1890 : // FIXME: we should not rely on dfNoDataValue when we support native
1891 : // data type for nodata
1892 0 : int64_t nNoDataValue = 0;
1893 0 : if (bHasNoData &&
1894 : dfNoDataValue >=
1895 0 : static_cast<double>(std::numeric_limits<int64_t>::min()) &&
1896 : dfNoDataValue <=
1897 0 : static_cast<double>(std::numeric_limits<int64_t>::max()) &&
1898 : dfNoDataValue ==
1899 0 : static_cast<double>(static_cast<int64_t>(dfNoDataValue)))
1900 : {
1901 0 : nNoDataValue = static_cast<int64_t>(dfNoDataValue);
1902 : }
1903 : else
1904 : {
1905 0 : bHasNoData = false;
1906 : }
1907 0 : DiscardLsbT<int64_t, int64_t>(pabyBuffer, nBytes, iBand, nBands,
1908 : nPlanarConfig, panMaskOffsetLsb,
1909 0 : bHasNoData, nNoDataValue);
1910 : }
1911 38 : else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_UINT)
1912 : {
1913 : // FIXME: we should not rely on dfNoDataValue when we support native
1914 : // data type for nodata
1915 0 : uint64_t nNoDataValue = 0;
1916 0 : if (bHasNoData &&
1917 : dfNoDataValue >=
1918 0 : static_cast<double>(std::numeric_limits<uint64_t>::min()) &&
1919 : dfNoDataValue <=
1920 0 : static_cast<double>(std::numeric_limits<uint64_t>::max()) &&
1921 : dfNoDataValue ==
1922 0 : static_cast<double>(static_cast<uint64_t>(dfNoDataValue)))
1923 : {
1924 0 : nNoDataValue = static_cast<uint64_t>(dfNoDataValue);
1925 : }
1926 : else
1927 : {
1928 0 : bHasNoData = false;
1929 : }
1930 0 : DiscardLsbT<uint64_t, uint64_t>(pabyBuffer, nBytes, iBand, nBands,
1931 : nPlanarConfig, panMaskOffsetLsb,
1932 0 : bHasNoData, nNoDataValue);
1933 : }
1934 38 : else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
1935 : {
1936 19 : float fNoDataValue = static_cast<float>(dfNoDataValue);
1937 19 : DiscardLsbT<float, uint32_t>(pabyBuffer, nBytes, iBand, nBands,
1938 : nPlanarConfig, panMaskOffsetLsb,
1939 19 : bHasNoData, fNoDataValue);
1940 : }
1941 19 : else if (nBitsPerSample == 64 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
1942 : {
1943 19 : DiscardLsbT<double, uint64_t>(pabyBuffer, nBytes, iBand, nBands,
1944 : nPlanarConfig, panMaskOffsetLsb,
1945 : bHasNoData, dfNoDataValue);
1946 : }
1947 183 : }
1948 :
1949 183 : void GTiffDataset::DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes,
1950 : int iBand) const
1951 : {
1952 183 : ::DiscardLsb(pabyBuffer, nBytes, iBand, nBands, m_nSampleFormat,
1953 183 : m_nBitsPerSample, m_nPlanarConfig, m_panMaskOffsetLsb,
1954 183 : m_bNoDataSet, m_dfNoDataValue);
1955 183 : }
1956 :
1957 : /************************************************************************/
1958 : /* WriteEncodedTileOrStrip() */
1959 : /************************************************************************/
1960 :
1961 179988 : CPLErr GTiffDataset::WriteEncodedTileOrStrip(uint32_t tile_or_strip, void *data,
1962 : int bPreserveDataBuffer)
1963 : {
1964 179988 : CPLErr eErr = CE_None;
1965 :
1966 179988 : if (TIFFIsTiled(m_hTIFF))
1967 : {
1968 46809 : if (!(WriteEncodedTile(tile_or_strip, static_cast<GByte *>(data),
1969 : bPreserveDataBuffer)))
1970 : {
1971 14 : eErr = CE_Failure;
1972 : }
1973 : }
1974 : else
1975 : {
1976 133052 : if (!(WriteEncodedStrip(tile_or_strip, static_cast<GByte *>(data),
1977 : bPreserveDataBuffer)))
1978 : {
1979 8 : eErr = CE_Failure;
1980 : }
1981 : }
1982 :
1983 180420 : return eErr;
1984 : }
1985 :
1986 : /************************************************************************/
1987 : /* FlushBlockBuf() */
1988 : /************************************************************************/
1989 :
1990 8094 : CPLErr GTiffDataset::FlushBlockBuf()
1991 :
1992 : {
1993 8094 : if (m_nLoadedBlock < 0 || !m_bLoadedBlockDirty)
1994 0 : return CE_None;
1995 :
1996 8094 : m_bLoadedBlockDirty = false;
1997 :
1998 : const CPLErr eErr =
1999 8094 : WriteEncodedTileOrStrip(m_nLoadedBlock, m_pabyBlockBuf, true);
2000 8094 : if (eErr != CE_None)
2001 : {
2002 0 : ReportError(CE_Failure, CPLE_AppDefined,
2003 : "WriteEncodedTile/Strip() failed.");
2004 0 : m_bWriteError = true;
2005 : }
2006 :
2007 8094 : return eErr;
2008 : }
2009 :
2010 : /************************************************************************/
2011 : /* GTiffFillStreamableOffsetAndCount() */
2012 : /************************************************************************/
2013 :
2014 8 : static void GTiffFillStreamableOffsetAndCount(TIFF *hTIFF, int nSize)
2015 : {
2016 8 : uint32_t nXSize = 0;
2017 8 : uint32_t nYSize = 0;
2018 8 : TIFFGetField(hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize);
2019 8 : TIFFGetField(hTIFF, TIFFTAG_IMAGELENGTH, &nYSize);
2020 8 : const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(hTIFF));
2021 : const int nBlockCount =
2022 8 : bIsTiled ? TIFFNumberOfTiles(hTIFF) : TIFFNumberOfStrips(hTIFF);
2023 :
2024 8 : toff_t *panOffset = nullptr;
2025 8 : TIFFGetField(hTIFF, bIsTiled ? TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS,
2026 : &panOffset);
2027 8 : toff_t *panSize = nullptr;
2028 8 : TIFFGetField(hTIFF,
2029 : bIsTiled ? TIFFTAG_TILEBYTECOUNTS : TIFFTAG_STRIPBYTECOUNTS,
2030 : &panSize);
2031 8 : toff_t nOffset = nSize;
2032 : // Trick to avoid clang static analyzer raising false positive about
2033 : // divide by zero later.
2034 8 : int nBlocksPerBand = 1;
2035 8 : uint32_t nRowsPerStrip = 0;
2036 8 : if (!bIsTiled)
2037 : {
2038 6 : TIFFGetField(hTIFF, TIFFTAG_ROWSPERSTRIP, &nRowsPerStrip);
2039 6 : if (nRowsPerStrip > static_cast<uint32_t>(nYSize))
2040 0 : nRowsPerStrip = nYSize;
2041 6 : nBlocksPerBand = DIV_ROUND_UP(nYSize, nRowsPerStrip);
2042 : }
2043 2947 : for (int i = 0; i < nBlockCount; ++i)
2044 : {
2045 : GPtrDiff_t cc = bIsTiled
2046 2939 : ? static_cast<GPtrDiff_t>(TIFFTileSize(hTIFF))
2047 2907 : : static_cast<GPtrDiff_t>(TIFFStripSize(hTIFF));
2048 2939 : if (!bIsTiled)
2049 : {
2050 : /* --------------------------------------------------------------------
2051 : */
2052 : /* If this is the last strip in the image, and is partial, then
2053 : */
2054 : /* we need to trim the number of scanlines written to the */
2055 : /* amount of valid data we have. (#2748) */
2056 : /* --------------------------------------------------------------------
2057 : */
2058 2907 : int nStripWithinBand = i % nBlocksPerBand;
2059 2907 : if (nStripWithinBand * nRowsPerStrip > nYSize - nRowsPerStrip)
2060 : {
2061 1 : cc = (cc / nRowsPerStrip) *
2062 1 : (nYSize - nStripWithinBand * nRowsPerStrip);
2063 : }
2064 : }
2065 2939 : panOffset[i] = nOffset;
2066 2939 : panSize[i] = cc;
2067 2939 : nOffset += cc;
2068 : }
2069 8 : }
2070 :
2071 : /************************************************************************/
2072 : /* Crystalize() */
2073 : /* */
2074 : /* Make sure that the directory information is written out for */
2075 : /* a new file, require before writing any imagery data. */
2076 : /************************************************************************/
2077 :
2078 2464210 : void GTiffDataset::Crystalize()
2079 :
2080 : {
2081 2464210 : if (m_bCrystalized)
2082 2461150 : return;
2083 :
2084 : // TODO: libtiff writes extended tags in the order they are specified
2085 : // and not in increasing order.
2086 3060 : WriteMetadata(this, m_hTIFF, true, m_eProfile, m_pszFilename,
2087 3060 : m_papszCreationOptions);
2088 3031 : WriteGeoTIFFInfo();
2089 3031 : if (m_bNoDataSet)
2090 235 : WriteNoDataValue(m_hTIFF, m_dfNoDataValue);
2091 2796 : else if (m_bNoDataSetAsInt64)
2092 1 : WriteNoDataValue(m_hTIFF, m_nNoDataValueInt64);
2093 2795 : else if (m_bNoDataSetAsUInt64)
2094 1 : WriteNoDataValue(m_hTIFF, m_nNoDataValueUInt64);
2095 :
2096 3031 : m_bMetadataChanged = false;
2097 3031 : m_bGeoTIFFInfoChanged = false;
2098 3031 : m_bNoDataChanged = false;
2099 3031 : m_bNeedsRewrite = false;
2100 :
2101 3031 : m_bCrystalized = true;
2102 :
2103 3031 : TIFFWriteCheck(m_hTIFF, TIFFIsTiled(m_hTIFF), "GTiffDataset::Crystalize");
2104 :
2105 3031 : TIFFWriteDirectory(m_hTIFF);
2106 3031 : if (m_bStreamingOut)
2107 : {
2108 : // We need to write twice the directory to be sure that custom
2109 : // TIFF tags are correctly sorted and that padding bytes have been
2110 : // added.
2111 3 : TIFFSetDirectory(m_hTIFF, 0);
2112 3 : TIFFWriteDirectory(m_hTIFF);
2113 :
2114 3 : if (VSIFSeekL(m_fpL, 0, SEEK_END) != 0)
2115 : {
2116 0 : ReportError(CE_Failure, CPLE_FileIO, "Could not seek");
2117 : }
2118 3 : const int nSize = static_cast<int>(VSIFTellL(m_fpL));
2119 :
2120 3 : TIFFSetDirectory(m_hTIFF, 0);
2121 3 : GTiffFillStreamableOffsetAndCount(m_hTIFF, nSize);
2122 3 : TIFFWriteDirectory(m_hTIFF);
2123 :
2124 3 : vsi_l_offset nDataLength = 0;
2125 : void *pabyBuffer =
2126 3 : VSIGetMemFileBuffer(m_pszTmpFilename, &nDataLength, FALSE);
2127 3 : if (static_cast<int>(VSIFWriteL(
2128 3 : pabyBuffer, 1, static_cast<int>(nDataLength), m_fpToWrite)) !=
2129 : static_cast<int>(nDataLength))
2130 : {
2131 0 : ReportError(CE_Failure, CPLE_FileIO, "Could not write %d bytes",
2132 : static_cast<int>(nDataLength));
2133 : }
2134 : // In case of single strip file, there's a libtiff check that would
2135 : // issue a warning since the file hasn't the required size.
2136 3 : CPLPushErrorHandler(CPLQuietErrorHandler);
2137 3 : TIFFSetDirectory(m_hTIFF, 0);
2138 3 : CPLPopErrorHandler();
2139 : }
2140 : else
2141 : {
2142 3028 : TIFFSetDirectory(
2143 3028 : m_hTIFF, static_cast<tdir_t>(TIFFNumberOfDirectories(m_hTIFF) - 1));
2144 : }
2145 :
2146 3031 : RestoreVolatileParameters(m_hTIFF);
2147 :
2148 3031 : m_nDirOffset = TIFFCurrentDirOffset(m_hTIFF);
2149 : }
2150 :
2151 : /************************************************************************/
2152 : /* FlushCache() */
2153 : /* */
2154 : /* We override this so we can also flush out local tiff strip */
2155 : /* cache if need be. */
2156 : /************************************************************************/
2157 :
2158 3233 : CPLErr GTiffDataset::FlushCache(bool bAtClosing)
2159 :
2160 : {
2161 3233 : return FlushCacheInternal(bAtClosing, true);
2162 : }
2163 :
2164 28965 : CPLErr GTiffDataset::FlushCacheInternal(bool bAtClosing, bool bFlushDirectory)
2165 : {
2166 28965 : if (m_bIsFinalized)
2167 2 : return CE_None;
2168 :
2169 28963 : CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
2170 :
2171 28963 : if (m_bLoadedBlockDirty && m_nLoadedBlock != -1)
2172 : {
2173 242 : if (FlushBlockBuf() != CE_None)
2174 0 : eErr = CE_Failure;
2175 : }
2176 :
2177 28963 : CPLFree(m_pabyBlockBuf);
2178 28963 : m_pabyBlockBuf = nullptr;
2179 28963 : m_nLoadedBlock = -1;
2180 28963 : m_bLoadedBlockDirty = false;
2181 :
2182 : // Finish compression
2183 28963 : auto poQueue = m_poBaseDS ? m_poBaseDS->m_poCompressQueue.get()
2184 27028 : : m_poCompressQueue.get();
2185 28963 : if (poQueue)
2186 : {
2187 159 : poQueue->WaitCompletion();
2188 :
2189 : // Flush remaining data
2190 : // cppcheck-suppress constVariableReference
2191 :
2192 159 : auto &oQueue =
2193 159 : m_poBaseDS ? m_poBaseDS->m_asQueueJobIdx : m_asQueueJobIdx;
2194 228 : while (!oQueue.empty())
2195 : {
2196 69 : WaitCompletionForJobIdx(oQueue.front());
2197 : }
2198 : }
2199 :
2200 28963 : if (bFlushDirectory && GetAccess() == GA_Update)
2201 : {
2202 8562 : if (FlushDirectory() != CE_None)
2203 12 : eErr = CE_Failure;
2204 : }
2205 28963 : return eErr;
2206 : }
2207 :
2208 : /************************************************************************/
2209 : /* FlushDirectory() */
2210 : /************************************************************************/
2211 :
2212 14021 : CPLErr GTiffDataset::FlushDirectory()
2213 :
2214 : {
2215 14021 : CPLErr eErr = CE_None;
2216 :
2217 427 : const auto ReloadAllOtherDirectories = [this]()
2218 : {
2219 209 : const auto poBaseDS = m_poBaseDS ? m_poBaseDS : this;
2220 209 : if (poBaseDS->m_papoOverviewDS)
2221 : {
2222 12 : for (int i = 0; i < poBaseDS->m_nOverviewCount; ++i)
2223 : {
2224 3 : if (poBaseDS->m_papoOverviewDS[i]->m_bCrystalized &&
2225 3 : poBaseDS->m_papoOverviewDS[i] != this)
2226 : {
2227 3 : poBaseDS->m_papoOverviewDS[i]->ReloadDirectory(true);
2228 : }
2229 :
2230 3 : if (poBaseDS->m_papoOverviewDS[i]->m_poMaskDS &&
2231 0 : poBaseDS->m_papoOverviewDS[i]->m_poMaskDS != this &&
2232 0 : poBaseDS->m_papoOverviewDS[i]->m_poMaskDS->m_bCrystalized)
2233 : {
2234 0 : poBaseDS->m_papoOverviewDS[i]->m_poMaskDS->ReloadDirectory(
2235 : true);
2236 : }
2237 : }
2238 : }
2239 209 : if (poBaseDS->m_poMaskDS && poBaseDS->m_poMaskDS != this &&
2240 0 : poBaseDS->m_poMaskDS->m_bCrystalized)
2241 : {
2242 0 : poBaseDS->m_poMaskDS->ReloadDirectory(true);
2243 : }
2244 209 : if (poBaseDS->m_bCrystalized && poBaseDS != this)
2245 : {
2246 6 : poBaseDS->ReloadDirectory(true);
2247 : }
2248 14230 : };
2249 :
2250 14021 : if (eAccess == GA_Update)
2251 : {
2252 9962 : if (m_bMetadataChanged)
2253 : {
2254 119 : m_bNeedsRewrite =
2255 238 : WriteMetadata(this, m_hTIFF, true, m_eProfile, m_pszFilename,
2256 119 : m_papszCreationOptions);
2257 119 : m_bMetadataChanged = false;
2258 :
2259 119 : if (m_bForceUnsetRPC)
2260 : {
2261 5 : double *padfRPCTag = nullptr;
2262 : uint16_t nCount;
2263 5 : if (TIFFGetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT, &nCount,
2264 5 : &padfRPCTag))
2265 : {
2266 3 : std::vector<double> zeroes(92);
2267 3 : TIFFSetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT, 92,
2268 : zeroes.data());
2269 3 : TIFFUnsetField(m_hTIFF, TIFFTAG_RPCCOEFFICIENT);
2270 3 : m_bNeedsRewrite = true;
2271 : }
2272 :
2273 5 : GDALWriteRPCTXTFile(m_pszFilename, nullptr);
2274 5 : GDALWriteRPBFile(m_pszFilename, nullptr);
2275 : }
2276 : }
2277 :
2278 9962 : if (m_bGeoTIFFInfoChanged)
2279 : {
2280 107 : WriteGeoTIFFInfo();
2281 107 : m_bGeoTIFFInfoChanged = false;
2282 : }
2283 :
2284 9962 : if (m_bNoDataChanged)
2285 : {
2286 17 : if (m_bNoDataSet)
2287 : {
2288 16 : WriteNoDataValue(m_hTIFF, m_dfNoDataValue);
2289 : }
2290 1 : else if (m_bNoDataSetAsInt64)
2291 : {
2292 0 : WriteNoDataValue(m_hTIFF, m_nNoDataValueInt64);
2293 : }
2294 1 : else if (m_bNoDataSetAsUInt64)
2295 : {
2296 0 : WriteNoDataValue(m_hTIFF, m_nNoDataValueUInt64);
2297 : }
2298 : else
2299 : {
2300 1 : UnsetNoDataValue(m_hTIFF);
2301 : }
2302 17 : m_bNeedsRewrite = true;
2303 17 : m_bNoDataChanged = false;
2304 : }
2305 :
2306 9962 : if (m_bNeedsRewrite)
2307 : {
2308 231 : if (!m_bCrystalized)
2309 : {
2310 25 : Crystalize();
2311 : }
2312 : else
2313 : {
2314 206 : const TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(m_hTIFF);
2315 :
2316 206 : m_nDirOffset = pfnSizeProc(TIFFClientdata(m_hTIFF));
2317 206 : if ((m_nDirOffset % 2) == 1)
2318 48 : ++m_nDirOffset;
2319 :
2320 206 : if (TIFFRewriteDirectory(m_hTIFF) == 0)
2321 0 : eErr = CE_Failure;
2322 :
2323 206 : TIFFSetSubDirectory(m_hTIFF, m_nDirOffset);
2324 :
2325 206 : ReloadAllOtherDirectories();
2326 :
2327 206 : if (m_bLayoutIFDSBeforeData && m_bBlockOrderRowMajor &&
2328 1 : m_bLeaderSizeAsUInt4 &&
2329 1 : m_bTrailerRepeatedLast4BytesRepeated &&
2330 1 : !m_bKnownIncompatibleEdition &&
2331 1 : !m_bWriteKnownIncompatibleEdition)
2332 : {
2333 1 : ReportError(CE_Warning, CPLE_AppDefined,
2334 : "The IFD has been rewritten at the end of "
2335 : "the file, which breaks COG layout.");
2336 1 : m_bKnownIncompatibleEdition = true;
2337 1 : m_bWriteKnownIncompatibleEdition = true;
2338 : }
2339 : }
2340 :
2341 231 : m_bNeedsRewrite = false;
2342 : }
2343 : }
2344 :
2345 : // There are some circumstances in which we can reach this point
2346 : // without having made this our directory (SetDirectory()) in which
2347 : // case we should not risk a flush.
2348 23983 : if (GetAccess() == GA_Update &&
2349 9962 : TIFFCurrentDirOffset(m_hTIFF) == m_nDirOffset)
2350 : {
2351 9962 : const TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(m_hTIFF);
2352 :
2353 9962 : toff_t nNewDirOffset = pfnSizeProc(TIFFClientdata(m_hTIFF));
2354 9962 : if ((nNewDirOffset % 2) == 1)
2355 2861 : ++nNewDirOffset;
2356 :
2357 9962 : if (TIFFFlush(m_hTIFF) == 0)
2358 12 : eErr = CE_Failure;
2359 :
2360 9962 : if (m_nDirOffset != TIFFCurrentDirOffset(m_hTIFF))
2361 : {
2362 3 : m_nDirOffset = nNewDirOffset;
2363 3 : ReloadAllOtherDirectories();
2364 3 : CPLDebug("GTiff",
2365 : "directory moved during flush in FlushDirectory()");
2366 : }
2367 : }
2368 :
2369 14021 : SetDirectory();
2370 14021 : return eErr;
2371 : }
2372 :
2373 : /************************************************************************/
2374 : /* CleanOverviews() */
2375 : /************************************************************************/
2376 :
2377 3 : CPLErr GTiffDataset::CleanOverviews()
2378 :
2379 : {
2380 3 : CPLAssert(!m_poBaseDS);
2381 :
2382 3 : ScanDirectories();
2383 :
2384 3 : FlushDirectory();
2385 :
2386 : /* -------------------------------------------------------------------- */
2387 : /* Cleanup overviews objects, and get offsets to all overview */
2388 : /* directories. */
2389 : /* -------------------------------------------------------------------- */
2390 6 : std::vector<toff_t> anOvDirOffsets;
2391 :
2392 6 : for (int i = 0; i < m_nOverviewCount; ++i)
2393 : {
2394 3 : anOvDirOffsets.push_back(m_papoOverviewDS[i]->m_nDirOffset);
2395 3 : if (m_papoOverviewDS[i]->m_poMaskDS)
2396 1 : anOvDirOffsets.push_back(
2397 1 : m_papoOverviewDS[i]->m_poMaskDS->m_nDirOffset);
2398 3 : delete m_papoOverviewDS[i];
2399 : }
2400 :
2401 : /* -------------------------------------------------------------------- */
2402 : /* Loop through all the directories, translating the offsets */
2403 : /* into indexes we can use with TIFFUnlinkDirectory(). */
2404 : /* -------------------------------------------------------------------- */
2405 6 : std::vector<uint16_t> anOvDirIndexes;
2406 3 : int iThisOffset = 1;
2407 :
2408 3 : TIFFSetDirectory(m_hTIFF, 0);
2409 :
2410 : while (true)
2411 : {
2412 20 : for (toff_t nOffset : anOvDirOffsets)
2413 : {
2414 12 : if (nOffset == TIFFCurrentDirOffset(m_hTIFF))
2415 : {
2416 4 : anOvDirIndexes.push_back(static_cast<uint16_t>(iThisOffset));
2417 : }
2418 : }
2419 :
2420 8 : if (TIFFLastDirectory(m_hTIFF))
2421 3 : break;
2422 :
2423 5 : TIFFReadDirectory(m_hTIFF);
2424 5 : ++iThisOffset;
2425 5 : }
2426 :
2427 : /* -------------------------------------------------------------------- */
2428 : /* Actually unlink the target directories. Note that we do */
2429 : /* this from last to first so as to avoid renumbering any of */
2430 : /* the earlier directories we need to remove. */
2431 : /* -------------------------------------------------------------------- */
2432 7 : while (!anOvDirIndexes.empty())
2433 : {
2434 4 : TIFFUnlinkDirectory(m_hTIFF, anOvDirIndexes.back());
2435 4 : anOvDirIndexes.pop_back();
2436 : }
2437 :
2438 3 : CPLFree(m_papoOverviewDS);
2439 3 : m_nOverviewCount = 0;
2440 3 : m_papoOverviewDS = nullptr;
2441 :
2442 3 : if (m_poMaskDS)
2443 : {
2444 1 : CPLFree(m_poMaskDS->m_papoOverviewDS);
2445 1 : m_poMaskDS->m_nOverviewCount = 0;
2446 1 : m_poMaskDS->m_papoOverviewDS = nullptr;
2447 : }
2448 :
2449 3 : if (!SetDirectory())
2450 0 : return CE_Failure;
2451 :
2452 3 : return CE_None;
2453 : }
2454 :
2455 : /************************************************************************/
2456 : /* RegisterNewOverviewDataset() */
2457 : /************************************************************************/
2458 :
2459 450 : CPLErr GTiffDataset::RegisterNewOverviewDataset(toff_t nOverviewOffset,
2460 : int l_nJpegQuality,
2461 : CSLConstList papszOptions)
2462 : {
2463 450 : if (m_nOverviewCount == 127)
2464 0 : return CE_Failure;
2465 :
2466 : const auto GetOptionValue =
2467 2700 : [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
2468 5399 : const char **ppszKeyUsed = nullptr)
2469 : {
2470 2700 : const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
2471 2700 : if (pszVal)
2472 : {
2473 1 : if (ppszKeyUsed)
2474 1 : *ppszKeyUsed = pszOptionKey;
2475 1 : return pszVal;
2476 : }
2477 2699 : pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey);
2478 2699 : if (pszVal)
2479 : {
2480 0 : if (ppszKeyUsed)
2481 0 : *ppszKeyUsed = pszConfigOptionKey;
2482 0 : return pszVal;
2483 : }
2484 2699 : pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
2485 2699 : if (pszVal && ppszKeyUsed)
2486 13 : *ppszKeyUsed = pszConfigOptionKey;
2487 2699 : return pszVal;
2488 450 : };
2489 :
2490 450 : int nZLevel = m_nZLevel;
2491 450 : if (const char *opt = GetOptionValue("ZLEVEL", "ZLEVEL_OVERVIEW"))
2492 : {
2493 4 : nZLevel = atoi(opt);
2494 : }
2495 :
2496 450 : int nZSTDLevel = m_nZSTDLevel;
2497 450 : if (const char *opt = GetOptionValue("ZSTD_LEVEL", "ZSTD_LEVEL_OVERVIEW"))
2498 : {
2499 4 : nZSTDLevel = atoi(opt);
2500 : }
2501 :
2502 450 : bool bWebpLossless = m_bWebPLossless;
2503 : const char *pszWebPLosslessOverview =
2504 450 : GetOptionValue("WEBP_LOSSLESS", "WEBP_LOSSLESS_OVERVIEW");
2505 450 : if (pszWebPLosslessOverview)
2506 : {
2507 2 : bWebpLossless = CPLTestBool(pszWebPLosslessOverview);
2508 : }
2509 :
2510 450 : int nWebpLevel = m_nWebPLevel;
2511 450 : const char *pszKeyWebpLevel = "";
2512 450 : if (const char *opt = GetOptionValue("WEBP_LEVEL", "WEBP_LEVEL_OVERVIEW",
2513 : &pszKeyWebpLevel))
2514 : {
2515 14 : if (pszWebPLosslessOverview == nullptr && m_bWebPLossless)
2516 : {
2517 1 : CPLDebug("GTiff",
2518 : "%s specified, but not WEBP_LOSSLESS_OVERVIEW. "
2519 : "Assuming WEBP_LOSSLESS_OVERVIEW=NO",
2520 : pszKeyWebpLevel);
2521 1 : bWebpLossless = false;
2522 : }
2523 13 : else if (bWebpLossless)
2524 : {
2525 0 : CPLError(CE_Warning, CPLE_AppDefined,
2526 : "%s is specified, but WEBP_LOSSLESS_OVERVIEW=YES. "
2527 : "%s will be ignored.",
2528 : pszKeyWebpLevel, pszKeyWebpLevel);
2529 : }
2530 14 : nWebpLevel = atoi(opt);
2531 : }
2532 :
2533 450 : double dfMaxZError = m_dfMaxZErrorOverview;
2534 450 : if (const char *opt = GetOptionValue("MAX_Z_ERROR", "MAX_Z_ERROR_OVERVIEW"))
2535 : {
2536 20 : dfMaxZError = CPLAtof(opt);
2537 : }
2538 :
2539 450 : GTiffDataset *poODS = new GTiffDataset();
2540 450 : poODS->ShareLockWithParentDataset(this);
2541 450 : poODS->m_pszFilename = CPLStrdup(m_pszFilename);
2542 450 : const char *pszSparseOK = GetOptionValue("SPARSE_OK", "SPARSE_OK_OVERVIEW");
2543 450 : if (pszSparseOK && CPLTestBool(pszSparseOK))
2544 : {
2545 1 : poODS->m_bWriteEmptyTiles = false;
2546 1 : poODS->m_bFillEmptyTilesAtClosing = false;
2547 : }
2548 : else
2549 : {
2550 449 : poODS->m_bWriteEmptyTiles = m_bWriteEmptyTiles;
2551 449 : poODS->m_bFillEmptyTilesAtClosing = m_bFillEmptyTilesAtClosing;
2552 : }
2553 450 : poODS->m_nJpegQuality = static_cast<signed char>(l_nJpegQuality);
2554 450 : poODS->m_nWebPLevel = static_cast<signed char>(nWebpLevel);
2555 450 : poODS->m_nZLevel = static_cast<signed char>(nZLevel);
2556 450 : poODS->m_nLZMAPreset = m_nLZMAPreset;
2557 450 : poODS->m_nZSTDLevel = static_cast<signed char>(nZSTDLevel);
2558 450 : poODS->m_bWebPLossless = bWebpLossless;
2559 450 : poODS->m_nJpegTablesMode = m_nJpegTablesMode;
2560 450 : poODS->m_dfMaxZError = dfMaxZError;
2561 450 : poODS->m_dfMaxZErrorOverview = dfMaxZError;
2562 450 : memcpy(poODS->m_anLercAddCompressionAndVersion,
2563 450 : m_anLercAddCompressionAndVersion,
2564 : sizeof(m_anLercAddCompressionAndVersion));
2565 : #ifdef HAVE_JXL
2566 450 : poODS->m_bJXLLossless = m_bJXLLossless;
2567 450 : poODS->m_fJXLDistance = m_fJXLDistance;
2568 450 : poODS->m_fJXLAlphaDistance = m_fJXLAlphaDistance;
2569 450 : poODS->m_nJXLEffort = m_nJXLEffort;
2570 : #endif
2571 :
2572 450 : if (poODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nOverviewOffset,
2573 450 : GA_Update) != CE_None)
2574 : {
2575 0 : delete poODS;
2576 0 : return CE_Failure;
2577 : }
2578 :
2579 : // Assign color interpretation from main dataset
2580 450 : const int l_nBands = GetRasterCount();
2581 1345 : for (int i = 1; i <= l_nBands; i++)
2582 : {
2583 895 : auto poBand = dynamic_cast<GTiffRasterBand *>(poODS->GetRasterBand(i));
2584 895 : if (poBand)
2585 895 : poBand->m_eBandInterp = GetRasterBand(i)->GetColorInterpretation();
2586 : }
2587 :
2588 : // Do that now that m_nCompression is set
2589 450 : poODS->RestoreVolatileParameters(poODS->m_hTIFF);
2590 :
2591 450 : ++m_nOverviewCount;
2592 450 : m_papoOverviewDS = static_cast<GTiffDataset **>(
2593 450 : CPLRealloc(m_papoOverviewDS, m_nOverviewCount * (sizeof(void *))));
2594 450 : m_papoOverviewDS[m_nOverviewCount - 1] = poODS;
2595 450 : poODS->m_poBaseDS = this;
2596 450 : poODS->m_bIsOverview = true;
2597 450 : return CE_None;
2598 : }
2599 :
2600 : /************************************************************************/
2601 : /* CreateTIFFColorTable() */
2602 : /************************************************************************/
2603 :
2604 12 : static void CreateTIFFColorTable(GDALColorTable *poColorTable, int nBits,
2605 : std::vector<unsigned short> &anTRed,
2606 : std::vector<unsigned short> &anTGreen,
2607 : std::vector<unsigned short> &anTBlue,
2608 : unsigned short *&panRed,
2609 : unsigned short *&panGreen,
2610 : unsigned short *&panBlue)
2611 : {
2612 : int nColors;
2613 :
2614 12 : if (nBits == 8)
2615 12 : nColors = 256;
2616 0 : else if (nBits < 8)
2617 0 : nColors = 1 << nBits;
2618 : else
2619 0 : nColors = 65536;
2620 :
2621 12 : anTRed.resize(nColors, 0);
2622 12 : anTGreen.resize(nColors, 0);
2623 12 : anTBlue.resize(nColors, 0);
2624 :
2625 3084 : for (int iColor = 0; iColor < nColors; ++iColor)
2626 : {
2627 3072 : if (iColor < poColorTable->GetColorEntryCount())
2628 : {
2629 : GDALColorEntry sRGB;
2630 :
2631 3072 : poColorTable->GetColorEntryAsRGB(iColor, &sRGB);
2632 :
2633 3072 : anTRed[iColor] = static_cast<unsigned short>(257 * sRGB.c1);
2634 3072 : anTGreen[iColor] = static_cast<unsigned short>(257 * sRGB.c2);
2635 3072 : anTBlue[iColor] = static_cast<unsigned short>(257 * sRGB.c3);
2636 : }
2637 : else
2638 : {
2639 0 : anTRed[iColor] = 0;
2640 0 : anTGreen[iColor] = 0;
2641 0 : anTBlue[iColor] = 0;
2642 : }
2643 : }
2644 :
2645 12 : panRed = &(anTRed[0]);
2646 12 : panGreen = &(anTGreen[0]);
2647 12 : panBlue = &(anTBlue[0]);
2648 12 : }
2649 :
2650 : /************************************************************************/
2651 : /* GetOverviewParameters() */
2652 : /************************************************************************/
2653 :
2654 275 : bool GTiffDataset::GetOverviewParameters(
2655 : int &nCompression, uint16_t &nPlanarConfig, uint16_t &nPredictor,
2656 : uint16_t &nPhotometric, int &nOvrJpegQuality, std::string &osNoData,
2657 : uint16_t *&panExtraSampleValues, uint16_t &nExtraSamples,
2658 : CSLConstList papszOptions) const
2659 : {
2660 : const auto GetOptionValue =
2661 908 : [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
2662 1812 : const char **ppszKeyUsed = nullptr)
2663 : {
2664 908 : const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
2665 908 : if (pszVal)
2666 : {
2667 4 : if (ppszKeyUsed)
2668 4 : *ppszKeyUsed = pszOptionKey;
2669 4 : return pszVal;
2670 : }
2671 904 : pszVal = CSLFetchNameValue(papszOptions, pszConfigOptionKey);
2672 904 : if (pszVal)
2673 : {
2674 4 : if (ppszKeyUsed)
2675 4 : *ppszKeyUsed = pszConfigOptionKey;
2676 4 : return pszVal;
2677 : }
2678 900 : pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
2679 900 : if (pszVal && ppszKeyUsed)
2680 44 : *ppszKeyUsed = pszConfigOptionKey;
2681 900 : return pszVal;
2682 275 : };
2683 :
2684 : /* -------------------------------------------------------------------- */
2685 : /* Determine compression method. */
2686 : /* -------------------------------------------------------------------- */
2687 275 : nCompression = m_nCompression;
2688 275 : const char *pszOptionKey = "";
2689 : const char *pszCompressValue =
2690 275 : GetOptionValue("COMPRESS", "COMPRESS_OVERVIEW", &pszOptionKey);
2691 275 : if (pszCompressValue != nullptr)
2692 : {
2693 42 : nCompression =
2694 42 : GTIFFGetCompressionMethod(pszCompressValue, pszOptionKey);
2695 42 : if (nCompression < 0)
2696 : {
2697 0 : nCompression = m_nCompression;
2698 : }
2699 : }
2700 :
2701 : /* -------------------------------------------------------------------- */
2702 : /* Determine planar configuration. */
2703 : /* -------------------------------------------------------------------- */
2704 275 : nPlanarConfig = m_nPlanarConfig;
2705 275 : if (nCompression == COMPRESSION_WEBP)
2706 : {
2707 11 : nPlanarConfig = PLANARCONFIG_CONTIG;
2708 : }
2709 : const char *pszInterleave =
2710 275 : GetOptionValue("INTERLEAVE", "INTERLEAVE_OVERVIEW", &pszOptionKey);
2711 275 : if (pszInterleave != nullptr && pszInterleave[0] != '\0')
2712 : {
2713 2 : if (EQUAL(pszInterleave, "PIXEL"))
2714 1 : nPlanarConfig = PLANARCONFIG_CONTIG;
2715 1 : else if (EQUAL(pszInterleave, "BAND"))
2716 1 : nPlanarConfig = PLANARCONFIG_SEPARATE;
2717 : else
2718 : {
2719 0 : CPLError(CE_Warning, CPLE_AppDefined,
2720 : "%s=%s unsupported, "
2721 : "value must be PIXEL or BAND. ignoring",
2722 : pszOptionKey, pszInterleave);
2723 : }
2724 : }
2725 :
2726 : /* -------------------------------------------------------------------- */
2727 : /* Determine predictor tag */
2728 : /* -------------------------------------------------------------------- */
2729 275 : nPredictor = PREDICTOR_NONE;
2730 275 : if (GTIFFSupportsPredictor(nCompression))
2731 : {
2732 : const char *pszPredictor =
2733 56 : GetOptionValue("PREDICTOR", "PREDICTOR_OVERVIEW");
2734 56 : if (pszPredictor != nullptr)
2735 : {
2736 1 : nPredictor = static_cast<uint16_t>(atoi(pszPredictor));
2737 : }
2738 55 : else if (GTIFFSupportsPredictor(m_nCompression))
2739 55 : TIFFGetField(m_hTIFF, TIFFTAG_PREDICTOR, &nPredictor);
2740 : }
2741 :
2742 : /* -------------------------------------------------------------------- */
2743 : /* Determine photometric tag */
2744 : /* -------------------------------------------------------------------- */
2745 275 : nPhotometric = m_nPhotometric;
2746 : const char *pszPhotometric =
2747 275 : GetOptionValue("PHOTOMETRIC", "PHOTOMETRIC_OVERVIEW", &pszOptionKey);
2748 275 : if (!GTIFFUpdatePhotometric(pszPhotometric, pszOptionKey, nCompression,
2749 275 : pszInterleave, nBands, nPhotometric,
2750 : nPlanarConfig))
2751 : {
2752 0 : return false;
2753 : }
2754 :
2755 : /* -------------------------------------------------------------------- */
2756 : /* Determine JPEG quality */
2757 : /* -------------------------------------------------------------------- */
2758 275 : nOvrJpegQuality = m_nJpegQuality;
2759 275 : if (nCompression == COMPRESSION_JPEG)
2760 : {
2761 : const char *pszJPEGQuality =
2762 27 : GetOptionValue("JPEG_QUALITY", "JPEG_QUALITY_OVERVIEW");
2763 27 : if (pszJPEGQuality != nullptr)
2764 : {
2765 9 : nOvrJpegQuality = atoi(pszJPEGQuality);
2766 : }
2767 : }
2768 :
2769 : /* -------------------------------------------------------------------- */
2770 : /* Set nodata. */
2771 : /* -------------------------------------------------------------------- */
2772 275 : if (m_bNoDataSet)
2773 : {
2774 16 : osNoData = GTiffFormatGDALNoDataTagValue(m_dfNoDataValue);
2775 : }
2776 :
2777 : /* -------------------------------------------------------------------- */
2778 : /* Fetch extra sample tag */
2779 : /* -------------------------------------------------------------------- */
2780 275 : panExtraSampleValues = nullptr;
2781 275 : nExtraSamples = 0;
2782 275 : if (TIFFGetField(m_hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples,
2783 275 : &panExtraSampleValues))
2784 : {
2785 : uint16_t *panExtraSampleValuesNew = static_cast<uint16_t *>(
2786 36 : CPLMalloc(nExtraSamples * sizeof(uint16_t)));
2787 36 : memcpy(panExtraSampleValuesNew, panExtraSampleValues,
2788 36 : nExtraSamples * sizeof(uint16_t));
2789 36 : panExtraSampleValues = panExtraSampleValuesNew;
2790 : }
2791 : else
2792 : {
2793 239 : panExtraSampleValues = nullptr;
2794 239 : nExtraSamples = 0;
2795 : }
2796 :
2797 275 : return true;
2798 : }
2799 :
2800 : /************************************************************************/
2801 : /* CreateOverviewsFromSrcOverviews() */
2802 : /************************************************************************/
2803 :
2804 : // If poOvrDS is not null, it is used and poSrcDS is ignored.
2805 :
2806 55 : CPLErr GTiffDataset::CreateOverviewsFromSrcOverviews(GDALDataset *poSrcDS,
2807 : GDALDataset *poOvrDS,
2808 : int nOverviews)
2809 : {
2810 55 : CPLAssert(poSrcDS->GetRasterCount() != 0);
2811 55 : CPLAssert(m_nOverviewCount == 0);
2812 :
2813 55 : ScanDirectories();
2814 :
2815 55 : FlushDirectory();
2816 :
2817 55 : int nOvBitsPerSample = m_nBitsPerSample;
2818 :
2819 : /* -------------------------------------------------------------------- */
2820 : /* Do we need some metadata for the overviews? */
2821 : /* -------------------------------------------------------------------- */
2822 110 : CPLString osMetadata;
2823 :
2824 55 : GTIFFBuildOverviewMetadata("NONE", this, osMetadata);
2825 :
2826 : int nCompression;
2827 : uint16_t nPlanarConfig;
2828 : uint16_t nPredictor;
2829 : uint16_t nPhotometric;
2830 : int nOvrJpegQuality;
2831 110 : std::string osNoData;
2832 55 : uint16_t *panExtraSampleValues = nullptr;
2833 55 : uint16_t nExtraSamples = 0;
2834 55 : if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
2835 : nPhotometric, nOvrJpegQuality, osNoData,
2836 : panExtraSampleValues, nExtraSamples,
2837 : /*papszOptions=*/nullptr))
2838 : {
2839 0 : return CE_Failure;
2840 : }
2841 :
2842 : /* -------------------------------------------------------------------- */
2843 : /* Do we have a palette? If so, create a TIFF compatible version. */
2844 : /* -------------------------------------------------------------------- */
2845 110 : std::vector<unsigned short> anTRed;
2846 110 : std::vector<unsigned short> anTGreen;
2847 55 : std::vector<unsigned short> anTBlue;
2848 55 : unsigned short *panRed = nullptr;
2849 55 : unsigned short *panGreen = nullptr;
2850 55 : unsigned short *panBlue = nullptr;
2851 :
2852 55 : if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
2853 : {
2854 0 : CreateTIFFColorTable(m_poColorTable, nOvBitsPerSample, anTRed, anTGreen,
2855 : anTBlue, panRed, panGreen, panBlue);
2856 : }
2857 :
2858 55 : int nOvrBlockXSize = 0;
2859 55 : int nOvrBlockYSize = 0;
2860 55 : GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
2861 : &nOvrBlockXSize, &nOvrBlockYSize);
2862 :
2863 55 : CPLErr eErr = CE_None;
2864 :
2865 163 : for (int i = 0; i < nOverviews && eErr == CE_None; ++i)
2866 : {
2867 : GDALRasterBand *poOvrBand =
2868 143 : poOvrDS ? ((i == 0) ? poOvrDS->GetRasterBand(1)
2869 35 : : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
2870 43 : : poSrcDS->GetRasterBand(1)->GetOverview(i);
2871 :
2872 108 : int nOXSize = poOvrBand->GetXSize();
2873 108 : int nOYSize = poOvrBand->GetYSize();
2874 :
2875 216 : toff_t nOverviewOffset = GTIFFWriteDirectory(
2876 : m_hTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize, nOvBitsPerSample,
2877 108 : nPlanarConfig, m_nSamplesPerPixel, nOvrBlockXSize, nOvrBlockYSize,
2878 108 : TRUE, nCompression, nPhotometric, m_nSampleFormat, nPredictor,
2879 : panRed, panGreen, panBlue, nExtraSamples, panExtraSampleValues,
2880 : osMetadata,
2881 108 : nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality) : nullptr,
2882 108 : CPLSPrintf("%d", m_nJpegTablesMode),
2883 2 : osNoData.empty() ? nullptr : osNoData.c_str(),
2884 108 : m_anLercAddCompressionAndVersion, m_bWriteCOGLayout);
2885 :
2886 108 : if (nOverviewOffset == 0)
2887 0 : eErr = CE_Failure;
2888 : else
2889 108 : eErr = RegisterNewOverviewDataset(nOverviewOffset, nOvrJpegQuality,
2890 : nullptr);
2891 : }
2892 :
2893 : // For directory reloading, so that the chaining to the next directory is
2894 : // reloaded, as well as compression parameters.
2895 55 : ReloadDirectory();
2896 :
2897 55 : CPLFree(panExtraSampleValues);
2898 55 : panExtraSampleValues = nullptr;
2899 :
2900 55 : return eErr;
2901 : }
2902 :
2903 : /************************************************************************/
2904 : /* CreateInternalMaskOverviews() */
2905 : /************************************************************************/
2906 :
2907 233 : CPLErr GTiffDataset::CreateInternalMaskOverviews(int nOvrBlockXSize,
2908 : int nOvrBlockYSize)
2909 : {
2910 233 : ScanDirectories();
2911 :
2912 : /* -------------------------------------------------------------------- */
2913 : /* Create overviews for the mask. */
2914 : /* -------------------------------------------------------------------- */
2915 233 : CPLErr eErr = CE_None;
2916 :
2917 233 : if (m_poMaskDS != nullptr && m_poMaskDS->GetRasterCount() == 1)
2918 : {
2919 : int nMaskOvrCompression;
2920 30 : if (strstr(GDALGetMetadataItem(GDALGetDriverByName("GTiff"),
2921 : GDAL_DMD_CREATIONOPTIONLIST, nullptr),
2922 30 : "<Value>DEFLATE</Value>") != nullptr)
2923 30 : nMaskOvrCompression = COMPRESSION_ADOBE_DEFLATE;
2924 : else
2925 0 : nMaskOvrCompression = COMPRESSION_PACKBITS;
2926 :
2927 87 : for (int i = 0; i < m_nOverviewCount; ++i)
2928 : {
2929 57 : if (m_papoOverviewDS[i]->m_poMaskDS == nullptr)
2930 : {
2931 90 : const toff_t nOverviewOffset = GTIFFWriteDirectory(
2932 : m_hTIFF, FILETYPE_REDUCEDIMAGE | FILETYPE_MASK,
2933 45 : m_papoOverviewDS[i]->nRasterXSize,
2934 45 : m_papoOverviewDS[i]->nRasterYSize, 1, PLANARCONFIG_CONTIG,
2935 : 1, nOvrBlockXSize, nOvrBlockYSize, TRUE,
2936 : nMaskOvrCompression, PHOTOMETRIC_MASK, SAMPLEFORMAT_UINT,
2937 : PREDICTOR_NONE, nullptr, nullptr, nullptr, 0, nullptr, "",
2938 45 : nullptr, nullptr, nullptr, nullptr, m_bWriteCOGLayout);
2939 :
2940 45 : if (nOverviewOffset == 0)
2941 : {
2942 0 : eErr = CE_Failure;
2943 0 : continue;
2944 : }
2945 :
2946 45 : GTiffDataset *poODS = new GTiffDataset();
2947 45 : poODS->ShareLockWithParentDataset(this);
2948 45 : poODS->m_pszFilename = CPLStrdup(m_pszFilename);
2949 45 : if (poODS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF),
2950 45 : nOverviewOffset, GA_Update) != CE_None)
2951 : {
2952 0 : delete poODS;
2953 0 : eErr = CE_Failure;
2954 : }
2955 : else
2956 : {
2957 45 : poODS->m_bPromoteTo8Bits = CPLTestBool(CPLGetConfigOption(
2958 : "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
2959 45 : poODS->m_poBaseDS = this;
2960 45 : poODS->m_poImageryDS = m_papoOverviewDS[i];
2961 45 : m_papoOverviewDS[i]->m_poMaskDS = poODS;
2962 45 : ++m_poMaskDS->m_nOverviewCount;
2963 90 : m_poMaskDS->m_papoOverviewDS =
2964 90 : static_cast<GTiffDataset **>(CPLRealloc(
2965 45 : m_poMaskDS->m_papoOverviewDS,
2966 45 : m_poMaskDS->m_nOverviewCount * (sizeof(void *))));
2967 45 : m_poMaskDS
2968 45 : ->m_papoOverviewDS[m_poMaskDS->m_nOverviewCount - 1] =
2969 : poODS;
2970 : }
2971 : }
2972 : }
2973 : }
2974 :
2975 233 : ReloadDirectory();
2976 :
2977 233 : return eErr;
2978 : }
2979 :
2980 : /************************************************************************/
2981 : /* IBuildOverviews() */
2982 : /************************************************************************/
2983 :
2984 363 : CPLErr GTiffDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
2985 : const int *panOverviewList, int nBandsIn,
2986 : const int *panBandList,
2987 : GDALProgressFunc pfnProgress,
2988 : void *pProgressData,
2989 : CSLConstList papszOptions)
2990 :
2991 : {
2992 363 : ScanDirectories();
2993 :
2994 : // Make implicit JPEG overviews invisible, but do not destroy
2995 : // them in case they are already used (not sure that the client
2996 : // has the right to do that. Behavior maybe undefined in GDAL API.
2997 363 : m_nJPEGOverviewCount = 0;
2998 :
2999 : /* -------------------------------------------------------------------- */
3000 : /* If RRD or external OVR overviews requested, then invoke */
3001 : /* generic handling. */
3002 : /* -------------------------------------------------------------------- */
3003 363 : bool bUseGenericHandling = false;
3004 :
3005 363 : if (CPLTestBool(CSLFetchNameValueDef(
3006 724 : papszOptions, "USE_RRD", CPLGetConfigOption("USE_RRD", "NO"))) ||
3007 361 : CPLTestBool(
3008 : CSLFetchNameValueDef(papszOptions, "TIFF_USE_OVR",
3009 : CPLGetConfigOption("TIFF_USE_OVR", "NO"))))
3010 : {
3011 2 : bUseGenericHandling = true;
3012 : }
3013 :
3014 : /* -------------------------------------------------------------------- */
3015 : /* If we don't have read access, then create the overviews */
3016 : /* externally. */
3017 : /* -------------------------------------------------------------------- */
3018 363 : if (GetAccess() != GA_Update)
3019 : {
3020 135 : CPLDebug("GTiff", "File open for read-only accessing, "
3021 : "creating overviews externally.");
3022 :
3023 135 : bUseGenericHandling = true;
3024 : }
3025 :
3026 363 : if (bUseGenericHandling)
3027 : {
3028 137 : if (m_nOverviewCount != 0)
3029 : {
3030 0 : ReportError(CE_Failure, CPLE_NotSupported,
3031 : "Cannot add external overviews when there are already "
3032 : "internal overviews");
3033 0 : return CE_Failure;
3034 : }
3035 :
3036 137 : CPLStringList aosOptions(papszOptions);
3037 137 : if (!m_bWriteEmptyTiles)
3038 : {
3039 1 : aosOptions.SetNameValue("SPARSE_OK", "YES");
3040 : }
3041 :
3042 137 : CPLErr eErr = GDALDataset::IBuildOverviews(
3043 : pszResampling, nOverviews, panOverviewList, nBandsIn, panBandList,
3044 137 : pfnProgress, pProgressData, aosOptions);
3045 137 : if (eErr == CE_None && m_poMaskDS)
3046 : {
3047 1 : ReportError(
3048 : CE_Warning, CPLE_NotSupported,
3049 : "Building external overviews whereas there is an internal "
3050 : "mask is not fully supported. "
3051 : "The overviews of the non-mask bands will be created, "
3052 : "but not the overviews of the mask band.");
3053 : }
3054 137 : return eErr;
3055 : }
3056 :
3057 : /* -------------------------------------------------------------------- */
3058 : /* Our TIFF overview support currently only works safely if all */
3059 : /* bands are handled at the same time. */
3060 : /* -------------------------------------------------------------------- */
3061 226 : if (nBandsIn != GetRasterCount())
3062 : {
3063 0 : ReportError(CE_Failure, CPLE_NotSupported,
3064 : "Generation of overviews in TIFF currently only "
3065 : "supported when operating on all bands. "
3066 : "Operation failed.");
3067 0 : return CE_Failure;
3068 : }
3069 :
3070 : /* -------------------------------------------------------------------- */
3071 : /* If zero overviews were requested, we need to clear all */
3072 : /* existing overviews. */
3073 : /* -------------------------------------------------------------------- */
3074 226 : if (nOverviews == 0)
3075 : {
3076 6 : if (m_nOverviewCount == 0)
3077 3 : return GDALDataset::IBuildOverviews(
3078 : pszResampling, nOverviews, panOverviewList, nBandsIn,
3079 3 : panBandList, pfnProgress, pProgressData, papszOptions);
3080 :
3081 3 : return CleanOverviews();
3082 : }
3083 :
3084 220 : CPLErr eErr = CE_None;
3085 :
3086 : /* -------------------------------------------------------------------- */
3087 : /* Initialize progress counter. */
3088 : /* -------------------------------------------------------------------- */
3089 220 : if (!pfnProgress(0.0, nullptr, pProgressData))
3090 : {
3091 0 : ReportError(CE_Failure, CPLE_UserInterrupt, "User terminated");
3092 0 : return CE_Failure;
3093 : }
3094 :
3095 220 : FlushDirectory();
3096 :
3097 : /* -------------------------------------------------------------------- */
3098 : /* If we are averaging bit data to grayscale we need to create */
3099 : /* 8bit overviews. */
3100 : /* -------------------------------------------------------------------- */
3101 220 : int nOvBitsPerSample = m_nBitsPerSample;
3102 :
3103 220 : if (STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
3104 2 : nOvBitsPerSample = 8;
3105 :
3106 : /* -------------------------------------------------------------------- */
3107 : /* Do we need some metadata for the overviews? */
3108 : /* -------------------------------------------------------------------- */
3109 440 : CPLString osMetadata;
3110 :
3111 220 : GTIFFBuildOverviewMetadata(pszResampling, this, osMetadata);
3112 :
3113 : int nCompression;
3114 : uint16_t nPlanarConfig;
3115 : uint16_t nPredictor;
3116 : uint16_t nPhotometric;
3117 : int nOvrJpegQuality;
3118 440 : std::string osNoData;
3119 220 : uint16_t *panExtraSampleValues = nullptr;
3120 220 : uint16_t nExtraSamples = 0;
3121 220 : if (!GetOverviewParameters(nCompression, nPlanarConfig, nPredictor,
3122 : nPhotometric, nOvrJpegQuality, osNoData,
3123 : panExtraSampleValues, nExtraSamples,
3124 : papszOptions))
3125 : {
3126 0 : return CE_Failure;
3127 : }
3128 :
3129 : /* -------------------------------------------------------------------- */
3130 : /* Do we have a palette? If so, create a TIFF compatible version. */
3131 : /* -------------------------------------------------------------------- */
3132 440 : std::vector<unsigned short> anTRed;
3133 440 : std::vector<unsigned short> anTGreen;
3134 440 : std::vector<unsigned short> anTBlue;
3135 220 : unsigned short *panRed = nullptr;
3136 220 : unsigned short *panGreen = nullptr;
3137 220 : unsigned short *panBlue = nullptr;
3138 :
3139 220 : if (nPhotometric == PHOTOMETRIC_PALETTE && m_poColorTable != nullptr)
3140 : {
3141 12 : CreateTIFFColorTable(m_poColorTable, nOvBitsPerSample, anTRed, anTGreen,
3142 : anTBlue, panRed, panGreen, panBlue);
3143 : }
3144 :
3145 : /* -------------------------------------------------------------------- */
3146 : /* Establish which of the overview levels we already have, and */
3147 : /* which are new. We assume that band 1 of the file is */
3148 : /* representative. */
3149 : /* -------------------------------------------------------------------- */
3150 220 : int nOvrBlockXSize = 0;
3151 220 : int nOvrBlockYSize = 0;
3152 220 : GTIFFGetOverviewBlockSize(GDALRasterBand::ToHandle(GetRasterBand(1)),
3153 : &nOvrBlockXSize, &nOvrBlockYSize);
3154 440 : std::vector<bool> abRequireNewOverview(nOverviews, true);
3155 616 : for (int i = 0; i < nOverviews && eErr == CE_None; ++i)
3156 : {
3157 718 : for (int j = 0; j < m_nOverviewCount && eErr == CE_None; ++j)
3158 : {
3159 376 : GTiffDataset *poODS = m_papoOverviewDS[j];
3160 :
3161 : const int nOvFactor =
3162 376 : GDALComputeOvFactor(poODS->GetRasterXSize(), GetRasterXSize(),
3163 : poODS->GetRasterYSize(), GetRasterYSize());
3164 :
3165 : // If we already have a 1x1 overview and this new one would result
3166 : // in it too, then don't create it.
3167 415 : if (poODS->GetRasterXSize() == 1 && poODS->GetRasterYSize() == 1 &&
3168 21 : (GetRasterXSize() + panOverviewList[i] - 1) /
3169 21 : panOverviewList[i] ==
3170 415 : 1 &&
3171 21 : (GetRasterYSize() + panOverviewList[i] - 1) /
3172 21 : panOverviewList[i] ==
3173 : 1)
3174 : {
3175 21 : abRequireNewOverview[i] = false;
3176 21 : break;
3177 : }
3178 :
3179 679 : if (nOvFactor == panOverviewList[i] ||
3180 324 : nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
3181 : GetRasterXSize(),
3182 : GetRasterYSize()))
3183 : {
3184 33 : abRequireNewOverview[i] = false;
3185 33 : break;
3186 : }
3187 : }
3188 :
3189 396 : if (abRequireNewOverview[i])
3190 : {
3191 342 : if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
3192 0 : !m_bWriteKnownIncompatibleEdition)
3193 : {
3194 0 : ReportError(CE_Warning, CPLE_AppDefined,
3195 : "Adding new overviews invalidates the "
3196 : "LAYOUT=IFDS_BEFORE_DATA property");
3197 0 : m_bKnownIncompatibleEdition = true;
3198 0 : m_bWriteKnownIncompatibleEdition = true;
3199 : }
3200 :
3201 342 : const int nOXSize = (GetRasterXSize() + panOverviewList[i] - 1) /
3202 342 : panOverviewList[i];
3203 342 : const int nOYSize = (GetRasterYSize() + panOverviewList[i] - 1) /
3204 342 : panOverviewList[i];
3205 :
3206 684 : const toff_t nOverviewOffset = GTIFFWriteDirectory(
3207 : m_hTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize,
3208 342 : nOvBitsPerSample, nPlanarConfig, m_nSamplesPerPixel,
3209 : nOvrBlockXSize, nOvrBlockYSize, TRUE, nCompression,
3210 342 : nPhotometric, m_nSampleFormat, nPredictor, panRed, panGreen,
3211 : panBlue, nExtraSamples, panExtraSampleValues, osMetadata,
3212 342 : nOvrJpegQuality >= 0 ? CPLSPrintf("%d", nOvrJpegQuality)
3213 : : nullptr,
3214 342 : CPLSPrintf("%d", m_nJpegTablesMode),
3215 24 : osNoData.empty() ? nullptr : osNoData.c_str(),
3216 342 : m_anLercAddCompressionAndVersion, false);
3217 :
3218 342 : if (nOverviewOffset == 0)
3219 0 : eErr = CE_Failure;
3220 : else
3221 342 : eErr = RegisterNewOverviewDataset(
3222 : nOverviewOffset, nOvrJpegQuality, papszOptions);
3223 : }
3224 : }
3225 :
3226 220 : CPLFree(panExtraSampleValues);
3227 220 : panExtraSampleValues = nullptr;
3228 :
3229 220 : ReloadDirectory();
3230 :
3231 : /* -------------------------------------------------------------------- */
3232 : /* Create overviews for the mask. */
3233 : /* -------------------------------------------------------------------- */
3234 220 : if (eErr != CE_None)
3235 0 : return eErr;
3236 :
3237 220 : eErr = CreateInternalMaskOverviews(nOvrBlockXSize, nOvrBlockYSize);
3238 :
3239 : /* -------------------------------------------------------------------- */
3240 : /* Refresh overviews for the mask */
3241 : /* -------------------------------------------------------------------- */
3242 : const bool bHasInternalMask =
3243 220 : m_poMaskDS != nullptr && m_poMaskDS->GetRasterCount() == 1;
3244 : const bool bHasExternalMask =
3245 220 : !bHasInternalMask && oOvManager.HaveMaskFile();
3246 220 : const bool bHasMask = bHasInternalMask || bHasExternalMask;
3247 :
3248 220 : if (bHasInternalMask)
3249 : {
3250 17 : int nMaskOverviews = 0;
3251 :
3252 : GDALRasterBand **papoOverviewBands = static_cast<GDALRasterBand **>(
3253 17 : CPLCalloc(sizeof(void *), m_nOverviewCount));
3254 50 : for (int i = 0; i < m_nOverviewCount; ++i)
3255 : {
3256 33 : if (m_papoOverviewDS[i]->m_poMaskDS != nullptr)
3257 : {
3258 33 : papoOverviewBands[nMaskOverviews++] =
3259 33 : m_papoOverviewDS[i]->m_poMaskDS->GetRasterBand(1);
3260 : }
3261 : }
3262 :
3263 34 : void *pScaledProgressData = GDALCreateScaledProgress(
3264 17 : 0, 1.0 / (nBands + 1), pfnProgress, pProgressData);
3265 34 : eErr = GDALRegenerateOverviewsEx(
3266 17 : m_poMaskDS->GetRasterBand(1), nMaskOverviews,
3267 : reinterpret_cast<GDALRasterBandH *>(papoOverviewBands),
3268 : pszResampling, GDALScaledProgress, pScaledProgressData,
3269 : papszOptions);
3270 17 : GDALDestroyScaledProgress(pScaledProgressData);
3271 17 : CPLFree(papoOverviewBands);
3272 : }
3273 203 : else if (bHasExternalMask)
3274 : {
3275 4 : void *pScaledProgressData = GDALCreateScaledProgress(
3276 2 : 0, 1.0 / (nBands + 1), pfnProgress, pProgressData);
3277 2 : eErr = oOvManager.BuildOverviewsMask(
3278 : pszResampling, nOverviews, panOverviewList, GDALScaledProgress,
3279 : pScaledProgressData, papszOptions);
3280 2 : GDALDestroyScaledProgress(pScaledProgressData);
3281 : }
3282 :
3283 : // If we have an alpha band, we want it to be generated before downsampling
3284 : // other bands
3285 220 : bool bHasAlphaBand = false;
3286 626 : for (int iBand = 0; iBand < nBands; iBand++)
3287 : {
3288 406 : if (papoBands[iBand]->GetColorInterpretation() == GCI_AlphaBand)
3289 15 : bHasAlphaBand = true;
3290 : }
3291 :
3292 : /* -------------------------------------------------------------------- */
3293 : /* Refresh old overviews that were listed. */
3294 : /* -------------------------------------------------------------------- */
3295 220 : const auto poColorTable = GetRasterBand(panBandList[0])->GetColorTable();
3296 15 : if ((m_nPlanarConfig == PLANARCONFIG_CONTIG || bHasAlphaBand) &&
3297 207 : GDALDataTypeIsComplex(
3298 207 : GetRasterBand(panBandList[0])->GetRasterDataType()) == FALSE &&
3299 12 : (poColorTable == nullptr || STARTS_WITH_CI(pszResampling, "NEAR") ||
3300 441 : poColorTable->IsIdentity()) &&
3301 199 : (STARTS_WITH_CI(pszResampling, "NEAR") ||
3302 112 : EQUAL(pszResampling, "AVERAGE") || EQUAL(pszResampling, "RMS") ||
3303 48 : EQUAL(pszResampling, "GAUSS") || EQUAL(pszResampling, "CUBIC") ||
3304 29 : EQUAL(pszResampling, "CUBICSPLINE") ||
3305 28 : EQUAL(pszResampling, "LANCZOS") || EQUAL(pszResampling, "BILINEAR") ||
3306 24 : EQUAL(pszResampling, "MODE")))
3307 : {
3308 : // In the case of pixel interleaved compressed overviews, we want to
3309 : // generate the overviews for all the bands block by block, and not
3310 : // band after band, in order to write the block once and not loose
3311 : // space in the TIFF file. We also use that logic for uncompressed
3312 : // overviews, since GDALRegenerateOverviewsMultiBand() will be able to
3313 : // trigger cascading overview regeneration even in the presence
3314 : // of an alpha band.
3315 :
3316 178 : int nNewOverviews = 0;
3317 :
3318 : GDALRasterBand ***papapoOverviewBands = static_cast<GDALRasterBand ***>(
3319 178 : CPLCalloc(sizeof(void *), nBandsIn));
3320 : GDALRasterBand **papoBandList =
3321 178 : static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nBandsIn));
3322 506 : for (int iBand = 0; iBand < nBandsIn; ++iBand)
3323 : {
3324 328 : GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
3325 :
3326 328 : papoBandList[iBand] = poBand;
3327 656 : papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
3328 328 : CPLCalloc(sizeof(void *), poBand->GetOverviewCount()));
3329 :
3330 328 : int iCurOverview = 0;
3331 : std::vector<bool> abAlreadyUsedOverviewBand(
3332 328 : poBand->GetOverviewCount(), false);
3333 :
3334 958 : for (int i = 0; i < nOverviews; ++i)
3335 : {
3336 1123 : for (int j = 0; j < poBand->GetOverviewCount(); ++j)
3337 : {
3338 1109 : if (abAlreadyUsedOverviewBand[j])
3339 493 : continue;
3340 :
3341 : int nOvFactor;
3342 616 : GDALRasterBand *poOverview = poBand->GetOverview(j);
3343 :
3344 616 : nOvFactor = GDALComputeOvFactor(
3345 : poOverview->GetXSize(), poBand->GetXSize(),
3346 : poOverview->GetYSize(), poBand->GetYSize());
3347 :
3348 616 : GDALCopyNoDataValue(poOverview, poBand);
3349 :
3350 625 : if (nOvFactor == panOverviewList[i] ||
3351 9 : nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
3352 : poBand->GetXSize(),
3353 : poBand->GetYSize()))
3354 : {
3355 616 : if (iBand == 0)
3356 : {
3357 : const auto osNewResampling =
3358 620 : GDALGetNormalizedOvrResampling(pszResampling);
3359 : const char *pszExistingResampling =
3360 310 : poOverview->GetMetadataItem("RESAMPLING");
3361 620 : if (pszExistingResampling &&
3362 310 : pszExistingResampling != osNewResampling)
3363 : {
3364 2 : poOverview->SetMetadataItem(
3365 2 : "RESAMPLING", osNewResampling.c_str());
3366 : }
3367 : }
3368 :
3369 616 : abAlreadyUsedOverviewBand[j] = true;
3370 616 : CPLAssert(iCurOverview < poBand->GetOverviewCount());
3371 616 : papapoOverviewBands[iBand][iCurOverview] = poOverview;
3372 616 : ++iCurOverview;
3373 616 : break;
3374 : }
3375 : }
3376 : }
3377 :
3378 328 : if (nNewOverviews == 0)
3379 : {
3380 178 : nNewOverviews = iCurOverview;
3381 : }
3382 150 : else if (nNewOverviews != iCurOverview)
3383 : {
3384 0 : CPLAssert(false);
3385 : return CE_Failure;
3386 : }
3387 : }
3388 :
3389 : void *pScaledProgressData =
3390 178 : bHasMask ? GDALCreateScaledProgress(1.0 / (nBands + 1), 1.0,
3391 : pfnProgress, pProgressData)
3392 159 : : GDALCreateScaledProgress(0.0, 1.0, pfnProgress,
3393 178 : pProgressData);
3394 178 : GDALRegenerateOverviewsMultiBand(nBandsIn, papoBandList, nNewOverviews,
3395 : papapoOverviewBands, pszResampling,
3396 : GDALScaledProgress,
3397 : pScaledProgressData, papszOptions);
3398 178 : GDALDestroyScaledProgress(pScaledProgressData);
3399 :
3400 506 : for (int iBand = 0; iBand < nBandsIn; ++iBand)
3401 : {
3402 328 : CPLFree(papapoOverviewBands[iBand]);
3403 : }
3404 178 : CPLFree(papapoOverviewBands);
3405 178 : CPLFree(papoBandList);
3406 : }
3407 : else
3408 : {
3409 : GDALRasterBand **papoOverviewBands = static_cast<GDALRasterBand **>(
3410 42 : CPLCalloc(sizeof(void *), nOverviews));
3411 :
3412 42 : const int iBandOffset = bHasMask ? 1 : 0;
3413 :
3414 120 : for (int iBand = 0; iBand < nBandsIn && eErr == CE_None; ++iBand)
3415 : {
3416 78 : GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
3417 78 : if (poBand == nullptr)
3418 : {
3419 0 : eErr = CE_Failure;
3420 0 : break;
3421 : }
3422 :
3423 : std::vector<bool> abAlreadyUsedOverviewBand(
3424 156 : poBand->GetOverviewCount(), false);
3425 :
3426 78 : int nNewOverviews = 0;
3427 216 : for (int i = 0; i < nOverviews; ++i)
3428 : {
3429 336 : for (int j = 0; j < poBand->GetOverviewCount(); ++j)
3430 : {
3431 315 : if (abAlreadyUsedOverviewBand[j])
3432 176 : continue;
3433 :
3434 139 : GDALRasterBand *poOverview = poBand->GetOverview(j);
3435 :
3436 139 : GDALCopyNoDataValue(poOverview, poBand);
3437 :
3438 139 : const int nOvFactor = GDALComputeOvFactor(
3439 : poOverview->GetXSize(), poBand->GetXSize(),
3440 : poOverview->GetYSize(), poBand->GetYSize());
3441 :
3442 171 : if (nOvFactor == panOverviewList[i] ||
3443 32 : nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
3444 : poBand->GetXSize(),
3445 : poBand->GetYSize()))
3446 : {
3447 117 : if (iBand == 0)
3448 : {
3449 : const auto osNewResampling =
3450 130 : GDALGetNormalizedOvrResampling(pszResampling);
3451 : const char *pszExistingResampling =
3452 65 : poOverview->GetMetadataItem("RESAMPLING");
3453 98 : if (pszExistingResampling &&
3454 33 : pszExistingResampling != osNewResampling)
3455 : {
3456 1 : poOverview->SetMetadataItem(
3457 1 : "RESAMPLING", osNewResampling.c_str());
3458 : }
3459 : }
3460 :
3461 117 : abAlreadyUsedOverviewBand[j] = true;
3462 117 : CPLAssert(nNewOverviews < poBand->GetOverviewCount());
3463 117 : papoOverviewBands[nNewOverviews++] = poOverview;
3464 117 : break;
3465 : }
3466 : }
3467 : }
3468 :
3469 156 : void *pScaledProgressData = GDALCreateScaledProgress(
3470 78 : (iBand + iBandOffset) /
3471 78 : static_cast<double>(nBandsIn + iBandOffset),
3472 78 : (iBand + iBandOffset + 1) /
3473 78 : static_cast<double>(nBandsIn + iBandOffset),
3474 : pfnProgress, pProgressData);
3475 :
3476 78 : eErr = GDALRegenerateOverviewsEx(
3477 : poBand, nNewOverviews,
3478 : reinterpret_cast<GDALRasterBandH *>(papoOverviewBands),
3479 : pszResampling, GDALScaledProgress, pScaledProgressData,
3480 : papszOptions);
3481 :
3482 78 : GDALDestroyScaledProgress(pScaledProgressData);
3483 : }
3484 :
3485 : /* --------------------------------------------------------------------
3486 : */
3487 : /* Cleanup */
3488 : /* --------------------------------------------------------------------
3489 : */
3490 42 : CPLFree(papoOverviewBands);
3491 : }
3492 :
3493 220 : pfnProgress(1.0, nullptr, pProgressData);
3494 :
3495 220 : return eErr;
3496 : }
3497 :
3498 : /************************************************************************/
3499 : /* GTiffWriteDummyGeokeyDirectory() */
3500 : /************************************************************************/
3501 :
3502 1316 : static void GTiffWriteDummyGeokeyDirectory(TIFF *hTIFF)
3503 : {
3504 : // If we have existing geokeys, try to wipe them
3505 : // by writing a dummy geokey directory. (#2546)
3506 1316 : uint16_t *panVI = nullptr;
3507 1316 : uint16_t nKeyCount = 0;
3508 :
3509 1316 : if (TIFFGetField(hTIFF, TIFFTAG_GEOKEYDIRECTORY, &nKeyCount, &panVI))
3510 : {
3511 19 : GUInt16 anGKVersionInfo[4] = {1, 1, 0, 0};
3512 19 : double adfDummyDoubleParams[1] = {0.0};
3513 19 : TIFFSetField(hTIFF, TIFFTAG_GEOKEYDIRECTORY, 4, anGKVersionInfo);
3514 19 : TIFFSetField(hTIFF, TIFFTAG_GEODOUBLEPARAMS, 1, adfDummyDoubleParams);
3515 19 : TIFFSetField(hTIFF, TIFFTAG_GEOASCIIPARAMS, "");
3516 : }
3517 1316 : }
3518 :
3519 : /************************************************************************/
3520 : /* IsSRSCompatibleOfGeoTIFF() */
3521 : /************************************************************************/
3522 :
3523 1918 : static bool IsSRSCompatibleOfGeoTIFF(const OGRSpatialReference *poSRS,
3524 : GTIFFKeysFlavorEnum eGeoTIFFKeysFlavor)
3525 : {
3526 1918 : char *pszWKT = nullptr;
3527 1918 : if ((poSRS->IsGeographic() || poSRS->IsProjected()) && !poSRS->IsCompound())
3528 : {
3529 1900 : const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
3530 1900 : const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
3531 1900 : if (pszAuthName && pszAuthCode && EQUAL(pszAuthName, "EPSG"))
3532 1381 : return true;
3533 : }
3534 : OGRErr eErr;
3535 : {
3536 1074 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
3537 1074 : if (poSRS->IsDerivedGeographic() ||
3538 537 : (poSRS->IsProjected() && !poSRS->IsCompound() &&
3539 71 : poSRS->GetAxesCount() == 3))
3540 : {
3541 0 : eErr = OGRERR_FAILURE;
3542 : }
3543 : else
3544 : {
3545 : // Geographic3D CRS can't be exported to WKT1, but are
3546 : // valid GeoTIFF 1.1
3547 537 : const char *const apszOptions[] = {
3548 537 : poSRS->IsGeographic() ? nullptr : "FORMAT=WKT1", nullptr};
3549 537 : eErr = poSRS->exportToWkt(&pszWKT, apszOptions);
3550 537 : if (eErr == OGRERR_FAILURE && poSRS->IsProjected() &&
3551 : eGeoTIFFKeysFlavor == GEOTIFF_KEYS_ESRI_PE)
3552 : {
3553 0 : CPLFree(pszWKT);
3554 0 : const char *const apszOptionsESRIWKT[] = {"FORMAT=WKT1_ESRI",
3555 : nullptr};
3556 0 : eErr = poSRS->exportToWkt(&pszWKT, apszOptionsESRIWKT);
3557 : }
3558 : }
3559 : }
3560 537 : const bool bCompatibleOfGeoTIFF =
3561 1073 : (eErr == OGRERR_NONE && pszWKT != nullptr &&
3562 536 : strstr(pszWKT, "custom_proj4") == nullptr);
3563 537 : CPLFree(pszWKT);
3564 537 : return bCompatibleOfGeoTIFF;
3565 : }
3566 :
3567 : /************************************************************************/
3568 : /* WriteGeoTIFFInfo() */
3569 : /************************************************************************/
3570 :
3571 3138 : void GTiffDataset::WriteGeoTIFFInfo()
3572 :
3573 : {
3574 3138 : bool bPixelIsPoint = false;
3575 3138 : bool bPointGeoIgnore = false;
3576 :
3577 : const char *pszAreaOrPoint =
3578 3138 : GTiffDataset::GetMetadataItem(GDALMD_AREA_OR_POINT);
3579 3138 : if (pszAreaOrPoint && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT))
3580 : {
3581 12 : bPixelIsPoint = true;
3582 : bPointGeoIgnore =
3583 12 : CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
3584 : }
3585 :
3586 3138 : if (m_bForceUnsetGTOrGCPs)
3587 : {
3588 10 : m_bNeedsRewrite = true;
3589 10 : m_bForceUnsetGTOrGCPs = false;
3590 :
3591 10 : TIFFUnsetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE);
3592 10 : TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS);
3593 10 : TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX);
3594 : }
3595 :
3596 3138 : if (m_bForceUnsetProjection)
3597 : {
3598 7 : m_bNeedsRewrite = true;
3599 7 : m_bForceUnsetProjection = false;
3600 :
3601 7 : TIFFUnsetField(m_hTIFF, TIFFTAG_GEOKEYDIRECTORY);
3602 7 : TIFFUnsetField(m_hTIFF, TIFFTAG_GEODOUBLEPARAMS);
3603 7 : TIFFUnsetField(m_hTIFF, TIFFTAG_GEOASCIIPARAMS);
3604 : }
3605 :
3606 : /* -------------------------------------------------------------------- */
3607 : /* Write geotransform if valid. */
3608 : /* -------------------------------------------------------------------- */
3609 3138 : if (m_bGeoTransformValid)
3610 : {
3611 1477 : m_bNeedsRewrite = true;
3612 :
3613 : /* --------------------------------------------------------------------
3614 : */
3615 : /* Clear old tags to ensure we don't end up with conflicting */
3616 : /* information. (#2625) */
3617 : /* --------------------------------------------------------------------
3618 : */
3619 1477 : TIFFUnsetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE);
3620 1477 : TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS);
3621 1477 : TIFFUnsetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX);
3622 :
3623 : /* --------------------------------------------------------------------
3624 : */
3625 : /* Write the transform. If we have a normal north-up image we */
3626 : /* use the tiepoint plus pixelscale otherwise we use a matrix. */
3627 : /* --------------------------------------------------------------------
3628 : */
3629 1477 : if (m_adfGeoTransform[2] == 0.0 && m_adfGeoTransform[4] == 0.0 &&
3630 1460 : m_adfGeoTransform[5] < 0.0)
3631 : {
3632 1429 : double dfOffset = 0.0;
3633 1429 : if (m_eProfile != GTiffProfile::BASELINE)
3634 : {
3635 : // In the case the SRS has a vertical component and we have
3636 : // a single band, encode its scale/offset in the GeoTIFF tags
3637 1424 : int bHasScale = FALSE;
3638 1424 : double dfScale = GetRasterBand(1)->GetScale(&bHasScale);
3639 1424 : int bHasOffset = FALSE;
3640 1424 : dfOffset = GetRasterBand(1)->GetOffset(&bHasOffset);
3641 : const bool bApplyScaleOffset =
3642 1424 : m_oSRS.IsVertical() && GetRasterCount() == 1;
3643 1424 : if (bApplyScaleOffset && !bHasScale)
3644 0 : dfScale = 1.0;
3645 1424 : if (!bApplyScaleOffset || !bHasOffset)
3646 1421 : dfOffset = 0.0;
3647 : const double adfPixelScale[3] = {
3648 1424 : m_adfGeoTransform[1], fabs(m_adfGeoTransform[5]),
3649 1424 : bApplyScaleOffset ? dfScale : 0.0};
3650 1424 : TIFFSetField(m_hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale);
3651 : }
3652 :
3653 1429 : double adfTiePoints[6] = {
3654 1429 : 0.0, 0.0, 0.0, m_adfGeoTransform[0], m_adfGeoTransform[3],
3655 1429 : dfOffset};
3656 :
3657 1429 : if (bPixelIsPoint && !bPointGeoIgnore)
3658 : {
3659 8 : adfTiePoints[3] +=
3660 8 : m_adfGeoTransform[1] * 0.5 + m_adfGeoTransform[2] * 0.5;
3661 8 : adfTiePoints[4] +=
3662 8 : m_adfGeoTransform[4] * 0.5 + m_adfGeoTransform[5] * 0.5;
3663 : }
3664 :
3665 1429 : if (m_eProfile != GTiffProfile::BASELINE)
3666 1429 : TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
3667 : }
3668 : else
3669 : {
3670 48 : double adfMatrix[16] = {};
3671 :
3672 48 : adfMatrix[0] = m_adfGeoTransform[1];
3673 48 : adfMatrix[1] = m_adfGeoTransform[2];
3674 48 : adfMatrix[3] = m_adfGeoTransform[0];
3675 48 : adfMatrix[4] = m_adfGeoTransform[4];
3676 48 : adfMatrix[5] = m_adfGeoTransform[5];
3677 48 : adfMatrix[7] = m_adfGeoTransform[3];
3678 48 : adfMatrix[15] = 1.0;
3679 :
3680 48 : if (bPixelIsPoint && !bPointGeoIgnore)
3681 : {
3682 0 : adfMatrix[3] +=
3683 0 : m_adfGeoTransform[1] * 0.5 + m_adfGeoTransform[2] * 0.5;
3684 0 : adfMatrix[7] +=
3685 0 : m_adfGeoTransform[4] * 0.5 + m_adfGeoTransform[5] * 0.5;
3686 : }
3687 :
3688 48 : if (m_eProfile != GTiffProfile::BASELINE)
3689 48 : TIFFSetField(m_hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
3690 : }
3691 :
3692 : // Do we need a world file?
3693 1477 : if (CPLFetchBool(m_papszCreationOptions, "TFW", false))
3694 7 : GDALWriteWorldFile(m_pszFilename, "tfw", m_adfGeoTransform);
3695 1470 : else if (CPLFetchBool(m_papszCreationOptions, "WORLDFILE", false))
3696 2 : GDALWriteWorldFile(m_pszFilename, "wld", m_adfGeoTransform);
3697 : }
3698 1661 : else if (GetGCPCount() > 0)
3699 : {
3700 12 : m_bNeedsRewrite = true;
3701 :
3702 : double *padfTiePoints = static_cast<double *>(
3703 12 : CPLMalloc(6 * sizeof(double) * GetGCPCount()));
3704 :
3705 57 : for (size_t iGCP = 0; iGCP < m_aoGCPs.size(); ++iGCP)
3706 : {
3707 :
3708 45 : padfTiePoints[iGCP * 6 + 0] = m_aoGCPs[iGCP].Pixel();
3709 45 : padfTiePoints[iGCP * 6 + 1] = m_aoGCPs[iGCP].Line();
3710 45 : padfTiePoints[iGCP * 6 + 2] = 0;
3711 45 : padfTiePoints[iGCP * 6 + 3] = m_aoGCPs[iGCP].X();
3712 45 : padfTiePoints[iGCP * 6 + 4] = m_aoGCPs[iGCP].Y();
3713 45 : padfTiePoints[iGCP * 6 + 5] = m_aoGCPs[iGCP].Z();
3714 :
3715 45 : if (bPixelIsPoint && !bPointGeoIgnore)
3716 : {
3717 0 : padfTiePoints[iGCP * 6 + 0] += 0.5;
3718 0 : padfTiePoints[iGCP * 6 + 1] += 0.5;
3719 : }
3720 : }
3721 :
3722 12 : if (m_eProfile != GTiffProfile::BASELINE)
3723 12 : TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * GetGCPCount(),
3724 : padfTiePoints);
3725 12 : CPLFree(padfTiePoints);
3726 : }
3727 :
3728 : /* -------------------------------------------------------------------- */
3729 : /* Write out projection definition. */
3730 : /* -------------------------------------------------------------------- */
3731 3138 : const bool bHasProjection = !m_oSRS.IsEmpty();
3732 3138 : if ((bHasProjection || bPixelIsPoint) &&
3733 1319 : m_eProfile != GTiffProfile::BASELINE)
3734 : {
3735 1316 : m_bNeedsRewrite = true;
3736 :
3737 : // If we have existing geokeys, try to wipe them
3738 : // by writing a dummy geokey directory. (#2546)
3739 1316 : GTiffWriteDummyGeokeyDirectory(m_hTIFF);
3740 :
3741 1316 : GTIF *psGTIF = GTiffDataset::GTIFNew(m_hTIFF);
3742 :
3743 : // Set according to coordinate system.
3744 1316 : if (bHasProjection)
3745 : {
3746 1315 : if (IsSRSCompatibleOfGeoTIFF(&m_oSRS, m_eGeoTIFFKeysFlavor))
3747 : {
3748 1313 : GTIFSetFromOGISDefnEx(psGTIF,
3749 : OGRSpatialReference::ToHandle(&m_oSRS),
3750 : m_eGeoTIFFKeysFlavor, m_eGeoTIFFVersion);
3751 : }
3752 : else
3753 : {
3754 2 : GDALPamDataset::SetSpatialRef(&m_oSRS);
3755 : }
3756 : }
3757 :
3758 1316 : if (bPixelIsPoint)
3759 : {
3760 12 : GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
3761 : RasterPixelIsPoint);
3762 : }
3763 :
3764 1316 : GTIFWriteKeys(psGTIF);
3765 1316 : GTIFFree(psGTIF);
3766 : }
3767 3138 : }
3768 :
3769 : /************************************************************************/
3770 : /* AppendMetadataItem() */
3771 : /************************************************************************/
3772 :
3773 3276 : static void AppendMetadataItem(CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
3774 : const char *pszKey, const char *pszValue,
3775 : int nBand, const char *pszRole,
3776 : const char *pszDomain)
3777 :
3778 : {
3779 : /* -------------------------------------------------------------------- */
3780 : /* Create the Item element, and subcomponents. */
3781 : /* -------------------------------------------------------------------- */
3782 3276 : CPLXMLNode *psItem = CPLCreateXMLNode(nullptr, CXT_Element, "Item");
3783 3276 : CPLCreateXMLNode(CPLCreateXMLNode(psItem, CXT_Attribute, "name"), CXT_Text,
3784 : pszKey);
3785 :
3786 3276 : if (nBand > 0)
3787 : {
3788 766 : char szBandId[32] = {};
3789 766 : snprintf(szBandId, sizeof(szBandId), "%d", nBand - 1);
3790 766 : CPLCreateXMLNode(CPLCreateXMLNode(psItem, CXT_Attribute, "sample"),
3791 : CXT_Text, szBandId);
3792 : }
3793 :
3794 3276 : if (pszRole != nullptr)
3795 306 : CPLCreateXMLNode(CPLCreateXMLNode(psItem, CXT_Attribute, "role"),
3796 : CXT_Text, pszRole);
3797 :
3798 3276 : if (pszDomain != nullptr && strlen(pszDomain) > 0)
3799 935 : CPLCreateXMLNode(CPLCreateXMLNode(psItem, CXT_Attribute, "domain"),
3800 : CXT_Text, pszDomain);
3801 :
3802 : // Note: this escaping should not normally be done, as the serialization
3803 : // of the tree to XML also does it, so we end up width double XML escaping,
3804 : // but keep it for backward compatibility.
3805 3276 : char *pszEscapedItemValue = CPLEscapeString(pszValue, -1, CPLES_XML);
3806 3276 : CPLCreateXMLNode(psItem, CXT_Text, pszEscapedItemValue);
3807 3276 : CPLFree(pszEscapedItemValue);
3808 :
3809 : /* -------------------------------------------------------------------- */
3810 : /* Create root, if missing. */
3811 : /* -------------------------------------------------------------------- */
3812 3276 : if (*ppsRoot == nullptr)
3813 569 : *ppsRoot = CPLCreateXMLNode(nullptr, CXT_Element, "GDALMetadata");
3814 :
3815 : /* -------------------------------------------------------------------- */
3816 : /* Append item to tail. We keep track of the tail to avoid */
3817 : /* O(nsquared) time as the list gets longer. */
3818 : /* -------------------------------------------------------------------- */
3819 3276 : if (*ppsTail == nullptr)
3820 569 : CPLAddXMLChild(*ppsRoot, psItem);
3821 : else
3822 2707 : CPLAddXMLSibling(*ppsTail, psItem);
3823 :
3824 3276 : *ppsTail = psItem;
3825 3276 : }
3826 :
3827 : /************************************************************************/
3828 : /* WriteMDMetadata() */
3829 : /************************************************************************/
3830 :
3831 205320 : static void WriteMDMetadata(GDALMultiDomainMetadata *poMDMD, TIFF *hTIFF,
3832 : CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
3833 : int nBand, GTiffProfile eProfile)
3834 :
3835 : {
3836 :
3837 : /* ==================================================================== */
3838 : /* Process each domain. */
3839 : /* ==================================================================== */
3840 205320 : CSLConstList papszDomainList = poMDMD->GetDomainList();
3841 209892 : for (int iDomain = 0; papszDomainList && papszDomainList[iDomain];
3842 : ++iDomain)
3843 : {
3844 4572 : char **papszMD = poMDMD->GetMetadata(papszDomainList[iDomain]);
3845 4572 : bool bIsXML = false;
3846 :
3847 4572 : if (EQUAL(papszDomainList[iDomain], "IMAGE_STRUCTURE") ||
3848 1345 : EQUAL(papszDomainList[iDomain], "DERIVED_SUBDATASETS"))
3849 3229 : continue; // Ignored.
3850 1343 : if (EQUAL(papszDomainList[iDomain], "COLOR_PROFILE"))
3851 3 : continue; // Handled elsewhere.
3852 1340 : if (EQUAL(papszDomainList[iDomain], MD_DOMAIN_RPC))
3853 7 : continue; // Handled elsewhere.
3854 1334 : if (EQUAL(papszDomainList[iDomain], "xml:ESRI") &&
3855 1 : CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
3856 1 : continue; // Handled elsewhere.
3857 1332 : if (EQUAL(papszDomainList[iDomain], "xml:XMP"))
3858 2 : continue; // Handled in SetMetadata.
3859 :
3860 1330 : if (STARTS_WITH_CI(papszDomainList[iDomain], "xml:"))
3861 2 : bIsXML = true;
3862 :
3863 : /* --------------------------------------------------------------------
3864 : */
3865 : /* Process each item in this domain. */
3866 : /* --------------------------------------------------------------------
3867 : */
3868 4929 : for (int iItem = 0; papszMD && papszMD[iItem]; ++iItem)
3869 : {
3870 3599 : const char *pszItemValue = nullptr;
3871 3599 : char *pszItemName = nullptr;
3872 :
3873 3599 : if (bIsXML)
3874 : {
3875 2 : pszItemName = CPLStrdup("doc");
3876 2 : pszItemValue = papszMD[iItem];
3877 : }
3878 : else
3879 : {
3880 3597 : pszItemValue = CPLParseNameValue(papszMD[iItem], &pszItemName);
3881 3597 : if (pszItemName == nullptr)
3882 : {
3883 49 : CPLDebug("GTiff", "Invalid metadata item : %s",
3884 49 : papszMD[iItem]);
3885 49 : continue;
3886 : }
3887 : }
3888 :
3889 : /* --------------------------------------------------------------------
3890 : */
3891 : /* Convert into XML item or handle as a special TIFF tag. */
3892 : /* --------------------------------------------------------------------
3893 : */
3894 3550 : if (strlen(papszDomainList[iDomain]) == 0 && nBand == 0 &&
3895 2526 : (STARTS_WITH_CI(pszItemName, "TIFFTAG_") ||
3896 2465 : (EQUAL(pszItemName, "GEO_METADATA") &&
3897 2464 : eProfile == GTiffProfile::GDALGEOTIFF) ||
3898 2464 : (EQUAL(pszItemName, "TIFF_RSID") &&
3899 : eProfile == GTiffProfile::GDALGEOTIFF)))
3900 : {
3901 63 : if (EQUAL(pszItemName, "TIFFTAG_RESOLUTIONUNIT"))
3902 : {
3903 : // ResolutionUnit can't be 0, which is the default if
3904 : // atoi() fails. Set to 1=Unknown.
3905 9 : int v = atoi(pszItemValue);
3906 9 : if (!v)
3907 1 : v = RESUNIT_NONE;
3908 9 : TIFFSetField(hTIFF, TIFFTAG_RESOLUTIONUNIT, v);
3909 : }
3910 : else
3911 : {
3912 54 : bool bFoundTag = false;
3913 54 : size_t iTag = 0; // Used after for.
3914 54 : const auto *pasTIFFTags = GTiffDataset::GetTIFFTags();
3915 286 : for (; pasTIFFTags[iTag].pszTagName; ++iTag)
3916 : {
3917 286 : if (EQUAL(pszItemName, pasTIFFTags[iTag].pszTagName))
3918 : {
3919 54 : bFoundTag = true;
3920 54 : break;
3921 : }
3922 : }
3923 :
3924 54 : if (bFoundTag &&
3925 54 : pasTIFFTags[iTag].eType == GTIFFTAGTYPE_STRING)
3926 33 : TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
3927 : pszItemValue);
3928 21 : else if (bFoundTag &&
3929 21 : pasTIFFTags[iTag].eType == GTIFFTAGTYPE_FLOAT)
3930 16 : TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
3931 : CPLAtof(pszItemValue));
3932 5 : else if (bFoundTag &&
3933 5 : pasTIFFTags[iTag].eType == GTIFFTAGTYPE_SHORT)
3934 4 : TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal,
3935 : atoi(pszItemValue));
3936 1 : else if (bFoundTag && pasTIFFTags[iTag].eType ==
3937 : GTIFFTAGTYPE_BYTE_STRING)
3938 : {
3939 1 : uint32_t nLen =
3940 1 : static_cast<uint32_t>(strlen(pszItemValue));
3941 1 : if (nLen)
3942 : {
3943 1 : TIFFSetField(hTIFF, pasTIFFTags[iTag].nTagVal, nLen,
3944 : pszItemValue);
3945 1 : }
3946 : }
3947 : else
3948 0 : CPLError(CE_Warning, CPLE_NotSupported,
3949 : "%s metadata item is unhandled and "
3950 : "will not be written",
3951 : pszItemName);
3952 63 : }
3953 : }
3954 3487 : else if (nBand == 0 && EQUAL(pszItemName, GDALMD_AREA_OR_POINT))
3955 : {
3956 : /* Do nothing, handled elsewhere. */;
3957 : }
3958 : else
3959 : {
3960 2599 : AppendMetadataItem(ppsRoot, ppsTail, pszItemName, pszItemValue,
3961 2599 : nBand, nullptr, papszDomainList[iDomain]);
3962 : }
3963 :
3964 3550 : CPLFree(pszItemName);
3965 : }
3966 :
3967 : /* --------------------------------------------------------------------
3968 : */
3969 : /* Remove TIFFTAG_xxxxxx that are already set but no longer in */
3970 : /* the metadata list (#5619) */
3971 : /* --------------------------------------------------------------------
3972 : */
3973 1330 : if (strlen(papszDomainList[iDomain]) == 0 && nBand == 0)
3974 : {
3975 1145 : const auto *pasTIFFTags = GTiffDataset::GetTIFFTags();
3976 17175 : for (size_t iTag = 0; pasTIFFTags[iTag].pszTagName; ++iTag)
3977 : {
3978 16030 : uint32_t nCount = 0;
3979 16030 : char *pszText = nullptr;
3980 16030 : int16_t nVal = 0;
3981 16030 : float fVal = 0.0f;
3982 : const char *pszVal =
3983 16030 : CSLFetchNameValue(papszMD, pasTIFFTags[iTag].pszTagName);
3984 31997 : if (pszVal == nullptr &&
3985 15967 : ((pasTIFFTags[iTag].eType == GTIFFTAGTYPE_STRING &&
3986 9127 : TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal,
3987 15959 : &pszText)) ||
3988 15959 : (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_SHORT &&
3989 3422 : TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &nVal)) ||
3990 15956 : (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_FLOAT &&
3991 2274 : TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &fVal)) ||
3992 15955 : (pasTIFFTags[iTag].eType == GTIFFTAGTYPE_BYTE_STRING &&
3993 1144 : TIFFGetField(hTIFF, pasTIFFTags[iTag].nTagVal, &nCount,
3994 : &pszText))))
3995 : {
3996 13 : TIFFUnsetField(hTIFF, pasTIFFTags[iTag].nTagVal);
3997 : }
3998 : }
3999 : }
4000 : }
4001 205320 : }
4002 :
4003 : /************************************************************************/
4004 : /* WriteRPC() */
4005 : /************************************************************************/
4006 :
4007 5217 : void GTiffDataset::WriteRPC(GDALDataset *poSrcDS, TIFF *l_hTIFF,
4008 : int bSrcIsGeoTIFF, GTiffProfile eProfile,
4009 : const char *pszTIFFFilename,
4010 : CSLConstList papszCreationOptions,
4011 : bool bWriteOnlyInPAMIfNeeded)
4012 : {
4013 : /* -------------------------------------------------------------------- */
4014 : /* Handle RPC data written to TIFF RPCCoefficient tag, RPB file, */
4015 : /* RPCTEXT file or PAM. */
4016 : /* -------------------------------------------------------------------- */
4017 5217 : char **papszRPCMD = poSrcDS->GetMetadata(MD_DOMAIN_RPC);
4018 5217 : if (papszRPCMD != nullptr)
4019 : {
4020 32 : bool bRPCSerializedOtherWay = false;
4021 :
4022 32 : if (eProfile == GTiffProfile::GDALGEOTIFF)
4023 : {
4024 20 : if (!bWriteOnlyInPAMIfNeeded)
4025 11 : GTiffDatasetWriteRPCTag(l_hTIFF, papszRPCMD);
4026 20 : bRPCSerializedOtherWay = true;
4027 : }
4028 :
4029 : // Write RPB file if explicitly asked, or if a non GDAL specific
4030 : // profile is selected and RPCTXT is not asked.
4031 : bool bRPBExplicitlyAsked =
4032 32 : CPLFetchBool(papszCreationOptions, "RPB", false);
4033 : bool bRPBExplicitlyDenied =
4034 32 : !CPLFetchBool(papszCreationOptions, "RPB", true);
4035 44 : if ((eProfile != GTiffProfile::GDALGEOTIFF &&
4036 12 : !CPLFetchBool(papszCreationOptions, "RPCTXT", false) &&
4037 44 : !bRPBExplicitlyDenied) ||
4038 : bRPBExplicitlyAsked)
4039 : {
4040 8 : if (!bWriteOnlyInPAMIfNeeded)
4041 4 : GDALWriteRPBFile(pszTIFFFilename, papszRPCMD);
4042 8 : bRPCSerializedOtherWay = true;
4043 : }
4044 :
4045 32 : if (CPLFetchBool(papszCreationOptions, "RPCTXT", false))
4046 : {
4047 2 : if (!bWriteOnlyInPAMIfNeeded)
4048 1 : GDALWriteRPCTXTFile(pszTIFFFilename, papszRPCMD);
4049 2 : bRPCSerializedOtherWay = true;
4050 : }
4051 :
4052 32 : if (!bRPCSerializedOtherWay && bWriteOnlyInPAMIfNeeded && bSrcIsGeoTIFF)
4053 1 : cpl::down_cast<GTiffDataset *>(poSrcDS)
4054 1 : ->GDALPamDataset::SetMetadata(papszRPCMD, MD_DOMAIN_RPC);
4055 : }
4056 5217 : }
4057 :
4058 : /************************************************************************/
4059 : /* WriteMetadata() */
4060 : /************************************************************************/
4061 :
4062 4197 : bool GTiffDataset::WriteMetadata(GDALDataset *poSrcDS, TIFF *l_hTIFF,
4063 : bool bSrcIsGeoTIFF, GTiffProfile eProfile,
4064 : const char *pszTIFFFilename,
4065 : CSLConstList papszCreationOptions,
4066 : bool bExcludeRPBandIMGFileWriting)
4067 :
4068 : {
4069 : /* -------------------------------------------------------------------- */
4070 : /* Convert all the remaining metadata into a simple XML */
4071 : /* format. */
4072 : /* -------------------------------------------------------------------- */
4073 4197 : CPLXMLNode *psRoot = nullptr;
4074 4197 : CPLXMLNode *psTail = nullptr;
4075 :
4076 4197 : if (bSrcIsGeoTIFF)
4077 : {
4078 3156 : GTiffDataset *poSrcDSGTiff = cpl::down_cast<GTiffDataset *>(poSrcDS);
4079 3156 : assert(poSrcDSGTiff);
4080 3156 : WriteMDMetadata(&poSrcDSGTiff->m_oGTiffMDMD, l_hTIFF, &psRoot, &psTail,
4081 : 0, eProfile);
4082 : }
4083 : else
4084 : {
4085 : const char *pszCopySrcMDD =
4086 1041 : CSLFetchNameValueDef(papszCreationOptions, "COPY_SRC_MDD", "AUTO");
4087 : char **papszSrcMDD =
4088 1041 : CSLFetchNameValueMultiple(papszCreationOptions, "SRC_MDD");
4089 1041 : if (EQUAL(pszCopySrcMDD, "AUTO") || CPLTestBool(pszCopySrcMDD) ||
4090 : papszSrcMDD)
4091 : {
4092 2078 : GDALMultiDomainMetadata l_oMDMD;
4093 1039 : char **papszMD = poSrcDS->GetMetadata();
4094 1043 : if (CSLCount(papszMD) > 0 &&
4095 4 : (!papszSrcMDD || CSLFindString(papszSrcMDD, "") >= 0 ||
4096 2 : CSLFindString(papszSrcMDD, "_DEFAULT_") >= 0))
4097 : {
4098 638 : l_oMDMD.SetMetadata(papszMD);
4099 : }
4100 :
4101 1039 : if ((!EQUAL(pszCopySrcMDD, "AUTO") && CPLTestBool(pszCopySrcMDD)) ||
4102 : papszSrcMDD)
4103 : {
4104 6 : char **papszDomainList = poSrcDS->GetMetadataDomainList();
4105 30 : for (char **papszIter = papszDomainList;
4106 30 : papszIter && *papszIter; ++papszIter)
4107 : {
4108 24 : const char *pszDomain = *papszIter;
4109 36 : if (pszDomain[0] != 0 &&
4110 12 : (!papszSrcMDD ||
4111 12 : CSLFindString(papszSrcMDD, pszDomain) >= 0))
4112 : {
4113 10 : l_oMDMD.SetMetadata(poSrcDS->GetMetadata(pszDomain),
4114 : pszDomain);
4115 : }
4116 : }
4117 6 : CSLDestroy(papszDomainList);
4118 : }
4119 :
4120 1039 : WriteMDMetadata(&l_oMDMD, l_hTIFF, &psRoot, &psTail, 0, eProfile);
4121 : }
4122 1041 : CSLDestroy(papszSrcMDD);
4123 : }
4124 :
4125 4197 : if (!bExcludeRPBandIMGFileWriting)
4126 : {
4127 4191 : WriteRPC(poSrcDS, l_hTIFF, bSrcIsGeoTIFF, eProfile, pszTIFFFilename,
4128 : papszCreationOptions);
4129 :
4130 : /* --------------------------------------------------------------------
4131 : */
4132 : /* Handle metadata data written to an IMD file. */
4133 : /* --------------------------------------------------------------------
4134 : */
4135 4191 : char **papszIMDMD = poSrcDS->GetMetadata(MD_DOMAIN_IMD);
4136 4191 : if (papszIMDMD != nullptr)
4137 : {
4138 20 : GDALWriteIMDFile(pszTIFFFilename, papszIMDMD);
4139 : }
4140 : }
4141 :
4142 4197 : uint16_t nPhotometric = 0;
4143 4197 : if (!TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric)))
4144 1 : nPhotometric = PHOTOMETRIC_MINISBLACK;
4145 :
4146 4197 : const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
4147 : GDALDataset::ToHandle(poSrcDS), nPhotometric, papszCreationOptions);
4148 :
4149 : /* -------------------------------------------------------------------- */
4150 : /* We also need to address band specific metadata, and special */
4151 : /* "role" metadata. */
4152 : /* -------------------------------------------------------------------- */
4153 207473 : for (int nBand = 1; nBand <= poSrcDS->GetRasterCount(); ++nBand)
4154 : {
4155 203276 : GDALRasterBand *poBand = poSrcDS->GetRasterBand(nBand);
4156 :
4157 203276 : if (bSrcIsGeoTIFF)
4158 : {
4159 : GTiffRasterBand *poSrcBandGTiff =
4160 201043 : cpl::down_cast<GTiffRasterBand *>(poBand);
4161 201043 : assert(poSrcBandGTiff);
4162 201043 : WriteMDMetadata(&poSrcBandGTiff->m_oGTiffMDMD, l_hTIFF, &psRoot,
4163 : &psTail, nBand, eProfile);
4164 : }
4165 : else
4166 : {
4167 2233 : char **papszMD = poBand->GetMetadata();
4168 :
4169 2233 : if (CSLCount(papszMD) > 0)
4170 : {
4171 164 : GDALMultiDomainMetadata l_oMDMD;
4172 82 : l_oMDMD.SetMetadata(papszMD);
4173 :
4174 82 : WriteMDMetadata(&l_oMDMD, l_hTIFF, &psRoot, &psTail, nBand,
4175 : eProfile);
4176 : }
4177 : }
4178 :
4179 203276 : const double dfOffset = poBand->GetOffset();
4180 203276 : const double dfScale = poBand->GetScale();
4181 203276 : bool bGeoTIFFScaleOffsetInZ = false;
4182 : double adfGeoTransform[6];
4183 : // Check if we have already encoded scale/offset in the GeoTIFF tags
4184 203276 : if (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None &&
4185 2840 : adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0 &&
4186 2829 : adfGeoTransform[5] < 0.0 && poSrcDS->GetSpatialRef() &&
4187 206123 : poSrcDS->GetSpatialRef()->IsVertical() &&
4188 7 : poSrcDS->GetRasterCount() == 1)
4189 : {
4190 7 : bGeoTIFFScaleOffsetInZ = true;
4191 : }
4192 :
4193 203276 : if ((dfOffset != 0.0 || dfScale != 1.0) && !bGeoTIFFScaleOffsetInZ)
4194 : {
4195 13 : char szValue[128] = {};
4196 :
4197 13 : CPLsnprintf(szValue, sizeof(szValue), "%.18g", dfOffset);
4198 13 : AppendMetadataItem(&psRoot, &psTail, "OFFSET", szValue, nBand,
4199 : "offset", "");
4200 13 : CPLsnprintf(szValue, sizeof(szValue), "%.18g", dfScale);
4201 13 : AppendMetadataItem(&psRoot, &psTail, "SCALE", szValue, nBand,
4202 : "scale", "");
4203 : }
4204 :
4205 203276 : const char *pszUnitType = poBand->GetUnitType();
4206 203276 : if (pszUnitType != nullptr && pszUnitType[0] != '\0')
4207 : {
4208 33 : bool bWriteUnit = true;
4209 33 : auto poSRS = poSrcDS->GetSpatialRef();
4210 33 : if (poSRS && poSRS->IsCompound())
4211 : {
4212 2 : const char *pszVertUnit = nullptr;
4213 2 : poSRS->GetTargetLinearUnits("COMPD_CS|VERT_CS", &pszVertUnit);
4214 2 : if (pszVertUnit && EQUAL(pszVertUnit, pszUnitType))
4215 : {
4216 2 : bWriteUnit = false;
4217 : }
4218 : }
4219 33 : if (bWriteUnit)
4220 : {
4221 31 : AppendMetadataItem(&psRoot, &psTail, "UNITTYPE", pszUnitType,
4222 : nBand, "unittype", "");
4223 : }
4224 : }
4225 :
4226 203276 : if (strlen(poBand->GetDescription()) > 0)
4227 : {
4228 15 : AppendMetadataItem(&psRoot, &psTail, "DESCRIPTION",
4229 15 : poBand->GetDescription(), nBand, "description",
4230 : "");
4231 : }
4232 :
4233 203478 : if (!bStandardColorInterp &&
4234 202 : !(nBand <= 3 && EQUAL(CSLFetchNameValueDef(papszCreationOptions,
4235 : "PHOTOMETRIC", ""),
4236 : "RGB")))
4237 : {
4238 234 : AppendMetadataItem(&psRoot, &psTail, "COLORINTERP",
4239 : GDALGetColorInterpretationName(
4240 234 : poBand->GetColorInterpretation()),
4241 : nBand, "colorinterp", "");
4242 : }
4243 : }
4244 :
4245 : const char *pszTilingSchemeName =
4246 4197 : CSLFetchNameValue(papszCreationOptions, "@TILING_SCHEME_NAME");
4247 4197 : if (pszTilingSchemeName)
4248 : {
4249 22 : AppendMetadataItem(&psRoot, &psTail, "NAME", pszTilingSchemeName, 0,
4250 : nullptr, "TILING_SCHEME");
4251 :
4252 22 : const char *pszZoomLevel = CSLFetchNameValue(
4253 : papszCreationOptions, "@TILING_SCHEME_ZOOM_LEVEL");
4254 22 : if (pszZoomLevel)
4255 : {
4256 22 : AppendMetadataItem(&psRoot, &psTail, "ZOOM_LEVEL", pszZoomLevel, 0,
4257 : nullptr, "TILING_SCHEME");
4258 : }
4259 :
4260 22 : const char *pszAlignedLevels = CSLFetchNameValue(
4261 : papszCreationOptions, "@TILING_SCHEME_ALIGNED_LEVELS");
4262 22 : if (pszAlignedLevels)
4263 : {
4264 4 : AppendMetadataItem(&psRoot, &psTail, "ALIGNED_LEVELS",
4265 : pszAlignedLevels, 0, nullptr, "TILING_SCHEME");
4266 : }
4267 : }
4268 :
4269 : /* -------------------------------------------------------------------- */
4270 : /* Write information about some codecs. */
4271 : /* -------------------------------------------------------------------- */
4272 4197 : if (CPLTestBool(
4273 : CPLGetConfigOption("GTIFF_WRITE_IMAGE_STRUCTURE_METADATA", "YES")))
4274 : {
4275 : const char *pszCompress =
4276 4192 : CSLFetchNameValue(papszCreationOptions, "COMPRESS");
4277 4192 : if (pszCompress && EQUAL(pszCompress, "WEBP"))
4278 : {
4279 31 : if (GTiffGetWebPLossless(papszCreationOptions))
4280 : {
4281 6 : AppendMetadataItem(&psRoot, &psTail,
4282 : "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
4283 : nullptr, "IMAGE_STRUCTURE");
4284 : }
4285 : else
4286 : {
4287 25 : AppendMetadataItem(
4288 : &psRoot, &psTail, "WEBP_LEVEL",
4289 25 : CPLSPrintf("%d", GTiffGetWebPLevel(papszCreationOptions)),
4290 : 0, nullptr, "IMAGE_STRUCTURE");
4291 : }
4292 : }
4293 4161 : else if (pszCompress && STARTS_WITH_CI(pszCompress, "LERC"))
4294 : {
4295 : const double dfMaxZError =
4296 97 : GTiffGetLERCMaxZError(papszCreationOptions);
4297 : const double dfMaxZErrorOverview =
4298 97 : GTiffGetLERCMaxZErrorOverview(papszCreationOptions);
4299 97 : if (dfMaxZError == 0.0 && dfMaxZErrorOverview == 0.0)
4300 : {
4301 83 : AppendMetadataItem(&psRoot, &psTail,
4302 : "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
4303 : nullptr, "IMAGE_STRUCTURE");
4304 : }
4305 : else
4306 : {
4307 14 : AppendMetadataItem(&psRoot, &psTail, "MAX_Z_ERROR",
4308 : CSLFetchNameValueDef(papszCreationOptions,
4309 : "MAX_Z_ERROR", ""),
4310 : 0, nullptr, "IMAGE_STRUCTURE");
4311 14 : if (dfMaxZError != dfMaxZErrorOverview)
4312 : {
4313 3 : AppendMetadataItem(
4314 : &psRoot, &psTail, "MAX_Z_ERROR_OVERVIEW",
4315 : CSLFetchNameValueDef(papszCreationOptions,
4316 : "MAX_Z_ERROR_OVERVIEW", ""),
4317 : 0, nullptr, "IMAGE_STRUCTURE");
4318 : }
4319 97 : }
4320 : }
4321 : #if HAVE_JXL
4322 4064 : else if (pszCompress && EQUAL(pszCompress, "JXL"))
4323 : {
4324 95 : float fDistance = 0.0f;
4325 95 : if (GTiffGetJXLLossless(papszCreationOptions))
4326 : {
4327 78 : AppendMetadataItem(&psRoot, &psTail,
4328 : "COMPRESSION_REVERSIBILITY", "LOSSLESS", 0,
4329 : nullptr, "IMAGE_STRUCTURE");
4330 : }
4331 : else
4332 : {
4333 17 : fDistance = GTiffGetJXLDistance(papszCreationOptions);
4334 17 : AppendMetadataItem(&psRoot, &psTail, "JXL_DISTANCE",
4335 : CPLSPrintf("%f", fDistance), 0, nullptr,
4336 : "IMAGE_STRUCTURE");
4337 : }
4338 : const float fAlphaDistance =
4339 95 : GTiffGetJXLAlphaDistance(papszCreationOptions);
4340 95 : if (fAlphaDistance >= 0.0f && fAlphaDistance != fDistance)
4341 : {
4342 2 : AppendMetadataItem(&psRoot, &psTail, "JXL_ALPHA_DISTANCE",
4343 : CPLSPrintf("%f", fAlphaDistance), 0, nullptr,
4344 : "IMAGE_STRUCTURE");
4345 : }
4346 95 : AppendMetadataItem(
4347 : &psRoot, &psTail, "JXL_EFFORT",
4348 : CPLSPrintf("%d", GTiffGetJXLEffort(papszCreationOptions)), 0,
4349 : nullptr, "IMAGE_STRUCTURE");
4350 : }
4351 : #endif
4352 : }
4353 :
4354 : /* -------------------------------------------------------------------- */
4355 : /* Write out the generic XML metadata if there is any. */
4356 : /* -------------------------------------------------------------------- */
4357 4197 : if (psRoot != nullptr)
4358 : {
4359 569 : bool bRet = true;
4360 :
4361 569 : if (eProfile == GTiffProfile::GDALGEOTIFF)
4362 : {
4363 552 : char *pszXML_MD = CPLSerializeXMLTree(psRoot);
4364 552 : TIFFSetField(l_hTIFF, TIFFTAG_GDAL_METADATA, pszXML_MD);
4365 552 : CPLFree(pszXML_MD);
4366 : }
4367 : else
4368 : {
4369 17 : if (bSrcIsGeoTIFF)
4370 11 : cpl::down_cast<GTiffDataset *>(poSrcDS)->PushMetadataToPam();
4371 : else
4372 6 : bRet = false;
4373 : }
4374 :
4375 569 : CPLDestroyXMLNode(psRoot);
4376 :
4377 569 : return bRet;
4378 : }
4379 :
4380 : // If we have no more metadata but it existed before,
4381 : // remove the GDAL_METADATA tag.
4382 3628 : if (eProfile == GTiffProfile::GDALGEOTIFF)
4383 : {
4384 3607 : char *pszText = nullptr;
4385 3607 : if (TIFFGetField(l_hTIFF, TIFFTAG_GDAL_METADATA, &pszText))
4386 : {
4387 6 : TIFFUnsetField(l_hTIFF, TIFFTAG_GDAL_METADATA);
4388 : }
4389 : }
4390 :
4391 3628 : return true;
4392 : }
4393 :
4394 : /************************************************************************/
4395 : /* PushMetadataToPam() */
4396 : /* */
4397 : /* When producing a strict profile TIFF or if our aggregate */
4398 : /* metadata is too big for a single tiff tag we may end up */
4399 : /* needing to write it via the PAM mechanisms. This method */
4400 : /* copies all the appropriate metadata into the PAM level */
4401 : /* metadata object but with special care to avoid copying */
4402 : /* metadata handled in other ways in TIFF format. */
4403 : /************************************************************************/
4404 :
4405 20 : void GTiffDataset::PushMetadataToPam()
4406 :
4407 : {
4408 20 : if (GetPamFlags() & GPF_DISABLED)
4409 0 : return;
4410 :
4411 20 : const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
4412 20 : GDALDataset::ToHandle(this), m_nPhotometric, m_papszCreationOptions);
4413 :
4414 66 : for (int nBand = 0; nBand <= GetRasterCount(); ++nBand)
4415 : {
4416 46 : GDALMultiDomainMetadata *poSrcMDMD = nullptr;
4417 46 : GTiffRasterBand *poBand = nullptr;
4418 :
4419 46 : if (nBand == 0)
4420 : {
4421 20 : poSrcMDMD = &(this->m_oGTiffMDMD);
4422 : }
4423 : else
4424 : {
4425 26 : poBand = cpl::down_cast<GTiffRasterBand *>(GetRasterBand(nBand));
4426 26 : poSrcMDMD = &(poBand->m_oGTiffMDMD);
4427 : }
4428 :
4429 : /* --------------------------------------------------------------------
4430 : */
4431 : /* Loop over the available domains. */
4432 : /* --------------------------------------------------------------------
4433 : */
4434 46 : CSLConstList papszDomainList = poSrcMDMD->GetDomainList();
4435 96 : for (int iDomain = 0; papszDomainList && papszDomainList[iDomain];
4436 : ++iDomain)
4437 : {
4438 50 : char **papszMD = poSrcMDMD->GetMetadata(papszDomainList[iDomain]);
4439 :
4440 50 : if (EQUAL(papszDomainList[iDomain], MD_DOMAIN_RPC) ||
4441 50 : EQUAL(papszDomainList[iDomain], MD_DOMAIN_IMD) ||
4442 50 : EQUAL(papszDomainList[iDomain], "_temporary_") ||
4443 50 : EQUAL(papszDomainList[iDomain], "IMAGE_STRUCTURE") ||
4444 30 : EQUAL(papszDomainList[iDomain], "COLOR_PROFILE"))
4445 20 : continue;
4446 :
4447 30 : papszMD = CSLDuplicate(papszMD);
4448 :
4449 105 : for (int i = CSLCount(papszMD) - 1; i >= 0; --i)
4450 : {
4451 75 : if (STARTS_WITH_CI(papszMD[i], "TIFFTAG_") ||
4452 75 : EQUALN(papszMD[i], GDALMD_AREA_OR_POINT,
4453 : strlen(GDALMD_AREA_OR_POINT)))
4454 4 : papszMD = CSLRemoveStrings(papszMD, i, 1, nullptr);
4455 : }
4456 :
4457 30 : if (nBand == 0)
4458 16 : GDALPamDataset::SetMetadata(papszMD, papszDomainList[iDomain]);
4459 : else
4460 14 : poBand->GDALPamRasterBand::SetMetadata(
4461 14 : papszMD, papszDomainList[iDomain]);
4462 :
4463 30 : CSLDestroy(papszMD);
4464 : }
4465 :
4466 : /* --------------------------------------------------------------------
4467 : */
4468 : /* Handle some "special domain" stuff. */
4469 : /* --------------------------------------------------------------------
4470 : */
4471 46 : if (poBand != nullptr)
4472 : {
4473 26 : poBand->GDALPamRasterBand::SetOffset(poBand->GetOffset());
4474 26 : poBand->GDALPamRasterBand::SetScale(poBand->GetScale());
4475 26 : poBand->GDALPamRasterBand::SetUnitType(poBand->GetUnitType());
4476 26 : poBand->GDALPamRasterBand::SetDescription(poBand->GetDescription());
4477 26 : if (!bStandardColorInterp)
4478 : {
4479 3 : poBand->GDALPamRasterBand::SetColorInterpretation(
4480 3 : poBand->GetColorInterpretation());
4481 : }
4482 : }
4483 : }
4484 20 : MarkPamDirty();
4485 : }
4486 :
4487 : /************************************************************************/
4488 : /* WriteNoDataValue() */
4489 : /************************************************************************/
4490 :
4491 340 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, double dfNoData)
4492 :
4493 : {
4494 680 : CPLString osVal(GTiffFormatGDALNoDataTagValue(dfNoData));
4495 340 : TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA, osVal.c_str());
4496 340 : }
4497 :
4498 2 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, int64_t nNoData)
4499 :
4500 : {
4501 2 : TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA,
4502 : CPLSPrintf(CPL_FRMT_GIB, static_cast<GIntBig>(nNoData)));
4503 2 : }
4504 :
4505 2 : void GTiffDataset::WriteNoDataValue(TIFF *hTIFF, uint64_t nNoData)
4506 :
4507 : {
4508 2 : TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA,
4509 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoData)));
4510 2 : }
4511 :
4512 : /************************************************************************/
4513 : /* UnsetNoDataValue() */
4514 : /************************************************************************/
4515 :
4516 1 : void GTiffDataset::UnsetNoDataValue(TIFF *l_hTIFF)
4517 :
4518 : {
4519 1 : TIFFUnsetField(l_hTIFF, TIFFTAG_GDAL_NODATA);
4520 1 : }
4521 :
4522 : /************************************************************************/
4523 : /* SaveICCProfile() */
4524 : /* */
4525 : /* Save ICC Profile or colorimetric data into file */
4526 : /* pDS: */
4527 : /* Dataset that contains the metadata with the ICC or colorimetric */
4528 : /* data. If this argument is specified, all other arguments are */
4529 : /* ignored. Set them to NULL or 0. */
4530 : /* hTIFF: */
4531 : /* Pointer to TIFF handle. Only needed if pDS is NULL or */
4532 : /* pDS->m_hTIFF is NULL. */
4533 : /* papszParamList: */
4534 : /* Options containing the ICC profile or colorimetric metadata. */
4535 : /* Ignored if pDS is not NULL. */
4536 : /* nBitsPerSample: */
4537 : /* Bits per sample. Ignored if pDS is not NULL. */
4538 : /************************************************************************/
4539 :
4540 6029 : void GTiffDataset::SaveICCProfile(GTiffDataset *pDS, TIFF *l_hTIFF,
4541 : char **papszParamList,
4542 : uint32_t l_nBitsPerSample)
4543 : {
4544 6029 : if ((pDS != nullptr) && (pDS->eAccess != GA_Update))
4545 0 : return;
4546 :
4547 6029 : if (l_hTIFF == nullptr)
4548 : {
4549 2 : if (pDS == nullptr)
4550 0 : return;
4551 :
4552 2 : l_hTIFF = pDS->m_hTIFF;
4553 2 : if (l_hTIFF == nullptr)
4554 0 : return;
4555 : }
4556 :
4557 6029 : if ((papszParamList == nullptr) && (pDS == nullptr))
4558 2265 : return;
4559 :
4560 3764 : const char *pszValue = nullptr;
4561 3764 : if (pDS != nullptr)
4562 2 : pszValue = pDS->GetMetadataItem("SOURCE_ICC_PROFILE", "COLOR_PROFILE");
4563 : else
4564 3762 : pszValue = CSLFetchNameValue(papszParamList, "SOURCE_ICC_PROFILE");
4565 3764 : if (pszValue != nullptr)
4566 : {
4567 8 : char *pEmbedBuffer = CPLStrdup(pszValue);
4568 : int32_t nEmbedLen =
4569 8 : CPLBase64DecodeInPlace(reinterpret_cast<GByte *>(pEmbedBuffer));
4570 :
4571 8 : TIFFSetField(l_hTIFF, TIFFTAG_ICCPROFILE, nEmbedLen, pEmbedBuffer);
4572 :
4573 8 : CPLFree(pEmbedBuffer);
4574 : }
4575 : else
4576 : {
4577 : // Output colorimetric data.
4578 3756 : float pCHR[6] = {}; // Primaries.
4579 3756 : uint16_t pTXR[6] = {}; // Transfer range.
4580 3756 : const char *pszCHRNames[] = {"SOURCE_PRIMARIES_RED",
4581 : "SOURCE_PRIMARIES_GREEN",
4582 : "SOURCE_PRIMARIES_BLUE"};
4583 3756 : const char *pszTXRNames[] = {"TIFFTAG_TRANSFERRANGE_BLACK",
4584 : "TIFFTAG_TRANSFERRANGE_WHITE"};
4585 :
4586 : // Output chromacities.
4587 3756 : bool bOutputCHR = true;
4588 3771 : for (int i = 0; i < 3 && bOutputCHR; ++i)
4589 : {
4590 3766 : if (pDS != nullptr)
4591 : pszValue =
4592 3 : pDS->GetMetadataItem(pszCHRNames[i], "COLOR_PROFILE");
4593 : else
4594 3763 : pszValue = CSLFetchNameValue(papszParamList, pszCHRNames[i]);
4595 3766 : if (pszValue == nullptr)
4596 : {
4597 3751 : bOutputCHR = false;
4598 3751 : break;
4599 : }
4600 :
4601 15 : char **papszTokens = CSLTokenizeString2(pszValue, ",",
4602 : CSLT_ALLOWEMPTYTOKENS |
4603 : CSLT_STRIPLEADSPACES |
4604 : CSLT_STRIPENDSPACES);
4605 :
4606 15 : if (CSLCount(papszTokens) != 3)
4607 : {
4608 0 : bOutputCHR = false;
4609 0 : CSLDestroy(papszTokens);
4610 0 : break;
4611 : }
4612 :
4613 60 : for (int j = 0; j < 3; ++j)
4614 : {
4615 45 : float v = static_cast<float>(CPLAtof(papszTokens[j]));
4616 :
4617 45 : if (j == 2)
4618 : {
4619 : // Last term of xyY color must be 1.0.
4620 15 : if (v != 1.0)
4621 : {
4622 0 : bOutputCHR = false;
4623 0 : break;
4624 : }
4625 : }
4626 : else
4627 : {
4628 30 : pCHR[i * 2 + j] = v;
4629 : }
4630 : }
4631 :
4632 15 : CSLDestroy(papszTokens);
4633 : }
4634 :
4635 3756 : if (bOutputCHR)
4636 : {
4637 5 : TIFFSetField(l_hTIFF, TIFFTAG_PRIMARYCHROMATICITIES, pCHR);
4638 : }
4639 :
4640 : // Output whitepoint.
4641 3756 : if (pDS != nullptr)
4642 : pszValue =
4643 1 : pDS->GetMetadataItem("SOURCE_WHITEPOINT", "COLOR_PROFILE");
4644 : else
4645 3755 : pszValue = CSLFetchNameValue(papszParamList, "SOURCE_WHITEPOINT");
4646 3756 : if (pszValue != nullptr)
4647 : {
4648 5 : char **papszTokens = CSLTokenizeString2(pszValue, ",",
4649 : CSLT_ALLOWEMPTYTOKENS |
4650 : CSLT_STRIPLEADSPACES |
4651 : CSLT_STRIPENDSPACES);
4652 :
4653 5 : bool bOutputWhitepoint = true;
4654 5 : float pWP[2] = {0.0f, 0.0f}; // Whitepoint
4655 5 : if (CSLCount(papszTokens) != 3)
4656 : {
4657 0 : bOutputWhitepoint = false;
4658 : }
4659 : else
4660 : {
4661 20 : for (int j = 0; j < 3; ++j)
4662 : {
4663 15 : const float v = static_cast<float>(CPLAtof(papszTokens[j]));
4664 :
4665 15 : if (j == 2)
4666 : {
4667 : // Last term of xyY color must be 1.0.
4668 5 : if (v != 1.0)
4669 : {
4670 0 : bOutputWhitepoint = false;
4671 0 : break;
4672 : }
4673 : }
4674 : else
4675 : {
4676 10 : pWP[j] = v;
4677 : }
4678 : }
4679 : }
4680 5 : CSLDestroy(papszTokens);
4681 :
4682 5 : if (bOutputWhitepoint)
4683 : {
4684 5 : TIFFSetField(l_hTIFF, TIFFTAG_WHITEPOINT, pWP);
4685 : }
4686 : }
4687 :
4688 : // Set transfer function metadata.
4689 3756 : char const *pszTFRed = nullptr;
4690 3756 : if (pDS != nullptr)
4691 1 : pszTFRed = pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_RED",
4692 : "COLOR_PROFILE");
4693 : else
4694 3755 : pszTFRed = CSLFetchNameValue(papszParamList,
4695 : "TIFFTAG_TRANSFERFUNCTION_RED");
4696 :
4697 3756 : char const *pszTFGreen = nullptr;
4698 3756 : if (pDS != nullptr)
4699 1 : pszTFGreen = pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_GREEN",
4700 : "COLOR_PROFILE");
4701 : else
4702 3755 : pszTFGreen = CSLFetchNameValue(papszParamList,
4703 : "TIFFTAG_TRANSFERFUNCTION_GREEN");
4704 :
4705 3756 : char const *pszTFBlue = nullptr;
4706 3756 : if (pDS != nullptr)
4707 1 : pszTFBlue = pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_BLUE",
4708 : "COLOR_PROFILE");
4709 : else
4710 3755 : pszTFBlue = CSLFetchNameValue(papszParamList,
4711 : "TIFFTAG_TRANSFERFUNCTION_BLUE");
4712 :
4713 3756 : if ((pszTFRed != nullptr) && (pszTFGreen != nullptr) &&
4714 : (pszTFBlue != nullptr))
4715 : {
4716 : // Get length of table.
4717 4 : const int nTransferFunctionLength =
4718 4 : 1 << ((pDS != nullptr) ? pDS->m_nBitsPerSample
4719 : : l_nBitsPerSample);
4720 :
4721 4 : char **papszTokensRed = CSLTokenizeString2(
4722 : pszTFRed, ",",
4723 : CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
4724 : CSLT_STRIPENDSPACES);
4725 4 : char **papszTokensGreen = CSLTokenizeString2(
4726 : pszTFGreen, ",",
4727 : CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
4728 : CSLT_STRIPENDSPACES);
4729 4 : char **papszTokensBlue = CSLTokenizeString2(
4730 : pszTFBlue, ",",
4731 : CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
4732 : CSLT_STRIPENDSPACES);
4733 :
4734 4 : if ((CSLCount(papszTokensRed) == nTransferFunctionLength) &&
4735 8 : (CSLCount(papszTokensGreen) == nTransferFunctionLength) &&
4736 4 : (CSLCount(papszTokensBlue) == nTransferFunctionLength))
4737 : {
4738 : uint16_t *pTransferFuncRed = static_cast<uint16_t *>(
4739 4 : CPLMalloc(sizeof(uint16_t) * nTransferFunctionLength));
4740 : uint16_t *pTransferFuncGreen = static_cast<uint16_t *>(
4741 4 : CPLMalloc(sizeof(uint16_t) * nTransferFunctionLength));
4742 : uint16_t *pTransferFuncBlue = static_cast<uint16_t *>(
4743 4 : CPLMalloc(sizeof(uint16_t) * nTransferFunctionLength));
4744 :
4745 : // Convert our table in string format into int16_t format.
4746 1028 : for (int i = 0; i < nTransferFunctionLength; ++i)
4747 : {
4748 1024 : pTransferFuncRed[i] =
4749 1024 : static_cast<uint16_t>(atoi(papszTokensRed[i]));
4750 1024 : pTransferFuncGreen[i] =
4751 1024 : static_cast<uint16_t>(atoi(papszTokensGreen[i]));
4752 1024 : pTransferFuncBlue[i] =
4753 1024 : static_cast<uint16_t>(atoi(papszTokensBlue[i]));
4754 : }
4755 :
4756 4 : TIFFSetField(l_hTIFF, TIFFTAG_TRANSFERFUNCTION,
4757 : pTransferFuncRed, pTransferFuncGreen,
4758 : pTransferFuncBlue);
4759 :
4760 4 : CPLFree(pTransferFuncRed);
4761 4 : CPLFree(pTransferFuncGreen);
4762 4 : CPLFree(pTransferFuncBlue);
4763 : }
4764 :
4765 4 : CSLDestroy(papszTokensRed);
4766 4 : CSLDestroy(papszTokensGreen);
4767 4 : CSLDestroy(papszTokensBlue);
4768 : }
4769 :
4770 : // Output transfer range.
4771 3756 : bool bOutputTransferRange = true;
4772 3756 : for (int i = 0; (i < 2) && bOutputTransferRange; ++i)
4773 : {
4774 3756 : if (pDS != nullptr)
4775 : pszValue =
4776 1 : pDS->GetMetadataItem(pszTXRNames[i], "COLOR_PROFILE");
4777 : else
4778 3755 : pszValue = CSLFetchNameValue(papszParamList, pszTXRNames[i]);
4779 3756 : if (pszValue == nullptr)
4780 : {
4781 3756 : bOutputTransferRange = false;
4782 3756 : break;
4783 : }
4784 :
4785 0 : char **papszTokens = CSLTokenizeString2(pszValue, ",",
4786 : CSLT_ALLOWEMPTYTOKENS |
4787 : CSLT_STRIPLEADSPACES |
4788 : CSLT_STRIPENDSPACES);
4789 :
4790 0 : if (CSLCount(papszTokens) != 3)
4791 : {
4792 0 : bOutputTransferRange = false;
4793 0 : CSLDestroy(papszTokens);
4794 0 : break;
4795 : }
4796 :
4797 0 : for (int j = 0; j < 3; ++j)
4798 : {
4799 0 : pTXR[i + j * 2] = static_cast<uint16_t>(atoi(papszTokens[j]));
4800 : }
4801 :
4802 0 : CSLDestroy(papszTokens);
4803 : }
4804 :
4805 3756 : if (bOutputTransferRange)
4806 : {
4807 0 : const int TIFFTAG_TRANSFERRANGE = 0x0156;
4808 0 : TIFFSetField(l_hTIFF, TIFFTAG_TRANSFERRANGE, pTXR);
4809 : }
4810 : }
4811 : }
4812 :
4813 10137 : static signed char GTiffGetLZMAPreset(char **papszOptions)
4814 : {
4815 10137 : int nLZMAPreset = -1;
4816 10137 : const char *pszValue = CSLFetchNameValue(papszOptions, "LZMA_PRESET");
4817 10137 : if (pszValue != nullptr)
4818 : {
4819 20 : nLZMAPreset = atoi(pszValue);
4820 20 : if (!(nLZMAPreset >= 0 && nLZMAPreset <= 9))
4821 : {
4822 0 : CPLError(CE_Warning, CPLE_IllegalArg,
4823 : "LZMA_PRESET=%s value not recognised, ignoring.",
4824 : pszValue);
4825 0 : nLZMAPreset = -1;
4826 : }
4827 : }
4828 10137 : return static_cast<signed char>(nLZMAPreset);
4829 : }
4830 :
4831 10137 : static signed char GTiffGetZSTDPreset(char **papszOptions)
4832 : {
4833 10137 : int nZSTDLevel = -1;
4834 10137 : const char *pszValue = CSLFetchNameValue(papszOptions, "ZSTD_LEVEL");
4835 10137 : if (pszValue != nullptr)
4836 : {
4837 24 : nZSTDLevel = atoi(pszValue);
4838 24 : if (!(nZSTDLevel >= 1 && nZSTDLevel <= 22))
4839 : {
4840 0 : CPLError(CE_Warning, CPLE_IllegalArg,
4841 : "ZSTD_LEVEL=%s value not recognised, ignoring.", pszValue);
4842 0 : nZSTDLevel = -1;
4843 : }
4844 : }
4845 10137 : return static_cast<signed char>(nZSTDLevel);
4846 : }
4847 :
4848 10137 : static signed char GTiffGetZLevel(char **papszOptions)
4849 : {
4850 10137 : int nZLevel = -1;
4851 10137 : const char *pszValue = CSLFetchNameValue(papszOptions, "ZLEVEL");
4852 10137 : if (pszValue != nullptr)
4853 : {
4854 42 : nZLevel = atoi(pszValue);
4855 : #ifdef TIFFTAG_DEFLATE_SUBCODEC
4856 42 : constexpr int nMaxLevel = 12;
4857 : #ifndef LIBDEFLATE_SUPPORT
4858 : if (nZLevel > 9 && nZLevel <= nMaxLevel)
4859 : {
4860 : CPLDebug("GTiff",
4861 : "ZLEVEL=%d not supported in a non-libdeflate enabled "
4862 : "libtiff build. Capping to 9",
4863 : nZLevel);
4864 : nZLevel = 9;
4865 : }
4866 : #endif
4867 : #else
4868 : constexpr int nMaxLevel = 9;
4869 : #endif
4870 42 : if (nZLevel < 1 || nZLevel > nMaxLevel)
4871 : {
4872 0 : CPLError(CE_Warning, CPLE_IllegalArg,
4873 : "ZLEVEL=%s value not recognised, ignoring.", pszValue);
4874 0 : nZLevel = -1;
4875 : }
4876 : }
4877 10137 : return static_cast<signed char>(nZLevel);
4878 : }
4879 :
4880 10137 : static signed char GTiffGetJpegQuality(char **papszOptions)
4881 : {
4882 10137 : int nJpegQuality = -1;
4883 10137 : const char *pszValue = CSLFetchNameValue(papszOptions, "JPEG_QUALITY");
4884 10137 : if (pszValue != nullptr)
4885 : {
4886 1939 : nJpegQuality = atoi(pszValue);
4887 1939 : if (nJpegQuality < 1 || nJpegQuality > 100)
4888 : {
4889 0 : CPLError(CE_Warning, CPLE_IllegalArg,
4890 : "JPEG_QUALITY=%s value not recognised, ignoring.",
4891 : pszValue);
4892 0 : nJpegQuality = -1;
4893 : }
4894 : }
4895 10137 : return static_cast<signed char>(nJpegQuality);
4896 : }
4897 :
4898 10137 : static signed char GTiffGetJpegTablesMode(char **papszOptions)
4899 : {
4900 10137 : return static_cast<signed char>(atoi(
4901 : CSLFetchNameValueDef(papszOptions, "JPEGTABLESMODE",
4902 10137 : CPLSPrintf("%d", knGTIFFJpegTablesModeDefault))));
4903 : }
4904 :
4905 : /************************************************************************/
4906 : /* GetDiscardLsbOption() */
4907 : /************************************************************************/
4908 :
4909 4061 : static GTiffDataset::MaskOffset *GetDiscardLsbOption(TIFF *hTIFF,
4910 : char **papszOptions)
4911 : {
4912 4061 : const char *pszBits = CSLFetchNameValue(papszOptions, "DISCARD_LSB");
4913 4061 : if (pszBits == nullptr)
4914 3939 : return nullptr;
4915 :
4916 122 : uint16_t nPhotometric = 0;
4917 122 : TIFFGetFieldDefaulted(hTIFF, TIFFTAG_PHOTOMETRIC, &nPhotometric);
4918 :
4919 122 : uint16_t nBitsPerSample = 0;
4920 122 : if (!TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &nBitsPerSample))
4921 0 : nBitsPerSample = 1;
4922 :
4923 122 : uint16_t nSamplesPerPixel = 0;
4924 122 : if (!TIFFGetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSamplesPerPixel))
4925 0 : nSamplesPerPixel = 1;
4926 :
4927 122 : uint16_t nSampleFormat = 0;
4928 122 : if (!TIFFGetField(hTIFF, TIFFTAG_SAMPLEFORMAT, &nSampleFormat))
4929 0 : nSampleFormat = SAMPLEFORMAT_UINT;
4930 :
4931 122 : if (nPhotometric == PHOTOMETRIC_PALETTE)
4932 : {
4933 1 : CPLError(CE_Warning, CPLE_AppDefined,
4934 : "DISCARD_LSB ignored on a paletted image");
4935 1 : return nullptr;
4936 : }
4937 121 : if (!(nBitsPerSample == 8 || nBitsPerSample == 16 || nBitsPerSample == 32 ||
4938 13 : nBitsPerSample == 64))
4939 : {
4940 1 : CPLError(CE_Warning, CPLE_AppDefined,
4941 : "DISCARD_LSB ignored on non 8, 16, 32 or 64 bits images");
4942 1 : return nullptr;
4943 : }
4944 :
4945 240 : const CPLStringList aosTokens(CSLTokenizeString2(pszBits, ",", 0));
4946 120 : const int nTokens = aosTokens.size();
4947 120 : GTiffDataset::MaskOffset *panMaskOffsetLsb = nullptr;
4948 120 : if (nTokens == 1 || nTokens == nSamplesPerPixel)
4949 : {
4950 : panMaskOffsetLsb = static_cast<GTiffDataset::MaskOffset *>(
4951 119 : CPLCalloc(nSamplesPerPixel, sizeof(GTiffDataset::MaskOffset)));
4952 374 : for (int i = 0; i < nSamplesPerPixel; ++i)
4953 : {
4954 255 : const int nBits = atoi(aosTokens[nTokens == 1 ? 0 : i]);
4955 510 : const int nMaxBits = (nSampleFormat == SAMPLEFORMAT_IEEEFP)
4956 484 : ? ((nBitsPerSample == 32) ? 23 - 1
4957 26 : : (nBitsPerSample == 64) ? 53 - 1
4958 : : 0)
4959 203 : : nSampleFormat == SAMPLEFORMAT_INT
4960 203 : ? nBitsPerSample - 2
4961 119 : : nBitsPerSample - 1;
4962 :
4963 255 : if (nBits < 0 || nBits > nMaxBits)
4964 : {
4965 0 : CPLError(
4966 : CE_Warning, CPLE_AppDefined,
4967 : "DISCARD_LSB ignored: values should be in [0,%d] range",
4968 : nMaxBits);
4969 0 : VSIFree(panMaskOffsetLsb);
4970 0 : return nullptr;
4971 : }
4972 255 : panMaskOffsetLsb[i].nMask =
4973 255 : ~((static_cast<uint64_t>(1) << nBits) - 1);
4974 255 : if (nBits > 1)
4975 : {
4976 249 : panMaskOffsetLsb[i].nRoundUpBitTest = static_cast<uint64_t>(1)
4977 249 : << (nBits - 1);
4978 : }
4979 119 : }
4980 : }
4981 : else
4982 : {
4983 1 : CPLError(CE_Warning, CPLE_AppDefined,
4984 : "DISCARD_LSB ignored: wrong number of components");
4985 : }
4986 120 : return panMaskOffsetLsb;
4987 : }
4988 :
4989 4061 : void GTiffDataset::GetDiscardLsbOption(char **papszOptions)
4990 : {
4991 4061 : m_panMaskOffsetLsb = ::GetDiscardLsbOption(m_hTIFF, papszOptions);
4992 4061 : }
4993 :
4994 : /************************************************************************/
4995 : /* GetProfile() */
4996 : /************************************************************************/
4997 :
4998 10170 : static GTiffProfile GetProfile(const char *pszProfile)
4999 : {
5000 10170 : GTiffProfile eProfile = GTiffProfile::GDALGEOTIFF;
5001 10170 : if (pszProfile != nullptr)
5002 : {
5003 64 : if (EQUAL(pszProfile, szPROFILE_BASELINE))
5004 44 : eProfile = GTiffProfile::BASELINE;
5005 20 : else if (EQUAL(pszProfile, szPROFILE_GeoTIFF))
5006 18 : eProfile = GTiffProfile::GEOTIFF;
5007 2 : else if (!EQUAL(pszProfile, szPROFILE_GDALGeoTIFF))
5008 : {
5009 0 : CPLError(CE_Warning, CPLE_NotSupported,
5010 : "Unsupported value for PROFILE: %s", pszProfile);
5011 : }
5012 : }
5013 10170 : return eProfile;
5014 : }
5015 :
5016 : /************************************************************************/
5017 : /* GTiffCreate() */
5018 : /* */
5019 : /* Shared functionality between GTiffDataset::Create() and */
5020 : /* GTiffCreateCopy() for creating TIFF file based on a set of */
5021 : /* options and a configuration. */
5022 : /************************************************************************/
5023 :
5024 6082 : TIFF *GTiffDataset::CreateLL(const char *pszFilename, int nXSize, int nYSize,
5025 : int l_nBands, GDALDataType eType,
5026 : double dfExtraSpaceForOverviews,
5027 : char **papszParamList, VSILFILE **pfpL,
5028 : CPLString &l_osTmpFilename)
5029 :
5030 : {
5031 6082 : GTiffOneTimeInit();
5032 :
5033 : /* -------------------------------------------------------------------- */
5034 : /* Blow on a few errors. */
5035 : /* -------------------------------------------------------------------- */
5036 6082 : if (nXSize < 1 || nYSize < 1 || l_nBands < 1)
5037 : {
5038 1 : ReportError(
5039 : pszFilename, CE_Failure, CPLE_AppDefined,
5040 : "Attempt to create %dx%dx%d TIFF file, but width, height and bands"
5041 : "must be positive.",
5042 : nXSize, nYSize, l_nBands);
5043 :
5044 1 : return nullptr;
5045 : }
5046 :
5047 6081 : if (l_nBands > 65535)
5048 : {
5049 1 : ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
5050 : "Attempt to create %dx%dx%d TIFF file, but bands "
5051 : "must be lesser or equal to 65535.",
5052 : nXSize, nYSize, l_nBands);
5053 :
5054 1 : return nullptr;
5055 : }
5056 :
5057 : /* -------------------------------------------------------------------- */
5058 : /* Setup values based on options. */
5059 : /* -------------------------------------------------------------------- */
5060 : const GTiffProfile eProfile =
5061 6080 : GetProfile(CSLFetchNameValue(papszParamList, "PROFILE"));
5062 :
5063 6080 : const bool bTiled = CPLFetchBool(papszParamList, "TILED", false);
5064 :
5065 6080 : int l_nBlockXSize = 0;
5066 6080 : const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKXSIZE");
5067 6080 : if (pszValue != nullptr)
5068 : {
5069 330 : l_nBlockXSize = atoi(pszValue);
5070 330 : if (l_nBlockXSize < 0)
5071 : {
5072 0 : ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
5073 : "Invalid value for BLOCKXSIZE");
5074 0 : return nullptr;
5075 : }
5076 : }
5077 :
5078 6080 : int l_nBlockYSize = 0;
5079 6080 : pszValue = CSLFetchNameValue(papszParamList, "BLOCKYSIZE");
5080 6080 : if (pszValue != nullptr)
5081 : {
5082 2395 : l_nBlockYSize = atoi(pszValue);
5083 2395 : if (l_nBlockYSize < 0)
5084 : {
5085 0 : ReportError(pszFilename, CE_Failure, CPLE_IllegalArg,
5086 : "Invalid value for BLOCKYSIZE");
5087 0 : return nullptr;
5088 : }
5089 : }
5090 :
5091 6080 : if (bTiled)
5092 : {
5093 619 : if (l_nBlockXSize == 0)
5094 298 : l_nBlockXSize = 256;
5095 :
5096 619 : if (l_nBlockYSize == 0)
5097 296 : l_nBlockYSize = 256;
5098 : }
5099 :
5100 6080 : int nPlanar = 0;
5101 6080 : pszValue = CSLFetchNameValue(papszParamList, "INTERLEAVE");
5102 6080 : if (pszValue != nullptr)
5103 : {
5104 529 : if (EQUAL(pszValue, "PIXEL"))
5105 227 : nPlanar = PLANARCONFIG_CONTIG;
5106 302 : else if (EQUAL(pszValue, "BAND"))
5107 : {
5108 301 : nPlanar = PLANARCONFIG_SEPARATE;
5109 : }
5110 : else
5111 : {
5112 1 : ReportError(
5113 : pszFilename, CE_Failure, CPLE_IllegalArg,
5114 : "INTERLEAVE=%s unsupported, value must be PIXEL or BAND.",
5115 : pszValue);
5116 1 : return nullptr;
5117 : }
5118 : }
5119 : else
5120 : {
5121 5551 : nPlanar = PLANARCONFIG_CONTIG;
5122 : }
5123 :
5124 6079 : int l_nCompression = COMPRESSION_NONE;
5125 6079 : pszValue = CSLFetchNameValue(papszParamList, "COMPRESS");
5126 6079 : if (pszValue != nullptr)
5127 : {
5128 3067 : l_nCompression = GTIFFGetCompressionMethod(pszValue, "COMPRESS");
5129 3067 : if (l_nCompression < 0)
5130 0 : return nullptr;
5131 : }
5132 :
5133 : /* -------------------------------------------------------------------- */
5134 : /* How many bits per sample? We have a special case if NBITS */
5135 : /* specified for GDT_Byte, GDT_UInt16, GDT_UInt32. */
5136 : /* -------------------------------------------------------------------- */
5137 6079 : int l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
5138 6079 : if (CSLFetchNameValue(papszParamList, "NBITS") != nullptr)
5139 : {
5140 1748 : int nMinBits = 0;
5141 1748 : int nMaxBits = 0;
5142 1748 : l_nBitsPerSample = atoi(CSLFetchNameValue(papszParamList, "NBITS"));
5143 1748 : if (eType == GDT_Byte)
5144 : {
5145 527 : nMinBits = 1;
5146 527 : nMaxBits = 8;
5147 : }
5148 1221 : else if (eType == GDT_UInt16)
5149 : {
5150 1202 : nMinBits = 9;
5151 1202 : nMaxBits = 16;
5152 : }
5153 19 : else if (eType == GDT_UInt32)
5154 : {
5155 14 : nMinBits = 17;
5156 14 : nMaxBits = 32;
5157 : }
5158 5 : else if (eType == GDT_Float32)
5159 : {
5160 5 : if (l_nBitsPerSample != 16 && l_nBitsPerSample != 32)
5161 : {
5162 1 : ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
5163 : "Only NBITS=16 is supported for data type Float32");
5164 1 : l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
5165 : }
5166 : }
5167 : else
5168 : {
5169 0 : ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
5170 : "NBITS is not supported for data type %s",
5171 : GDALGetDataTypeName(eType));
5172 0 : l_nBitsPerSample = GDALGetDataTypeSizeBits(eType);
5173 : }
5174 :
5175 1748 : if (nMinBits != 0)
5176 : {
5177 1743 : if (l_nBitsPerSample < nMinBits)
5178 : {
5179 2 : ReportError(
5180 : pszFilename, CE_Warning, CPLE_AppDefined,
5181 : "NBITS=%d is invalid for data type %s. Using NBITS=%d",
5182 : l_nBitsPerSample, GDALGetDataTypeName(eType), nMinBits);
5183 2 : l_nBitsPerSample = nMinBits;
5184 : }
5185 1741 : else if (l_nBitsPerSample > nMaxBits)
5186 : {
5187 3 : ReportError(
5188 : pszFilename, CE_Warning, CPLE_AppDefined,
5189 : "NBITS=%d is invalid for data type %s. Using NBITS=%d",
5190 : l_nBitsPerSample, GDALGetDataTypeName(eType), nMaxBits);
5191 3 : l_nBitsPerSample = nMaxBits;
5192 : }
5193 : }
5194 : }
5195 :
5196 : #ifdef HAVE_JXL
5197 6079 : if (l_nCompression == COMPRESSION_JXL)
5198 : {
5199 : // Reflects tif_jxl's GetJXLDataType()
5200 98 : if (eType != GDT_Byte && eType != GDT_UInt16 && eType != GDT_Float32)
5201 : {
5202 0 : ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
5203 : "Data type %s not supported for JXL compression. Only "
5204 : "Byte, UInt16, Float32 are supported",
5205 : GDALGetDataTypeName(eType));
5206 0 : return nullptr;
5207 : }
5208 :
5209 : const struct
5210 : {
5211 : GDALDataType eDT;
5212 : int nBitsPerSample;
5213 98 : } asSupportedDTBitsPerSample[] = {
5214 : {GDT_Byte, 8},
5215 : {GDT_UInt16, 16},
5216 : {GDT_Float32, 32},
5217 : };
5218 :
5219 392 : for (const auto &sSupportedDTBitsPerSample : asSupportedDTBitsPerSample)
5220 : {
5221 294 : if (eType == sSupportedDTBitsPerSample.eDT &&
5222 98 : l_nBitsPerSample != sSupportedDTBitsPerSample.nBitsPerSample)
5223 : {
5224 0 : ReportError(
5225 : pszFilename, CE_Failure, CPLE_NotSupported,
5226 : "Bits per sample=%d not supported for JXL compression. "
5227 : "Only %d is supported for %s data type.",
5228 0 : l_nBitsPerSample, sSupportedDTBitsPerSample.nBitsPerSample,
5229 : GDALGetDataTypeName(eType));
5230 0 : return nullptr;
5231 : }
5232 : }
5233 : }
5234 : #endif
5235 :
5236 6079 : int nPredictor = PREDICTOR_NONE;
5237 6079 : pszValue = CSLFetchNameValue(papszParamList, "PREDICTOR");
5238 6079 : if (pszValue != nullptr)
5239 : {
5240 24 : nPredictor = atoi(pszValue);
5241 : }
5242 :
5243 : // Do early checks as libtiff will only error out when starting to write.
5244 6103 : if (nPredictor != PREDICTOR_NONE &&
5245 24 : CPLTestBool(CPLGetConfigOption("GDAL_GTIFF_PREDICTOR_CHECKS", "YES")))
5246 : {
5247 : #if (TIFFLIB_VERSION > 20210416) || defined(INTERNAL_LIBTIFF)
5248 : #define HAVE_PREDICTOR_2_FOR_64BIT
5249 : #endif
5250 24 : if (nPredictor == 2)
5251 : {
5252 21 : if (l_nBitsPerSample != 8 && l_nBitsPerSample != 16 &&
5253 : l_nBitsPerSample != 32
5254 : #ifdef HAVE_PREDICTOR_2_FOR_64BIT
5255 2 : && l_nBitsPerSample != 64
5256 : #endif
5257 : )
5258 : {
5259 : #if !defined(HAVE_PREDICTOR_2_FOR_64BIT)
5260 : if (l_nBitsPerSample == 64)
5261 : {
5262 : ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
5263 : "PREDICTOR=2 is supported on 64 bit samples "
5264 : "starting with libtiff > 4.3.0.");
5265 : }
5266 : else
5267 : #endif
5268 : {
5269 1 : ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
5270 : #ifdef HAVE_PREDICTOR_2_FOR_64BIT
5271 : "PREDICTOR=2 is only supported with 8/16/32/64 "
5272 : "bit samples."
5273 : #else
5274 : "PREDICTOR=2 is only supported with 8/16/32 "
5275 : "bit samples."
5276 : #endif
5277 : );
5278 : }
5279 1 : return nullptr;
5280 : }
5281 : }
5282 3 : else if (nPredictor == 3)
5283 : {
5284 2 : if (eType != GDT_Float32 && eType != GDT_Float64)
5285 : {
5286 1 : ReportError(
5287 : pszFilename, CE_Failure, CPLE_AppDefined,
5288 : "PREDICTOR=3 is only supported with Float32 or Float64.");
5289 1 : return nullptr;
5290 : }
5291 : }
5292 : else
5293 : {
5294 1 : ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
5295 : "PREDICTOR=%s is not supported.", pszValue);
5296 1 : return nullptr;
5297 : }
5298 : }
5299 :
5300 6076 : const int l_nZLevel = GTiffGetZLevel(papszParamList);
5301 6076 : const int l_nLZMAPreset = GTiffGetLZMAPreset(papszParamList);
5302 6076 : const int l_nZSTDLevel = GTiffGetZSTDPreset(papszParamList);
5303 6076 : const int l_nWebPLevel = GTiffGetWebPLevel(papszParamList);
5304 6076 : const bool l_bWebPLossless = GTiffGetWebPLossless(papszParamList);
5305 6076 : const int l_nJpegQuality = GTiffGetJpegQuality(papszParamList);
5306 6076 : const int l_nJpegTablesMode = GTiffGetJpegTablesMode(papszParamList);
5307 6076 : const double l_dfMaxZError = GTiffGetLERCMaxZError(papszParamList);
5308 : #if HAVE_JXL
5309 6076 : const bool l_bJXLLossless = GTiffGetJXLLossless(papszParamList);
5310 6076 : const uint32_t l_nJXLEffort = GTiffGetJXLEffort(papszParamList);
5311 6076 : const float l_fJXLDistance = GTiffGetJXLDistance(papszParamList);
5312 6076 : const float l_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszParamList);
5313 : #endif
5314 : /* -------------------------------------------------------------------- */
5315 : /* Streaming related code */
5316 : /* -------------------------------------------------------------------- */
5317 12152 : const CPLString osOriFilename(pszFilename);
5318 12152 : bool bStreaming = strcmp(pszFilename, "/vsistdout/") == 0 ||
5319 6076 : CPLFetchBool(papszParamList, "STREAMABLE_OUTPUT", false);
5320 : #ifdef S_ISFIFO
5321 6076 : if (!bStreaming)
5322 : {
5323 : VSIStatBufL sStat;
5324 6064 : if (VSIStatExL(pszFilename, &sStat,
5325 6942 : VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
5326 878 : S_ISFIFO(sStat.st_mode))
5327 : {
5328 0 : bStreaming = true;
5329 : }
5330 : }
5331 : #endif
5332 6076 : if (bStreaming && !EQUAL("NONE", CSLFetchNameValueDef(papszParamList,
5333 : "COMPRESS", "NONE")))
5334 : {
5335 1 : ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
5336 : "Streaming only supported to uncompressed TIFF");
5337 1 : return nullptr;
5338 : }
5339 6075 : if (bStreaming && CPLFetchBool(papszParamList, "SPARSE_OK", false))
5340 : {
5341 1 : ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
5342 : "Streaming not supported with SPARSE_OK");
5343 1 : return nullptr;
5344 : }
5345 : const bool bCopySrcOverviews =
5346 6074 : CPLFetchBool(papszParamList, "COPY_SRC_OVERVIEWS", false);
5347 6074 : if (bStreaming && bCopySrcOverviews)
5348 : {
5349 1 : ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
5350 : "Streaming not supported with COPY_SRC_OVERVIEWS");
5351 1 : return nullptr;
5352 : }
5353 6073 : if (bStreaming)
5354 : {
5355 : static int nCounter = 0;
5356 9 : l_osTmpFilename = CPLSPrintf("/vsimem/vsistdout_%d.tif", ++nCounter);
5357 9 : pszFilename = l_osTmpFilename.c_str();
5358 : }
5359 :
5360 : /* -------------------------------------------------------------------- */
5361 : /* Compute the uncompressed size. */
5362 : /* -------------------------------------------------------------------- */
5363 6073 : const unsigned nTileXCount =
5364 6073 : bTiled ? DIV_ROUND_UP(nXSize, l_nBlockXSize) : 0;
5365 6073 : const unsigned nTileYCount =
5366 6073 : bTiled ? DIV_ROUND_UP(nYSize, l_nBlockYSize) : 0;
5367 : const double dfUncompressedImageSize =
5368 6073 : (bTiled ? (static_cast<double>(nTileXCount) * nTileYCount *
5369 619 : l_nBlockXSize * l_nBlockYSize)
5370 5454 : : (nXSize * static_cast<double>(nYSize))) *
5371 6073 : l_nBands * GDALGetDataTypeSizeBytes(eType) +
5372 6073 : dfExtraSpaceForOverviews;
5373 :
5374 : /* -------------------------------------------------------------------- */
5375 : /* Should the file be created as a bigtiff file? */
5376 : /* -------------------------------------------------------------------- */
5377 6073 : const char *pszBIGTIFF = CSLFetchNameValue(papszParamList, "BIGTIFF");
5378 :
5379 6073 : if (pszBIGTIFF == nullptr)
5380 5699 : pszBIGTIFF = "IF_NEEDED";
5381 :
5382 6073 : bool bCreateBigTIFF = false;
5383 6073 : if (EQUAL(pszBIGTIFF, "IF_NEEDED"))
5384 : {
5385 5700 : if (l_nCompression == COMPRESSION_NONE &&
5386 : dfUncompressedImageSize > 4200000000.0)
5387 16 : bCreateBigTIFF = true;
5388 : }
5389 373 : else if (EQUAL(pszBIGTIFF, "IF_SAFER"))
5390 : {
5391 355 : if (dfUncompressedImageSize > 2000000000.0)
5392 1 : bCreateBigTIFF = true;
5393 : }
5394 : else
5395 : {
5396 18 : bCreateBigTIFF = CPLTestBool(pszBIGTIFF);
5397 18 : if (!bCreateBigTIFF && l_nCompression == COMPRESSION_NONE &&
5398 : dfUncompressedImageSize > 4200000000.0)
5399 : {
5400 2 : ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
5401 : "The TIFF file will be larger than 4GB, so BigTIFF is "
5402 : "necessary. Creation failed.");
5403 2 : return nullptr;
5404 : }
5405 : }
5406 :
5407 6071 : if (bCreateBigTIFF)
5408 31 : CPLDebug("GTiff", "File being created as a BigTIFF.");
5409 :
5410 : /* -------------------------------------------------------------------- */
5411 : /* Sanity check. */
5412 : /* -------------------------------------------------------------------- */
5413 6071 : if (bTiled)
5414 : {
5415 : // libtiff implementation limitation
5416 619 : if (nTileXCount > 0x80000000U / (bCreateBigTIFF ? 8 : 4) / nTileYCount)
5417 : {
5418 1 : ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
5419 : "File too large regarding tile size. This would result "
5420 : "in a file with tile arrays larger than 2GB");
5421 1 : return nullptr;
5422 : }
5423 : }
5424 :
5425 : /* -------------------------------------------------------------------- */
5426 : /* Check free space (only for big, non sparse, uncompressed) */
5427 : /* -------------------------------------------------------------------- */
5428 3011 : if (l_nCompression == COMPRESSION_NONE && dfUncompressedImageSize >= 1e9 &&
5429 18 : !CPLFetchBool(papszParamList, "SPARSE_OK", false) &&
5430 3 : osOriFilename != "/vsistdout/" &&
5431 9084 : osOriFilename != "/vsistdout_redirect/" &&
5432 3 : CPLTestBool(CPLGetConfigOption("CHECK_DISK_FREE_SPACE", "TRUE")))
5433 : {
5434 : GIntBig nFreeDiskSpace =
5435 2 : VSIGetDiskFreeSpace(CPLGetDirname(pszFilename));
5436 2 : if (nFreeDiskSpace >= 0 && nFreeDiskSpace < dfUncompressedImageSize)
5437 : {
5438 1 : ReportError(pszFilename, CE_Failure, CPLE_FileIO,
5439 : "Free disk space available is " CPL_FRMT_GIB " bytes, "
5440 : "whereas " CPL_FRMT_GIB " are at least necessary. "
5441 : "You can disable this check by defining the "
5442 : "CHECK_DISK_FREE_SPACE configuration option to FALSE.",
5443 : nFreeDiskSpace,
5444 : static_cast<GIntBig>(dfUncompressedImageSize));
5445 1 : return nullptr;
5446 : }
5447 : }
5448 :
5449 : /* -------------------------------------------------------------------- */
5450 : /* Check if the user wishes a particular endianness */
5451 : /* -------------------------------------------------------------------- */
5452 :
5453 6069 : int eEndianness = ENDIANNESS_NATIVE;
5454 6069 : pszValue = CSLFetchNameValue(papszParamList, "ENDIANNESS");
5455 6069 : if (pszValue == nullptr)
5456 6011 : pszValue = CPLGetConfigOption("GDAL_TIFF_ENDIANNESS", nullptr);
5457 6069 : if (pszValue != nullptr)
5458 : {
5459 116 : if (EQUAL(pszValue, "LITTLE"))
5460 : {
5461 30 : eEndianness = ENDIANNESS_LITTLE;
5462 : }
5463 86 : else if (EQUAL(pszValue, "BIG"))
5464 : {
5465 1 : eEndianness = ENDIANNESS_BIG;
5466 : }
5467 85 : else if (EQUAL(pszValue, "INVERTED"))
5468 : {
5469 : #ifdef CPL_LSB
5470 81 : eEndianness = ENDIANNESS_BIG;
5471 : #else
5472 : eEndianness = ENDIANNESS_LITTLE;
5473 : #endif
5474 : }
5475 4 : else if (!EQUAL(pszValue, "NATIVE"))
5476 : {
5477 1 : ReportError(pszFilename, CE_Warning, CPLE_NotSupported,
5478 : "ENDIANNESS=%s not supported. Defaulting to NATIVE",
5479 : pszValue);
5480 : }
5481 : }
5482 :
5483 : /* -------------------------------------------------------------------- */
5484 : /* Try opening the dataset. */
5485 : /* -------------------------------------------------------------------- */
5486 :
5487 : const bool bAppend =
5488 6069 : CPLFetchBool(papszParamList, "APPEND_SUBDATASET", false);
5489 :
5490 6069 : char szOpeningFlag[5] = {};
5491 6069 : strcpy(szOpeningFlag, bAppend ? "r+" : "w+");
5492 6069 : if (bCreateBigTIFF)
5493 29 : strcat(szOpeningFlag, "8");
5494 6069 : if (eEndianness == ENDIANNESS_BIG)
5495 82 : strcat(szOpeningFlag, "b");
5496 5987 : else if (eEndianness == ENDIANNESS_LITTLE)
5497 30 : strcat(szOpeningFlag, "l");
5498 :
5499 6069 : VSILFILE *l_fpL = VSIFOpenL(pszFilename, bAppend ? "r+b" : "w+b");
5500 6069 : if (l_fpL == nullptr)
5501 : {
5502 15 : CPLError(CE_Failure, CPLE_OpenFailed,
5503 : "Attempt to create new tiff file `%s' failed: %s", pszFilename,
5504 15 : VSIStrerror(errno));
5505 15 : return nullptr;
5506 : }
5507 6054 : TIFF *l_hTIFF = VSI_TIFFOpen(pszFilename, szOpeningFlag, l_fpL);
5508 6054 : if (l_hTIFF == nullptr)
5509 : {
5510 2 : if (CPLGetLastErrorNo() == 0)
5511 0 : CPLError(CE_Failure, CPLE_OpenFailed,
5512 : "Attempt to create new tiff file `%s' "
5513 : "failed in XTIFFOpen().",
5514 : pszFilename);
5515 2 : CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
5516 2 : return nullptr;
5517 : }
5518 :
5519 6052 : if (bAppend)
5520 : {
5521 : // This is a bit of a hack to cause (*tif->tif_cleanup)(tif); to be
5522 : // called. See https://trac.osgeo.org/gdal/ticket/2055
5523 5 : TIFFSetField(l_hTIFF, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
5524 5 : TIFFFreeDirectory(l_hTIFF);
5525 5 : TIFFCreateDirectory(l_hTIFF);
5526 : }
5527 :
5528 : /* -------------------------------------------------------------------- */
5529 : /* Do we have a custom pixel type (just used for signed byte now). */
5530 : /* -------------------------------------------------------------------- */
5531 6052 : const char *pszPixelType = CSLFetchNameValue(papszParamList, "PIXELTYPE");
5532 6052 : if (pszPixelType == nullptr)
5533 6044 : pszPixelType = "";
5534 6052 : if (eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE"))
5535 : {
5536 8 : CPLError(CE_Warning, CPLE_AppDefined,
5537 : "Using PIXELTYPE=SIGNEDBYTE with Byte data type is deprecated "
5538 : "(but still works). "
5539 : "Using Int8 data type instead is now recommended.");
5540 : }
5541 :
5542 : /* -------------------------------------------------------------------- */
5543 : /* Setup some standard flags. */
5544 : /* -------------------------------------------------------------------- */
5545 6052 : TIFFSetField(l_hTIFF, TIFFTAG_IMAGEWIDTH, nXSize);
5546 6052 : TIFFSetField(l_hTIFF, TIFFTAG_IMAGELENGTH, nYSize);
5547 6052 : TIFFSetField(l_hTIFF, TIFFTAG_BITSPERSAMPLE, l_nBitsPerSample);
5548 :
5549 6052 : uint16_t l_nSampleFormat = 0;
5550 6052 : if ((eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE")) ||
5551 6026 : eType == GDT_Int8 || eType == GDT_Int16 || eType == GDT_Int32 ||
5552 : eType == GDT_Int64)
5553 296 : l_nSampleFormat = SAMPLEFORMAT_INT;
5554 5756 : else if (eType == GDT_CInt16 || eType == GDT_CInt32)
5555 133 : l_nSampleFormat = SAMPLEFORMAT_COMPLEXINT;
5556 5623 : else if (eType == GDT_Float32 || eType == GDT_Float64)
5557 471 : l_nSampleFormat = SAMPLEFORMAT_IEEEFP;
5558 5152 : else if (eType == GDT_CFloat32 || eType == GDT_CFloat64)
5559 128 : l_nSampleFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
5560 : else
5561 5024 : l_nSampleFormat = SAMPLEFORMAT_UINT;
5562 :
5563 6052 : TIFFSetField(l_hTIFF, TIFFTAG_SAMPLEFORMAT, l_nSampleFormat);
5564 6052 : TIFFSetField(l_hTIFF, TIFFTAG_SAMPLESPERPIXEL, l_nBands);
5565 6052 : TIFFSetField(l_hTIFF, TIFFTAG_PLANARCONFIG, nPlanar);
5566 :
5567 : /* -------------------------------------------------------------------- */
5568 : /* Setup Photometric Interpretation. Take this value from the user */
5569 : /* passed option or guess correct value otherwise. */
5570 : /* -------------------------------------------------------------------- */
5571 6052 : int nSamplesAccountedFor = 1;
5572 6052 : bool bForceColorTable = false;
5573 :
5574 6052 : pszValue = CSLFetchNameValue(papszParamList, "PHOTOMETRIC");
5575 6052 : if (pszValue != nullptr)
5576 : {
5577 1073 : if (EQUAL(pszValue, "MINISBLACK"))
5578 14 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
5579 1059 : else if (EQUAL(pszValue, "MINISWHITE"))
5580 : {
5581 2 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
5582 : }
5583 1057 : else if (EQUAL(pszValue, "PALETTE"))
5584 : {
5585 5 : if (eType == GDT_Byte || eType == GDT_UInt16)
5586 : {
5587 4 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
5588 4 : nSamplesAccountedFor = 1;
5589 4 : bForceColorTable = true;
5590 : }
5591 : else
5592 : {
5593 1 : ReportError(
5594 : pszFilename, CE_Warning, CPLE_AppDefined,
5595 : "PHOTOMETRIC=PALETTE only compatible with Byte or UInt16");
5596 : }
5597 : }
5598 1052 : else if (EQUAL(pszValue, "RGB"))
5599 : {
5600 310 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
5601 310 : nSamplesAccountedFor = 3;
5602 : }
5603 742 : else if (EQUAL(pszValue, "CMYK"))
5604 : {
5605 10 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
5606 10 : nSamplesAccountedFor = 4;
5607 : }
5608 732 : else if (EQUAL(pszValue, "YCBCR"))
5609 : {
5610 : // Because of subsampling, setting YCBCR without JPEG compression
5611 : // leads to a crash currently. Would need to make
5612 : // GTiffRasterBand::IWriteBlock() aware of subsampling so that it
5613 : // doesn't overrun buffer size returned by libtiff.
5614 731 : if (l_nCompression != COMPRESSION_JPEG)
5615 : {
5616 1 : ReportError(
5617 : pszFilename, CE_Failure, CPLE_NotSupported,
5618 : "Currently, PHOTOMETRIC=YCBCR requires COMPRESS=JPEG");
5619 1 : XTIFFClose(l_hTIFF);
5620 1 : CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
5621 1 : return nullptr;
5622 : }
5623 :
5624 730 : if (nPlanar == PLANARCONFIG_SEPARATE)
5625 : {
5626 1 : ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
5627 : "PHOTOMETRIC=YCBCR requires INTERLEAVE=PIXEL");
5628 1 : XTIFFClose(l_hTIFF);
5629 1 : CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
5630 1 : return nullptr;
5631 : }
5632 :
5633 : // YCBCR strictly requires 3 bands. Not less, not more Issue an
5634 : // explicit error message as libtiff one is a bit cryptic:
5635 : // TIFFVStripSize64:Invalid td_samplesperpixel value.
5636 729 : if (l_nBands != 3)
5637 : {
5638 1 : ReportError(
5639 : pszFilename, CE_Failure, CPLE_NotSupported,
5640 : "PHOTOMETRIC=YCBCR not supported on a %d-band raster: "
5641 : "only compatible of a 3-band (RGB) raster",
5642 : l_nBands);
5643 1 : XTIFFClose(l_hTIFF);
5644 1 : CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
5645 1 : return nullptr;
5646 : }
5647 :
5648 728 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
5649 728 : nSamplesAccountedFor = 3;
5650 :
5651 : // Explicitly register the subsampling so that JPEGFixupTags
5652 : // is a no-op (helps for cloud optimized geotiffs)
5653 728 : TIFFSetField(l_hTIFF, TIFFTAG_YCBCRSUBSAMPLING, 2, 2);
5654 : }
5655 1 : else if (EQUAL(pszValue, "CIELAB"))
5656 : {
5657 0 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB);
5658 0 : nSamplesAccountedFor = 3;
5659 : }
5660 1 : else if (EQUAL(pszValue, "ICCLAB"))
5661 : {
5662 0 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ICCLAB);
5663 0 : nSamplesAccountedFor = 3;
5664 : }
5665 1 : else if (EQUAL(pszValue, "ITULAB"))
5666 : {
5667 0 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ITULAB);
5668 0 : nSamplesAccountedFor = 3;
5669 : }
5670 : else
5671 : {
5672 1 : ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
5673 : "PHOTOMETRIC=%s value not recognised, ignoring. "
5674 : "Set the Photometric Interpretation as MINISBLACK.",
5675 : pszValue);
5676 1 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
5677 : }
5678 :
5679 1070 : if (l_nBands < nSamplesAccountedFor)
5680 : {
5681 1 : ReportError(pszFilename, CE_Warning, CPLE_IllegalArg,
5682 : "PHOTOMETRIC=%s value does not correspond to number "
5683 : "of bands (%d), ignoring. "
5684 : "Set the Photometric Interpretation as MINISBLACK.",
5685 : pszValue, l_nBands);
5686 1 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
5687 : }
5688 : }
5689 : else
5690 : {
5691 : // If image contains 3 or 4 bands and datatype is Byte then we will
5692 : // assume it is RGB. In all other cases assume it is MINISBLACK.
5693 4979 : if (l_nBands == 3 && eType == GDT_Byte)
5694 : {
5695 244 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
5696 244 : nSamplesAccountedFor = 3;
5697 : }
5698 4735 : else if (l_nBands == 4 && eType == GDT_Byte)
5699 : {
5700 : uint16_t v[1] = {
5701 712 : GTiffGetAlphaValue(CSLFetchNameValue(papszParamList, "ALPHA"),
5702 712 : DEFAULT_ALPHA_TYPE)};
5703 :
5704 712 : TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
5705 712 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
5706 712 : nSamplesAccountedFor = 4;
5707 : }
5708 : else
5709 : {
5710 4023 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
5711 4023 : nSamplesAccountedFor = 1;
5712 : }
5713 : }
5714 :
5715 : /* -------------------------------------------------------------------- */
5716 : /* If there are extra samples, we need to mark them with an */
5717 : /* appropriate extrasamples definition here. */
5718 : /* -------------------------------------------------------------------- */
5719 6049 : if (l_nBands > nSamplesAccountedFor)
5720 : {
5721 1115 : const int nExtraSamples = l_nBands - nSamplesAccountedFor;
5722 :
5723 : uint16_t *v = static_cast<uint16_t *>(
5724 1115 : CPLMalloc(sizeof(uint16_t) * nExtraSamples));
5725 :
5726 1115 : v[0] = GTiffGetAlphaValue(CSLFetchNameValue(papszParamList, "ALPHA"),
5727 : EXTRASAMPLE_UNSPECIFIED);
5728 :
5729 198747 : for (int i = 1; i < nExtraSamples; ++i)
5730 197632 : v[i] = EXTRASAMPLE_UNSPECIFIED;
5731 :
5732 1115 : TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples, v);
5733 :
5734 1115 : CPLFree(v);
5735 : }
5736 :
5737 : // Set the ICC color profile.
5738 6049 : if (eProfile != GTiffProfile::BASELINE)
5739 : {
5740 6027 : SaveICCProfile(nullptr, l_hTIFF, papszParamList, l_nBitsPerSample);
5741 : }
5742 :
5743 : // Set the compression method before asking the default strip size
5744 : // This is useful when translating to a JPEG-In-TIFF file where
5745 : // the default strip size is 8 or 16 depending on the photometric value.
5746 6049 : TIFFSetField(l_hTIFF, TIFFTAG_COMPRESSION, l_nCompression);
5747 :
5748 6049 : if (l_nCompression == COMPRESSION_LERC)
5749 : {
5750 : const char *pszCompress =
5751 97 : CSLFetchNameValueDef(papszParamList, "COMPRESS", "");
5752 97 : if (EQUAL(pszCompress, "LERC_DEFLATE"))
5753 : {
5754 16 : TIFFSetField(l_hTIFF, TIFFTAG_LERC_ADD_COMPRESSION,
5755 : LERC_ADD_COMPRESSION_DEFLATE);
5756 : }
5757 81 : else if (EQUAL(pszCompress, "LERC_ZSTD"))
5758 : {
5759 14 : if (TIFFSetField(l_hTIFF, TIFFTAG_LERC_ADD_COMPRESSION,
5760 14 : LERC_ADD_COMPRESSION_ZSTD) != 1)
5761 : {
5762 0 : XTIFFClose(l_hTIFF);
5763 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
5764 0 : return nullptr;
5765 : }
5766 : }
5767 : }
5768 : // TODO later: take into account LERC version
5769 :
5770 : /* -------------------------------------------------------------------- */
5771 : /* Setup tiling/stripping flags. */
5772 : /* -------------------------------------------------------------------- */
5773 6049 : if (bTiled)
5774 : {
5775 1230 : if (!TIFFSetField(l_hTIFF, TIFFTAG_TILEWIDTH, l_nBlockXSize) ||
5776 615 : !TIFFSetField(l_hTIFF, TIFFTAG_TILELENGTH, l_nBlockYSize))
5777 : {
5778 1 : XTIFFClose(l_hTIFF);
5779 1 : CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
5780 1 : return nullptr;
5781 : }
5782 : }
5783 : else
5784 : {
5785 5434 : const uint32_t l_nRowsPerStrip = std::min(
5786 : nYSize, l_nBlockYSize == 0
5787 5434 : ? static_cast<int>(TIFFDefaultStripSize(l_hTIFF, 0))
5788 5434 : : l_nBlockYSize);
5789 :
5790 5434 : TIFFSetField(l_hTIFF, TIFFTAG_ROWSPERSTRIP, l_nRowsPerStrip);
5791 : }
5792 :
5793 : /* -------------------------------------------------------------------- */
5794 : /* Set compression related tags. */
5795 : /* -------------------------------------------------------------------- */
5796 6048 : if (GTIFFSupportsPredictor(l_nCompression))
5797 711 : TIFFSetField(l_hTIFF, TIFFTAG_PREDICTOR, nPredictor);
5798 6048 : if (l_nCompression == COMPRESSION_ADOBE_DEFLATE ||
5799 : l_nCompression == COMPRESSION_LERC)
5800 : {
5801 257 : GTiffSetDeflateSubCodec(l_hTIFF);
5802 :
5803 257 : if (l_nZLevel != -1)
5804 21 : TIFFSetField(l_hTIFF, TIFFTAG_ZIPQUALITY, l_nZLevel);
5805 : }
5806 6048 : if (l_nCompression == COMPRESSION_JPEG && l_nJpegQuality != -1)
5807 1905 : TIFFSetField(l_hTIFF, TIFFTAG_JPEGQUALITY, l_nJpegQuality);
5808 6048 : if (l_nCompression == COMPRESSION_LZMA && l_nLZMAPreset != -1)
5809 10 : TIFFSetField(l_hTIFF, TIFFTAG_LZMAPRESET, l_nLZMAPreset);
5810 6048 : if ((l_nCompression == COMPRESSION_ZSTD ||
5811 133 : l_nCompression == COMPRESSION_LERC) &&
5812 : l_nZSTDLevel != -1)
5813 12 : TIFFSetField(l_hTIFF, TIFFTAG_ZSTD_LEVEL, l_nZSTDLevel);
5814 6048 : if (l_nCompression == COMPRESSION_LERC)
5815 : {
5816 97 : TIFFSetField(l_hTIFF, TIFFTAG_LERC_MAXZERROR, l_dfMaxZError);
5817 : }
5818 : #if HAVE_JXL
5819 6048 : if (l_nCompression == COMPRESSION_JXL)
5820 : {
5821 98 : TIFFSetField(l_hTIFF, TIFFTAG_JXL_LOSSYNESS,
5822 : l_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
5823 98 : TIFFSetField(l_hTIFF, TIFFTAG_JXL_EFFORT, l_nJXLEffort);
5824 98 : TIFFSetField(l_hTIFF, TIFFTAG_JXL_DISTANCE, l_fJXLDistance);
5825 98 : TIFFSetField(l_hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE, l_fJXLAlphaDistance);
5826 : }
5827 : #endif
5828 6048 : if (l_nCompression == COMPRESSION_WEBP)
5829 33 : TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LEVEL, l_nWebPLevel);
5830 6048 : if (l_nCompression == COMPRESSION_WEBP && l_bWebPLossless)
5831 7 : TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LOSSLESS, 1);
5832 :
5833 6048 : if (l_nCompression == COMPRESSION_JPEG)
5834 2081 : TIFFSetField(l_hTIFF, TIFFTAG_JPEGTABLESMODE, l_nJpegTablesMode);
5835 :
5836 : /* -------------------------------------------------------------------- */
5837 : /* If we forced production of a file with photometric=palette, */
5838 : /* we need to push out a default color table. */
5839 : /* -------------------------------------------------------------------- */
5840 6048 : if (bForceColorTable)
5841 : {
5842 4 : const int nColors = eType == GDT_Byte ? 256 : 65536;
5843 :
5844 : unsigned short *panTRed = static_cast<unsigned short *>(
5845 4 : CPLMalloc(sizeof(unsigned short) * nColors));
5846 : unsigned short *panTGreen = static_cast<unsigned short *>(
5847 4 : CPLMalloc(sizeof(unsigned short) * nColors));
5848 : unsigned short *panTBlue = static_cast<unsigned short *>(
5849 4 : CPLMalloc(sizeof(unsigned short) * nColors));
5850 :
5851 1028 : for (int iColor = 0; iColor < nColors; ++iColor)
5852 : {
5853 1024 : if (eType == GDT_Byte)
5854 : {
5855 1024 : panTRed[iColor] = static_cast<unsigned short>(257 * iColor);
5856 1024 : panTGreen[iColor] = static_cast<unsigned short>(257 * iColor);
5857 1024 : panTBlue[iColor] = static_cast<unsigned short>(257 * iColor);
5858 : }
5859 : else
5860 : {
5861 0 : panTRed[iColor] = static_cast<unsigned short>(iColor);
5862 0 : panTGreen[iColor] = static_cast<unsigned short>(iColor);
5863 0 : panTBlue[iColor] = static_cast<unsigned short>(iColor);
5864 : }
5865 : }
5866 :
5867 4 : TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue);
5868 :
5869 4 : CPLFree(panTRed);
5870 4 : CPLFree(panTGreen);
5871 4 : CPLFree(panTBlue);
5872 : }
5873 :
5874 : // This trick
5875 : // creates a temporary in-memory file and fetches its JPEG tables so that
5876 : // we can directly set them, before tif_jpeg.c compute them at the first
5877 : // strip/tile writing, which is too late, since we have already crystalized
5878 : // the directory. This way we avoid a directory rewriting.
5879 8129 : if (l_nCompression == COMPRESSION_JPEG &&
5880 7059 : !STARTS_WITH(pszFilename, szJPEGGTiffDatasetTmpPrefix) &&
5881 1011 : CPLTestBool(
5882 : CSLFetchNameValueDef(papszParamList, "WRITE_JPEGTABLE_TAG", "YES")))
5883 : {
5884 1011 : GTiffWriteJPEGTables(
5885 : l_hTIFF, CSLFetchNameValue(papszParamList, "PHOTOMETRIC"),
5886 : CSLFetchNameValue(papszParamList, "JPEG_QUALITY"),
5887 : CSLFetchNameValue(papszParamList, "JPEGTABLESMODE"));
5888 : }
5889 :
5890 6048 : *pfpL = l_fpL;
5891 :
5892 6048 : return l_hTIFF;
5893 : }
5894 :
5895 : /************************************************************************/
5896 : /* GuessJPEGQuality() */
5897 : /* */
5898 : /* Guess JPEG quality from JPEGTABLES tag. */
5899 : /************************************************************************/
5900 :
5901 3804 : static const GByte *GTIFFFindNextTable(const GByte *paby, GByte byMarker,
5902 : int nLen, int *pnLenTable)
5903 : {
5904 7867 : for (int i = 0; i + 1 < nLen;)
5905 : {
5906 7867 : if (paby[i] != 0xFF)
5907 0 : return nullptr;
5908 7867 : ++i;
5909 7867 : if (paby[i] == 0xD8)
5910 : {
5911 3078 : ++i;
5912 3078 : continue;
5913 : }
5914 4789 : if (i + 2 >= nLen)
5915 809 : return nullptr;
5916 3980 : int nMarkerLen = paby[i + 1] * 256 + paby[i + 2];
5917 3980 : if (i + 1 + nMarkerLen >= nLen)
5918 0 : return nullptr;
5919 3980 : if (paby[i] == byMarker)
5920 : {
5921 2995 : if (pnLenTable)
5922 2466 : *pnLenTable = nMarkerLen;
5923 2995 : return paby + i + 1;
5924 : }
5925 985 : i += 1 + nMarkerLen;
5926 : }
5927 0 : return nullptr;
5928 : }
5929 :
5930 : constexpr GByte MARKER_HUFFMAN_TABLE = 0xC4;
5931 : constexpr GByte MARKER_QUANT_TABLE = 0xDB;
5932 :
5933 : // We assume that if there are several quantization tables, they are
5934 : // in the same order. Which is a reasonable assumption for updating
5935 : // a file generated by ourselves.
5936 904 : static bool GTIFFQuantizationTablesEqual(const GByte *paby1, int nLen1,
5937 : const GByte *paby2, int nLen2)
5938 : {
5939 904 : bool bFound = false;
5940 : while (true)
5941 : {
5942 945 : int nLenTable1 = 0;
5943 945 : int nLenTable2 = 0;
5944 : const GByte *paby1New =
5945 945 : GTIFFFindNextTable(paby1, MARKER_QUANT_TABLE, nLen1, &nLenTable1);
5946 : const GByte *paby2New =
5947 945 : GTIFFFindNextTable(paby2, MARKER_QUANT_TABLE, nLen2, &nLenTable2);
5948 945 : if (paby1New == nullptr && paby2New == nullptr)
5949 904 : return bFound;
5950 911 : if (paby1New == nullptr || paby2New == nullptr)
5951 0 : return false;
5952 911 : if (nLenTable1 != nLenTable2)
5953 207 : return false;
5954 704 : if (memcmp(paby1New, paby2New, nLenTable1) != 0)
5955 663 : return false;
5956 41 : paby1New += nLenTable1;
5957 41 : paby2New += nLenTable2;
5958 41 : nLen1 -= static_cast<int>(paby1New - paby1);
5959 41 : nLen2 -= static_cast<int>(paby2New - paby2);
5960 41 : paby1 = paby1New;
5961 41 : paby2 = paby2New;
5962 41 : bFound = true;
5963 41 : }
5964 : }
5965 :
5966 : // Guess the JPEG quality by comparing against the MD5Sum of precomputed
5967 : // quantization tables
5968 396 : static int GuessJPEGQualityFromMD5(const uint8_t md5JPEGQuantTable[][16],
5969 : const GByte *const pabyJPEGTable,
5970 : int nJPEGTableSize)
5971 : {
5972 396 : int nRemainingLen = nJPEGTableSize;
5973 396 : const GByte *pabyCur = pabyJPEGTable;
5974 :
5975 : struct CPLMD5Context context;
5976 396 : CPLMD5Init(&context);
5977 :
5978 : while (true)
5979 : {
5980 1040 : int nLenTable = 0;
5981 1040 : const GByte *pabyNew = GTIFFFindNextTable(pabyCur, MARKER_QUANT_TABLE,
5982 : nRemainingLen, &nLenTable);
5983 1040 : if (pabyNew == nullptr)
5984 396 : break;
5985 644 : CPLMD5Update(&context, pabyNew, nLenTable);
5986 644 : pabyNew += nLenTable;
5987 644 : nRemainingLen -= static_cast<int>(pabyNew - pabyCur);
5988 644 : pabyCur = pabyNew;
5989 644 : }
5990 :
5991 : GByte digest[16];
5992 396 : CPLMD5Final(digest, &context);
5993 :
5994 27871 : for (int i = 0; i < 100; i++)
5995 : {
5996 27868 : if (memcmp(md5JPEGQuantTable[i], digest, 16) == 0)
5997 : {
5998 393 : return i + 1;
5999 : }
6000 : }
6001 3 : return -1;
6002 : }
6003 :
6004 451 : int GTiffDataset::GuessJPEGQuality(bool &bOutHasQuantizationTable,
6005 : bool &bOutHasHuffmanTable)
6006 : {
6007 451 : CPLAssert(m_nCompression == COMPRESSION_JPEG);
6008 451 : uint32_t nJPEGTableSize = 0;
6009 451 : void *pJPEGTable = nullptr;
6010 451 : if (!TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize,
6011 : &pJPEGTable))
6012 : {
6013 14 : bOutHasQuantizationTable = false;
6014 14 : bOutHasHuffmanTable = false;
6015 14 : return -1;
6016 : }
6017 :
6018 437 : bOutHasQuantizationTable =
6019 437 : GTIFFFindNextTable(static_cast<const GByte *>(pJPEGTable),
6020 : MARKER_QUANT_TABLE, nJPEGTableSize,
6021 437 : nullptr) != nullptr;
6022 437 : bOutHasHuffmanTable =
6023 437 : GTIFFFindNextTable(static_cast<const GByte *>(pJPEGTable),
6024 : MARKER_HUFFMAN_TABLE, nJPEGTableSize,
6025 437 : nullptr) != nullptr;
6026 437 : if (!bOutHasQuantizationTable)
6027 7 : return -1;
6028 :
6029 430 : if ((nBands == 1 && m_nBitsPerSample == 8) ||
6030 371 : (nBands == 3 && m_nBitsPerSample == 8 &&
6031 325 : m_nPhotometric == PHOTOMETRIC_RGB) ||
6032 294 : (nBands == 4 && m_nBitsPerSample == 8 &&
6033 27 : m_nPhotometric == PHOTOMETRIC_SEPARATED))
6034 : {
6035 148 : return GuessJPEGQualityFromMD5(md5JPEGQuantTable_generic_8bit,
6036 : static_cast<const GByte *>(pJPEGTable),
6037 148 : static_cast<int>(nJPEGTableSize));
6038 : }
6039 :
6040 282 : if (nBands == 3 && m_nBitsPerSample == 8 &&
6041 248 : m_nPhotometric == PHOTOMETRIC_YCBCR)
6042 : {
6043 : int nRet =
6044 248 : GuessJPEGQualityFromMD5(md5JPEGQuantTable_3_YCBCR_8bit,
6045 : static_cast<const GByte *>(pJPEGTable),
6046 : static_cast<int>(nJPEGTableSize));
6047 248 : if (nRet < 0)
6048 : {
6049 : // libjpeg 9e has modified the YCbCr quantization tables.
6050 : nRet =
6051 0 : GuessJPEGQualityFromMD5(md5JPEGQuantTable_3_YCBCR_8bit_jpeg9e,
6052 : static_cast<const GByte *>(pJPEGTable),
6053 : static_cast<int>(nJPEGTableSize));
6054 : }
6055 248 : return nRet;
6056 : }
6057 :
6058 34 : char **papszLocalParameters = nullptr;
6059 : papszLocalParameters =
6060 34 : CSLSetNameValue(papszLocalParameters, "COMPRESS", "JPEG");
6061 34 : if (m_nPhotometric == PHOTOMETRIC_YCBCR)
6062 : papszLocalParameters =
6063 7 : CSLSetNameValue(papszLocalParameters, "PHOTOMETRIC", "YCBCR");
6064 27 : else if (m_nPhotometric == PHOTOMETRIC_SEPARATED)
6065 : papszLocalParameters =
6066 0 : CSLSetNameValue(papszLocalParameters, "PHOTOMETRIC", "CMYK");
6067 : papszLocalParameters =
6068 34 : CSLSetNameValue(papszLocalParameters, "BLOCKYSIZE", "16");
6069 34 : if (m_nBitsPerSample == 12)
6070 : papszLocalParameters =
6071 16 : CSLSetNameValue(papszLocalParameters, "NBITS", "12");
6072 :
6073 34 : CPLString osTmpFilenameIn;
6074 : osTmpFilenameIn.Printf("/vsimem/gtiffdataset_guess_jpeg_quality_tmp_%p",
6075 34 : this);
6076 :
6077 34 : int nRet = -1;
6078 938 : for (int nQuality = 0; nQuality <= 100 && nRet < 0; ++nQuality)
6079 : {
6080 904 : VSILFILE *fpTmp = nullptr;
6081 904 : if (nQuality == 0)
6082 : papszLocalParameters =
6083 34 : CSLSetNameValue(papszLocalParameters, "JPEG_QUALITY", "75");
6084 : else
6085 : papszLocalParameters =
6086 870 : CSLSetNameValue(papszLocalParameters, "JPEG_QUALITY",
6087 : CPLSPrintf("%d", nQuality));
6088 :
6089 904 : CPLPushErrorHandler(CPLQuietErrorHandler);
6090 904 : CPLString osTmp;
6091 : TIFF *hTIFFTmp =
6092 904 : CreateLL(osTmpFilenameIn, 16, 16, (nBands <= 4) ? nBands : 1,
6093 : GetRasterBand(1)->GetRasterDataType(), 0.0,
6094 : papszLocalParameters, &fpTmp, osTmp);
6095 904 : CPLPopErrorHandler();
6096 904 : if (!hTIFFTmp)
6097 : {
6098 0 : break;
6099 : }
6100 :
6101 904 : TIFFWriteCheck(hTIFFTmp, FALSE, "CreateLL");
6102 904 : TIFFWriteDirectory(hTIFFTmp);
6103 904 : TIFFSetDirectory(hTIFFTmp, 0);
6104 : // Now reset jpegcolormode.
6105 1196 : if (m_nPhotometric == PHOTOMETRIC_YCBCR &&
6106 292 : CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
6107 : {
6108 292 : TIFFSetField(hTIFFTmp, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
6109 : }
6110 :
6111 904 : GByte abyZeroData[(16 * 16 * 4 * 3) / 2] = {};
6112 904 : const int nBlockSize =
6113 904 : (16 * 16 * ((nBands <= 4) ? nBands : 1) * m_nBitsPerSample) / 8;
6114 904 : TIFFWriteEncodedStrip(hTIFFTmp, 0, abyZeroData, nBlockSize);
6115 :
6116 904 : uint32_t nJPEGTableSizeTry = 0;
6117 904 : void *pJPEGTableTry = nullptr;
6118 904 : if (TIFFGetField(hTIFFTmp, TIFFTAG_JPEGTABLES, &nJPEGTableSizeTry,
6119 904 : &pJPEGTableTry))
6120 : {
6121 904 : if (GTIFFQuantizationTablesEqual(
6122 : static_cast<GByte *>(pJPEGTable), nJPEGTableSize,
6123 : static_cast<GByte *>(pJPEGTableTry), nJPEGTableSizeTry))
6124 : {
6125 34 : nRet = (nQuality == 0) ? 75 : nQuality;
6126 : }
6127 : }
6128 :
6129 904 : XTIFFClose(hTIFFTmp);
6130 904 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpTmp));
6131 : }
6132 :
6133 34 : CSLDestroy(papszLocalParameters);
6134 34 : VSIUnlink(osTmpFilenameIn);
6135 :
6136 34 : return nRet;
6137 : }
6138 :
6139 : /************************************************************************/
6140 : /* SetJPEGQualityAndTablesModeFromFile() */
6141 : /************************************************************************/
6142 :
6143 162 : void GTiffDataset::SetJPEGQualityAndTablesModeFromFile(
6144 : int nQuality, bool bHasQuantizationTable, bool bHasHuffmanTable)
6145 : {
6146 162 : if (nQuality > 0)
6147 : {
6148 155 : CPLDebug("GTiff", "Guessed JPEG quality to be %d", nQuality);
6149 155 : m_nJpegQuality = static_cast<signed char>(nQuality);
6150 155 : TIFFSetField(m_hTIFF, TIFFTAG_JPEGQUALITY, nQuality);
6151 :
6152 : // This means we will use the quantization tables from the
6153 : // JpegTables tag.
6154 155 : m_nJpegTablesMode = JPEGTABLESMODE_QUANT;
6155 : }
6156 : else
6157 : {
6158 7 : uint32_t nJPEGTableSize = 0;
6159 7 : void *pJPEGTable = nullptr;
6160 7 : if (!TIFFGetField(m_hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize,
6161 : &pJPEGTable))
6162 : {
6163 4 : toff_t *panByteCounts = nullptr;
6164 8 : const int nBlockCount = m_nPlanarConfig == PLANARCONFIG_SEPARATE
6165 4 : ? m_nBlocksPerBand * nBands
6166 : : m_nBlocksPerBand;
6167 4 : if (TIFFIsTiled(m_hTIFF))
6168 1 : TIFFGetField(m_hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts);
6169 : else
6170 3 : TIFFGetField(m_hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts);
6171 :
6172 4 : bool bFoundNonEmptyBlock = false;
6173 4 : if (panByteCounts != nullptr)
6174 : {
6175 56 : for (int iBlock = 0; iBlock < nBlockCount; ++iBlock)
6176 : {
6177 53 : if (panByteCounts[iBlock] != 0)
6178 : {
6179 1 : bFoundNonEmptyBlock = true;
6180 1 : break;
6181 : }
6182 : }
6183 : }
6184 4 : if (bFoundNonEmptyBlock)
6185 : {
6186 1 : CPLDebug("GTiff", "Could not guess JPEG quality. "
6187 : "JPEG tables are missing, so going in "
6188 : "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
6189 : // Write quantization tables in each strile.
6190 1 : m_nJpegTablesMode = 0;
6191 : }
6192 : }
6193 : else
6194 : {
6195 3 : if (bHasQuantizationTable)
6196 : {
6197 : // FIXME in libtiff: this is likely going to cause issues
6198 : // since libtiff will reuse in each strile the number of
6199 : // the global quantization table, which is invalid.
6200 1 : CPLDebug("GTiff",
6201 : "Could not guess JPEG quality although JPEG "
6202 : "quantization tables are present, so going in "
6203 : "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
6204 : }
6205 : else
6206 : {
6207 2 : CPLDebug("GTiff",
6208 : "Could not guess JPEG quality since JPEG "
6209 : "quantization tables are not present, so going in "
6210 : "TIFFTAG_JPEGTABLESMODE = 0/2 mode");
6211 : }
6212 :
6213 : // Write quantization tables in each strile.
6214 3 : m_nJpegTablesMode = 0;
6215 : }
6216 : }
6217 162 : if (bHasHuffmanTable)
6218 : {
6219 : // If there are Huffman tables in header use them, otherwise
6220 : // if we use optimized tables, libtiff will currently reuse
6221 : // the number of the Huffman tables of the header for the
6222 : // optimized version of each strile, which is illegal.
6223 23 : m_nJpegTablesMode |= JPEGTABLESMODE_HUFF;
6224 : }
6225 162 : if (m_nJpegTablesMode >= 0)
6226 160 : TIFFSetField(m_hTIFF, TIFFTAG_JPEGTABLESMODE, m_nJpegTablesMode);
6227 162 : }
6228 :
6229 : /************************************************************************/
6230 : /* Create() */
6231 : /* */
6232 : /* Create a new GeoTIFF or TIFF file. */
6233 : /************************************************************************/
6234 :
6235 3052 : GDALDataset *GTiffDataset::Create(const char *pszFilename, int nXSize,
6236 : int nYSize, int l_nBands, GDALDataType eType,
6237 : char **papszParamList)
6238 :
6239 : {
6240 3052 : VSILFILE *l_fpL = nullptr;
6241 6104 : CPLString l_osTmpFilename;
6242 :
6243 : /* -------------------------------------------------------------------- */
6244 : /* Create the underlying TIFF file. */
6245 : /* -------------------------------------------------------------------- */
6246 3052 : TIFF *l_hTIFF = CreateLL(pszFilename, nXSize, nYSize, l_nBands, eType, 0,
6247 : papszParamList, &l_fpL, l_osTmpFilename);
6248 3052 : const bool bStreaming = !l_osTmpFilename.empty();
6249 :
6250 3052 : if (l_hTIFF == nullptr)
6251 20 : return nullptr;
6252 :
6253 : /* -------------------------------------------------------------------- */
6254 : /* Create the new GTiffDataset object. */
6255 : /* -------------------------------------------------------------------- */
6256 3032 : GTiffDataset *poDS = new GTiffDataset();
6257 3032 : poDS->m_hTIFF = l_hTIFF;
6258 3032 : poDS->m_fpL = l_fpL;
6259 3032 : if (bStreaming)
6260 : {
6261 4 : poDS->m_bStreamingOut = true;
6262 4 : poDS->m_pszTmpFilename = CPLStrdup(l_osTmpFilename);
6263 4 : poDS->m_fpToWrite = VSIFOpenL(pszFilename, "wb");
6264 4 : if (poDS->m_fpToWrite == nullptr)
6265 : {
6266 1 : VSIUnlink(l_osTmpFilename);
6267 1 : delete poDS;
6268 1 : return nullptr;
6269 : }
6270 : }
6271 3031 : poDS->nRasterXSize = nXSize;
6272 3031 : poDS->nRasterYSize = nYSize;
6273 3031 : poDS->eAccess = GA_Update;
6274 3031 : poDS->m_bCrystalized = false;
6275 3031 : poDS->m_nSamplesPerPixel = static_cast<uint16_t>(l_nBands);
6276 3031 : poDS->m_pszFilename = CPLStrdup(pszFilename);
6277 :
6278 : // Don't try to load external metadata files (#6597).
6279 3031 : poDS->m_bIMDRPCMetadataLoaded = true;
6280 :
6281 : // Avoid premature crystalization that will cause directory re-writing if
6282 : // GetProjectionRef() or GetGeoTransform() are called on the newly created
6283 : // GeoTIFF.
6284 3031 : poDS->m_bLookedForProjection = true;
6285 :
6286 3031 : TIFFGetField(l_hTIFF, TIFFTAG_SAMPLEFORMAT, &(poDS->m_nSampleFormat));
6287 3031 : TIFFGetField(l_hTIFF, TIFFTAG_PLANARCONFIG, &(poDS->m_nPlanarConfig));
6288 : // Weird that we need this, but otherwise we get a Valgrind warning on
6289 : // tiff_write_124.
6290 3031 : if (!TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &(poDS->m_nPhotometric)))
6291 1 : poDS->m_nPhotometric = PHOTOMETRIC_MINISBLACK;
6292 3031 : TIFFGetField(l_hTIFF, TIFFTAG_BITSPERSAMPLE, &(poDS->m_nBitsPerSample));
6293 3031 : TIFFGetField(l_hTIFF, TIFFTAG_COMPRESSION, &(poDS->m_nCompression));
6294 :
6295 3031 : if (TIFFIsTiled(l_hTIFF))
6296 : {
6297 313 : TIFFGetField(l_hTIFF, TIFFTAG_TILEWIDTH, &(poDS->m_nBlockXSize));
6298 313 : TIFFGetField(l_hTIFF, TIFFTAG_TILELENGTH, &(poDS->m_nBlockYSize));
6299 : }
6300 : else
6301 : {
6302 2718 : if (!TIFFGetField(l_hTIFF, TIFFTAG_ROWSPERSTRIP,
6303 : &(poDS->m_nRowsPerStrip)))
6304 0 : poDS->m_nRowsPerStrip = 1; // Dummy value.
6305 :
6306 2718 : poDS->m_nBlockXSize = nXSize;
6307 2718 : poDS->m_nBlockYSize =
6308 2718 : std::min(static_cast<int>(poDS->m_nRowsPerStrip), nYSize);
6309 : }
6310 :
6311 3031 : if (!poDS->ComputeBlocksPerColRowAndBand(l_nBands))
6312 : {
6313 0 : delete poDS;
6314 0 : return nullptr;
6315 : }
6316 :
6317 3031 : poDS->m_eProfile = GetProfile(CSLFetchNameValue(papszParamList, "PROFILE"));
6318 :
6319 : /* -------------------------------------------------------------------- */
6320 : /* YCbCr JPEG compressed images should be translated on the fly */
6321 : /* to RGB by libtiff/libjpeg unless specifically requested */
6322 : /* otherwise. */
6323 : /* -------------------------------------------------------------------- */
6324 6095 : if (poDS->m_nCompression == COMPRESSION_JPEG &&
6325 3051 : poDS->m_nPhotometric == PHOTOMETRIC_YCBCR &&
6326 20 : CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
6327 : {
6328 20 : int nColorMode = 0;
6329 :
6330 20 : poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr", "IMAGE_STRUCTURE");
6331 40 : if (!TIFFGetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode) ||
6332 20 : nColorMode != JPEGCOLORMODE_RGB)
6333 20 : TIFFSetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
6334 : }
6335 :
6336 3031 : if (poDS->m_nCompression == COMPRESSION_LERC)
6337 : {
6338 26 : uint32_t nLercParamCount = 0;
6339 26 : uint32_t *panLercParams = nullptr;
6340 26 : if (TIFFGetField(l_hTIFF, TIFFTAG_LERC_PARAMETERS, &nLercParamCount,
6341 52 : &panLercParams) &&
6342 26 : nLercParamCount == 2)
6343 : {
6344 26 : memcpy(poDS->m_anLercAddCompressionAndVersion, panLercParams,
6345 : sizeof(poDS->m_anLercAddCompressionAndVersion));
6346 : }
6347 : }
6348 :
6349 : /* -------------------------------------------------------------------- */
6350 : /* Read palette back as a color table if it has one. */
6351 : /* -------------------------------------------------------------------- */
6352 3031 : unsigned short *panRed = nullptr;
6353 3031 : unsigned short *panGreen = nullptr;
6354 3031 : unsigned short *panBlue = nullptr;
6355 :
6356 3035 : if (poDS->m_nPhotometric == PHOTOMETRIC_PALETTE &&
6357 4 : TIFFGetField(l_hTIFF, TIFFTAG_COLORMAP, &panRed, &panGreen, &panBlue))
6358 : {
6359 :
6360 4 : poDS->m_poColorTable = new GDALColorTable();
6361 :
6362 4 : const int nColorCount = 1 << poDS->m_nBitsPerSample;
6363 :
6364 1028 : for (int iColor = nColorCount - 1; iColor >= 0; iColor--)
6365 : {
6366 1024 : const unsigned short divisor = 257;
6367 1024 : const GDALColorEntry oEntry = {
6368 1024 : static_cast<short>(panRed[iColor] / divisor),
6369 1024 : static_cast<short>(panGreen[iColor] / divisor),
6370 1024 : static_cast<short>(panBlue[iColor] / divisor),
6371 1024 : static_cast<short>(255)};
6372 :
6373 1024 : poDS->m_poColorTable->SetColorEntry(iColor, &oEntry);
6374 : }
6375 : }
6376 :
6377 : /* -------------------------------------------------------------------- */
6378 : /* Do we want to ensure all blocks get written out on close to */
6379 : /* avoid sparse files? */
6380 : /* -------------------------------------------------------------------- */
6381 3031 : if (!CPLFetchBool(papszParamList, "SPARSE_OK", false))
6382 2929 : poDS->m_bFillEmptyTilesAtClosing = true;
6383 :
6384 3031 : poDS->m_bWriteEmptyTiles =
6385 3697 : bStreaming || (poDS->m_nCompression != COMPRESSION_NONE &&
6386 666 : poDS->m_bFillEmptyTilesAtClosing);
6387 : // Only required for people writing non-compressed striped files in the
6388 : // right order and wanting all tstrips to be written in the same order
6389 : // so that the end result can be memory mapped without knowledge of each
6390 : // strip offset.
6391 3031 : if (CPLTestBool(CSLFetchNameValueDef(
6392 6062 : papszParamList, "WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")) ||
6393 3031 : CPLTestBool(CSLFetchNameValueDef(
6394 : papszParamList, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")))
6395 : {
6396 19 : poDS->m_bWriteEmptyTiles = true;
6397 : }
6398 :
6399 : /* -------------------------------------------------------------------- */
6400 : /* Preserve creation options for consulting later (for instance */
6401 : /* to decide if a TFW file should be written). */
6402 : /* -------------------------------------------------------------------- */
6403 3031 : poDS->m_papszCreationOptions = CSLDuplicate(papszParamList);
6404 :
6405 3031 : poDS->m_nZLevel = GTiffGetZLevel(papszParamList);
6406 3031 : poDS->m_nLZMAPreset = GTiffGetLZMAPreset(papszParamList);
6407 3031 : poDS->m_nZSTDLevel = GTiffGetZSTDPreset(papszParamList);
6408 3031 : poDS->m_nWebPLevel = GTiffGetWebPLevel(papszParamList);
6409 3031 : poDS->m_bWebPLossless = GTiffGetWebPLossless(papszParamList);
6410 3033 : if (poDS->m_nWebPLevel != 100 && poDS->m_bWebPLossless &&
6411 2 : CSLFetchNameValue(papszParamList, "WEBP_LEVEL"))
6412 : {
6413 0 : CPLError(CE_Warning, CPLE_AppDefined,
6414 : "WEBP_LEVEL is specified, but WEBP_LOSSLESS=YES. "
6415 : "WEBP_LEVEL will be ignored.");
6416 : }
6417 3031 : poDS->m_nJpegQuality = GTiffGetJpegQuality(papszParamList);
6418 3031 : poDS->m_nJpegTablesMode = GTiffGetJpegTablesMode(papszParamList);
6419 3031 : poDS->m_dfMaxZError = GTiffGetLERCMaxZError(papszParamList);
6420 3031 : poDS->m_dfMaxZErrorOverview = GTiffGetLERCMaxZErrorOverview(papszParamList);
6421 : #if HAVE_JXL
6422 3031 : poDS->m_bJXLLossless = GTiffGetJXLLossless(papszParamList);
6423 3031 : poDS->m_nJXLEffort = GTiffGetJXLEffort(papszParamList);
6424 3031 : poDS->m_fJXLDistance = GTiffGetJXLDistance(papszParamList);
6425 3031 : poDS->m_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszParamList);
6426 : #endif
6427 3031 : poDS->InitCreationOrOpenOptions(true, papszParamList);
6428 :
6429 : /* -------------------------------------------------------------------- */
6430 : /* Create band information objects. */
6431 : /* -------------------------------------------------------------------- */
6432 203846 : for (int iBand = 0; iBand < l_nBands; ++iBand)
6433 : {
6434 200815 : if (poDS->m_nBitsPerSample == 8 ||
6435 1425 : (poDS->m_nBitsPerSample == 16 && eType != GDT_Float32) ||
6436 903 : poDS->m_nBitsPerSample == 32 || poDS->m_nBitsPerSample == 64 ||
6437 127 : poDS->m_nBitsPerSample == 128)
6438 : {
6439 200748 : poDS->SetBand(iBand + 1, new GTiffRasterBand(poDS, iBand + 1));
6440 : }
6441 : else
6442 : {
6443 67 : poDS->SetBand(iBand + 1, new GTiffOddBitsBand(poDS, iBand + 1));
6444 134 : poDS->GetRasterBand(iBand + 1)->SetMetadataItem(
6445 134 : "NBITS", CPLString().Printf("%d", poDS->m_nBitsPerSample),
6446 67 : "IMAGE_STRUCTURE");
6447 : }
6448 : }
6449 :
6450 3031 : poDS->GetDiscardLsbOption(papszParamList);
6451 :
6452 3031 : if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG && l_nBands != 1)
6453 529 : poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
6454 : else
6455 2502 : poDS->SetMetadataItem("INTERLEAVE", "BAND", "IMAGE_STRUCTURE");
6456 :
6457 3031 : poDS->oOvManager.Initialize(poDS, pszFilename);
6458 :
6459 3031 : return poDS;
6460 : }
6461 :
6462 : /************************************************************************/
6463 : /* CopyImageryAndMask() */
6464 : /************************************************************************/
6465 :
6466 263 : CPLErr GTiffDataset::CopyImageryAndMask(GTiffDataset *poDstDS,
6467 : GDALDataset *poSrcDS,
6468 : GDALRasterBand *poSrcMaskBand,
6469 : GDALProgressFunc pfnProgress,
6470 : void *pProgressData)
6471 : {
6472 263 : CPLErr eErr = CE_None;
6473 :
6474 263 : const auto eType = poDstDS->GetRasterBand(1)->GetRasterDataType();
6475 263 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
6476 263 : const int l_nBands = poDstDS->GetRasterCount();
6477 : void *pBlockBuffer =
6478 263 : VSI_MALLOC3_VERBOSE(poDstDS->m_nBlockXSize, poDstDS->m_nBlockYSize,
6479 : cpl::fits_on<int>(l_nBands * nDataTypeSize));
6480 263 : if (pBlockBuffer == nullptr)
6481 : {
6482 0 : eErr = CE_Failure;
6483 : }
6484 263 : const int nYSize = poDstDS->nRasterYSize;
6485 263 : const int nXSize = poDstDS->nRasterXSize;
6486 263 : const int nBlocks = poDstDS->m_nBlocksPerBand;
6487 :
6488 263 : CPLAssert(l_nBands == 1 || poDstDS->m_nPlanarConfig == PLANARCONFIG_CONTIG);
6489 :
6490 : const bool bIsOddBand =
6491 263 : dynamic_cast<GTiffOddBitsBand *>(poDstDS->GetRasterBand(1)) != nullptr;
6492 :
6493 263 : if (poDstDS->m_poMaskDS)
6494 : {
6495 45 : CPLAssert(poDstDS->m_poMaskDS->m_nBlockXSize == poDstDS->m_nBlockXSize);
6496 45 : CPLAssert(poDstDS->m_poMaskDS->m_nBlockYSize == poDstDS->m_nBlockYSize);
6497 : }
6498 :
6499 263 : int iBlock = 0;
6500 7004 : for (int iY = 0, nYBlock = 0; iY < nYSize && eErr == CE_None;
6501 6741 : iY = ((nYSize - iY < poDstDS->m_nBlockYSize)
6502 6741 : ? nYSize
6503 6550 : : iY + poDstDS->m_nBlockYSize),
6504 : nYBlock++)
6505 : {
6506 6741 : const int nReqYSize = std::min(nYSize - iY, poDstDS->m_nBlockYSize);
6507 26352 : for (int iX = 0, nXBlock = 0; iX < nXSize && eErr == CE_None;
6508 19611 : iX = ((nXSize - iX < poDstDS->m_nBlockXSize)
6509 19611 : ? nXSize
6510 19351 : : iX + poDstDS->m_nBlockXSize),
6511 : nXBlock++)
6512 : {
6513 19611 : const int nReqXSize = std::min(nXSize - iX, poDstDS->m_nBlockXSize);
6514 19611 : if (nReqXSize < poDstDS->m_nBlockXSize ||
6515 19351 : nReqYSize < poDstDS->m_nBlockYSize)
6516 : {
6517 406 : memset(pBlockBuffer, 0,
6518 406 : static_cast<size_t>(poDstDS->m_nBlockXSize) *
6519 406 : poDstDS->m_nBlockYSize * l_nBands * nDataTypeSize);
6520 : }
6521 :
6522 19611 : if (!bIsOddBand)
6523 : {
6524 39100 : eErr = poSrcDS->RasterIO(
6525 : GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
6526 : nReqXSize, nReqYSize, eType, l_nBands, nullptr,
6527 19550 : static_cast<GSpacing>(nDataTypeSize) * l_nBands,
6528 19550 : static_cast<GSpacing>(nDataTypeSize) * l_nBands *
6529 19550 : poDstDS->m_nBlockXSize,
6530 : nDataTypeSize, nullptr);
6531 19550 : if (eErr == CE_None)
6532 : {
6533 19549 : eErr = poDstDS->WriteEncodedTileOrStrip(
6534 : iBlock, pBlockBuffer, false);
6535 : }
6536 : }
6537 : else
6538 : {
6539 : // In the odd bit case, this is a bit messy to ensure
6540 : // the strile gets written synchronously.
6541 : // We load the content of the n-1 bands in the cache,
6542 : // and for the last band we invoke WriteBlock() directly
6543 : // We also force FlushBlockBuf()
6544 122 : std::vector<GDALRasterBlock *> apoLockedBlocks;
6545 91 : for (int i = 0; eErr == CE_None && i < l_nBands - 1; i++)
6546 : {
6547 : auto poBlock =
6548 30 : poDstDS->GetRasterBand(i + 1)->GetLockedBlockRef(
6549 30 : nXBlock, nYBlock, TRUE);
6550 30 : if (poBlock)
6551 : {
6552 60 : eErr = poSrcDS->GetRasterBand(i + 1)->RasterIO(
6553 : GF_Read, iX, iY, nReqXSize, nReqYSize,
6554 : poBlock->GetDataRef(), nReqXSize, nReqYSize, eType,
6555 : nDataTypeSize,
6556 30 : static_cast<GSpacing>(nDataTypeSize) *
6557 30 : poDstDS->m_nBlockXSize,
6558 : nullptr);
6559 30 : poBlock->MarkDirty();
6560 30 : apoLockedBlocks.emplace_back(poBlock);
6561 : }
6562 : else
6563 : {
6564 0 : eErr = CE_Failure;
6565 : }
6566 : }
6567 61 : if (eErr == CE_None)
6568 : {
6569 122 : eErr = poSrcDS->GetRasterBand(l_nBands)->RasterIO(
6570 : GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
6571 : nReqXSize, nReqYSize, eType, nDataTypeSize,
6572 61 : static_cast<GSpacing>(nDataTypeSize) *
6573 61 : poDstDS->m_nBlockXSize,
6574 : nullptr);
6575 : }
6576 61 : if (eErr == CE_None)
6577 : {
6578 : // Avoid any attempt to load from disk
6579 61 : poDstDS->m_nLoadedBlock = iBlock;
6580 61 : eErr = poDstDS->GetRasterBand(l_nBands)->WriteBlock(
6581 : nXBlock, nYBlock, pBlockBuffer);
6582 61 : if (eErr == CE_None)
6583 61 : eErr = poDstDS->FlushBlockBuf();
6584 : }
6585 91 : for (auto poBlock : apoLockedBlocks)
6586 : {
6587 30 : poBlock->MarkClean();
6588 30 : poBlock->DropLock();
6589 : }
6590 : }
6591 :
6592 19611 : if (eErr == CE_None && poDstDS->m_poMaskDS)
6593 : {
6594 4619 : if (nReqXSize < poDstDS->m_nBlockXSize ||
6595 4587 : nReqYSize < poDstDS->m_nBlockYSize)
6596 : {
6597 57 : memset(pBlockBuffer, 0,
6598 57 : static_cast<size_t>(poDstDS->m_nBlockXSize) *
6599 57 : poDstDS->m_nBlockYSize);
6600 : }
6601 9238 : eErr = poSrcMaskBand->RasterIO(
6602 : GF_Read, iX, iY, nReqXSize, nReqYSize, pBlockBuffer,
6603 4619 : nReqXSize, nReqYSize, GDT_Byte, 1, poDstDS->m_nBlockXSize,
6604 : nullptr);
6605 4619 : if (eErr == CE_None)
6606 : {
6607 : // Avoid any attempt to load from disk
6608 4619 : poDstDS->m_poMaskDS->m_nLoadedBlock = iBlock;
6609 4619 : eErr = poDstDS->m_poMaskDS->GetRasterBand(1)->WriteBlock(
6610 : nXBlock, nYBlock, pBlockBuffer);
6611 4619 : if (eErr == CE_None)
6612 4619 : eErr = poDstDS->m_poMaskDS->FlushBlockBuf();
6613 : }
6614 : }
6615 19611 : if (poDstDS->m_bWriteError)
6616 6 : eErr = CE_Failure;
6617 :
6618 19611 : iBlock++;
6619 39222 : if (pfnProgress &&
6620 19611 : !pfnProgress(static_cast<double>(iBlock) / nBlocks, nullptr,
6621 : pProgressData))
6622 : {
6623 0 : eErr = CE_Failure;
6624 : }
6625 : }
6626 : }
6627 263 : poDstDS->FlushCache(false); // mostly to wait for thread completion
6628 263 : VSIFree(pBlockBuffer);
6629 :
6630 263 : return eErr;
6631 : }
6632 :
6633 : /************************************************************************/
6634 : /* CreateCopy() */
6635 : /************************************************************************/
6636 :
6637 1061 : GDALDataset *GTiffDataset::CreateCopy(const char *pszFilename,
6638 : GDALDataset *poSrcDS, int bStrict,
6639 : char **papszOptions,
6640 : GDALProgressFunc pfnProgress,
6641 : void *pProgressData)
6642 :
6643 : {
6644 1061 : if (poSrcDS->GetRasterCount() == 0)
6645 : {
6646 2 : ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
6647 : "Unable to export GeoTIFF files with zero bands.");
6648 2 : return nullptr;
6649 : }
6650 :
6651 1059 : GDALRasterBand *const poPBand = poSrcDS->GetRasterBand(1);
6652 1059 : GDALDataType eType = poPBand->GetRasterDataType();
6653 :
6654 : /* -------------------------------------------------------------------- */
6655 : /* Check, whether all bands in input dataset has the same type. */
6656 : /* -------------------------------------------------------------------- */
6657 1059 : const int l_nBands = poSrcDS->GetRasterCount();
6658 2255 : for (int iBand = 2; iBand <= l_nBands; ++iBand)
6659 : {
6660 1196 : if (eType != poSrcDS->GetRasterBand(iBand)->GetRasterDataType())
6661 : {
6662 0 : if (bStrict)
6663 : {
6664 0 : ReportError(
6665 : pszFilename, CE_Failure, CPLE_AppDefined,
6666 : "Unable to export GeoTIFF file with different datatypes "
6667 : "per different bands. All bands should have the same "
6668 : "types in TIFF.");
6669 0 : return nullptr;
6670 : }
6671 : else
6672 : {
6673 0 : ReportError(
6674 : pszFilename, CE_Warning, CPLE_AppDefined,
6675 : "Unable to export GeoTIFF file with different datatypes "
6676 : "per different bands. All bands should have the same "
6677 : "types in TIFF.");
6678 : }
6679 : }
6680 : }
6681 :
6682 : /* -------------------------------------------------------------------- */
6683 : /* Capture the profile. */
6684 : /* -------------------------------------------------------------------- */
6685 : const GTiffProfile eProfile =
6686 1059 : GetProfile(CSLFetchNameValue(papszOptions, "PROFILE"));
6687 :
6688 1059 : const bool bGeoTIFF = eProfile != GTiffProfile::BASELINE;
6689 :
6690 : /* -------------------------------------------------------------------- */
6691 : /* Special handling for NBITS. Copy from band metadata if found. */
6692 : /* -------------------------------------------------------------------- */
6693 1059 : char **papszCreateOptions = CSLDuplicate(papszOptions);
6694 :
6695 1059 : if (poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE") != nullptr &&
6696 1077 : atoi(poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE")) > 0 &&
6697 18 : CSLFetchNameValue(papszCreateOptions, "NBITS") == nullptr)
6698 : {
6699 4 : papszCreateOptions = CSLSetNameValue(
6700 : papszCreateOptions, "NBITS",
6701 4 : poPBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE"));
6702 : }
6703 :
6704 1059 : if (CSLFetchNameValue(papszOptions, "PIXELTYPE") == nullptr &&
6705 : eType == GDT_Byte)
6706 : {
6707 771 : poPBand->EnablePixelTypeSignedByteWarning(false);
6708 : const char *pszPixelType =
6709 771 : poPBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
6710 771 : poPBand->EnablePixelTypeSignedByteWarning(true);
6711 771 : if (pszPixelType)
6712 : {
6713 1 : papszCreateOptions =
6714 1 : CSLSetNameValue(papszCreateOptions, "PIXELTYPE", pszPixelType);
6715 : }
6716 : }
6717 :
6718 : /* -------------------------------------------------------------------- */
6719 : /* Color profile. Copy from band metadata if found. */
6720 : /* -------------------------------------------------------------------- */
6721 1059 : if (bGeoTIFF)
6722 : {
6723 1043 : const char *pszOptionsMD[] = {"SOURCE_ICC_PROFILE",
6724 : "SOURCE_PRIMARIES_RED",
6725 : "SOURCE_PRIMARIES_GREEN",
6726 : "SOURCE_PRIMARIES_BLUE",
6727 : "SOURCE_WHITEPOINT",
6728 : "TIFFTAG_TRANSFERFUNCTION_RED",
6729 : "TIFFTAG_TRANSFERFUNCTION_GREEN",
6730 : "TIFFTAG_TRANSFERFUNCTION_BLUE",
6731 : "TIFFTAG_TRANSFERRANGE_BLACK",
6732 : "TIFFTAG_TRANSFERRANGE_WHITE",
6733 : nullptr};
6734 :
6735 : // Copy all the tags. Options will override tags in the source.
6736 1043 : int i = 0;
6737 11453 : while (pszOptionsMD[i] != nullptr)
6738 : {
6739 : char const *pszMD =
6740 10412 : CSLFetchNameValue(papszOptions, pszOptionsMD[i]);
6741 10412 : if (pszMD == nullptr)
6742 : pszMD =
6743 10404 : poSrcDS->GetMetadataItem(pszOptionsMD[i], "COLOR_PROFILE");
6744 :
6745 10412 : if ((pszMD != nullptr) && !EQUAL(pszMD, ""))
6746 : {
6747 16 : papszCreateOptions =
6748 16 : CSLSetNameValue(papszCreateOptions, pszOptionsMD[i], pszMD);
6749 :
6750 : // If an ICC profile exists, other tags are not needed.
6751 16 : if (EQUAL(pszOptionsMD[i], "SOURCE_ICC_PROFILE"))
6752 2 : break;
6753 : }
6754 :
6755 10410 : ++i;
6756 : }
6757 : }
6758 :
6759 1059 : double dfExtraSpaceForOverviews = 0;
6760 : const bool bCopySrcOverviews =
6761 1059 : CPLFetchBool(papszCreateOptions, "COPY_SRC_OVERVIEWS", false);
6762 1059 : std::unique_ptr<GDALDataset> poOvrDS;
6763 1059 : int nSrcOverviews = 0;
6764 1059 : if (bCopySrcOverviews)
6765 : {
6766 : const char *pszOvrDS =
6767 168 : CSLFetchNameValue(papszCreateOptions, "@OVERVIEW_DATASET");
6768 168 : if (pszOvrDS)
6769 : {
6770 : // Empty string is used by COG driver to indicate that we want
6771 : // to ignore source overviews.
6772 33 : if (!EQUAL(pszOvrDS, ""))
6773 : {
6774 31 : poOvrDS.reset(GDALDataset::Open(pszOvrDS));
6775 31 : if (!poOvrDS)
6776 : {
6777 0 : CSLDestroy(papszCreateOptions);
6778 0 : return nullptr;
6779 : }
6780 31 : if (poOvrDS->GetRasterCount() != l_nBands)
6781 : {
6782 0 : CSLDestroy(papszCreateOptions);
6783 0 : return nullptr;
6784 : }
6785 31 : nSrcOverviews =
6786 31 : poOvrDS->GetRasterBand(1)->GetOverviewCount() + 1;
6787 : }
6788 : }
6789 : else
6790 : {
6791 135 : nSrcOverviews = poSrcDS->GetRasterBand(1)->GetOverviewCount();
6792 : }
6793 :
6794 : // Limit number of overviews if specified
6795 : const char *pszOverviewCount =
6796 168 : CSLFetchNameValue(papszCreateOptions, "@OVERVIEW_COUNT");
6797 168 : if (pszOverviewCount)
6798 7 : nSrcOverviews =
6799 7 : std::max(0, std::min(nSrcOverviews, atoi(pszOverviewCount)));
6800 :
6801 168 : if (nSrcOverviews)
6802 : {
6803 160 : for (int j = 1; j <= l_nBands; ++j)
6804 : {
6805 : const int nOtherBandOverviewCount =
6806 104 : poOvrDS ? poOvrDS->GetRasterBand(j)->GetOverviewCount() + 1
6807 144 : : poSrcDS->GetRasterBand(j)->GetOverviewCount();
6808 104 : if (nOtherBandOverviewCount < nSrcOverviews)
6809 : {
6810 1 : ReportError(
6811 : pszFilename, CE_Failure, CPLE_NotSupported,
6812 : "COPY_SRC_OVERVIEWS cannot be used when the bands have "
6813 : "not the same number of overview levels.");
6814 1 : CSLDestroy(papszCreateOptions);
6815 1 : return nullptr;
6816 : }
6817 320 : for (int i = 0; i < nSrcOverviews; ++i)
6818 : {
6819 : GDALRasterBand *poOvrBand =
6820 : poOvrDS
6821 309 : ? (i == 0 ? poOvrDS->GetRasterBand(j)
6822 180 : : poOvrDS->GetRasterBand(j)->GetOverview(
6823 90 : i - 1))
6824 284 : : poSrcDS->GetRasterBand(j)->GetOverview(i);
6825 219 : if (poOvrBand == nullptr)
6826 : {
6827 1 : ReportError(
6828 : pszFilename, CE_Failure, CPLE_NotSupported,
6829 : "COPY_SRC_OVERVIEWS cannot be used when one "
6830 : "overview band is NULL.");
6831 1 : CSLDestroy(papszCreateOptions);
6832 1 : return nullptr;
6833 : }
6834 : GDALRasterBand *poOvrFirstBand =
6835 : poOvrDS
6836 308 : ? (i == 0 ? poOvrDS->GetRasterBand(1)
6837 180 : : poOvrDS->GetRasterBand(1)->GetOverview(
6838 90 : i - 1))
6839 282 : : poSrcDS->GetRasterBand(1)->GetOverview(i);
6840 435 : if (poOvrBand->GetXSize() != poOvrFirstBand->GetXSize() ||
6841 217 : poOvrBand->GetYSize() != poOvrFirstBand->GetYSize())
6842 : {
6843 1 : ReportError(
6844 : pszFilename, CE_Failure, CPLE_NotSupported,
6845 : "COPY_SRC_OVERVIEWS cannot be used when the "
6846 : "overview bands have not the same dimensions "
6847 : "among bands.");
6848 1 : CSLDestroy(papszCreateOptions);
6849 1 : return nullptr;
6850 : }
6851 : }
6852 : }
6853 :
6854 166 : for (int i = 0; i < nSrcOverviews; ++i)
6855 : {
6856 : GDALRasterBand *poOvrFirstBand =
6857 : poOvrDS
6858 177 : ? (i == 0
6859 67 : ? poOvrDS->GetRasterBand(1)
6860 36 : : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
6861 153 : : poSrcDS->GetRasterBand(1)->GetOverview(i);
6862 110 : dfExtraSpaceForOverviews +=
6863 110 : static_cast<double>(poOvrFirstBand->GetXSize()) *
6864 110 : poOvrFirstBand->GetYSize();
6865 : }
6866 56 : dfExtraSpaceForOverviews *=
6867 56 : l_nBands * GDALGetDataTypeSizeBytes(eType);
6868 : }
6869 : else
6870 : {
6871 109 : CPLDebug("GTiff", "No source overviews to copy");
6872 : }
6873 : }
6874 :
6875 : /* -------------------------------------------------------------------- */
6876 : /* Should we use optimized way of copying from an input JPEG */
6877 : /* dataset? */
6878 : /* -------------------------------------------------------------------- */
6879 :
6880 : // TODO(schwehr): Refactor bDirectCopyFromJPEG to be a const.
6881 : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
6882 1056 : bool bDirectCopyFromJPEG = false;
6883 : #endif
6884 :
6885 : // Note: JPEG_DIRECT_COPY is not defined by default, because it is mainly
6886 : // useful for debugging purposes.
6887 : #ifdef JPEG_DIRECT_COPY
6888 : if (CPLFetchBool(papszCreateOptions, "JPEG_DIRECT_COPY", false) &&
6889 : GTIFF_CanDirectCopyFromJPEG(poSrcDS, papszCreateOptions))
6890 : {
6891 : CPLDebug("GTiff", "Using special direct copy mode from a JPEG dataset");
6892 :
6893 : bDirectCopyFromJPEG = true;
6894 : }
6895 : #endif
6896 :
6897 : #ifdef HAVE_LIBJPEG
6898 1056 : bool bCopyFromJPEG = false;
6899 :
6900 : // When CreateCopy'ing() from a JPEG dataset, and asking for COMPRESS=JPEG,
6901 : // use DCT coefficients (unless other options are incompatible, like
6902 : // strip/tile dimensions, specifying JPEG_QUALITY option, incompatible
6903 : // PHOTOMETRIC with the source colorspace, etc.) to avoid the lossy steps
6904 : // involved by decompression/recompression.
6905 2112 : if (!bDirectCopyFromJPEG &&
6906 1056 : GTIFF_CanCopyFromJPEG(poSrcDS, papszCreateOptions))
6907 : {
6908 12 : CPLDebug("GTiff", "Using special copy mode from a JPEG dataset");
6909 :
6910 12 : bCopyFromJPEG = true;
6911 : }
6912 : #endif
6913 :
6914 : /* -------------------------------------------------------------------- */
6915 : /* If the source is RGB, then set the PHOTOMETRIC=RGB value */
6916 : /* -------------------------------------------------------------------- */
6917 :
6918 : const bool bForcePhotometric =
6919 1056 : CSLFetchNameValue(papszOptions, "PHOTOMETRIC") != nullptr;
6920 :
6921 375 : if (l_nBands >= 3 && !bForcePhotometric &&
6922 : #ifdef HAVE_LIBJPEG
6923 337 : !bCopyFromJPEG &&
6924 : #endif
6925 331 : poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_RedBand &&
6926 1670 : poSrcDS->GetRasterBand(2)->GetColorInterpretation() == GCI_GreenBand &&
6927 239 : poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_BlueBand)
6928 : {
6929 233 : papszCreateOptions =
6930 233 : CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "RGB");
6931 : }
6932 :
6933 : /* -------------------------------------------------------------------- */
6934 : /* Create the file. */
6935 : /* -------------------------------------------------------------------- */
6936 1056 : VSILFILE *l_fpL = nullptr;
6937 2112 : CPLString l_osTmpFilename;
6938 :
6939 1056 : const int nXSize = poSrcDS->GetRasterXSize();
6940 1056 : const int nYSize = poSrcDS->GetRasterYSize();
6941 1056 : TIFF *l_hTIFF = CreateLL(pszFilename, nXSize, nYSize, l_nBands, eType,
6942 : dfExtraSpaceForOverviews, papszCreateOptions,
6943 : &l_fpL, l_osTmpFilename);
6944 1056 : const bool bStreaming = !l_osTmpFilename.empty();
6945 :
6946 1056 : CSLDestroy(papszCreateOptions);
6947 1056 : papszCreateOptions = nullptr;
6948 :
6949 1056 : if (l_hTIFF == nullptr)
6950 : {
6951 14 : if (bStreaming)
6952 0 : VSIUnlink(l_osTmpFilename);
6953 14 : return nullptr;
6954 : }
6955 :
6956 1042 : uint16_t l_nPlanarConfig = 0;
6957 1042 : TIFFGetField(l_hTIFF, TIFFTAG_PLANARCONFIG, &l_nPlanarConfig);
6958 :
6959 1042 : uint16_t l_nCompression = 0;
6960 :
6961 1042 : if (!TIFFGetField(l_hTIFF, TIFFTAG_COMPRESSION, &(l_nCompression)))
6962 0 : l_nCompression = COMPRESSION_NONE;
6963 :
6964 : /* -------------------------------------------------------------------- */
6965 : /* Set the alpha channel if we find one. */
6966 : /* -------------------------------------------------------------------- */
6967 1042 : uint16_t *extraSamples = nullptr;
6968 1042 : uint16_t nExtraSamples = 0;
6969 1042 : if (TIFFGetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples,
6970 1269 : &extraSamples) &&
6971 227 : nExtraSamples > 0)
6972 : {
6973 : // We need to allocate a new array as (current) libtiff
6974 : // versions will not like that we reuse the array we got from
6975 : // TIFFGetField().
6976 : uint16_t *pasNewExtraSamples = static_cast<uint16_t *>(
6977 227 : CPLMalloc(nExtraSamples * sizeof(uint16_t)));
6978 227 : memcpy(pasNewExtraSamples, extraSamples,
6979 227 : nExtraSamples * sizeof(uint16_t));
6980 227 : uint16_t nAlpha = GTiffGetAlphaValue(
6981 : CPLGetConfigOption("GTIFF_ALPHA",
6982 : CSLFetchNameValue(papszOptions, "ALPHA")),
6983 : DEFAULT_ALPHA_TYPE);
6984 227 : const int nBaseSamples = l_nBands - nExtraSamples;
6985 798 : for (int iExtraBand = nBaseSamples + 1; iExtraBand <= l_nBands;
6986 : iExtraBand++)
6987 : {
6988 571 : if (poSrcDS->GetRasterBand(iExtraBand)->GetColorInterpretation() ==
6989 : GCI_AlphaBand)
6990 : {
6991 128 : pasNewExtraSamples[iExtraBand - nBaseSamples - 1] = nAlpha;
6992 : }
6993 : }
6994 227 : TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples,
6995 : pasNewExtraSamples);
6996 :
6997 227 : CPLFree(pasNewExtraSamples);
6998 : }
6999 :
7000 : /* -------------------------------------------------------------------- */
7001 : /* If the output is jpeg compressed, and the input is RGB make */
7002 : /* sure we note that. */
7003 : /* -------------------------------------------------------------------- */
7004 :
7005 1042 : if (l_nCompression == COMPRESSION_JPEG)
7006 : {
7007 130 : if (l_nBands >= 3 &&
7008 56 : (poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
7009 0 : GCI_YCbCr_YBand) &&
7010 0 : (poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
7011 130 : GCI_YCbCr_CbBand) &&
7012 0 : (poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
7013 : GCI_YCbCr_CrBand))
7014 : {
7015 : // Do nothing.
7016 : }
7017 : else
7018 : {
7019 : // Assume RGB if it is not explicitly YCbCr.
7020 74 : CPLDebug("GTiff", "Setting JPEGCOLORMODE_RGB");
7021 74 : TIFFSetField(l_hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
7022 : }
7023 : }
7024 :
7025 : /* -------------------------------------------------------------------- */
7026 : /* Does the source image consist of one band, with a palette? */
7027 : /* If so, copy over. */
7028 : /* -------------------------------------------------------------------- */
7029 446 : if ((l_nBands == 1 || l_nBands == 2) &&
7030 1488 : poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
7031 : eType == GDT_Byte)
7032 : {
7033 10 : unsigned short anTRed[256] = {0};
7034 10 : unsigned short anTGreen[256] = {0};
7035 10 : unsigned short anTBlue[256] = {0};
7036 10 : GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
7037 :
7038 2570 : for (int iColor = 0; iColor < 256; ++iColor)
7039 : {
7040 2560 : if (iColor < poCT->GetColorEntryCount())
7041 : {
7042 1505 : GDALColorEntry sRGB = {0, 0, 0, 0};
7043 :
7044 1505 : poCT->GetColorEntryAsRGB(iColor, &sRGB);
7045 :
7046 1505 : anTRed[iColor] = static_cast<unsigned short>(257 * sRGB.c1);
7047 1505 : anTGreen[iColor] = static_cast<unsigned short>(257 * sRGB.c2);
7048 1505 : anTBlue[iColor] = static_cast<unsigned short>(257 * sRGB.c3);
7049 : }
7050 : else
7051 : {
7052 1055 : anTRed[iColor] = 0;
7053 1055 : anTGreen[iColor] = 0;
7054 1055 : anTBlue[iColor] = 0;
7055 : }
7056 : }
7057 :
7058 10 : if (!bForcePhotometric)
7059 10 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
7060 10 : TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, anTRed, anTGreen, anTBlue);
7061 : }
7062 445 : else if ((l_nBands == 1 || l_nBands == 2) &&
7063 1477 : poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
7064 : eType == GDT_UInt16)
7065 : {
7066 : unsigned short *panTRed = static_cast<unsigned short *>(
7067 1 : CPLMalloc(65536 * sizeof(unsigned short)));
7068 : unsigned short *panTGreen = static_cast<unsigned short *>(
7069 1 : CPLMalloc(65536 * sizeof(unsigned short)));
7070 : unsigned short *panTBlue = static_cast<unsigned short *>(
7071 1 : CPLMalloc(65536 * sizeof(unsigned short)));
7072 :
7073 1 : GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
7074 :
7075 65537 : for (int iColor = 0; iColor < 65536; ++iColor)
7076 : {
7077 65536 : if (iColor < poCT->GetColorEntryCount())
7078 : {
7079 65536 : GDALColorEntry sRGB = {0, 0, 0, 0};
7080 :
7081 65536 : poCT->GetColorEntryAsRGB(iColor, &sRGB);
7082 :
7083 65536 : panTRed[iColor] = static_cast<unsigned short>(257 * sRGB.c1);
7084 65536 : panTGreen[iColor] = static_cast<unsigned short>(257 * sRGB.c2);
7085 65536 : panTBlue[iColor] = static_cast<unsigned short>(257 * sRGB.c3);
7086 : }
7087 : else
7088 : {
7089 0 : panTRed[iColor] = 0;
7090 0 : panTGreen[iColor] = 0;
7091 0 : panTBlue[iColor] = 0;
7092 : }
7093 : }
7094 :
7095 1 : if (!bForcePhotometric)
7096 1 : TIFFSetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
7097 1 : TIFFSetField(l_hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue);
7098 :
7099 1 : CPLFree(panTRed);
7100 1 : CPLFree(panTGreen);
7101 1 : CPLFree(panTBlue);
7102 : }
7103 1031 : else if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
7104 1 : ReportError(
7105 : pszFilename, CE_Failure, CPLE_AppDefined,
7106 : "Unable to export color table to GeoTIFF file. Color tables "
7107 : "can only be written to 1 band or 2 bands Byte or "
7108 : "UInt16 GeoTIFF files.");
7109 :
7110 1042 : if (l_nCompression == COMPRESSION_JPEG)
7111 : {
7112 74 : uint16_t l_nPhotometric = 0;
7113 74 : TIFFGetField(l_hTIFF, TIFFTAG_PHOTOMETRIC, &l_nPhotometric);
7114 : // Check done in tif_jpeg.c later, but not with a very clear error
7115 : // message
7116 74 : if (l_nPhotometric == PHOTOMETRIC_PALETTE)
7117 : {
7118 1 : ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
7119 : "JPEG compression not supported with paletted image");
7120 1 : XTIFFClose(l_hTIFF);
7121 1 : VSIUnlink(l_osTmpFilename);
7122 1 : CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
7123 1 : return nullptr;
7124 : }
7125 : }
7126 :
7127 1113 : if (l_nBands == 2 &&
7128 1041 : poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
7129 0 : (eType == GDT_Byte || eType == GDT_UInt16))
7130 : {
7131 1 : uint16_t v[1] = {EXTRASAMPLE_UNASSALPHA};
7132 :
7133 1 : TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
7134 : }
7135 :
7136 1041 : const int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags();
7137 1041 : bool bCreateMask = false;
7138 2082 : CPLString osHiddenStructuralMD;
7139 1041 : if ((l_nBands == 1 || l_nPlanarConfig == PLANARCONFIG_CONTIG) &&
7140 : bCopySrcOverviews)
7141 : {
7142 161 : osHiddenStructuralMD += "LAYOUT=IFDS_BEFORE_DATA\n";
7143 161 : osHiddenStructuralMD += "BLOCK_ORDER=ROW_MAJOR\n";
7144 161 : osHiddenStructuralMD += "BLOCK_LEADER=SIZE_AS_UINT4\n";
7145 161 : osHiddenStructuralMD += "BLOCK_TRAILER=LAST_4_BYTES_REPEATED\n";
7146 : osHiddenStructuralMD +=
7147 161 : "KNOWN_INCOMPATIBLE_EDITION=NO\n "; // Final space intended, so
7148 : // this can be replaced by YES
7149 : }
7150 1041 : if (!(nMaskFlags & (GMF_ALL_VALID | GMF_ALPHA | GMF_NODATA)) &&
7151 30 : (nMaskFlags & GMF_PER_DATASET) && !bStreaming)
7152 : {
7153 27 : bCreateMask = true;
7154 53 : if (GTiffDataset::MustCreateInternalMask() &&
7155 26 : !osHiddenStructuralMD.empty())
7156 : {
7157 21 : osHiddenStructuralMD += "MASK_INTERLEAVED_WITH_IMAGERY=YES\n";
7158 : }
7159 : }
7160 1041 : if (!osHiddenStructuralMD.empty())
7161 : {
7162 161 : const int nHiddenMDSize = static_cast<int>(osHiddenStructuralMD.size());
7163 : osHiddenStructuralMD =
7164 161 : CPLOPrintf("GDAL_STRUCTURAL_METADATA_SIZE=%06d bytes\n",
7165 322 : nHiddenMDSize) +
7166 161 : osHiddenStructuralMD;
7167 161 : VSI_TIFFWrite(l_hTIFF, osHiddenStructuralMD.c_str(),
7168 : osHiddenStructuralMD.size());
7169 : }
7170 :
7171 : // FIXME? libtiff writes extended tags in the order they are specified
7172 : // and not in increasing order.
7173 :
7174 : /* -------------------------------------------------------------------- */
7175 : /* Transfer some TIFF specific metadata, if available. */
7176 : /* The return value will tell us if we need to try again later with*/
7177 : /* PAM because the profile doesn't allow to write some metadata */
7178 : /* as TIFF tag */
7179 : /* -------------------------------------------------------------------- */
7180 1041 : const bool bHasWrittenMDInGeotiffTAG = GTiffDataset::WriteMetadata(
7181 : poSrcDS, l_hTIFF, false, eProfile, pszFilename, papszOptions);
7182 :
7183 : /* -------------------------------------------------------------------- */
7184 : /* Write NoData value, if exist. */
7185 : /* -------------------------------------------------------------------- */
7186 1041 : if (eProfile == GTiffProfile::GDALGEOTIFF)
7187 : {
7188 1021 : int bSuccess = FALSE;
7189 1021 : GDALRasterBand *poFirstBand = poSrcDS->GetRasterBand(1);
7190 1021 : if (poFirstBand->GetRasterDataType() == GDT_Int64)
7191 : {
7192 2 : const auto nNoData = poFirstBand->GetNoDataValueAsInt64(&bSuccess);
7193 2 : if (bSuccess)
7194 1 : GTiffDataset::WriteNoDataValue(l_hTIFF, nNoData);
7195 : }
7196 1019 : else if (poFirstBand->GetRasterDataType() == GDT_UInt64)
7197 : {
7198 2 : const auto nNoData = poFirstBand->GetNoDataValueAsUInt64(&bSuccess);
7199 2 : if (bSuccess)
7200 1 : GTiffDataset::WriteNoDataValue(l_hTIFF, nNoData);
7201 : }
7202 : else
7203 : {
7204 1017 : const auto dfNoData = poFirstBand->GetNoDataValue(&bSuccess);
7205 1017 : if (bSuccess)
7206 89 : GTiffDataset::WriteNoDataValue(l_hTIFF, dfNoData);
7207 : }
7208 : }
7209 :
7210 : /* -------------------------------------------------------------------- */
7211 : /* Are we addressing PixelIsPoint mode? */
7212 : /* -------------------------------------------------------------------- */
7213 1041 : bool bPixelIsPoint = false;
7214 1041 : bool bPointGeoIgnore = false;
7215 :
7216 1514 : if (poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT) &&
7217 473 : EQUAL(poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT), GDALMD_AOP_POINT))
7218 : {
7219 12 : bPixelIsPoint = true;
7220 : bPointGeoIgnore =
7221 12 : CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
7222 : }
7223 :
7224 : /* -------------------------------------------------------------------- */
7225 : /* Write affine transform if it is meaningful. */
7226 : /* -------------------------------------------------------------------- */
7227 1041 : const OGRSpatialReference *l_poSRS = nullptr;
7228 1041 : double l_adfGeoTransform[6] = {0.0};
7229 :
7230 1041 : if (poSrcDS->GetGeoTransform(l_adfGeoTransform) == CE_None)
7231 : {
7232 611 : if (bGeoTIFF)
7233 : {
7234 606 : l_poSRS = poSrcDS->GetSpatialRef();
7235 :
7236 606 : if (l_adfGeoTransform[2] == 0.0 && l_adfGeoTransform[4] == 0.0 &&
7237 603 : l_adfGeoTransform[5] < 0.0)
7238 : {
7239 600 : double dfOffset = 0.0;
7240 : {
7241 : // In the case the SRS has a vertical component and we have
7242 : // a single band, encode its scale/offset in the GeoTIFF
7243 : // tags
7244 600 : int bHasScale = FALSE;
7245 : double dfScale =
7246 600 : poSrcDS->GetRasterBand(1)->GetScale(&bHasScale);
7247 600 : int bHasOffset = FALSE;
7248 : dfOffset =
7249 600 : poSrcDS->GetRasterBand(1)->GetOffset(&bHasOffset);
7250 : const bool bApplyScaleOffset =
7251 604 : l_poSRS && l_poSRS->IsVertical() &&
7252 4 : poSrcDS->GetRasterCount() == 1;
7253 600 : if (bApplyScaleOffset && !bHasScale)
7254 0 : dfScale = 1.0;
7255 600 : if (!bApplyScaleOffset || !bHasOffset)
7256 596 : dfOffset = 0.0;
7257 : const double adfPixelScale[3] = {
7258 600 : l_adfGeoTransform[1], fabs(l_adfGeoTransform[5]),
7259 600 : bApplyScaleOffset ? dfScale : 0.0};
7260 :
7261 600 : TIFFSetField(l_hTIFF, TIFFTAG_GEOPIXELSCALE, 3,
7262 : adfPixelScale);
7263 : }
7264 :
7265 600 : double adfTiePoints[6] = {0.0,
7266 : 0.0,
7267 : 0.0,
7268 600 : l_adfGeoTransform[0],
7269 600 : l_adfGeoTransform[3],
7270 600 : dfOffset};
7271 :
7272 600 : if (bPixelIsPoint && !bPointGeoIgnore)
7273 : {
7274 8 : adfTiePoints[3] +=
7275 8 : l_adfGeoTransform[1] * 0.5 + l_adfGeoTransform[2] * 0.5;
7276 8 : adfTiePoints[4] +=
7277 8 : l_adfGeoTransform[4] * 0.5 + l_adfGeoTransform[5] * 0.5;
7278 : }
7279 :
7280 600 : TIFFSetField(l_hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
7281 : }
7282 : else
7283 : {
7284 6 : double adfMatrix[16] = {0.0};
7285 :
7286 6 : adfMatrix[0] = l_adfGeoTransform[1];
7287 6 : adfMatrix[1] = l_adfGeoTransform[2];
7288 6 : adfMatrix[3] = l_adfGeoTransform[0];
7289 6 : adfMatrix[4] = l_adfGeoTransform[4];
7290 6 : adfMatrix[5] = l_adfGeoTransform[5];
7291 6 : adfMatrix[7] = l_adfGeoTransform[3];
7292 6 : adfMatrix[15] = 1.0;
7293 :
7294 6 : if (bPixelIsPoint && !bPointGeoIgnore)
7295 : {
7296 0 : adfMatrix[3] +=
7297 0 : l_adfGeoTransform[1] * 0.5 + l_adfGeoTransform[2] * 0.5;
7298 0 : adfMatrix[7] +=
7299 0 : l_adfGeoTransform[4] * 0.5 + l_adfGeoTransform[5] * 0.5;
7300 : }
7301 :
7302 6 : TIFFSetField(l_hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
7303 : }
7304 : }
7305 :
7306 : /* --------------------------------------------------------------------
7307 : */
7308 : /* Do we need a TFW file? */
7309 : /* --------------------------------------------------------------------
7310 : */
7311 611 : if (CPLFetchBool(papszOptions, "TFW", false))
7312 2 : GDALWriteWorldFile(pszFilename, "tfw", l_adfGeoTransform);
7313 609 : else if (CPLFetchBool(papszOptions, "WORLDFILE", false))
7314 1 : GDALWriteWorldFile(pszFilename, "wld", l_adfGeoTransform);
7315 : }
7316 :
7317 : /* -------------------------------------------------------------------- */
7318 : /* Otherwise write tiepoints if they are available. */
7319 : /* -------------------------------------------------------------------- */
7320 430 : else if (poSrcDS->GetGCPCount() > 0 && bGeoTIFF)
7321 : {
7322 12 : const GDAL_GCP *pasGCPs = poSrcDS->GetGCPs();
7323 : double *padfTiePoints = static_cast<double *>(
7324 12 : CPLMalloc(6 * sizeof(double) * poSrcDS->GetGCPCount()));
7325 :
7326 60 : for (int iGCP = 0; iGCP < poSrcDS->GetGCPCount(); ++iGCP)
7327 : {
7328 :
7329 48 : padfTiePoints[iGCP * 6 + 0] = pasGCPs[iGCP].dfGCPPixel;
7330 48 : padfTiePoints[iGCP * 6 + 1] = pasGCPs[iGCP].dfGCPLine;
7331 48 : padfTiePoints[iGCP * 6 + 2] = 0;
7332 48 : padfTiePoints[iGCP * 6 + 3] = pasGCPs[iGCP].dfGCPX;
7333 48 : padfTiePoints[iGCP * 6 + 4] = pasGCPs[iGCP].dfGCPY;
7334 48 : padfTiePoints[iGCP * 6 + 5] = pasGCPs[iGCP].dfGCPZ;
7335 :
7336 48 : if (bPixelIsPoint && !bPointGeoIgnore)
7337 : {
7338 4 : padfTiePoints[iGCP * 6 + 0] -= 0.5;
7339 4 : padfTiePoints[iGCP * 6 + 1] -= 0.5;
7340 : }
7341 : }
7342 :
7343 12 : TIFFSetField(l_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * poSrcDS->GetGCPCount(),
7344 : padfTiePoints);
7345 12 : CPLFree(padfTiePoints);
7346 :
7347 12 : l_poSRS = poSrcDS->GetGCPSpatialRef();
7348 :
7349 24 : if (CPLFetchBool(papszOptions, "TFW", false) ||
7350 12 : CPLFetchBool(papszOptions, "WORLDFILE", false))
7351 : {
7352 0 : ReportError(
7353 : pszFilename, CE_Warning, CPLE_AppDefined,
7354 : "TFW=ON or WORLDFILE=ON creation options are ignored when "
7355 : "GCPs are available");
7356 : }
7357 : }
7358 : else
7359 : {
7360 418 : l_poSRS = poSrcDS->GetSpatialRef();
7361 : }
7362 :
7363 : /* -------------------------------------------------------------------- */
7364 : /* Copy xml:XMP data */
7365 : /* -------------------------------------------------------------------- */
7366 1041 : char **papszXMP = poSrcDS->GetMetadata("xml:XMP");
7367 1041 : if (papszXMP != nullptr && *papszXMP != nullptr)
7368 : {
7369 9 : int nTagSize = static_cast<int>(strlen(*papszXMP));
7370 9 : TIFFSetField(l_hTIFF, TIFFTAG_XMLPACKET, nTagSize, *papszXMP);
7371 : }
7372 :
7373 : /* -------------------------------------------------------------------- */
7374 : /* Write the projection information, if possible. */
7375 : /* -------------------------------------------------------------------- */
7376 1041 : const bool bHasProjection = l_poSRS != nullptr;
7377 1041 : bool bExportSRSToPAM = false;
7378 1041 : if ((bHasProjection || bPixelIsPoint) && bGeoTIFF)
7379 : {
7380 603 : GTIF *psGTIF = GTiffDataset::GTIFNew(l_hTIFF);
7381 :
7382 603 : if (bHasProjection)
7383 : {
7384 603 : const auto eGeoTIFFKeysFlavor = GetGTIFFKeysFlavor(papszOptions);
7385 603 : if (IsSRSCompatibleOfGeoTIFF(l_poSRS, eGeoTIFFKeysFlavor))
7386 : {
7387 603 : GTIFSetFromOGISDefnEx(
7388 : psGTIF,
7389 : OGRSpatialReference::ToHandle(
7390 : const_cast<OGRSpatialReference *>(l_poSRS)),
7391 : eGeoTIFFKeysFlavor, GetGeoTIFFVersion(papszOptions));
7392 : }
7393 : else
7394 : {
7395 0 : bExportSRSToPAM = true;
7396 : }
7397 : }
7398 :
7399 603 : if (bPixelIsPoint)
7400 : {
7401 12 : GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
7402 : RasterPixelIsPoint);
7403 : }
7404 :
7405 603 : GTIFWriteKeys(psGTIF);
7406 603 : GTIFFree(psGTIF);
7407 : }
7408 :
7409 1041 : bool l_bDontReloadFirstBlock = false;
7410 :
7411 : #ifdef HAVE_LIBJPEG
7412 1041 : if (bCopyFromJPEG)
7413 : {
7414 12 : GTIFF_CopyFromJPEG_WriteAdditionalTags(l_hTIFF, poSrcDS);
7415 : }
7416 : #endif
7417 :
7418 : /* -------------------------------------------------------------------- */
7419 : /* Cleanup */
7420 : /* -------------------------------------------------------------------- */
7421 1041 : if (bCopySrcOverviews)
7422 : {
7423 162 : TIFFDeferStrileArrayWriting(l_hTIFF);
7424 : }
7425 1041 : TIFFWriteCheck(l_hTIFF, TIFFIsTiled(l_hTIFF), "GTiffCreateCopy()");
7426 1041 : TIFFWriteDirectory(l_hTIFF);
7427 1041 : if (bStreaming)
7428 : {
7429 : // We need to write twice the directory to be sure that custom
7430 : // TIFF tags are correctly sorted and that padding bytes have been
7431 : // added.
7432 5 : TIFFSetDirectory(l_hTIFF, 0);
7433 5 : TIFFWriteDirectory(l_hTIFF);
7434 :
7435 5 : if (VSIFSeekL(l_fpL, 0, SEEK_END) != 0)
7436 0 : ReportError(pszFilename, CE_Failure, CPLE_FileIO, "Cannot seek");
7437 5 : const int nSize = static_cast<int>(VSIFTellL(l_fpL));
7438 :
7439 5 : vsi_l_offset nDataLength = 0;
7440 5 : VSIGetMemFileBuffer(l_osTmpFilename, &nDataLength, FALSE);
7441 5 : TIFFSetDirectory(l_hTIFF, 0);
7442 5 : GTiffFillStreamableOffsetAndCount(l_hTIFF, nSize);
7443 5 : TIFFWriteDirectory(l_hTIFF);
7444 : }
7445 1041 : const auto nDirCount = TIFFNumberOfDirectories(l_hTIFF);
7446 1041 : if (nDirCount >= 1)
7447 : {
7448 1035 : TIFFSetDirectory(l_hTIFF, static_cast<tdir_t>(nDirCount - 1));
7449 : }
7450 1041 : const toff_t l_nDirOffset = TIFFCurrentDirOffset(l_hTIFF);
7451 1041 : TIFFFlush(l_hTIFF);
7452 1041 : XTIFFClose(l_hTIFF);
7453 :
7454 1041 : VSIFSeekL(l_fpL, 0, SEEK_SET);
7455 :
7456 : // fpStreaming will assigned to the instance and not closed here.
7457 1041 : VSILFILE *fpStreaming = nullptr;
7458 1041 : if (bStreaming)
7459 : {
7460 5 : vsi_l_offset nDataLength = 0;
7461 : void *pabyBuffer =
7462 5 : VSIGetMemFileBuffer(l_osTmpFilename, &nDataLength, FALSE);
7463 5 : fpStreaming = VSIFOpenL(pszFilename, "wb");
7464 5 : if (fpStreaming == nullptr)
7465 : {
7466 1 : VSIUnlink(l_osTmpFilename);
7467 1 : CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
7468 1 : return nullptr;
7469 : }
7470 4 : if (static_cast<vsi_l_offset>(VSIFWriteL(pabyBuffer, 1,
7471 : static_cast<int>(nDataLength),
7472 4 : fpStreaming)) != nDataLength)
7473 : {
7474 0 : ReportError(pszFilename, CE_Failure, CPLE_FileIO,
7475 : "Could not write %d bytes",
7476 : static_cast<int>(nDataLength));
7477 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpStreaming));
7478 0 : VSIUnlink(l_osTmpFilename);
7479 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
7480 0 : return nullptr;
7481 : }
7482 : }
7483 :
7484 : /* -------------------------------------------------------------------- */
7485 : /* Re-open as a dataset and copy over missing metadata using */
7486 : /* PAM facilities. */
7487 : /* -------------------------------------------------------------------- */
7488 1040 : l_hTIFF = VSI_TIFFOpen(bStreaming ? l_osTmpFilename.c_str() : pszFilename,
7489 : "r+", l_fpL);
7490 1040 : if (l_hTIFF == nullptr)
7491 : {
7492 10 : if (bStreaming)
7493 0 : VSIUnlink(l_osTmpFilename);
7494 10 : CPL_IGNORE_RET_VAL(VSIFCloseL(l_fpL));
7495 10 : return nullptr;
7496 : }
7497 :
7498 : /* -------------------------------------------------------------------- */
7499 : /* Create a corresponding GDALDataset. */
7500 : /* -------------------------------------------------------------------- */
7501 1030 : GTiffDataset *poDS = new GTiffDataset();
7502 1030 : poDS->SetDescription(pszFilename);
7503 1030 : poDS->eAccess = GA_Update;
7504 1030 : poDS->m_pszFilename = CPLStrdup(pszFilename);
7505 1030 : poDS->m_fpL = l_fpL;
7506 1030 : poDS->m_bIMDRPCMetadataLoaded = true;
7507 :
7508 1030 : const bool bAppend = CPLFetchBool(papszOptions, "APPEND_SUBDATASET", false);
7509 2059 : if (poDS->OpenOffset(l_hTIFF,
7510 1029 : bAppend ? l_nDirOffset : TIFFCurrentDirOffset(l_hTIFF),
7511 : GA_Update,
7512 : false, // bAllowRGBAInterface
7513 : true // bReadGeoTransform
7514 1030 : ) != CE_None)
7515 : {
7516 0 : delete poDS;
7517 0 : if (bStreaming)
7518 0 : VSIUnlink(l_osTmpFilename);
7519 0 : return nullptr;
7520 : }
7521 :
7522 : // Legacy... Patch back GDT_Int8 type to GDT_Byte if the user used
7523 : // PIXELTYPE=SIGNEDBYTE
7524 1030 : const char *pszPixelType = CSLFetchNameValue(papszOptions, "PIXELTYPE");
7525 1030 : if (pszPixelType == nullptr)
7526 1025 : pszPixelType = "";
7527 1030 : if (eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE"))
7528 : {
7529 10 : for (int i = 0; i < poDS->nBands; ++i)
7530 : {
7531 5 : auto poBand = static_cast<GTiffRasterBand *>(poDS->papoBands[i]);
7532 5 : poBand->eDataType = GDT_Byte;
7533 5 : poBand->EnablePixelTypeSignedByteWarning(false);
7534 5 : poBand->SetMetadataItem("PIXELTYPE", "SIGNEDBYTE",
7535 : "IMAGE_STRUCTURE");
7536 5 : poBand->EnablePixelTypeSignedByteWarning(true);
7537 : }
7538 : }
7539 :
7540 1030 : poDS->oOvManager.Initialize(poDS, pszFilename);
7541 :
7542 1030 : if (bStreaming)
7543 : {
7544 4 : VSIUnlink(l_osTmpFilename);
7545 4 : poDS->m_fpToWrite = fpStreaming;
7546 : }
7547 1030 : poDS->m_eProfile = eProfile;
7548 :
7549 1030 : int nCloneInfoFlags = GCIF_PAM_DEFAULT & ~GCIF_MASK;
7550 :
7551 : // If we explicitly asked not to tag the alpha band as such, do not
7552 : // reintroduce this alpha color interpretation in PAM.
7553 1030 : if (poSrcDS->GetRasterBand(l_nBands)->GetColorInterpretation() ==
7554 1140 : GCI_AlphaBand &&
7555 110 : GTiffGetAlphaValue(
7556 : CPLGetConfigOption("GTIFF_ALPHA",
7557 : CSLFetchNameValue(papszOptions, "ALPHA")),
7558 : DEFAULT_ALPHA_TYPE) == EXTRASAMPLE_UNSPECIFIED)
7559 : {
7560 1 : nCloneInfoFlags &= ~GCIF_COLORINTERP;
7561 : }
7562 : // Ignore source band color interpretation if requesting PHOTOMETRIC=RGB
7563 1401 : else if (l_nBands >= 3 &&
7564 372 : EQUAL(CSLFetchNameValueDef(papszOptions, "PHOTOMETRIC", ""),
7565 : "RGB"))
7566 : {
7567 28 : for (int i = 1; i <= 3; i++)
7568 : {
7569 21 : poDS->GetRasterBand(i)->SetColorInterpretation(
7570 21 : static_cast<GDALColorInterp>(GCI_RedBand + (i - 1)));
7571 : }
7572 7 : nCloneInfoFlags &= ~GCIF_COLORINTERP;
7573 9 : if (!(l_nBands == 4 &&
7574 2 : CSLFetchNameValue(papszOptions, "ALPHA") != nullptr))
7575 : {
7576 15 : for (int i = 4; i <= l_nBands; i++)
7577 : {
7578 18 : poDS->GetRasterBand(i)->SetColorInterpretation(
7579 9 : poSrcDS->GetRasterBand(i)->GetColorInterpretation());
7580 : }
7581 : }
7582 : }
7583 :
7584 : CPLString osOldGTIFF_REPORT_COMPD_CSVal(
7585 2060 : CPLGetConfigOption("GTIFF_REPORT_COMPD_CS", ""));
7586 1030 : CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS", "YES");
7587 1030 : poDS->CloneInfo(poSrcDS, nCloneInfoFlags);
7588 1030 : CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS",
7589 1030 : osOldGTIFF_REPORT_COMPD_CSVal.empty()
7590 : ? nullptr
7591 0 : : osOldGTIFF_REPORT_COMPD_CSVal.c_str());
7592 :
7593 1046 : if ((!bGeoTIFF || bExportSRSToPAM) &&
7594 16 : (poDS->GetPamFlags() & GPF_DISABLED) == 0)
7595 : {
7596 : // Copy georeferencing info to PAM if the profile is not GeoTIFF
7597 15 : poDS->GDALPamDataset::SetSpatialRef(poDS->GetSpatialRef());
7598 : double adfGeoTransform[6];
7599 15 : if (poDS->GetGeoTransform(adfGeoTransform) == CE_None)
7600 : {
7601 5 : poDS->GDALPamDataset::SetGeoTransform(adfGeoTransform);
7602 : }
7603 15 : poDS->GDALPamDataset::SetGCPs(poDS->GetGCPCount(), poDS->GetGCPs(),
7604 : poDS->GetGCPSpatialRef());
7605 : }
7606 :
7607 1030 : poDS->m_papszCreationOptions = CSLDuplicate(papszOptions);
7608 1030 : poDS->m_bDontReloadFirstBlock = l_bDontReloadFirstBlock;
7609 :
7610 : /* -------------------------------------------------------------------- */
7611 : /* CloneInfo() does not merge metadata, it just replaces it */
7612 : /* totally. So we have to merge it. */
7613 : /* -------------------------------------------------------------------- */
7614 :
7615 1030 : char **papszSRC_MD = poSrcDS->GetMetadata();
7616 1030 : char **papszDST_MD = CSLDuplicate(poDS->GetMetadata());
7617 :
7618 1030 : papszDST_MD = CSLMerge(papszDST_MD, papszSRC_MD);
7619 :
7620 1030 : poDS->SetMetadata(papszDST_MD);
7621 1030 : CSLDestroy(papszDST_MD);
7622 :
7623 : // Depending on the PHOTOMETRIC tag, the TIFF file may not have the same
7624 : // band count as the source. Will fail later in GDALDatasetCopyWholeRaster
7625 : // anyway.
7626 3250 : for (int nBand = 1;
7627 3250 : nBand <= std::min(poDS->GetRasterCount(), poSrcDS->GetRasterCount());
7628 : ++nBand)
7629 : {
7630 2220 : GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(nBand);
7631 2220 : GDALRasterBand *poDstBand = poDS->GetRasterBand(nBand);
7632 2220 : papszSRC_MD = poSrcBand->GetMetadata();
7633 2220 : papszDST_MD = CSLDuplicate(poDstBand->GetMetadata());
7634 :
7635 2220 : papszDST_MD = CSLMerge(papszDST_MD, papszSRC_MD);
7636 :
7637 2220 : poDstBand->SetMetadata(papszDST_MD);
7638 2220 : CSLDestroy(papszDST_MD);
7639 :
7640 2220 : char **papszCatNames = poSrcBand->GetCategoryNames();
7641 2220 : if (nullptr != papszCatNames)
7642 0 : poDstBand->SetCategoryNames(papszCatNames);
7643 : }
7644 :
7645 1030 : l_hTIFF = static_cast<TIFF *>(poDS->GetInternalHandle(nullptr));
7646 :
7647 : /* -------------------------------------------------------------------- */
7648 : /* Handle forcing xml:ESRI data to be written to PAM. */
7649 : /* -------------------------------------------------------------------- */
7650 1030 : if (CPLTestBool(CPLGetConfigOption("ESRI_XML_PAM", "NO")))
7651 : {
7652 1 : char **papszESRIMD = poSrcDS->GetMetadata("xml:ESRI");
7653 1 : if (papszESRIMD)
7654 : {
7655 1 : poDS->SetMetadata(papszESRIMD, "xml:ESRI");
7656 : }
7657 : }
7658 :
7659 : /* -------------------------------------------------------------------- */
7660 : /* Second chance: now that we have a PAM dataset, it is possible */
7661 : /* to write metadata that we could not write as a TIFF tag. */
7662 : /* -------------------------------------------------------------------- */
7663 1030 : if (!bHasWrittenMDInGeotiffTAG && !bStreaming)
7664 : {
7665 6 : GTiffDataset::WriteMetadata(
7666 : poDS, l_hTIFF, true, eProfile, pszFilename, papszOptions,
7667 : true /* don't write RPC and IMD file again */);
7668 : }
7669 :
7670 1030 : if (!bStreaming)
7671 1026 : GTiffDataset::WriteRPC(poDS, l_hTIFF, true, eProfile, pszFilename,
7672 : papszOptions,
7673 : true /* write only in PAM AND if needed */);
7674 :
7675 : // Propagate ISIS3 or VICAR metadata, but only as PAM metadata.
7676 3090 : for (const char *pszMDD : {"json:ISIS3", "json:VICAR"})
7677 : {
7678 2060 : char **papszMD = poSrcDS->GetMetadata(pszMDD);
7679 2060 : if (papszMD)
7680 : {
7681 3 : poDS->SetMetadata(papszMD, pszMDD);
7682 3 : poDS->PushMetadataToPam();
7683 : }
7684 : }
7685 :
7686 1030 : poDS->m_bWriteCOGLayout = bCopySrcOverviews;
7687 :
7688 : // To avoid unnecessary directory rewriting.
7689 1030 : poDS->m_bMetadataChanged = false;
7690 1030 : poDS->m_bGeoTIFFInfoChanged = false;
7691 1030 : poDS->m_bNoDataChanged = false;
7692 1030 : poDS->m_bForceUnsetGTOrGCPs = false;
7693 1030 : poDS->m_bForceUnsetProjection = false;
7694 1030 : poDS->m_bStreamingOut = bStreaming;
7695 :
7696 : // Don't try to load external metadata files (#6597).
7697 1030 : poDS->m_bIMDRPCMetadataLoaded = true;
7698 :
7699 : // We must re-set the compression level at this point, since it has been
7700 : // lost a few lines above when closing the newly create TIFF file The
7701 : // TIFFTAG_ZIPQUALITY & TIFFTAG_JPEGQUALITY are not store in the TIFF file.
7702 : // They are just TIFF session parameters.
7703 :
7704 1030 : poDS->m_nZLevel = GTiffGetZLevel(papszOptions);
7705 1030 : poDS->m_nLZMAPreset = GTiffGetLZMAPreset(papszOptions);
7706 1030 : poDS->m_nZSTDLevel = GTiffGetZSTDPreset(papszOptions);
7707 1030 : poDS->m_nWebPLevel = GTiffGetWebPLevel(papszOptions);
7708 1030 : poDS->m_bWebPLossless = GTiffGetWebPLossless(papszOptions);
7709 1033 : if (poDS->m_nWebPLevel != 100 && poDS->m_bWebPLossless &&
7710 3 : CSLFetchNameValue(papszOptions, "WEBP_LEVEL"))
7711 : {
7712 0 : CPLError(CE_Warning, CPLE_AppDefined,
7713 : "WEBP_LEVEL is specified, but WEBP_LOSSLESS=YES. "
7714 : "WEBP_LEVEL will be ignored.");
7715 : }
7716 1030 : poDS->m_nJpegQuality = GTiffGetJpegQuality(papszOptions);
7717 1030 : poDS->m_nJpegTablesMode = GTiffGetJpegTablesMode(papszOptions);
7718 1030 : poDS->GetDiscardLsbOption(papszOptions);
7719 1030 : poDS->m_dfMaxZError = GTiffGetLERCMaxZError(papszOptions);
7720 1030 : poDS->m_dfMaxZErrorOverview = GTiffGetLERCMaxZErrorOverview(papszOptions);
7721 : #if HAVE_JXL
7722 1030 : poDS->m_bJXLLossless = GTiffGetJXLLossless(papszOptions);
7723 1030 : poDS->m_nJXLEffort = GTiffGetJXLEffort(papszOptions);
7724 1030 : poDS->m_fJXLDistance = GTiffGetJXLDistance(papszOptions);
7725 1030 : poDS->m_fJXLAlphaDistance = GTiffGetJXLAlphaDistance(papszOptions);
7726 : #endif
7727 1030 : poDS->InitCreationOrOpenOptions(true, papszOptions);
7728 :
7729 1030 : if (l_nCompression == COMPRESSION_ADOBE_DEFLATE ||
7730 1006 : l_nCompression == COMPRESSION_LERC)
7731 : {
7732 95 : GTiffSetDeflateSubCodec(l_hTIFF);
7733 :
7734 95 : if (poDS->m_nZLevel != -1)
7735 : {
7736 12 : TIFFSetField(l_hTIFF, TIFFTAG_ZIPQUALITY, poDS->m_nZLevel);
7737 : }
7738 : }
7739 1030 : if (l_nCompression == COMPRESSION_JPEG)
7740 : {
7741 73 : if (poDS->m_nJpegQuality != -1)
7742 : {
7743 9 : TIFFSetField(l_hTIFF, TIFFTAG_JPEGQUALITY, poDS->m_nJpegQuality);
7744 : }
7745 73 : TIFFSetField(l_hTIFF, TIFFTAG_JPEGTABLESMODE, poDS->m_nJpegTablesMode);
7746 : }
7747 1030 : if (l_nCompression == COMPRESSION_LZMA)
7748 : {
7749 7 : if (poDS->m_nLZMAPreset != -1)
7750 : {
7751 6 : TIFFSetField(l_hTIFF, TIFFTAG_LZMAPRESET, poDS->m_nLZMAPreset);
7752 : }
7753 : }
7754 1030 : if (l_nCompression == COMPRESSION_ZSTD ||
7755 1023 : l_nCompression == COMPRESSION_LERC)
7756 : {
7757 78 : if (poDS->m_nZSTDLevel != -1)
7758 : {
7759 8 : TIFFSetField(l_hTIFF, TIFFTAG_ZSTD_LEVEL, poDS->m_nZSTDLevel);
7760 : }
7761 : }
7762 1030 : if (l_nCompression == COMPRESSION_LERC)
7763 : {
7764 71 : TIFFSetField(l_hTIFF, TIFFTAG_LERC_MAXZERROR, poDS->m_dfMaxZError);
7765 : }
7766 : #if HAVE_JXL
7767 1030 : if (l_nCompression == COMPRESSION_JXL)
7768 : {
7769 85 : TIFFSetField(l_hTIFF, TIFFTAG_JXL_LOSSYNESS,
7770 85 : poDS->m_bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
7771 85 : TIFFSetField(l_hTIFF, TIFFTAG_JXL_EFFORT, poDS->m_nJXLEffort);
7772 85 : TIFFSetField(l_hTIFF, TIFFTAG_JXL_DISTANCE, poDS->m_fJXLDistance);
7773 85 : TIFFSetField(l_hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE,
7774 85 : poDS->m_fJXLAlphaDistance);
7775 : }
7776 : #endif
7777 1030 : if (l_nCompression == COMPRESSION_WEBP)
7778 : {
7779 14 : if (poDS->m_nWebPLevel != -1)
7780 : {
7781 14 : TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LEVEL, poDS->m_nWebPLevel);
7782 : }
7783 :
7784 14 : if (poDS->m_bWebPLossless)
7785 : {
7786 5 : TIFFSetField(l_hTIFF, TIFFTAG_WEBP_LOSSLESS, poDS->m_bWebPLossless);
7787 : }
7788 : }
7789 :
7790 : /* -------------------------------------------------------------------- */
7791 : /* Do we want to ensure all blocks get written out on close to */
7792 : /* avoid sparse files? */
7793 : /* -------------------------------------------------------------------- */
7794 1030 : if (!CPLFetchBool(papszOptions, "SPARSE_OK", false))
7795 1002 : poDS->m_bFillEmptyTilesAtClosing = true;
7796 :
7797 1030 : poDS->m_bWriteEmptyTiles =
7798 1902 : (bCopySrcOverviews && poDS->m_bFillEmptyTilesAtClosing) || bStreaming ||
7799 872 : (poDS->m_nCompression != COMPRESSION_NONE &&
7800 262 : poDS->m_bFillEmptyTilesAtClosing);
7801 : // Only required for people writing non-compressed striped files in the
7802 : // rightorder and wanting all tstrips to be written in the same order
7803 : // so that the end result can be memory mapped without knowledge of each
7804 : // strip offset
7805 1030 : if (CPLTestBool(CSLFetchNameValueDef(
7806 2060 : papszOptions, "WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")) ||
7807 1030 : CPLTestBool(CSLFetchNameValueDef(
7808 : papszOptions, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "FALSE")))
7809 : {
7810 0 : poDS->m_bWriteEmptyTiles = true;
7811 : }
7812 :
7813 : // Precreate (internal) mask, so that the IBuildOverviews() below
7814 : // has a chance to create also the overviews of the mask.
7815 1030 : CPLErr eErr = CE_None;
7816 :
7817 1030 : if (bCreateMask)
7818 : {
7819 27 : eErr = poDS->CreateMaskBand(nMaskFlags);
7820 27 : if (poDS->m_poMaskDS)
7821 : {
7822 26 : poDS->m_poMaskDS->m_bFillEmptyTilesAtClosing =
7823 26 : poDS->m_bFillEmptyTilesAtClosing;
7824 26 : poDS->m_poMaskDS->m_bWriteEmptyTiles = poDS->m_bWriteEmptyTiles;
7825 : }
7826 : }
7827 :
7828 : /* -------------------------------------------------------------------- */
7829 : /* Create and then copy existing overviews if requested */
7830 : /* We do it such that all the IFDs are at the beginning of the file, */
7831 : /* and that the imagery data for the smallest overview is written */
7832 : /* first, that way the file is more usable when embedded in a */
7833 : /* compressed stream. */
7834 : /* -------------------------------------------------------------------- */
7835 :
7836 : // For scaled progress due to overview copying.
7837 1030 : const int nBandsWidthMask = l_nBands + (bCreateMask ? 1 : 0);
7838 1030 : double dfTotalPixels =
7839 1030 : static_cast<double>(nXSize) * nYSize * nBandsWidthMask;
7840 1030 : double dfCurPixels = 0;
7841 :
7842 1030 : if (eErr == CE_None && bCopySrcOverviews)
7843 : {
7844 0 : std::unique_ptr<GDALDataset> poMaskOvrDS;
7845 : const char *pszMaskOvrDS =
7846 159 : CSLFetchNameValue(papszOptions, "@MASK_OVERVIEW_DATASET");
7847 159 : if (pszMaskOvrDS)
7848 : {
7849 7 : poMaskOvrDS.reset(GDALDataset::Open(pszMaskOvrDS));
7850 7 : if (!poMaskOvrDS)
7851 : {
7852 0 : delete poDS;
7853 0 : return nullptr;
7854 : }
7855 7 : if (poMaskOvrDS->GetRasterCount() != 1)
7856 : {
7857 0 : delete poDS;
7858 0 : return nullptr;
7859 : }
7860 : }
7861 159 : if (nSrcOverviews)
7862 : {
7863 55 : eErr = poDS->CreateOverviewsFromSrcOverviews(poSrcDS, poOvrDS.get(),
7864 : nSrcOverviews);
7865 :
7866 158 : if (eErr == CE_None &&
7867 55 : (poMaskOvrDS != nullptr ||
7868 48 : (poSrcDS->GetRasterBand(1)->GetOverview(0) &&
7869 25 : poSrcDS->GetRasterBand(1)->GetOverview(0)->GetMaskFlags() ==
7870 : GMF_PER_DATASET)))
7871 : {
7872 13 : int nOvrBlockXSize = 0;
7873 13 : int nOvrBlockYSize = 0;
7874 13 : GTIFFGetOverviewBlockSize(
7875 : GDALRasterBand::ToHandle(poDS->GetRasterBand(1)),
7876 : &nOvrBlockXSize, &nOvrBlockYSize);
7877 13 : eErr = poDS->CreateInternalMaskOverviews(nOvrBlockXSize,
7878 : nOvrBlockYSize);
7879 : }
7880 : }
7881 :
7882 159 : TIFFForceStrileArrayWriting(poDS->m_hTIFF);
7883 :
7884 159 : if (poDS->m_poMaskDS)
7885 : {
7886 21 : TIFFForceStrileArrayWriting(poDS->m_poMaskDS->m_hTIFF);
7887 : }
7888 :
7889 267 : for (int i = 0; i < poDS->m_nOverviewCount; i++)
7890 : {
7891 108 : TIFFForceStrileArrayWriting(poDS->m_papoOverviewDS[i]->m_hTIFF);
7892 :
7893 108 : if (poDS->m_papoOverviewDS[i]->m_poMaskDS)
7894 : {
7895 24 : TIFFForceStrileArrayWriting(
7896 24 : poDS->m_papoOverviewDS[i]->m_poMaskDS->m_hTIFF);
7897 : }
7898 : }
7899 :
7900 159 : if (eErr == CE_None && nSrcOverviews)
7901 : {
7902 55 : if (poDS->m_nOverviewCount != nSrcOverviews)
7903 : {
7904 0 : ReportError(
7905 : pszFilename, CE_Failure, CPLE_AppDefined,
7906 : "Did only manage to instantiate %d overview levels, "
7907 : "whereas source contains %d",
7908 0 : poDS->m_nOverviewCount, nSrcOverviews);
7909 0 : eErr = CE_Failure;
7910 : }
7911 :
7912 163 : for (int i = 0; eErr == CE_None && i < nSrcOverviews; ++i)
7913 : {
7914 : GDALRasterBand *poOvrBand =
7915 : poOvrDS
7916 173 : ? (i == 0
7917 65 : ? poOvrDS->GetRasterBand(1)
7918 35 : : poOvrDS->GetRasterBand(1)->GetOverview(i - 1))
7919 151 : : poSrcDS->GetRasterBand(1)->GetOverview(i);
7920 : const double dfOvrPixels =
7921 108 : static_cast<double>(poOvrBand->GetXSize()) *
7922 108 : poOvrBand->GetYSize();
7923 108 : dfTotalPixels += dfOvrPixels * l_nBands;
7924 205 : if (poOvrBand->GetMaskFlags() == GMF_PER_DATASET ||
7925 97 : poMaskOvrDS != nullptr)
7926 : {
7927 24 : dfTotalPixels += dfOvrPixels;
7928 : }
7929 84 : else if (i == 0 && poDS->GetRasterBand(1)->GetMaskFlags() ==
7930 : GMF_PER_DATASET)
7931 : {
7932 1 : ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
7933 : "Source dataset has a mask band on full "
7934 : "resolution, overviews on the regular bands, "
7935 : "but lacks overviews on the mask band.");
7936 : }
7937 : }
7938 :
7939 55 : char *papszCopyWholeRasterOptions[2] = {nullptr, nullptr};
7940 55 : if (l_nCompression != COMPRESSION_NONE)
7941 47 : papszCopyWholeRasterOptions[0] =
7942 : const_cast<char *>("COMPRESSED=YES");
7943 : // Now copy the imagery.
7944 : // Begin with the smallest overview.
7945 55 : for (int iOvrLevel = nSrcOverviews - 1;
7946 162 : eErr == CE_None && iOvrLevel >= 0; --iOvrLevel)
7947 : {
7948 107 : auto poDstDS = poDS->m_papoOverviewDS[iOvrLevel];
7949 :
7950 : // Create a fake dataset with the source overview level so that
7951 : // GDALDatasetCopyWholeRaster can cope with it.
7952 : GDALDataset *poSrcOvrDS =
7953 : poOvrDS
7954 142 : ? (iOvrLevel == 0 ? poOvrDS.get()
7955 35 : : GDALCreateOverviewDataset(
7956 : poOvrDS.get(), iOvrLevel - 1,
7957 : /* bThisLevelOnly = */ true))
7958 42 : : GDALCreateOverviewDataset(
7959 : poSrcDS, iOvrLevel,
7960 107 : /* bThisLevelOnly = */ true);
7961 : GDALRasterBand *poSrcOvrBand =
7962 172 : poOvrDS ? (iOvrLevel == 0
7963 65 : ? poOvrDS->GetRasterBand(1)
7964 70 : : poOvrDS->GetRasterBand(1)->GetOverview(
7965 35 : iOvrLevel - 1))
7966 149 : : poSrcDS->GetRasterBand(1)->GetOverview(iOvrLevel);
7967 : double dfNextCurPixels =
7968 : dfCurPixels +
7969 107 : static_cast<double>(poSrcOvrBand->GetXSize()) *
7970 107 : poSrcOvrBand->GetYSize() * l_nBands;
7971 :
7972 107 : poDstDS->m_bBlockOrderRowMajor = true;
7973 107 : poDstDS->m_bLeaderSizeAsUInt4 = true;
7974 107 : poDstDS->m_bTrailerRepeatedLast4BytesRepeated = true;
7975 107 : poDstDS->m_bFillEmptyTilesAtClosing =
7976 107 : poDS->m_bFillEmptyTilesAtClosing;
7977 107 : poDstDS->m_bWriteEmptyTiles = poDS->m_bWriteEmptyTiles;
7978 107 : GDALRasterBand *poSrcMaskBand = nullptr;
7979 107 : if (poDstDS->m_poMaskDS)
7980 : {
7981 24 : poDstDS->m_poMaskDS->m_bBlockOrderRowMajor = true;
7982 24 : poDstDS->m_poMaskDS->m_bLeaderSizeAsUInt4 = true;
7983 24 : poDstDS->m_poMaskDS->m_bTrailerRepeatedLast4BytesRepeated =
7984 : true;
7985 24 : poDstDS->m_poMaskDS->m_bFillEmptyTilesAtClosing =
7986 24 : poDS->m_bFillEmptyTilesAtClosing;
7987 24 : poDstDS->m_poMaskDS->m_bWriteEmptyTiles =
7988 24 : poDS->m_bWriteEmptyTiles;
7989 :
7990 24 : poSrcMaskBand =
7991 : poMaskOvrDS
7992 37 : ? (iOvrLevel == 0
7993 13 : ? poMaskOvrDS->GetRasterBand(1)
7994 12 : : poMaskOvrDS->GetRasterBand(1)->GetOverview(
7995 6 : iOvrLevel - 1))
7996 35 : : poSrcOvrBand->GetMaskBand();
7997 : }
7998 :
7999 107 : if (l_nBands == 1 ||
8000 53 : poDstDS->m_nPlanarConfig == PLANARCONFIG_CONTIG)
8001 : {
8002 106 : if (poDstDS->m_poMaskDS)
8003 : {
8004 24 : dfNextCurPixels +=
8005 24 : static_cast<double>(poSrcOvrBand->GetXSize()) *
8006 24 : poSrcOvrBand->GetYSize();
8007 : }
8008 106 : void *pScaledData = GDALCreateScaledProgress(
8009 : dfCurPixels / dfTotalPixels,
8010 : dfNextCurPixels / dfTotalPixels, pfnProgress,
8011 : pProgressData);
8012 :
8013 : eErr =
8014 106 : CopyImageryAndMask(poDstDS, poSrcOvrDS, poSrcMaskBand,
8015 : GDALScaledProgress, pScaledData);
8016 :
8017 106 : dfCurPixels = dfNextCurPixels;
8018 106 : GDALDestroyScaledProgress(pScaledData);
8019 : }
8020 : else
8021 : {
8022 1 : void *pScaledData = GDALCreateScaledProgress(
8023 : dfCurPixels / dfTotalPixels,
8024 : dfNextCurPixels / dfTotalPixels, pfnProgress,
8025 : pProgressData);
8026 :
8027 1 : eErr = GDALDatasetCopyWholeRaster(
8028 : GDALDataset::ToHandle(poSrcOvrDS),
8029 : GDALDataset::ToHandle(poDstDS),
8030 : papszCopyWholeRasterOptions, GDALScaledProgress,
8031 : pScaledData);
8032 :
8033 1 : dfCurPixels = dfNextCurPixels;
8034 1 : GDALDestroyScaledProgress(pScaledData);
8035 :
8036 1 : poDstDS->FlushCache(false);
8037 :
8038 : // Copy mask of the overview.
8039 1 : if (eErr == CE_None &&
8040 1 : (poMaskOvrDS ||
8041 2 : poSrcOvrBand->GetMaskFlags() == GMF_PER_DATASET) &&
8042 0 : poDstDS->m_poMaskDS != nullptr)
8043 : {
8044 0 : dfNextCurPixels +=
8045 0 : static_cast<double>(poSrcOvrBand->GetXSize()) *
8046 0 : poSrcOvrBand->GetYSize();
8047 0 : pScaledData = GDALCreateScaledProgress(
8048 : dfCurPixels / dfTotalPixels,
8049 : dfNextCurPixels / dfTotalPixels, pfnProgress,
8050 : pProgressData);
8051 0 : eErr = GDALRasterBandCopyWholeRaster(
8052 : poSrcMaskBand,
8053 0 : poDstDS->m_poMaskDS->GetRasterBand(1),
8054 : papszCopyWholeRasterOptions, GDALScaledProgress,
8055 : pScaledData);
8056 0 : dfCurPixels = dfNextCurPixels;
8057 0 : GDALDestroyScaledProgress(pScaledData);
8058 0 : poDstDS->m_poMaskDS->FlushCache(false);
8059 : }
8060 : }
8061 :
8062 107 : if (poSrcOvrDS != poOvrDS.get())
8063 77 : delete poSrcOvrDS;
8064 107 : poSrcOvrDS = nullptr;
8065 : }
8066 : }
8067 : }
8068 :
8069 : /* -------------------------------------------------------------------- */
8070 : /* Copy actual imagery. */
8071 : /* -------------------------------------------------------------------- */
8072 1030 : double dfNextCurPixels =
8073 1030 : dfCurPixels + static_cast<double>(nXSize) * nYSize * l_nBands;
8074 1030 : void *pScaledData = GDALCreateScaledProgress(
8075 : dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
8076 : pfnProgress, pProgressData);
8077 :
8078 : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
8079 1030 : bool bTryCopy = true;
8080 : #endif
8081 :
8082 : #ifdef HAVE_LIBJPEG
8083 1030 : if (bCopyFromJPEG)
8084 : {
8085 12 : eErr = GTIFF_CopyFromJPEG(poDS, poSrcDS, pfnProgress, pProgressData,
8086 : bTryCopy);
8087 :
8088 : // In case of failure in the decompression step, try normal copy.
8089 12 : if (bTryCopy)
8090 0 : eErr = CE_None;
8091 : }
8092 : #endif
8093 :
8094 : #ifdef JPEG_DIRECT_COPY
8095 : if (bDirectCopyFromJPEG)
8096 : {
8097 : eErr = GTIFF_DirectCopyFromJPEG(poDS, poSrcDS, pfnProgress,
8098 : pProgressData, bTryCopy);
8099 :
8100 : // In case of failure in the reading step, try normal copy.
8101 : if (bTryCopy)
8102 : eErr = CE_None;
8103 : }
8104 : #endif
8105 :
8106 1030 : bool bWriteMask = true;
8107 1030 : if (
8108 : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
8109 1018 : bTryCopy &&
8110 : #endif
8111 1018 : (poDS->m_bTreatAsSplit || poDS->m_bTreatAsSplitBitmap))
8112 : {
8113 : // For split bands, we use TIFFWriteScanline() interface.
8114 9 : CPLAssert(poDS->m_nBitsPerSample == 8 || poDS->m_nBitsPerSample == 1);
8115 :
8116 9 : if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG && poDS->nBands > 1)
8117 : {
8118 : GByte *pabyScanline = static_cast<GByte *>(
8119 3 : VSI_MALLOC_VERBOSE(TIFFScanlineSize(l_hTIFF)));
8120 3 : if (pabyScanline == nullptr)
8121 0 : eErr = CE_Failure;
8122 9052 : for (int j = 0; j < nYSize && eErr == CE_None; ++j)
8123 : {
8124 18098 : eErr = poSrcDS->RasterIO(GF_Read, 0, j, nXSize, 1, pabyScanline,
8125 : nXSize, 1, GDT_Byte, l_nBands, nullptr,
8126 9049 : poDS->nBands, 0, 1, nullptr);
8127 18098 : if (eErr == CE_None &&
8128 9049 : TIFFWriteScanline(l_hTIFF, pabyScanline, j, 0) == -1)
8129 : {
8130 0 : ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
8131 : "TIFFWriteScanline() failed.");
8132 0 : eErr = CE_Failure;
8133 : }
8134 9049 : if (!GDALScaledProgress((j + 1) * 1.0 / nYSize, nullptr,
8135 : pScaledData))
8136 0 : eErr = CE_Failure;
8137 : }
8138 3 : CPLFree(pabyScanline);
8139 : }
8140 : else
8141 : {
8142 : GByte *pabyScanline =
8143 6 : static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
8144 6 : if (pabyScanline == nullptr)
8145 0 : eErr = CE_Failure;
8146 : else
8147 6 : eErr = CE_None;
8148 14 : for (int iBand = 1; iBand <= l_nBands && eErr == CE_None; ++iBand)
8149 : {
8150 48211 : for (int j = 0; j < nYSize && eErr == CE_None; ++j)
8151 : {
8152 48203 : eErr = poSrcDS->GetRasterBand(iBand)->RasterIO(
8153 : GF_Read, 0, j, nXSize, 1, pabyScanline, nXSize, 1,
8154 : GDT_Byte, 0, 0, nullptr);
8155 48203 : if (poDS->m_bTreatAsSplitBitmap)
8156 : {
8157 7225210 : for (int i = 0; i < nXSize; ++i)
8158 : {
8159 7216010 : const GByte byVal = pabyScanline[i];
8160 7216010 : if ((i & 0x7) == 0)
8161 902001 : pabyScanline[i >> 3] = 0;
8162 7216010 : if (byVal)
8163 7097220 : pabyScanline[i >> 3] |= 0x80 >> (i & 0x7);
8164 : }
8165 : }
8166 96406 : if (eErr == CE_None &&
8167 48203 : TIFFWriteScanline(l_hTIFF, pabyScanline, j,
8168 48203 : static_cast<uint16_t>(iBand - 1)) ==
8169 : -1)
8170 : {
8171 0 : ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
8172 : "TIFFWriteScanline() failed.");
8173 0 : eErr = CE_Failure;
8174 : }
8175 48203 : if (!GDALScaledProgress((j + 1 + (iBand - 1) * nYSize) *
8176 48203 : 1.0 / (l_nBands * nYSize),
8177 : nullptr, pScaledData))
8178 0 : eErr = CE_Failure;
8179 : }
8180 : }
8181 6 : CPLFree(pabyScanline);
8182 : }
8183 :
8184 : // Necessary to be able to read the file without re-opening.
8185 9 : TIFFSizeProc pfnSizeProc = TIFFGetSizeProc(l_hTIFF);
8186 :
8187 9 : TIFFFlushData(l_hTIFF);
8188 :
8189 9 : toff_t nNewDirOffset = pfnSizeProc(TIFFClientdata(l_hTIFF));
8190 9 : if ((nNewDirOffset % 2) == 1)
8191 5 : ++nNewDirOffset;
8192 :
8193 9 : TIFFFlush(l_hTIFF);
8194 :
8195 9 : if (poDS->m_nDirOffset != TIFFCurrentDirOffset(l_hTIFF))
8196 : {
8197 0 : poDS->m_nDirOffset = nNewDirOffset;
8198 0 : CPLDebug("GTiff", "directory moved during flush.");
8199 9 : }
8200 : }
8201 1021 : else if (
8202 : #if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY)
8203 1009 : bTryCopy &&
8204 : #endif
8205 : eErr == CE_None)
8206 : {
8207 1008 : const char *papszCopyWholeRasterOptions[3] = {nullptr, nullptr,
8208 : nullptr};
8209 1008 : int iNextOption = 0;
8210 1008 : papszCopyWholeRasterOptions[iNextOption++] = "SKIP_HOLES=YES";
8211 1008 : if (l_nCompression != COMPRESSION_NONE)
8212 : {
8213 388 : papszCopyWholeRasterOptions[iNextOption++] = "COMPRESSED=YES";
8214 : }
8215 : // For streaming with separate, we really want that bands are written
8216 : // after each other, even if the source is pixel interleaved.
8217 620 : else if (bStreaming && poDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE)
8218 : {
8219 1 : papszCopyWholeRasterOptions[iNextOption++] = "INTERLEAVE=BAND";
8220 : }
8221 :
8222 1008 : if (bCopySrcOverviews &&
8223 86 : (l_nBands == 1 || poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG))
8224 : {
8225 157 : poDS->m_bBlockOrderRowMajor = true;
8226 157 : poDS->m_bLeaderSizeAsUInt4 = true;
8227 157 : poDS->m_bTrailerRepeatedLast4BytesRepeated = true;
8228 157 : if (poDS->m_poMaskDS)
8229 : {
8230 21 : poDS->m_poMaskDS->m_bBlockOrderRowMajor = true;
8231 21 : poDS->m_poMaskDS->m_bLeaderSizeAsUInt4 = true;
8232 21 : poDS->m_poMaskDS->m_bTrailerRepeatedLast4BytesRepeated = true;
8233 : }
8234 :
8235 157 : if (poDS->m_poMaskDS)
8236 : {
8237 21 : GDALDestroyScaledProgress(pScaledData);
8238 : pScaledData =
8239 21 : GDALCreateScaledProgress(dfCurPixels / dfTotalPixels, 1.0,
8240 : pfnProgress, pProgressData);
8241 : }
8242 :
8243 157 : eErr = CopyImageryAndMask(poDS, poSrcDS,
8244 157 : poSrcDS->GetRasterBand(1)->GetMaskBand(),
8245 : GDALScaledProgress, pScaledData);
8246 157 : if (poDS->m_poMaskDS)
8247 : {
8248 21 : bWriteMask = false;
8249 : }
8250 : }
8251 : else
8252 : {
8253 851 : eErr = GDALDatasetCopyWholeRaster(
8254 : /* (GDALDatasetH) */ poSrcDS,
8255 : /* (GDALDatasetH) */ poDS, papszCopyWholeRasterOptions,
8256 : GDALScaledProgress, pScaledData);
8257 : }
8258 : }
8259 :
8260 1030 : GDALDestroyScaledProgress(pScaledData);
8261 :
8262 1030 : if (eErr == CE_None && !bStreaming && bWriteMask)
8263 : {
8264 991 : pScaledData = GDALCreateScaledProgress(dfNextCurPixels / dfTotalPixels,
8265 : 1.0, pfnProgress, pProgressData);
8266 991 : if (poDS->m_poMaskDS)
8267 : {
8268 5 : const char *l_papszOptions[2] = {"COMPRESSED=YES", nullptr};
8269 5 : eErr = GDALRasterBandCopyWholeRaster(
8270 5 : poSrcDS->GetRasterBand(1)->GetMaskBand(),
8271 5 : poDS->GetRasterBand(1)->GetMaskBand(),
8272 : const_cast<char **>(l_papszOptions), GDALScaledProgress,
8273 : pScaledData);
8274 : }
8275 : else
8276 : {
8277 : eErr =
8278 986 : GDALDriver::DefaultCopyMasks(poSrcDS, poDS, bStrict, nullptr,
8279 : GDALScaledProgress, pScaledData);
8280 : }
8281 991 : GDALDestroyScaledProgress(pScaledData);
8282 : }
8283 :
8284 1030 : poDS->m_bWriteCOGLayout = false;
8285 :
8286 1030 : if (eErr == CE_Failure)
8287 : {
8288 14 : delete poDS;
8289 14 : poDS = nullptr;
8290 :
8291 14 : if (CPLTestBool(CPLGetConfigOption("GTIFF_DELETE_ON_ERROR", "YES")))
8292 : {
8293 13 : if (!bStreaming)
8294 : {
8295 : // Should really delete more carefully.
8296 13 : VSIUnlink(pszFilename);
8297 : }
8298 : }
8299 : }
8300 :
8301 1030 : return poDS;
8302 : }
8303 :
8304 : /************************************************************************/
8305 : /* SetSpatialRef() */
8306 : /************************************************************************/
8307 :
8308 1322 : CPLErr GTiffDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
8309 :
8310 : {
8311 1322 : if (m_bStreamingOut && m_bCrystalized)
8312 : {
8313 1 : ReportError(CE_Failure, CPLE_NotSupported,
8314 : "Cannot modify projection at that point in "
8315 : "a streamed output file");
8316 1 : return CE_Failure;
8317 : }
8318 :
8319 1321 : LoadGeoreferencingAndPamIfNeeded();
8320 1321 : LookForProjection();
8321 :
8322 1321 : CPLErr eErr = CE_None;
8323 1321 : if (eAccess == GA_Update)
8324 : {
8325 1323 : if ((m_eProfile == GTiffProfile::BASELINE) &&
8326 6 : (GetPamFlags() & GPF_DISABLED) == 0)
8327 : {
8328 6 : eErr = GDALPamDataset::SetSpatialRef(poSRS);
8329 : }
8330 : else
8331 : {
8332 1311 : if (GDALPamDataset::GetSpatialRef() != nullptr)
8333 : {
8334 : // Cancel any existing SRS from PAM file.
8335 1 : GDALPamDataset::SetSpatialRef(nullptr);
8336 : }
8337 1311 : m_bGeoTIFFInfoChanged = true;
8338 : }
8339 : }
8340 : else
8341 : {
8342 4 : CPLDebug("GTIFF", "SetSpatialRef() goes to PAM instead of TIFF tags");
8343 4 : eErr = GDALPamDataset::SetSpatialRef(poSRS);
8344 : }
8345 :
8346 1321 : if (eErr == CE_None)
8347 : {
8348 1321 : if (poSRS == nullptr || poSRS->IsEmpty())
8349 : {
8350 15 : if (!m_oSRS.IsEmpty())
8351 : {
8352 3 : m_bForceUnsetProjection = true;
8353 : }
8354 15 : m_oSRS.Clear();
8355 : }
8356 : else
8357 : {
8358 1306 : m_oSRS = *poSRS;
8359 1306 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
8360 : }
8361 : }
8362 :
8363 1321 : return eErr;
8364 : }
8365 :
8366 : /************************************************************************/
8367 : /* SetGeoTransform() */
8368 : /************************************************************************/
8369 :
8370 1487 : CPLErr GTiffDataset::SetGeoTransform(double *padfTransform)
8371 :
8372 : {
8373 1487 : if (m_bStreamingOut && m_bCrystalized)
8374 : {
8375 1 : ReportError(CE_Failure, CPLE_NotSupported,
8376 : "Cannot modify geotransform at that point in a "
8377 : "streamed output file");
8378 1 : return CE_Failure;
8379 : }
8380 :
8381 1486 : LoadGeoreferencingAndPamIfNeeded();
8382 :
8383 1486 : CPLErr eErr = CE_None;
8384 1486 : if (eAccess == GA_Update)
8385 : {
8386 1480 : if (!m_aoGCPs.empty())
8387 : {
8388 1 : ReportError(CE_Warning, CPLE_AppDefined,
8389 : "GCPs previously set are going to be cleared "
8390 : "due to the setting of a geotransform.");
8391 1 : m_bForceUnsetGTOrGCPs = true;
8392 1 : m_aoGCPs.clear();
8393 : }
8394 1479 : else if (padfTransform[0] == 0.0 && padfTransform[1] == 0.0 &&
8395 2 : padfTransform[2] == 0.0 && padfTransform[3] == 0.0 &&
8396 2 : padfTransform[4] == 0.0 && padfTransform[5] == 0.0)
8397 : {
8398 2 : if (m_bGeoTransformValid)
8399 : {
8400 2 : m_bForceUnsetGTOrGCPs = true;
8401 2 : m_bGeoTIFFInfoChanged = true;
8402 : }
8403 2 : m_bGeoTransformValid = false;
8404 2 : memcpy(m_adfGeoTransform, padfTransform, sizeof(double) * 6);
8405 2 : return CE_None;
8406 : }
8407 :
8408 2964 : if ((m_eProfile == GTiffProfile::BASELINE) &&
8409 8 : !CPLFetchBool(m_papszCreationOptions, "TFW", false) &&
8410 1490 : !CPLFetchBool(m_papszCreationOptions, "WORLDFILE", false) &&
8411 4 : (GetPamFlags() & GPF_DISABLED) == 0)
8412 : {
8413 4 : eErr = GDALPamDataset::SetGeoTransform(padfTransform);
8414 : }
8415 : else
8416 : {
8417 : // Cancel any existing geotransform from PAM file.
8418 1474 : GDALPamDataset::DeleteGeoTransform();
8419 1474 : m_bGeoTIFFInfoChanged = true;
8420 : }
8421 : }
8422 : else
8423 : {
8424 6 : CPLDebug("GTIFF", "SetGeoTransform() goes to PAM instead of TIFF tags");
8425 6 : eErr = GDALPamDataset::SetGeoTransform(padfTransform);
8426 : }
8427 :
8428 1484 : if (eErr == CE_None)
8429 : {
8430 1484 : memcpy(m_adfGeoTransform, padfTransform, sizeof(double) * 6);
8431 1484 : m_bGeoTransformValid = true;
8432 : }
8433 :
8434 1484 : return eErr;
8435 : }
8436 :
8437 : /************************************************************************/
8438 : /* SetGCPs() */
8439 : /************************************************************************/
8440 :
8441 18 : CPLErr GTiffDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
8442 : const OGRSpatialReference *poGCPSRS)
8443 : {
8444 18 : CPLErr eErr = CE_None;
8445 18 : LoadGeoreferencingAndPamIfNeeded();
8446 18 : LookForProjection();
8447 :
8448 18 : if (eAccess == GA_Update)
8449 : {
8450 16 : if (!m_aoGCPs.empty() && nGCPCountIn == 0)
8451 : {
8452 3 : m_bForceUnsetGTOrGCPs = true;
8453 : }
8454 13 : else if (nGCPCountIn > 0 && m_bGeoTransformValid)
8455 : {
8456 4 : ReportError(CE_Warning, CPLE_AppDefined,
8457 : "A geotransform previously set is going to be cleared "
8458 : "due to the setting of GCPs.");
8459 4 : m_adfGeoTransform[0] = 0.0;
8460 4 : m_adfGeoTransform[1] = 1.0;
8461 4 : m_adfGeoTransform[2] = 0.0;
8462 4 : m_adfGeoTransform[3] = 0.0;
8463 4 : m_adfGeoTransform[4] = 0.0;
8464 4 : m_adfGeoTransform[5] = 1.0;
8465 4 : m_bGeoTransformValid = false;
8466 4 : m_bForceUnsetGTOrGCPs = true;
8467 : }
8468 :
8469 16 : if ((m_eProfile == GTiffProfile::BASELINE) &&
8470 0 : (GetPamFlags() & GPF_DISABLED) == 0)
8471 : {
8472 0 : eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn, poGCPSRS);
8473 : }
8474 : else
8475 : {
8476 16 : if (GDALPamDataset::GetGCPCount() > 0)
8477 : {
8478 : // Cancel any existing GCPs from PAM file.
8479 1 : GDALPamDataset::SetGCPs(
8480 : 0, nullptr,
8481 : static_cast<const OGRSpatialReference *>(nullptr));
8482 : }
8483 16 : m_bGeoTIFFInfoChanged = true;
8484 : }
8485 : }
8486 : else
8487 : {
8488 2 : CPLDebug("GTIFF", "SetGCPs() goes to PAM instead of TIFF tags");
8489 2 : eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn, poGCPSRS);
8490 : }
8491 :
8492 18 : if (eErr == CE_None)
8493 : {
8494 18 : if (poGCPSRS == nullptr || poGCPSRS->IsEmpty())
8495 : {
8496 8 : if (!m_oSRS.IsEmpty())
8497 : {
8498 4 : m_bForceUnsetProjection = true;
8499 : }
8500 8 : m_oSRS.Clear();
8501 : }
8502 : else
8503 : {
8504 10 : m_oSRS = *poGCPSRS;
8505 10 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
8506 : }
8507 :
8508 18 : m_aoGCPs = gdal::GCP::fromC(pasGCPListIn, nGCPCountIn);
8509 : }
8510 :
8511 18 : return eErr;
8512 : }
8513 :
8514 : /************************************************************************/
8515 : /* SetMetadata() */
8516 : /************************************************************************/
8517 1583 : CPLErr GTiffDataset::SetMetadata(char **papszMD, const char *pszDomain)
8518 :
8519 : {
8520 1583 : LoadGeoreferencingAndPamIfNeeded();
8521 :
8522 1583 : if (m_bStreamingOut && m_bCrystalized)
8523 : {
8524 1 : ReportError(
8525 : CE_Failure, CPLE_NotSupported,
8526 : "Cannot modify metadata at that point in a streamed output file");
8527 1 : return CE_Failure;
8528 : }
8529 :
8530 1582 : CPLErr eErr = CE_None;
8531 1582 : if (eAccess == GA_Update)
8532 : {
8533 1580 : if (pszDomain != nullptr && EQUAL(pszDomain, MD_DOMAIN_RPC))
8534 : {
8535 : // So that a subsequent GetMetadata() wouldn't override our new
8536 : // values
8537 22 : LoadMetadata();
8538 22 : m_bForceUnsetRPC = (CSLCount(papszMD) == 0);
8539 : }
8540 :
8541 1580 : if ((papszMD != nullptr) && (pszDomain != nullptr) &&
8542 805 : EQUAL(pszDomain, "COLOR_PROFILE"))
8543 : {
8544 0 : m_bColorProfileMetadataChanged = true;
8545 : }
8546 1580 : else if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
8547 : {
8548 1580 : m_bMetadataChanged = true;
8549 : // Cancel any existing metadata from PAM file.
8550 1580 : if (GDALPamDataset::GetMetadata(pszDomain) != nullptr)
8551 1 : GDALPamDataset::SetMetadata(nullptr, pszDomain);
8552 : }
8553 :
8554 3123 : if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
8555 1543 : CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT) != nullptr)
8556 : {
8557 940 : const char *pszPrevValue = GetMetadataItem(GDALMD_AREA_OR_POINT);
8558 : const char *pszNewValue =
8559 940 : CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT);
8560 940 : if (pszPrevValue == nullptr || pszNewValue == nullptr ||
8561 549 : !EQUAL(pszPrevValue, pszNewValue))
8562 : {
8563 395 : LookForProjection();
8564 395 : m_bGeoTIFFInfoChanged = true;
8565 : }
8566 : }
8567 :
8568 1580 : if (pszDomain != nullptr && EQUAL(pszDomain, "xml:XMP"))
8569 : {
8570 2 : if (papszMD != nullptr && *papszMD != nullptr)
8571 : {
8572 1 : int nTagSize = static_cast<int>(strlen(*papszMD));
8573 1 : TIFFSetField(m_hTIFF, TIFFTAG_XMLPACKET, nTagSize, *papszMD);
8574 : }
8575 : else
8576 : {
8577 1 : TIFFUnsetField(m_hTIFF, TIFFTAG_XMLPACKET);
8578 : }
8579 : }
8580 : }
8581 : else
8582 : {
8583 2 : CPLDebug(
8584 : "GTIFF",
8585 : "GTiffDataset::SetMetadata() goes to PAM instead of TIFF tags");
8586 2 : eErr = GDALPamDataset::SetMetadata(papszMD, pszDomain);
8587 : }
8588 :
8589 1582 : if (eErr == CE_None)
8590 : {
8591 1582 : eErr = m_oGTiffMDMD.SetMetadata(papszMD, pszDomain);
8592 : }
8593 1582 : return eErr;
8594 : }
8595 :
8596 : /************************************************************************/
8597 : /* SetMetadataItem() */
8598 : /************************************************************************/
8599 :
8600 3141 : CPLErr GTiffDataset::SetMetadataItem(const char *pszName, const char *pszValue,
8601 : const char *pszDomain)
8602 :
8603 : {
8604 3141 : LoadGeoreferencingAndPamIfNeeded();
8605 :
8606 3141 : if (m_bStreamingOut && m_bCrystalized)
8607 : {
8608 1 : ReportError(
8609 : CE_Failure, CPLE_NotSupported,
8610 : "Cannot modify metadata at that point in a streamed output file");
8611 1 : return CE_Failure;
8612 : }
8613 :
8614 3140 : CPLErr eErr = CE_None;
8615 3140 : if (eAccess == GA_Update)
8616 : {
8617 3133 : if ((pszDomain != nullptr) && EQUAL(pszDomain, "COLOR_PROFILE"))
8618 : {
8619 8 : m_bColorProfileMetadataChanged = true;
8620 : }
8621 3125 : else if (pszDomain == nullptr || !EQUAL(pszDomain, "_temporary_"))
8622 : {
8623 3125 : m_bMetadataChanged = true;
8624 : // Cancel any existing metadata from PAM file.
8625 3125 : if (GDALPamDataset::GetMetadataItem(pszName, pszDomain) != nullptr)
8626 1 : GDALPamDataset::SetMetadataItem(pszName, nullptr, pszDomain);
8627 : }
8628 :
8629 3133 : if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
8630 66 : pszName != nullptr && EQUAL(pszName, GDALMD_AREA_OR_POINT))
8631 : {
8632 7 : LookForProjection();
8633 7 : m_bGeoTIFFInfoChanged = true;
8634 : }
8635 : }
8636 : else
8637 : {
8638 7 : CPLDebug(
8639 : "GTIFF",
8640 : "GTiffDataset::SetMetadataItem() goes to PAM instead of TIFF tags");
8641 7 : eErr = GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
8642 : }
8643 :
8644 3140 : if (eErr == CE_None)
8645 : {
8646 3140 : eErr = m_oGTiffMDMD.SetMetadataItem(pszName, pszValue, pszDomain);
8647 : }
8648 :
8649 3140 : return eErr;
8650 : }
8651 :
8652 : /************************************************************************/
8653 : /* CreateMaskBand() */
8654 : /************************************************************************/
8655 :
8656 82 : CPLErr GTiffDataset::CreateMaskBand(int nFlagsIn)
8657 : {
8658 82 : ScanDirectories();
8659 :
8660 82 : if (m_poMaskDS != nullptr)
8661 : {
8662 1 : ReportError(CE_Failure, CPLE_AppDefined,
8663 : "This TIFF dataset has already an internal mask band");
8664 1 : return CE_Failure;
8665 : }
8666 81 : else if (MustCreateInternalMask())
8667 : {
8668 68 : if (nFlagsIn != GMF_PER_DATASET)
8669 : {
8670 1 : ReportError(CE_Failure, CPLE_AppDefined,
8671 : "The only flag value supported for internal mask is "
8672 : "GMF_PER_DATASET");
8673 1 : return CE_Failure;
8674 : }
8675 :
8676 67 : int l_nCompression = COMPRESSION_PACKBITS;
8677 67 : if (strstr(GDALGetMetadataItem(GDALGetDriverByName("GTiff"),
8678 : GDAL_DMD_CREATIONOPTIONLIST, nullptr),
8679 67 : "<Value>DEFLATE</Value>") != nullptr)
8680 67 : l_nCompression = COMPRESSION_ADOBE_DEFLATE;
8681 :
8682 : /* --------------------------------------------------------------------
8683 : */
8684 : /* If we don't have read access, then create the mask externally.
8685 : */
8686 : /* --------------------------------------------------------------------
8687 : */
8688 67 : if (GetAccess() != GA_Update)
8689 : {
8690 1 : ReportError(CE_Warning, CPLE_AppDefined,
8691 : "File open for read-only accessing, "
8692 : "creating mask externally.");
8693 :
8694 1 : return GDALPamDataset::CreateMaskBand(nFlagsIn);
8695 : }
8696 :
8697 66 : if (m_bLayoutIFDSBeforeData && !m_bKnownIncompatibleEdition &&
8698 0 : !m_bWriteKnownIncompatibleEdition)
8699 : {
8700 0 : ReportError(CE_Warning, CPLE_AppDefined,
8701 : "Adding a mask invalidates the "
8702 : "LAYOUT=IFDS_BEFORE_DATA property");
8703 0 : m_bKnownIncompatibleEdition = true;
8704 0 : m_bWriteKnownIncompatibleEdition = true;
8705 : }
8706 :
8707 66 : bool bIsOverview = false;
8708 66 : uint32_t nSubType = 0;
8709 66 : if (TIFFGetField(m_hTIFF, TIFFTAG_SUBFILETYPE, &nSubType))
8710 : {
8711 8 : bIsOverview = (nSubType & FILETYPE_REDUCEDIMAGE) != 0;
8712 :
8713 8 : if ((nSubType & FILETYPE_MASK) != 0)
8714 : {
8715 0 : ReportError(CE_Failure, CPLE_AppDefined,
8716 : "Cannot create a mask on a TIFF mask IFD !");
8717 0 : return CE_Failure;
8718 : }
8719 : }
8720 :
8721 66 : const int bIsTiled = TIFFIsTiled(m_hTIFF);
8722 :
8723 66 : FlushDirectory();
8724 :
8725 66 : const toff_t nOffset = GTIFFWriteDirectory(
8726 : m_hTIFF,
8727 : bIsOverview ? FILETYPE_REDUCEDIMAGE | FILETYPE_MASK : FILETYPE_MASK,
8728 : nRasterXSize, nRasterYSize, 1, PLANARCONFIG_CONTIG, 1,
8729 : m_nBlockXSize, m_nBlockYSize, bIsTiled, l_nCompression,
8730 : PHOTOMETRIC_MASK, PREDICTOR_NONE, SAMPLEFORMAT_UINT, nullptr,
8731 : nullptr, nullptr, 0, nullptr, "", nullptr, nullptr, nullptr,
8732 66 : nullptr, m_bWriteCOGLayout);
8733 :
8734 66 : ReloadDirectory();
8735 :
8736 66 : if (nOffset == 0)
8737 0 : return CE_Failure;
8738 :
8739 66 : m_poMaskDS = new GTiffDataset();
8740 66 : m_poMaskDS->m_poBaseDS = this;
8741 66 : m_poMaskDS->m_poImageryDS = this;
8742 66 : m_poMaskDS->ShareLockWithParentDataset(this);
8743 66 : m_poMaskDS->m_bPromoteTo8Bits = CPLTestBool(
8744 : CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES"));
8745 66 : if (m_poMaskDS->OpenOffset(VSI_TIFFOpenChild(m_hTIFF), nOffset,
8746 66 : GA_Update) != CE_None)
8747 : {
8748 0 : delete m_poMaskDS;
8749 0 : m_poMaskDS = nullptr;
8750 0 : return CE_Failure;
8751 : }
8752 :
8753 66 : return CE_None;
8754 : }
8755 :
8756 13 : return GDALPamDataset::CreateMaskBand(nFlagsIn);
8757 : }
8758 :
8759 : /************************************************************************/
8760 : /* MustCreateInternalMask() */
8761 : /************************************************************************/
8762 :
8763 108 : bool GTiffDataset::MustCreateInternalMask()
8764 : {
8765 108 : return CPLTestBool(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", "YES"));
8766 : }
8767 :
8768 : /************************************************************************/
8769 : /* CreateMaskBand() */
8770 : /************************************************************************/
8771 :
8772 27 : CPLErr GTiffRasterBand::CreateMaskBand(int nFlagsIn)
8773 : {
8774 27 : m_poGDS->ScanDirectories();
8775 :
8776 27 : if (m_poGDS->m_poMaskDS != nullptr)
8777 : {
8778 5 : ReportError(CE_Failure, CPLE_AppDefined,
8779 : "This TIFF dataset has already an internal mask band");
8780 5 : return CE_Failure;
8781 : }
8782 :
8783 : const char *pszGDAL_TIFF_INTERNAL_MASK =
8784 22 : CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", nullptr);
8785 25 : if ((pszGDAL_TIFF_INTERNAL_MASK &&
8786 22 : CPLTestBool(pszGDAL_TIFF_INTERNAL_MASK)) ||
8787 : nFlagsIn == GMF_PER_DATASET)
8788 : {
8789 15 : return m_poGDS->CreateMaskBand(nFlagsIn);
8790 : }
8791 :
8792 7 : return GDALPamRasterBand::CreateMaskBand(nFlagsIn);
8793 : }
|