Reorganize and add Window class

This commit is contained in:
Andrew Cassidy 2022-06-05 17:12:04 -07:00
parent f88212af85
commit 6f075b6c1d
35 changed files with 612 additions and 282 deletions

View File

@ -19,6 +19,7 @@ file(GLOB SOURCE_FILES
"quicktex/*.cpp"
"quicktex/util/*.cpp"
"quicktex/ctests/*.cpp"
"quicktex/texture/*.cpp"
"quicktex/s3tc/*.cpp"
"quicktex/s3tc/bc1/*.cpp"
"quicktex/s3tc/bc3/*.cpp"
@ -31,6 +32,7 @@ file(GLOB HEADER_FILES
"quicktex/*.h"
"quicktex/util/*.h"
"quicktex/ctests/*.h"
"quicktex/texture/*.h"
"quicktex/s3tc/*.h"
"quicktex/s3tc/bc1/*.h"
"quicktex/s3tc/bc3/*.h"

View File

@ -19,9 +19,12 @@
#pragma once
#include "Vec.h"
#include "bitbash.h"
#include "util/bitbash.h"
namespace quicktex::color {
namespace quicktex {
using Color = Vec<uint8_t, 4>;
using ColorRGB = Vec<uint8_t, 3>;
constexpr size_t uint5_max = (1 << 5) - 1;
constexpr size_t uint6_max = (1 << 6) - 1;
@ -71,4 +74,4 @@ template <typename T = int16_t> Vec<T, 3> precise_round_565(Vec<float, 3> &v) {
return rounded;
}
} // namespace quicktex::color
} // namespace quicktex

View File

@ -22,7 +22,7 @@
#include <memory>
#include "ColorBlock.h"
#include "Texture.h"
#include "texture/RawTexture.h"
namespace quicktex {
@ -46,19 +46,19 @@ template <class T> class BlockDecoder : public Decoder<T> {
virtual DecodedBlock DecodeBlock(const EncodedBlock &block) const = 0;
virtual RawTexture Decode(const T &encoded) const override {
auto decoded = RawTexture(encoded.Width(), encoded.Height());
auto decoded = RawTexture(encoded.width, encoded.height);
int blocks_x = encoded.BlocksX();
int blocks_y = encoded.BlocksY();
int blocks_x = encoded.bwidth();
int blocks_y = encoded.bheight();
// from experimentation, multithreading this using OpenMP actually makes decoding slower
// due to thread creation/teardown taking longer than the decoding process itself.
// As a result, this is left as a serial operation despite being embarassingly parallelizable
for (int y = 0; y < blocks_y; y++) {
for (int x = 0; x < blocks_x; x++) {
auto block = encoded.GetBlock(x, y);
auto block = encoded.get_block(x, y);
auto pixels = DecodeBlock(block);
decoded.SetBlock<BlockWidth, BlockHeight>(x, y, pixels);
decoded.set_block<BlockWidth, BlockHeight>(x, y, pixels);
}
}

View File

@ -22,7 +22,7 @@
#include <memory>
#include "ColorBlock.h"
#include "Texture.h"
#include "texture/RawTexture.h"
namespace quicktex {
@ -46,10 +46,10 @@ template <typename T> class BlockEncoder : public Encoder<T> {
virtual EncodedBlock EncodeBlock(const DecodedBlock &block) const = 0;
virtual T Encode(const RawTexture &decoded) const override {
auto encoded = T(decoded.Width(), decoded.Height());
auto encoded = T(decoded.width, decoded.height);
int blocks_x = encoded.BlocksX();
int blocks_y = encoded.BlocksY();
int blocks_x = encoded.bwidth();
int blocks_y = encoded.bheight();
// from experimentation, multithreading this using OpenMP sometimes actually makes encoding slower
// due to thread creation/teardown taking longer than the encoding process itself.
@ -58,9 +58,9 @@ template <typename T> class BlockEncoder : public Encoder<T> {
#pragma omp parallel for if (blocks_x * blocks_y >= MTThreshold())
for (int y = 0; y < blocks_y; y++) {
for (int x = 0; x < blocks_x; x++) {
auto pixels = decoded.GetBlock<BlockWidth, BlockHeight>(x, y);
auto pixels = decoded.get_block<BlockWidth, BlockHeight>(x, y);
auto block = EncodeBlock(pixels);
encoded.SetBlock(x, y, block);
encoded.set_block(x, y, block);
}
}

View File

@ -28,8 +28,6 @@
namespace quicktex {
using namespace quicktex::util;
OldColor::OldColor(Vector4Int v) {
if (v.MaxAbs() > 0xFF) throw std::invalid_argument("Vector members out of range");
for (int i = 0; i < 4; i++) {

View File

@ -22,6 +22,8 @@
#include <cstddef> // for size_t
#include <cstdint> // for uint8_t, uint16_t
#include "Vec.h"
namespace quicktex {
class Vector4;
class Vector4Int;
@ -84,16 +86,29 @@ class OldColor {
int GetLuma() const { return (13938U * r + 46869U * g + 4729U * b + 32768U) >> 16U; } // REC709 weightings
operator Vec<uint8_t, 4>() const { return {r, g, b, a}; }
OldColor(const Vec<uint8_t, 4> v) {
r = v.r();
g = v.g();
b = v.b();
a = v.a();
}
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};
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)
} // namespace quicktex

View File

@ -1,187 +0,0 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 <climits>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <vector>
#include "ColorBlock.h"
#include "OldColor.h"
namespace quicktex {
class Texture {
public:
virtual ~Texture() = default;
virtual int Width() const { return _width; }
virtual int Height() const { return _height; }
virtual std::tuple<int, int> Size() const { return std::tuple<int, int>(_width, _height); }
/**
* The texture's total size
* @return The size of the texture in bytes.
*/
virtual size_t NBytes() const noexcept = 0;
virtual const uint8_t *Data() const noexcept = 0;
virtual uint8_t *Data() noexcept = 0;
protected:
Texture(int width, int height) : _width(width), _height(height) {
if (width <= 0) throw std::invalid_argument("Texture width must be greater than 0");
if (height <= 0) throw std::invalid_argument("Texture height must be greater than 0");
}
int _width;
int _height;
};
class RawTexture : public Texture {
using Base = Texture;
public:
/**
* Create a new RawTexture
* @param width width of the texture in pixels
* @param height height of the texture in pixels
*/
RawTexture(int width, int height) : Base(width, height), _pixels(_width * _height) {}
OldColor GetPixel(int x, int y) const {
if (x < 0 || x >= _width) throw std::invalid_argument("x value out of range.");
if (y < 0 || y >= _height) throw std::invalid_argument("y value out of range.");
return _pixels.at(x + (y * _width));
}
void SetPixel(int x, int y, OldColor val) {
if (x < 0 || x >= _width) throw std::invalid_argument("x value out of range.");
if (y < 0 || y >= _height) throw std::invalid_argument("y value out of range.");
_pixels.at(x + (y * _width)) = val;
}
size_t NBytes() const noexcept override { return static_cast<unsigned long>(Width() * Height()) * sizeof(OldColor); }
template <int N, int M> ColorBlock<N, M> GetBlock(int block_x, int block_y) const {
if (block_x < 0) throw std::out_of_range("x value out of range.");
if (block_y < 0) throw std::out_of_range("y value out of range.");
// coordinates in the image of the top-left pixel of the selected block
ColorBlock<N, M> block;
int pixel_x = block_x * N;
int pixel_y = block_y * M;
if (pixel_x + N < _width && pixel_y + M < _height) {
// fast memcpy if the block is entirely inside the bounds of the texture
for (int y = 0; y < M; y++) {
// copy each row into the ColorBlock
block.SetRow(y, &_pixels[pixel_x + (_width * (pixel_y + y))]);
}
} else {
// slower pixel-wise copy if the block goes over the edges
for (int x = 0; x < N; x++) {
for (int y = 0; y < M; y++) { block.Set(x, y, GetPixel((pixel_x + x) % _width, (pixel_y + y) % _height)); }
}
}
return block;
}
template <int N, int M> void SetBlock(int block_x, int block_y, const ColorBlock<N, M> &block) {
if (block_x < 0) throw std::out_of_range("x value out of range.");
if (block_y < 0) throw std::out_of_range("y value out of range.");
// coordinates in the image of the top-left pixel of the selected block
int pixel_x = block_x * N;
int pixel_y = block_y * M;
if (pixel_x + N < _width && pixel_y + M < _height) {
// fast row-wise memcpy if the block is entirely inside the bounds of the texture
for (int y = 0; y < M; y++) {
// copy each row out of the ColorBlock
block.GetRow(y, &_pixels[pixel_x + (_width * (pixel_y + y))]);
}
} else {
// slower pixel-wise copy if the block goes over the edges
for (int x = 0; x < N; x++) {
for (int y = 0; y < M; y++) { SetPixel((pixel_x + x) % _width, (pixel_y + y) % _height, block.Get(x, y)); }
}
}
}
virtual const uint8_t *Data() const noexcept override { return reinterpret_cast<const uint8_t *>(_pixels.data()); }
virtual uint8_t *Data() noexcept override { return reinterpret_cast<uint8_t *>(_pixels.data()); }
protected:
std::vector<OldColor> _pixels;
};
template <typename B> class BlockTexture final : public Texture {
private:
std::vector<B> _blocks;
int _width_b;
int _height_b;
public:
using BlockType = B;
using Base = Texture;
/**
* Create a new BlockTexture
* @param width width of the texture in pixels. must be divisible by B::Width
* @param height height of the texture in pixels. must be divisible by B::Height
*/
BlockTexture(int width, int height) : Base(width, height) {
_width_b = (_width + B::Width - 1) / B::Width;
_height_b = (_height + B::Height - 1) / B::Height;
_blocks = std::vector<B>(_width_b * _height_b);
}
constexpr int BlocksX() const { return _width_b; }
constexpr int BlocksY() const { return _height_b; }
constexpr std::tuple<int, int> BlocksXY() const { return std::tuple<int, int>(_width_b, _height_b); }
B GetBlock(int x, int y) const {
if (x < 0 || x >= _width_b) throw std::out_of_range("x value out of range.");
if (y < 0 || y >= _height_b) throw std::out_of_range("y value out of range.");
return _blocks.at(x + (y * _width_b));
}
void SetBlock(int x, int y, const B &val) {
if (x < 0 || x >= _width_b) throw std::out_of_range("x value out of range.");
if (y < 0 || y >= _height_b) throw std::out_of_range("y value out of range.");
_blocks.at(x + (y * _width_b)) = val;
}
size_t NBytes() const noexcept override { return _blocks.size() * sizeof(B); }
const uint8_t *Data() const noexcept override { return reinterpret_cast<const uint8_t *>(_blocks.data()); }
uint8_t *Data() noexcept override { return reinterpret_cast<uint8_t *>(_blocks.data()); }
};
} // namespace quicktex

View File

@ -117,24 +117,24 @@ template <typename T, size_t N> class Vec {
// region accessor shortcuts
// RGBA accessors
std::enable_if<N >= 1, T> r() const { return _c[0]; }
std::enable_if<N >= 1, T &> r() { return _c[0]; }
std::enable_if<N >= 2, T> g() const { return _c[1]; }
std::enable_if<N >= 2, T &> g() { return _c[1]; }
std::enable_if<N >= 3, T> b() const { return _c[2]; }
std::enable_if<N >= 3, T &> b() { return _c[2]; }
std::enable_if<N >= 4, T> a() const { return _c[3]; }
std::enable_if<N >= 4, T &> a() { return _c[3]; }
T r() const { return _c[0]; }
T &r() { return _c[0]; }
template <typename S = T> std::enable_if_t<N >= 2, S> g() const { return _c[1]; }
template <typename S = T> std::enable_if_t<N >= 2, S &> g() { return _c[1]; }
template <typename S = T> std::enable_if_t<N >= 3, S> b() const { return _c[2]; }
template <typename S = T> std::enable_if_t<N >= 3, S &> b() { return _c[2]; }
template <typename S = T> std::enable_if_t<N >= 4, S> a() const { return _c[3]; }
template <typename S = T> std::enable_if_t<N >= 4, S &> a() { return _c[3]; }
// XYZW accessors
std::enable_if<N >= 1, T> x() const { return _c[0]; }
std::enable_if<N >= 1, T &> x() { return _c[0]; }
std::enable_if<N >= 2, T> y() const { return _c[1]; }
std::enable_if<N >= 2, T &> y() { return _c[1]; }
std::enable_if<N >= 3, T> z() const { return _c[2]; }
std::enable_if<N >= 3, T &> z() { return _c[2]; }
std::enable_if<N >= 4, T> w() const { return _c[3]; }
std::enable_if<N >= 4, T &> w() { return _c[3]; }
T x() const { return _c[0]; }
T &x() { return _c[0]; }
template <typename S = T> std::enable_if_t<N >= 2, S> y() const { return _c[1]; }
template <typename S = T> std::enable_if_t<N >= 2, S &> y() { return _c[1]; }
template <typename S = T> std::enable_if_t<N >= 3, S> z() const { return _c[2]; }
template <typename S = T> std::enable_if_t<N >= 3, S &> z() { return _c[2]; }
template <typename S = T> std::enable_if_t<N >= 4, S> w() const { return _c[3]; }
template <typename S = T> std::enable_if_t<N >= 4, S &> w() { return _c[3]; }
// endregion
// region simple operators

View File

@ -25,8 +25,9 @@
#include "Decoder.h"
#include "Encoder.h"
#include "OldColor.h"
#include "Texture.h"
#include "_bindings.h"
#include "texture/RawTexture.h"
#include "texture/Texture.h"
#define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x)
@ -60,14 +61,14 @@ PYBIND11_MODULE(_quicktex, m) {
py::class_<Texture> texture(m, "Texture", py::buffer_protocol());
texture.def_property_readonly("nbytes", &Texture::NBytes);
texture.def_property_readonly("nbytes", &Texture::nbytes);
texture.def_property_readonly("size", &Texture::Size);
texture.def_property_readonly("width", &Texture::Width);
texture.def_property_readonly("height", &Texture::Height);
texture.def_readonly("width", &Texture::width);
texture.def_readonly("height", &Texture::height);
texture.def_buffer([](Texture &t) { return py::buffer_info(t.Data(), t.NBytes()); });
texture.def_buffer([](Texture &t) { return py::buffer_info(t.data(), t.nbytes()); });
texture.def("tobytes",
[](const Texture &t) { return py::bytes(reinterpret_cast<const char *>(t.Data()), t.NBytes()); });
[](const Texture &t) { return py::bytes(reinterpret_cast<const char *>(t.data()), t.nbytes()); });
// RawTexture
@ -76,7 +77,9 @@ PYBIND11_MODULE(_quicktex, m) {
raw_texture.def(py::init<int, int>(), "width"_a, "height"_a);
raw_texture.def_static("frombytes", &BufferToTexture<RawTexture>, "data"_a, "width"_a, "height"_a);
DefSubscript2D(raw_texture, &RawTexture::GetPixel, &RawTexture::SetPixel, &RawTexture::Size);
DefSubscript2DRef(
raw_texture, [](RawTexture &self, int x, int y) -> Color { return self.pixel(x, y); },
[](RawTexture &self, int x, int y, Color val) { self.pixel(x, y) = val; }, &RawTexture::Size);
InitS3TC(m);

View File

@ -32,7 +32,7 @@
#include <vector>
#include "OldColor.h"
#include "Texture.h"
#include "texture/BlockTexture.h"
#include "util/math.h"
namespace pybind11::detail {
@ -83,6 +83,52 @@ template <> struct type_caster<OldColor> {
return val;
}
};
template <> struct type_caster<Color> {
public:
PYBIND11_TYPE_CASTER(Color, _("Color"));
bool load(handle src, bool) {
PyObject* source = src.ptr();
PyObject* tmp = PySequence_Tuple(source);
// if the object is not a tuple, return false
if (!tmp) { return false; } // incorrect type
// check the size
Py_ssize_t size = PyTuple_Size(tmp);
if (size < 3 || size > 4) { return false; } // incorrect size
value.a() = 0xFF;
// now we get the contents
for (int i = 0; i < size; i++) {
PyObject* src_chan = PyTuple_GetItem(tmp, i);
PyObject* tmp_chan = PyNumber_Long(src_chan);
if (!tmp_chan) return false; // incorrect channel type
auto chan = PyLong_AsLong(tmp_chan);
if (chan > 0xFF || chan < 0) return false; // item out of range
value[static_cast<unsigned>(i)] = static_cast<uint8_t>(chan);
Py_DECREF(tmp_chan);
}
Py_DECREF(tmp);
return !PyErr_Occurred();
}
static handle cast(Color src, return_value_policy, handle) {
PyObject* val = PyTuple_New(4);
for (int i = 0; i < 4; i++) {
PyObject* chan = PyLong_FromLong(src[static_cast<unsigned>(i)]);
PyTuple_SetItem(val, i, chan);
}
return val;
}
};
} // namespace pybind11::detail
namespace py = pybind11;
@ -114,7 +160,7 @@ template <typename T> T BufferToTexture(py::buffer buf, int width, int height) {
auto info = buf.request(false);
auto output = T(width, height);
auto dst_size = output.NBytes();
auto dst_size = output.nbytes();
if (info.format != py::format_descriptor<uint8_t>::format())
throw std::runtime_error("Incompatible format in python buffer: expected a byte array.");
@ -129,7 +175,7 @@ template <typename T> T BufferToTexture(py::buffer buf, int width, int height) {
throw std::runtime_error("Incompatible format in python buffer: Incorrect number of dimensions.");
}
std::memcpy(output.Data(), info.ptr, dst_size);
std::memcpy(output.data(), info.ptr, dst_size);
return output;
}
@ -197,6 +243,32 @@ void DefSubscript2D(Tpy t, Getter&& get, Setter&& set, Extent&& ext) {
"key"_a, "value"_a);
}
// TODO: untangle this mess
template <typename Tpy, typename Getter, typename Setter, typename Extent>
void DefSubscript2DRef(Tpy t, Getter&& get, Setter&& set, Extent&& ext) {
using T = typename Tpy::type;
using V = typename std::remove_cvref_t<std::invoke_result_t<Getter, T&, int, int>>;
using Coords = std::tuple<int, int>;
t.def(
"__getitem__",
[get, ext](T& self, Coords pnt) {
Coords s = (self.*ext)();
int x = PyIndex(std::get<0>(pnt), std::get<0>(s), "x");
int y = PyIndex(std::get<1>(pnt), std::get<1>(s), "y");
return get(self, x, y);
},
"key"_a);
t.def(
"__setitem__",
[set, ext](T& self, Coords pnt, const V& val) {
Coords s = (self.*ext)();
int x = PyIndex(std::get<0>(pnt), std::get<0>(s), "x");
int y = PyIndex(std::get<1>(pnt), std::get<1>(s), "y");
set(self, x, y, val);
},
"key"_a, "value"_a);
}
template <typename B> py::class_<B> BindBlock(py::module_& m, const char* name) {
const char* frombytes_doc = R"doc(
Create a new {0} by copying a bytes-like object.
@ -259,11 +331,11 @@ template <typename B> py::class_<BlockTexture<B>> BindBlockTexture(py::module_&
block_texture.def_static("from_bytes", &BufferToTexture<BTex>, "data"_a, "width"_a, "height"_a,
Format(from_bytes_str, name).c_str());
block_texture.def_property_readonly("width_blocks", &BTex::BlocksX, "The width of the texture in blocks.");
block_texture.def_property_readonly("height_blocks", &BTex::BlocksY, "The height of the texture in blocks.");
block_texture.def_property_readonly("size_blocks", &BTex::BlocksXY, "The dimensions of the texture in blocks.");
block_texture.def_property_readonly("width_blocks", &BTex::bwidth, "The width of the texture in blocks.");
block_texture.def_property_readonly("height_blocks", &BTex::bheight, "The height of the texture in blocks.");
block_texture.def_property_readonly("size_blocks", &BTex::bsize, "The dimensions of the texture in blocks.");
DefSubscript2D(block_texture, &BTex::GetBlock, &BTex::SetBlock, &BTex::BlocksXY);
DefSubscript2D(block_texture, &BTex::get_block, &BTex::set_block, &BTex::bsize);
return std::move(block_texture);
}

View File

@ -32,8 +32,6 @@
namespace quicktex::tests {
using namespace quicktex::util;
template <typename T> constexpr auto make_arrays() {
std::vector<std::array<T, xsimd::batch<T>::size>> arrays;
std::array<T, xsimd::batch<T>::size> buffer;

View File

@ -21,14 +21,12 @@
#include <stdexcept>
#include "util/ranges.h"
#include "util/bitbash.h"
#include "util/math.h"
#include "util/ranges.h"
namespace quicktex::s3tc {
using namespace quicktex::util;
uint16_t BC1Block::GetColor0Raw() const { return pack<uint16_t>(_color0, 8); }
uint16_t BC1Block::GetColor1Raw() const { return pack<uint16_t>(_color1, 8); }
@ -40,10 +38,10 @@ BC1Block::SelectorArray BC1Block::GetSelectors() const {
}
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.");
// }
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 = map(unpacked, [](auto row) { return pack<uint8_t>(row, SelectorBits, true); });
}

View File

@ -23,9 +23,9 @@
#include "ColorBlock.h"
#include "Decoder.h"
#include "Texture.h"
#include "s3tc/bc1/BC1Block.h"
#include "s3tc/interpolator/Interpolator.h"
#include "texture/BlockTexture.h"
namespace quicktex::s3tc {
class BC1Decoder final : public BlockDecoder<BlockTexture<BC1Block>> {

View File

@ -33,20 +33,18 @@
#include "Histogram.h"
#include "Matrix4x4.h"
#include "OldColor.h"
#include "Texture.h"
#include "Vector4.h"
#include "Vector4Int.h"
#include "s3tc/bc1/BC1Block.h"
#include "s3tc/bc1/OrderTable.h"
#include "s3tc/bc1/SingleColorTable.h"
#include "texture/Texture.h"
#include "util/bitbash.h"
#include "util/bitwiseEnums.h"
#include "util/math.h"
namespace quicktex::s3tc {
using namespace quicktex::util;
// constructors
BC1Encoder::BC1Encoder(unsigned int level, ColorMode color_mode, InterpolatorPtr interpolator)

View File

@ -29,10 +29,10 @@
#include "ColorBlock.h"
#include "Encoder.h"
#include "OldColor.h"
#include "Texture.h"
#include "s3tc/bc1/BC1Block.h"
#include "s3tc/bc1/SingleColorTable.h"
#include "s3tc/interpolator/Interpolator.h"
#include "texture/BlockTexture.h"
namespace quicktex {
class Vector4;

View File

@ -29,8 +29,6 @@
namespace quicktex::s3tc {
using namespace quicktex::util;
struct BC1MatchEntry {
uint8_t high;
uint8_t low;

View File

@ -23,11 +23,11 @@
#include "ColorBlock.h"
#include "Decoder.h"
#include "Texture.h"
#include "s3tc/bc1/BC1Decoder.h"
#include "s3tc/bc3/BC3Block.h"
#include "s3tc/bc4/BC4Decoder.h"
#include "s3tc/interpolator/Interpolator.h"
#include "texture/BlockTexture.h"
namespace quicktex::s3tc {

View File

@ -23,11 +23,11 @@
#include "ColorBlock.h"
#include "Encoder.h"
#include "Texture.h"
#include "s3tc/bc1/BC1Encoder.h"
#include "s3tc/bc3/BC3Block.h"
#include "s3tc/bc4/BC4Encoder.h"
#include "s3tc/interpolator/Interpolator.h"
#include "texture/BlockTexture.h"
namespace quicktex::s3tc {

View File

@ -28,8 +28,6 @@
namespace quicktex::s3tc {
using namespace quicktex::util;
BC4Block::SelectorArray BC4Block::GetSelectors() const {
auto packed = pack<uint64_t>(_selectors, 8);
auto rows = unpack<uint16_t, Height>(packed, SelectorBits * Width);

View File

@ -22,10 +22,10 @@
#include <cstdint>
#include <stdexcept>
#include "../../ColorBlock.h"
#include "../../Decoder.h"
#include "../../Texture.h"
#include "BC4Block.h"
#include "ColorBlock.h"
#include "Decoder.h"
#include "s3tc/bc4/BC4Block.h"
#include "texture/BlockTexture.h"
namespace quicktex::s3tc {

View File

@ -22,10 +22,10 @@
#include <cstdint>
#include <stdexcept>
#include "../../ColorBlock.h"
#include "../../Encoder.h"
#include "../../Texture.h"
#include "BC4Block.h"
#include "ColorBlock.h"
#include "Encoder.h"
#include "s3tc/bc4/BC4Block.h"
#include "texture/BlockTexture.h"
namespace quicktex::s3tc {

View File

@ -26,9 +26,9 @@
#include "ColorBlock.h"
#include "Decoder.h"
#include "Texture.h"
#include "s3tc/bc4/BC4Decoder.h"
#include "s3tc/bc5/BC5Block.h"
#include "texture/BlockTexture.h"
namespace quicktex::s3tc {

View File

@ -26,9 +26,9 @@
#include "ColorBlock.h"
#include "Encoder.h"
#include "Texture.h"
#include "s3tc/bc4/BC4Encoder.h"
#include "s3tc/bc5/BC5Block.h"
#include "texture/BlockTexture.h"
namespace quicktex::s3tc {
class BC5Encoder : public BlockEncoder<BlockTexture<BC5Block>> {

View File

@ -29,8 +29,6 @@
namespace quicktex::s3tc {
using namespace quicktex::util;
// region Interpolator implementation
std::unique_ptr<Interpolator> Interpolator::MakeInterpolator(Interpolator::Type type) {
switch (type) {

View File

@ -0,0 +1,70 @@
/* 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/>.
*/
#pragma once
#include <array>
#include <vector>
#include "Texture.h"
namespace quicktex {
template <typename B> class BlockTexture final : public Texture {
private:
std::vector<B> _blocks;
unsigned _width_b;
unsigned _height_b;
public:
using BlockType = B;
using Base = Texture;
/**
* Create a new BlockTexture
* @param width width of the texture in pixels. must be divisible by B::width
* @param height height of the texture in pixels. must be divisible by B::height
*/
BlockTexture(int width, int height) : Base(width, height) {
_width_b = (width + B::Width - 1) / B::Width;
_height_b = (height + B::Height - 1) / B::Height;
_blocks = std::vector<B>(_width_b * _height_b);
}
constexpr unsigned bwidth() const { return _width_b; }
constexpr unsigned bheight() const { return _height_b; }
constexpr std::tuple<int, int> bsize() const { return std::tuple<int, int>(_width_b, _height_b); }
B get_block(unsigned x, unsigned y) const {
if (x >= _width_b) throw std::out_of_range("x value out of range.");
if (y >= _height_b) throw std::out_of_range("y value out of range.");
return _blocks.at(x + (y * _width_b));
}
void set_block(unsigned x, unsigned y, const B &val) {
if (x >= _width_b) throw std::out_of_range("x value out of range.");
if (y >= _height_b) throw std::out_of_range("y value out of range.");
_blocks.at(x + (y * _width_b)) = val;
}
size_t nbytes() const noexcept override { return _blocks.size() * sizeof(B); }
const uint8_t *data() const noexcept override { return reinterpret_cast<const uint8_t *>(_blocks.data()); }
uint8_t *data() noexcept override { return reinterpret_cast<uint8_t *>(_blocks.data()); }
};
} // namespace quicktex

View File

@ -0,0 +1,33 @@
/* 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 "RawTexture.h"
namespace quicktex {
Color RawTexture::pixel(unsigned int x, unsigned int y) const {
if (x < 0 || x >= width) throw std::invalid_argument("x value out of range.");
if (y < 0 || y >= height) throw std::invalid_argument("y value out of range.");
return _pixels.at(x + (y * width));
}
quicktex::Color& RawTexture::pixel(unsigned int x, unsigned int y) {
if (x < 0 || x >= width) throw std::invalid_argument("x value out of range.");
if (y < 0 || y >= height) throw std::invalid_argument("y value out of range.");
return _pixels.at(x + (y * width));
}
} // namespace quicktex

View File

@ -0,0 +1,97 @@
/* 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/>.
*/
#pragma once
#include <array>
#include <climits>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <vector>
#include "Color.h"
#include "ColorBlock.h"
#include "OldColor.h"
#include "texture/Texture.h"
namespace quicktex {
class RawTexture : public Texture {
using Base = Texture;
public:
/**
* Create a new RawTexture
* @param width width of the texture in pixels
* @param height height of the texture in pixels
*/
RawTexture(int width, int height) : Base(width, height), _pixels(width * height) {}
quicktex::Color pixel(unsigned x, unsigned y) const;
quicktex::Color &pixel(unsigned x, unsigned y);
quicktex::Color pixel_wrapped(unsigned x, unsigned y) const { return pixel(x % width, y % height); }
quicktex::Color &pixel_wrapped(unsigned x, unsigned y) { return pixel(x % width, y % height); }
size_t nbytes() const noexcept override { return static_cast<size_t>(width * height) * sizeof(quicktex::Color); }
template <int N, int M> quicktex::ColorBlock<N, M> get_block(int block_x, int block_y) const {
if (block_x < 0) throw std::out_of_range("x value out of range.");
if (block_y < 0) throw std::out_of_range("y value out of range.");
// coordinates in the image of the top-left pixel of the selected block
quicktex::ColorBlock<N, M> block;
int pixel_x = block_x * N;
int pixel_y = block_y * M;
// slower pixel-wise copy if the block goes over the edges
for (int x = 0; x < N; x++) {
for (int y = 0; y < M; y++) { block.Set(x, y, pixel((pixel_x + x) % width, (pixel_y + y) % height)); }
}
return block;
}
template <int N, int M> void set_block(int block_x, int block_y, const quicktex::ColorBlock<N, M> &block) {
if (block_x < 0) throw std::out_of_range("x value out of range.");
if (block_y < 0) throw std::out_of_range("y value out of range.");
// coordinates in the image of the top-left pixel of the selected block
int pixel_x = block_x * N;
int pixel_y = block_y * M;
// slower pixel-wise copy if the block goes over the edges
for (int x = 0; x < N; x++) {
for (int y = 0; y < M; y++) { pixel((pixel_x + x) % width, (pixel_y + y) % height) = block.Get(x, y); }
}
}
virtual const uint8_t *data() const noexcept override { return reinterpret_cast<const uint8_t *>(_pixels.data()); }
virtual uint8_t *data() noexcept override { return reinterpret_cast<uint8_t *>(_pixels.data()); }
protected:
std::vector<quicktex::Color> _pixels;
};
} // namespace quicktex

View File

@ -0,0 +1,64 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 <climits>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <vector>
#include "Color.h"
#include "ColorBlock.h"
#include "OldColor.h"
#include "Window.h"
namespace quicktex {
class Texture {
public:
const unsigned width;
const unsigned height;
virtual ~Texture() = default;
virtual std::tuple<unsigned, unsigned> Size() const { return {width, height}; }
/**
* The texture's total size
* @return The size of the texture in bytes.
*/
virtual size_t nbytes() const noexcept = 0;
virtual const uint8_t *data() const noexcept = 0;
virtual uint8_t *data() noexcept = 0;
protected:
Texture(unsigned width, unsigned height) : width(width), height(height) {}
};
} // namespace quicktex

View File

@ -0,0 +1,90 @@
/* 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 "Window.h"
#include "texture/RawTexture.h"
namespace quicktex {
// Window
Window::Window(RawTexture& texture, unsigned int width, unsigned int height, unsigned int x, unsigned int y)
: width(width), height(height), x(x), y(y), _texture(texture) {
assert(x < texture.width);
assert(y < texture.height);
}
Color& Window::pixel(unsigned px, unsigned py) {
assert(px < width && py < height);
return _texture.pixel(x + px, y + py);
}
Color Window::pixel(unsigned px, unsigned py) const {
assert(px < width && py < height);
return _texture.pixel(x + px, y + py);
}
WindowIterator Window::begin() { return WindowIterator(*this, 0, 0); }
WindowIterator Window::end() { return WindowIterator(*this, 0, height); }
WindowIterator Window::row_begin(unsigned int row) { return WindowIterator(*this, 0, row); }
WindowIterator Window::row_end(unsigned int row) { return WindowIterator(*this, 0, row + 1); }
bool Window::operator==(const Window& rhs) const {
return width == rhs.width && height == rhs.height && x == rhs.x && y == rhs.y && &_texture == &rhs._texture;
}
// WindowIterator
WindowIterator::WindowIterator(Window& view, unsigned int x, unsigned int y) : x(x), y(y), _view(&view) {
assert(x < view.width);
assert(y < view.height || (y == view.height && x == 0));
// if y == the height, and x == 0, then this is a sentinel for the end of iteration, and cannot be dereferenced
}
WindowIterator& quicktex::WindowIterator::operator++() { // prefix increment
x++;
if (x >= _view->width) {
x = 0;
y++;
}
return *this;
}
WindowIterator WindowIterator::operator++(int) { // postfix increment
WindowIterator old = *this;
++(*this);
return old;
}
Color& WindowIterator::operator*() const { // dereference operator
assert(_view != nullptr);
assert(x < _view->width && y < _view->height);
return _view->pixel(x, y);
}
Color* WindowIterator::operator->() { return &(**this); } // returns a pointer to what's returned by operator*
bool WindowIterator::operator==(const WindowIterator& rhs) const {
return x == rhs.x && y == rhs.y && _view == rhs._view;
}
static_assert(std::forward_iterator<WindowIterator>);
static_assert(sized_range<Window>);
} // namespace quicktex

82
quicktex/texture/Window.h Normal file
View File

@ -0,0 +1,82 @@
/* 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/>.
*/
#pragma once
#include "Color.h"
#include "util/ranges.h"
namespace quicktex {
// forward declarations
class WindowIterator;
class RawTexture;
/**
* Class representing a window into a RawTexture
*/
class Window {
public:
typedef Color value_type;
const unsigned width, height;
const unsigned x, y;
Window(RawTexture &texture, unsigned width, unsigned height, unsigned x, unsigned y);
Color &pixel(unsigned px, unsigned py);
Color pixel(unsigned px, unsigned py) const;
WindowIterator begin();
WindowIterator end();
WindowIterator row_begin(unsigned row);
WindowIterator row_end(unsigned row);
size_t size() const { return width * height; }
bool operator==(const Window &rhs) const;
private:
RawTexture &_texture;
};
/**
* Iterator returned by Window
*/
class WindowIterator {
public:
typedef long long difference_type;
typedef Color value_type;
unsigned x, y;
WindowIterator(Window &view, unsigned x, unsigned y);
WindowIterator() : x(0), y(0), _view(nullptr) {}
Color &operator*() const; // dereference
Color *operator->(); // member access
WindowIterator &operator++(); // prefix increment
WindowIterator operator++(int); // postfix increment
bool operator==(const WindowIterator &rhs) const;
private:
Window *_view;
};
} // namespace quicktex

View File

@ -35,7 +35,7 @@
#define assert5bit(x) assert(x <= UINT5_MAX)
#define assert6bit(x) assert(x <= UINT6_MAX)
namespace quicktex::util {
namespace quicktex {
template <size_t N, typename S> S scale_from_8(S v) {
static_assert(N < 8);
@ -309,4 +309,4 @@ template <typename P, typename 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);
}
} // namespace quicktex::util
} // namespace quicktex

View File

@ -21,7 +21,7 @@
#include <type_traits>
namespace quicktex::util {
namespace quicktex {
// Thanks dkavolis
template <typename E> requires std::is_enum_v<E>

View File

@ -32,7 +32,7 @@
#include "xsimd/xsimd.hpp"
namespace quicktex::util {
namespace quicktex {
// std::ranges::range is currently not usable by default in libc++
template <class T>

View File

@ -28,7 +28,7 @@
template <typename T> using requires_arch = xsimd::kernel::requires_arch<T>;
namespace quicktex::util::simd {
namespace quicktex::simd {
namespace kernel {
@ -94,4 +94,4 @@ template <class A, class T> inline next_size_t<T> whadd(xsimd::batch<T, A> const
return kernel::whadd(arg, A{});
}
} // namespace quicktex::util::simd
} // namespace quicktex::simd

View File

@ -20,7 +20,7 @@
#pragma once
#include <cstdint>
namespace quicktex::util {
namespace quicktex {
template <class> struct next_size;
template <class T> using next_size_t = typename next_size<T>::type;
template <class T> struct type_tag { using type = T; };