Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Core
4 : * Purpose: Inline C++ templates
5 : * Author: Phil Vachon, <philippe at cowpig.ca>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2009, Phil Vachon, <philippe at cowpig.ca>
9 : * Copyright (c) 2025, Even Rouault, <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #ifndef GDAL_PRIV_TEMPLATES_HPP_INCLUDED
15 : #define GDAL_PRIV_TEMPLATES_HPP_INCLUDED
16 :
17 : #include "cpl_port.h"
18 :
19 : #include <algorithm>
20 : #include <cmath>
21 : #include <cstdint>
22 : #include <limits>
23 : #include <type_traits>
24 :
25 : /************************************************************************/
26 : /* GDALGetDataLimits() */
27 : /************************************************************************/
28 : /**
29 : * Compute the limits of values that can be placed in Tout in terms of
30 : * Tin. Usually used for output clamping, when the output data type's
31 : * limits are stable relative to the input type (i.e. no roundoff error).
32 : *
33 : * @param tMaxValue the returned maximum value
34 : * @param tMinValue the returned minimum value
35 : */
36 :
37 : template <class Tin, class Tout>
38 377487080 : inline void GDALGetDataLimits(Tin &tMaxValue, Tin &tMinValue)
39 : {
40 377487080 : tMaxValue = std::numeric_limits<Tin>::max();
41 378097190 : tMinValue = std::numeric_limits<Tin>::min();
42 :
43 : // Compute the actual minimum value of Tout in terms of Tin.
44 : if constexpr (std::numeric_limits<Tout>::is_signed &&
45 : std::numeric_limits<Tout>::is_integer)
46 : {
47 : // the minimum value is less than zero
48 : if constexpr (std::numeric_limits<Tout>::digits <
49 : std::numeric_limits<Tin>::digits ||
50 : !std::numeric_limits<Tin>::is_integer)
51 : {
52 : // Tout is smaller than Tin, so we need to clamp values in input
53 : // to the range of Tout's min/max values
54 : if (std::numeric_limits<Tin>::is_signed)
55 : {
56 78397992 : tMinValue = static_cast<Tin>(std::numeric_limits<Tout>::min());
57 : }
58 78912370 : tMaxValue = static_cast<Tin>(std::numeric_limits<Tout>::max());
59 : }
60 : }
61 : else if constexpr (std::numeric_limits<Tout>::is_integer)
62 : {
63 : // the output is unsigned, so we just need to determine the max
64 : /* coverity[same_on_both_sides] */
65 : if constexpr (std::numeric_limits<Tout>::digits <=
66 : std::numeric_limits<Tin>::digits)
67 : {
68 : // Tout is smaller than Tin, so we need to clamp the input values
69 : // to the range of Tout's max
70 145004597 : tMaxValue = static_cast<Tin>(std::numeric_limits<Tout>::max());
71 : }
72 146172771 : tMinValue = 0;
73 : }
74 376370770 : }
75 :
76 : /************************************************************************/
77 : /* GDALClampValue() */
78 : /************************************************************************/
79 : /**
80 : * Clamp values of type T to a specified range
81 : *
82 : * @param tValue the value
83 : * @param tMax the max value
84 : * @param tMin the min value
85 : */
86 : template <class T>
87 374935902 : inline T GDALClampValue(const T tValue, const T tMax, const T tMin)
88 : {
89 374935902 : return tValue > tMax ? tMax : tValue < tMin ? tMin : tValue;
90 : }
91 :
92 : /************************************************************************/
93 : /* GDALClampDoubleValue() */
94 : /************************************************************************/
95 : /**
96 : * Clamp double values to a specified range, this uses the same
97 : * argument ordering as std::clamp, returns TRUE if the value was clamped.
98 : *
99 : * @param tValue the value
100 : * @param tMin the min value
101 : * @param tMax the max value
102 : *
103 : */
104 : template <class T2, class T3>
105 227 : inline bool GDALClampDoubleValue(double &tValue, const T2 tMin, const T3 tMax)
106 : {
107 227 : const double tMin2{static_cast<double>(tMin)};
108 227 : const double tMax2{static_cast<double>(tMax)};
109 227 : if (tValue > tMax2 || tValue < tMin2)
110 : {
111 22 : tValue = tValue > tMax2 ? tMax2 : tValue < tMin2 ? tMin2 : tValue;
112 22 : return true;
113 : }
114 : else
115 : {
116 205 : return false;
117 : }
118 : }
119 :
120 : /************************************************************************/
121 : /* GDALIsValueInRange() */
122 : /************************************************************************/
123 : /**
124 : * Returns whether a value is in the type range.
125 : * NaN is considered not to be in type range.
126 : *
127 : * @param dfValue the value
128 : * @return whether the value is in the type range.
129 : */
130 142435 : template <class T> inline bool GDALIsValueInRange(double dfValue)
131 : {
132 284822 : return dfValue >= static_cast<double>(std::numeric_limits<T>::lowest()) &&
133 284822 : dfValue <= static_cast<double>(std::numeric_limits<T>::max());
134 : }
135 :
136 26 : template <> inline bool GDALIsValueInRange<double>(double dfValue)
137 : {
138 26 : return !std::isnan(dfValue);
139 : }
140 :
141 42075 : template <> inline bool GDALIsValueInRange<float>(double dfValue)
142 : {
143 84072 : return std::isinf(dfValue) ||
144 42005 : (dfValue >= -std::numeric_limits<float>::max() &&
145 84062 : dfValue <= std::numeric_limits<float>::max());
146 : }
147 :
148 6814 : template <> inline bool GDALIsValueInRange<int64_t>(double dfValue)
149 : {
150 : // Values in the range [INT64_MAX - 1023, INT64_MAX - 1]
151 : // get converted to a double that once cast to int64_t is
152 : // INT64_MAX + 1, hence the < strict comparison.
153 : return dfValue >=
154 13627 : static_cast<double>(std::numeric_limits<int64_t>::min()) &&
155 13627 : dfValue < static_cast<double>(std::numeric_limits<int64_t>::max());
156 : }
157 :
158 7981 : template <> inline bool GDALIsValueInRange<uint64_t>(double dfValue)
159 : {
160 : // Values in the range [UINT64_MAX - 2047, UINT64_MAX - 1]
161 : // get converted to a double that once cast to uint64_t is
162 : // UINT64_MAX + 1, hence the < strict comparison.
163 15959 : return dfValue >= 0 &&
164 15959 : dfValue < static_cast<double>(std::numeric_limits<uint64_t>::max());
165 : }
166 :
167 : /************************************************************************/
168 : /* GDALIsValueExactAs() */
169 : /************************************************************************/
170 : /**
171 : * Returns whether a value can be exactly represented on type T.
172 : *
173 : * That is static_cast\<double\>(static_cast\<T\>(dfValue)) is legal and is
174 : * equal to dfValue.
175 : *
176 : * Note: for T=float or double, a NaN input leads to true
177 : *
178 : * @param dfValue the value
179 : * @return whether the value can be exactly represented on type T.
180 : */
181 578 : template <class T> inline bool GDALIsValueExactAs(double dfValue)
182 : {
183 1109 : return GDALIsValueInRange<T>(dfValue) &&
184 1109 : static_cast<double>(static_cast<T>(dfValue)) == dfValue;
185 : }
186 :
187 97 : template <> inline bool GDALIsValueExactAs<float>(double dfValue)
188 : {
189 277 : return std::isnan(dfValue) ||
190 92 : (GDALIsValueInRange<float>(dfValue) &&
191 185 : static_cast<double>(static_cast<float>(dfValue)) == dfValue);
192 : }
193 :
194 16 : template <> inline bool GDALIsValueExactAs<double>(double)
195 : {
196 16 : return true;
197 : }
198 :
199 : /************************************************************************/
200 : /* GDALCopyWord() */
201 : /************************************************************************/
202 :
203 : template <class Tin, class Tout> struct sGDALCopyWord
204 : {
205 210344340 : static inline void f(const Tin tValueIn, Tout &tValueOut)
206 : {
207 : Tin tMaxVal, tMinVal;
208 210344340 : GDALGetDataLimits<Tin, Tout>(tMaxVal, tMinVal);
209 210704440 : tValueOut =
210 209581040 : static_cast<Tout>(GDALClampValue(tValueIn, tMaxVal, tMinVal));
211 210704440 : }
212 : };
213 :
214 : template <class Tin> struct sGDALCopyWord<Tin, float>
215 : {
216 10886548 : static inline void f(const Tin tValueIn, float &fValueOut)
217 : {
218 10886548 : fValueOut = static_cast<float>(tValueIn);
219 10886548 : }
220 : };
221 :
222 : template <class Tin> struct sGDALCopyWord<Tin, double>
223 : {
224 78202236 : static inline void f(const Tin tValueIn, double &dfValueOut)
225 : {
226 78202236 : dfValueOut = static_cast<double>(tValueIn);
227 78202236 : }
228 : };
229 :
230 : template <> struct sGDALCopyWord<float, double>
231 : {
232 46469100 : static inline void f(const float fValueIn, double &dfValueOut)
233 : {
234 46469100 : dfValueOut = fValueIn;
235 46469100 : }
236 : };
237 :
238 : template <> struct sGDALCopyWord<double, float>
239 : {
240 2303950 : static inline void f(const double dfValueIn, float &fValueOut)
241 : {
242 2303950 : if (dfValueIn > std::numeric_limits<float>::max())
243 : {
244 48 : fValueOut = std::numeric_limits<float>::infinity();
245 48 : return;
246 : }
247 2303900 : if (dfValueIn < -std::numeric_limits<float>::max())
248 : {
249 52 : fValueOut = -std::numeric_limits<float>::infinity();
250 52 : return;
251 : }
252 :
253 2303850 : fValueOut = static_cast<float>(dfValueIn);
254 : }
255 : };
256 :
257 : template <class Tout> struct sGDALCopyWord<float, Tout>
258 : {
259 3978020 : static inline void f(const float fValueIn, Tout &tValueOut)
260 : {
261 3978020 : if (std::isnan(fValueIn))
262 : {
263 0 : tValueOut = 0;
264 0 : return;
265 : }
266 : float fMaxVal, fMinVal;
267 3978020 : GDALGetDataLimits<float, Tout>(fMaxVal, fMinVal);
268 3977970 : tValueOut = static_cast<Tout>(
269 3977940 : GDALClampValue(fValueIn + 0.5f, fMaxVal, fMinVal));
270 : }
271 : };
272 :
273 : template <> struct sGDALCopyWord<float, short>
274 : {
275 2928940 : static inline void f(const float fValueIn, short &nValueOut)
276 : {
277 2928940 : if (std::isnan(fValueIn))
278 : {
279 0 : nValueOut = 0;
280 0 : return;
281 : }
282 : float fMaxVal, fMinVal;
283 2928940 : GDALGetDataLimits<float, short>(fMaxVal, fMinVal);
284 2928940 : float fValue = fValueIn >= 0.0f ? fValueIn + 0.5f : fValueIn - 0.5f;
285 2928940 : nValueOut =
286 2928940 : static_cast<short>(GDALClampValue(fValue, fMaxVal, fMinVal));
287 : }
288 : };
289 :
290 : template <> struct sGDALCopyWord<float, signed char>
291 : {
292 297 : static inline void f(const float fValueIn, signed char &nValueOut)
293 : {
294 297 : if (std::isnan(fValueIn))
295 : {
296 0 : nValueOut = 0;
297 0 : return;
298 : }
299 : float fMaxVal, fMinVal;
300 297 : GDALGetDataLimits<float, signed char>(fMaxVal, fMinVal);
301 297 : float fValue = fValueIn >= 0.0f ? fValueIn + 0.5f : fValueIn - 0.5f;
302 297 : nValueOut =
303 297 : static_cast<signed char>(GDALClampValue(fValue, fMaxVal, fMinVal));
304 : }
305 : };
306 :
307 : template <class Tout> struct sGDALCopyWord<double, Tout>
308 : {
309 84311720 : static inline void f(const double dfValueIn, Tout &tValueOut)
310 : {
311 84311720 : if (std::isnan(dfValueIn))
312 : {
313 0 : tValueOut = 0;
314 0 : return;
315 : }
316 : double dfMaxVal, dfMinVal;
317 84884120 : GDALGetDataLimits<double, Tout>(dfMaxVal, dfMinVal);
318 83982820 : tValueOut = static_cast<Tout>(
319 82617920 : GDALClampValue(dfValueIn + 0.5, dfMaxVal, dfMinVal));
320 : }
321 : };
322 :
323 : template <> struct sGDALCopyWord<double, int>
324 : {
325 70358300 : static inline void f(const double dfValueIn, int &nValueOut)
326 : {
327 70358300 : if (std::isnan(dfValueIn))
328 : {
329 0 : nValueOut = 0;
330 0 : return;
331 : }
332 : double dfMaxVal, dfMinVal;
333 70358300 : GDALGetDataLimits<double, int>(dfMaxVal, dfMinVal);
334 70358300 : double dfValue = dfValueIn >= 0.0 ? dfValueIn + 0.5 : dfValueIn - 0.5;
335 70358300 : nValueOut =
336 70358300 : static_cast<int>(GDALClampValue(dfValue, dfMaxVal, dfMinVal));
337 : }
338 : };
339 :
340 : template <> struct sGDALCopyWord<double, std::int64_t>
341 : {
342 688 : static inline void f(const double dfValueIn, std::int64_t &nValueOut)
343 : {
344 688 : if (std::isnan(dfValueIn))
345 : {
346 1 : nValueOut = 0;
347 : }
348 687 : else if (dfValueIn >=
349 687 : static_cast<double>(std::numeric_limits<std::int64_t>::max()))
350 : {
351 6 : nValueOut = std::numeric_limits<std::int64_t>::max();
352 : }
353 681 : else if (dfValueIn <=
354 681 : static_cast<double>(std::numeric_limits<std::int64_t>::min()))
355 : {
356 4 : nValueOut = std::numeric_limits<std::int64_t>::min();
357 : }
358 : else
359 : {
360 1354 : nValueOut = static_cast<std::int64_t>(
361 677 : dfValueIn > 0.0f ? dfValueIn + 0.5f : dfValueIn - 0.5f);
362 : }
363 688 : }
364 : };
365 :
366 : template <> struct sGDALCopyWord<double, std::uint64_t>
367 : {
368 604 : static inline void f(const double dfValueIn, std::uint64_t &nValueOut)
369 : {
370 604 : if (!(dfValueIn > 0))
371 : {
372 164 : nValueOut = 0;
373 : }
374 440 : else if (dfValueIn >
375 440 : static_cast<double>(std::numeric_limits<uint64_t>::max()))
376 : {
377 4 : nValueOut = std::numeric_limits<uint64_t>::max();
378 : }
379 : else
380 : {
381 436 : nValueOut = static_cast<std::uint64_t>(dfValueIn + 0.5);
382 : }
383 604 : }
384 : };
385 :
386 : template <> struct sGDALCopyWord<double, short>
387 : {
388 5101810 : static inline void f(const double dfValueIn, short &nValueOut)
389 : {
390 5101810 : if (std::isnan(dfValueIn))
391 : {
392 0 : nValueOut = 0;
393 0 : return;
394 : }
395 : double dfMaxVal, dfMinVal;
396 5101810 : GDALGetDataLimits<double, short>(dfMaxVal, dfMinVal);
397 5101810 : double dfValue = dfValueIn > 0.0 ? dfValueIn + 0.5 : dfValueIn - 0.5;
398 5101810 : nValueOut =
399 5101810 : static_cast<short>(GDALClampValue(dfValue, dfMaxVal, dfMinVal));
400 : }
401 : };
402 :
403 : template <> struct sGDALCopyWord<double, signed char>
404 : {
405 463 : static inline void f(const double dfValueIn, signed char &nValueOut)
406 : {
407 463 : if (std::isnan(dfValueIn))
408 : {
409 0 : nValueOut = 0;
410 0 : return;
411 : }
412 : double dfMaxVal, dfMinVal;
413 463 : GDALGetDataLimits<double, signed char>(dfMaxVal, dfMinVal);
414 463 : double dfValue = dfValueIn > 0.0 ? dfValueIn + 0.5 : dfValueIn - 0.5;
415 463 : nValueOut = static_cast<signed char>(
416 463 : GDALClampValue(dfValue, dfMaxVal, dfMinVal));
417 : }
418 : };
419 :
420 : // Roundoff occurs for Float32 -> int32 for max/min. Overload GDALCopyWord
421 : // specifically for this case.
422 : template <> struct sGDALCopyWord<float, int>
423 : {
424 154090000 : static inline void f(const float fValueIn, int &nValueOut)
425 : {
426 154090000 : if (std::isnan(fValueIn))
427 : {
428 0 : nValueOut = 0;
429 : }
430 155489000 : else if (fValueIn >=
431 155381000 : static_cast<float>(std::numeric_limits<int>::max()))
432 : {
433 160 : nValueOut = std::numeric_limits<int>::max();
434 : }
435 153264000 : else if (fValueIn <=
436 155489000 : static_cast<float>(std::numeric_limits<int>::min()))
437 : {
438 0 : nValueOut = std::numeric_limits<int>::min();
439 : }
440 : else
441 : {
442 155723000 : nValueOut = static_cast<int>(fValueIn > 0.0f ? fValueIn + 0.5f
443 120973 : : fValueIn - 0.5f);
444 : }
445 155603000 : }
446 : };
447 :
448 : // Roundoff occurs for Float32 -> uint32 for max. Overload GDALCopyWord
449 : // specifically for this case.
450 : template <> struct sGDALCopyWord<float, unsigned int>
451 : {
452 203 : static inline void f(const float fValueIn, unsigned int &nValueOut)
453 : {
454 203 : if (!(fValueIn > 0))
455 : {
456 20 : nValueOut = 0;
457 : }
458 183 : else if (fValueIn >=
459 183 : static_cast<float>(std::numeric_limits<unsigned int>::max()))
460 : {
461 20 : nValueOut = std::numeric_limits<unsigned int>::max();
462 : }
463 : else
464 : {
465 163 : nValueOut = static_cast<unsigned int>(fValueIn + 0.5f);
466 : }
467 203 : }
468 : };
469 :
470 : // Roundoff occurs for Float32 -> std::int64_t for max/min. Overload
471 : // GDALCopyWord specifically for this case.
472 : template <> struct sGDALCopyWord<float, std::int64_t>
473 : {
474 238 : static inline void f(const float fValueIn, std::int64_t &nValueOut)
475 : {
476 238 : if (std::isnan(fValueIn))
477 : {
478 1 : nValueOut = 0;
479 : }
480 237 : else if (fValueIn >=
481 237 : static_cast<float>(std::numeric_limits<std::int64_t>::max()))
482 : {
483 2 : nValueOut = std::numeric_limits<std::int64_t>::max();
484 : }
485 235 : else if (fValueIn <=
486 235 : static_cast<float>(std::numeric_limits<std::int64_t>::min()))
487 : {
488 2 : nValueOut = std::numeric_limits<std::int64_t>::min();
489 : }
490 : else
491 : {
492 466 : nValueOut = static_cast<std::int64_t>(
493 233 : fValueIn > 0.0f ? fValueIn + 0.5f : fValueIn - 0.5f);
494 : }
495 238 : }
496 : };
497 :
498 : // Roundoff occurs for Float32 -> std::uint64_t for max. Overload GDALCopyWord
499 : // specifically for this case.
500 : template <> struct sGDALCopyWord<float, std::uint64_t>
501 : {
502 168 : static inline void f(const float fValueIn, std::uint64_t &nValueOut)
503 : {
504 168 : if (!(fValueIn > 0))
505 : {
506 3 : nValueOut = 0;
507 : }
508 165 : else if (fValueIn >=
509 165 : static_cast<float>(std::numeric_limits<std::uint64_t>::max()))
510 : {
511 2 : nValueOut = std::numeric_limits<std::uint64_t>::max();
512 : }
513 : else
514 : {
515 163 : nValueOut = static_cast<std::uint64_t>(fValueIn + 0.5f);
516 : }
517 168 : }
518 : };
519 :
520 : /**
521 : * Copy a single word, optionally rounding if appropriate (i.e. going
522 : * from the float to the integer case). Note that this is the function
523 : * you should specialize if you're adding a new data type.
524 : *
525 : * @param tValueIn value of type Tin; the input value to be converted
526 : * @param tValueOut value of type Tout; the output value
527 : */
528 :
529 : template <class Tin, class Tout>
530 702760028 : inline void GDALCopyWord(const Tin tValueIn, Tout &tValueOut)
531 : {
532 : if constexpr (std::is_same<Tin, Tout>::value)
533 32391383 : tValueOut = tValueIn;
534 : else
535 670368645 : sGDALCopyWord<Tin, Tout>::f(tValueIn, tValueOut);
536 700459888 : }
537 :
538 : /************************************************************************/
539 : /* GDALCopy4Words() */
540 : /************************************************************************/
541 : /**
542 : * Copy 4 packed words to 4 packed words, optionally rounding if appropriate
543 : * (i.e. going from the float to the integer case).
544 : *
545 : * @param pValueIn pointer to 4 input values of type Tin.
546 : * @param pValueOut pointer to 4 output values of type Tout.
547 : */
548 :
549 : template <class Tin, class Tout>
550 16 : inline void GDALCopy4Words(const Tin *pValueIn, Tout *const pValueOut)
551 : {
552 16 : GDALCopyWord(pValueIn[0], pValueOut[0]);
553 16 : GDALCopyWord(pValueIn[1], pValueOut[1]);
554 16 : GDALCopyWord(pValueIn[2], pValueOut[2]);
555 16 : GDALCopyWord(pValueIn[3], pValueOut[3]);
556 16 : }
557 :
558 : /************************************************************************/
559 : /* GDALCopy8Words() */
560 : /************************************************************************/
561 : /**
562 : * Copy 8 packed words to 8 packed words, optionally rounding if appropriate
563 : * (i.e. going from the float to the integer case).
564 : *
565 : * @param pValueIn pointer to 8 input values of type Tin.
566 : * @param pValueOut pointer to 8 output values of type Tout.
567 : */
568 :
569 : template <class Tin, class Tout>
570 14783279 : inline void GDALCopy8Words(const Tin *pValueIn, Tout *const pValueOut)
571 : {
572 14783279 : GDALCopy4Words(pValueIn, pValueOut);
573 14784779 : GDALCopy4Words(pValueIn + 4, pValueOut + 4);
574 14784579 : }
575 :
576 : // Needs SSE2
577 : #if defined(__x86_64) || defined(_M_X64) || defined(USE_SSE2) || \
578 : defined(USE_NEON_OPTIMIZATIONS)
579 :
580 : #ifdef USE_NEON_OPTIMIZATIONS
581 : #include "include_sse2neon.h"
582 : #else
583 : #include <emmintrin.h>
584 : #endif
585 :
586 32672745 : static inline void GDALCopyXMMToInt32(const __m128i xmm, void *pDest)
587 : {
588 32672745 : int n32 = _mm_cvtsi128_si32(xmm); // Extract lower 32 bit word
589 32672745 : memcpy(pDest, &n32, sizeof(n32));
590 32672745 : }
591 :
592 77688273 : static inline void GDALCopyXMMToInt64(const __m128i xmm, void *pDest)
593 : {
594 : _mm_storel_epi64(reinterpret_cast<__m128i *>(pDest), xmm);
595 77688273 : }
596 :
597 : #if __SSSE3__
598 : #include <tmmintrin.h>
599 : #endif
600 :
601 : #if defined(__SSE4_1__) || defined(__AVX__)
602 : #include <smmintrin.h>
603 : #endif
604 :
605 : template <>
606 26204902 : inline void GDALCopy4Words(const float *pValueIn, GByte *const pValueOut)
607 : {
608 26204902 : __m128 xmm = _mm_loadu_ps(pValueIn);
609 :
610 : // The following clamping would be useless due to the final saturating
611 : // packing if we could guarantee the input range in [INT_MIN,INT_MAX]
612 26204902 : const __m128 p0d5 = _mm_set1_ps(0.5f);
613 26204902 : const __m128 xmm_max = _mm_set1_ps(255);
614 26204902 : xmm = _mm_add_ps(xmm, p0d5);
615 52412204 : xmm = _mm_min_ps(_mm_max_ps(xmm, p0d5), xmm_max);
616 :
617 26207102 : __m128i xmm_i = _mm_cvttps_epi32(xmm);
618 :
619 : #if defined(__SSSE3__) || defined(USE_NEON_OPTIMIZATIONS)
620 : xmm_i = _mm_shuffle_epi8(
621 : xmm_i, _mm_cvtsi32_si128(0 | (4 << 8) | (8 << 16) | (12 << 24)));
622 : #else
623 26205202 : xmm_i = _mm_packs_epi32(xmm_i, xmm_i); // Pack int32 to int16
624 26208602 : xmm_i = _mm_packus_epi16(xmm_i, xmm_i); // Pack int16 to uint8
625 : #endif
626 26208602 : GDALCopyXMMToInt32(xmm_i, pValueOut);
627 26192902 : }
628 :
629 : template <>
630 3355730 : inline void GDALCopy4Words(const float *pValueIn, GInt16 *const pValueOut)
631 : {
632 3355730 : __m128 xmm = _mm_loadu_ps(pValueIn);
633 :
634 3355730 : const __m128 xmm_min = _mm_set1_ps(-32768);
635 3355730 : const __m128 xmm_max = _mm_set1_ps(32767);
636 6711460 : xmm = _mm_min_ps(_mm_max_ps(xmm, xmm_min), xmm_max);
637 :
638 3355730 : const __m128 p0d5 = _mm_set1_ps(0.5f);
639 3355730 : const __m128 m0d5 = _mm_set1_ps(-0.5f);
640 3355730 : const __m128 mask = _mm_cmpge_ps(xmm, p0d5);
641 : // f >= 0.5f ? f + 0.5f : f - 0.5f
642 13422900 : xmm = _mm_add_ps(
643 : xmm, _mm_or_ps(_mm_and_ps(mask, p0d5), _mm_andnot_ps(mask, m0d5)));
644 :
645 3355730 : __m128i xmm_i = _mm_cvttps_epi32(xmm);
646 :
647 3355730 : xmm_i = _mm_packs_epi32(xmm_i, xmm_i); // Pack int32 to int16
648 3355730 : GDALCopyXMMToInt64(xmm_i, pValueOut);
649 3355730 : }
650 :
651 : template <>
652 1 : inline void GDALCopy4Words(const float *pValueIn, GUInt16 *const pValueOut)
653 : {
654 1 : __m128 xmm = _mm_loadu_ps(pValueIn);
655 :
656 1 : const __m128 p0d5 = _mm_set1_ps(0.5f);
657 1 : const __m128 xmm_max = _mm_set1_ps(65535);
658 1 : xmm = _mm_add_ps(xmm, p0d5);
659 2 : xmm = _mm_min_ps(_mm_max_ps(xmm, p0d5), xmm_max);
660 :
661 1 : __m128i xmm_i = _mm_cvttps_epi32(xmm);
662 :
663 : #if defined(__SSE4_1__) || defined(__AVX__) || defined(USE_NEON_OPTIMIZATIONS)
664 : xmm_i = _mm_packus_epi32(xmm_i, xmm_i); // Pack int32 to uint16
665 : #else
666 : // Translate to int16 range because _mm_packus_epi32 is SSE4.1 only
667 2 : xmm_i = _mm_add_epi32(xmm_i, _mm_set1_epi32(-32768));
668 1 : xmm_i = _mm_packs_epi32(xmm_i, xmm_i); // Pack int32 to int16
669 : // Translate back to uint16 range (actually -32768==32768 in int16)
670 1 : xmm_i = _mm_add_epi16(xmm_i, _mm_set1_epi16(-32768));
671 : #endif
672 1 : GDALCopyXMMToInt64(xmm_i, pValueOut);
673 1 : }
674 :
675 : #ifdef __AVX2__
676 :
677 : #include <immintrin.h>
678 :
679 : template <>
680 : inline void GDALCopy8Words(const float *pValueIn, GByte *const pValueOut)
681 : {
682 : __m256 ymm = _mm256_loadu_ps(pValueIn);
683 :
684 : const __m256 p0d5 = _mm256_set1_ps(0.5f);
685 : const __m256 ymm_max = _mm256_set1_ps(255);
686 : ymm = _mm256_add_ps(ymm, p0d5);
687 : ymm = _mm256_min_ps(_mm256_max_ps(ymm, p0d5), ymm_max);
688 :
689 : __m256i ymm_i = _mm256_cvttps_epi32(ymm);
690 :
691 : ymm_i = _mm256_packus_epi32(ymm_i, ymm_i); // Pack int32 to uint16
692 : ymm_i = _mm256_permute4x64_epi64(ymm_i, 0 | (2 << 2)); // AVX2
693 :
694 : __m128i xmm_i = _mm256_castsi256_si128(ymm_i);
695 : xmm_i = _mm_packus_epi16(xmm_i, xmm_i);
696 : GDALCopyXMMToInt64(xmm_i, pValueOut);
697 : }
698 :
699 : template <>
700 : inline void GDALCopy8Words(const float *pValueIn, GUInt16 *const pValueOut)
701 : {
702 : __m256 ymm = _mm256_loadu_ps(pValueIn);
703 :
704 : const __m256 p0d5 = _mm256_set1_ps(0.5f);
705 : const __m256 ymm_max = _mm256_set1_ps(65535);
706 : ymm = _mm256_add_ps(ymm, p0d5);
707 : ymm = _mm256_min_ps(_mm256_max_ps(ymm, p0d5), ymm_max);
708 :
709 : __m256i ymm_i = _mm256_cvttps_epi32(ymm);
710 :
711 : ymm_i = _mm256_packus_epi32(ymm_i, ymm_i); // Pack int32 to uint16
712 : ymm_i = _mm256_permute4x64_epi64(ymm_i, 0 | (2 << 2)); // AVX2
713 :
714 : _mm_storeu_si128(reinterpret_cast<__m128i *>(pValueOut),
715 : _mm256_castsi256_si128(ymm_i));
716 : }
717 : #else
718 : template <>
719 7754841 : inline void GDALCopy8Words(const float *pValueIn, GUInt16 *const pValueOut)
720 : {
721 7754841 : __m128 xmm = _mm_loadu_ps(pValueIn);
722 15509702 : __m128 xmm1 = _mm_loadu_ps(pValueIn + 4);
723 :
724 7754841 : const __m128 p0d5 = _mm_set1_ps(0.5f);
725 7754841 : const __m128 xmm_max = _mm_set1_ps(65535);
726 7754841 : xmm = _mm_add_ps(xmm, p0d5);
727 7754841 : xmm1 = _mm_add_ps(xmm1, p0d5);
728 15437002 : xmm = _mm_min_ps(_mm_max_ps(xmm, p0d5), xmm_max);
729 15513902 : xmm1 = _mm_min_ps(_mm_max_ps(xmm1, p0d5), xmm_max);
730 :
731 7754191 : __m128i xmm_i = _mm_cvttps_epi32(xmm);
732 7753501 : __m128i xmm1_i = _mm_cvttps_epi32(xmm1);
733 :
734 : #if defined(__SSE4_1__) || defined(__AVX__) || defined(USE_NEON_OPTIMIZATIONS)
735 : xmm_i = _mm_packus_epi32(xmm_i, xmm1_i); // Pack int32 to uint16
736 : #else
737 : // Translate to int16 range because _mm_packus_epi32 is SSE4.1 only
738 15507002 : xmm_i = _mm_add_epi32(xmm_i, _mm_set1_epi32(-32768));
739 15507002 : xmm1_i = _mm_add_epi32(xmm1_i, _mm_set1_epi32(-32768));
740 7758271 : xmm_i = _mm_packs_epi32(xmm_i, xmm1_i); // Pack int32 to int16
741 : // Translate back to uint16 range (actually -32768==32768 in int16)
742 15516502 : xmm_i = _mm_add_epi16(xmm_i, _mm_set1_epi16(-32768));
743 : #endif
744 : _mm_storeu_si128(reinterpret_cast<__m128i *>(pValueOut), xmm_i);
745 7758271 : }
746 : #endif
747 :
748 : #ifdef notdef_because_slightly_slower_than_default_implementation
749 : template <>
750 : inline void GDALCopy4Words(const double *pValueIn, float *const pValueOut)
751 : {
752 : __m128d float_posmax = _mm_set1_pd(std::numeric_limits<float>::max());
753 : __m128d float_negmax = _mm_set1_pd(-std::numeric_limits<float>::max());
754 : __m128d float_posinf = _mm_set1_pd(std::numeric_limits<float>::infinity());
755 : __m128d float_neginf = _mm_set1_pd(-std::numeric_limits<float>::infinity());
756 : __m128d val01 = _mm_loadu_pd(pValueIn);
757 : __m128d val23 = _mm_loadu_pd(pValueIn + 2);
758 : __m128d mask_max = _mm_cmpge_pd(val01, float_posmax);
759 : __m128d mask_max23 = _mm_cmpge_pd(val23, float_posmax);
760 : val01 = _mm_or_pd(_mm_and_pd(mask_max, float_posinf),
761 : _mm_andnot_pd(mask_max, val01));
762 : val23 = _mm_or_pd(_mm_and_pd(mask_max23, float_posinf),
763 : _mm_andnot_pd(mask_max23, val23));
764 : __m128d mask_min = _mm_cmple_pd(val01, float_negmax);
765 : __m128d mask_min23 = _mm_cmple_pd(val23, float_negmax);
766 : val01 = _mm_or_pd(_mm_and_pd(mask_min, float_neginf),
767 : _mm_andnot_pd(mask_min, val01));
768 : val23 = _mm_or_pd(_mm_and_pd(mask_min23, float_neginf),
769 : _mm_andnot_pd(mask_min23, val23));
770 : __m128 val01_s = _mm_cvtpd_ps(val01);
771 : __m128 val23_s = _mm_cvtpd_ps(val23);
772 : __m128i val01_i = _mm_castps_si128(val01_s);
773 : __m128i val23_i = _mm_castps_si128(val23_s);
774 : GDALCopyXMMToInt64(val01_i, pValueOut);
775 : GDALCopyXMMToInt64(val23_i, pValueOut + 2);
776 : }
777 : #endif
778 :
779 : #endif // defined(__x86_64) || defined(_M_X64)
780 :
781 : #endif // GDAL_PRIV_TEMPLATES_HPP_INCLUDED
|