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