LCOV - code coverage report
Current view: top level - gcore - gdal_priv_templates.hpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 233 249 93.6 %
Date: 2025-01-18 12:42:00 Functions: 305 306 99.7 %

          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

Generated by: LCOV version 1.14