diff --git a/.gitignore b/.gitignore index fb19e63..c2c6052 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # Python env/ +dist/ +build/ +*.egg-info # IDEs **/.idea diff --git a/CMakeLists.txt b/CMakeLists.txt index ed37bb6..d518b56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ add_subdirectory(extern/pybind11) # Collect source files file(GLOB SOURCE_FILES "src/*.cpp" "src/BC*/*.cpp") file(GLOB HEADER_FILES "src/*.h" "src/BC*/*.h") -file(GLOB PYTHON_FILES "src/python/*.cpp" "src/python/*.h") +file(GLOB PYTHON_FILES "python/*.cpp" "python/*.h") file(GLOB TEST_FILES "src/test/*.c" "src/test/*.cpp" "src/test/*.h") # Organize source files together for some IDEs @@ -38,7 +38,7 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") if (CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -Rpass=loop-vectorize -Rpass-analysis=loop-vectorize") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined") set(PROJECT_WARNINGS ${CLANG_WARNINGS}) endif () diff --git a/python/BC1.cpp b/python/BC1.cpp new file mode 100644 index 0000000..f42d719 --- /dev/null +++ b/python/BC1.cpp @@ -0,0 +1,63 @@ +/* Python-rgbcx Texture Compression Library + Copyright (C) 2021 Andrew Cassidy + Partially derived from rgbcx.h written by Richard Geldreich + 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 . + */ + +#include + +#include "../src/BC1/BC1Encoder.h" +#include "../src/BlockEncoder.h" +#include "../src/bitwiseEnums.h" + +#define STRINGIFY(x) #x +#define MACRO_STRINGIFY(x) STRINGIFY(x) + +namespace py = pybind11; +namespace rgbcx::bindings { + +void InitBC1(py::module_ &m) { + auto block_encoder = py::type::of(); + py::class_ bc1_encoder(m, "BC1Encoder", block_encoder); + + bc1_encoder.def(py::init<>()); + + using Flags = BC1Encoder::Flags; + py::enum_(bc1_encoder, "Flags", py::arithmetic()) + .value("UseLikelyTotalOrderings", Flags::UseLikelyTotalOrderings) + .value("TwoLeastSquaresPasses", Flags::TwoLeastSquaresPasses) + .value("Use3ColorBlocksForBlackPixels", Flags::Use3ColorBlocksForBlackPixels) + .value("Use3ColorBlocks", Flags::Use3ColorBlocks) + .value("Iterative", Flags::Iterative) + .value("Use6PowerIters", Flags::Use6PowerIters) + .value("Exhaustive", Flags::Exhaustive) + .value("TryAllInitialEndpoints", Flags::TryAllInitialEndpoints) + .def("__invert__", [](Flags f1) { return ~unsigned(f1); }) + .def("__and__", [](Flags f1, Flags f2) { return unsigned(f1) & unsigned(f2); }) + .def("__rand__", [](Flags f1, Flags f2) { return unsigned(f1) & unsigned(f2); }) + .def("__or__", [](Flags f1, Flags f2) { return unsigned(f1) | unsigned(f2); }) + .def("__ror__", [](Flags f1, Flags f2) { return unsigned(f1) | unsigned(f2); }) + .def("__xor__", [](Flags f1, Flags f2) { return unsigned(f1) ^ unsigned(f2); }) + .def("__rxor__", [](Flags f1, Flags f2) { return unsigned(f2) ^ unsigned(f1); }); + + py::enum_(bc1_encoder, "EndpointMode") + .value("LeastSquares", BC1Encoder::EndpointMode::LeastSquares) + .value("BoundingBox", BC1Encoder::EndpointMode::BoundingBox) + .value("BoundingBoxInt", BC1Encoder::EndpointMode::BoundingBoxInt) + .value("PCA", BC1Encoder::EndpointMode::PCA); +} + +} // namespace rgbcx::bindings \ No newline at end of file diff --git a/python/BlockEncoder.cpp b/python/BlockEncoder.cpp new file mode 100644 index 0000000..db46e9e --- /dev/null +++ b/python/BlockEncoder.cpp @@ -0,0 +1,64 @@ +/* Python-rgbcx Texture Compression Library + Copyright (C) 2021 Andrew Cassidy + Partially derived from rgbcx.h written by Richard Geldreich + 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 . + */ + +#include "../src/BlockEncoder.h" + +#include + +#include + +#include "../src/bitwiseEnums.h" + +#define STRINGIFY(x) #x +#define MACRO_STRINGIFY(x) STRINGIFY(x) + +namespace py = pybind11; +namespace rgbcx::bindings { + +py::bytes EncodeImage(const BlockEncoder &self, py::bytes decoded, unsigned image_width, unsigned image_height) { + if (image_width % self.BlockWidth() != 0) throw std::invalid_argument("Width is not an even multiple of block_width"); + if (image_height % self.BlockHeight() != 0) throw std::invalid_argument("Height is not an even multiple of block_height"); + if (image_width == 0 || image_height == 0) throw std::invalid_argument("Image has zero size"); + + size_t size = image_width * image_height; + size_t block_size = (size / (self.BlockHeight() * self.BlockWidth())) * self.BlockSize(); + size_t color_size = size * sizeof(Color); + + std::string encoded_str = std::string(block_size, 0); + std::string decoded_str = (std::string)decoded; // decoded data is copied here, unfortunately + + if (decoded_str.size() != color_size) throw std::invalid_argument("Incompatible data: image width and height do not match the size of the decoded image"); + + self.EncodeImage(reinterpret_cast(encoded_str.data()), reinterpret_cast(decoded_str.data()), image_width, image_height); + + auto bytes = py::bytes(encoded_str); // encoded data is copied here, unfortunately + + return bytes; +} + +void InitBlockEncoder(py::module_ &m) { + py::class_ block_encoder(m, "BlockEncoder"); + + block_encoder.def("encode_image", &EncodeImage); + block_encoder.def_property_readonly("block_size", &BlockEncoder::BlockSize); + block_encoder.def_property_readonly("block_width", &BlockEncoder::BlockWidth); + block_encoder.def_property_readonly("block_height", &BlockEncoder::BlockHeight); +} + +} // namespace rgbcx::bindings \ No newline at end of file diff --git a/src/python/BC1Encoder.cpp b/python/Module.cpp similarity index 84% rename from src/python/BC1Encoder.cpp rename to python/Module.cpp index 40c6bea..934550c 100644 --- a/src/python/BC1Encoder.cpp +++ b/python/Module.cpp @@ -18,13 +18,21 @@ */ #include -#include "../BC1/BC1Encoder.h" +#include "../src/BlockEncoder.h" #define STRINGIFY(x) #x #define MACRO_STRINGIFY(x) STRINGIFY(x) namespace py = pybind11; +namespace rgbcx::bindings { + +void InitBlockEncoder(py::module_ &m); +void InitBC1(py::module_ &m); PYBIND11_MODULE(python_rgbcx, m) { m.doc() = "More Stuff"; -} \ No newline at end of file + InitBlockEncoder(m); + InitBC1(m); +} + +} // namespace python_rgbcx::py \ No newline at end of file diff --git a/src/BC1/BC1Encoder.h b/src/BC1/BC1Encoder.h index f1fb2c5..0f8802c 100644 --- a/src/BC1/BC1Encoder.h +++ b/src/BC1/BC1Encoder.h @@ -34,7 +34,7 @@ namespace rgbcx { class Interpolator; class Vector4; -class BC1Encoder final : public BlockEncoder { +class BC1Encoder final : public BlockEncoderTemplate { public: using InterpolatorPtr = std::shared_ptr; diff --git a/src/BC4/BC4Encoder.h b/src/BC4/BC4Encoder.h index f388cbe..b32308a 100644 --- a/src/BC4/BC4Encoder.h +++ b/src/BC4/BC4Encoder.h @@ -29,7 +29,7 @@ namespace rgbcx { -class BC4Encoder : public BlockEncoder { +class BC4Encoder : public BlockEncoderTemplate { public: BC4Encoder(const uint8_t channel) : _channel(channel) { assert(channel < 4); } diff --git a/src/BlockEncoder.cpp b/src/BlockEncoder.cpp new file mode 100644 index 0000000..4a928ba --- /dev/null +++ b/src/BlockEncoder.cpp @@ -0,0 +1,25 @@ +/* Python-rgbcx Texture Compression Library + Copyright (C) 2021 Andrew Cassidy + Partially derived from rgbcx.h written by Richard Geldreich + 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 . + */ +#include "BlockEncoder.h" + +#include "BC1/BC1Encoder.h" + +namespace rgbcx { +std::shared_ptr rgbcx::BlockEncoder::MakeEncoder(std::string fourcc) { return std::make_shared(); } +} // namespace rgbcx diff --git a/src/BlockEncoder.h b/src/BlockEncoder.h index 5a0710a..76cb8b4 100644 --- a/src/BlockEncoder.h +++ b/src/BlockEncoder.h @@ -21,22 +21,37 @@ #include #include +#include +#include #include "BlockView.h" namespace rgbcx { -template class BlockEncoder { +class BlockEncoder { + public: + using EncoderPtr = std::shared_ptr; + + virtual ~BlockEncoder() = default; + virtual void EncodeImage(uint8_t *encoded, Color *decoded, unsigned image_width, unsigned image_height) const = 0; + virtual size_t BlockSize() const = 0; + virtual size_t BlockWidth() const = 0; + virtual size_t BlockHeight() const = 0; + + static EncoderPtr MakeEncoder(std::string fourcc); +}; + +template class BlockEncoderTemplate : public BlockEncoder { public: using DecodedBlock = ColorBlockView; using EncodedBlock = B; - BlockEncoder() noexcept = default; - virtual ~BlockEncoder() noexcept = default; + BlockEncoderTemplate() noexcept = default; + virtual ~BlockEncoderTemplate() noexcept = default; virtual void EncodeBlock(DecodedBlock pixels, EncodedBlock *dest) const = 0; - virtual void EncodeImage(uint8_t *encoded, Color *decoded, unsigned image_width, unsigned image_height) { + virtual void EncodeImage(uint8_t *encoded, Color *decoded, unsigned image_width, unsigned image_height) const override { assert(image_width % N == 0); assert(image_width % M == 0); @@ -61,11 +76,13 @@ template class BlockEncoder { unsigned top_left = pixel_x + (pixel_y * image_width); auto src = DecodedBlock(&decoded[top_left], (int)image_width); -// if (pixel_x != 684 || pixel_y != 492) continue; - EncodeBlock(src, &blocks[x + block_width * y]); } } } + + virtual size_t BlockSize() const override { return sizeof(B); } + virtual size_t BlockWidth() const override { return N; } + virtual size_t BlockHeight() const override { return M; } }; } // namespace rgbcx diff --git a/tools/install.fish b/tools/install.fish deleted file mode 100755 index 462ddc6..0000000 --- a/tools/install.fish +++ /dev/null @@ -1,6 +0,0 @@ -#! /usr/bin/bash - -set dir (status dirname) - -source "$dir"/../env/bin/activate.fish -pip install "$dir"/.. \ No newline at end of file