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