Line data Source code
1 : /******************************************************************************
2 : * Project: GDAL Core
3 : * Purpose: Utility functions to find minimum and maximum values in a buffer
4 : * Author: Even Rouault, <even dot rouault at spatialys.com>
5 : *
6 : ******************************************************************************
7 : * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
8 : *
9 : * SPDX-License-Identifier: MIT
10 : ****************************************************************************/
11 :
12 : #ifndef GDAL_MINMAX_ELEMENT_INCLUDED
13 : #define GDAL_MINMAX_ELEMENT_INCLUDED
14 :
15 : // NOTE: This header requires C++17
16 :
17 : // This file may be vendored by other applications than GDAL
18 : // WARNING: if modifying this file, please also update the upstream GDAL version
19 : // at https://github.com/OSGeo/gdal/blob/master/gcore/gdal_minmax_element.hpp
20 :
21 : #include <algorithm>
22 : #include <cmath>
23 : #include <cstdint>
24 : #include <limits>
25 : #include <type_traits>
26 : #include <utility>
27 :
28 : #include "gdal.h"
29 :
30 : #ifdef GDAL_COMPILATION
31 : #define GDAL_MINMAXELT_NS gdal
32 : #elif !defined(GDAL_MINMAXELT_NS)
33 : #error "Please define the GDAL_MINMAXELT_NS macro to define the namespace"
34 : #endif
35 :
36 : #ifdef USE_NEON_OPTIMIZATIONS
37 : #include "include_sse2neon.h"
38 : #define GDAL_MINMAX_ELEMENT_USE_SSE2
39 : #else
40 : #if defined(__x86_64) || defined(_M_X64)
41 : #define GDAL_MINMAX_ELEMENT_USE_SSE2
42 : #endif
43 : #ifdef GDAL_MINMAX_ELEMENT_USE_SSE2
44 : // SSE2 header
45 : #include <emmintrin.h>
46 : #ifdef __SSE4_1__
47 : #include <smmintrin.h>
48 : #endif
49 : #endif
50 : #endif
51 :
52 : #include "gdal_priv_templates.hpp"
53 :
54 : namespace GDAL_MINMAXELT_NS
55 : {
56 : namespace detail
57 : {
58 :
59 : #ifdef GDAL_MINMAX_ELEMENT_USE_SSE2
60 : /************************************************************************/
61 : /* compScalar() */
62 : /************************************************************************/
63 :
64 949167 : template <class T, bool IS_MAX> inline static bool compScalar(T x, T y)
65 : {
66 : if constexpr (IS_MAX)
67 2982 : return x > y;
68 : else
69 946185 : return x < y;
70 : }
71 :
72 : /************************************************************************/
73 : /* extremum_element() */
74 : /************************************************************************/
75 :
76 : template <class T, bool IS_MAX>
77 16 : size_t extremum_element(const T *v, size_t size, T noDataValue)
78 : {
79 : static_assert(!(std::is_floating_point_v<T>));
80 16 : if (size == 0)
81 2 : return 0;
82 14 : size_t idx_of_extremum = 0;
83 14 : T extremum = v[0];
84 14 : bool extremum_is_nodata = extremum == noDataValue;
85 14 : size_t i = 1;
86 550 : for (; i < size; ++i)
87 : {
88 1578 : if (v[i] != noDataValue &&
89 1042 : (compScalar<T, IS_MAX>(v[i], extremum) || extremum_is_nodata))
90 : {
91 12 : extremum = v[i];
92 12 : idx_of_extremum = i;
93 12 : extremum_is_nodata = false;
94 : }
95 : }
96 14 : return idx_of_extremum;
97 : }
98 :
99 : /************************************************************************/
100 : /* extremum_element() */
101 : /************************************************************************/
102 :
103 10 : template <class T, bool IS_MAX> size_t extremum_element(const T *v, size_t size)
104 : {
105 : static_assert(!(std::is_floating_point_v<T>));
106 10 : if (size == 0)
107 2 : return 0;
108 8 : size_t idx_of_extremum = 0;
109 8 : T extremum = v[0];
110 8 : size_t i = 1;
111 1048 : for (; i < size; ++i)
112 : {
113 1040 : if (compScalar<T, IS_MAX>(v[i], extremum))
114 : {
115 10 : extremum = v[i];
116 10 : idx_of_extremum = i;
117 : }
118 : }
119 8 : return idx_of_extremum;
120 : }
121 :
122 : #ifdef GDAL_MINMAX_ELEMENT_USE_SSE2
123 :
124 : /************************************************************************/
125 : /* extremum_element_with_nan() */
126 : /************************************************************************/
127 :
128 77 : static inline int8_t Shift8(uint8_t x)
129 : {
130 77 : return static_cast<int8_t>(x + std::numeric_limits<int8_t>::min());
131 : }
132 :
133 32 : static inline int16_t Shift16(uint16_t x)
134 : {
135 32 : return static_cast<int16_t>(x + std::numeric_limits<int16_t>::min());
136 : }
137 :
138 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
139 38 : static inline int32_t Shift32(uint32_t x)
140 : {
141 38 : x += static_cast<uint32_t>(std::numeric_limits<int32_t>::min());
142 : int32_t ret;
143 38 : memcpy(&ret, &x, sizeof(x));
144 38 : return ret;
145 : }
146 :
147 : // Return a _mm128[i|d] register with all its elements set to x
148 78601 : template <class T> static inline auto set1(T x)
149 : {
150 : if constexpr (std::is_same_v<T, uint8_t>)
151 154 : return _mm_set1_epi8(Shift8(x));
152 : else if constexpr (std::is_same_v<T, int8_t>)
153 34 : return _mm_set1_epi8(x);
154 : else if constexpr (std::is_same_v<T, uint16_t>)
155 64 : return _mm_set1_epi16(Shift16(x));
156 : else if constexpr (std::is_same_v<T, int16_t>)
157 70 : return _mm_set1_epi16(x);
158 : else if constexpr (std::is_same_v<T, uint32_t>)
159 76 : return _mm_set1_epi32(Shift32(x));
160 : else if constexpr (std::is_same_v<T, int32_t>)
161 40 : return _mm_set1_epi32(x);
162 : else if constexpr (std::is_same_v<T, float>)
163 39191 : return _mm_set1_ps(x);
164 : else
165 39171 : return _mm_set1_pd(x);
166 : }
167 :
168 : // Return a _mm128[i|d] register with all its elements set to x
169 39552 : template <class T> static inline auto set1_unshifted(T x)
170 : {
171 : if constexpr (std::is_same_v<T, uint8_t>)
172 : {
173 : int8_t xSigned;
174 102 : memcpy(&xSigned, &x, sizeof(xSigned));
175 204 : return _mm_set1_epi8(xSigned);
176 : }
177 : else if constexpr (std::is_same_v<T, int8_t>)
178 72 : return _mm_set1_epi8(x);
179 : else if constexpr (std::is_same_v<T, uint16_t>)
180 : {
181 : int16_t xSigned;
182 43 : memcpy(&xSigned, &x, sizeof(xSigned));
183 86 : return _mm_set1_epi16(xSigned);
184 : }
185 : else if constexpr (std::is_same_v<T, int16_t>)
186 90 : return _mm_set1_epi16(x);
187 : else if constexpr (std::is_same_v<T, uint32_t>)
188 : {
189 : int32_t xSigned;
190 46 : memcpy(&xSigned, &x, sizeof(xSigned));
191 92 : return _mm_set1_epi32(xSigned);
192 : }
193 : else if constexpr (std::is_same_v<T, int32_t>)
194 47 : return _mm_set1_epi32(x);
195 : else if constexpr (std::is_same_v<T, float>)
196 19617 : return _mm_set1_ps(x);
197 : else
198 19616 : return _mm_set1_pd(x);
199 : }
200 :
201 : // Load as many values of type T at a _mm128[i|d] register can contain from x
202 47501384 : template <class T> static inline auto loadv(const T *x)
203 : {
204 : if constexpr (std::is_same_v<T, float>)
205 10000280 : return _mm_loadu_ps(x);
206 : else if constexpr (std::is_same_v<T, double>)
207 20000260 : return _mm_loadu_pd(x);
208 : else
209 17500844 : return _mm_loadu_si128(reinterpret_cast<const __m128i *>(x));
210 : }
211 :
212 : // Return a __m128i register with bits set when x[i] < y[i] when !IS_MAX
213 : // or x[i] > y[i] when IS_MAX
214 : template <class T, bool IS_MAX, class SSE_T>
215 47501384 : static inline __m128i comp(SSE_T x, SSE_T y)
216 : {
217 : if constexpr (IS_MAX)
218 : {
219 : if constexpr (std::is_same_v<T, uint8_t>)
220 492 : return _mm_cmpgt_epi8(
221 : _mm_add_epi8(x,
222 164 : _mm_set1_epi8(std::numeric_limits<int8_t>::min())),
223 164 : y);
224 : else if constexpr (std::is_same_v<T, int8_t>)
225 12 : return _mm_cmpgt_epi8(x, y);
226 : else if constexpr (std::is_same_v<T, uint16_t>)
227 84 : return _mm_cmpgt_epi16(
228 : _mm_add_epi16(
229 28 : x, _mm_set1_epi16(std::numeric_limits<int16_t>::min())),
230 28 : y);
231 : else if constexpr (std::is_same_v<T, int16_t>)
232 28 : return _mm_cmpgt_epi16(x, y);
233 : else if constexpr (std::is_same_v<T, uint32_t>)
234 180 : return _mm_cmpgt_epi32(
235 : _mm_add_epi32(
236 : x, _mm_set1_epi32(std::numeric_limits<int32_t>::min())),
237 60 : y);
238 : else if constexpr (std::is_same_v<T, int32_t>)
239 60 : return _mm_cmpgt_epi32(x, y);
240 : // We could use _mm_cmpgt_pX() if there was no NaN values
241 : else if constexpr (std::is_same_v<T, float>)
242 240 : return _mm_castps_si128(_mm_cmpnle_ps(x, y));
243 : else
244 136 : return _mm_castpd_si128(_mm_cmpnle_pd(x, y));
245 : }
246 : else
247 : {
248 : if constexpr (std::is_same_v<T, uint8_t>)
249 3750508 : return _mm_cmplt_epi8(
250 : _mm_add_epi8(x,
251 1250166 : _mm_set1_epi8(std::numeric_limits<int8_t>::min())),
252 1250166 : y);
253 : else if constexpr (std::is_same_v<T, int8_t>)
254 1250014 : return _mm_cmplt_epi8(x, y);
255 : else if constexpr (std::is_same_v<T, uint16_t>)
256 7500148 : return _mm_cmplt_epi16(
257 : _mm_add_epi16(
258 2500046 : x, _mm_set1_epi16(std::numeric_limits<int16_t>::min())),
259 2500046 : y);
260 : else if constexpr (std::is_same_v<T, int16_t>)
261 2500046 : return _mm_cmplt_epi16(x, y);
262 : else if constexpr (std::is_same_v<T, uint32_t>)
263 15000360 : return _mm_cmplt_epi32(
264 : _mm_add_epi32(
265 : x, _mm_set1_epi32(std::numeric_limits<int32_t>::min())),
266 5000110 : y);
267 : else if constexpr (std::is_same_v<T, int32_t>)
268 5000110 : return _mm_cmplt_epi32(x, y);
269 : // We could use _mm_cmplt_pX() if there was no NaN values
270 : else if constexpr (std::is_same_v<T, float>)
271 20000360 : return _mm_castps_si128(_mm_cmpnge_ps(x, y));
272 : else
273 40000384 : return _mm_castpd_si128(_mm_cmpnge_pd(x, y));
274 : }
275 : }
276 :
277 : template <class T, class SSE_T> static inline SSE_T compeq(SSE_T a, SSE_T b);
278 :
279 625024 : template <> __m128i compeq<uint8_t>(__m128i a, __m128i b)
280 : {
281 625024 : return _mm_cmpeq_epi8(a, b);
282 : }
283 :
284 625008 : template <> __m128i compeq<int8_t>(__m128i a, __m128i b)
285 : {
286 625008 : return _mm_cmpeq_epi8(a, b);
287 : }
288 :
289 1250028 : template <> __m128i compeq<uint16_t>(__m128i a, __m128i b)
290 : {
291 1250028 : return _mm_cmpeq_epi16(a, b);
292 : }
293 :
294 1250028 : template <> __m128i compeq<int16_t>(__m128i a, __m128i b)
295 : {
296 1250028 : return _mm_cmpeq_epi16(a, b);
297 : }
298 :
299 2500060 : template <> __m128i compeq<uint32_t>(__m128i a, __m128i b)
300 : {
301 2500060 : return _mm_cmpeq_epi32(a, b);
302 : }
303 :
304 2500060 : template <> __m128i compeq<int32_t>(__m128i a, __m128i b)
305 : {
306 2500060 : return _mm_cmpeq_epi32(a, b);
307 : }
308 :
309 5000050 : template <> __m128 compeq<float>(__m128 a, __m128 b)
310 : {
311 5000050 : return _mm_cmpeq_ps(a, b);
312 : }
313 :
314 10000114 : template <> __m128d compeq<double>(__m128d a, __m128d b)
315 : {
316 10000114 : return _mm_cmpeq_pd(a, b);
317 : }
318 :
319 : template <class T> static inline T blendv(T a, T b, T mask);
320 :
321 8750196 : template <> __m128i blendv(__m128i a, __m128i b, __m128i mask)
322 : {
323 : #if defined(__SSE4_1__) || defined(USE_NEON_OPTIMIZATIONS)
324 : return _mm_blendv_epi8(a, b, mask);
325 : #else
326 26250548 : return _mm_or_si128(_mm_andnot_si128(mask, a), _mm_and_si128(mask, b));
327 : #endif
328 : }
329 :
330 5000050 : template <> __m128 blendv(__m128 a, __m128 b, __m128 mask)
331 : {
332 : #if defined(__SSE4_1__) || defined(USE_NEON_OPTIMIZATIONS)
333 : return _mm_blendv_ps(a, b, mask);
334 : #else
335 15000180 : return _mm_or_ps(_mm_andnot_ps(mask, a), _mm_and_ps(mask, b));
336 : #endif
337 : }
338 :
339 10000114 : template <> __m128d blendv(__m128d a, __m128d b, __m128d mask)
340 : {
341 : #if defined(__SSE4_1__) || defined(USE_NEON_OPTIMIZATIONS)
342 : return _mm_blendv_pd(a, b, mask);
343 : #else
344 30000372 : return _mm_or_pd(_mm_andnot_pd(mask, a), _mm_and_pd(mask, b));
345 : #endif
346 : }
347 :
348 : // Using SSE2
349 : template <class T, bool IS_MAX, bool HAS_NODATA>
350 170 : inline size_t extremum_element_with_nan(const T *v, size_t size, T noDataValue)
351 : {
352 : static_assert(std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t> ||
353 : std::is_same_v<T, uint16_t> || std::is_same_v<T, int16_t> ||
354 : std::is_same_v<T, uint32_t> || std::is_same_v<T, int32_t> ||
355 : std::is_floating_point_v<T>);
356 170 : if (size == 0)
357 16 : return 0;
358 154 : size_t idx_of_extremum = 0;
359 154 : T extremum = v[0];
360 154 : [[maybe_unused]] bool extremum_is_invalid = false;
361 : if constexpr (std::is_floating_point_v<T>)
362 : {
363 48 : extremum_is_invalid = std::isnan(extremum);
364 : }
365 : if constexpr (HAS_NODATA)
366 : {
367 75 : if (extremum == noDataValue)
368 19 : extremum_is_invalid = true;
369 : }
370 154 : size_t i = 1;
371 :
372 154 : constexpr size_t VALS_PER_REG = sizeof(set1(extremum)) / sizeof(extremum);
373 154 : constexpr int LOOP_UNROLLING = 4;
374 : // If changing the value, then we need to adjust the number of sse_valX
375 : // loading in the loop.
376 : static_assert(LOOP_UNROLLING == 4);
377 154 : constexpr size_t VALS_PER_ITER = VALS_PER_REG * LOOP_UNROLLING;
378 :
379 3785759 : const auto update = [v, noDataValue, &extremum, &idx_of_extremum,
380 : &extremum_is_invalid](size_t idx)
381 : {
382 : if constexpr (HAS_NODATA)
383 : {
384 472296 : if (v[idx] == noDataValue)
385 671 : return;
386 471625 : if (extremum_is_invalid)
387 : {
388 : if constexpr (std::is_floating_point_v<T>)
389 : {
390 20 : if (std::isnan(v[idx]))
391 8 : return;
392 : }
393 26 : extremum = v[idx];
394 26 : idx_of_extremum = idx;
395 26 : extremum_is_invalid = false;
396 26 : return;
397 : }
398 : }
399 : else
400 : {
401 476010 : CPL_IGNORE_RET_VAL(noDataValue);
402 : }
403 947601 : if (compScalar<T, IS_MAX>(v[idx], extremum))
404 : {
405 416 : extremum = v[idx];
406 416 : idx_of_extremum = idx;
407 416 : extremum_is_invalid = false;
408 : }
409 : else if constexpr (std::is_floating_point_v<T>)
410 : {
411 940305 : if (extremum_is_invalid && !std::isnan(v[idx]))
412 : {
413 14 : extremum = v[idx];
414 14 : idx_of_extremum = idx;
415 14 : extremum_is_invalid = false;
416 : }
417 : }
418 : };
419 :
420 3101 : for (; i < VALS_PER_ITER && i < size; ++i)
421 : {
422 2947 : update(i);
423 : }
424 :
425 154 : [[maybe_unused]] auto sse_neutral = set1_unshifted(static_cast<T>(0));
426 154 : [[maybe_unused]] auto sse_nodata = set1_unshifted(noDataValue);
427 : if constexpr (HAS_NODATA)
428 : {
429 334 : for (; i < size && extremum_is_invalid; ++i)
430 : {
431 259 : update(i);
432 : }
433 75 : if (!extremum_is_invalid)
434 : {
435 136 : for (; i < size && (i % VALS_PER_ITER) != 0; ++i)
436 : {
437 62 : update(i);
438 : }
439 74 : sse_neutral = set1_unshifted(extremum);
440 : }
441 : }
442 :
443 154 : auto sse_extremum = set1(extremum);
444 :
445 154 : [[maybe_unused]] size_t hits = 0;
446 154 : const auto sse_iter_count = (size / VALS_PER_ITER) * VALS_PER_ITER;
447 11875496 : for (; i < sse_iter_count; i += VALS_PER_ITER)
448 : {
449 : // A bit of loop unrolling to save 3/4 of slow movemask operations.
450 11875354 : auto sse_val0 = loadv(v + i + 0 * VALS_PER_REG);
451 11875354 : auto sse_val1 = loadv(v + i + 1 * VALS_PER_REG);
452 11875354 : auto sse_val2 = loadv(v + i + 2 * VALS_PER_REG);
453 11875354 : auto sse_val3 = loadv(v + i + 3 * VALS_PER_REG);
454 :
455 : if constexpr (HAS_NODATA)
456 : {
457 : // Replace all components that are at the nodata value by a
458 : // neutral value (current minimum)
459 5937594 : const auto replaceNoDataByNeutral =
460 47500720 : [sse_neutral, sse_nodata](auto sse_val)
461 : {
462 23750400 : const auto eq_nodata = compeq<T>(sse_val, sse_nodata);
463 23750400 : return blendv(sse_val, sse_neutral, eq_nodata);
464 : };
465 :
466 5937594 : sse_val0 = replaceNoDataByNeutral(sse_val0);
467 5937594 : sse_val1 = replaceNoDataByNeutral(sse_val1);
468 5937594 : sse_val2 = replaceNoDataByNeutral(sse_val2);
469 5937594 : sse_val3 = replaceNoDataByNeutral(sse_val3);
470 : }
471 :
472 47501408 : if (_mm_movemask_epi8(_mm_or_si128(
473 : _mm_or_si128(comp<T, IS_MAX>(sse_val0, sse_extremum),
474 : comp<T, IS_MAX>(sse_val1, sse_extremum)),
475 : _mm_or_si128(comp<T, IS_MAX>(sse_val2, sse_extremum),
476 11875354 : comp<T, IS_MAX>(sse_val3, sse_extremum)))) != 0)
477 : {
478 : if constexpr (!std::is_same_v<T, int8_t> &&
479 : !std::is_same_v<T, uint8_t>)
480 : {
481 : // The above tests excluding int8_t/uint8_t is due to the fact
482 : // with those small ranges of values we will quickly converge
483 : // to the minimum, so no need to do the below "smart" test.
484 :
485 78411 : if (++hits == size / 16)
486 : {
487 : // If we have an almost sorted array, then using this code path
488 : // will hurt performance. Arbitrary give up if we get here
489 : // more than 1. / 16 of the size of the array.
490 : // fprintf(stderr, "going to non-vector path\n");
491 4 : break;
492 : }
493 : }
494 1022991 : for (size_t j = 0; j < VALS_PER_ITER; j++)
495 : {
496 944544 : update(i + j);
497 : }
498 78447 : sse_extremum = set1(extremum);
499 : if constexpr (HAS_NODATA)
500 : {
501 39170 : sse_neutral = set1_unshifted(extremum);
502 : }
503 : }
504 : }
505 648 : for (; i < size; ++i)
506 : {
507 494 : update(i);
508 : }
509 154 : return idx_of_extremum;
510 : }
511 :
512 : #else
513 :
514 : /************************************************************************/
515 : /* extremum_element_with_nan() */
516 : /************************************************************************/
517 :
518 : template <class T, bool IS_MAX, bool HAS_NODATA>
519 : inline size_t extremum_element_with_nan(const T *v, size_t size, T /* nodata */)
520 : {
521 : static_assert(!HAS_NODATA);
522 : if (size == 0)
523 : return 0;
524 : size_t idx_of_extremum = 0;
525 : auto extremum = v[0];
526 : bool extremum_is_nan = std::isnan(extremum);
527 : size_t i = 1;
528 : for (; i < size; ++i)
529 : {
530 : if (compScalar<T, IS_MAX>(v[i], extremum) ||
531 : (extremum_is_nan && !std::isnan(v[i])))
532 : {
533 : extremum = v[i];
534 : idx_of_extremum = i;
535 : extremum_is_nan = false;
536 : }
537 : }
538 : return idx_of_extremum;
539 : }
540 : #endif
541 :
542 : /************************************************************************/
543 : /* extremum_element() */
544 : /************************************************************************/
545 :
546 : #ifdef GDAL_MINMAX_ELEMENT_USE_SSE2
547 :
548 : template <>
549 4 : size_t extremum_element<uint8_t, true>(const uint8_t *v, size_t size,
550 : uint8_t noDataValue)
551 : {
552 4 : return extremum_element_with_nan<uint8_t, true, true>(v, size, noDataValue);
553 : }
554 :
555 : template <>
556 8 : size_t extremum_element<uint8_t, false>(const uint8_t *v, size_t size,
557 : uint8_t noDataValue)
558 : {
559 8 : return extremum_element_with_nan<uint8_t, false, true>(v, size,
560 8 : noDataValue);
561 : }
562 :
563 : template <>
564 15 : size_t extremum_element<uint8_t, true>(const uint8_t *v, size_t size)
565 : {
566 15 : return extremum_element_with_nan<uint8_t, true, false>(v, size, 0);
567 : }
568 :
569 : template <>
570 16 : size_t extremum_element<uint8_t, false>(const uint8_t *v, size_t size)
571 : {
572 16 : return extremum_element_with_nan<uint8_t, false, false>(v, size, 0);
573 : }
574 :
575 : template <>
576 3 : size_t extremum_element<int8_t, true>(const int8_t *v, size_t size,
577 : int8_t noDataValue)
578 : {
579 3 : return extremum_element_with_nan<int8_t, true, true>(v, size, noDataValue);
580 : }
581 :
582 : template <>
583 6 : size_t extremum_element<int8_t, false>(const int8_t *v, size_t size,
584 : int8_t noDataValue)
585 : {
586 6 : return extremum_element_with_nan<int8_t, false, true>(v, size, noDataValue);
587 : }
588 :
589 2 : template <> size_t extremum_element<int8_t, true>(const int8_t *v, size_t size)
590 : {
591 2 : return extremum_element_with_nan<int8_t, true, false>(v, size, 0);
592 : }
593 :
594 4 : template <> size_t extremum_element<int8_t, false>(const int8_t *v, size_t size)
595 : {
596 4 : return extremum_element_with_nan<int8_t, false, false>(v, size, 0);
597 : }
598 :
599 : template <>
600 3 : size_t extremum_element<uint16_t, true>(const uint16_t *v, size_t size,
601 : uint16_t noDataValue)
602 : {
603 3 : return extremum_element_with_nan<uint16_t, true, true>(v, size,
604 3 : noDataValue);
605 : }
606 :
607 : template <>
608 6 : size_t extremum_element<uint16_t, false>(const uint16_t *v, size_t size,
609 : uint16_t noDataValue)
610 : {
611 6 : return extremum_element_with_nan<uint16_t, false, true>(v, size,
612 6 : noDataValue);
613 : }
614 :
615 : template <>
616 2 : size_t extremum_element<uint16_t, true>(const uint16_t *v, size_t size)
617 : {
618 2 : return extremum_element_with_nan<uint16_t, true, false>(v, size, 0);
619 : }
620 :
621 : template <>
622 4 : size_t extremum_element<uint16_t, false>(const uint16_t *v, size_t size)
623 : {
624 4 : return extremum_element_with_nan<uint16_t, false, false>(v, size, 0);
625 : }
626 :
627 : template <>
628 3 : size_t extremum_element<int16_t, true>(const int16_t *v, size_t size,
629 : int16_t noDataValue)
630 : {
631 3 : return extremum_element_with_nan<int16_t, true, true>(v, size, noDataValue);
632 : }
633 :
634 : template <>
635 6 : size_t extremum_element<int16_t, false>(const int16_t *v, size_t size,
636 : int16_t noDataValue)
637 : {
638 6 : return extremum_element_with_nan<int16_t, false, true>(v, size,
639 6 : noDataValue);
640 : }
641 :
642 : template <>
643 2 : size_t extremum_element<int16_t, true>(const int16_t *v, size_t size)
644 : {
645 2 : return extremum_element_with_nan<int16_t, true, false>(v, size, 0);
646 : }
647 :
648 : template <>
649 4 : size_t extremum_element<int16_t, false>(const int16_t *v, size_t size)
650 : {
651 4 : return extremum_element_with_nan<int16_t, false, false>(v, size, 0);
652 : }
653 :
654 : template <>
655 3 : size_t extremum_element<uint32_t, true>(const uint32_t *v, size_t size,
656 : uint32_t noDataValue)
657 : {
658 3 : return extremum_element_with_nan<uint32_t, true, true>(v, size,
659 3 : noDataValue);
660 : }
661 :
662 : template <>
663 6 : size_t extremum_element<uint32_t, false>(const uint32_t *v, size_t size,
664 : uint32_t noDataValue)
665 : {
666 6 : return extremum_element_with_nan<uint32_t, false, true>(v, size,
667 6 : noDataValue);
668 : }
669 :
670 : template <>
671 2 : size_t extremum_element<uint32_t, true>(const uint32_t *v, size_t size)
672 : {
673 2 : return extremum_element_with_nan<uint32_t, true, false>(v, size, 0);
674 : }
675 :
676 : template <>
677 4 : size_t extremum_element<uint32_t, false>(const uint32_t *v, size_t size)
678 : {
679 4 : return extremum_element_with_nan<uint32_t, false, false>(v, size, 0);
680 : }
681 :
682 : template <>
683 3 : size_t extremum_element<int32_t, true>(const int32_t *v, size_t size,
684 : int32_t noDataValue)
685 : {
686 3 : return extremum_element_with_nan<int32_t, true, true>(v, size, noDataValue);
687 : }
688 :
689 : template <>
690 6 : size_t extremum_element<int32_t, false>(const int32_t *v, size_t size,
691 : int32_t noDataValue)
692 : {
693 6 : return extremum_element_with_nan<int32_t, false, true>(v, size,
694 6 : noDataValue);
695 : }
696 :
697 : template <>
698 2 : size_t extremum_element<int32_t, true>(const int32_t *v, size_t size)
699 : {
700 2 : return extremum_element_with_nan<int32_t, true, false>(v, size, 0);
701 : }
702 :
703 : template <>
704 4 : size_t extremum_element<int32_t, false>(const int32_t *v, size_t size)
705 : {
706 4 : return extremum_element_with_nan<int32_t, false, false>(v, size, 0);
707 : }
708 :
709 : template <>
710 5 : size_t extremum_element<float, true>(const float *v, size_t size,
711 : float noDataValue)
712 : {
713 5 : return extremum_element_with_nan<float, true, true>(v, size, noDataValue);
714 : }
715 :
716 : template <>
717 9 : size_t extremum_element<float, false>(const float *v, size_t size,
718 : float noDataValue)
719 : {
720 9 : return extremum_element_with_nan<float, false, true>(v, size, noDataValue);
721 : }
722 :
723 : template <>
724 4 : size_t extremum_element<double, true>(const double *v, size_t size,
725 : double noDataValue)
726 : {
727 4 : return extremum_element_with_nan<double, true, true>(v, size, noDataValue);
728 : }
729 :
730 : template <>
731 8 : size_t extremum_element<double, false>(const double *v, size_t size,
732 : double noDataValue)
733 : {
734 8 : return extremum_element_with_nan<double, false, true>(v, size, noDataValue);
735 : }
736 :
737 : #endif
738 :
739 5 : template <> size_t extremum_element<float, true>(const float *v, size_t size)
740 : {
741 5 : return extremum_element_with_nan<float, true, false>(v, size, 0);
742 : }
743 :
744 5 : template <> size_t extremum_element<double, true>(const double *v, size_t size)
745 : {
746 5 : return extremum_element_with_nan<double, true, false>(v, size, 0);
747 : }
748 :
749 8 : template <> size_t extremum_element<float, false>(const float *v, size_t size)
750 : {
751 8 : return extremum_element_with_nan<float, false, false>(v, size, 0);
752 : }
753 :
754 8 : template <> size_t extremum_element<double, false>(const double *v, size_t size)
755 : {
756 8 : return extremum_element_with_nan<double, false, false>(v, size, 0);
757 : }
758 :
759 : /************************************************************************/
760 : /* extremum_element_with_nan() */
761 : /************************************************************************/
762 :
763 : template <class T, bool IS_MAX>
764 : inline size_t extremum_element_with_nan(const T *v, size_t size, T noDataValue)
765 : {
766 : if (std::isnan(noDataValue))
767 : return extremum_element_with_nan<T, IS_MAX, false>(v, size, 0);
768 : if (size == 0)
769 : return 0;
770 : size_t idx_of_extremum = 0;
771 : auto extremum = v[0];
772 : bool extremum_is_nan_or_nodata =
773 : std::isnan(extremum) || (extremum == noDataValue);
774 : size_t i = 1;
775 : for (; i < size; ++i)
776 : {
777 : if (v[i] != noDataValue &&
778 : (compScalar<T, IS_MAX>(v[i], extremum) ||
779 : (extremum_is_nan_or_nodata && !std::isnan(v[i]))))
780 : {
781 : extremum = v[i];
782 : idx_of_extremum = i;
783 : extremum_is_nan_or_nodata = false;
784 : }
785 : }
786 : return idx_of_extremum;
787 : }
788 :
789 : /************************************************************************/
790 : /* extremum_element() */
791 : /************************************************************************/
792 :
793 : #if !defined(GDAL_MINMAX_ELEMENT_USE_SSE2)
794 :
795 : template <>
796 : size_t extremum_element<float, true>(const float *v, size_t size,
797 : float noDataValue)
798 : {
799 : return extremum_element_with_nan<float, true>(v, size, noDataValue);
800 : }
801 :
802 : template <>
803 : size_t extremum_element<float, false>(const float *v, size_t size,
804 : float noDataValue)
805 : {
806 : return extremum_element_with_nan<float, false>(v, size, noDataValue);
807 : }
808 :
809 : template <>
810 : size_t extremum_element<double, true>(const double *v, size_t size,
811 : double noDataValue)
812 : {
813 : return extremum_element_with_nan<double, true>(v, size, noDataValue);
814 : }
815 :
816 : template <>
817 : size_t extremum_element<double, false>(const double *v, size_t size,
818 : double noDataValue)
819 : {
820 : return extremum_element_with_nan<double, false>(v, size, noDataValue);
821 : }
822 :
823 : #endif
824 :
825 : template <class T, bool IS_MAX>
826 196 : inline size_t extremum_element(const T *buffer, size_t size, bool bHasNoData,
827 : T noDataValue)
828 : {
829 196 : if (bHasNoData)
830 99 : return extremum_element<T, IS_MAX>(buffer, size, noDataValue);
831 : else
832 97 : return extremum_element<T, IS_MAX>(buffer, size);
833 : }
834 :
835 : #else
836 :
837 : template <class T, bool IS_MAX>
838 : inline size_t extremum_element(const T *buffer, size_t size, bool bHasNoData,
839 : T noDataValue)
840 : {
841 : if (bHasNoData)
842 : {
843 : if constexpr (std::is_floating_point_v<T>)
844 : {
845 : if (std::isnan(noDataValue))
846 : {
847 : if constexpr (IS_MAX)
848 : {
849 : return std::max_element(buffer, buffer + size,
850 : [](T a, T b) {
851 : return std::isnan(b) ? false
852 : : std::isnan(a) ? true
853 : : a < b;
854 : }) -
855 : buffer;
856 : }
857 : else
858 : {
859 : return std::min_element(buffer, buffer + size,
860 : [](T a, T b) {
861 : return std::isnan(b) ? true
862 : : std::isnan(a) ? false
863 : : a < b;
864 : }) -
865 : buffer;
866 : }
867 : }
868 : else
869 : {
870 : if constexpr (IS_MAX)
871 : {
872 : return std::max_element(buffer, buffer + size,
873 : [noDataValue](T a, T b)
874 : {
875 : return std::isnan(b) ? false
876 : : std::isnan(a) ? true
877 : : (b == noDataValue)
878 : ? false
879 : : (a == noDataValue)
880 : ? true
881 : : a < b;
882 : }) -
883 : buffer;
884 : }
885 : else
886 : {
887 : return std::min_element(buffer, buffer + size,
888 : [noDataValue](T a, T b)
889 : {
890 : return std::isnan(b) ? true
891 : : std::isnan(a) ? false
892 : : (b == noDataValue)
893 : ? true
894 : : (a == noDataValue)
895 : ? false
896 : : a < b;
897 : }) -
898 : buffer;
899 : }
900 : }
901 : }
902 : else
903 : {
904 : if constexpr (IS_MAX)
905 : {
906 : return std::max_element(buffer, buffer + size,
907 : [noDataValue](T a, T b) {
908 : return (b == noDataValue) ? false
909 : : (a == noDataValue) ? true
910 : : a < b;
911 : }) -
912 : buffer;
913 : }
914 : else
915 : {
916 : return std::min_element(buffer, buffer + size,
917 : [noDataValue](T a, T b) {
918 : return (b == noDataValue) ? true
919 : : (a == noDataValue) ? false
920 : : a < b;
921 : }) -
922 : buffer;
923 : }
924 : }
925 : }
926 : else
927 : {
928 : if constexpr (std::is_floating_point_v<T>)
929 : {
930 : if constexpr (IS_MAX)
931 : {
932 : return std::max_element(buffer, buffer + size,
933 : [](T a, T b) {
934 : return std::isnan(b) ? false
935 : : std::isnan(a) ? true
936 : : a < b;
937 : }) -
938 : buffer;
939 : }
940 : else
941 : {
942 : return std::min_element(buffer, buffer + size,
943 : [](T a, T b) {
944 : return std::isnan(b) ? true
945 : : std::isnan(a) ? false
946 : : a < b;
947 : }) -
948 : buffer;
949 : }
950 : }
951 : else
952 : {
953 : if constexpr (IS_MAX)
954 : {
955 : return std::max_element(buffer, buffer + size) - buffer;
956 : }
957 : else
958 : {
959 : return std::min_element(buffer, buffer + size) - buffer;
960 : }
961 : }
962 : }
963 : }
964 : #endif
965 :
966 : template <bool IS_MAX>
967 168 : size_t extremum_element(const void *buffer, size_t nElts, GDALDataType eDT,
968 : bool bHasNoData, double dfNoDataValue)
969 : {
970 168 : switch (eDT)
971 : {
972 : #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 7, 0)
973 13 : case GDT_Int8:
974 : {
975 : using T = int8_t;
976 13 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
977 13 : return extremum_element<T, IS_MAX>(
978 : static_cast<const T *>(buffer), nElts, bHasNoData,
979 13 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
980 : }
981 : #endif
982 33 : case GDT_Byte:
983 : {
984 : using T = uint8_t;
985 33 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
986 33 : return extremum_element<T, IS_MAX>(
987 : static_cast<const T *>(buffer), nElts, bHasNoData,
988 33 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
989 : }
990 13 : case GDT_Int16:
991 : {
992 : using T = int16_t;
993 13 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
994 13 : return extremum_element<T, IS_MAX>(
995 : static_cast<const T *>(buffer), nElts, bHasNoData,
996 13 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
997 : }
998 13 : case GDT_UInt16:
999 : {
1000 : using T = uint16_t;
1001 13 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1002 13 : return extremum_element<T, IS_MAX>(
1003 : static_cast<const T *>(buffer), nElts, bHasNoData,
1004 13 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1005 : }
1006 13 : case GDT_Int32:
1007 : {
1008 : using T = int32_t;
1009 13 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1010 13 : return extremum_element<T, IS_MAX>(
1011 : static_cast<const T *>(buffer), nElts, bHasNoData,
1012 13 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1013 : }
1014 13 : case GDT_UInt32:
1015 : {
1016 : using T = uint32_t;
1017 13 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1018 13 : return extremum_element<T, IS_MAX>(
1019 : static_cast<const T *>(buffer), nElts, bHasNoData,
1020 13 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1021 : }
1022 : #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 5, 0)
1023 11 : case GDT_Int64:
1024 : {
1025 : using T = int64_t;
1026 11 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1027 11 : return extremum_element<T, IS_MAX>(
1028 : static_cast<const T *>(buffer), nElts, bHasNoData,
1029 11 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1030 : }
1031 11 : case GDT_UInt64:
1032 : {
1033 : using T = uint64_t;
1034 11 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1035 11 : return extremum_element<T, IS_MAX>(
1036 : static_cast<const T *>(buffer), nElts, bHasNoData,
1037 11 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1038 : }
1039 : #endif
1040 25 : case GDT_Float32:
1041 : {
1042 : using T = float;
1043 25 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1044 25 : return extremum_element<T, IS_MAX>(
1045 : static_cast<const T *>(buffer), nElts, bHasNoData,
1046 25 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1047 : }
1048 21 : case GDT_Float64:
1049 : {
1050 : using T = double;
1051 21 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1052 21 : return extremum_element<T, IS_MAX>(
1053 : static_cast<const T *>(buffer), nElts, bHasNoData,
1054 21 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1055 : }
1056 2 : default:
1057 2 : break;
1058 : }
1059 2 : CPLError(CE_Failure, CPLE_NotSupported,
1060 : "%s not supported for this data type.", __FUNCTION__);
1061 2 : return 0;
1062 : }
1063 :
1064 : } // namespace detail
1065 :
1066 : /************************************************************************/
1067 : /* max_element() */
1068 : /************************************************************************/
1069 :
1070 : /** Return the index of the element where the maximum value is hit.
1071 : *
1072 : * If it is hit in several locations, it is not specified which one will be
1073 : * returned.
1074 : *
1075 : * @param buffer Vector of nElts elements of type eDT.
1076 : * @param nElts Number of elements in buffer.
1077 : * @param eDT Data type of the elements of buffer.
1078 : * @param bHasNoData Whether dfNoDataValue is valid.
1079 : * @param dfNoDataValue Nodata value, only taken into account if bHasNoData == true
1080 : *
1081 : * @since GDAL 3.11
1082 : */
1083 59 : inline size_t max_element(const void *buffer, size_t nElts, GDALDataType eDT,
1084 : bool bHasNoData, double dfNoDataValue)
1085 : {
1086 59 : return detail::extremum_element<true>(buffer, nElts, eDT, bHasNoData,
1087 59 : dfNoDataValue);
1088 : }
1089 :
1090 : /************************************************************************/
1091 : /* min_element() */
1092 : /************************************************************************/
1093 :
1094 : /** Return the index of the element where the minimum value is hit.
1095 : *
1096 : * If it is hit in several locations, it is not specified which one will be
1097 : * returned.
1098 : *
1099 : * @param buffer Vector of nElts elements of type eDT.
1100 : * @param nElts Number of elements in buffer.
1101 : * @param eDT Data type of the elements of buffer.
1102 : * @param bHasNoData Whether dfNoDataValue is valid.
1103 : * @param dfNoDataValue Nodata value, only taken into account if bHasNoData == true
1104 : *
1105 : * @since GDAL 3.11
1106 : */
1107 109 : inline size_t min_element(const void *buffer, size_t nElts, GDALDataType eDT,
1108 : bool bHasNoData, double dfNoDataValue)
1109 : {
1110 109 : return detail::extremum_element<false>(buffer, nElts, eDT, bHasNoData,
1111 109 : dfNoDataValue);
1112 : }
1113 :
1114 : namespace detail
1115 : {
1116 :
1117 : #ifdef NOT_EFFICIENT
1118 :
1119 : /************************************************************************/
1120 : /* minmax_element() */
1121 : /************************************************************************/
1122 :
1123 : template <class T>
1124 : std::pair<size_t, size_t> minmax_element(const T *v, size_t size, T noDataValue)
1125 : {
1126 : static_assert(!(std::is_floating_point_v<T>));
1127 : if (size == 0)
1128 : return std::pair(0, 0);
1129 : size_t idx_of_min = 0;
1130 : size_t idx_of_max = 0;
1131 : T vmin = v[0];
1132 : T vmax = v[0];
1133 : bool extremum_is_nodata = vmin == noDataValue;
1134 : size_t i = 1;
1135 : for (; i < size; ++i)
1136 : {
1137 : if (v[i] != noDataValue && (v[i] < vmin || extremum_is_nodata))
1138 : {
1139 : vmin = v[i];
1140 : idx_of_min = i;
1141 : extremum_is_nodata = false;
1142 : }
1143 : if (v[i] != noDataValue && (v[i] > vmax || extremum_is_nodata))
1144 : {
1145 : vmax = v[i];
1146 : idx_of_max = i;
1147 : extremum_is_nodata = false;
1148 : }
1149 : }
1150 : return std::pair(idx_of_min, idx_of_max);
1151 : }
1152 :
1153 : template <class T>
1154 : std::pair<size_t, size_t> minmax_element(const T *v, size_t size)
1155 : {
1156 : static_assert(!(std::is_floating_point_v<T>));
1157 : if (size == 0)
1158 : return std::pair(0, 0);
1159 : size_t idx_of_min = 0;
1160 : size_t idx_of_max = 0;
1161 : T vmin = v[0];
1162 : T vmax = v[0];
1163 : size_t i = 1;
1164 : for (; i < size; ++i)
1165 : {
1166 : if (v[i] < vmin)
1167 : {
1168 : vmin = v[i];
1169 : idx_of_min = i;
1170 : }
1171 : if (v[i] > vmax)
1172 : {
1173 : vmax = v[i];
1174 : idx_of_max = i;
1175 : }
1176 : }
1177 : return std::pair(idx_of_min, idx_of_max);
1178 : }
1179 :
1180 : template <class T>
1181 : inline std::pair<size_t, size_t> minmax_element_with_nan(const T *v,
1182 : size_t size)
1183 : {
1184 : if (size == 0)
1185 : return std::pair(0, 0);
1186 : size_t idx_of_min = 0;
1187 : size_t idx_of_max = 0;
1188 : T vmin = v[0];
1189 : T vmax = v[0];
1190 : size_t i = 1;
1191 : if (std::isnan(v[0]))
1192 : {
1193 : for (; i < size; ++i)
1194 : {
1195 : if (!std::isnan(v[i]))
1196 : {
1197 : vmin = v[i];
1198 : idx_of_min = i;
1199 : vmax = v[i];
1200 : idx_of_max = i;
1201 : break;
1202 : }
1203 : }
1204 : }
1205 : for (; i < size; ++i)
1206 : {
1207 : if (v[i] < vmin)
1208 : {
1209 : vmin = v[i];
1210 : idx_of_min = i;
1211 : }
1212 : if (v[i] > vmax)
1213 : {
1214 : vmax = v[i];
1215 : idx_of_max = i;
1216 : }
1217 : }
1218 : return std::pair(idx_of_min, idx_of_max);
1219 : }
1220 :
1221 : template <>
1222 : std::pair<size_t, size_t> minmax_element<float>(const float *v, size_t size)
1223 : {
1224 : return minmax_element_with_nan<float>(v, size);
1225 : }
1226 :
1227 : template <>
1228 : std::pair<size_t, size_t> minmax_element<double>(const double *v, size_t size)
1229 : {
1230 : return minmax_element_with_nan<double>(v, size);
1231 : }
1232 :
1233 : template <class T>
1234 : inline std::pair<size_t, size_t> minmax_element(const T *buffer, size_t size,
1235 : bool bHasNoData, T noDataValue)
1236 : {
1237 : if (bHasNoData)
1238 : {
1239 : return minmax_element<T>(buffer, size, noDataValue);
1240 : }
1241 : else
1242 : {
1243 : return minmax_element<T>(buffer, size);
1244 : }
1245 : }
1246 : #else
1247 :
1248 : /************************************************************************/
1249 : /* minmax_element() */
1250 : /************************************************************************/
1251 :
1252 : template <class T>
1253 15 : inline std::pair<size_t, size_t> minmax_element(const T *buffer, size_t size,
1254 : bool bHasNoData, T noDataValue)
1255 : {
1256 : #ifdef NOT_EFFICIENT
1257 : if (bHasNoData)
1258 : {
1259 : return minmax_element<T>(buffer, size, noDataValue);
1260 : }
1261 : else
1262 : {
1263 : return minmax_element<T>(buffer, size);
1264 : //auto [imin, imax] = std::minmax_element(buffer, buffer + size);
1265 : //return std::pair(imin - buffer, imax - buffer);
1266 : }
1267 : #else
1268 :
1269 : #if !defined(GDAL_MINMAX_ELEMENT_USE_SSE2)
1270 : if constexpr (!std::is_floating_point_v<T>)
1271 : {
1272 : if (!bHasNoData)
1273 : {
1274 : auto [min_iter, max_iter] =
1275 : std::minmax_element(buffer, buffer + size);
1276 : return std::pair(min_iter - buffer, max_iter - buffer);
1277 : }
1278 : }
1279 : #endif
1280 :
1281 : // Using separately min and max is more efficient than computing them
1282 : // within the same loop
1283 : return std::pair(
1284 45 : extremum_element<T, false>(buffer, size, bHasNoData, noDataValue),
1285 15 : extremum_element<T, true>(buffer, size, bHasNoData, noDataValue));
1286 :
1287 : #endif
1288 : }
1289 : #endif
1290 :
1291 : } // namespace detail
1292 :
1293 : /************************************************************************/
1294 : /* minmax_element() */
1295 : /************************************************************************/
1296 :
1297 : /** Return the index of the elements where the minimum and maximum values are hit.
1298 : *
1299 : * If they are hit in several locations, it is not specified which one will be
1300 : * returned (contrary to std::minmax_element).
1301 : *
1302 : * @param buffer Vector of nElts elements of type eDT.
1303 : * @param nElts Number of elements in buffer.
1304 : * @param eDT Data type of the elements of buffer.
1305 : * @param bHasNoData Whether dfNoDataValue is valid.
1306 : * @param dfNoDataValue Nodata value, only taken into account if bHasNoData == true
1307 : *
1308 : * @since GDAL 3.11
1309 : */
1310 16 : inline std::pair<size_t, size_t> minmax_element(const void *buffer,
1311 : size_t nElts, GDALDataType eDT,
1312 : bool bHasNoData,
1313 : double dfNoDataValue)
1314 : {
1315 16 : switch (eDT)
1316 : {
1317 : #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 7, 0)
1318 1 : case GDT_Int8:
1319 : {
1320 : using T = int8_t;
1321 1 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1322 : return detail::minmax_element<T>(
1323 : static_cast<const T *>(buffer), nElts, bHasNoData,
1324 1 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1325 : }
1326 : #endif
1327 5 : case GDT_Byte:
1328 : {
1329 : using T = uint8_t;
1330 5 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1331 : return detail::minmax_element<T>(
1332 : static_cast<const T *>(buffer), nElts, bHasNoData,
1333 5 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1334 : }
1335 1 : case GDT_Int16:
1336 : {
1337 : using T = int16_t;
1338 1 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1339 : return detail::minmax_element<T>(
1340 : static_cast<const T *>(buffer), nElts, bHasNoData,
1341 1 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1342 : }
1343 1 : case GDT_UInt16:
1344 : {
1345 : using T = uint16_t;
1346 1 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1347 : return detail::minmax_element<T>(
1348 : static_cast<const T *>(buffer), nElts, bHasNoData,
1349 1 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1350 : }
1351 1 : case GDT_Int32:
1352 : {
1353 : using T = int32_t;
1354 1 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1355 : return detail::minmax_element<T>(
1356 : static_cast<const T *>(buffer), nElts, bHasNoData,
1357 1 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1358 : }
1359 1 : case GDT_UInt32:
1360 : {
1361 : using T = uint32_t;
1362 1 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1363 : return detail::minmax_element<T>(
1364 : static_cast<const T *>(buffer), nElts, bHasNoData,
1365 1 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1366 : }
1367 : #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 5, 0)
1368 1 : case GDT_Int64:
1369 : {
1370 : using T = int64_t;
1371 1 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1372 : return detail::minmax_element<T>(
1373 : static_cast<const T *>(buffer), nElts, bHasNoData,
1374 1 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1375 : }
1376 1 : case GDT_UInt64:
1377 : {
1378 : using T = uint64_t;
1379 1 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1380 : return detail::minmax_element<T>(
1381 : static_cast<const T *>(buffer), nElts, bHasNoData,
1382 1 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1383 : }
1384 : #endif
1385 1 : case GDT_Float32:
1386 : {
1387 : using T = float;
1388 1 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1389 : return detail::minmax_element<T>(
1390 : static_cast<const T *>(buffer), nElts, bHasNoData,
1391 1 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1392 : }
1393 2 : case GDT_Float64:
1394 : {
1395 : using T = double;
1396 2 : bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
1397 : return detail::minmax_element<T>(
1398 : static_cast<const T *>(buffer), nElts, bHasNoData,
1399 2 : bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
1400 : }
1401 1 : default:
1402 1 : break;
1403 : }
1404 1 : CPLError(CE_Failure, CPLE_NotSupported,
1405 : "%s not supported for this data type.", __FUNCTION__);
1406 1 : return std::pair(0, 0);
1407 : }
1408 :
1409 : } // namespace GDAL_MINMAXELT_NS
1410 :
1411 : #endif // GDAL_MINMAX_ELEMENT_INCLUDED
|