Initial endpoint finding code

This commit is contained in:
Andrew Cassidy 2021-02-21 00:57:48 -08:00
parent 3169f1ae67
commit 148d32e28c
16 changed files with 566 additions and 65 deletions

View File

@ -19,11 +19,15 @@
#include "BC1Encoder.h"
#include <algorithm>
#include <array>
#include <cstdint>
#include <memory>
#include "../BlockView.h"
#include "../Color.h"
#include "../Matrix4x4.h"
#include "../Vector4.h"
#include "../bitwiseEnums.h"
namespace rgbcx {
@ -98,14 +102,15 @@ void BC1Encoder::EncodeBlock(Color4x4 pixels, BC1Block *dest) const {
auto g_view = pixels.GetChannel(1);
auto b_view = pixels.GetChannel(2);
if (pixels.IsSingleColor() || true) { // for now assume (wrongly) everything is a single-color block
Color first = pixels.Get(0, 0);
if (pixels.IsSingleColor()) { // for now assume (wrongly) everything is a single-color block
// single-color pixel block, do it the fast way
EncodeBlockSingleColor(pixels.Get(0, 0), dest);
EncodeBlockSingleColor(first, dest);
return;
}
Color min, max, avg;
pixels.GetMinMaxAvgRGB(min, max, avg);
auto metrics = pixels.GetMetrics();
}
void BC1Encoder::EncodeBlockSingleColor(Color color, BC1Block *dest) const {
@ -155,7 +160,7 @@ void BC1Encoder::EncodeBlockSingleColor(Color color, BC1Block *dest) const {
assert(min16 == 0 && max16 == 0);
max16 = 1;
min16 = 0;
mask = 0x55; // 1111 (min value only, max is ignored)
mask = 0x55; // 1111 (Min value only, max is ignored)
}
} else if (max16 < min16) {
std::swap(min16, max16);
@ -172,4 +177,193 @@ void BC1Encoder::EncodeBlockSingleColor(Color color, BC1Block *dest) const {
dest->selectors[3] = mask;
}
void BC1Encoder::FindEndpoints(Color4x4 pixels, BC1Encoder::Flags flags, const BC1Encoder::BlockMetrics metrics, Color &low, Color &high) const {
if (metrics.is_greyscale) {
// specialized greyscale case
const unsigned fr = pixels.Get(0).r;
if (metrics.max.r - metrics.min.r < 2) {
// single color block
low.r = high.r = scale8To5(fr);
low.g = high.g = scale8To6(fr);
low.b = high.b = low.r;
} else {
low.r = low.b = scale8To5(metrics.min.r);
low.g = scale8To6(metrics.min.r);
high.r = high.b = scale8To5(metrics.max.r);
high.g = scale8To6(metrics.max.r);
}
} else if ((flags & Flags::Use2DLS) != Flags::None) {
// 2D Least Squares approach from Humus's example, with added inset and optimal rounding.
Color diff = Color(metrics.max.r - metrics.min.r, metrics.max.g - metrics.min.g, metrics.max.b - metrics.min.b);
Vector4 l = {0, 0, 0};
Vector4 h = {0, 0, 0};
auto &sums = metrics.sums;
auto &min = metrics.min;
auto &max = metrics.max;
unsigned chan0 = diff.MaxChannelRGB(); // primary axis of the bounding box
l[chan0] = (float)min[chan0];
h[chan0] = (float)min[chan0];
assert(diff[chan0] >= diff[(chan0 + 1) % 3] && diff[chan0] >= diff[(chan0 + 2) % 3]);
std::array<unsigned, 3> sums_xy;
for (unsigned i = 0; i < 16; i++) {
auto val = pixels.Get(i);
for (unsigned c = 0; c < 3; c++) { sums_xy[c] += val[chan0] * val[c]; }
}
auto &sum_x = sums[chan0];
auto &sum_xx = sums_xy[chan0];
float denominator = (float)(16 * sum_xx) - (float)(sum_x * sum_x);
// once per secondary axis, calculate high and low using least squares
if (fabs(denominator > 1e-8f)) {
for (unsigned i = 1; i < 3; i++) {
/* each secondary axis is fitted with a linear formula of the form
* y = ax + b
* where y is the secondary axis and x is the primary axis
* a = (mxy - xy) / mx² - (x)²
* b = (x²y - xyx) / mx² - (x)²
* see Giordano/Weir pg.103 */
const unsigned chan = (chan0 + i) % 3;
const unsigned &sum_y = sums[chan];
const unsigned &sum_xy = sums_xy[chan];
float a = (float)((16 * sum_xy) - (sum_x * sum_y)) / denominator;
float b = (float)((sum_xx * sum_y) - (sum_xy * sum_x)) / denominator;
l[chan] = b + (a * l[chan0]);
h[chan] = b + (a * h[chan0]);
}
}
// once per axis, inset towards the center by 1/16 of the delta and scale
for (unsigned c = 0; c < 3; c++) {
float inset = (h[c] - l[c]) / 16.0f;
l[c] = ((l[c] + inset) / 255.0f);
h[c] = ((h[c] - inset) / 255.0f);
}
low = Color::PreciseRound565(l);
high = Color::PreciseRound565(h);
} else if ((flags & Flags::BoundingBox) != Flags::None) {
// Algorithm from icbc.h compress_dxt1_fast()
Vector4 l, h;
const float bias = 8.0f / 255.0f;
// rescale and inset values
for (unsigned c = 0; c < 3; c++) { // heh, c++
l[c] = (float)metrics.min[c] / 255.0f;
h[c] = (float)metrics.max[c] / 255.0f;
float inset = (h[c] - l[c] - bias) / 16.0f;
l[c] += inset;
h[c] -= inset;
}
// Select the correct diagonal across the bounding box
int icov_xz = 0, icov_yz = 0;
for (unsigned i = 0; i < 16; i++) {
int b = (int)pixels.Get(i).b - metrics.avg.b;
icov_xz += b * (int)pixels.Get(i).r - metrics.avg.r;
icov_yz += b * (int)pixels.Get(i).g - metrics.avg.g;
}
if (icov_xz < 0) std::swap(l[0], h[0]);
if (icov_yz < 0) std::swap(l[1], h[1]);
low = Color::PreciseRound565(l);
high = Color::PreciseRound565(h);
} else if ((flags & Flags::BoundingBoxInt) != Flags::None) {
// Algorithm from icbc.h compress_dxt1_fast(), but converted to integer.
Color min, max;
const float bias = 8.0f / 255.0f;
// rescale and inset values
for (unsigned c = 0; c < 3; c++) {
int inset = ((int)(metrics.max[c] - metrics.min[c]) - 8) >> 4; // 1/16 of delta, with bias
min[c] = clamp255(metrics.min[c] + inset);
max[c] = clamp255(metrics.max[c] - inset);
}
int icov_xz = 0, icov_yz = 0;
for (unsigned i = 0; i < 16; i++) {
int b = (int)pixels.Get(i).b - metrics.avg.b;
icov_xz += b * (int)pixels.Get(i).r - metrics.avg.r;
icov_yz += b * (int)pixels.Get(i).g - metrics.avg.g;
}
if (icov_xz < 0) std::swap(min.r, max.r);
if (icov_yz < 0) std::swap(min.g, max.g);
low = min.ScaleTo565();
high = max.ScaleTo565();
} else {
// the slow way
// Select 2 colors along the principle axis. (There must be a faster/simpler way.)
auto min = Vector4::FromColorRGB(metrics.min);
auto max = Vector4::FromColorRGB(metrics.max);
auto avg = Vector4::FromColorRGB(metrics.avg);
std::array<Vector4, 16> colors;
Vector4 axis = {306, 601, 117}; // I think this is luma?
Matrix4x4 covariance;
const unsigned total_power_iters = (flags & Flags::Use6PowerIters) != Flags::None ? 6 : 4;
for (unsigned i = 0; i < 16; i++) {
colors[i] = Vector4::FromColorRGB(pixels.Get(i));
Vector4 diff = colors[i] - avg;
for (unsigned c1 = 0; c1 < 3; c1++) {
for (unsigned c2 = c1; c2 < 3; c2++) {
covariance[c1][c2] += (diff[c1] * diff[c2]);
assert(c1 <= c2);
}
}
}
covariance /= 255.0f;
covariance.Mirror();
Vector4 delta = max - min;
// realign r and g axes to match
if (covariance[0][2] < 0) delta[0] = -delta[0]; // r vs b
if (covariance[1][2] < 0) delta[1] = -delta[1]; // g vs b
for (unsigned power_iter = 0; power_iter < total_power_iters; power_iter++) { delta = covariance * delta; }
float k = delta.MaxAbs(3);
if (k > 2) { axis = delta * (2048.0f / k); }
float min_dot = INFINITY;
float max_dot = -INFINITY;
unsigned min_index, max_index;
for (unsigned i = 0; i < 16; i++) {
float dot = colors[i].Dot(axis);
if (dot > max_dot) {
max_dot = dot;
max_index = i;
} else if (dot < min_dot) {
min_dot = dot;
min_index = i;
}
}
low = pixels.Get(min_index).ScaleTo565();
high = pixels.Get(max_index).ScaleTo565();
}
}
} // namespace rgbcx

View File

@ -29,8 +29,8 @@
#include "../Interpolator.h"
#include "../bitwiseEnums.h"
#include "../ndebug.h"
#include "../tables.h"
#include "BC1Block.h"
#include "tables.h"
namespace rgbcx {
@ -43,6 +43,7 @@ struct BC1MatchEntry {
class BC1Encoder : public BlockEncoder<BC1Block, 4, 4> {
public:
using InterpolatorPtr = std::shared_ptr<Interpolator>;
using BlockMetrics = Color4x4::BlockMetrics;
enum class Flags : uint32_t {
None = 0,
@ -105,10 +106,11 @@ class BC1Encoder : public BlockEncoder<BC1Block, 4, 4> {
BC1Encoder(InterpolatorPtr interpolator);
void EncodeBlock(Color4x4 pixels, BC1Block *dest) const override;
private:
using Vec3F = std::array<float, 3>;
const InterpolatorPtr _interpolator;
Flags _flags;
@ -116,7 +118,16 @@ class BC1Encoder : public BlockEncoder<BC1Block, 4, 4> {
unsigned _orderings4;
unsigned _orderings3;
// Unpacked BC1 block with metadata
struct EncodeResults {
Color low;
Color high;
std::array<uint8_t, 16> selectors;
bool is_3_color;
};
void EncodeBlockSingleColor(Color color, BC1Block *dest) const;
void FindEndpoints(Color4x4 pixels, Flags flags, BlockMetrics const metrics, Color &low, Color &high) const;
// match tables used for single-color blocks
// Each entry includes a high and low pair that best reproduces the 8-bit index as well as possible,

View File

@ -3,6 +3,15 @@
#include "tables.h"
const float g_midpoint5[32] = {.015686f, .047059f, .078431f, .111765f, .145098f, .176471f, .207843f, .241176f, .274510f, .305882f, .337255f,
.370588f, .403922f, .435294f, .466667f, .5f, .533333f, .564706f, .596078f, .629412f, .662745f, .694118f,
.725490f, .758824f, .792157f, .823529f, .854902f, .888235f, .921569f, .952941f, .984314f, 1e+37f};
const float g_midpoint6[64] = {.007843f, .023529f, .039216f, .054902f, .070588f, .086275f, .101961f, .117647f, .133333f, .149020f, .164706f, .180392f, .196078f,
.211765f, .227451f, .245098f, .262745f, .278431f, .294118f, .309804f, .325490f, .341176f, .356863f, .372549f, .388235f, .403922f,
.419608f, .435294f, .450980f, .466667f, .482353f, .500000f, .517647f, .533333f, .549020f, .564706f, .580392f, .596078f, .611765f,
.627451f, .643137f, .658824f, .674510f, .690196f, .705882f, .721569f, .737255f, .754902f, .772549f, .788235f, .803922f, .819608f,
.835294f, .850980f, .866667f, .882353f, .898039f, .913725f, .929412f, .945098f, .960784f, .976471f, .992157f, 1e+37f};
// All total orderings for 16 pixels 2-bit selectors.
// BC1 selector order 0, 2, 3, 1 (i.e. the selectors are reordered into linear order).
const uint8_t g_unique_total_orders4[NUM_UNIQUE_TOTAL_ORDERINGS4][4] = {

View File

@ -13,6 +13,9 @@ const uint32_t MAX_TOTAL_ORDERINGS4 = 32;
const uint32_t MAX_TOTAL_ORDERINGS4 = 128;
#endif
extern const float g_midpoint5[32];
extern const float g_midpoint6[64];
const uint32_t NUM_UNIQUE_TOTAL_ORDERINGS4 = 969;
extern const uint8_t g_unique_total_orders4[NUM_UNIQUE_TOTAL_ORDERINGS4][4];

View File

@ -50,7 +50,7 @@ void BC4Encoder::EncodeBlock(Byte4x4 pixels, BC4Block *const dest) const noexcep
const int bias = 4 - min * 14;
const int delta = max - min;
// min is now 0. Compute thresholds between values by scaling max. It's x14 because we're adding two x7 scale factors.
// Min is now 0. Compute thresholds between values by scaling max. It's x14 because we're adding two x7 scale factors.
// bias is applied here
std::array<int, 7> thresholds = {};
for (unsigned i = 0; i < 7; i++) thresholds[i] = delta * (1 + (2 * (int)i)) - bias;

View File

@ -102,6 +102,15 @@ template <size_t M, size_t N> class ColorBlockView : public BlockView<Color, M,
using Base = BlockView<Color, M, N>;
using ChannelView = BlockView<uint8_t, M, N>;
struct BlockMetrics {
Color min;
Color max;
Color avg;
bool is_greyscale;
bool has_black;
std::array<unsigned, 3> sums;
};
ColorBlockView(Color *start, int row_stride = N, int pixel_stride = 1) : Base(start, row_stride, pixel_stride) {}
constexpr ChannelView GetChannel(uint8_t index) noexcept(ndebug) {
@ -120,24 +129,32 @@ template <size_t M, size_t N> class ColorBlockView : public BlockView<Color, M,
return true;
}
void GetMinMaxAvgRGB(Color &min, Color &max, Color &avg) {
min = Base::Get(0, 0);
max = Base::Get(0, 0);
BlockMetrics GetMetrics(unsigned black_threshold = 4) {
BlockMetrics metrics;
metrics.min = Color(UINT8_MAX, UINT8_MAX, UINT8_MAX);
metrics.max = Color(0, 0, 0);
metrics.has_black = false;
metrics.is_greyscale = true;
std::array<unsigned, 3> sums;
for (unsigned i = 1; i < M * N; i++) {
for (unsigned i = 0; i < M * N; i++) {
auto val = Base::Get(i);
for (unsigned c = 0; c < 3; c++) {
if (val[c] < min[c]) {
min[c] = val[c];
if (val[c] < metrics.min[c]) {
metrics.min[c] = val[c];
} else {
max[c] = val[c];
metrics.max[c] = val[c];
}
sums[c] += val[c];
}
metrics.is_greyscale &= ((val.r == val.g) && (val.r == val.b));
metrics.has_black |= (val.r | val.g | val.b < black_threshold);
}
for (unsigned c = 0; c < 3; c++) { avg[c] = (uint8_t)(sums[c] / (M * N)); }
for (unsigned c = 0; c < 3; c++) { metrics.avg[c] = (uint8_t)(metrics.sums[c] / (M * N)); }
return metrics;
}
};

View File

@ -16,14 +16,15 @@
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 "Color.h"
#include <algorithm> // for max, min
#include <algorithm> // for max, Min
#include "Vector4.h"
#include "util.h" // for scale5To8, scale8To5, assert5bit, scale6To8
// region Color implementation
namespace rgbcx {
Color::Color() { SetRGBA(0, 0, 0, 0xFF); }
Color::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { SetRGBA(r, g, b, a); }
@ -37,6 +38,14 @@ uint16_t Color::Pack565Unscaled(uint8_t r, uint8_t g, uint8_t b) {
uint16_t Color::Pack565(uint8_t r, uint8_t g, uint8_t b) { return Pack565Unscaled(scale8To5(r), scale8To6(g), scale8To5(b)); }
Color Color::Unpack565Unscaled(uint16_t Packed) {
uint8_t r = (Packed >> 11) & 0x1F;
uint8_t g = (Packed >> 5) & 0x3F;
uint8_t b = Packed & 0x1F;
return Color(r, g, b);
}
Color Color::Unpack565(uint16_t Packed) {
uint8_t r = static_cast<uint8_t>(scale5To8((Packed >> 11) & 0x1FU));
uint8_t g = static_cast<uint8_t>(scale6To8((Packed >> 5) & 0x3FU));
@ -45,10 +54,24 @@ Color Color::Unpack565(uint16_t Packed) {
return Color(r, g, b);
}
Color Color::Unpack565Unscaled(uint16_t Packed) {
uint8_t r = (Packed >> 11) & 0x1F;
uint8_t g = (Packed >> 5) & 0x3F;
uint8_t b = Packed & 0x1F;
Color Color::PreciseRound565(Vector4 &v) {
int trial_r = (int)(v[0] * UINT5_MAX);
int trial_g = (int)(v[1] * UINT6_MAX);
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);
// increment each channel if above the rounding point
r += v[0] > Midpoints5bit[r];
g += v[1] > Midpoints6bit[g];
b += v[2] > Midpoints5bit[b];
assert5bit(r);
assert6bit(g);
assert5bit(b);
return Color(r, g, b);
}
@ -66,15 +89,26 @@ void Color::SetRGB(uint8_t vr, uint8_t vg, uint8_t vb) {
b = vb;
}
Color Color::min(const Color &a, const Color &b) { return Color(std::min(a[0], b[0]), std::min(a[1], b[1]), std::min(a[2], b[2]), std::min(a[3], b[3])); }
size_t Color::MinChannelRGB() {
if (r < g && r < b) return 0;
if (g < b && g < r) return 1;
return 2;
}
Color Color::max(const Color &a, const Color &b) { return Color(std::max(a[0], b[0]), std::max(a[1], b[1]), std::max(a[2], b[2]), std::max(a[3], b[3])); }
size_t Color::MaxChannelRGB() {
if (r > g && r > b) return 0;
if (g > b && g > r) return 1;
return 2;
}
Color Color::Min(const Color &A, const Color &B) { return Color(std::min(A[0], B[0]), std::min(A[1], B[1]), std::min(A[2], B[2]), std::min(A[3], B[3])); }
Color Color::Max(const Color &a, const Color &b) { return Color(std::max(a[0], b[0]), std::max(a[1], b[1]), std::max(a[2], b[2]), std::max(a[3], b[3])); }
uint16_t Color::pack565() { return Pack565(r, g, b); }
uint16_t Color::pack565Unscaled() { return Pack565Unscaled(r, g, b); }
Color Color::ScaleTo565() const { return Color(scale8To5(r), scale8To6(g), scale8To5(b)); }
Color Color::ScaleFrom565() const { return Color(scale5To8(r), scale6To8(g), scale5To8(b)); }
// endregion
} // namespace rgbcx

View File

@ -23,7 +23,11 @@
#include <cstdint> // for uint8_t, uint16_t
namespace rgbcx {
class Vector4;
#pragma pack(push, 1)
class Color {
public:
uint8_t r;
@ -41,6 +45,11 @@ class Color {
static Color Unpack565Unscaled(uint16_t Packed);
static Color Unpack565(uint16_t Packed);
static Color PreciseRound565(Vector4 &v);
static Color Min(const Color &A, const Color &B);
static Color Max(const Color &A, const Color &B);
bool operator==(const Color &Rhs) const { return r == Rhs.r && g == Rhs.g && b == Rhs.b && a == Rhs.a; }
uint8_t operator[](size_t index) const {
@ -64,9 +73,23 @@ class Color {
Color ScaleTo565() const;
Color ScaleFrom565() const;
static Color min(const Color &A, const Color &B);
static Color max(const Color &A, const Color &B);
size_t MinChannelRGB();
size_t MaxChannelRGB();
int get_luma() const { return (13938U * r + 46869U * g + 4729U * b + 32768U) >> 16U; } // REC709 weightings
bool IsGrayscale() const { return ((r == g) && (r == b)); }
int GetLuma() const { return (13938U * r + 46869U * g + 4729U * b + 32768U) >> 16U; } // REC709 weightings
private:
static constexpr float Midpoints5bit[32] = {.015686f, .047059f, .078431f, .111765f, .145098f, .176471f, .207843f, .241176f, .274510f, .305882f, .337255f,
.370588f, .403922f, .435294f, .466667f, .5f, .533333f, .564706f, .596078f, .629412f, .662745f, .694118f,
.725490f, .758824f, .792157f, .823529f, .854902f, .888235f, .921569f, .952941f, .984314f, 1e+37f};
static constexpr float Midpoints6bit[64] = {.007843f, .023529f, .039216f, .054902f, .070588f, .086275f, .101961f, .117647f, .133333f, .149020f, .164706f,
.180392f, .196078f, .211765f, .227451f, .245098f, .262745f, .278431f, .294118f, .309804f, .325490f, .341176f,
.356863f, .372549f, .388235f, .403922f, .419608f, .435294f, .450980f, .466667f, .482353f, .500000f, .517647f,
.533333f, .549020f, .564706f, .580392f, .596078f, .611765f, .627451f, .643137f, .658824f, .674510f, .690196f,
.705882f, .721569f, .737255f, .754902f, .772549f, .788235f, .803922f, .819608f, .835294f, .850980f, .866667f,
.882353f, .898039f, .913725f, .929412f, .945098f, .960784f, .976471f, .992157f, 1e+37f};
};
#pragma pack(pop)
#pragma pack(pop)
} // namespace rgbcx

View File

@ -1,22 +0,0 @@
/* 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/>.
*/
#include "Image.h"
namespace rgbcx {} // namespace rgbcx

View File

@ -17,9 +17,34 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "Matrix4x4.h"
namespace rgbcx {
class Image {};
Matrix4x4 operator*(Matrix4x4& lhs, Matrix4x4& rhs) {
Matrix4x4 trans_rhs = rhs.Transpose(); // 🏳️‍⚧️
Matrix4x4 result;
for (unsigned r = 0; r < 4; r++) {
for (unsigned c = 0; c < 4; c++) { result[r][c] = lhs[r].Dot(trans_rhs[c]); }
}
return result;
}
Vector4 operator*(Matrix4x4& lhs, Vector4& rhs) {
Vector4 result;
for (unsigned r = 0; r < 4; r++) { result[r] = rhs.Dot(lhs[r]); }
return result;
}
void Matrix4x4::Mirror() {
for (unsigned r = 0; r < 3; r++) {
for (unsigned c = (r + 1); c < 4; c++) {
_r[c][r] = _r[r][c];
}
}
}
} // namespace rgbcx

93
src/Matrix4x4.h Normal file
View File

@ -0,0 +1,93 @@
/* 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 <array>
#include <functional>
#include "Vector4.h"
namespace rgbcx {
class Matrix4x4 {
public:
static Matrix4x4 Identity() {
Matrix4x4 result;
for (unsigned i = 0; i < 4; i++) { result[i][i] = 1; }
return result;
}
static Matrix4x4 Transpose(Matrix4x4 &val) {
Matrix4x4 result;
for (unsigned r = 0; r < 3; r++) {
for (unsigned c = 0; c < 3; c++) { result[r][c] = val[c][r]; }
}
return result;
}
Vector4 operator[](size_t index) const {
assert(index < 4);
return _r[index];
}
Vector4 &operator[](size_t index) {
assert(index < 4);
return _r[index];
}
friend Matrix4x4 operator*(const Matrix4x4 &lhs, const Matrix4x4 &rhs);
friend Vector4 operator*(const Matrix4x4 &lhs, const Vector4 &rhs);
friend Matrix4x4 operator+(const Matrix4x4 &lhs, const Matrix4x4 &rhs) { return DoOp(lhs, rhs, std::plus()); }
friend Matrix4x4 operator-(const Matrix4x4 &lhs, const Matrix4x4 &rhs) { return DoOp(lhs, rhs, std::minus()); }
friend Matrix4x4 operator+(const Matrix4x4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::plus()); }
friend Matrix4x4 operator-(const Matrix4x4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::minus()); }
friend Matrix4x4 operator*(const Matrix4x4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::multiplies()); }
friend Matrix4x4 operator/(const Matrix4x4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::divides()); }
friend Matrix4x4 &operator+=(Matrix4x4 &lhs, const Matrix4x4 &rhs) { return lhs = lhs + rhs; }
friend Matrix4x4 &operator-=(Matrix4x4 &lhs, const Matrix4x4 &rhs) { return lhs = lhs - rhs; }
friend Matrix4x4 &operator*=(Matrix4x4 &lhs, const Matrix4x4 &rhs) { return lhs = lhs * rhs; }
friend Matrix4x4 &operator+=(Matrix4x4 &lhs, const float &rhs) { return lhs = lhs + rhs; }
friend Matrix4x4 &operator-=(Matrix4x4 &lhs, const float &rhs) { return lhs = lhs - rhs; }
friend Matrix4x4 &operator*=(Matrix4x4 &lhs, const float &rhs) { return lhs = lhs * rhs; }
friend Matrix4x4 &operator/=(Matrix4x4 &lhs, const float &rhs) { return lhs = lhs / rhs; }
Matrix4x4 Transpose() { return Transpose(*this); }
void Mirror();
private:
template <typename Op> friend Matrix4x4 DoOp(const Matrix4x4 &lhs, const Matrix4x4 &rhs, Op f) {
Matrix4x4 result;
for (unsigned r = 0; r < 4; r++) { result[r] = f(lhs[r], rhs[r]); }
return result;
}
template <typename Op> friend Matrix4x4 DoOp(const Matrix4x4 &lhs, const float &rhs, Op f) {
Matrix4x4 result;
for (unsigned r = 0; r < 4; r++) { result[r] = f(lhs[r], rhs); }
return result;
}
std::array<Vector4, 4> _r;
};
} // namespace rgbcx

114
src/Vector4.h Normal file
View File

@ -0,0 +1,114 @@
/* 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 <array>
#include <functional>
#include "Color.h"
namespace rgbcx {
class Vector4 {
public:
Vector4(float x = 0, float y = 0, float z = 0, float w = 0) {
_c[0] = x;
_c[1] = y;
_c[2] = z;
_c[3] = w;
}
Vector4(float scalar) {
_c[0] = scalar;
_c[1] = scalar;
_c[2] = scalar;
_c[3] = scalar;
}
Vector4(const Color &c) : Vector4(c.r, c.g, c.b, c.a) {}
static Vector4 FromColor(const Color &c) { return Vector4(c); }
static Vector4 FromColorRGB(const Color &c) { return Vector4(c.r, c.g, c.b); }
static float Dot(Vector4 &lhs, Vector4 &rhs) {
float sum = 0;
for (unsigned i = 0; i < 4; i++) { sum += lhs[i] * rhs[i]; }
return sum;
}
float operator[](size_t index) const {
assert(index < 4);
return _c[index];
}
float &operator[](size_t index) {
assert(index < 4);
return _c[index];
}
friend Vector4 operator+(const Vector4 &lhs, const Vector4 &rhs) { return DoOp(lhs, rhs, std::plus()); }
friend Vector4 operator-(const Vector4 &lhs, const Vector4 &rhs) { return DoOp(lhs, rhs, std::minus()); }
friend Vector4 operator*(const Vector4 &lhs, const Vector4 &rhs) { return DoOp(lhs, rhs, std::multiplies()); }
friend Vector4 operator/(const Vector4 &lhs, const Vector4 &rhs) { return DoOp(lhs, rhs, std::divides()); }
friend Vector4 operator+(const Vector4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::plus()); }
friend Vector4 operator-(const Vector4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::minus()); }
friend Vector4 operator*(const Vector4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::multiplies()); }
friend Vector4 operator/(const Vector4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::divides()); }
friend Vector4 &operator+=(Vector4 &lhs, const Vector4 &rhs) { return lhs = lhs + rhs; }
friend Vector4 &operator-=(Vector4 &lhs, const Vector4 &rhs) { return lhs = lhs - rhs; }
friend Vector4 &operator*=(Vector4 &lhs, const Vector4 &rhs) { return lhs = lhs * rhs; }
friend Vector4 &operator/=(Vector4 &lhs, const Vector4 &rhs) { return lhs = lhs / rhs; }
friend Vector4 &operator+=(Vector4 &lhs, const float &rhs) { return lhs = lhs + rhs; }
friend Vector4 &operator-=(Vector4 &lhs, const float &rhs) { return lhs = lhs - rhs; }
friend Vector4 &operator*=(Vector4 &lhs, const float &rhs) { return lhs = lhs * rhs; }
friend Vector4 &operator/=(Vector4 &lhs, const float &rhs) { return lhs = lhs / rhs; }
float Dot(Vector4 other) { return Dot(*this, other); }
float MaxAbs(unsigned channels = 4) {
assert(channels < 5);
assert(channels > 0);
float max = 0;
for (unsigned i = 0; i < channels; i++) {
float a = fabs((*this)[i]);
if (a > max) max = a;
}
return max;
}
private:
template <typename Op> friend Vector4 DoOp(const Vector4 &lhs, const Vector4 &rhs, Op f) {
Vector4 r;
for (unsigned i = 0; i < 4; i++) { r[i] = f(lhs[i], rhs[i]); }
return r;
}
template <typename Op> friend Vector4 DoOp(const Vector4 &lhs, const float &rhs, Op f) {
Vector4 r;
for (unsigned i = 0; i < 4; i++) { r[i] = f(lhs[i], rhs); }
return r;
}
std::array<float, 4> _c;
};
} // namespace rgbcx

View File

@ -13,8 +13,8 @@
#include <type_traits>
#include "BC1/BC1Block.h"
#include "BC1/tables.h"
#include "Color.h"
#include "tables.h"
#include "util.h"
namespace rgbcx {
@ -1703,12 +1703,12 @@ static inline void encode_bc1_pick_initial(const Color *pSrc_pixels, uint32_t fl
int r = (int)pSrc_pixels[i].r - avg_r;
int g = (int)pSrc_pixels[i].g - avg_g;
int b = (int)pSrc_pixels[i].b - avg_b;
icov[0] += r * r;
icov[1] += r * g;
icov[2] += r * b;
icov[3] += g * g;
icov[4] += g * b;
icov[5] += b * b;
icov[0] += r * r; //0, 0, 0
icov[1] += r * g; //1, 0, 1
icov[2] += r * b; //2, 0, 2
icov[3] += g * g; //3, 1, 1
icov[4] += g * b; //4, 1, 2
icov[5] += b * b; //5, 2, 2
}
int saxis_r = 306, saxis_g = 601, saxis_b = 117;

View File

@ -104,7 +104,7 @@ struct color_quad_u8 {
return m_c[i];
}
inline int get_luma() const { return (13938U * m_c[0] + 46869U * m_c[1] + 4729U * m_c[2] + 32768U) >> 16U; } // REC709 weightings
inline int GetLuma() const { return (13938U * m_c[0] + 46869U * m_c[1] + 4729U * m_c[2] + 32768U) >> 16U; } // REC709 weightings
};*/
using color_quad_u8 = Color;
typedef std::vector<color_quad_u8> color_quad_u8_vec;
@ -267,7 +267,7 @@ class image_metrics {
if (!num_channels) {
// int luma_diff = ;
unsigned index = iabs(ca.get_luma() - cb.get_luma());
unsigned index = iabs(ca.GetLuma() - cb.GetLuma());
hist[index]++;
} else {
for (uint32_t c = 0; c < num_channels; c++) hist[iabs(ca[first_channel + c] - cb[first_channel + c])]++;

View File

@ -125,7 +125,7 @@ template <typename S> constexpr S minimum(S a, S b, S c, S d) { return minimum(m
template <typename T> constexpr T square(T a) { return a * a; }
constexpr float clampf(float value, float low, float high) {
constexpr float clampf(float value, float low = 0.0f, float high = 1.0f) {
if (value < low)
value = low;
else if (value > high)