mirror of
https://github.com/drewcassidy/quicktex.git
synced 2024-09-13 06:37:34 +00:00
Bind BC1Block and BC1Texture
This commit is contained in:
parent
28b541d49a
commit
3852da6249
@ -29,6 +29,7 @@
|
||||
#include "Vector4Int.h"
|
||||
|
||||
namespace quicktex {
|
||||
using Coords = std::tuple<int, int>;
|
||||
|
||||
/**
|
||||
* Base class for all compressed blocks
|
||||
@ -46,6 +47,7 @@ template <int N, int M> class Block {
|
||||
public:
|
||||
static constexpr int Width = N;
|
||||
static constexpr int Height = M;
|
||||
static constexpr Coords Dimensions = Coords(Width, Height);
|
||||
};
|
||||
|
||||
template <int N, int M> class ColorBlock : public Block<N, M> {
|
||||
|
@ -151,11 +151,10 @@ class RawTexture : public Texture {
|
||||
virtual uint8_t *Data() noexcept override { return reinterpret_cast<uint8_t *>(_pixels); }
|
||||
|
||||
protected:
|
||||
|
||||
Color *_pixels;
|
||||
};
|
||||
|
||||
template <typename B> class BlockTexture : public Texture {
|
||||
template <typename B> class BlockTexture final: public Texture {
|
||||
public:
|
||||
using BlockType = B;
|
||||
using Base = Texture;
|
||||
@ -180,7 +179,7 @@ template <typename B> class BlockTexture : public Texture {
|
||||
* Copy constructor
|
||||
* @param other object to copy
|
||||
*/
|
||||
BlockTexture(const BlockTexture<B> &other) : BlockTexture(other.width, other.height) { std::memcpy(_blocks, other._blocks, Size()); }
|
||||
BlockTexture(const BlockTexture<B> &other) : BlockTexture(other._width, other._height) { std::memcpy(_blocks, other._blocks, Size()); }
|
||||
|
||||
/**
|
||||
* assignment operator
|
||||
@ -201,29 +200,28 @@ template <typename B> class BlockTexture : public Texture {
|
||||
|
||||
~BlockTexture() { delete[] _blocks; }
|
||||
|
||||
constexpr int BlocksX() const { return _width / B::Width; }
|
||||
constexpr int BlocksY() const { return _height / B::Height; }
|
||||
constexpr int BlocksX() const { return (_width + B::Width - 1) / B::Width; }
|
||||
constexpr int BlocksY() const { return (_height + B::Height - 1) / B::Height; }
|
||||
constexpr std::tuple<int, int> BlocksXY() const { return std::tuple<int, int>(BlocksX(), BlocksY()); }
|
||||
|
||||
virtual B GetBlock(int x, int y) const {
|
||||
if (x < 0 || x >= BlocksX()) throw std::range_error("x value out of range.");
|
||||
if (y < 0 || y >= BlocksY()) throw std::range_error("y value out of range.");
|
||||
B GetBlock(int x, int y) const {
|
||||
if (x < 0 || x >= BlocksX()) throw std::out_of_range("x value out of range.");
|
||||
if (y < 0 || y >= BlocksY()) throw std::out_of_range("y value out of range.");
|
||||
return _blocks[x + (y * _width)];
|
||||
}
|
||||
|
||||
virtual void SetBlock(int x, int y, const B &val) {
|
||||
if (x < 0 || x >= BlocksX()) throw std::range_error("x value out of range.");
|
||||
if (y < 0 || y >= BlocksY()) throw std::range_error("y value out of range.");
|
||||
void SetBlock(int x, int y, const B &val) {
|
||||
if (x < 0 || x >= BlocksX()) throw std::out_of_range("x value out of range.");
|
||||
if (y < 0 || y >= BlocksY()) throw std::out_of_range("y value out of range.");
|
||||
_blocks[x + (y * _width)] = val;
|
||||
}
|
||||
|
||||
virtual size_t Size() const noexcept override { return (size_t)(BlocksX() * BlocksY()) * sizeof(B); }
|
||||
size_t Size() const noexcept override { return (size_t)(BlocksX() * BlocksY()) * sizeof(B); }
|
||||
|
||||
virtual const uint8_t *Data() const noexcept override { return reinterpret_cast<const uint8_t *>(_blocks); }
|
||||
virtual uint8_t *Data() noexcept override { return reinterpret_cast<uint8_t *>(_blocks); }
|
||||
const uint8_t *Data() const noexcept override { return reinterpret_cast<const uint8_t *>(_blocks); }
|
||||
uint8_t *Data() noexcept override { return reinterpret_cast<uint8_t *>(_blocks); }
|
||||
|
||||
protected:
|
||||
|
||||
B *_blocks;
|
||||
};
|
||||
|
||||
|
@ -29,56 +29,6 @@
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
namespace pybind11::detail {
|
||||
using namespace quicktex;
|
||||
/// Type caster for color class to allow it to be converted to and from a python tuple
|
||||
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 quicktex::bindings {
|
||||
|
||||
void InitS3TC(py::module_ &m);
|
||||
@ -100,17 +50,16 @@ PYBIND11_MODULE(_quicktex, m) {
|
||||
texture.def_buffer([](Texture &t) { return py::buffer_info(t.Data(), t.Size()); });
|
||||
texture.def("tobytes", [](const Texture &t) { return py::bytes(reinterpret_cast<const char *>(t.Data()), t.Size()); });
|
||||
|
||||
//RawTexture
|
||||
// RawTexture
|
||||
|
||||
py::class_<RawTexture> raw_texture(m, "RawTexture", texture);
|
||||
|
||||
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::Dimensions);
|
||||
|
||||
raw_texture.def_static("frombytes", &BufferToTexture<RawTexture>, "data"_a, "width"_a, "height"_a);
|
||||
|
||||
// InitS3TC(m);
|
||||
InitS3TC(m);
|
||||
}
|
||||
|
||||
} // namespace quicktex::bindings
|
@ -30,9 +30,60 @@
|
||||
#include "Block.h"
|
||||
#include "Color.h"
|
||||
#include "Texture.h"
|
||||
#include "util.h"
|
||||
|
||||
/*namespace pybind11::detail {
|
||||
extern template struct type_caster<quicktex::Color>;
|
||||
} // namespace pybind11::detail*/
|
||||
|
||||
namespace pybind11::detail {
|
||||
template <> struct type_caster<quicktex::Color>;
|
||||
using namespace quicktex;
|
||||
/// Type caster for color class to allow it to be converted to and from a python tuple
|
||||
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;
|
||||
@ -44,7 +95,7 @@ template <typename T> T BufferToTexture(py::buffer buf, int width, int height) {
|
||||
static_assert(std::is_constructible<T, int, int>::value);
|
||||
|
||||
auto info = buf.request(false);
|
||||
auto output = T(width, height); // std::make_shared<T>(width, height);
|
||||
auto output = T(width, height);
|
||||
auto dst_size = output.Size();
|
||||
|
||||
if (info.format != py::format_descriptor<uint8_t>::format()) throw std::runtime_error("Incompatible format in python buffer: expected a byte array.");
|
||||
@ -61,6 +112,24 @@ template <typename T> T BufferToTexture(py::buffer buf, int width, int height) {
|
||||
return output;
|
||||
}
|
||||
|
||||
template <typename T> T BufferToPOD(py::buffer buf) {
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
|
||||
auto info = buf.request(false);
|
||||
|
||||
if (info.format != py::format_descriptor<uint8_t>::format()) throw std::runtime_error("Incompatible format in python buffer: expected a byte array.");
|
||||
if (info.size < (ssize_t)sizeof(T)) std::runtime_error("Incompatible format in python buffer: Input data is smaller than texture size.");
|
||||
if (info.ndim == 1) {
|
||||
if (info.shape[0] < (ssize_t)sizeof(T)) throw std::runtime_error("Incompatible format in python buffer: 1-D buffer has incorrect length.");
|
||||
if (info.strides[0] != 1) throw std::runtime_error("Incompatible format in python buffer: 1-D buffer is not contiguous.");
|
||||
} else {
|
||||
throw std::runtime_error("Incompatible format in python buffer: Incorrect number of dimensions.");
|
||||
}
|
||||
|
||||
const T* ptr = reinterpret_cast<const T*>(info.ptr);
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
inline int PyIndex(int val, int size, std::string name = "index") {
|
||||
if (val < -size || val >= size) throw std::out_of_range(name + " value out of range");
|
||||
if (val < 0) return size + val;
|
||||
@ -98,23 +167,79 @@ template <typename T, typename Getter, typename Setter, typename Extent> void De
|
||||
"key"_a, "value"_a);
|
||||
}
|
||||
|
||||
template <typename B> py::class_<B> BindBlock(py::module_& m, const char* name) {
|
||||
const char* frombytes_doc = R"doc(
|
||||
from_bytes(b) -> {0}
|
||||
|
||||
Create a new {0} by copying a bytes-like object.
|
||||
|
||||
:param b: A bytes-like object at least the size of the block.
|
||||
)doc";
|
||||
|
||||
const char* tobytes_doc = R"doc(
|
||||
tobytes(self) -> bytes
|
||||
|
||||
Pack the {0} into a bytestring.
|
||||
|
||||
:returns: A bytes object of length {1}.
|
||||
)doc";
|
||||
|
||||
py::class_<B> block(m, name, py::buffer_protocol());
|
||||
block.def_static("frombytes", &BufferToPOD<B>, "data"_a, Format(frombytes_doc, name).c_str());
|
||||
|
||||
block.def_readonly_static("width", &B::Width, "The width of the block in pixels.");
|
||||
block.def_readonly_static("height", &B::Height, "The height of the block in pixels.");
|
||||
block.def_readonly_static("dimensions", &B::Dimensions, "The dimensions of the block in pixels.");
|
||||
block.def_property_readonly_static("size", [](py::object) { return sizeof(B); }, "The size of the block in bytes.");
|
||||
|
||||
block.def_buffer([](B& b) { return py::buffer_info(reinterpret_cast<uint8_t*>(&b), sizeof(B)); });
|
||||
block.def(
|
||||
"tobytes", [](const B& b) { return py::bytes(reinterpret_cast<const char*>(&b), sizeof(B)); },
|
||||
Format(tobytes_doc, name, std::to_string(sizeof(B))).c_str());
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
template <typename B> py::class_<BlockTexture<B>> BindBlockTexture(py::module_& m, const char* name) {
|
||||
const auto* const constructor_str = R"doc(
|
||||
__init__(self, width: int, height: int) -> None
|
||||
|
||||
Create a new blank {0} with the given dimensions.
|
||||
If the dimenions are not multiples of the block dimensions, enough blocks will be allocated
|
||||
to cover the entire texture, and it will be implicitly cropped during decoding.
|
||||
|
||||
:param int width: The width of the texture in pixels. Must be > 0.
|
||||
:param int height: The height of the texture in pixels. must be > 0
|
||||
)doc";
|
||||
|
||||
const auto* const from_bytes_str = R"doc(
|
||||
from_bytes(b, width: int, height: int) -> {0}
|
||||
|
||||
Create a new {0} with the given dimensions, and copy a bytes-like object into it.
|
||||
If the dimenions are not multiples of the block dimensions, enough blocks will be allocated
|
||||
to cover the entire texture, and it will be implicitly cropped during decoding.
|
||||
|
||||
:param b: A bytes-like object at least the size of the resulting texture.
|
||||
:param int width: The width of the texture in pixels. Must be > 0.
|
||||
:param int height: The height of the texture in pixels. must be > 0
|
||||
)doc";
|
||||
|
||||
using BTex = BlockTexture<B>;
|
||||
|
||||
py::type texture = py::type::of<Texture>();
|
||||
py::class_<BTex> block_texture(m, name, py::type::of<Texture>(), py::is_final());
|
||||
|
||||
py::class_<BTex> block_texture(m, name, texture);
|
||||
block_texture.def(py::init<int, int>(), "width"_a, "height"_a, Format(constructor_str, name).c_str());
|
||||
block_texture.def_static("from_bytes", &BufferToTexture<BTex>, "data"_a, "width"_a, "height"_a, Format(from_bytes_str, name).c_str());
|
||||
|
||||
block_texture.def(py::init<int, int>(), "width"_a, "height"_a);
|
||||
block_texture.def("get_block", &BTex::GetBlock, "x"_a, "y"_a);
|
||||
block_texture.def("set_block", &BTex::SetBlock, "x"_a, "y"_a, "block"_a);
|
||||
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("dimensions_blocks", &BTex::BlocksXY, "The dimensions of the texture in blocks.");
|
||||
|
||||
block_texture.def_static("from_bytes", &BufferToTexture<BTex>, "data"_a, "width"_a, "height"_a);
|
||||
block_texture.def_property_readonly_static(
|
||||
"block", [](py::object) { return py::type::of<B>(); }, "The block type used by this texture.");
|
||||
|
||||
block_texture.def_property_readonly("blocks_x", &BTex::BlocksX);
|
||||
block_texture.def_property_readonly("blocks_y", &BTex::BlocksY);
|
||||
block_texture.def_property_readonly("blocks_xy", &BTex::BlocksXY);
|
||||
DefSubscript2D(block_texture, &BTex::GetBlock, &BTex::SetBlock, &BTex::BlocksXY);
|
||||
|
||||
DefSubscript2D(block_texture, &BTex::GetPixel, &BTex::SetPixel, &BTex::BlocksXY);
|
||||
return block_texture;
|
||||
}
|
||||
} // namespace quicktex::bindings
|
@ -33,44 +33,69 @@ namespace quicktex::s3tc {
|
||||
#pragma pack(push, 1)
|
||||
class BC1Block : public Block<4, 4> {
|
||||
public:
|
||||
using UnpackedSelectors = std::array<std::array<uint8_t, Width>, Height>;
|
||||
using SelectorArray = std::array<std::array<uint8_t, Width>, Height>;
|
||||
using ColorPair = std::tuple<Color, Color>;
|
||||
|
||||
constexpr BC1Block() { static_assert(sizeof(BC1Block) == 8); }
|
||||
|
||||
uint16_t GetLowColor() const { return static_cast<uint16_t>(_low_color[0] | (_low_color[1] << 8U)); }
|
||||
uint16_t GetHighColor() const { return static_cast<uint16_t>(_high_color[0] | (_high_color[1] << 8U)); }
|
||||
Color GetLowColor32() const { return Color::Unpack565(GetLowColor()); }
|
||||
Color GetHighColor32() const { return Color::Unpack565(GetHighColor()); }
|
||||
|
||||
bool Is3Color() const { return GetLowColor() <= GetHighColor(); }
|
||||
void SetLowColor(uint16_t c) {
|
||||
_low_color[0] = c & 0xFF;
|
||||
_low_color[1] = (c >> 8) & 0xFF;
|
||||
}
|
||||
void SetHighColor(uint16_t c) {
|
||||
_high_color[0] = c & 0xFF;
|
||||
_high_color[1] = (c >> 8) & 0xFF;
|
||||
}
|
||||
uint32_t GetSelector(uint32_t x, uint32_t y) const {
|
||||
assert((x < 4U) && (y < 4U));
|
||||
return (selectors[y] >> (x * SelectorBits)) & SelectorMask;
|
||||
}
|
||||
void SetSelector(uint32_t x, uint32_t y, uint32_t val) {
|
||||
assert((x < 4U) && (y < 4U) && (val < 4U));
|
||||
selectors[y] &= (~(SelectorMask << (x * SelectorBits)));
|
||||
selectors[y] |= (val << (x * SelectorBits));
|
||||
constexpr BC1Block() {
|
||||
SetColor0Raw(0);
|
||||
SetColor1Raw(0);
|
||||
SetSelectorsSolid(0);
|
||||
}
|
||||
|
||||
UnpackedSelectors UnpackSelectors() const {
|
||||
UnpackedSelectors unpacked;
|
||||
constexpr BC1Block(Color color0, Color color1, const SelectorArray& selectors) {
|
||||
SetColor0(color0);
|
||||
SetColor1(color1);
|
||||
SetSelectors(selectors);
|
||||
}
|
||||
|
||||
constexpr BC1Block(Color color0, Color color1, uint8_t solid_mask) {
|
||||
SetColor0(color0);
|
||||
SetColor1(color1);
|
||||
SetSelectorsSolid(solid_mask);
|
||||
}
|
||||
|
||||
uint16_t GetColor0Raw() const { return static_cast<uint16_t>(_color_0[0] | (_color_0[1] << 8U)); }
|
||||
uint16_t GetColor1Raw() const { return static_cast<uint16_t>(_color_1[0] | (_color_1[1] << 8U)); }
|
||||
void SetColor0Raw(uint16_t c) {
|
||||
_color_0[0] = c & 0xFF;
|
||||
_color_0[1] = (c >> 8) & 0xFF;
|
||||
}
|
||||
void SetColor1Raw(uint16_t c) {
|
||||
_color_1[0] = c & 0xFF;
|
||||
_color_1[1] = (c >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
Color GetColor0() const { return Color::Unpack565(GetColor0Raw()); }
|
||||
Color GetColor1() const { return Color::Unpack565(GetColor1Raw()); }
|
||||
ColorPair GetColors() const { return {GetColor0(), GetColor1()}; }
|
||||
|
||||
void SetColor0(Color c) { SetColor0Raw(c.Pack565()); }
|
||||
void SetColor1(Color c) { SetColor1Raw(c.Pack565()); }
|
||||
void SetColors(ColorPair cs) {
|
||||
SetColor0(std::get<0>(cs));
|
||||
SetColor1(std::get<1>(cs));
|
||||
}
|
||||
|
||||
bool Is3Color() const { return GetColor0Raw() <= GetColor1Raw(); }
|
||||
|
||||
SelectorArray GetSelectors() const {
|
||||
SelectorArray unpacked;
|
||||
for (unsigned i = 0; i < 4; i++) { unpacked[i] = Unpack<uint8_t, uint8_t, 2, 4>(selectors[i]); }
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
void PackSelectors(const UnpackedSelectors& unpacked, uint8_t mask = 0) {
|
||||
void SetSelectors(const SelectorArray& unpacked, uint8_t mask = 0) {
|
||||
for (unsigned i = 0; i < 4; i++) { selectors[i] = mask ^ Pack<uint8_t, uint8_t, 2, 4>(unpacked[i]); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Set every row of selectors to the same 8-bit mask. useful for solid-color blocks
|
||||
* @param mask the 8-bit mask to use for each row
|
||||
*/
|
||||
void SetSelectorsSolid(uint8_t mask) {
|
||||
for (unsigned i = 0; i < 4; i++) selectors[i] = mask;
|
||||
}
|
||||
|
||||
constexpr static inline size_t EndpointSize = 2;
|
||||
constexpr static inline size_t SelectorSize = 4;
|
||||
constexpr static inline uint8_t SelectorBits = 2;
|
||||
@ -78,10 +103,8 @@ class BC1Block : public Block<4, 4> {
|
||||
constexpr static inline uint8_t SelectorMask = SelectorValues - 1;
|
||||
|
||||
private:
|
||||
std::array<uint8_t, EndpointSize> _low_color;
|
||||
std::array<uint8_t, EndpointSize> _high_color;
|
||||
|
||||
public:
|
||||
std::array<uint8_t, EndpointSize> _color_0;
|
||||
std::array<uint8_t, EndpointSize> _color_1;
|
||||
std::array<uint8_t, 4> selectors;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
@ -33,9 +33,9 @@ ColorBlock<4, 4> BC1Decoder::DecodeBlock(const BC1Block &block) const { return D
|
||||
|
||||
ColorBlock<4, 4> BC1Decoder::DecodeBlock(const BC1Block &block, bool use_3color) const {
|
||||
auto output = ColorBlock<4, 4>();
|
||||
const auto l = block.GetLowColor();
|
||||
const auto h = block.GetHighColor();
|
||||
const auto selectors = block.UnpackSelectors();
|
||||
const auto l = block.GetColor0Raw();
|
||||
const auto h = block.GetColor1Raw();
|
||||
const auto selectors = block.GetSelectors();
|
||||
const auto colors = _interpolator->InterpolateBC1(l, h, use_3color);
|
||||
|
||||
for (unsigned y = 0; y < 4; y++) {
|
||||
|
@ -394,18 +394,15 @@ BC1Block BC1Encoder::WriteBlockSolid(Color color) const {
|
||||
}
|
||||
}
|
||||
|
||||
block.SetLowColor(max16);
|
||||
block.SetHighColor(min16);
|
||||
block.selectors[0] = mask;
|
||||
block.selectors[1] = mask;
|
||||
block.selectors[2] = mask;
|
||||
block.selectors[3] = mask;
|
||||
block.SetColor0Raw(max16);
|
||||
block.SetColor1Raw(min16);
|
||||
block.SetSelectorsSolid(mask);
|
||||
return block;
|
||||
}
|
||||
|
||||
BC1Block BC1Encoder::WriteBlock(EncodeResults &result) const {
|
||||
BC1Block block;
|
||||
BC1Block::UnpackedSelectors selectors;
|
||||
BC1Block::SelectorArray selectors;
|
||||
uint16_t color1 = result.low.Pack565Unscaled();
|
||||
uint16_t color0 = result.high.Pack565Unscaled();
|
||||
std::array<uint8_t, 4> lut;
|
||||
@ -449,9 +446,9 @@ BC1Block BC1Encoder::WriteBlock(EncodeResults &result) const {
|
||||
if (result.color_mode == ColorMode::ThreeColor) { assert(selectors[y][x] != 3); }
|
||||
}
|
||||
|
||||
block.SetLowColor(color0);
|
||||
block.SetHighColor(color1);
|
||||
block.PackSelectors(selectors);
|
||||
block.SetColor0Raw(color0);
|
||||
block.SetColor1Raw(color1);
|
||||
block.SetSelectors(selectors);
|
||||
return block;
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,10 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../../_bindings.h"
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
@ -45,11 +48,43 @@ void InitBC1(py::module_ &s3tc) {
|
||||
py::options options;
|
||||
options.disable_function_signatures();
|
||||
|
||||
// BC1Encoder
|
||||
py::class_<BC1Encoder> bc1_encoder(bc1, "BC1Encoder" R"doc(
|
||||
Encodes RGB textures to BC1.
|
||||
// region BC1Block
|
||||
auto bc1_block = BindBlock<BC1Block>(bc1, "BC1Block");
|
||||
bc1_block.doc() = "A single BC1 block.";
|
||||
|
||||
bc1_block.def(py::init<>());
|
||||
bc1_block.def(py::init<Color, Color, BC1Block::SelectorArray>(), "color0"_a, "color1"_a, "selectors"_a, R"doc(
|
||||
__init__(self, color0, color1) -> None
|
||||
|
||||
Create a new BC1Block with the specified endpoints and selectors
|
||||
|
||||
:param color0: The first endpoint
|
||||
:param color1: The second endpoint
|
||||
:param selectors: the selectors as a 4x4 list of integers, between 0 and 3 inclusive.
|
||||
)doc");
|
||||
|
||||
bc1_block.def_property("colors", &BC1Block::GetColors, &BC1Block::SetColors, "The block's endpoint colors as a 2-tuple.");
|
||||
bc1_block.def_property("selectors", &BC1Block::GetSelectors, &BC1Block::SetSelectors, R"doc(
|
||||
The block's selectors as a 4x4 list of integers between 0 and 3 inclusive.
|
||||
|
||||
.. note::
|
||||
This is a property, so directly modifying its value will not propogate back to the block.
|
||||
Instead you must read, modify, then write the new value back to the property, like so::
|
||||
|
||||
selectors = block.selectors
|
||||
selectors[0,0] = 0
|
||||
block.selectors = selectors
|
||||
)doc");
|
||||
// endregion
|
||||
|
||||
//region BC1Texture
|
||||
auto bc1_texture = BindBlockTexture<BC1Block>(bc1, "BC1Texture");
|
||||
bc1_texture.doc() = "A texture comprised of BC1 blocks.";
|
||||
//endregion
|
||||
|
||||
//region BC1Encoder
|
||||
py::class_<BC1Encoder> bc1_encoder(bc1, "BC1Encoder", "Encodes RGB textures to BC1.");
|
||||
|
||||
py::enum_<BC1Encoder::EndpointMode>(bc1_encoder, "EndpointMode", "Enum representing various methods of finding endpoints in a block.")
|
||||
.value("LeastSquares", BC1Encoder::EndpointMode::LeastSquares, "Find endpoints using a 2D least squares approach.")
|
||||
.value("BoundingBox", BC1Encoder::EndpointMode::BoundingBox, "Find endpoints using a simple bounding box. Fast but inaccurate.")
|
||||
@ -127,8 +162,9 @@ void InitBC1(py::module_ &s3tc) {
|
||||
bc1_encoder.def_property("power_iterations", &BC1Encoder::GetPowerIterations, &BC1Encoder::SetPowerIterations,
|
||||
"Number of power iterations used with the PCA endpoint mode. Value should be around 4 to 6. "
|
||||
"Automatically clamped to between :py:const:`BC1Encoder.min_power_iterations` and :py:const:`BC1Encoder.max_power_iterations`");
|
||||
//endregion
|
||||
|
||||
// BC1Decoder
|
||||
//region BC1Decoder
|
||||
py::class_<BC1Decoder> bc1_decoder(bc1, "BC1Decoder", R"doc(
|
||||
Base: :py:class:`~quicktex.BlockDecoder`
|
||||
|
||||
@ -147,5 +183,6 @@ void InitBC1(py::module_ &s3tc) {
|
||||
|
||||
bc1_decoder.def_property_readonly("interpolator", &BC1Decoder::GetInterpolator, "The interpolator used by this decoder. This is a readonly property.");
|
||||
bc1_decoder.def_readwrite("write_alpha", &BC1Decoder::write_alpha, "Determines if the alpha channel of the output is written to.");
|
||||
//endregion
|
||||
}
|
||||
} // namespace quicktex::bindings
|
@ -21,7 +21,9 @@
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "ndebug.h"
|
||||
|
||||
@ -73,7 +75,7 @@ template <typename I, typename O, size_t S, size_t C> constexpr auto Unpack(I pa
|
||||
* @param vals Unpacked std::array of type I and size C.
|
||||
* @return Packed integer input of type O.
|
||||
*/
|
||||
template <typename I, typename O, size_t S, size_t C> constexpr auto Pack(const std::array<I, C> &vals) noexcept(ndebug) {
|
||||
template <typename I, typename O, size_t S, size_t C> constexpr auto Pack(const std::array<I, C> &vals) {
|
||||
// type checking
|
||||
static_assert(std::is_unsigned<I>::value, "Unpacked input type must be unsigned");
|
||||
static_assert(std::is_unsigned<O>::value, "Packed output type must be unsigned");
|
||||
@ -81,9 +83,10 @@ template <typename I, typename O, size_t S, size_t C> constexpr auto Pack(const
|
||||
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++) {
|
||||
assert(vals[i] <= (1U << S) - 1U);
|
||||
if (vals[i] > max) throw std::invalid_argument("Input value at index " + std::to_string(i) + " is larger than " + std::to_string(max));
|
||||
packed |= static_cast<O>(vals[i]) << (i * S);
|
||||
}
|
||||
|
||||
@ -147,3 +150,21 @@ constexpr int squarei(int a) { return a * a; }
|
||||
constexpr int absi(int a) { return (a < 0) ? -a : a; }
|
||||
|
||||
template <typename F> constexpr F lerp(F a, F b, F s) { return a + (b - a) * s; }
|
||||
|
||||
template <typename... Args> std::string Format(const char *str, const Args &...args) {
|
||||
auto output = std::string(str);
|
||||
|
||||
std::vector<std::string> values = {{args...}};
|
||||
|
||||
for (unsigned i = 0; i < values.size(); i++) {
|
||||
auto key = "{" + std::to_string(i) + "}";
|
||||
auto value = values[i];
|
||||
while(true) {
|
||||
size_t where = output.find(key);
|
||||
if (where == output.npos) break;
|
||||
output.replace(where, key.length(), value);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
@ -37,6 +37,6 @@ int main() {
|
||||
site.attr("addsitedir")(CUSTOM_SYS_PATH);
|
||||
|
||||
py::module_ nose = py::module_::import("nose");
|
||||
py::module_ tests = py::module_::import("test_BC1");
|
||||
nose.attr("main")("test_BC1");
|
||||
py::module_ tests = py::module_::import("test_texture");
|
||||
nose.attr("main")("test_texture");
|
||||
}
|
Loading…
Reference in New Issue
Block a user