mirror of
https://github.com/drewcassidy/quicktex.git
synced 2024-09-13 06:37:34 +00:00
Bind BC1Decoder
This commit is contained in:
parent
52d1185dac
commit
04d11112d4
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -28,19 +28,19 @@
|
||||
#include "BC1Block.h"
|
||||
|
||||
namespace rgbcx {
|
||||
class BC1Decoder final : public BlockDecoder<BC1Block, 4, 4> {
|
||||
class BC1Decoder final : public BlockDecoderTemplate<BC1Block, 4, 4> {
|
||||
public:
|
||||
using InterpolatorPtr = std::shared_ptr<Interpolator>;
|
||||
BC1Decoder(const InterpolatorPtr interpolator = std::make_shared<Interpolator>(), 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
|
||||
|
@ -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;
|
||||
|
@ -106,16 +106,9 @@ class BC1Encoder final : public BlockEncoderTemplate<BC1Block, 4, 4> {
|
||||
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<BC1Block, 4, 4> {
|
||||
|
||||
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;
|
||||
|
@ -30,13 +30,13 @@
|
||||
#include "BC3Block.h"
|
||||
|
||||
namespace rgbcx {
|
||||
class BC3Decoder : public BlockDecoder<BC3Block, 4, 4> {
|
||||
class BC3Decoder : public BlockDecoderTemplate<BC3Block, 4, 4> {
|
||||
public:
|
||||
using InterpolatorPtr = std::shared_ptr<Interpolator>;
|
||||
using BC1DecoderPtr = std::shared_ptr<BC1Decoder>;
|
||||
using BC4DecoderPtr = std::shared_ptr<BC4Decoder>;
|
||||
|
||||
BC3Decoder(InterpolatorPtr interpolator = std::make_shared<Interpolator>()) : BC3Decoder(std::make_shared<BC1Decoder>(interpolator)) {}
|
||||
BC3Decoder(Interpolator::Type type = Interpolator::Type::Ideal) : BC3Decoder(std::make_shared<BC1Decoder>(type)) {}
|
||||
BC3Decoder(BC1DecoderPtr bc1_decoder, BC4DecoderPtr bc4_decoder = std::make_shared<BC4Decoder>()) : _bc1_decoder(bc1_decoder), _bc4_decoder(bc4_decoder) {}
|
||||
|
||||
void DecodeBlock(Color4x4 dest, BC3Block *const block) const noexcept(ndebug) override;
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include "BC4Block.h"
|
||||
|
||||
namespace rgbcx {
|
||||
class BC4Decoder : public BlockDecoder<BC4Block, 4, 4> {
|
||||
class BC4Decoder : public BlockDecoderTemplate<BC4Block, 4, 4> {
|
||||
public:
|
||||
BC4Decoder(uint8_t channel = 3) : _channel(channel) { assert(channel < 4U); }
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
||||
#include "BC5Block.h"
|
||||
|
||||
namespace rgbcx {
|
||||
class BC5Decoder : public BlockDecoder<BC5Block, 4, 4> {
|
||||
class BC5Decoder : public BlockDecoderTemplate<BC5Block, 4, 4> {
|
||||
public:
|
||||
using BC4DecoderPtr = std::shared_ptr<BC4Decoder>;
|
||||
|
||||
|
@ -30,17 +30,29 @@
|
||||
|
||||
namespace rgbcx {
|
||||
|
||||
template <class B, size_t M, size_t N> class BlockDecoder {
|
||||
class BlockDecoder {
|
||||
public:
|
||||
using DecoderPtr = std::shared_ptr<BlockDecoder>;
|
||||
|
||||
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 B, size_t M, size_t N>
|
||||
class BlockDecoderTemplate : public BlockDecoder {
|
||||
public:
|
||||
using DecodedBlock = ColorBlockView<M, N>;
|
||||
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 B, size_t M, size_t N> 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
|
||||
|
@ -1,25 +0,0 @@
|
||||
/* Python-rgbcx 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 "BlockEncoder.h"
|
||||
|
||||
#include "BC1/BC1Encoder.h"
|
||||
|
||||
namespace rgbcx {
|
||||
std::shared_ptr<BlockEncoder> rgbcx::BlockEncoder::MakeEncoder(std::string fourcc) { return std::make_shared<BC1Encoder>(); }
|
||||
} // namespace rgbcx
|
@ -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 B, size_t M, size_t N> class BlockEncoderTemplate : public BlockEncoder {
|
||||
@ -57,12 +55,16 @@ template <class B, size_t M, size_t N> 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<B *>(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 B, size_t M, size_t N> 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
|
||||
|
@ -19,7 +19,9 @@
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#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<BC1Encoder> MakeBC1Encoder(Interpolator::Type interpolator, unsigned level, bool use_3color, bool use_3color_black) {
|
||||
auto interpolator_ptr = (std::shared_ptr<Interpolator>)Interpolator::MakeInterpolator(interpolator);
|
||||
return std::make_unique<BC1Encoder>(interpolator_ptr, level, use_3color, use_3color_black);
|
||||
}
|
||||
|
||||
void InitBC1(py::module_ &m) {
|
||||
auto block_encoder = py::type::of<BlockEncoder>();
|
||||
auto block_decoder = py::type::of<BlockDecoder>();
|
||||
|
||||
// BC1Encoder
|
||||
py::class_<BC1Encoder> 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<Interpolator::Type, unsigned, bool, bool>(), 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_<BC1Decoder> bc1_decoder(m, "BC1Decoder", block_decoder);
|
||||
|
||||
bc1_decoder.def(py::init<Interpolator::Type, bool>(), 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
|
64
src/rgbcx/bindings/BlockDecoder.cpp
Normal file
64
src/rgbcx/bindings/BlockDecoder.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
/* Python-rgbcx 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 "../BlockDecoder.h"
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#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<uint8_t *>(encoded_str.data()), reinterpret_cast<Color *>(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_<BlockDecoder> 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
|
@ -19,16 +19,13 @@
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#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
|
||||
} // namespace rgbcx::bindings
|
30
tests/blocks.py
Normal file
30
tests/blocks.py
Normal file
@ -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)}
|
||||
]
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -1,13 +1,12 @@
|
||||
import unittest
|
||||
|
||||
import nose
|
||||
import parameterized
|
||||
import s3tc
|
||||
import rgbcx
|
||||
import unittest
|
||||
import nose
|
||||
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")
|
||||
|
Loading…
Reference in New Issue
Block a user