Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GeoTIFF Driver
4 : * Purpose: Code to build overviews of external databases as a TIFF file.
5 : * Only used by the GDALDefaultOverviews::BuildOverviews() method.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2000, Frank Warmerdam
10 : * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_port.h"
16 :
17 : #include "gt_overview.h"
18 :
19 : #include <cstdlib>
20 : #include <cstring>
21 :
22 : #include <algorithm>
23 : #include <string>
24 :
25 : #include "cpl_conv.h"
26 : #include "cpl_error.h"
27 : #include "cpl_progress.h"
28 : #include "cpl_string.h"
29 : #include "cpl_vsi.h"
30 : #include "gdal.h"
31 : #include "gdal_priv.h"
32 : #include "gtiff.h"
33 : #include "gtiffdataset.h"
34 : #include "tiff.h"
35 : #include "tiffvers.h"
36 : #include "tifvsi.h"
37 : #include "tif_jxl.h"
38 : #include "xtiffio.h"
39 :
40 : // TODO(schwehr): Explain why 128 and not 127.
41 : constexpr int knMaxOverviews = 128;
42 :
43 : /************************************************************************/
44 : /* GTIFFWriteDirectory() */
45 : /* */
46 : /* Create a new directory, without any image data for an overview */
47 : /* or a mask */
48 : /* Returns offset of newly created directory, but the */
49 : /* current directory is reset to be the one in used when this */
50 : /* function is called. */
51 : /************************************************************************/
52 :
53 893 : toff_t GTIFFWriteDirectory(TIFF *hTIFF, int nSubfileType, int nXSize,
54 : int nYSize, int nBitsPerPixel, int nPlanarConfig,
55 : int nSamples, int nBlockXSize, int nBlockYSize,
56 : int bTiled, int nCompressFlag, int nPhotometric,
57 : int nSampleFormat, int nPredictor,
58 : unsigned short *panRed, unsigned short *panGreen,
59 : unsigned short *panBlue, int nExtraSamples,
60 : unsigned short *panExtraSampleValues,
61 : const char *pszMetadata, const char *pszJPEGQuality,
62 : const char *pszJPEGTablesMode, const char *pszNoData,
63 : const uint32_t *panLercAddCompressionAndVersion,
64 : bool bDeferStrileArrayWriting)
65 :
66 : {
67 893 : const toff_t nBaseDirOffset = TIFFCurrentDirOffset(hTIFF);
68 :
69 : // This is a bit of a hack to cause (*tif->tif_cleanup)(tif); to be called.
70 : // See https://trac.osgeo.org/gdal/ticket/2055
71 893 : TIFFSetField(hTIFF, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
72 893 : TIFFFreeDirectory(hTIFF);
73 :
74 893 : TIFFCreateDirectory(hTIFF);
75 :
76 : /* -------------------------------------------------------------------- */
77 : /* Setup TIFF fields. */
78 : /* -------------------------------------------------------------------- */
79 893 : TIFFSetField(hTIFF, TIFFTAG_IMAGEWIDTH, nXSize);
80 893 : TIFFSetField(hTIFF, TIFFTAG_IMAGELENGTH, nYSize);
81 893 : if (nSamples == 1)
82 555 : TIFFSetField(hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
83 : else
84 338 : TIFFSetField(hTIFF, TIFFTAG_PLANARCONFIG, nPlanarConfig);
85 :
86 893 : TIFFSetField(hTIFF, TIFFTAG_BITSPERSAMPLE, nBitsPerPixel);
87 893 : TIFFSetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, nSamples);
88 893 : TIFFSetField(hTIFF, TIFFTAG_COMPRESSION, nCompressFlag);
89 893 : TIFFSetField(hTIFF, TIFFTAG_PHOTOMETRIC, nPhotometric);
90 893 : TIFFSetField(hTIFF, TIFFTAG_SAMPLEFORMAT, nSampleFormat);
91 :
92 893 : if (bTiled)
93 : {
94 857 : TIFFSetField(hTIFF, TIFFTAG_TILEWIDTH, nBlockXSize);
95 857 : TIFFSetField(hTIFF, TIFFTAG_TILELENGTH, nBlockYSize);
96 : }
97 : else
98 : {
99 36 : TIFFSetField(hTIFF, TIFFTAG_ROWSPERSTRIP, nBlockYSize);
100 : }
101 :
102 893 : TIFFSetField(hTIFF, TIFFTAG_SUBFILETYPE, nSubfileType);
103 :
104 893 : if (panExtraSampleValues != nullptr)
105 : {
106 112 : TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples,
107 : panExtraSampleValues);
108 : }
109 :
110 893 : if (GTIFFSupportsPredictor(nCompressFlag))
111 340 : TIFFSetField(hTIFF, TIFFTAG_PREDICTOR, nPredictor);
112 :
113 : /* -------------------------------------------------------------------- */
114 : /* Write color table if one is present. */
115 : /* -------------------------------------------------------------------- */
116 893 : if (panRed != nullptr)
117 : {
118 18 : TIFFSetField(hTIFF, TIFFTAG_COLORMAP, panRed, panGreen, panBlue);
119 : }
120 :
121 : /* -------------------------------------------------------------------- */
122 : /* Write metadata if we have some. */
123 : /* -------------------------------------------------------------------- */
124 893 : if (pszMetadata && strlen(pszMetadata) > 0)
125 617 : TIFFSetField(hTIFF, TIFFTAG_GDAL_METADATA, pszMetadata);
126 :
127 : /* -------------------------------------------------------------------- */
128 : /* Write JPEG tables if needed. */
129 : /* -------------------------------------------------------------------- */
130 893 : if (nCompressFlag == COMPRESSION_JPEG)
131 : {
132 113 : GTiffWriteJPEGTables(hTIFF,
133 : (nPhotometric == PHOTOMETRIC_RGB) ? "RGB"
134 : : (nPhotometric == PHOTOMETRIC_YCBCR)
135 54 : ? "YCBCR"
136 : : "MINISBLACK",
137 : pszJPEGQuality, pszJPEGTablesMode);
138 :
139 59 : if (nPhotometric == PHOTOMETRIC_YCBCR)
140 : {
141 : // Explicitly register the subsampling so that JPEGFixupTags
142 : // is a no-op (helps for cloud optimized geotiffs)
143 46 : TIFFSetField(hTIFF, TIFFTAG_YCBCRSUBSAMPLING, 2, 2);
144 : }
145 : }
146 :
147 893 : if (nCompressFlag == COMPRESSION_LERC && panLercAddCompressionAndVersion)
148 : {
149 48 : TIFFSetField(hTIFF, TIFFTAG_LERC_PARAMETERS, 2,
150 : panLercAddCompressionAndVersion);
151 : }
152 :
153 : /* -------------------------------------------------------------------- */
154 : /* Write no data value if we have one. */
155 : /* -------------------------------------------------------------------- */
156 893 : if (pszNoData != nullptr)
157 : {
158 40 : TIFFSetField(hTIFF, TIFFTAG_GDAL_NODATA, pszNoData);
159 : }
160 :
161 893 : if (bDeferStrileArrayWriting)
162 : {
163 161 : TIFFDeferStrileArrayWriting(hTIFF);
164 : }
165 :
166 : /* -------------------------------------------------------------------- */
167 : /* Write directory, and return byte offset. */
168 : /* -------------------------------------------------------------------- */
169 893 : if (TIFFWriteCheck(hTIFF, bTiled, "GTIFFWriteDirectory") == 0)
170 : {
171 0 : TIFFSetSubDirectory(hTIFF, nBaseDirOffset);
172 0 : return 0;
173 : }
174 :
175 893 : TIFFWriteDirectory(hTIFF);
176 893 : const tdir_t nNumberOfDirs = TIFFNumberOfDirectories(hTIFF);
177 893 : if (nNumberOfDirs > 0) // always true, but to please Coverity
178 : {
179 893 : TIFFSetDirectory(hTIFF, static_cast<tdir_t>(nNumberOfDirs - 1));
180 : }
181 :
182 893 : const toff_t nOffset = TIFFCurrentDirOffset(hTIFF);
183 :
184 893 : TIFFSetSubDirectory(hTIFF, nBaseDirOffset);
185 :
186 893 : return nOffset;
187 : }
188 :
189 : /************************************************************************/
190 : /* GTIFFBuildOverviewMetadata() */
191 : /************************************************************************/
192 :
193 493 : void GTIFFBuildOverviewMetadata(const char *pszResampling,
194 : GDALDataset *poBaseDS, bool bIsForMaskBand,
195 : CPLString &osMetadata)
196 :
197 : {
198 493 : osMetadata = "<GDALMetadata>";
199 :
200 986 : auto osNormalizedResampling = GDALGetNormalizedOvrResampling(pszResampling);
201 493 : if (!osNormalizedResampling.empty())
202 : {
203 407 : osMetadata += "<Item name=\"RESAMPLING\" sample=\"0\">";
204 407 : osMetadata += osNormalizedResampling;
205 407 : osMetadata += "</Item>";
206 : }
207 :
208 493 : if (bIsForMaskBand)
209 : {
210 11 : osMetadata += "<Item name=\"INTERNAL_MASK_FLAGS_1\">2</Item>";
211 : }
212 482 : else if (poBaseDS->GetMetadataItem("INTERNAL_MASK_FLAGS_1"))
213 : {
214 0 : for (int iBand = 0; iBand < 200; iBand++)
215 : {
216 0 : CPLString osItem;
217 0 : CPLString osName;
218 :
219 0 : osName.Printf("INTERNAL_MASK_FLAGS_%d", iBand + 1);
220 0 : if (poBaseDS->GetMetadataItem(osName))
221 : {
222 : osItem.Printf("<Item name=\"%s\">%s</Item>", osName.c_str(),
223 0 : poBaseDS->GetMetadataItem(osName));
224 0 : osMetadata += osItem;
225 : }
226 : }
227 : }
228 :
229 493 : const char *pszNoDataValues = poBaseDS->GetMetadataItem("NODATA_VALUES");
230 493 : if (pszNoDataValues)
231 : {
232 12 : CPLString osItem;
233 : osItem.Printf("<Item name=\"NODATA_VALUES\">%s</Item>",
234 6 : pszNoDataValues);
235 6 : osMetadata += osItem;
236 : }
237 :
238 493 : if (!EQUAL(osMetadata, "<GDALMetadata>"))
239 407 : osMetadata += "</GDALMetadata>";
240 : else
241 86 : osMetadata = "";
242 493 : }
243 :
244 : /************************************************************************/
245 : /* GTIFFGetMaxColorChannels() */
246 : /************************************************************************/
247 :
248 : /*
249 : * Return the maximum number of color channels specified for a given photometric
250 : * type. 0 is returned if photometric type isn't supported or no default value
251 : * is defined by the specification.
252 : */
253 212 : static int GTIFFGetMaxColorChannels(int photometric)
254 : {
255 212 : switch (photometric)
256 : {
257 154 : case PHOTOMETRIC_PALETTE:
258 : case PHOTOMETRIC_MINISWHITE:
259 : case PHOTOMETRIC_MINISBLACK:
260 154 : return 1;
261 58 : case PHOTOMETRIC_YCBCR:
262 : case PHOTOMETRIC_RGB:
263 : case PHOTOMETRIC_CIELAB:
264 : case PHOTOMETRIC_LOGLUV:
265 : case PHOTOMETRIC_ITULAB:
266 : case PHOTOMETRIC_ICCLAB:
267 58 : return 3;
268 0 : case PHOTOMETRIC_SEPARATED:
269 : case PHOTOMETRIC_MASK:
270 0 : return 4;
271 0 : case PHOTOMETRIC_LOGL:
272 : default:
273 0 : return 0;
274 : }
275 : }
276 :
277 : /************************************************************************/
278 : /* GTIFFBuildOverviews() */
279 : /************************************************************************/
280 :
281 180 : CPLErr GTIFFBuildOverviews(const char *pszFilename, int nBands,
282 : GDALRasterBand *const *papoBandList, int nOverviews,
283 : const int *panOverviewList,
284 : const char *pszResampling,
285 : GDALProgressFunc pfnProgress, void *pProgressData,
286 : CSLConstList papszOptions)
287 :
288 : {
289 180 : return GTIFFBuildOverviewsEx(pszFilename, nBands, papoBandList, nOverviews,
290 : panOverviewList, nullptr, pszResampling,
291 180 : papszOptions, pfnProgress, pProgressData);
292 : }
293 :
294 221 : CPLErr GTIFFBuildOverviewsEx(const char *pszFilename, int nBands,
295 : GDALRasterBand *const *papoBandList,
296 : int nOverviews, const int *panOverviewList,
297 : const std::pair<int, int> *pasOverviewSize,
298 : const char *pszResampling,
299 : const char *const *papszOptions,
300 : GDALProgressFunc pfnProgress, void *pProgressData)
301 : {
302 221 : if (nBands == 0 || nOverviews == 0)
303 5 : return CE_None;
304 :
305 216 : CPLAssert((panOverviewList != nullptr) ^ (pasOverviewSize != nullptr));
306 :
307 216 : GTiffOneTimeInit();
308 :
309 216 : TIFF *hOTIFF = nullptr;
310 216 : int nBitsPerPixel = 0;
311 216 : int nCompression = COMPRESSION_NONE;
312 216 : uint16_t nPhotometric = 0;
313 216 : int nSampleFormat = 0;
314 216 : uint16_t nPlanarConfig = 0;
315 216 : int iOverview = 0;
316 216 : int nXSize = 0;
317 216 : int nYSize = 0;
318 :
319 : /* -------------------------------------------------------------------- */
320 : /* Verify that the list of bands is suitable for emitting in */
321 : /* TIFF file. */
322 : /* -------------------------------------------------------------------- */
323 576 : for (int iBand = 0; iBand < nBands; iBand++)
324 : {
325 360 : int nBandBits = 0;
326 360 : int nBandFormat = 0;
327 360 : GDALRasterBand *hBand = papoBandList[iBand];
328 :
329 360 : switch (hBand->GetRasterDataType())
330 : {
331 300 : case GDT_Byte:
332 300 : nBandBits = 8;
333 300 : nBandFormat = SAMPLEFORMAT_UINT;
334 300 : break;
335 :
336 0 : case GDT_Int8:
337 0 : nBandBits = 8;
338 0 : nBandFormat = SAMPLEFORMAT_INT;
339 0 : break;
340 :
341 4 : case GDT_UInt16:
342 4 : nBandBits = 16;
343 4 : nBandFormat = SAMPLEFORMAT_UINT;
344 4 : break;
345 :
346 10 : case GDT_Int16:
347 10 : nBandBits = 16;
348 10 : nBandFormat = SAMPLEFORMAT_INT;
349 10 : break;
350 :
351 2 : case GDT_UInt32:
352 2 : nBandBits = 32;
353 2 : nBandFormat = SAMPLEFORMAT_UINT;
354 2 : break;
355 :
356 3 : case GDT_Int32:
357 3 : nBandBits = 32;
358 3 : nBandFormat = SAMPLEFORMAT_INT;
359 3 : break;
360 :
361 1 : case GDT_UInt64:
362 1 : nBandBits = 64;
363 1 : nBandFormat = SAMPLEFORMAT_UINT;
364 1 : break;
365 :
366 1 : case GDT_Int64:
367 1 : nBandBits = 64;
368 1 : nBandFormat = SAMPLEFORMAT_INT;
369 1 : break;
370 :
371 29 : case GDT_Float32:
372 29 : nBandBits = 32;
373 29 : nBandFormat = SAMPLEFORMAT_IEEEFP;
374 29 : break;
375 :
376 2 : case GDT_Float64:
377 2 : nBandBits = 64;
378 2 : nBandFormat = SAMPLEFORMAT_IEEEFP;
379 2 : break;
380 :
381 2 : case GDT_CInt16:
382 2 : nBandBits = 32;
383 2 : nBandFormat = SAMPLEFORMAT_COMPLEXINT;
384 2 : break;
385 :
386 2 : case GDT_CInt32:
387 2 : nBandBits = 64;
388 2 : nBandFormat = SAMPLEFORMAT_COMPLEXINT;
389 2 : break;
390 :
391 2 : case GDT_CFloat32:
392 2 : nBandBits = 64;
393 2 : nBandFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
394 2 : break;
395 :
396 2 : case GDT_CFloat64:
397 2 : nBandBits = 128;
398 2 : nBandFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
399 2 : break;
400 :
401 0 : case GDT_Unknown:
402 : case GDT_TypeCount:
403 0 : CPLAssert(false);
404 : return CE_Failure;
405 : }
406 :
407 360 : if (hBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE"))
408 : {
409 : nBandBits =
410 8 : atoi(hBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE"));
411 :
412 8 : if (nBandBits == 1 && STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
413 4 : nBandBits = 8;
414 : }
415 :
416 360 : if (iBand == 0)
417 : {
418 216 : nBitsPerPixel = nBandBits;
419 216 : nSampleFormat = nBandFormat;
420 216 : nXSize = hBand->GetXSize();
421 216 : nYSize = hBand->GetYSize();
422 : }
423 144 : else if (nBitsPerPixel != nBandBits || nSampleFormat != nBandFormat)
424 : {
425 0 : CPLError(CE_Failure, CPLE_NotSupported,
426 : "GTIFFBuildOverviews() doesn't support a mixture of band"
427 : " data types.");
428 0 : return CE_Failure;
429 : }
430 144 : else if (hBand->GetColorTable() != nullptr)
431 : {
432 0 : CPLError(CE_Failure, CPLE_NotSupported,
433 : "GTIFFBuildOverviews() doesn't support building"
434 : " overviews of multiple colormapped bands.");
435 0 : return CE_Failure;
436 : }
437 144 : else if (hBand->GetXSize() != nXSize || hBand->GetYSize() != nYSize)
438 : {
439 0 : CPLError(CE_Failure, CPLE_NotSupported,
440 : "GTIFFBuildOverviews() doesn't support building"
441 : " overviews of different sized bands.");
442 0 : return CE_Failure;
443 : }
444 : }
445 :
446 : const auto GetOptionValue =
447 3287 : [papszOptions](const char *pszOptionKey, const char *pszConfigOptionKey,
448 3287 : const char **ppszKeyUsed = nullptr)
449 : {
450 3287 : const char *pszVal = CSLFetchNameValue(papszOptions, pszOptionKey);
451 3287 : if (pszVal)
452 : {
453 151 : if (ppszKeyUsed)
454 48 : *ppszKeyUsed = pszOptionKey;
455 151 : return pszVal;
456 : }
457 3136 : pszVal = CPLGetConfigOption(pszConfigOptionKey, nullptr);
458 3136 : if (pszVal && ppszKeyUsed)
459 57 : *ppszKeyUsed = pszConfigOptionKey;
460 3136 : return pszVal;
461 216 : };
462 :
463 : /* -------------------------------------------------------------------- */
464 : /* Use specified compression method. */
465 : /* -------------------------------------------------------------------- */
466 216 : const char *pszCompressKey = "";
467 : const char *pszCompress =
468 216 : GetOptionValue("COMPRESS", "COMPRESS_OVERVIEW", &pszCompressKey);
469 :
470 216 : if (pszCompress != nullptr && pszCompress[0] != '\0')
471 : {
472 96 : nCompression = GTIFFGetCompressionMethod(pszCompress, pszCompressKey);
473 96 : if (nCompression < 0)
474 0 : return CE_Failure;
475 : }
476 :
477 216 : if (nCompression == COMPRESSION_JPEG && nBitsPerPixel > 8)
478 : {
479 2 : if (nBitsPerPixel > 16)
480 : {
481 0 : CPLError(CE_Failure, CPLE_NotSupported,
482 : "GTIFFBuildOverviews() doesn't support building"
483 : " JPEG compressed overviews of nBitsPerPixel > 16.");
484 0 : return CE_Failure;
485 : }
486 :
487 2 : nBitsPerPixel = 12;
488 : }
489 :
490 : /* -------------------------------------------------------------------- */
491 : /* Figure out the planar configuration to use. */
492 : /* -------------------------------------------------------------------- */
493 216 : if (nBands == 1)
494 148 : nPlanarConfig = PLANARCONFIG_CONTIG;
495 : else
496 68 : nPlanarConfig = PLANARCONFIG_SEPARATE;
497 :
498 216 : bool bSourceIsPixelInterleaved = false;
499 216 : bool bSourceIsJPEG2000 = false;
500 216 : if (nBands > 1)
501 : {
502 68 : GDALDataset *poSrcDS = papoBandList[0]->GetDataset();
503 68 : if (poSrcDS)
504 : {
505 : const char *pszSrcInterleave =
506 68 : poSrcDS->GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE");
507 68 : if (pszSrcInterleave && EQUAL(pszSrcInterleave, "PIXEL"))
508 : {
509 40 : bSourceIsPixelInterleaved = true;
510 : }
511 : }
512 :
513 : const char *pszSrcCompression =
514 68 : papoBandList[0]->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE");
515 68 : if (pszSrcCompression)
516 : {
517 2 : bSourceIsJPEG2000 = EQUAL(pszSrcCompression, "JPEG2000");
518 : }
519 68 : if (bSourceIsPixelInterleaved && bSourceIsJPEG2000)
520 : {
521 1 : nPlanarConfig = PLANARCONFIG_CONTIG;
522 : }
523 67 : else if (nCompression == COMPRESSION_WEBP ||
524 : nCompression == COMPRESSION_JXL)
525 : {
526 7 : nPlanarConfig = PLANARCONFIG_CONTIG;
527 : }
528 : }
529 :
530 : const char *pszInterleave =
531 216 : GetOptionValue("INTERLEAVE", "INTERLEAVE_OVERVIEW");
532 216 : if (pszInterleave != nullptr && pszInterleave[0] != '\0')
533 : {
534 27 : if (EQUAL(pszInterleave, "PIXEL"))
535 27 : nPlanarConfig = PLANARCONFIG_CONTIG;
536 0 : else if (EQUAL(pszInterleave, "BAND"))
537 0 : nPlanarConfig = PLANARCONFIG_SEPARATE;
538 : else
539 : {
540 0 : CPLError(CE_Failure, CPLE_AppDefined,
541 : "INTERLEAVE_OVERVIEW=%s unsupported, "
542 : "value must be PIXEL or BAND. ignoring",
543 : pszInterleave);
544 : }
545 : }
546 :
547 : /* -------------------------------------------------------------------- */
548 : /* Figure out the photometric interpretation to use. */
549 : /* -------------------------------------------------------------------- */
550 216 : if (nBands == 3)
551 43 : nPhotometric = PHOTOMETRIC_RGB;
552 173 : else if (papoBandList[0]->GetColorTable() != nullptr &&
553 7 : (papoBandList[0]->GetRasterDataType() == GDT_Byte ||
554 180 : papoBandList[0]->GetRasterDataType() == GDT_UInt16) &&
555 6 : !STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
556 : {
557 : // Would also apply to other lossy compression scheme, but for JPEG,
558 : // this at least avoids a later cryptic error message from libtiff:
559 : // "JPEGSetupEncode:PhotometricInterpretation 3 not allowed for JPEG"
560 6 : if (nCompression == COMPRESSION_JPEG)
561 : {
562 1 : CPLError(CE_Failure, CPLE_AppDefined,
563 : "Cannot create JPEG compressed overviews on a raster "
564 : "with a color table");
565 1 : return CE_Failure;
566 : }
567 :
568 5 : nPhotometric = PHOTOMETRIC_PALETTE;
569 : // Color map is set up after
570 : }
571 183 : else if (nBands >= 3 &&
572 16 : papoBandList[0]->GetColorInterpretation() == GCI_RedBand &&
573 198 : papoBandList[1]->GetColorInterpretation() == GCI_GreenBand &&
574 15 : papoBandList[2]->GetColorInterpretation() == GCI_BlueBand)
575 : {
576 15 : nPhotometric = PHOTOMETRIC_RGB;
577 : }
578 : else
579 152 : nPhotometric = PHOTOMETRIC_MINISBLACK;
580 :
581 215 : const char *pszOptionKey = "";
582 : const char *pszPhotometric =
583 215 : GetOptionValue("PHOTOMETRIC", "PHOTOMETRIC_OVERVIEW", &pszOptionKey);
584 215 : if (!GTIFFUpdatePhotometric(pszPhotometric, pszOptionKey, nCompression,
585 : pszInterleave, nBands, nPhotometric,
586 : nPlanarConfig))
587 : {
588 0 : return CE_Failure;
589 : }
590 :
591 : /* -------------------------------------------------------------------- */
592 : /* Figure out the predictor value to use. */
593 : /* -------------------------------------------------------------------- */
594 215 : int nPredictor = PREDICTOR_NONE;
595 215 : if (GTIFFSupportsPredictor(nCompression))
596 : {
597 : const char *pszPredictor =
598 63 : GetOptionValue("PREDICTOR", "PREDICTOR_OVERVIEW");
599 63 : if (pszPredictor != nullptr)
600 : {
601 2 : nPredictor = atoi(pszPredictor);
602 : }
603 : }
604 :
605 : /* -------------------------------------------------------------------- */
606 : /* Create the file, if it does not already exist. */
607 : /* -------------------------------------------------------------------- */
608 : VSIStatBufL sStatBuf;
609 215 : VSILFILE *fpL = nullptr;
610 :
611 215 : bool bCreateBigTIFF = false;
612 215 : if (VSIStatExL(pszFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
613 : {
614 : /* --------------------------------------------------------------------
615 : */
616 : /* Compute the uncompressed size. */
617 : /* --------------------------------------------------------------------
618 : */
619 211 : double dfUncompressedOverviewSize = 0;
620 : int nDataTypeSize =
621 211 : GDALGetDataTypeSizeBytes(papoBandList[0]->GetRasterDataType());
622 :
623 531 : for (iOverview = 0; iOverview < nOverviews; iOverview++)
624 : {
625 320 : const int nOXSize =
626 320 : panOverviewList ? (nXSize + panOverviewList[iOverview] - 1) /
627 232 : panOverviewList[iOverview]
628 : :
629 : // cppcheck-suppress nullPointer
630 88 : pasOverviewSize[iOverview].first;
631 320 : const int nOYSize =
632 320 : panOverviewList ? (nYSize + panOverviewList[iOverview] - 1) /
633 232 : panOverviewList[iOverview]
634 : :
635 : // cppcheck-suppress nullPointer
636 88 : pasOverviewSize[iOverview].second;
637 :
638 320 : dfUncompressedOverviewSize +=
639 320 : nOXSize * static_cast<double>(nOYSize) * nBands * nDataTypeSize;
640 : }
641 :
642 : /* --------------------------------------------------------------------
643 : */
644 : /* Should the file be created as a bigtiff file? */
645 : /* --------------------------------------------------------------------
646 : */
647 211 : const char *pszBIGTIFF = GetOptionValue("BIGTIFF", "BIGTIFF_OVERVIEW");
648 :
649 211 : if (pszBIGTIFF == nullptr)
650 162 : pszBIGTIFF = "IF_SAFER";
651 :
652 211 : if (EQUAL(pszBIGTIFF, "IF_NEEDED"))
653 : {
654 0 : if (nCompression == COMPRESSION_NONE &&
655 : dfUncompressedOverviewSize > 4200000000.0)
656 0 : bCreateBigTIFF = true;
657 : }
658 211 : else if (EQUAL(pszBIGTIFF, "IF_SAFER"))
659 : {
660 : // Look at the size of the base image and suppose that
661 : // the added overview levels won't be more than 1/2 of
662 : // the size of the base image. The theory says 1/3 of the
663 : // base image size if the overview levels are 2, 4, 8, 16.
664 : // Thus take 1/2 as the security margin for 1/3.
665 164 : const double dfUncompressedImageSize =
666 164 : nXSize * static_cast<double>(nYSize) * nBands * nDataTypeSize;
667 164 : if (dfUncompressedImageSize * 0.5 > 4200000000.0)
668 5 : bCreateBigTIFF = true;
669 : }
670 : else
671 : {
672 47 : bCreateBigTIFF = CPLTestBool(pszBIGTIFF);
673 47 : if (!bCreateBigTIFF && nCompression == COMPRESSION_NONE &&
674 : dfUncompressedOverviewSize > 4200000000.0)
675 : {
676 2 : CPLError(CE_Failure, CPLE_NotSupported,
677 : "The overview file will be larger than 4GB, "
678 : "so BigTIFF is necessary. "
679 : "Creation failed.");
680 2 : return CE_Failure;
681 : }
682 : }
683 :
684 209 : if (bCreateBigTIFF)
685 48 : CPLDebug("GTiff", "File being created as a BigTIFF.");
686 :
687 209 : fpL = VSIFOpenL(pszFilename, "w+");
688 209 : if (fpL == nullptr)
689 1 : hOTIFF = nullptr;
690 : else
691 : hOTIFF =
692 208 : VSI_TIFFOpen(pszFilename, bCreateBigTIFF ? "w+8" : "w+", fpL);
693 209 : if (hOTIFF == nullptr)
694 : {
695 1 : if (CPLGetLastErrorNo() == 0)
696 1 : CPLError(CE_Failure, CPLE_OpenFailed,
697 : "Attempt to create new tiff file `%s' "
698 : "failed in VSI_TIFFOpen().",
699 : pszFilename);
700 1 : if (fpL != nullptr)
701 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
702 1 : return CE_Failure;
703 : }
704 : }
705 : /* -------------------------------------------------------------------- */
706 : /* Otherwise just open it for update access. */
707 : /* -------------------------------------------------------------------- */
708 : else
709 : {
710 4 : fpL = VSIFOpenL(pszFilename, "r+");
711 4 : if (fpL == nullptr)
712 0 : hOTIFF = nullptr;
713 : else
714 : {
715 4 : GByte abyBuffer[4] = {0};
716 4 : VSIFReadL(abyBuffer, 1, 4, fpL);
717 4 : VSIFSeekL(fpL, 0, SEEK_SET);
718 4 : bCreateBigTIFF = abyBuffer[2] == 43 || abyBuffer[3] == 43;
719 4 : hOTIFF = VSI_TIFFOpen(pszFilename, "r+", fpL);
720 : }
721 4 : if (hOTIFF == nullptr)
722 : {
723 0 : if (CPLGetLastErrorNo() == 0)
724 0 : CPLError(CE_Failure, CPLE_OpenFailed,
725 : "Attempt to create new tiff file `%s' "
726 : "failed in VSI_TIFFOpen().",
727 : pszFilename);
728 0 : if (fpL != nullptr)
729 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
730 0 : return CE_Failure;
731 : }
732 : }
733 :
734 : /* -------------------------------------------------------------------- */
735 : /* Do we have a palette? If so, create a TIFF compatible version. */
736 : /* -------------------------------------------------------------------- */
737 212 : unsigned short *panRed = nullptr;
738 212 : unsigned short *panGreen = nullptr;
739 212 : unsigned short *panBlue = nullptr;
740 :
741 212 : if (nPhotometric == PHOTOMETRIC_PALETTE)
742 : {
743 5 : GDALColorTable *poCT = papoBandList[0]->GetColorTable();
744 5 : int nColorCount = 65536;
745 :
746 5 : if (nBitsPerPixel <= 8)
747 5 : nColorCount = 256;
748 :
749 : panRed = static_cast<unsigned short *>(
750 5 : CPLCalloc(nColorCount, sizeof(unsigned short)));
751 : panGreen = static_cast<unsigned short *>(
752 5 : CPLCalloc(nColorCount, sizeof(unsigned short)));
753 : panBlue = static_cast<unsigned short *>(
754 5 : CPLCalloc(nColorCount, sizeof(unsigned short)));
755 :
756 : const int nColorTableMultiplier = std::max(
757 10 : 1,
758 : std::min(
759 10 : 257,
760 5 : atoi(CSLFetchNameValueDef(
761 : papszOptions, "COLOR_TABLE_MULTIPLIER",
762 : CPLSPrintf(
763 : "%d",
764 5 : GTiffDataset::DEFAULT_COLOR_TABLE_MULTIPLIER_257)))));
765 :
766 1285 : for (int iColor = 0; iColor < nColorCount; iColor++)
767 : {
768 1280 : GDALColorEntry sRGB = {0, 0, 0, 0};
769 :
770 1280 : if (poCT->GetColorEntryAsRGB(iColor, &sRGB))
771 : {
772 2560 : panRed[iColor] = GTiffDataset::ClampCTEntry(
773 1280 : iColor, 1, sRGB.c1, nColorTableMultiplier);
774 2560 : panGreen[iColor] = GTiffDataset::ClampCTEntry(
775 1280 : iColor, 2, sRGB.c2, nColorTableMultiplier);
776 1280 : panBlue[iColor] = GTiffDataset::ClampCTEntry(
777 1280 : iColor, 3, sRGB.c3, nColorTableMultiplier);
778 : }
779 : }
780 : }
781 :
782 : /* -------------------------------------------------------------------- */
783 : /* Do we need some metadata for the overviews? */
784 : /* -------------------------------------------------------------------- */
785 424 : CPLString osMetadata;
786 212 : GDALDataset *poBaseDS = papoBandList[0]->GetDataset();
787 212 : if (poBaseDS)
788 : {
789 : const bool bIsForMaskBand =
790 210 : nBands == 1 && papoBandList[0]->IsMaskBand();
791 210 : GTIFFBuildOverviewMetadata(pszResampling, poBaseDS, bIsForMaskBand,
792 : osMetadata);
793 : }
794 :
795 212 : if (poBaseDS != nullptr && poBaseDS->GetRasterCount() == nBands)
796 : {
797 204 : const bool bStandardColorInterp = GTIFFIsStandardColorInterpretation(
798 : GDALDataset::ToHandle(poBaseDS),
799 : static_cast<uint16_t>(nPhotometric), nullptr);
800 204 : if (!bStandardColorInterp)
801 : {
802 18 : if (osMetadata.size() >= strlen("</GDALMetadata>") &&
803 18 : osMetadata.substr(osMetadata.size() -
804 9 : strlen("</GDALMetadata>")) ==
805 : "</GDALMetadata>")
806 : {
807 9 : osMetadata.resize(osMetadata.size() -
808 : strlen("</GDALMetadata>"));
809 : }
810 : else
811 : {
812 0 : CPLAssert(osMetadata.empty());
813 0 : osMetadata = "<GDALMetadata>";
814 : }
815 36 : for (int i = 0; i < poBaseDS->GetRasterCount(); ++i)
816 : {
817 : const GDALColorInterp eInterp =
818 27 : poBaseDS->GetRasterBand(i + 1)->GetColorInterpretation();
819 : osMetadata +=
820 : CPLSPrintf("<Item sample=\"%d\" name=\"COLORINTERP\" "
821 : "role=\"colorinterp\">",
822 27 : i);
823 27 : osMetadata += GDALGetColorInterpretationName(eInterp);
824 27 : osMetadata += "</Item>";
825 : }
826 9 : osMetadata += "</GDALMetadata>";
827 : }
828 : }
829 :
830 : /* -------------------------------------------------------------------- */
831 : /* Loop, creating overviews. */
832 : /* -------------------------------------------------------------------- */
833 212 : int nOvrBlockXSize = 0;
834 212 : int nOvrBlockYSize = 0;
835 212 : GTIFFGetOverviewBlockSize(papoBandList[0], &nOvrBlockXSize,
836 : &nOvrBlockYSize);
837 :
838 424 : CPLString osNoData; // don't move this in inner scope
839 212 : const char *pszNoData = nullptr;
840 212 : int bNoDataSet = FALSE;
841 212 : const double dfNoDataValue = papoBandList[0]->GetNoDataValue(&bNoDataSet);
842 212 : if (bNoDataSet)
843 : {
844 12 : osNoData = GTiffFormatGDALNoDataTagValue(dfNoDataValue);
845 12 : pszNoData = osNoData.c_str();
846 : }
847 :
848 424 : std::vector<uint16_t> anExtraSamples;
849 240 : for (int i = GTIFFGetMaxColorChannels(nPhotometric) + 1; i <= nBands; i++)
850 : {
851 28 : if (papoBandList[i - 1]->GetColorInterpretation() == GCI_AlphaBand)
852 : {
853 18 : anExtraSamples.push_back(GTiffGetAlphaValue(
854 : GetOptionValue("ALPHA", "GTIFF_ALPHA"), DEFAULT_ALPHA_TYPE));
855 : }
856 : else
857 : {
858 10 : anExtraSamples.push_back(EXTRASAMPLE_UNSPECIFIED);
859 : }
860 : }
861 :
862 212 : const uint32_t *panLercAddCompressionAndVersion = nullptr;
863 212 : uint32_t anLercAddCompressionAndVersion[2] = {LERC_VERSION_2_4,
864 : LERC_ADD_COMPRESSION_NONE};
865 212 : if (pszCompress && EQUAL(pszCompress, "LERC_DEFLATE"))
866 : {
867 5 : anLercAddCompressionAndVersion[1] = LERC_ADD_COMPRESSION_DEFLATE;
868 5 : panLercAddCompressionAndVersion = anLercAddCompressionAndVersion;
869 : }
870 207 : else if (pszCompress && EQUAL(pszCompress, "LERC_ZSTD"))
871 : {
872 5 : anLercAddCompressionAndVersion[1] = LERC_ADD_COMPRESSION_ZSTD;
873 5 : panLercAddCompressionAndVersion = anLercAddCompressionAndVersion;
874 : }
875 :
876 532 : for (iOverview = 0; iOverview < nOverviews; iOverview++)
877 : {
878 321 : const int nOXSize = panOverviewList
879 321 : ? (nXSize + panOverviewList[iOverview] - 1) /
880 233 : panOverviewList[iOverview]
881 : :
882 : // cppcheck-suppress nullPointer
883 88 : pasOverviewSize[iOverview].first;
884 321 : const int nOYSize = panOverviewList
885 321 : ? (nYSize + panOverviewList[iOverview] - 1) /
886 233 : panOverviewList[iOverview]
887 : :
888 : // cppcheck-suppress nullPointer
889 88 : pasOverviewSize[iOverview].second;
890 :
891 321 : unsigned nTileXCount = DIV_ROUND_UP(nOXSize, nOvrBlockXSize);
892 321 : unsigned nTileYCount = DIV_ROUND_UP(nOYSize, nOvrBlockYSize);
893 : // libtiff implementation limitation
894 321 : if (nTileXCount > 0x80000000U / (bCreateBigTIFF ? 8 : 4) / nTileYCount)
895 : {
896 1 : CPLError(CE_Failure, CPLE_NotSupported,
897 : "File too large regarding tile size. This would result "
898 : "in a file with tile arrays larger than 2GB");
899 1 : XTIFFClose(hOTIFF);
900 1 : VSIFCloseL(fpL);
901 1 : return CE_Failure;
902 : }
903 :
904 960 : if (GTIFFWriteDirectory(
905 : hOTIFF, FILETYPE_REDUCEDIMAGE, nOXSize, nOYSize, nBitsPerPixel,
906 : nPlanarConfig, nBands, nOvrBlockXSize, nOvrBlockYSize, TRUE,
907 : nCompression, nPhotometric, nSampleFormat, nPredictor, panRed,
908 320 : panGreen, panBlue, static_cast<int>(anExtraSamples.size()),
909 365 : anExtraSamples.empty() ? nullptr : anExtraSamples.data(),
910 : osMetadata,
911 : GetOptionValue("JPEG_QUALITY", "JPEG_QUALITY_OVERVIEW"),
912 : GetOptionValue("JPEG_TABLESMODE", "JPEG_TABLESMODE_OVERVIEW"),
913 320 : pszNoData, panLercAddCompressionAndVersion, false) == 0)
914 : {
915 0 : XTIFFClose(hOTIFF);
916 0 : VSIFCloseL(fpL);
917 0 : return CE_Failure;
918 : }
919 : }
920 :
921 211 : if (panRed)
922 : {
923 5 : CPLFree(panRed);
924 5 : CPLFree(panGreen);
925 5 : CPLFree(panBlue);
926 5 : panRed = nullptr;
927 5 : panGreen = nullptr;
928 5 : panBlue = nullptr;
929 : }
930 :
931 211 : XTIFFClose(hOTIFF);
932 211 : if (VSIFCloseL(fpL) != 0)
933 0 : return CE_Failure;
934 211 : fpL = nullptr;
935 :
936 : /* -------------------------------------------------------------------- */
937 : /* Open the overview dataset so that we can get at the overview */
938 : /* bands. */
939 : /* -------------------------------------------------------------------- */
940 422 : CPLStringList aosOpenOptions;
941 : aosOpenOptions.SetNameValue("NUM_THREADS",
942 211 : CSLFetchNameValue(papszOptions, "NUM_THREADS"));
943 : aosOpenOptions.SetNameValue(
944 211 : "SPARSE_OK", GetOptionValue("SPARSE_OK", "SPARSE_OK_OVERVIEW"));
945 : aosOpenOptions.SetNameValue(
946 : "@MASK_OVERVIEW_DATASET",
947 211 : CSLFetchNameValue(papszOptions, "MASK_OVERVIEW_DATASET"));
948 : GDALDataset *hODS =
949 211 : GDALDataset::Open(pszFilename, GDAL_OF_RASTER | GDAL_OF_UPDATE, nullptr,
950 211 : aosOpenOptions.List());
951 211 : if (hODS == nullptr)
952 0 : return CE_Failure;
953 :
954 : /* -------------------------------------------------------------------- */
955 : /* Do we need to set the jpeg quality? */
956 : /* -------------------------------------------------------------------- */
957 211 : TIFF *hTIFF = static_cast<TIFF *>(hODS->GetInternalHandle(nullptr));
958 :
959 : const char *pszJPEGQuality =
960 211 : GetOptionValue("JPEG_QUALITY", "JPEG_QUALITY_OVERVIEW");
961 211 : if (nCompression == COMPRESSION_JPEG && pszJPEGQuality != nullptr)
962 : {
963 4 : const int nJpegQuality = atoi(pszJPEGQuality);
964 4 : TIFFSetField(hTIFF, TIFFTAG_JPEGQUALITY, nJpegQuality);
965 4 : GTIFFSetJpegQuality(GDALDataset::ToHandle(hODS), nJpegQuality);
966 : }
967 :
968 : const char *pszWebpLevel =
969 211 : GetOptionValue("WEBP_LEVEL", "WEBP_LEVEL_OVERVIEW");
970 211 : if (nCompression == COMPRESSION_WEBP && pszWebpLevel != nullptr)
971 : {
972 3 : const int nWebpLevel = atoi(pszWebpLevel);
973 3 : if (nWebpLevel >= 1)
974 : {
975 3 : TIFFSetField(hTIFF, TIFFTAG_WEBP_LEVEL, nWebpLevel);
976 3 : GTIFFSetWebPLevel(GDALDataset::ToHandle(hODS), nWebpLevel);
977 : }
978 : }
979 :
980 : const char *pszWebpLossless =
981 211 : GetOptionValue("WEBP_LOSSLESS", "WEBP_LOSSLESS_OVERVIEW");
982 211 : if (nCompression == COMPRESSION_WEBP && pszWebpLossless != nullptr)
983 : {
984 1 : const bool bWebpLossless = CPLTestBool(pszWebpLossless);
985 1 : TIFFSetField(hTIFF, TIFFTAG_WEBP_LOSSLESS,
986 : static_cast<int>(bWebpLossless));
987 1 : GTIFFSetWebPLossless(GDALDataset::ToHandle(hODS), bWebpLossless);
988 : }
989 :
990 : const char *pszJPEGTablesMode =
991 211 : GetOptionValue("JPEG_TABLESMODE", "JPEG_TABLESMODE_OVERVIEW");
992 211 : if (nCompression == COMPRESSION_JPEG && pszJPEGTablesMode != nullptr)
993 : {
994 0 : const int nJpegTablesMode = atoi(pszJPEGTablesMode);
995 0 : TIFFSetField(hTIFF, TIFFTAG_JPEGTABLESMODE, nJpegTablesMode);
996 0 : GTIFFSetJpegTablesMode(GDALDataset::ToHandle(hODS), nJpegTablesMode);
997 : }
998 :
999 211 : const char *pszZLevel = GetOptionValue("ZLEVEL", "ZLEVEL_OVERVIEW");
1000 211 : if ((nCompression == COMPRESSION_DEFLATE ||
1001 211 : anLercAddCompressionAndVersion[1] == LERC_ADD_COMPRESSION_DEFLATE) &&
1002 : pszZLevel != nullptr)
1003 : {
1004 2 : const int nZLevel = atoi(pszZLevel);
1005 2 : if (nZLevel >= 1)
1006 : {
1007 2 : TIFFSetField(hTIFF, TIFFTAG_ZIPQUALITY, nZLevel);
1008 2 : GTIFFSetZLevel(GDALDataset::ToHandle(hODS), nZLevel);
1009 : }
1010 : }
1011 :
1012 : const char *pszZSTDLevel =
1013 211 : GetOptionValue("ZSTD_LEVEL", "ZSTD_LEVEL_OVERVIEW");
1014 211 : if ((nCompression == COMPRESSION_ZSTD ||
1015 211 : anLercAddCompressionAndVersion[1] == LERC_ADD_COMPRESSION_ZSTD) &&
1016 : pszZSTDLevel != nullptr)
1017 : {
1018 2 : const int nZSTDLevel = atoi(pszZSTDLevel);
1019 2 : if (nZSTDLevel >= 1)
1020 : {
1021 2 : TIFFSetField(hTIFF, TIFFTAG_ZSTD_LEVEL, nZSTDLevel);
1022 2 : GTIFFSetZSTDLevel(GDALDataset::ToHandle(hODS), nZSTDLevel);
1023 : }
1024 : }
1025 :
1026 : const char *pszMaxZError =
1027 211 : GetOptionValue("MAX_Z_ERROR", "MAX_Z_ERROR_OVERVIEW");
1028 211 : if (nCompression == COMPRESSION_LERC && pszMaxZError != nullptr)
1029 : {
1030 10 : const double dfMaxZError = CPLAtof(pszMaxZError);
1031 10 : if (dfMaxZError >= 0)
1032 : {
1033 10 : TIFFSetField(hTIFF, TIFFTAG_LERC_MAXZERROR, dfMaxZError);
1034 10 : GTIFFSetMaxZError(GDALDataset::ToHandle(hODS), dfMaxZError);
1035 : }
1036 : }
1037 :
1038 : #if HAVE_JXL
1039 211 : if (nCompression == COMPRESSION_JXL)
1040 : {
1041 5 : if (const char *pszJXLLossLess =
1042 5 : GetOptionValue("JXL_LOSSLESS", "JXL_LOSSLESS_OVERVIEW"))
1043 : {
1044 5 : const bool bJXLLossless = CPLTestBool(pszJXLLossLess);
1045 5 : TIFFSetField(hTIFF, TIFFTAG_JXL_LOSSYNESS,
1046 : bJXLLossless ? JXL_LOSSLESS : JXL_LOSSY);
1047 5 : GTIFFSetJXLLossless(GDALDataset::ToHandle(hODS), bJXLLossless);
1048 : }
1049 5 : if (const char *pszJXLEffort =
1050 5 : GetOptionValue("JXL_EFFORT", "JXL_EFFORT_OVERVIEW"))
1051 : {
1052 0 : const int nJXLEffort = atoi(pszJXLEffort);
1053 0 : TIFFSetField(hTIFF, TIFFTAG_JXL_EFFORT, nJXLEffort);
1054 0 : GTIFFSetJXLEffort(GDALDataset::ToHandle(hODS), nJXLEffort);
1055 : }
1056 5 : if (const char *pszJXLDistance =
1057 5 : GetOptionValue("JXL_DISTANCE", "JXL_DISTANCE_OVERVIEW"))
1058 : {
1059 : const float fJXLDistance =
1060 1 : static_cast<float>(CPLAtof(pszJXLDistance));
1061 1 : TIFFSetField(hTIFF, TIFFTAG_JXL_DISTANCE, fJXLDistance);
1062 1 : GTIFFSetJXLDistance(GDALDataset::ToHandle(hODS), fJXLDistance);
1063 : }
1064 5 : if (const char *pszJXLAlphaDistance = GetOptionValue(
1065 : "JXL_ALPHA_DISTANCE", "JXL_ALPHA_DISTANCE_OVERVIEW"))
1066 : {
1067 : const float fJXLAlphaDistance =
1068 1 : static_cast<float>(CPLAtof(pszJXLAlphaDistance));
1069 1 : TIFFSetField(hTIFF, TIFFTAG_JXL_ALPHA_DISTANCE, fJXLAlphaDistance);
1070 1 : GTIFFSetJXLAlphaDistance(GDALDataset::ToHandle(hODS),
1071 : fJXLAlphaDistance);
1072 : }
1073 : }
1074 : #endif
1075 :
1076 : /* -------------------------------------------------------------------- */
1077 : /* Loop writing overview data. */
1078 : /* -------------------------------------------------------------------- */
1079 :
1080 211 : int *panOverviewListSorted = nullptr;
1081 211 : if (panOverviewList)
1082 : {
1083 : panOverviewListSorted =
1084 170 : static_cast<int *>(CPLMalloc(sizeof(int) * nOverviews));
1085 170 : memcpy(panOverviewListSorted, panOverviewList,
1086 170 : sizeof(int) * nOverviews);
1087 170 : std::sort(panOverviewListSorted, panOverviewListSorted + nOverviews);
1088 : }
1089 :
1090 211 : GTIFFSetThreadLocalInExternalOvr(true);
1091 :
1092 211 : CPLErr eErr = CE_None;
1093 :
1094 : // If we have an alpha band, we want it to be generated before downsampling
1095 : // other bands
1096 211 : bool bHasAlphaBand = false;
1097 566 : for (int iBand = 0; iBand < nBands; iBand++)
1098 : {
1099 355 : if (papoBandList[iBand]->GetColorInterpretation() == GCI_AlphaBand)
1100 18 : bHasAlphaBand = true;
1101 : }
1102 :
1103 211 : const auto poColorTable = papoBandList[0]->GetColorTable();
1104 211 : if (((((bSourceIsPixelInterleaved && bSourceIsJPEG2000) ||
1105 96 : (nCompression != COMPRESSION_NONE)) &&
1106 211 : nPlanarConfig == PLANARCONFIG_CONTIG) ||
1107 104 : bHasAlphaBand) &&
1108 104 : !GDALDataTypeIsComplex(papoBandList[0]->GetRasterDataType()) &&
1109 1 : (poColorTable == nullptr || STARTS_WITH_CI(pszResampling, "NEAR") ||
1110 422 : poColorTable->IsIdentity()) &&
1111 104 : (STARTS_WITH_CI(pszResampling, "NEAR") ||
1112 69 : EQUAL(pszResampling, "AVERAGE") || EQUAL(pszResampling, "RMS") ||
1113 42 : EQUAL(pszResampling, "GAUSS") || EQUAL(pszResampling, "CUBIC") ||
1114 10 : EQUAL(pszResampling, "CUBICSPLINE") ||
1115 10 : EQUAL(pszResampling, "LANCZOS") || EQUAL(pszResampling, "BILINEAR") ||
1116 6 : EQUAL(pszResampling, "MODE")))
1117 : {
1118 : // In the case of pixel interleaved compressed overviews, we want to
1119 : // generate the overviews for all the bands block by block, and not
1120 : // band after band, in order to write the block once and not loose
1121 : // space in the TIFF file.
1122 : GDALRasterBand ***papapoOverviewBands =
1123 100 : static_cast<GDALRasterBand ***>(CPLCalloc(sizeof(void *), nBands));
1124 302 : for (int iBand = 0; iBand < nBands && eErr == CE_None; iBand++)
1125 : {
1126 202 : GDALRasterBand *poSrcBand = papoBandList[iBand];
1127 202 : GDALRasterBand *poDstBand = hODS->GetRasterBand(iBand + 1);
1128 404 : papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
1129 202 : CPLCalloc(sizeof(void *), nOverviews));
1130 :
1131 202 : int bHasNoData = FALSE;
1132 202 : const double noDataValue = poSrcBand->GetNoDataValue(&bHasNoData);
1133 202 : if (bHasNoData)
1134 9 : poDstBand->SetNoDataValue(noDataValue);
1135 : std::vector<bool> abDstOverviewAssigned(
1136 404 : 1 + poDstBand->GetOverviewCount());
1137 :
1138 564 : for (int i = 0; i < nOverviews && eErr == CE_None; i++)
1139 : {
1140 : const bool bDegenerateOverview =
1141 186 : panOverviewListSorted != nullptr &&
1142 381 : (poSrcBand->GetXSize() >> panOverviewListSorted[i]) == 0 &&
1143 19 : (poSrcBand->GetYSize() >> panOverviewListSorted[i]) == 0;
1144 :
1145 680 : for (int j = -1;
1146 680 : j < poDstBand->GetOverviewCount() && eErr == CE_None; j++)
1147 : {
1148 680 : if (abDstOverviewAssigned[1 + j])
1149 312 : continue;
1150 : GDALRasterBand *poOverview =
1151 368 : (j < 0) ? poDstBand : poDstBand->GetOverview(j);
1152 368 : if (poOverview == nullptr)
1153 : {
1154 0 : eErr = CE_Failure;
1155 0 : continue;
1156 : }
1157 :
1158 : bool bMatch;
1159 368 : if (panOverviewListSorted)
1160 : {
1161 192 : const int nOvFactor = GDALComputeOvFactor(
1162 : poOverview->GetXSize(), poSrcBand->GetXSize(),
1163 : poOverview->GetYSize(), poSrcBand->GetYSize());
1164 :
1165 192 : bMatch = nOvFactor == panOverviewListSorted[i] ||
1166 16 : nOvFactor == GDALOvLevelAdjust2(
1167 8 : panOverviewListSorted[i],
1168 : poSrcBand->GetXSize(),
1169 : poSrcBand->GetYSize())
1170 : // Deal with edge cases where overview levels
1171 : // lead to degenerate 1x1 overviews
1172 201 : || (bDegenerateOverview &&
1173 1 : poOverview->GetXSize() == 1 &&
1174 1 : poOverview->GetYSize() == 1);
1175 : }
1176 : else
1177 : {
1178 176 : bMatch = (
1179 : // cppcheck-suppress nullPointer
1180 176 : poOverview->GetXSize() ==
1181 352 : pasOverviewSize[i].first &&
1182 : // cppcheck-suppress nullPointer
1183 176 : poOverview->GetYSize() ==
1184 176 : pasOverviewSize[i].second);
1185 : }
1186 368 : if (bMatch)
1187 : {
1188 362 : abDstOverviewAssigned[j + 1] = true;
1189 362 : papapoOverviewBands[iBand][i] = poOverview;
1190 362 : if (bHasNoData)
1191 14 : poOverview->SetNoDataValue(noDataValue);
1192 362 : break;
1193 : }
1194 : }
1195 :
1196 362 : CPLAssert(papapoOverviewBands[iBand][i] != nullptr);
1197 : }
1198 : }
1199 :
1200 : {
1201 : CPLConfigOptionSetter oSetter(
1202 : "GDAL_NUM_THREADS",
1203 200 : CSLFetchNameValue(papszOptions, "NUM_THREADS"), true);
1204 :
1205 100 : if (eErr == CE_None)
1206 100 : eErr = GDALRegenerateOverviewsMultiBand(
1207 : nBands, papoBandList, nOverviews, papapoOverviewBands,
1208 : pszResampling, pfnProgress, pProgressData, papszOptions);
1209 : }
1210 :
1211 302 : for (int iBand = 0; iBand < nBands; iBand++)
1212 : {
1213 202 : CPLFree(papapoOverviewBands[iBand]);
1214 : }
1215 100 : CPLFree(papapoOverviewBands);
1216 : }
1217 : else
1218 : {
1219 : GDALRasterBand **papoOverviews = static_cast<GDALRasterBand **>(
1220 111 : CPLCalloc(sizeof(void *), knMaxOverviews));
1221 :
1222 264 : for (int iBand = 0; iBand < nBands && eErr == CE_None; iBand++)
1223 : {
1224 153 : GDALRasterBand *hSrcBand = papoBandList[iBand];
1225 153 : GDALRasterBand *hDstBand = hODS->GetRasterBand(iBand + 1);
1226 :
1227 153 : int bHasNoData = FALSE;
1228 153 : const double noDataValue = hSrcBand->GetNoDataValue(&bHasNoData);
1229 153 : if (bHasNoData)
1230 9 : hDstBand->SetNoDataValue(noDataValue);
1231 :
1232 : // FIXME: this logic regenerates all overview bands, not only the
1233 : // ones requested.
1234 :
1235 153 : papoOverviews[0] = hDstBand;
1236 153 : int nDstOverviews = hDstBand->GetOverviewCount() + 1;
1237 153 : CPLAssert(nDstOverviews < knMaxOverviews);
1238 153 : nDstOverviews = std::min(knMaxOverviews, nDstOverviews);
1239 :
1240 : // TODO(schwehr): Convert to starting with i = 1 and remove +1.
1241 228 : for (int i = 0; i < nDstOverviews - 1 && eErr == CE_None; i++)
1242 : {
1243 75 : papoOverviews[i + 1] = hDstBand->GetOverview(i);
1244 75 : if (papoOverviews[i + 1] == nullptr)
1245 : {
1246 0 : eErr = CE_Failure;
1247 : }
1248 : else
1249 : {
1250 75 : if (bHasNoData)
1251 1 : papoOverviews[i + 1]->SetNoDataValue(noDataValue);
1252 : }
1253 : }
1254 :
1255 306 : void *pScaledProgressData = GDALCreateScaledProgress(
1256 153 : iBand / static_cast<double>(nBands),
1257 153 : (iBand + 1) / static_cast<double>(nBands), pfnProgress,
1258 : pProgressData);
1259 :
1260 : {
1261 : CPLConfigOptionSetter oSetter(
1262 : "GDAL_NUM_THREADS",
1263 306 : CSLFetchNameValue(papszOptions, "NUM_THREADS"), true);
1264 :
1265 153 : if (eErr == CE_None)
1266 153 : eErr = GDALRegenerateOverviewsEx(
1267 : hSrcBand, nDstOverviews,
1268 : reinterpret_cast<GDALRasterBandH *>(papoOverviews),
1269 : pszResampling, GDALScaledProgress, pScaledProgressData,
1270 : papszOptions);
1271 : }
1272 :
1273 153 : GDALDestroyScaledProgress(pScaledProgressData);
1274 : }
1275 :
1276 111 : CPLFree(papoOverviews);
1277 : }
1278 :
1279 : /* -------------------------------------------------------------------- */
1280 : /* Cleanup */
1281 : /* -------------------------------------------------------------------- */
1282 211 : if (eErr == CE_None)
1283 210 : hODS->FlushCache(true);
1284 211 : delete hODS;
1285 :
1286 211 : GTIFFSetThreadLocalInExternalOvr(false);
1287 :
1288 211 : CPLFree(panOverviewListSorted);
1289 :
1290 211 : pfnProgress(1.0, nullptr, pProgressData);
1291 :
1292 211 : return eErr;
1293 : }
|