diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c64efa..d5c3244 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,9 @@ include(tools/CompilerWarnings.cmake) project(rgbcx) -# Link to Pybind +# Find dependencies find_package(Python COMPONENTS Interpreter Development REQUIRED) +find_package(OpenMP) add_subdirectory(extern/pybind11) # Collect source files @@ -33,6 +34,11 @@ target_link_libraries(test_rgbcx PRIVATE pybind11::embed) target_compile_definitions(test_rgbcx PRIVATE -DCUSTOM_SYS_PATH="${CMAKE_HOME_DIRECTORY}/env/lib/python3.8/site-packages") message("\"${CMAKE_HOME_DIRECTORY}/env/lib/python3.8/site-packages\"") +# enable openMP if available +if(OpenMP_CXX_FOUND) + target_link_libraries(_rgbcx PUBLIC OpenMP::OpenMP_CXX) +endif() + # Set module features, like C/C++ standards target_compile_features(_rgbcx PUBLIC cxx_std_20 c_std_11) target_compile_features(test_rgbcx PUBLIC cxx_std_20 c_std_11) diff --git a/src/rgbcx/BC1/BC1Decoder.cpp b/src/rgbcx/BC1/BC1Decoder.cpp index 1c9c7d7..dabbfe5 100644 --- a/src/rgbcx/BC1/BC1Decoder.cpp +++ b/src/rgbcx/BC1/BC1Decoder.cpp @@ -41,7 +41,7 @@ void BC1Decoder::DecodeBlock(Color4x4 dest, BC1Block *const block) const noexcep const auto color = colors[selector]; assert(selector < 4); assert((color.a == 0 && selector == 3 && l <= h) || color.a == UINT8_MAX); - if (_write_alpha) { + if (write_alpha) { dest.Get(x, y).SetRGBA(color); } else { dest.Get(x, y).SetRGB(color); diff --git a/src/rgbcx/BC1/BC1Decoder.h b/src/rgbcx/BC1/BC1Decoder.h index 72be6ac..9d97817 100644 --- a/src/rgbcx/BC1/BC1Decoder.h +++ b/src/rgbcx/BC1/BC1Decoder.h @@ -28,19 +28,19 @@ #include "BC1Block.h" namespace rgbcx { -class BC1Decoder final : public BlockDecoder { +class BC1Decoder final : public BlockDecoderTemplate { public: using InterpolatorPtr = std::shared_ptr; - BC1Decoder(const InterpolatorPtr interpolator = std::make_shared(), bool write_alpha = false) - : _interpolator(interpolator), _write_alpha(write_alpha) {} + BC1Decoder(Interpolator::Type type = Interpolator::Type::Ideal, bool write_alpha = false) + : _interpolator(Interpolator::MakeInterpolator(type)), write_alpha(write_alpha) {} void DecodeBlock(Color4x4 dest, BC1Block *const block) const noexcept(ndebug) override; - InterpolatorPtr GetInterpolator() const { return _interpolator; } - constexpr bool WritesAlpha() const { return _write_alpha; } + Interpolator::Type GetInterpolatorType() const { return _interpolator->GetType(); } + constexpr bool WritesAlpha() const { return write_alpha; } + bool write_alpha; private: const InterpolatorPtr _interpolator; - const bool _write_alpha; }; } // namespace rgbcx diff --git a/src/rgbcx/BC1/BC1Encoder.cpp b/src/rgbcx/BC1/BC1Encoder.cpp index 0ea15c5..74d600b 100644 --- a/src/rgbcx/BC1/BC1Encoder.cpp +++ b/src/rgbcx/BC1/BC1Encoder.cpp @@ -43,40 +43,18 @@ using namespace BC1; // constructors -BC1Encoder::BC1Encoder(InterpolatorPtr interpolator) : _interpolator(interpolator) { - _flags = Flags::None; - _error_mode = ErrorMode::Check2; - _endpoint_mode = EndpointMode::PCA; - _search_rounds = 0; - _orderings3 = 1; - _orderings4 = 1; +BC1Encoder::BC1Encoder(Interpolator::Type type, unsigned int level, bool allow_3color, bool allow_3color_black) + : _interpolator(Interpolator::MakeInterpolator(type)) { OrderTable<3>::Generate(); OrderTable<4>::Generate(); assert(OrderTable<3>::generated); assert(OrderTable<4>::generated); -} - -BC1Encoder::BC1Encoder(unsigned int level, bool allow_3color, bool allow_3color_black) : BC1Encoder(Interpolator::MakeInterpolator()) { - SetLevel(level, allow_3color, allow_3color_black); -} -BC1Encoder::BC1Encoder(InterpolatorPtr interpolator, unsigned level, bool allow_3color, bool allow_3color_black) : BC1Encoder(interpolator) { SetLevel(level, allow_3color, allow_3color_black); } -BC1Encoder::BC1Encoder(InterpolatorPtr interpolator, Flags flags, ErrorMode error_mode, EndpointMode endpoint_mode, unsigned search_rounds, unsigned orderings4, - unsigned orderings3) - : BC1Encoder(interpolator) { - SetFlags(flags); - SetErrorMode(error_mode); - SetEndpointMode(endpoint_mode); - SetSearchRounds(search_rounds); - SetOrderings4(orderings4); - SetOrderings3(orderings3); -} - // Getters and Setters void BC1Encoder::SetLevel(unsigned level, bool allow_3color, bool allow_3color_black) { _flags = Flags::None; diff --git a/src/rgbcx/BC1/BC1Encoder.h b/src/rgbcx/BC1/BC1Encoder.h index 4edc504..895d569 100644 --- a/src/rgbcx/BC1/BC1Encoder.h +++ b/src/rgbcx/BC1/BC1Encoder.h @@ -106,16 +106,9 @@ class BC1Encoder final : public BlockEncoderTemplate { PCA }; - BC1Encoder(InterpolatorPtr interpolator); + BC1Encoder(Interpolator::Type type = Interpolator::Type::Ideal, unsigned level = 5, bool allow_3color = true, bool allow_3color_black = true); - BC1Encoder(unsigned level = 5, bool allow_3color = true, bool allow_3color_black = true); - - BC1Encoder(InterpolatorPtr interpolator, unsigned level, bool allow_3color = true, bool allow_3color_black = true); - - BC1Encoder(InterpolatorPtr interpolator, Flags flags, ErrorMode error_mode = ErrorMode::Full, EndpointMode endpoint_mode = EndpointMode::PCA, - unsigned search_rounds = 16, unsigned orderings4 = 32, unsigned orderings3 = 32); - - const InterpolatorPtr &GetInterpolator() const; + Interpolator::Type GetInterpolatorType() const { return _interpolator->GetType(); } void SetLevel(unsigned level, bool allow_3color = true, bool allow_3color_black = true); @@ -138,6 +131,8 @@ class BC1Encoder final : public BlockEncoderTemplate { void EncodeBlock(Color4x4 pixels, BC1Block *dest) const override; + virtual size_t MTThreshold() const override { return 16; } + private: using Hash = uint16_t; using BlockMetrics = Color4x4::BlockMetrics; diff --git a/src/rgbcx/BC3/BC3Decoder.h b/src/rgbcx/BC3/BC3Decoder.h index 71f546d..187d3e9 100644 --- a/src/rgbcx/BC3/BC3Decoder.h +++ b/src/rgbcx/BC3/BC3Decoder.h @@ -30,13 +30,13 @@ #include "BC3Block.h" namespace rgbcx { -class BC3Decoder : public BlockDecoder { +class BC3Decoder : public BlockDecoderTemplate { public: using InterpolatorPtr = std::shared_ptr; using BC1DecoderPtr = std::shared_ptr; using BC4DecoderPtr = std::shared_ptr; - BC3Decoder(InterpolatorPtr interpolator = std::make_shared()) : BC3Decoder(std::make_shared(interpolator)) {} + BC3Decoder(Interpolator::Type type = Interpolator::Type::Ideal) : BC3Decoder(std::make_shared(type)) {} BC3Decoder(BC1DecoderPtr bc1_decoder, BC4DecoderPtr bc4_decoder = std::make_shared()) : _bc1_decoder(bc1_decoder), _bc4_decoder(bc4_decoder) {} void DecodeBlock(Color4x4 dest, BC3Block *const block) const noexcept(ndebug) override; diff --git a/src/rgbcx/BC4/BC4Decoder.h b/src/rgbcx/BC4/BC4Decoder.h index 2b8cb9e..1ea8a81 100644 --- a/src/rgbcx/BC4/BC4Decoder.h +++ b/src/rgbcx/BC4/BC4Decoder.h @@ -28,7 +28,7 @@ #include "BC4Block.h" namespace rgbcx { -class BC4Decoder : public BlockDecoder { +class BC4Decoder : public BlockDecoderTemplate { public: BC4Decoder(uint8_t channel = 3) : _channel(channel) { assert(channel < 4U); } diff --git a/src/rgbcx/BC5/BC5Decoder.h b/src/rgbcx/BC5/BC5Decoder.h index f50fdfa..15c1207 100644 --- a/src/rgbcx/BC5/BC5Decoder.h +++ b/src/rgbcx/BC5/BC5Decoder.h @@ -31,7 +31,7 @@ #include "BC5Block.h" namespace rgbcx { -class BC5Decoder : public BlockDecoder { +class BC5Decoder : public BlockDecoderTemplate { public: using BC4DecoderPtr = std::shared_ptr; diff --git a/src/rgbcx/BlockDecoder.h b/src/rgbcx/BlockDecoder.h index 4a8db5a..b9245b9 100644 --- a/src/rgbcx/BlockDecoder.h +++ b/src/rgbcx/BlockDecoder.h @@ -30,17 +30,29 @@ namespace rgbcx { -template class BlockDecoder { +class BlockDecoder { + public: + using DecoderPtr = std::shared_ptr; + + virtual ~BlockDecoder() = default; + virtual void DecodeImage(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; +}; + +template +class BlockDecoderTemplate : public BlockDecoder { public: using DecodedBlock = ColorBlockView; using EncodedBlock = B; - BlockDecoder() noexcept = default; - virtual ~BlockDecoder() noexcept = default; + BlockDecoderTemplate() noexcept = default; + virtual ~BlockDecoderTemplate() noexcept = default; virtual void DecodeBlock(DecodedBlock dest, EncodedBlock *const block) const noexcept(ndebug) = 0; - void DecodeImage(uint8_t *encoded, Color *decoded, unsigned image_width, unsigned image_height) { + virtual void DecodeImage(uint8_t *encoded, Color *decoded, unsigned image_width, unsigned image_height) const override { assert(image_width % N == 0); assert(image_width % M == 0); @@ -69,5 +81,9 @@ template class BlockDecoder { } } } + + 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/src/rgbcx/BlockEncoder.cpp b/src/rgbcx/BlockEncoder.cpp deleted file mode 100644 index 4a928ba..0000000 --- a/src/rgbcx/BlockEncoder.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* 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/rgbcx/BlockEncoder.h b/src/rgbcx/BlockEncoder.h index 76cb8b4..6807a25 100644 --- a/src/rgbcx/BlockEncoder.h +++ b/src/rgbcx/BlockEncoder.h @@ -37,8 +37,6 @@ class BlockEncoder { 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 { @@ -57,12 +55,16 @@ template class BlockEncoderTemplate : public Block unsigned block_width = image_width / N; unsigned block_height = image_height / M; + unsigned block_count = block_width * block_height; auto blocks = reinterpret_cast(encoded); - // from experimentation, multithreading this using OpenMP actually makes decoding slower + // from experimentation, multithreading this using OpenMP sometimes 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 + // As a result, this is sometimes left as a serial operation despite being embarassingly parallelizable + // threshold for number of blocks before multithreading is set by overriding MTThreshold() + +#pragma omp parallel for if (block_count >= MTThreshold()) for (unsigned y = 0; y < block_height; y++) { for (unsigned x = 0; x < block_width; x++) { unsigned pixel_x = x * N; @@ -84,5 +86,7 @@ template class BlockEncoderTemplate : public Block 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; } + + virtual size_t MTThreshold() const { return SIZE_MAX; }; }; } // namespace rgbcx diff --git a/src/rgbcx/bindings/BC1.cpp b/src/rgbcx/bindings/BC1.cpp index caf8b69..abbf6a6 100644 --- a/src/rgbcx/bindings/BC1.cpp +++ b/src/rgbcx/bindings/BC1.cpp @@ -19,7 +19,9 @@ #include +#include "../BC1/BC1Decoder.h" #include "../BC1/BC1Encoder.h" +#include "../BlockDecoder.h" #include "../BlockEncoder.h" #include "../bitwiseEnums.h" @@ -29,18 +31,17 @@ namespace py = pybind11; namespace rgbcx::bindings { -std::unique_ptr MakeBC1Encoder(Interpolator::Type interpolator, unsigned level, bool use_3color, bool use_3color_black) { - auto interpolator_ptr = (std::shared_ptr)Interpolator::MakeInterpolator(interpolator); - return std::make_unique(interpolator_ptr, level, use_3color, use_3color_black); -} - void InitBC1(py::module_ &m) { auto block_encoder = py::type::of(); + auto block_decoder = py::type::of(); + + // BC1Encoder py::class_ bc1_encoder(m, "BC1Encoder", block_encoder); - bc1_encoder.def(py::init(&MakeBC1Encoder), py::arg("interpolator") = Interpolator::Type::Ideal, py::arg("level") = 5, py::arg("use_3color") = true, - py::arg("use_3color_black") = true); + bc1_encoder.def(py::init(), py::arg("interpolator") = Interpolator::Type::Ideal, py::arg("level") = 5, + py::arg("use_3color") = true, py::arg("use_3color_black") = true); bc1_encoder.def("set_level", &BC1Encoder::SetLevel); + bc1_encoder.def_property_readonly("interpolator_type", &BC1Encoder::GetInterpolatorType); bc1_encoder.def_property("flags", &BC1Encoder::GetFlags, &BC1Encoder::SetFlags); bc1_encoder.def_property("error_mode", &BC1Encoder::GetErrorMode, &BC1Encoder::SetErrorMode); bc1_encoder.def_property("endpoint_mode", &BC1Encoder::GetEndpointMode, &BC1Encoder::SetEndpointMode); @@ -77,6 +78,13 @@ void InitBC1(py::module_ &m) { .value("Faster", BC1Encoder::ErrorMode::Faster) .value("Check2", BC1Encoder::ErrorMode::Check2) .value("Full", BC1Encoder::ErrorMode::Full); + + // BC1Decoder + py::class_ bc1_decoder(m, "BC1Decoder", block_decoder); + + bc1_decoder.def(py::init(), py::arg("interpolator") = Interpolator::Type::Ideal, py::arg("write_alpha") = false); + bc1_decoder.def_property_readonly("interpolator_type", &BC1Decoder::GetInterpolatorType); + bc1_decoder.def_readwrite("write_alpha", &BC1Decoder::write_alpha); } } // namespace rgbcx::bindings \ No newline at end of file diff --git a/src/rgbcx/bindings/BlockDecoder.cpp b/src/rgbcx/bindings/BlockDecoder.cpp new file mode 100644 index 0000000..0d0e2cc --- /dev/null +++ b/src/rgbcx/bindings/BlockDecoder.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 "../BlockDecoder.h" + +#include + +#include + +#include "../bitwiseEnums.h" + +#define STRINGIFY(x) #x +#define MACRO_STRINGIFY(x) STRINGIFY(x) + +namespace py = pybind11; +namespace rgbcx::bindings { + +py::bytes DecodeImage(const BlockDecoder &self, py::bytes encoded, 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)encoded; // encoded data is copied here, unfortunately + std::string decoded_str = std::string(color_size, 0); + + 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.DecodeImage(reinterpret_cast(encoded_str.data()), reinterpret_cast(decoded_str.data()), image_width, image_height); + + auto bytes = py::bytes(decoded_str); // decoded data is copied here, unfortunately + + return bytes; +} + +void InitBlockDecoder(py::module_ &m) { + py::class_ block_decoder(m, "BlockDecoder"); + + block_decoder.def("decode_image", &DecodeImage); + block_decoder.def_property_readonly("block_size", &BlockDecoder::BlockSize); + block_decoder.def_property_readonly("block_width", &BlockDecoder::BlockWidth); + block_decoder.def_property_readonly("block_height", &BlockDecoder::BlockHeight); +} + +} // namespace rgbcx::bindings \ No newline at end of file diff --git a/src/rgbcx/bindings/Module.cpp b/src/rgbcx/bindings/Module.cpp index 1e45a7d..deedd8e 100644 --- a/src/rgbcx/bindings/Module.cpp +++ b/src/rgbcx/bindings/Module.cpp @@ -19,16 +19,13 @@ #include -#include "../BlockEncoder.h" #include "../Interpolator.h" -#define STRINGIFY(x) #x -#define MACRO_STRINGIFY(x) STRINGIFY(x) - namespace py = pybind11; namespace rgbcx::bindings { void InitBlockEncoder(py::module_ &m); +void InitBlockDecoder(py::module_ &m); void InitBC1(py::module_ &m); PYBIND11_MODULE(_rgbcx, m) { @@ -42,7 +39,8 @@ PYBIND11_MODULE(_rgbcx, m) { .value("AMD", IType::AMD); InitBlockEncoder(m); + InitBlockDecoder(m); InitBC1(m); } -} // namespace python_rgbcx::py \ No newline at end of file +} // namespace rgbcx::bindings \ No newline at end of file diff --git a/tests/blocks.py b/tests/blocks.py new file mode 100644 index 0000000..7589094 --- /dev/null +++ b/tests/blocks.py @@ -0,0 +1,30 @@ +from s3tc import BC1Block +from color import Color +from PIL import Image +import os + +image_path = os.path.dirname(os.path.realpath(__file__)) + "/images" + +# A block that should always encode greyscale, where every row of pixels is identical, and the left side is lighter than the right side +greyscale = Image.open(image_path + "/blocks/greyscale.png").tobytes("raw", "RGBX") + +# A block that should always encode 3-color when available. +# from left to right: red, yellow, yellow, green +three_color = Image.open(image_path + "/blocks/3color.png").tobytes("raw", "RGBX") + +# A block that should always encode 3-color with black when available +# from left to right: black, red, yellow, green +three_color_black = Image.open(image_path + "/blocks/3color black.png").tobytes("raw", "RGBX") + +bc1_test_blocks = [ + # A block that should always encode greyscale, where every row of pixels is identical, and the left side is lighter than the right side + {"name": "greyscale", + "image": Image.open(image_path + "/blocks/greyscale.png").tobytes("raw", "RGBX"), + "expected": BC1Block(Color(0xFF, 0xFF, 0xFF), Color(0x44, 0x44, 0x44), [[0, 2, 3, 1]] * 4)}, + + # A block that should always encode 3-color when available. + # from left to right: red, yellow, yellow, green + {"name": "3color", + "image": Image.open(image_path + "/blocks/3color.png").tobytes("raw", "RGBX"), + "expected": BC1Block(Color(0x00, 0xFF, 0x00), Color(0xFF, 0x00, 0x00), [[0, 2, 3, 1]] * 4)} +] diff --git a/tests/color.py b/tests/color.py index 04b6ae7..1b4b186 100644 --- a/tests/color.py +++ b/tests/color.py @@ -1,5 +1,5 @@ class Color: - def __init__(self, r=0, g=0, b=0, a=1): + def __init__(self, r=0, g=0, b=0, a=0xFF): self.r = r self.g = g self.b = b @@ -23,6 +23,10 @@ class Color: def __str__(self): return self.to_hex() + def error(self, other): + assert isinstance(other, Color) + return ((self.r - other.r) ** 2) + ((self.g - other.g) ** 2) + ((self.b - other.b) ** 2) + @classmethod def from_565(cls, int_value): r = float((int_value & 0xF800) >> 11) / 0x1F diff --git a/tests/s3tc.py b/tests/s3tc.py index cd31d14..034d571 100644 --- a/tests/s3tc.py +++ b/tests/s3tc.py @@ -28,16 +28,16 @@ def triple_merge(rows): class BC1Block: size = 8 - def __init__(self): - self.color0 = Color() - self.color1 = Color() - self.selectors = [[0] * 4] * 4 + def __init__(self, color0=Color(), color1=Color(), selectors=[[0] * 4] * 4): + self.color0 = color0 + self.color1 = color1 + self.selectors = selectors def __repr__(self): return repr(self.__dict__) def __str__(self): - return f'color0: {str(self.color0)} color1: {str(self.color1)}, indices:{self.selectors}' + return f'color0: {str(self.color0)} color1: {str(self.color1)}, selectors:{self.selectors}' @staticmethod def frombytes(data): diff --git a/tests/test_BC1.py b/tests/test_BC1.py index 917ba46..e6f7dcc 100644 --- a/tests/test_BC1.py +++ b/tests/test_BC1.py @@ -1,13 +1,12 @@ +import rgbcx import unittest - import nose -import parameterized -import s3tc -import rgbcx +from parameterized import parameterized_class +from s3tc import BC1Block from images import Blocks -def get_class_name_bc1encoder(cls, num, params_dict): +def get_class_name(cls, num, params_dict): # By default the generated class named includes either the "name" # parameter (if present), or the first string value. This example shows # multiple parameters being included in the generated class name: @@ -17,11 +16,11 @@ def get_class_name_bc1encoder(cls, num, params_dict): ) -@parameterized.parameterized_class([ +@parameterized_class([ {"use_3color": False, "use_3color_black": False, "suffix": "4Color"}, {"use_3color": True, "use_3color_black": False, "suffix": "3Color"}, {"use_3color": True, "use_3color_black": True, "suffix": "3ColorBlack"}, -], class_name_func=get_class_name_bc1encoder) +], class_name_func=get_class_name) class TestBC1EncoderBlocks(unittest.TestCase): """Test BC1 encoder with a variety of inputs with 3 color blocks disabled.""" @@ -39,7 +38,7 @@ class TestBC1EncoderBlocks(unittest.TestCase): def test_block_4color(self): """Test encoder output with 4 color greyscale testblock.""" - out = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(Blocks.greyscale, 4, 4)) + out = BC1Block.frombytes(self.bc1_encoder.encode_image(Blocks.greyscale, 4, 4)) selectors = [[0, 2, 3, 1]] * 4 self.assertFalse(out.is_3color(), "returned block color mode for greyscale test block") @@ -47,7 +46,7 @@ class TestBC1EncoderBlocks(unittest.TestCase): def test_block_3color(self): """Test encoder output with 3 color test block.""" - out = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(Blocks.three_color, 4, 4)) + out = BC1Block.frombytes(self.bc1_encoder.encode_image(Blocks.three_color, 4, 4)) selectors = [[1, 2, 2, 0]] * 4 self.assertEqual(out.is_3color(), self.use_3color, "returned incorrect block color mode for 3 color test block") @@ -56,7 +55,7 @@ class TestBC1EncoderBlocks(unittest.TestCase): def test_block_3color_black(self): """Test encoder output with 3 color test block with black pixels.""" - out = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(Blocks.three_color_black, 4, 4)) + out = BC1Block.frombytes(self.bc1_encoder.encode_image(Blocks.three_color_black, 4, 4)) selectors = [[3, 1, 2, 0]] * 4 self.assertEqual(out.is_3color_black(), self.use_3color_black, "returned incorrect block color mode for 3 color with black test block")