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