/* Quicktex Texture Compression Library Copyright (C) 2021-2022 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 "../../_bindings.h" #include #include #include #include #include #include #include #include "../../Decoder.h" #include "../../Encoder.h" #include "../interpolator/Interpolator.h" #include "BC1Decoder.h" #include "BC1Encoder.h" namespace py = pybind11; namespace quicktex::bindings { using namespace quicktex::s3tc; using namespace pybind11::literals; using InterpolatorPtr = std::shared_ptr; void InitBC1(py::module_ &s3tc) { auto bc1 = s3tc.def_submodule("_bc1", "internal bc1 module"); // region BC1Block auto bc1_block = BindBlock(bc1, "BC1Block"); bc1_block.doc() = "A single BC1 block."; bc1_block.def(py::init<>()); bc1_block.def(py::init(), "color0"_a, "color1"_a, "selectors"_a, R"doc( 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("endpoints", &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 list back to the property, like so:: selectors = block.selectors selectors[0,0] = 0 block.selectors = selectors )doc"); bc1_block.def_property_readonly("is_3color", &BC1Block::Is3Color, R"doc( "True if the block uses 3-color interpolation, i.e. color0 <= color1. This value should be ignored when decoding as part of a BC3 block. Readonly. )doc"); // endregion // region BC1Texture auto bc1_texture = BindBlockTexture(bc1, "BC1Texture"); bc1_texture.doc() = "A texture comprised of BC1 blocks."; // endregion // region BC1Encoder py::class_ bc1_encoder(bc1, "BC1Encoder", "Encodes RGB textures to BC1."); py::enum_(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.") .value("BoundingBoxInt", BC1Encoder::EndpointMode::BoundingBoxInt, "Same as BoundingBox but using integers, slightly faster.") .value("PCA", BC1Encoder::EndpointMode::PCA, "Find endpoints using Principle Component Analysis. Slowest but highest quality method."); py::enum_(bc1_encoder, "ErrorMode", "Enum representing various methods of finding selectors in a block.") .value("None", BC1Encoder::ErrorMode::None, "The same as Faster but error is not calculated. This disables any cluster-fit options") .value("Faster", BC1Encoder::ErrorMode::Faster, "Use a slightly lower quality, but ~30% faster MSE evaluation function for 4-color blocks.") .value("Check2", BC1Encoder::ErrorMode::Check2, "Default error-checking method.") .value("Full", BC1Encoder::ErrorMode::Full, "Examine all colors to compute selectors/MSE. Slower but slightly higher quality."); py::enum_(bc1_encoder, "ColorMode", "Enum representing various methods of writing BC1 blocks.") .value("FourColor", BC1Encoder::ColorMode::FourColor, "Default color mode. Only 4-color blocks will be output, where color0 > color1") .value("ThreeColor", BC1Encoder::ColorMode::ThreeColor, "Additionally use 3-color blocks when they have a lower error, where color0 <= color1") .value("ThreeColorBlack", BC1Encoder::ColorMode::ThreeColorBlack, "Additionally use 3-color blocks with black pixels (selector 3). Note that this requires your shader/engine to not sample the alpha channel " "when using a BC1 texture."); bc1_encoder.def(py::init(), "level"_a = 5, "color_mode"_a = BC1Encoder::ColorMode::FourColor); bc1_encoder.def(py::init(), "level"_a, "color_mode"_a, "interpolator"_a, R"doc( Create a new BC1 encoder with the specified preset level, color mode, and interpolator. :param int level: The preset level of the resulting encoder, between 0 and 18 inclusive. See :py:meth:`set_level` for more information. Default: 5. :param ColorMode color_mode: The color mode of the resulting BC1Encoder. Default: :py:class:`~quicktex.s3tc.bc1.BC1Encoder.ColorMode.FourColor`. :param Interpolator interpolator: The interpolation mode to use for encoding. Default: :py:class:`~quicktex.s3tc.interpolator.Interpolator`. )doc"); bc1_encoder.def("encode", &BC1Encoder::Encode, "texture"_a, R"doc( Encode a raw texture into a new BC1Texture using the encoder's current settings. :param RawTexture texture: Input texture to encode. :returns: A new BC1Texture with the same dimension as the input. )doc"); bc1_encoder.def("set_level", &BC1Encoder::SetLevel, "level"_a, R"doc( Select a preset quality level, between 0 and 18 inclusive. Higher quality levels are slower, but produce blocks that are a closer match to input. This has no effect on the size of the resulting texture, since BC1 is a fixed-ratio compression method. For better control, see the advanced API below :param int level: The preset level of the resulting encoder, between 0 and 18 inclusive. Default: 5. )doc"); bc1_encoder.def_property_readonly("interpolator", &BC1Encoder::GetInterpolator, "The :py:class:`~quicktex.s3tc.interpolator.Interpolator` used by this encoder. This is a readonly property."); bc1_encoder.def_property_readonly("color_mode", &BC1Encoder::GetColorMode, "The :py:class:`~quicktex.s3tc.bc1.BC1Encoder.ColorMode` used by this encoder. This is a readonly property."); // Advanced API bc1_encoder.def_property("error_mode", &BC1Encoder::GetErrorMode, &BC1Encoder::SetErrorMode, "The error mode used by this encoder for finding selectors."); bc1_encoder.def_property("endpoint_mode", &BC1Encoder::GetEndpointMode, &BC1Encoder::SetEndpointMode, "The endpoint mode used by this encoder."); bc1_encoder.def_readwrite("two_ls_passes", &BC1Encoder::two_ls_passes, "Use 2 least squares pass, instead of one (same as stb_dxt's HIGHQUAL option).\n" "Recommended if you're setting the orderings settings greater than 0."); bc1_encoder.def_readwrite("two_ep_passes", &BC1Encoder::two_ep_passes, "Try 2 different ways of choosing the initial endpoints."); bc1_encoder.def_readwrite("two_cf_passes", &BC1Encoder::two_cf_passes, "Greatly increase encode time, with very slightly higher quality.\n" "Same as squish's iterative cluster fit option. Not really worth the tiny boost in quality, " "unless you just don't care about performance at all."); bc1_encoder.def_readwrite("exhaustive", &BC1Encoder::exhaustive, "Check all total orderings - *very* slow. The encoder is not designed to be used in this way"); bc1_encoder.def_property("search_rounds", &BC1Encoder::GetSearchRounds, &BC1Encoder::SetSearchRounds, "Setting search rounds > 0 enables refining the final endpoints by examining nearby colors. A higher value has a higher quality " "at the expense of performance."); bc1_encoder.def_property("orderings", &BC1Encoder::GetOrderings, &BC1Encoder::SetOrderings, "setting the orderings > 0 enables ordered cluster fit using a lookup table of similar blocks. Value is a tuple of (4 color " "orders, 3 color orders), where higher values have a higher quality at the expense of performance."); bc1_encoder.def_readonly_static("max_power_iterations", &BC1Encoder::max_power_iterations); bc1_encoder.def_readonly_static("min_power_iterations", &BC1Encoder::min_power_iterations); 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 // region BC1Decoder py::class_ bc1_decoder(bc1, "BC1Decoder", R"doc( Decodes BC1 textures to RGB )doc"); bc1_decoder.def(py::init(), "write_alpha"_a = false); bc1_decoder.def(py::init(), "write_alpha"_a, "interpolator"_a, R"doc( Create a new BC1 decoder with the specificied interpolator. :param bool write_alpha: Determines if the alpha channel of the output is written to. Default: False; :param Interpolator interpolator: The interpolation mode to use for decoding. Default: :py:class:`~quicktex.s3tc.interpolator.Interpolator`. )doc"); bc1_decoder.def("decode", &BC1Decoder::Decode, "texture"_a, R"doc( Decode a BC1 texture into a new RawTexture using the decoder's current settings. :param RawTexture texture: Input texture to encode. :returns: A new RawTexture with the same dimensions as the input )doc"); 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