mirror of
https://github.com/drewcassidy/quicktex.git
synced 2024-09-13 06:37:34 +00:00
BC1Block/BC4Block cleanup
This commit is contained in:
parent
e1739b99d3
commit
3d91ac2bed
44
quicktex/s3tc/bc1/BC1Block.cpp
Normal file
44
quicktex/s3tc/bc1/BC1Block.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#include "BC1Block.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#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); }
|
||||
|
||||
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); }
|
||||
|
||||
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>);
|
||||
}
|
||||
|
||||
} // namespace quicktex::s3tc
|
@ -20,23 +20,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <utility>
|
||||
|
||||
#include "../../Color.h"
|
||||
#include "../../util.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
class alignas(8) BC1Block {
|
||||
public:
|
||||
static constexpr int Width = 4;
|
||||
static constexpr int Height = 4;
|
||||
static constexpr size_t Width = 4;
|
||||
static constexpr size_t Height = 4;
|
||||
|
||||
static constexpr size_t EndpointSize = 2; // size of a 5:6:5 endpoint in bytes
|
||||
static constexpr size_t SelectorSize = 4; // size of selector array in bytes
|
||||
static constexpr size_t SelectorBits = 2; // size of a selector in bits
|
||||
static constexpr uint8_t SelectorMax = (1 << SelectorBits) - 1; // maximum value of a selector
|
||||
|
||||
using SelectorArray = std::array<std::array<uint8_t, Width>, Height>;
|
||||
using ColorPair = std::pair<Color, Color>;
|
||||
|
||||
private:
|
||||
std::array<uint8_t, EndpointSize> _color0;
|
||||
std::array<uint8_t, EndpointSize> _color1;
|
||||
std::array<uint8_t, SelectorSize> _selectors;
|
||||
|
||||
public:
|
||||
/// Create a new BC1Block
|
||||
constexpr BC1Block() {
|
||||
static_assert(sizeof(BC1Block) == 8);
|
||||
static_assert(sizeof(std::array<BC1Block, 10>) == 8 * 10);
|
||||
@ -45,29 +56,47 @@ class alignas(8) BC1Block {
|
||||
_selectors = {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BC1Block
|
||||
* @param color0 first endpoint color
|
||||
* @param color1 second endpoint color
|
||||
* @param selectors the selectors as a 4x4 list of integers, between 0 and 3 inclusive.
|
||||
*/
|
||||
BC1Block(Color color0, Color color1, const SelectorArray& selectors) {
|
||||
SetColor0(color0);
|
||||
SetColor1(color1);
|
||||
SetSelectors(selectors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BC1Block
|
||||
* @param ep0 first endpoint
|
||||
* @param ep1 second endpoint
|
||||
* @param selectors the selectors as a 4x4 list of integers, between 0 and 3 inclusive.
|
||||
*/
|
||||
BC1Block(uint16_t ep0, uint16_t ep1, const SelectorArray& selectors) {
|
||||
SetColor0Raw(ep0);
|
||||
SetColor1Raw(ep1);
|
||||
SetSelectors(selectors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BC1Block
|
||||
* @param ep0 first endpoint
|
||||
* @param ep1 second endpoint
|
||||
* @param solid_mask single byte mask to use for each row
|
||||
*/
|
||||
BC1Block(uint16_t ep0, uint16_t ep1, uint8_t solid_mask) {
|
||||
SetColor0Raw(ep0);
|
||||
SetColor1Raw(ep1);
|
||||
_selectors.fill(solid_mask);
|
||||
}
|
||||
|
||||
constexpr uint16_t GetColor0Raw() const { return Pack<uint8_t, uint16_t, 8, EndpointSize>(_color0); }
|
||||
constexpr uint16_t GetColor1Raw() const { return Pack<uint8_t, uint16_t, 8, EndpointSize>(_color1); }
|
||||
uint16_t GetColor0Raw() const;
|
||||
uint16_t GetColor1Raw() const;
|
||||
|
||||
void SetColor0Raw(uint16_t c) { _color0 = Unpack<uint16_t, uint8_t, 8, EndpointSize>(c); }
|
||||
void SetColor1Raw(uint16_t c) { _color1 = Unpack<uint16_t, uint8_t, 8, EndpointSize>(c); }
|
||||
void SetColor0Raw(uint16_t c);
|
||||
void SetColor1Raw(uint16_t c);
|
||||
|
||||
Color GetColor0() const { return Color::Unpack565(GetColor0Raw()); }
|
||||
Color GetColor1() const { return Color::Unpack565(GetColor1Raw()); }
|
||||
@ -80,19 +109,18 @@ class alignas(8) BC1Block {
|
||||
SetColor1(cs.second);
|
||||
}
|
||||
|
||||
constexpr SelectorArray GetSelectors() const { return MapArray(_selectors, Unpack<uint8_t, uint8_t, SelectorBits, Width>); }
|
||||
/**
|
||||
* Get this block's selectors
|
||||
* @return a 4x4 array of integers between 0 and 3 inclusive
|
||||
*/
|
||||
SelectorArray GetSelectors() const;
|
||||
|
||||
void SetSelectors(const SelectorArray& unpacked) { _selectors = MapArray(unpacked, Pack<uint8_t, uint8_t, SelectorBits, Width>); }
|
||||
/**
|
||||
* Set this block's selectors
|
||||
* @param unpacked a 4x4 array of integers between 0 and 3 inclusive
|
||||
*/
|
||||
void SetSelectors(const SelectorArray& unpacked);
|
||||
|
||||
constexpr bool Is3Color() const { return GetColor0Raw() <= GetColor1Raw(); }
|
||||
|
||||
constexpr static inline size_t EndpointSize = 2; // in bytes
|
||||
constexpr static inline size_t SelectorSize = 4; // in bytes
|
||||
constexpr static inline uint8_t SelectorBits = 2; // in bits
|
||||
|
||||
private:
|
||||
std::array<uint8_t, EndpointSize> _color0;
|
||||
std::array<uint8_t, EndpointSize> _color1;
|
||||
std::array<uint8_t, SelectorSize> _selectors;
|
||||
};
|
||||
} // namespace quicktex::s3tc
|
66
quicktex/s3tc/bc4/BC4Block.cpp
Normal file
66
quicktex/s3tc/bc4/BC4Block.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#include "BC4Block.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../../util.h"
|
||||
|
||||
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>);
|
||||
}
|
||||
|
||||
void BC4Block::SetSelectors(const BC4Block::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.");
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
std::array<uint8_t, 8> BC4Block::GetValues6() const {
|
||||
return {alpha0,
|
||||
alpha1,
|
||||
static_cast<uint8_t>((alpha0 * 4 + alpha1) / 5),
|
||||
static_cast<uint8_t>((alpha0 * 3 + alpha1 * 2) / 5),
|
||||
static_cast<uint8_t>((alpha0 * 2 + alpha1 * 3) / 5),
|
||||
static_cast<uint8_t>((alpha0 + alpha1 * 4) / 5),
|
||||
0,
|
||||
0xFF};
|
||||
}
|
||||
|
||||
std::array<uint8_t, 8> BC4Block::GetValues8() const {
|
||||
return {alpha0,
|
||||
alpha1,
|
||||
static_cast<uint8_t>((alpha0 * 6 + alpha1) / 7),
|
||||
static_cast<uint8_t>((alpha0 * 5 + alpha1 * 2) / 7),
|
||||
static_cast<uint8_t>((alpha0 * 4 + alpha1 * 3) / 7),
|
||||
static_cast<uint8_t>((alpha0 * 3 + alpha1 * 4) / 7),
|
||||
static_cast<uint8_t>((alpha0 * 2 + alpha1 * 5) / 7),
|
||||
static_cast<uint8_t>((alpha0 + alpha1 * 6) / 7)};
|
||||
}
|
||||
} // namespace quicktex::s3tc
|
@ -20,22 +20,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "../../util.h"
|
||||
#include <utility>
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
class alignas(8) BC4Block {
|
||||
public:
|
||||
static constexpr int Width = 4;
|
||||
static constexpr int Height = 4;
|
||||
static constexpr size_t Width = 4;
|
||||
static constexpr size_t Height = 4;
|
||||
|
||||
static constexpr size_t SelectorSize = 6; // size of selector array in bytes
|
||||
static constexpr size_t SelectorBits = 3; // size of a selector in bits
|
||||
static constexpr size_t SelectorMax = (1 << SelectorBits) - 1; // maximum value of a selector
|
||||
|
||||
using SelectorArray = std::array<std::array<uint8_t, Width>, Height>;
|
||||
using AlphaPair = std::pair<uint8_t, uint8_t>;
|
||||
|
||||
uint8_t alpha0; // first endpoint
|
||||
uint8_t alpha1; // second endpoint
|
||||
|
||||
private:
|
||||
std::array<uint8_t, SelectorSize> _selectors; // internal array of selector bytes
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
|
||||
/// Create a new BC4Block
|
||||
constexpr BC4Block() {
|
||||
static_assert(sizeof(BC4Block) == 8);
|
||||
static_assert(sizeof(std::array<BC4Block, 10>) == 8 * 10);
|
||||
@ -44,76 +56,51 @@ class alignas(8) BC4Block {
|
||||
_selectors = {0, 0, 0, 0, 0, 0};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BC4Block
|
||||
* @param valpha0 first endpoint value
|
||||
* @param valpha1 second endpoint value
|
||||
* @param selectors the selectors as a 4x4 array of integers, between 0 and 7 inclusive.
|
||||
*/
|
||||
BC4Block(uint8_t valpha0, uint8_t valpha1, const SelectorArray& selectors) {
|
||||
alpha0 = valpha0;
|
||||
alpha1 = valpha1;
|
||||
SetSelectors(selectors);
|
||||
}
|
||||
|
||||
constexpr AlphaPair GetAlphas() const { return AlphaPair(alpha0, alpha1); }
|
||||
/**
|
||||
* Create a new solid BC4Block
|
||||
* @param alpha first endpoint value
|
||||
*/
|
||||
BC4Block(uint8_t alpha) {
|
||||
alpha0 = alpha;
|
||||
alpha1 = alpha;
|
||||
_selectors.fill(0);
|
||||
}
|
||||
|
||||
/// Get a alpha0 and alpha1 as a pair
|
||||
AlphaPair GetAlphas() const { return AlphaPair(alpha0, alpha1); }
|
||||
|
||||
/// Set alpha0 and alpha1 as a pair
|
||||
void SetAlphas(AlphaPair as) {
|
||||
alpha0 = as.first;
|
||||
alpha1 = as.second;
|
||||
}
|
||||
|
||||
constexpr uint64_t GetSelectorBits() const {
|
||||
auto packed = Pack<uint8_t, uint64_t, 8, SelectorSize>(_selectors);
|
||||
assert(packed <= SelectorsPackedMax);
|
||||
return packed;
|
||||
}
|
||||
/// Get the block's selectors as a 4x4 array of integers between 0 and 7 inclusive.
|
||||
SelectorArray GetSelectors() const;
|
||||
|
||||
void SetSelectorBits(uint64_t packed) {
|
||||
assert(packed <= SelectorsPackedMax);
|
||||
_selectors = Unpack<uint64_t, uint8_t, 8, SelectorSize>(packed);
|
||||
}
|
||||
/// Get the block's selectors as a 4x4 array of integers between 0 and 7 inclusive.
|
||||
void SetSelectors(const SelectorArray& unpacked);
|
||||
|
||||
constexpr SelectorArray GetSelectors() const {
|
||||
auto rows = Unpack<uint64_t, uint16_t, SelectorBits * Width, Height>(GetSelectorBits());
|
||||
auto unpacked = MapArray(rows, Unpack<uint16_t, uint8_t, SelectorBits, Width>);
|
||||
return unpacked;
|
||||
}
|
||||
/// True if the block uses 6-value interpolation, i.e. alpha0 <= alpha1.
|
||||
bool Is6Value() const { return alpha0 <= alpha1; }
|
||||
|
||||
void SetSelectors(const SelectorArray& unpacked) {
|
||||
auto rows = MapArray(unpacked, Pack<uint8_t, uint16_t, SelectorBits, Width>);
|
||||
auto packed = Pack<uint16_t, uint64_t, SelectorBits * Width, Height>(rows);
|
||||
SetSelectorBits(packed);
|
||||
}
|
||||
|
||||
constexpr bool Is6Value() const { return alpha0 <= alpha1; }
|
||||
|
||||
static constexpr std::array<uint8_t, 8> GetValues6(unsigned l, unsigned h) {
|
||||
return {static_cast<uint8_t>(l),
|
||||
static_cast<uint8_t>(h),
|
||||
static_cast<uint8_t>((l * 4 + h) / 5),
|
||||
static_cast<uint8_t>((l * 3 + h * 2) / 5),
|
||||
static_cast<uint8_t>((l * 2 + h * 3) / 5),
|
||||
static_cast<uint8_t>((l + h * 4) / 5),
|
||||
0,
|
||||
255};
|
||||
}
|
||||
|
||||
static constexpr std::array<uint8_t, 8> GetValues8(unsigned l, unsigned h) {
|
||||
return {static_cast<uint8_t>(l),
|
||||
static_cast<uint8_t>(h),
|
||||
static_cast<uint8_t>((l * 6 + h) / 7),
|
||||
static_cast<uint8_t>((l * 5 + h * 2) / 7),
|
||||
static_cast<uint8_t>((l * 4 + h * 3) / 7),
|
||||
static_cast<uint8_t>((l * 3 + h * 4) / 7),
|
||||
static_cast<uint8_t>((l * 2 + h * 5) / 7),
|
||||
static_cast<uint8_t>((l + h * 6) / 7)};
|
||||
}
|
||||
|
||||
static constexpr std::array<uint8_t, 8> GetValues(unsigned l, unsigned h) { return l > h ? GetValues8(l, h) : GetValues6(l, h); }
|
||||
|
||||
static constexpr size_t SelectorSize = 6;
|
||||
static constexpr uint8_t SelectorBits = 3;
|
||||
static constexpr uint64_t SelectorsPackedMax = (1ULL << (8U * SelectorSize)) - 1U;
|
||||
|
||||
uint8_t alpha0;
|
||||
uint8_t alpha1;
|
||||
/// The interpolated values of this block as an array of 8 integers.
|
||||
std::array<uint8_t, 8> GetValues() const { return Is6Value() ? GetValues6() : GetValues8(); }
|
||||
|
||||
private:
|
||||
std::array<uint8_t, SelectorSize> _selectors;
|
||||
std::array<uint8_t, 8> GetValues6() const;
|
||||
std::array<uint8_t, 8> GetValues8() const;
|
||||
};
|
||||
} // namespace quicktex::s3tc
|
||||
|
@ -28,10 +28,7 @@
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
void BC4Decoder::DecodeInto(ColorBlock<4, 4> &dest, const BC4Block &block) const {
|
||||
auto l = block.alpha0;
|
||||
auto h = block.alpha1;
|
||||
|
||||
auto values = BC4Block::GetValues(l, h);
|
||||
auto values = block.GetValues();
|
||||
auto selectors = block.GetSelectors();
|
||||
|
||||
for (unsigned y = 0; y < 4; y++) {
|
||||
|
@ -25,12 +25,11 @@
|
||||
|
||||
#include "../../Color.h"
|
||||
#include "../../ColorBlock.h"
|
||||
#include "../../util.h"
|
||||
#include "BC4Block.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
BC4Block BC4Encoder::EncodeBlock(const ColorBlock<4, 4> &pixels) const {
|
||||
auto output = BC4Block();
|
||||
|
||||
uint8_t min = UINT8_MAX;
|
||||
uint8_t max = 0;
|
||||
|
||||
@ -40,12 +39,8 @@ BC4Block BC4Encoder::EncodeBlock(const ColorBlock<4, 4> &pixels) const {
|
||||
max = std::max(max, value);
|
||||
}
|
||||
|
||||
output.alpha1 = min;
|
||||
output.alpha0 = max;
|
||||
|
||||
if (max == min) {
|
||||
output.SetSelectorBits(0);
|
||||
return output;
|
||||
return BC4Block(min); // solid block
|
||||
}
|
||||
|
||||
auto selectors = BC4Block::SelectorArray();
|
||||
@ -74,9 +69,7 @@ BC4Block BC4Encoder::EncodeBlock(const ColorBlock<4, 4> &pixels) const {
|
||||
}
|
||||
}
|
||||
|
||||
output.SetSelectors(selectors);
|
||||
|
||||
return output;
|
||||
return BC4Block(max, min, selectors);
|
||||
}
|
||||
|
||||
} // namespace quicktex::s3tc
|
@ -84,10 +84,9 @@ template <typename I, typename O, size_t S, size_t C> constexpr O Pack(const std
|
||||
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
|
||||
const I max = (1U << S) - 1U;
|
||||
|
||||
for (unsigned i = 0; i < C; i++) {
|
||||
if (vals[i] > max) throw std::invalid_argument("Input value at index " + std::to_string(i) + " is larger than " + std::to_string(max));
|
||||
assert(vals[i] <= (1U << S) - 1U);
|
||||
packed |= static_cast<O>(vals[i]) << (i * S);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user