Line data Source code
1 :
2 : /******************************************************************************
3 : *
4 : * Project: GDAL Core
5 : * Purpose: Helper code to implement overview support in different drivers.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2000, Frank Warmerdam
10 : * Copyright (c) 2007-2010, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_port.h"
16 : #include "gdal_priv.h"
17 :
18 : #include <cmath>
19 : #include <cstddef>
20 : #include <cstdlib>
21 :
22 : #include <algorithm>
23 : #include <complex>
24 : #include <condition_variable>
25 : #include <limits>
26 : #include <list>
27 : #include <memory>
28 : #include <mutex>
29 : #include <vector>
30 :
31 : #include "cpl_conv.h"
32 : #include "cpl_error.h"
33 : #include "cpl_float.h"
34 : #include "cpl_progress.h"
35 : #include "cpl_vsi.h"
36 : #include "cpl_worker_thread_pool.h"
37 : #include "gdal.h"
38 : #include "gdal_thread_pool.h"
39 : #include "gdalwarper.h"
40 : #include "gdal_vrt.h"
41 : #include "vrtdataset.h"
42 :
43 : #ifdef USE_NEON_OPTIMIZATIONS
44 : #include "include_sse2neon.h"
45 :
46 : #if (!defined(__aarch64__) && !defined(_M_ARM64))
47 : #define ARM_V7
48 : #endif
49 :
50 : #define USE_SSE2
51 :
52 : #include "gdalsse_priv.h"
53 :
54 : // Restrict to 64bit processors because they are guaranteed to have SSE2,
55 : // or if __AVX2__ is defined.
56 : #elif defined(__x86_64) || defined(_M_X64) || defined(__AVX2__)
57 : #define USE_SSE2
58 :
59 : #include "gdalsse_priv.h"
60 :
61 : #ifdef __SSE3__
62 : #include <pmmintrin.h>
63 : #endif
64 : #ifdef __SSSE3__
65 : #include <tmmintrin.h>
66 : #endif
67 : #ifdef __SSE4_1__
68 : #include <smmintrin.h>
69 : #endif
70 : #ifdef __AVX2__
71 : #include <immintrin.h>
72 : #endif
73 :
74 : #endif
75 :
76 : // To be included after above USE_SSE2 and include gdalsse_priv.h
77 : // to avoid build issue on Windows x86
78 : #include "gdal_priv_templates.hpp"
79 :
80 : /************************************************************************/
81 : /* GDALResampleChunk_Near() */
82 : /************************************************************************/
83 :
84 : template <class T>
85 1309 : static CPLErr GDALResampleChunk_NearT(const GDALOverviewResampleArgs &args,
86 : const T *pChunk, T **ppDstBuffer)
87 :
88 : {
89 1309 : const double dfXRatioDstToSrc = args.dfXRatioDstToSrc;
90 1309 : const double dfYRatioDstToSrc = args.dfYRatioDstToSrc;
91 1309 : const GDALDataType eWrkDataType = args.eWrkDataType;
92 1309 : const int nChunkXOff = args.nChunkXOff;
93 1309 : const int nChunkXSize = args.nChunkXSize;
94 1309 : const int nChunkYOff = args.nChunkYOff;
95 1309 : const int nDstXOff = args.nDstXOff;
96 1309 : const int nDstXOff2 = args.nDstXOff2;
97 1309 : const int nDstYOff = args.nDstYOff;
98 1309 : const int nDstYOff2 = args.nDstYOff2;
99 1309 : const int nDstXWidth = nDstXOff2 - nDstXOff;
100 :
101 : /* -------------------------------------------------------------------- */
102 : /* Allocate buffers. */
103 : /* -------------------------------------------------------------------- */
104 1309 : *ppDstBuffer = static_cast<T *>(
105 1309 : VSI_MALLOC3_VERBOSE(nDstXWidth, nDstYOff2 - nDstYOff,
106 : GDALGetDataTypeSizeBytes(eWrkDataType)));
107 1309 : if (*ppDstBuffer == nullptr)
108 : {
109 0 : return CE_Failure;
110 : }
111 1309 : T *const pDstBuffer = *ppDstBuffer;
112 :
113 : int *panSrcXOff =
114 1309 : static_cast<int *>(VSI_MALLOC2_VERBOSE(nDstXWidth, sizeof(int)));
115 :
116 1309 : if (panSrcXOff == nullptr)
117 : {
118 0 : return CE_Failure;
119 : }
120 :
121 : /* ==================================================================== */
122 : /* Precompute inner loop constants. */
123 : /* ==================================================================== */
124 842487 : for (int iDstPixel = nDstXOff; iDstPixel < nDstXOff2; ++iDstPixel)
125 : {
126 841178 : int nSrcXOff = static_cast<int>(0.5 + iDstPixel * dfXRatioDstToSrc);
127 841178 : if (nSrcXOff < nChunkXOff)
128 0 : nSrcXOff = nChunkXOff;
129 :
130 841178 : panSrcXOff[iDstPixel - nDstXOff] = nSrcXOff;
131 : }
132 :
133 : /* ==================================================================== */
134 : /* Loop over destination scanlines. */
135 : /* ==================================================================== */
136 144088 : for (int iDstLine = nDstYOff; iDstLine < nDstYOff2; ++iDstLine)
137 : {
138 142779 : int nSrcYOff = static_cast<int>(0.5 + iDstLine * dfYRatioDstToSrc);
139 142779 : if (nSrcYOff < nChunkYOff)
140 0 : nSrcYOff = nChunkYOff;
141 :
142 142779 : const T *const pSrcScanline =
143 : pChunk +
144 142779 : (static_cast<size_t>(nSrcYOff - nChunkYOff) * nChunkXSize) -
145 139322 : nChunkXOff;
146 :
147 : /* --------------------------------------------------------------------
148 : */
149 : /* Loop over destination pixels */
150 : /* --------------------------------------------------------------------
151 : */
152 142779 : T *pDstScanline =
153 142779 : pDstBuffer + static_cast<size_t>(iDstLine - nDstYOff) * nDstXWidth;
154 120999521 : for (int iDstPixel = 0; iDstPixel < nDstXWidth; ++iDstPixel)
155 : {
156 120856064 : pDstScanline[iDstPixel] = pSrcScanline[panSrcXOff[iDstPixel]];
157 : }
158 : }
159 :
160 1309 : CPLFree(panSrcXOff);
161 :
162 1309 : return CE_None;
163 : }
164 :
165 1309 : static CPLErr GDALResampleChunk_Near(const GDALOverviewResampleArgs &args,
166 : const void *pChunk, void **ppDstBuffer,
167 : GDALDataType *peDstBufferDataType)
168 : {
169 1309 : *peDstBufferDataType = args.eWrkDataType;
170 1309 : switch (args.eWrkDataType)
171 : {
172 : // For nearest resampling, as no computation is done, only the
173 : // size of the data type matters.
174 1109 : case GDT_UInt8:
175 : case GDT_Int8:
176 : {
177 1109 : CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 1);
178 1109 : return GDALResampleChunk_NearT(
179 : args, static_cast<const uint8_t *>(pChunk),
180 1109 : reinterpret_cast<uint8_t **>(ppDstBuffer));
181 : }
182 :
183 84 : case GDT_Int16:
184 : case GDT_UInt16:
185 : case GDT_Float16:
186 : {
187 84 : CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 2);
188 84 : return GDALResampleChunk_NearT(
189 : args, static_cast<const uint16_t *>(pChunk),
190 84 : reinterpret_cast<uint16_t **>(ppDstBuffer));
191 : }
192 :
193 68 : case GDT_CInt16:
194 : case GDT_CFloat16:
195 : case GDT_Int32:
196 : case GDT_UInt32:
197 : case GDT_Float32:
198 : {
199 68 : CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 4);
200 68 : return GDALResampleChunk_NearT(
201 : args, static_cast<const uint32_t *>(pChunk),
202 68 : reinterpret_cast<uint32_t **>(ppDstBuffer));
203 : }
204 :
205 44 : case GDT_CInt32:
206 : case GDT_CFloat32:
207 : case GDT_Int64:
208 : case GDT_UInt64:
209 : case GDT_Float64:
210 : {
211 44 : CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 8);
212 44 : return GDALResampleChunk_NearT(
213 : args, static_cast<const uint64_t *>(pChunk),
214 44 : reinterpret_cast<uint64_t **>(ppDstBuffer));
215 : }
216 :
217 4 : case GDT_CFloat64:
218 : {
219 4 : return GDALResampleChunk_NearT(
220 : args, static_cast<const std::complex<double> *>(pChunk),
221 4 : reinterpret_cast<std::complex<double> **>(ppDstBuffer));
222 : }
223 :
224 0 : case GDT_Unknown:
225 : case GDT_TypeCount:
226 0 : break;
227 : }
228 0 : CPLAssert(false);
229 : return CE_Failure;
230 : }
231 :
232 : namespace
233 : {
234 :
235 : // Find in the color table the entry whose RGB value is the closest
236 : // (using quadratic distance) to the test color, ignoring transparent entries.
237 3837 : int BestColorEntry(const std::vector<GDALColorEntry> &entries,
238 : const GDALColorEntry &test)
239 : {
240 3837 : int nMinDist = std::numeric_limits<int>::max();
241 3837 : size_t bestEntry = 0;
242 986109 : for (size_t i = 0; i < entries.size(); ++i)
243 : {
244 982272 : const GDALColorEntry &entry = entries[i];
245 : // Ignore transparent entries
246 982272 : if (entry.c4 == 0)
247 3237 : continue;
248 :
249 979035 : int nDist = ((test.c1 - entry.c1) * (test.c1 - entry.c1)) +
250 979035 : ((test.c2 - entry.c2) * (test.c2 - entry.c2)) +
251 979035 : ((test.c3 - entry.c3) * (test.c3 - entry.c3));
252 979035 : if (nDist < nMinDist)
253 : {
254 15847 : nMinDist = nDist;
255 15847 : bestEntry = i;
256 : }
257 : }
258 3837 : return static_cast<int>(bestEntry);
259 : }
260 :
261 7 : std::vector<GDALColorEntry> ReadColorTable(const GDALColorTable &table,
262 : int &transparentIdx)
263 : {
264 7 : std::vector<GDALColorEntry> entries(table.GetColorEntryCount());
265 :
266 7 : transparentIdx = -1;
267 7 : int i = 0;
268 1799 : for (auto &entry : entries)
269 : {
270 1792 : table.GetColorEntryAsRGB(i, &entry);
271 1792 : if (transparentIdx < 0 && entry.c4 == 0)
272 1 : transparentIdx = i;
273 1792 : ++i;
274 : }
275 7 : return entries;
276 : }
277 :
278 : } // unnamed namespace
279 :
280 : /************************************************************************/
281 : /* SQUARE() */
282 : /************************************************************************/
283 :
284 6427 : template <class T, class Tsquare = T> inline Tsquare SQUARE(T val)
285 : {
286 6427 : return static_cast<Tsquare>(val) * val;
287 : }
288 :
289 : /************************************************************************/
290 : /* ComputeIntegerRMS() */
291 : /************************************************************************/
292 : // Compute rms = sqrt(sumSquares / weight) in such a way that it is the
293 : // integer that minimizes abs(rms**2 - sumSquares / weight)
294 : template <class T, class Twork>
295 42 : inline T ComputeIntegerRMS(double sumSquares, double weight)
296 : {
297 42 : const double sumDivWeight = sumSquares / weight;
298 42 : T rms = static_cast<T>(sqrt(sumDivWeight));
299 :
300 : // Is rms**2 or (rms+1)**2 closest to sumSquares / weight ?
301 : // Naive version:
302 : // if( weight * (rms+1)**2 - sumSquares < sumSquares - weight * rms**2 )
303 42 : if (static_cast<double>(static_cast<Twork>(2) * rms * (rms + 1) + 1) <
304 42 : 2 * sumDivWeight)
305 6 : rms += 1;
306 42 : return rms;
307 : }
308 :
309 : template <class T, class Tsum> inline T ComputeIntegerRMS_4values(Tsum)
310 : {
311 : CPLAssert(false);
312 : return 0;
313 : }
314 :
315 28 : template <> inline GByte ComputeIntegerRMS_4values<GByte, int>(int sumSquares)
316 : {
317 : // It has been verified that given the correction on rms below, using
318 : // sqrt((float)((sumSquares + 1)/ 4)) or sqrt((float)sumSquares * 0.25f)
319 : // is equivalent, so use the former as it is used twice.
320 28 : const int sumSquaresPlusOneDiv4 = (sumSquares + 1) / 4;
321 28 : const float sumDivWeight = static_cast<float>(sumSquaresPlusOneDiv4);
322 28 : GByte rms = static_cast<GByte>(std::sqrt(sumDivWeight));
323 :
324 : // Is rms**2 or (rms+1)**2 closest to sumSquares / weight ?
325 : // Naive version:
326 : // if( weight * (rms+1)**2 - sumSquares < sumSquares - weight * rms**2 )
327 : // Optimized version for integer case and weight == 4
328 28 : if (static_cast<int>(rms) * (rms + 1) < sumSquaresPlusOneDiv4)
329 5 : rms += 1;
330 28 : return rms;
331 : }
332 :
333 : template <>
334 24 : inline GUInt16 ComputeIntegerRMS_4values<GUInt16, double>(double sumSquares)
335 : {
336 24 : const double sumDivWeight = sumSquares * 0.25;
337 24 : GUInt16 rms = static_cast<GUInt16>(std::sqrt(sumDivWeight));
338 :
339 : // Is rms**2 or (rms+1)**2 closest to sumSquares / weight ?
340 : // Naive version:
341 : // if( weight * (rms+1)**2 - sumSquares < sumSquares - weight * rms**2 )
342 : // Optimized version for integer case and weight == 4
343 24 : if (static_cast<GUInt32>(rms) * (rms + 1) <
344 24 : static_cast<GUInt32>(sumDivWeight + 0.25))
345 4 : rms += 1;
346 24 : return rms;
347 : }
348 :
349 : #ifdef USE_SSE2
350 :
351 : /************************************************************************/
352 : /* QuadraticMeanByteSSE2OrAVX2() */
353 : /************************************************************************/
354 :
355 : #if defined(__SSSE3__) || defined(USE_NEON_OPTIMIZATIONS)
356 : #define sse2_hadd_epi16 _mm_hadd_epi16
357 : #else
358 5064270 : inline __m128i sse2_hadd_epi16(__m128i a, __m128i b)
359 : {
360 : // Horizontal addition of adjacent pairs
361 5064270 : const auto mask = _mm_set1_epi32(0xFFFF);
362 : const auto horizLo =
363 15192800 : _mm_add_epi32(_mm_and_si128(a, mask), _mm_srli_epi32(a, 16));
364 : const auto horizHi =
365 15192800 : _mm_add_epi32(_mm_and_si128(b, mask), _mm_srli_epi32(b, 16));
366 :
367 : // Recombine low and high parts
368 5064270 : return _mm_packs_epi32(horizLo, horizHi);
369 : }
370 : #endif
371 :
372 : #ifdef __AVX2__
373 :
374 : #define set1_epi16 _mm256_set1_epi16
375 : #define set1_epi32 _mm256_set1_epi32
376 : #define setzero _mm256_setzero_si256
377 : #define set1_ps _mm256_set1_ps
378 : #define loadu_int(x) _mm256_loadu_si256(reinterpret_cast<__m256i const *>(x))
379 : #define unpacklo_epi8 _mm256_unpacklo_epi8
380 : #define unpackhi_epi8 _mm256_unpackhi_epi8
381 : #define madd_epi16 _mm256_madd_epi16
382 : #define add_epi32 _mm256_add_epi32
383 : #define mul_ps _mm256_mul_ps
384 : #define cvtepi32_ps _mm256_cvtepi32_ps
385 : #define sqrt_ps _mm256_sqrt_ps
386 : #define cvttps_epi32 _mm256_cvttps_epi32
387 : #define packs_epi32 _mm256_packs_epi32
388 : #define packus_epi32 _mm256_packus_epi32
389 : #define srli_epi32 _mm256_srli_epi32
390 : #define mullo_epi16 _mm256_mullo_epi16
391 : #define srli_epi16 _mm256_srli_epi16
392 : #define cmpgt_epi16 _mm256_cmpgt_epi16
393 : #define add_epi16 _mm256_add_epi16
394 : #define sub_epi16 _mm256_sub_epi16
395 : #define packus_epi16 _mm256_packus_epi16
396 :
397 : /* AVX2 operates on 2 separate 128-bit lanes, so we have to do shuffling */
398 : /* to get the lower 128-bit bits of what would be a true 256-bit vector register
399 : */
400 :
401 : inline __m256i FIXUP_LANES(__m256i x)
402 : {
403 : return _mm256_permute4x64_epi64(x, _MM_SHUFFLE(3, 1, 2, 0));
404 : }
405 :
406 : #define store_lo(x, y) \
407 : _mm_storeu_si128(reinterpret_cast<__m128i *>(x), \
408 : _mm256_extracti128_si256(FIXUP_LANES(y), 0))
409 : #define storeu_int(x, y) \
410 : _mm256_storeu_si256(reinterpret_cast<__m256i *>(x), FIXUP_LANES(y))
411 : #define hadd_epi16 _mm256_hadd_epi16
412 : #else
413 : #define set1_epi16 _mm_set1_epi16
414 : #define set1_epi32 _mm_set1_epi32
415 : #define setzero _mm_setzero_si128
416 : #define set1_ps _mm_set1_ps
417 : #define loadu_int(x) _mm_loadu_si128(reinterpret_cast<__m128i const *>(x))
418 : #define unpacklo_epi8 _mm_unpacklo_epi8
419 : #define unpackhi_epi8 _mm_unpackhi_epi8
420 : #define madd_epi16 _mm_madd_epi16
421 : #define add_epi32 _mm_add_epi32
422 : #define mul_ps _mm_mul_ps
423 : #define cvtepi32_ps _mm_cvtepi32_ps
424 : #define sqrt_ps _mm_sqrt_ps
425 : #define cvttps_epi32 _mm_cvttps_epi32
426 : #define packs_epi32 _mm_packs_epi32
427 : #define packus_epi32 GDAL_mm_packus_epi32
428 : #define srli_epi32 _mm_srli_epi32
429 : #define mullo_epi16 _mm_mullo_epi16
430 : #define srli_epi16 _mm_srli_epi16
431 : #define cmpgt_epi16 _mm_cmpgt_epi16
432 : #define add_epi16 _mm_add_epi16
433 : #define sub_epi16 _mm_sub_epi16
434 : #define packus_epi16 _mm_packus_epi16
435 : #define store_lo(x, y) _mm_storel_epi64(reinterpret_cast<__m128i *>(x), (y))
436 : #define storeu_int(x, y) _mm_storeu_si128(reinterpret_cast<__m128i *>(x), (y))
437 : #define hadd_epi16 sse2_hadd_epi16
438 : #endif
439 :
440 : template <class T>
441 : static int
442 : #if defined(__GNUC__)
443 : __attribute__((noinline))
444 : #endif
445 5389 : QuadraticMeanByteSSE2OrAVX2(int nDstXWidth, int nChunkXSize,
446 : const T *&CPL_RESTRICT pSrcScanlineShiftedInOut,
447 : T *CPL_RESTRICT pDstScanline)
448 : {
449 : // Optimized implementation for RMS on Byte by
450 : // processing by group of 8 output pixels, so as to use
451 : // a single _mm_sqrt_ps() call for 4 output pixels
452 5389 : const T *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
453 :
454 5389 : int iDstPixel = 0;
455 5389 : const auto one16 = set1_epi16(1);
456 5389 : const auto one32 = set1_epi32(1);
457 5389 : const auto zero = setzero();
458 5389 : const auto minus32768 = set1_epi16(-32768);
459 :
460 5389 : constexpr int DEST_ELTS = static_cast<int>(sizeof(zero)) / 2;
461 521504 : for (; iDstPixel < nDstXWidth - (DEST_ELTS - 1); iDstPixel += DEST_ELTS)
462 : {
463 : // Load 2 * DEST_ELTS bytes from each line
464 516115 : auto firstLine = loadu_int(pSrcScanlineShifted);
465 1032230 : auto secondLine = loadu_int(pSrcScanlineShifted + nChunkXSize);
466 : // Extend those Bytes as UInt16s
467 516115 : auto firstLineLo = unpacklo_epi8(firstLine, zero);
468 516115 : auto firstLineHi = unpackhi_epi8(firstLine, zero);
469 516115 : auto secondLineLo = unpacklo_epi8(secondLine, zero);
470 516115 : auto secondLineHi = unpackhi_epi8(secondLine, zero);
471 :
472 : // Multiplication of 16 bit values and horizontal
473 : // addition of 32 bit results
474 : // [ src[2*i+0]^2 + src[2*i+1]^2 for i in range(4) ]
475 516115 : firstLineLo = madd_epi16(firstLineLo, firstLineLo);
476 516115 : firstLineHi = madd_epi16(firstLineHi, firstLineHi);
477 516115 : secondLineLo = madd_epi16(secondLineLo, secondLineLo);
478 516115 : secondLineHi = madd_epi16(secondLineHi, secondLineHi);
479 :
480 : // Vertical addition
481 516115 : const auto sumSquaresLo = add_epi32(firstLineLo, secondLineLo);
482 516115 : const auto sumSquaresHi = add_epi32(firstLineHi, secondLineHi);
483 :
484 : const auto sumSquaresPlusOneDiv4Lo =
485 1032230 : srli_epi32(add_epi32(sumSquaresLo, one32), 2);
486 : const auto sumSquaresPlusOneDiv4Hi =
487 1032230 : srli_epi32(add_epi32(sumSquaresHi, one32), 2);
488 :
489 : // Take square root and truncate/floor to int32
490 : const auto rmsLo =
491 1548340 : cvttps_epi32(sqrt_ps(cvtepi32_ps(sumSquaresPlusOneDiv4Lo)));
492 : const auto rmsHi =
493 1548340 : cvttps_epi32(sqrt_ps(cvtepi32_ps(sumSquaresPlusOneDiv4Hi)));
494 :
495 : // Merge back low and high registers with each RMS value
496 : // as a 16 bit value.
497 516115 : auto rms = packs_epi32(rmsLo, rmsHi);
498 :
499 : // Round to upper value if it minimizes the
500 : // error |rms^2 - sumSquares/4|
501 : // if( 2 * (2 * rms * (rms + 1) + 1) < sumSquares )
502 : // rms += 1;
503 : // which is equivalent to:
504 : // if( rms * (rms + 1) < (sumSquares+1) / 4 )
505 : // rms += 1;
506 : // And both left and right parts fit on 16 (unsigned) bits
507 : const auto sumSquaresPlusOneDiv4 =
508 516115 : packus_epi32(sumSquaresPlusOneDiv4Lo, sumSquaresPlusOneDiv4Hi);
509 : // cmpgt_epi16 operates on signed int16, but here
510 : // we have unsigned values, so shift them by -32768 before
511 2580580 : const auto mask = cmpgt_epi16(
512 : add_epi16(sumSquaresPlusOneDiv4, minus32768),
513 : add_epi16(mullo_epi16(rms, add_epi16(rms, one16)), minus32768));
514 : // The value of the mask will be -1 when the correction needs to be
515 : // applied
516 516115 : rms = sub_epi16(rms, mask);
517 :
518 : // Pack each 16 bit RMS value to 8 bits
519 516115 : rms = packus_epi16(rms, rms /* could be anything */);
520 516115 : store_lo(&pDstScanline[iDstPixel], rms);
521 516115 : pSrcScanlineShifted += 2 * DEST_ELTS;
522 : }
523 :
524 5389 : pSrcScanlineShiftedInOut = pSrcScanlineShifted;
525 5389 : return iDstPixel;
526 : }
527 :
528 : /************************************************************************/
529 : /* AverageByteSSE2OrAVX2() */
530 : /************************************************************************/
531 :
532 : static int
533 123976 : AverageByteSSE2OrAVX2(int nDstXWidth, int nChunkXSize,
534 : const GByte *&CPL_RESTRICT pSrcScanlineShiftedInOut,
535 : GByte *CPL_RESTRICT pDstScanline)
536 : {
537 : // Optimized implementation for average on Byte by
538 : // processing by group of 16 output pixels for SSE2, or 32 for AVX2
539 :
540 123976 : const auto zero = setzero();
541 123976 : const auto two16 = set1_epi16(2);
542 123976 : const GByte *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
543 :
544 123976 : constexpr int DEST_ELTS = static_cast<int>(sizeof(zero)) / 2;
545 123976 : int iDstPixel = 0;
546 2656110 : for (; iDstPixel < nDstXWidth - (2 * DEST_ELTS - 1);
547 2532130 : iDstPixel += 2 * DEST_ELTS)
548 : {
549 : decltype(setzero()) average0;
550 : {
551 : // Load 2 * DEST_ELTS bytes from each line
552 2532130 : const auto firstLine = loadu_int(pSrcScanlineShifted);
553 : const auto secondLine =
554 5064270 : loadu_int(pSrcScanlineShifted + nChunkXSize);
555 : // Extend those Bytes as UInt16s
556 2532130 : const auto firstLineLo = unpacklo_epi8(firstLine, zero);
557 2532130 : const auto firstLineHi = unpackhi_epi8(firstLine, zero);
558 2532130 : const auto secondLineLo = unpacklo_epi8(secondLine, zero);
559 2532130 : const auto secondLineHi = unpackhi_epi8(secondLine, zero);
560 :
561 : // Vertical addition
562 2532130 : const auto sumLo = add_epi16(firstLineLo, secondLineLo);
563 2532130 : const auto sumHi = add_epi16(firstLineHi, secondLineHi);
564 :
565 : // Horizontal addition of adjacent pairs, and recombine low and high
566 : // parts
567 2532130 : const auto sum = hadd_epi16(sumLo, sumHi);
568 :
569 : // average = (sum + 2) / 4
570 2532130 : average0 = srli_epi16(add_epi16(sum, two16), 2);
571 :
572 2532130 : pSrcScanlineShifted += 2 * DEST_ELTS;
573 : }
574 :
575 : decltype(setzero()) average1;
576 : {
577 : // Load 2 * DEST_ELTS bytes from each line
578 2532130 : const auto firstLine = loadu_int(pSrcScanlineShifted);
579 : const auto secondLine =
580 5064270 : loadu_int(pSrcScanlineShifted + nChunkXSize);
581 : // Extend those Bytes as UInt16s
582 2532130 : const auto firstLineLo = unpacklo_epi8(firstLine, zero);
583 2532130 : const auto firstLineHi = unpackhi_epi8(firstLine, zero);
584 2532130 : const auto secondLineLo = unpacklo_epi8(secondLine, zero);
585 2532130 : const auto secondLineHi = unpackhi_epi8(secondLine, zero);
586 :
587 : // Vertical addition
588 2532130 : const auto sumLo = add_epi16(firstLineLo, secondLineLo);
589 2532130 : const auto sumHi = add_epi16(firstLineHi, secondLineHi);
590 :
591 : // Horizontal addition of adjacent pairs, and recombine low and high
592 : // parts
593 2532130 : const auto sum = hadd_epi16(sumLo, sumHi);
594 :
595 : // average = (sum + 2) / 4
596 2532130 : average1 = srli_epi16(add_epi16(sum, two16), 2);
597 :
598 2532130 : pSrcScanlineShifted += 2 * DEST_ELTS;
599 : }
600 :
601 : // Pack each 16 bit average value to 8 bits
602 2532130 : const auto average = packus_epi16(average0, average1);
603 2532130 : storeu_int(&pDstScanline[iDstPixel], average);
604 : }
605 :
606 123976 : pSrcScanlineShiftedInOut = pSrcScanlineShifted;
607 123976 : return iDstPixel;
608 : }
609 :
610 : /************************************************************************/
611 : /* QuadraticMeanUInt16SSE2() */
612 : /************************************************************************/
613 :
614 : #ifdef __SSE3__
615 : #define sse2_hadd_pd _mm_hadd_pd
616 : #else
617 185 : inline __m128d sse2_hadd_pd(__m128d a, __m128d b)
618 : {
619 : auto aLo_bLo =
620 740 : _mm_castps_pd(_mm_movelh_ps(_mm_castpd_ps(a), _mm_castpd_ps(b)));
621 : auto aHi_bHi =
622 740 : _mm_castps_pd(_mm_movehl_ps(_mm_castpd_ps(b), _mm_castpd_ps(a)));
623 185 : return _mm_add_pd(aLo_bLo, aHi_bHi); // (aLo + aHi, bLo + bHi)
624 : }
625 : #endif
626 :
627 120 : inline __m128d SQUARE_PD(__m128d x)
628 : {
629 120 : return _mm_mul_pd(x, x);
630 : }
631 :
632 : #ifdef __AVX2__
633 :
634 : inline __m256d SQUARE_PD(__m256d x)
635 : {
636 : return _mm256_mul_pd(x, x);
637 : }
638 :
639 : inline __m256d FIXUP_LANES(__m256d x)
640 : {
641 : return _mm256_permute4x64_pd(x, _MM_SHUFFLE(3, 1, 2, 0));
642 : }
643 :
644 : inline __m256 FIXUP_LANES(__m256 x)
645 : {
646 : return _mm256_castpd_ps(FIXUP_LANES(_mm256_castps_pd(x)));
647 : }
648 :
649 : #endif
650 :
651 : static int
652 14 : QuadraticMeanUInt16SSE2(int nDstXWidth, int nChunkXSize,
653 : const uint16_t *&CPL_RESTRICT pSrcScanlineShiftedInOut,
654 : uint16_t *CPL_RESTRICT pDstScanline)
655 : {
656 : // Optimized implementation for RMS on UInt16 by
657 : // processing by group of 4 output pixels.
658 14 : const uint16_t *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
659 :
660 14 : int iDstPixel = 0;
661 14 : const auto zero = _mm_setzero_si128();
662 :
663 : #ifdef __AVX2__
664 : const auto zeroDot25 = _mm256_set1_pd(0.25);
665 : const auto zeroDot5 = _mm256_set1_pd(0.5);
666 :
667 : // The first four 0's could be anything, as we only take the bottom
668 : // 128 bits.
669 : const auto permutation = _mm256_set_epi32(0, 0, 0, 0, 6, 4, 2, 0);
670 : #else
671 14 : const auto zeroDot25 = _mm_set1_pd(0.25);
672 14 : const auto zeroDot5 = _mm_set1_pd(0.5);
673 : #endif
674 :
675 14 : constexpr int DEST_ELTS =
676 : static_cast<int>(sizeof(zero) / sizeof(uint16_t)) / 2;
677 52 : for (; iDstPixel < nDstXWidth - (DEST_ELTS - 1); iDstPixel += DEST_ELTS)
678 : {
679 : // Load 8 UInt16 from each line
680 38 : const auto firstLine = _mm_loadu_si128(
681 : reinterpret_cast<__m128i const *>(pSrcScanlineShifted));
682 : const auto secondLine =
683 38 : _mm_loadu_si128(reinterpret_cast<__m128i const *>(
684 38 : pSrcScanlineShifted + nChunkXSize));
685 :
686 : // Detect if all of the source values fit in 14 bits.
687 : // because if x < 2^14, then 4 * x^2 < 2^30 which fits in a signed int32
688 : // and we can do a much faster implementation.
689 : const auto maskTmp =
690 76 : _mm_srli_epi16(_mm_or_si128(firstLine, secondLine), 14);
691 : #if defined(__i386__) || defined(_M_IX86)
692 : uint64_t nMaskFitsIn14Bits = 0;
693 : _mm_storel_epi64(
694 : reinterpret_cast<__m128i *>(&nMaskFitsIn14Bits),
695 : _mm_packus_epi16(maskTmp, maskTmp /* could be anything */));
696 : #else
697 38 : const auto nMaskFitsIn14Bits = _mm_cvtsi128_si64(
698 : _mm_packus_epi16(maskTmp, maskTmp /* could be anything */));
699 : #endif
700 38 : if (nMaskFitsIn14Bits == 0)
701 : {
702 : // Multiplication of 16 bit values and horizontal
703 : // addition of 32 bit results
704 : const auto firstLineHSumSquare =
705 26 : _mm_madd_epi16(firstLine, firstLine);
706 : const auto secondLineHSumSquare =
707 26 : _mm_madd_epi16(secondLine, secondLine);
708 : // Vertical addition
709 : const auto sumSquares =
710 26 : _mm_add_epi32(firstLineHSumSquare, secondLineHSumSquare);
711 : // In theory we should take sqrt(sumSquares * 0.25f)
712 : // but given the rounding we do, this is equivalent to
713 : // sqrt((sumSquares + 1)/4). This has been verified exhaustively for
714 : // sumSquares <= 4 * 16383^2
715 26 : const auto one32 = _mm_set1_epi32(1);
716 : const auto sumSquaresPlusOneDiv4 =
717 52 : _mm_srli_epi32(_mm_add_epi32(sumSquares, one32), 2);
718 : // Take square root and truncate/floor to int32
719 78 : auto rms = _mm_cvttps_epi32(
720 : _mm_sqrt_ps(_mm_cvtepi32_ps(sumSquaresPlusOneDiv4)));
721 :
722 : // Round to upper value if it minimizes the
723 : // error |rms^2 - sumSquares/4|
724 : // if( 2 * (2 * rms * (rms + 1) + 1) < sumSquares )
725 : // rms += 1;
726 : // which is equivalent to:
727 : // if( rms * rms + rms < (sumSquares+1) / 4 )
728 : // rms += 1;
729 : auto mask =
730 78 : _mm_cmpgt_epi32(sumSquaresPlusOneDiv4,
731 : _mm_add_epi32(_mm_madd_epi16(rms, rms), rms));
732 26 : rms = _mm_sub_epi32(rms, mask);
733 : // Pack each 32 bit RMS value to 16 bits
734 26 : rms = _mm_packs_epi32(rms, rms /* could be anything */);
735 : _mm_storel_epi64(
736 26 : reinterpret_cast<__m128i *>(&pDstScanline[iDstPixel]), rms);
737 26 : pSrcScanlineShifted += 2 * DEST_ELTS;
738 26 : continue;
739 : }
740 :
741 : // An approach using _mm_mullo_epi16, _mm_mulhi_epu16 before extending
742 : // to 32 bit would result in 4 multiplications instead of 8, but
743 : // mullo/mulhi have a worse throughput than mul_pd.
744 :
745 : // Extend those UInt16s as UInt32s
746 12 : const auto firstLineLo = _mm_unpacklo_epi16(firstLine, zero);
747 12 : const auto firstLineHi = _mm_unpackhi_epi16(firstLine, zero);
748 12 : const auto secondLineLo = _mm_unpacklo_epi16(secondLine, zero);
749 12 : const auto secondLineHi = _mm_unpackhi_epi16(secondLine, zero);
750 :
751 : #ifdef __AVX2__
752 : // Multiplication of 32 bit values previously converted to 64 bit double
753 : const auto firstLineLoDbl = SQUARE_PD(_mm256_cvtepi32_pd(firstLineLo));
754 : const auto firstLineHiDbl = SQUARE_PD(_mm256_cvtepi32_pd(firstLineHi));
755 : const auto secondLineLoDbl =
756 : SQUARE_PD(_mm256_cvtepi32_pd(secondLineLo));
757 : const auto secondLineHiDbl =
758 : SQUARE_PD(_mm256_cvtepi32_pd(secondLineHi));
759 :
760 : // Vertical addition of squares
761 : const auto sumSquaresLo =
762 : _mm256_add_pd(firstLineLoDbl, secondLineLoDbl);
763 : const auto sumSquaresHi =
764 : _mm256_add_pd(firstLineHiDbl, secondLineHiDbl);
765 :
766 : // Horizontal addition of squares
767 : const auto sumSquares =
768 : FIXUP_LANES(_mm256_hadd_pd(sumSquaresLo, sumSquaresHi));
769 :
770 : const auto sumDivWeight = _mm256_mul_pd(sumSquares, zeroDot25);
771 :
772 : // Take square root and truncate/floor to int32
773 : auto rms = _mm256_cvttpd_epi32(_mm256_sqrt_pd(sumDivWeight));
774 : const auto rmsDouble = _mm256_cvtepi32_pd(rms);
775 : const auto right = _mm256_sub_pd(
776 : sumDivWeight, _mm256_add_pd(SQUARE_PD(rmsDouble), rmsDouble));
777 :
778 : auto mask =
779 : _mm256_castpd_ps(_mm256_cmp_pd(zeroDot5, right, _CMP_LT_OS));
780 : // Extract 32-bit from each of the 4 64-bit masks
781 : // mask = FIXUP_LANES(_mm256_shuffle_ps(mask, mask,
782 : // _MM_SHUFFLE(2,0,2,0)));
783 : mask = _mm256_permutevar8x32_ps(mask, permutation);
784 : const auto maskI = _mm_castps_si128(_mm256_extractf128_ps(mask, 0));
785 :
786 : // Apply the correction
787 : rms = _mm_sub_epi32(rms, maskI);
788 :
789 : // Pack each 32 bit RMS value to 16 bits
790 : rms = _mm_packus_epi32(rms, rms /* could be anything */);
791 : #else
792 : // Multiplication of 32 bit values previously converted to 64 bit double
793 12 : const auto firstLineLoLo = SQUARE_PD(_mm_cvtepi32_pd(firstLineLo));
794 : const auto firstLineLoHi =
795 24 : SQUARE_PD(_mm_cvtepi32_pd(_mm_srli_si128(firstLineLo, 8)));
796 12 : const auto firstLineHiLo = SQUARE_PD(_mm_cvtepi32_pd(firstLineHi));
797 : const auto firstLineHiHi =
798 24 : SQUARE_PD(_mm_cvtepi32_pd(_mm_srli_si128(firstLineHi, 8)));
799 :
800 12 : const auto secondLineLoLo = SQUARE_PD(_mm_cvtepi32_pd(secondLineLo));
801 : const auto secondLineLoHi =
802 24 : SQUARE_PD(_mm_cvtepi32_pd(_mm_srli_si128(secondLineLo, 8)));
803 12 : const auto secondLineHiLo = SQUARE_PD(_mm_cvtepi32_pd(secondLineHi));
804 : const auto secondLineHiHi =
805 24 : SQUARE_PD(_mm_cvtepi32_pd(_mm_srli_si128(secondLineHi, 8)));
806 :
807 : // Vertical addition of squares
808 12 : const auto sumSquaresLoLo = _mm_add_pd(firstLineLoLo, secondLineLoLo);
809 12 : const auto sumSquaresLoHi = _mm_add_pd(firstLineLoHi, secondLineLoHi);
810 12 : const auto sumSquaresHiLo = _mm_add_pd(firstLineHiLo, secondLineHiLo);
811 12 : const auto sumSquaresHiHi = _mm_add_pd(firstLineHiHi, secondLineHiHi);
812 :
813 : // Horizontal addition of squares
814 12 : const auto sumSquaresLo = sse2_hadd_pd(sumSquaresLoLo, sumSquaresLoHi);
815 12 : const auto sumSquaresHi = sse2_hadd_pd(sumSquaresHiLo, sumSquaresHiHi);
816 :
817 12 : const auto sumDivWeightLo = _mm_mul_pd(sumSquaresLo, zeroDot25);
818 12 : const auto sumDivWeightHi = _mm_mul_pd(sumSquaresHi, zeroDot25);
819 : // Take square root and truncate/floor to int32
820 24 : const auto rmsLo = _mm_cvttpd_epi32(_mm_sqrt_pd(sumDivWeightLo));
821 24 : const auto rmsHi = _mm_cvttpd_epi32(_mm_sqrt_pd(sumDivWeightHi));
822 :
823 : // Correctly round rms to minimize | rms^2 - sumSquares / 4 |
824 : // if( 0.5 < sumDivWeight - (rms * rms + rms) )
825 : // rms += 1;
826 12 : const auto rmsLoDouble = _mm_cvtepi32_pd(rmsLo);
827 12 : const auto rmsHiDouble = _mm_cvtepi32_pd(rmsHi);
828 24 : const auto rightLo = _mm_sub_pd(
829 : sumDivWeightLo, _mm_add_pd(SQUARE_PD(rmsLoDouble), rmsLoDouble));
830 36 : const auto rightHi = _mm_sub_pd(
831 : sumDivWeightHi, _mm_add_pd(SQUARE_PD(rmsHiDouble), rmsHiDouble));
832 :
833 24 : const auto maskLo = _mm_castpd_ps(_mm_cmplt_pd(zeroDot5, rightLo));
834 12 : const auto maskHi = _mm_castpd_ps(_mm_cmplt_pd(zeroDot5, rightHi));
835 : // The value of the mask will be -1 when the correction needs to be
836 : // applied
837 24 : const auto mask = _mm_castps_si128(_mm_shuffle_ps(
838 : maskLo, maskHi, (0 << 0) | (2 << 2) | (0 << 4) | (2 << 6)));
839 :
840 48 : auto rms = _mm_castps_si128(
841 : _mm_movelh_ps(_mm_castsi128_ps(rmsLo), _mm_castsi128_ps(rmsHi)));
842 : // Apply the correction
843 12 : rms = _mm_sub_epi32(rms, mask);
844 :
845 : // Pack each 32 bit RMS value to 16 bits
846 12 : rms = GDAL_mm_int32_to_uint16(rms);
847 : #endif
848 :
849 12 : _mm_storel_epi64(reinterpret_cast<__m128i *>(&pDstScanline[iDstPixel]),
850 : rms);
851 12 : pSrcScanlineShifted += 2 * DEST_ELTS;
852 : }
853 :
854 14 : pSrcScanlineShiftedInOut = pSrcScanlineShifted;
855 14 : return iDstPixel;
856 : }
857 :
858 : /************************************************************************/
859 : /* AverageUInt16SSE2() */
860 : /************************************************************************/
861 :
862 : static int
863 13 : AverageUInt16SSE2(int nDstXWidth, int nChunkXSize,
864 : const uint16_t *&CPL_RESTRICT pSrcScanlineShiftedInOut,
865 : uint16_t *CPL_RESTRICT pDstScanline)
866 : {
867 : // Optimized implementation for average on UInt16 by
868 : // processing by group of 8 output pixels.
869 :
870 13 : const auto mask = _mm_set1_epi32(0xFFFF);
871 13 : const auto two = _mm_set1_epi32(2);
872 13 : const uint16_t *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
873 :
874 13 : int iDstPixel = 0;
875 13 : constexpr int DEST_ELTS = static_cast<int>(sizeof(mask) / sizeof(uint16_t));
876 25 : for (; iDstPixel < nDstXWidth - (DEST_ELTS - 1); iDstPixel += DEST_ELTS)
877 : {
878 : __m128i averageLow;
879 : // Load 8 UInt16 from each line
880 : {
881 12 : const auto firstLine = _mm_loadu_si128(
882 : reinterpret_cast<__m128i const *>(pSrcScanlineShifted));
883 : const auto secondLine =
884 12 : _mm_loadu_si128(reinterpret_cast<__m128i const *>(
885 12 : pSrcScanlineShifted + nChunkXSize));
886 :
887 : // Horizontal addition and extension to 32 bit
888 36 : const auto horizAddFirstLine = _mm_add_epi32(
889 : _mm_and_si128(firstLine, mask), _mm_srli_epi32(firstLine, 16));
890 : const auto horizAddSecondLine =
891 36 : _mm_add_epi32(_mm_and_si128(secondLine, mask),
892 : _mm_srli_epi32(secondLine, 16));
893 :
894 : // Vertical addition and average computation
895 : // average = (sum + 2) >> 2
896 24 : const auto sum = _mm_add_epi32(
897 : _mm_add_epi32(horizAddFirstLine, horizAddSecondLine), two);
898 12 : averageLow = _mm_srli_epi32(sum, 2);
899 : }
900 : // Load 8 UInt16 from each line
901 : __m128i averageHigh;
902 : {
903 : const auto firstLine =
904 12 : _mm_loadu_si128(reinterpret_cast<__m128i const *>(
905 12 : pSrcScanlineShifted + DEST_ELTS));
906 : const auto secondLine =
907 12 : _mm_loadu_si128(reinterpret_cast<__m128i const *>(
908 12 : pSrcScanlineShifted + DEST_ELTS + nChunkXSize));
909 :
910 : // Horizontal addition and extension to 32 bit
911 36 : const auto horizAddFirstLine = _mm_add_epi32(
912 : _mm_and_si128(firstLine, mask), _mm_srli_epi32(firstLine, 16));
913 : const auto horizAddSecondLine =
914 36 : _mm_add_epi32(_mm_and_si128(secondLine, mask),
915 : _mm_srli_epi32(secondLine, 16));
916 :
917 : // Vertical addition and average computation
918 : // average = (sum + 2) >> 2
919 24 : const auto sum = _mm_add_epi32(
920 : _mm_add_epi32(horizAddFirstLine, horizAddSecondLine), two);
921 12 : averageHigh = _mm_srli_epi32(sum, 2);
922 : }
923 :
924 : // Pack each 32 bit average value to 16 bits
925 12 : auto average = GDAL_mm_packus_epi32(averageLow, averageHigh);
926 12 : _mm_storeu_si128(reinterpret_cast<__m128i *>(&pDstScanline[iDstPixel]),
927 : average);
928 12 : pSrcScanlineShifted += 2 * DEST_ELTS;
929 : }
930 :
931 13 : pSrcScanlineShiftedInOut = pSrcScanlineShifted;
932 13 : return iDstPixel;
933 : }
934 :
935 : /************************************************************************/
936 : /* QuadraticMeanFloatSSE2() */
937 : /************************************************************************/
938 :
939 : #if !defined(ARM_V7)
940 :
941 : #ifdef __SSE3__
942 : #define sse2_hadd_ps _mm_hadd_ps
943 : #else
944 82 : inline __m128 sse2_hadd_ps(__m128 a, __m128 b)
945 : {
946 82 : auto aEven_bEven = _mm_shuffle_ps(a, b, _MM_SHUFFLE(2, 0, 2, 0));
947 82 : auto aOdd_bOdd = _mm_shuffle_ps(a, b, _MM_SHUFFLE(3, 1, 3, 1));
948 82 : return _mm_add_ps(aEven_bEven, aOdd_bOdd); // (aEven + aOdd, bEven + bOdd)
949 : }
950 : #endif
951 :
952 : #ifdef __AVX2__
953 : #define set1_ps _mm256_set1_ps
954 : #define loadu_ps _mm256_loadu_ps
955 : #define andnot_ps _mm256_andnot_ps
956 : #define and_ps _mm256_and_ps
957 : #define max_ps _mm256_max_ps
958 : #define shuffle_ps _mm256_shuffle_ps
959 : #define div_ps _mm256_div_ps
960 : #define cmpeq_ps(x, y) _mm256_cmp_ps((x), (y), _CMP_EQ_OQ)
961 : #define mul_ps _mm256_mul_ps
962 : #define add_ps _mm256_add_ps
963 : #define hadd_ps _mm256_hadd_ps
964 : #define sqrt_ps _mm256_sqrt_ps
965 : #define or_ps _mm256_or_ps
966 : #define unpacklo_ps _mm256_unpacklo_ps
967 : #define unpackhi_ps _mm256_unpackhi_ps
968 : #define storeu_ps _mm256_storeu_ps
969 : #define blendv_ps _mm256_blendv_ps
970 :
971 : inline __m256 SQUARE_PS(__m256 x)
972 : {
973 : return _mm256_mul_ps(x, x);
974 : }
975 :
976 : #else
977 :
978 : #define set1_ps _mm_set1_ps
979 : #define loadu_ps _mm_loadu_ps
980 : #define andnot_ps _mm_andnot_ps
981 : #define and_ps _mm_and_ps
982 : #define max_ps _mm_max_ps
983 : #define shuffle_ps _mm_shuffle_ps
984 : #define div_ps _mm_div_ps
985 : #define cmpeq_ps _mm_cmpeq_ps
986 : #define mul_ps _mm_mul_ps
987 : #define add_ps _mm_add_ps
988 : #define hadd_ps sse2_hadd_ps
989 : #define sqrt_ps _mm_sqrt_ps
990 : #define or_ps _mm_or_ps
991 : #define unpacklo_ps _mm_unpacklo_ps
992 : #define unpackhi_ps _mm_unpackhi_ps
993 : #define storeu_ps _mm_storeu_ps
994 :
995 132 : inline __m128 blendv_ps(__m128 a, __m128 b, __m128 mask)
996 : {
997 : #if defined(__SSE4_1__) || defined(__AVX__) || defined(USE_NEON_OPTIMIZATIONS)
998 : return _mm_blendv_ps(a, b, mask);
999 : #else
1000 396 : return _mm_or_ps(_mm_andnot_ps(mask, a), _mm_and_ps(mask, b));
1001 : #endif
1002 : }
1003 :
1004 528 : inline __m128 SQUARE_PS(__m128 x)
1005 : {
1006 528 : return _mm_mul_ps(x, x);
1007 : }
1008 :
1009 132 : inline __m128 FIXUP_LANES(__m128 x)
1010 : {
1011 132 : return x;
1012 : }
1013 :
1014 : #endif
1015 :
1016 : static int
1017 : #if defined(__GNUC__)
1018 : __attribute__((noinline))
1019 : #endif
1020 66 : QuadraticMeanFloatSSE2(int nDstXWidth, int nChunkXSize,
1021 : const float *&CPL_RESTRICT pSrcScanlineShiftedInOut,
1022 : float *CPL_RESTRICT pDstScanline)
1023 : {
1024 : // Optimized implementation for RMS on Float32 by
1025 : // processing by group of output pixels.
1026 66 : const float *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
1027 :
1028 66 : int iDstPixel = 0;
1029 66 : const auto minus_zero = set1_ps(-0.0f);
1030 66 : const auto zeroDot25 = set1_ps(0.25f);
1031 66 : const auto one = set1_ps(1.0f);
1032 66 : const auto infv = set1_ps(std::numeric_limits<float>::infinity());
1033 66 : constexpr int DEST_ELTS = static_cast<int>(sizeof(one) / sizeof(float));
1034 :
1035 198 : for (; iDstPixel < nDstXWidth - (DEST_ELTS - 1); iDstPixel += DEST_ELTS)
1036 : {
1037 : // Load 2*DEST_ELTS Float32 from each line
1038 132 : auto firstLineLo = loadu_ps(pSrcScanlineShifted);
1039 132 : auto firstLineHi = loadu_ps(pSrcScanlineShifted + DEST_ELTS);
1040 132 : auto secondLineLo = loadu_ps(pSrcScanlineShifted + nChunkXSize);
1041 : auto secondLineHi =
1042 264 : loadu_ps(pSrcScanlineShifted + DEST_ELTS + nChunkXSize);
1043 :
1044 : // Take the absolute value
1045 132 : firstLineLo = andnot_ps(minus_zero, firstLineLo);
1046 132 : firstLineHi = andnot_ps(minus_zero, firstLineHi);
1047 132 : secondLineLo = andnot_ps(minus_zero, secondLineLo);
1048 132 : secondLineHi = andnot_ps(minus_zero, secondLineHi);
1049 :
1050 : auto firstLineEven =
1051 132 : shuffle_ps(firstLineLo, firstLineHi, _MM_SHUFFLE(2, 0, 2, 0));
1052 : auto firstLineOdd =
1053 132 : shuffle_ps(firstLineLo, firstLineHi, _MM_SHUFFLE(3, 1, 3, 1));
1054 : auto secondLineEven =
1055 132 : shuffle_ps(secondLineLo, secondLineHi, _MM_SHUFFLE(2, 0, 2, 0));
1056 : auto secondLineOdd =
1057 132 : shuffle_ps(secondLineLo, secondLineHi, _MM_SHUFFLE(3, 1, 3, 1));
1058 :
1059 : // Compute the maximum of each DEST_ELTS value to RMS-average
1060 396 : const auto maxV = max_ps(max_ps(firstLineEven, firstLineOdd),
1061 : max_ps(secondLineEven, secondLineOdd));
1062 :
1063 : // Normalize each value by the maximum of the DEST_ELTS ones.
1064 : // This step is important to avoid that the square evaluates to infinity
1065 : // for sufficiently big input.
1066 132 : auto invMax = div_ps(one, maxV);
1067 : // Deal with 0 being the maximum to correct division by zero
1068 : // note: comparing to -0 leads to identical results as to comparing with
1069 : // 0
1070 264 : invMax = andnot_ps(cmpeq_ps(maxV, minus_zero), invMax);
1071 :
1072 132 : firstLineEven = mul_ps(firstLineEven, invMax);
1073 132 : firstLineOdd = mul_ps(firstLineOdd, invMax);
1074 132 : secondLineEven = mul_ps(secondLineEven, invMax);
1075 132 : secondLineOdd = mul_ps(secondLineOdd, invMax);
1076 :
1077 : // Compute squares
1078 132 : firstLineEven = SQUARE_PS(firstLineEven);
1079 132 : firstLineOdd = SQUARE_PS(firstLineOdd);
1080 132 : secondLineEven = SQUARE_PS(secondLineEven);
1081 132 : secondLineOdd = SQUARE_PS(secondLineOdd);
1082 :
1083 396 : const auto sumSquares = add_ps(add_ps(firstLineEven, firstLineOdd),
1084 : add_ps(secondLineEven, secondLineOdd));
1085 :
1086 396 : auto rms = mul_ps(maxV, sqrt_ps(mul_ps(sumSquares, zeroDot25)));
1087 :
1088 : // Deal with infinity being the maximum
1089 132 : const auto maskIsInf = cmpeq_ps(maxV, infv);
1090 132 : rms = blendv_ps(rms, infv, maskIsInf);
1091 :
1092 132 : rms = FIXUP_LANES(rms);
1093 :
1094 132 : storeu_ps(&pDstScanline[iDstPixel], rms);
1095 132 : pSrcScanlineShifted += DEST_ELTS * 2;
1096 : }
1097 :
1098 66 : pSrcScanlineShiftedInOut = pSrcScanlineShifted;
1099 66 : return iDstPixel;
1100 : }
1101 :
1102 : /************************************************************************/
1103 : /* AverageFloatSSE2() */
1104 : /************************************************************************/
1105 :
1106 50 : static int AverageFloatSSE2(int nDstXWidth, int nChunkXSize,
1107 : const float *&CPL_RESTRICT pSrcScanlineShiftedInOut,
1108 : float *CPL_RESTRICT pDstScanline)
1109 : {
1110 : // Optimized implementation for average on Float32 by
1111 : // processing by group of output pixels.
1112 50 : const float *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
1113 :
1114 50 : int iDstPixel = 0;
1115 50 : const auto zeroDot25 = _mm_set1_ps(0.25f);
1116 50 : constexpr int DEST_ELTS =
1117 : static_cast<int>(sizeof(zeroDot25) / sizeof(float));
1118 :
1119 132 : for (; iDstPixel < nDstXWidth - (DEST_ELTS - 1); iDstPixel += DEST_ELTS)
1120 : {
1121 : // Load 2 * DEST_ELTS Float32 from each line
1122 : const auto firstLineLo =
1123 82 : _mm_mul_ps(_mm_loadu_ps(pSrcScanlineShifted), zeroDot25);
1124 164 : const auto firstLineHi = _mm_mul_ps(
1125 : _mm_loadu_ps(pSrcScanlineShifted + DEST_ELTS), zeroDot25);
1126 82 : const auto secondLineLo = _mm_mul_ps(
1127 82 : _mm_loadu_ps(pSrcScanlineShifted + nChunkXSize), zeroDot25);
1128 164 : const auto secondLineHi = _mm_mul_ps(
1129 82 : _mm_loadu_ps(pSrcScanlineShifted + DEST_ELTS + nChunkXSize),
1130 : zeroDot25);
1131 :
1132 : // Vertical addition
1133 82 : const auto tmpLo = _mm_add_ps(firstLineLo, secondLineLo);
1134 82 : const auto tmpHi = _mm_add_ps(firstLineHi, secondLineHi);
1135 :
1136 : // Horizontal addition
1137 82 : const auto average = sse2_hadd_ps(tmpLo, tmpHi);
1138 :
1139 82 : _mm_storeu_ps(&pDstScanline[iDstPixel], average);
1140 82 : pSrcScanlineShifted += DEST_ELTS * 2;
1141 : }
1142 :
1143 50 : pSrcScanlineShiftedInOut = pSrcScanlineShifted;
1144 50 : return iDstPixel;
1145 : }
1146 :
1147 : /************************************************************************/
1148 : /* AverageDoubleSSE2() */
1149 : /************************************************************************/
1150 :
1151 : static int
1152 50 : AverageDoubleSSE2(int nDstXWidth, int nChunkXSize,
1153 : const double *&CPL_RESTRICT pSrcScanlineShiftedInOut,
1154 : double *CPL_RESTRICT pDstScanline)
1155 : {
1156 : // Optimized implementation for average on Float64 by
1157 : // processing by group of output pixels.
1158 50 : const double *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
1159 :
1160 50 : int iDstPixel = 0;
1161 50 : const auto zeroDot25 = _mm_set1_pd(0.25);
1162 50 : constexpr int DEST_ELTS =
1163 : static_cast<int>(sizeof(zeroDot25) / sizeof(double));
1164 :
1165 211 : for (; iDstPixel < nDstXWidth - (DEST_ELTS - 1); iDstPixel += DEST_ELTS)
1166 : {
1167 : // Load 4 * DEST_ELTS Float64 from each line
1168 161 : const auto firstLine0 = _mm_mul_pd(
1169 : _mm_loadu_pd(pSrcScanlineShifted + 0 * DEST_ELTS), zeroDot25);
1170 322 : const auto firstLine1 = _mm_mul_pd(
1171 : _mm_loadu_pd(pSrcScanlineShifted + 1 * DEST_ELTS), zeroDot25);
1172 161 : const auto secondLine0 = _mm_mul_pd(
1173 161 : _mm_loadu_pd(pSrcScanlineShifted + 0 * DEST_ELTS + nChunkXSize),
1174 : zeroDot25);
1175 322 : const auto secondLine1 = _mm_mul_pd(
1176 161 : _mm_loadu_pd(pSrcScanlineShifted + 1 * DEST_ELTS + nChunkXSize),
1177 : zeroDot25);
1178 :
1179 : // Vertical addition
1180 161 : const auto tmp0 = _mm_add_pd(firstLine0, secondLine0);
1181 161 : const auto tmp1 = _mm_add_pd(firstLine1, secondLine1);
1182 :
1183 : // Horizontal addition
1184 161 : const auto average0 = sse2_hadd_pd(tmp0, tmp1);
1185 :
1186 161 : _mm_storeu_pd(&pDstScanline[iDstPixel + 0], average0);
1187 161 : pSrcScanlineShifted += DEST_ELTS * 2;
1188 : }
1189 :
1190 50 : pSrcScanlineShiftedInOut = pSrcScanlineShifted;
1191 50 : return iDstPixel;
1192 : }
1193 :
1194 : #endif
1195 :
1196 : #endif
1197 :
1198 : /************************************************************************/
1199 : /* GDALResampleChunk_AverageOrRMS() */
1200 : /************************************************************************/
1201 :
1202 : template <class T, class Tsum, GDALDataType eWrkDataType, bool bQuadraticMean>
1203 : static CPLErr
1204 7362 : GDALResampleChunk_AverageOrRMS_T(const GDALOverviewResampleArgs &args,
1205 : const T *pChunk, void **ppDstBuffer)
1206 : {
1207 7362 : const double dfXRatioDstToSrc = args.dfXRatioDstToSrc;
1208 7362 : const double dfYRatioDstToSrc = args.dfYRatioDstToSrc;
1209 7362 : const double dfSrcXDelta = args.dfSrcXDelta;
1210 7362 : const double dfSrcYDelta = args.dfSrcYDelta;
1211 7362 : const GByte *pabyChunkNodataMask = args.pabyChunkNodataMask;
1212 7362 : const int nChunkXOff = args.nChunkXOff;
1213 7362 : const int nChunkYOff = args.nChunkYOff;
1214 7362 : const int nChunkXSize = args.nChunkXSize;
1215 7362 : const int nChunkYSize = args.nChunkYSize;
1216 7362 : const int nDstXOff = args.nDstXOff;
1217 7362 : const int nDstXOff2 = args.nDstXOff2;
1218 7362 : const int nDstYOff = args.nDstYOff;
1219 7362 : const int nDstYOff2 = args.nDstYOff2;
1220 7362 : const char *pszResampling = args.pszResampling;
1221 7362 : bool bHasNoData = args.bHasNoData;
1222 7362 : const double dfNoDataValue = args.dfNoDataValue;
1223 7362 : const GDALColorTable *const poColorTable =
1224 : !bQuadraticMean &&
1225 : // AVERAGE_BIT2GRAYSCALE
1226 7279 : STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2G")
1227 : ? nullptr
1228 : : args.poColorTable;
1229 7362 : const bool bPropagateNoData = args.bPropagateNoData;
1230 :
1231 7362 : T tNoDataValue = (!bHasNoData) ? 0 : static_cast<T>(dfNoDataValue);
1232 7362 : const T tReplacementVal =
1233 206 : bHasNoData ? static_cast<T>(GDALGetNoDataReplacementValue(
1234 72 : args.eOvrDataType, dfNoDataValue))
1235 : : 0;
1236 :
1237 7362 : const int nChunkRightXOff = nChunkXOff + nChunkXSize;
1238 7362 : const int nChunkBottomYOff = nChunkYOff + nChunkYSize;
1239 7362 : const int nDstXWidth = nDstXOff2 - nDstXOff;
1240 :
1241 : /* -------------------------------------------------------------------- */
1242 : /* Allocate buffers. */
1243 : /* -------------------------------------------------------------------- */
1244 7362 : *ppDstBuffer = static_cast<T *>(
1245 7362 : VSI_MALLOC3_VERBOSE(nDstXWidth, nDstYOff2 - nDstYOff,
1246 : GDALGetDataTypeSizeBytes(eWrkDataType)));
1247 7362 : if (*ppDstBuffer == nullptr)
1248 : {
1249 0 : return CE_Failure;
1250 : }
1251 7362 : T *const pDstBuffer = static_cast<T *>(*ppDstBuffer);
1252 :
1253 : struct PrecomputedXValue
1254 : {
1255 : int nLeftXOffShifted;
1256 : int nRightXOffShifted;
1257 : double dfLeftWeight;
1258 : double dfRightWeight;
1259 : double dfTotalWeightFullLine;
1260 : };
1261 :
1262 : PrecomputedXValue *pasSrcX = static_cast<PrecomputedXValue *>(
1263 7362 : VSI_MALLOC2_VERBOSE(nDstXWidth, sizeof(PrecomputedXValue)));
1264 :
1265 7362 : if (pasSrcX == nullptr)
1266 : {
1267 0 : return CE_Failure;
1268 : }
1269 :
1270 7362 : std::vector<GDALColorEntry> colorEntries;
1271 :
1272 7362 : if (poColorTable)
1273 : {
1274 5 : int nTransparentIdx = -1;
1275 5 : colorEntries = ReadColorTable(*poColorTable, nTransparentIdx);
1276 :
1277 : // Force c4 of nodata entry to 0 so that GDALFindBestEntry() identifies
1278 : // it as nodata value
1279 6 : if (bHasNoData && dfNoDataValue >= 0.0 &&
1280 1 : tNoDataValue < colorEntries.size())
1281 1 : colorEntries[static_cast<int>(tNoDataValue)].c4 = 0;
1282 :
1283 : // Or if we have no explicit nodata, but a color table entry that is
1284 : // transparent, consider it as the nodata value
1285 4 : else if (!bHasNoData && nTransparentIdx >= 0)
1286 : {
1287 0 : bHasNoData = true;
1288 0 : tNoDataValue = static_cast<T>(nTransparentIdx);
1289 : }
1290 : }
1291 :
1292 : /* ==================================================================== */
1293 : /* Precompute inner loop constants. */
1294 : /* ==================================================================== */
1295 7362 : bool bSrcXSpacingIsTwo = true;
1296 7362 : int nLastSrcXOff2 = -1;
1297 1689160 : for (int iDstPixel = nDstXOff; iDstPixel < nDstXOff2; ++iDstPixel)
1298 : {
1299 1681805 : const double dfSrcXOff = dfSrcXDelta + iDstPixel * dfXRatioDstToSrc;
1300 : // Apply some epsilon to avoid numerical precision issues
1301 1681805 : const int nSrcXOff =
1302 1681805 : std::max(static_cast<int>(dfSrcXOff + 1e-8), nChunkXOff);
1303 1681805 : const double dfSrcXOff2 =
1304 1681805 : dfSrcXDelta + (iDstPixel + 1) * dfXRatioDstToSrc;
1305 1681805 : int nSrcXOff2 = static_cast<int>(ceil(dfSrcXOff2 - 1e-8));
1306 1681805 : if (nSrcXOff2 == nSrcXOff)
1307 0 : nSrcXOff2++;
1308 1681805 : if (nSrcXOff2 > nChunkRightXOff)
1309 1 : nSrcXOff2 = nChunkRightXOff;
1310 :
1311 1681805 : pasSrcX[iDstPixel - nDstXOff].nLeftXOffShifted = nSrcXOff - nChunkXOff;
1312 1681805 : pasSrcX[iDstPixel - nDstXOff].nRightXOffShifted =
1313 1681805 : nSrcXOff2 - nChunkXOff;
1314 21 : pasSrcX[iDstPixel - nDstXOff].dfLeftWeight =
1315 1681805 : (nSrcXOff2 == nSrcXOff + 1) ? 1.0 : 1 - (dfSrcXOff - nSrcXOff);
1316 1681805 : pasSrcX[iDstPixel - nDstXOff].dfRightWeight =
1317 1681805 : 1 - (nSrcXOff2 - dfSrcXOff2);
1318 1681805 : pasSrcX[iDstPixel - nDstXOff].dfTotalWeightFullLine =
1319 1681805 : pasSrcX[iDstPixel - nDstXOff].dfLeftWeight;
1320 1681805 : if (nSrcXOff + 1 < nSrcXOff2)
1321 : {
1322 1681779 : pasSrcX[iDstPixel - nDstXOff].dfTotalWeightFullLine +=
1323 1681779 : nSrcXOff2 - nSrcXOff - 2;
1324 1681779 : pasSrcX[iDstPixel - nDstXOff].dfTotalWeightFullLine +=
1325 1681779 : pasSrcX[iDstPixel - nDstXOff].dfRightWeight;
1326 : }
1327 :
1328 1681805 : if (nSrcXOff2 - nSrcXOff != 2 ||
1329 1583882 : (nLastSrcXOff2 >= 0 && nLastSrcXOff2 != nSrcXOff))
1330 : {
1331 91989 : bSrcXSpacingIsTwo = false;
1332 : }
1333 1681805 : nLastSrcXOff2 = nSrcXOff2;
1334 : }
1335 :
1336 : /* ==================================================================== */
1337 : /* Loop over destination scanlines. */
1338 : /* ==================================================================== */
1339 705422 : for (int iDstLine = nDstYOff; iDstLine < nDstYOff2; ++iDstLine)
1340 : {
1341 698060 : const double dfSrcYOff = dfSrcYDelta + iDstLine * dfYRatioDstToSrc;
1342 698060 : int nSrcYOff = std::max(static_cast<int>(dfSrcYOff + 1e-8), nChunkYOff);
1343 :
1344 698060 : const double dfSrcYOff2 =
1345 698060 : dfSrcYDelta + (iDstLine + 1) * dfYRatioDstToSrc;
1346 698060 : int nSrcYOff2 = static_cast<int>(ceil(dfSrcYOff2 - 1e-8));
1347 698060 : if (nSrcYOff2 == nSrcYOff)
1348 0 : ++nSrcYOff2;
1349 698060 : if (nSrcYOff2 > nChunkBottomYOff)
1350 3 : nSrcYOff2 = nChunkBottomYOff;
1351 :
1352 698060 : T *const pDstScanline =
1353 698060 : pDstBuffer + static_cast<size_t>(iDstLine - nDstYOff) * nDstXWidth;
1354 :
1355 : /* --------------------------------------------------------------------
1356 : */
1357 : /* Loop over destination pixels */
1358 : /* --------------------------------------------------------------------
1359 : */
1360 698060 : if (poColorTable == nullptr)
1361 : {
1362 697945 : if (bSrcXSpacingIsTwo && nSrcYOff2 == nSrcYOff + 2 &&
1363 : pabyChunkNodataMask == nullptr)
1364 : {
1365 : if constexpr (eWrkDataType == GDT_UInt8 ||
1366 : eWrkDataType == GDT_UInt16)
1367 : {
1368 : // Optimized case : no nodata, overview by a factor of 2 and
1369 : // regular x and y src spacing.
1370 129392 : const T *pSrcScanlineShifted =
1371 129392 : pChunk + pasSrcX[0].nLeftXOffShifted +
1372 129392 : static_cast<size_t>(nSrcYOff - nChunkYOff) *
1373 129392 : nChunkXSize;
1374 129392 : int iDstPixel = 0;
1375 : #ifdef USE_SSE2
1376 : if constexpr (eWrkDataType == GDT_UInt8)
1377 : {
1378 : if constexpr (bQuadraticMean)
1379 : {
1380 5389 : iDstPixel = QuadraticMeanByteSSE2OrAVX2(
1381 : nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1382 : pDstScanline);
1383 : }
1384 : else
1385 : {
1386 123976 : iDstPixel = AverageByteSSE2OrAVX2(
1387 : nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1388 : pDstScanline);
1389 : }
1390 : }
1391 : else
1392 : {
1393 : static_assert(eWrkDataType == GDT_UInt16);
1394 : if constexpr (bQuadraticMean)
1395 : {
1396 14 : iDstPixel = QuadraticMeanUInt16SSE2(
1397 : nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1398 : pDstScanline);
1399 : }
1400 : else
1401 : {
1402 13 : iDstPixel = AverageUInt16SSE2(
1403 : nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1404 : pDstScanline);
1405 : }
1406 : }
1407 : #endif
1408 303851 : for (; iDstPixel < nDstXWidth; ++iDstPixel)
1409 : {
1410 174459 : Tsum nTotal = 0;
1411 : T nVal;
1412 : if constexpr (bQuadraticMean)
1413 52 : nTotal =
1414 52 : SQUARE<Tsum>(pSrcScanlineShifted[0]) +
1415 52 : SQUARE<Tsum>(pSrcScanlineShifted[1]) +
1416 52 : SQUARE<Tsum>(pSrcScanlineShifted[nChunkXSize]) +
1417 52 : SQUARE<Tsum>(
1418 52 : pSrcScanlineShifted[1 + nChunkXSize]);
1419 : else
1420 174407 : nTotal = pSrcScanlineShifted[0] +
1421 174407 : pSrcScanlineShifted[1] +
1422 174407 : pSrcScanlineShifted[nChunkXSize] +
1423 174407 : pSrcScanlineShifted[1 + nChunkXSize];
1424 :
1425 174459 : constexpr int nTotalWeight = 4;
1426 : if constexpr (bQuadraticMean)
1427 52 : nVal = ComputeIntegerRMS_4values<T>(nTotal);
1428 : else
1429 174407 : nVal = static_cast<T>((nTotal + nTotalWeight / 2) /
1430 : nTotalWeight);
1431 :
1432 : // No need to compare nVal against tNoDataValue as we
1433 : // are in a case where pabyChunkNodataMask == nullptr
1434 : // implies the absence of nodata value.
1435 174459 : pDstScanline[iDstPixel] = nVal;
1436 174459 : pSrcScanlineShifted += 2;
1437 : }
1438 : }
1439 : else
1440 : {
1441 : static_assert(eWrkDataType == GDT_Float32 ||
1442 : eWrkDataType == GDT_Float64);
1443 202 : const T *pSrcScanlineShifted =
1444 202 : pChunk + pasSrcX[0].nLeftXOffShifted +
1445 202 : static_cast<size_t>(nSrcYOff - nChunkYOff) *
1446 202 : nChunkXSize;
1447 202 : int iDstPixel = 0;
1448 : #if defined(USE_SSE2) && !defined(ARM_V7)
1449 : if constexpr (eWrkDataType == GDT_Float32)
1450 : {
1451 : static_assert(std::is_same_v<T, float>);
1452 : if constexpr (bQuadraticMean)
1453 : {
1454 66 : iDstPixel = QuadraticMeanFloatSSE2(
1455 : nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1456 : pDstScanline);
1457 : }
1458 : else
1459 : {
1460 50 : iDstPixel = AverageFloatSSE2(
1461 : nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1462 : pDstScanline);
1463 : }
1464 : }
1465 : else
1466 : {
1467 : if constexpr (!bQuadraticMean)
1468 : {
1469 50 : iDstPixel = AverageDoubleSSE2(
1470 : nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1471 : pDstScanline);
1472 : }
1473 : }
1474 : #endif
1475 :
1476 726 : for (; iDstPixel < nDstXWidth; ++iDstPixel)
1477 : {
1478 : T nVal;
1479 :
1480 : if constexpr (bQuadraticMean)
1481 : {
1482 : // Avoid issues with large values by renormalizing
1483 96 : const auto max = std::max(
1484 420 : {std::fabs(pSrcScanlineShifted[0]),
1485 420 : std::fabs(pSrcScanlineShifted[1]),
1486 420 : std::fabs(pSrcScanlineShifted[nChunkXSize]),
1487 420 : std::fabs(
1488 420 : pSrcScanlineShifted[1 + nChunkXSize])});
1489 420 : if (max == 0)
1490 : {
1491 8 : nVal = 0;
1492 : }
1493 412 : else if (std::isinf(max))
1494 : {
1495 : // If there is at least one infinity value,
1496 : // then just summing, and taking the abs
1497 : // value will give the expected result:
1498 : // * +inf if all values are +inf
1499 : // * +inf if all values are -inf
1500 : // * NaN otherwise
1501 82 : nVal = std::fabs(
1502 82 : pSrcScanlineShifted[0] +
1503 82 : pSrcScanlineShifted[1] +
1504 82 : pSrcScanlineShifted[nChunkXSize] +
1505 82 : pSrcScanlineShifted[1 + nChunkXSize]);
1506 : }
1507 : else
1508 : {
1509 330 : const auto inv_max = static_cast<T>(1.0) / max;
1510 330 : nVal =
1511 : max *
1512 330 : std::sqrt(
1513 : static_cast<T>(0.25) *
1514 330 : (SQUARE(pSrcScanlineShifted[0] *
1515 330 : inv_max) +
1516 330 : SQUARE(pSrcScanlineShifted[1] *
1517 330 : inv_max) +
1518 330 : SQUARE(
1519 330 : pSrcScanlineShifted[nChunkXSize] *
1520 330 : inv_max) +
1521 330 : SQUARE(
1522 330 : pSrcScanlineShifted[1 +
1523 : nChunkXSize] *
1524 : inv_max)));
1525 : }
1526 : }
1527 : else
1528 : {
1529 104 : constexpr auto weight = static_cast<T>(0.25);
1530 : // Multiply each value by weight to avoid
1531 : // potential overflow
1532 104 : nVal =
1533 104 : (weight * pSrcScanlineShifted[0] +
1534 104 : weight * pSrcScanlineShifted[1] +
1535 104 : weight * pSrcScanlineShifted[nChunkXSize] +
1536 104 : weight * pSrcScanlineShifted[1 + nChunkXSize]);
1537 : }
1538 :
1539 : // No need to compare nVal against tNoDataValue as we
1540 : // are in a case where pabyChunkNodataMask == nullptr
1541 : // implies the absence of nodata value.
1542 524 : pDstScanline[iDstPixel] = nVal;
1543 524 : pSrcScanlineShifted += 2;
1544 : }
1545 129594 : }
1546 : }
1547 : else
1548 : {
1549 17 : const double dfBottomWeight =
1550 568351 : (nSrcYOff + 1 == nSrcYOff2) ? 1.0
1551 568334 : : 1.0 - (dfSrcYOff - nSrcYOff);
1552 568351 : const double dfTopWeight = 1.0 - (nSrcYOff2 - dfSrcYOff2);
1553 568351 : nSrcYOff -= nChunkYOff;
1554 568351 : nSrcYOff2 -= nChunkYOff;
1555 :
1556 568351 : double dfTotalWeightFullColumn = dfBottomWeight;
1557 568351 : if (nSrcYOff + 1 < nSrcYOff2)
1558 : {
1559 568334 : dfTotalWeightFullColumn += nSrcYOff2 - nSrcYOff - 2;
1560 568334 : dfTotalWeightFullColumn += dfTopWeight;
1561 : }
1562 :
1563 9784185 : for (int iDstPixel = 0; iDstPixel < nDstXWidth; ++iDstPixel)
1564 : {
1565 9215839 : const int nSrcXOff = pasSrcX[iDstPixel].nLeftXOffShifted;
1566 9215839 : const int nSrcXOff2 = pasSrcX[iDstPixel].nRightXOffShifted;
1567 :
1568 9215839 : double dfTotal = 0;
1569 9215839 : double dfTotalWeight = 0;
1570 9215839 : [[maybe_unused]] double dfMulFactor = 1.0;
1571 9215839 : [[maybe_unused]] double dfInvMulFactor = 1.0;
1572 9215839 : constexpr bool bUseMulFactor =
1573 : (eWrkDataType == GDT_Float32 ||
1574 : eWrkDataType == GDT_Float64);
1575 9215839 : if (pabyChunkNodataMask == nullptr)
1576 : {
1577 : if constexpr (bUseMulFactor)
1578 : {
1579 : if constexpr (bQuadraticMean)
1580 : {
1581 80 : T mulFactor = 0;
1582 80 : auto pChunkShifted =
1583 80 : pChunk +
1584 80 : static_cast<size_t>(nSrcYOff) * nChunkXSize;
1585 :
1586 240 : for (int iY = nSrcYOff; iY < nSrcYOff2;
1587 160 : ++iY, pChunkShifted += nChunkXSize)
1588 : {
1589 480 : for (int iX = nSrcXOff; iX < nSrcXOff2;
1590 : ++iX)
1591 640 : mulFactor = std::max(
1592 : mulFactor,
1593 320 : std::fabs(pChunkShifted[iX]));
1594 : }
1595 80 : dfMulFactor = double(mulFactor);
1596 142 : dfInvMulFactor =
1597 62 : dfMulFactor > 0 &&
1598 62 : std::isfinite(dfMulFactor)
1599 : ? 1.0 / dfMulFactor
1600 : : 1.0;
1601 : }
1602 : else
1603 : {
1604 139 : dfMulFactor = (nSrcYOff2 - nSrcYOff) *
1605 139 : (nSrcXOff2 - nSrcXOff);
1606 139 : dfInvMulFactor = 1.0 / dfMulFactor;
1607 : }
1608 : }
1609 :
1610 1746545 : auto pChunkShifted =
1611 227 : pChunk +
1612 1746545 : static_cast<size_t>(nSrcYOff) * nChunkXSize;
1613 1746545 : int nCounterY = nSrcYOff2 - nSrcYOff - 1;
1614 1746545 : double dfWeightY = dfBottomWeight;
1615 3493539 : while (true)
1616 : {
1617 : double dfTotalLine;
1618 : if constexpr (bQuadraticMean)
1619 : {
1620 : // Left pixel
1621 : {
1622 216 : const T val = pChunkShifted[nSrcXOff];
1623 216 : dfTotalLine =
1624 216 : SQUARE(double(val) * dfInvMulFactor) *
1625 216 : pasSrcX[iDstPixel].dfLeftWeight;
1626 : }
1627 :
1628 216 : if (nSrcXOff + 1 < nSrcXOff2)
1629 : {
1630 : // Middle pixels
1631 216 : for (int iX = nSrcXOff + 1;
1632 536 : iX < nSrcXOff2 - 1; ++iX)
1633 : {
1634 320 : const T val = pChunkShifted[iX];
1635 320 : dfTotalLine += SQUARE(double(val) *
1636 : dfInvMulFactor);
1637 : }
1638 :
1639 : // Right pixel
1640 : {
1641 216 : const T val =
1642 216 : pChunkShifted[nSrcXOff2 - 1];
1643 216 : dfTotalLine +=
1644 216 : SQUARE(double(val) *
1645 216 : dfInvMulFactor) *
1646 216 : pasSrcX[iDstPixel].dfRightWeight;
1647 : }
1648 : }
1649 : }
1650 : else
1651 : {
1652 : // Left pixel
1653 : {
1654 5239868 : const T val = pChunkShifted[nSrcXOff];
1655 5239868 : dfTotalLine =
1656 5239868 : double(val) * dfInvMulFactor *
1657 5239868 : pasSrcX[iDstPixel].dfLeftWeight;
1658 : }
1659 :
1660 5239868 : if (nSrcXOff + 1 < nSrcXOff2)
1661 : {
1662 : // Middle pixels
1663 4239442 : for (int iX = nSrcXOff + 1;
1664 64183238 : iX < nSrcXOff2 - 1; ++iX)
1665 : {
1666 59943836 : const T val = pChunkShifted[iX];
1667 59943836 : dfTotalLine +=
1668 59943836 : double(val) * dfInvMulFactor;
1669 : }
1670 :
1671 : // Right pixel
1672 : {
1673 4239442 : const T val =
1674 4239442 : pChunkShifted[nSrcXOff2 - 1];
1675 4239442 : dfTotalLine +=
1676 4239442 : double(val) * dfInvMulFactor *
1677 4239442 : pasSrcX[iDstPixel].dfRightWeight;
1678 : }
1679 : }
1680 : }
1681 :
1682 5240084 : dfTotal += dfTotalLine * dfWeightY;
1683 5240084 : --nCounterY;
1684 5240084 : if (nCounterY < 0)
1685 1746545 : break;
1686 3493539 : pChunkShifted += nChunkXSize;
1687 3493539 : dfWeightY = (nCounterY == 0) ? dfTopWeight : 1.0;
1688 : }
1689 :
1690 1746545 : dfTotalWeight =
1691 1746545 : pasSrcX[iDstPixel].dfTotalWeightFullLine *
1692 : dfTotalWeightFullColumn;
1693 : }
1694 : else
1695 : {
1696 7469294 : size_t nCount = 0;
1697 30285576 : for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
1698 : {
1699 22816292 : const auto pChunkShifted =
1700 22816292 : pChunk + static_cast<size_t>(iY) * nChunkXSize;
1701 :
1702 22816292 : double dfTotalLine = 0;
1703 22816292 : double dfTotalWeightLine = 0;
1704 : // Left pixel
1705 : {
1706 22816292 : const int iX = nSrcXOff;
1707 22816292 : const T val = pChunkShifted[iX];
1708 22816292 : if (pabyChunkNodataMask
1709 22816292 : [iX +
1710 22816292 : static_cast<size_t>(iY) * nChunkXSize])
1711 : {
1712 17325139 : nCount++;
1713 17325139 : const double dfWeightX =
1714 17325139 : pasSrcX[iDstPixel].dfLeftWeight;
1715 17325139 : dfTotalWeightLine = dfWeightX;
1716 : if constexpr (bQuadraticMean)
1717 508 : dfTotalLine =
1718 508 : SQUARE(double(val)) * dfWeightX;
1719 : else
1720 17324631 : dfTotalLine = double(val) * dfWeightX;
1721 : }
1722 : }
1723 :
1724 22816292 : if (nSrcXOff < nSrcXOff2 - 1)
1725 : {
1726 : // Middle pixels
1727 61618372 : for (int iX = nSrcXOff + 1; iX < nSrcXOff2 - 1;
1728 : ++iX)
1729 : {
1730 38802080 : const T val = pChunkShifted[iX];
1731 38802080 : if (pabyChunkNodataMask
1732 38802080 : [iX + static_cast<size_t>(iY) *
1733 38802080 : nChunkXSize])
1734 : {
1735 28038780 : nCount++;
1736 28038780 : dfTotalWeightLine += 1;
1737 : if constexpr (bQuadraticMean)
1738 640 : dfTotalLine += SQUARE(double(val));
1739 : else
1740 28038140 : dfTotalLine += double(val);
1741 : }
1742 : }
1743 :
1744 : // Right pixel
1745 : {
1746 22816292 : const int iX = nSrcXOff2 - 1;
1747 22816292 : const T val = pChunkShifted[iX];
1748 22816292 : if (pabyChunkNodataMask
1749 22816292 : [iX + static_cast<size_t>(iY) *
1750 22816292 : nChunkXSize])
1751 : {
1752 17324495 : nCount++;
1753 17324495 : const double dfWeightX =
1754 17324495 : pasSrcX[iDstPixel].dfRightWeight;
1755 17324495 : dfTotalWeightLine += dfWeightX;
1756 : if constexpr (bQuadraticMean)
1757 503 : dfTotalLine +=
1758 503 : SQUARE(double(val)) * dfWeightX;
1759 : else
1760 17323992 : dfTotalLine +=
1761 17323992 : double(val) * dfWeightX;
1762 : }
1763 : }
1764 : }
1765 :
1766 38163300 : const double dfWeightY =
1767 : (iY == nSrcYOff) ? dfBottomWeight
1768 15347008 : : (iY + 1 == nSrcYOff2) ? dfTopWeight
1769 : : 1.0;
1770 22816292 : dfTotal += dfTotalLine * dfWeightY;
1771 22816292 : dfTotalWeight += dfTotalWeightLine * dfWeightY;
1772 : }
1773 :
1774 7469294 : if (nCount == 0 ||
1775 8 : (bPropagateNoData &&
1776 : nCount <
1777 8 : static_cast<size_t>(nSrcYOff2 - nSrcYOff) *
1778 8 : (nSrcXOff2 - nSrcXOff)))
1779 : {
1780 2307682 : pDstScanline[iDstPixel] = tNoDataValue;
1781 2307682 : continue;
1782 : }
1783 : }
1784 : if constexpr (eWrkDataType == GDT_UInt8)
1785 : {
1786 : T nVal;
1787 : if constexpr (bQuadraticMean)
1788 38 : nVal = ComputeIntegerRMS<T, int>(dfTotal,
1789 : dfTotalWeight);
1790 : else
1791 6901260 : nVal =
1792 6901260 : static_cast<T>(dfTotal / dfTotalWeight + 0.5);
1793 6901298 : if (bHasNoData && nVal == tNoDataValue)
1794 0 : nVal = tReplacementVal;
1795 6901298 : pDstScanline[iDstPixel] = nVal;
1796 : }
1797 : else if constexpr (eWrkDataType == GDT_UInt16)
1798 : {
1799 : T nVal;
1800 : if constexpr (bQuadraticMean)
1801 4 : nVal = ComputeIntegerRMS<T, uint64_t>(
1802 : dfTotal, dfTotalWeight);
1803 : else
1804 4 : nVal =
1805 4 : static_cast<T>(dfTotal / dfTotalWeight + 0.5);
1806 8 : if (bHasNoData && nVal == tNoDataValue)
1807 0 : nVal = tReplacementVal;
1808 8 : pDstScanline[iDstPixel] = nVal;
1809 : }
1810 : else
1811 : {
1812 : T nVal;
1813 : if constexpr (bQuadraticMean)
1814 : {
1815 : if constexpr (bUseMulFactor)
1816 249 : nVal = static_cast<T>(
1817 132 : dfMulFactor *
1818 249 : sqrt(dfTotal / dfTotalWeight));
1819 : else
1820 : nVal = static_cast<T>(
1821 : sqrt(dfTotal / dfTotalWeight));
1822 : }
1823 : else
1824 : {
1825 : if constexpr (bUseMulFactor)
1826 6602 : nVal = static_cast<T>(
1827 6602 : dfMulFactor * (dfTotal / dfTotalWeight));
1828 : else
1829 : nVal = static_cast<T>(dfTotal / dfTotalWeight);
1830 : }
1831 6851 : if (bHasNoData && nVal == tNoDataValue)
1832 2 : nVal = tReplacementVal;
1833 6851 : pDstScanline[iDstPixel] = nVal;
1834 : }
1835 : }
1836 : }
1837 : }
1838 : else
1839 : {
1840 115 : nSrcYOff -= nChunkYOff;
1841 115 : nSrcYOff2 -= nChunkYOff;
1842 :
1843 6590 : for (int iDstPixel = 0; iDstPixel < nDstXWidth; ++iDstPixel)
1844 : {
1845 6475 : const int nSrcXOff = pasSrcX[iDstPixel].nLeftXOffShifted;
1846 6475 : const int nSrcXOff2 = pasSrcX[iDstPixel].nRightXOffShifted;
1847 :
1848 6475 : uint64_t nTotalR = 0;
1849 6475 : uint64_t nTotalG = 0;
1850 6475 : uint64_t nTotalB = 0;
1851 6475 : size_t nCount = 0;
1852 :
1853 19425 : for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
1854 : {
1855 38850 : for (int iX = nSrcXOff; iX < nSrcXOff2; ++iX)
1856 : {
1857 25900 : const T val =
1858 25900 : pChunk[iX + static_cast<size_t>(iY) * nChunkXSize];
1859 : // cppcheck-suppress unsignedLessThanZero
1860 25900 : if (val < 0 || val >= colorEntries.size())
1861 0 : continue;
1862 25900 : const size_t idx = static_cast<size_t>(val);
1863 25900 : const auto &entry = colorEntries[idx];
1864 25900 : if (entry.c4)
1865 : {
1866 : if constexpr (bQuadraticMean)
1867 : {
1868 800 : nTotalR += SQUARE<int>(entry.c1);
1869 800 : nTotalG += SQUARE<int>(entry.c2);
1870 800 : nTotalB += SQUARE<int>(entry.c3);
1871 800 : ++nCount;
1872 : }
1873 : else
1874 : {
1875 13328 : nTotalR += entry.c1;
1876 13328 : nTotalG += entry.c2;
1877 13328 : nTotalB += entry.c3;
1878 13328 : ++nCount;
1879 : }
1880 : }
1881 : }
1882 : }
1883 :
1884 6475 : if (nCount == 0 ||
1885 0 : (bPropagateNoData &&
1886 0 : nCount < static_cast<size_t>(nSrcYOff2 - nSrcYOff) *
1887 0 : (nSrcXOff2 - nSrcXOff)))
1888 : {
1889 2838 : pDstScanline[iDstPixel] = tNoDataValue;
1890 : }
1891 : else
1892 : {
1893 : GDALColorEntry color;
1894 : if constexpr (bQuadraticMean)
1895 : {
1896 200 : color.c1 =
1897 200 : static_cast<short>(sqrt(nTotalR / nCount) + 0.5);
1898 200 : color.c2 =
1899 200 : static_cast<short>(sqrt(nTotalG / nCount) + 0.5);
1900 200 : color.c3 =
1901 200 : static_cast<short>(sqrt(nTotalB / nCount) + 0.5);
1902 : }
1903 : else
1904 : {
1905 3437 : color.c1 =
1906 3437 : static_cast<short>((nTotalR + nCount / 2) / nCount);
1907 3437 : color.c2 =
1908 3437 : static_cast<short>((nTotalG + nCount / 2) / nCount);
1909 3437 : color.c3 =
1910 3437 : static_cast<short>((nTotalB + nCount / 2) / nCount);
1911 : }
1912 3637 : pDstScanline[iDstPixel] =
1913 3637 : static_cast<T>(BestColorEntry(colorEntries, color));
1914 : }
1915 : }
1916 : }
1917 : }
1918 :
1919 7362 : CPLFree(pasSrcX);
1920 :
1921 7362 : return CE_None;
1922 : }
1923 :
1924 : template <bool bQuadraticMean>
1925 : static CPLErr
1926 7362 : GDALResampleChunk_AverageOrRMSInternal(const GDALOverviewResampleArgs &args,
1927 : const void *pChunk, void **ppDstBuffer,
1928 : GDALDataType *peDstBufferDataType)
1929 : {
1930 7362 : *peDstBufferDataType = args.eWrkDataType;
1931 7362 : switch (args.eWrkDataType)
1932 : {
1933 7217 : case GDT_UInt8:
1934 : {
1935 : return GDALResampleChunk_AverageOrRMS_T<GByte, int, GDT_UInt8,
1936 7217 : bQuadraticMean>(
1937 7217 : args, static_cast<const GByte *>(pChunk), ppDstBuffer);
1938 : }
1939 :
1940 11 : case GDT_UInt16:
1941 : {
1942 : if constexpr (bQuadraticMean)
1943 : {
1944 : // Use double as accumulation type, because UInt32 could overflow
1945 : return GDALResampleChunk_AverageOrRMS_T<
1946 6 : GUInt16, double, GDT_UInt16, bQuadraticMean>(
1947 6 : args, static_cast<const GUInt16 *>(pChunk), ppDstBuffer);
1948 : }
1949 : else
1950 : {
1951 : return GDALResampleChunk_AverageOrRMS_T<
1952 5 : GUInt16, GUInt32, GDT_UInt16, bQuadraticMean>(
1953 5 : args, static_cast<const GUInt16 *>(pChunk), ppDstBuffer);
1954 : }
1955 : }
1956 :
1957 81 : case GDT_Float32:
1958 : {
1959 : return GDALResampleChunk_AverageOrRMS_T<float, double, GDT_Float32,
1960 81 : bQuadraticMean>(
1961 81 : args, static_cast<const float *>(pChunk), ppDstBuffer);
1962 : }
1963 :
1964 53 : case GDT_Float64:
1965 : {
1966 : return GDALResampleChunk_AverageOrRMS_T<double, double, GDT_Float64,
1967 53 : bQuadraticMean>(
1968 53 : args, static_cast<const double *>(pChunk), ppDstBuffer);
1969 : }
1970 :
1971 0 : default:
1972 0 : break;
1973 : }
1974 :
1975 0 : CPLAssert(false);
1976 : return CE_Failure;
1977 : }
1978 :
1979 : static CPLErr
1980 7362 : GDALResampleChunk_AverageOrRMS(const GDALOverviewResampleArgs &args,
1981 : const void *pChunk, void **ppDstBuffer,
1982 : GDALDataType *peDstBufferDataType)
1983 : {
1984 7362 : if (EQUAL(args.pszResampling, "RMS"))
1985 83 : return GDALResampleChunk_AverageOrRMSInternal<true>(
1986 83 : args, pChunk, ppDstBuffer, peDstBufferDataType);
1987 : else
1988 7279 : return GDALResampleChunk_AverageOrRMSInternal<false>(
1989 7279 : args, pChunk, ppDstBuffer, peDstBufferDataType);
1990 : }
1991 :
1992 : /************************************************************************/
1993 : /* GDALResampleChunk_Gauss() */
1994 : /************************************************************************/
1995 :
1996 86 : static CPLErr GDALResampleChunk_Gauss(const GDALOverviewResampleArgs &args,
1997 : const void *pChunk, void **ppDstBuffer,
1998 : GDALDataType *peDstBufferDataType)
1999 :
2000 : {
2001 86 : const double dfXRatioDstToSrc = args.dfXRatioDstToSrc;
2002 86 : const double dfYRatioDstToSrc = args.dfYRatioDstToSrc;
2003 86 : const GByte *pabyChunkNodataMask = args.pabyChunkNodataMask;
2004 86 : const int nChunkXOff = args.nChunkXOff;
2005 86 : const int nChunkXSize = args.nChunkXSize;
2006 86 : const int nChunkYOff = args.nChunkYOff;
2007 86 : const int nChunkYSize = args.nChunkYSize;
2008 86 : const int nDstXOff = args.nDstXOff;
2009 86 : const int nDstXOff2 = args.nDstXOff2;
2010 86 : const int nDstYOff = args.nDstYOff;
2011 86 : const int nDstYOff2 = args.nDstYOff2;
2012 86 : const bool bHasNoData = args.bHasNoData;
2013 86 : double dfNoDataValue = args.dfNoDataValue;
2014 86 : const GDALColorTable *poColorTable = args.poColorTable;
2015 :
2016 86 : const double *const padfChunk = static_cast<const double *>(pChunk);
2017 :
2018 86 : *ppDstBuffer =
2019 86 : VSI_MALLOC3_VERBOSE(nDstXOff2 - nDstXOff, nDstYOff2 - nDstYOff,
2020 : GDALGetDataTypeSizeBytes(GDT_Float64));
2021 86 : if (*ppDstBuffer == nullptr)
2022 : {
2023 0 : return CE_Failure;
2024 : }
2025 86 : *peDstBufferDataType = GDT_Float64;
2026 86 : double *const padfDstBuffer = static_cast<double *>(*ppDstBuffer);
2027 :
2028 : /* -------------------------------------------------------------------- */
2029 : /* Create the filter kernel and allocate scanline buffer. */
2030 : /* -------------------------------------------------------------------- */
2031 86 : int nGaussMatrixDim = 3;
2032 : const int *panGaussMatrix;
2033 86 : constexpr int anGaussMatrix3x3[] = {1, 2, 1, 2, 4, 2, 1, 2, 1};
2034 86 : constexpr int anGaussMatrix5x5[] = {1, 4, 6, 4, 1, 4, 16, 24, 16,
2035 : 4, 6, 24, 36, 24, 6, 4, 16, 24,
2036 : 16, 4, 1, 4, 6, 4, 1};
2037 86 : constexpr int anGaussMatrix7x7[] = {
2038 : 1, 6, 15, 20, 15, 6, 1, 6, 36, 90, 120, 90, 36,
2039 : 6, 15, 90, 225, 300, 225, 90, 15, 20, 120, 300, 400, 300,
2040 : 120, 20, 15, 90, 225, 300, 225, 90, 15, 6, 36, 90, 120,
2041 : 90, 36, 6, 1, 6, 15, 20, 15, 6, 1};
2042 :
2043 86 : const int nOXSize = args.nOvrXSize;
2044 86 : const int nOYSize = args.nOvrYSize;
2045 86 : const int nResYFactor = static_cast<int>(0.5 + dfYRatioDstToSrc);
2046 :
2047 : // matrix for gauss filter
2048 86 : if (nResYFactor <= 2)
2049 : {
2050 85 : panGaussMatrix = anGaussMatrix3x3;
2051 85 : nGaussMatrixDim = 3;
2052 : }
2053 1 : else if (nResYFactor <= 4)
2054 : {
2055 0 : panGaussMatrix = anGaussMatrix5x5;
2056 0 : nGaussMatrixDim = 5;
2057 : }
2058 : else
2059 : {
2060 1 : panGaussMatrix = anGaussMatrix7x7;
2061 1 : nGaussMatrixDim = 7;
2062 : }
2063 :
2064 : #ifdef DEBUG_OUT_OF_BOUND_ACCESS
2065 : int *panGaussMatrixDup = static_cast<int *>(
2066 : CPLMalloc(sizeof(int) * nGaussMatrixDim * nGaussMatrixDim));
2067 : memcpy(panGaussMatrixDup, panGaussMatrix,
2068 : sizeof(int) * nGaussMatrixDim * nGaussMatrixDim);
2069 : panGaussMatrix = panGaussMatrixDup;
2070 : #endif
2071 :
2072 86 : if (!bHasNoData)
2073 79 : dfNoDataValue = 0.0;
2074 :
2075 86 : std::vector<GDALColorEntry> colorEntries;
2076 86 : int nTransparentIdx = -1;
2077 86 : if (poColorTable)
2078 2 : colorEntries = ReadColorTable(*poColorTable, nTransparentIdx);
2079 :
2080 : // Force c4 of nodata entry to 0 so that GDALFindBestEntry() identifies
2081 : // it as nodata value.
2082 92 : if (bHasNoData && dfNoDataValue >= 0.0 &&
2083 6 : dfNoDataValue < colorEntries.size())
2084 0 : colorEntries[static_cast<int>(dfNoDataValue)].c4 = 0;
2085 :
2086 : // Or if we have no explicit nodata, but a color table entry that is
2087 : // transparent, consider it as the nodata value.
2088 86 : else if (!bHasNoData && nTransparentIdx >= 0)
2089 : {
2090 0 : dfNoDataValue = nTransparentIdx;
2091 : }
2092 :
2093 86 : const int nChunkRightXOff = nChunkXOff + nChunkXSize;
2094 86 : const int nChunkBottomYOff = nChunkYOff + nChunkYSize;
2095 86 : const int nDstXWidth = nDstXOff2 - nDstXOff;
2096 :
2097 : /* ==================================================================== */
2098 : /* Loop over destination scanlines. */
2099 : /* ==================================================================== */
2100 16488 : for (int iDstLine = nDstYOff; iDstLine < nDstYOff2; ++iDstLine)
2101 : {
2102 16402 : int nSrcYOff = static_cast<int>(0.5 + iDstLine * dfYRatioDstToSrc);
2103 16402 : int nSrcYOff2 =
2104 16402 : static_cast<int>(0.5 + (iDstLine + 1) * dfYRatioDstToSrc) + 1;
2105 :
2106 16402 : if (nSrcYOff < nChunkYOff)
2107 : {
2108 0 : nSrcYOff = nChunkYOff;
2109 0 : nSrcYOff2++;
2110 : }
2111 :
2112 16402 : const int iSizeY = nSrcYOff2 - nSrcYOff;
2113 16402 : nSrcYOff = nSrcYOff + iSizeY / 2 - nGaussMatrixDim / 2;
2114 16402 : nSrcYOff2 = nSrcYOff + nGaussMatrixDim;
2115 :
2116 16402 : if (nSrcYOff2 > nChunkBottomYOff ||
2117 16359 : (dfYRatioDstToSrc > 1 && iDstLine == nOYSize - 1))
2118 : {
2119 44 : nSrcYOff2 = std::min(nChunkBottomYOff, nSrcYOff + nGaussMatrixDim);
2120 : }
2121 :
2122 16402 : int nYShiftGaussMatrix = 0;
2123 16402 : if (nSrcYOff < nChunkYOff)
2124 : {
2125 0 : nYShiftGaussMatrix = -(nSrcYOff - nChunkYOff);
2126 0 : nSrcYOff = nChunkYOff;
2127 : }
2128 :
2129 16402 : const double *const padfSrcScanline =
2130 16402 : padfChunk + ((nSrcYOff - nChunkYOff) * nChunkXSize);
2131 16402 : const GByte *pabySrcScanlineNodataMask = nullptr;
2132 16402 : if (pabyChunkNodataMask != nullptr)
2133 152 : pabySrcScanlineNodataMask =
2134 152 : pabyChunkNodataMask + ((nSrcYOff - nChunkYOff) * nChunkXSize);
2135 :
2136 : /* --------------------------------------------------------------------
2137 : */
2138 : /* Loop over destination pixels */
2139 : /* --------------------------------------------------------------------
2140 : */
2141 16402 : double *const padfDstScanline =
2142 16402 : padfDstBuffer + (iDstLine - nDstYOff) * nDstXWidth;
2143 4149980 : for (int iDstPixel = nDstXOff; iDstPixel < nDstXOff2; ++iDstPixel)
2144 : {
2145 4133580 : int nSrcXOff = static_cast<int>(0.5 + iDstPixel * dfXRatioDstToSrc);
2146 4133580 : int nSrcXOff2 =
2147 4133580 : static_cast<int>(0.5 + (iDstPixel + 1) * dfXRatioDstToSrc) + 1;
2148 :
2149 4133580 : if (nSrcXOff < nChunkXOff)
2150 : {
2151 0 : nSrcXOff = nChunkXOff;
2152 0 : nSrcXOff2++;
2153 : }
2154 :
2155 4133580 : const int iSizeX = nSrcXOff2 - nSrcXOff;
2156 4133580 : nSrcXOff = nSrcXOff + iSizeX / 2 - nGaussMatrixDim / 2;
2157 4133580 : nSrcXOff2 = nSrcXOff + nGaussMatrixDim;
2158 :
2159 4133580 : if (nSrcXOff2 > nChunkRightXOff ||
2160 4127930 : (dfXRatioDstToSrc > 1 && iDstPixel == nOXSize - 1))
2161 : {
2162 5650 : nSrcXOff2 =
2163 5650 : std::min(nChunkRightXOff, nSrcXOff + nGaussMatrixDim);
2164 : }
2165 :
2166 4133580 : int nXShiftGaussMatrix = 0;
2167 4133580 : if (nSrcXOff < nChunkXOff)
2168 : {
2169 0 : nXShiftGaussMatrix = -(nSrcXOff - nChunkXOff);
2170 0 : nSrcXOff = nChunkXOff;
2171 : }
2172 :
2173 4133580 : if (poColorTable == nullptr)
2174 : {
2175 4133380 : double dfTotal = 0.0;
2176 4133380 : GInt64 nCount = 0;
2177 4133380 : const int *panLineWeight =
2178 4133380 : panGaussMatrix + nYShiftGaussMatrix * nGaussMatrixDim +
2179 : nXShiftGaussMatrix;
2180 :
2181 16527900 : for (int iY = nSrcYOff; iY < nSrcYOff2;
2182 12394500 : ++iY, panLineWeight += nGaussMatrixDim)
2183 : {
2184 49561300 : for (int i = 0, iX = nSrcXOff; iX < nSrcXOff2; ++iX, ++i)
2185 : {
2186 37166800 : const double val =
2187 37166800 : padfSrcScanline[iX - nChunkXOff +
2188 37166800 : static_cast<GPtrDiff_t>(iY -
2189 37166800 : nSrcYOff) *
2190 37166800 : nChunkXSize];
2191 37166800 : if (pabySrcScanlineNodataMask == nullptr ||
2192 32872 : pabySrcScanlineNodataMask[iX - nChunkXOff +
2193 32872 : static_cast<GPtrDiff_t>(
2194 32872 : iY - nSrcYOff) *
2195 32872 : nChunkXSize])
2196 : {
2197 37146100 : const int nWeight = panLineWeight[i];
2198 37146100 : dfTotal += val * nWeight;
2199 37146100 : nCount += nWeight;
2200 : }
2201 : }
2202 : }
2203 :
2204 4133380 : if (nCount == 0)
2205 : {
2206 2217 : padfDstScanline[iDstPixel - nDstXOff] = dfNoDataValue;
2207 : }
2208 : else
2209 : {
2210 4131160 : padfDstScanline[iDstPixel - nDstXOff] = dfTotal / nCount;
2211 : }
2212 : }
2213 : else
2214 : {
2215 200 : GInt64 nTotalR = 0;
2216 200 : GInt64 nTotalG = 0;
2217 200 : GInt64 nTotalB = 0;
2218 200 : GInt64 nTotalWeight = 0;
2219 200 : const int *panLineWeight =
2220 200 : panGaussMatrix + nYShiftGaussMatrix * nGaussMatrixDim +
2221 : nXShiftGaussMatrix;
2222 :
2223 780 : for (int iY = nSrcYOff; iY < nSrcYOff2;
2224 580 : ++iY, panLineWeight += nGaussMatrixDim)
2225 : {
2226 2262 : for (int i = 0, iX = nSrcXOff; iX < nSrcXOff2; ++iX, ++i)
2227 : {
2228 1682 : const double val =
2229 1682 : padfSrcScanline[iX - nChunkXOff +
2230 1682 : static_cast<GPtrDiff_t>(iY -
2231 1682 : nSrcYOff) *
2232 1682 : nChunkXSize];
2233 1682 : if (val < 0 || val >= colorEntries.size())
2234 0 : continue;
2235 :
2236 1682 : size_t idx = static_cast<size_t>(val);
2237 1682 : if (colorEntries[idx].c4)
2238 : {
2239 1682 : const int nWeight = panLineWeight[i];
2240 1682 : nTotalR +=
2241 1682 : static_cast<GInt64>(colorEntries[idx].c1) *
2242 1682 : nWeight;
2243 1682 : nTotalG +=
2244 1682 : static_cast<GInt64>(colorEntries[idx].c2) *
2245 1682 : nWeight;
2246 1682 : nTotalB +=
2247 1682 : static_cast<GInt64>(colorEntries[idx].c3) *
2248 1682 : nWeight;
2249 1682 : nTotalWeight += nWeight;
2250 : }
2251 : }
2252 : }
2253 :
2254 200 : if (nTotalWeight == 0)
2255 : {
2256 0 : padfDstScanline[iDstPixel - nDstXOff] = dfNoDataValue;
2257 : }
2258 : else
2259 : {
2260 : GDALColorEntry color;
2261 :
2262 200 : color.c1 = static_cast<short>((nTotalR + nTotalWeight / 2) /
2263 : nTotalWeight);
2264 200 : color.c2 = static_cast<short>((nTotalG + nTotalWeight / 2) /
2265 : nTotalWeight);
2266 200 : color.c3 = static_cast<short>((nTotalB + nTotalWeight / 2) /
2267 : nTotalWeight);
2268 200 : padfDstScanline[iDstPixel - nDstXOff] =
2269 200 : BestColorEntry(colorEntries, color);
2270 : }
2271 : }
2272 : }
2273 : }
2274 :
2275 : #ifdef DEBUG_OUT_OF_BOUND_ACCESS
2276 : CPLFree(panGaussMatrixDup);
2277 : #endif
2278 :
2279 86 : return CE_None;
2280 : }
2281 :
2282 : /************************************************************************/
2283 : /* GDALResampleChunk_Mode() */
2284 : /************************************************************************/
2285 :
2286 688 : template <class T> static inline bool IsSame(T a, T b)
2287 : {
2288 688 : return a == b;
2289 : }
2290 :
2291 60 : template <> bool IsSame<GFloat16>(GFloat16 a, GFloat16 b)
2292 : {
2293 60 : return a == b || (CPLIsNan(a) && CPLIsNan(b));
2294 : }
2295 :
2296 5583 : template <> bool IsSame<float>(float a, float b)
2297 : {
2298 5583 : return a == b || (std::isnan(a) && std::isnan(b));
2299 : }
2300 :
2301 1701 : template <> bool IsSame<double>(double a, double b)
2302 : {
2303 1701 : return a == b || (std::isnan(a) && std::isnan(b));
2304 : }
2305 :
2306 : namespace
2307 : {
2308 : struct ComplexFloat16
2309 : {
2310 : GFloat16 r;
2311 : GFloat16 i;
2312 : };
2313 : } // namespace
2314 :
2315 60 : template <> bool IsSame<ComplexFloat16>(ComplexFloat16 a, ComplexFloat16 b)
2316 : {
2317 90 : return (a.r == b.r && a.i == b.i) ||
2318 90 : (CPLIsNan(a.r) && CPLIsNan(a.i) && CPLIsNan(b.r) && CPLIsNan(b.i));
2319 : }
2320 :
2321 : template <>
2322 60 : bool IsSame<std::complex<float>>(std::complex<float> a, std::complex<float> b)
2323 : {
2324 120 : return a == b || (std::isnan(a.real()) && std::isnan(a.imag()) &&
2325 120 : std::isnan(b.real()) && std::isnan(b.imag()));
2326 : }
2327 :
2328 : template <>
2329 60 : bool IsSame<std::complex<double>>(std::complex<double> a,
2330 : std::complex<double> b)
2331 : {
2332 120 : return a == b || (std::isnan(a.real()) && std::isnan(a.imag()) &&
2333 120 : std::isnan(b.real()) && std::isnan(b.imag()));
2334 : }
2335 :
2336 : template <class T>
2337 188 : static CPLErr GDALResampleChunk_ModeT(const GDALOverviewResampleArgs &args,
2338 : const T *pChunk, T *const pDstBuffer)
2339 :
2340 : {
2341 188 : const double dfXRatioDstToSrc = args.dfXRatioDstToSrc;
2342 188 : const double dfYRatioDstToSrc = args.dfYRatioDstToSrc;
2343 188 : const double dfSrcXDelta = args.dfSrcXDelta;
2344 188 : const double dfSrcYDelta = args.dfSrcYDelta;
2345 188 : const GByte *pabyChunkNodataMask = args.pabyChunkNodataMask;
2346 188 : const int nChunkXOff = args.nChunkXOff;
2347 188 : const int nChunkXSize = args.nChunkXSize;
2348 188 : const int nChunkYOff = args.nChunkYOff;
2349 188 : const int nChunkYSize = args.nChunkYSize;
2350 188 : const int nDstXOff = args.nDstXOff;
2351 188 : const int nDstXOff2 = args.nDstXOff2;
2352 188 : const int nDstYOff = args.nDstYOff;
2353 188 : const int nDstYOff2 = args.nDstYOff2;
2354 188 : const bool bHasNoData = args.bHasNoData;
2355 188 : const GDALColorTable *poColorTable = args.poColorTable;
2356 188 : const int nDstXSize = nDstXOff2 - nDstXOff;
2357 :
2358 8 : T tNoDataValue;
2359 : if constexpr (std::is_same<T, ComplexFloat16>::value)
2360 : {
2361 4 : tNoDataValue.r = cpl::NumericLimits<GFloat16>::quiet_NaN();
2362 4 : tNoDataValue.i = cpl::NumericLimits<GFloat16>::quiet_NaN();
2363 : }
2364 : else if constexpr (std::is_same<T, std::complex<float>>::value ||
2365 : std::is_same<T, std::complex<double>>::value)
2366 : {
2367 : using BaseT = typename T::value_type;
2368 8 : tNoDataValue =
2369 : std::complex<BaseT>(std::numeric_limits<BaseT>::quiet_NaN(),
2370 : std::numeric_limits<BaseT>::quiet_NaN());
2371 : }
2372 176 : else if (!bHasNoData || !GDALIsValueInRange<T>(args.dfNoDataValue))
2373 175 : tNoDataValue = 0;
2374 : else
2375 1 : tNoDataValue = static_cast<T>(args.dfNoDataValue);
2376 :
2377 : using CountType = uint32_t;
2378 188 : CountType nMaxNumPx = 0;
2379 188 : T *paVals = nullptr;
2380 188 : CountType *panCounts = nullptr;
2381 :
2382 188 : const int nChunkRightXOff = nChunkXOff + nChunkXSize;
2383 188 : const int nChunkBottomYOff = nChunkYOff + nChunkYSize;
2384 376 : std::vector<int> anVals(256, 0);
2385 :
2386 : /* ==================================================================== */
2387 : /* Loop over destination scanlines. */
2388 : /* ==================================================================== */
2389 7725 : for (int iDstLine = nDstYOff; iDstLine < nDstYOff2; ++iDstLine)
2390 : {
2391 7537 : const double dfSrcYOff = dfSrcYDelta + iDstLine * dfYRatioDstToSrc;
2392 7537 : int nSrcYOff = static_cast<int>(dfSrcYOff + 1e-8);
2393 : #ifdef only_pixels_with_more_than_10_pct_participation
2394 : // When oversampling, don't take into account pixels that have a tiny
2395 : // participation in the resulting pixel
2396 : if (dfYRatioDstToSrc > 1 && dfSrcYOff - nSrcYOff > 0.9 &&
2397 : nSrcYOff < nChunkBottomYOff)
2398 : nSrcYOff++;
2399 : #endif
2400 7537 : if (nSrcYOff < nChunkYOff)
2401 0 : nSrcYOff = nChunkYOff;
2402 :
2403 7537 : const double dfSrcYOff2 =
2404 7537 : dfSrcYDelta + (iDstLine + 1) * dfYRatioDstToSrc;
2405 7537 : int nSrcYOff2 = static_cast<int>(ceil(dfSrcYOff2 - 1e-8));
2406 : #ifdef only_pixels_with_more_than_10_pct_participation
2407 : // When oversampling, don't take into account pixels that have a tiny
2408 : // participation in the resulting pixel
2409 : if (dfYRatioDstToSrc > 1 && nSrcYOff2 - dfSrcYOff2 > 0.9 &&
2410 : nSrcYOff2 > nChunkYOff)
2411 : nSrcYOff2--;
2412 : #endif
2413 7537 : if (nSrcYOff2 == nSrcYOff)
2414 0 : ++nSrcYOff2;
2415 7537 : if (nSrcYOff2 > nChunkBottomYOff)
2416 0 : nSrcYOff2 = nChunkBottomYOff;
2417 :
2418 7537 : const T *const paSrcScanline =
2419 281 : pChunk +
2420 7537 : (static_cast<GPtrDiff_t>(nSrcYOff - nChunkYOff) * nChunkXSize);
2421 7537 : const GByte *pabySrcScanlineNodataMask = nullptr;
2422 7537 : if (pabyChunkNodataMask != nullptr)
2423 1838 : pabySrcScanlineNodataMask =
2424 : pabyChunkNodataMask +
2425 1838 : static_cast<GPtrDiff_t>(nSrcYOff - nChunkYOff) * nChunkXSize;
2426 :
2427 7537 : T *const paDstScanline = pDstBuffer + (iDstLine - nDstYOff) * nDstXSize;
2428 : /* --------------------------------------------------------------------
2429 : */
2430 : /* Loop over destination pixels */
2431 : /* --------------------------------------------------------------------
2432 : */
2433 4260606 : for (int iDstPixel = nDstXOff; iDstPixel < nDstXOff2; ++iDstPixel)
2434 : {
2435 4253071 : const double dfSrcXOff = dfSrcXDelta + iDstPixel * dfXRatioDstToSrc;
2436 : // Apply some epsilon to avoid numerical precision issues
2437 4253071 : int nSrcXOff = static_cast<int>(dfSrcXOff + 1e-8);
2438 : #ifdef only_pixels_with_more_than_10_pct_participation
2439 : // When oversampling, don't take into account pixels that have a
2440 : // tiny participation in the resulting pixel
2441 : if (dfXRatioDstToSrc > 1 && dfSrcXOff - nSrcXOff > 0.9 &&
2442 : nSrcXOff < nChunkRightXOff)
2443 : nSrcXOff++;
2444 : #endif
2445 4253071 : if (nSrcXOff < nChunkXOff)
2446 0 : nSrcXOff = nChunkXOff;
2447 :
2448 4253071 : const double dfSrcXOff2 =
2449 4253071 : dfSrcXDelta + (iDstPixel + 1) * dfXRatioDstToSrc;
2450 4253071 : int nSrcXOff2 = static_cast<int>(ceil(dfSrcXOff2 - 1e-8));
2451 : #ifdef only_pixels_with_more_than_10_pct_participation
2452 : // When oversampling, don't take into account pixels that have a
2453 : // tiny participation in the resulting pixel
2454 : if (dfXRatioDstToSrc > 1 && nSrcXOff2 - dfSrcXOff2 > 0.9 &&
2455 : nSrcXOff2 > nChunkXOff)
2456 : nSrcXOff2--;
2457 : #endif
2458 4253071 : if (nSrcXOff2 == nSrcXOff)
2459 0 : nSrcXOff2++;
2460 4253071 : if (nSrcXOff2 > nChunkRightXOff)
2461 0 : nSrcXOff2 = nChunkRightXOff;
2462 :
2463 4253071 : bool bRegularProcessing = false;
2464 : if constexpr (!std::is_same<T, GByte>::value)
2465 1671 : bRegularProcessing = true;
2466 4251400 : else if (poColorTable && poColorTable->GetColorEntryCount() > 256)
2467 0 : bRegularProcessing = true;
2468 :
2469 4253071 : if (bRegularProcessing)
2470 : {
2471 : // Sanity check to make sure the allocation of paVals and
2472 : // panCounts don't overflow.
2473 : static_assert(sizeof(CountType) <= sizeof(size_t));
2474 3342 : if (nSrcYOff2 - nSrcYOff <= 0 || nSrcXOff2 - nSrcXOff <= 0 ||
2475 1671 : static_cast<CountType>(nSrcYOff2 - nSrcYOff) >
2476 1671 : (std::numeric_limits<CountType>::max() /
2477 3342 : std::max(sizeof(T), sizeof(CountType))) /
2478 1671 : static_cast<CountType>(nSrcXOff2 - nSrcXOff))
2479 : {
2480 0 : CPLError(CE_Failure, CPLE_NotSupported,
2481 : "Too big downsampling factor");
2482 0 : CPLFree(paVals);
2483 0 : CPLFree(panCounts);
2484 0 : return CE_Failure;
2485 : }
2486 1671 : const CountType nNumPx =
2487 1671 : static_cast<CountType>(nSrcYOff2 - nSrcYOff) *
2488 1671 : (nSrcXOff2 - nSrcXOff);
2489 1671 : CountType iMaxInd = 0;
2490 1671 : CountType iMaxVal = 0;
2491 :
2492 1671 : if (paVals == nullptr || nNumPx > nMaxNumPx)
2493 : {
2494 : T *paValsNew = static_cast<T *>(
2495 116 : VSI_REALLOC_VERBOSE(paVals, nNumPx * sizeof(T)));
2496 : CountType *panCountsNew =
2497 116 : static_cast<CountType *>(VSI_REALLOC_VERBOSE(
2498 : panCounts, nNumPx * sizeof(CountType)));
2499 116 : if (paValsNew != nullptr)
2500 116 : paVals = paValsNew;
2501 116 : if (panCountsNew != nullptr)
2502 116 : panCounts = panCountsNew;
2503 116 : if (paValsNew == nullptr || panCountsNew == nullptr)
2504 : {
2505 0 : CPLFree(paVals);
2506 0 : CPLFree(panCounts);
2507 0 : return CE_Failure;
2508 : }
2509 116 : nMaxNumPx = nNumPx;
2510 : }
2511 :
2512 5245 : for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
2513 : {
2514 3574 : const GPtrDiff_t iTotYOff =
2515 3574 : static_cast<GPtrDiff_t>(iY - nSrcYOff) * nChunkXSize -
2516 3574 : nChunkXOff;
2517 11842 : for (int iX = nSrcXOff; iX < nSrcXOff2; ++iX)
2518 : {
2519 8268 : if (pabySrcScanlineNodataMask == nullptr ||
2520 1552 : pabySrcScanlineNodataMask[iX + iTotYOff])
2521 : {
2522 8247 : const T val = paSrcScanline[iX + iTotYOff];
2523 8247 : CountType i = 0; // Used after for.
2524 :
2525 : // Check array for existing entry.
2526 11611 : for (; i < iMaxInd; ++i)
2527 : {
2528 8212 : if (IsSame(paVals[i], val))
2529 : {
2530 4848 : if (++panCounts[i] > panCounts[iMaxVal])
2531 : {
2532 246 : iMaxVal = i;
2533 : }
2534 4848 : break;
2535 : }
2536 : }
2537 :
2538 : // Add to arr if entry not already there.
2539 8247 : if (i == iMaxInd)
2540 : {
2541 3399 : paVals[iMaxInd] = val;
2542 3399 : panCounts[iMaxInd] = 1;
2543 :
2544 3399 : if (iMaxInd == 0)
2545 : {
2546 1668 : iMaxVal = iMaxInd;
2547 : }
2548 :
2549 3399 : ++iMaxInd;
2550 : }
2551 : }
2552 : }
2553 : }
2554 :
2555 1671 : if (iMaxInd == 0)
2556 3 : paDstScanline[iDstPixel - nDstXOff] = tNoDataValue;
2557 : else
2558 1668 : paDstScanline[iDstPixel - nDstXOff] = paVals[iMaxVal];
2559 : }
2560 : else if constexpr (std::is_same<T, GByte>::value)
2561 : // ( eSrcDataType == GDT_UInt8 && nEntryCount < 256 )
2562 : {
2563 : // So we go here for a paletted or non-paletted byte band.
2564 : // The input values are then between 0 and 255.
2565 4251400 : int nMaxVal = 0;
2566 4251400 : int iMaxInd = -1;
2567 :
2568 : // The cost of this zeroing might be high. Perhaps we should
2569 : // just use the above generic case, and go to this one if the
2570 : // number of source pixels is large enough
2571 4251400 : std::fill(anVals.begin(), anVals.end(), 0);
2572 :
2573 12777900 : for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
2574 : {
2575 8526460 : const GPtrDiff_t iTotYOff =
2576 8526460 : static_cast<GPtrDiff_t>(iY - nSrcYOff) * nChunkXSize -
2577 8526460 : nChunkXOff;
2578 25649600 : for (int iX = nSrcXOff; iX < nSrcXOff2; ++iX)
2579 : {
2580 17123200 : const T val = paSrcScanline[iX + iTotYOff];
2581 17123200 : if (!bHasNoData || val != tNoDataValue)
2582 : {
2583 17123200 : int nVal = static_cast<int>(val);
2584 17123200 : if (++anVals[nVal] > nMaxVal)
2585 : {
2586 : // Sum the density.
2587 : // Is it the most common value so far?
2588 17006400 : iMaxInd = nVal;
2589 17006400 : nMaxVal = anVals[nVal];
2590 : }
2591 : }
2592 : }
2593 : }
2594 :
2595 4251400 : if (iMaxInd == -1)
2596 0 : paDstScanline[iDstPixel - nDstXOff] = tNoDataValue;
2597 : else
2598 4251400 : paDstScanline[iDstPixel - nDstXOff] =
2599 : static_cast<T>(iMaxInd);
2600 : }
2601 : }
2602 : }
2603 :
2604 188 : CPLFree(paVals);
2605 188 : CPLFree(panCounts);
2606 :
2607 188 : return CE_None;
2608 : }
2609 :
2610 188 : static CPLErr GDALResampleChunk_Mode(const GDALOverviewResampleArgs &args,
2611 : const void *pChunk, void **ppDstBuffer,
2612 : GDALDataType *peDstBufferDataType)
2613 : {
2614 188 : *ppDstBuffer = VSI_MALLOC3_VERBOSE(
2615 : args.nDstXOff2 - args.nDstXOff, args.nDstYOff2 - args.nDstYOff,
2616 : GDALGetDataTypeSizeBytes(args.eWrkDataType));
2617 188 : if (*ppDstBuffer == nullptr)
2618 : {
2619 0 : return CE_Failure;
2620 : }
2621 :
2622 188 : CPLAssert(args.eSrcDataType == args.eWrkDataType);
2623 :
2624 188 : *peDstBufferDataType = args.eWrkDataType;
2625 188 : switch (args.eWrkDataType)
2626 : {
2627 : // For mode resampling, as no computation is done, only the
2628 : // size of the data type matters... except for Byte where we have
2629 : // special processing. And for floating point values
2630 72 : case GDT_UInt8:
2631 : {
2632 72 : return GDALResampleChunk_ModeT(args,
2633 : static_cast<const GByte *>(pChunk),
2634 72 : static_cast<GByte *>(*ppDstBuffer));
2635 : }
2636 :
2637 4 : case GDT_Int8:
2638 : {
2639 4 : return GDALResampleChunk_ModeT(args,
2640 : static_cast<const int8_t *>(pChunk),
2641 4 : static_cast<int8_t *>(*ppDstBuffer));
2642 : }
2643 :
2644 10 : case GDT_Int16:
2645 : case GDT_UInt16:
2646 : {
2647 10 : CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 2);
2648 10 : return GDALResampleChunk_ModeT(
2649 : args, static_cast<const uint16_t *>(pChunk),
2650 10 : static_cast<uint16_t *>(*ppDstBuffer));
2651 : }
2652 :
2653 15 : case GDT_CInt16:
2654 : case GDT_Int32:
2655 : case GDT_UInt32:
2656 : {
2657 15 : CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 4);
2658 15 : return GDALResampleChunk_ModeT(
2659 : args, static_cast<const uint32_t *>(pChunk),
2660 15 : static_cast<uint32_t *>(*ppDstBuffer));
2661 : }
2662 :
2663 12 : case GDT_CInt32:
2664 : case GDT_Int64:
2665 : case GDT_UInt64:
2666 : {
2667 12 : CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 8);
2668 12 : return GDALResampleChunk_ModeT(
2669 : args, static_cast<const uint64_t *>(pChunk),
2670 12 : static_cast<uint64_t *>(*ppDstBuffer));
2671 : }
2672 :
2673 4 : case GDT_Float16:
2674 : {
2675 4 : return GDALResampleChunk_ModeT(
2676 : args, static_cast<const GFloat16 *>(pChunk),
2677 4 : static_cast<GFloat16 *>(*ppDstBuffer));
2678 : }
2679 :
2680 35 : case GDT_Float32:
2681 : {
2682 35 : return GDALResampleChunk_ModeT(args,
2683 : static_cast<const float *>(pChunk),
2684 35 : static_cast<float *>(*ppDstBuffer));
2685 : }
2686 :
2687 24 : case GDT_Float64:
2688 : {
2689 24 : return GDALResampleChunk_ModeT(args,
2690 : static_cast<const double *>(pChunk),
2691 24 : static_cast<double *>(*ppDstBuffer));
2692 : }
2693 :
2694 4 : case GDT_CFloat16:
2695 : {
2696 4 : return GDALResampleChunk_ModeT(
2697 : args, static_cast<const ComplexFloat16 *>(pChunk),
2698 4 : static_cast<ComplexFloat16 *>(*ppDstBuffer));
2699 : }
2700 :
2701 4 : case GDT_CFloat32:
2702 : {
2703 4 : return GDALResampleChunk_ModeT(
2704 : args, static_cast<const std::complex<float> *>(pChunk),
2705 4 : static_cast<std::complex<float> *>(*ppDstBuffer));
2706 : }
2707 :
2708 4 : case GDT_CFloat64:
2709 : {
2710 4 : return GDALResampleChunk_ModeT(
2711 : args, static_cast<const std::complex<double> *>(pChunk),
2712 4 : static_cast<std::complex<double> *>(*ppDstBuffer));
2713 : }
2714 :
2715 0 : case GDT_Unknown:
2716 : case GDT_TypeCount:
2717 0 : break;
2718 : }
2719 :
2720 0 : CPLAssert(false);
2721 : return CE_Failure;
2722 : }
2723 :
2724 : /************************************************************************/
2725 : /* GDALResampleConvolutionHorizontal() */
2726 : /************************************************************************/
2727 :
2728 : template <class T>
2729 : static inline double
2730 46038 : GDALResampleConvolutionHorizontal(const T *pChunk, const double *padfWeights,
2731 : int nSrcPixelCount)
2732 : {
2733 46038 : double dfVal1 = 0.0;
2734 46038 : double dfVal2 = 0.0;
2735 46038 : int i = 0; // Used after for.
2736 : // Intel Compiler 2024.0.2.29 (maybe other versions?) crashes on this
2737 : // manually (untypical) unrolled loop in -O2 and -O3:
2738 : // https://github.com/OSGeo/gdal/issues/9508
2739 : #if !defined(__INTEL_CLANG_COMPILER)
2740 92396 : for (; i < nSrcPixelCount - 3; i += 4)
2741 : {
2742 46358 : dfVal1 += double(pChunk[i + 0]) * padfWeights[i];
2743 46358 : dfVal1 += double(pChunk[i + 1]) * padfWeights[i + 1];
2744 46358 : dfVal2 += double(pChunk[i + 2]) * padfWeights[i + 2];
2745 46358 : dfVal2 += double(pChunk[i + 3]) * padfWeights[i + 3];
2746 : }
2747 : #endif
2748 48662 : for (; i < nSrcPixelCount; ++i)
2749 : {
2750 2624 : dfVal1 += double(pChunk[i]) * padfWeights[i];
2751 : }
2752 46038 : return dfVal1 + dfVal2;
2753 : }
2754 :
2755 : template <class T, bool bHasNaN>
2756 46368 : static inline void GDALResampleConvolutionHorizontalWithMask(
2757 : const T *pChunk, const GByte *pabyMask, const double *padfWeights,
2758 : int nSrcPixelCount, double &dfWeightValMaskSum, double &dfWeightMaskSum,
2759 : double &dfWeightSum)
2760 : {
2761 46368 : dfWeightValMaskSum = 0;
2762 46368 : dfWeightMaskSum = 0;
2763 46368 : dfWeightSum = 0;
2764 46368 : int i = 0;
2765 103804 : for (; i < nSrcPixelCount - 3; i += 4)
2766 : {
2767 57436 : double dfWeightMask0 = padfWeights[i + 0] * pabyMask[i + 0];
2768 57436 : double dfWeightMask1 = padfWeights[i + 1] * pabyMask[i + 1];
2769 57436 : double dfWeightMask2 = padfWeights[i + 2] * pabyMask[i + 2];
2770 57436 : double dfWeightMask3 = padfWeights[i + 3] * pabyMask[i + 3];
2771 :
2772 229744 : const auto MulNaNAware = [](double v, double &w, double &val)
2773 : {
2774 : if constexpr (bHasNaN)
2775 : {
2776 14848 : if (std::isnan(v))
2777 : {
2778 76 : w = 0;
2779 76 : return;
2780 : }
2781 : }
2782 14772 : val += v * w;
2783 : };
2784 :
2785 57436 : MulNaNAware(double(pChunk[i + 0]), dfWeightMask0, dfWeightValMaskSum);
2786 57436 : MulNaNAware(double(pChunk[i + 1]), dfWeightMask1, dfWeightValMaskSum);
2787 57436 : MulNaNAware(double(pChunk[i + 2]), dfWeightMask2, dfWeightValMaskSum);
2788 57436 : MulNaNAware(double(pChunk[i + 3]), dfWeightMask3, dfWeightValMaskSum);
2789 57436 : dfWeightMaskSum +=
2790 57436 : dfWeightMask0 + dfWeightMask1 + dfWeightMask2 + dfWeightMask3;
2791 57436 : dfWeightSum += padfWeights[i + 0] + padfWeights[i + 1] +
2792 57436 : padfWeights[i + 2] + padfWeights[i + 3];
2793 : }
2794 64874 : for (; i < nSrcPixelCount; ++i)
2795 : {
2796 18506 : const double dfWeightMask = padfWeights[i] * pabyMask[i];
2797 : if constexpr (bHasNaN)
2798 : {
2799 1920 : if (!std::isnan(pChunk[i]))
2800 : {
2801 1920 : dfWeightValMaskSum += double(pChunk[i]) * dfWeightMask;
2802 1920 : dfWeightMaskSum += dfWeightMask;
2803 1920 : dfWeightSum += padfWeights[i];
2804 : }
2805 : }
2806 : else
2807 : {
2808 16586 : dfWeightValMaskSum += double(pChunk[i]) * dfWeightMask;
2809 16586 : dfWeightMaskSum += dfWeightMask;
2810 16586 : dfWeightSum += padfWeights[i];
2811 : }
2812 : }
2813 46368 : }
2814 :
2815 : template <class T, bool bHasNaN>
2816 1341366 : static inline void GDALResampleConvolutionHorizontal_3rows(
2817 : const T *pChunkRow1, const T *pChunkRow2, const T *pChunkRow3,
2818 : const double *padfWeights, int nSrcPixelCount, double &dfRes1,
2819 : double &dfRes2, double &dfRes3)
2820 : {
2821 1341366 : double dfVal1 = 0.0;
2822 1341366 : double dfVal2 = 0.0;
2823 1341366 : double dfVal3 = 0.0;
2824 1341366 : double dfVal4 = 0.0;
2825 1341366 : double dfVal5 = 0.0;
2826 1341366 : double dfVal6 = 0.0;
2827 1341366 : int i = 0; // Used after for.
2828 :
2829 16866840 : const auto MulNaNAware = [](double a, double w)
2830 : {
2831 : if constexpr (bHasNaN)
2832 : {
2833 0 : if (std::isnan(a))
2834 0 : return 0.0;
2835 : }
2836 16866900 : return a * w;
2837 : };
2838 :
2839 2736937 : for (; i < nSrcPixelCount - 3; i += 4)
2840 : {
2841 1395570 : dfVal1 += MulNaNAware(double(pChunkRow1[i + 0]), padfWeights[i + 0]);
2842 1395570 : dfVal1 += MulNaNAware(double(pChunkRow1[i + 1]), padfWeights[i + 1]);
2843 1395570 : dfVal2 += MulNaNAware(double(pChunkRow1[i + 2]), padfWeights[i + 2]);
2844 1395570 : dfVal2 += MulNaNAware(double(pChunkRow1[i + 3]), padfWeights[i + 3]);
2845 1395570 : dfVal3 += MulNaNAware(double(pChunkRow2[i + 0]), padfWeights[i + 0]);
2846 1395570 : dfVal3 += MulNaNAware(double(pChunkRow2[i + 1]), padfWeights[i + 1]);
2847 1395570 : dfVal4 += MulNaNAware(double(pChunkRow2[i + 2]), padfWeights[i + 2]);
2848 1395570 : dfVal4 += MulNaNAware(double(pChunkRow2[i + 3]), padfWeights[i + 3]);
2849 1395570 : dfVal5 += MulNaNAware(double(pChunkRow3[i + 0]), padfWeights[i + 0]);
2850 1395570 : dfVal5 += MulNaNAware(double(pChunkRow3[i + 1]), padfWeights[i + 1]);
2851 1395570 : dfVal6 += MulNaNAware(double(pChunkRow3[i + 2]), padfWeights[i + 2]);
2852 1395570 : dfVal6 += MulNaNAware(double(pChunkRow3[i + 3]), padfWeights[i + 3]);
2853 : }
2854 1381377 : for (; i < nSrcPixelCount; ++i)
2855 : {
2856 40011 : dfVal1 += MulNaNAware(double(pChunkRow1[i]), padfWeights[i]);
2857 40011 : dfVal3 += MulNaNAware(double(pChunkRow2[i]), padfWeights[i]);
2858 40011 : dfVal5 += MulNaNAware(double(pChunkRow3[i]), padfWeights[i]);
2859 : }
2860 1341366 : dfRes1 = dfVal1 + dfVal2;
2861 1341366 : dfRes2 = dfVal3 + dfVal4;
2862 1341366 : dfRes3 = dfVal5 + dfVal6;
2863 1341366 : }
2864 :
2865 : template <class T, bool bHasNaN>
2866 18980 : static inline void GDALResampleConvolutionHorizontalPixelCountLess8_3rows(
2867 : const T *pChunkRow1, const T *pChunkRow2, const T *pChunkRow3,
2868 : const double *padfWeights, int nSrcPixelCount, double &dfRes1,
2869 : double &dfRes2, double &dfRes3)
2870 : {
2871 18980 : GDALResampleConvolutionHorizontal_3rows<T, bHasNaN>(
2872 : pChunkRow1, pChunkRow2, pChunkRow3, padfWeights, nSrcPixelCount, dfRes1,
2873 : dfRes2, dfRes3);
2874 18980 : }
2875 :
2876 : template <class T, bool bHasNaN>
2877 1256690 : static inline void GDALResampleConvolutionHorizontalPixelCount4_3rows(
2878 : const T *pChunkRow1, const T *pChunkRow2, const T *pChunkRow3,
2879 : const double *padfWeights, double &dfRes1, double &dfRes2, double &dfRes3)
2880 : {
2881 1256690 : GDALResampleConvolutionHorizontal_3rows<T, bHasNaN>(
2882 : pChunkRow1, pChunkRow2, pChunkRow3, padfWeights, 4, dfRes1, dfRes2,
2883 : dfRes3);
2884 1256690 : }
2885 :
2886 : /************************************************************************/
2887 : /* GDALResampleConvolutionVertical() */
2888 : /************************************************************************/
2889 :
2890 : template <class T>
2891 : static inline double
2892 472559 : GDALResampleConvolutionVertical(const T *pChunk, size_t nStride,
2893 : const double *padfWeights, int nSrcLineCount)
2894 : {
2895 472559 : double dfVal1 = 0.0;
2896 472559 : double dfVal2 = 0.0;
2897 472559 : int i = 0;
2898 472559 : size_t j = 0;
2899 936200 : for (; i < nSrcLineCount - 3; i += 4, j += 4 * nStride)
2900 : {
2901 463641 : dfVal1 += pChunk[j + 0 * nStride] * padfWeights[i + 0];
2902 463641 : dfVal1 += pChunk[j + 1 * nStride] * padfWeights[i + 1];
2903 463641 : dfVal2 += pChunk[j + 2 * nStride] * padfWeights[i + 2];
2904 463641 : dfVal2 += pChunk[j + 3 * nStride] * padfWeights[i + 3];
2905 : }
2906 526926 : for (; i < nSrcLineCount; ++i, j += nStride)
2907 : {
2908 54367 : dfVal1 += pChunk[j] * padfWeights[i];
2909 : }
2910 472559 : return dfVal1 + dfVal2;
2911 : }
2912 :
2913 : template <class T>
2914 2930610 : static inline void GDALResampleConvolutionVertical_2cols(
2915 : const T *pChunk, size_t nStride, const double *padfWeights,
2916 : int nSrcLineCount, double &dfRes1, double &dfRes2)
2917 : {
2918 2930610 : double dfVal1 = 0.0;
2919 2930610 : double dfVal2 = 0.0;
2920 2930610 : double dfVal3 = 0.0;
2921 2930610 : double dfVal4 = 0.0;
2922 2930610 : int i = 0;
2923 2930610 : size_t j = 0;
2924 5863170 : for (; i < nSrcLineCount - 3; i += 4, j += 4 * nStride)
2925 : {
2926 2932560 : dfVal1 += pChunk[j + 0 + 0 * nStride] * padfWeights[i + 0];
2927 2932560 : dfVal3 += pChunk[j + 1 + 0 * nStride] * padfWeights[i + 0];
2928 2932560 : dfVal1 += pChunk[j + 0 + 1 * nStride] * padfWeights[i + 1];
2929 2932560 : dfVal3 += pChunk[j + 1 + 1 * nStride] * padfWeights[i + 1];
2930 2932560 : dfVal2 += pChunk[j + 0 + 2 * nStride] * padfWeights[i + 2];
2931 2932560 : dfVal4 += pChunk[j + 1 + 2 * nStride] * padfWeights[i + 2];
2932 2932560 : dfVal2 += pChunk[j + 0 + 3 * nStride] * padfWeights[i + 3];
2933 2932560 : dfVal4 += pChunk[j + 1 + 3 * nStride] * padfWeights[i + 3];
2934 : }
2935 3053490 : for (; i < nSrcLineCount; ++i, j += nStride)
2936 : {
2937 122880 : dfVal1 += pChunk[j + 0] * padfWeights[i];
2938 122880 : dfVal3 += pChunk[j + 1] * padfWeights[i];
2939 : }
2940 2930610 : dfRes1 = dfVal1 + dfVal2;
2941 2930610 : dfRes2 = dfVal3 + dfVal4;
2942 2930610 : }
2943 :
2944 : #ifdef USE_SSE2
2945 :
2946 : #ifdef __AVX__
2947 : /************************************************************************/
2948 : /* GDALResampleConvolutionVertical_16cols<T> */
2949 : /************************************************************************/
2950 :
2951 : template <class T>
2952 : static inline void
2953 : GDALResampleConvolutionVertical_16cols(const T *pChunk, size_t nStride,
2954 : const double *padfWeights,
2955 : int nSrcLineCount, float *afDest)
2956 : {
2957 : int i = 0;
2958 : size_t j = 0;
2959 : XMMReg4Double v_acc0 = XMMReg4Double::Zero();
2960 : XMMReg4Double v_acc1 = XMMReg4Double::Zero();
2961 : XMMReg4Double v_acc2 = XMMReg4Double::Zero();
2962 : XMMReg4Double v_acc3 = XMMReg4Double::Zero();
2963 : for (; i < nSrcLineCount - 3; i += 4, j += 4 * nStride)
2964 : {
2965 : XMMReg4Double w0 =
2966 : XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 0);
2967 : XMMReg4Double w1 =
2968 : XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 1);
2969 : XMMReg4Double w2 =
2970 : XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 2);
2971 : XMMReg4Double w3 =
2972 : XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 3);
2973 : v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 0 * nStride) * w0;
2974 : v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 0 * nStride) * w0;
2975 : v_acc2 += XMMReg4Double::Load4Val(pChunk + j + 8 + 0 * nStride) * w0;
2976 : v_acc3 += XMMReg4Double::Load4Val(pChunk + j + 12 + 0 * nStride) * w0;
2977 : v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 1 * nStride) * w1;
2978 : v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 1 * nStride) * w1;
2979 : v_acc2 += XMMReg4Double::Load4Val(pChunk + j + 8 + 1 * nStride) * w1;
2980 : v_acc3 += XMMReg4Double::Load4Val(pChunk + j + 12 + 1 * nStride) * w1;
2981 : v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 2 * nStride) * w2;
2982 : v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 2 * nStride) * w2;
2983 : v_acc2 += XMMReg4Double::Load4Val(pChunk + j + 8 + 2 * nStride) * w2;
2984 : v_acc3 += XMMReg4Double::Load4Val(pChunk + j + 12 + 2 * nStride) * w2;
2985 : v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 3 * nStride) * w3;
2986 : v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 3 * nStride) * w3;
2987 : v_acc2 += XMMReg4Double::Load4Val(pChunk + j + 8 + 3 * nStride) * w3;
2988 : v_acc3 += XMMReg4Double::Load4Val(pChunk + j + 12 + 3 * nStride) * w3;
2989 : }
2990 : for (; i < nSrcLineCount; ++i, j += nStride)
2991 : {
2992 : XMMReg4Double w = XMMReg4Double::Load1ValHighAndLow(padfWeights + i);
2993 : v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0) * w;
2994 : v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4) * w;
2995 : v_acc2 += XMMReg4Double::Load4Val(pChunk + j + 8) * w;
2996 : v_acc3 += XMMReg4Double::Load4Val(pChunk + j + 12) * w;
2997 : }
2998 : v_acc0.Store4Val(afDest);
2999 : v_acc1.Store4Val(afDest + 4);
3000 : v_acc2.Store4Val(afDest + 8);
3001 : v_acc3.Store4Val(afDest + 12);
3002 : }
3003 :
3004 : template <class T>
3005 : static inline void GDALResampleConvolutionVertical_16cols(const T *, int,
3006 : const double *, int,
3007 : double *)
3008 : {
3009 : // Cannot be reached
3010 : CPLAssert(false);
3011 : }
3012 :
3013 : #else
3014 :
3015 : /************************************************************************/
3016 : /* GDALResampleConvolutionVertical_8cols<T> */
3017 : /************************************************************************/
3018 :
3019 : template <class T>
3020 : static inline void
3021 25804100 : GDALResampleConvolutionVertical_8cols(const T *pChunk, size_t nStride,
3022 : const double *padfWeights,
3023 : int nSrcLineCount, float *afDest)
3024 : {
3025 25804100 : int i = 0;
3026 25804100 : size_t j = 0;
3027 25804100 : XMMReg4Double v_acc0 = XMMReg4Double::Zero();
3028 25804100 : XMMReg4Double v_acc1 = XMMReg4Double::Zero();
3029 53883400 : for (; i < nSrcLineCount - 3; i += 4, j += 4 * nStride)
3030 : {
3031 28079400 : XMMReg4Double w0 =
3032 28079400 : XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 0);
3033 28079400 : XMMReg4Double w1 =
3034 28079400 : XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 1);
3035 28079400 : XMMReg4Double w2 =
3036 28079400 : XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 2);
3037 28079400 : XMMReg4Double w3 =
3038 28079400 : XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 3);
3039 28079400 : v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 0 * nStride) * w0;
3040 28079400 : v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 0 * nStride) * w0;
3041 28079400 : v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 1 * nStride) * w1;
3042 28079400 : v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 1 * nStride) * w1;
3043 28079400 : v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 2 * nStride) * w2;
3044 28079400 : v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 2 * nStride) * w2;
3045 28079400 : v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 3 * nStride) * w3;
3046 28079400 : v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 3 * nStride) * w3;
3047 : }
3048 37376200 : for (; i < nSrcLineCount; ++i, j += nStride)
3049 : {
3050 11572200 : XMMReg4Double w = XMMReg4Double::Load1ValHighAndLow(padfWeights + i);
3051 11572200 : v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0) * w;
3052 11572200 : v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4) * w;
3053 : }
3054 25804100 : v_acc0.Store4Val(afDest);
3055 25804100 : v_acc1.Store4Val(afDest + 4);
3056 25804100 : }
3057 :
3058 : template <class T>
3059 : [[maybe_unused]]
3060 : static inline void GDALResampleConvolutionVertical_8cols(const T *, int,
3061 : const double *, int,
3062 : double *)
3063 : {
3064 : // Cannot be reached
3065 : CPLAssert(false);
3066 : }
3067 :
3068 : #endif // __AVX__
3069 :
3070 : /************************************************************************/
3071 : /* GDALResampleConvolutionHorizontalSSE2<T> */
3072 : /************************************************************************/
3073 :
3074 : template <class T>
3075 3375702 : static inline double GDALResampleConvolutionHorizontalSSE2(
3076 : const T *pChunk, const double *padfWeightsAligned, int nSrcPixelCount)
3077 : {
3078 3375702 : XMMReg4Double v_acc1 = XMMReg4Double::Zero();
3079 3375702 : XMMReg4Double v_acc2 = XMMReg4Double::Zero();
3080 3375702 : int i = 0; // Used after for.
3081 3754648 : for (; i < nSrcPixelCount - 7; i += 8)
3082 : {
3083 : // Retrieve the pixel & accumulate
3084 378952 : const XMMReg4Double v_pixels1 = XMMReg4Double::Load4Val(pChunk + i);
3085 378952 : const XMMReg4Double v_pixels2 = XMMReg4Double::Load4Val(pChunk + i + 4);
3086 378952 : const XMMReg4Double v_weight1 =
3087 378952 : XMMReg4Double::Load4ValAligned(padfWeightsAligned + i);
3088 378952 : const XMMReg4Double v_weight2 =
3089 378952 : XMMReg4Double::Load4ValAligned(padfWeightsAligned + i + 4);
3090 :
3091 378952 : v_acc1 += v_pixels1 * v_weight1;
3092 378952 : v_acc2 += v_pixels2 * v_weight2;
3093 : }
3094 :
3095 3375702 : v_acc1 += v_acc2;
3096 :
3097 3375702 : double dfVal = v_acc1.GetHorizSum();
3098 11491480 : for (; i < nSrcPixelCount; ++i)
3099 : {
3100 8115780 : dfVal += pChunk[i] * padfWeightsAligned[i];
3101 : }
3102 3375702 : return dfVal;
3103 : }
3104 :
3105 : /************************************************************************/
3106 : /* GDALResampleConvolutionHorizontal<GByte> */
3107 : /************************************************************************/
3108 :
3109 : template <>
3110 2826540 : inline double GDALResampleConvolutionHorizontal<GByte>(
3111 : const GByte *pChunk, const double *padfWeightsAligned, int nSrcPixelCount)
3112 : {
3113 2826540 : return GDALResampleConvolutionHorizontalSSE2(pChunk, padfWeightsAligned,
3114 2826540 : nSrcPixelCount);
3115 : }
3116 :
3117 : template <>
3118 549162 : inline double GDALResampleConvolutionHorizontal<GUInt16>(
3119 : const GUInt16 *pChunk, const double *padfWeightsAligned, int nSrcPixelCount)
3120 : {
3121 549162 : return GDALResampleConvolutionHorizontalSSE2(pChunk, padfWeightsAligned,
3122 549162 : nSrcPixelCount);
3123 : }
3124 :
3125 : /************************************************************************/
3126 : /* GDALResampleConvolutionHorizontalWithMaskSSE2<T> */
3127 : /************************************************************************/
3128 :
3129 : template <class T>
3130 10626463 : static inline void GDALResampleConvolutionHorizontalWithMaskSSE2(
3131 : const T *pChunk, const GByte *pabyMask, const double *padfWeightsAligned,
3132 : int nSrcPixelCount, double &dfWeightValMaskSum, double &dfWeightMaskSum,
3133 : double &dfWeightSum)
3134 : {
3135 10626463 : int i = 0; // Used after for.
3136 10626463 : XMMReg4Double v_acc_val_mask_weight = XMMReg4Double::Zero();
3137 10626463 : XMMReg4Double v_acc_mask_weight = XMMReg4Double::Zero();
3138 10626463 : XMMReg4Double v_acc_weight = XMMReg4Double::Zero();
3139 26199121 : for (; i < nSrcPixelCount - 3; i += 4)
3140 : {
3141 15572658 : const XMMReg4Double v_pixels = XMMReg4Double::Load4Val(pChunk + i);
3142 15572658 : const XMMReg4Double v_mask = XMMReg4Double::Load4Val(pabyMask + i);
3143 15572658 : XMMReg4Double v_weight =
3144 15572658 : XMMReg4Double::Load4ValAligned(padfWeightsAligned + i);
3145 15572658 : v_acc_weight += v_weight;
3146 15572658 : v_weight *= v_mask;
3147 15572658 : v_acc_val_mask_weight += v_pixels * v_weight;
3148 15572658 : v_acc_mask_weight += v_weight;
3149 : }
3150 :
3151 10626463 : dfWeightValMaskSum = v_acc_val_mask_weight.GetHorizSum();
3152 10626463 : dfWeightMaskSum = v_acc_mask_weight.GetHorizSum();
3153 10626463 : dfWeightSum = v_acc_weight.GetHorizSum();
3154 10910963 : for (; i < nSrcPixelCount; ++i)
3155 : {
3156 284454 : const double dfWeight = padfWeightsAligned[i];
3157 284454 : const double dfWeightMask = dfWeight * pabyMask[i];
3158 284454 : dfWeightValMaskSum += pChunk[i] * dfWeightMask;
3159 284454 : dfWeightMaskSum += dfWeightMask;
3160 284454 : dfWeightSum += dfWeight;
3161 : }
3162 10626463 : }
3163 :
3164 : /************************************************************************/
3165 : /* GDALResampleConvolutionHorizontalWithMask<GByte> */
3166 : /************************************************************************/
3167 :
3168 : template <>
3169 10626400 : inline void GDALResampleConvolutionHorizontalWithMask<GByte, false>(
3170 : const GByte *pChunk, const GByte *pabyMask,
3171 : const double *padfWeightsAligned, int nSrcPixelCount,
3172 : double &dfWeightValMaskSum, double &dfWeightMaskSum, double &dfWeightSum)
3173 : {
3174 10626400 : GDALResampleConvolutionHorizontalWithMaskSSE2(
3175 : pChunk, pabyMask, padfWeightsAligned, nSrcPixelCount,
3176 : dfWeightValMaskSum, dfWeightMaskSum, dfWeightSum);
3177 10626400 : }
3178 :
3179 : template <>
3180 63 : inline void GDALResampleConvolutionHorizontalWithMask<GUInt16, false>(
3181 : const GUInt16 *pChunk, const GByte *pabyMask,
3182 : const double *padfWeightsAligned, int nSrcPixelCount,
3183 : double &dfWeightValMaskSum, double &dfWeightMaskSum, double &dfWeightSum)
3184 : {
3185 63 : GDALResampleConvolutionHorizontalWithMaskSSE2(
3186 : pChunk, pabyMask, padfWeightsAligned, nSrcPixelCount,
3187 : dfWeightValMaskSum, dfWeightMaskSum, dfWeightSum);
3188 63 : }
3189 :
3190 : /************************************************************************/
3191 : /* GDALResampleConvolutionHorizontal_3rows_SSE2<T> */
3192 : /************************************************************************/
3193 :
3194 : template <class T>
3195 35560186 : static inline void GDALResampleConvolutionHorizontal_3rows_SSE2(
3196 : const T *pChunkRow1, const T *pChunkRow2, const T *pChunkRow3,
3197 : const double *padfWeightsAligned, int nSrcPixelCount, double &dfRes1,
3198 : double &dfRes2, double &dfRes3)
3199 : {
3200 35560186 : XMMReg4Double v_acc1 = XMMReg4Double::Zero(),
3201 35560186 : v_acc2 = XMMReg4Double::Zero(),
3202 35560186 : v_acc3 = XMMReg4Double::Zero();
3203 35560186 : int i = 0;
3204 70929556 : for (; i < nSrcPixelCount - 7; i += 8)
3205 : {
3206 : // Retrieve the pixel & accumulate.
3207 35369370 : XMMReg4Double v_pixels1 = XMMReg4Double::Load4Val(pChunkRow1 + i);
3208 35369370 : XMMReg4Double v_pixels2 = XMMReg4Double::Load4Val(pChunkRow1 + i + 4);
3209 35369370 : const XMMReg4Double v_weight1 =
3210 35369370 : XMMReg4Double::Load4ValAligned(padfWeightsAligned + i);
3211 35369370 : const XMMReg4Double v_weight2 =
3212 35369370 : XMMReg4Double::Load4ValAligned(padfWeightsAligned + i + 4);
3213 :
3214 35369370 : v_acc1 += v_pixels1 * v_weight1;
3215 35369370 : v_acc1 += v_pixels2 * v_weight2;
3216 :
3217 35369370 : v_pixels1 = XMMReg4Double::Load4Val(pChunkRow2 + i);
3218 35369370 : v_pixels2 = XMMReg4Double::Load4Val(pChunkRow2 + i + 4);
3219 35369370 : v_acc2 += v_pixels1 * v_weight1;
3220 35369370 : v_acc2 += v_pixels2 * v_weight2;
3221 :
3222 35369370 : v_pixels1 = XMMReg4Double::Load4Val(pChunkRow3 + i);
3223 35369370 : v_pixels2 = XMMReg4Double::Load4Val(pChunkRow3 + i + 4);
3224 35369370 : v_acc3 += v_pixels1 * v_weight1;
3225 35369370 : v_acc3 += v_pixels2 * v_weight2;
3226 : }
3227 :
3228 35560186 : dfRes1 = v_acc1.GetHorizSum();
3229 35560186 : dfRes2 = v_acc2.GetHorizSum();
3230 35560186 : dfRes3 = v_acc3.GetHorizSum();
3231 47825952 : for (; i < nSrcPixelCount; ++i)
3232 : {
3233 12265766 : dfRes1 += pChunkRow1[i] * padfWeightsAligned[i];
3234 12265766 : dfRes2 += pChunkRow2[i] * padfWeightsAligned[i];
3235 12265766 : dfRes3 += pChunkRow3[i] * padfWeightsAligned[i];
3236 : }
3237 35560186 : }
3238 :
3239 : /************************************************************************/
3240 : /* GDALResampleConvolutionHorizontal_3rows<GByte> */
3241 : /************************************************************************/
3242 :
3243 : template <>
3244 35560100 : inline void GDALResampleConvolutionHorizontal_3rows<GByte, false>(
3245 : const GByte *pChunkRow1, const GByte *pChunkRow2, const GByte *pChunkRow3,
3246 : const double *padfWeightsAligned, int nSrcPixelCount, double &dfRes1,
3247 : double &dfRes2, double &dfRes3)
3248 : {
3249 35560100 : GDALResampleConvolutionHorizontal_3rows_SSE2(
3250 : pChunkRow1, pChunkRow2, pChunkRow3, padfWeightsAligned, nSrcPixelCount,
3251 : dfRes1, dfRes2, dfRes3);
3252 35560100 : }
3253 :
3254 : template <>
3255 86 : inline void GDALResampleConvolutionHorizontal_3rows<GUInt16, false>(
3256 : const GUInt16 *pChunkRow1, const GUInt16 *pChunkRow2,
3257 : const GUInt16 *pChunkRow3, const double *padfWeightsAligned,
3258 : int nSrcPixelCount, double &dfRes1, double &dfRes2, double &dfRes3)
3259 : {
3260 86 : GDALResampleConvolutionHorizontal_3rows_SSE2(
3261 : pChunkRow1, pChunkRow2, pChunkRow3, padfWeightsAligned, nSrcPixelCount,
3262 : dfRes1, dfRes2, dfRes3);
3263 86 : }
3264 :
3265 : /************************************************************************/
3266 : /* GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2<T> */
3267 : /************************************************************************/
3268 :
3269 : template <class T>
3270 7849130 : static inline void GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2(
3271 : const T *pChunkRow1, const T *pChunkRow2, const T *pChunkRow3,
3272 : const double *padfWeightsAligned, int nSrcPixelCount, double &dfRes1,
3273 : double &dfRes2, double &dfRes3)
3274 : {
3275 7849130 : XMMReg4Double v_acc1 = XMMReg4Double::Zero();
3276 7849130 : XMMReg4Double v_acc2 = XMMReg4Double::Zero();
3277 7849130 : XMMReg4Double v_acc3 = XMMReg4Double::Zero();
3278 7849130 : int i = 0; // Use after for.
3279 19113750 : for (; i < nSrcPixelCount - 3; i += 4)
3280 : {
3281 : // Retrieve the pixel & accumulate.
3282 11264600 : const XMMReg4Double v_pixels1 = XMMReg4Double::Load4Val(pChunkRow1 + i);
3283 11264600 : const XMMReg4Double v_pixels2 = XMMReg4Double::Load4Val(pChunkRow2 + i);
3284 11264600 : const XMMReg4Double v_pixels3 = XMMReg4Double::Load4Val(pChunkRow3 + i);
3285 11264600 : const XMMReg4Double v_weight =
3286 11264600 : XMMReg4Double::Load4ValAligned(padfWeightsAligned + i);
3287 :
3288 11264600 : v_acc1 += v_pixels1 * v_weight;
3289 11264600 : v_acc2 += v_pixels2 * v_weight;
3290 11264600 : v_acc3 += v_pixels3 * v_weight;
3291 : }
3292 :
3293 7849130 : dfRes1 = v_acc1.GetHorizSum();
3294 7849130 : dfRes2 = v_acc2.GetHorizSum();
3295 7849130 : dfRes3 = v_acc3.GetHorizSum();
3296 :
3297 12324622 : for (; i < nSrcPixelCount; ++i)
3298 : {
3299 4475542 : dfRes1 += pChunkRow1[i] * padfWeightsAligned[i];
3300 4475542 : dfRes2 += pChunkRow2[i] * padfWeightsAligned[i];
3301 4475542 : dfRes3 += pChunkRow3[i] * padfWeightsAligned[i];
3302 : }
3303 7849130 : }
3304 :
3305 : /************************************************************************/
3306 : /* GDALResampleConvolutionHorizontalPixelCountLess8_3rows<GByte> */
3307 : /************************************************************************/
3308 :
3309 : template <>
3310 : inline void
3311 7781980 : GDALResampleConvolutionHorizontalPixelCountLess8_3rows<GByte, false>(
3312 : const GByte *pChunkRow1, const GByte *pChunkRow2, const GByte *pChunkRow3,
3313 : const double *padfWeightsAligned, int nSrcPixelCount, double &dfRes1,
3314 : double &dfRes2, double &dfRes3)
3315 : {
3316 7781980 : GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2(
3317 : pChunkRow1, pChunkRow2, pChunkRow3, padfWeightsAligned, nSrcPixelCount,
3318 : dfRes1, dfRes2, dfRes3);
3319 7781980 : }
3320 :
3321 : template <>
3322 : inline void
3323 67150 : GDALResampleConvolutionHorizontalPixelCountLess8_3rows<GUInt16, false>(
3324 : const GUInt16 *pChunkRow1, const GUInt16 *pChunkRow2,
3325 : const GUInt16 *pChunkRow3, const double *padfWeightsAligned,
3326 : int nSrcPixelCount, double &dfRes1, double &dfRes2, double &dfRes3)
3327 : {
3328 67150 : GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2(
3329 : pChunkRow1, pChunkRow2, pChunkRow3, padfWeightsAligned, nSrcPixelCount,
3330 : dfRes1, dfRes2, dfRes3);
3331 67150 : }
3332 :
3333 : /************************************************************************/
3334 : /* GDALResampleConvolutionHorizontalPixelCount4_3rows_SSE2<T> */
3335 : /************************************************************************/
3336 :
3337 : template <class T>
3338 14905020 : static inline void GDALResampleConvolutionHorizontalPixelCount4_3rows_SSE2(
3339 : const T *pChunkRow1, const T *pChunkRow2, const T *pChunkRow3,
3340 : const double *padfWeightsAligned, double &dfRes1, double &dfRes2,
3341 : double &dfRes3)
3342 : {
3343 14905020 : const XMMReg4Double v_weight =
3344 : XMMReg4Double::Load4ValAligned(padfWeightsAligned);
3345 :
3346 : // Retrieve the pixel & accumulate.
3347 14905020 : const XMMReg4Double v_pixels1 = XMMReg4Double::Load4Val(pChunkRow1);
3348 14905020 : const XMMReg4Double v_pixels2 = XMMReg4Double::Load4Val(pChunkRow2);
3349 14905020 : const XMMReg4Double v_pixels3 = XMMReg4Double::Load4Val(pChunkRow3);
3350 :
3351 14905020 : XMMReg4Double v_acc1 = v_pixels1 * v_weight;
3352 14905020 : XMMReg4Double v_acc2 = v_pixels2 * v_weight;
3353 14905020 : XMMReg4Double v_acc3 = v_pixels3 * v_weight;
3354 :
3355 14905020 : dfRes1 = v_acc1.GetHorizSum();
3356 14905020 : dfRes2 = v_acc2.GetHorizSum();
3357 14905020 : dfRes3 = v_acc3.GetHorizSum();
3358 14905020 : }
3359 :
3360 : /************************************************************************/
3361 : /* GDALResampleConvolutionHorizontalPixelCount4_3rows<GByte> */
3362 : /************************************************************************/
3363 :
3364 : template <>
3365 9192300 : inline void GDALResampleConvolutionHorizontalPixelCount4_3rows<GByte, false>(
3366 : const GByte *pChunkRow1, const GByte *pChunkRow2, const GByte *pChunkRow3,
3367 : const double *padfWeightsAligned, double &dfRes1, double &dfRes2,
3368 : double &dfRes3)
3369 : {
3370 9192300 : GDALResampleConvolutionHorizontalPixelCount4_3rows_SSE2(
3371 : pChunkRow1, pChunkRow2, pChunkRow3, padfWeightsAligned, dfRes1, dfRes2,
3372 : dfRes3);
3373 9192300 : }
3374 :
3375 : template <>
3376 5712720 : inline void GDALResampleConvolutionHorizontalPixelCount4_3rows<GUInt16, false>(
3377 : const GUInt16 *pChunkRow1, const GUInt16 *pChunkRow2,
3378 : const GUInt16 *pChunkRow3, const double *padfWeightsAligned, double &dfRes1,
3379 : double &dfRes2, double &dfRes3)
3380 : {
3381 5712720 : GDALResampleConvolutionHorizontalPixelCount4_3rows_SSE2(
3382 : pChunkRow1, pChunkRow2, pChunkRow3, padfWeightsAligned, dfRes1, dfRes2,
3383 : dfRes3);
3384 5712720 : }
3385 :
3386 : #endif // USE_SSE2
3387 :
3388 : /************************************************************************/
3389 : /* GDALResampleChunk_Convolution() */
3390 : /************************************************************************/
3391 :
3392 : template <class T, class Twork, GDALDataType eWrkDataType,
3393 : bool bKernelWithNegativeWeights, bool bNeedRescale>
3394 9598 : static CPLErr GDALResampleChunk_ConvolutionT(
3395 : const GDALOverviewResampleArgs &args, const T *pChunk, void *pDstBuffer,
3396 : FilterFuncType pfnFilterFunc, FilterFunc4ValuesType pfnFilterFunc4Values,
3397 : int nKernelRadius, float fMaxVal)
3398 :
3399 : {
3400 9598 : const double dfXRatioDstToSrc = args.dfXRatioDstToSrc;
3401 9598 : const double dfYRatioDstToSrc = args.dfYRatioDstToSrc;
3402 9598 : const double dfSrcXDelta = args.dfSrcXDelta;
3403 9598 : const double dfSrcYDelta = args.dfSrcYDelta;
3404 9598 : constexpr int nBands = 1;
3405 9598 : const GByte *pabyChunkNodataMask = args.pabyChunkNodataMask;
3406 9598 : const int nChunkXOff = args.nChunkXOff;
3407 9598 : const int nChunkXSize = args.nChunkXSize;
3408 9598 : const int nChunkYOff = args.nChunkYOff;
3409 9598 : const int nChunkYSize = args.nChunkYSize;
3410 9598 : const int nDstXOff = args.nDstXOff;
3411 9598 : const int nDstXOff2 = args.nDstXOff2;
3412 9598 : const int nDstYOff = args.nDstYOff;
3413 9598 : const int nDstYOff2 = args.nDstYOff2;
3414 9598 : const bool bHasNoData = args.bHasNoData;
3415 9598 : double dfNoDataValue = args.dfNoDataValue;
3416 :
3417 9598 : if (!bHasNoData)
3418 9499 : dfNoDataValue = 0.0;
3419 9598 : const auto dstDataType = args.eOvrDataType;
3420 9598 : const int nDstDataTypeSize = GDALGetDataTypeSizeBytes(dstDataType);
3421 9598 : const double dfReplacementVal =
3422 99 : bHasNoData ? GDALGetNoDataReplacementValue(dstDataType, dfNoDataValue)
3423 : : dfNoDataValue;
3424 : // cppcheck-suppress unreadVariable
3425 9598 : const int isIntegerDT = GDALDataTypeIsInteger(dstDataType);
3426 9598 : const bool bNoDataValueInt64Valid =
3427 9598 : isIntegerDT && GDALIsValueExactAs<GInt64>(dfNoDataValue);
3428 9598 : const auto nNodataValueInt64 =
3429 : bNoDataValueInt64Valid ? static_cast<GInt64>(dfNoDataValue) : 0;
3430 9598 : constexpr int nWrkDataTypeSize = static_cast<int>(sizeof(Twork));
3431 :
3432 : // TODO: we should have some generic function to do this.
3433 9598 : Twork fDstMin = cpl::NumericLimits<Twork>::lowest();
3434 9598 : Twork fDstMax = cpl::NumericLimits<Twork>::max();
3435 9598 : if (dstDataType == GDT_UInt8)
3436 : {
3437 8668 : fDstMin = std::numeric_limits<GByte>::min();
3438 8668 : fDstMax = std::numeric_limits<GByte>::max();
3439 : }
3440 930 : else if (dstDataType == GDT_Int8)
3441 : {
3442 1 : fDstMin = std::numeric_limits<GInt8>::min();
3443 1 : fDstMax = std::numeric_limits<GInt8>::max();
3444 : }
3445 929 : else if (dstDataType == GDT_UInt16)
3446 : {
3447 402 : fDstMin = std::numeric_limits<GUInt16>::min();
3448 402 : fDstMax = std::numeric_limits<GUInt16>::max();
3449 : }
3450 527 : else if (dstDataType == GDT_Int16)
3451 : {
3452 292 : fDstMin = std::numeric_limits<GInt16>::min();
3453 292 : fDstMax = std::numeric_limits<GInt16>::max();
3454 : }
3455 235 : else if (dstDataType == GDT_UInt32)
3456 : {
3457 1 : fDstMin = static_cast<Twork>(std::numeric_limits<GUInt32>::min());
3458 1 : fDstMax = static_cast<Twork>(std::numeric_limits<GUInt32>::max());
3459 : }
3460 234 : else if (dstDataType == GDT_Int32)
3461 : {
3462 : // cppcheck-suppress unreadVariable
3463 6 : fDstMin = static_cast<Twork>(std::numeric_limits<GInt32>::min());
3464 : // cppcheck-suppress unreadVariable
3465 6 : fDstMax = static_cast<Twork>(std::numeric_limits<GInt32>::max());
3466 : }
3467 228 : else if (dstDataType == GDT_UInt64)
3468 : {
3469 : // cppcheck-suppress unreadVariable
3470 1 : fDstMin = static_cast<Twork>(std::numeric_limits<uint64_t>::min());
3471 : // cppcheck-suppress unreadVariable
3472 : // (1 << 64) - 2048: largest uint64 value a double can hold
3473 1 : fDstMax = static_cast<Twork>(18446744073709549568ULL);
3474 : }
3475 227 : else if (dstDataType == GDT_Int64)
3476 : {
3477 : // cppcheck-suppress unreadVariable
3478 1 : fDstMin = static_cast<Twork>(std::numeric_limits<int64_t>::min());
3479 : // cppcheck-suppress unreadVariable
3480 : // (1 << 63) - 1024: largest int64 that a double can hold
3481 1 : fDstMax = static_cast<Twork>(9223372036854774784LL);
3482 : }
3483 :
3484 9598 : bool bHasNaN = false;
3485 490 : if (pabyChunkNodataMask)
3486 : {
3487 : if constexpr (std::is_floating_point_v<T>)
3488 : {
3489 120140 : for (size_t i = 0;
3490 120140 : i < static_cast<size_t>(nChunkXSize) * nChunkYSize; ++i)
3491 : {
3492 120122 : if (std::isnan(pChunk[i]))
3493 : {
3494 24 : bHasNaN = true;
3495 24 : break;
3496 : }
3497 : }
3498 : }
3499 : }
3500 :
3501 37413247 : auto replaceValIfNodata = [bHasNoData, isIntegerDT, fDstMin, fDstMax,
3502 : bNoDataValueInt64Valid, nNodataValueInt64,
3503 : dfNoDataValue, dfReplacementVal](Twork fVal)
3504 : {
3505 16299800 : if (!bHasNoData)
3506 12078600 : return fVal;
3507 :
3508 : // Clamp value before comparing to nodata: this is only needed for
3509 : // kernels with negative weights (Lanczos)
3510 4221160 : Twork fClamped = fVal;
3511 4221160 : if (fClamped < fDstMin)
3512 14504 : fClamped = fDstMin;
3513 4206660 : else if (fClamped > fDstMax)
3514 13638 : fClamped = fDstMax;
3515 4221160 : if (isIntegerDT)
3516 : {
3517 4220480 : if (bNoDataValueInt64Valid)
3518 : {
3519 4220470 : const double fClampedRounded = double(std::round(fClamped));
3520 8440960 : if (fClampedRounded >=
3521 : static_cast<double>(static_cast<Twork>(
3522 8440960 : std::numeric_limits<int64_t>::min())) &&
3523 : fClampedRounded <= static_cast<double>(static_cast<Twork>(
3524 8440960 : 9223372036854774784LL)) &&
3525 4220470 : nNodataValueInt64 ==
3526 4220480 : static_cast<GInt64>(std::round(fClamped)))
3527 : {
3528 : // Do not use the nodata value
3529 13195 : return static_cast<Twork>(dfReplacementVal);
3530 : }
3531 : }
3532 : }
3533 679 : else if (dfNoDataValue == static_cast<double>(fClamped))
3534 : {
3535 : // Do not use the nodata value
3536 1 : return static_cast<Twork>(dfReplacementVal);
3537 : }
3538 4207960 : return fClamped;
3539 : };
3540 :
3541 : /* -------------------------------------------------------------------- */
3542 : /* Allocate work buffers. */
3543 : /* -------------------------------------------------------------------- */
3544 9598 : const int nDstXSize = nDstXOff2 - nDstXOff;
3545 9598 : Twork *pafWrkScanline = nullptr;
3546 9598 : if (dstDataType != eWrkDataType)
3547 : {
3548 : pafWrkScanline =
3549 9386 : static_cast<Twork *>(VSI_MALLOC2_VERBOSE(nDstXSize, sizeof(Twork)));
3550 9386 : if (pafWrkScanline == nullptr)
3551 0 : return CE_Failure;
3552 : }
3553 :
3554 9598 : const double dfXScale = 1.0 / dfXRatioDstToSrc;
3555 9598 : const double dfXScaleWeight = (dfXScale >= 1.0) ? 1.0 : dfXScale;
3556 9598 : const double dfXScaledRadius = nKernelRadius / dfXScaleWeight;
3557 9598 : const double dfYScale = 1.0 / dfYRatioDstToSrc;
3558 9598 : const double dfYScaleWeight = (dfYScale >= 1.0) ? 1.0 : dfYScale;
3559 9598 : const double dfYScaledRadius = nKernelRadius / dfYScaleWeight;
3560 :
3561 9598 : const uint64_t nWeightCount = static_cast<uint64_t>(
3562 9598 : 2 + 2 * std::max(dfXScaledRadius, dfYScaledRadius) + 0.5);
3563 9598 : if (nWeightCount > std::numeric_limits<uint32_t>::max() / sizeof(double))
3564 : {
3565 0 : VSIFree(pafWrkScanline);
3566 0 : CPLError(CE_Failure, CPLE_NotSupported,
3567 : "Too large downsampling factor");
3568 0 : return CE_Failure;
3569 : }
3570 :
3571 : // Temporary array to store result of horizontal filter.
3572 : double *const padfHorizontalFiltered = static_cast<double *>(
3573 9598 : VSI_MALLOC3_VERBOSE(nChunkYSize, nDstXSize, sizeof(double) * nBands));
3574 : // To store convolution coefficients.
3575 : double *const padfWeights =
3576 9598 : static_cast<double *>(VSI_MALLOC_ALIGNED_AUTO_VERBOSE(
3577 : static_cast<size_t>(nWeightCount) * sizeof(double)));
3578 :
3579 9598 : GByte *pabyChunkNodataMaskHorizontalFiltered = nullptr;
3580 9598 : if (pabyChunkNodataMask)
3581 : pabyChunkNodataMaskHorizontalFiltered =
3582 3357 : static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nChunkYSize, nDstXSize));
3583 9598 : if (padfHorizontalFiltered == nullptr || padfWeights == nullptr ||
3584 3357 : (pabyChunkNodataMask != nullptr &&
3585 : pabyChunkNodataMaskHorizontalFiltered == nullptr))
3586 : {
3587 0 : VSIFree(pafWrkScanline);
3588 0 : VSIFree(padfHorizontalFiltered);
3589 0 : VSIFreeAligned(padfWeights);
3590 0 : VSIFree(pabyChunkNodataMaskHorizontalFiltered);
3591 0 : return CE_Failure;
3592 : }
3593 :
3594 : /* ==================================================================== */
3595 : /* First pass: horizontal filter */
3596 : /* ==================================================================== */
3597 9598 : const int nChunkRightXOff = nChunkXOff + nChunkXSize;
3598 : #ifdef USE_SSE2
3599 9598 : const bool bSrcPixelCountLess8 = dfXScaledRadius < 4;
3600 : #endif
3601 3723654 : for (int iDstPixel = nDstXOff; iDstPixel < nDstXOff2; ++iDstPixel)
3602 : {
3603 3714051 : const double dfSrcPixel =
3604 3714051 : (iDstPixel + 0.5) * dfXRatioDstToSrc + dfSrcXDelta;
3605 3714051 : const int nSrcPixelStart = std::max(
3606 3714051 : static_cast<int>(floor(dfSrcPixel - dfXScaledRadius + 0.5)),
3607 3714051 : nChunkXOff);
3608 3714051 : const int nSrcPixelStop =
3609 3714051 : std::min(static_cast<int>(dfSrcPixel + dfXScaledRadius + 0.5),
3610 3714051 : nChunkRightXOff);
3611 : #if 0
3612 : if( nSrcPixelStart < nChunkXOff && nChunkXOff > 0 )
3613 : {
3614 : printf( "truncated iDstPixel = %d\n", iDstPixel );/*ok*/
3615 : }
3616 : if( nSrcPixelStop > nChunkRightXOff && nChunkRightXOff < nSrcWidth )
3617 : {
3618 : printf( "truncated iDstPixel = %d\n", iDstPixel );/*ok*/
3619 : }
3620 : #endif
3621 3714051 : const int nSrcPixelCount = nSrcPixelStop - nSrcPixelStart;
3622 3714051 : double dfWeightSum = 0.0;
3623 :
3624 : // Compute convolution coefficients.
3625 3714051 : int nSrcPixel = nSrcPixelStart;
3626 3714051 : double dfX = dfXScaleWeight * (nSrcPixel - dfSrcPixel + 0.5);
3627 5823956 : for (; nSrcPixel < nSrcPixelStop - 3; nSrcPixel += 4)
3628 : {
3629 2109902 : padfWeights[nSrcPixel - nSrcPixelStart] = dfX;
3630 2109902 : dfX += dfXScaleWeight;
3631 2109902 : padfWeights[nSrcPixel + 1 - nSrcPixelStart] = dfX;
3632 2109902 : dfX += dfXScaleWeight;
3633 2109902 : padfWeights[nSrcPixel + 2 - nSrcPixelStart] = dfX;
3634 2109902 : dfX += dfXScaleWeight;
3635 2109902 : padfWeights[nSrcPixel + 3 - nSrcPixelStart] = dfX;
3636 2109902 : dfX += dfXScaleWeight;
3637 2109902 : dfWeightSum +=
3638 2109902 : pfnFilterFunc4Values(padfWeights + nSrcPixel - nSrcPixelStart);
3639 : }
3640 7719197 : for (; nSrcPixel < nSrcPixelStop; ++nSrcPixel, dfX += dfXScaleWeight)
3641 : {
3642 4005146 : const double dfWeight = pfnFilterFunc(dfX);
3643 4005146 : padfWeights[nSrcPixel - nSrcPixelStart] = dfWeight;
3644 4005146 : dfWeightSum += dfWeight;
3645 : }
3646 :
3647 3714051 : const int nHeight = nChunkYSize * nBands;
3648 3714051 : if (pabyChunkNodataMask == nullptr)
3649 : {
3650 : // For floating-point data types, we must scale down a bit values
3651 : // if input values are close to +/- std::numeric_limits<T>::max()
3652 : #ifdef OLD_CPPCHECK
3653 : constexpr double mulFactor = 1;
3654 : #else
3655 3192042 : constexpr double mulFactor =
3656 : (bNeedRescale &&
3657 : (std::is_same_v<T, float> || std::is_same_v<T, double>))
3658 : ? 2
3659 : : 1;
3660 : #endif
3661 :
3662 3192042 : if (dfWeightSum != 0)
3663 : {
3664 3192042 : const double dfInvWeightSum = 1.0 / (mulFactor * dfWeightSum);
3665 13087314 : for (int i = 0; i < nSrcPixelCount; ++i)
3666 : {
3667 9895271 : padfWeights[i] *= dfInvWeightSum;
3668 : }
3669 : }
3670 :
3671 182388430 : const auto ScaleValue = [
3672 : #ifdef _MSC_VER
3673 : mulFactor
3674 : #endif
3675 : ](double dfVal, [[maybe_unused]] const T *inputValues,
3676 : [[maybe_unused]] int nInputValues)
3677 : {
3678 182389000 : constexpr bool isFloat =
3679 : std::is_same_v<T, float> || std::is_same_v<T, double>;
3680 : if constexpr (isFloat)
3681 : {
3682 4070140 : if (std::isfinite(dfVal))
3683 : {
3684 : return std::clamp(dfVal,
3685 12204800 : -std::numeric_limits<double>::max() /
3686 : mulFactor,
3687 4068260 : std::numeric_limits<double>::max() /
3688 4068260 : mulFactor) *
3689 4068260 : mulFactor;
3690 : }
3691 : else if constexpr (bKernelWithNegativeWeights)
3692 : {
3693 936 : if (std::isnan(dfVal))
3694 : {
3695 : // Either one of the input value is NaN or they are +/-Inf
3696 936 : const bool isPositive = inputValues[0] >= 0;
3697 6008 : for (int i = 0; i < nInputValues; ++i)
3698 : {
3699 5384 : if (std::isnan(inputValues[i]))
3700 312 : return dfVal;
3701 : // cppcheck-suppress knownConditionTrueFalse
3702 5072 : if ((inputValues[i] >= 0) != isPositive)
3703 0 : return dfVal;
3704 : }
3705 : // All values are positive or negative infinity
3706 624 : return static_cast<double>(inputValues[0]);
3707 : }
3708 : }
3709 : }
3710 178320000 : return dfVal;
3711 : };
3712 :
3713 3192042 : int iSrcLineOff = 0;
3714 : #ifdef USE_SSE2
3715 3192042 : if (nSrcPixelCount == 4)
3716 : {
3717 17007339 : for (; iSrcLineOff < nHeight - 2; iSrcLineOff += 3)
3718 : {
3719 16161708 : const size_t j =
3720 16161708 : static_cast<size_t>(iSrcLineOff) * nChunkXSize +
3721 16161708 : (nSrcPixelStart - nChunkXOff);
3722 16161708 : double dfVal1 = 0.0;
3723 16161708 : double dfVal2 = 0.0;
3724 16161708 : double dfVal3 = 0.0;
3725 : if constexpr (std::is_floating_point_v<T>)
3726 : {
3727 1256690 : if (bHasNaN)
3728 : {
3729 : GDALResampleConvolutionHorizontalPixelCount4_3rows<
3730 0 : T, true>(pChunk + j, pChunk + j + nChunkXSize,
3731 0 : pChunk + j + 2 * nChunkXSize,
3732 : padfWeights, dfVal1, dfVal2, dfVal3);
3733 : }
3734 : else
3735 : {
3736 : GDALResampleConvolutionHorizontalPixelCount4_3rows<
3737 1256690 : T, false>(pChunk + j, pChunk + j + nChunkXSize,
3738 1256690 : pChunk + j + 2 * nChunkXSize,
3739 : padfWeights, dfVal1, dfVal2, dfVal3);
3740 : }
3741 : }
3742 : else
3743 : {
3744 : GDALResampleConvolutionHorizontalPixelCount4_3rows<
3745 14905018 : T, false>(pChunk + j, pChunk + j + nChunkXSize,
3746 14905018 : pChunk + j + 2 * nChunkXSize, padfWeights,
3747 : dfVal1, dfVal2, dfVal3);
3748 : }
3749 32323380 : padfHorizontalFiltered[static_cast<size_t>(iSrcLineOff) *
3750 16161708 : nDstXSize +
3751 16161708 : iDstPixel - nDstXOff] =
3752 16161708 : ScaleValue(dfVal1, pChunk + j, 4);
3753 32323380 : padfHorizontalFiltered[(static_cast<size_t>(iSrcLineOff) +
3754 16161708 : 1) *
3755 16161708 : nDstXSize +
3756 16161708 : iDstPixel - nDstXOff] =
3757 16161708 : ScaleValue(dfVal2, pChunk + j + nChunkXSize, 4);
3758 16162117 : padfHorizontalFiltered[(static_cast<size_t>(iSrcLineOff) +
3759 16161708 : 2) *
3760 16161708 : nDstXSize +
3761 16161708 : iDstPixel - nDstXOff] =
3762 16161708 : ScaleValue(dfVal3, pChunk + j + 2 * nChunkXSize, 4);
3763 : }
3764 : }
3765 2346410 : else if (bSrcPixelCountLess8)
3766 : {
3767 9938318 : for (; iSrcLineOff < nHeight - 2; iSrcLineOff += 3)
3768 : {
3769 7868108 : const size_t j =
3770 7868108 : static_cast<size_t>(iSrcLineOff) * nChunkXSize +
3771 7868108 : (nSrcPixelStart - nChunkXOff);
3772 7868108 : double dfVal1 = 0.0;
3773 7868108 : double dfVal2 = 0.0;
3774 7868108 : double dfVal3 = 0.0;
3775 : if constexpr (std::is_floating_point_v<T>)
3776 : {
3777 18980 : if (bHasNaN)
3778 : {
3779 : GDALResampleConvolutionHorizontalPixelCountLess8_3rows<
3780 0 : T, true>(pChunk + j, pChunk + j + nChunkXSize,
3781 0 : pChunk + j + 2 * nChunkXSize,
3782 : padfWeights, nSrcPixelCount, dfVal1,
3783 : dfVal2, dfVal3);
3784 : }
3785 : else
3786 : {
3787 : GDALResampleConvolutionHorizontalPixelCountLess8_3rows<
3788 18980 : T, false>(pChunk + j, pChunk + j + nChunkXSize,
3789 18980 : pChunk + j + 2 * nChunkXSize,
3790 : padfWeights, nSrcPixelCount, dfVal1,
3791 : dfVal2, dfVal3);
3792 : }
3793 : }
3794 : else
3795 : {
3796 : GDALResampleConvolutionHorizontalPixelCountLess8_3rows<
3797 7849128 : T, false>(pChunk + j, pChunk + j + nChunkXSize,
3798 7849128 : pChunk + j + 2 * nChunkXSize, padfWeights,
3799 : nSrcPixelCount, dfVal1, dfVal2, dfVal3);
3800 : }
3801 15736256 : padfHorizontalFiltered[static_cast<size_t>(iSrcLineOff) *
3802 7868108 : nDstXSize +
3803 7868108 : iDstPixel - nDstXOff] =
3804 7868108 : ScaleValue(dfVal1, pChunk + j, nSrcPixelCount);
3805 15736256 : padfHorizontalFiltered[(static_cast<size_t>(iSrcLineOff) +
3806 7868108 : 1) *
3807 7868108 : nDstXSize +
3808 7868108 : iDstPixel - nDstXOff] =
3809 7868108 : ScaleValue(dfVal2, pChunk + j + nChunkXSize,
3810 : nSrcPixelCount);
3811 7868196 : padfHorizontalFiltered[(static_cast<size_t>(iSrcLineOff) +
3812 7868108 : 2) *
3813 7868108 : nDstXSize +
3814 7868108 : iDstPixel - nDstXOff] =
3815 7868108 : ScaleValue(dfVal3, pChunk + j + 2 * nChunkXSize,
3816 : nSrcPixelCount);
3817 : }
3818 : }
3819 : else
3820 : #endif
3821 : {
3822 35902058 : for (; iSrcLineOff < nHeight - 2; iSrcLineOff += 3)
3823 : {
3824 35625944 : const size_t j =
3825 35625944 : static_cast<size_t>(iSrcLineOff) * nChunkXSize +
3826 35625944 : (nSrcPixelStart - nChunkXOff);
3827 35625944 : double dfVal1 = 0.0;
3828 35625944 : double dfVal2 = 0.0;
3829 35625944 : double dfVal3 = 0.0;
3830 : if constexpr (std::is_floating_point_v<T>)
3831 : {
3832 65696 : if (bHasNaN)
3833 : {
3834 0 : GDALResampleConvolutionHorizontal_3rows<T, true>(
3835 0 : pChunk + j, pChunk + j + nChunkXSize,
3836 0 : pChunk + j + 2 * nChunkXSize, padfWeights,
3837 : nSrcPixelCount, dfVal1, dfVal2, dfVal3);
3838 : }
3839 : else
3840 : {
3841 65696 : GDALResampleConvolutionHorizontal_3rows<T, false>(
3842 65696 : pChunk + j, pChunk + j + nChunkXSize,
3843 65696 : pChunk + j + 2 * nChunkXSize, padfWeights,
3844 : nSrcPixelCount, dfVal1, dfVal2, dfVal3);
3845 : }
3846 : }
3847 : else
3848 : {
3849 35560248 : GDALResampleConvolutionHorizontal_3rows<T, false>(
3850 35560248 : pChunk + j, pChunk + j + nChunkXSize,
3851 35560248 : pChunk + j + 2 * nChunkXSize, padfWeights,
3852 : nSrcPixelCount, dfVal1, dfVal2, dfVal3);
3853 : }
3854 71251798 : padfHorizontalFiltered[static_cast<size_t>(iSrcLineOff) *
3855 35625944 : nDstXSize +
3856 35625944 : iDstPixel - nDstXOff] =
3857 35625944 : ScaleValue(dfVal1, pChunk + j, nSrcPixelCount);
3858 71251798 : padfHorizontalFiltered[(static_cast<size_t>(iSrcLineOff) +
3859 35625944 : 1) *
3860 35625944 : nDstXSize +
3861 35625944 : iDstPixel - nDstXOff] =
3862 35625944 : ScaleValue(dfVal2, pChunk + j + nChunkXSize,
3863 : nSrcPixelCount);
3864 35691048 : padfHorizontalFiltered[(static_cast<size_t>(iSrcLineOff) +
3865 35625944 : 2) *
3866 35625944 : nDstXSize +
3867 35625944 : iDstPixel - nDstXOff] =
3868 35625944 : ScaleValue(dfVal3, pChunk + j + 2 * nChunkXSize,
3869 : nSrcPixelCount);
3870 : }
3871 : }
3872 6613770 : for (; iSrcLineOff < nHeight; ++iSrcLineOff)
3873 : {
3874 3421743 : const size_t j =
3875 3421743 : static_cast<size_t>(iSrcLineOff) * nChunkXSize +
3876 3421743 : (nSrcPixelStart - nChunkXOff);
3877 3970903 : const double dfVal = GDALResampleConvolutionHorizontal(
3878 595200 : pChunk + j, padfWeights, nSrcPixelCount);
3879 3422192 : padfHorizontalFiltered[static_cast<size_t>(iSrcLineOff) *
3880 3421743 : nDstXSize +
3881 3421743 : iDstPixel - nDstXOff] =
3882 3421743 : ScaleValue(dfVal, pChunk + j, nSrcPixelCount);
3883 : }
3884 : }
3885 : else
3886 : {
3887 32759623 : for (int iSrcLineOff = 0; iSrcLineOff < nHeight; ++iSrcLineOff)
3888 : {
3889 32237528 : const size_t j =
3890 32237528 : static_cast<size_t>(iSrcLineOff) * nChunkXSize +
3891 32237528 : (nSrcPixelStart - nChunkXOff);
3892 :
3893 : if (bKernelWithNegativeWeights)
3894 : {
3895 27492508 : int nConsecutiveValid = 0;
3896 27492508 : int nMaxConsecutiveValid = 0;
3897 747674146 : for (int k = 0; k < nSrcPixelCount; k++)
3898 : {
3899 720181938 : if (pabyChunkNodataMask[j + k])
3900 43694301 : nConsecutiveValid++;
3901 676487837 : else if (nConsecutiveValid)
3902 : {
3903 107658 : nMaxConsecutiveValid = std::max(
3904 107658 : nMaxConsecutiveValid, nConsecutiveValid);
3905 107658 : nConsecutiveValid = 0;
3906 : }
3907 : }
3908 27492508 : nMaxConsecutiveValid =
3909 27492508 : std::max(nMaxConsecutiveValid, nConsecutiveValid);
3910 27492508 : if (nMaxConsecutiveValid < nSrcPixelCount / 2)
3911 : {
3912 21564707 : const size_t nTempOffset =
3913 21564707 : static_cast<size_t>(iSrcLineOff) * nDstXSize +
3914 21564707 : iDstPixel - nDstXOff;
3915 21564707 : padfHorizontalFiltered[nTempOffset] = 0.0;
3916 21564707 : pabyChunkNodataMaskHorizontalFiltered[nTempOffset] = 0;
3917 21564707 : continue;
3918 : }
3919 : }
3920 :
3921 10672871 : double dfSumWeightedVal = 0.0;
3922 10672871 : double dfSumWeightedAlpha = 0.0;
3923 : if constexpr (std::is_floating_point_v<T>)
3924 : {
3925 46368 : if (bHasNaN)
3926 : {
3927 1792 : GDALResampleConvolutionHorizontalWithMask<T, true>(
3928 1792 : pChunk + j, pabyChunkNodataMask + j, padfWeights,
3929 : nSrcPixelCount, dfSumWeightedVal,
3930 : dfSumWeightedAlpha, dfWeightSum);
3931 : }
3932 : else
3933 : {
3934 44576 : GDALResampleConvolutionHorizontalWithMask<T, false>(
3935 44576 : pChunk + j, pabyChunkNodataMask + j, padfWeights,
3936 : nSrcPixelCount, dfSumWeightedVal,
3937 : dfSumWeightedAlpha, dfWeightSum);
3938 : }
3939 : }
3940 : else
3941 : {
3942 10626503 : GDALResampleConvolutionHorizontalWithMask<T, false>(
3943 63 : pChunk + j, pabyChunkNodataMask + j, padfWeights,
3944 : nSrcPixelCount, dfSumWeightedVal, dfSumWeightedAlpha,
3945 : dfWeightSum);
3946 : }
3947 10672871 : const size_t nTempOffset =
3948 10672871 : static_cast<size_t>(iSrcLineOff) * nDstXSize + iDstPixel -
3949 10672871 : nDstXOff;
3950 10672871 : if (dfSumWeightedAlpha > 0.0)
3951 : {
3952 8760088 : padfHorizontalFiltered[nTempOffset] =
3953 8760088 : dfSumWeightedVal / dfSumWeightedAlpha;
3954 : // Not entirely clear if clamping values in the horizontal filter
3955 : // is the right thing to do, but otherwise, for
3956 : // https://github.com/OSGeo/gdal/issues/14728
3957 : // with very small values of alpha, we get very strong under
3958 : // and over shoots.
3959 : if constexpr (std::is_same_v<T, uint8_t>)
3960 : {
3961 8713690 : padfHorizontalFiltered[nTempOffset] = std::clamp(
3962 8713690 : padfHorizontalFiltered[nTempOffset], 0.0, 255.0);
3963 : }
3964 : else if constexpr (std::is_same_v<T, uint16_t>)
3965 : {
3966 60 : padfHorizontalFiltered[nTempOffset] = std::clamp(
3967 60 : padfHorizontalFiltered[nTempOffset], 0.0, 65535.0);
3968 : }
3969 8760088 : const double dfAlpha = dfSumWeightedAlpha / dfWeightSum;
3970 8760088 : pabyChunkNodataMaskHorizontalFiltered[nTempOffset] =
3971 8760088 : static_cast<uint8_t>(std::min(dfAlpha + 0.5, 255.0));
3972 : }
3973 : else
3974 : {
3975 1912797 : padfHorizontalFiltered[nTempOffset] = 0.0;
3976 1912797 : pabyChunkNodataMaskHorizontalFiltered[nTempOffset] = 0;
3977 : }
3978 : }
3979 : }
3980 : }
3981 :
3982 : /* ==================================================================== */
3983 : /* Second pass: vertical filter */
3984 : /* ==================================================================== */
3985 9598 : const int nChunkBottomYOff = nChunkYOff + nChunkYSize;
3986 :
3987 414144 : for (int iDstLine = nDstYOff; iDstLine < nDstYOff2; ++iDstLine)
3988 : {
3989 404546 : Twork *const pafDstScanline =
3990 : pafWrkScanline
3991 404546 : ? pafWrkScanline
3992 14028 : : static_cast<Twork *>(pDstBuffer) +
3993 14028 : static_cast<size_t>(iDstLine - nDstYOff) * nDstXSize;
3994 :
3995 404546 : const double dfSrcLine =
3996 404546 : (iDstLine + 0.5) * dfYRatioDstToSrc + dfSrcYDelta;
3997 404546 : const int nSrcLineStart =
3998 404546 : std::max(static_cast<int>(floor(dfSrcLine - dfYScaledRadius + 0.5)),
3999 404546 : nChunkYOff);
4000 404546 : const int nSrcLineStop =
4001 404546 : std::min(static_cast<int>(dfSrcLine + dfYScaledRadius + 0.5),
4002 404546 : nChunkBottomYOff);
4003 : #if 0
4004 : if( nSrcLineStart < nChunkYOff &&
4005 : nChunkYOff > 0 )
4006 : {
4007 : printf( "truncated iDstLine = %d\n", iDstLine );/*ok*/
4008 : }
4009 : if( nSrcLineStop > nChunkBottomYOff && nChunkBottomYOff < nSrcHeight )
4010 : {
4011 : printf( "truncated iDstLine = %d\n", iDstLine );/*ok*/
4012 : }
4013 : #endif
4014 404546 : const int nSrcLineCount = nSrcLineStop - nSrcLineStart;
4015 404546 : double dfWeightSum = 0.0;
4016 :
4017 : // Compute convolution coefficients.
4018 404546 : int nSrcLine = nSrcLineStart; // Used after for.
4019 404546 : double dfY = dfYScaleWeight * (nSrcLine - dfSrcLine + 0.5);
4020 1076799 : for (; nSrcLine < nSrcLineStop - 3;
4021 672253 : nSrcLine += 4, dfY += 4 * dfYScaleWeight)
4022 : {
4023 672253 : padfWeights[nSrcLine - nSrcLineStart] = dfY;
4024 672253 : padfWeights[nSrcLine + 1 - nSrcLineStart] = dfY + dfYScaleWeight;
4025 672253 : padfWeights[nSrcLine + 2 - nSrcLineStart] =
4026 672253 : dfY + 2 * dfYScaleWeight;
4027 672253 : padfWeights[nSrcLine + 3 - nSrcLineStart] =
4028 672253 : dfY + 3 * dfYScaleWeight;
4029 672253 : dfWeightSum +=
4030 672253 : pfnFilterFunc4Values(padfWeights + nSrcLine - nSrcLineStart);
4031 : }
4032 443440 : for (; nSrcLine < nSrcLineStop; ++nSrcLine, dfY += dfYScaleWeight)
4033 : {
4034 38894 : const double dfWeight = pfnFilterFunc(dfY);
4035 38894 : padfWeights[nSrcLine - nSrcLineStart] = dfWeight;
4036 38894 : dfWeightSum += dfWeight;
4037 : }
4038 :
4039 404546 : if (pabyChunkNodataMask == nullptr)
4040 : {
4041 : // For floating-point data types, we must scale down a bit values
4042 : // if input values are close to +/- std::numeric_limits<T>::max()
4043 : #ifdef OLD_CPPCHECK
4044 : constexpr double mulFactor = 1;
4045 : #else
4046 360194 : constexpr double mulFactor =
4047 : (bNeedRescale &&
4048 : (std::is_same_v<T, float> || std::is_same_v<T, double>))
4049 : ? 2
4050 : : 1;
4051 : #endif
4052 :
4053 360194 : if (dfWeightSum != 0)
4054 : {
4055 360194 : const double dfInvWeightSum = 1.0 / (mulFactor * dfWeightSum);
4056 2617663 : for (int i = 0; i < nSrcLineCount; ++i)
4057 2257467 : padfWeights[i] *= dfInvWeightSum;
4058 : }
4059 :
4060 360194 : int iFilteredPixelOff = 0; // Used after for.
4061 : // j used after for.
4062 360194 : size_t j =
4063 360194 : (nSrcLineStart - nChunkYOff) * static_cast<size_t>(nDstXSize);
4064 : #ifdef USE_SSE2
4065 : if constexpr ((!bNeedRescale || !std::is_same_v<T, float>) &&
4066 : eWrkDataType == GDT_Float32)
4067 : {
4068 : #ifdef __AVX__
4069 : for (; iFilteredPixelOff < nDstXSize - 15;
4070 : iFilteredPixelOff += 16, j += 16)
4071 : {
4072 : GDALResampleConvolutionVertical_16cols(
4073 : padfHorizontalFiltered + j, nDstXSize, padfWeights,
4074 : nSrcLineCount, pafDstScanline + iFilteredPixelOff);
4075 : if (bHasNoData)
4076 : {
4077 : for (int k = 0; k < 16; k++)
4078 : {
4079 : pafDstScanline[iFilteredPixelOff + k] =
4080 : replaceValIfNodata(
4081 : pafDstScanline[iFilteredPixelOff + k]);
4082 : }
4083 : }
4084 : }
4085 : #else
4086 26155459 : for (; iFilteredPixelOff < nDstXSize - 7;
4087 : iFilteredPixelOff += 8, j += 8)
4088 : {
4089 25804048 : GDALResampleConvolutionVertical_8cols(
4090 25804048 : padfHorizontalFiltered + j, nDstXSize, padfWeights,
4091 25804048 : nSrcLineCount, pafDstScanline + iFilteredPixelOff);
4092 25804048 : if (bHasNoData)
4093 : {
4094 123192 : for (int k = 0; k < 8; k++)
4095 : {
4096 109504 : pafDstScanline[iFilteredPixelOff + k] =
4097 109504 : replaceValIfNodata(
4098 109504 : pafDstScanline[iFilteredPixelOff + k]);
4099 : }
4100 : }
4101 : }
4102 : #endif
4103 :
4104 822507 : for (; iFilteredPixelOff < nDstXSize; iFilteredPixelOff++, j++)
4105 : {
4106 471132 : const Twork fVal =
4107 471132 : static_cast<Twork>(GDALResampleConvolutionVertical(
4108 471132 : padfHorizontalFiltered + j, nDstXSize, padfWeights,
4109 : nSrcLineCount));
4110 471132 : pafDstScanline[iFilteredPixelOff] =
4111 471132 : replaceValIfNodata(fVal);
4112 : }
4113 : }
4114 : else
4115 : #endif
4116 : {
4117 5862642 : const auto ScaleValue = [
4118 : #ifdef _MSC_VER
4119 : mulFactor
4120 : #endif
4121 : ](double dfVal, [[maybe_unused]] const double *inputValues,
4122 : [[maybe_unused]] int nStride,
4123 : [[maybe_unused]] int nInputValues)
4124 : {
4125 5862640 : constexpr bool isFloat =
4126 : std::is_same_v<T, float> || std::is_same_v<T, double>;
4127 : if constexpr (isFloat)
4128 : {
4129 5862640 : if (std::isfinite(dfVal))
4130 : {
4131 : return std::clamp(
4132 : dfVal,
4133 : static_cast<double>(
4134 17585400 : -std::numeric_limits<Twork>::max()) /
4135 : mulFactor,
4136 : static_cast<double>(
4137 5861800 : std::numeric_limits<Twork>::max()) /
4138 5861800 : mulFactor) *
4139 5861800 : mulFactor;
4140 : }
4141 : else if constexpr (bKernelWithNegativeWeights)
4142 : {
4143 480 : if (std::isnan(dfVal))
4144 : {
4145 : // Either one of the input value is NaN or they are +/-Inf
4146 480 : const bool isPositive = inputValues[0] >= 0;
4147 2520 : for (int i = 0; i < nInputValues; ++i)
4148 : {
4149 2200 : if (std::isnan(inputValues[i * nStride]))
4150 160 : return dfVal;
4151 : // cppcheck-suppress knownConditionTrueFalse
4152 2040 : if ((inputValues[i] >= 0) != isPositive)
4153 0 : return dfVal;
4154 : }
4155 : // All values are positive or negative infinity
4156 320 : return inputValues[0];
4157 : }
4158 : }
4159 : }
4160 :
4161 360 : return dfVal;
4162 : };
4163 :
4164 2939422 : for (; iFilteredPixelOff < nDstXSize - 1;
4165 : iFilteredPixelOff += 2, j += 2)
4166 : {
4167 2930610 : double dfVal1 = 0.0;
4168 2930610 : double dfVal2 = 0.0;
4169 2930610 : GDALResampleConvolutionVertical_2cols(
4170 2930610 : padfHorizontalFiltered + j, nDstXSize, padfWeights,
4171 : nSrcLineCount, dfVal1, dfVal2);
4172 5861220 : pafDstScanline[iFilteredPixelOff] =
4173 2930610 : replaceValIfNodata(static_cast<Twork>(
4174 2930610 : ScaleValue(dfVal1, padfHorizontalFiltered + j,
4175 : nDstXSize, nSrcLineCount)));
4176 2930610 : pafDstScanline[iFilteredPixelOff + 1] =
4177 2930610 : replaceValIfNodata(static_cast<Twork>(
4178 2930610 : ScaleValue(dfVal2, padfHorizontalFiltered + j + 1,
4179 : nDstXSize, nSrcLineCount)));
4180 : }
4181 8819 : if (iFilteredPixelOff < nDstXSize)
4182 : {
4183 1427 : const double dfVal = GDALResampleConvolutionVertical(
4184 1427 : padfHorizontalFiltered + j, nDstXSize, padfWeights,
4185 : nSrcLineCount);
4186 1427 : pafDstScanline[iFilteredPixelOff] =
4187 1427 : replaceValIfNodata(static_cast<Twork>(
4188 1427 : ScaleValue(dfVal, padfHorizontalFiltered + j,
4189 : nDstXSize, nSrcLineCount)));
4190 : }
4191 : }
4192 : }
4193 : else
4194 : {
4195 19948965 : for (int iFilteredPixelOff = 0; iFilteredPixelOff < nDstXSize;
4196 : ++iFilteredPixelOff)
4197 : {
4198 19904685 : double dfVal = 0.0;
4199 19904685 : dfWeightSum = 0.0;
4200 19904685 : size_t j = (nSrcLineStart - nChunkYOff) *
4201 19904685 : static_cast<size_t>(nDstXSize) +
4202 19904685 : iFilteredPixelOff;
4203 : if (bKernelWithNegativeWeights)
4204 : {
4205 18637437 : int nConsecutiveValid = 0;
4206 18637437 : int nMaxConsecutiveValid = 0;
4207 162845921 : for (int i = 0; i < nSrcLineCount; ++i, j += nDstXSize)
4208 : {
4209 144208284 : const double dfWeight =
4210 144208284 : padfWeights[i] *
4211 : pabyChunkNodataMaskHorizontalFiltered[j];
4212 144208284 : if (pabyChunkNodataMaskHorizontalFiltered[j])
4213 : {
4214 45969501 : nConsecutiveValid++;
4215 : }
4216 98238683 : else if (nConsecutiveValid)
4217 : {
4218 211128 : nMaxConsecutiveValid = std::max(
4219 211128 : nMaxConsecutiveValid, nConsecutiveValid);
4220 211128 : nConsecutiveValid = 0;
4221 : }
4222 144208284 : dfVal += padfHorizontalFiltered[j] * dfWeight;
4223 144208284 : dfWeightSum += dfWeight;
4224 : }
4225 18637437 : nMaxConsecutiveValid =
4226 18637437 : std::max(nMaxConsecutiveValid, nConsecutiveValid);
4227 18637437 : if (nMaxConsecutiveValid < nSrcLineCount / 2)
4228 : {
4229 9501801 : pafDstScanline[iFilteredPixelOff] =
4230 9501709 : static_cast<Twork>(dfNoDataValue);
4231 9501801 : continue;
4232 : }
4233 : }
4234 : else
4235 : {
4236 6353336 : for (int i = 0; i < nSrcLineCount; ++i, j += nDstXSize)
4237 : {
4238 5086078 : const double dfWeight =
4239 5086078 : padfWeights[i] *
4240 : pabyChunkNodataMaskHorizontalFiltered[j];
4241 5086078 : dfVal += padfHorizontalFiltered[j] * dfWeight;
4242 5086078 : dfWeightSum += dfWeight;
4243 : }
4244 : }
4245 10402854 : if (dfWeightSum > 0.0)
4246 : {
4247 9856520 : pafDstScanline[iFilteredPixelOff] = replaceValIfNodata(
4248 9856172 : static_cast<Twork>(dfVal / dfWeightSum));
4249 : }
4250 : else
4251 : {
4252 546347 : pafDstScanline[iFilteredPixelOff] =
4253 546323 : static_cast<Twork>(dfNoDataValue);
4254 : }
4255 : }
4256 : }
4257 :
4258 404546 : if (fMaxVal != 0.0f)
4259 : {
4260 : if constexpr (std::is_same_v<T, double>)
4261 : {
4262 0 : for (int i = 0; i < nDstXSize; ++i)
4263 : {
4264 0 : if (pafDstScanline[i] > static_cast<double>(fMaxVal))
4265 0 : pafDstScanline[i] = static_cast<double>(fMaxVal);
4266 : }
4267 : }
4268 : else
4269 : {
4270 192324 : for (int i = 0; i < nDstXSize; ++i)
4271 : {
4272 192088 : if (pafDstScanline[i] > fMaxVal)
4273 96022 : pafDstScanline[i] = fMaxVal;
4274 : }
4275 : }
4276 : }
4277 :
4278 404546 : if (pafWrkScanline)
4279 : {
4280 390518 : GDALCopyWords64(pafWrkScanline, eWrkDataType, nWrkDataTypeSize,
4281 : static_cast<GByte *>(pDstBuffer) +
4282 390518 : static_cast<size_t>(iDstLine - nDstYOff) *
4283 390518 : nDstXSize * nDstDataTypeSize,
4284 : dstDataType, nDstDataTypeSize, nDstXSize);
4285 : }
4286 : }
4287 :
4288 9598 : VSIFree(pafWrkScanline);
4289 9598 : VSIFreeAligned(padfWeights);
4290 9598 : VSIFree(padfHorizontalFiltered);
4291 9598 : VSIFree(pabyChunkNodataMaskHorizontalFiltered);
4292 :
4293 9598 : return CE_None;
4294 : }
4295 :
4296 : template <bool bKernelWithNegativeWeights, bool bNeedRescale>
4297 : static CPLErr
4298 9598 : GDALResampleChunk_ConvolutionInternal(const GDALOverviewResampleArgs &args,
4299 : const void *pChunk, void **ppDstBuffer,
4300 : GDALDataType *peDstBufferDataType)
4301 : {
4302 : GDALResampleAlg eResample;
4303 9598 : if (EQUAL(args.pszResampling, "BILINEAR"))
4304 7097 : eResample = GRA_Bilinear;
4305 2501 : else if (EQUAL(args.pszResampling, "CUBIC"))
4306 2319 : eResample = GRA_Cubic;
4307 182 : else if (EQUAL(args.pszResampling, "CUBICSPLINE"))
4308 86 : eResample = GRA_CubicSpline;
4309 96 : else if (EQUAL(args.pszResampling, "LANCZOS"))
4310 96 : eResample = GRA_Lanczos;
4311 : else
4312 : {
4313 0 : CPLAssert(false);
4314 : return CE_Failure;
4315 : }
4316 9598 : const int nKernelRadius = GWKGetFilterRadius(eResample);
4317 9598 : FilterFuncType pfnFilterFunc = GWKGetFilterFunc(eResample);
4318 : const FilterFunc4ValuesType pfnFilterFunc4Values =
4319 9598 : GWKGetFilterFunc4Values(eResample);
4320 :
4321 9598 : float fMaxVal = 0.f;
4322 : // Cubic, etc... can have overshoots, so make sure we clamp values to the
4323 : // maximum value if NBITS is set.
4324 9598 : if (eResample != GRA_Bilinear && args.nOvrNBITS > 0 &&
4325 8 : (args.eOvrDataType == GDT_UInt8 || args.eOvrDataType == GDT_UInt16 ||
4326 0 : args.eOvrDataType == GDT_UInt32))
4327 : {
4328 8 : int nBits = args.nOvrNBITS;
4329 8 : if (nBits == GDALGetDataTypeSizeBits(args.eOvrDataType))
4330 1 : nBits = 0;
4331 8 : if (nBits > 0 && nBits < 32)
4332 7 : fMaxVal = static_cast<float>((1U << nBits) - 1);
4333 : }
4334 :
4335 9598 : *ppDstBuffer = VSI_MALLOC3_VERBOSE(
4336 : args.nDstXOff2 - args.nDstXOff, args.nDstYOff2 - args.nDstYOff,
4337 : GDALGetDataTypeSizeBytes(args.eOvrDataType));
4338 9598 : if (*ppDstBuffer == nullptr)
4339 : {
4340 0 : return CE_Failure;
4341 : }
4342 9598 : *peDstBufferDataType = args.eOvrDataType;
4343 :
4344 9598 : switch (args.eWrkDataType)
4345 : {
4346 8706 : case GDT_UInt8:
4347 : {
4348 : return GDALResampleChunk_ConvolutionT<GByte, float, GDT_Float32,
4349 : bKernelWithNegativeWeights,
4350 8706 : bNeedRescale>(
4351 : args, static_cast<const GByte *>(pChunk), *ppDstBuffer,
4352 8706 : pfnFilterFunc, pfnFilterFunc4Values, nKernelRadius, fMaxVal);
4353 : }
4354 :
4355 402 : case GDT_UInt16:
4356 : {
4357 : return GDALResampleChunk_ConvolutionT<GUInt16, float, GDT_Float32,
4358 : bKernelWithNegativeWeights,
4359 402 : bNeedRescale>(
4360 : args, static_cast<const GUInt16 *>(pChunk), *ppDstBuffer,
4361 402 : pfnFilterFunc, pfnFilterFunc4Values, nKernelRadius, fMaxVal);
4362 : }
4363 :
4364 387 : case GDT_Float32:
4365 : {
4366 : return GDALResampleChunk_ConvolutionT<float, float, GDT_Float32,
4367 : bKernelWithNegativeWeights,
4368 387 : bNeedRescale>(
4369 : args, static_cast<const float *>(pChunk), *ppDstBuffer,
4370 387 : pfnFilterFunc, pfnFilterFunc4Values, nKernelRadius, fMaxVal);
4371 : }
4372 :
4373 103 : case GDT_Float64:
4374 : {
4375 : return GDALResampleChunk_ConvolutionT<double, double, GDT_Float64,
4376 : bKernelWithNegativeWeights,
4377 103 : bNeedRescale>(
4378 : args, static_cast<const double *>(pChunk), *ppDstBuffer,
4379 103 : pfnFilterFunc, pfnFilterFunc4Values, nKernelRadius, fMaxVal);
4380 : }
4381 :
4382 0 : default:
4383 0 : break;
4384 : }
4385 :
4386 0 : CPLAssert(false);
4387 : return CE_Failure;
4388 : }
4389 :
4390 : static CPLErr
4391 9598 : GDALResampleChunk_Convolution(const GDALOverviewResampleArgs &args,
4392 : const void *pChunk, void **ppDstBuffer,
4393 : GDALDataType *peDstBufferDataType)
4394 : {
4395 9598 : if (EQUAL(args.pszResampling, "CUBIC") ||
4396 7279 : EQUAL(args.pszResampling, "LANCZOS"))
4397 : return GDALResampleChunk_ConvolutionInternal<
4398 2415 : /* bKernelWithNegativeWeights=*/true, /* bNeedRescale = */ true>(
4399 2415 : args, pChunk, ppDstBuffer, peDstBufferDataType);
4400 7183 : else if (EQUAL(args.pszResampling, "CUBICSPLINE"))
4401 86 : return GDALResampleChunk_ConvolutionInternal<false, true>(
4402 86 : args, pChunk, ppDstBuffer, peDstBufferDataType);
4403 : else
4404 7097 : return GDALResampleChunk_ConvolutionInternal<false, false>(
4405 7097 : args, pChunk, ppDstBuffer, peDstBufferDataType);
4406 : }
4407 :
4408 : /************************************************************************/
4409 : /* GDALResampleChunkC32R() */
4410 : /************************************************************************/
4411 :
4412 2 : static CPLErr GDALResampleChunkC32R(const int nSrcWidth, const int nSrcHeight,
4413 : const float *pafChunk, const int nChunkYOff,
4414 : const int nChunkYSize, const int nDstYOff,
4415 : const int nDstYOff2, const int nOvrXSize,
4416 : const int nOvrYSize, void **ppDstBuffer,
4417 : GDALDataType *peDstBufferDataType,
4418 : const char *pszResampling)
4419 :
4420 : {
4421 : enum Method
4422 : {
4423 : NEAR,
4424 : AVERAGE,
4425 : AVERAGE_MAGPHASE,
4426 : RMS,
4427 : };
4428 :
4429 2 : Method eMethod = NEAR;
4430 2 : if (STARTS_WITH_CI(pszResampling, "NEAR"))
4431 : {
4432 0 : eMethod = NEAR;
4433 : }
4434 2 : else if (EQUAL(pszResampling, "AVERAGE_MAGPHASE"))
4435 : {
4436 0 : eMethod = AVERAGE_MAGPHASE;
4437 : }
4438 2 : else if (EQUAL(pszResampling, "RMS"))
4439 : {
4440 2 : eMethod = RMS;
4441 : }
4442 0 : else if (STARTS_WITH_CI(pszResampling, "AVER"))
4443 : {
4444 0 : eMethod = AVERAGE;
4445 : }
4446 : else
4447 : {
4448 0 : CPLError(
4449 : CE_Failure, CPLE_NotSupported,
4450 : "Resampling method %s is not supported for complex data types. "
4451 : "Only NEAREST, AVERAGE, AVERAGE_MAGPHASE and RMS are supported",
4452 : pszResampling);
4453 0 : return CE_Failure;
4454 : }
4455 :
4456 2 : const int nOXSize = nOvrXSize;
4457 2 : *ppDstBuffer = VSI_MALLOC3_VERBOSE(nOXSize, nDstYOff2 - nDstYOff,
4458 : GDALGetDataTypeSizeBytes(GDT_CFloat32));
4459 2 : if (*ppDstBuffer == nullptr)
4460 : {
4461 0 : return CE_Failure;
4462 : }
4463 2 : float *const pafDstBuffer = static_cast<float *>(*ppDstBuffer);
4464 2 : *peDstBufferDataType = GDT_CFloat32;
4465 :
4466 2 : const int nOYSize = nOvrYSize;
4467 2 : const double dfXRatioDstToSrc = static_cast<double>(nSrcWidth) / nOXSize;
4468 2 : const double dfYRatioDstToSrc = static_cast<double>(nSrcHeight) / nOYSize;
4469 :
4470 : /* ==================================================================== */
4471 : /* Loop over destination scanlines. */
4472 : /* ==================================================================== */
4473 8 : for (int iDstLine = nDstYOff; iDstLine < nDstYOff2; ++iDstLine)
4474 : {
4475 6 : int nSrcYOff = static_cast<int>(0.5 + iDstLine * dfYRatioDstToSrc);
4476 6 : if (nSrcYOff < nChunkYOff)
4477 0 : nSrcYOff = nChunkYOff;
4478 :
4479 6 : int nSrcYOff2 =
4480 6 : static_cast<int>(0.5 + (iDstLine + 1) * dfYRatioDstToSrc);
4481 6 : if (nSrcYOff2 == nSrcYOff)
4482 0 : nSrcYOff2++;
4483 :
4484 6 : if (nSrcYOff2 > nSrcHeight || iDstLine == nOYSize - 1)
4485 : {
4486 2 : if (nSrcYOff == nSrcHeight && nSrcHeight - 1 >= nChunkYOff)
4487 0 : nSrcYOff = nSrcHeight - 1;
4488 2 : nSrcYOff2 = nSrcHeight;
4489 : }
4490 6 : if (nSrcYOff2 > nChunkYOff + nChunkYSize)
4491 0 : nSrcYOff2 = nChunkYOff + nChunkYSize;
4492 :
4493 6 : const float *const pafSrcScanline =
4494 6 : pafChunk +
4495 6 : (static_cast<size_t>(nSrcYOff - nChunkYOff) * nSrcWidth) * 2;
4496 6 : float *const pafDstScanline =
4497 6 : pafDstBuffer +
4498 6 : static_cast<size_t>(iDstLine - nDstYOff) * 2 * nOXSize;
4499 :
4500 : /* --------------------------------------------------------------------
4501 : */
4502 : /* Loop over destination pixels */
4503 : /* --------------------------------------------------------------------
4504 : */
4505 18 : for (int iDstPixel = 0; iDstPixel < nOXSize; ++iDstPixel)
4506 : {
4507 12 : const size_t iDstPixelSZ = static_cast<size_t>(iDstPixel);
4508 12 : int nSrcXOff = static_cast<int>(0.5 + iDstPixel * dfXRatioDstToSrc);
4509 12 : int nSrcXOff2 =
4510 12 : static_cast<int>(0.5 + (iDstPixel + 1) * dfXRatioDstToSrc);
4511 12 : if (nSrcXOff2 == nSrcXOff)
4512 0 : nSrcXOff2++;
4513 12 : if (nSrcXOff2 > nSrcWidth || iDstPixel == nOXSize - 1)
4514 : {
4515 6 : if (nSrcXOff == nSrcWidth && nSrcWidth - 1 >= 0)
4516 0 : nSrcXOff = nSrcWidth - 1;
4517 6 : nSrcXOff2 = nSrcWidth;
4518 : }
4519 12 : const size_t nSrcXOffSZ = static_cast<size_t>(nSrcXOff);
4520 :
4521 12 : if (eMethod == NEAR)
4522 : {
4523 0 : pafDstScanline[iDstPixelSZ * 2] =
4524 0 : pafSrcScanline[nSrcXOffSZ * 2];
4525 0 : pafDstScanline[iDstPixelSZ * 2 + 1] =
4526 0 : pafSrcScanline[nSrcXOffSZ * 2 + 1];
4527 : }
4528 12 : else if (eMethod == AVERAGE_MAGPHASE)
4529 : {
4530 0 : double dfTotalR = 0.0;
4531 0 : double dfTotalI = 0.0;
4532 0 : double dfTotalM = 0.0;
4533 0 : size_t nCount = 0;
4534 :
4535 0 : for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
4536 : {
4537 0 : for (int iX = nSrcXOff; iX < nSrcXOff2; ++iX)
4538 : {
4539 0 : const double dfR = double(
4540 0 : pafSrcScanline[static_cast<size_t>(iX) * 2 +
4541 0 : static_cast<size_t>(iY - nSrcYOff) *
4542 0 : nSrcWidth * 2]);
4543 0 : const double dfI = double(
4544 0 : pafSrcScanline[static_cast<size_t>(iX) * 2 +
4545 0 : static_cast<size_t>(iY - nSrcYOff) *
4546 0 : nSrcWidth * 2 +
4547 0 : 1]);
4548 0 : dfTotalR += dfR;
4549 0 : dfTotalI += dfI;
4550 0 : dfTotalM += std::hypot(dfR, dfI);
4551 0 : ++nCount;
4552 : }
4553 : }
4554 :
4555 0 : CPLAssert(nCount > 0);
4556 0 : if (nCount == 0)
4557 : {
4558 0 : pafDstScanline[iDstPixelSZ * 2] = 0.0;
4559 0 : pafDstScanline[iDstPixelSZ * 2 + 1] = 0.0;
4560 : }
4561 : else
4562 : {
4563 0 : pafDstScanline[iDstPixelSZ * 2] = static_cast<float>(
4564 0 : dfTotalR / static_cast<double>(nCount));
4565 0 : pafDstScanline[iDstPixelSZ * 2 + 1] = static_cast<float>(
4566 0 : dfTotalI / static_cast<double>(nCount));
4567 : const double dfM =
4568 0 : double(std::hypot(pafDstScanline[iDstPixelSZ * 2],
4569 0 : pafDstScanline[iDstPixelSZ * 2 + 1]));
4570 0 : const double dfDesiredM =
4571 0 : dfTotalM / static_cast<double>(nCount);
4572 0 : double dfRatio = 1.0;
4573 0 : if (dfM != 0.0)
4574 0 : dfRatio = dfDesiredM / dfM;
4575 :
4576 0 : pafDstScanline[iDstPixelSZ * 2] *=
4577 0 : static_cast<float>(dfRatio);
4578 0 : pafDstScanline[iDstPixelSZ * 2 + 1] *=
4579 0 : static_cast<float>(dfRatio);
4580 : }
4581 : }
4582 12 : else if (eMethod == RMS)
4583 : {
4584 12 : double dfTotalR = 0.0;
4585 12 : double dfTotalI = 0.0;
4586 12 : size_t nCount = 0;
4587 :
4588 36 : for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
4589 : {
4590 72 : for (int iX = nSrcXOff; iX < nSrcXOff2; ++iX)
4591 : {
4592 48 : const double dfR = double(
4593 48 : pafSrcScanline[static_cast<size_t>(iX) * 2 +
4594 48 : static_cast<size_t>(iY - nSrcYOff) *
4595 48 : nSrcWidth * 2]);
4596 48 : const double dfI = double(
4597 48 : pafSrcScanline[static_cast<size_t>(iX) * 2 +
4598 48 : static_cast<size_t>(iY - nSrcYOff) *
4599 48 : nSrcWidth * 2 +
4600 48 : 1]);
4601 :
4602 48 : dfTotalR += SQUARE(dfR);
4603 48 : dfTotalI += SQUARE(dfI);
4604 :
4605 48 : ++nCount;
4606 : }
4607 : }
4608 :
4609 12 : CPLAssert(nCount > 0);
4610 12 : if (nCount == 0)
4611 : {
4612 0 : pafDstScanline[iDstPixelSZ * 2] = 0.0;
4613 0 : pafDstScanline[iDstPixelSZ * 2 + 1] = 0.0;
4614 : }
4615 : else
4616 : {
4617 : /* compute RMS */
4618 12 : pafDstScanline[iDstPixelSZ * 2] = static_cast<float>(
4619 12 : sqrt(dfTotalR / static_cast<double>(nCount)));
4620 12 : pafDstScanline[iDstPixelSZ * 2 + 1] = static_cast<float>(
4621 12 : sqrt(dfTotalI / static_cast<double>(nCount)));
4622 : }
4623 : }
4624 0 : else if (eMethod == AVERAGE)
4625 : {
4626 0 : double dfTotalR = 0.0;
4627 0 : double dfTotalI = 0.0;
4628 0 : size_t nCount = 0;
4629 :
4630 0 : for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
4631 : {
4632 0 : for (int iX = nSrcXOff; iX < nSrcXOff2; ++iX)
4633 : {
4634 : // TODO(schwehr): Maybe use std::complex?
4635 0 : dfTotalR += double(
4636 0 : pafSrcScanline[static_cast<size_t>(iX) * 2 +
4637 0 : static_cast<size_t>(iY - nSrcYOff) *
4638 0 : nSrcWidth * 2]);
4639 0 : dfTotalI += double(
4640 0 : pafSrcScanline[static_cast<size_t>(iX) * 2 +
4641 0 : static_cast<size_t>(iY - nSrcYOff) *
4642 0 : nSrcWidth * 2 +
4643 0 : 1]);
4644 0 : ++nCount;
4645 : }
4646 : }
4647 :
4648 0 : CPLAssert(nCount > 0);
4649 0 : if (nCount == 0)
4650 : {
4651 0 : pafDstScanline[iDstPixelSZ * 2] = 0.0;
4652 0 : pafDstScanline[iDstPixelSZ * 2 + 1] = 0.0;
4653 : }
4654 : else
4655 : {
4656 0 : pafDstScanline[iDstPixelSZ * 2] = static_cast<float>(
4657 0 : dfTotalR / static_cast<double>(nCount));
4658 0 : pafDstScanline[iDstPixelSZ * 2 + 1] = static_cast<float>(
4659 0 : dfTotalI / static_cast<double>(nCount));
4660 : }
4661 : }
4662 : }
4663 : }
4664 :
4665 2 : return CE_None;
4666 : }
4667 :
4668 : /************************************************************************/
4669 : /* GDALRegenerateCascadingOverviews() */
4670 : /* */
4671 : /* Generate a list of overviews in order from largest to */
4672 : /* smallest, computing each from the next larger. */
4673 : /************************************************************************/
4674 :
4675 44 : static CPLErr GDALRegenerateCascadingOverviews(
4676 : GDALRasterBand *poSrcBand, int nOverviews, GDALRasterBand **papoOvrBands,
4677 : const char *pszResampling, GDALProgressFunc pfnProgress,
4678 : void *pProgressData, CSLConstList papszOptions)
4679 :
4680 : {
4681 : /* -------------------------------------------------------------------- */
4682 : /* First, we must put the overviews in order from largest to */
4683 : /* smallest. */
4684 : /* -------------------------------------------------------------------- */
4685 127 : for (int i = 0; i < nOverviews - 1; ++i)
4686 : {
4687 292 : for (int j = 0; j < nOverviews - i - 1; ++j)
4688 : {
4689 209 : if (papoOvrBands[j]->GetXSize() *
4690 209 : static_cast<float>(papoOvrBands[j]->GetYSize()) <
4691 209 : papoOvrBands[j + 1]->GetXSize() *
4692 209 : static_cast<float>(papoOvrBands[j + 1]->GetYSize()))
4693 : {
4694 0 : GDALRasterBand *poTempBand = papoOvrBands[j];
4695 0 : papoOvrBands[j] = papoOvrBands[j + 1];
4696 0 : papoOvrBands[j + 1] = poTempBand;
4697 : }
4698 : }
4699 : }
4700 :
4701 : /* -------------------------------------------------------------------- */
4702 : /* Count total pixels so we can prepare appropriate scaled */
4703 : /* progress functions. */
4704 : /* -------------------------------------------------------------------- */
4705 44 : double dfTotalPixels = 0.0;
4706 :
4707 171 : for (int i = 0; i < nOverviews; ++i)
4708 : {
4709 127 : dfTotalPixels += papoOvrBands[i]->GetXSize() *
4710 127 : static_cast<double>(papoOvrBands[i]->GetYSize());
4711 : }
4712 :
4713 : /* -------------------------------------------------------------------- */
4714 : /* Generate all the bands. */
4715 : /* -------------------------------------------------------------------- */
4716 44 : double dfPixelsProcessed = 0.0;
4717 :
4718 88 : CPLStringList aosOptions(papszOptions);
4719 44 : aosOptions.SetNameValue("CASCADING", "YES");
4720 171 : for (int i = 0; i < nOverviews; ++i)
4721 : {
4722 127 : GDALRasterBand *poBaseBand = poSrcBand;
4723 127 : if (i != 0)
4724 83 : poBaseBand = papoOvrBands[i - 1];
4725 :
4726 127 : double dfPixels = papoOvrBands[i]->GetXSize() *
4727 127 : static_cast<double>(papoOvrBands[i]->GetYSize());
4728 :
4729 254 : void *pScaledProgressData = GDALCreateScaledProgress(
4730 : dfPixelsProcessed / dfTotalPixels,
4731 127 : (dfPixelsProcessed + dfPixels) / dfTotalPixels, pfnProgress,
4732 : pProgressData);
4733 :
4734 254 : const CPLErr eErr = GDALRegenerateOverviewsEx(
4735 : poBaseBand, 1,
4736 127 : reinterpret_cast<GDALRasterBandH *>(papoOvrBands) + i,
4737 : pszResampling, GDALScaledProgress, pScaledProgressData,
4738 127 : aosOptions.List());
4739 127 : GDALDestroyScaledProgress(pScaledProgressData);
4740 :
4741 127 : if (eErr != CE_None)
4742 0 : return eErr;
4743 :
4744 127 : dfPixelsProcessed += dfPixels;
4745 :
4746 : // Only do the bit2grayscale promotion on the base band.
4747 127 : if (STARTS_WITH_CI(pszResampling,
4748 : "AVERAGE_BIT2G" /* AVERAGE_BIT2GRAYSCALE */))
4749 8 : pszResampling = "AVERAGE";
4750 : }
4751 :
4752 44 : return CE_None;
4753 : }
4754 :
4755 : /************************************************************************/
4756 : /* GDALGetResampleFunction() */
4757 : /************************************************************************/
4758 :
4759 19329 : GDALResampleFunction GDALGetResampleFunction(const char *pszResampling,
4760 : int *pnRadius)
4761 : {
4762 19329 : if (pnRadius)
4763 19329 : *pnRadius = 0;
4764 19329 : if (STARTS_WITH_CI(pszResampling, "NEAR"))
4765 586 : return GDALResampleChunk_Near;
4766 18743 : else if (STARTS_WITH_CI(pszResampling, "AVER") ||
4767 7515 : EQUAL(pszResampling, "RMS"))
4768 11293 : return GDALResampleChunk_AverageOrRMS;
4769 7450 : else if (EQUAL(pszResampling, "GAUSS"))
4770 : {
4771 26 : if (pnRadius)
4772 26 : *pnRadius = 1;
4773 26 : return GDALResampleChunk_Gauss;
4774 : }
4775 7424 : else if (EQUAL(pszResampling, "MODE"))
4776 148 : return GDALResampleChunk_Mode;
4777 7276 : else if (EQUAL(pszResampling, "CUBIC"))
4778 : {
4779 1649 : if (pnRadius)
4780 1649 : *pnRadius = GWKGetFilterRadius(GRA_Cubic);
4781 1649 : return GDALResampleChunk_Convolution;
4782 : }
4783 5627 : else if (EQUAL(pszResampling, "CUBICSPLINE"))
4784 : {
4785 60 : if (pnRadius)
4786 60 : *pnRadius = GWKGetFilterRadius(GRA_CubicSpline);
4787 60 : return GDALResampleChunk_Convolution;
4788 : }
4789 5567 : else if (EQUAL(pszResampling, "LANCZOS"))
4790 : {
4791 50 : if (pnRadius)
4792 50 : *pnRadius = GWKGetFilterRadius(GRA_Lanczos);
4793 50 : return GDALResampleChunk_Convolution;
4794 : }
4795 5517 : else if (EQUAL(pszResampling, "BILINEAR"))
4796 : {
4797 5517 : if (pnRadius)
4798 5517 : *pnRadius = GWKGetFilterRadius(GRA_Bilinear);
4799 5517 : return GDALResampleChunk_Convolution;
4800 : }
4801 : else
4802 : {
4803 0 : CPLError(
4804 : CE_Failure, CPLE_AppDefined,
4805 : "GDALGetResampleFunction: Unsupported resampling method \"%s\".",
4806 : pszResampling);
4807 0 : return nullptr;
4808 : }
4809 : }
4810 :
4811 : /************************************************************************/
4812 : /* GDALGetOvrWorkDataType() */
4813 : /************************************************************************/
4814 :
4815 19210 : GDALDataType GDALGetOvrWorkDataType(const char *pszResampling,
4816 : GDALDataType eSrcDataType)
4817 : {
4818 19210 : if (STARTS_WITH_CI(pszResampling, "NEAR") || EQUAL(pszResampling, "MODE"))
4819 : {
4820 726 : return eSrcDataType;
4821 : }
4822 18484 : else if (eSrcDataType == GDT_UInt8 &&
4823 17911 : (STARTS_WITH_CI(pszResampling, "AVER") ||
4824 6781 : EQUAL(pszResampling, "RMS") || EQUAL(pszResampling, "CUBIC") ||
4825 5375 : EQUAL(pszResampling, "CUBICSPLINE") ||
4826 5355 : EQUAL(pszResampling, "LANCZOS") ||
4827 5348 : EQUAL(pszResampling, "BILINEAR") || EQUAL(pszResampling, "MODE")))
4828 : {
4829 17904 : return GDT_UInt8;
4830 : }
4831 580 : else if (eSrcDataType == GDT_UInt16 &&
4832 131 : (STARTS_WITH_CI(pszResampling, "AVER") ||
4833 126 : EQUAL(pszResampling, "RMS") || EQUAL(pszResampling, "CUBIC") ||
4834 8 : EQUAL(pszResampling, "CUBICSPLINE") ||
4835 6 : EQUAL(pszResampling, "LANCZOS") ||
4836 3 : EQUAL(pszResampling, "BILINEAR") || EQUAL(pszResampling, "MODE")))
4837 : {
4838 131 : return GDT_UInt16;
4839 : }
4840 449 : else if (EQUAL(pszResampling, "GAUSS"))
4841 20 : return GDT_Float64;
4842 :
4843 429 : if (eSrcDataType == GDT_UInt8 || eSrcDataType == GDT_Int8 ||
4844 428 : eSrcDataType == GDT_UInt16 || eSrcDataType == GDT_Int16 ||
4845 : eSrcDataType == GDT_Float32)
4846 : {
4847 277 : return GDT_Float32;
4848 : }
4849 152 : return GDT_Float64;
4850 : }
4851 :
4852 : namespace
4853 : {
4854 : // Structure to hold a pointer to free with CPLFree()
4855 : struct PointerHolder
4856 : {
4857 : void *ptr = nullptr;
4858 :
4859 4145 : template <class T> explicit PointerHolder(T *&ptrIn) : ptr(ptrIn)
4860 : {
4861 4145 : ptrIn = nullptr;
4862 4145 : }
4863 :
4864 : template <class T>
4865 38 : explicit PointerHolder(std::unique_ptr<T, VSIFreeReleaser> ptrIn)
4866 38 : : ptr(ptrIn.release())
4867 : {
4868 38 : }
4869 :
4870 4183 : ~PointerHolder()
4871 4183 : {
4872 4183 : CPLFree(ptr);
4873 4183 : }
4874 :
4875 : PointerHolder(const PointerHolder &) = delete;
4876 : PointerHolder &operator=(const PointerHolder &) = delete;
4877 : };
4878 : } // namespace
4879 :
4880 : /************************************************************************/
4881 : /* GDALRegenerateOverviews() */
4882 : /************************************************************************/
4883 :
4884 : /**
4885 : * \brief Generate downsampled overviews.
4886 : *
4887 : * This function will generate one or more overview images from a base image
4888 : * using the requested downsampling algorithm. Its primary use is for
4889 : * generating overviews via GDALDataset::BuildOverviews(), but it can also be
4890 : * used to generate downsampled images in one file from another outside the
4891 : * overview architecture.
4892 : *
4893 : * The output bands need to exist in advance.
4894 : *
4895 : * The full set of resampling algorithms is documented in
4896 : * GDALDataset::BuildOverviews().
4897 : *
4898 : * This function will honour properly NODATA_VALUES tuples (special dataset
4899 : * metadata) so that only a given RGB triplet (in case of a RGB image) will be
4900 : * considered as the nodata value and not each value of the triplet
4901 : * independently per band.
4902 : *
4903 : * Starting with GDAL 3.2, the GDAL_NUM_THREADS configuration option can be set
4904 : * to "ALL_CPUS" or a integer value to specify the number of threads to use for
4905 : * overview computation.
4906 : *
4907 : * @param hSrcBand the source (base level) band.
4908 : * @param nOverviewCount the number of downsampled bands being generated.
4909 : * @param pahOvrBands the list of downsampled bands to be generated.
4910 : * @param pszResampling Resampling algorithm (e.g. "AVERAGE").
4911 : * @param pfnProgress progress report function.
4912 : * @param pProgressData progress function callback data.
4913 : * @return CE_None on success or CE_Failure on failure.
4914 : */
4915 121 : CPLErr GDALRegenerateOverviews(GDALRasterBandH hSrcBand, int nOverviewCount,
4916 : GDALRasterBandH *pahOvrBands,
4917 : const char *pszResampling,
4918 : GDALProgressFunc pfnProgress,
4919 : void *pProgressData)
4920 :
4921 : {
4922 121 : return GDALRegenerateOverviewsEx(hSrcBand, nOverviewCount, pahOvrBands,
4923 : pszResampling, pfnProgress, pProgressData,
4924 121 : nullptr);
4925 : }
4926 :
4927 : /************************************************************************/
4928 : /* GDALRegenerateOverviewsEx() */
4929 : /************************************************************************/
4930 :
4931 : constexpr int RADIUS_TO_DIAMETER = 2;
4932 :
4933 : /**
4934 : * \brief Generate downsampled overviews.
4935 : *
4936 : * This function will generate one or more overview images from a base image
4937 : * using the requested downsampling algorithm. Its primary use is for
4938 : * generating overviews via GDALDataset::BuildOverviews(), but it can also be
4939 : * used to generate downsampled images in one file from another outside the
4940 : * overview architecture.
4941 : *
4942 : * The output bands need to exist in advance.
4943 : *
4944 : * The full set of resampling algorithms is documented in
4945 : * GDALDataset::BuildOverviews().
4946 : *
4947 : * This function will honour properly NODATA_VALUES tuples (special dataset
4948 : * metadata) so that only a given RGB triplet (in case of a RGB image) will be
4949 : * considered as the nodata value and not each value of the triplet
4950 : * independently per band.
4951 : *
4952 : * Starting with GDAL 3.2, the GDAL_NUM_THREADS configuration option can be set
4953 : * to "ALL_CPUS" or a integer value to specify the number of threads to use for
4954 : * overview computation.
4955 : *
4956 : * @param hSrcBand the source (base level) band.
4957 : * @param nOverviewCount the number of downsampled bands being generated.
4958 : * @param pahOvrBands the list of downsampled bands to be generated.
4959 : * @param pszResampling Resampling algorithm (e.g. "AVERAGE").
4960 : * @param pfnProgress progress report function.
4961 : * @param pProgressData progress function callback data.
4962 : * @param papszOptions NULL terminated list of options as key=value pairs, or
4963 : * NULL
4964 : * @return CE_None on success or CE_Failure on failure.
4965 : * @since GDAL 3.6
4966 : */
4967 834 : CPLErr GDALRegenerateOverviewsEx(GDALRasterBandH hSrcBand, int nOverviewCount,
4968 : GDALRasterBandH *pahOvrBands,
4969 : const char *pszResampling,
4970 : GDALProgressFunc pfnProgress,
4971 : void *pProgressData, CSLConstList papszOptions)
4972 :
4973 : {
4974 834 : GDALRasterBand *poSrcBand = GDALRasterBand::FromHandle(hSrcBand);
4975 834 : GDALRasterBand **papoOvrBands =
4976 : reinterpret_cast<GDALRasterBand **>(pahOvrBands);
4977 :
4978 834 : if (pfnProgress == nullptr)
4979 102 : pfnProgress = GDALDummyProgress;
4980 :
4981 834 : if (EQUAL(pszResampling, "NONE"))
4982 51 : return CE_None;
4983 :
4984 783 : int nKernelRadius = 0;
4985 : GDALResampleFunction pfnResampleFn =
4986 783 : GDALGetResampleFunction(pszResampling, &nKernelRadius);
4987 :
4988 783 : if (pfnResampleFn == nullptr)
4989 0 : return CE_Failure;
4990 :
4991 : /* -------------------------------------------------------------------- */
4992 : /* Check color tables... */
4993 : /* -------------------------------------------------------------------- */
4994 783 : GDALColorTable *poColorTable = nullptr;
4995 :
4996 560 : if ((STARTS_WITH_CI(pszResampling, "AVER") || EQUAL(pszResampling, "RMS") ||
4997 1644 : EQUAL(pszResampling, "MODE") || EQUAL(pszResampling, "GAUSS")) &&
4998 312 : poSrcBand->GetColorInterpretation() == GCI_PaletteIndex)
4999 : {
5000 9 : poColorTable = poSrcBand->GetColorTable();
5001 9 : if (poColorTable != nullptr)
5002 : {
5003 9 : if (poColorTable->GetPaletteInterpretation() != GPI_RGB)
5004 : {
5005 0 : CPLError(CE_Warning, CPLE_AppDefined,
5006 : "Computing overviews on palette index raster bands "
5007 : "with a palette whose color interpretation is not RGB "
5008 : "will probably lead to unexpected results.");
5009 0 : poColorTable = nullptr;
5010 : }
5011 9 : else if (poColorTable->IsIdentity())
5012 : {
5013 0 : poColorTable = nullptr;
5014 : }
5015 : }
5016 : else
5017 : {
5018 0 : CPLError(CE_Warning, CPLE_AppDefined,
5019 : "Computing overviews on palette index raster bands "
5020 : "without a palette will probably lead to unexpected "
5021 : "results.");
5022 : }
5023 : }
5024 : // Not ready yet
5025 2268 : else if ((EQUAL(pszResampling, "CUBIC") ||
5026 720 : EQUAL(pszResampling, "CUBICSPLINE") ||
5027 720 : EQUAL(pszResampling, "LANCZOS") ||
5028 1574 : EQUAL(pszResampling, "BILINEAR")) &&
5029 80 : poSrcBand->GetColorInterpretation() == GCI_PaletteIndex)
5030 : {
5031 0 : CPLError(CE_Warning, CPLE_AppDefined,
5032 : "Computing %s overviews on palette index raster bands "
5033 : "will probably lead to unexpected results.",
5034 : pszResampling);
5035 : }
5036 :
5037 : // If we have a nodata mask and we are doing something more complicated
5038 : // than nearest neighbouring, we have to fetch to nodata mask.
5039 :
5040 783 : GDALRasterBand *poMaskBand = nullptr;
5041 783 : bool bUseNoDataMask = false;
5042 783 : bool bCanUseCascaded = true;
5043 :
5044 783 : if (!STARTS_WITH_CI(pszResampling, "NEAR"))
5045 : {
5046 : // Special case if we are an alpha/mask band. We want it to be
5047 : // considered as the mask band to avoid alpha=0 to be taken into account
5048 : // in average computation.
5049 392 : if (poSrcBand->IsMaskBand())
5050 : {
5051 51 : poMaskBand = poSrcBand;
5052 51 : bUseNoDataMask = true;
5053 : }
5054 : else
5055 : {
5056 341 : poMaskBand = poSrcBand->GetMaskBand();
5057 341 : const int nMaskFlags = poSrcBand->GetMaskFlags();
5058 341 : bCanUseCascaded =
5059 341 : (nMaskFlags == GMF_NODATA || nMaskFlags == GMF_ALL_VALID);
5060 341 : bUseNoDataMask = (nMaskFlags & GMF_ALL_VALID) == 0;
5061 : }
5062 : }
5063 :
5064 783 : int nHasNoData = 0;
5065 783 : const double dfNoDataValue = poSrcBand->GetNoDataValue(&nHasNoData);
5066 783 : const bool bHasNoData = CPL_TO_BOOL(nHasNoData);
5067 : const bool bPropagateNoData =
5068 783 : CPLTestBool(CPLGetConfigOption("GDAL_OVR_PROPAGATE_NODATA", "NO"));
5069 :
5070 851 : if (poSrcBand->GetBand() == 1 && bUseNoDataMask &&
5071 68 : CSLFetchNameValue(papszOptions, "CASCADING") == nullptr)
5072 : {
5073 112 : std::string osDetailMessage;
5074 56 : if (poSrcBand->HasConflictingMaskSources(&osDetailMessage, false))
5075 : {
5076 2 : CPLError(
5077 : CE_Warning, CPLE_AppDefined, "%s%s", osDetailMessage.c_str(),
5078 : bHasNoData
5079 : ? "Only the nodata value will be taken into account."
5080 : : "Only the first listed one will be taken into account.");
5081 : }
5082 : }
5083 :
5084 : /* -------------------------------------------------------------------- */
5085 : /* If we are operating on multiple overviews, and using */
5086 : /* averaging, lets do them in cascading order to reduce the */
5087 : /* amount of computation. */
5088 : /* -------------------------------------------------------------------- */
5089 :
5090 : // In case the mask made be computed from another band of the dataset,
5091 : // we can't use cascaded generation, as the computation of the overviews
5092 : // of the band used for the mask band may not have yet occurred (#3033).
5093 783 : if ((STARTS_WITH_CI(pszResampling, "AVER") ||
5094 560 : EQUAL(pszResampling, "GAUSS") || EQUAL(pszResampling, "RMS") ||
5095 529 : EQUAL(pszResampling, "CUBIC") || EQUAL(pszResampling, "CUBICSPLINE") ||
5096 475 : EQUAL(pszResampling, "LANCZOS") || EQUAL(pszResampling, "BILINEAR") ||
5097 783 : EQUAL(pszResampling, "MODE")) &&
5098 44 : nOverviewCount > 1 && bCanUseCascaded)
5099 44 : return GDALRegenerateCascadingOverviews(
5100 : poSrcBand, nOverviewCount, papoOvrBands, pszResampling, pfnProgress,
5101 44 : pProgressData, papszOptions);
5102 :
5103 : /* -------------------------------------------------------------------- */
5104 : /* Setup one horizontal swath to read from the raw buffer. */
5105 : /* -------------------------------------------------------------------- */
5106 739 : int nFRXBlockSize = 0;
5107 739 : int nFRYBlockSize = 0;
5108 739 : poSrcBand->GetBlockSize(&nFRXBlockSize, &nFRYBlockSize);
5109 :
5110 739 : const GDALDataType eSrcDataType = poSrcBand->GetRasterDataType();
5111 1087 : const bool bUseGenericResampleFn = STARTS_WITH_CI(pszResampling, "NEAR") ||
5112 1037 : EQUAL(pszResampling, "MODE") ||
5113 298 : !GDALDataTypeIsComplex(eSrcDataType);
5114 : const GDALDataType eWrkDataType =
5115 : bUseGenericResampleFn
5116 739 : ? GDALGetOvrWorkDataType(pszResampling, eSrcDataType)
5117 739 : : GDT_CFloat32;
5118 :
5119 739 : const int nWidth = poSrcBand->GetXSize();
5120 739 : const int nHeight = poSrcBand->GetYSize();
5121 :
5122 739 : int nMaxOvrFactor = 1;
5123 1601 : for (int iOverview = 0; iOverview < nOverviewCount; ++iOverview)
5124 : {
5125 862 : const int nDstWidth = papoOvrBands[iOverview]->GetXSize();
5126 862 : const int nDstHeight = papoOvrBands[iOverview]->GetYSize();
5127 862 : nMaxOvrFactor = std::max(
5128 : nMaxOvrFactor,
5129 862 : static_cast<int>(static_cast<double>(nWidth) / nDstWidth + 0.5));
5130 862 : nMaxOvrFactor = std::max(
5131 : nMaxOvrFactor,
5132 862 : static_cast<int>(static_cast<double>(nHeight) / nDstHeight + 0.5));
5133 : }
5134 :
5135 739 : int nFullResYChunk = nFRYBlockSize;
5136 739 : int nMaxChunkYSizeQueried = 0;
5137 :
5138 : const auto UpdateChunkHeightAndGetChunkSize =
5139 10233 : [&nFullResYChunk, &nMaxChunkYSizeQueried, nKernelRadius, nMaxOvrFactor,
5140 82825 : eWrkDataType, nWidth]()
5141 : {
5142 : // Make sure that round(nChunkYOff / nMaxOvrFactor) < round((nChunkYOff
5143 : // + nFullResYChunk) / nMaxOvrFactor)
5144 10233 : if (nMaxOvrFactor > INT_MAX / RADIUS_TO_DIAMETER)
5145 : {
5146 1 : return GINTBIG_MAX;
5147 : }
5148 10232 : nFullResYChunk =
5149 10232 : std::max(nFullResYChunk, RADIUS_TO_DIAMETER * nMaxOvrFactor);
5150 10232 : if ((nKernelRadius > 0 &&
5151 970 : nMaxOvrFactor > INT_MAX / (RADIUS_TO_DIAMETER * nKernelRadius)) ||
5152 10232 : nFullResYChunk >
5153 10232 : INT_MAX - RADIUS_TO_DIAMETER * nKernelRadius * nMaxOvrFactor)
5154 : {
5155 0 : return GINTBIG_MAX;
5156 : }
5157 10232 : nMaxChunkYSizeQueried =
5158 10232 : nFullResYChunk + RADIUS_TO_DIAMETER * nKernelRadius * nMaxOvrFactor;
5159 10232 : if (GDALGetDataTypeSizeBytes(eWrkDataType) >
5160 10232 : std::numeric_limits<int64_t>::max() /
5161 10232 : (static_cast<int64_t>(nMaxChunkYSizeQueried) * nWidth))
5162 : {
5163 1 : return GINTBIG_MAX;
5164 : }
5165 10231 : return static_cast<GIntBig>(GDALGetDataTypeSizeBytes(eWrkDataType)) *
5166 10231 : nMaxChunkYSizeQueried * nWidth;
5167 739 : };
5168 :
5169 : const char *pszChunkYSize =
5170 739 : CPLGetConfigOption("GDAL_OVR_CHUNKYSIZE", nullptr);
5171 : #ifndef __COVERITY__
5172 : // Only configurable for debug / testing
5173 739 : if (pszChunkYSize)
5174 : {
5175 0 : nFullResYChunk = atoi(pszChunkYSize);
5176 : }
5177 : #endif
5178 :
5179 : // Only configurable for debug / testing
5180 : const int nChunkMaxSize =
5181 739 : atoi(CPLGetConfigOption("GDAL_OVR_CHUNK_MAX_SIZE", "10485760"));
5182 :
5183 739 : auto nChunkSize = UpdateChunkHeightAndGetChunkSize();
5184 739 : if (nChunkSize > nChunkMaxSize)
5185 : {
5186 15 : if (poColorTable == nullptr && nFRXBlockSize < nWidth &&
5187 44 : !GDALDataTypeIsComplex(eSrcDataType) &&
5188 14 : (!STARTS_WITH_CI(pszResampling, "AVER") ||
5189 2 : EQUAL(pszResampling, "AVERAGE")))
5190 : {
5191 : // If this is tiled, then use GDALRegenerateOverviewsMultiBand()
5192 : // which use a block based strategy, which is much less memory
5193 : // hungry.
5194 14 : return GDALRegenerateOverviewsMultiBand(
5195 : 1, &poSrcBand, nOverviewCount, &papoOvrBands, pszResampling,
5196 14 : pfnProgress, pProgressData, papszOptions);
5197 : }
5198 1 : else if (nOverviewCount > 1 && STARTS_WITH_CI(pszResampling, "NEAR"))
5199 : {
5200 0 : return GDALRegenerateCascadingOverviews(
5201 : poSrcBand, nOverviewCount, papoOvrBands, pszResampling,
5202 0 : pfnProgress, pProgressData, papszOptions);
5203 : }
5204 : }
5205 724 : else if (pszChunkYSize == nullptr)
5206 : {
5207 : // Try to get as close as possible to nChunkMaxSize
5208 10218 : while (nChunkSize < nChunkMaxSize / 2)
5209 : {
5210 9494 : nFullResYChunk *= 2;
5211 9494 : nChunkSize = UpdateChunkHeightAndGetChunkSize();
5212 : }
5213 : }
5214 :
5215 : // Structure describing a resampling job
5216 : struct OvrJob
5217 : {
5218 : // Buffers to free when job is finished
5219 : std::shared_ptr<PointerHolder> oSrcMaskBufferHolder{};
5220 : std::shared_ptr<PointerHolder> oSrcBufferHolder{};
5221 : std::unique_ptr<PointerHolder> oDstBufferHolder{};
5222 :
5223 : GDALRasterBand *poDstBand = nullptr;
5224 :
5225 : // Input parameters of pfnResampleFn
5226 : GDALResampleFunction pfnResampleFn = nullptr;
5227 : int nSrcWidth = 0;
5228 : int nSrcHeight = 0;
5229 : int nDstWidth = 0;
5230 : GDALOverviewResampleArgs args{};
5231 : const void *pChunk = nullptr;
5232 : bool bUseGenericResampleFn = false;
5233 :
5234 : // Output values of resampling function
5235 : CPLErr eErr = CE_Failure;
5236 : void *pDstBuffer = nullptr;
5237 : GDALDataType eDstBufferDataType = GDT_Unknown;
5238 :
5239 0 : void SetSrcMaskBufferHolder(
5240 : const std::shared_ptr<PointerHolder> &oSrcMaskBufferHolderIn)
5241 : {
5242 0 : oSrcMaskBufferHolder = oSrcMaskBufferHolderIn;
5243 0 : }
5244 :
5245 0 : void SetSrcBufferHolder(
5246 : const std::shared_ptr<PointerHolder> &oSrcBufferHolderIn)
5247 : {
5248 0 : oSrcBufferHolder = oSrcBufferHolderIn;
5249 0 : }
5250 :
5251 831 : void NotifyFinished()
5252 : {
5253 1662 : std::lock_guard guard(mutex);
5254 831 : bFinished = true;
5255 831 : cv.notify_one();
5256 831 : }
5257 :
5258 0 : bool IsFinished()
5259 : {
5260 0 : std::lock_guard guard(mutex);
5261 0 : return bFinished;
5262 : }
5263 :
5264 0 : void WaitFinished()
5265 : {
5266 0 : std::unique_lock oGuard(mutex);
5267 0 : while (!bFinished)
5268 : {
5269 0 : cv.wait(oGuard);
5270 : }
5271 0 : }
5272 :
5273 : private:
5274 : // Synchronization
5275 : bool bFinished = false;
5276 : std::mutex mutex{};
5277 : std::condition_variable cv{};
5278 : };
5279 :
5280 : // Thread function to resample
5281 831 : const auto JobResampleFunc = [](void *pData)
5282 : {
5283 831 : OvrJob *poJob = static_cast<OvrJob *>(pData);
5284 :
5285 831 : if (poJob->bUseGenericResampleFn)
5286 : {
5287 829 : poJob->eErr = poJob->pfnResampleFn(poJob->args, poJob->pChunk,
5288 : &(poJob->pDstBuffer),
5289 : &(poJob->eDstBufferDataType));
5290 : }
5291 : else
5292 : {
5293 2 : poJob->eErr = GDALResampleChunkC32R(
5294 : poJob->nSrcWidth, poJob->nSrcHeight,
5295 2 : static_cast<const float *>(poJob->pChunk),
5296 : poJob->args.nChunkYOff, poJob->args.nChunkYSize,
5297 : poJob->args.nDstYOff, poJob->args.nDstYOff2,
5298 : poJob->args.nOvrXSize, poJob->args.nOvrYSize,
5299 : &(poJob->pDstBuffer), &(poJob->eDstBufferDataType),
5300 : poJob->args.pszResampling);
5301 : }
5302 :
5303 831 : auto pDstBuffer = poJob->pDstBuffer;
5304 831 : poJob->oDstBufferHolder = std::make_unique<PointerHolder>(pDstBuffer);
5305 :
5306 831 : poJob->NotifyFinished();
5307 831 : };
5308 :
5309 : // Function to write resample data to target band
5310 831 : const auto WriteJobData = [](const OvrJob *poJob)
5311 : {
5312 1662 : return poJob->poDstBand->RasterIO(
5313 831 : GF_Write, 0, poJob->args.nDstYOff, poJob->nDstWidth,
5314 831 : poJob->args.nDstYOff2 - poJob->args.nDstYOff, poJob->pDstBuffer,
5315 831 : poJob->nDstWidth, poJob->args.nDstYOff2 - poJob->args.nDstYOff,
5316 831 : poJob->eDstBufferDataType, 0, 0, nullptr);
5317 : };
5318 :
5319 : // Wait for completion of oldest job and serialize it
5320 : const auto WaitAndFinalizeOldestJob =
5321 0 : [WriteJobData](std::list<std::unique_ptr<OvrJob>> &jobList)
5322 : {
5323 0 : auto poOldestJob = jobList.front().get();
5324 0 : poOldestJob->WaitFinished();
5325 0 : CPLErr l_eErr = poOldestJob->eErr;
5326 0 : if (l_eErr == CE_None)
5327 : {
5328 0 : l_eErr = WriteJobData(poOldestJob);
5329 : }
5330 :
5331 0 : jobList.pop_front();
5332 0 : return l_eErr;
5333 : };
5334 :
5335 : // Queue of jobs
5336 1450 : std::list<std::unique_ptr<OvrJob>> jobList;
5337 :
5338 725 : GByte *pabyChunkNodataMask = nullptr;
5339 725 : void *pChunk = nullptr;
5340 :
5341 725 : const int nThreads = GDALGetNumThreads(GDAL_DEFAULT_MAX_THREAD_COUNT,
5342 : /* bDefaultToAllCPUs=*/false);
5343 : auto poThreadPool =
5344 725 : nThreads > 1 ? GDALGetGlobalThreadPool(nThreads) : nullptr;
5345 : auto poJobQueue = poThreadPool ? poThreadPool->CreateJobQueue()
5346 1450 : : std::unique_ptr<CPLJobQueue>(nullptr);
5347 :
5348 : /* -------------------------------------------------------------------- */
5349 : /* Loop over image operating on chunks. */
5350 : /* -------------------------------------------------------------------- */
5351 725 : int nChunkYOff = 0;
5352 725 : CPLErr eErr = CE_None;
5353 :
5354 1455 : for (nChunkYOff = 0; nChunkYOff < nHeight && eErr == CE_None;
5355 730 : nChunkYOff += nFullResYChunk)
5356 : {
5357 730 : if (!pfnProgress(nChunkYOff / static_cast<double>(nHeight), nullptr,
5358 : pProgressData))
5359 : {
5360 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
5361 0 : eErr = CE_Failure;
5362 : }
5363 :
5364 730 : if (nFullResYChunk + nChunkYOff > nHeight)
5365 722 : nFullResYChunk = nHeight - nChunkYOff;
5366 :
5367 730 : int nChunkYOffQueried = nChunkYOff - nKernelRadius * nMaxOvrFactor;
5368 730 : int nChunkYSizeQueried =
5369 730 : nFullResYChunk + 2 * nKernelRadius * nMaxOvrFactor;
5370 730 : if (nChunkYOffQueried < 0)
5371 : {
5372 83 : nChunkYSizeQueried += nChunkYOffQueried;
5373 83 : nChunkYOffQueried = 0;
5374 : }
5375 730 : if (nChunkYOffQueried + nChunkYSizeQueried > nHeight)
5376 83 : nChunkYSizeQueried = nHeight - nChunkYOffQueried;
5377 :
5378 : // Avoid accumulating too many tasks and exhaust RAM
5379 : // Try to complete already finished jobs
5380 730 : while (eErr == CE_None && !jobList.empty())
5381 : {
5382 0 : auto poOldestJob = jobList.front().get();
5383 0 : if (!poOldestJob->IsFinished())
5384 0 : break;
5385 0 : eErr = poOldestJob->eErr;
5386 0 : if (eErr == CE_None)
5387 : {
5388 0 : eErr = WriteJobData(poOldestJob);
5389 : }
5390 :
5391 0 : jobList.pop_front();
5392 : }
5393 :
5394 : // And in case we have saturated the number of threads,
5395 : // wait for completion of tasks to go below the threshold.
5396 1460 : while (eErr == CE_None &&
5397 730 : jobList.size() >= static_cast<size_t>(nThreads))
5398 : {
5399 0 : eErr = WaitAndFinalizeOldestJob(jobList);
5400 : }
5401 :
5402 : // (Re)allocate buffers if needed
5403 730 : if (pChunk == nullptr)
5404 : {
5405 725 : pChunk = VSI_MALLOC3_VERBOSE(GDALGetDataTypeSizeBytes(eWrkDataType),
5406 : nMaxChunkYSizeQueried, nWidth);
5407 : }
5408 730 : if (bUseNoDataMask && pabyChunkNodataMask == nullptr)
5409 : {
5410 139 : pabyChunkNodataMask = static_cast<GByte *>(
5411 139 : VSI_MALLOC2_VERBOSE(nMaxChunkYSizeQueried, nWidth));
5412 : }
5413 :
5414 730 : if (pChunk == nullptr ||
5415 139 : (bUseNoDataMask && pabyChunkNodataMask == nullptr))
5416 : {
5417 0 : CPLFree(pChunk);
5418 0 : CPLFree(pabyChunkNodataMask);
5419 0 : return CE_Failure;
5420 : }
5421 :
5422 : // Read chunk.
5423 730 : if (eErr == CE_None)
5424 730 : eErr = poSrcBand->RasterIO(GF_Read, 0, nChunkYOffQueried, nWidth,
5425 : nChunkYSizeQueried, pChunk, nWidth,
5426 : nChunkYSizeQueried, eWrkDataType, 0, 0,
5427 : nullptr);
5428 730 : if (eErr == CE_None && bUseNoDataMask)
5429 139 : eErr = poMaskBand->RasterIO(GF_Read, 0, nChunkYOffQueried, nWidth,
5430 : nChunkYSizeQueried, pabyChunkNodataMask,
5431 : nWidth, nChunkYSizeQueried, GDT_UInt8,
5432 : 0, 0, nullptr);
5433 :
5434 : // Special case to promote 1bit data to 8bit 0/255 values.
5435 730 : if (EQUAL(pszResampling, "AVERAGE_BIT2GRAYSCALE"))
5436 : {
5437 9 : if (eWrkDataType == GDT_Float32)
5438 : {
5439 0 : float *pafChunk = static_cast<float *>(pChunk);
5440 0 : for (size_t i = 0;
5441 0 : i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5442 : {
5443 0 : if (pafChunk[i] == 1.0f)
5444 0 : pafChunk[i] = 255.0f;
5445 : }
5446 : }
5447 9 : else if (eWrkDataType == GDT_UInt8)
5448 : {
5449 9 : GByte *pabyChunk = static_cast<GByte *>(pChunk);
5450 168417 : for (size_t i = 0;
5451 168417 : i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5452 : {
5453 168408 : if (pabyChunk[i] == 1)
5454 127437 : pabyChunk[i] = 255;
5455 : }
5456 : }
5457 0 : else if (eWrkDataType == GDT_UInt16)
5458 : {
5459 0 : GUInt16 *pasChunk = static_cast<GUInt16 *>(pChunk);
5460 0 : for (size_t i = 0;
5461 0 : i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5462 : {
5463 0 : if (pasChunk[i] == 1)
5464 0 : pasChunk[i] = 255;
5465 : }
5466 : }
5467 0 : else if (eWrkDataType == GDT_Float64)
5468 : {
5469 0 : double *padfChunk = static_cast<double *>(pChunk);
5470 0 : for (size_t i = 0;
5471 0 : i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5472 : {
5473 0 : if (padfChunk[i] == 1.0)
5474 0 : padfChunk[i] = 255.0;
5475 : }
5476 : }
5477 : else
5478 : {
5479 0 : CPLAssert(false);
5480 : }
5481 : }
5482 721 : else if (EQUAL(pszResampling, "AVERAGE_BIT2GRAYSCALE_MINISWHITE"))
5483 : {
5484 0 : if (eWrkDataType == GDT_Float32)
5485 : {
5486 0 : float *pafChunk = static_cast<float *>(pChunk);
5487 0 : for (size_t i = 0;
5488 0 : i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5489 : {
5490 0 : if (pafChunk[i] == 1.0f)
5491 0 : pafChunk[i] = 0.0f;
5492 0 : else if (pafChunk[i] == 0.0f)
5493 0 : pafChunk[i] = 255.0f;
5494 : }
5495 : }
5496 0 : else if (eWrkDataType == GDT_UInt8)
5497 : {
5498 0 : GByte *pabyChunk = static_cast<GByte *>(pChunk);
5499 0 : for (size_t i = 0;
5500 0 : i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5501 : {
5502 0 : if (pabyChunk[i] == 1)
5503 0 : pabyChunk[i] = 0;
5504 0 : else if (pabyChunk[i] == 0)
5505 0 : pabyChunk[i] = 255;
5506 : }
5507 : }
5508 0 : else if (eWrkDataType == GDT_UInt16)
5509 : {
5510 0 : GUInt16 *pasChunk = static_cast<GUInt16 *>(pChunk);
5511 0 : for (size_t i = 0;
5512 0 : i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5513 : {
5514 0 : if (pasChunk[i] == 1)
5515 0 : pasChunk[i] = 0;
5516 0 : else if (pasChunk[i] == 0)
5517 0 : pasChunk[i] = 255;
5518 : }
5519 : }
5520 0 : else if (eWrkDataType == GDT_Float64)
5521 : {
5522 0 : double *padfChunk = static_cast<double *>(pChunk);
5523 0 : for (size_t i = 0;
5524 0 : i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5525 : {
5526 0 : if (padfChunk[i] == 1.0)
5527 0 : padfChunk[i] = 0.0;
5528 0 : else if (padfChunk[i] == 0.0)
5529 0 : padfChunk[i] = 255.0;
5530 : }
5531 : }
5532 : else
5533 : {
5534 0 : CPLAssert(false);
5535 : }
5536 : }
5537 :
5538 730 : auto pChunkRaw = pChunk;
5539 730 : auto pabyChunkNodataMaskRaw = pabyChunkNodataMask;
5540 730 : std::shared_ptr<PointerHolder> oSrcBufferHolder;
5541 730 : std::shared_ptr<PointerHolder> oSrcMaskBufferHolder;
5542 730 : if (poJobQueue)
5543 : {
5544 0 : oSrcBufferHolder = std::make_shared<PointerHolder>(pChunk);
5545 : oSrcMaskBufferHolder =
5546 0 : std::make_shared<PointerHolder>(pabyChunkNodataMask);
5547 : }
5548 :
5549 1561 : for (int iOverview = 0; iOverview < nOverviewCount && eErr == CE_None;
5550 : ++iOverview)
5551 : {
5552 831 : GDALRasterBand *poDstBand = papoOvrBands[iOverview];
5553 831 : const int nDstWidth = poDstBand->GetXSize();
5554 831 : const int nDstHeight = poDstBand->GetYSize();
5555 :
5556 831 : const double dfXRatioDstToSrc =
5557 831 : static_cast<double>(nWidth) / nDstWidth;
5558 831 : const double dfYRatioDstToSrc =
5559 831 : static_cast<double>(nHeight) / nDstHeight;
5560 :
5561 : /* --------------------------------------------------------------------
5562 : */
5563 : /* Figure out the line to start writing to, and the first line
5564 : */
5565 : /* to not write to. In theory this approach should ensure that
5566 : */
5567 : /* every output line will be written if all input chunks are */
5568 : /* processed. */
5569 : /* --------------------------------------------------------------------
5570 : */
5571 831 : int nDstYOff =
5572 831 : static_cast<int>(0.5 + nChunkYOff / dfYRatioDstToSrc);
5573 831 : if (nDstYOff == nDstHeight)
5574 0 : continue;
5575 831 : int nDstYOff2 = static_cast<int>(
5576 831 : 0.5 + (nChunkYOff + nFullResYChunk) / dfYRatioDstToSrc);
5577 :
5578 831 : if (nChunkYOff + nFullResYChunk == nHeight)
5579 824 : nDstYOff2 = nDstHeight;
5580 : #if DEBUG_VERBOSE
5581 : CPLDebug("GDAL",
5582 : "Reading (%dx%d -> %dx%d) for output (%dx%d -> %dx%d)", 0,
5583 : nChunkYOffQueried, nWidth, nChunkYSizeQueried, 0, nDstYOff,
5584 : nDstWidth, nDstYOff2 - nDstYOff);
5585 : #endif
5586 :
5587 1662 : auto poJob = std::make_unique<OvrJob>();
5588 831 : poJob->pfnResampleFn = pfnResampleFn;
5589 831 : poJob->bUseGenericResampleFn = bUseGenericResampleFn;
5590 831 : poJob->args.eOvrDataType = poDstBand->GetRasterDataType();
5591 831 : poJob->args.nOvrXSize = poDstBand->GetXSize();
5592 831 : poJob->args.nOvrYSize = poDstBand->GetYSize();
5593 1662 : const char *pszNBITS = poDstBand->GetMetadataItem(
5594 831 : GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE);
5595 831 : poJob->args.nOvrNBITS = pszNBITS ? atoi(pszNBITS) : 0;
5596 831 : poJob->args.dfXRatioDstToSrc = dfXRatioDstToSrc;
5597 831 : poJob->args.dfYRatioDstToSrc = dfYRatioDstToSrc;
5598 831 : poJob->args.eWrkDataType = eWrkDataType;
5599 831 : poJob->pChunk = pChunkRaw;
5600 831 : poJob->args.pabyChunkNodataMask = pabyChunkNodataMaskRaw;
5601 831 : poJob->nSrcWidth = nWidth;
5602 831 : poJob->nSrcHeight = nHeight;
5603 831 : poJob->args.nChunkXOff = 0;
5604 831 : poJob->args.nChunkXSize = nWidth;
5605 831 : poJob->args.nChunkYOff = nChunkYOffQueried;
5606 831 : poJob->args.nChunkYSize = nChunkYSizeQueried;
5607 831 : poJob->nDstWidth = nDstWidth;
5608 831 : poJob->args.nDstXOff = 0;
5609 831 : poJob->args.nDstXOff2 = nDstWidth;
5610 831 : poJob->args.nDstYOff = nDstYOff;
5611 831 : poJob->args.nDstYOff2 = nDstYOff2;
5612 831 : poJob->poDstBand = poDstBand;
5613 831 : poJob->args.pszResampling = pszResampling;
5614 831 : poJob->args.bHasNoData = bHasNoData;
5615 831 : poJob->args.dfNoDataValue = dfNoDataValue;
5616 831 : poJob->args.poColorTable = poColorTable;
5617 831 : poJob->args.eSrcDataType = eSrcDataType;
5618 831 : poJob->args.bPropagateNoData = bPropagateNoData;
5619 :
5620 831 : if (poJobQueue)
5621 : {
5622 0 : poJob->SetSrcMaskBufferHolder(oSrcMaskBufferHolder);
5623 0 : poJob->SetSrcBufferHolder(oSrcBufferHolder);
5624 0 : poJobQueue->SubmitJob(JobResampleFunc, poJob.get());
5625 0 : jobList.emplace_back(std::move(poJob));
5626 : }
5627 : else
5628 : {
5629 831 : JobResampleFunc(poJob.get());
5630 831 : eErr = poJob->eErr;
5631 831 : if (eErr == CE_None)
5632 : {
5633 831 : eErr = WriteJobData(poJob.get());
5634 : }
5635 : }
5636 : }
5637 : }
5638 :
5639 725 : VSIFree(pChunk);
5640 725 : VSIFree(pabyChunkNodataMask);
5641 :
5642 : // Wait for all pending jobs to complete
5643 725 : while (!jobList.empty())
5644 : {
5645 0 : const auto l_eErr = WaitAndFinalizeOldestJob(jobList);
5646 0 : if (l_eErr != CE_None && eErr == CE_None)
5647 0 : eErr = l_eErr;
5648 : }
5649 :
5650 : /* -------------------------------------------------------------------- */
5651 : /* Renormalized overview mean / stddev if needed. */
5652 : /* -------------------------------------------------------------------- */
5653 725 : if (eErr == CE_None && EQUAL(pszResampling, "AVERAGE_MP"))
5654 : {
5655 0 : GDALOverviewMagnitudeCorrection(
5656 : poSrcBand, nOverviewCount,
5657 : reinterpret_cast<GDALRasterBandH *>(papoOvrBands),
5658 : GDALDummyProgress, nullptr);
5659 : }
5660 :
5661 : /* -------------------------------------------------------------------- */
5662 : /* It can be important to flush out data to overviews. */
5663 : /* -------------------------------------------------------------------- */
5664 1549 : for (int iOverview = 0; eErr == CE_None && iOverview < nOverviewCount;
5665 : ++iOverview)
5666 : {
5667 824 : eErr = papoOvrBands[iOverview]->FlushCache(false);
5668 : }
5669 :
5670 725 : if (eErr == CE_None)
5671 725 : pfnProgress(1.0, nullptr, pProgressData);
5672 :
5673 725 : return eErr;
5674 : }
5675 :
5676 : /************************************************************************/
5677 : /* GDALRegenerateOverviewsMultiBand() */
5678 : /************************************************************************/
5679 :
5680 : /**
5681 : * \brief Variant of GDALRegenerateOverviews, specially dedicated for generating
5682 : * compressed pixel-interleaved overviews (JPEG-IN-TIFF for example)
5683 : *
5684 : * This function will generate one or more overview images from a base
5685 : * image using the requested downsampling algorithm. Its primary use
5686 : * is for generating overviews via GDALDataset::BuildOverviews(), but it
5687 : * can also be used to generate downsampled images in one file from another
5688 : * outside the overview architecture.
5689 : *
5690 : * The output bands need to exist in advance and share the same characteristics
5691 : * (type, dimensions)
5692 : *
5693 : * The resampling algorithms supported for the moment are "NEAREST", "AVERAGE",
5694 : * "RMS", "GAUSS", "CUBIC", "CUBICSPLINE", "LANCZOS" and "BILINEAR"
5695 : *
5696 : * It does not support color tables or complex data types.
5697 : *
5698 : * The pseudo-algorithm used by the function is :
5699 : * for each overview
5700 : * iterate on lines of the source by a step of deltay
5701 : * iterate on columns of the source by a step of deltax
5702 : * read the source data of size deltax * deltay for all the bands
5703 : * generate the corresponding overview block for all the bands
5704 : *
5705 : * This function will honour properly NODATA_VALUES tuples (special dataset
5706 : * metadata) so that only a given RGB triplet (in case of a RGB image) will be
5707 : * considered as the nodata value and not each value of the triplet
5708 : * independently per band.
5709 : *
5710 : * Starting with GDAL 3.2, the GDAL_NUM_THREADS configuration option can be set
5711 : * to "ALL_CPUS" or a integer value to specify the number of threads to use for
5712 : * overview computation.
5713 : *
5714 : * @param nBands the number of bands, size of papoSrcBands and size of
5715 : * first dimension of papapoOverviewBands
5716 : * @param papoSrcBands the list of source bands to downsample
5717 : * @param nOverviews the number of downsampled overview levels being generated.
5718 : * @param papapoOverviewBands bidimension array of bands. First dimension is
5719 : * indexed by nBands. Second dimension is indexed by
5720 : * nOverviews.
5721 : * @param pszResampling Resampling algorithm ("NEAREST", "AVERAGE", "RMS",
5722 : * "GAUSS", "CUBIC", "CUBICSPLINE", "LANCZOS" or "BILINEAR").
5723 : * @param pfnProgress progress report function.
5724 : * @param pProgressData progress function callback data.
5725 : * @param papszOptions (GDAL >= 3.6) NULL terminated list of options as
5726 : * key=value pairs, or NULL
5727 : * Starting with GDAL 3.8, the XOFF, YOFF, XSIZE and YSIZE
5728 : * options can be specified to express that overviews should
5729 : * be regenerated only in the specified subset of the source
5730 : * dataset.
5731 : * @return CE_None on success or CE_Failure on failure.
5732 : */
5733 :
5734 390 : CPLErr GDALRegenerateOverviewsMultiBand(
5735 : int nBands, GDALRasterBand *const *papoSrcBands, int nOverviews,
5736 : GDALRasterBand *const *const *papapoOverviewBands,
5737 : const char *pszResampling, GDALProgressFunc pfnProgress,
5738 : void *pProgressData, CSLConstList papszOptions)
5739 : {
5740 390 : CPL_IGNORE_RET_VAL(papszOptions);
5741 :
5742 390 : if (pfnProgress == nullptr)
5743 11 : pfnProgress = GDALDummyProgress;
5744 :
5745 390 : if (EQUAL(pszResampling, "NONE") || nBands == 0 || nOverviews == 0)
5746 3 : return CE_None;
5747 :
5748 : // Sanity checks.
5749 387 : if (!STARTS_WITH_CI(pszResampling, "NEAR") &&
5750 192 : !EQUAL(pszResampling, "RMS") && !EQUAL(pszResampling, "AVERAGE") &&
5751 83 : !EQUAL(pszResampling, "GAUSS") && !EQUAL(pszResampling, "CUBIC") &&
5752 25 : !EQUAL(pszResampling, "CUBICSPLINE") &&
5753 24 : !EQUAL(pszResampling, "LANCZOS") && !EQUAL(pszResampling, "BILINEAR") &&
5754 5 : !EQUAL(pszResampling, "MODE"))
5755 : {
5756 0 : CPLError(CE_Failure, CPLE_NotSupported,
5757 : "GDALRegenerateOverviewsMultiBand: pszResampling='%s' "
5758 : "not supported",
5759 : pszResampling);
5760 0 : return CE_Failure;
5761 : }
5762 :
5763 387 : int nKernelRadius = 0;
5764 : GDALResampleFunction pfnResampleFn =
5765 387 : GDALGetResampleFunction(pszResampling, &nKernelRadius);
5766 387 : if (pfnResampleFn == nullptr)
5767 0 : return CE_Failure;
5768 :
5769 387 : const int nToplevelSrcWidth = papoSrcBands[0]->GetXSize();
5770 387 : const int nToplevelSrcHeight = papoSrcBands[0]->GetYSize();
5771 387 : if (nToplevelSrcWidth <= 0 || nToplevelSrcHeight <= 0)
5772 0 : return CE_None;
5773 387 : GDALDataType eDataType = papoSrcBands[0]->GetRasterDataType();
5774 66237 : for (int iBand = 1; iBand < nBands; ++iBand)
5775 : {
5776 131700 : if (papoSrcBands[iBand]->GetXSize() != nToplevelSrcWidth ||
5777 65850 : papoSrcBands[iBand]->GetYSize() != nToplevelSrcHeight)
5778 : {
5779 0 : CPLError(
5780 : CE_Failure, CPLE_NotSupported,
5781 : "GDALRegenerateOverviewsMultiBand: all the source bands must "
5782 : "have the same dimensions");
5783 0 : return CE_Failure;
5784 : }
5785 65850 : if (papoSrcBands[iBand]->GetRasterDataType() != eDataType)
5786 : {
5787 0 : CPLError(
5788 : CE_Failure, CPLE_NotSupported,
5789 : "GDALRegenerateOverviewsMultiBand: all the source bands must "
5790 : "have the same data type");
5791 0 : return CE_Failure;
5792 : }
5793 : }
5794 :
5795 1029 : for (int iOverview = 0; iOverview < nOverviews; ++iOverview)
5796 : {
5797 642 : const auto poOvrFirstBand = papapoOverviewBands[0][iOverview];
5798 642 : const int nDstWidth = poOvrFirstBand->GetXSize();
5799 642 : const int nDstHeight = poOvrFirstBand->GetYSize();
5800 66752 : for (int iBand = 1; iBand < nBands; ++iBand)
5801 : {
5802 66110 : const auto poOvrBand = papapoOverviewBands[iBand][iOverview];
5803 132220 : if (poOvrBand->GetXSize() != nDstWidth ||
5804 66110 : poOvrBand->GetYSize() != nDstHeight)
5805 : {
5806 0 : CPLError(
5807 : CE_Failure, CPLE_NotSupported,
5808 : "GDALRegenerateOverviewsMultiBand: all the overviews bands "
5809 : "of the same level must have the same dimensions");
5810 0 : return CE_Failure;
5811 : }
5812 66110 : if (poOvrBand->GetRasterDataType() != eDataType)
5813 : {
5814 0 : CPLError(
5815 : CE_Failure, CPLE_NotSupported,
5816 : "GDALRegenerateOverviewsMultiBand: all the overviews bands "
5817 : "must have the same data type as the source bands");
5818 0 : return CE_Failure;
5819 : }
5820 : }
5821 : }
5822 :
5823 : // First pass to compute the total number of pixels to write.
5824 387 : double dfTotalPixelCount = 0;
5825 387 : const int nSrcXOff = atoi(CSLFetchNameValueDef(papszOptions, "XOFF", "0"));
5826 387 : const int nSrcYOff = atoi(CSLFetchNameValueDef(papszOptions, "YOFF", "0"));
5827 387 : const int nSrcXSize = atoi(CSLFetchNameValueDef(
5828 : papszOptions, "XSIZE", CPLSPrintf("%d", nToplevelSrcWidth)));
5829 387 : const int nSrcYSize = atoi(CSLFetchNameValueDef(
5830 : papszOptions, "YSIZE", CPLSPrintf("%d", nToplevelSrcHeight)));
5831 1029 : for (int iOverview = 0; iOverview < nOverviews; ++iOverview)
5832 : {
5833 642 : dfTotalPixelCount +=
5834 1284 : static_cast<double>(nSrcXSize) / nToplevelSrcWidth *
5835 642 : papapoOverviewBands[0][iOverview]->GetXSize() *
5836 1284 : static_cast<double>(nSrcYSize) / nToplevelSrcHeight *
5837 642 : papapoOverviewBands[0][iOverview]->GetYSize();
5838 : }
5839 :
5840 : const GDALDataType eWrkDataType =
5841 387 : GDALGetOvrWorkDataType(pszResampling, eDataType);
5842 : const int nWrkDataTypeSize =
5843 387 : std::max(1, GDALGetDataTypeSizeBytes(eWrkDataType));
5844 :
5845 387 : const bool bIsMask = papoSrcBands[0]->IsMaskBand();
5846 :
5847 : // If we have a nodata mask and we are doing something more complicated
5848 : // than nearest neighbouring, we have to fetch to nodata mask.
5849 : const bool bUseNoDataMask =
5850 573 : !STARTS_WITH_CI(pszResampling, "NEAR") &&
5851 186 : (bIsMask || (papoSrcBands[0]->GetMaskFlags() & GMF_ALL_VALID) == 0);
5852 :
5853 774 : std::vector<bool> abHasNoData(nBands);
5854 774 : std::vector<double> adfNoDataValue(nBands);
5855 :
5856 66624 : for (int iBand = 0; iBand < nBands; ++iBand)
5857 : {
5858 66237 : int nHasNoData = 0;
5859 132474 : adfNoDataValue[iBand] =
5860 66237 : papoSrcBands[iBand]->GetNoDataValue(&nHasNoData);
5861 66237 : abHasNoData[iBand] = CPL_TO_BOOL(nHasNoData);
5862 : }
5863 :
5864 774 : std::string osDetailMessage;
5865 440 : if (bUseNoDataMask &&
5866 53 : papoSrcBands[0]->HasConflictingMaskSources(&osDetailMessage, false))
5867 : {
5868 9 : CPLError(CE_Warning, CPLE_AppDefined, "%s%s", osDetailMessage.c_str(),
5869 18 : abHasNoData[0]
5870 : ? "Only the nodata value will be taken into account."
5871 9 : : "Only the first listed one will be taken into account.");
5872 : }
5873 :
5874 : const bool bPropagateNoData =
5875 387 : CPLTestBool(CPLGetConfigOption("GDAL_OVR_PROPAGATE_NODATA", "NO"));
5876 :
5877 387 : const int nThreads = GDALGetNumThreads(GDAL_DEFAULT_MAX_THREAD_COUNT,
5878 : /* bDefaultToAllCPUs=*/false);
5879 : auto poThreadPool =
5880 387 : nThreads > 1 ? GDALGetGlobalThreadPool(nThreads) : nullptr;
5881 : auto poJobQueue = poThreadPool ? poThreadPool->CreateJobQueue()
5882 774 : : std::unique_ptr<CPLJobQueue>(nullptr);
5883 :
5884 : // Only configurable for debug / testing
5885 387 : const GIntBig nChunkMaxSize = []() -> GIntBig
5886 : {
5887 : const char *pszVal =
5888 387 : CPLGetConfigOption("GDAL_OVR_CHUNK_MAX_SIZE", nullptr);
5889 387 : if (pszVal)
5890 : {
5891 15 : GIntBig nRet = 0;
5892 15 : CPLParseMemorySize(pszVal, &nRet, nullptr);
5893 15 : return std::max<GIntBig>(100, nRet);
5894 : }
5895 372 : return 10 * 1024 * 1024;
5896 387 : }();
5897 :
5898 : // Only configurable for debug / testing
5899 387 : const GIntBig nChunkMaxSizeForTempFile = []() -> GIntBig
5900 : {
5901 387 : const char *pszVal = CPLGetConfigOption(
5902 : "GDAL_OVR_CHUNK_MAX_SIZE_FOR_TEMP_FILE", nullptr);
5903 387 : if (pszVal)
5904 : {
5905 14 : GIntBig nRet = 0;
5906 14 : CPLParseMemorySize(pszVal, &nRet, nullptr);
5907 14 : return std::max<GIntBig>(100, nRet);
5908 : }
5909 373 : const auto nUsableRAM = CPLGetUsablePhysicalRAM();
5910 373 : if (nUsableRAM > 0)
5911 373 : return nUsableRAM / 10;
5912 : // Select a value to be able to at least downsample by 2 for a RGB
5913 : // 1024x1024 tiled output: (2 * 1024 + 2) * (2 * 1024 + 2) * 3 = 12 MB
5914 0 : return 100 * 1024 * 1024;
5915 387 : }();
5916 :
5917 : // Second pass to do the real job.
5918 387 : double dfCurPixelCount = 0;
5919 387 : CPLErr eErr = CE_None;
5920 1024 : for (int iOverview = 0; iOverview < nOverviews && eErr == CE_None;
5921 : ++iOverview)
5922 : {
5923 642 : int iSrcOverview = -1; // -1 means the source bands.
5924 :
5925 : const int nDstTotalWidth =
5926 642 : papapoOverviewBands[0][iOverview]->GetXSize();
5927 : const int nDstTotalHeight =
5928 642 : papapoOverviewBands[0][iOverview]->GetYSize();
5929 :
5930 : // Compute the coordinates of the target region to refresh
5931 642 : constexpr double EPS = 1e-8;
5932 642 : const int nDstXOffStart = static_cast<int>(
5933 642 : static_cast<double>(nSrcXOff) / nToplevelSrcWidth * nDstTotalWidth +
5934 : EPS);
5935 : const int nDstXOffEnd =
5936 1284 : std::min(static_cast<int>(
5937 642 : std::ceil(static_cast<double>(nSrcXOff + nSrcXSize) /
5938 642 : nToplevelSrcWidth * nDstTotalWidth -
5939 : EPS)),
5940 642 : nDstTotalWidth);
5941 642 : const int nDstWidth = nDstXOffEnd - nDstXOffStart;
5942 642 : const int nDstYOffStart =
5943 642 : static_cast<int>(static_cast<double>(nSrcYOff) /
5944 642 : nToplevelSrcHeight * nDstTotalHeight +
5945 : EPS);
5946 : const int nDstYOffEnd =
5947 1284 : std::min(static_cast<int>(
5948 642 : std::ceil(static_cast<double>(nSrcYOff + nSrcYSize) /
5949 642 : nToplevelSrcHeight * nDstTotalHeight -
5950 : EPS)),
5951 642 : nDstTotalHeight);
5952 642 : const int nDstHeight = nDstYOffEnd - nDstYOffStart;
5953 :
5954 : // Try to use previous level of overview as the source to compute
5955 : // the next level.
5956 642 : int nSrcWidth = nToplevelSrcWidth;
5957 642 : int nSrcHeight = nToplevelSrcHeight;
5958 897 : if (iOverview > 0 &&
5959 255 : papapoOverviewBands[0][iOverview - 1]->GetXSize() > nDstTotalWidth)
5960 : {
5961 247 : nSrcWidth = papapoOverviewBands[0][iOverview - 1]->GetXSize();
5962 247 : nSrcHeight = papapoOverviewBands[0][iOverview - 1]->GetYSize();
5963 247 : iSrcOverview = iOverview - 1;
5964 : }
5965 :
5966 642 : const double dfXRatioDstToSrc =
5967 642 : static_cast<double>(nSrcWidth) / nDstTotalWidth;
5968 642 : const double dfYRatioDstToSrc =
5969 642 : static_cast<double>(nSrcHeight) / nDstTotalHeight;
5970 :
5971 : const int nOvrFactor =
5972 1926 : std::max(1, std::max(static_cast<int>(0.5 + dfXRatioDstToSrc),
5973 642 : static_cast<int>(0.5 + dfYRatioDstToSrc)));
5974 :
5975 642 : int nDstChunkXSize = 0;
5976 642 : int nDstChunkYSize = 0;
5977 642 : papapoOverviewBands[0][iOverview]->GetBlockSize(&nDstChunkXSize,
5978 : &nDstChunkYSize);
5979 :
5980 642 : constexpr int PIXEL_MARGIN = 2;
5981 : // Try to extend the chunk size so that the memory needed to acquire
5982 : // source pixels goes up to 10 MB.
5983 : // This can help for drivers that support multi-threaded reading
5984 642 : const int nFullResYChunk = static_cast<int>(std::min<double>(
5985 642 : nSrcHeight, PIXEL_MARGIN + nDstChunkYSize * dfYRatioDstToSrc));
5986 642 : const int nFullResYChunkQueried = static_cast<int>(std::min<int64_t>(
5987 1284 : nSrcHeight,
5988 1284 : nFullResYChunk + static_cast<int64_t>(RADIUS_TO_DIAMETER) *
5989 642 : nKernelRadius * nOvrFactor));
5990 873 : while (nDstChunkXSize < nDstWidth)
5991 : {
5992 251 : constexpr int INCREASE_FACTOR = 2;
5993 :
5994 251 : const int nFullResXChunk = static_cast<int>(std::min<double>(
5995 502 : nSrcWidth, PIXEL_MARGIN + INCREASE_FACTOR * nDstChunkXSize *
5996 251 : dfXRatioDstToSrc));
5997 :
5998 : const int nFullResXChunkQueried =
5999 251 : static_cast<int>(std::min<int64_t>(
6000 502 : nSrcWidth,
6001 502 : nFullResXChunk + static_cast<int64_t>(RADIUS_TO_DIAMETER) *
6002 251 : nKernelRadius * nOvrFactor));
6003 :
6004 251 : if (nBands > nChunkMaxSize / nFullResXChunkQueried /
6005 251 : nFullResYChunkQueried / nWrkDataTypeSize)
6006 : {
6007 20 : break;
6008 : }
6009 :
6010 231 : nDstChunkXSize *= INCREASE_FACTOR;
6011 : }
6012 642 : nDstChunkXSize = std::min(nDstChunkXSize, nDstWidth);
6013 :
6014 642 : const int nFullResXChunk = static_cast<int>(std::min<double>(
6015 642 : nSrcWidth, PIXEL_MARGIN + nDstChunkXSize * dfXRatioDstToSrc));
6016 642 : const int nFullResXChunkQueried = static_cast<int>(std::min<int64_t>(
6017 1284 : nSrcWidth,
6018 1284 : nFullResXChunk + static_cast<int64_t>(RADIUS_TO_DIAMETER) *
6019 642 : nKernelRadius * nOvrFactor));
6020 :
6021 : // Make sure that the RAM requirements to acquire the source data does
6022 : // not exceed nChunkMaxSizeForTempFile
6023 : // If so, reduce the destination chunk size, generate overviews in a
6024 : // temporary dataset, and copy that temporary dataset over the target
6025 : // overview bands (to avoid issues with lossy compression)
6026 : const bool bOverflowFullResXChunkYChunkQueried =
6027 642 : nBands > std::numeric_limits<int64_t>::max() /
6028 642 : nFullResXChunkQueried / nFullResYChunkQueried /
6029 642 : nWrkDataTypeSize;
6030 :
6031 642 : const auto nMemRequirement =
6032 : bOverflowFullResXChunkYChunkQueried
6033 642 : ? 0
6034 638 : : static_cast<GIntBig>(nFullResXChunkQueried) *
6035 638 : nFullResYChunkQueried * nBands * nWrkDataTypeSize;
6036 : // Use a temporary dataset with a smaller destination chunk size
6037 642 : const auto nOverShootFactor =
6038 : nMemRequirement / nChunkMaxSizeForTempFile;
6039 :
6040 642 : constexpr int MIN_OVERSHOOT_FACTOR = 4;
6041 : const auto nSqrtOverShootFactor = std::max<GIntBig>(
6042 1284 : MIN_OVERSHOOT_FACTOR, static_cast<GIntBig>(std::ceil(std::sqrt(
6043 642 : static_cast<double>(nOverShootFactor)))));
6044 642 : constexpr int DEFAULT_CHUNK_SIZE = 256;
6045 642 : constexpr int GTIFF_BLOCK_SIZE_MULTIPLE = 16;
6046 : const int nReducedDstChunkXSize =
6047 : bOverflowFullResXChunkYChunkQueried
6048 1280 : ? DEFAULT_CHUNK_SIZE
6049 1280 : : std::max(1, static_cast<int>(nDstChunkXSize /
6050 1280 : nSqrtOverShootFactor) &
6051 638 : ~(GTIFF_BLOCK_SIZE_MULTIPLE - 1));
6052 : const int nReducedDstChunkYSize =
6053 : bOverflowFullResXChunkYChunkQueried
6054 1280 : ? DEFAULT_CHUNK_SIZE
6055 1280 : : std::max(1, static_cast<int>(nDstChunkYSize /
6056 1280 : nSqrtOverShootFactor) &
6057 638 : ~(GTIFF_BLOCK_SIZE_MULTIPLE - 1));
6058 :
6059 642 : if (bOverflowFullResXChunkYChunkQueried ||
6060 : nMemRequirement > nChunkMaxSizeForTempFile)
6061 : {
6062 : const auto nDTSize =
6063 43 : std::max(1, GDALGetDataTypeSizeBytes(eDataType));
6064 : const bool bTmpDSMemRequirementOverflow =
6065 43 : nBands > std::numeric_limits<int64_t>::max() / nDstWidth /
6066 43 : nDstHeight / nDTSize;
6067 43 : const auto nTmpDSMemRequirement =
6068 : bTmpDSMemRequirementOverflow
6069 43 : ? 0
6070 41 : : static_cast<GIntBig>(nDstWidth) * nDstHeight * nBands *
6071 41 : nDTSize;
6072 :
6073 : // make sure that one band buffer doesn't overflow size_t
6074 : const bool bChunkSizeOverflow =
6075 43 : static_cast<size_t>(nDTSize) >
6076 43 : std::numeric_limits<size_t>::max() / nDstWidth / nDstHeight;
6077 43 : const size_t nChunkSize =
6078 : bChunkSizeOverflow
6079 43 : ? 0
6080 41 : : static_cast<size_t>(nDstWidth) * nDstHeight * nDTSize;
6081 :
6082 : const auto CreateVRT =
6083 41 : [nBands, nSrcWidth, nSrcHeight, nDstTotalWidth, nDstTotalHeight,
6084 : pszResampling, eWrkDataType, papoSrcBands, papapoOverviewBands,
6085 : iSrcOverview, &abHasNoData,
6086 393585 : &adfNoDataValue](int nVRTBlockXSize, int nVRTBlockYSize)
6087 : {
6088 : auto poVRTDS = std::make_unique<VRTDataset>(
6089 41 : nDstTotalWidth, nDstTotalHeight, nVRTBlockXSize,
6090 41 : nVRTBlockYSize);
6091 :
6092 65620 : for (int iBand = 0; iBand < nBands; ++iBand)
6093 : {
6094 131158 : auto poVRTSrc = std::make_unique<VRTSimpleSource>();
6095 65579 : poVRTSrc->SetResampling(pszResampling);
6096 65579 : poVRTDS->AddBand(eWrkDataType);
6097 : auto poVRTBand = static_cast<VRTSourcedRasterBand *>(
6098 65579 : poVRTDS->GetRasterBand(iBand + 1));
6099 :
6100 65579 : auto poSrcBand = papoSrcBands[iBand];
6101 65579 : if (iSrcOverview != -1)
6102 24 : poSrcBand = papapoOverviewBands[iBand][iSrcOverview];
6103 65579 : poVRTBand->ConfigureSource(
6104 : poVRTSrc.get(), poSrcBand, false, 0, 0, nSrcWidth,
6105 : nSrcHeight, 0, 0, nDstTotalWidth, nDstTotalHeight);
6106 : // Add the source to the band
6107 65579 : poVRTBand->AddSource(poVRTSrc.release());
6108 65579 : if (abHasNoData[iBand])
6109 3 : poVRTBand->SetNoDataValue(adfNoDataValue[iBand]);
6110 : }
6111 :
6112 42 : if (papoSrcBands[0]->GetMaskFlags() == GMF_PER_DATASET &&
6113 1 : poVRTDS->CreateMaskBand(GMF_PER_DATASET) == CE_None)
6114 : {
6115 : VRTSourcedRasterBand *poMaskVRTBand =
6116 1 : cpl::down_cast<VRTSourcedRasterBand *>(
6117 1 : poVRTDS->GetRasterBand(1)->GetMaskBand());
6118 1 : auto poSrcBand = papoSrcBands[0];
6119 1 : if (iSrcOverview != -1)
6120 0 : poSrcBand = papapoOverviewBands[0][iSrcOverview];
6121 1 : poMaskVRTBand->AddMaskBandSource(
6122 1 : poSrcBand->GetMaskBand(), 0, 0, nSrcWidth, nSrcHeight,
6123 : 0, 0, nDstTotalWidth, nDstTotalHeight);
6124 : }
6125 :
6126 41 : return poVRTDS;
6127 43 : };
6128 :
6129 : // If the overview accommodates chunking, do so and recurse
6130 : // to avoid generating full size temporary files
6131 43 : if (!bOverflowFullResXChunkYChunkQueried &&
6132 39 : !bTmpDSMemRequirementOverflow && !bChunkSizeOverflow &&
6133 39 : (nDstChunkXSize < nDstWidth || nDstChunkYSize < nDstHeight))
6134 : {
6135 : // Create a VRT with the smaller chunk to do the scaling
6136 : auto poVRTDS =
6137 13 : CreateVRT(nReducedDstChunkXSize, nReducedDstChunkYSize);
6138 :
6139 13 : std::vector<GDALRasterBand *> apoVRTBand(nBands);
6140 13 : std::vector<GDALRasterBand *> apoDstBand(nBands);
6141 65560 : for (int iBand = 0; iBand < nBands; ++iBand)
6142 : {
6143 65547 : apoDstBand[iBand] = papapoOverviewBands[iBand][iOverview];
6144 65547 : apoVRTBand[iBand] = poVRTDS->GetRasterBand(iBand + 1);
6145 : }
6146 :
6147 : // Use a flag to avoid reading from the overview being built
6148 : GDALRasterIOExtraArg sExtraArg;
6149 13 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
6150 13 : if (iSrcOverview == -1)
6151 13 : sExtraArg.bUseOnlyThisScale = true;
6152 :
6153 : // A single band buffer for data transfer to the overview
6154 13 : std::vector<GByte> abyChunk;
6155 : try
6156 : {
6157 13 : abyChunk.resize(nChunkSize);
6158 : }
6159 0 : catch (const std::exception &)
6160 : {
6161 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
6162 : "Out of memory allocating temporary buffer");
6163 0 : return CE_Failure;
6164 : }
6165 :
6166 : // Loop over output height, in chunks
6167 13 : for (int nDstYOff = nDstYOffStart;
6168 38 : nDstYOff < nDstYOffEnd && eErr == CE_None;
6169 : /* */)
6170 : {
6171 : const int nDstYCount =
6172 25 : std::min(nDstChunkYSize, nDstYOffEnd - nDstYOff);
6173 : // Loop over output width, in output chunks
6174 25 : for (int nDstXOff = nDstXOffStart;
6175 74 : nDstXOff < nDstXOffEnd && eErr == CE_None;
6176 : /* */)
6177 : {
6178 : const int nDstXCount =
6179 49 : std::min(nDstChunkXSize, nDstXOffEnd - nDstXOff);
6180 : // Read and transfer the chunk to the overview
6181 98 : for (int iBand = 0; iBand < nBands && eErr == CE_None;
6182 : ++iBand)
6183 : {
6184 98 : eErr = apoVRTBand[iBand]->RasterIO(
6185 : GF_Read, nDstXOff, nDstYOff, nDstXCount,
6186 49 : nDstYCount, abyChunk.data(), nDstXCount,
6187 : nDstYCount, eDataType, 0, 0, &sExtraArg);
6188 49 : if (eErr == CE_None)
6189 : {
6190 96 : eErr = apoDstBand[iBand]->RasterIO(
6191 : GF_Write, nDstXOff, nDstYOff, nDstXCount,
6192 48 : nDstYCount, abyChunk.data(), nDstXCount,
6193 : nDstYCount, eDataType, 0, 0, nullptr);
6194 : }
6195 : }
6196 :
6197 49 : dfCurPixelCount +=
6198 49 : static_cast<double>(nDstXCount) * nDstYCount;
6199 :
6200 49 : nDstXOff += nDstXCount;
6201 : } // width
6202 :
6203 25 : if (!pfnProgress(dfCurPixelCount / dfTotalPixelCount,
6204 : nullptr, pProgressData))
6205 : {
6206 0 : CPLError(CE_Failure, CPLE_UserInterrupt,
6207 : "User terminated");
6208 0 : eErr = CE_Failure;
6209 : }
6210 :
6211 25 : nDstYOff += nDstYCount;
6212 : } // height
6213 :
6214 13 : if (CE_None != eErr)
6215 : {
6216 1 : CPLError(CE_Failure, CPLE_AppDefined,
6217 : "Error while writing overview");
6218 1 : return CE_Failure;
6219 : }
6220 :
6221 12 : pfnProgress(1.0, nullptr, pProgressData);
6222 : // Flush the overviews we just generated
6223 24 : for (int iBand = 0; iBand < nBands; ++iBand)
6224 12 : apoDstBand[iBand]->FlushCache(false);
6225 :
6226 12 : continue; // Next overview
6227 : } // chunking via temporary dataset
6228 :
6229 0 : std::unique_ptr<GDALDataset> poTmpDS;
6230 : // Config option mostly/only for autotest purposes
6231 : const char *pszGDAL_OVR_TEMP_DRIVER =
6232 30 : CPLGetConfigOption("GDAL_OVR_TEMP_DRIVER", "");
6233 30 : if ((!bTmpDSMemRequirementOverflow &&
6234 4 : nTmpDSMemRequirement <= nChunkMaxSizeForTempFile &&
6235 4 : !EQUAL(pszGDAL_OVR_TEMP_DRIVER, "GTIFF")) ||
6236 26 : EQUAL(pszGDAL_OVR_TEMP_DRIVER, "MEM"))
6237 : {
6238 10 : auto poTmpDrv = GetGDALDriverManager()->GetDriverByName("MEM");
6239 10 : if (!poTmpDrv)
6240 : {
6241 0 : eErr = CE_Failure;
6242 0 : break;
6243 : }
6244 10 : poTmpDS.reset(poTmpDrv->Create("", nDstTotalWidth,
6245 : nDstTotalHeight, nBands,
6246 10 : eDataType, nullptr));
6247 : }
6248 : else
6249 : {
6250 : // Create a temporary file for the overview
6251 : auto poTmpDrv =
6252 20 : GetGDALDriverManager()->GetDriverByName("GTiff");
6253 20 : if (!poTmpDrv)
6254 : {
6255 0 : eErr = CE_Failure;
6256 0 : break;
6257 : }
6258 40 : std::string osTmpFilename;
6259 20 : auto poDstDS = papapoOverviewBands[0][0]->GetDataset();
6260 20 : if (poDstDS)
6261 : {
6262 20 : osTmpFilename = poDstDS->GetDescription();
6263 : VSIStatBufL sStatBuf;
6264 20 : if (!osTmpFilename.empty() &&
6265 0 : VSIStatL(osTmpFilename.c_str(), &sStatBuf) == 0)
6266 0 : osTmpFilename += "_tmp_ovr.tif";
6267 : }
6268 20 : if (osTmpFilename.empty())
6269 : {
6270 20 : osTmpFilename = CPLGenerateTempFilenameSafe(nullptr);
6271 20 : osTmpFilename += ".tif";
6272 : }
6273 20 : CPLDebug("GDAL", "Creating temporary file %s of %d x %d x %d",
6274 : osTmpFilename.c_str(), nDstWidth, nDstHeight, nBands);
6275 40 : CPLStringList aosCO;
6276 20 : if (0 == ((nReducedDstChunkXSize % GTIFF_BLOCK_SIZE_MULTIPLE) |
6277 20 : (nReducedDstChunkYSize % GTIFF_BLOCK_SIZE_MULTIPLE)))
6278 : {
6279 14 : aosCO.SetNameValue("TILED", "YES");
6280 : aosCO.SetNameValue("BLOCKXSIZE",
6281 14 : CPLSPrintf("%d", nReducedDstChunkXSize));
6282 : aosCO.SetNameValue("BLOCKYSIZE",
6283 14 : CPLSPrintf("%d", nReducedDstChunkYSize));
6284 : }
6285 20 : if (const char *pszCOList =
6286 20 : poTmpDrv->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST))
6287 : {
6288 : aosCO.SetNameValue(
6289 20 : "COMPRESS", strstr(pszCOList, "ZSTD") ? "ZSTD" : "LZW");
6290 : }
6291 20 : poTmpDS.reset(poTmpDrv->Create(osTmpFilename.c_str(), nDstWidth,
6292 : nDstHeight, nBands, eDataType,
6293 20 : aosCO.List()));
6294 20 : if (poTmpDS)
6295 : {
6296 18 : poTmpDS->MarkSuppressOnClose();
6297 18 : VSIUnlink(osTmpFilename.c_str());
6298 : }
6299 : }
6300 30 : if (!poTmpDS)
6301 : {
6302 2 : eErr = CE_Failure;
6303 2 : break;
6304 : }
6305 :
6306 : // Create a full size VRT to do the resampling without edge effects
6307 : auto poVRTDS =
6308 28 : CreateVRT(nReducedDstChunkXSize, nReducedDstChunkYSize);
6309 :
6310 : // Allocate a band buffer with the overview chunk size
6311 : std::unique_ptr<void, VSIFreeReleaser> pDstBuffer(
6312 : VSI_MALLOC3_VERBOSE(size_t(nWrkDataTypeSize), nDstChunkXSize,
6313 28 : nDstChunkYSize));
6314 28 : if (pDstBuffer == nullptr)
6315 : {
6316 0 : eErr = CE_Failure;
6317 0 : break;
6318 : }
6319 :
6320 : // Use a flag to avoid reading the overview being built
6321 : GDALRasterIOExtraArg sExtraArg;
6322 28 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
6323 28 : if (iSrcOverview == -1)
6324 4 : sExtraArg.bUseOnlyThisScale = true;
6325 :
6326 : // Scale and copy data from the VRT to the temp file
6327 28 : for (int nDstYOff = nDstYOffStart;
6328 914 : nDstYOff < nDstYOffEnd && eErr == CE_None;
6329 : /* */)
6330 : {
6331 : const int nDstYCount =
6332 886 : std::min(nReducedDstChunkYSize, nDstYOffEnd - nDstYOff);
6333 886 : for (int nDstXOff = nDstXOffStart;
6334 201218 : nDstXOff < nDstXOffEnd && eErr == CE_None;
6335 : /* */)
6336 : {
6337 : const int nDstXCount =
6338 200332 : std::min(nReducedDstChunkXSize, nDstXOffEnd - nDstXOff);
6339 400668 : for (int iBand = 0; iBand < nBands && eErr == CE_None;
6340 : ++iBand)
6341 : {
6342 200336 : auto poSrcBand = poVRTDS->GetRasterBand(iBand + 1);
6343 200336 : eErr = poSrcBand->RasterIO(
6344 : GF_Read, nDstXOff, nDstYOff, nDstXCount, nDstYCount,
6345 : pDstBuffer.get(), nDstXCount, nDstYCount,
6346 : eWrkDataType, 0, 0, &sExtraArg);
6347 200336 : if (eErr == CE_None)
6348 : {
6349 : // Write to the temporary dataset, shifted
6350 200334 : auto poOvrBand = poTmpDS->GetRasterBand(iBand + 1);
6351 200334 : eErr = poOvrBand->RasterIO(
6352 : GF_Write, nDstXOff - nDstXOffStart,
6353 : nDstYOff - nDstYOffStart, nDstXCount,
6354 : nDstYCount, pDstBuffer.get(), nDstXCount,
6355 : nDstYCount, eWrkDataType, 0, 0, nullptr);
6356 : }
6357 : }
6358 200332 : nDstXOff += nDstXCount;
6359 : }
6360 886 : nDstYOff += nDstYCount;
6361 : }
6362 :
6363 : // Copy from the temporary to the overview
6364 28 : for (int nDstYOff = nDstYOffStart;
6365 54 : nDstYOff < nDstYOffEnd && eErr == CE_None;
6366 : /* */)
6367 : {
6368 : const int nDstYCount =
6369 26 : std::min(nDstChunkYSize, nDstYOffEnd - nDstYOff);
6370 26 : for (int nDstXOff = nDstXOffStart;
6371 52 : nDstXOff < nDstXOffEnd && eErr == CE_None;
6372 : /* */)
6373 : {
6374 : const int nDstXCount =
6375 26 : std::min(nDstChunkXSize, nDstXOffEnd - nDstXOff);
6376 56 : for (int iBand = 0; iBand < nBands && eErr == CE_None;
6377 : ++iBand)
6378 : {
6379 30 : auto poSrcBand = poTmpDS->GetRasterBand(iBand + 1);
6380 30 : eErr = poSrcBand->RasterIO(
6381 : GF_Read, nDstXOff - nDstXOffStart,
6382 : nDstYOff - nDstYOffStart, nDstXCount, nDstYCount,
6383 : pDstBuffer.get(), nDstXCount, nDstYCount,
6384 : eWrkDataType, 0, 0, nullptr);
6385 30 : if (eErr == CE_None)
6386 : {
6387 : // Write to the destination overview bands
6388 30 : auto poOvrBand =
6389 30 : papapoOverviewBands[iBand][iOverview];
6390 30 : eErr = poOvrBand->RasterIO(
6391 : GF_Write, nDstXOff, nDstYOff, nDstXCount,
6392 : nDstYCount, pDstBuffer.get(), nDstXCount,
6393 : nDstYCount, eWrkDataType, 0, 0, nullptr);
6394 : }
6395 : }
6396 26 : nDstXOff += nDstXCount;
6397 : }
6398 26 : nDstYOff += nDstYCount;
6399 : }
6400 :
6401 28 : if (eErr != CE_None)
6402 : {
6403 2 : CPLError(CE_Failure, CPLE_AppDefined,
6404 : "Failed to write overview %d", iOverview);
6405 2 : return eErr;
6406 : }
6407 :
6408 : // Flush the data to overviews.
6409 56 : for (int iBand = 0; iBand < nBands; ++iBand)
6410 30 : papapoOverviewBands[iBand][iOverview]->FlushCache(false);
6411 :
6412 26 : continue;
6413 : }
6414 :
6415 : // Structure describing a resampling job
6416 : struct OvrJob
6417 : {
6418 : // Buffers to free when job is finished
6419 : std::unique_ptr<PointerHolder> oSrcMaskBufferHolder{};
6420 : std::unique_ptr<PointerHolder> oSrcBufferHolder{};
6421 : std::unique_ptr<PointerHolder> oDstBufferHolder{};
6422 :
6423 : GDALRasterBand *poDstBand = nullptr;
6424 :
6425 : // Input parameters of pfnResampleFn
6426 : GDALResampleFunction pfnResampleFn = nullptr;
6427 : GDALOverviewResampleArgs args{};
6428 : const void *pChunk = nullptr;
6429 :
6430 : // Output values of resampling function
6431 : CPLErr eErr = CE_Failure;
6432 : void *pDstBuffer = nullptr;
6433 : GDALDataType eDstBufferDataType = GDT_Unknown;
6434 :
6435 3314 : void NotifyFinished()
6436 : {
6437 6628 : std::lock_guard guard(mutex);
6438 3314 : bFinished = true;
6439 3314 : cv.notify_one();
6440 3314 : }
6441 :
6442 2 : bool IsFinished()
6443 : {
6444 2 : std::lock_guard guard(mutex);
6445 4 : return bFinished;
6446 : }
6447 :
6448 19 : void WaitFinished()
6449 : {
6450 38 : std::unique_lock oGuard(mutex);
6451 29 : while (!bFinished)
6452 : {
6453 10 : cv.wait(oGuard);
6454 : }
6455 19 : }
6456 :
6457 : private:
6458 : // Synchronization
6459 : bool bFinished = false;
6460 : std::mutex mutex{};
6461 : std::condition_variable cv{};
6462 : };
6463 :
6464 : // Thread function to resample
6465 3314 : const auto JobResampleFunc = [](void *pData)
6466 : {
6467 3314 : OvrJob *poJob = static_cast<OvrJob *>(pData);
6468 :
6469 3314 : poJob->eErr = poJob->pfnResampleFn(poJob->args, poJob->pChunk,
6470 : &(poJob->pDstBuffer),
6471 : &(poJob->eDstBufferDataType));
6472 :
6473 3314 : auto pDstBuffer = poJob->pDstBuffer;
6474 : poJob->oDstBufferHolder =
6475 3314 : std::make_unique<PointerHolder>(pDstBuffer);
6476 :
6477 3314 : poJob->NotifyFinished();
6478 3314 : };
6479 :
6480 : // Function to write resample data to target band
6481 3314 : const auto WriteJobData = [](const OvrJob *poJob)
6482 : {
6483 6628 : return poJob->poDstBand->RasterIO(
6484 3314 : GF_Write, poJob->args.nDstXOff, poJob->args.nDstYOff,
6485 3314 : poJob->args.nDstXOff2 - poJob->args.nDstXOff,
6486 3314 : poJob->args.nDstYOff2 - poJob->args.nDstYOff, poJob->pDstBuffer,
6487 3314 : poJob->args.nDstXOff2 - poJob->args.nDstXOff,
6488 3314 : poJob->args.nDstYOff2 - poJob->args.nDstYOff,
6489 3314 : poJob->eDstBufferDataType, 0, 0, nullptr);
6490 : };
6491 :
6492 : // Wait for completion of oldest job and serialize it
6493 : const auto WaitAndFinalizeOldestJob =
6494 19 : [WriteJobData](std::list<std::unique_ptr<OvrJob>> &jobList)
6495 : {
6496 19 : auto poOldestJob = jobList.front().get();
6497 19 : poOldestJob->WaitFinished();
6498 19 : CPLErr l_eErr = poOldestJob->eErr;
6499 19 : if (l_eErr == CE_None)
6500 : {
6501 19 : l_eErr = WriteJobData(poOldestJob);
6502 : }
6503 :
6504 19 : jobList.pop_front();
6505 19 : return l_eErr;
6506 : };
6507 :
6508 : // Queue of jobs
6509 1198 : std::list<std::unique_ptr<OvrJob>> jobList;
6510 :
6511 1198 : std::vector<std::unique_ptr<void, VSIFreeReleaser>> apaChunk(nBands);
6512 : std::vector<std::unique_ptr<GByte, VSIFreeReleaser>>
6513 1198 : apabyChunkNoDataMask(nBands);
6514 :
6515 : // Iterate on destination overview, block by block.
6516 599 : for (int nDstYOff = nDstYOffStart;
6517 2106 : nDstYOff < nDstYOffEnd && eErr == CE_None;
6518 1507 : nDstYOff += nDstChunkYSize)
6519 : {
6520 : int nDstYCount;
6521 1507 : if (nDstYOff + nDstChunkYSize <= nDstYOffEnd)
6522 1085 : nDstYCount = nDstChunkYSize;
6523 : else
6524 422 : nDstYCount = nDstYOffEnd - nDstYOff;
6525 :
6526 1507 : int nChunkYOff = static_cast<int>(nDstYOff * dfYRatioDstToSrc);
6527 1507 : int nChunkYOff2 = static_cast<int>(
6528 1507 : ceil((nDstYOff + nDstYCount) * dfYRatioDstToSrc));
6529 1507 : if (nChunkYOff2 > nSrcHeight ||
6530 1507 : nDstYOff + nDstYCount == nDstTotalHeight)
6531 593 : nChunkYOff2 = nSrcHeight;
6532 1507 : int nYCount = nChunkYOff2 - nChunkYOff;
6533 1507 : CPLAssert(nYCount <= nFullResYChunk);
6534 :
6535 1507 : int nChunkYOffQueried = nChunkYOff - nKernelRadius * nOvrFactor;
6536 1507 : int nChunkYSizeQueried =
6537 1507 : nYCount + RADIUS_TO_DIAMETER * nKernelRadius * nOvrFactor;
6538 1507 : if (nChunkYOffQueried < 0)
6539 : {
6540 145 : nChunkYSizeQueried += nChunkYOffQueried;
6541 145 : nChunkYOffQueried = 0;
6542 : }
6543 1507 : if (nChunkYSizeQueried + nChunkYOffQueried > nSrcHeight)
6544 146 : nChunkYSizeQueried = nSrcHeight - nChunkYOffQueried;
6545 1507 : CPLAssert(nChunkYSizeQueried <= nFullResYChunkQueried);
6546 :
6547 1507 : if (!pfnProgress(std::min(1.0, dfCurPixelCount / dfTotalPixelCount),
6548 : nullptr, pProgressData))
6549 : {
6550 1 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
6551 1 : eErr = CE_Failure;
6552 : }
6553 :
6554 : // Iterate on destination overview, block by block.
6555 1507 : for (int nDstXOff = nDstXOffStart;
6556 3059 : nDstXOff < nDstXOffEnd && eErr == CE_None;
6557 1552 : nDstXOff += nDstChunkXSize)
6558 : {
6559 1552 : int nDstXCount = 0;
6560 1552 : if (nDstXOff + nDstChunkXSize <= nDstXOffEnd)
6561 1532 : nDstXCount = nDstChunkXSize;
6562 : else
6563 20 : nDstXCount = nDstXOffEnd - nDstXOff;
6564 :
6565 1552 : dfCurPixelCount += static_cast<double>(nDstXCount) * nDstYCount;
6566 :
6567 1552 : int nChunkXOff = static_cast<int>(nDstXOff * dfXRatioDstToSrc);
6568 1552 : int nChunkXOff2 = static_cast<int>(
6569 1552 : ceil((nDstXOff + nDstXCount) * dfXRatioDstToSrc));
6570 1552 : if (nChunkXOff2 > nSrcWidth ||
6571 1552 : nDstXOff + nDstXCount == nDstTotalWidth)
6572 1471 : nChunkXOff2 = nSrcWidth;
6573 1552 : const int nXCount = nChunkXOff2 - nChunkXOff;
6574 1552 : CPLAssert(nXCount <= nFullResXChunk);
6575 :
6576 1552 : int nChunkXOffQueried = nChunkXOff - nKernelRadius * nOvrFactor;
6577 1552 : int nChunkXSizeQueried =
6578 1552 : nXCount + RADIUS_TO_DIAMETER * nKernelRadius * nOvrFactor;
6579 1552 : if (nChunkXOffQueried < 0)
6580 : {
6581 208 : nChunkXSizeQueried += nChunkXOffQueried;
6582 208 : nChunkXOffQueried = 0;
6583 : }
6584 1552 : if (nChunkXSizeQueried + nChunkXOffQueried > nSrcWidth)
6585 217 : nChunkXSizeQueried = nSrcWidth - nChunkXOffQueried;
6586 1552 : CPLAssert(nChunkXSizeQueried <= nFullResXChunkQueried);
6587 : #if DEBUG_VERBOSE
6588 : CPLDebug("GDAL",
6589 : "Reading (%dx%d -> %dx%d) for output (%dx%d -> %dx%d)",
6590 : nChunkXOffQueried, nChunkYOffQueried,
6591 : nChunkXSizeQueried, nChunkYSizeQueried, nDstXOff,
6592 : nDstYOff, nDstXCount, nDstYCount);
6593 : #endif
6594 :
6595 : // Avoid accumulating too many tasks and exhaust RAM
6596 :
6597 : // Try to complete already finished jobs
6598 1552 : while (eErr == CE_None && !jobList.empty())
6599 : {
6600 2 : auto poOldestJob = jobList.front().get();
6601 2 : if (!poOldestJob->IsFinished())
6602 2 : break;
6603 0 : eErr = poOldestJob->eErr;
6604 0 : if (eErr == CE_None)
6605 : {
6606 0 : eErr = WriteJobData(poOldestJob);
6607 : }
6608 :
6609 0 : jobList.pop_front();
6610 : }
6611 :
6612 : // And in case we have saturated the number of threads,
6613 : // wait for completion of tasks to go below the threshold.
6614 3104 : while (eErr == CE_None &&
6615 1552 : jobList.size() >= static_cast<size_t>(nThreads))
6616 : {
6617 0 : eErr = WaitAndFinalizeOldestJob(jobList);
6618 : }
6619 :
6620 : // Read the source buffers for all the bands.
6621 4866 : for (int iBand = 0; iBand < nBands && eErr == CE_None; ++iBand)
6622 : {
6623 : // (Re)allocate buffers if needed
6624 3314 : if (apaChunk[iBand] == nullptr)
6625 : {
6626 1173 : apaChunk[iBand].reset(VSI_MALLOC3_VERBOSE(
6627 : nFullResXChunkQueried, nFullResYChunkQueried,
6628 : nWrkDataTypeSize));
6629 1173 : if (apaChunk[iBand] == nullptr)
6630 : {
6631 0 : eErr = CE_Failure;
6632 : }
6633 : }
6634 3649 : if (bUseNoDataMask &&
6635 335 : apabyChunkNoDataMask[iBand] == nullptr)
6636 : {
6637 268 : apabyChunkNoDataMask[iBand].reset(
6638 268 : static_cast<GByte *>(VSI_MALLOC2_VERBOSE(
6639 : nFullResXChunkQueried, nFullResYChunkQueried)));
6640 268 : if (apabyChunkNoDataMask[iBand] == nullptr)
6641 : {
6642 0 : eErr = CE_Failure;
6643 : }
6644 : }
6645 :
6646 3314 : if (eErr == CE_None)
6647 : {
6648 3314 : GDALRasterBand *poSrcBand = nullptr;
6649 3314 : if (iSrcOverview == -1)
6650 2422 : poSrcBand = papoSrcBands[iBand];
6651 : else
6652 892 : poSrcBand =
6653 892 : papapoOverviewBands[iBand][iSrcOverview];
6654 3314 : eErr = poSrcBand->RasterIO(
6655 : GF_Read, nChunkXOffQueried, nChunkYOffQueried,
6656 : nChunkXSizeQueried, nChunkYSizeQueried,
6657 3314 : apaChunk[iBand].get(), nChunkXSizeQueried,
6658 : nChunkYSizeQueried, eWrkDataType, 0, 0, nullptr);
6659 :
6660 3314 : if (bUseNoDataMask && eErr == CE_None)
6661 : {
6662 335 : auto poMaskBand = poSrcBand->IsMaskBand()
6663 335 : ? poSrcBand
6664 253 : : poSrcBand->GetMaskBand();
6665 335 : eErr = poMaskBand->RasterIO(
6666 : GF_Read, nChunkXOffQueried, nChunkYOffQueried,
6667 : nChunkXSizeQueried, nChunkYSizeQueried,
6668 335 : apabyChunkNoDataMask[iBand].get(),
6669 : nChunkXSizeQueried, nChunkYSizeQueried,
6670 : GDT_UInt8, 0, 0, nullptr);
6671 : }
6672 : }
6673 : }
6674 :
6675 : // Compute the resulting overview block.
6676 4866 : for (int iBand = 0; iBand < nBands && eErr == CE_None; ++iBand)
6677 : {
6678 6628 : auto poJob = std::make_unique<OvrJob>();
6679 3314 : poJob->pfnResampleFn = pfnResampleFn;
6680 3314 : poJob->poDstBand = papapoOverviewBands[iBand][iOverview];
6681 6628 : poJob->args.eOvrDataType =
6682 3314 : poJob->poDstBand->GetRasterDataType();
6683 3314 : poJob->args.nOvrXSize = poJob->poDstBand->GetXSize();
6684 3314 : poJob->args.nOvrYSize = poJob->poDstBand->GetYSize();
6685 3314 : const char *pszNBITS = poJob->poDstBand->GetMetadataItem(
6686 3314 : GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE);
6687 3314 : poJob->args.nOvrNBITS = pszNBITS ? atoi(pszNBITS) : 0;
6688 3314 : poJob->args.dfXRatioDstToSrc = dfXRatioDstToSrc;
6689 3314 : poJob->args.dfYRatioDstToSrc = dfYRatioDstToSrc;
6690 3314 : poJob->args.eWrkDataType = eWrkDataType;
6691 3314 : poJob->pChunk = apaChunk[iBand].get();
6692 3314 : poJob->args.pabyChunkNodataMask =
6693 3314 : apabyChunkNoDataMask[iBand].get();
6694 3314 : poJob->args.nChunkXOff = nChunkXOffQueried;
6695 3314 : poJob->args.nChunkXSize = nChunkXSizeQueried;
6696 3314 : poJob->args.nChunkYOff = nChunkYOffQueried;
6697 3314 : poJob->args.nChunkYSize = nChunkYSizeQueried;
6698 3314 : poJob->args.nDstXOff = nDstXOff;
6699 3314 : poJob->args.nDstXOff2 = nDstXOff + nDstXCount;
6700 3314 : poJob->args.nDstYOff = nDstYOff;
6701 3314 : poJob->args.nDstYOff2 = nDstYOff + nDstYCount;
6702 3314 : poJob->args.pszResampling = pszResampling;
6703 3314 : poJob->args.bHasNoData = abHasNoData[iBand];
6704 3314 : poJob->args.dfNoDataValue = adfNoDataValue[iBand];
6705 3314 : poJob->args.eSrcDataType = eDataType;
6706 3314 : poJob->args.bPropagateNoData = bPropagateNoData;
6707 :
6708 3314 : if (poJobQueue)
6709 : {
6710 19 : poJob->oSrcMaskBufferHolder =
6711 38 : std::make_unique<PointerHolder>(
6712 38 : std::move(apabyChunkNoDataMask[iBand]));
6713 :
6714 19 : poJob->oSrcBufferHolder =
6715 38 : std::make_unique<PointerHolder>(
6716 38 : std::move(apaChunk[iBand]));
6717 :
6718 19 : poJobQueue->SubmitJob(JobResampleFunc, poJob.get());
6719 19 : jobList.emplace_back(std::move(poJob));
6720 : }
6721 : else
6722 : {
6723 3295 : JobResampleFunc(poJob.get());
6724 3295 : eErr = poJob->eErr;
6725 3295 : if (eErr == CE_None)
6726 : {
6727 3295 : eErr = WriteJobData(poJob.get());
6728 : }
6729 : }
6730 : }
6731 : }
6732 : }
6733 :
6734 : // Wait for all pending jobs to complete
6735 618 : while (!jobList.empty())
6736 : {
6737 19 : const auto l_eErr = WaitAndFinalizeOldestJob(jobList);
6738 19 : if (l_eErr != CE_None && eErr == CE_None)
6739 0 : eErr = l_eErr;
6740 : }
6741 :
6742 : // Flush the data to overviews.
6743 1770 : for (int iBand = 0; iBand < nBands; ++iBand)
6744 : {
6745 1171 : if (papapoOverviewBands[iBand][iOverview]->FlushCache(false) !=
6746 : CE_None)
6747 0 : eErr = CE_Failure;
6748 : }
6749 : }
6750 :
6751 384 : if (eErr == CE_None)
6752 381 : pfnProgress(1.0, nullptr, pProgressData);
6753 :
6754 384 : return eErr;
6755 : }
6756 :
6757 : /************************************************************************/
6758 : /* GDALRegenerateOverviewsMultiBand() */
6759 : /************************************************************************/
6760 :
6761 : /**
6762 : * \brief Variant of GDALRegenerateOverviews, specially dedicated for generating
6763 : * compressed pixel-interleaved overviews (JPEG-IN-TIFF for example)
6764 : *
6765 : * This function will generate one or more overview images from a base
6766 : * image using the requested downsampling algorithm. Its primary use
6767 : * is for generating overviews via GDALDataset::BuildOverviews(), but it
6768 : * can also be used to generate downsampled images in one file from another
6769 : * outside the overview architecture.
6770 : *
6771 : * The output bands need to exist in advance and share the same characteristics
6772 : * (type, dimensions)
6773 : *
6774 : * The resampling algorithms supported for the moment are "NEAREST", "AVERAGE",
6775 : * "RMS", "GAUSS", "CUBIC", "CUBICSPLINE", "LANCZOS" and "BILINEAR"
6776 : *
6777 : * It does not support color tables or complex data types.
6778 : *
6779 : * The pseudo-algorithm used by the function is :
6780 : * for each overview
6781 : * iterate on lines of the source by a step of deltay
6782 : * iterate on columns of the source by a step of deltax
6783 : * read the source data of size deltax * deltay for all the bands
6784 : * generate the corresponding overview block for all the bands
6785 : *
6786 : * This function will honour properly NODATA_VALUES tuples (special dataset
6787 : * metadata) so that only a given RGB triplet (in case of a RGB image) will be
6788 : * considered as the nodata value and not each value of the triplet
6789 : * independently per band.
6790 : *
6791 : * The GDAL_NUM_THREADS configuration option can be set
6792 : * to "ALL_CPUS" or a integer value to specify the number of threads to use for
6793 : * overview computation.
6794 : *
6795 : * @param apoSrcBands the list of source bands to downsample
6796 : * @param aapoOverviewBands bidimension array of bands. First dimension is
6797 : * indexed by bands. Second dimension is indexed by
6798 : * overview levels. All aapoOverviewBands[i] arrays
6799 : * must have the same size (i.e. same number of
6800 : * overviews)
6801 : * @param pszResampling Resampling algorithm ("NEAREST", "AVERAGE", "RMS",
6802 : * "GAUSS", "CUBIC", "CUBICSPLINE", "LANCZOS" or "BILINEAR").
6803 : * @param pfnProgress progress report function.
6804 : * @param pProgressData progress function callback data.
6805 : * @param papszOptions NULL terminated list of options as
6806 : * key=value pairs, or NULL
6807 : * The XOFF, YOFF, XSIZE and YSIZE
6808 : * options can be specified to express that overviews should
6809 : * be regenerated only in the specified subset of the source
6810 : * dataset.
6811 : * @return CE_None on success or CE_Failure on failure.
6812 : * @since 3.10
6813 : */
6814 :
6815 19 : CPLErr GDALRegenerateOverviewsMultiBand(
6816 : const std::vector<GDALRasterBand *> &apoSrcBands,
6817 : const std::vector<std::vector<GDALRasterBand *>> &aapoOverviewBands,
6818 : const char *pszResampling, GDALProgressFunc pfnProgress,
6819 : void *pProgressData, CSLConstList papszOptions)
6820 : {
6821 19 : CPLAssert(apoSrcBands.size() == aapoOverviewBands.size());
6822 29 : for (size_t i = 1; i < aapoOverviewBands.size(); ++i)
6823 : {
6824 10 : CPLAssert(aapoOverviewBands[i].size() == aapoOverviewBands[0].size());
6825 : }
6826 :
6827 19 : if (aapoOverviewBands.empty())
6828 0 : return CE_None;
6829 :
6830 19 : std::vector<GDALRasterBand **> apapoOverviewBands;
6831 48 : for (auto &apoOverviewBands : aapoOverviewBands)
6832 : {
6833 : auto papoOverviewBands = static_cast<GDALRasterBand **>(
6834 29 : CPLMalloc(apoOverviewBands.size() * sizeof(GDALRasterBand *)));
6835 61 : for (size_t i = 0; i < apoOverviewBands.size(); ++i)
6836 : {
6837 32 : papoOverviewBands[i] = apoOverviewBands[i];
6838 : }
6839 29 : apapoOverviewBands.push_back(papoOverviewBands);
6840 : }
6841 38 : const CPLErr eErr = GDALRegenerateOverviewsMultiBand(
6842 19 : static_cast<int>(apoSrcBands.size()), apoSrcBands.data(),
6843 19 : static_cast<int>(aapoOverviewBands[0].size()),
6844 19 : apapoOverviewBands.data(), pszResampling, pfnProgress, pProgressData,
6845 : papszOptions);
6846 48 : for (GDALRasterBand **papoOverviewBands : apapoOverviewBands)
6847 29 : CPLFree(papoOverviewBands);
6848 19 : return eErr;
6849 : }
6850 :
6851 : /************************************************************************/
6852 : /* GDALComputeBandStats() */
6853 : /************************************************************************/
6854 :
6855 : /** Undocumented
6856 : * @param hSrcBand undocumented.
6857 : * @param nSampleStep Step between scanlines used to compute statistics.
6858 : * When nSampleStep is equal to 1, all scanlines will
6859 : * be processed.
6860 : * @param pdfMean undocumented.
6861 : * @param pdfStdDev undocumented.
6862 : * @param pfnProgress undocumented.
6863 : * @param pProgressData undocumented.
6864 : * @return undocumented
6865 : */
6866 18 : CPLErr CPL_STDCALL GDALComputeBandStats(GDALRasterBandH hSrcBand,
6867 : int nSampleStep, double *pdfMean,
6868 : double *pdfStdDev,
6869 : GDALProgressFunc pfnProgress,
6870 : void *pProgressData)
6871 :
6872 : {
6873 18 : VALIDATE_POINTER1(hSrcBand, "GDALComputeBandStats", CE_Failure);
6874 :
6875 18 : GDALRasterBand *poSrcBand = GDALRasterBand::FromHandle(hSrcBand);
6876 :
6877 18 : if (pfnProgress == nullptr)
6878 18 : pfnProgress = GDALDummyProgress;
6879 :
6880 18 : const int nWidth = poSrcBand->GetXSize();
6881 18 : const int nHeight = poSrcBand->GetYSize();
6882 :
6883 18 : if (nSampleStep >= nHeight || nSampleStep < 1)
6884 5 : nSampleStep = 1;
6885 :
6886 18 : GDALDataType eWrkType = GDT_Unknown;
6887 18 : float *pafData = nullptr;
6888 18 : GDALDataType eType = poSrcBand->GetRasterDataType();
6889 18 : const bool bComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eType));
6890 18 : if (bComplex)
6891 : {
6892 : pafData = static_cast<float *>(
6893 0 : VSI_MALLOC2_VERBOSE(nWidth, 2 * sizeof(float)));
6894 0 : eWrkType = GDT_CFloat32;
6895 : }
6896 : else
6897 : {
6898 : pafData =
6899 18 : static_cast<float *>(VSI_MALLOC2_VERBOSE(nWidth, sizeof(float)));
6900 18 : eWrkType = GDT_Float32;
6901 : }
6902 :
6903 18 : if (nWidth == 0 || pafData == nullptr)
6904 : {
6905 0 : VSIFree(pafData);
6906 0 : return CE_Failure;
6907 : }
6908 :
6909 : /* -------------------------------------------------------------------- */
6910 : /* Loop over all sample lines. */
6911 : /* -------------------------------------------------------------------- */
6912 18 : double dfSum = 0.0;
6913 18 : double dfSum2 = 0.0;
6914 18 : int iLine = 0;
6915 18 : GIntBig nSamples = 0;
6916 :
6917 2143 : do
6918 : {
6919 2161 : if (!pfnProgress(iLine / static_cast<double>(nHeight), nullptr,
6920 : pProgressData))
6921 : {
6922 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
6923 0 : CPLFree(pafData);
6924 0 : return CE_Failure;
6925 : }
6926 :
6927 : const CPLErr eErr =
6928 2161 : poSrcBand->RasterIO(GF_Read, 0, iLine, nWidth, 1, pafData, nWidth,
6929 : 1, eWrkType, 0, 0, nullptr);
6930 2161 : if (eErr != CE_None)
6931 : {
6932 1 : CPLFree(pafData);
6933 1 : return eErr;
6934 : }
6935 :
6936 725208 : for (int iPixel = 0; iPixel < nWidth; ++iPixel)
6937 : {
6938 723048 : float fValue = 0.0f;
6939 :
6940 723048 : if (bComplex)
6941 : {
6942 : // Compute the magnitude of the complex value.
6943 : fValue =
6944 0 : std::hypot(pafData[static_cast<size_t>(iPixel) * 2],
6945 0 : pafData[static_cast<size_t>(iPixel) * 2 + 1]);
6946 : }
6947 : else
6948 : {
6949 723048 : fValue = pafData[iPixel];
6950 : }
6951 :
6952 723048 : dfSum += static_cast<double>(fValue);
6953 723048 : dfSum2 += static_cast<double>(fValue) * static_cast<double>(fValue);
6954 : }
6955 :
6956 2160 : nSamples += nWidth;
6957 2160 : iLine += nSampleStep;
6958 2160 : } while (iLine < nHeight);
6959 :
6960 17 : if (!pfnProgress(1.0, nullptr, pProgressData))
6961 : {
6962 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
6963 0 : CPLFree(pafData);
6964 0 : return CE_Failure;
6965 : }
6966 :
6967 : /* -------------------------------------------------------------------- */
6968 : /* Produce the result values. */
6969 : /* -------------------------------------------------------------------- */
6970 17 : if (pdfMean != nullptr)
6971 17 : *pdfMean = dfSum / nSamples;
6972 :
6973 17 : if (pdfStdDev != nullptr)
6974 : {
6975 17 : const double dfMean = dfSum / nSamples;
6976 :
6977 17 : *pdfStdDev = sqrt((dfSum2 / nSamples) - (dfMean * dfMean));
6978 : }
6979 :
6980 17 : CPLFree(pafData);
6981 :
6982 17 : return CE_None;
6983 : }
6984 :
6985 : /************************************************************************/
6986 : /* GDALOverviewMagnitudeCorrection() */
6987 : /* */
6988 : /* Correct the mean and standard deviation of the overviews of */
6989 : /* the given band to match the base layer approximately. */
6990 : /************************************************************************/
6991 :
6992 : /** Undocumented
6993 : * @param hBaseBand undocumented.
6994 : * @param nOverviewCount undocumented.
6995 : * @param pahOverviews undocumented.
6996 : * @param pfnProgress undocumented.
6997 : * @param pProgressData undocumented.
6998 : * @return undocumented
6999 : */
7000 0 : CPLErr GDALOverviewMagnitudeCorrection(GDALRasterBandH hBaseBand,
7001 : int nOverviewCount,
7002 : GDALRasterBandH *pahOverviews,
7003 : GDALProgressFunc pfnProgress,
7004 : void *pProgressData)
7005 :
7006 : {
7007 0 : VALIDATE_POINTER1(hBaseBand, "GDALOverviewMagnitudeCorrection", CE_Failure);
7008 :
7009 : /* -------------------------------------------------------------------- */
7010 : /* Compute mean/stddev for source raster. */
7011 : /* -------------------------------------------------------------------- */
7012 0 : double dfOrigMean = 0.0;
7013 0 : double dfOrigStdDev = 0.0;
7014 : {
7015 : const CPLErr eErr =
7016 0 : GDALComputeBandStats(hBaseBand, 2, &dfOrigMean, &dfOrigStdDev,
7017 : pfnProgress, pProgressData);
7018 :
7019 0 : if (eErr != CE_None)
7020 0 : return eErr;
7021 : }
7022 :
7023 : /* -------------------------------------------------------------------- */
7024 : /* Loop on overview bands. */
7025 : /* -------------------------------------------------------------------- */
7026 0 : for (int iOverview = 0; iOverview < nOverviewCount; ++iOverview)
7027 : {
7028 : GDALRasterBand *poOverview =
7029 0 : GDALRasterBand::FromHandle(pahOverviews[iOverview]);
7030 : double dfOverviewMean, dfOverviewStdDev;
7031 :
7032 : const CPLErr eErr =
7033 0 : GDALComputeBandStats(pahOverviews[iOverview], 1, &dfOverviewMean,
7034 : &dfOverviewStdDev, pfnProgress, pProgressData);
7035 :
7036 0 : if (eErr != CE_None)
7037 0 : return eErr;
7038 :
7039 0 : double dfGain = 1.0;
7040 0 : if (dfOrigStdDev >= 0.0001)
7041 0 : dfGain = dfOrigStdDev / dfOverviewStdDev;
7042 :
7043 : /* --------------------------------------------------------------------
7044 : */
7045 : /* Apply gain and offset. */
7046 : /* --------------------------------------------------------------------
7047 : */
7048 0 : const int nWidth = poOverview->GetXSize();
7049 0 : const int nHeight = poOverview->GetYSize();
7050 :
7051 0 : GDALDataType eWrkType = GDT_Unknown;
7052 0 : float *pafData = nullptr;
7053 0 : const GDALDataType eType = poOverview->GetRasterDataType();
7054 0 : const bool bComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eType));
7055 0 : if (bComplex)
7056 : {
7057 : pafData = static_cast<float *>(
7058 0 : VSI_MALLOC2_VERBOSE(nWidth, 2 * sizeof(float)));
7059 0 : eWrkType = GDT_CFloat32;
7060 : }
7061 : else
7062 : {
7063 : pafData = static_cast<float *>(
7064 0 : VSI_MALLOC2_VERBOSE(nWidth, sizeof(float)));
7065 0 : eWrkType = GDT_Float32;
7066 : }
7067 :
7068 0 : if (pafData == nullptr)
7069 : {
7070 0 : return CE_Failure;
7071 : }
7072 :
7073 0 : for (int iLine = 0; iLine < nHeight; ++iLine)
7074 : {
7075 0 : if (!pfnProgress(iLine / static_cast<double>(nHeight), nullptr,
7076 : pProgressData))
7077 : {
7078 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
7079 0 : CPLFree(pafData);
7080 0 : return CE_Failure;
7081 : }
7082 :
7083 0 : if (poOverview->RasterIO(GF_Read, 0, iLine, nWidth, 1, pafData,
7084 : nWidth, 1, eWrkType, 0, 0,
7085 0 : nullptr) != CE_None)
7086 : {
7087 0 : CPLFree(pafData);
7088 0 : return CE_Failure;
7089 : }
7090 :
7091 0 : for (int iPixel = 0; iPixel < nWidth; ++iPixel)
7092 : {
7093 0 : if (bComplex)
7094 : {
7095 0 : pafData[static_cast<size_t>(iPixel) * 2] *=
7096 0 : static_cast<float>(dfGain);
7097 0 : pafData[static_cast<size_t>(iPixel) * 2 + 1] *=
7098 0 : static_cast<float>(dfGain);
7099 : }
7100 : else
7101 : {
7102 0 : pafData[iPixel] = static_cast<float>(
7103 0 : (double(pafData[iPixel]) - dfOverviewMean) * dfGain +
7104 : dfOrigMean);
7105 : }
7106 : }
7107 :
7108 0 : if (poOverview->RasterIO(GF_Write, 0, iLine, nWidth, 1, pafData,
7109 : nWidth, 1, eWrkType, 0, 0,
7110 0 : nullptr) != CE_None)
7111 : {
7112 0 : CPLFree(pafData);
7113 0 : return CE_Failure;
7114 : }
7115 : }
7116 :
7117 0 : if (!pfnProgress(1.0, nullptr, pProgressData))
7118 : {
7119 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
7120 0 : CPLFree(pafData);
7121 0 : return CE_Failure;
7122 : }
7123 :
7124 0 : CPLFree(pafData);
7125 : }
7126 :
7127 0 : return CE_None;
7128 : }
|