diff --git a/quicktex/Matrix.h b/quicktex/Matrix.h index 96b27b5..dbffff5 100644 --- a/quicktex/Matrix.h +++ b/quicktex/Matrix.h @@ -29,9 +29,9 @@ namespace quicktex { -template class Matrix; +template class Matrix; -template using Vec = Matrix; +template using Vec = Matrix; // region helper concepts template @@ -42,7 +42,7 @@ concept is_matrix = requires(V &v) { V::width(); V::height(); V::value_type; - } && std::same_as < Matrix, + } && std::same_as < Matrix, std::remove_cvref_t < V >> ; template struct vector_stats { @@ -66,17 +66,14 @@ template constexpr size_t vector_dims = vector_stats::dims; // endregion template class VecBase { - public: - const T &operator[](size_t index) const { return _c[index]; } - T &operator[](size_t index) { return _c[index]; } - - const T &at(size_t index) const { return _c.at(index); } - T &at(size_t index) { return _c.at(index); } + protected: + const T &_at(size_t index) const { return _c.at(index); } + T &_at(size_t index) { return _c.at(index); } - auto begin() { return _c.begin(); } - auto begin() const { return _c.begin(); } - auto end() { return _c.end(); } - auto end() const { return _c.end(); } + auto _begin() { return _c.begin(); } + auto _begin() const { return _c.begin(); } + auto _end() { return _c.end(); } + auto _end() const { return _c.end(); } private: std::array _c; @@ -91,7 +88,7 @@ template using matrix_column_type = std::condit * @tparam N Width of the matrix * @tparam M Height of the matrix */ -template +template class Matrix : public VecBase>, M> { public: using base = VecBase>, M>; @@ -101,9 +98,9 @@ class Matrix : public VecBase>, M> { using column_type = matrix_column_type; using base::base; - using base::begin; - using base::end; - using base::operator[]; + // using base::begin; + // using base::end; + // using base::operator[]; // region constructors /** @@ -144,6 +141,14 @@ class Matrix : public VecBase>, M> { : Matrix(input_range.begin()) { assert(std::distance(input_range.begin(), input_range.end()) == M); } + + template + requires(N == M) + static constexpr Matrix identity() { + Matrix result = Matrix(0); + for (unsigned i = 0; i < N; i++) { result.element(i, i) = 1; } + return result; + } // endregion // region iterators and accessors @@ -152,11 +157,17 @@ class Matrix : public VecBase>, M> { static constexpr size_t height = M; static constexpr size_t dims = ((width > 1) ? 1 : 0) + ((height > 1) ? 1 : 0); - auto row_begin() { return this->begin(); } - auto row_begin() const { return this->begin(); } + const row_type &at(size_t index) const { return static_cast(base::_at(index)); } + row_type &at(size_t index) { return static_cast(base::_at(index)); } - auto row_end() { return this->end(); } - auto row_end() const { return this->end(); } + const row_type &operator[](size_t index) const { return at(index); } + row_type &operator[](size_t index) { return at(index); } + + const row_type *begin() const { return static_cast(base::_begin()); } + row_type *begin() { return static_cast(base::_begin()); } + + const row_type *end() const { return static_cast(base::_end()); } + row_type *end() { return static_cast(base::_end()); } auto column_begin() const { return column_iterator(this, 0); } auto column_end() const { return column_iterator(this, N); } @@ -167,20 +178,32 @@ class Matrix : public VecBase>, M> { auto all_end() const { return linear_iterator(this, N * M); } auto all_end() { return linear_iterator(this, N * M); } - const row_type &get_row(size_t y) const { return this->at(y); } + const row_type &get_row(size_t m) const { + assert(m < M); + return static_cast(this->at(m)); + } - template void set_row(size_t y, const R &value) { this->at(y) = value; } + template void set_row(size_t m, const R &value) { + assert(m < M); + this->at(m) = value; + } template column_type get_column(size_t n) const { - column_type ret; - for (unsigned m = 0; m < M; m++) { ret[m] = element(m, n); } - return ret; + if constexpr (M == 1) { + return element(0, n); + } else { + column_type ret; + for (unsigned m = 0; m < M; m++) { ret[m] = element(m, n); } + return ret; + } } void set_column(size_t n, const column_type &value) { - column_type ret; - for (unsigned m = 0; m < M; m++) { element(m, n) = value[m]; } - return ret; + if constexpr (M == 1) { + element(0, n) = value; + } else { + for (unsigned m = 0; m < M; m++) { element(m, n) = value[m]; } + } } // n/m accessors @@ -202,7 +225,7 @@ class Matrix : public VecBase>, M> { T &element(size_t i) { return element(i / N, i % N); } // RGBA accessors - const T &r() const { return this->at(0); } + const T &r() const { return (*this)[0]; } T &r() { return this->at(0); } template std::enable_if_t= 2, const S &> g() const { return this->at(1); } template std::enable_if_t= 2, S &> g() { return this->at(1); } @@ -224,7 +247,7 @@ class Matrix : public VecBase>, M> { template requires std::equality_comparable_with bool - operator==(const Matrix &rhs) const { + operator==(const Matrix &rhs) const { return size() == rhs.size() && std::equal(this->begin(), this->end(), rhs.begin()); }; @@ -238,14 +261,14 @@ class Matrix : public VecBase>, M> { // add vectors template requires operable> - Matrix operator+(const Matrix &rhs) const { + Matrix operator+(const Matrix &rhs) const { return _map(std::plus(), *this, rhs); }; // subtract vectors template requires operable> - Matrix operator-(const Matrix &rhs) const { + Matrix operator-(const Matrix &rhs) const { // we can't just add the negation because that's invalid for unsigned types return _map(std::minus(), *this, rhs); }; @@ -253,7 +276,7 @@ class Matrix : public VecBase>, M> { // multiply matrix with a matrix or column vector template requires(NN == 1 || NN == N) && operable> - Matrix operator*(const Matrix &rhs) const { + Matrix operator*(const Matrix &rhs) const { return _map(std::multiplies(), *this, rhs); }; @@ -267,7 +290,7 @@ class Matrix : public VecBase>, M> { // divides a matrix by a matrix or column vector template requires(NN == 1 || NN == N) && operable> - Matrix operator/(const Matrix &rhs) const { + Matrix operator/(const Matrix &rhs) const { return _map(std::divides(), *this, rhs); }; @@ -322,36 +345,50 @@ class Matrix : public VecBase>, M> { column_type hsum() const { return std::accumulate(column_begin(), column_end(), column_type{}); } // sum up all rows - row_type vsum() const { return std::accumulate(row_begin(), row_end(), row_type{}); } + row_type vsum() const { return std::accumulate(begin(), end(), row_type{}); } // sum up all values T sum() const { return std::accumulate(all_begin(), all_end(), T{}); } template requires operable> - Matrix mult(const Matrix &rhs) { + Matrix mult(const Matrix &rhs) const { auto rt = rhs.transpose(); - Matrix res(0); - for (unsigned i = 0; i < P; i++) { + Matrix res(0); + for (unsigned p = 0; p < P; p++) { // for each column of the RHS/Result - for (unsigned j = 0; j < M; j++) { + for (unsigned m = 0; m < M; m++) { // for each row of the LHS/Result - res.element(i, j) = get_row(i).dot(rt.get_row(j)); + for (unsigned n = 0; n < N; n++) { + res.element(m, p) += element(m, n) * rhs.element(n, p); + } } } return res; } - Matrix transpose() { - Matrix res; + Matrix transpose() const { + Matrix res; for (unsigned m = 0; m < M; m++) { res.set_column(m, get_row(m)); } return res; } + template + requires(N == M) + Matrix mirror() const { + Matrix result = *this; + for (unsigned n = 0; n < N - 1; n++) { + for (unsigned m = (n + 1); m < M; m++) { + result.element(m,n) = result.element(n,m); + } + } + return result; + } + // dot product of two compatible matrices template requires(N == 1) && operable> && operable> - row_type dot(const Matrix &rhs) const { + row_type dot(const Matrix &rhs) const { // technically this is Lt * R, but the vsum method is probably faster/more readable // than allocationg a new transpose matrix Matrix product = *this * rhs; diff --git a/quicktex/s3tc/bc1/BC1Encoder.cpp b/quicktex/s3tc/bc1/BC1Encoder.cpp index 42f3f33..e38873b 100644 --- a/quicktex/s3tc/bc1/BC1Encoder.cpp +++ b/quicktex/s3tc/bc1/BC1Encoder.cpp @@ -631,19 +631,18 @@ void BC1Encoder::FindEndpoints(EncodeResults &result, const CBlock &pixels, cons // TODO: handle constant blue channel better - auto min = Vector4::FromColorRGB(metrics.min); - auto max = Vector4::FromColorRGB(metrics.max); - auto avg = Vector4::FromColorRGB(metrics.avg); + Color min = metrics.min; + Color max = metrics.max; + Color avg = metrics.avg; - Vector4 axis = {306, 601, 117}; // Luma vector - Matrix4x4 covariance = Matrix4x4::Identity(); + Vec axis = {306, 601, 117, 0}; // Luma vector + auto covariance = Matrix::identity(); for (int i = 0; i < 16; i++) { auto val = pixels.Get(i); if (ignore_black && val.IsBlack()) continue; - auto color_vec = Vector4::FromColorRGB(val); - Vector4 diff = color_vec - avg; + auto diff = val - avg; for (unsigned c1 = 0; c1 < 3; c1++) { for (unsigned c2 = c1; c2 < 3; c2++) { covariance[c1][c2] += (diff[c1] * diff[c2]); @@ -653,9 +652,9 @@ void BC1Encoder::FindEndpoints(EncodeResults &result, const CBlock &pixels, cons } covariance /= 255.0f; - covariance.Mirror(); + covariance = covariance.mirror(); - Vector4 delta = max - min; + Vec delta = max - min; // realign r and g axes to match if (covariance[0][2] < 0) delta[0] = -delta[0]; // r vs b @@ -664,10 +663,13 @@ void BC1Encoder::FindEndpoints(EncodeResults &result, const CBlock &pixels, cons // using the covariance matrix, stretch the delta vector towards the primary axis of the data using power // iteration the end result of this may actually be the same as the least squares approach, will have to do more // research - for (unsigned power_iter = 0; power_iter < _power_iterations; power_iter++) { delta = covariance * delta; } + for (unsigned power_iter = 0; power_iter < _power_iterations; power_iter++) { + delta = covariance.mult(delta); + } // if we found any correlation, then this is our new axis. otherwise we fallback to the luma vector - float k = delta.MaxAbs(3); + auto delta_abs = delta.abs(); + float k = *std::max(delta_abs.begin(), delta_abs.end()); if (k >= 2) { axis = delta * (2048.0f / k); } axis *= 16; @@ -678,13 +680,12 @@ void BC1Encoder::FindEndpoints(EncodeResults &result, const CBlock &pixels, cons int min_index = 0, max_index = 0; for (int i = 0; i < 16; i++) { - auto val = pixels.Get(i); - if (ignore_black && val.IsBlack()) continue; + Color val = pixels.Get(i); //todo: fix this mess + if (ignore_black && (val.r() | val.g() | val.b()) < 4) continue; - auto color_vec = Vector4::FromColorRGB(val); // since axis is constant here, I dont think its magnitude actually matters, // since we only care about the min or max dot product - float dot = color_vec.Dot(axis); + float dot = (Vec(val)).dot(axis); if (dot > max_dot) { max_dot = dot; max_index = i;