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