mirror of
https://github.com/drewcassidy/quicktex.git
synced 2024-09-13 06:37:34 +00:00
Refactor pack() and unpack()
This commit is contained in:
parent
dae507acc9
commit
3b7164ffba
@ -72,9 +72,9 @@ OldColor OldColor::PreciseRound565(Vector4 &v) {
|
||||
int trial_b = (int)(v[2] * UINT5_MAX);
|
||||
|
||||
// clamp to prevent weirdness with slightly out of bounds float values
|
||||
uint8_t r = (uint8_t)clampi(trial_r, 0, UINT5_MAX);
|
||||
uint8_t g = (uint8_t)clampi(trial_g, 0, UINT6_MAX);
|
||||
uint8_t b = (uint8_t)clampi(trial_b, 0, UINT5_MAX);
|
||||
uint8_t r = (uint8_t)clamp<int>(trial_r, 0, UINT5_MAX);
|
||||
uint8_t g = (uint8_t)clamp<int>(trial_g, 0, UINT6_MAX);
|
||||
uint8_t b = (uint8_t)clamp<int>(trial_b, 0, UINT5_MAX);
|
||||
|
||||
// increment each channel if above the rounding point
|
||||
r += v[0] > Midpoints5bit[r];
|
||||
|
@ -30,6 +30,8 @@ namespace quicktex {
|
||||
|
||||
template <typename T, size_t N> class Vec {
|
||||
public:
|
||||
typedef T value_type;
|
||||
|
||||
// region constructors
|
||||
/**
|
||||
* Create a vector from an intializer list
|
||||
@ -106,6 +108,7 @@ template <typename T, size_t N> class Vec {
|
||||
T *end() { return &_c[N]; }
|
||||
const T *begin() const { return &_c[0]; }
|
||||
const T *end() const { return &_c[N]; }
|
||||
size_t size() const { return N; }
|
||||
|
||||
// endregion
|
||||
|
||||
@ -158,24 +161,6 @@ template <typename T, size_t N> class Vec {
|
||||
|
||||
template <typename OI> void copy(OI output_iterator) const { std::copy(begin(), end(), output_iterator); }
|
||||
|
||||
template <typename P = T, typename W = size_t>
|
||||
requires std::is_unsigned_v<P> && std::is_integral_v<T>
|
||||
P pack(const Vec<W, N> &widths) const {
|
||||
assert((sizeof(P) * 8) >= (size_t)std::accumulate(widths.begin(), widths.end(), 0));
|
||||
|
||||
P packed = 0;
|
||||
|
||||
for (unsigned i = 0; i < N; i++) {
|
||||
T val = at(i);
|
||||
if constexpr (std::is_signed_v<T>) { val &= ((1 << widths[i]) - 1); } // mask out upper bits of signed vals
|
||||
|
||||
assert(val < (1 << widths[i]));
|
||||
|
||||
packed = (packed << widths[i]) | val;
|
||||
}
|
||||
return packed;
|
||||
}
|
||||
|
||||
T sum() const { return std::accumulate(begin(), end(), T{0}); }
|
||||
|
||||
T dot(const Vec &rhs) const {
|
||||
@ -200,7 +185,7 @@ template <typename T, size_t N> class Vec {
|
||||
}
|
||||
|
||||
protected:
|
||||
T _c[N]; // internal array of components
|
||||
std::array<T, N> _c; // internal array of components
|
||||
|
||||
template <typename Op> static inline Vec map(const Vec &lhs, Op f) {
|
||||
Vec r;
|
||||
|
@ -116,14 +116,6 @@ UTEST(Vec_int, subscript) {
|
||||
ASSERT_EQ(a[2], 4);
|
||||
}
|
||||
|
||||
UTEST(Vec_int, pack) {
|
||||
auto a = Vec<uint16_t, 3>{0x1F, 0x2A, 0x01};
|
||||
auto w = Vec<int, 3>{5, 6, 5};
|
||||
auto result = a.pack(w);
|
||||
|
||||
ASSERT_EQ(result, 0xFD41);
|
||||
}
|
||||
|
||||
UTEST(Vec_int, copy) {
|
||||
std::array<int, 4> arr{1, 3, 1, 2};
|
||||
Vec<int, 4> a(arr);
|
||||
|
@ -19,29 +19,33 @@
|
||||
|
||||
#include "BC1Block.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../../util.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
uint16_t BC1Block::GetColor0Raw() const { return Pack<uint8_t, uint16_t, 8, EndpointSize>(_color0); }
|
||||
uint16_t BC1Block::GetColor1Raw() const { return Pack<uint8_t, uint16_t, 8, EndpointSize>(_color1); }
|
||||
uint16_t BC1Block::GetColor0Raw() const { return pack<uint16_t>(_color0, 8); }
|
||||
uint16_t BC1Block::GetColor1Raw() const { return pack<uint16_t>(_color1, 8); }
|
||||
|
||||
void BC1Block::SetColor0Raw(uint16_t c) { _color0 = Unpack<uint16_t, uint8_t, 8, EndpointSize>(c); }
|
||||
void BC1Block::SetColor1Raw(uint16_t c) { _color1 = Unpack<uint16_t, uint8_t, 8, EndpointSize>(c); }
|
||||
void BC1Block::SetColor0Raw(uint16_t c) { _color0 = unpack<uint8_t, EndpointSize>(c, 8); }
|
||||
void BC1Block::SetColor1Raw(uint16_t c) { _color1 = unpack<uint8_t, EndpointSize>(c, 8); }
|
||||
|
||||
BC1Block::SelectorArray BC1Block::GetSelectors() const { return MapArray(_selectors, Unpack<uint8_t, uint8_t, SelectorBits, Width>); }
|
||||
|
||||
void BC1Block::SetSelectors(const BC1Block::SelectorArray& unpacked) {
|
||||
for (unsigned y = 0; y < (unsigned)Height; y++) {
|
||||
if (std::any_of(unpacked[y].begin(), unpacked[y].end(), [](uint8_t i) { return i > SelectorMax; }))
|
||||
throw std::invalid_argument("Selector value out of bounds.");
|
||||
}
|
||||
_selectors = MapArray(unpacked, Pack<uint8_t, uint8_t, SelectorBits, Width>);
|
||||
BC1Block::SelectorArray BC1Block::GetSelectors() const {
|
||||
return MapArray(_selectors, [](auto row) { return unpack<uint8_t, Width>(row, SelectorBits); });
|
||||
}
|
||||
|
||||
bool BC1Block::operator==(const BC1Block& Rhs) const { return _color0 == Rhs._color0 && _color1 == Rhs._color1 && _selectors == Rhs._selectors; }
|
||||
void BC1Block::SetSelectors(const BC1Block::SelectorArray& unpacked) {
|
||||
// for (unsigned y = 0; y < (unsigned)Height; y++) {
|
||||
// if (std::any_of(unpacked[y].begin(), unpacked[y].end(), [](uint8_t i) { return i > SelectorMax; }))
|
||||
// throw std::invalid_argument("Selector value out of bounds.");
|
||||
// }
|
||||
_selectors = MapArray(unpacked, [](auto row) { return pack<uint8_t>(row, SelectorBits, true); });
|
||||
}
|
||||
|
||||
bool BC1Block::operator==(const BC1Block& Rhs) const {
|
||||
return _color0 == Rhs._color0 && _color1 == Rhs._color1 && _selectors == Rhs._selectors;
|
||||
}
|
||||
bool BC1Block::operator!=(const BC1Block& Rhs) const { return !(Rhs == *this); }
|
||||
|
||||
} // namespace quicktex::s3tc
|
||||
|
@ -753,7 +753,7 @@ void BC1Encoder::FindSelectors(EncodeResults &result, const CBlock &pixels, Erro
|
||||
Vector4Int pixel_vector = Vector4Int::FromColorRGB(pixels.Get(i));
|
||||
auto diff = pixel_vector - color_vectors[0];
|
||||
float sel_f = (float)diff.Dot(axis) * f + 0.5f;
|
||||
uint8_t sel = (uint8_t)clampi((int)sel_f, 1, 3);
|
||||
uint8_t sel = (uint8_t)clamp<int>((int)sel_f, 1, 3);
|
||||
|
||||
unsigned err0 = (color_vectors[sel - 1] - pixel_vector).SqrMag();
|
||||
unsigned err1 = (color_vectors[sel] - pixel_vector).SqrMag();
|
||||
|
@ -71,10 +71,10 @@ template <size_t B, size_t N> MatchListPtr SingleColorTable(InterpolatorPtr inte
|
||||
value = (B == 5) ? interpolator->Interpolate5(high, low) : interpolator->Interpolate6(high, low);
|
||||
}
|
||||
|
||||
unsigned new_error = iabs(value - (int)i);
|
||||
unsigned new_error = abs(value - (int)i);
|
||||
|
||||
// We only need to factor in 3% error in BC1 ideal mode.
|
||||
if (ideal) new_error += (iabs(high8 - (int)low8) * 3) / 100;
|
||||
if (ideal) new_error += (abs(high8 - (int)low8) * 3) / 100;
|
||||
|
||||
if ((new_error < error) || (new_error == error && low == high)) {
|
||||
assert(new_error <= UINT8_MAX);
|
||||
|
@ -27,9 +27,9 @@
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
BC4Block::SelectorArray BC4Block::GetSelectors() const {
|
||||
auto packed = Pack<uint8_t, uint64_t, 8, SelectorSize>(_selectors);
|
||||
auto rows = Unpack<uint64_t, uint16_t, SelectorBits * Width, Height>(packed);
|
||||
return MapArray(rows, Unpack<uint16_t, uint8_t, SelectorBits, Width>);
|
||||
auto packed = pack<uint64_t>(_selectors, 8);
|
||||
auto rows = unpack<uint16_t, Height>(packed, SelectorBits * Width);
|
||||
return MapArray(rows, [](auto row) { return unpack<uint8_t, Width>(row, SelectorBits); });
|
||||
}
|
||||
|
||||
void BC4Block::SetSelectors(const BC4Block::SelectorArray& unpacked) {
|
||||
@ -37,9 +37,9 @@ void BC4Block::SetSelectors(const BC4Block::SelectorArray& unpacked) {
|
||||
if (std::any_of(unpacked[y].begin(), unpacked[y].end(), [](uint8_t i) { return i > SelectorMax; }))
|
||||
throw std::invalid_argument("Selector value out of bounds.");
|
||||
}
|
||||
auto rows = MapArray(unpacked, Pack<uint8_t, uint16_t, SelectorBits, Width>);
|
||||
auto packed = Pack<uint16_t, uint64_t, SelectorBits * Width, Height>(rows);
|
||||
_selectors = Unpack<uint64_t, uint8_t, 8, SelectorSize>(packed);
|
||||
auto rows = MapArray(unpacked, [](auto r) { return pack<uint16_t>(r, SelectorBits); });
|
||||
auto packed = pack<uint64_t>(rows, SelectorBits * Width);
|
||||
_selectors = unpack<uint8_t, SelectorSize>(packed, 8);
|
||||
}
|
||||
|
||||
std::array<uint8_t, 8> BC4Block::GetValues6() const {
|
||||
@ -64,6 +64,8 @@ std::array<uint8_t, 8> BC4Block::GetValues8() const {
|
||||
static_cast<uint8_t>((alpha0 + alpha1 * 6) / 7)};
|
||||
}
|
||||
|
||||
bool BC4Block::operator==(const BC4Block& Rhs) const { return alpha0 == Rhs.alpha0 && alpha1 == Rhs.alpha1 && _selectors == Rhs._selectors; }
|
||||
bool BC4Block::operator==(const BC4Block& Rhs) const {
|
||||
return alpha0 == Rhs.alpha0 && alpha1 == Rhs.alpha1 && _selectors == Rhs._selectors;
|
||||
}
|
||||
bool BC4Block::operator!=(const BC4Block& Rhs) const { return !(Rhs == *this); }
|
||||
} // namespace quicktex::s3tc
|
||||
|
360
quicktex/util.h
360
quicktex/util.h
@ -22,6 +22,7 @@
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
@ -35,6 +36,64 @@
|
||||
|
||||
namespace quicktex {
|
||||
|
||||
// std::ranges::range is currently not usable by default in libc++
|
||||
template <class T>
|
||||
concept range = requires(T &t) {
|
||||
std::ranges::begin(t);
|
||||
std::ranges::end(t);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
concept sized_range = range<T> && requires(T &t) { std::size(t); };
|
||||
|
||||
template <class T>
|
||||
requires range<T>
|
||||
size_t distance(T range) {
|
||||
return std::distance(range.begin(), range.end());
|
||||
}
|
||||
|
||||
template <typename T> class const_iterator {
|
||||
public:
|
||||
typedef ssize_t difference_type;
|
||||
typedef T value_type;
|
||||
|
||||
const_iterator() : _value(T{}), _index(0) {}
|
||||
const_iterator(T value, size_t index = 0) : _value(value), _index(index) {}
|
||||
|
||||
const_iterator &operator++() {
|
||||
_index++;
|
||||
return *this;
|
||||
}
|
||||
const_iterator operator++(int) {
|
||||
const_iterator old = *this;
|
||||
_index++;
|
||||
return old;
|
||||
}
|
||||
const_iterator &operator--() {
|
||||
_index++;
|
||||
return *this;
|
||||
}
|
||||
const_iterator operator--(int) {
|
||||
const_iterator old = *this;
|
||||
_index++;
|
||||
return old;
|
||||
}
|
||||
|
||||
T operator*() const { return _value; }
|
||||
|
||||
ssize_t operator-(const_iterator rhs) const { return (ssize_t)_index - rhs._index; }
|
||||
const_iterator operator+(size_t rhs) const { return const_iterator(rhs + _index); }
|
||||
const_iterator operator-(size_t rhs) const { return const_iterator(rhs - _index); }
|
||||
|
||||
friend bool operator==(const const_iterator &lhs, const const_iterator &rhs) {
|
||||
return (lhs._value == rhs._value) && (lhs._index == rhs._index);
|
||||
}
|
||||
|
||||
private:
|
||||
T _value;
|
||||
size_t _index;
|
||||
};
|
||||
|
||||
template <typename S, size_t N> S scale_from_8(S v) {
|
||||
static_assert(N < 8);
|
||||
assert(v < (1 << 8));
|
||||
@ -61,70 +120,258 @@ template <typename S, size_t N> S scale_to_8(S v) {
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename S> constexpr auto iabs(S i) {
|
||||
static_assert(!std::is_unsigned<S>::value);
|
||||
using O = typename std::make_unsigned<S>::type;
|
||||
return (i < 0) ? static_cast<O>(-i) : static_cast<O>(i);
|
||||
/**
|
||||
* Unpacks an unsigned integer into a range of smaller integers.
|
||||
* @param packed value to unpack
|
||||
* @param begin destination start iterator
|
||||
* @param end destination end iterator
|
||||
* @param widths widths iterator. values are in bits
|
||||
* @param little_endian if the input has the first element in the least significant place
|
||||
* @return the total number of bits unpacked
|
||||
*/
|
||||
template <typename P, typename OI, typename WI>
|
||||
requires std::unsigned_integral<P> && std::output_iterator<OI, P> && std::forward_iterator<WI>
|
||||
size_t unpack_into(P packed, OI begin, OI end, WI widths, bool little_endian = true) {
|
||||
using U = std::remove_cvref_t<decltype(*begin)>;
|
||||
if (little_endian) {
|
||||
// first element is in the least significant place of packed
|
||||
|
||||
unsigned offset = 0;
|
||||
while (begin < end) {
|
||||
auto w = *(widths++);
|
||||
assert(w <= std::numeric_limits<U>::digits);
|
||||
U result{0};
|
||||
|
||||
auto mask = ((1 << w) - 1);
|
||||
result = (packed >> offset) & mask;
|
||||
|
||||
*(begin++) = result;
|
||||
offset += w; // increment offset
|
||||
}
|
||||
|
||||
assert(offset <= std::numeric_limits<P>::digits); // detect an overflow condition
|
||||
return offset;
|
||||
} else {
|
||||
// first element is in the most significant place of packed
|
||||
|
||||
// with non-constant width, we either need to iterate backwards or
|
||||
// add up all the widths beforehand to know where to begin
|
||||
unsigned total_offset = std::accumulate(widths, widths + std::distance(begin, end), 0);
|
||||
assert(total_offset <= std::numeric_limits<P>::digits); // detect an overflow condition
|
||||
|
||||
unsigned offset = total_offset;
|
||||
while (begin < end) {
|
||||
auto w = *(widths++);
|
||||
offset -= w; // decrement offset
|
||||
assert(w < std::numeric_limits<U>::digits);
|
||||
U result{0};
|
||||
|
||||
auto mask = ((1 << w) - 1);
|
||||
result = (packed >> offset) & mask;
|
||||
|
||||
*(begin++) = result;
|
||||
}
|
||||
|
||||
return total_offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 * N 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.
|
||||
* Unpacks an unsigned integer into a range of smaller integers.
|
||||
* @param packed value to unpack
|
||||
* @param dest destination range
|
||||
* @param widths widths range. values are in bits
|
||||
* @param little_endian if the input has the first element in the least significant place
|
||||
* @return the total number of bits unpacked
|
||||
*/
|
||||
template <typename I, typename O, size_t S, size_t C> constexpr std::array<O, C> Unpack(I packed) {
|
||||
// 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 N 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;
|
||||
template <typename P, typename OR, typename WR>
|
||||
requires std::unsigned_integral<P> && range<OR> && range<WR>
|
||||
size_t unpack_into(P packed, OR &dest, const WR &widths, bool little_endian = true) {
|
||||
assert(distance(widths) == distance(dest));
|
||||
return unpack_into(packed, dest.begin(), dest.end(), widths.begin(), little_endian = true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 * N 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.
|
||||
* Unpacks an unsigned integer into a range of smaller integers.
|
||||
* @param packed value to unpack
|
||||
* @param begin destination start iterator
|
||||
* @param end destination end iterator
|
||||
* @param width width of each packed element in bits
|
||||
* @param little_endian if the input has the first element in the least significant place
|
||||
* @return the total number of bits unpacked
|
||||
*/
|
||||
template <typename I, typename O, size_t S, size_t C> constexpr O Pack(const std::array<I, C> &vals) {
|
||||
// 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");
|
||||
template <typename P, typename OI>
|
||||
requires std::unsigned_integral<P> && std::output_iterator<OI, P>
|
||||
size_t unpack_into(P packed, OI begin, OI end, size_t width, bool little_endian = true) {
|
||||
return unpack_into(packed, begin, end, const_iterator(width), little_endian);
|
||||
}
|
||||
|
||||
O packed = 0; // output value of type O
|
||||
/**
|
||||
* Unpacks an unsigned integer into a range of smaller integers.
|
||||
* @param packed value to unpack
|
||||
* @param dest destination range
|
||||
* @param width width of each packed element in bits
|
||||
* @param little_endian if the input has the first element in the least significant place
|
||||
* @return the total number of bits unpacked
|
||||
*/
|
||||
template <typename P, typename OR>
|
||||
requires std::unsigned_integral<P> && range<OR>
|
||||
size_t unpack_into(P packed, OR &dest, size_t width, bool little_endian = true) {
|
||||
return unpack_into(packed, dest.begin(), dest.end(), const_iterator(width), little_endian = true);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < C; i++) {
|
||||
assert(vals[i] <= (1U << S) - 1U);
|
||||
packed |= static_cast<O>(vals[i]) << (i * S);
|
||||
/**
|
||||
* Unpacks an unsigned integer into an array of smaller integers
|
||||
* @tparam U unpacked data type
|
||||
* @tparam N number of values to unpack
|
||||
* @param packed value to unpack
|
||||
* @param width width of each packed element in bits
|
||||
* @param little_endian if the input has the first element in the least significant place
|
||||
* @return an array of unpacked values
|
||||
*/
|
||||
template <typename U, size_t N, typename P>
|
||||
requires std::unsigned_integral<P>
|
||||
std::array<U, N> unpack(P packed, size_t width, bool little_endian = true) {
|
||||
std::array<U, N> unpacked;
|
||||
unpack_into(packed, unpacked, width, little_endian);
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks an unsigned integer into an array of smaller integers
|
||||
* @tparam U unpacked data type
|
||||
* @tparam N number of values to unpack
|
||||
* @param packed value to unpack
|
||||
* @param widths widths iterator. values are in bits
|
||||
* @param little_endian if the input has the first element in the least significant place
|
||||
* @return an array of unpacked values
|
||||
*/
|
||||
template <typename U, size_t N, typename P, typename WI>
|
||||
requires std::unsigned_integral<P> && std::forward_iterator<WI>
|
||||
std::array<U, N> unpack(P packed, WI widths, bool little_endian = true) {
|
||||
std::array<U, N> unpacked;
|
||||
unpack_into(packed, unpacked, widths, little_endian);
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks an unsigned integer into an array of smaller integers
|
||||
* @tparam U unpacked data type
|
||||
* @param packed value to unpack
|
||||
* @param widths widths array. values are in bits
|
||||
* @param little_endian if the input has the first element in the least significant place
|
||||
* @return an array of unpacked values
|
||||
*/
|
||||
template <typename U, size_t N, typename P>
|
||||
requires std::unsigned_integral<P>
|
||||
std::array<U, N> unpack(P packed, const std::array<size_t, N> &widths, bool little_endian = true) {
|
||||
return unpack<U, N>(packed, widths.begin(), little_endian);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks an unsigned integer into an array of smaller integers
|
||||
* @tparam U unpacked data type
|
||||
* @tparam N number of values to unpack
|
||||
* @param packed value to unpack
|
||||
* @param widths widths range. values are in bits
|
||||
* @param little_endian if the input has the first element in the least significant place
|
||||
* @return an array of unpacked values
|
||||
*/
|
||||
template <typename U, size_t N, typename P, typename WR>
|
||||
requires std::unsigned_integral<P> && sized_range<WR>
|
||||
std::array<U, N> unpack(P packed, const WR &widths, bool little_endian = true) {
|
||||
assert(widths.size() >= N);
|
||||
return unpack<U, N>(packed, widths.begin(), little_endian);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs an iterable of integers into a single integer.
|
||||
* @tparam II input iterator type
|
||||
* @tparam WI width iterator type
|
||||
* @tparam P Output data type. must be an unsigned integral type large enough to hold all input values
|
||||
* @param start start iterator
|
||||
* @param end end iterator
|
||||
* @param widths width iterator. must be at least as large as the input data
|
||||
* @param little_endian if the output value should have the first element in the least significant place
|
||||
* of the output or not
|
||||
* @return Packed integer of type P.
|
||||
*/
|
||||
template <typename P, typename II, typename WI>
|
||||
requires std::unsigned_integral<P> && std::input_iterator<II> && std::input_iterator<WI>
|
||||
inline constexpr P pack(II start, II end, WI widths, bool little_endian = true) {
|
||||
P packed = 0;
|
||||
unsigned offset = 0;
|
||||
while (start < end) {
|
||||
auto val = static_cast<P>(*(start++));
|
||||
auto w = *(widths++);
|
||||
|
||||
val &= ((1 << w) - 1);
|
||||
assert(val < (1 << w)); // ensure value can fit in W bits
|
||||
|
||||
if (little_endian) {
|
||||
packed |= static_cast<P>(val) << offset; // first element is in the least significant place of packed
|
||||
} else {
|
||||
packed = (packed << w) | static_cast<P>(val); // first element is in the most significant place of packed
|
||||
}
|
||||
|
||||
offset += w; // increment offset
|
||||
}
|
||||
|
||||
assert(packed <= (static_cast<O>(1U) << (C * S)) - 1U);
|
||||
assert(offset <= std::numeric_limits<P>::digits); // detect an overflow condition
|
||||
return packed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs an iterable of integers into a single integer.
|
||||
* @tparam IR input range type
|
||||
* @tparam WR width range type
|
||||
* @tparam P Output data type. must be an unsigned integral type large enough to hold all input values
|
||||
* @param r range of values to pack
|
||||
* @param widths range of widths to pack with. must be at least as large as r
|
||||
* @param little_endian if the output value should have the first element in the least significant place
|
||||
* of the output or not
|
||||
* @return Packed integer of type P.
|
||||
*/
|
||||
template <typename P, typename IR, typename WR>
|
||||
requires std::unsigned_integral<P> && range<IR> && range<WR>
|
||||
inline constexpr P pack(IR r, WR widths, bool little_endian = true) {
|
||||
assert(distance(widths) == distance(r));
|
||||
return pack<P>(r.begin(), r.end(), widths.start(), little_endian);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs an iterable of integers into a single integer.
|
||||
* @tparam II input iterator type
|
||||
* @tparam P Output data type. must be an unsigned integral type large enough to hold all input values
|
||||
* @param start start iterator
|
||||
* @param end end iterator
|
||||
* @param width Number of bits in each value
|
||||
* @param little_endian if the output value should have the first element in the least significant place
|
||||
* of the output or not
|
||||
* @return Packed integer of type P.
|
||||
*/
|
||||
template <typename P, typename II>
|
||||
requires std::unsigned_integral<P> && std::input_iterator<II>
|
||||
inline constexpr P pack(II start, II end, size_t width, bool little_endian = true) {
|
||||
return pack<P>(start, end, const_iterator(width), little_endian);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs a range of integers into a single integer.
|
||||
* @tparam IR range type
|
||||
* @tparam P Output data type. must be an unsigned integral type large enough to hold all input values
|
||||
* @param r range of values to pack
|
||||
* @param width Number of bits in each value
|
||||
* @param little_endian if the output value should have the first element in the least significant place
|
||||
* of the output or not
|
||||
* @return Packed integer of type P.
|
||||
*/
|
||||
template <typename P, typename IR>
|
||||
requires std::unsigned_integral<P> && range<IR>
|
||||
inline constexpr P pack(IR r, size_t width, bool little_endian = true) {
|
||||
return pack<P>(r.begin(), r.end(), const_iterator(width), little_endian);
|
||||
}
|
||||
|
||||
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); }
|
||||
@ -133,7 +380,7 @@ template <size_t Size, int Op(int)> constexpr std::array<uint8_t, Size> ExpandAr
|
||||
|
||||
template <typename Seq, typename Fn> constexpr auto MapArray(const Seq &input, Fn op) {
|
||||
using I = typename Seq::value_type;
|
||||
using O = decltype(op(std::declval<I>()));
|
||||
using O = decltype(op(I{}));
|
||||
constexpr size_t N = std::tuple_size<Seq>::value;
|
||||
|
||||
std::array<O, N> output;
|
||||
@ -165,18 +412,8 @@ template <typename S> constexpr S clamp(S value, S low, S high) {
|
||||
if (value > high) return high;
|
||||
return 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;
|
||||
}
|
||||
|
||||
template <typename T> std::enable_if<std::is_unsigned_v<T>, T> abs(const T &sval) { return sval; }
|
||||
template <typename T> std::enable_if<std::is_signed_v<T> && std::is_arithmetic_v<T>, T> abs(const T &a) {
|
||||
return (a < 0) ? -a : a;
|
||||
}
|
||||
using std::abs; // abs overload for builtin types
|
||||
using xsimd::abs; // provides overload for abs<xsimd::batch>
|
||||
|
||||
template <typename F> constexpr F lerp(F a, F b, F s) { return a + (b - a) * s; }
|
||||
@ -210,4 +447,5 @@ template <> struct next_size<int32_t> : Tag<int64_t> {};
|
||||
template <> struct next_size<uint8_t> : Tag<uint16_t> {};
|
||||
template <> struct next_size<uint16_t> : Tag<uint32_t> {};
|
||||
template <> struct next_size<uint32_t> : Tag<uint64_t> {};
|
||||
|
||||
} // namespace quicktex
|
Loading…
Reference in New Issue
Block a user