BC1Block/BC4Block cleanup

hotfix/mipmap-alpha-fix
Andrew Cassidy 3 years ago
parent e1739b99d3
commit 3d91ac2bed

@ -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

@ -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;
}
void SetSelectorBits(uint64_t packed) {
assert(packed <= SelectorsPackedMax);
_selectors = Unpack<uint64_t, uint8_t, 8, SelectorSize>(packed);
}
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;
}
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)};
}
/// Get the block's selectors as a 4x4 array of integers between 0 and 7 inclusive.
SelectorArray GetSelectors() const;
static constexpr std::array<uint8_t, 8> GetValues(unsigned l, unsigned h) { return l > h ? GetValues8(l, h) : GetValues6(l, h); }
/// Get the block's selectors as a 4x4 array of integers between 0 and 7 inclusive.
void SetSelectors(const SelectorArray& unpacked);
static constexpr size_t SelectorSize = 6;
static constexpr uint8_t SelectorBits = 3;
static constexpr uint64_t SelectorsPackedMax = (1ULL << (8U * SelectorSize)) - 1U;
/// True if the block uses 6-value interpolation, i.e. alpha0 <= alpha1.
bool Is6Value() const { return alpha0 <= alpha1; }
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…
Cancel
Save