You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
quicktex/src/util.h

156 lines
6.2 KiB
C

/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
and licenced under the public domain
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cassert>
#include <cstdint>
#include <limits>
#include <type_traits>
#include "ndebug.h"
#define UINT5_MAX 0x1FU // 31
#define UINT6_MAX 0x3FU // 63
template <typename S> constexpr void Assert5Bit(S x) {
static_assert(std::is_unsigned<S>::value);
assert(x <= UINT5_MAX);
}
template <typename S> constexpr void Assert6Bit(S x) {
static_assert(std::is_unsigned<S>::value);
assert(x <= UINT6_MAX);
}
template <typename S> constexpr auto iabs(S i) {
using O = typename std::make_unsigned<S>::type;
return (i < 0) ? static_cast<O>(-i) : static_cast<O>(i);
}
/**
* Unpacks an unsigned integer into an array of smaller integers.
* @tparam I Input data type. Must be an unsigned integral type large enough to hold C * S bits.
* @tparam O Output data type. must be an unsigned integral type large enough to hold C bits..
* @tparam S Number of bits in each value.
* @tparam C Number of values to unpack.
* @param packed Packed integer input of type I.
* @return Unpacked std::array of type O and size C.
*/
template <typename I, typename O, size_t S, size_t C> constexpr auto Unpack(I packed) noexcept(ndebug) {
// type checking
static_assert(std::is_unsigned<I>::value, "Packed input type must be unsigned");
static_assert(std::is_unsigned<O>::value, "Unpacked output type must be unsigned");
static_assert(std::numeric_limits<I>::digits >= (C * S), "Packed input type must be big enough to represent the number of bits multiplied by count");
static_assert(std::numeric_limits<O>::digits >= S, "Unpacked output type must be big enough to represent the number of bits");
constexpr O mask = (1U << S) - 1U; // maximum value representable by S bits
std::array<O, C> vals; // output values array of size C
for (unsigned i = 0; i < C; i++) {
vals[i] = static_cast<O>(packed >> (i * S)) & mask;
assert(vals[i] <= mask);
}
return vals;
}
/**
* Packs an array of unsigned integers into a single integer.
* @tparam I Input data type. Must be an unsigned integral type large enough to hold C bits.
* @tparam O Output data type. must be an unsigned integral type large enough to hold C * S bits.
* @tparam S Number of bits in each value.
* @tparam C Number of values to unpack.
* @param vals Unpacked std::array of type I and size C.
* @return Packed integer input of type O.
*/
template <typename I, typename O, size_t S, size_t C> constexpr auto Pack(const std::array<I, C> &vals) noexcept(ndebug) {
// type checking
static_assert(std::is_unsigned<I>::value, "Unpacked input type must be unsigned");
static_assert(std::is_unsigned<O>::value, "Packed output type must be unsigned");
static_assert(std::numeric_limits<I>::digits >= S, "Unpacked input type must be big enough to represent the number of bits");
static_assert(std::numeric_limits<O>::digits >= (C * S), "Packed output type must be big enough to represent the number of bits multiplied by count");
O packed = 0; // output value of type O
for (unsigned i = 0; i < C; i++) {
assert(vals[i] <= (1U << S) - 1U);
packed |= static_cast<O>(vals[i]) << (i * S);
}
assert(packed <= (static_cast<O>(1U) << (C * S)) - 1U);
return packed;
}
template <size_t Size, int Op(int)> constexpr std::array<uint8_t, Size> ExpandArray() {
std::array<uint8_t, Size> res;
for (int i = 0; i < Size; i++) { res[i] = Op(i); }
return res;
}
template <typename S> constexpr S scale8To5(S v) {
v = v * 31 + 128;
return static_cast<S>((v + (v >> 8)) >> 8);
}
template <typename S> constexpr S scale8To6(S v) {
v = v * 63 + 128;
return static_cast<S>((v + (v >> 8)) >> 8);
}
template <typename S> constexpr S scale5To8(S v) {
Assert5Bit(v);
assert(v <= UINT5_MAX);
return static_cast<S>((v << 3) | (v >> 2));
}
template <typename S> constexpr S scale6To8(S v) {
Assert6Bit(v);
return static_cast<S>((v << 2) | (v >> 4));
}
template <typename S> constexpr S maximum(S a, S b) { return (a > b) ? a : b; }
template <typename S> constexpr S maximum(S a, S b, S c) { return maximum(maximum(a, b), c); }
template <typename S> constexpr S maximum(S a, S b, S c, S d) { return maximum(maximum(maximum(a, b), c), d); }
template <typename S> constexpr S minimum(S a, S b) { return (a < b) ? a : b; }
template <typename S> constexpr S minimum(S a, S b, S c) { return minimum(minimum(a, b), c); }
template <typename S> constexpr S minimum(S a, S b, S c, S d) { return minimum(minimum(minimum(a, b), c), d); }
template <typename T> constexpr T square(T a) { return a * a; }
constexpr float clampf(float value, float low, float high) {
if (value < low)
value = low;
else if (value > high)
value = high;
return value;
}
constexpr uint8_t clamp255(int32_t i) { return static_cast<uint8_t>((static_cast<unsigned int>(i) & 0xFFFFFF00U) ? (~(i >> 31)) : i); }
template <typename S> constexpr S clamp(S value, S low, S high) { return (value < low) ? low : ((value > high) ? high : value); }
constexpr int32_t clampi(int32_t value, int32_t low, int32_t high) {
if (value < low)
value = low;
else if (value > high)
value = high;
return value;
}
constexpr int squarei(int a) { return a * a; }
constexpr int absi(int a) { return (a < 0) ? -a : a; }
template <typename F> constexpr F lerp(F a, F b, F s) { return a + (b - a) * s; }