Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit caf2ca41 authored by Romain Guy's avatar Romain Guy
Browse files

Add ColorSpace class

The ColorSpace class can be used to create an RGB color space from
either primaries/whitepoint or an RGB->XYZ matrix.

The primaries and whitepoint are in xyY space. A utility function
is provided to compute xyY coordinates from XYZ coordinats.

The class contains numerous functions to create common RGB color
spaces (sRGB, DCI-P3, etc.).

Test: colorspace_test
Bug: 29940137
Change-Id: Ifba8701377d058f5877176dabf4183e904a4cde0
parent 5d4bae7f
Loading
Loading
Loading
Loading
+192 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_UI_COLOR_SPACE
#define ANDROID_UI_COLOR_SPACE

#include <array>
#include <cmath>
#include <functional>
#include <string>

#include <ui/mat3.h>
#include <ui/scalar.h>
#include <ui/vec2.h>
#include <ui/vec3.h>

namespace android {

class ColorSpace {
public:
    typedef std::function<float(float)> transfer_function;
    typedef std::function<float(float)> clamping_function;

    /**
     * Creates a named color space with the specified RGB->XYZ
     * conversion matrix. The white point and primaries will be
     * computed from the supplied matrix.
     *
     * The default transfer functions are a linear response x->x
     * and the default clamping function is a simple saturate
     * (clamp(x, 0, 1)).
     */
    ColorSpace(
            const std::string& name,
            const mat3& rgbToXYZ,
            transfer_function OETF = linearReponse,
            transfer_function EOTF = linearReponse,
            clamping_function clamper = saturate<float>
    ) noexcept;

    /**
     * Creates a named color space with the specified primaries
     * and white point. The RGB<>XYZ conversion matrices are
     * computed from the primaries and white point.
     *
     * The default transfer functions are a linear response x->x
     * and the default clamping function is a simple saturate
     * (clamp(x, 0, 1)).
     */
    ColorSpace(
            const std::string& name,
            const std::array<float2, 3>& primaries,
            const float2& whitePoint,
            transfer_function OETF = linearReponse,
            transfer_function EOTF = linearReponse,
            clamping_function clamper = saturate<float>
    ) noexcept;

    ColorSpace() noexcept = delete;

    /**
     * Encodes the supplied RGB value using this color space's
     * opto-electronic transfer function.
     */
    constexpr float3 fromLinear(const float3& v) const noexcept {
        return apply(v, mOETF);
    }

    /**
     * Decodes the supplied RGB value using this color space's
     * electro-optical transfer function.
     */
    constexpr float3 toLinear(const float3& v) const noexcept {
        return apply(v, mEOTF);
    }

    /**
     * Converts the supplied XYZ value to RGB. The returned value
     * is encoded with this color space's opto-electronic transfer
     * function and clamped by this color space's clamping function.
     */
    constexpr float3 xyzToRGB(const float3& xyz) const noexcept {
        return apply(fromLinear(mXYZtoRGB * xyz), mClamper);
    }

    /**
     * Converts the supplied RGB value to XYZ. The input RGB value
     * is decoded using this color space's electro-optical function
     * before being converted to XYZ. The returned result is clamped
     * by this color space's clamping function.
     */
    constexpr float3 rgbToXYZ(const float3& rgb) const noexcept {
        return apply(mRGBtoXYZ * toLinear(rgb), mClamper);
    }

    constexpr const std::string& getName() const noexcept {
        return mName;
    }

    constexpr const mat3& getRGBtoXYZ() const noexcept {
        return mRGBtoXYZ;
    }

    constexpr const mat3& getXYZtoRGB() const noexcept {
        return mXYZtoRGB;
    }

    constexpr const transfer_function& getOETF() const noexcept {
        return mOETF;
    }

    constexpr const transfer_function& getEOTF() const noexcept {
        return mEOTF;
    }

    constexpr const clamping_function& getClamper() const noexcept {
        return mClamper;
    }

    constexpr const std::array<float2, 3>& getPrimaries() const noexcept {
        return mPrimaries;
    }

    constexpr const float2& getWhitePoint() const noexcept {
        return mWhitePoint;
    }

    /**
     * Converts the supplied XYZ value to xyY.
     */
    static constexpr float2 xyY(const float3& XYZ) {
        return XYZ.xy / dot(XYZ, float3{1});
    }

    /**
     * Converts the supplied xyY value to XYZ.
     */
    static constexpr float3 XYZ(const float3& xyY) {
        return float3{(xyY.x * xyY.z) / xyY.y, xyY.z, ((1 - xyY.x - xyY.y) * xyY.z) / xyY.y};
    }

    static const ColorSpace sRGB();
    static const ColorSpace linearSRGB();
    static const ColorSpace extendedSRGB();
    static const ColorSpace linearExtendedSRGB();
    static const ColorSpace NTSC();
    static const ColorSpace BT709();
    static const ColorSpace BT2020();
    static const ColorSpace AdobeRGB();
    static const ColorSpace ProPhotoRGB();
    static const ColorSpace DisplayP3();
    static const ColorSpace DCIP3();
    static const ColorSpace ACES();
    static const ColorSpace ACEScg();

private:
    static constexpr mat3 computeXYZMatrix(
            const std::array<float2, 3>& primaries, const float2& whitePoint);

    static constexpr float linearReponse(float v) {
        return v;
    }

    const std::string mName;

    const mat3 mRGBtoXYZ;
    const mat3 mXYZtoRGB;

    const transfer_function mOETF;
    const transfer_function mEOTF;
    const clamping_function mClamper;

    std::array<float2, 3> mPrimaries;
    float2 mWhitePoint;
};

}; // namespace android

#endif // ANDROID_UI_COLOR_SPACE
+23 −17
Original line number Diff line number Diff line
@@ -41,6 +41,12 @@

#define PURE __attribute__((pure))

#if __cplusplus >= 201402L
#define CONSTEXPR constexpr
#else
#define CONSTEXPR
#endif

namespace android {
namespace details {
// -------------------------------------------------------------------------------------
@@ -103,10 +109,10 @@ MATRIX PURE gaussJordanInverse(const MATRIX& src) {
        // Factor out the lower triangle
        for (size_t j = 0; j < N; ++j) {
            if (j != i) {
                const T t = tmp[j][i];
                const T d = tmp[j][i];
                for (size_t k = 0; k < N; ++k) {
                    tmp[j][k] -= tmp[i][k] * t;
                    inverted[j][k] -= inverted[i][k] * t;
                    tmp[j][k] -= tmp[i][k] * d;
                    inverted[j][k] -= inverted[i][k] * d;
                }
            }
        }
@@ -119,7 +125,7 @@ MATRIX PURE gaussJordanInverse(const MATRIX& src) {
//------------------------------------------------------------------------------
// 2x2 matrix inverse is easy.
template <typename MATRIX>
MATRIX PURE fastInverse2(const MATRIX& x) {
CONSTEXPR MATRIX PURE fastInverse2(const MATRIX& x) {
    typedef typename MATRIX::value_type T;

    // Assuming the input matrix is:
@@ -153,7 +159,7 @@ MATRIX PURE fastInverse2(const MATRIX& x) {
// matrix inversion:
// http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3.C3.973_matrices
template <typename MATRIX>
MATRIX PURE fastInverse3(const MATRIX& x) {
CONSTEXPR MATRIX PURE fastInverse3(const MATRIX& x) {
    typedef typename MATRIX::value_type T;

    // Assuming the input matrix is:
@@ -216,7 +222,6 @@ MATRIX PURE fastInverse3(const MATRIX& x) {
    return inverted;
}


/**
 * Inversion function which switches on the matrix size.
 * @warning This function assumes the matrix is invertible. The result is
@@ -232,7 +237,7 @@ inline constexpr MATRIX PURE inverse(const MATRIX& matrix) {
}

template<typename MATRIX_R, typename MATRIX_A, typename MATRIX_B>
MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {
CONSTEXPR MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {
    // pre-requisite:
    //  lhs : D columns, R rows
    //  rhs : C columns, D rows
@@ -254,7 +259,7 @@ MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {

// transpose. this handles matrices of matrices
template <typename MATRIX>
MATRIX PURE transpose(const MATRIX& m) {
CONSTEXPR MATRIX PURE transpose(const MATRIX& m) {
    // for now we only handle square matrix transpose
    static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "transpose only supports square matrices");
    MATRIX result(MATRIX::NO_INIT);
@@ -268,7 +273,7 @@ MATRIX PURE transpose(const MATRIX& m) {

// trace. this handles matrices of matrices
template <typename MATRIX>
typename MATRIX::value_type PURE trace(const MATRIX& m) {
CONSTEXPR typename MATRIX::value_type PURE trace(const MATRIX& m) {
    static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "trace only defined for square matrices");
    typename MATRIX::value_type result(0);
    for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
@@ -279,7 +284,7 @@ typename MATRIX::value_type PURE trace(const MATRIX& m) {

// diag. this handles matrices of matrices
template <typename MATRIX>
typename MATRIX::col_type PURE diag(const MATRIX& m) {
CONSTEXPR typename MATRIX::col_type PURE diag(const MATRIX& m) {
    static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "diag only defined for square matrices");
    typename MATRIX::col_type result(MATRIX::col_type::NO_INIT);
    for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
@@ -389,7 +394,7 @@ public:

    // matrix * matrix, result is a matrix of the same type than the lhs matrix
    template<typename U>
    friend BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) {
    friend CONSTEXPR BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) {
        return matrix::multiply<BASE<T> >(lhs, rhs);
    }
};
@@ -419,7 +424,7 @@ public:
     * is instantiated, at which point they're only templated on the 2nd parameter
     * (the first one, BASE<T> being known).
     */
    friend inline BASE<T> PURE inverse(const BASE<T>& matrix) {
    friend inline CONSTEXPR BASE<T> PURE inverse(const BASE<T>& matrix) {
        return matrix::inverse(matrix);
    }
    friend inline constexpr BASE<T> PURE transpose(const BASE<T>& m) {
@@ -454,7 +459,7 @@ public:
    }

    template <typename VEC>
    static BASE<T> translate(const VEC& t) {
    static CONSTEXPR BASE<T> translate(const VEC& t) {
        BASE<T> r;
        r[BASE<T>::NUM_COLS-1] = t;
        return r;
@@ -465,7 +470,7 @@ public:
        return BASE<T>(s);
    }

    friend inline BASE<T> PURE abs(BASE<T> m) {
    friend inline CONSTEXPR BASE<T> PURE abs(BASE<T> m) {
        for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
            m[col] = abs(m[col]);
        }
@@ -482,7 +487,7 @@ public:
    }

    template <typename A, typename VEC>
    static BASE<T> rotate(A radian, const VEC& about) {
    static CONSTEXPR BASE<T> rotate(A radian, const VEC& about) {
        BASE<T> r;
        T c = std::cos(radian);
        T s = std::sin(radian);
@@ -533,7 +538,7 @@ public:
        typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
        typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
    >
    static BASE<T> eulerYXZ(Y yaw, P pitch, R roll) {
    static CONSTEXPR BASE<T> eulerYXZ(Y yaw, P pitch, R roll) {
        return eulerZYX(roll, pitch, yaw);
    }

@@ -552,7 +557,7 @@ public:
    typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
    typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
    >
    static BASE<T> eulerZYX(Y yaw, P pitch, R roll) {
    static CONSTEXPR BASE<T> eulerZYX(Y yaw, P pitch, R roll) {
        BASE<T> r;
        T cy = std::cos(yaw);
        T sy = std::sin(yaw);
@@ -630,5 +635,6 @@ public:
#undef LIKELY
#undef UNLIKELY
#undef PURE
#undef CONSTEXPR

#endif  // UI_TMATHELPERS_H_
+33 −17
Original line number Diff line number Diff line
@@ -28,6 +28,12 @@

#define PURE __attribute__((pure))

#if __cplusplus >= 201402L
#define CONSTEXPR constexpr
#else
#define CONSTEXPR
#endif

namespace android {
namespace details {
// -------------------------------------------------------------------------------------
@@ -228,6 +234,7 @@ public:
        }
        return rhs;
    }

    VECTOR<T>& operator --() {
        VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
        for (size_t i = 0; i < rhs.size(); i++) {
@@ -235,7 +242,8 @@ public:
        }
        return rhs;
    }
    VECTOR<T> operator -() const {

    CONSTEXPR VECTOR<T> operator -() const {
        VECTOR<T> r(VECTOR<T>::NO_INIT);
        VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
        for (size_t i = 0; i < r.size(); i++) {
@@ -333,7 +341,7 @@ public:
     * (the first one, BASE<T> being known).
     */
    template<typename RT>
    friend inline T PURE dot(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
    friend inline CONSTEXPR T PURE dot(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
        T r(0);
        for (size_t i = 0; i < lv.size(); i++) {
            //r = std::fma(lv[i], rv[i], r);
@@ -372,71 +380,71 @@ public:
        return lv * (T(1) / length(lv));
    }

    friend inline VECTOR<T> PURE rcp(VECTOR<T> v) {
    friend inline constexpr VECTOR<T> PURE rcp(VECTOR<T> v) {
        return T(1) / v;
    }

    friend inline VECTOR<T> PURE abs(VECTOR<T> v) {
    friend inline CONSTEXPR VECTOR<T> PURE abs(VECTOR<T> v) {
        for (size_t i=0 ; i<v.size() ; i++) {
            v[i] = std::abs(v[i]);
        }
        return v;
    }

    friend inline VECTOR<T> PURE floor(VECTOR<T> v) {
    friend inline CONSTEXPR VECTOR<T> PURE floor(VECTOR<T> v) {
        for (size_t i=0 ; i<v.size() ; i++) {
            v[i] = std::floor(v[i]);
        }
        return v;
    }

    friend inline VECTOR<T> PURE ceil(VECTOR<T> v) {
    friend inline CONSTEXPR VECTOR<T> PURE ceil(VECTOR<T> v) {
        for (size_t i=0 ; i<v.size() ; i++) {
            v[i] = std::ceil(v[i]);
        }
        return v;
    }

    friend inline VECTOR<T> PURE round(VECTOR<T> v) {
    friend inline CONSTEXPR VECTOR<T> PURE round(VECTOR<T> v) {
        for (size_t i=0 ; i<v.size() ; i++) {
            v[i] = std::round(v[i]);
        }
        return v;
    }

    friend inline VECTOR<T> PURE inversesqrt(VECTOR<T> v) {
    friend inline CONSTEXPR VECTOR<T> PURE inversesqrt(VECTOR<T> v) {
        for (size_t i=0 ; i<v.size() ; i++) {
            v[i] = T(1) / std::sqrt(v[i]);
        }
        return v;
    }

    friend inline VECTOR<T> PURE sqrt(VECTOR<T> v) {
    friend inline CONSTEXPR VECTOR<T> PURE sqrt(VECTOR<T> v) {
        for (size_t i=0 ; i<v.size() ; i++) {
            v[i] = std::sqrt(v[i]);
        }
        return v;
    }

    friend inline VECTOR<T> PURE pow(VECTOR<T> v, T p) {
    friend inline CONSTEXPR VECTOR<T> PURE pow(VECTOR<T> v, T p) {
        for (size_t i=0 ; i<v.size() ; i++) {
            v[i] = std::pow(v[i], p);
        }
        return v;
    }

    friend inline VECTOR<T> PURE saturate(const VECTOR<T>& lv) {
    friend inline CONSTEXPR VECTOR<T> PURE saturate(const VECTOR<T>& lv) {
        return clamp(lv, T(0), T(1));
    }

    friend inline VECTOR<T> PURE clamp(VECTOR<T> v, T min, T max) {
    friend inline CONSTEXPR VECTOR<T> PURE clamp(VECTOR<T> v, T min, T max) {
        for (size_t i=0 ; i< v.size() ; i++) {
            v[i] = std::min(max, std::max(min, v[i]));
        }
        return v;
    }

    friend inline VECTOR<T> PURE fma(const VECTOR<T>& lv, const VECTOR<T>& rv, VECTOR<T> a) {
    friend inline CONSTEXPR VECTOR<T> PURE fma(const VECTOR<T>& lv, const VECTOR<T>& rv, VECTOR<T> a) {
        for (size_t i=0 ; i<lv.size() ; i++) {
            //a[i] = std::fma(lv[i], rv[i], a[i]);
            a[i] += (lv[i] * rv[i]);
@@ -444,21 +452,21 @@ public:
        return a;
    }

    friend inline VECTOR<T> PURE min(const VECTOR<T>& u, VECTOR<T> v) {
    friend inline CONSTEXPR VECTOR<T> PURE min(const VECTOR<T>& u, VECTOR<T> v) {
        for (size_t i=0 ; i<v.size() ; i++) {
            v[i] = std::min(u[i], v[i]);
        }
        return v;
    }

    friend inline VECTOR<T> PURE max(const VECTOR<T>& u, VECTOR<T> v) {
    friend inline CONSTEXPR VECTOR<T> PURE max(const VECTOR<T>& u, VECTOR<T> v) {
        for (size_t i=0 ; i<v.size() ; i++) {
            v[i] = std::max(u[i], v[i]);
        }
        return v;
    }

    friend inline T PURE max(const VECTOR<T>& v) {
    friend inline CONSTEXPR T PURE max(const VECTOR<T>& v) {
        T r(std::numeric_limits<T>::lowest());
        for (size_t i=0 ; i<v.size() ; i++) {
            r = std::max(r, v[i]);
@@ -466,13 +474,20 @@ public:
        return r;
    }

    friend inline T PURE min(const VECTOR<T>& v) {
    friend inline CONSTEXPR T PURE min(const VECTOR<T>& v) {
        T r(std::numeric_limits<T>::max());
        for (size_t i=0 ; i<v.size() ; i++) {
            r = std::min(r, v[i]);
        }
        return r;
    }

    friend inline CONSTEXPR VECTOR<T> PURE apply(VECTOR<T> v, const std::function<T(T)>& f) {
        for (size_t i=0 ; i<v.size() ; i++) {
            v[i] = f(v[i]);
        }
        return v;
    }
};

/*
@@ -502,6 +517,7 @@ public:
    }
};

#undef CONSTEXPR
#undef PURE

// -------------------------------------------------------------------------------------
+25 −18
Original line number Diff line number Diff line
@@ -30,6 +30,12 @@
#   define UNLIKELY( exp )  (__builtin_expect( !!(exp), 0 ))
#endif

#if __cplusplus >= 201402L
#define CONSTEXPR constexpr
#else
#define CONSTEXPR
#endif

namespace android {

/*
@@ -50,13 +56,13 @@ class half {
    struct fp16 {
        uint16_t bits = 0;
        fp16() noexcept = default;
        explicit constexpr fp16(uint16_t bits) noexcept : bits(bits) { }
        explicit constexpr fp16(uint16_t b) noexcept : bits(b) { }
        void setS(unsigned int s) noexcept { bits = uint16_t((bits & 0x7FFF) | (s<<15)); }
        void setE(unsigned int s) noexcept { bits = uint16_t((bits & 0xE3FF) | (s<<10)); }
        void setM(unsigned int s) noexcept { bits = uint16_t((bits & 0xFC00) | (s<< 0)); }
        unsigned int getS() const noexcept { return  bits >> 15u; }
        unsigned int getE() const noexcept { return (bits >> 10u) & 0x1Fu; }
        unsigned int getM() const noexcept { return  bits         & 0x3FFu; }
        constexpr unsigned int getS() const noexcept { return  bits >> 15u; }
        constexpr unsigned int getE() const noexcept { return (bits >> 10u) & 0x1Fu; }
        constexpr unsigned int getM() const noexcept { return  bits         & 0x3FFu; }
    };
    struct fp32 {
        union {
@@ -68,14 +74,14 @@ class half {
        void setS(unsigned int s) noexcept { bits = uint32_t((bits & 0x7FFFFFFF) | (s<<31)); }
        void setE(unsigned int s) noexcept { bits = uint32_t((bits & 0x807FFFFF) | (s<<23)); }
        void setM(unsigned int s) noexcept { bits = uint32_t((bits & 0xFF800000) | (s<< 0)); }
        unsigned int getS() const noexcept { return  bits >> 31u; }
        unsigned int getE() const noexcept { return (bits >> 23u) & 0xFFu; }
        unsigned int getM() const noexcept { return  bits         & 0x7FFFFFu; }
        constexpr unsigned int getS() const noexcept { return  bits >> 31u; }
        constexpr unsigned int getE() const noexcept { return (bits >> 23u) & 0xFFu; }
        constexpr unsigned int getM() const noexcept { return  bits         & 0x7FFFFFu; }
    };

public:
    half(float v) noexcept : mBits(ftoh(v)) { }
    operator float() const noexcept { return htof(mBits); }
    CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { }
    CONSTEXPR operator float() const noexcept { return htof(mBits); }

    uint16_t getBits() const noexcept { return mBits.bits; }
    unsigned int getExponent() const noexcept { return mBits.getE(); }
@@ -83,23 +89,23 @@ public:

private:
    friend class std::numeric_limits<half>;
    friend half operator"" _hf(long double v);
    friend CONSTEXPR half operator"" _hf(long double v);

    enum Binary { binary };
    explicit constexpr half(Binary, uint16_t bits) noexcept : mBits(bits) { }
    static fp16 ftoh(float v) noexcept;
    static float htof(fp16 v) noexcept;
    static CONSTEXPR fp16 ftoh(float v) noexcept;
    static CONSTEXPR float htof(fp16 v) noexcept;
    fp16 mBits;
};

inline /* constexpr */ half::fp16 half::ftoh(float v) noexcept {
inline CONSTEXPR half::fp16 half::ftoh(float v) noexcept {
    fp16 out;
    fp32 in(v);
    if (UNLIKELY(in.getE() == 0xFF)) { // inf or nan
        out.setE(0x1F);
        out.setM(in.getM() ? 0x200 : 0);
    } else {
        int e = in.getE() - 127 + 15;
        int e = static_cast<int>(in.getE()) - 127 + 15;
        if (e >= 0x1F) {
            // overflow
            out.setE(0x31); // +/- inf
@@ -120,7 +126,7 @@ inline /* constexpr */ half::fp16 half::ftoh(float v) noexcept {
    return out;
}

inline float half::htof(half::fp16 in) noexcept {
inline CONSTEXPR float half::htof(half::fp16 in) noexcept {
    fp32 out;
    if (UNLIKELY(in.getE() == 0x1F)) { // inf or nan
        out.setE(0xFF);
@@ -132,7 +138,7 @@ inline float half::htof(half::fp16 in) noexcept {
                // (it's stupid because they can be represented as regular float)
            }
        } else {
            int e = in.getE() - 15 + 127;
            int e = static_cast<int>(in.getE()) - 15 + 127;
            unsigned int m = in.getM();
            out.setE(uint32_t(e));
            out.setM(m << 13);
@@ -142,8 +148,8 @@ inline float half::htof(half::fp16 in) noexcept {
    return out.fp;
}

inline /* constexpr */ android::half operator"" _hf(long double v) {
    return android::half(android::half::binary, android::half::ftoh(v).bits);
inline CONSTEXPR android::half operator"" _hf(long double v) {
    return android::half(android::half::binary, android::half::ftoh(static_cast<float>(v)).bits);
}

} // namespace android
@@ -197,5 +203,6 @@ public:

#undef LIKELY
#undef UNLIKELY
#undef CONSTEXPR

#endif // UI_HALF_H
+26 −22

File changed.

Preview size limit exceeded, changes collapsed.

Loading