mirror of
https://github.com/drewcassidy/quicktex.git
synced 2024-09-13 06:37:34 +00:00
Compare commits
231 Commits
460785ee7d
...
scikit-bui
Author | SHA1 | Date | |
---|---|---|---|
1aa08a0cda | |||
421876ab0f | |||
29590e0323 | |||
71379b7ae1 | |||
d1346ca11d | |||
e488dbcbff | |||
1f7b45aa57 | |||
70b7251eae | |||
e8e0f4e29b | |||
6d7f56476f | |||
b4d2388615 | |||
f7d57aa859 | |||
24b064e6b4 | |||
28930d89d5 | |||
4f25e0f750 | |||
433e728424 | |||
4939171d5d | |||
f0f132328c | |||
7a11901aa6 | |||
cb8b251baf | |||
463956b63b | |||
22cc5f6148 | |||
0478532cb9 | |||
f2873f3a38 | |||
2618faadfc | |||
38d3805add | |||
f87226ad50 | |||
8967bc2f1a | |||
b3d6585799 | |||
cde6ae006e | |||
2b36086bf5 | |||
faa7e4df08 | |||
f353bf6e92 | |||
7954ec1de6 | |||
890d60cd82 | |||
3ebf07ae74 | |||
4a8e416f1f | |||
a07fd55014 | |||
803dccae2d | |||
148fb36273 | |||
b3f6350353 | |||
d4c91e21b8 | |||
de2dc0c0a8 | |||
96dc62a1b0 | |||
c18cebb519 | |||
784f2393f1 | |||
83b4914bb0 | |||
72aeb54c18 | |||
60a944500b | |||
f34824b45b | |||
68e37ca836 | |||
46a62b0dab | |||
9156ac199b | |||
69fdd6a4c8 | |||
d5a3f09a58 | |||
0152e99c44 | |||
560acb20ea | |||
362c1c0715 | |||
4c6b683616 | |||
b8aba4d382 | |||
1f0a577d7f | |||
76da50d003 | |||
8a85f5c920 | |||
156880d430 | |||
8b6ea69300 | |||
09e05b7cde | |||
7149864173 | |||
2fa4f59104 | |||
ac16b14a92 | |||
7154857323 | |||
6e0c855470 | |||
253b9c76ba | |||
a4a19505d7 | |||
f5bacd5940 | |||
d77665a8a9 | |||
48e118042d | |||
42c9110a40 | |||
d100f10931 | |||
822633ca7e | |||
88f555d076 | |||
b62d64d540 | |||
497203e76a | |||
b7b5c5f843 | |||
d6b8a18e6f | |||
824292c9bd | |||
ca98bbb1f7 | |||
d5c2dfb96b | |||
aacceb6cff | |||
0348407acb | |||
6578729e1c | |||
93baaacf5f | |||
2bb2b143f2 | |||
3c538f0843 | |||
b35c7d5ae9 | |||
fea4d6c2b1 | |||
f06fc809b2 | |||
50d411a349 | |||
901dcc45df | |||
8ab324a661 | |||
9fdbaf2909 | |||
25cd3bba24 | |||
190bb58367 | |||
6e30ba871c | |||
b800511fcb | |||
5d89684a6e | |||
4fdf1085e0 | |||
424d635292 | |||
d25d8c3c56 | |||
6831c3f6c9 | |||
639ce6ad14 | |||
831e86fda6 | |||
3d91ac2bed | |||
e1739b99d3 | |||
53605c983d | |||
8cd870ed26 | |||
0caa4fbc4c | |||
fa3fab4ca0 | |||
3852da6249 | |||
28b541d49a | |||
bd454d9d20 | |||
22bbdeb7b8 | |||
0965f3958d | |||
0b3e3b5cb0 | |||
c70907ced2 | |||
67751e87c7 | |||
caef02e57f | |||
539980f75f | |||
eea15dcc4e | |||
8e758d5739 | |||
c1bde8560e | |||
fb56c6af04 | |||
c843871ac1 | |||
e5f60ec030 | |||
2244b2117d | |||
18e3089eb0 | |||
98061239f9 | |||
96497cac20 | |||
5f13b841ad | |||
dae82c0662 | |||
8676a6c10f | |||
c86eca293c | |||
023be6d770 | |||
3ed1e7dfa4 | |||
f7821c3a70 | |||
7c91139775 | |||
920bc8d9fe | |||
882c970e27 | |||
63978703cd | |||
2a9dbe6a92 | |||
d39679adaf | |||
862500cec3 | |||
6a85b43542 | |||
9d4e4c6c2b | |||
4ec588c8e3 | |||
7a81bbb58d | |||
5a272c954f | |||
285e891bef | |||
71119b9279 | |||
70ebb43017 | |||
64919ab55a | |||
d39e0c06f7 | |||
8f1e76bb43 | |||
f72b794d20 | |||
ab65c0a7c8 | |||
c930d10cc4 | |||
543601ce57 | |||
d686ec3c9f | |||
46d64139bd | |||
ab0d4b30af | |||
b77a5acfb6 | |||
18544645a2 | |||
83d547dd8e | |||
04d11112d4 | |||
52d1185dac | |||
60ed47ab10 | |||
d182cbae65 | |||
beb3d76a4b | |||
4d82dee240 | |||
d42eadcf86 | |||
a4c1f00014 | |||
637e7b44bb | |||
36652b212e | |||
b63c26a45a | |||
e58871167e | |||
5056e07f37 | |||
289a4fa001 | |||
19028db209 | |||
af7860c06a | |||
be5a439b08 | |||
9fbe5a2b22 | |||
277b38a548 | |||
70c8169711 | |||
06c9bacab0 | |||
0924e43dc2 | |||
4eb8c397f9 | |||
f7f5a10b66 | |||
ac62e1c262 | |||
e75ae9c570 | |||
5e2f790ee6 | |||
05af708e84 | |||
d0e66b31f7 | |||
f65008ccc3 | |||
645549978d | |||
63cf1745f9 | |||
28c9f857ea | |||
b4f180f5de | |||
193ac22eb5 | |||
9435003553 | |||
ed89203510 | |||
ab752e51b6 | |||
b57a3d2561 | |||
5b492fd4b5 | |||
68896aca1a | |||
2e5cf991b0 | |||
8acaf1ed96 | |||
4d3a0c0b61 | |||
265a41bc2e | |||
90ea798568 | |||
534c4f7cfc | |||
6427edb050 | |||
d0f8272fa5 | |||
d21798d4a4 | |||
64757f34c8 | |||
9e2e9fba4c | |||
148d32e28c | |||
3169f1ae67 | |||
c879061e4e | |||
628ad558d8 | |||
db2d5dbe61 | |||
a61e8c0ca0 | |||
4217d526cf |
71
.github/workflows/cmake.yml
vendored
71
.github/workflows/cmake.yml
vendored
@ -1,71 +0,0 @@
|
||||
name: CMake
|
||||
|
||||
on: [push]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally
|
||||
# well on Windows or Mac. You can convert this to a matrix build if you need
|
||||
# cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.os }}-${{ matrix.cc }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-latest
|
||||
cc: "clang"
|
||||
cxx: "clang++"
|
||||
- os: Ubuntu-20.04
|
||||
cc: "clang-10"
|
||||
cxx: "clang++-10"
|
||||
- os: Ubuntu-20.04
|
||||
cc: "gcc-10"
|
||||
cxx: "g++-10"
|
||||
- os: windows-latest
|
||||
cc: "cl"
|
||||
cxx: "cl"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
# Whether to checkout submodules: `true` to checkout submodules or `recursive` to
|
||||
# recursively checkout submodules.
|
||||
submodules: 'true'
|
||||
|
||||
- name: Set Windows ENV
|
||||
if: runner.os == 'Windows'
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Create Build Environment
|
||||
# Some projects don't allow in-source building, so create a separate build directory
|
||||
# We'll use this as our working directory for all subsequent commands
|
||||
run: cmake -E make_directory ${{github.workspace}}/build
|
||||
|
||||
- name: Configure CMake
|
||||
# Use a bash shell so we can use the same syntax for environment variable
|
||||
# access regardless of the host operating system
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}/build
|
||||
# Note the current convention is to use the -S and -B options here to specify source
|
||||
# and build directories, but this is only available with CMake 3.13 and higher.
|
||||
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_COMPILER=${{ matrix.cc }} -DCMAKE_CXX_COMPILER=${{ matrix.cxx }}
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{github.workspace}}/build
|
||||
shell: bash
|
||||
|
||||
# Execute the build. You can specify a specific target with "--target <NAME>"
|
||||
run: cmake --build . --config $BUILD_TYPE --verbose
|
||||
|
||||
# - name: Test
|
||||
# working-directory: ${{github.workspace}}/build
|
||||
# shell: bash
|
||||
# # Execute tests defined by the CMake configuration.
|
||||
# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
# run: ctest -C $BUILD_TYPE
|
155
.github/workflows/python-package.yml
vendored
Normal file
155
.github/workflows/python-package.yml
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||
|
||||
name: Python Package
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
build-sdist:
|
||||
name: Build SDist
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
# Whether to checkout submodules: `true` to checkout submodules or `recursive` to
|
||||
# recursively checkout submodules.
|
||||
submodules: 'true'
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install flake8
|
||||
python -m pip install setuptools twine build
|
||||
|
||||
- name: Lint with flake8
|
||||
run: |
|
||||
# stop the build if there are Python syntax errors or undefined names
|
||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||
|
||||
- name: Build SDist
|
||||
run: python -m build --sdist
|
||||
|
||||
- name: Check metadata
|
||||
run: python -m twine check dist/*
|
||||
|
||||
- name: Upload SDist
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: dist/*.tar.gz
|
||||
|
||||
build-wheels:
|
||||
name: Build Wheels for ${{ matrix.os }}-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ macos-latest, windows-latest, ubuntu-latest ]
|
||||
arch: [ x86_64 ]
|
||||
include:
|
||||
- arch: arm64
|
||||
os: macos-latest
|
||||
- arch: aarch64
|
||||
os: ubuntu-latest
|
||||
qemu: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
# Whether to checkout submodules: `true` to checkout submodules or `recursive` to
|
||||
# recursively checkout submodules.
|
||||
submodules: 'true'
|
||||
|
||||
- name: Install libomp
|
||||
if: runner.os == 'macOS'
|
||||
# openMP isnt part of core apple clang for some reason?
|
||||
run: brew install libomp
|
||||
|
||||
- name: Install QEMU
|
||||
# install QEMU if building for arm linux
|
||||
uses: docker/setup-qemu-action@v1
|
||||
if: matrix.qemu
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Install test images
|
||||
run: git clone https://git.pileof.rocks/drewcassidy/quicktex-test-images.git tests/images
|
||||
|
||||
- name: Build wheels
|
||||
uses: joerick/cibuildwheel@v2.3.1
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: "10.15"
|
||||
CIBW_BUILD: "cp{37,38,39,310}-*"
|
||||
CIBW_ARCHS_MACOS: ${{ matrix.arch }}
|
||||
CIBW_ARCHS_LINUX: ${{ matrix.arch }}
|
||||
CIBW_ARCHS_WINDOWS: "auto64"
|
||||
CIBW_MANYLINUX_X86_64_IMAGE: "manylinux2014"
|
||||
CIBW_MANYLINUX_AARCH64_IMAGE: "manylinux2014"
|
||||
CIBW_TEST_EXTRAS: "tests"
|
||||
CIBW_TEST_COMMAND: nosetests {project}/tests -d
|
||||
CIBW_TEST_SKIP: "*-macosx_arm64"
|
||||
|
||||
- name: Upload Wheels
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
publish:
|
||||
name: Publish to PyPI and Github
|
||||
needs: [build-wheels, build-sdist]
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
# Whether to checkout submodules: `true` to checkout submodules or `recursive` to
|
||||
# recursively checkout submodules.
|
||||
submodules: 'true'
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install yaclog
|
||||
|
||||
- name: Get version name and body
|
||||
run: |
|
||||
echo "VERSION_TILE=Version $(yaclog show -n)" >> $GITHUB_ENV
|
||||
echo "$(yaclog show -mb)" >> RELEASE.md
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: artifact
|
||||
path: dist
|
||||
|
||||
- name: List artifacts
|
||||
run: ls -l dist
|
||||
|
||||
- name: Publish to Test PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
|
||||
repository_url: https://test.pypi.org/legacy/
|
||||
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
|
||||
- name: Publish to Github
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: dist/*
|
||||
name: ${{ env.VERSION_TITLE }}
|
||||
body_path: RELEASE.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
19
.gitignore
vendored
19
.gitignore
vendored
@ -1,11 +1,27 @@
|
||||
# Python
|
||||
env/
|
||||
dist/
|
||||
build/
|
||||
*.egg-info
|
||||
*.pyc
|
||||
*.pyi
|
||||
|
||||
#sphinx
|
||||
docs/_build/
|
||||
|
||||
#mypy
|
||||
out
|
||||
|
||||
# Test images
|
||||
tests/images/
|
||||
|
||||
# IDEs
|
||||
**/.idea
|
||||
|
||||
# cmake
|
||||
# binaries
|
||||
*.so
|
||||
|
||||
# cmake
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
@ -18,3 +34,4 @@ compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
cmake-build-*
|
||||
_skbuild
|
||||
|
28
CHANGELOG.md
Normal file
28
CHANGELOG.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file
|
||||
|
||||
## 0.1.2 - 2022-03-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed sdist not including pybind
|
||||
|
||||
|
||||
## 0.1.1 - 2021-09-29
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed alpha premultiplication when generating mipmaps
|
||||
|
||||
|
||||
## 0.1.0 - 2021-05-10
|
||||
|
||||
### Added
|
||||
|
||||
- Began publishing to PyPI
|
||||
|
||||
### Changed
|
||||
|
||||
- Rewrote CI workflow to include ManyLinux2014 builds
|
||||
- Reverted project to C++17 for better compiler compatibility
|
@ -2,43 +2,88 @@ cmake_minimum_required(VERSION 3.17)
|
||||
include(CheckIPOSupported)
|
||||
include(tools/CompilerWarnings.cmake)
|
||||
|
||||
project(python_rgbcx)
|
||||
project(quicktex)
|
||||
|
||||
# Link to Pybind
|
||||
add_subdirectory(extern/pybind11)
|
||||
if (SKBUILD)
|
||||
# Scikit-Build does not add your site-packages to the search path
|
||||
# automatically, so we need to add it _or_ the pybind11 specific directory
|
||||
# here.
|
||||
execute_process(
|
||||
COMMAND "${PYTHON_EXECUTABLE}" -c
|
||||
"import pybind11; print(pybind11.get_cmake_dir())"
|
||||
OUTPUT_VARIABLE _tmp_dir
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ECHO STDOUT)
|
||||
list(APPEND CMAKE_PREFIX_PATH "${_tmp_dir}")
|
||||
endif ()
|
||||
|
||||
# Find dependencies
|
||||
find_package(Python COMPONENTS Interpreter Development)
|
||||
find_package(pybind11 CONFIG REQUIRED)
|
||||
find_package(OpenMP)
|
||||
|
||||
# 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 "python/*.cpp" "python/*.h")
|
||||
file(GLOB TEST_FILES "src/test/*.c" "src/test/*.cpp" "src/test/*.h")
|
||||
file(GLOB SOURCE_FILES
|
||||
"quicktex/*.cpp"
|
||||
"quicktex/s3tc/*.cpp"
|
||||
"quicktex/s3tc/bc1/*.cpp"
|
||||
"quicktex/s3tc/bc3/*.cpp"
|
||||
"quicktex/s3tc/bc4/*.cpp"
|
||||
"quicktex/s3tc/bc5/*.cpp"
|
||||
"quicktex/s3tc/interpolator/*.cpp"
|
||||
)
|
||||
|
||||
file(GLOB HEADER_FILES
|
||||
"quicktex/*.h"
|
||||
"quicktex/s3tc/*.h"
|
||||
"quicktex/s3tc/bc1/*.h"
|
||||
"quicktex/s3tc/bc3/*.h"
|
||||
"quicktex/s3tc/bc4/*.h"
|
||||
"quicktex/s3tc/bc5/*.h"
|
||||
"quicktex/s3tc/interpolator/*.h"
|
||||
)
|
||||
|
||||
file(GLOB TEST_FILES "tests/*.cpp")
|
||||
|
||||
file(GLOB_RECURSE PYTHON_FILES "src/**/*.py")
|
||||
|
||||
# Organize source files together for some IDEs
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE_FILES} ${HEADER_FILES} ${PYTHON_FILES})
|
||||
|
||||
# Add python module
|
||||
pybind11_add_module(python_rgbcx
|
||||
pybind11_add_module(_quicktex
|
||||
${SOURCE_FILES}
|
||||
${HEADER_FILES}
|
||||
${PYTHON_FILES})
|
||||
${HEADER_FILES})
|
||||
|
||||
add_executable(test_rgbcx
|
||||
add_executable(test_quicktex
|
||||
${SOURCE_FILES}
|
||||
${HEADER_FILES}
|
||||
${TEST_FILES})
|
||||
|
||||
target_link_libraries(test_quicktex PRIVATE pybind11::embed)
|
||||
|
||||
target_compile_definitions(test_quicktex PRIVATE -DCUSTOM_SYS_PATH="${CMAKE_HOME_DIRECTORY}/env/lib/python3.9/site-packages")
|
||||
target_compile_definitions(_quicktex PRIVATE VERSION_INFO=${QUICKTEX_VERSION_INFO})
|
||||
|
||||
# enable openMP if available
|
||||
if (OpenMP_CXX_FOUND)
|
||||
target_link_libraries(_quicktex PUBLIC OpenMP::OpenMP_CXX)
|
||||
target_link_libraries(test_quicktex PUBLIC OpenMP::OpenMP_CXX)
|
||||
endif ()
|
||||
|
||||
# Set module features, like C/C++ standards
|
||||
target_compile_features(python_rgbcx PUBLIC cxx_std_20 c_std_11)
|
||||
target_compile_features(test_rgbcx PUBLIC cxx_std_20 c_std_11)
|
||||
target_compile_features(_quicktex PUBLIC cxx_std_17 c_std_11)
|
||||
target_compile_features(test_quicktex PUBLIC cxx_std_17 c_std_11)
|
||||
|
||||
set_project_warnings(python_rgbcx)
|
||||
set_project_warnings(test_rgbcx)
|
||||
set_project_warnings(_quicktex)
|
||||
set_project_warnings(test_quicktex)
|
||||
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||
|
||||
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++")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -stdlib=libc++ -fsanitize=undefined")
|
||||
set(PROJECT_WARNINGS ${CLANG_WARNINGS})
|
||||
endif ()
|
||||
|
||||
install(TARGETS _quicktex LIBRARY DESTINATION quicktex)
|
||||
|
1
MANIFEST.in
Normal file
1
MANIFEST.in
Normal file
@ -0,0 +1 @@
|
||||
graft extern
|
69
README.md
69
README.md
@ -1,3 +1,68 @@
|
||||
# python-rgbcx
|
||||
A python library for using the rgbcx texture compressor
|
||||
# Quicktex
|
||||
A python library for using DDS files
|
||||
|
||||
Quicktex is a python library and command line tool for encoding and decoding DDS files.
|
||||
It is based on the [RGBCX encoder](https://github.com/richgel999/bc7enc), which is currently
|
||||
one of the [highest quality S3TC encoders available](https://aras-p.info/blog/2020/12/08/Texture-Compression-in-2020/).
|
||||
Quicktex has a python front end, but the encoding and decoding is all done in C++ for speed
|
||||
comparable to the original library.
|
||||
|
||||
## Installation
|
||||
|
||||
To install, first clone this repo and cd into it, then run:
|
||||
|
||||
```shell
|
||||
git submodule update --init
|
||||
pip install .
|
||||
```
|
||||
and setuptools will take care of any dependencies for you.
|
||||
|
||||
If you are on macOS, it is recommended to first install openMP from homebrew to enable
|
||||
multithreading, since it is not included in the default Apple Clang install:
|
||||
|
||||
```shell
|
||||
brew install libomp
|
||||
```
|
||||
|
||||
The package also makes tests, stub generation, and docs available. To install the
|
||||
required dependencies for them, install with options like so:
|
||||
|
||||
```shell
|
||||
pip install .[tests,stubs,docs]
|
||||
```
|
||||
|
||||
Quicktex will be available on Pypi once it is out of alpha.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
Usage: quicktex [OPTIONS] COMMAND [ARGS]...
|
||||
|
||||
Encode and Decode various image formats
|
||||
|
||||
Options:
|
||||
--version Show the version and exit.
|
||||
--help Show this message and exit.
|
||||
|
||||
Commands:
|
||||
decode Decode DDS files to images.
|
||||
encode Encode images to DDS files of the given format.
|
||||
```
|
||||
|
||||
To decode DDS files to images, use the `decode` subdommand, along with a glob or a
|
||||
list of files to decode.
|
||||
|
||||
To encode images to DDS files, use the `encode` subcommand, plus an additional
|
||||
subcommand for the format. For example, `quicktex encode bc1 bun.png` will encode
|
||||
bun.png in the current directory to a bc1/DXT1 dds file next to it.
|
||||
|
||||
`encode` and `decode` both accept several common parameters:
|
||||
|
||||
- `-f, --flip / -F, --no-flip`: Vertically flip image before/after converting.
|
||||
[default: True]
|
||||
- `-r, --remove`: Remove input images after converting.
|
||||
- `-s, --suffix TEXT`: Suffix to append to output filename(s).
|
||||
Ignored if `output` is a single file.
|
||||
- `-o, --output`: Output file or directory. If outputting to a file, input filenames
|
||||
must be only a single item. By default, files are converted in place.
|
||||
|
||||
|
74
docs/conf.py
Normal file
74
docs/conf.py
Normal file
@ -0,0 +1,74 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('..'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'Quicktex'
|
||||
copyright = '2021, Andrew Cassidy'
|
||||
author = 'Andrew Cassidy'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'myst_parser',
|
||||
'sphinx_rtd_theme',
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.intersphinx',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# -- Options for Autodoc -----------------------------------------------------
|
||||
|
||||
add_module_names = False
|
||||
autodoc_docstring_signature = True
|
||||
|
||||
autodoc_default_options = {
|
||||
'member-order': 'bysource',
|
||||
'exclude-members': '__weakref__',
|
||||
'imported-members': True,
|
||||
}
|
||||
|
||||
# -- Options for Intersphinx ------------------------------------------------
|
||||
|
||||
# This config value contains the locations and names of other projects that
|
||||
# should be linked to in this documentation.
|
||||
intersphinx_mapping = {
|
||||
'python': ('https://docs.python.org/3', None),
|
||||
'PIL': ('https://pillow.readthedocs.io/en/stable/', None)
|
||||
}
|
15
docs/index.rst
Normal file
15
docs/index.rst
Normal file
@ -0,0 +1,15 @@
|
||||
Welcome to Quicktex's documentation!
|
||||
========================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
reference/index.rst
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
35
docs/make.bat
Normal file
35
docs/make.bat
Normal file
@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
10
docs/reference/conversion.rst
Normal file
10
docs/reference/conversion.rst
Normal file
@ -0,0 +1,10 @@
|
||||
.. py:currentmodule:: quicktex
|
||||
|
||||
Conversion
|
||||
============
|
||||
|
||||
.. autoclass:: BlockEncoder
|
||||
:members:
|
||||
|
||||
.. autoclass:: BlockDecoder
|
||||
:members:
|
6
docs/reference/dds.rst
Normal file
6
docs/reference/dds.rst
Normal file
@ -0,0 +1,6 @@
|
||||
dds module
|
||||
==========
|
||||
|
||||
.. automodule:: quicktex.dds
|
||||
:members:
|
||||
|
7
docs/reference/formats/index.rst
Normal file
7
docs/reference/formats/index.rst
Normal file
@ -0,0 +1,7 @@
|
||||
Formats
|
||||
=======
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
s3tc.rst
|
97
docs/reference/formats/s3tc.rst
Normal file
97
docs/reference/formats/s3tc.rst
Normal file
@ -0,0 +1,97 @@
|
||||
s3tc module
|
||||
===========
|
||||
|
||||
.. automodule:: quicktex.s3tc
|
||||
|
||||
bc1 module
|
||||
----------
|
||||
.. automodule:: quicktex.s3tc.bc1
|
||||
|
||||
.. autoclass:: BC1Encoder
|
||||
|
||||
.. automethod:: __init__
|
||||
.. automethod:: set_level
|
||||
|
||||
.. autoproperty:: color_mode(self) -> ColorMode
|
||||
.. autoproperty:: interpolator(self) -> quicktex.s3tc.interpolator.Interpolator
|
||||
|
||||
.. autoclass:: quicktex.s3tc.bc1::BC1Encoder.ColorMode
|
||||
|
||||
**Advanced API**
|
||||
|
||||
Additional properties are provided for finer-grained control over quality and performance
|
||||
|
||||
.. autoproperty:: error_mode(self) -> ErrorMode
|
||||
.. autoproperty:: endpoint_mode(self) -> EndpointMode
|
||||
|
||||
.. autoproperty:: two_ls_passes(self) -> bool
|
||||
.. autoproperty:: two_ep_passes(self) -> bool
|
||||
.. autoproperty:: two_cf_passes(self) -> bool
|
||||
|
||||
.. autoproperty:: exhaustive(self) -> bool
|
||||
.. autoproperty:: search_rounds(self) -> int
|
||||
.. autoproperty:: orderings(self) -> tuple[int, int]
|
||||
.. autoproperty:: power_iterations(self) -> int
|
||||
.. autoattribute:: max_power_iterations
|
||||
.. autoattribute:: min_power_iterations
|
||||
|
||||
.. autoclass:: quicktex.s3tc.bc1::BC1Encoder.EndpointMode
|
||||
.. autoclass:: quicktex.s3tc.bc1::BC1Encoder.ErrorMode
|
||||
|
||||
.. autoclass:: BC1Decoder
|
||||
|
||||
.. automethod:: __init__
|
||||
.. autoproperty:: interpolator(self) -> quicktex.s3tc.interpolator.Interpolator
|
||||
.. autoproperty:: write_alpha(self) -> bool
|
||||
|
||||
bc3 module
|
||||
----------
|
||||
.. automodule:: quicktex.s3tc.bc3
|
||||
|
||||
.. autoclass:: BC3Encoder
|
||||
|
||||
.. automethod:: __init__
|
||||
.. autoproperty:: bc1_encoder(self) -> quicktex.s3tc.bc1.BC1Encoder
|
||||
.. autoproperty:: bc4_encoder(self) -> quicktex.s3tc.bc4.BC4Encoder
|
||||
|
||||
.. autoclass:: BC3Decoder
|
||||
|
||||
.. automethod:: __init__
|
||||
.. autoproperty:: bc1_decoder(self) -> quicktex.s3tc.bc1.BC1Decoder
|
||||
.. autoproperty:: bc4_decoder(self) -> quicktex.s3tc.bc4.BC4Decoder
|
||||
|
||||
bc4 module
|
||||
----------
|
||||
.. automodule:: quicktex.s3tc.bc4
|
||||
|
||||
.. autoclass:: BC4Encoder
|
||||
|
||||
.. automethod:: __init__
|
||||
.. autoproperty:: channel(self) -> int
|
||||
|
||||
.. autoclass:: BC4Decoder
|
||||
|
||||
.. automethod:: __init__
|
||||
.. autoproperty:: channel(self) -> int
|
||||
|
||||
bc5 module
|
||||
----------
|
||||
.. automodule:: quicktex.s3tc.bc5
|
||||
|
||||
.. autoclass:: BC5Encoder
|
||||
|
||||
.. automethod:: __init__
|
||||
.. autoproperty:: bc4_encoders(self) -> tuple[quicktex.s3tc.bc4.BC4Encoder]
|
||||
.. autoproperty:: channels(self) -> tuple[int, int]
|
||||
|
||||
.. autoclass:: BC5Decoder
|
||||
|
||||
.. automethod:: __init__
|
||||
.. autoproperty:: bc4_decoders(self) -> tuple[quicktex.s3tc.bc4.BC4Decoder]
|
||||
.. autoproperty:: channels(self) -> tuple[int, int]
|
||||
|
||||
interpolator module
|
||||
-------------------
|
||||
|
||||
.. automodule:: quicktex.s3tc.interpolator
|
||||
:members:
|
5
docs/reference/image_utils.rst
Normal file
5
docs/reference/image_utils.rst
Normal file
@ -0,0 +1,5 @@
|
||||
image_utils module
|
||||
==================
|
||||
|
||||
.. automodule:: quicktex.image_utils
|
||||
:members:
|
10
docs/reference/index.rst
Normal file
10
docs/reference/index.rst
Normal file
@ -0,0 +1,10 @@
|
||||
Reference
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
conversion.rst
|
||||
dds.rst
|
||||
image_utils.rst
|
||||
formats/index.rst
|
1
extern/pybind11
vendored
1
extern/pybind11
vendored
Submodule extern/pybind11 deleted from 8de7772cc7
58
pyproject.toml
Normal file
58
pyproject.toml
Normal file
@ -0,0 +1,58 @@
|
||||
[build-system]
|
||||
requires = [
|
||||
"setuptools>=61",
|
||||
"setuptools_scm>=6.2",
|
||||
"wheel",
|
||||
"pybind11>=2.9.0",
|
||||
"cmake>=3.22",
|
||||
"scikit-build>0.13",
|
||||
"ninja; sys_platform != 'win32'",
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "quicktex"
|
||||
description = "A fast block compression library for python"
|
||||
readme = "README.md"
|
||||
license = { file = "LICENSE.md" }
|
||||
authors = [{ name = "Andrew Cassidy", email = "drewcassidy@me.com" }]
|
||||
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Topic :: Multimedia :: Graphics :: Graphics Conversion",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: C++",
|
||||
]
|
||||
|
||||
requires-python = ">=3.7"
|
||||
dependencies = ["Pillow", "click"]
|
||||
dynamic = ["version"]
|
||||
|
||||
[project.optional-dependencies]
|
||||
tests = ["nose", "parameterized"]
|
||||
docs = ["sphinx", "myst-parser", "sphinx-rtd-theme"]
|
||||
stubs = ["pybind11-stubgen"]
|
||||
|
||||
[project.urls]
|
||||
repository = "https://github.com/drewcassidy/quicktex"
|
||||
changelog = "https://github.com/drewcassidy/quicktex/blob/main/CHANGELOG.md"
|
||||
|
||||
[project.scripts]
|
||||
quicktex = "quicktex.__main__:main"
|
||||
|
||||
[tool.setuptools]
|
||||
zip-safe = false
|
||||
packages = { find = { include = ["quicktex*"] } } # only include quicktex and not tests
|
||||
package-data = { '*' = ['py.typed', '*.pyi'] } # include stubs
|
||||
package-dir = { '' = '.' } # without this line, C++ source files get included in the bdist
|
||||
|
||||
[tool.setuptools_scm]
|
128
quicktex/Color.cpp
Normal file
128
quicktex/Color.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
/* Quicktex 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 "Color.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Vector4.h"
|
||||
#include "Vector4Int.h"
|
||||
#include "util.h" // for scale5To8, scale8To5, assert5bit, scale6To8
|
||||
|
||||
namespace quicktex {
|
||||
|
||||
Color::Color(Vector4Int v) {
|
||||
if (v.MaxAbs() > 0xFF) throw std::invalid_argument("Vector members out of range");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (v[i] < 0) throw std::range_error("Color members cannot be negative");
|
||||
}
|
||||
|
||||
r = static_cast<uint8_t>(v[0]);
|
||||
g = static_cast<uint8_t>(v[1]);
|
||||
b = static_cast<uint8_t>(v[2]);
|
||||
a = static_cast<uint8_t>(v[3]);
|
||||
}
|
||||
|
||||
uint16_t Color::Pack565Unscaled(uint8_t r, uint8_t g, uint8_t b) {
|
||||
assert5bit(r);
|
||||
assert6bit(g);
|
||||
assert5bit(b);
|
||||
return static_cast<uint16_t>(b | (g << 5) | (r << 11));
|
||||
}
|
||||
|
||||
uint16_t Color::Pack565(uint8_t r, uint8_t g, uint8_t b) { return Pack565Unscaled(scale8To5(r), scale8To6(g), scale8To5(b)); }
|
||||
|
||||
Color Color::Unpack565Unscaled(uint16_t Packed) {
|
||||
uint8_t r = (Packed >> 11) & 0x1F;
|
||||
uint8_t g = (Packed >> 5) & 0x3F;
|
||||
uint8_t b = Packed & 0x1F;
|
||||
|
||||
return Color(r, g, b);
|
||||
}
|
||||
|
||||
Color Color::Unpack565(uint16_t Packed) {
|
||||
uint8_t r = static_cast<uint8_t>(scale5To8((Packed >> 11) & 0x1FU));
|
||||
uint8_t g = static_cast<uint8_t>(scale6To8((Packed >> 5) & 0x3FU));
|
||||
uint8_t b = static_cast<uint8_t>(scale5To8(Packed & 0x1FU));
|
||||
|
||||
return Color(r, g, b);
|
||||
}
|
||||
|
||||
Color Color::PreciseRound565(Vector4 &v) {
|
||||
int trial_r = (int)(v[0] * UINT5_MAX);
|
||||
int trial_g = (int)(v[1] * UINT6_MAX);
|
||||
int trial_b = (int)(v[2] * UINT5_MAX);
|
||||
|
||||
// clamp to prevent weirdness with slightly out of bounds float values
|
||||
uint8_t r = (uint8_t)clampi(trial_r, 0, UINT5_MAX);
|
||||
uint8_t g = (uint8_t)clampi(trial_g, 0, UINT6_MAX);
|
||||
uint8_t b = (uint8_t)clampi(trial_b, 0, UINT5_MAX);
|
||||
|
||||
// increment each channel if above the rounding point
|
||||
r += v[0] > Midpoints5bit[r];
|
||||
g += v[1] > Midpoints6bit[g];
|
||||
b += v[2] > Midpoints5bit[b];
|
||||
|
||||
assert5bit(r);
|
||||
assert6bit(g);
|
||||
assert5bit(b);
|
||||
|
||||
return Color(r, g, b);
|
||||
}
|
||||
|
||||
void Color::SetRGB(uint8_t vr, uint8_t vg, uint8_t vb) {
|
||||
r = vr;
|
||||
g = vg;
|
||||
b = vb;
|
||||
}
|
||||
|
||||
size_t Color::MinChannelRGB() {
|
||||
if (r <= g && r <= b) return 0;
|
||||
if (g <= b && g <= r) return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
size_t Color::MaxChannelRGB() {
|
||||
if (r >= g && r >= b) return 0;
|
||||
if (g >= b && g >= r) return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
Color Color::Min(const Color &A, const Color &B) { return Color(std::min(A[0], B[0]), std::min(A[1], B[1]), std::min(A[2], B[2]), std::min(A[3], B[3])); }
|
||||
|
||||
Color Color::Max(const Color &a, const Color &b) { return Color(std::max(a[0], b[0]), std::max(a[1], b[1]), std::max(a[2], b[2]), std::max(a[3], b[3])); }
|
||||
|
||||
Color::operator Vector4() const { return Vector4(r, g, b, a); }
|
||||
Color::operator Vector4Int() const { return Vector4Int(r, g, b, a); }
|
||||
Vector4Int operator-(const Color &lhs, const Color &rhs) {
|
||||
Vector4Int result;
|
||||
for (unsigned i = 0; i < 4; i++) { result[i] = (int)lhs[i] - rhs[i]; }
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t Color::Pack565() const { return Pack565(r, g, b); }
|
||||
uint16_t Color::Pack565Unscaled() const { return Pack565Unscaled(r, g, b); }
|
||||
|
||||
Color Color::ScaleTo565() const { return Color(scale8To5(r), scale8To6(g), scale8To5(b)); }
|
||||
Color Color::ScaleFrom565() const { return Color(scale5To8(r), scale6To8(g), scale5To8(b)); }
|
||||
|
||||
bool Color::operator==(const Color &Rhs) const { return r == Rhs.r && g == Rhs.g && b == Rhs.b && a == Rhs.a; }
|
||||
bool Color::operator!=(const Color &Rhs) const { return !(Rhs == *this); }
|
||||
|
||||
} // namespace quicktex
|
99
quicktex/Color.h
Normal file
99
quicktex/Color.h
Normal file
@ -0,0 +1,99 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cassert> // for assert
|
||||
#include <cstddef> // for size_t
|
||||
#include <cstdint> // for uint8_t, uint16_t
|
||||
|
||||
namespace quicktex {
|
||||
class Vector4;
|
||||
class Vector4Int;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
class Color {
|
||||
public:
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
|
||||
constexpr Color() : Color(0, 0, 0, 0xFF) {}
|
||||
|
||||
constexpr Color(uint8_t vr, uint8_t vg, uint8_t vb, uint8_t va = 0xFF) : r(vr), g(vg), b(vb), a(va) {}
|
||||
|
||||
Color(Vector4Int v);
|
||||
|
||||
static uint16_t Pack565Unscaled(uint8_t r, uint8_t g, uint8_t b);
|
||||
static uint16_t Pack565(uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
static Color Unpack565Unscaled(uint16_t Packed);
|
||||
static Color Unpack565(uint16_t Packed);
|
||||
|
||||
static Color PreciseRound565(Vector4 &v);
|
||||
|
||||
static Color Min(const Color &A, const Color &B);
|
||||
static Color Max(const Color &A, const Color &B);
|
||||
|
||||
bool operator==(const Color &Rhs) const;
|
||||
bool operator!=(const Color &Rhs) const;
|
||||
|
||||
uint8_t operator[](size_t index) const {
|
||||
assert(index < 4);
|
||||
return reinterpret_cast<const uint8_t *>(this)[index];
|
||||
}
|
||||
uint8_t &operator[](size_t index) {
|
||||
assert(index < 4);
|
||||
return reinterpret_cast<uint8_t *>(this)[index];
|
||||
}
|
||||
|
||||
operator Vector4() const;
|
||||
operator Vector4Int() const;
|
||||
friend Vector4Int operator-(const Color &lhs, const Color &rhs);
|
||||
|
||||
void SetRGB(uint8_t vr, uint8_t vg, uint8_t vb);
|
||||
void SetRGB(const Color &other) { SetRGB(other.r, other.g, other.b); }
|
||||
|
||||
uint16_t Pack565() const;
|
||||
uint16_t Pack565Unscaled() const;
|
||||
|
||||
Color ScaleTo565() const;
|
||||
Color ScaleFrom565() const;
|
||||
|
||||
size_t MinChannelRGB();
|
||||
size_t MaxChannelRGB();
|
||||
|
||||
bool IsGrayscale() const { return ((r == g) && (r == b)); }
|
||||
bool IsBlack() const { return (r | g | b) < 4; }
|
||||
|
||||
int GetLuma() const { return (13938U * r + 46869U * g + 4729U * b + 32768U) >> 16U; } // REC709 weightings
|
||||
|
||||
private:
|
||||
static constexpr float Midpoints5bit[32] = {.015686f, .047059f, .078431f, .111765f, .145098f, .176471f, .207843f, .241176f, .274510f, .305882f, .337255f,
|
||||
.370588f, .403922f, .435294f, .466667f, .5f, .533333f, .564706f, .596078f, .629412f, .662745f, .694118f,
|
||||
.725490f, .758824f, .792157f, .823529f, .854902f, .888235f, .921569f, .952941f, .984314f, 1e+37f};
|
||||
static constexpr float Midpoints6bit[64] = {.007843f, .023529f, .039216f, .054902f, .070588f, .086275f, .101961f, .117647f, .133333f, .149020f, .164706f,
|
||||
.180392f, .196078f, .211765f, .227451f, .245098f, .262745f, .278431f, .294118f, .309804f, .325490f, .341176f,
|
||||
.356863f, .372549f, .388235f, .403922f, .419608f, .435294f, .450980f, .466667f, .482353f, .500000f, .517647f,
|
||||
.533333f, .549020f, .564706f, .580392f, .596078f, .611765f, .627451f, .643137f, .658824f, .674510f, .690196f,
|
||||
.705882f, .721569f, .737255f, .754902f, .772549f, .788235f, .803922f, .819608f, .835294f, .850980f, .866667f,
|
||||
.882353f, .898039f, .913725f, .929412f, .945098f, .960784f, .976471f, .992157f, 1e+37f};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
} // namespace quicktex
|
124
quicktex/ColorBlock.h
Normal file
124
quicktex/ColorBlock.h
Normal file
@ -0,0 +1,124 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Color.h"
|
||||
#include "Vector4Int.h"
|
||||
|
||||
namespace quicktex {
|
||||
using Coords = std::tuple<int, int>;
|
||||
|
||||
template <int N, int M> class ColorBlock {
|
||||
public:
|
||||
struct Metrics {
|
||||
Color min;
|
||||
Color max;
|
||||
Color avg;
|
||||
bool is_greyscale;
|
||||
bool has_black;
|
||||
Vector4Int sums;
|
||||
};
|
||||
|
||||
static constexpr int Width = N;
|
||||
static constexpr int Height = M;
|
||||
|
||||
constexpr Color Get(int x, int y) const {
|
||||
if (x >= Width || x < 0) throw std::invalid_argument("x value out of range");
|
||||
if (y >= Height || y < 0) throw std::invalid_argument("y value out of range");
|
||||
|
||||
return _pixels[x + (N * y)];
|
||||
}
|
||||
|
||||
constexpr Color Get(int i) const {
|
||||
if (i >= N * M || i < 0) throw std::invalid_argument("i value out of range");
|
||||
return _pixels[i];
|
||||
}
|
||||
|
||||
void Set(int x, int y, const Color &value) {
|
||||
if (x >= Width || x < 0) throw std::invalid_argument("x value out of range");
|
||||
if (y >= Height || y < 0) throw std::invalid_argument("y value out of range");
|
||||
_pixels[x + (N * y)] = value;
|
||||
}
|
||||
|
||||
void Set(int i, const Color &value) {
|
||||
if (i >= N * M || i < 0) throw std::invalid_argument("i value out of range");
|
||||
_pixels[i] = value;
|
||||
}
|
||||
|
||||
void GetRow(int y, Color *dst) const {
|
||||
if (y >= Height || y < 0) throw std::invalid_argument("y value out of range");
|
||||
std::memcpy(dst, &_pixels[N * y], N * sizeof(Color));
|
||||
}
|
||||
|
||||
void SetRow(int y, const Color *src) {
|
||||
if (y >= Height || y < 0) throw std::invalid_argument("y value out of range");
|
||||
std::memcpy(&_pixels[N * y], src, N * sizeof(Color));
|
||||
}
|
||||
|
||||
bool IsSingleColor() const {
|
||||
auto first = Get(0, 0);
|
||||
for (unsigned j = 1; j < M * N; j++) {
|
||||
if (Get(j) != first) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Metrics GetMetrics(bool ignore_black = false) const {
|
||||
Metrics metrics;
|
||||
metrics.min = Color(UINT8_MAX, UINT8_MAX, UINT8_MAX);
|
||||
metrics.max = Color(0, 0, 0);
|
||||
metrics.has_black = false;
|
||||
metrics.is_greyscale = true;
|
||||
metrics.sums = {0, 0, 0};
|
||||
|
||||
unsigned total = 0;
|
||||
|
||||
for (unsigned i = 0; i < M * N; i++) {
|
||||
Color val = Get(i);
|
||||
bool is_black = val.IsBlack();
|
||||
|
||||
metrics.has_black |= is_black;
|
||||
|
||||
if (ignore_black && is_black) { continue; }
|
||||
|
||||
metrics.is_greyscale &= val.IsGrayscale();
|
||||
for (unsigned c = 0; c < 3; c++) {
|
||||
metrics.min[c] = std::min(metrics.min[c], val[c]);
|
||||
metrics.max[c] = std::max(metrics.max[c], val[c]);
|
||||
metrics.sums[c] += val[c];
|
||||
}
|
||||
total++;
|
||||
}
|
||||
|
||||
if (total > 0) metrics.avg = (metrics.sums + Vector4Int(total / 2)) / (int)total; // half-total added for better rounding
|
||||
return metrics;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<Color, N * M> _pixels;
|
||||
};
|
||||
|
||||
} // namespace quicktex
|
68
quicktex/Decoder.h
Normal file
68
quicktex/Decoder.h
Normal file
@ -0,0 +1,68 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich 2020 <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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "ColorBlock.h"
|
||||
#include "Texture.h"
|
||||
|
||||
namespace quicktex {
|
||||
|
||||
template <class T> class Decoder {
|
||||
public:
|
||||
using Texture = T;
|
||||
|
||||
virtual ~Decoder() = default;
|
||||
virtual RawTexture Decode(const T &encoded) const = 0;
|
||||
};
|
||||
|
||||
template <class T> class BlockDecoder : public Decoder<T> {
|
||||
public:
|
||||
inline static constexpr int BlockWidth = T::BlockType::Width;
|
||||
inline static constexpr int BlockHeight = T::BlockType::Height;
|
||||
|
||||
using Texture = T;
|
||||
using EncodedBlock = typename T::BlockType;
|
||||
using DecodedBlock = ColorBlock<BlockWidth, BlockHeight>;
|
||||
|
||||
virtual DecodedBlock DecodeBlock(const EncodedBlock &block) const = 0;
|
||||
|
||||
virtual RawTexture Decode(const T &encoded) const override {
|
||||
auto decoded = RawTexture(encoded.Width(), encoded.Height());
|
||||
|
||||
int blocks_x = encoded.BlocksX();
|
||||
int blocks_y = encoded.BlocksY();
|
||||
|
||||
// from experimentation, multithreading this using OpenMP 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
|
||||
for (int y = 0; y < blocks_y; y++) {
|
||||
for (int x = 0; x < blocks_x; x++) {
|
||||
auto block = encoded.GetBlock(x, y);
|
||||
auto pixels = DecodeBlock(block);
|
||||
decoded.SetBlock<BlockWidth, BlockHeight>(x, y, pixels);
|
||||
}
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
};
|
||||
} // namespace quicktex
|
72
quicktex/Encoder.h
Normal file
72
quicktex/Encoder.h
Normal file
@ -0,0 +1,72 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich 2020 <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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "ColorBlock.h"
|
||||
#include "Texture.h"
|
||||
|
||||
namespace quicktex {
|
||||
|
||||
template <typename T> class Encoder {
|
||||
public:
|
||||
using Texture = T;
|
||||
|
||||
virtual ~Encoder() = default;
|
||||
virtual T Encode(const RawTexture &decoded) const = 0;
|
||||
};
|
||||
|
||||
template <typename T> class BlockEncoder : public Encoder<T> {
|
||||
public:
|
||||
inline static constexpr int BlockWidth = T::BlockType::Width;
|
||||
inline static constexpr int BlockHeight = T::BlockType::Height;
|
||||
|
||||
using Texture = T;
|
||||
using EncodedBlock = typename T::BlockType;
|
||||
using DecodedBlock = ColorBlock<BlockWidth, BlockHeight>;
|
||||
|
||||
virtual EncodedBlock EncodeBlock(const DecodedBlock &block) const = 0;
|
||||
|
||||
virtual T Encode(const RawTexture &decoded) const override {
|
||||
auto encoded = T(decoded.Width(), decoded.Height());
|
||||
|
||||
int blocks_x = encoded.BlocksX();
|
||||
int blocks_y = encoded.BlocksY();
|
||||
|
||||
// from experimentation, multithreading this using OpenMP sometimes actually makes encoding slower
|
||||
// due to thread creation/teardown taking longer than the encoding process itself.
|
||||
// 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 (blocks_x * blocks_y >= MTThreshold())
|
||||
for (int y = 0; y < blocks_y; y++) {
|
||||
for (int x = 0; x < blocks_x; x++) {
|
||||
auto pixels = decoded.GetBlock<BlockWidth, BlockHeight>(x, y);
|
||||
auto block = EncodeBlock(pixels);
|
||||
encoded.SetBlock(x, y, block);
|
||||
}
|
||||
}
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
virtual size_t MTThreshold() const { return SIZE_MAX; };
|
||||
};
|
||||
} // namespace quicktex
|
50
quicktex/Matrix4x4.cpp
Normal file
50
quicktex/Matrix4x4.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
/* Quicktex 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 "Matrix4x4.h"
|
||||
|
||||
namespace quicktex {
|
||||
|
||||
Matrix4x4 operator*(const Matrix4x4& lhs, const Matrix4x4& rhs) {
|
||||
Matrix4x4 trans_rhs = rhs.Transpose(); // 🏳️⚧️
|
||||
Matrix4x4 result;
|
||||
for (unsigned r = 0; r < 4; r++) {
|
||||
for (unsigned c = 0; c < 4; c++) { result[r][c] = lhs[r].Dot(trans_rhs[c]); }
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector4 operator*(const Matrix4x4& lhs, const Vector4& rhs) {
|
||||
Vector4 result;
|
||||
|
||||
for (unsigned r = 0; r < 4; r++) { result[r] = rhs.Dot(lhs[r]); }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Matrix4x4::Mirror() {
|
||||
for (unsigned r = 0; r < 3; r++) {
|
||||
for (unsigned c = (r + 1); c < 4; c++) {
|
||||
_r[c][r] = _r[r][c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace quicktex
|
95
quicktex/Matrix4x4.h
Normal file
95
quicktex/Matrix4x4.h
Normal file
@ -0,0 +1,95 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
|
||||
#include "Vector4.h"
|
||||
|
||||
namespace quicktex {
|
||||
|
||||
class Matrix4x4 {
|
||||
public:
|
||||
static Matrix4x4 Identity() {
|
||||
Matrix4x4 result;
|
||||
for (unsigned i = 0; i < 4; i++) { result[i][i] = 1; }
|
||||
return result;
|
||||
}
|
||||
|
||||
static Matrix4x4 Transpose(const Matrix4x4 &val) {
|
||||
Matrix4x4 result;
|
||||
for (unsigned r = 0; r < 3; r++) {
|
||||
for (unsigned c = 0; c < 3; c++) { result[r][c] = val[c][r]; }
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector4 operator[](size_t index) const {
|
||||
assert(index < 4);
|
||||
return _r[index];
|
||||
}
|
||||
Vector4 &operator[](size_t index) {
|
||||
assert(index < 4);
|
||||
return _r[index];
|
||||
}
|
||||
|
||||
friend Matrix4x4 operator*(const Matrix4x4 &lhs, const Matrix4x4 &rhs);
|
||||
friend Vector4 operator*(const Matrix4x4 &lhs, const Vector4 &rhs);
|
||||
|
||||
friend Matrix4x4 operator+(const Matrix4x4 &lhs, const Matrix4x4 &rhs) { return DoOp(lhs, rhs, std::plus()); }
|
||||
friend Matrix4x4 operator-(const Matrix4x4 &lhs, const Matrix4x4 &rhs) { return DoOp(lhs, rhs, std::minus()); }
|
||||
|
||||
friend Matrix4x4 operator+(const Matrix4x4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::plus()); }
|
||||
friend Matrix4x4 operator-(const Matrix4x4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::minus()); }
|
||||
friend Matrix4x4 operator*(const Matrix4x4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::multiplies()); }
|
||||
friend Matrix4x4 operator/(const Matrix4x4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::divides()); }
|
||||
|
||||
friend Matrix4x4 &operator+=(Matrix4x4 &lhs, const Matrix4x4 &rhs) { return lhs = lhs + rhs; }
|
||||
friend Matrix4x4 &operator-=(Matrix4x4 &lhs, const Matrix4x4 &rhs) { return lhs = lhs - rhs; }
|
||||
friend Matrix4x4 &operator*=(Matrix4x4 &lhs, const Matrix4x4 &rhs) { return lhs = lhs * rhs; }
|
||||
|
||||
friend Matrix4x4 &operator+=(Matrix4x4 &lhs, const float &rhs) { return lhs = lhs + rhs; }
|
||||
friend Matrix4x4 &operator-=(Matrix4x4 &lhs, const float &rhs) { return lhs = lhs - rhs; }
|
||||
friend Matrix4x4 &operator*=(Matrix4x4 &lhs, const float &rhs) { return lhs = lhs * rhs; }
|
||||
friend Matrix4x4 &operator/=(Matrix4x4 &lhs, const float &rhs) { return lhs = lhs / rhs; }
|
||||
|
||||
Matrix4x4 Transpose() const { return Transpose(*this); }
|
||||
|
||||
void Mirror();
|
||||
|
||||
private:
|
||||
template <typename Op> friend Matrix4x4 DoOp(const Matrix4x4 &lhs, const Matrix4x4 &rhs, Op f) {
|
||||
Matrix4x4 result;
|
||||
for (unsigned r = 0; r < 4; r++) { result[r] = f(lhs[r], rhs[r]); }
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Op> friend Matrix4x4 DoOp(const Matrix4x4 &lhs, const float &rhs, Op f) {
|
||||
Matrix4x4 result;
|
||||
for (unsigned r = 0; r < 4; r++) { result[r] = f(lhs[r], rhs); }
|
||||
return result;
|
||||
}
|
||||
|
||||
std::array<Vector4, 4> _r;
|
||||
};
|
||||
} // namespace quicktex
|
187
quicktex/Texture.h
Normal file
187
quicktex/Texture.h
Normal file
@ -0,0 +1,187 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "Color.h"
|
||||
#include "ColorBlock.h"
|
||||
|
||||
namespace quicktex {
|
||||
|
||||
class Texture {
|
||||
public:
|
||||
virtual ~Texture() = default;
|
||||
|
||||
virtual int Width() const { return _width; }
|
||||
virtual int Height() const { return _height; }
|
||||
virtual std::tuple<int, int> Size() const { return std::tuple<int, int>(_width, _height); }
|
||||
|
||||
/**
|
||||
* The texture's total size
|
||||
* @return The size of the texture in bytes.
|
||||
*/
|
||||
virtual size_t NBytes() const noexcept = 0;
|
||||
|
||||
virtual const uint8_t *Data() const noexcept = 0;
|
||||
virtual uint8_t *Data() noexcept = 0;
|
||||
|
||||
protected:
|
||||
Texture(int width, int height) : _width(width), _height(height) {
|
||||
if (width <= 0) throw std::invalid_argument("Texture width must be greater than 0");
|
||||
if (height <= 0) throw std::invalid_argument("Texture height must be greater than 0");
|
||||
}
|
||||
|
||||
int _width;
|
||||
int _height;
|
||||
};
|
||||
|
||||
class RawTexture : public Texture {
|
||||
using Base = Texture;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new RawTexture
|
||||
* @param width width of the texture in pixels
|
||||
* @param height height of the texture in pixels
|
||||
*/
|
||||
RawTexture(int width, int height) : Base(width, height), _pixels(_width * _height) {}
|
||||
|
||||
Color GetPixel(int x, int y) const {
|
||||
if (x < 0 || x >= _width) throw std::invalid_argument("x value out of range.");
|
||||
if (y < 0 || y >= _height) throw std::invalid_argument("y value out of range.");
|
||||
return _pixels.at(x + (y * _width));
|
||||
}
|
||||
|
||||
void SetPixel(int x, int y, Color val) {
|
||||
if (x < 0 || x >= _width) throw std::invalid_argument("x value out of range.");
|
||||
if (y < 0 || y >= _height) throw std::invalid_argument("y value out of range.");
|
||||
_pixels.at(x + (y * _width)) = val;
|
||||
}
|
||||
|
||||
size_t NBytes() const noexcept override { return static_cast<unsigned long>(Width() * Height()) * sizeof(Color); }
|
||||
|
||||
template <int N, int M> ColorBlock<N, M> GetBlock(int block_x, int block_y) const {
|
||||
if (block_x < 0) throw std::out_of_range("x value out of range.");
|
||||
if (block_y < 0) throw std::out_of_range("y value out of range.");
|
||||
|
||||
// coordinates in the image of the top-left pixel of the selected block
|
||||
ColorBlock<N, M> block;
|
||||
int pixel_x = block_x * N;
|
||||
int pixel_y = block_y * M;
|
||||
|
||||
if (pixel_x + N < _width && pixel_y + M < _height) {
|
||||
// fast memcpy if the block is entirely inside the bounds of the texture
|
||||
for (int y = 0; y < M; y++) {
|
||||
// copy each row into the ColorBlock
|
||||
block.SetRow(y, &_pixels[pixel_x + (_width * (pixel_y + y))]);
|
||||
}
|
||||
} else {
|
||||
// slower pixel-wise copy if the block goes over the edges
|
||||
for (int x = 0; x < N; x++) {
|
||||
for (int y = 0; y < M; y++) { block.Set(x, y, GetPixel((pixel_x + x) % _width, (pixel_y + y) % _height)); }
|
||||
}
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
template <int N, int M> void SetBlock(int block_x, int block_y, const ColorBlock<N, M> &block) {
|
||||
if (block_x < 0) throw std::out_of_range("x value out of range.");
|
||||
if (block_y < 0) throw std::out_of_range("y value out of range.");
|
||||
|
||||
// coordinates in the image of the top-left pixel of the selected block
|
||||
int pixel_x = block_x * N;
|
||||
int pixel_y = block_y * M;
|
||||
|
||||
if (pixel_x + N < _width && pixel_y + M < _height) {
|
||||
// fast row-wise memcpy if the block is entirely inside the bounds of the texture
|
||||
for (int y = 0; y < M; y++) {
|
||||
// copy each row out of the ColorBlock
|
||||
block.GetRow(y, &_pixels[pixel_x + (_width * (pixel_y + y))]);
|
||||
}
|
||||
} else {
|
||||
// slower pixel-wise copy if the block goes over the edges
|
||||
for (int x = 0; x < N; x++) {
|
||||
for (int y = 0; y < M; y++) { SetPixel((pixel_x + x) % _width, (pixel_y + y) % _height, block.Get(x, y)); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual const uint8_t *Data() const noexcept override { return reinterpret_cast<const uint8_t *>(_pixels.data()); }
|
||||
virtual uint8_t *Data() noexcept override { return reinterpret_cast<uint8_t *>(_pixels.data()); }
|
||||
|
||||
protected:
|
||||
std::vector<Color> _pixels;
|
||||
};
|
||||
|
||||
template <typename B> class BlockTexture final : public Texture {
|
||||
private:
|
||||
std::vector<B> _blocks;
|
||||
int _width_b;
|
||||
int _height_b;
|
||||
|
||||
public:
|
||||
using BlockType = B;
|
||||
using Base = Texture;
|
||||
|
||||
/**
|
||||
* Create a new BlockTexture
|
||||
* @param width width of the texture in pixels. must be divisible by B::Width
|
||||
* @param height height of the texture in pixels. must be divisible by B::Height
|
||||
*/
|
||||
BlockTexture(int width, int height) : Base(width, height) {
|
||||
_width_b = (_width + B::Width - 1) / B::Width;
|
||||
_height_b = (_height + B::Height - 1) / B::Height;
|
||||
_blocks = std::vector<B>(_width_b * _height_b);
|
||||
}
|
||||
|
||||
constexpr int BlocksX() const { return _width_b; }
|
||||
constexpr int BlocksY() const { return _height_b; }
|
||||
constexpr std::tuple<int, int> BlocksXY() const { return std::tuple<int, int>(_width_b, _height_b); }
|
||||
|
||||
B GetBlock(int x, int y) const {
|
||||
if (x < 0 || x >= _width_b) throw std::out_of_range("x value out of range.");
|
||||
if (y < 0 || y >= _height_b) throw std::out_of_range("y value out of range.");
|
||||
return _blocks.at(x + (y * _width_b));
|
||||
}
|
||||
|
||||
void SetBlock(int x, int y, const B &val) {
|
||||
if (x < 0 || x >= _width_b) throw std::out_of_range("x value out of range.");
|
||||
if (y < 0 || y >= _height_b) throw std::out_of_range("y value out of range.");
|
||||
_blocks.at(x + (y * _width_b)) = val;
|
||||
}
|
||||
|
||||
size_t NBytes() const noexcept override { return _blocks.size() * sizeof(B); }
|
||||
|
||||
const uint8_t *Data() const noexcept override { return reinterpret_cast<const uint8_t *>(_blocks.data()); }
|
||||
uint8_t *Data() noexcept override { return reinterpret_cast<uint8_t *>(_blocks.data()); }
|
||||
};
|
||||
|
||||
} // namespace quicktex
|
124
quicktex/Vector4.h
Normal file
124
quicktex/Vector4.h
Normal file
@ -0,0 +1,124 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
|
||||
#include "Color.h"
|
||||
|
||||
namespace quicktex {
|
||||
|
||||
class Vector4 {
|
||||
public:
|
||||
Vector4() : Vector4(0) {}
|
||||
|
||||
Vector4(float x, float y, float z = 0, float w = 0) {
|
||||
_c[0] = x;
|
||||
_c[1] = y;
|
||||
_c[2] = z;
|
||||
_c[3] = w;
|
||||
}
|
||||
|
||||
Vector4(float scalar) {
|
||||
_c[0] = scalar;
|
||||
_c[1] = scalar;
|
||||
_c[2] = scalar;
|
||||
_c[3] = scalar;
|
||||
}
|
||||
|
||||
Vector4(const Color &c) : Vector4(c.r, c.g, c.b, c.a) {}
|
||||
|
||||
static Vector4 FromColor(const Color &c) { return Vector4(c); }
|
||||
|
||||
static Vector4 FromColorRGB(const Color &c) { return Vector4(c.r, c.g, c.b); }
|
||||
|
||||
static float Dot(const Vector4 &lhs, const Vector4 &rhs) {
|
||||
float sum = 0;
|
||||
for (unsigned i = 0; i < 4; i++) { sum += lhs[i] * rhs[i]; }
|
||||
return sum;
|
||||
}
|
||||
|
||||
float operator[](size_t index) const {
|
||||
assert(index < 4);
|
||||
return _c[index];
|
||||
}
|
||||
float &operator[](size_t index) {
|
||||
assert(index < 4);
|
||||
return _c[index];
|
||||
}
|
||||
|
||||
friend Vector4 operator+(const Vector4 &lhs, const Vector4 &rhs) { return DoOp(lhs, rhs, std::plus()); }
|
||||
friend Vector4 operator-(const Vector4 &lhs, const Vector4 &rhs) { return DoOp(lhs, rhs, std::minus()); }
|
||||
friend Vector4 operator*(const Vector4 &lhs, const Vector4 &rhs) { return DoOp(lhs, rhs, std::multiplies()); }
|
||||
friend Vector4 operator/(const Vector4 &lhs, const Vector4 &rhs) { return DoOp(lhs, rhs, std::divides()); }
|
||||
|
||||
friend Vector4 operator+(const Vector4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::plus()); }
|
||||
friend Vector4 operator-(const Vector4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::minus()); }
|
||||
friend Vector4 operator*(const Vector4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::multiplies()); }
|
||||
friend Vector4 operator/(const Vector4 &lhs, const float &rhs) { return DoOp(lhs, rhs, std::divides()); }
|
||||
|
||||
friend Vector4 &operator+=(Vector4 &lhs, const Vector4 &rhs) { return lhs = lhs + rhs; }
|
||||
friend Vector4 &operator-=(Vector4 &lhs, const Vector4 &rhs) { return lhs = lhs - rhs; }
|
||||
friend Vector4 &operator*=(Vector4 &lhs, const Vector4 &rhs) { return lhs = lhs * rhs; }
|
||||
friend Vector4 &operator/=(Vector4 &lhs, const Vector4 &rhs) { return lhs = lhs / rhs; }
|
||||
|
||||
friend Vector4 &operator+=(Vector4 &lhs, const float &rhs) { return lhs = lhs + rhs; }
|
||||
friend Vector4 &operator-=(Vector4 &lhs, const float &rhs) { return lhs = lhs - rhs; }
|
||||
friend Vector4 &operator*=(Vector4 &lhs, const float &rhs) { return lhs = lhs * rhs; }
|
||||
friend Vector4 &operator/=(Vector4 &lhs, const float &rhs) { return lhs = lhs / rhs; }
|
||||
|
||||
float Dot(Vector4 other) const { return Dot(*this, other); }
|
||||
float MaxAbs(unsigned channels = 4) const {
|
||||
assert(channels < 5);
|
||||
assert(channels > 0);
|
||||
float max = 0;
|
||||
for (unsigned i = 0; i < channels; i++) {
|
||||
float a = fabs((*this)[i]);
|
||||
if (a > max) max = a;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
float SqrMag() { return Dot(*this, *this); }
|
||||
|
||||
float Determinant2x2() {
|
||||
//z00 * z11 - z01 * z10;
|
||||
return (_c[0] * _c[3]) - (_c[1] * _c[2]);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Op> static inline Vector4 DoOp(const Vector4 &lhs, const Vector4 &rhs, Op f) {
|
||||
Vector4 r;
|
||||
for (unsigned i = 0; i < 4; i++) { r[i] = f(lhs[i], rhs[i]); }
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename Op> static inline Vector4 DoOp(const Vector4 &lhs, const float &rhs, Op f) {
|
||||
Vector4 r;
|
||||
for (unsigned i = 0; i < 4; i++) { r[i] = f(lhs[i], rhs); }
|
||||
return r;
|
||||
}
|
||||
|
||||
std::array<float, 4> _c;
|
||||
};
|
||||
|
||||
} // namespace quicktex
|
120
quicktex/Vector4Int.h
Normal file
120
quicktex/Vector4Int.h
Normal file
@ -0,0 +1,120 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
#include "Color.h"
|
||||
#include "Vector4.h"
|
||||
|
||||
namespace quicktex {
|
||||
|
||||
class Vector4Int {
|
||||
public:
|
||||
Vector4Int() : Vector4Int(0) {}
|
||||
|
||||
Vector4Int(int x, int y, int z = 0, int w = 0) {
|
||||
_c[0] = x;
|
||||
_c[1] = y;
|
||||
_c[2] = z;
|
||||
_c[3] = w;
|
||||
}
|
||||
|
||||
Vector4Int(int scalar) {
|
||||
_c[0] = scalar;
|
||||
_c[1] = scalar;
|
||||
_c[2] = scalar;
|
||||
_c[3] = scalar;
|
||||
}
|
||||
|
||||
Vector4Int(const Color &c) : Vector4Int(c.r, c.g, c.b, c.a) {}
|
||||
|
||||
static Vector4Int FromColor(const Color &c) { return Vector4Int(c); }
|
||||
|
||||
static Vector4Int FromColorRGB(const Color &c) { return Vector4Int(c.r, c.g, c.b); }
|
||||
|
||||
static int Dot(const Vector4Int &lhs, const Vector4Int &rhs) {
|
||||
int sum = 0;
|
||||
for (unsigned i = 0; i < 4; i++) { sum += lhs[i] * rhs[i]; }
|
||||
return sum;
|
||||
}
|
||||
|
||||
int operator[](size_t index) const {
|
||||
assert(index < 4);
|
||||
return _c[index];
|
||||
}
|
||||
int &operator[](size_t index) {
|
||||
assert(index < 4);
|
||||
return _c[index];
|
||||
}
|
||||
|
||||
operator Vector4() const { return Vector4((float)_c[0], (float)_c[1], (float)_c[2], (float)_c[3]); }
|
||||
|
||||
friend Vector4Int operator+(const Vector4Int &lhs, const Vector4Int &rhs) { return DoOp(lhs, rhs, std::plus()); }
|
||||
friend Vector4Int operator-(const Vector4Int &lhs, const Vector4Int &rhs) { return DoOp(lhs, rhs, std::minus()); }
|
||||
friend Vector4Int operator*(const Vector4Int &lhs, const Vector4Int &rhs) { return DoOp(lhs, rhs, std::multiplies()); }
|
||||
friend Vector4Int operator/(const Vector4Int &lhs, const Vector4Int &rhs) { return DoOp(lhs, rhs, std::divides()); }
|
||||
|
||||
friend Vector4Int operator+(const Vector4Int &lhs, const int &rhs) { return DoOp(lhs, rhs, std::plus()); }
|
||||
friend Vector4Int operator-(const Vector4Int &lhs, const int &rhs) { return DoOp(lhs, rhs, std::minus()); }
|
||||
friend Vector4Int operator*(const Vector4Int &lhs, const int &rhs) { return DoOp(lhs, rhs, std::multiplies()); }
|
||||
friend Vector4Int operator/(const Vector4Int &lhs, const int &rhs) { return DoOp(lhs, rhs, std::divides()); }
|
||||
|
||||
friend Vector4Int &operator+=(Vector4Int &lhs, const Vector4Int &rhs) { return lhs = lhs + rhs; }
|
||||
friend Vector4Int &operator-=(Vector4Int &lhs, const Vector4Int &rhs) { return lhs = lhs - rhs; }
|
||||
friend Vector4Int &operator*=(Vector4Int &lhs, const Vector4Int &rhs) { return lhs = lhs * rhs; }
|
||||
friend Vector4Int &operator/=(Vector4Int &lhs, const Vector4Int &rhs) { return lhs = lhs / rhs; }
|
||||
|
||||
friend Vector4Int &operator+=(Vector4Int &lhs, const int &rhs) { return lhs = lhs + rhs; }
|
||||
friend Vector4Int &operator-=(Vector4Int &lhs, const int &rhs) { return lhs = lhs - rhs; }
|
||||
friend Vector4Int &operator*=(Vector4Int &lhs, const int &rhs) { return lhs = lhs * rhs; }
|
||||
friend Vector4Int &operator/=(Vector4Int &lhs, const int &rhs) { return lhs = lhs / rhs; }
|
||||
|
||||
int Dot(const Vector4Int &other) const { return Dot(*this, other); }
|
||||
int MaxAbs(unsigned channels = 4) {
|
||||
assert(channels < 5);
|
||||
assert(channels > 0);
|
||||
int max = 0;
|
||||
for (unsigned i = 0; i < channels; i++) {
|
||||
int a = abs((*this)[i]);
|
||||
if (a > max) max = a;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
unsigned int SqrMag() { return (unsigned)Dot(*this, *this); }
|
||||
|
||||
private:
|
||||
template <typename Op> friend Vector4Int DoOp(const Vector4Int &lhs, const Vector4Int &rhs, Op f) {
|
||||
Vector4Int r;
|
||||
for (unsigned i = 0; i < 4; i++) { r[i] = f(lhs[i], rhs[i]); }
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename Op> friend Vector4Int DoOp(const Vector4Int &lhs, const int &rhs, Op f) {
|
||||
Vector4Int r;
|
||||
for (unsigned i = 0; i < 4; i++) { r[i] = f(lhs[i], rhs); }
|
||||
return r;
|
||||
}
|
||||
|
||||
std::array<int, 4> _c;
|
||||
};
|
||||
|
||||
} // namespace quicktex
|
2
quicktex/__init__.py
Normal file
2
quicktex/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from _quicktex import *
|
||||
from _quicktex import __version__
|
16
quicktex/__main__.py
Normal file
16
quicktex/__main__.py
Normal file
@ -0,0 +1,16 @@
|
||||
import click
|
||||
from quicktex.cli.encode import encode
|
||||
from quicktex.cli.decode import decode
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.version_option()
|
||||
def main():
|
||||
"""Encode and Decode various image formats"""
|
||||
|
||||
|
||||
main.add_command(encode)
|
||||
main.add_command(decode)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
74
quicktex/_bindings.cpp
Normal file
74
quicktex/_bindings.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
/* Quicktex 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 "_bindings.h"
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include "Color.h"
|
||||
#include "Decoder.h"
|
||||
#include "Encoder.h"
|
||||
#include "Texture.h"
|
||||
#include "_bindings.h"
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
#define MACRO_STRINGIFY(x) STRINGIFY(x)
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
namespace quicktex::bindings {
|
||||
|
||||
void InitS3TC(py::module_ &m);
|
||||
|
||||
PYBIND11_MODULE(_quicktex, m) {
|
||||
m.doc() = "More Stuff";
|
||||
|
||||
#ifdef VERSION_INFO
|
||||
m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO);
|
||||
#else
|
||||
m.attr("__version__") = "dev";
|
||||
#endif
|
||||
|
||||
py::options options;
|
||||
|
||||
// Texture
|
||||
|
||||
py::class_<Texture> texture(m, "Texture", py::buffer_protocol());
|
||||
|
||||
texture.def_property_readonly("nbytes", &Texture::NBytes);
|
||||
texture.def_property_readonly("size", &Texture::Size);
|
||||
texture.def_property_readonly("width", &Texture::Width);
|
||||
texture.def_property_readonly("height", &Texture::Height);
|
||||
|
||||
texture.def_buffer([](Texture &t) { return py::buffer_info(t.Data(), t.NBytes()); });
|
||||
texture.def("tobytes", [](const Texture &t) { return py::bytes(reinterpret_cast<const char *>(t.Data()), t.NBytes()); });
|
||||
|
||||
// RawTexture
|
||||
|
||||
py::class_<RawTexture, Texture> raw_texture(m, "RawTexture");
|
||||
|
||||
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::Size);
|
||||
|
||||
InitS3TC(m);
|
||||
}
|
||||
|
||||
} // namespace quicktex::bindings
|
236
quicktex/_bindings.h
Normal file
236
quicktex/_bindings.h
Normal file
@ -0,0 +1,236 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pybind11/operators.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include "Color.h"
|
||||
#include "ColorBlock.h"
|
||||
#include "Texture.h"
|
||||
#include "util.h"
|
||||
|
||||
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 py = pybind11;
|
||||
namespace quicktex::bindings {
|
||||
using namespace pybind11::literals;
|
||||
|
||||
template <typename T> T BufferToTexture(py::buffer buf, int width, int height) {
|
||||
static_assert(std::is_base_of<Texture, T>::value);
|
||||
static_assert(std::is_constructible<T, int, int>::value);
|
||||
|
||||
auto info = buf.request(false);
|
||||
auto output = T(width, height);
|
||||
auto dst_size = output.NBytes();
|
||||
|
||||
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)dst_size) 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)dst_size) 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.");
|
||||
}
|
||||
|
||||
std::memcpy(output.Data(), info.ptr, dst_size);
|
||||
|
||||
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;
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T, typename Getter, typename Setter, typename Extent> void DefSubscript(py::class_<T> t, Getter&& get, Setter&& set, Extent&& ext) {
|
||||
using V = typename std::invoke_result<Getter, T*, int>::type;
|
||||
t.def(
|
||||
"__getitem__", [get, ext](T& self, int index) { return (self.*get)(PyIndex(index, (self.*ext)())); }, "key"_a);
|
||||
t.def(
|
||||
"__setitem__", [set, ext](T& self, int index, V val) { (self.*set)(PyIndex(index, (self.*ext)()), val); }, "key"_a, "value"_a);
|
||||
}
|
||||
|
||||
template <typename Tpy, typename Getter, typename Setter, typename Extent> void DefSubscript2D(Tpy t, Getter&& get, Setter&& set, Extent&& ext) {
|
||||
using T = typename Tpy::type;
|
||||
using V = typename std::invoke_result<Getter, T*, int, int>::type;
|
||||
using Coords = std::tuple<int, int>;
|
||||
t.def(
|
||||
"__getitem__",
|
||||
[get, ext](T& self, Coords pnt) {
|
||||
Coords s = (self.*ext)();
|
||||
int x = PyIndex(std::get<0>(pnt), std::get<0>(s), "x");
|
||||
int y = PyIndex(std::get<1>(pnt), std::get<1>(s), "y");
|
||||
return (self.*get)(x, y);
|
||||
},
|
||||
"key"_a);
|
||||
t.def(
|
||||
"__setitem__",
|
||||
[set, ext](T& self, Coords pnt, const V& val) {
|
||||
Coords s = (self.*ext)();
|
||||
int x = PyIndex(std::get<0>(pnt), std::get<0>(s), "x");
|
||||
int y = PyIndex(std::get<1>(pnt), std::get<1>(s), "y");
|
||||
(self.*set)(x, y, val);
|
||||
},
|
||||
"key"_a, "value"_a);
|
||||
}
|
||||
|
||||
template <typename B> py::class_<B> BindBlock(py::module_& m, const char* name) {
|
||||
const char* frombytes_doc = R"doc(
|
||||
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(
|
||||
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_property_readonly_static(
|
||||
"size", [](py::object) { return std::make_tuple(B::Width, B::Height); }, "The dimensions of the block in pixels.");
|
||||
block.def_property_readonly_static(
|
||||
"nbytes", [](py::object) { return sizeof(B); }, "The size of the block in bytes.");
|
||||
|
||||
block.def(py::self == py::self);
|
||||
|
||||
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 std::move(block);
|
||||
}
|
||||
|
||||
template <typename B> py::class_<BlockTexture<B>> BindBlockTexture(py::module_& m, const char* name) {
|
||||
const auto* const constructor_str = R"doc(
|
||||
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(
|
||||
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::class_<BTex, Texture> block_texture(m, name);
|
||||
|
||||
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_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("size_blocks", &BTex::BlocksXY, "The dimensions of the texture in blocks.");
|
||||
|
||||
DefSubscript2D(block_texture, &BTex::GetBlock, &BTex::SetBlock, &BTex::BlocksXY);
|
||||
|
||||
return std::move(block_texture);
|
||||
}
|
||||
} // namespace quicktex::bindings
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich 2020 <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
0
quicktex/cli/__init__.py
Normal file
0
quicktex/cli/__init__.py
Normal file
62
quicktex/cli/common.py
Normal file
62
quicktex/cli/common.py
Normal file
@ -0,0 +1,62 @@
|
||||
from PIL import Image
|
||||
from typing import List
|
||||
import pathlib
|
||||
import click
|
||||
|
||||
|
||||
def get_decoded_extensions(feature: str = 'open') -> List[str]:
|
||||
"""Gets a list of extensions for Pillow formats supporting a supplied feature"""
|
||||
extensions = Image.registered_extensions() # using registered_extensions() triggers lazy loading of format data
|
||||
formats = getattr(Image, feature.upper()).keys()
|
||||
|
||||
return [ext for ext, fmt in extensions.items() if fmt in formats]
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def validate_decoded_extension(ctx, param, value) -> str:
|
||||
"""Check if an extension for a decoded image is valid"""
|
||||
if value[0] != '.':
|
||||
value = '.' + value
|
||||
|
||||
if value not in decoded_extensions:
|
||||
raise click.BadParameter(f'Invalid extension for decoded file. Valid extensions are:\n{decoded_extensions}')
|
||||
|
||||
return value
|
||||
|
||||
|
||||
decoded_extensions = get_decoded_extensions()
|
||||
encoded_extensions = '.dds'
|
||||
|
||||
|
||||
def path_pairs(inputs, output, suffix, extension):
|
||||
"""
|
||||
Generates pairs of (inpath, outpath) for the given parameters
|
||||
:param inputs: A list of input paths to read from
|
||||
:param output: output to write to. can be a file, directory, or None to convert in place
|
||||
:param suffix: suffix to add to destinations
|
||||
:param extension: extension to use for destinations
|
||||
:return: A list of pairs of (inpath, outpath)
|
||||
"""
|
||||
|
||||
if len(inputs) < 1:
|
||||
raise click.BadArgumentUsage('No input files were provided.')
|
||||
|
||||
inpaths = [pathlib.Path(i) for i in inputs]
|
||||
|
||||
if not output:
|
||||
# decode in place
|
||||
return [(inpath, inpath.with_name(inpath.stem + suffix + extension)) for inpath in inpaths]
|
||||
|
||||
else:
|
||||
outpath = pathlib.Path(output)
|
||||
if outpath.is_file():
|
||||
# decode to a file
|
||||
if len(inputs) > 1:
|
||||
raise click.BadOptionUsage('output', 'Output is a single file, but multiple input files were provided.')
|
||||
# if outpath.suffix not in decoded_extensions:
|
||||
# raise click.BadOptionUsage('output', f'File has incorrect extension for decoded file. Valid extensions are:\n{decoded_extensions}')
|
||||
|
||||
return [(inpath, outpath) for inpath in inpaths]
|
||||
else:
|
||||
# decode to directory
|
||||
return [(inpath, outpath / (inpath.stem + suffix + extension)) for inpath in inpaths]
|
42
quicktex/cli/decode.py
Normal file
42
quicktex/cli/decode.py
Normal file
@ -0,0 +1,42 @@
|
||||
import click
|
||||
import os.path
|
||||
import quicktex.dds as dds
|
||||
import quicktex.cli.common as common
|
||||
from PIL import Image
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option('-f/-F', '--flip/--no-flip', default=True, show_default=True, help="Vertically flip image after converting.")
|
||||
@click.option('-r', '--remove', is_flag=True, help="Remove input images after converting.")
|
||||
@click.option('-s', '--suffix', type=str, default='', help="Suffix to append to output file(s). Ignored if output is a single file.")
|
||||
@click.option('-x', '--extension',
|
||||
callback=common.validate_decoded_extension,
|
||||
type=str, default='.png', show_default=True,
|
||||
help="Extension to use for output. Ignored if output is a single file. Output filetype is deduced from this")
|
||||
@click.option('-o', '--output',
|
||||
type=click.Path(writable=True), default=None,
|
||||
help="Output file or directory. If outputting to a file, input filenames must be only a single item. By default, files are decoded in place.")
|
||||
@click.argument('filenames', nargs=-1, type=click.Path(exists=True, readable=True, dir_okay=False))
|
||||
def decode(flip, remove, suffix, extension, output, filenames):
|
||||
"""Decode DDS files to images."""
|
||||
|
||||
path_pairs = common.path_pairs(filenames, output, suffix, extension)
|
||||
|
||||
with click.progressbar(path_pairs, show_eta=False, show_pos=True, item_show_func=lambda x: str(x[0]) if x else '') as bar:
|
||||
for inpath, outpath in bar:
|
||||
if inpath.suffix != '.dds':
|
||||
raise click.BadArgumentUsage(f"Input file '{inpath}' is not a DDS file.")
|
||||
|
||||
image = dds.read(inpath).decode()
|
||||
|
||||
if flip:
|
||||
image = image.transpose(Image.FLIP_TOP_BOTTOM)
|
||||
|
||||
image.save(outpath)
|
||||
|
||||
if remove:
|
||||
os.remove(inpath)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
decode()
|
139
quicktex/cli/encode.py
Normal file
139
quicktex/cli/encode.py
Normal file
@ -0,0 +1,139 @@
|
||||
import click
|
||||
import os
|
||||
import pathlib
|
||||
import quicktex.s3tc.bc1
|
||||
import quicktex.s3tc.bc3
|
||||
import quicktex.s3tc.bc4
|
||||
import quicktex.s3tc.bc5
|
||||
import quicktex.dds as dds
|
||||
import quicktex.cli.common as common
|
||||
from PIL import Image
|
||||
|
||||
|
||||
@click.group()
|
||||
def encode():
|
||||
"""Encode images to DDS files of the given format."""
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option('-f/-F', '--flip/--no-flip', default=True, show_default=True, help="Vertically flip image before converting.")
|
||||
@click.option('-r', '--remove', is_flag=True, help="Remove input images after converting.")
|
||||
@click.option('-s', '--suffix', type=str, default='', help="Suffix to append to output file(s). Ignored if output is a single file.")
|
||||
@click.option('-o', '--output',
|
||||
type=click.Path(writable=True), default=None,
|
||||
help="Output file or directory. If outputting to a file, input filenames must be only a single item. By default, files are decoded in place.")
|
||||
@click.argument('filenames', nargs=-1, type=click.Path(exists=True, readable=True, dir_okay=False))
|
||||
def encode_format(encoder, four_cc, flip, remove, suffix, output, filenames):
|
||||
path_pairs = common.path_pairs(filenames, output, suffix, '.dds')
|
||||
|
||||
with click.progressbar(path_pairs, show_eta=False, show_pos=True, item_show_func=lambda x: str(x[0]) if x else '') as bar:
|
||||
for inpath, outpath in bar:
|
||||
image = Image.open(inpath)
|
||||
|
||||
if flip:
|
||||
image = image.transpose(Image.FLIP_TOP_BOTTOM)
|
||||
|
||||
dds.encode(image, encoder, four_cc).save(outpath)
|
||||
|
||||
if remove:
|
||||
os.remove(inpath)
|
||||
|
||||
|
||||
@click.command('auto')
|
||||
@click.option('-l', '--level', type=click.IntRange(0, 18), default=18, help='Quality level to use. Higher values = higher quality, but slower.')
|
||||
@click.option('-b/-B', '--black/--no-black',
|
||||
help='[BC1 only] Enable 3-color mode for blocks containing black or very dark pixels. --3color must also be enabled for this to work.'
|
||||
' (Important: engine/shader MUST ignore decoded texture alpha if this flag is enabled!)')
|
||||
@click.option('-3/-4', '--3color/--4color', 'threecolor', default=True, help='[BC1 only] Enable 3-color mode for non-black pixels. Higher quality, but slightly slower.')
|
||||
@click.option('-f/-F', '--flip/--no-flip', default=True, show_default=True, help="Vertically flip image before converting.")
|
||||
@click.option('-r', '--remove', is_flag=True, help="Remove input images after converting.")
|
||||
@click.option('-s', '--suffix', type=str, default='', help="Suffix to append to output file(s). Ignored if output is a single file.")
|
||||
@click.option('-o', '--output',
|
||||
type=click.Path(writable=True), default=None,
|
||||
help="Output file or directory. If outputting to a file, input filenames must be only a single item. By default, files are decoded in place.")
|
||||
@click.argument('filenames', nargs=-1, type=click.Path(exists=True, readable=True, dir_okay=False))
|
||||
def encode_auto(level, black, threecolor, flip, remove, suffix, output, filenames):
|
||||
"""Encode images to BC1 or BC3, with the format chosen based on each image's alpha channel."""
|
||||
|
||||
color_mode = quicktex.s3tc.bc1.BC1Encoder.ColorMode
|
||||
if not threecolor:
|
||||
mode = color_mode.FourColor
|
||||
elif not black:
|
||||
mode = color_mode.ThreeColor
|
||||
else:
|
||||
mode = color_mode.ThreeColorBlack
|
||||
|
||||
bc1_encoder = quicktex.s3tc.bc1.BC1Encoder(level, mode)
|
||||
bc3_encoder = quicktex.s3tc.bc3.BC3Encoder(level)
|
||||
path_pairs = common.path_pairs(filenames, output, suffix, '.dds')
|
||||
|
||||
with click.progressbar(path_pairs, show_eta=False, show_pos=True, item_show_func=lambda x: str(x[0]) if x else '') as bar:
|
||||
for inpath, outpath in bar:
|
||||
image = Image.open(inpath)
|
||||
|
||||
if flip:
|
||||
image = image.transpose(Image.FLIP_TOP_BOTTOM)
|
||||
|
||||
if 'A' not in image.mode:
|
||||
has_alpha = False
|
||||
else:
|
||||
alpha_hist = image.getchannel('A').histogram()
|
||||
has_alpha = any([a > 0 for a in alpha_hist[:-1]])
|
||||
|
||||
if has_alpha:
|
||||
dds.encode(image, bc3_encoder, 'DXT5').save(outpath)
|
||||
else:
|
||||
dds.encode(image, bc1_encoder, 'DXT1').save(outpath)
|
||||
|
||||
if remove:
|
||||
os.remove(inpath)
|
||||
|
||||
|
||||
@click.command('bc1')
|
||||
@click.option('-l', '--level', type=click.IntRange(0, 18), default=18, help='Quality level to use. Higher values = higher quality, but slower.')
|
||||
@click.option('-b/-B', '--black/--no-black',
|
||||
help='Enable 3-color mode for blocks containing black or very dark pixels. --3color must also be enabled for this to work.'
|
||||
' (Important: engine/shader MUST ignore decoded texture alpha if this flag is enabled!)')
|
||||
@click.option('-3/-4', '--3color/--4color', 'threecolor', default=True, help='Enable 3-color mode for non-black pixels. Higher quality, but slightly slower.')
|
||||
def encode_bc1(level, black, threecolor, **kwargs):
|
||||
"""Encode images to BC1 (RGB, no alpha)."""
|
||||
color_mode = quicktex.s3tc.bc1.BC1Encoder.ColorMode
|
||||
if not threecolor:
|
||||
mode = color_mode.FourColor
|
||||
elif not black:
|
||||
mode = color_mode.ThreeColor
|
||||
else:
|
||||
mode = color_mode.ThreeColorBlack
|
||||
|
||||
encode_format.callback(encoder=quicktex.s3tc.bc1.BC1Encoder(level, mode), four_cc='DXT1', **kwargs)
|
||||
|
||||
|
||||
@click.command('bc3')
|
||||
@click.option('-l', '--level', type=click.IntRange(0, 18), default=18, help='Quality level to use. Higher values = higher quality, but slower.')
|
||||
def encode_bc3(level, **kwargs):
|
||||
"""Encode images to BC4 (RGBA, 8-bit interpolated alpha)."""
|
||||
encode_format.callback(quicktex.s3tc.bc3.BC3Encoder(level), 'DXT5', **kwargs)
|
||||
|
||||
|
||||
@click.command('bc4')
|
||||
def encode_bc4(**kwargs):
|
||||
"""Encode images to BC4 (Single channel, 8-bit interpolated red channel)."""
|
||||
encode_format.callback(quicktex.s3tc.bc4.BC4Encoder(), 'ATI1', **kwargs)
|
||||
|
||||
|
||||
@click.command('bc5')
|
||||
def encode_bc5(**kwargs):
|
||||
"""Encode images to BC5 (2-channel, 8-bit interpolated red and green channels)."""
|
||||
encode_format.callback(quicktex.s3tc.bc5.BC5Encoder(), 'ATI2', **kwargs)
|
||||
|
||||
|
||||
encode_bc1.params += encode_format.params
|
||||
encode_bc3.params += encode_format.params
|
||||
encode_bc4.params += encode_format.params
|
||||
encode_bc5.params += encode_format.params
|
||||
|
||||
encode.add_command(encode_bc1)
|
||||
encode.add_command(encode_bc3)
|
||||
encode.add_command(encode_bc4)
|
||||
encode.add_command(encode_bc5)
|
||||
encode.add_command(encode_auto)
|
280
quicktex/dds.py
Normal file
280
quicktex/dds.py
Normal file
@ -0,0 +1,280 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
import os
|
||||
import struct
|
||||
import typing
|
||||
import quicktex.image_utils
|
||||
import quicktex.s3tc.bc1 as bc1
|
||||
import quicktex.s3tc.bc3 as bc3
|
||||
import quicktex.s3tc.bc4 as bc4
|
||||
import quicktex.s3tc.bc5 as bc5
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class DDSFormat:
|
||||
def __init__(self, name: str, texture, encoder, decoder, four_cc: str = None):
|
||||
self.four_cc = four_cc
|
||||
self.decoder = decoder
|
||||
self.encoder = encoder
|
||||
self.texture = texture
|
||||
self.name = name
|
||||
|
||||
|
||||
dds_formats = [
|
||||
DDSFormat('BC1', bc1.BC1Texture, bc1.BC1Encoder, bc1.BC1Decoder, 'DXT1'),
|
||||
DDSFormat('BC3', bc3.BC3Texture, bc3.BC3Encoder, bc3.BC3Decoder, 'DXT5'),
|
||||
DDSFormat('BC4', bc4.BC4Texture, bc4.BC4Encoder, bc4.BC4Decoder, 'ATI1'),
|
||||
DDSFormat('BC5', bc5.BC5Texture, bc5.BC5Encoder, bc5.BC5Decoder, 'ATI2'),
|
||||
]
|
||||
|
||||
|
||||
class PFFlags(enum.IntFlag):
|
||||
"""Values which indicate what type of data is in the surface."""
|
||||
|
||||
ALPHAPIXELS = 0x1
|
||||
"""Texture contains alpha data (:py:attr:`~PixelFormat.pixel_bitmasks[3]` contains valid data)."""
|
||||
|
||||
ALPHA = 0x2
|
||||
"""Used in some older DDS files for alpha channel only uncompressed data
|
||||
(:py:attr:`~PixelFormat.pixel_size` contains the alpha channel bitcount; :py:attr:`~PixelFormat.pixel_bitmasks[3]` contains valid data)."""
|
||||
|
||||
FOURCC = 0x4
|
||||
"""Texture contains compressed RGB data; :py:attr:`~PixelFormat.four_cc` contains valid data."""
|
||||
|
||||
RGB = 0x40
|
||||
"""Texture contains uncompressed RGB data; :py:attr:`~PixelFormat.pixel_size` and the RGB masks
|
||||
(:py:attr:`~PixelFormat.pixel_bitmasks[0:3]`) contain valid data."""
|
||||
|
||||
YUV = 0x200
|
||||
"""Used in some older DDS files for YUV uncompressed data
|
||||
(:py:attr:`~PixelFormat.pixel_size` contains the YUV bit count; :py:attr:`~PixelFormat.pixel_bitmasks[0]` contains the Y mask,
|
||||
:py:attr:`~PixelFormat.pixel_bitmasks[1]` contains the U mask, :py:attr:`~PixelFormat.pixel_bitmasks[2]` contains the V mask)."""
|
||||
|
||||
LUMINANCE = 0x20000
|
||||
"""Used in some older DDS files for single channel color uncompressed data (:py:attr:`~PixelFormat.pixel_size`
|
||||
contains the luminance channel bit count; :py:attr:`~PixelFormat.pixel_bitmasks[0]` contains the channel mask).
|
||||
Can be combined with :py:attr:`ALPHAPIXELS` for a two channel uncompressed DDS file."""
|
||||
|
||||
|
||||
class DDSFlags(enum.IntFlag):
|
||||
"""Flags to indicate which members contain valid data."""
|
||||
|
||||
CAPS = 0x1
|
||||
"""Required in every .dds file."""
|
||||
|
||||
HEIGHT = 0x2
|
||||
"""Required in every .dds file."""
|
||||
|
||||
WIDTH = 0x4
|
||||
"""Required in every .dds file."""
|
||||
|
||||
PITCH = 0x8
|
||||
"""Required when :py:attr:`~DDSHeader.pitch` is provided for an uncompressed texture."""
|
||||
|
||||
PIXEL_FORMAT = 0x1000
|
||||
"""Required in every .dds file."""
|
||||
|
||||
MIPMAPCOUNT = 0x20000
|
||||
"""Required when :py:attr:`~DDSHeader.mipmap_count` is provided for a mipmapped texture."""
|
||||
|
||||
LINEAR_SIZE = 0x80000
|
||||
"""Required when :py:attr:`~DDSHeader.pitch` is provided for a compressed texture."""
|
||||
|
||||
DEPTH = 0x800000
|
||||
"""Required when :py:attr:`~DDSHeader.depth` is provided for a depth texture."""
|
||||
|
||||
TEXTURE = CAPS | HEIGHT | WIDTH | PIXEL_FORMAT
|
||||
|
||||
|
||||
class Caps0(enum.IntFlag):
|
||||
"""Flags to indicate surface complexity"""
|
||||
|
||||
COMPLEX = 0x8
|
||||
"""Optional; must be used on any file that contains more than one surface (a mipmap, a cubic environment map, or mipmapped volume texture)."""
|
||||
|
||||
MIPMAP = 0x400000
|
||||
"""Optional; should be used for a mipmap."""
|
||||
|
||||
TEXTURE = 0x1000
|
||||
"""Required"""
|
||||
|
||||
|
||||
@typing.final
|
||||
class DDSFile:
|
||||
"""
|
||||
A microsoft DDS file, containing header information and one or more textures
|
||||
|
||||
For more information, see microsoft's `Reference for DDS <https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-reference>`_.
|
||||
"""
|
||||
|
||||
magic = b'DDS '
|
||||
"""Magic bytes at the start of every DDS file."""
|
||||
|
||||
extension = 'dds'
|
||||
"""Extension for a DDS file."""
|
||||
|
||||
header_bytes = 124
|
||||
"""The size of a DDS header in bytes."""
|
||||
|
||||
def __init__(self):
|
||||
self.flags: DDSFlags = DDSFlags.TEXTURE
|
||||
"""Flags to indicate which members contain valid data."""
|
||||
|
||||
self.size: typing.Tuple[int, int] = (0, 0)
|
||||
"""Width and height of the texture or its first mipmap"""
|
||||
|
||||
self.pitch: int = 0
|
||||
"""The pitch or number of bytes per row in an uncompressed texture;
|
||||
the total number of bytes in the top level texture for a compressed texture."""
|
||||
|
||||
self.depth: int = 1
|
||||
"""Depth of a volume texture (in pixels), otherwise unused."""
|
||||
|
||||
self.mipmap_count: int = 1
|
||||
"""Number of mipmap levels, otherwise unused."""
|
||||
|
||||
self.pf_flags: PFFlags = PFFlags.FOURCC
|
||||
"""Flags representing which pixel format data is valid."""
|
||||
|
||||
self.four_cc: str = "NONE"
|
||||
"""FourCC code of the texture format. Valid texture format strings are ``DXT1``, ``DXT2``, ``DXT3``, ``DXT4``, or ``DXT5``.
|
||||
If a DirectX 10 header is used, this is ``DX10``."""
|
||||
|
||||
self.pixel_size: int = 0
|
||||
"""Number of bits in each pixel if the texture is uncompressed"""
|
||||
|
||||
self.pixel_bitmasks: typing.Tuple[int, int, int, int] = (0, 0, 0, 0)
|
||||
"""Tuple of bitmasks for each channel"""
|
||||
|
||||
self.caps: typing.Tuple[Caps0, int, int, int] = (Caps0.TEXTURE, 0, 0, 0)
|
||||
"""Specifies the complexity of the surfaces stored."""
|
||||
|
||||
self.textures: typing.List = []
|
||||
"""A list of bytes objects for each texture in the file"""
|
||||
|
||||
self.format: DDSFormat = DDSFormat('NONE', None, None, None)
|
||||
"""The format used by this dds file"""
|
||||
|
||||
def save(self, path: os.PathLike) -> None:
|
||||
"""
|
||||
Save the DDSFile to a file
|
||||
:param path: string or path-like object to write to
|
||||
"""
|
||||
with open(path, 'wb') as file:
|
||||
file.write(DDSFile.magic)
|
||||
|
||||
# WRITE HEADER
|
||||
file.write(struct.pack('<7I44x', DDSFile.header_bytes, int(self.flags), self.size[1], self.size[0], self.pitch, self.depth, self.mipmap_count))
|
||||
file.write(struct.pack('<2I4s5I', 32, int(self.pf_flags), bytes(self.four_cc, 'ascii'), self.pixel_size, *self.pixel_bitmasks))
|
||||
file.write(struct.pack('<4I4x', *self.caps))
|
||||
|
||||
assert file.tell() == 4 + DDSFile.header_bytes, 'error writing file: incorrect header size'
|
||||
|
||||
for texture in self.textures:
|
||||
file.write(texture)
|
||||
|
||||
def decode(self, mip: int = 0, *args, **kwargs) -> Image.Image:
|
||||
"""
|
||||
Decode a single texture in the file to images
|
||||
:param mip: the mip level to decode. Default: 0
|
||||
:return: The decoded image
|
||||
"""
|
||||
decoder = self.format.decoder(*args, **kwargs)
|
||||
texture = decoder.decode(self.textures[mip])
|
||||
return Image.frombuffer('RGBA', texture.size, texture)
|
||||
|
||||
def decode_all(self, *args, **kwargs) -> typing.List[Image.Image]:
|
||||
"""
|
||||
Decade all textures in the file to images
|
||||
:return: the decoded images
|
||||
"""
|
||||
decoder = self.format.decoder(*args, **kwargs)
|
||||
textures = [decoder.decode(encoded) for encoded in self.textures]
|
||||
return [Image.frombuffer('RGBA', tex.size, tex) for tex in textures]
|
||||
|
||||
|
||||
def read(path: os.PathLike) -> DDSFile:
|
||||
with open(path, 'rb') as file:
|
||||
assert file.read(4) == DDSFile.magic, "Incorrect magic bytes in DDS file."
|
||||
|
||||
dds = DDSFile()
|
||||
|
||||
# READ HEADER
|
||||
header_bytes = struct.unpack('<I', file.read(4))[0]
|
||||
assert header_bytes == DDSFile.header_bytes, "Incorrect DDS header size."
|
||||
|
||||
dds.flags = DDSFlags(struct.unpack('<I', file.read(4))[0]) # read flags enum
|
||||
dds.size = struct.unpack('<2I', file.read(8))[::-1] # read dimensions
|
||||
dds.pitch, dds.depth, dds.mipmap_count = struct.unpack('<3I', file.read(12))
|
||||
file.read(44) # skip 44 unused bytes of data
|
||||
|
||||
assert struct.unpack('<I', file.read(4))[0] == 32, "Incorrect pixel format size."
|
||||
|
||||
dds.pf_flags = PFFlags(struct.unpack('<I', file.read(4))[0])
|
||||
dds.four_cc = file.read(4).decode()
|
||||
dds.pixel_size, *pixel_bitmasks = struct.unpack('<5I', file.read(20))
|
||||
|
||||
dds.caps = struct.unpack('<4I', file.read(16))
|
||||
file.read(4) # skip 4 unused bytes of data
|
||||
|
||||
assert file.tell() == 4 + DDSFile.header_bytes, "Unexpected EOF" # make sure we are where we expect to be
|
||||
|
||||
if DDSFlags.DEPTH not in dds.flags:
|
||||
dds.depth = 1
|
||||
if DDSFlags.MIPMAPCOUNT not in dds.flags:
|
||||
dds.mipmap_count = 1
|
||||
if PFFlags.FOURCC not in dds.pf_flags:
|
||||
dds.four_cc = 'NONE'
|
||||
|
||||
# READ DX10_HEADER
|
||||
if dds.four_cc == 'DX10':
|
||||
raise NotImplementedError('DX10 headers are not yet supported')
|
||||
|
||||
# identify the format used
|
||||
dds.format = next(entry for entry in dds_formats if entry.four_cc == dds.four_cc)
|
||||
|
||||
# calculate the size of each level of the texture
|
||||
sizes = quicktex.image_utils.mip_sizes(dds.size, dds.mipmap_count)
|
||||
|
||||
# READ TEXTURES
|
||||
dds.textures = []
|
||||
for size in sizes:
|
||||
texture = dds.format.texture(*size) # make a new blocktexture of the current mip size
|
||||
nbytes = file.readinto(texture)
|
||||
|
||||
assert nbytes == texture.nbytes, 'Unexpected end of file'
|
||||
|
||||
dds.textures.append(texture)
|
||||
|
||||
return dds
|
||||
|
||||
|
||||
def encode(image: Image.Image, encoder, four_cc: str, mip_count: typing.Optional[int] = None) -> DDSFile:
|
||||
if image.mode != 'RGBA' or image.mode != 'RGBX':
|
||||
mode = 'RGBA' if 'A' in image.mode else 'RGBX'
|
||||
image = image.convert(mode)
|
||||
|
||||
sizes = quicktex.image_utils.mip_sizes(image.size, mip_count)
|
||||
images = [image] + [quicktex.image_utils.resize_no_premultiply(image, size) for size in sizes[1:]]
|
||||
dds = DDSFile()
|
||||
|
||||
for i in images:
|
||||
rawtex = quicktex.RawTexture.frombytes(i.tobytes('raw', mode), *i.size)
|
||||
dds.textures.append(encoder.encode(rawtex))
|
||||
|
||||
dds.flags = DDSFlags.TEXTURE | DDSFlags.LINEAR_SIZE
|
||||
caps0 = Caps0.TEXTURE
|
||||
|
||||
if len(images) > 1:
|
||||
dds.flags |= DDSFlags.MIPMAPCOUNT
|
||||
caps0 |= Caps0.MIPMAP | Caps0.COMPLEX
|
||||
|
||||
dds.caps = (caps0, 0, 0, 0)
|
||||
dds.mipmap_count = len(images)
|
||||
dds.pitch = dds.textures[0].nbytes
|
||||
dds.size = dds.textures[0].size
|
||||
dds.pf_flags = PFFlags.FOURCC
|
||||
dds.four_cc = four_cc
|
||||
|
||||
return dds
|
51
quicktex/image_utils.py
Normal file
51
quicktex/image_utils.py
Normal file
@ -0,0 +1,51 @@
|
||||
"""Various utilities for working with Pillow images"""
|
||||
|
||||
from PIL import Image
|
||||
from typing import List, Tuple, Optional
|
||||
import math
|
||||
|
||||
|
||||
def mip_sizes(dimensions: Tuple[int, int], mip_count: Optional[int] = None) -> List[Tuple[int, int]]:
|
||||
"""
|
||||
Create a chain of mipmap sizes for a given source source size, where each source is half the size of the one before.
|
||||
Note that the division by 2 rounds down. So a 63x63 texture has as its next lowest mipmap level 31x31. And so on.
|
||||
|
||||
See the `OpenGL wiki page on mipmaps <https://www.khronos.org/opengl/wiki/Texture#Mip_maps>`_ for more info.
|
||||
|
||||
:param dimensions: Size of the source source in pixels
|
||||
:param mip_count: Number of mipmap sizes to generate. By default, generate until the last mip level is 1x1.
|
||||
Resulting mip chain will be smaller if a 1x1 mip level is reached before this value.
|
||||
:return: A list of 2-tuples representing the dimensions of each mip level, including ``dimensions`` at element 0.
|
||||
"""
|
||||
assert all([dim > 0 for dim in dimensions]), "Invalid source dimensions"
|
||||
if not mip_count:
|
||||
mip_count = math.ceil(math.log2(max(dimensions)) + 1) # maximum possible number of mips for a given source
|
||||
|
||||
assert mip_count > 0, "mip_count must be greater than 0"
|
||||
|
||||
chain = []
|
||||
|
||||
for mip in range(mip_count):
|
||||
chain.append(dimensions)
|
||||
if all([dim == 1 for dim in dimensions]):
|
||||
break # we've reached a 1x1 mip and can get no smaller
|
||||
dimensions = tuple([max(dim // 2, 1) for dim in dimensions])
|
||||
|
||||
return chain
|
||||
|
||||
|
||||
def resize_no_premultiply(image: Image.Image, size: Tuple[int, int]) -> Image.Image:
|
||||
"""
|
||||
Resize an image without premulitplying the alpha. Required due to a quick in Pillow
|
||||
|
||||
:param image: Image to resize
|
||||
:param size: Size to resize to
|
||||
:return: The resized image
|
||||
"""
|
||||
if image.mode == 'RGBA':
|
||||
rgb = image.convert('RGB').resize(size, Image.BILINEAR)
|
||||
a = image.getchannel('A').resize(size, Image.BILINEAR)
|
||||
rgb.putalpha(a)
|
||||
return rgb
|
||||
else:
|
||||
return image.resize(size, Image.BILINEAR)
|
0
quicktex/py.typed
Normal file
0
quicktex/py.typed
Normal file
5
quicktex/s3tc/__init__.py
Normal file
5
quicktex/s3tc/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
"""
|
||||
The s3tc module provides encoders and decoders for the common S3 Texture Compression formats. BC1-5 encoding is done
|
||||
internally using a modified version of rgbcx.h.
|
||||
"""
|
||||
from _quicktex._s3tc import *
|
45
quicktex/s3tc/_bindings.cpp
Normal file
45
quicktex/s3tc/_bindings.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
/* Quicktex 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 <pybind11/pybind11.h>
|
||||
|
||||
#include "interpolator/Interpolator.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
namespace quicktex::bindings {
|
||||
|
||||
using namespace quicktex;
|
||||
using namespace quicktex::s3tc;
|
||||
|
||||
void InitInterpolator(py::module_ &s3tc);
|
||||
void InitBC1(py::module_ &s3tc);
|
||||
void InitBC3(py::module_ &s3tc);
|
||||
void InitBC4(py::module_ &s3tc);
|
||||
void InitBC5(py::module_ &s3tc);
|
||||
|
||||
void InitS3TC(py::module_ &m) {
|
||||
py::module_ s3tc = m.def_submodule("_s3tc", "s3tc compression library based on rgbcx.h written by Richard Goldreich");
|
||||
|
||||
InitInterpolator(s3tc);
|
||||
InitBC1(s3tc);
|
||||
InitBC4(s3tc);
|
||||
InitBC3(s3tc);
|
||||
InitBC5(s3tc);
|
||||
}
|
||||
} // namespace quicktex::bindings
|
47
quicktex/s3tc/bc1/BC1Block.cpp
Normal file
47
quicktex/s3tc/bc1/BC1Block.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
/* Quicktex 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 "BC1Block.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../../util.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
uint16_t BC1Block::GetColor0Raw() const { return Pack<uint8_t, uint16_t, 8, EndpointSize>(_color0); }
|
||||
uint16_t BC1Block::GetColor1Raw() const { return Pack<uint8_t, uint16_t, 8, EndpointSize>(_color1); }
|
||||
|
||||
void BC1Block::SetColor0Raw(uint16_t c) { _color0 = Unpack<uint16_t, uint8_t, 8, EndpointSize>(c); }
|
||||
void BC1Block::SetColor1Raw(uint16_t c) { _color1 = Unpack<uint16_t, uint8_t, 8, EndpointSize>(c); }
|
||||
|
||||
BC1Block::SelectorArray BC1Block::GetSelectors() const { return MapArray(_selectors, Unpack<uint8_t, uint8_t, SelectorBits, Width>); }
|
||||
|
||||
void BC1Block::SetSelectors(const BC1Block::SelectorArray& unpacked) {
|
||||
for (unsigned y = 0; y < (unsigned)Height; y++) {
|
||||
if (std::any_of(unpacked[y].begin(), unpacked[y].end(), [](uint8_t i) { return i > SelectorMax; }))
|
||||
throw std::invalid_argument("Selector value out of bounds.");
|
||||
}
|
||||
_selectors = MapArray(unpacked, Pack<uint8_t, uint8_t, SelectorBits, Width>);
|
||||
}
|
||||
|
||||
bool BC1Block::operator==(const BC1Block& Rhs) const { return _color0 == Rhs._color0 && _color1 == Rhs._color1 && _selectors == Rhs._selectors; }
|
||||
bool BC1Block::operator!=(const BC1Block& Rhs) const { return !(Rhs == *this); }
|
||||
|
||||
} // namespace quicktex::s3tc
|
127
quicktex/s3tc/bc1/BC1Block.h
Normal file
127
quicktex/s3tc/bc1/BC1Block.h
Normal file
@ -0,0 +1,127 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <utility>
|
||||
|
||||
#include "../../Color.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
class alignas(8) BC1Block {
|
||||
public:
|
||||
static constexpr size_t Width = 4;
|
||||
static constexpr size_t Height = 4;
|
||||
|
||||
static constexpr size_t EndpointSize = 2; // size of a 5:6:5 endpoint in bytes
|
||||
static constexpr size_t SelectorSize = 4; // size of selector array in bytes
|
||||
static constexpr size_t SelectorBits = 2; // size of a selector in bits
|
||||
static constexpr uint8_t SelectorMax = (1 << SelectorBits) - 1; // maximum value of a selector
|
||||
|
||||
using SelectorArray = std::array<std::array<uint8_t, Width>, Height>;
|
||||
using ColorPair = std::pair<Color, Color>;
|
||||
|
||||
private:
|
||||
std::array<uint8_t, EndpointSize> _color0;
|
||||
std::array<uint8_t, EndpointSize> _color1;
|
||||
std::array<uint8_t, SelectorSize> _selectors;
|
||||
|
||||
public:
|
||||
/// Create a new BC1Blok
|
||||
constexpr BC1Block() : _color0(), _color1(), _selectors() {
|
||||
static_assert(sizeof(BC1Block) == 8);
|
||||
static_assert(sizeof(std::array<BC1Block, 10>) == 8 * 10);
|
||||
static_assert(alignof(BC1Block) >= 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BC1Block
|
||||
* @param color0 first endpoint color
|
||||
* @param color1 second endpoint color
|
||||
* @param selectors the selectors as a 4x4 list of integers, between 0 and 3 inclusive.
|
||||
*/
|
||||
BC1Block(Color color0, Color color1, const SelectorArray& selectors) {
|
||||
SetColor0(color0);
|
||||
SetColor1(color1);
|
||||
SetSelectors(selectors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BC1Block
|
||||
* @param ep0 first endpoint
|
||||
* @param ep1 second endpoint
|
||||
* @param selectors the selectors as a 4x4 list of integers, between 0 and 3 inclusive.
|
||||
*/
|
||||
BC1Block(uint16_t ep0, uint16_t ep1, const SelectorArray& selectors) {
|
||||
SetColor0Raw(ep0);
|
||||
SetColor1Raw(ep1);
|
||||
SetSelectors(selectors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BC1Block
|
||||
* @param ep0 first endpoint
|
||||
* @param ep1 second endpoint
|
||||
* @param solid_mask single byte mask to use for each row
|
||||
*/
|
||||
BC1Block(uint16_t ep0, uint16_t ep1, uint8_t solid_mask) {
|
||||
SetColor0Raw(ep0);
|
||||
SetColor1Raw(ep1);
|
||||
_selectors.fill(solid_mask);
|
||||
}
|
||||
|
||||
uint16_t GetColor0Raw() const;
|
||||
uint16_t GetColor1Raw() const;
|
||||
|
||||
void SetColor0Raw(uint16_t c);
|
||||
void SetColor1Raw(uint16_t c);
|
||||
|
||||
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(cs.first);
|
||||
SetColor1(cs.second);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this block's selectors
|
||||
* @return a 4x4 array of integers between 0 and 3 inclusive
|
||||
*/
|
||||
SelectorArray GetSelectors() const;
|
||||
|
||||
/**
|
||||
* Set this block's selectors
|
||||
* @param unpacked a 4x4 array of integers between 0 and 3 inclusive
|
||||
*/
|
||||
void SetSelectors(const SelectorArray& unpacked);
|
||||
|
||||
bool Is3Color() const { return GetColor0Raw() <= GetColor1Raw(); }
|
||||
|
||||
bool operator==(const BC1Block& Rhs) const;
|
||||
bool operator!=(const BC1Block& Rhs) const;
|
||||
};
|
||||
} // namespace quicktex::s3tc
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -19,36 +19,36 @@
|
||||
|
||||
#include "BC1Decoder.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../BlockView.h"
|
||||
#include "../Color.h"
|
||||
#include "../Interpolator.h"
|
||||
#include "../ndebug.h"
|
||||
#include "../../Color.h"
|
||||
#include "../../ColorBlock.h"
|
||||
#include "BC1Block.h"
|
||||
|
||||
namespace rgbcx {
|
||||
void BC1Decoder::DecodeBlock(Color4x4 dest, BC1Block *const block) const noexcept(ndebug) {
|
||||
const auto l = block->GetLowColor();
|
||||
const auto h = block->GetHighColor();
|
||||
const auto selectors = block->UnpackSelectors();
|
||||
const auto colors = _interpolator->InterpolateBC1(l, h);
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
ColorBlock<4, 4> BC1Decoder::DecodeBlock(const BC1Block &block) const { return DecodeBlock(block, true); }
|
||||
|
||||
ColorBlock<4, 4> BC1Decoder::DecodeBlock(const BC1Block &block, bool use_3color) const {
|
||||
auto output = ColorBlock<4, 4>();
|
||||
const auto l = block.GetColor0Raw();
|
||||
const auto h = block.GetColor1Raw();
|
||||
const auto selectors = block.GetSelectors();
|
||||
const auto colors = _interpolator->Interpolate565BC1(l, h, use_3color);
|
||||
|
||||
for (unsigned y = 0; y < 4; y++) {
|
||||
for (unsigned x = 0; x < 4; x++) {
|
||||
const auto selector = selectors[y][x];
|
||||
const auto color = colors[selector];
|
||||
auto color = colors[selector];
|
||||
assert(selector < 4);
|
||||
assert((color.a == 0 && selector == 3 && l <= h) || color.a == UINT8_MAX);
|
||||
if (_write_alpha) {
|
||||
dest.Get(x, y).SetRGBA(color);
|
||||
} else {
|
||||
dest.Get(x, y).SetRGB(color);
|
||||
}
|
||||
if (!write_alpha) { color.a = output.Get(x, y).a; }
|
||||
output.Set(x, y, color);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
} // namespace rgbcx
|
||||
} // namespace quicktex::s3tc
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -21,26 +21,31 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "../BlockDecoder.h"
|
||||
#include "../BlockView.h"
|
||||
#include "../Interpolator.h"
|
||||
#include "../ndebug.h"
|
||||
#include "../../ColorBlock.h"
|
||||
#include "../../Decoder.h"
|
||||
#include "../../Texture.h"
|
||||
#include "../interpolator/Interpolator.h"
|
||||
#include "BC1Block.h"
|
||||
|
||||
namespace rgbcx {
|
||||
class BC1Decoder final : public BlockDecoder<BC1Block, 4, 4> {
|
||||
namespace quicktex::s3tc {
|
||||
class BC1Decoder final : public BlockDecoder<BlockTexture<BC1Block>> {
|
||||
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) {}
|
||||
|
||||
void DecodeBlock(Color4x4 dest, BC1Block *const block) const noexcept(ndebug) override;
|
||||
BC1Decoder(bool vwrite_alpha, InterpolatorPtr interpolator) : write_alpha(vwrite_alpha), _interpolator(interpolator) {}
|
||||
|
||||
BC1Decoder(bool vwrite_alpha = false) : BC1Decoder(vwrite_alpha, std::make_shared<Interpolator>()) {}
|
||||
|
||||
BC1Decoder(InterpolatorPtr interpolator) : BC1Decoder(false, interpolator) {}
|
||||
|
||||
ColorBlock<4, 4> DecodeBlock(const BC1Block& block) const override;
|
||||
ColorBlock<4, 4> DecodeBlock(const BC1Block& block, bool use_3color) const;
|
||||
|
||||
InterpolatorPtr GetInterpolator() const { return _interpolator; }
|
||||
constexpr bool WritesAlpha() const { return _write_alpha; }
|
||||
|
||||
bool write_alpha;
|
||||
|
||||
private:
|
||||
const InterpolatorPtr _interpolator;
|
||||
const bool _write_alpha;
|
||||
};
|
||||
} // namespace rgbcx
|
||||
} // namespace quicktex::s3tc
|
998
quicktex/s3tc/bc1/BC1Encoder.cpp
Normal file
998
quicktex/s3tc/bc1/BC1Encoder.cpp
Normal file
@ -0,0 +1,998 @@
|
||||
/* Quicktex 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 "BC1Encoder.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../../Color.h"
|
||||
#include "../../ColorBlock.h"
|
||||
#include "../../Matrix4x4.h"
|
||||
#include "../../Texture.h"
|
||||
#include "../../Vector4.h"
|
||||
#include "../../Vector4Int.h"
|
||||
#include "../../bitwiseEnums.h"
|
||||
#include "../../util.h"
|
||||
#include "Histogram.h"
|
||||
#include "OrderTable.h"
|
||||
#include "SingleColorTable.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
// constructors
|
||||
|
||||
BC1Encoder::BC1Encoder(unsigned int level, ColorMode color_mode, InterpolatorPtr interpolator) : _interpolator(interpolator), _color_mode(color_mode) {
|
||||
if (color_mode != ColorMode::FourColor && color_mode != ColorMode::ThreeColor && color_mode != ColorMode::ThreeColorBlack) {
|
||||
throw std::invalid_argument("Encoder color mode must be FourColor, ThreeColor, or ThreeColorBlack");
|
||||
}
|
||||
|
||||
OrderTable<4>::Generate();
|
||||
_single_match5 = SingleColorTable<5, 4>(_interpolator);
|
||||
_single_match6 = SingleColorTable<6, 4>(_interpolator);
|
||||
|
||||
if (!OrderTable<4>::generated) throw std::runtime_error("Failed to generate 4-color order tables");
|
||||
if (!_single_match5) throw std::runtime_error("Failed to generate 5-bit 4-color single color table");
|
||||
if (!_single_match6) throw std::runtime_error("Failed to generate 6-bit 4-color single color table");
|
||||
|
||||
if (color_mode != ColorMode::FourColor) {
|
||||
OrderTable<3>::Generate();
|
||||
_single_match5_half = SingleColorTable<5, 3>(_interpolator);
|
||||
_single_match6_half = SingleColorTable<6, 3>(_interpolator);
|
||||
|
||||
if (!OrderTable<3>::generated) throw std::runtime_error("Failed to generate 3-color order tables");
|
||||
if (!_single_match5_half) throw std::runtime_error("Failed to generate 5-bit 3-color single color table");
|
||||
if (!_single_match6_half) throw std::runtime_error("Failed to generate 6-bit 3-color single color table");
|
||||
}
|
||||
|
||||
SetLevel(level);
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
void BC1Encoder::SetLevel(unsigned level) {
|
||||
if (level > 19) throw std::invalid_argument("Level out of range, bust be between 0 and 18 inclusive"); // theres a secret level 19 but shhhhhh
|
||||
|
||||
two_ls_passes = false;
|
||||
two_ep_passes = false;
|
||||
two_cf_passes = false;
|
||||
exhaustive = false;
|
||||
|
||||
_power_iterations = 4;
|
||||
_error_mode = ErrorMode::Check2;
|
||||
_endpoint_mode = EndpointMode::PCA;
|
||||
_search_rounds = 0;
|
||||
_orderings3 = 0;
|
||||
_orderings4 = 0;
|
||||
|
||||
switch (level) {
|
||||
case 0:
|
||||
// Faster/higher quality than stb_dxt default.
|
||||
_endpoint_mode = EndpointMode::BoundingBoxInt;
|
||||
break;
|
||||
case 1:
|
||||
// Faster/higher quality than stb_dxt default. a bit higher average quality vs. mode 0.
|
||||
_endpoint_mode = EndpointMode::LeastSquares;
|
||||
break;
|
||||
case 2:
|
||||
// On average mode 2 is a little weaker than modes 0/1, but it's stronger on outliers (very tough textures).
|
||||
// Slightly stronger than stb_dxt.
|
||||
// Uses default settings.
|
||||
break;
|
||||
case 3:
|
||||
// Slightly stronger than stb_dxt HIGHQUAL.
|
||||
two_ls_passes = true;
|
||||
break;
|
||||
case 4:
|
||||
two_ls_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Full;
|
||||
_power_iterations = 6;
|
||||
break;
|
||||
default:
|
||||
case 5:
|
||||
// stb_dxt HIGHQUAL + permit 3 color (if it's enabled).
|
||||
two_ls_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Faster;
|
||||
break;
|
||||
case 6:
|
||||
two_ls_passes = true;
|
||||
|
||||
_orderings4 = 1;
|
||||
_orderings3 = 1;
|
||||
_error_mode = ErrorMode::Faster;
|
||||
break;
|
||||
case 7:
|
||||
two_ls_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Faster;
|
||||
_orderings4 = 4;
|
||||
_orderings3 = 1;
|
||||
break;
|
||||
case 8:
|
||||
two_ls_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Faster;
|
||||
_orderings4 = 8;
|
||||
_orderings3 = 1;
|
||||
break;
|
||||
case 9:
|
||||
two_ls_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Check2;
|
||||
_orderings4 = 11;
|
||||
_orderings3 = 3;
|
||||
break;
|
||||
case 10:
|
||||
two_ls_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Check2;
|
||||
_orderings4 = 20;
|
||||
_orderings3 = 8;
|
||||
break;
|
||||
case 11:
|
||||
two_ls_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Check2;
|
||||
_orderings4 = 28;
|
||||
_orderings3 = 16;
|
||||
break;
|
||||
case 12:
|
||||
two_ls_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Check2;
|
||||
_orderings4 = 32;
|
||||
_orderings3 = 32;
|
||||
break;
|
||||
case 13:
|
||||
two_ls_passes = true;
|
||||
two_ep_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Full;
|
||||
_orderings4 = 32;
|
||||
_orderings3 = 32;
|
||||
_search_rounds = 20;
|
||||
_power_iterations = 6;
|
||||
break;
|
||||
case 14:
|
||||
two_ls_passes = true;
|
||||
two_ep_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Full;
|
||||
_orderings4 = 32;
|
||||
_orderings3 = 32;
|
||||
_search_rounds = 32;
|
||||
_power_iterations = 6;
|
||||
break;
|
||||
case 15:
|
||||
two_ls_passes = true;
|
||||
two_ep_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Full;
|
||||
_orderings4 = 56;
|
||||
_orderings3 = 32;
|
||||
_search_rounds = 32;
|
||||
_power_iterations = 6;
|
||||
break;
|
||||
case 16:
|
||||
two_ls_passes = true;
|
||||
two_ep_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Full;
|
||||
_orderings4 = 80;
|
||||
_orderings3 = 32;
|
||||
_search_rounds = 256;
|
||||
_power_iterations = 6;
|
||||
|
||||
break;
|
||||
case 17:
|
||||
two_ls_passes = true;
|
||||
two_ep_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Full;
|
||||
_orderings4 = 128;
|
||||
_orderings3 = 32;
|
||||
_search_rounds = 256;
|
||||
break;
|
||||
case 18:
|
||||
two_ls_passes = true;
|
||||
two_ep_passes = true;
|
||||
two_cf_passes = true;
|
||||
|
||||
_error_mode = ErrorMode::Full;
|
||||
_orderings4 = 128;
|
||||
_orderings3 = 32;
|
||||
_search_rounds = 256;
|
||||
_power_iterations = 6;
|
||||
break;
|
||||
|
||||
case 19:
|
||||
// This hidden mode is *extremely* slow and abuses the encoder. It's just for testing/training.
|
||||
|
||||
two_ls_passes = true;
|
||||
two_ep_passes = true;
|
||||
two_cf_passes = true;
|
||||
exhaustive = true;
|
||||
|
||||
_error_mode = ErrorMode::Full;
|
||||
_orderings4 = 128;
|
||||
_orderings3 = 32;
|
||||
_search_rounds = 256;
|
||||
_power_iterations = 6;
|
||||
break;
|
||||
}
|
||||
|
||||
_orderings4 = clamp(_orderings4, 1U, OrderTable<4>::BestOrderCount);
|
||||
_orderings3 = clamp(_orderings3, 1U, OrderTable<3>::BestOrderCount);
|
||||
}
|
||||
|
||||
void BC1Encoder::SetOrderings4(unsigned orderings4) { _orderings4 = clamp(orderings4, 1U, OrderTable<4>::BestOrderCount); }
|
||||
void BC1Encoder::SetOrderings3(unsigned orderings3) { _orderings3 = clamp(orderings3, 1U, OrderTable<3>::BestOrderCount); }
|
||||
void BC1Encoder::SetOrderings(OrderingPair orderings) {
|
||||
SetOrderings4(std::get<0>(orderings));
|
||||
SetOrderings3(std::get<1>(orderings));
|
||||
}
|
||||
|
||||
void BC1Encoder::SetPowerIterations(unsigned int power_iters) { _power_iterations = clamp(power_iters, min_power_iterations, max_power_iterations); }
|
||||
|
||||
// Public methods
|
||||
BC1Block BC1Encoder::EncodeBlock(const ColorBlock<4, 4> &pixels) const {
|
||||
if (pixels.IsSingleColor()) {
|
||||
// single-color pixel block, do it the fast way
|
||||
return WriteBlockSolid(pixels.Get(0, 0));
|
||||
}
|
||||
|
||||
auto metrics = pixels.GetMetrics();
|
||||
|
||||
const bool use_likely_orderings = (exhaustive || _orderings3 > 0 || _orderings4 > 0);
|
||||
|
||||
bool needs_block_error = use_likely_orderings;
|
||||
needs_block_error |= (_color_mode == ColorMode::ThreeColor);
|
||||
needs_block_error |= (_color_mode == ColorMode::ThreeColorBlack) && metrics.has_black;
|
||||
needs_block_error |= (_error_mode != ErrorMode::None);
|
||||
needs_block_error |= (_search_rounds > 0);
|
||||
ErrorMode error_mode = needs_block_error ? _error_mode : ErrorMode::None;
|
||||
|
||||
assert(!((_error_mode == ErrorMode::None) && needs_block_error));
|
||||
|
||||
const unsigned total_ls_passes = two_ls_passes ? 2 : 1;
|
||||
const unsigned total_cf_passes = two_cf_passes ? 2 : 1;
|
||||
const unsigned total_ep_passes = (needs_block_error && two_ep_passes) ? 2 : 1;
|
||||
|
||||
// Initial block generation
|
||||
EncodeResults orig;
|
||||
EncodeResults result;
|
||||
for (unsigned round = 0; round < total_ep_passes; round++) {
|
||||
EndpointMode endpoint_mode = (round == 1) ? EndpointMode::BoundingBox : _endpoint_mode;
|
||||
|
||||
EncodeResults trial_orig;
|
||||
FindEndpoints(trial_orig, pixels, metrics, endpoint_mode);
|
||||
|
||||
EncodeResults trial_result = trial_orig;
|
||||
|
||||
FindSelectors<ColorMode::FourColor>(trial_result, pixels, error_mode);
|
||||
RefineBlockLS<ColorMode::FourColor>(trial_result, pixels, metrics, error_mode, total_ls_passes);
|
||||
|
||||
if (!needs_block_error || trial_result.error < result.error) {
|
||||
result = trial_result;
|
||||
orig = trial_orig;
|
||||
}
|
||||
}
|
||||
|
||||
// First refinement pass using ordered cluster fit
|
||||
if (result.error > 0 && use_likely_orderings) {
|
||||
for (unsigned iter = 0; iter < total_cf_passes; iter++) { RefineBlockCF<ColorMode::FourColor>(result, pixels, metrics, _error_mode, _orderings4); }
|
||||
}
|
||||
|
||||
// try for 3-color block
|
||||
if (result.error > 0 && (bool)(_color_mode & ColorMode::ThreeColor)) {
|
||||
EncodeResults trial_result = orig;
|
||||
|
||||
FindSelectors<ColorMode::ThreeColor>(trial_result, pixels, ErrorMode::Full);
|
||||
RefineBlockLS<ColorMode::ThreeColor>(trial_result, pixels, metrics, ErrorMode::Full, total_ls_passes);
|
||||
|
||||
// First refinement pass using ordered cluster fit
|
||||
if (trial_result.error > 0 && use_likely_orderings) {
|
||||
for (unsigned iter = 0; iter < total_cf_passes; iter++) {
|
||||
RefineBlockCF<ColorMode::ThreeColor>(trial_result, pixels, metrics, ErrorMode::Full, _orderings3);
|
||||
}
|
||||
}
|
||||
|
||||
if (trial_result.error < result.error) { result = trial_result; }
|
||||
}
|
||||
|
||||
// try for 3-color block with black
|
||||
if (result.error > 0 && (_color_mode == ColorMode::ThreeColorBlack) && metrics.has_black && !metrics.max.IsBlack()) {
|
||||
EncodeResults trial_result;
|
||||
BlockMetrics metrics_no_black = pixels.GetMetrics(true);
|
||||
|
||||
FindEndpoints(trial_result, pixels, metrics_no_black, EndpointMode::PCA, true);
|
||||
FindSelectors<ColorMode::ThreeColorBlack>(trial_result, pixels, ErrorMode::Full);
|
||||
RefineBlockLS<ColorMode::ThreeColorBlack>(trial_result, pixels, metrics_no_black, ErrorMode::Full, total_ls_passes);
|
||||
|
||||
if (trial_result.error < result.error) { result = trial_result; }
|
||||
}
|
||||
|
||||
// refine endpoints by searching for nearby colors
|
||||
if (result.error > 0 && _search_rounds > 0) { EndpointSearch(result, pixels); }
|
||||
|
||||
return WriteBlock(result);
|
||||
}
|
||||
|
||||
// Private methods
|
||||
BC1Block BC1Encoder::WriteBlockSolid(Color color) const {
|
||||
uint8_t mask = 0xAA; // 2222
|
||||
uint16_t min16, max16;
|
||||
|
||||
if ((color.r | color.g | color.b) == 0) {
|
||||
// quick shortcut for all-black blocks
|
||||
min16 = 0;
|
||||
max16 = 1;
|
||||
mask = 0x55; // 1111 (Min value only, max is ignored)
|
||||
} else {
|
||||
// why is there no subscript operator for shared_ptr<array>
|
||||
EncodeResults result;
|
||||
FindEndpointsSingleColor(result, color, false);
|
||||
|
||||
if ((bool)(_color_mode & ColorMode::ThreeColor)) {
|
||||
EncodeResults result_3color;
|
||||
FindEndpointsSingleColor(result_3color, color, true);
|
||||
|
||||
if (result_3color.error < result.error) { result = result_3color; }
|
||||
}
|
||||
|
||||
min16 = result.low.Pack565Unscaled();
|
||||
max16 = result.high.Pack565Unscaled();
|
||||
|
||||
if (result.color_mode == ColorMode::FourColor) {
|
||||
if (min16 == max16) {
|
||||
// make sure this isnt accidentally a 3-color block
|
||||
// so make max16 > min16 (l > h)
|
||||
if (min16 > 0) {
|
||||
min16--;
|
||||
mask = 0; // endpoints are equal so mask doesnt matter
|
||||
} else {
|
||||
assert(min16 == 0 && max16 == 0);
|
||||
max16 = 1;
|
||||
min16 = 0;
|
||||
mask = 0x55; // 1111 (Min value only, max is ignored)
|
||||
}
|
||||
} else if (max16 < min16) {
|
||||
std::swap(min16, max16);
|
||||
mask = 0xFF; // invert mask to 3333
|
||||
}
|
||||
assert(max16 > min16);
|
||||
} else if (max16 > min16) {
|
||||
std::swap(min16, max16); // assure 3-color blocks
|
||||
}
|
||||
}
|
||||
|
||||
return BC1Block(max16, min16, mask);
|
||||
}
|
||||
|
||||
BC1Block BC1Encoder::WriteBlock(EncodeResults &result) const {
|
||||
BC1Block::SelectorArray selectors;
|
||||
uint16_t ep1 = result.low.Pack565Unscaled();
|
||||
uint16_t ep0 = result.high.Pack565Unscaled();
|
||||
std::array<uint8_t, 4> lut;
|
||||
|
||||
assert(result.color_mode != ColorMode::Incomplete);
|
||||
|
||||
if ((bool)(result.color_mode & ColorMode::FourColor)) {
|
||||
lut = {1, 3, 2, 0};
|
||||
|
||||
if (ep1 > ep0) {
|
||||
std::swap(ep1, ep0);
|
||||
lut = {0, 2, 3, 1};
|
||||
} else if (ep1 == ep0) {
|
||||
if (ep1 > 0) {
|
||||
ep1--;
|
||||
lut = {0, 0, 0, 0};
|
||||
} else {
|
||||
assert(ep1 == 0 && ep0 == 0);
|
||||
ep0 = 1;
|
||||
ep1 = 0;
|
||||
lut = {1, 1, 1, 1};
|
||||
}
|
||||
}
|
||||
|
||||
assert(ep0 > ep1);
|
||||
} else {
|
||||
lut = {1, 2, 0, 3};
|
||||
|
||||
if (ep1 < ep0) {
|
||||
std::swap(ep1, ep0);
|
||||
lut = {0, 2, 1, 3};
|
||||
}
|
||||
|
||||
assert(ep0 <= ep1);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 16; i++) {
|
||||
unsigned x = i % 4;
|
||||
unsigned y = i / 4;
|
||||
selectors[y][x] = lut[result.selectors[i]];
|
||||
if (result.color_mode == ColorMode::ThreeColor) { assert(selectors[y][x] != 3); }
|
||||
}
|
||||
|
||||
return BC1Block(ep0, ep1, selectors);
|
||||
}
|
||||
|
||||
void BC1Encoder::FindEndpointsSingleColor(EncodeResults &result, Color color, bool is_3color) const {
|
||||
auto &match5 = is_3color ? _single_match5_half : _single_match5;
|
||||
auto &match6 = is_3color ? _single_match6_half : _single_match6;
|
||||
|
||||
BC1MatchEntry match_r = match5->at(color.r);
|
||||
BC1MatchEntry match_g = match6->at(color.g);
|
||||
BC1MatchEntry match_b = match5->at(color.b);
|
||||
|
||||
result.color_mode = is_3color ? ColorMode::ThreeColor : ColorMode::FourColor;
|
||||
result.error = match_r.error + match_g.error + match_b.error;
|
||||
result.low = Color(match_r.low, match_g.low, match_b.low);
|
||||
result.high = Color(match_r.high, match_g.high, match_b.high);
|
||||
// selectors decided when writing, no point deciding them now
|
||||
}
|
||||
|
||||
void BC1Encoder::FindEndpointsSingleColor(EncodeResults &result, const CBlock &pixels, Color color, bool is_3color) const {
|
||||
std::array<Color, 4> colors = _interpolator->InterpolateBC1(result.low, result.high, is_3color);
|
||||
Vector4Int result_vector = (Vector4Int)colors[2];
|
||||
|
||||
FindEndpointsSingleColor(result, color, is_3color);
|
||||
|
||||
result.error = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
Vector4Int pixel_vector = (Vector4Int)pixels.Get(i);
|
||||
auto diff = pixel_vector - result_vector;
|
||||
result.error += diff.SqrMag();
|
||||
result.selectors[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void BC1Encoder::FindEndpoints(EncodeResults &result, const CBlock &pixels, const BlockMetrics &metrics, EndpointMode endpoint_mode, bool ignore_black) const {
|
||||
if (metrics.is_greyscale) {
|
||||
// specialized greyscale case
|
||||
const unsigned fr = pixels.Get(0, 0).r;
|
||||
|
||||
if (metrics.max.r - metrics.min.r < 2) {
|
||||
// single color block
|
||||
uint8_t fr5 = (uint8_t)scale8To5(fr);
|
||||
uint8_t fr6 = (uint8_t)scale8To6(fr);
|
||||
|
||||
result.low = Color(fr5, fr6, fr5);
|
||||
result.high = result.low;
|
||||
} else {
|
||||
uint8_t lr5 = scale8To5(metrics.min.r);
|
||||
uint8_t lr6 = scale8To6(metrics.min.r);
|
||||
|
||||
uint8_t hr5 = scale8To5(metrics.max.r);
|
||||
uint8_t hr6 = scale8To6(metrics.max.r);
|
||||
|
||||
result.low = Color(lr5, lr6, lr5);
|
||||
result.high = Color(hr5, hr6, hr5);
|
||||
}
|
||||
} else if (endpoint_mode == EndpointMode::LeastSquares) {
|
||||
// 2D Least Squares approach from Humus's example, with added inset and optimal rounding.
|
||||
Color diff = Color(metrics.max.r - metrics.min.r, metrics.max.g - metrics.min.g, metrics.max.b - metrics.min.b);
|
||||
Vector4 l = {0, 0, 0};
|
||||
Vector4 h = {0, 0, 0};
|
||||
|
||||
auto &sums = metrics.sums;
|
||||
auto &min = metrics.min;
|
||||
|
||||
unsigned chan0 = (unsigned)diff.MaxChannelRGB(); // primary axis of the bounding box
|
||||
l[chan0] = (float)min[chan0];
|
||||
h[chan0] = (float)min[chan0];
|
||||
|
||||
assert((diff[chan0] >= diff[(chan0 + 1) % 3]) && (diff[chan0] >= diff[(chan0 + 2) % 3]));
|
||||
|
||||
std::array<unsigned, 3> sums_xy;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
auto val = pixels.Get(i);
|
||||
for (unsigned c = 0; c < 3; c++) { sums_xy[c] += val[chan0] * val[c]; }
|
||||
}
|
||||
|
||||
const unsigned sum_x = (unsigned)sums[chan0];
|
||||
const unsigned sum_xx = sums_xy[chan0];
|
||||
|
||||
float denominator = (float)(16 * sum_xx) - (float)(sum_x * sum_x);
|
||||
|
||||
// once per secondary axis, calculate high and low using least squares
|
||||
if (fabs(denominator) > 1e-8f) {
|
||||
for (unsigned i = 1; i < 3; i++) {
|
||||
/* each secondary axis is fitted with a linear formula of the form
|
||||
* y = ax + b
|
||||
* where y is the secondary axis and x is the primary axis
|
||||
* a = (m∑xy - ∑x∑y) / m∑x² - (∑x)²
|
||||
* b = (∑x²∑y - ∑xy∑x) / m∑x² - (∑x)²
|
||||
* see Giordano/Weir pg.103 */
|
||||
const unsigned chan = (chan0 + i) % 3;
|
||||
const unsigned sum_y = (unsigned)sums[chan];
|
||||
const unsigned sum_xy = sums_xy[chan];
|
||||
|
||||
float a = (float)((16 * sum_xy) - (sum_x * sum_y)) / denominator;
|
||||
float b = (float)((sum_xx * sum_y) - (sum_xy * sum_x)) / denominator;
|
||||
|
||||
l[chan] = b + (a * l[chan0]);
|
||||
h[chan] = b + (a * h[chan0]);
|
||||
}
|
||||
}
|
||||
|
||||
// once per axis, inset towards the center by 1/16 of the delta and scale
|
||||
for (unsigned c = 0; c < 3; c++) {
|
||||
float inset = (h[c] - l[c]) / 16.0f;
|
||||
|
||||
l[c] = ((l[c] + inset) / 255.0f);
|
||||
h[c] = ((h[c] - inset) / 255.0f);
|
||||
}
|
||||
|
||||
result.low = Color::PreciseRound565(l);
|
||||
result.high = Color::PreciseRound565(h);
|
||||
} else if (endpoint_mode == EndpointMode::BoundingBox) {
|
||||
// Algorithm from icbc.h compress_dxt1_fast()
|
||||
Vector4 l, h;
|
||||
const float bias = 8.0f / 255.0f;
|
||||
|
||||
// rescale and inset values
|
||||
for (unsigned c = 0; c < 3; c++) { // heh, c++
|
||||
l[c] = (float)metrics.min[c] / 255.0f;
|
||||
h[c] = (float)metrics.max[c] / 255.0f;
|
||||
|
||||
float inset = (h[c] - l[c] - bias) / 16.0f;
|
||||
l[c] += inset;
|
||||
h[c] -= inset;
|
||||
}
|
||||
|
||||
// Select the correct diagonal across the bounding box
|
||||
int icov_xz = 0, icov_yz = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int b = (int)pixels.Get(i).b - metrics.avg.b;
|
||||
icov_xz += b * (int)pixels.Get(i).r - metrics.avg.r;
|
||||
icov_yz += b * (int)pixels.Get(i).g - metrics.avg.g;
|
||||
}
|
||||
|
||||
if (icov_xz < 0) std::swap(l[0], h[0]);
|
||||
if (icov_yz < 0) std::swap(l[1], h[1]);
|
||||
|
||||
result.low = Color::PreciseRound565(l);
|
||||
result.high = Color::PreciseRound565(h);
|
||||
} else if (endpoint_mode == EndpointMode::BoundingBoxInt) {
|
||||
// Algorithm from icbc.h compress_dxt1_fast(), but converted to integer.
|
||||
|
||||
Color min, max;
|
||||
|
||||
// rescale and inset values
|
||||
for (unsigned c = 0; c < 3; c++) {
|
||||
int inset = ((int)(metrics.max[c] - metrics.min[c]) - 8) >> 4; // 1/16 of delta, with bias
|
||||
|
||||
min[c] = clamp255(metrics.min[c] + inset);
|
||||
max[c] = clamp255(metrics.max[c] - inset);
|
||||
}
|
||||
|
||||
int icov_xz = 0, icov_yz = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int b = (int)pixels.Get(i).b - metrics.avg.b;
|
||||
icov_xz += b * (int)pixels.Get(i).r - metrics.avg.r;
|
||||
icov_yz += b * (int)pixels.Get(i).g - metrics.avg.g;
|
||||
}
|
||||
|
||||
if (icov_xz < 0) std::swap(min.r, max.r);
|
||||
if (icov_yz < 0) std::swap(min.g, max.g);
|
||||
|
||||
result.low = min.ScaleTo565();
|
||||
result.high = max.ScaleTo565();
|
||||
} else if (endpoint_mode == EndpointMode::PCA) {
|
||||
// the slow way
|
||||
// Select 2 colors along the principle axis. (There must be a faster/simpler way.)
|
||||
auto min = Vector4::FromColorRGB(metrics.min);
|
||||
auto max = Vector4::FromColorRGB(metrics.max);
|
||||
auto avg = Vector4::FromColorRGB(metrics.avg);
|
||||
|
||||
Vector4 axis = {306, 601, 117}; // Luma vector
|
||||
Matrix4x4 covariance = Matrix4x4::Identity();
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
auto val = pixels.Get(i);
|
||||
if (ignore_black && val.IsBlack()) continue;
|
||||
|
||||
auto color_vec = Vector4::FromColorRGB(val);
|
||||
Vector4 diff = color_vec - avg;
|
||||
for (unsigned c1 = 0; c1 < 3; c1++) {
|
||||
for (unsigned c2 = c1; c2 < 3; c2++) {
|
||||
covariance[c1][c2] += (diff[c1] * diff[c2]);
|
||||
assert(c1 <= c2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
covariance /= 255.0f;
|
||||
covariance.Mirror();
|
||||
|
||||
Vector4 delta = max - min;
|
||||
|
||||
// realign r and g axes to match
|
||||
if (covariance[0][2] < 0) delta[0] = -delta[0]; // r vs b
|
||||
if (covariance[1][2] < 0) delta[1] = -delta[1]; // g vs b
|
||||
|
||||
// using the covariance matrix, stretch the delta vector towards the primary axis of the data using power iteration
|
||||
// the end result of this may actually be the same as the least squares approach, will have to do more research
|
||||
for (unsigned power_iter = 0; power_iter < _power_iterations; power_iter++) { delta = covariance * delta; }
|
||||
|
||||
// if we found any correlation, then this is our new axis. otherwise we fallback to the luma vector
|
||||
float k = delta.MaxAbs(3);
|
||||
if (k >= 2) { axis = delta * (2048.0f / k); }
|
||||
|
||||
axis *= 16;
|
||||
|
||||
float min_dot = INFINITY;
|
||||
float max_dot = -INFINITY;
|
||||
|
||||
int min_index = 0, max_index = 0;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
auto val = pixels.Get(i);
|
||||
if (ignore_black && val.IsBlack()) continue;
|
||||
|
||||
auto color_vec = Vector4::FromColorRGB(val);
|
||||
// since axis is constant here, I dont think its magnitude actually matters,
|
||||
// since we only care about the min or max dot product
|
||||
float dot = color_vec.Dot(axis);
|
||||
if (dot > max_dot) {
|
||||
max_dot = dot;
|
||||
max_index = i;
|
||||
}
|
||||
if (dot < min_dot) {
|
||||
min_dot = dot;
|
||||
min_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
result.low = pixels.Get(min_index).ScaleTo565();
|
||||
result.high = pixels.Get(max_index).ScaleTo565();
|
||||
}
|
||||
|
||||
result.color_mode = ColorMode::Incomplete;
|
||||
}
|
||||
|
||||
template <BC1Encoder::ColorMode M> void BC1Encoder::FindSelectors(EncodeResults &result, const CBlock &pixels, ErrorMode error_mode) const {
|
||||
assert(!((error_mode != ErrorMode::Full) && (bool)(M & ColorMode::ThreeColor)));
|
||||
|
||||
const int color_count = (unsigned)M & 0x0F;
|
||||
|
||||
std::array<Color, 4> colors = _interpolator->InterpolateBC1(result.low, result.high, color_count == 3);
|
||||
std::array<Vector4Int, 4> color_vectors;
|
||||
|
||||
if (color_count == 4) {
|
||||
color_vectors = {Vector4Int::FromColorRGB(colors[0]), Vector4Int::FromColorRGB(colors[2]), Vector4Int::FromColorRGB(colors[3]),
|
||||
Vector4Int::FromColorRGB(colors[1])};
|
||||
} else {
|
||||
color_vectors = {Vector4Int::FromColorRGB(colors[0]), Vector4Int::FromColorRGB(colors[2]), Vector4Int::FromColorRGB(colors[1]),
|
||||
Vector4Int::FromColorRGB(colors[3])};
|
||||
}
|
||||
|
||||
unsigned total_error = 0;
|
||||
|
||||
if (error_mode == ErrorMode::None || error_mode == ErrorMode::Faster) {
|
||||
Vector4Int axis = color_vectors[3] - color_vectors[0];
|
||||
std::array<int, 4> dots;
|
||||
for (int i = 0; i < 4; i++) { dots[i] = axis.Dot(color_vectors[i]); }
|
||||
int t0 = dots[0] + dots[1], t1 = dots[1] + dots[2], t2 = dots[2] + dots[3];
|
||||
axis *= 2;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
Vector4Int pixel_vector = Vector4Int::FromColorRGB(pixels.Get(i));
|
||||
int dot = axis.Dot(pixel_vector);
|
||||
uint8_t level = (dot <= t0) + (dot < t1) + (dot < t2);
|
||||
uint8_t selector = 3 - level;
|
||||
assert(level < 4);
|
||||
assert(selector < 4);
|
||||
|
||||
if (error_mode == ErrorMode::Faster) {
|
||||
// llvm is just going to unswitch this anyways so its not an issue
|
||||
auto diff = pixel_vector - color_vectors[selector];
|
||||
total_error += diff.SqrMag();
|
||||
if (i % 4 != 0 && total_error >= result.error) break; // check only once per row if we're generating too much error
|
||||
}
|
||||
|
||||
result.selectors[i] = selector;
|
||||
}
|
||||
} else if (error_mode == ErrorMode::Check2) {
|
||||
Vector4Int axis = color_vectors[3] - color_vectors[0];
|
||||
const float f = 4.0f / ((float)axis.SqrMag() + .00000125f);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
Vector4Int pixel_vector = Vector4Int::FromColorRGB(pixels.Get(i));
|
||||
auto diff = pixel_vector - color_vectors[0];
|
||||
float sel_f = (float)diff.Dot(axis) * f + 0.5f;
|
||||
uint8_t sel = (uint8_t)clampi((int)sel_f, 1, 3);
|
||||
|
||||
unsigned err0 = (color_vectors[sel - 1] - pixel_vector).SqrMag();
|
||||
unsigned err1 = (color_vectors[sel] - pixel_vector).SqrMag();
|
||||
|
||||
uint8_t best_sel = sel;
|
||||
unsigned best_err = err1;
|
||||
if (err0 == err1) {
|
||||
// prefer non-interpolation
|
||||
if ((best_sel) == 1) best_sel = 0;
|
||||
} else if (err0 < best_err) {
|
||||
best_sel = sel - 1;
|
||||
best_err = err0;
|
||||
}
|
||||
|
||||
total_error += best_err;
|
||||
|
||||
if (total_error >= result.error) break;
|
||||
|
||||
result.selectors[i] = best_sel;
|
||||
}
|
||||
} else if (error_mode == ErrorMode::Full) {
|
||||
unsigned max_sel = (bool)(M == ColorMode::ThreeColor) ? 3 : 4;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
unsigned best_error = UINT_MAX;
|
||||
uint8_t best_sel = 0;
|
||||
Vector4Int pixel_vector = Vector4Int::FromColorRGB(pixels.Get(i));
|
||||
|
||||
// exhasustively check every pixel's distance from each color, and calculate the error
|
||||
for (uint8_t j = 0; j < max_sel; j++) {
|
||||
auto diff = color_vectors[j] - pixel_vector;
|
||||
unsigned err = diff.SqrMag();
|
||||
if (err < best_error || ((err == best_error) && (j == 3))) {
|
||||
best_error = err;
|
||||
best_sel = j;
|
||||
}
|
||||
}
|
||||
|
||||
total_error += best_error;
|
||||
if (total_error >= result.error) { break; }
|
||||
|
||||
assert(best_sel < max_sel);
|
||||
result.selectors[i] = best_sel;
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
result.error = total_error;
|
||||
result.color_mode = M;
|
||||
}
|
||||
|
||||
template <BC1Encoder::ColorMode M> bool BC1Encoder::RefineEndpointsLS(EncodeResults &result, const CBlock &pixels, BlockMetrics metrics) const {
|
||||
const int color_count = (unsigned)M & 0x0F;
|
||||
static_assert(color_count == 3 || color_count == 4);
|
||||
assert(result.color_mode != ColorMode::Incomplete);
|
||||
|
||||
int denominator = color_count - 1;
|
||||
|
||||
Vector4 q00 = {0, 0, 0};
|
||||
Vector4 matrix = Vector4(0);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
const Color color = pixels.Get(i);
|
||||
const uint8_t sel = result.selectors[i];
|
||||
|
||||
if ((bool)(M & ColorMode::ThreeColorBlack) && color.IsBlack()) continue;
|
||||
if ((bool)(M & ColorMode::ThreeColor) && sel == 3U) continue; // NOTE: selectors for 3-color are in linear order here, but not in original
|
||||
assert(sel < color_count);
|
||||
|
||||
const Vector4Int color_vector = Vector4Int::FromColorRGB(color);
|
||||
q00 += color_vector * sel;
|
||||
|
||||
matrix += OrderTable<color_count>::Weights[sel];
|
||||
}
|
||||
|
||||
// invert matrix
|
||||
float det = matrix.Determinant2x2(); // z00 * z11 - z01 * z10;
|
||||
if (fabs(det) < 1e-8f) {
|
||||
result.color_mode = ColorMode::Incomplete;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::swap(matrix[0], matrix[3]);
|
||||
matrix *= Vector4(1, -1, -1, 1);
|
||||
matrix *= ((float)denominator / 255.0f) / det;
|
||||
|
||||
Vector4 q10 = (metrics.sums * denominator) - q00;
|
||||
|
||||
Vector4 low = (matrix[0] * q00) + (matrix[1] * q10);
|
||||
Vector4 high = (matrix[2] * q00) + (matrix[3] * q10);
|
||||
|
||||
result.color_mode = M;
|
||||
result.low = Color::PreciseRound565(low);
|
||||
result.high = Color::PreciseRound565(high);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <BC1Encoder::ColorMode M> void BC1Encoder::RefineEndpointsLS(EncodeResults &result, std::array<Vector4, 17> &sums, Vector4 &matrix, Hash hash) const {
|
||||
const int color_count = (unsigned)M & 0x0F;
|
||||
static_assert(color_count == 3 || color_count == 4);
|
||||
assert(result.color_mode != ColorMode::Incomplete);
|
||||
|
||||
int denominator = color_count - 1;
|
||||
|
||||
Vector4 q10 = {0, 0, 0};
|
||||
unsigned level = 0;
|
||||
Histogram<color_count> h = OrderTable<color_count>::Orders[hash];
|
||||
for (unsigned i = 0; i < (color_count - 1); i++) {
|
||||
level += h[i];
|
||||
q10 += sums[level];
|
||||
}
|
||||
|
||||
Vector4 q00 = (sums[16] * (float)denominator) - q10;
|
||||
|
||||
Vector4 low = (matrix[0] * q00) + (matrix[1] * q10);
|
||||
Vector4 high = (matrix[2] * q00) + (matrix[3] * q10);
|
||||
|
||||
result.color_mode = M;
|
||||
result.low = Color::PreciseRound565(low);
|
||||
result.high = Color::PreciseRound565(high);
|
||||
}
|
||||
|
||||
template <BC1Encoder::ColorMode M>
|
||||
void BC1Encoder::RefineBlockLS(EncodeResults &result, const CBlock &pixels, const BlockMetrics &metrics, ErrorMode error_mode, unsigned passes) const {
|
||||
assert(error_mode != ErrorMode::None || passes == 1);
|
||||
|
||||
for (unsigned pass = 0; pass < passes; pass++) {
|
||||
EncodeResults trial_result = result;
|
||||
Vector4 low, high;
|
||||
|
||||
bool multicolor = RefineEndpointsLS<ColorMode::FourColor>(trial_result, pixels, metrics);
|
||||
if (!multicolor) {
|
||||
FindEndpointsSingleColor(trial_result, pixels, metrics.avg, (M != ColorMode::FourColor));
|
||||
} else {
|
||||
FindSelectors<M>(trial_result, pixels, error_mode);
|
||||
}
|
||||
|
||||
if (trial_result.low == result.low && trial_result.high == result.high) break;
|
||||
|
||||
if (error_mode == ErrorMode::None || trial_result.error < result.error) {
|
||||
result = trial_result;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <BC1Encoder::ColorMode M>
|
||||
void BC1Encoder::RefineBlockCF(EncodeResults &result, const CBlock &pixels, const BlockMetrics &metrics, ErrorMode error_mode, unsigned orderings) const {
|
||||
const int color_count = (unsigned)M & 0x0F;
|
||||
static_assert(color_count == 3 || color_count == 4);
|
||||
assert(result.color_mode != ColorMode::Incomplete);
|
||||
|
||||
using OrderTable = OrderTable<color_count>;
|
||||
using Hist = Histogram<color_count>;
|
||||
|
||||
EncodeResults orig = result;
|
||||
Hist h = Hist(orig.selectors);
|
||||
|
||||
Hash start_hash = OrderTable::GetHash(h);
|
||||
|
||||
Vector4 axis = orig.high.ScaleFrom565() - orig.low.ScaleFrom565();
|
||||
std::array<Vector4, 16> color_vectors;
|
||||
std::array<uint32_t, 16> dots;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
color_vectors[(unsigned)i] = Vector4::FromColorRGB(pixels.Get(i));
|
||||
int dot = 0x1000000 + (int)color_vectors[(unsigned)i].Dot(axis);
|
||||
assert(dot >= 0);
|
||||
dots[(unsigned)i] = (uint32_t)(dot << 4) | i;
|
||||
}
|
||||
|
||||
std::sort(dots.begin(), dots.end());
|
||||
|
||||
// we now have a list of indices and their dot products along the primary axis
|
||||
std::array<Vector4, 17> sums;
|
||||
for (unsigned i = 0; i < 16; i++) {
|
||||
const unsigned p = dots[i] & 0xF;
|
||||
sums[i + 1] = sums[i] + color_vectors[p];
|
||||
}
|
||||
|
||||
const unsigned q_total = exhaustive ? OrderTable::OrderCount : orderings;
|
||||
for (Hash q = 0; q < q_total; q++) {
|
||||
Hash trial_hash = exhaustive ? q : OrderTable::BestOrders[start_hash][q];
|
||||
Vector4 trial_matrix = OrderTable::GetFactors(trial_hash);
|
||||
|
||||
EncodeResults trial_result = orig;
|
||||
Vector4 low, high;
|
||||
if (OrderTable::IsSingleColor(trial_hash)) {
|
||||
FindEndpointsSingleColor(trial_result, pixels, metrics.avg, (color_count == 3));
|
||||
} else {
|
||||
RefineEndpointsLS<M>(trial_result, sums, trial_matrix, trial_hash);
|
||||
FindSelectors<M>(trial_result, pixels, error_mode);
|
||||
}
|
||||
|
||||
if (trial_result.error < result.error) { result = trial_result; }
|
||||
if (trial_result.error == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
void BC1Encoder::EndpointSearch(EncodeResults &result, const CBlock &pixels) const {
|
||||
if (result.solid) return;
|
||||
|
||||
static const std::array<Vector4Int, 16> Voxels = {{
|
||||
{1, 0, 0, 3}, // 0
|
||||
{0, 1, 0, 4}, // 1
|
||||
{0, 0, 1, 5}, // 2
|
||||
{-1, 0, 0, 0}, // 3
|
||||
{0, -1, 0, 1}, // 4
|
||||
{0, 0, -1, 2}, // 5
|
||||
{1, 1, 0, 9}, // 6
|
||||
{1, 0, 1, 10}, // 7
|
||||
{0, 1, 1, 11}, // 8
|
||||
{-1, -1, 0, 6}, // 9
|
||||
{-1, 0, -1, 7}, // 10
|
||||
{0, -1, -1, 8}, // 11
|
||||
{-1, 1, 0, 13}, // 12
|
||||
{1, -1, 0, 12}, // 13
|
||||
{0, -1, 1, 15}, // 14
|
||||
{0, 1, -1, 14}, // 15
|
||||
}};
|
||||
|
||||
unsigned prev_improvement_index = 0;
|
||||
int forbidden_direction = -1;
|
||||
|
||||
for (unsigned i = 0; i < _search_rounds; i++) {
|
||||
const unsigned voxel_index = (unsigned)(i & 15);
|
||||
assert((unsigned)Voxels[(unsigned)Voxels[voxel_index][3]][3] == voxel_index); // make sure voxels are symmetrical
|
||||
|
||||
if ((int)(i & 31) == forbidden_direction) continue;
|
||||
|
||||
Vector4Int delta = Voxels[voxel_index];
|
||||
EncodeResults trial_result = result;
|
||||
|
||||
if (i & 16) {
|
||||
trial_result.low.r = (uint8_t)clamp(trial_result.low.r + delta[0], 0, 31);
|
||||
trial_result.low.g = (uint8_t)clamp(trial_result.low.g + delta[1], 0, 63);
|
||||
trial_result.low.b = (uint8_t)clamp(trial_result.low.b + delta[2], 0, 31);
|
||||
} else {
|
||||
trial_result.high.r = (uint8_t)clamp(trial_result.high.r + delta[0], 0, 31);
|
||||
trial_result.high.g = (uint8_t)clamp(trial_result.high.g + delta[1], 0, 63);
|
||||
trial_result.high.b = (uint8_t)clamp(trial_result.high.b + delta[2], 0, 31);
|
||||
}
|
||||
|
||||
switch (result.color_mode) {
|
||||
default:
|
||||
case ColorMode::FourColor:
|
||||
FindSelectors<ColorMode::FourColor>(trial_result, pixels, _error_mode);
|
||||
break;
|
||||
case ColorMode::ThreeColor:
|
||||
FindSelectors<ColorMode::ThreeColor>(trial_result, pixels, ErrorMode::Full);
|
||||
break;
|
||||
case ColorMode::ThreeColorBlack:
|
||||
FindSelectors<ColorMode::ThreeColorBlack>(trial_result, pixels, ErrorMode::Full);
|
||||
break;
|
||||
}
|
||||
|
||||
if (trial_result.error < result.error) {
|
||||
result = trial_result;
|
||||
|
||||
forbidden_direction = delta[3] | (int)(i & 16);
|
||||
prev_improvement_index = i;
|
||||
}
|
||||
|
||||
if (i - prev_improvement_index > 32) break;
|
||||
}
|
||||
}
|
||||
} // namespace quicktex::s3tc
|
193
quicktex/s3tc/bc1/BC1Encoder.h
Normal file
193
quicktex/s3tc/bc1/BC1Encoder.h
Normal file
@ -0,0 +1,193 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
#include "../../Color.h"
|
||||
#include "../../ColorBlock.h"
|
||||
#include "../../Encoder.h"
|
||||
#include "../../Texture.h"
|
||||
#include "../interpolator/Interpolator.h"
|
||||
#include "BC1Block.h"
|
||||
#include "SingleColorTable.h"
|
||||
|
||||
namespace quicktex {
|
||||
class Vector4;
|
||||
}
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
class BC1Encoder final : public BlockEncoder<BlockTexture<BC1Block>> {
|
||||
public:
|
||||
using InterpolatorPtr = std::shared_ptr<Interpolator>;
|
||||
using OrderingPair = std::tuple<unsigned, unsigned>;
|
||||
using CBlock = ColorBlock<4, 4>;
|
||||
|
||||
static constexpr unsigned min_power_iterations = 4;
|
||||
static constexpr unsigned max_power_iterations = 10;
|
||||
|
||||
enum class ColorMode {
|
||||
// An incomplete block with invalid selectors or endpoints
|
||||
Incomplete = 0x00,
|
||||
|
||||
// A block where color0 <= color1
|
||||
ThreeColor = 0x03,
|
||||
|
||||
// A block where color0 > color1
|
||||
FourColor = 0x04,
|
||||
|
||||
// A 3 color block with black pixels (selector 3)
|
||||
UseBlack = 0x10,
|
||||
ThreeColorBlack = ThreeColor | UseBlack,
|
||||
};
|
||||
|
||||
enum class ErrorMode {
|
||||
// Perform no error checking at all.
|
||||
None,
|
||||
|
||||
// Use a slightly lower quality, but ~30% faster MSE evaluation function for 4-color blocks.
|
||||
Faster,
|
||||
|
||||
// Default error mode.
|
||||
Check2,
|
||||
|
||||
// Examine all colors to compute selectors/MSE (slower than default).
|
||||
Full
|
||||
};
|
||||
|
||||
enum class EndpointMode {
|
||||
// Use 2D least squares+inset+optimal rounding (the method used in Humus's GPU texture encoding demo), instead of PCA.
|
||||
// Around 18% faster, very slightly lower average quality to better (depends on the content).
|
||||
LeastSquares,
|
||||
|
||||
// BoundingBox enables a fast all-integer PCA approximation on 4-color blocks.
|
||||
// At level 0 options (no other flags), this is ~15% faster, and higher *average* quality.
|
||||
BoundingBox,
|
||||
|
||||
// Same as BoundingBox, but implemented using integer math (faster, slightly less quality)
|
||||
BoundingBoxInt,
|
||||
|
||||
// Full PCA implementation
|
||||
PCA
|
||||
};
|
||||
|
||||
bool exhaustive;
|
||||
bool two_ls_passes;
|
||||
bool two_ep_passes;
|
||||
bool two_cf_passes;
|
||||
|
||||
BC1Encoder(unsigned level, ColorMode color_mode, InterpolatorPtr interpolator);
|
||||
|
||||
BC1Encoder(unsigned int level = 5, ColorMode color_mode = ColorMode::FourColor) : BC1Encoder(level, color_mode, std::make_shared<Interpolator>()) {}
|
||||
|
||||
// Getters and Setters
|
||||
void SetLevel(unsigned level);
|
||||
|
||||
ErrorMode GetErrorMode() const { return _error_mode; }
|
||||
void SetErrorMode(ErrorMode error_mode) { _error_mode = error_mode; };
|
||||
|
||||
EndpointMode GetEndpointMode() const { return _endpoint_mode; }
|
||||
void SetEndpointMode(EndpointMode endpoint_mode) { _endpoint_mode = endpoint_mode; }
|
||||
|
||||
InterpolatorPtr GetInterpolator() const { return _interpolator; }
|
||||
ColorMode GetColorMode() const { return _color_mode; }
|
||||
|
||||
unsigned int GetSearchRounds() const { return _search_rounds; }
|
||||
void SetSearchRounds(unsigned search_rounds) { _search_rounds = search_rounds; }
|
||||
|
||||
unsigned GetOrderings4() const { return _orderings4; }
|
||||
unsigned GetOrderings3() const { return _orderings3; }
|
||||
|
||||
void SetOrderings4(unsigned orderings4);
|
||||
void SetOrderings3(unsigned orderings3);
|
||||
|
||||
OrderingPair GetOrderings() const { return OrderingPair(_orderings4, _orderings3); }
|
||||
void SetOrderings(OrderingPair orderings);
|
||||
|
||||
unsigned GetPowerIterations() const { return _power_iterations; }
|
||||
void SetPowerIterations(unsigned power_iters);
|
||||
|
||||
// Public Methods
|
||||
BC1Block EncodeBlock(const CBlock &pixels) const override;
|
||||
|
||||
virtual size_t MTThreshold() const override { return 16; }
|
||||
|
||||
private:
|
||||
using Hash = uint16_t;
|
||||
using BlockMetrics = CBlock::Metrics;
|
||||
|
||||
// Unpacked BC1 block with metadata
|
||||
struct EncodeResults {
|
||||
Color low;
|
||||
Color high;
|
||||
std::array<uint8_t, 16> selectors = {0};
|
||||
ColorMode color_mode = ColorMode::Incomplete;
|
||||
bool solid = false;
|
||||
unsigned error = UINT_MAX;
|
||||
};
|
||||
|
||||
const InterpolatorPtr _interpolator;
|
||||
const ColorMode _color_mode;
|
||||
|
||||
// match tables used for single-color blocks
|
||||
// Each entry includes a high and low pair that best reproduces the 8-bit index as well as possible,
|
||||
// with an included error value
|
||||
// these depend on the interpolator
|
||||
MatchListPtr _single_match5 = SingleColorTable<5, 4>(_interpolator);
|
||||
MatchListPtr _single_match6 = SingleColorTable<6, 4>(_interpolator);
|
||||
MatchListPtr _single_match5_half = SingleColorTable<5, 3>(_interpolator);
|
||||
MatchListPtr _single_match6_half = SingleColorTable<6, 3>(_interpolator);
|
||||
|
||||
ErrorMode _error_mode;
|
||||
EndpointMode _endpoint_mode;
|
||||
|
||||
unsigned _power_iterations;
|
||||
unsigned _search_rounds;
|
||||
unsigned _orderings4;
|
||||
unsigned _orderings3;
|
||||
|
||||
BC1Block WriteBlockSolid(Color color) const;
|
||||
BC1Block WriteBlock(EncodeResults &result) const;
|
||||
|
||||
void FindEndpoints(EncodeResults &result, const CBlock &pixels, const BlockMetrics &metrics, EndpointMode endpoint_mode, bool ignore_black = false) const;
|
||||
void FindEndpointsSingleColor(EncodeResults &result, Color color, bool is_3color = false) const;
|
||||
void FindEndpointsSingleColor(EncodeResults &result, const CBlock &pixels, Color color, bool is_3color) const;
|
||||
|
||||
template <ColorMode M> void FindSelectors(EncodeResults &result, const CBlock &pixels, ErrorMode error_mode) const;
|
||||
|
||||
template <ColorMode M> bool RefineEndpointsLS(EncodeResults &result, const CBlock &pixels, BlockMetrics metrics) const;
|
||||
|
||||
template <ColorMode M> void RefineEndpointsLS(EncodeResults &result, std::array<Vector4, 17> &sums, Vector4 &matrix, Hash hash) const;
|
||||
|
||||
template <ColorMode M>
|
||||
void RefineBlockLS(EncodeResults &result, const CBlock &pixels, const BlockMetrics &metrics, ErrorMode error_mode, unsigned passes) const;
|
||||
|
||||
template <ColorMode M>
|
||||
void RefineBlockCF(EncodeResults &result, const CBlock &pixels, const BlockMetrics &metrics, ErrorMode error_mode, unsigned orderings) const;
|
||||
|
||||
void EndpointSearch(EncodeResults &result, const CBlock &pixels) const;
|
||||
};
|
||||
} // namespace quicktex::s3tc
|
85
quicktex/s3tc/bc1/Histogram.h
Normal file
85
quicktex/s3tc/bc1/Histogram.h
Normal file
@ -0,0 +1,85 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <mutex>
|
||||
#include <numeric>
|
||||
|
||||
#include "../../Vector4.h"
|
||||
#include "../../util.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
template <size_t N> class Histogram {
|
||||
public:
|
||||
using Hash = uint16_t;
|
||||
|
||||
Histogram() { _bins.fill(0); }
|
||||
|
||||
Histogram(std::array<uint8_t, 16> sels) {
|
||||
_bins.fill(0);
|
||||
for (unsigned i = 0; i < 16; i++) {
|
||||
assert(sels[i] < N);
|
||||
_bins[sels[i]]++;
|
||||
}
|
||||
}
|
||||
|
||||
Histogram(std::initializer_list<uint8_t> init) {
|
||||
assert(init.size() <= N);
|
||||
_bins.fill(0);
|
||||
auto item = init.begin();
|
||||
for (unsigned i = 0; i < init.size(); i++) {
|
||||
_bins[i] = *item;
|
||||
item++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t operator[](size_t index) const {
|
||||
assert(index < N);
|
||||
return _bins[index];
|
||||
}
|
||||
uint8_t &operator[](size_t index) {
|
||||
assert(index < N);
|
||||
return _bins[index];
|
||||
}
|
||||
|
||||
bool Any16() {
|
||||
return std::any_of(_bins.begin(), _bins.end(), [](int i) { return i == 16; });
|
||||
}
|
||||
|
||||
unsigned GetPacked() const {
|
||||
Hash packed = 0;
|
||||
|
||||
for (unsigned i = 0; i < (N-1); i++) {
|
||||
assert(_bins[i] <= (1U << 4) - 1U);
|
||||
packed |= static_cast<uint16_t>(_bins[i]) << (i * 4U);
|
||||
}
|
||||
|
||||
return packed;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<uint8_t, N> _bins;
|
||||
};
|
||||
} // namespace quicktex::s3tc
|
327
quicktex/s3tc/bc1/OrderTable.cpp
Normal file
327
quicktex/s3tc/bc1/OrderTable.cpp
Normal file
@ -0,0 +1,327 @@
|
||||
/* Quicktex 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 "OrderTable.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "../../Vector4.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
using Hash = uint16_t;
|
||||
|
||||
template <> std::atomic<bool> OrderTable<3>::generated = false;
|
||||
template <> std::atomic<bool> OrderTable<4>::generated = false;
|
||||
|
||||
template <> std::mutex OrderTable<3>::table_mutex = std::mutex();
|
||||
template <> std::mutex OrderTable<4>::table_mutex = std::mutex();
|
||||
|
||||
template <> std::array<OrderTable<3>::Hash, OrderTable<3>::HashCount> *OrderTable<3>::hashes = nullptr;
|
||||
template <> std::array<OrderTable<4>::Hash, OrderTable<4>::HashCount> *OrderTable<4>::hashes = nullptr;
|
||||
|
||||
template <> std::array<Vector4, OrderTable<3>::OrderCount> *OrderTable<3>::factors = nullptr;
|
||||
template <> std::array<Vector4, OrderTable<4>::OrderCount> *OrderTable<4>::factors = nullptr;
|
||||
|
||||
template <> const std::array<Vector4, 3> OrderTable<3>::Weights = {{{0, 0, 0, 4}, {1, 1, 1, 1}, {4, 0, 0, 0}}};
|
||||
template <> const std::array<Vector4, 4> OrderTable<4>::Weights = {{{0, 0, 0, 9}, {1, 2, 2, 4}, {4, 2, 2, 1}, {9, 0, 0, 0}}};
|
||||
|
||||
template <> const std::array<Hash, 3> OrderTable<3>::SingleColorHashes = {12, 15, 89};
|
||||
template <> const std::array<Hash, 4> OrderTable<4>::SingleColorHashes = {15, 700, 753, 515};
|
||||
|
||||
// region OrderTable3
|
||||
template <>
|
||||
const OrderTable<3>::OrderArray OrderTable<3>::Orders = {
|
||||
{{6, 0, 10}, {3, 6, 7}, {3, 0, 13}, {13, 3, 0}, {12, 4, 0}, {9, 1, 6}, {2, 13, 1}, {4, 7, 5}, {7, 5, 4}, {9, 6, 1}, {7, 4, 5}, {8, 6, 2}, {16, 0, 0},
|
||||
{10, 6, 0}, {2, 7, 7}, {0, 0, 16}, {0, 3, 13}, {1, 15, 0}, {0, 2, 14}, {1, 4, 11}, {15, 1, 0}, {1, 12, 3}, {9, 2, 5}, {14, 1, 1}, {8, 2, 6}, {3, 3, 10},
|
||||
{4, 2, 10}, {14, 0, 2}, {0, 14, 2}, {1, 7, 8}, {6, 6, 4}, {11, 5, 0}, {6, 4, 6}, {11, 3, 2}, {4, 3, 9}, {7, 1, 8}, {10, 4, 2}, {12, 1, 3}, {11, 0, 5},
|
||||
{9, 3, 4}, {1, 0, 15}, {9, 0, 7}, {2, 6, 8}, {12, 2, 2}, {6, 2, 8}, {6, 8, 2}, {15, 0, 1}, {4, 8, 4}, {0, 4, 12}, {8, 5, 3}, {5, 9, 2}, {11, 2, 3},
|
||||
{12, 3, 1}, {6, 3, 7}, {1, 1, 14}, {2, 9, 5}, {1, 8, 7}, {4, 10, 2}, {7, 7, 2}, {13, 1, 2}, {0, 15, 1}, {3, 2, 11}, {7, 0, 9}, {4, 4, 8}, {3, 8, 5},
|
||||
{0, 5, 11}, {13, 2, 1}, {1, 10, 5}, {4, 11, 1}, {3, 10, 3}, {5, 10, 1}, {10, 2, 4}, {0, 6, 10}, {14, 2, 0}, {11, 4, 1}, {3, 12, 1}, {1, 13, 2}, {1, 5, 10},
|
||||
{5, 11, 0}, {12, 0, 4}, {8, 1, 7}, {6, 10, 0}, {3, 13, 0}, {7, 2, 7}, {0, 7, 9}, {5, 8, 3}, {0, 12, 4}, {11, 1, 4}, {13, 0, 3}, {0, 16, 0}, {5, 7, 4},
|
||||
{10, 3, 3}, {10, 0, 6}, {0, 13, 3}, {4, 6, 6}, {2, 8, 6}, {2, 5, 9}, {7, 8, 1}, {2, 1, 13}, {2, 0, 14}, {7, 3, 6}, {5, 1, 10}, {3, 11, 2}, {5, 4, 7},
|
||||
{8, 3, 5}, {10, 5, 1}, {6, 9, 1}, {1, 3, 12}, {4, 5, 7}, {2, 2, 12}, {4, 1, 11}, {0, 8, 8}, {4, 12, 0}, {6, 5, 5}, {8, 7, 1}, {5, 5, 6}, {3, 7, 6},
|
||||
{7, 9, 0}, {4, 9, 3}, {0, 10, 6}, {8, 0, 8}, {5, 3, 8}, {10, 1, 5}, {6, 1, 9}, {7, 6, 3}, {9, 5, 2}, {0, 1, 15}, {9, 7, 0}, {2, 14, 0}, {3, 4, 9},
|
||||
{8, 4, 4}, {9, 4, 3}, {0, 9, 7}, {1, 9, 6}, {3, 9, 4}, {5, 2, 9}, {2, 3, 11}, {5, 6, 5}, {1, 14, 1}, {6, 7, 3}, {2, 4, 10}, {2, 12, 2}, {8, 8, 0},
|
||||
{2, 10, 4}, {4, 0, 12}, {0, 11, 5}, {2, 11, 3}, {1, 11, 4}, {3, 5, 8}, {5, 0, 11}, {3, 1, 12}, {1, 2, 13}, {1, 6, 9}}};
|
||||
// endregion
|
||||
|
||||
// region OrderTable4
|
||||
template <>
|
||||
const OrderTable<4>::OrderArray OrderTable<4>::Orders = {
|
||||
{{0, 8, 2, 6}, {4, 3, 9, 0}, {4, 8, 1, 3}, {12, 0, 3, 1}, {11, 3, 2, 0}, {6, 4, 6, 0}, {7, 5, 0, 4}, {6, 0, 8, 2}, {1, 0, 0, 15}, {3, 0, 8, 5},
|
||||
{1, 1, 13, 1}, {13, 1, 2, 0}, {0, 14, 1, 1}, {0, 15, 1, 0}, {0, 13, 0, 3}, {16, 0, 0, 0}, {4, 3, 4, 5}, {8, 6, 0, 2}, {0, 10, 0, 6}, {10, 0, 4, 2},
|
||||
{7, 2, 1, 6}, {4, 7, 5, 0}, {1, 4, 7, 4}, {0, 14, 2, 0}, {2, 7, 2, 5}, {9, 0, 5, 2}, {9, 2, 2, 3}, {10, 0, 5, 1}, {2, 3, 7, 4}, {4, 9, 0, 3},
|
||||
{1, 5, 0, 10}, {1, 1, 6, 8}, {6, 6, 4, 0}, {11, 5, 0, 0}, {11, 2, 0, 3}, {4, 0, 10, 2}, {2, 3, 10, 1}, {1, 13, 1, 1}, {0, 14, 0, 2}, {2, 3, 3, 8},
|
||||
{12, 3, 1, 0}, {14, 0, 0, 2}, {9, 1, 3, 3}, {6, 4, 0, 6}, {1, 1, 5, 9}, {5, 9, 0, 2}, {2, 10, 1, 3}, {12, 0, 0, 4}, {4, 6, 6, 0}, {0, 6, 4, 6},
|
||||
{3, 7, 4, 2}, {0, 13, 3, 0}, {3, 10, 0, 3}, {10, 2, 1, 3}, {1, 12, 1, 2}, {2, 0, 13, 1}, {11, 0, 5, 0}, {12, 1, 3, 0}, {6, 4, 5, 1}, {10, 4, 2, 0},
|
||||
{3, 6, 1, 6}, {7, 3, 6, 0}, {10, 4, 0, 2}, {10, 0, 2, 4}, {0, 5, 9, 2}, {0, 9, 3, 4}, {6, 4, 2, 4}, {3, 4, 7, 2}, {3, 3, 5, 5}, {4, 2, 9, 1},
|
||||
{6, 2, 8, 0}, {3, 5, 3, 5}, {4, 10, 1, 1}, {10, 1, 3, 2}, {5, 7, 0, 4}, {5, 3, 7, 1}, {6, 8, 1, 1}, {8, 8, 0, 0}, {11, 1, 0, 4}, {14, 1, 0, 1},
|
||||
{9, 3, 2, 2}, {8, 2, 1, 5}, {0, 0, 2, 14}, {3, 3, 9, 1}, {10, 1, 5, 0}, {8, 3, 1, 4}, {1, 5, 8, 2}, {6, 1, 9, 0}, {3, 2, 1, 10}, {3, 11, 1, 1},
|
||||
{7, 6, 3, 0}, {9, 0, 3, 4}, {5, 2, 5, 4}, {0, 2, 3, 11}, {15, 0, 0, 1}, {0, 6, 6, 4}, {3, 4, 9, 0}, {4, 7, 0, 5}, {0, 4, 4, 8}, {0, 13, 2, 1},
|
||||
{2, 4, 1, 9}, {3, 2, 5, 6}, {10, 6, 0, 0}, {3, 5, 6, 2}, {8, 0, 4, 4}, {1, 3, 6, 6}, {7, 7, 0, 2}, {6, 1, 4, 5}, {0, 11, 1, 4}, {2, 2, 8, 4},
|
||||
{0, 1, 2, 13}, {15, 0, 1, 0}, {7, 2, 6, 1}, {8, 1, 7, 0}, {1, 8, 4, 3}, {2, 13, 1, 0}, {1, 0, 7, 8}, {14, 2, 0, 0}, {1, 8, 1, 6}, {9, 3, 3, 1},
|
||||
{0, 0, 7, 9}, {4, 4, 1, 7}, {9, 0, 6, 1}, {10, 2, 4, 0}, {1, 7, 3, 5}, {0, 3, 8, 5}, {5, 2, 4, 5}, {1, 2, 5, 8}, {0, 8, 7, 1}, {10, 3, 2, 1},
|
||||
{12, 0, 4, 0}, {2, 1, 4, 9}, {5, 2, 2, 7}, {1, 9, 3, 3}, {15, 1, 0, 0}, {6, 3, 4, 3}, {9, 5, 0, 2}, {1, 6, 9, 0}, {6, 6, 0, 4}, {13, 2, 1, 0},
|
||||
{5, 1, 8, 2}, {0, 5, 11, 0}, {7, 1, 0, 8}, {1, 2, 12, 1}, {0, 3, 3, 10}, {7, 4, 2, 3}, {5, 1, 4, 6}, {7, 0, 3, 6}, {3, 12, 0, 1}, {3, 4, 5, 4},
|
||||
{1, 10, 0, 5}, {7, 4, 3, 2}, {10, 5, 0, 1}, {13, 3, 0, 0}, {2, 5, 4, 5}, {3, 10, 1, 2}, {5, 1, 2, 8}, {14, 0, 1, 1}, {1, 5, 4, 6}, {1, 4, 5, 6},
|
||||
{2, 3, 11, 0}, {11, 0, 4, 1}, {11, 2, 2, 1}, {5, 3, 8, 0}, {1, 3, 10, 2}, {0, 1, 13, 2}, {3, 1, 4, 8}, {4, 2, 4, 6}, {1, 5, 6, 4}, {2, 1, 11, 2},
|
||||
{1, 2, 9, 4}, {4, 7, 3, 2}, {6, 2, 5, 3}, {7, 2, 2, 5}, {8, 1, 4, 3}, {3, 2, 8, 3}, {12, 1, 0, 3}, {7, 8, 1, 0}, {7, 0, 2, 7}, {5, 10, 0, 1},
|
||||
{0, 2, 14, 0}, {2, 9, 3, 2}, {7, 0, 0, 9}, {11, 1, 4, 0}, {10, 4, 1, 1}, {2, 2, 9, 3}, {5, 7, 2, 2}, {1, 3, 1, 11}, {13, 2, 0, 1}, {4, 2, 8, 2},
|
||||
{2, 3, 1, 10}, {4, 2, 5, 5}, {7, 0, 7, 2}, {10, 0, 0, 6}, {0, 8, 5, 3}, {4, 4, 0, 8}, {12, 4, 0, 0}, {0, 1, 14, 1}, {8, 0, 1, 7}, {5, 1, 5, 5},
|
||||
{11, 0, 3, 2}, {0, 4, 1, 11}, {0, 8, 8, 0}, {0, 2, 5, 9}, {7, 3, 2, 4}, {7, 8, 0, 1}, {1, 0, 3, 12}, {7, 4, 5, 0}, {1, 6, 7, 2}, {7, 6, 1, 2},
|
||||
{9, 6, 1, 0}, {12, 2, 0, 2}, {4, 1, 6, 5}, {4, 0, 1, 11}, {8, 4, 4, 0}, {13, 0, 1, 2}, {8, 6, 2, 0}, {4, 12, 0, 0}, {2, 7, 5, 2}, {2, 0, 5, 9},
|
||||
{5, 4, 5, 2}, {3, 8, 5, 0}, {7, 3, 3, 3}, {4, 4, 8, 0}, {2, 1, 3, 10}, {5, 0, 1, 10}, {6, 4, 3, 3}, {4, 9, 1, 2}, {1, 4, 0, 11}, {11, 3, 1, 1},
|
||||
{4, 0, 12, 0}, {13, 0, 0, 3}, {6, 1, 6, 3}, {9, 0, 4, 3}, {8, 0, 0, 8}, {8, 4, 0, 4}, {0, 12, 1, 3}, {0, 4, 10, 2}, {3, 4, 8, 1}, {1, 3, 8, 4},
|
||||
{9, 2, 5, 0}, {5, 7, 4, 0}, {1, 0, 11, 4}, {4, 10, 0, 2}, {1, 3, 12, 0}, {6, 9, 0, 1}, {5, 0, 9, 2}, {5, 9, 2, 0}, {13, 1, 0, 2}, {9, 3, 4, 0},
|
||||
{9, 4, 0, 3}, {3, 1, 12, 0}, {2, 4, 3, 7}, {1, 2, 13, 0}, {2, 2, 4, 8}, {6, 8, 0, 2}, {9, 2, 1, 4}, {9, 5, 1, 1}, {2, 0, 4, 10}, {5, 4, 0, 7},
|
||||
{0, 0, 6, 10}, {1, 2, 0, 13}, {4, 7, 2, 3}, {6, 5, 5, 0}, {3, 3, 1, 9}, {1, 6, 1, 8}, {12, 2, 1, 1}, {4, 4, 5, 3}, {1, 0, 6, 9}, {0, 6, 10, 0},
|
||||
{4, 8, 3, 1}, {4, 3, 2, 7}, {2, 1, 7, 6}, {1, 9, 1, 5}, {3, 1, 3, 9}, {8, 7, 1, 0}, {1, 2, 3, 10}, {14, 1, 1, 0}, {5, 4, 4, 3}, {3, 7, 0, 6},
|
||||
{7, 4, 1, 4}, {3, 7, 5, 1}, {1, 1, 0, 14}, {0, 10, 3, 3}, {0, 4, 3, 9}, {1, 7, 7, 1}, {2, 0, 10, 4}, {5, 8, 0, 3}, {6, 7, 3, 0}, {0, 8, 4, 4},
|
||||
{5, 7, 3, 1}, {7, 9, 0, 0}, {7, 6, 2, 1}, {0, 4, 5, 7}, {6, 3, 5, 2}, {1, 2, 1, 12}, {5, 2, 0, 9}, {8, 5, 0, 3}, {4, 6, 1, 5}, {1, 1, 7, 7},
|
||||
{10, 5, 1, 0}, {1, 2, 8, 5}, {1, 8, 2, 5}, {5, 1, 0, 10}, {6, 9, 1, 0}, {13, 0, 2, 1}, {8, 3, 5, 0}, {6, 3, 6, 1}, {2, 11, 3, 0}, {3, 7, 3, 3},
|
||||
{1, 5, 2, 8}, {7, 5, 2, 2}, {0, 6, 7, 3}, {13, 1, 1, 1}, {5, 3, 4, 4}, {7, 2, 7, 0}, {5, 8, 3, 0}, {3, 13, 0, 0}, {0, 7, 9, 0}, {8, 0, 3, 5},
|
||||
{1, 3, 7, 5}, {4, 0, 2, 10}, {12, 0, 1, 3}, {1, 7, 6, 2}, {3, 9, 0, 4}, {7, 2, 0, 7}, {0, 1, 7, 8}, {2, 1, 8, 5}, {0, 13, 1, 2}, {0, 8, 1, 7},
|
||||
{5, 0, 11, 0}, {5, 6, 2, 3}, {0, 3, 0, 13}, {2, 3, 4, 7}, {5, 6, 3, 2}, {4, 2, 10, 0}, {3, 3, 7, 3}, {7, 2, 5, 2}, {1, 1, 11, 3}, {12, 3, 0, 1},
|
||||
{5, 1, 1, 9}, {1, 15, 0, 0}, {9, 7, 0, 0}, {9, 1, 2, 4}, {0, 7, 3, 6}, {3, 0, 13, 0}, {3, 0, 11, 2}, {0, 6, 5, 5}, {8, 2, 2, 4}, {6, 10, 0, 0},
|
||||
{4, 8, 4, 0}, {0, 0, 3, 13}, {0, 4, 12, 0}, {7, 1, 6, 2}, {3, 5, 0, 8}, {8, 0, 6, 2}, {6, 2, 3, 5}, {2, 10, 0, 4}, {4, 11, 0, 1}, {6, 1, 5, 4},
|
||||
{5, 1, 3, 7}, {0, 11, 3, 2}, {4, 6, 0, 6}, {2, 6, 0, 8}, {3, 1, 7, 5}, {2, 14, 0, 0}, {2, 9, 2, 3}, {0, 3, 4, 9}, {11, 0, 1, 4}, {13, 0, 3, 0},
|
||||
{8, 3, 0, 5}, {0, 5, 3, 8}, {5, 11, 0, 0}, {0, 1, 4, 11}, {2, 1, 9, 4}, {3, 4, 4, 5}, {7, 1, 2, 6}, {12, 2, 2, 0}, {9, 4, 1, 2}, {6, 0, 2, 8},
|
||||
{4, 6, 2, 4}, {11, 2, 3, 0}, {3, 2, 2, 9}, {10, 3, 1, 2}, {1, 1, 2, 12}, {0, 5, 2, 9}, {0, 1, 11, 4}, {6, 2, 4, 4}, {2, 8, 2, 4}, {0, 9, 4, 3},
|
||||
{11, 0, 2, 3}, {0, 2, 11, 3}, {6, 0, 7, 3}, {0, 3, 6, 7}, {4, 5, 5, 2}, {1, 2, 6, 7}, {7, 5, 1, 3}, {9, 0, 2, 5}, {2, 6, 4, 4}, {4, 1, 9, 2},
|
||||
{4, 8, 2, 2}, {1, 12, 3, 0}, {0, 9, 6, 1}, {0, 10, 6, 0}, {3, 1, 5, 7}, {2, 13, 0, 1}, {2, 2, 1, 11}, {3, 6, 0, 7}, {5, 6, 5, 0}, {5, 5, 4, 2},
|
||||
{4, 0, 3, 9}, {3, 4, 1, 8}, {0, 11, 2, 3}, {2, 12, 1, 1}, {7, 1, 3, 5}, {7, 0, 9, 0}, {8, 0, 8, 0}, {1, 0, 2, 13}, {3, 3, 10, 0}, {2, 4, 4, 6},
|
||||
{2, 3, 8, 3}, {1, 10, 5, 0}, {7, 3, 0, 6}, {2, 9, 0, 5}, {1, 4, 6, 5}, {6, 6, 3, 1}, {5, 6, 0, 5}, {6, 3, 0, 7}, {3, 10, 2, 1}, {2, 5, 5, 4},
|
||||
{3, 8, 4, 1}, {1, 14, 0, 1}, {10, 3, 3, 0}, {3, 5, 7, 1}, {1, 1, 3, 11}, {2, 4, 0, 10}, {9, 3, 1, 3}, {5, 10, 1, 0}, {3, 0, 6, 7}, {3, 1, 9, 3},
|
||||
{11, 2, 1, 2}, {5, 3, 3, 5}, {0, 5, 1, 10}, {4, 1, 11, 0}, {10, 2, 0, 4}, {7, 6, 0, 3}, {2, 7, 0, 7}, {4, 2, 2, 8}, {6, 1, 7, 2}, {4, 9, 2, 1},
|
||||
{0, 0, 8, 8}, {3, 7, 2, 4}, {9, 6, 0, 1}, {0, 12, 4, 0}, {6, 7, 1, 2}, {0, 7, 2, 7}, {1, 0, 10, 5}, {0, 0, 14, 2}, {2, 7, 3, 4}, {5, 0, 0, 11},
|
||||
{7, 7, 1, 1}, {6, 2, 7, 1}, {4, 5, 3, 4}, {3, 5, 1, 7}, {5, 9, 1, 1}, {6, 2, 1, 7}, {3, 2, 0, 11}, {0, 11, 0, 5}, {3, 11, 2, 0}, {10, 1, 4, 1},
|
||||
{7, 0, 4, 5}, {11, 4, 0, 1}, {10, 3, 0, 3}, {0, 2, 4, 10}, {0, 15, 0, 1}, {0, 11, 5, 0}, {6, 7, 2, 1}, {1, 12, 2, 1}, {4, 1, 3, 8}, {1, 0, 13, 2},
|
||||
{1, 8, 5, 2}, {7, 0, 1, 8}, {3, 12, 1, 0}, {9, 2, 4, 1}, {1, 7, 4, 4}, {11, 4, 1, 0}, {4, 3, 8, 1}, {2, 8, 4, 2}, {1, 11, 3, 1}, {1, 1, 4, 10},
|
||||
{4, 10, 2, 0}, {8, 2, 5, 1}, {1, 0, 9, 6}, {5, 3, 2, 6}, {0, 9, 7, 0}, {10, 2, 2, 2}, {5, 8, 1, 2}, {8, 7, 0, 1}, {0, 3, 12, 1}, {1, 0, 1, 14},
|
||||
{4, 8, 0, 4}, {3, 8, 0, 5}, {4, 6, 5, 1}, {0, 9, 5, 2}, {10, 2, 3, 1}, {2, 3, 9, 2}, {1, 0, 12, 3}, {11, 3, 0, 2}, {4, 5, 2, 5}, {0, 2, 12, 2},
|
||||
{9, 1, 0, 6}, {9, 2, 0, 5}, {1, 2, 7, 6}, {4, 7, 4, 1}, {0, 12, 2, 2}, {0, 0, 0, 16}, {2, 8, 3, 3}, {3, 6, 2, 5}, {0, 6, 3, 7}, {7, 5, 4, 0},
|
||||
{3, 3, 3, 7}, {3, 3, 0, 10}, {5, 0, 6, 5}, {0, 0, 10, 6}, {8, 5, 3, 0}, {8, 1, 5, 2}, {6, 0, 9, 1}, {11, 1, 2, 2}, {2, 11, 2, 1}, {9, 5, 2, 0},
|
||||
{3, 0, 4, 9}, {2, 2, 12, 0}, {2, 6, 6, 2}, {2, 1, 13, 0}, {6, 0, 5, 5}, {2, 0, 14, 0}, {2, 11, 1, 2}, {4, 4, 7, 1}, {2, 0, 11, 3}, {3, 1, 1, 11},
|
||||
{2, 9, 4, 1}, {3, 7, 6, 0}, {14, 0, 2, 0}, {1, 10, 4, 1}, {8, 0, 7, 1}, {3, 6, 5, 2}, {0, 3, 11, 2}, {2, 5, 6, 3}, {11, 1, 3, 1}, {6, 5, 3, 2},
|
||||
{3, 8, 1, 4}, {0, 2, 7, 7}, {2, 10, 2, 2}, {1, 6, 2, 7}, {11, 0, 0, 5}, {12, 1, 1, 2}, {12, 1, 2, 1}, {0, 7, 1, 8}, {0, 3, 9, 4}, {0, 2, 1, 13},
|
||||
{7, 1, 4, 4}, {10, 1, 0, 5}, {4, 0, 8, 4}, {5, 2, 7, 2}, {0, 2, 0, 14}, {4, 3, 7, 2}, {2, 7, 1, 6}, {1, 2, 2, 11}, {6, 3, 3, 4}, {1, 14, 1, 0},
|
||||
{2, 4, 6, 4}, {5, 3, 6, 2}, {5, 3, 5, 3}, {8, 4, 1, 3}, {1, 3, 0, 12}, {3, 5, 2, 6}, {1, 8, 7, 0}, {0, 7, 4, 5}, {2, 1, 6, 7}, {4, 11, 1, 0},
|
||||
{7, 2, 4, 3}, {6, 1, 3, 6}, {4, 5, 4, 3}, {2, 11, 0, 3}, {1, 5, 7, 3}, {12, 0, 2, 2}, {5, 0, 4, 7}, {1, 13, 0, 2}, {7, 7, 2, 0}, {4, 1, 7, 4},
|
||||
{4, 5, 0, 7}, {5, 0, 5, 6}, {6, 5, 4, 1}, {2, 4, 2, 8}, {1, 10, 1, 4}, {6, 3, 1, 6}, {3, 3, 8, 2}, {0, 7, 7, 2}, {4, 4, 2, 6}, {1, 1, 8, 6},
|
||||
{1, 12, 0, 3}, {2, 1, 12, 1}, {1, 9, 2, 4}, {1, 11, 0, 4}, {2, 5, 2, 7}, {10, 0, 3, 3}, {4, 6, 3, 3}, {3, 7, 1, 5}, {1, 9, 0, 6}, {7, 1, 7, 1},
|
||||
{1, 6, 5, 4}, {9, 2, 3, 2}, {6, 2, 2, 6}, {2, 2, 2, 10}, {8, 3, 3, 2}, {0, 1, 8, 7}, {2, 0, 8, 6}, {0, 3, 1, 12}, {9, 4, 2, 1}, {9, 4, 3, 0},
|
||||
{6, 2, 6, 2}, {1, 8, 0, 7}, {5, 1, 10, 0}, {0, 5, 5, 6}, {8, 2, 4, 2}, {2, 3, 2, 9}, {6, 0, 3, 7}, {2, 2, 6, 6}, {2, 6, 2, 6}, {1, 13, 2, 0},
|
||||
{9, 3, 0, 4}, {7, 3, 5, 1}, {6, 5, 2, 3}, {5, 2, 6, 3}, {2, 0, 12, 2}, {5, 7, 1, 3}, {8, 1, 3, 4}, {3, 1, 10, 2}, {1, 0, 15, 0}, {0, 8, 0, 8},
|
||||
{5, 0, 7, 4}, {4, 4, 6, 2}, {0, 1, 0, 15}, {10, 0, 1, 5}, {7, 3, 4, 2}, {4, 9, 3, 0}, {2, 5, 7, 2}, {3, 4, 2, 7}, {8, 3, 2, 3}, {5, 1, 6, 4},
|
||||
{0, 10, 2, 4}, {6, 6, 1, 3}, {6, 0, 0, 10}, {4, 4, 3, 5}, {1, 3, 9, 3}, {7, 5, 3, 1}, {3, 0, 7, 6}, {1, 8, 6, 1}, {4, 3, 0, 9}, {3, 11, 0, 2},
|
||||
{6, 0, 6, 4}, {0, 1, 3, 12}, {0, 4, 2, 10}, {5, 5, 6, 0}, {4, 1, 4, 7}, {8, 1, 6, 1}, {5, 6, 4, 1}, {8, 4, 2, 2}, {4, 3, 1, 8}, {3, 0, 2, 11},
|
||||
{1, 11, 4, 0}, {0, 8, 3, 5}, {5, 1, 7, 3}, {7, 0, 8, 1}, {4, 3, 5, 4}, {4, 6, 4, 2}, {3, 2, 4, 7}, {1, 6, 3, 6}, {0, 7, 8, 1}, {3, 0, 1, 12},
|
||||
{9, 1, 4, 2}, {7, 4, 0, 5}, {1, 7, 0, 8}, {5, 4, 1, 6}, {9, 1, 5, 1}, {1, 1, 9, 5}, {4, 1, 1, 10}, {5, 3, 0, 8}, {2, 2, 5, 7}, {4, 0, 0, 12},
|
||||
{9, 0, 7, 0}, {3, 4, 0, 9}, {0, 2, 6, 8}, {8, 2, 0, 6}, {3, 2, 6, 5}, {4, 2, 6, 4}, {3, 6, 4, 3}, {2, 8, 6, 0}, {5, 0, 3, 8}, {0, 4, 0, 12},
|
||||
{0, 16, 0, 0}, {0, 9, 2, 5}, {4, 0, 11, 1}, {1, 6, 4, 5}, {0, 1, 6, 9}, {3, 4, 6, 3}, {3, 0, 10, 3}, {7, 0, 6, 3}, {1, 4, 9, 2}, {1, 5, 3, 7},
|
||||
{8, 5, 2, 1}, {0, 12, 0, 4}, {7, 2, 3, 4}, {0, 5, 6, 5}, {11, 1, 1, 3}, {6, 5, 0, 5}, {2, 1, 5, 8}, {1, 4, 11, 0}, {9, 1, 1, 5}, {0, 0, 13, 3},
|
||||
{5, 8, 2, 1}, {2, 12, 0, 2}, {3, 3, 6, 4}, {4, 1, 10, 1}, {4, 0, 5, 7}, {8, 1, 0, 7}, {5, 1, 9, 1}, {4, 3, 3, 6}, {0, 2, 2, 12}, {6, 3, 2, 5},
|
||||
{0, 0, 12, 4}, {1, 5, 1, 9}, {2, 6, 5, 3}, {3, 6, 3, 4}, {2, 12, 2, 0}, {1, 6, 8, 1}, {10, 1, 1, 4}, {1, 3, 4, 8}, {7, 4, 4, 1}, {1, 11, 1, 3},
|
||||
{1, 2, 10, 3}, {3, 9, 3, 1}, {8, 5, 1, 2}, {2, 10, 4, 0}, {4, 2, 0, 10}, {2, 7, 6, 1}, {8, 2, 3, 3}, {1, 5, 5, 5}, {3, 1, 0, 12}, {3, 10, 3, 0},
|
||||
{8, 0, 5, 3}, {0, 6, 8, 2}, {0, 3, 13, 0}, {0, 0, 16, 0}, {1, 9, 4, 2}, {4, 1, 8, 3}, {1, 6, 6, 3}, {0, 10, 5, 1}, {0, 1, 12, 3}, {4, 0, 6, 6},
|
||||
{3, 8, 3, 2}, {0, 5, 4, 7}, {1, 0, 14, 1}, {0, 4, 6, 6}, {3, 9, 1, 3}, {3, 5, 8, 0}, {3, 6, 6, 1}, {5, 4, 7, 0}, {3, 0, 12, 1}, {8, 6, 1, 1},
|
||||
{2, 9, 5, 0}, {6, 1, 1, 8}, {4, 1, 2, 9}, {3, 9, 4, 0}, {5, 2, 9, 0}, {0, 12, 3, 1}, {1, 4, 10, 1}, {4, 0, 7, 5}, {3, 1, 2, 10}, {5, 4, 2, 5},
|
||||
{5, 5, 5, 1}, {4, 2, 3, 7}, {1, 7, 5, 3}, {2, 8, 0, 6}, {8, 1, 2, 5}, {3, 8, 2, 3}, {6, 1, 2, 7}, {3, 9, 2, 2}, {9, 0, 0, 7}, {0, 8, 6, 2},
|
||||
{8, 4, 3, 1}, {0, 2, 8, 6}, {6, 5, 1, 4}, {2, 3, 5, 6}, {2, 10, 3, 1}, {0, 7, 0, 9}, {4, 2, 7, 3}, {2, 4, 8, 2}, {7, 1, 1, 7}, {2, 4, 7, 3},
|
||||
{2, 4, 10, 0}, {0, 1, 10, 5}, {4, 7, 1, 4}, {0, 10, 4, 2}, {9, 0, 1, 6}, {1, 9, 6, 0}, {3, 3, 4, 6}, {4, 5, 7, 0}, {5, 5, 2, 4}, {2, 8, 1, 5},
|
||||
{2, 3, 6, 5}, {0, 1, 1, 14}, {3, 2, 3, 8}, {10, 1, 2, 3}, {9, 1, 6, 0}, {3, 4, 3, 6}, {2, 2, 0, 12}, {0, 0, 9, 7}, {4, 0, 9, 3}, {7, 0, 5, 4},
|
||||
{4, 5, 6, 1}, {2, 5, 1, 8}, {2, 5, 9, 0}, {3, 5, 4, 4}, {1, 3, 11, 1}, {7, 1, 5, 3}, {3, 2, 7, 4}, {1, 4, 2, 9}, {1, 11, 2, 2}, {2, 2, 3, 9},
|
||||
{5, 0, 10, 1}, {3, 2, 11, 0}, {1, 10, 3, 2}, {8, 3, 4, 1}, {3, 6, 7, 0}, {0, 7, 5, 4}, {1, 3, 3, 9}, {2, 2, 10, 2}, {1, 9, 5, 1}, {0, 5, 0, 11},
|
||||
{3, 0, 3, 10}, {0, 4, 8, 4}, {2, 7, 7, 0}, {2, 0, 2, 12}, {1, 2, 11, 2}, {6, 3, 7, 0}, {0, 6, 2, 8}, {0, 10, 1, 5}, {0, 9, 0, 7}, {6, 4, 4, 2},
|
||||
{6, 0, 1, 9}, {1, 5, 10, 0}, {5, 4, 6, 1}, {5, 5, 3, 3}, {0, 0, 4, 12}, {0, 3, 2, 11}, {1, 4, 1, 10}, {3, 0, 9, 4}, {5, 5, 0, 6}, {1, 7, 8, 0},
|
||||
{2, 0, 3, 11}, {6, 4, 1, 5}, {10, 0, 6, 0}, {0, 6, 0, 10}, {0, 4, 11, 1}, {3, 1, 6, 6}, {2, 5, 8, 1}, {0, 2, 10, 4}, {3, 1, 11, 1}, {6, 6, 2, 2},
|
||||
{1, 1, 10, 4}, {2, 1, 2, 11}, {6, 1, 8, 1}, {0, 2, 13, 1}, {0, 7, 6, 3}, {6, 8, 2, 0}, {3, 0, 0, 13}, {4, 4, 4, 4}, {6, 2, 0, 8}, {7, 3, 1, 5},
|
||||
{0, 11, 4, 1}, {6, 7, 0, 3}, {2, 6, 3, 5}, {5, 2, 1, 8}, {7, 1, 8, 0}, {5, 5, 1, 5}, {1, 8, 3, 4}, {8, 2, 6, 0}, {6, 0, 10, 0}, {5, 6, 1, 4},
|
||||
{1, 4, 4, 7}, {2, 7, 4, 3}, {1, 4, 8, 3}, {5, 4, 3, 4}, {1, 10, 2, 3}, {2, 9, 1, 4}, {2, 2, 11, 1}, {2, 5, 0, 9}, {0, 0, 1, 15}, {0, 0, 11, 5},
|
||||
{0, 4, 7, 5}, {0, 1, 15, 0}, {2, 1, 0, 13}, {0, 3, 10, 3}, {8, 0, 2, 6}, {3, 3, 2, 8}, {3, 5, 5, 3}, {1, 7, 1, 7}, {1, 3, 2, 10}, {4, 0, 4, 8},
|
||||
{2, 0, 9, 5}, {1, 1, 1, 13}, {2, 2, 7, 5}, {2, 1, 10, 3}, {4, 2, 1, 9}, {4, 3, 6, 3}, {1, 3, 5, 7}, {2, 5, 3, 6}, {1, 0, 8, 7}, {5, 0, 2, 9},
|
||||
{2, 8, 5, 1}, {1, 6, 0, 9}, {0, 0, 5, 11}, {0, 4, 9, 3}, {2, 0, 7, 7}, {1, 7, 2, 6}, {2, 1, 1, 12}, {2, 4, 9, 1}, {0, 5, 7, 4}, {6, 0, 4, 6},
|
||||
{3, 2, 10, 1}, {0, 6, 1, 9}, {2, 6, 1, 7}, {0, 5, 8, 3}, {4, 1, 0, 11}, {1, 2, 4, 9}, {4, 1, 5, 6}, {6, 1, 0, 9}, {1, 4, 3, 8}, {4, 5, 1, 6},
|
||||
{1, 0, 5, 10}, {5, 3, 1, 7}, {0, 9, 1, 6}, {2, 0, 1, 13}, {2, 0, 6, 8}, {8, 1, 1, 6}, {1, 5, 9, 1}, {0, 6, 9, 1}, {0, 3, 5, 8}, {0, 2, 9, 5},
|
||||
{5, 2, 8, 1}, {1, 1, 14, 0}, {3, 2, 9, 2}, {5, 0, 8, 3}, {0, 5, 10, 1}, {5, 2, 3, 6}, {2, 6, 7, 1}, {2, 3, 0, 11}, {0, 1, 9, 6}, {1, 0, 4, 11},
|
||||
{3, 0, 5, 8}, {0, 0, 15, 1}, {2, 4, 5, 5}, {0, 3, 7, 6}, {2, 0, 0, 14}, {1, 1, 12, 2}, {2, 6, 8, 0}, {3, 1, 8, 4}, {0, 1, 5, 10}}};
|
||||
// endregion
|
||||
|
||||
// region BestOrderTable3
|
||||
template <>
|
||||
const OrderTable<3>::BestOrderArray OrderTable<3>::BestOrders = {
|
||||
{{12, 1, 3, 5, 27, 2, 4, 38, 8, 7, 16, 18, 6, 10, 41, 79, 40, 23, 46, 9, 20, 88, 22, 37, 14, 19, 24, 126, 99, 119, 35, 11},
|
||||
{7, 64, 116, 14, 94, 30, 8, 42, 1, 108, 47, 55, 137, 10, 134, 95, 96, 115, 69, 32, 63, 29, 90, 113, 11, 148, 16, 103, 19, 9, 34, 25},
|
||||
{12, 1, 0, 5, 3, 7, 4, 27, 8, 6, 38, 40, 41, 16, 18, 46, 9, 10, 20, 23, 79, 62, 14, 22, 88, 99, 37, 126, 92, 19, 120, 11},
|
||||
{16, 88, 27, 18, 46, 48, 126, 107, 79, 19, 59, 38, 37, 65, 23, 66, 0, 2, 3, 43, 12, 151, 28, 25, 5, 87, 72, 40, 1, 20, 52, 92},
|
||||
{79, 48, 88, 16, 27, 65, 18, 38, 46, 19, 37, 4, 72, 33, 126, 41, 52, 0, 12, 92, 5, 1, 2, 107, 3, 77, 23, 91, 43, 51, 22, 74},
|
||||
{1, 8, 41, 122, 10, 22, 2, 0, 87, 24, 37, 120, 38, 7, 39, 4, 5, 3, 9, 92, 62, 59, 23, 16, 104, 11, 27, 79, 19, 26, 25, 32},
|
||||
{2, 76, 99, 28, 40, 86, 93, 21, 138, 60, 6, 0, 17, 128, 145, 119, 98, 144, 141, 82, 147, 54, 67, 75, 5, 12, 27, 132, 146, 1, 38, 14},
|
||||
{47, 7, 64, 90, 1, 118, 116, 85, 57, 14, 30, 94, 50, 45, 137, 134, 8, 42, 69, 139, 55, 68, 58, 108, 95, 29, 10, 115, 0, 32, 2, 11},
|
||||
{49, 8, 10, 30, 124, 11, 32, 113, 130, 58, 125, 9, 100, 53, 104, 115, 131, 103, 24, 7, 1, 39, 45, 36, 139, 0, 137, 22, 90, 44, 114, 105},
|
||||
{9, 38, 72, 125, 49, 41, 84, 11, 13, 5, 27, 0, 16, 92, 8, 2, 65, 105, 10, 18, 48, 29, 127, 131, 36, 14, 1, 46, 111, 79, 130, 12},
|
||||
{130, 8, 10, 100, 104, 131, 49, 32, 53, 39, 30, 36, 113, 24, 11, 22, 124, 44, 83, 58, 7, 103, 1, 4, 9, 125, 5, 0, 91, 33, 115, 74},
|
||||
{114, 11, 58, 8, 120, 49, 9, 124, 142, 111, 41, 30, 10, 0, 97, 130, 62, 84, 38, 5, 72, 125, 92, 127, 100, 27, 139, 113, 13, 132, 32, 1},
|
||||
{60, 46, 28, 27, 40, 20, 0, 17, 18, 2, 126, 16, 6, 38, 86, 23, 79, 54, 1, 93, 5, 88, 41, 14, 21, 111, 7, 48, 3, 84, 72, 62},
|
||||
{72, 92, 38, 65, 84, 48, 41, 79, 27, 16, 29, 111, 88, 5, 18, 46, 1, 0, 152, 14, 37, 19, 77, 42, 132, 7, 22, 13, 119, 56, 12, 2},
|
||||
{7, 55, 1, 95, 29, 56, 64, 116, 143, 8, 14, 30, 47, 94, 152, 90, 65, 67, 10, 133, 42, 72, 146, 84, 16, 48, 6, 0, 25, 108, 77, 21},
|
||||
{27, 23, 20, 5, 0, 79, 38, 2, 3, 1, 59, 46, 4, 41, 33, 86, 37, 87, 88, 92, 7, 126, 43, 8, 22, 152, 151, 150, 149, 148, 147, 146},
|
||||
{12, 0, 1, 2, 7, 6, 3, 5, 28, 4, 8, 14, 60, 40, 17, 19, 21, 86, 126, 93, 10, 18, 9, 29, 48, 99, 65, 25, 84, 119, 72, 41},
|
||||
{60, 40, 99, 2, 54, 12, 0, 1, 19, 28, 98, 93, 6, 138, 21, 5, 27, 17, 151, 14, 76, 46, 16, 18, 38, 29, 86, 144, 107, 7, 25, 41},
|
||||
{12, 0, 1, 2, 3, 5, 6, 7, 4, 28, 8, 60, 14, 40, 16, 17, 21, 10, 19, 9, 86, 38, 126, 41, 93, 27, 29, 48, 62, 84, 79, 99},
|
||||
{0, 1, 2, 10, 5, 8, 3, 25, 4, 29, 32, 34, 63, 7, 77, 26, 16, 48, 65, 56, 14, 22, 129, 103, 72, 24, 18, 152, 140, 53, 96, 42},
|
||||
{46, 126, 18, 54, 12, 16, 1, 0, 5, 2, 27, 98, 20, 23, 6, 3, 88, 48, 28, 7, 19, 8, 4, 60, 151, 38, 37, 21, 79, 14, 65, 40},
|
||||
{76, 6, 141, 86, 119, 2, 138, 67, 28, 145, 0, 93, 17, 1, 40, 60, 146, 99, 147, 14, 21, 144, 132, 7, 5, 29, 55, 27, 16, 75, 19, 12},
|
||||
{71, 5, 51, 39, 22, 80, 0, 43, 10, 122, 8, 62, 41, 24, 104, 87, 35, 37, 2, 91, 33, 120, 36, 38, 1, 131, 9, 100, 130, 66, 3, 4},
|
||||
{126, 18, 46, 27, 20, 16, 88, 23, 12, 79, 54, 59, 48, 0, 73, 1, 37, 151, 5, 19, 28, 38, 2, 66, 60, 3, 65, 98, 14, 26, 6, 43},
|
||||
{22, 10, 8, 5, 0, 71, 35, 80, 104, 39, 24, 51, 100, 1, 62, 32, 2, 130, 11, 41, 7, 9, 53, 43, 49, 83, 122, 120, 30, 44, 37, 38},
|
||||
{1, 34, 14, 129, 53, 63, 42, 26, 121, 148, 7, 44, 96, 10, 0, 24, 100, 32, 64, 116, 140, 22, 5, 19, 29, 103, 135, 108, 8, 61, 39, 83},
|
||||
{1, 7, 34, 63, 44, 25, 135, 14, 24, 108, 22, 0, 83, 94, 5, 129, 35, 101, 47, 121, 2, 19, 42, 53, 6, 110, 103, 8, 148, 10, 16, 123},
|
||||
{12, 28, 16, 60, 18, 1, 6, 21, 14, 0, 86, 19, 2, 48, 93, 17, 38, 29, 7, 5, 65, 126, 46, 72, 41, 79, 84, 119, 40, 56, 54, 88},
|
||||
{0, 2, 12, 27, 5, 46, 38, 40, 41, 79, 88, 99, 3, 23, 1, 62, 20, 4, 22, 37, 92, 35, 18, 8, 16, 24, 10, 60, 7, 120, 98, 54},
|
||||
{1, 7, 14, 56, 8, 0, 84, 67, 10, 2, 133, 72, 42, 111, 5, 30, 21, 4, 9, 3, 25, 94, 16, 116, 47, 11, 65, 18, 132, 90, 55, 64},
|
||||
{30, 8, 124, 139, 45, 11, 58, 90, 113, 137, 7, 115, 10, 32, 1, 49, 94, 85, 9, 47, 108, 103, 0, 97, 63, 14, 50, 114, 53, 106, 100, 25},
|
||||
{65, 38, 48, 27, 16, 79, 72, 18, 88, 19, 46, 77, 84, 92, 37, 41, 0, 29, 1, 14, 12, 111, 2, 5, 31, 36, 87, 74, 105, 40, 28, 51},
|
||||
{10, 8, 30, 113, 130, 100, 53, 32, 115, 103, 104, 7, 1, 121, 39, 49, 131, 44, 24, 36, 63, 137, 34, 45, 22, 90, 108, 83, 26, 11, 94, 139},
|
||||
{51, 52, 43, 33, 5, 74, 16, 37, 71, 91, 38, 3, 36, 87, 48, 22, 4, 0, 122, 41, 39, 18, 66, 27, 79, 24, 65, 88, 59, 23, 62, 92},
|
||||
{1, 7, 63, 53, 108, 121, 94, 44, 103, 100, 14, 10, 129, 47, 32, 26, 24, 25, 148, 42, 135, 22, 0, 61, 83, 8, 39, 104, 5, 64, 115, 34},
|
||||
{1, 8, 10, 7, 5, 0, 80, 32, 62, 2, 24, 44, 53, 83, 9, 41, 30, 22, 100, 11, 14, 25, 120, 4, 26, 6, 3, 16, 122, 34, 19, 35},
|
||||
{74, 4, 36, 48, 33, 91, 39, 79, 22, 16, 65, 5, 131, 38, 24, 71, 27, 52, 0, 105, 51, 18, 88, 104, 3, 31, 10, 37, 72, 19, 41, 130},
|
||||
{59, 43, 38, 79, 23, 27, 92, 51, 0, 16, 46, 5, 18, 88, 41, 37, 66, 3, 87, 20, 48, 2, 122, 4, 22, 12, 1, 126, 19, 65, 33, 24},
|
||||
{12, 28, 1, 27, 0, 16, 2, 46, 65, 60, 21, 3, 5, 18, 6, 19, 48, 14, 4, 7, 79, 88, 86, 29, 22, 72, 93, 40, 23, 8, 17, 41},
|
||||
{22, 91, 39, 33, 24, 71, 5, 131, 36, 10, 51, 0, 130, 8, 104, 2, 35, 125, 9, 43, 52, 49, 83, 80, 100, 41, 122, 3, 37, 38, 4, 16},
|
||||
{12, 0, 1, 2, 5, 3, 4, 8, 7, 27, 18, 38, 10, 6, 16, 46, 9, 20, 41, 23, 126, 79, 22, 14, 19, 99, 88, 54, 37, 48, 62, 35},
|
||||
{12, 27, 1, 2, 3, 0, 46, 4, 38, 16, 8, 28, 7, 79, 18, 5, 84, 6, 88, 10, 14, 21, 23, 20, 40, 22, 60, 19, 9, 29, 72, 65},
|
||||
{1, 14, 7, 55, 95, 29, 8, 94, 30, 56, 10, 108, 77, 116, 152, 64, 32, 48, 63, 42, 143, 148, 16, 25, 137, 65, 11, 0, 115, 9, 19, 72},
|
||||
{37, 79, 66, 38, 16, 52, 48, 59, 43, 27, 87, 33, 41, 4, 23, 51, 3, 5, 88, 18, 92, 46, 73, 122, 22, 71, 20, 0, 65, 19, 2, 120},
|
||||
{24, 32, 83, 22, 53, 1, 8, 10, 7, 30, 35, 5, 103, 0, 100, 101, 121, 113, 34, 123, 63, 2, 44, 25, 71, 115, 80, 14, 26, 108, 51, 39},
|
||||
{97, 45, 111, 58, 85, 139, 0, 90, 47, 7, 120, 106, 142, 30, 50, 132, 41, 62, 84, 1, 119, 114, 14, 56, 117, 8, 38, 29, 2, 64, 116, 5},
|
||||
{12, 28, 16, 18, 1, 60, 6, 14, 2, 21, 0, 86, 126, 19, 48, 93, 7, 27, 17, 29, 5, 65, 54, 38, 72, 79, 84, 88, 119, 145, 8, 111},
|
||||
{118, 47, 64, 116, 57, 85, 7, 14, 50, 1, 42, 0, 45, 68, 86, 69, 2, 111, 134, 28, 90, 55, 16, 29, 56, 48, 84, 144, 60, 30, 112, 41},
|
||||
{12, 1, 2, 0, 7, 6, 28, 5, 3, 4, 8, 14, 60, 21, 18, 40, 17, 86, 10, 9, 16, 29, 19, 93, 126, 79, 38, 84, 72, 27, 111, 119},
|
||||
{11, 8, 49, 130, 10, 125, 9, 124, 100, 114, 131, 30, 58, 104, 32, 39, 24, 113, 36, 105, 0, 41, 22, 120, 5, 53, 111, 38, 142, 44, 83, 35},
|
||||
{50, 70, 47, 118, 85, 57, 106, 0, 45, 7, 64, 90, 81, 14, 2, 134, 28, 62, 86, 55, 69, 1, 78, 119, 68, 56, 18, 67, 16, 60, 29, 21},
|
||||
{43, 37, 33, 87, 51, 41, 66, 5, 122, 38, 22, 59, 92, 0, 23, 91, 27, 16, 71, 79, 18, 52, 120, 4, 3, 24, 46, 20, 73, 39, 62, 36},
|
||||
{79, 48, 4, 16, 27, 88, 43, 33, 18, 38, 65, 37, 46, 3, 19, 51, 52, 22, 66, 87, 74, 5, 41, 91, 23, 59, 0, 71, 122, 72, 20, 92},
|
||||
{32, 100, 10, 8, 30, 104, 24, 44, 39, 113, 83, 103, 1, 7, 22, 53, 115, 63, 135, 121, 26, 35, 34, 5, 0, 108, 137, 90, 91, 45, 2, 130},
|
||||
{0, 1, 2, 5, 16, 12, 6, 7, 14, 3, 19, 18, 29, 20, 4, 21, 40, 8, 17, 35, 23, 48, 126, 22, 25, 56, 26, 10, 98, 27, 38, 65},
|
||||
{143, 67, 56, 146, 1, 7, 133, 55, 64, 141, 134, 69, 6, 47, 14, 29, 84, 21, 111, 147, 57, 16, 95, 72, 118, 132, 50, 0, 2, 18, 119, 42},
|
||||
{1, 7, 67, 14, 133, 111, 8, 84, 0, 21, 2, 47, 64, 132, 55, 10, 95, 147, 119, 42, 16, 5, 72, 56, 4, 3, 6, 29, 9, 25, 18, 30},
|
||||
{68, 57, 69, 112, 144, 86, 102, 2, 134, 55, 0, 70, 118, 64, 75, 47, 14, 28, 93, 143, 67, 7, 50, 149, 1, 21, 29, 56, 119, 95, 60, 78},
|
||||
{58, 97, 114, 30, 124, 45, 11, 139, 8, 90, 0, 142, 7, 10, 41, 113, 84, 62, 49, 111, 85, 1, 9, 5, 137, 120, 32, 14, 2, 117, 47, 38},
|
||||
{23, 66, 18, 79, 38, 20, 43, 27, 16, 88, 46, 59, 126, 37, 87, 12, 73, 92, 3, 5, 48, 0, 19, 54, 2, 51, 28, 1, 41, 65, 122, 22},
|
||||
{0, 12, 2, 27, 5, 40, 46, 38, 1, 41, 3, 79, 88, 23, 99, 4, 20, 62, 22, 54, 92, 18, 8, 37, 16, 35, 10, 7, 19, 120, 144, 24},
|
||||
{1, 14, 25, 26, 0, 7, 44, 34, 129, 42, 24, 5, 135, 22, 19, 148, 6, 96, 83, 2, 29, 16, 63, 35, 101, 64, 140, 136, 116, 110, 3, 10},
|
||||
{12, 1, 2, 27, 3, 4, 38, 5, 7, 8, 18, 16, 46, 6, 0, 40, 41, 10, 79, 23, 88, 9, 20, 22, 14, 19, 37, 92, 48, 126, 28, 21},
|
||||
{7, 1, 10, 32, 108, 103, 94, 47, 8, 53, 25, 14, 34, 115, 100, 129, 121, 130, 148, 42, 64, 116, 63, 26, 44, 0, 24, 30, 113, 4, 104, 22},
|
||||
{47, 134, 7, 14, 55, 69, 64, 95, 1, 29, 85, 118, 56, 116, 45, 57, 102, 143, 50, 90, 42, 30, 16, 94, 0, 8, 67, 75, 133, 2, 18, 48},
|
||||
{12, 1, 2, 0, 7, 6, 28, 8, 14, 5, 3, 4, 40, 21, 17, 18, 60, 86, 16, 93, 126, 10, 9, 29, 99, 38, 119, 25, 19, 54, 27, 84},
|
||||
{59, 16, 27, 18, 23, 88, 79, 37, 46, 66, 38, 20, 73, 126, 3, 43, 48, 87, 92, 51, 41, 12, 19, 5, 52, 107, 65, 0, 151, 122, 54, 2},
|
||||
{1, 21, 147, 7, 119, 14, 76, 132, 55, 0, 86, 145, 2, 6, 69, 67, 16, 143, 111, 138, 17, 28, 29, 60, 18, 93, 8, 19, 40, 56, 84, 5},
|
||||
{144, 86, 112, 2, 68, 102, 69, 0, 149, 93, 75, 28, 57, 55, 145, 60, 21, 67, 99, 134, 143, 40, 146, 119, 82, 110, 62, 6, 29, 26, 78, 14},
|
||||
{102, 57, 55, 69, 143, 75, 146, 67, 56, 68, 134, 2, 29, 141, 0, 21, 6, 14, 133, 118, 64, 1, 7, 95, 47, 84, 111, 28, 147, 82, 72, 119},
|
||||
{0, 70, 57, 119, 50, 145, 2, 86, 28, 118, 69, 78, 149, 47, 60, 68, 67, 55, 93, 81, 134, 21, 14, 62, 64, 7, 5, 1, 132, 85, 41, 16},
|
||||
{51, 5, 43, 71, 122, 87, 41, 37, 91, 39, 0, 22, 33, 36, 38, 24, 66, 120, 62, 2, 80, 16, 92, 10, 59, 4, 27, 23, 35, 79, 8, 3},
|
||||
{12, 1, 2, 0, 7, 6, 28, 5, 8, 14, 3, 21, 40, 4, 60, 17, 86, 18, 16, 93, 10, 9, 126, 119, 99, 29, 19, 41, 38, 27, 25, 92},
|
||||
{27, 18, 46, 126, 23, 16, 88, 79, 20, 151, 59, 73, 48, 38, 0, 54, 12, 2, 37, 1, 19, 5, 28, 60, 66, 41, 3, 109, 86, 65, 40, 6},
|
||||
{48, 79, 4, 33, 16, 74, 65, 38, 88, 27, 91, 52, 18, 36, 22, 19, 46, 0, 37, 3, 51, 5, 71, 39, 72, 43, 24, 41, 92, 87, 2, 10},
|
||||
{86, 2, 144, 93, 28, 112, 141, 6, 102, 21, 99, 60, 75, 0, 68, 82, 69, 146, 67, 149, 55, 40, 145, 76, 111, 147, 56, 119, 110, 143, 26, 132},
|
||||
{6, 138, 2, 99, 86, 17, 40, 93, 28, 21, 145, 141, 0, 60, 119, 147, 128, 76, 67, 54, 1, 12, 5, 27, 144, 14, 38, 98, 146, 41, 29, 19},
|
||||
{1, 8, 0, 10, 2, 29, 7, 5, 3, 56, 4, 25, 14, 152, 63, 32, 65, 72, 96, 42, 34, 108, 48, 9, 26, 16, 84, 103, 67, 148, 22, 129},
|
||||
{149, 145, 0, 86, 2, 28, 93, 144, 62, 60, 119, 101, 21, 41, 5, 35, 78, 99, 26, 40, 12, 68, 57, 67, 110, 120, 69, 18, 55, 76, 132, 70},
|
||||
{12, 28, 16, 1, 48, 19, 6, 60, 2, 14, 18, 21, 0, 27, 46, 65, 86, 29, 5, 7, 72, 93, 40, 3, 17, 84, 56, 88, 126, 4, 38, 8},
|
||||
{1, 8, 5, 10, 7, 24, 2, 62, 0, 41, 22, 122, 120, 9, 4, 3, 32, 87, 11, 37, 38, 83, 100, 44, 25, 104, 16, 26, 39, 80, 14, 6},
|
||||
{0, 119, 62, 86, 145, 149, 28, 132, 93, 2, 120, 67, 60, 41, 35, 5, 144, 21, 123, 38, 111, 81, 84, 56, 12, 44, 24, 50, 92, 55, 40, 22},
|
||||
{2, 93, 99, 28, 40, 144, 60, 0, 86, 150, 76, 21, 149, 98, 6, 25, 1, 61, 82, 26, 12, 5, 54, 141, 7, 18, 145, 16, 27, 138, 110, 38},
|
||||
{24, 8, 10, 22, 32, 35, 100, 5, 1, 53, 0, 7, 71, 80, 30, 123, 83, 104, 51, 11, 2, 39, 44, 113, 9, 62, 25, 103, 34, 101, 43, 41},
|
||||
{12, 1, 2, 0, 7, 6, 28, 5, 40, 60, 8, 16, 3, 18, 14, 4, 86, 21, 17, 93, 41, 10, 9, 99, 27, 119, 38, 19, 126, 22, 48, 145},
|
||||
{45, 47, 50, 7, 85, 90, 97, 1, 64, 139, 116, 118, 30, 58, 14, 106, 70, 111, 0, 57, 94, 42, 137, 142, 29, 120, 8, 56, 18, 134, 84, 41},
|
||||
{12, 0, 2, 5, 27, 38, 1, 46, 41, 40, 79, 144, 3, 22, 88, 23, 28, 60, 99, 62, 6, 24, 26, 7, 4, 16, 10, 35, 37, 18, 14, 20},
|
||||
{37, 38, 59, 92, 0, 5, 23, 51, 79, 41, 27, 22, 2, 3, 87, 16, 46, 4, 1, 43, 20, 33, 18, 88, 24, 71, 8, 10, 48, 19, 126, 122},
|
||||
{12, 28, 16, 60, 1, 18, 6, 21, 19, 14, 48, 0, 2, 86, 93, 5, 46, 29, 17, 27, 65, 7, 3, 72, 38, 126, 119, 40, 84, 37, 56, 4},
|
||||
{0, 2, 5, 1, 16, 6, 27, 28, 18, 38, 60, 7, 14, 21, 46, 40, 86, 41, 19, 48, 93, 8, 3, 79, 22, 4, 10, 37, 62, 23, 24, 111},
|
||||
{85, 7, 90, 30, 47, 139, 45, 50, 94, 58, 137, 1, 8, 64, 14, 116, 118, 115, 113, 11, 124, 108, 0, 10, 97, 57, 32, 70, 42, 106, 29, 114},
|
||||
{33, 36, 22, 71, 51, 5, 91, 39, 0, 52, 43, 24, 131, 74, 16, 37, 38, 122, 41, 3, 87, 48, 4, 104, 35, 80, 10, 2, 105, 62, 27, 18},
|
||||
{12, 1, 27, 2, 0, 16, 3, 28, 46, 18, 4, 6, 5, 72, 21, 79, 38, 7, 14, 60, 88, 8, 65, 19, 48, 29, 23, 40, 22, 20, 86, 126},
|
||||
{0, 12, 2, 27, 5, 38, 46, 41, 1, 40, 79, 3, 88, 23, 22, 99, 20, 37, 62, 4, 18, 6, 16, 35, 60, 28, 24, 7, 92, 8, 14, 10},
|
||||
{7, 47, 1, 30, 137, 8, 116, 94, 90, 64, 14, 115, 108, 118, 57, 10, 148, 113, 42, 85, 32, 11, 63, 50, 103, 45, 124, 134, 55, 9, 69, 34},
|
||||
{55, 7, 1, 29, 56, 143, 64, 47, 67, 133, 14, 146, 95, 72, 84, 8, 116, 111, 6, 134, 141, 21, 65, 0, 69, 30, 16, 45, 85, 42, 50, 10},
|
||||
{14, 1, 42, 8, 10, 29, 108, 63, 55, 148, 95, 32, 7, 19, 25, 115, 103, 34, 56, 129, 77, 0, 16, 152, 94, 30, 113, 26, 2, 5, 48, 4},
|
||||
{111, 120, 142, 97, 58, 0, 41, 45, 62, 132, 114, 84, 139, 30, 5, 8, 38, 2, 7, 85, 119, 90, 117, 1, 124, 11, 56, 47, 28, 27, 35, 72},
|
||||
{1, 0, 14, 2, 6, 5, 16, 19, 7, 29, 42, 18, 3, 25, 12, 35, 21, 8, 26, 17, 40, 4, 20, 48, 109, 99, 22, 96, 55, 101, 10, 61},
|
||||
{12, 0, 1, 5, 3, 2, 4, 7, 27, 8, 38, 6, 40, 18, 16, 10, 20, 46, 9, 41, 23, 22, 79, 14, 62, 19, 37, 126, 88, 11, 92, 48},
|
||||
{10, 8, 104, 39, 24, 32, 22, 83, 44, 100, 30, 130, 53, 91, 113, 5, 11, 1, 35, 33, 7, 49, 0, 2, 103, 71, 36, 124, 9, 80, 131, 34},
|
||||
{1, 7, 0, 14, 8, 34, 5, 25, 35, 26, 6, 63, 10, 123, 2, 16, 103, 19, 44, 32, 135, 121, 108, 80, 62, 30, 115, 94, 149, 144, 53, 18},
|
||||
{75, 68, 146, 141, 102, 67, 2, 21, 6, 57, 69, 143, 0, 55, 82, 86, 28, 144, 147, 29, 93, 112, 56, 119, 133, 14, 76, 60, 84, 134, 111, 145},
|
||||
{10, 32, 115, 7, 8, 53, 1, 108, 30, 113, 94, 137, 100, 63, 90, 34, 130, 103, 121, 47, 44, 25, 104, 39, 24, 26, 85, 14, 49, 36, 22, 131},
|
||||
{39, 24, 10, 22, 8, 130, 91, 104, 83, 49, 5, 33, 100, 11, 0, 35, 32, 131, 71, 36, 9, 44, 53, 2, 80, 51, 30, 1, 41, 7, 43, 62},
|
||||
{38, 36, 65, 105, 27, 72, 31, 79, 41, 131, 5, 48, 125, 39, 0, 16, 92, 46, 22, 13, 18, 84, 24, 37, 88, 2, 33, 74, 91, 71, 130, 49},
|
||||
{0, 106, 62, 50, 45, 119, 85, 81, 132, 28, 2, 86, 41, 47, 38, 60, 35, 117, 5, 29, 7, 30, 145, 90, 55, 70, 14, 111, 18, 67, 93, 56},
|
||||
{0, 2, 5, 1, 3, 25, 19, 26, 4, 34, 29, 10, 22, 16, 8, 7, 24, 14, 48, 65, 53, 18, 6, 77, 44, 56, 72, 61, 121, 21, 136, 40},
|
||||
{7, 1, 94, 8, 47, 115, 10, 32, 113, 103, 30, 108, 137, 63, 14, 64, 116, 148, 129, 42, 90, 25, 34, 118, 53, 57, 11, 49, 85, 9, 96, 50},
|
||||
{14, 0, 1, 26, 19, 5, 42, 2, 25, 24, 29, 22, 6, 44, 61, 16, 7, 96, 136, 3, 140, 34, 35, 55, 135, 18, 48, 77, 83, 4, 8, 10},
|
||||
{1, 7, 14, 0, 25, 6, 34, 5, 26, 16, 63, 2, 19, 8, 35, 101, 108, 29, 94, 10, 18, 42, 123, 144, 129, 47, 61, 21, 3, 62, 149, 4},
|
||||
{12, 0, 2, 1, 28, 5, 6, 120, 7, 60, 40, 16, 18, 86, 27, 14, 21, 93, 8, 62, 41, 38, 3, 17, 4, 119, 99, 48, 19, 126, 10, 9},
|
||||
{86, 144, 93, 2, 28, 149, 0, 60, 99, 112, 110, 145, 40, 21, 102, 26, 75, 62, 69, 1, 12, 101, 119, 25, 76, 67, 7, 68, 55, 5, 6, 14},
|
||||
{8, 30, 10, 32, 113, 49, 115, 137, 124, 103, 45, 90, 7, 139, 11, 1, 58, 53, 130, 94, 108, 100, 9, 63, 85, 125, 34, 47, 0, 24, 44, 104},
|
||||
{120, 142, 111, 41, 58, 114, 97, 0, 11, 62, 84, 124, 5, 30, 8, 38, 132, 127, 27, 139, 92, 10, 72, 45, 49, 9, 28, 2, 29, 56, 16, 1},
|
||||
{8, 113, 30, 137, 7, 32, 10, 90, 94, 115, 1, 103, 108, 63, 47, 85, 49, 53, 11, 45, 34, 50, 14, 25, 9, 124, 100, 130, 139, 121, 42, 26},
|
||||
{64, 7, 14, 47, 134, 55, 1, 42, 95, 69, 116, 90, 94, 30, 8, 29, 56, 137, 45, 108, 85, 10, 57, 16, 102, 143, 118, 19, 63, 32, 11, 50},
|
||||
{62, 132, 0, 119, 120, 41, 111, 86, 35, 28, 5, 84, 56, 38, 2, 93, 145, 60, 67, 12, 92, 27, 29, 72, 55, 117, 21, 24, 133, 149, 22, 45},
|
||||
{57, 68, 69, 118, 134, 64, 50, 47, 55, 14, 7, 2, 102, 144, 0, 112, 70, 86, 85, 1, 95, 29, 116, 143, 42, 75, 16, 56, 28, 45, 21, 48},
|
||||
{0, 12, 2, 1, 5, 28, 6, 40, 60, 27, 7, 38, 16, 14, 86, 18, 93, 41, 62, 46, 99, 35, 8, 23, 3, 17, 22, 21, 10, 19, 79, 20},
|
||||
{12, 1, 2, 27, 16, 3, 38, 111, 4, 0, 18, 5, 7, 46, 40, 8, 79, 6, 14, 28, 88, 10, 48, 41, 19, 84, 21, 9, 22, 23, 20, 72},
|
||||
{53, 103, 32, 7, 1, 100, 22, 63, 71, 44, 10, 115, 108, 24, 92, 104, 26, 30, 122, 94, 8, 39, 83, 34, 137, 135, 90, 91, 121, 5, 87, 47},
|
||||
{87, 37, 41, 0, 22, 38, 2, 92, 1, 24, 4, 8, 3, 59, 10, 5, 39, 23, 71, 79, 122, 27, 16, 46, 33, 7, 91, 20, 18, 51, 9, 120},
|
||||
{1, 7, 8, 10, 0, 5, 35, 32, 53, 44, 14, 30, 2, 80, 25, 34, 6, 62, 26, 103, 16, 19, 63, 9, 149, 24, 121, 41, 22, 11, 113, 83},
|
||||
{11, 58, 8, 30, 124, 49, 10, 113, 9, 114, 139, 45, 97, 32, 7, 137, 90, 1, 0, 130, 115, 125, 100, 24, 5, 94, 53, 41, 14, 13, 35, 38},
|
||||
{125, 105, 9, 36, 131, 49, 8, 130, 39, 11, 10, 5, 22, 38, 41, 104, 0, 31, 13, 24, 27, 16, 2, 72, 65, 91, 48, 32, 84, 18, 100, 74},
|
||||
{12, 1, 0, 2, 6, 3, 7, 5, 4, 8, 14, 28, 16, 60, 18, 10, 21, 17, 19, 9, 40, 27, 86, 93, 29, 38, 54, 11, 25, 48, 46, 41},
|
||||
{84, 41, 38, 72, 92, 29, 111, 5, 65, 120, 79, 0, 27, 56, 48, 14, 132, 16, 119, 22, 86, 88, 46, 28, 62, 12, 1, 2, 93, 18, 24, 127},
|
||||
{99, 28, 40, 60, 2, 93, 138, 0, 98, 17, 86, 54, 76, 12, 27, 1, 21, 144, 128, 38, 5, 14, 46, 18, 25, 16, 109, 6, 41, 145, 7, 29},
|
||||
{1, 63, 10, 32, 148, 14, 103, 34, 42, 7, 8, 108, 116, 53, 64, 96, 25, 121, 26, 94, 140, 0, 29, 19, 55, 24, 100, 136, 5, 4, 44, 115},
|
||||
{131, 100, 130, 49, 10, 8, 36, 104, 39, 0, 48, 41, 11, 38, 4, 24, 27, 22, 16, 44, 79, 5, 33, 2, 53, 9, 125, 74, 91, 120, 32, 83},
|
||||
{36, 39, 131, 74, 4, 91, 22, 33, 125, 104, 130, 48, 10, 24, 16, 5, 49, 8, 100, 105, 79, 0, 9, 65, 71, 2, 18, 83, 31, 11, 19, 44},
|
||||
{0, 12, 2, 1, 6, 5, 7, 28, 40, 60, 16, 14, 18, 62, 86, 27, 93, 8, 17, 38, 21, 41, 35, 99, 3, 19, 10, 23, 22, 4, 9, 48},
|
||||
{1, 7, 67, 14, 21, 147, 111, 55, 132, 119, 0, 8, 2, 76, 64, 16, 47, 84, 6, 18, 86, 95, 145, 10, 42, 29, 133, 5, 56, 134, 17, 72},
|
||||
{69, 55, 47, 134, 102, 143, 7, 57, 118, 95, 14, 64, 29, 56, 1, 50, 75, 67, 146, 2, 0, 133, 68, 16, 21, 6, 141, 85, 116, 18, 72, 65},
|
||||
{1, 44, 7, 24, 83, 63, 34, 103, 22, 121, 53, 32, 25, 35, 0, 115, 108, 5, 14, 8, 10, 101, 94, 30, 2, 123, 110, 26, 137, 47, 90, 19},
|
||||
{14, 1, 25, 42, 34, 0, 26, 96, 19, 29, 140, 5, 53, 10, 2, 121, 3, 24, 44, 22, 55, 77, 129, 7, 63, 16, 8, 4, 6, 61, 100, 48},
|
||||
{30, 90, 7, 8, 137, 94, 85, 1, 47, 113, 115, 108, 45, 139, 124, 11, 10, 32, 50, 58, 103, 14, 63, 64, 9, 116, 49, 42, 25, 148, 0, 53},
|
||||
{40, 99, 2, 60, 28, 17, 0, 54, 93, 98, 86, 138, 6, 12, 21, 76, 1, 5, 27, 144, 128, 38, 19, 46, 14, 41, 145, 7, 16, 67, 3, 109},
|
||||
{45, 58, 30, 139, 90, 7, 85, 137, 97, 8, 124, 47, 1, 11, 106, 114, 50, 94, 0, 113, 10, 115, 14, 32, 9, 64, 108, 41, 49, 29, 62, 116},
|
||||
{14, 42, 10, 1, 63, 96, 32, 25, 34, 8, 129, 29, 0, 103, 55, 19, 26, 53, 77, 5, 95, 2, 4, 7, 3, 16, 148, 56, 18, 24, 121, 108},
|
||||
{21, 2, 75, 86, 6, 76, 144, 28, 119, 99, 93, 147, 141, 67, 102, 145, 60, 132, 146, 128, 0, 82, 40, 138, 55, 111, 143, 17, 133, 112, 69, 14},
|
||||
{111, 120, 41, 62, 84, 132, 0, 5, 38, 119, 56, 92, 72, 142, 27, 28, 29, 35, 58, 80, 2, 86, 65, 79, 12, 14, 1, 24, 145, 16, 21, 48},
|
||||
{146, 67, 141, 69, 133, 21, 6, 143, 57, 55, 111, 147, 56, 1, 14, 132, 7, 2, 134, 102, 0, 119, 29, 84, 76, 64, 86, 72, 28, 68, 47, 75},
|
||||
{12, 1, 0, 5, 27, 3, 7, 4, 38, 8, 6, 41, 16, 40, 46, 10, 18, 79, 2, 9, 23, 86, 20, 22, 62, 14, 37, 88, 92, 19, 24, 11},
|
||||
{0, 12, 2, 1, 27, 5, 38, 28, 60, 6, 40, 7, 16, 46, 18, 14, 41, 99, 93, 62, 3, 79, 86, 23, 149, 8, 22, 35, 88, 17, 19, 10},
|
||||
{141, 6, 21, 67, 147, 102, 146, 2, 76, 119, 132, 69, 55, 111, 86, 75, 28, 133, 143, 0, 1, 145, 14, 128, 56, 99, 17, 60, 29, 93, 84, 68},
|
||||
{21, 76, 1, 119, 86, 145, 2, 0, 14, 7, 6, 138, 146, 55, 17, 28, 132, 93, 67, 40, 60, 143, 29, 147, 111, 16, 69, 141, 5, 56, 19, 133},
|
||||
{1, 8, 108, 14, 7, 116, 64, 42, 10, 63, 94, 32, 115, 103, 113, 96, 30, 34, 55, 47, 95, 148, 29, 140, 129, 25, 134, 53, 69, 26, 19, 11},
|
||||
{12, 1, 3, 5, 4, 2, 0, 7, 8, 38, 27, 16, 18, 6, 10, 20, 41, 40, 79, 46, 9, 23, 22, 88, 92, 37, 14, 24, 62, 19, 48, 99},
|
||||
{1, 14, 7, 0, 6, 25, 5, 16, 19, 2, 42, 26, 29, 35, 61, 8, 18, 129, 101, 21, 3, 110, 34, 148, 96, 10, 17, 4, 22, 40, 12, 20},
|
||||
{0, 2, 5, 1, 3, 19, 22, 26, 16, 24, 29, 7, 14, 6, 4, 25, 18, 44, 8, 48, 12, 61, 20, 21, 10, 35, 65, 56, 23, 40, 17, 107},
|
||||
{1, 7, 8, 29, 56, 0, 10, 14, 2, 42, 72, 5, 4, 65, 3, 30, 84, 94, 67, 9, 25, 133, 111, 11, 32, 108, 16, 63, 21, 96, 26, 48}}};
|
||||
// endregion
|
||||
|
||||
template class OrderTable<3>;
|
||||
template class OrderTable<4>;
|
||||
|
||||
} // namespace quicktex::s3tc
|
151
quicktex/s3tc/bc1/OrderTable.h
Normal file
151
quicktex/s3tc/bc1/OrderTable.h
Normal file
@ -0,0 +1,151 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../../Vector4.h"
|
||||
#include "Histogram.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
template <size_t N> class OrderTable {
|
||||
public:
|
||||
static constexpr unsigned HashCount = 1 << ((N - 1) * 4); // 16**(N-1)
|
||||
static constexpr unsigned OrderCount = (N == 4) ? 969 : 153; //(16+N-1)C(N-1)
|
||||
#if RGBCX_USE_SMALLER_TABLES
|
||||
static constexpr unsigned BestOrderCount = 32;
|
||||
#else
|
||||
static constexpr unsigned BestOrderCount = (N == 4) ? 128 : 32;
|
||||
#endif
|
||||
|
||||
using Hash = uint16_t;
|
||||
using OrderArray = std::array<Histogram<N>, OrderCount>;
|
||||
using BestOrderRow = std::array<Hash, BestOrderCount>;
|
||||
using BestOrderArray = std::array<BestOrderRow, OrderCount>;
|
||||
|
||||
static std::atomic<bool> generated;
|
||||
|
||||
static const OrderArray Orders;
|
||||
static const BestOrderArray BestOrders;
|
||||
static const std::array<Vector4, N> Weights;
|
||||
static const std::array<Hash, N> SingleColorHashes;
|
||||
|
||||
static bool Generate() {
|
||||
static_assert(N == 4 || N == 3);
|
||||
|
||||
table_mutex.lock();
|
||||
if (!generated) {
|
||||
hashes = new std::array<Hash, HashCount>();
|
||||
factors = new std::array<Vector4, OrderCount>();
|
||||
|
||||
const float denominator = (N == 4) ? 3.0f : 2.0f;
|
||||
|
||||
for (uint16_t i = 0; i < OrderCount; i++) {
|
||||
Histogram<N> h = Orders[i];
|
||||
if (!h.Any16()) hashes->at(h.GetPacked()) = i;
|
||||
|
||||
Vector4 factor_matrix = 0;
|
||||
for (unsigned sel = 0; sel < N; sel++) factor_matrix += (Weights[sel] * h[sel]);
|
||||
|
||||
float det = factor_matrix.Determinant2x2();
|
||||
if (fabs(det) < 1e-8f) {
|
||||
factors->at(i) = Vector4(0);
|
||||
} else {
|
||||
std::swap(factor_matrix[0], factor_matrix[3]);
|
||||
factor_matrix *= Vector4(1, -1, -1, 1);
|
||||
factor_matrix *= (denominator / 255.0f) / det;
|
||||
factors->at(i) = factor_matrix;
|
||||
}
|
||||
}
|
||||
|
||||
generated = true;
|
||||
}
|
||||
table_mutex.unlock();
|
||||
|
||||
assert(generated);
|
||||
return true;
|
||||
}
|
||||
|
||||
static Hash GetHash(Histogram<N> &hist) {
|
||||
for (unsigned i = 0; i < N; i++) {
|
||||
if (hist[i] == 16) return SingleColorHashes[i];
|
||||
}
|
||||
|
||||
assert(generated);
|
||||
assert(hashes != nullptr);
|
||||
|
||||
auto hash = hashes->at(hist.GetPacked());
|
||||
|
||||
assert(hash < OrderCount);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static Vector4 GetFactors(Hash hash) {
|
||||
assert(generated.load());
|
||||
assert(factors != nullptr);
|
||||
|
||||
return factors->at(hash);
|
||||
}
|
||||
|
||||
static bool IsSingleColor(Hash hash) { return (std::find(SingleColorHashes.begin(), SingleColorHashes.end(), hash) != SingleColorHashes.end()); }
|
||||
|
||||
private:
|
||||
static std::mutex table_mutex;
|
||||
static std::array<Hash, HashCount> *hashes;
|
||||
static std::array<Vector4, OrderCount> *factors;
|
||||
};
|
||||
|
||||
template <> std::atomic<bool> OrderTable<3>::generated;
|
||||
template <> std::atomic<bool> OrderTable<4>::generated;
|
||||
|
||||
template <> std::mutex OrderTable<3>::table_mutex;
|
||||
template <> std::mutex OrderTable<4>::table_mutex;
|
||||
|
||||
template <> std::array<OrderTable<3>::Hash, OrderTable<3>::HashCount> *OrderTable<3>::hashes;
|
||||
template <> std::array<OrderTable<4>::Hash, OrderTable<4>::HashCount> *OrderTable<4>::hashes;
|
||||
|
||||
template <> std::array<Vector4, OrderTable<3>::OrderCount> *OrderTable<3>::factors;
|
||||
template <> std::array<Vector4, OrderTable<4>::OrderCount> *OrderTable<4>::factors;
|
||||
|
||||
template <> const std::array<Vector4, 3> OrderTable<3>::Weights;
|
||||
template <> const std::array<Vector4, 4> OrderTable<4>::Weights;
|
||||
|
||||
template <> const std::array<uint16_t, 3> OrderTable<3>::SingleColorHashes;
|
||||
template <> const std::array<uint16_t, 4> OrderTable<4>::SingleColorHashes;
|
||||
|
||||
template <> const OrderTable<3>::OrderArray OrderTable<3>::Orders;
|
||||
template <> const OrderTable<4>::OrderArray OrderTable<4>::Orders;
|
||||
|
||||
template <> const OrderTable<3>::BestOrderArray OrderTable<3>::BestOrders;
|
||||
template <> const OrderTable<4>::BestOrderArray OrderTable<4>::BestOrders;
|
||||
|
||||
extern template class OrderTable<3>;
|
||||
extern template class OrderTable<4>;
|
||||
|
||||
} // namespace quicktex::s3tc
|
File diff suppressed because it is too large
Load Diff
93
quicktex/s3tc/bc1/SingleColorTable.h
Normal file
93
quicktex/s3tc/bc1/SingleColorTable.h
Normal file
@ -0,0 +1,93 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "../../util.h"
|
||||
#include "../interpolator/Interpolator.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
struct BC1MatchEntry {
|
||||
uint8_t high;
|
||||
uint8_t low;
|
||||
uint8_t error;
|
||||
};
|
||||
|
||||
using MatchList = std::array<BC1MatchEntry, 256>;
|
||||
using MatchListPtr = std::shared_ptr<MatchList>;
|
||||
using InterpolatorPtr = std::shared_ptr<Interpolator>;
|
||||
|
||||
/**
|
||||
* Lookup table for single-color blocks
|
||||
* @tparam B Number of bits (5 or 6)
|
||||
* @tparam N Number of colors (3 or 4)
|
||||
*/
|
||||
template <size_t B, size_t N> MatchListPtr SingleColorTable(InterpolatorPtr interpolator) {
|
||||
constexpr size_t Size = 1 << B;
|
||||
MatchListPtr matches = std::make_shared<MatchList>();
|
||||
|
||||
static_assert((B == 5 && Size == 32) || (B == 6 && Size == 64));
|
||||
static_assert(N == 4 || N == 3);
|
||||
|
||||
bool ideal = interpolator->IsIdeal();
|
||||
bool use_8bit = interpolator->CanInterpolate8Bit();
|
||||
|
||||
for (unsigned i = 0; i < 256; i++) {
|
||||
unsigned error = 256;
|
||||
|
||||
// TODO: Can probably avoid testing for values that definitely wont yield good results,
|
||||
// e.g. low8 and high8 both much smaller or larger than index
|
||||
for (uint8_t low = 0; low < Size; low++) {
|
||||
uint8_t low8 = (B == 5) ? scale5To8(low) : scale6To8(low);
|
||||
|
||||
for (uint8_t high = 0; high < Size; high++) {
|
||||
uint8_t high8 = (B == 5) ? scale5To8(high) : scale6To8(high);
|
||||
uint8_t value;
|
||||
|
||||
if (use_8bit) {
|
||||
value = interpolator->Interpolate8(high8, low8);
|
||||
} else {
|
||||
value = (B == 5) ? interpolator->Interpolate5(high, low) : interpolator->Interpolate6(high, low);
|
||||
}
|
||||
|
||||
unsigned new_error = iabs(value - (int)i);
|
||||
|
||||
// We only need to factor in 3% error in BC1 ideal mode.
|
||||
if (ideal) new_error += (iabs(high8 - (int)low8) * 3) / 100;
|
||||
|
||||
if ((new_error < error) || (new_error == error && low == high)) {
|
||||
assert(new_error <= UINT8_MAX);
|
||||
|
||||
(*matches)[i].low = (uint8_t)low;
|
||||
(*matches)[i].high = (uint8_t)high;
|
||||
(*matches)[i].error = (uint8_t)new_error;
|
||||
|
||||
error = new_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
} // namespace quicktex::s3tc
|
3
quicktex/s3tc/bc1/__init__.py
Normal file
3
quicktex/s3tc/bc1/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""Classes for encoding/decoding BC1 textures"""
|
||||
|
||||
from _quicktex._s3tc._bc1 import *
|
192
quicktex/s3tc/bc1/_bindings.cpp
Normal file
192
quicktex/s3tc/bc1/_bindings.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
/* Quicktex 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 "../../_bindings.h"
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#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<Interpolator>;
|
||||
|
||||
void InitBC1(py::module_ &s3tc) {
|
||||
auto bc1 = s3tc.def_submodule("_bc1", "internal bc1 module");
|
||||
|
||||
// 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(
|
||||
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<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.")
|
||||
.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_<BC1Encoder::ErrorMode>(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_<BC1Encoder::ColorMode>(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<unsigned, BC1Encoder::ColorMode>(), "level"_a = 5, "color_mode"_a = BC1Encoder::ColorMode::FourColor);
|
||||
bc1_encoder.def(py::init<unsigned, BC1Encoder::ColorMode, InterpolatorPtr>(), "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_<BC1Decoder> bc1_decoder(bc1, "BC1Decoder", R"doc(
|
||||
Decodes BC1 textures to RGB
|
||||
)doc");
|
||||
|
||||
bc1_decoder.def(py::init<bool>(), "write_alpha"_a = false);
|
||||
bc1_decoder.def(py::init<bool, InterpolatorPtr>(), "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
|
60
quicktex/s3tc/bc3/BC3Block.h
Normal file
60
quicktex/s3tc/bc3/BC3Block.h
Normal file
@ -0,0 +1,60 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "../bc1/BC1Block.h"
|
||||
#include "../bc4/BC4Block.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
class alignas(8) BC3Block {
|
||||
public:
|
||||
static constexpr int Width = 4;
|
||||
static constexpr int Height = 4;
|
||||
|
||||
using BlockPair = std::pair<BC4Block, BC1Block>;
|
||||
|
||||
BC4Block alpha_block;
|
||||
BC1Block color_block;
|
||||
|
||||
constexpr BC3Block() : alpha_block(BC4Block()), color_block(BC1Block()) {
|
||||
static_assert(sizeof(BC3Block) == 16);
|
||||
static_assert(sizeof(std::array<BC3Block, 10>) == 16 * 10);
|
||||
static_assert(alignof(BC3Block) >= 8);
|
||||
}
|
||||
|
||||
BC3Block(const BC4Block &alpha, const BC1Block &color) {
|
||||
alpha_block = alpha;
|
||||
color_block = color;
|
||||
}
|
||||
|
||||
BlockPair GetBlocks() const { return BlockPair(alpha_block, color_block); }
|
||||
|
||||
void SetBlocks(const BlockPair &blocks) {
|
||||
alpha_block = blocks.first;
|
||||
color_block = blocks.second;
|
||||
}
|
||||
|
||||
bool operator==(const BC3Block &Rhs) const { return alpha_block == Rhs.alpha_block && color_block == Rhs.color_block; }
|
||||
bool operator!=(const BC3Block &Rhs) const { return !(Rhs == *this); }
|
||||
};
|
||||
} // namespace quicktex::s3tc
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -17,18 +17,18 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "BC3Decoder.h"
|
||||
|
||||
#include "../BC1/BC1Block.h"
|
||||
#include "../BC4/BC4Block.h"
|
||||
#include "../../ColorBlock.h"
|
||||
#include "BC3Block.h"
|
||||
|
||||
namespace rgbcx {
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
class BC3Block {
|
||||
public:
|
||||
BC4Block alpha_block;
|
||||
BC1Block color_block;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
} // namespace rgbcx
|
||||
ColorBlock<4, 4> BC3Decoder::DecodeBlock(const BC3Block &block) const {
|
||||
auto output = _bc1_decoder->DecodeBlock(block.color_block, false);
|
||||
|
||||
_bc4_decoder->DecodeInto(output, block.alpha_block);
|
||||
|
||||
return output;
|
||||
}
|
||||
} // namespace quicktex::s3tc
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -21,28 +21,33 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "../BC1/BC1Decoder.h"
|
||||
#include "../BC4/BC4Decoder.h"
|
||||
#include "../BlockDecoder.h"
|
||||
#include "../BlockView.h"
|
||||
#include "../Interpolator.h"
|
||||
#include "../ndebug.h"
|
||||
#include "../../ColorBlock.h"
|
||||
#include "../../Decoder.h"
|
||||
#include "../../Texture.h"
|
||||
#include "../bc1/BC1Decoder.h"
|
||||
#include "../bc4/BC4Decoder.h"
|
||||
#include "../interpolator/Interpolator.h"
|
||||
#include "BC3Block.h"
|
||||
|
||||
namespace rgbcx {
|
||||
class BC3Decoder : public BlockDecoder<BC3Block, 4, 4> {
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
class BC3Decoder : public BlockDecoder<BlockTexture<BC3Block>> {
|
||||
public:
|
||||
using InterpolatorPtr = std::shared_ptr<Interpolator>;
|
||||
using BC1DecoderPtr = std::shared_ptr<BC1Decoder>;
|
||||
using BC4DecoderPtr = std::shared_ptr<BC4Decoder>;
|
||||
using InterpolatorPtr = std::shared_ptr<Interpolator>;
|
||||
|
||||
BC3Decoder(InterpolatorPtr interpolator = std::make_shared<Interpolator>()) : BC3Decoder(std::make_shared<BC1Decoder>(interpolator)) {}
|
||||
BC3Decoder(BC1DecoderPtr bc1_decoder, BC4DecoderPtr bc4_decoder = std::make_shared<BC4Decoder>()) : _bc1_decoder(bc1_decoder), _bc4_decoder(bc4_decoder) {}
|
||||
BC3Decoder(InterpolatorPtr interpolator) : _bc1_decoder(std::make_shared<BC1Decoder>(interpolator)), _bc4_decoder(std::make_shared<BC4Decoder>(3)) {}
|
||||
|
||||
void DecodeBlock(Color4x4 dest, BC3Block *const block) const noexcept(ndebug) override;
|
||||
BC3Decoder() : BC3Decoder(std::make_shared<Interpolator>()) {}
|
||||
|
||||
ColorBlock<4, 4> DecodeBlock(const BC3Block &block) const override;
|
||||
|
||||
BC1DecoderPtr GetBC1Decoder() const { return _bc1_decoder; }
|
||||
BC4DecoderPtr GetBC4Decoder() const { return _bc4_decoder; }
|
||||
|
||||
private:
|
||||
const BC1DecoderPtr _bc1_decoder;
|
||||
const BC4DecoderPtr _bc4_decoder;
|
||||
};
|
||||
} // namespace rgbcx
|
||||
} // namespace quicktex::s3tc
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -17,17 +17,18 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "BC3Decoder.h"
|
||||
#include "BC3Encoder.h"
|
||||
|
||||
#include "../BC1/BC1Decoder.h"
|
||||
#include "../BC4/BC4Decoder.h"
|
||||
#include "../BlockView.h"
|
||||
#include "../ndebug.h"
|
||||
#include "../../ColorBlock.h"
|
||||
#include "../bc1/BC1Block.h"
|
||||
#include "../bc4/BC4Block.h"
|
||||
#include "BC3Block.h"
|
||||
|
||||
namespace rgbcx {
|
||||
|
||||
void BC3Decoder::DecodeBlock(Color4x4 dest, BC3Block *const block) const noexcept(ndebug) {
|
||||
_bc1_decoder->DecodeBlock(dest, &(block->color_block));
|
||||
_bc4_decoder->DecodeBlock(dest, &(block->alpha_block), 3);
|
||||
namespace quicktex::s3tc {
|
||||
BC3Block BC3Encoder::EncodeBlock(const ColorBlock<4, 4> &pixels) const {
|
||||
auto output = BC3Block();
|
||||
output.color_block = _bc1_encoder->EncodeBlock(pixels);
|
||||
output.alpha_block = _bc4_encoder->EncodeBlock(pixels);
|
||||
return output;
|
||||
}
|
||||
} // namespace rgbcx
|
||||
} // namespace quicktex::s3tc
|
54
quicktex/s3tc/bc3/BC3Encoder.h
Normal file
54
quicktex/s3tc/bc3/BC3Encoder.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "../../ColorBlock.h"
|
||||
#include "../../Encoder.h"
|
||||
#include "../../Texture.h"
|
||||
#include "../bc1/BC1Encoder.h"
|
||||
#include "../bc4/BC4Encoder.h"
|
||||
#include "../interpolator/Interpolator.h"
|
||||
#include "BC3Block.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
class BC3Encoder : public BlockEncoder<BlockTexture<BC3Block>> {
|
||||
public:
|
||||
using BC1EncoderPtr = std::shared_ptr<BC1Encoder>;
|
||||
using BC4EncoderPtr = std::shared_ptr<BC4Encoder>;
|
||||
using InterpolatorPtr = std::shared_ptr<Interpolator>;
|
||||
|
||||
BC3Encoder(unsigned level, InterpolatorPtr interpolator)
|
||||
: _bc1_encoder(std::make_shared<BC1Encoder>(level, BC1Encoder::ColorMode::FourColor, interpolator)), _bc4_encoder(std::make_shared<BC4Encoder>(3)) {}
|
||||
|
||||
BC3Encoder(unsigned level = 5) : BC3Encoder(level, std::make_shared<Interpolator>()) {}
|
||||
|
||||
BC3Block EncodeBlock(const ColorBlock<4, 4>& pixels) const override;
|
||||
|
||||
BC1EncoderPtr GetBC1Encoder() const { return _bc1_encoder; }
|
||||
BC4EncoderPtr GetBC4Encoder() const { return _bc4_encoder; }
|
||||
|
||||
private:
|
||||
const BC1EncoderPtr _bc1_encoder;
|
||||
const BC4EncoderPtr _bc4_encoder;
|
||||
};
|
||||
} // namespace quicktex::s3tc
|
1
quicktex/s3tc/bc3/__init__.py
Normal file
1
quicktex/s3tc/bc3/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from _quicktex._s3tc._bc3 import *
|
122
quicktex/s3tc/bc3/_bindings.cpp
Normal file
122
quicktex/s3tc/bc3/_bindings.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
/* Quicktex 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 "../../_bindings.h"
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "../../Decoder.h"
|
||||
#include "../../Encoder.h"
|
||||
#include "../interpolator/Interpolator.h"
|
||||
#include "BC3Decoder.h"
|
||||
#include "BC3Encoder.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
namespace quicktex::bindings {
|
||||
|
||||
using namespace quicktex::s3tc;
|
||||
using namespace pybind11::literals;
|
||||
using InterpolatorPtr = std::shared_ptr<Interpolator>;
|
||||
using BC1EncoderPtr = std::shared_ptr<BC1Encoder>;
|
||||
using BC1DecoderPtr = std::shared_ptr<BC1Decoder>;
|
||||
|
||||
void InitBC3(py::module_ &s3tc) {
|
||||
auto bc3 = s3tc.def_submodule("_bc3", "internal bc3 module");
|
||||
|
||||
// region BC3Block
|
||||
auto bc3_block = BindBlock<BC3Block>(bc3, "BC3Block");
|
||||
bc3_block.doc() = "A single BC3 block.";
|
||||
|
||||
bc3_block.def(py::init<>());
|
||||
bc3_block.def(py::init<BC4Block, BC1Block>(), "alpha_block"_a, "color_block"_a, R"doc(
|
||||
Create a new BC3Block out of a BC4 block and a BC1 block.
|
||||
|
||||
:param BC4Block alpha_block: The BC4 block used for alpha data.
|
||||
:param BC1Block color_block: The BC1 block used for RGB data.
|
||||
)doc");
|
||||
|
||||
bc3_block.def_readwrite("alpha_block", &BC3Block::alpha_block, "The BC4 block used for alpha data.");
|
||||
bc3_block.def_readwrite("color_block", &BC3Block::color_block, "The BC1 block used for rgb data.");
|
||||
bc3_block.def_property("blocks", &BC3Block::GetBlocks, &BC3Block::SetBlocks, "The BC4 and BC1 blocks that make up this block as a 2-tuple.");
|
||||
// endregion
|
||||
|
||||
// region BC3Texture
|
||||
auto bc3_texture = BindBlockTexture<BC3Block>(bc3, "BC3Texture");
|
||||
bc3_texture.doc() = "A texture comprised of BC3 blocks.";
|
||||
// endregion
|
||||
|
||||
// region BC3Encoder
|
||||
py::class_<BC3Encoder> bc3_encoder(bc3, "BC3Encoder", R"doc(
|
||||
Encodes RGBA textures to BC3
|
||||
)doc");
|
||||
|
||||
bc3_encoder.def(py::init<unsigned>(), "level"_a = 5);
|
||||
bc3_encoder.def(py::init<unsigned, InterpolatorPtr>(), "level"_a, "interpolator"_a, R"doc(
|
||||
Create a new BC3 encoder with the specified preset level and interpolator.
|
||||
|
||||
:param int level: The preset level of the resulting encoder, between 0 and 18 inclusive.
|
||||
See :py:meth:`~quicktex.s3tc.bc1.BC1Encoder.set_level` for more information. Default: 5.
|
||||
:param Interpolator interpolator: The interpolation mode to use for encoding. Default: :py:class:`~quicktex.s3tc.interpolator.Interpolator`.
|
||||
)doc");
|
||||
|
||||
bc3_encoder.def("encode", &BC3Encoder::Encode, "texture"_a, R"doc(
|
||||
Encode a raw texture into a new BC3Texture using the encoder's current settings.
|
||||
|
||||
:param RawTexture texture: Input texture to encode.
|
||||
:returns: A new BC3Texture with the same dimension as the input.
|
||||
)doc");
|
||||
|
||||
bc3_encoder.def_property_readonly("bc1_encoder", &BC3Encoder::GetBC1Encoder,
|
||||
"Internal :py:class:`~quicktex.s3tc.bc1.BC1Encoder` used for RGB data. Readonly.");
|
||||
bc3_encoder.def_property_readonly("bc4_encoder", &BC3Encoder::GetBC4Encoder,
|
||||
"Internal :py:class:`~quicktex.s3tc.bc4.BC4Encoder` used for alpha data. Readonly.");
|
||||
// endregion
|
||||
|
||||
// region BC3Decoder
|
||||
py::class_<BC3Decoder> bc3_decoder(bc3, "BC3Decoder", R"doc(
|
||||
Decodes BC3 textures to RGBA
|
||||
)doc");
|
||||
|
||||
bc3_decoder.def(py::init<>());
|
||||
bc3_decoder.def(py::init<InterpolatorPtr>(), "interpolator"_a, R"doc(
|
||||
Create a new BC3 decoder with the specified interpolator.
|
||||
|
||||
:param Interpolator interpolator: The interpolation mode to use for decoding. Default: :py:class:`~quicktex.s3tc.interpolator.Interpolator`.
|
||||
)doc");
|
||||
|
||||
bc3_decoder.def("decode", &BC3Decoder::Decode, "texture"_a, R"doc(
|
||||
Decode a BC3 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");
|
||||
|
||||
bc3_decoder.def_property_readonly("bc1_decoder", &BC3Decoder::GetBC1Decoder,
|
||||
"Internal :py:class:`~quicktex.s3tc.bc1.BC1Decoder` used for RGB data. Readonly.");
|
||||
bc3_decoder.def_property_readonly("bc4_decoder", &BC3Decoder::GetBC4Decoder,
|
||||
"Internal :py:class:`~quicktex.s3tc.bc4.BC4Decoder` used for alpha data. Readonly.");
|
||||
// endregion
|
||||
}
|
||||
} // namespace quicktex::bindings
|
69
quicktex/s3tc/bc4/BC4Block.cpp
Normal file
69
quicktex/s3tc/bc4/BC4Block.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
/* Quicktex 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 "BC4Block.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../../util.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
BC4Block::SelectorArray BC4Block::GetSelectors() const {
|
||||
auto packed = Pack<uint8_t, uint64_t, 8, SelectorSize>(_selectors);
|
||||
auto rows = Unpack<uint64_t, uint16_t, SelectorBits * Width, Height>(packed);
|
||||
return MapArray(rows, Unpack<uint16_t, uint8_t, SelectorBits, Width>);
|
||||
}
|
||||
|
||||
void BC4Block::SetSelectors(const BC4Block::SelectorArray& unpacked) {
|
||||
for (unsigned y = 0; y < (unsigned)Height; y++) {
|
||||
if (std::any_of(unpacked[y].begin(), unpacked[y].end(), [](uint8_t i) { return i > SelectorMax; }))
|
||||
throw std::invalid_argument("Selector value out of bounds.");
|
||||
}
|
||||
auto rows = MapArray(unpacked, Pack<uint8_t, uint16_t, SelectorBits, Width>);
|
||||
auto packed = Pack<uint16_t, uint64_t, SelectorBits * Width, Height>(rows);
|
||||
_selectors = Unpack<uint64_t, uint8_t, 8, SelectorSize>(packed);
|
||||
}
|
||||
|
||||
std::array<uint8_t, 8> BC4Block::GetValues6() const {
|
||||
return {alpha0,
|
||||
alpha1,
|
||||
static_cast<uint8_t>((alpha0 * 4 + alpha1) / 5),
|
||||
static_cast<uint8_t>((alpha0 * 3 + alpha1 * 2) / 5),
|
||||
static_cast<uint8_t>((alpha0 * 2 + alpha1 * 3) / 5),
|
||||
static_cast<uint8_t>((alpha0 + alpha1 * 4) / 5),
|
||||
0,
|
||||
0xFF};
|
||||
}
|
||||
|
||||
std::array<uint8_t, 8> BC4Block::GetValues8() const {
|
||||
return {alpha0,
|
||||
alpha1,
|
||||
static_cast<uint8_t>((alpha0 * 6 + alpha1) / 7),
|
||||
static_cast<uint8_t>((alpha0 * 5 + alpha1 * 2) / 7),
|
||||
static_cast<uint8_t>((alpha0 * 4 + alpha1 * 3) / 7),
|
||||
static_cast<uint8_t>((alpha0 * 3 + alpha1 * 4) / 7),
|
||||
static_cast<uint8_t>((alpha0 * 2 + alpha1 * 5) / 7),
|
||||
static_cast<uint8_t>((alpha0 + alpha1 * 6) / 7)};
|
||||
}
|
||||
|
||||
bool BC4Block::operator==(const BC4Block& Rhs) const { return alpha0 == Rhs.alpha0 && alpha1 == Rhs.alpha1 && _selectors == Rhs._selectors; }
|
||||
bool BC4Block::operator!=(const BC4Block& Rhs) const { return !(Rhs == *this); }
|
||||
} // namespace quicktex::s3tc
|
107
quicktex/s3tc/bc4/BC4Block.h
Normal file
107
quicktex/s3tc/bc4/BC4Block.h
Normal file
@ -0,0 +1,107 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <utility>
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
class alignas(8) BC4Block {
|
||||
public:
|
||||
static constexpr size_t Width = 4;
|
||||
static constexpr size_t Height = 4;
|
||||
|
||||
static constexpr size_t SelectorSize = 6; // size of selector array in bytes
|
||||
static constexpr size_t SelectorBits = 3; // size of a selector in bits
|
||||
static constexpr size_t SelectorMax = (1 << SelectorBits) - 1; // maximum value of a selector
|
||||
|
||||
using SelectorArray = std::array<std::array<uint8_t, Width>, Height>;
|
||||
using AlphaPair = std::pair<uint8_t, uint8_t>;
|
||||
|
||||
uint8_t alpha0; // first endpoint
|
||||
uint8_t alpha1; // second endpoint
|
||||
|
||||
private:
|
||||
std::array<uint8_t, SelectorSize> _selectors; // internal array of selector bytes
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
|
||||
/// Create a new BC4Block
|
||||
constexpr BC4Block() : alpha0(0), alpha1(0), _selectors() {
|
||||
static_assert(sizeof(BC4Block) == 8);
|
||||
static_assert(sizeof(std::array<BC4Block, 10>) == 8 * 10);
|
||||
static_assert(alignof(BC4Block) >= 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BC4Block
|
||||
* @param valpha0 first endpoint value
|
||||
* @param valpha1 second endpoint value
|
||||
* @param selectors the selectors as a 4x4 array of integers, between 0 and 7 inclusive.
|
||||
*/
|
||||
BC4Block(uint8_t valpha0, uint8_t valpha1, const SelectorArray& selectors) {
|
||||
alpha0 = valpha0;
|
||||
alpha1 = valpha1;
|
||||
SetSelectors(selectors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new solid BC4Block
|
||||
* @param alpha first endpoint value
|
||||
*/
|
||||
BC4Block(uint8_t alpha) {
|
||||
alpha0 = alpha;
|
||||
alpha1 = alpha;
|
||||
_selectors.fill(0);
|
||||
}
|
||||
|
||||
/// Get a alpha0 and alpha1 as a pair
|
||||
AlphaPair GetAlphas() const { return AlphaPair(alpha0, alpha1); }
|
||||
|
||||
/// Set alpha0 and alpha1 as a pair
|
||||
void SetAlphas(AlphaPair as) {
|
||||
alpha0 = as.first;
|
||||
alpha1 = as.second;
|
||||
}
|
||||
|
||||
/// Get the block's selectors as a 4x4 array of integers between 0 and 7 inclusive.
|
||||
SelectorArray GetSelectors() const;
|
||||
|
||||
/// Get the block's selectors as a 4x4 array of integers between 0 and 7 inclusive.
|
||||
void SetSelectors(const SelectorArray& unpacked);
|
||||
|
||||
/// True if the block uses 6-value interpolation, i.e. alpha0 <= alpha1.
|
||||
bool Is6Value() const { return alpha0 <= alpha1; }
|
||||
|
||||
/// The interpolated values of this block as an array of 8 integers.
|
||||
std::array<uint8_t, 8> GetValues() const { return Is6Value() ? GetValues6() : GetValues8(); }
|
||||
|
||||
bool operator==(const BC4Block& Rhs) const;
|
||||
bool operator!=(const BC4Block& Rhs) const;
|
||||
|
||||
private:
|
||||
std::array<uint8_t, 8> GetValues6() const;
|
||||
std::array<uint8_t, 8> GetValues8() const;
|
||||
};
|
||||
} // namespace quicktex::s3tc
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -19,27 +19,34 @@
|
||||
|
||||
#include "BC4Decoder.h"
|
||||
|
||||
#include <assert.h> // for assert
|
||||
#include <array> // for array
|
||||
#include <cassert> // for assert
|
||||
|
||||
#include <array> // for array
|
||||
|
||||
#include "../BlockView.h" // for ColorBlock
|
||||
#include "../Color.h" // for Color
|
||||
#include "../ndebug.h" // for ndebug
|
||||
#include "../../Color.h"
|
||||
#include "../../ColorBlock.h"
|
||||
#include "BC4Block.h"
|
||||
|
||||
void rgbcx::BC4Decoder::DecodeBlock(Byte4x4 dest, BC4Block *const block) const noexcept(ndebug) {
|
||||
auto l = block->GetLowAlpha();
|
||||
auto h = block->GetHighAlpha();
|
||||
|
||||
auto values = BC4Block::GetValues(l, h);
|
||||
auto selectors = block->UnpackSelectors();
|
||||
namespace quicktex::s3tc {
|
||||
void BC4Decoder::DecodeInto(ColorBlock<4, 4> &dest, const BC4Block &block) const {
|
||||
auto values = block.GetValues();
|
||||
auto selectors = block.GetSelectors();
|
||||
|
||||
for (unsigned y = 0; y < 4; y++) {
|
||||
for (unsigned x = 0; x < 4; x++) {
|
||||
const auto selector = selectors[y][x];
|
||||
assert(selector < 8);
|
||||
dest.Set(x, y, values[selector]);
|
||||
|
||||
auto color = dest.Get(x, y);
|
||||
color[_channel] = values[selector];
|
||||
dest.Set(x, y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColorBlock<4, 4> BC4Decoder::DecodeBlock(const BC4Block &block) const {
|
||||
auto output = ColorBlock<4, 4>();
|
||||
DecodeInto(output, block);
|
||||
|
||||
return output;
|
||||
}
|
||||
} // namespace quicktex::s3tc
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -19,15 +19,30 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../BC4/BC4Block.h"
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace rgbcx {
|
||||
#include "../../ColorBlock.h"
|
||||
#include "../../Decoder.h"
|
||||
#include "../../Texture.h"
|
||||
#include "BC4Block.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
class BC5Block {
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
class BC4Decoder : public BlockDecoder<BlockTexture<BC4Block>> {
|
||||
public:
|
||||
BC4Block chan0_block;
|
||||
BC4Block chan1_block;
|
||||
BC4Decoder(uint8_t channel = 3) {
|
||||
if (channel >= 4U) throw std::invalid_argument("Channel out of range");
|
||||
_channel = channel;
|
||||
}
|
||||
|
||||
ColorBlock<4, 4> DecodeBlock(const BC4Block &block) const override;
|
||||
|
||||
void DecodeInto(ColorBlock<4, 4> &dest, const BC4Block &block) const;
|
||||
|
||||
uint8_t GetChannel() const { return _channel; }
|
||||
|
||||
private:
|
||||
uint8_t _channel;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
} // namespace rgbcx
|
||||
} // namespace quicktex::s3tc
|
74
quicktex/s3tc/bc4/BC4Encoder.cpp
Normal file
74
quicktex/s3tc/bc4/BC4Encoder.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
/* Quicktex 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 "BC4Encoder.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../../Color.h"
|
||||
#include "../../ColorBlock.h"
|
||||
#include "BC4Block.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
BC4Block BC4Encoder::EncodeBlock(const ColorBlock<4, 4> &pixels) const {
|
||||
uint8_t min = UINT8_MAX;
|
||||
uint8_t max = 0;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
auto value = pixels.Get(i)[_channel];
|
||||
min = std::min(min, value);
|
||||
max = std::max(max, value);
|
||||
}
|
||||
|
||||
if (max == min) {
|
||||
return BC4Block(min); // solid block
|
||||
}
|
||||
|
||||
auto selectors = BC4Block::SelectorArray();
|
||||
const static std::array<uint8_t, 8> Levels = {1U, 7U, 6U, 5U, 4U, 3U, 2U, 0U}; // selector value options in linear order
|
||||
|
||||
// BC4 floors in its divisions, which we compensate for with the 4 bias.
|
||||
// This function is optimal for all possible inputs (i.e. it outputs the same results as checking all 8 values and choosing the closest one).
|
||||
const int bias = 4 - min * 14;
|
||||
const int delta = max - min;
|
||||
|
||||
// Min is now 0. Compute thresholds between values by scaling max. It's x14 because we're adding two x7 scale factors.
|
||||
// bias is applied here
|
||||
std::array<int, 7> thresholds = {};
|
||||
for (unsigned i = 0; i < 7; i++) thresholds[i] = delta * (1 + (2 * (int)i)) - bias;
|
||||
|
||||
// iterate over all values and calculate selectors
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
int value = (int)pixels.Get(x, y)[_channel] * 14; // multiply by demonimator
|
||||
|
||||
// level = number of thresholds this value is greater than
|
||||
unsigned level = 0;
|
||||
for (unsigned c = 0; c < 7; c++) level += value >= thresholds[c];
|
||||
|
||||
selectors[(unsigned)y][(unsigned)x] = Levels[level];
|
||||
}
|
||||
}
|
||||
|
||||
return BC4Block(max, min, selectors);
|
||||
}
|
||||
|
||||
} // namespace quicktex::s3tc
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -19,19 +19,28 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../BlockEncoder.h"
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../../ColorBlock.h"
|
||||
#include "../../Encoder.h"
|
||||
#include "../../Texture.h"
|
||||
#include "BC4Block.h"
|
||||
namespace rgbcx {
|
||||
|
||||
class BC4Encoder : public BlockEncoder<BC4Block, 4, 4> {
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
class BC4Encoder : public BlockEncoder<BlockTexture<BC4Block>> {
|
||||
public:
|
||||
BC4Encoder(const uint8_t channel) : _channel(channel) { assert(channel < 4); }
|
||||
BC4Encoder(const uint8_t channel) {
|
||||
if (channel >= 4) throw std::invalid_argument("Channel out of range");
|
||||
_channel = channel;
|
||||
}
|
||||
|
||||
void EncodeBlock(Color4x4 pixels, BC4Block *dest) const override { EncodeBlock(pixels.GetChannel(_channel), dest); }
|
||||
void EncodeBlock(Color4x4 pixels, BC4Block *const dest, uint8_t channel) const noexcept(ndebug) { EncodeBlock(pixels.GetChannel(channel), dest); }
|
||||
void EncodeBlock(Byte4x4 pixels, BC4Block *const dest) const noexcept(ndebug);
|
||||
BC4Block EncodeBlock(const ColorBlock<4, 4> &pixels) const override;
|
||||
|
||||
uint8_t GetChannel() const { return _channel; }
|
||||
|
||||
private:
|
||||
const uint8_t _channel;
|
||||
uint8_t _channel;
|
||||
};
|
||||
} // namespace rgbcx
|
||||
} // namespace quicktex::s3tc
|
1
quicktex/s3tc/bc4/__init__.py
Normal file
1
quicktex/s3tc/bc4/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from _quicktex._s3tc._bc4 import *
|
125
quicktex/s3tc/bc4/_bindings.cpp
Normal file
125
quicktex/s3tc/bc4/_bindings.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
/* Quicktex 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 "../../_bindings.h"
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "../../Decoder.h"
|
||||
#include "../../Encoder.h"
|
||||
#include "BC4Decoder.h"
|
||||
#include "BC4Encoder.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
namespace quicktex::bindings {
|
||||
|
||||
using namespace quicktex::s3tc;
|
||||
|
||||
void InitBC4(py::module_ &s3tc) {
|
||||
auto bc4 = s3tc.def_submodule("_bc4", "internal bc4 module");
|
||||
|
||||
// region BC4Block
|
||||
auto bc4_block = BindBlock<BC4Block>(bc4, "BC4Block");
|
||||
bc4_block.doc() = "A single BC4 block.";
|
||||
|
||||
bc4_block.def(py::init<>());
|
||||
bc4_block.def(py::init<uint8_t, uint8_t, BC4Block::SelectorArray>(), "endpoint0"_a, "endpoint1"_a, "selectors"_a, R"doc(
|
||||
Create a new BC4Block with the specified endpoints and selectors.
|
||||
|
||||
:param int endpoint0: The first endpoint.
|
||||
:param int endpoint1: The second endpoint.
|
||||
:param selectors: the selectors as a 4x4 list of integers, between 0 and 7 inclusive.
|
||||
)doc");
|
||||
|
||||
bc4_block.def_property("endpoints", &BC4Block::GetAlphas, &BC4Block::SetAlphas, "The block's endpoint values as a 2-tuple.");
|
||||
bc4_block.def_property("selectors", &BC4Block::GetSelectors, &BC4Block::SetSelectors, R"doc(
|
||||
The block's selectors as a 4x4 list of integers between 0 and 7 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");
|
||||
bc4_block.def_property_readonly("values", &BC4Block::GetValues, R"doc(
|
||||
The interpolated values used to decode the block, coresponding with the indices in :py:attr:`selectors`.
|
||||
)doc");
|
||||
bc4_block.def_property_readonly("is_6value", &BC4Block::Is6Value, R"doc(
|
||||
"True if the block uses 6-value interpolation, i.e. endpoint0 <= endpoint1. Readonly.
|
||||
)doc");
|
||||
// endregion
|
||||
|
||||
// region BC4Texture
|
||||
auto bc4_texture = BindBlockTexture<BC4Block>(bc4, "BC4Texture");
|
||||
bc4_texture.doc() = "A texture comprised of BC4 blocks.";
|
||||
// endregion
|
||||
|
||||
// region BC4Encoder
|
||||
py::class_<BC4Encoder> bc4_encoder(bc4, "BC4Encoder", R"doc(
|
||||
Encodes single-channel textures to BC4.
|
||||
)doc");
|
||||
|
||||
bc4_encoder.def(py::init<uint8_t>(), py::arg("channel") = 3, R"doc(
|
||||
Create a new BC4 encoder with the specified channel
|
||||
|
||||
:param int channel: the channel that will be read from. 0 to 3 inclusive. Default: 3 (alpha).
|
||||
)doc");
|
||||
|
||||
bc4_encoder.def("encode", &BC4Encoder::Encode, "texture"_a, R"doc(
|
||||
Encode a raw texture into a new BC4Texture using the encoder's current settings.
|
||||
|
||||
:param RawTexture texture: Input texture to encode.
|
||||
:returns: A new BC4Texture with the same dimension as the input.
|
||||
)doc");
|
||||
|
||||
bc4_encoder.def_property_readonly("channel", &BC4Encoder::GetChannel, "The channel that will be read from. 0 to 3 inclusive. Readonly.");
|
||||
// endregion
|
||||
|
||||
// region BC4Decoder
|
||||
py::class_<BC4Decoder> bc4_decoder(bc4, "BC4Decoder", R"doc(
|
||||
Decodes BC4 textures to a single-channel.
|
||||
)doc");
|
||||
|
||||
bc4_decoder.def(py::init<uint8_t>(), py::arg("channel") = 3, R"doc(
|
||||
Create a new BC4 decoder with the specified channel
|
||||
|
||||
:param int channel: The channel that will be written to. 0 to 3 inclusive. Default: 3 (alpha).
|
||||
)doc");
|
||||
|
||||
bc4_decoder.def("decode", &BC4Decoder::Decode, "texture"_a, R"doc(
|
||||
Decode a BC4 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");
|
||||
|
||||
bc4_decoder.def_property_readonly("channel", &BC4Decoder::GetChannel, "The channel that will be written to. 0 to 3 inclusive. Readonly.");
|
||||
// endregion
|
||||
}
|
||||
|
||||
} // namespace quicktex::bindings
|
59
quicktex/s3tc/bc5/BC5Block.h
Normal file
59
quicktex/s3tc/bc5/BC5Block.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "../bc4/BC4Block.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
class alignas(8) BC5Block {
|
||||
public:
|
||||
static constexpr int Width = 4;
|
||||
static constexpr int Height = 4;
|
||||
|
||||
using BlockPair = std::pair<BC4Block, BC4Block>;
|
||||
|
||||
BC4Block chan0_block;
|
||||
BC4Block chan1_block;
|
||||
|
||||
constexpr BC5Block() : chan0_block(BC4Block()), chan1_block(BC4Block()) {
|
||||
static_assert(sizeof(BC5Block) == 16);
|
||||
static_assert(sizeof(std::array<BC5Block, 10>) == 16 * 10);
|
||||
static_assert(alignof(BC5Block) >= 8);
|
||||
}
|
||||
|
||||
BC5Block(const BC4Block &chan0, const BC4Block &chan1) {
|
||||
chan0_block = chan0;
|
||||
chan1_block = chan1;
|
||||
}
|
||||
|
||||
BlockPair GetBlocks() const { return BlockPair(chan0_block, chan1_block); }
|
||||
|
||||
void SetBlocks(const BlockPair &pair) {
|
||||
chan0_block = pair.first;
|
||||
chan1_block = pair.second;
|
||||
}
|
||||
|
||||
bool operator==(const BC5Block &Rhs) const { return chan0_block == Rhs.chan0_block && chan1_block == Rhs.chan1_block; }
|
||||
bool operator!=(const BC5Block &Rhs) const { return !(Rhs == *this); }
|
||||
};
|
||||
} // namespace quicktex::s3tc
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -19,14 +19,14 @@
|
||||
|
||||
#include "BC5Decoder.h"
|
||||
|
||||
#include "../BC4/BC4Decoder.h"
|
||||
#include "../BlockView.h"
|
||||
#include "../ndebug.h"
|
||||
#include "../../ColorBlock.h"
|
||||
#include "BC5Block.h"
|
||||
|
||||
namespace rgbcx {
|
||||
|
||||
void BC5Decoder::DecodeBlock(Color4x4 dest, BC5Block *const block) const noexcept(ndebug) {
|
||||
_bc4_decoder->DecodeBlock(dest, &block->chan0_block, _chan0);
|
||||
_bc4_decoder->DecodeBlock(dest, &block->chan1_block, _chan1);
|
||||
namespace quicktex::s3tc {
|
||||
ColorBlock<4, 4> BC5Decoder::DecodeBlock(const BC5Block &block) const {
|
||||
auto output = ColorBlock<4, 4>();
|
||||
_chan0_decoder->DecodeInto(output, block.chan0_block);
|
||||
_chan1_decoder->DecodeInto(output, block.chan1_block);
|
||||
return output;
|
||||
}
|
||||
} // namespace rgbcx
|
||||
} // namespace quicktex::s3tc
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -19,36 +19,36 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../BC4/BC4Decoder.h"
|
||||
#include "../BlockDecoder.h"
|
||||
#include "../BlockView.h"
|
||||
#include "../ndebug.h"
|
||||
#include "../../ColorBlock.h"
|
||||
#include "../../Decoder.h"
|
||||
#include "../../Texture.h"
|
||||
#include "../bc4/BC4Decoder.h"
|
||||
#include "BC5Block.h"
|
||||
|
||||
namespace rgbcx {
|
||||
class BC5Decoder : public BlockDecoder<BC5Block, 4, 4> {
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
class BC5Decoder : public BlockDecoder<BlockTexture<BC5Block>> {
|
||||
public:
|
||||
using ChannelPair = std::tuple<uint8_t, uint8_t>;
|
||||
using BC4DecoderPtr = std::shared_ptr<BC4Decoder>;
|
||||
using BC4DecoderPair = std::tuple<BC4DecoderPtr, BC4DecoderPtr>;
|
||||
|
||||
BC5Decoder(uint8_t chan0 = 0, uint8_t chan1 = 1) : BC5Decoder(std::make_shared<BC4Decoder>(), chan0, chan1) {}
|
||||
BC5Decoder(BC4DecoderPtr bc4_decoder, uint8_t chan0 = 0, uint8_t chan1 = 1) : _bc4_decoder(bc4_decoder), _chan0(chan0), _chan1(chan1) {
|
||||
assert(chan0 < 4U);
|
||||
assert(chan1 < 4U);
|
||||
assert(chan0 != chan1);
|
||||
}
|
||||
BC5Decoder(uint8_t chan0 = 0, uint8_t chan1 = 1) : BC5Decoder(std::make_shared<BC4Decoder>(chan0), std::make_shared<BC4Decoder>(chan1)) {}
|
||||
BC5Decoder(BC4DecoderPtr chan0_decoder, BC4DecoderPtr chan1_decoder) : _chan0_decoder(chan0_decoder), _chan1_decoder(chan1_decoder) {}
|
||||
|
||||
void DecodeBlock(Color4x4 dest, BC5Block *const block) const noexcept(ndebug) override;
|
||||
ColorBlock<4, 4> DecodeBlock(const BC5Block &block) const override;
|
||||
|
||||
constexpr size_t GetChannel0() const { return _chan0; }
|
||||
constexpr size_t GetChannel1() const { return _chan1; }
|
||||
ChannelPair GetChannels() const { return ChannelPair(_chan0_decoder->GetChannel(), _chan1_decoder->GetChannel()); }
|
||||
|
||||
BC4DecoderPair GetBC4Decoders() const { return BC4DecoderPair(_chan0_decoder, _chan1_decoder); }
|
||||
|
||||
private:
|
||||
const BC4DecoderPtr _bc4_decoder;
|
||||
const uint8_t _chan0;
|
||||
const uint8_t _chan1;
|
||||
const BC4DecoderPtr _chan0_decoder;
|
||||
const BC4DecoderPtr _chan1_decoder;
|
||||
};
|
||||
} // namespace rgbcx
|
||||
} // namespace quicktex::s3tc
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -17,9 +17,16 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "BC5Encoder.h"
|
||||
|
||||
namespace rgbcx {
|
||||
#include "../../ColorBlock.h"
|
||||
#include "../bc4/BC4Block.h"
|
||||
|
||||
class Image {};
|
||||
} // namespace rgbcx
|
||||
namespace quicktex::s3tc {
|
||||
BC5Block BC5Encoder::EncodeBlock(const ColorBlock<4, 4> &pixels) const {
|
||||
auto output = BC5Block();
|
||||
output.chan0_block = _chan0_encoder->EncodeBlock(pixels);
|
||||
output.chan1_block = _chan1_encoder->EncodeBlock(pixels);
|
||||
return output;
|
||||
}
|
||||
} // namespace quicktex::s3tc
|
53
quicktex/s3tc/bc5/BC5Encoder.h
Normal file
53
quicktex/s3tc/bc5/BC5Encoder.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* Quicktex 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../../ColorBlock.h"
|
||||
#include "../../Encoder.h"
|
||||
#include "../../Texture.h"
|
||||
#include "../bc4/BC4Encoder.h"
|
||||
#include "BC5Block.h"
|
||||
|
||||
namespace quicktex::s3tc {
|
||||
class BC5Encoder : public BlockEncoder<BlockTexture<BC5Block>> {
|
||||
public:
|
||||
using ChannelPair = std::tuple<uint8_t, uint8_t>;
|
||||
using BC4EncoderPtr = std::shared_ptr<BC4Encoder>;
|
||||
using BC4EncoderPair = std::tuple<BC4EncoderPtr, BC4EncoderPtr>;
|
||||
|
||||
BC5Encoder(uint8_t chan0 = 0, uint8_t chan1 = 1) : BC5Encoder(std::make_shared<BC4Encoder>(chan0), std::make_shared<BC4Encoder>(chan1)) {}
|
||||
BC5Encoder(BC4EncoderPtr chan0_encoder, BC4EncoderPtr chan1_encoder) : _chan0_encoder(chan0_encoder), _chan1_encoder(chan1_encoder) {}
|
||||
|
||||
BC5Block EncodeBlock(const ColorBlock<4, 4> &pixels) const override;
|
||||
|
||||
ChannelPair GetChannels() const { return ChannelPair(_chan0_encoder->GetChannel(), _chan1_encoder->GetChannel()); }
|
||||
|
||||
BC4EncoderPair GetBC4Encoders() const { return BC4EncoderPair(_chan0_encoder, _chan1_encoder); }
|
||||
|
||||
private:
|
||||
const BC4EncoderPtr _chan0_encoder;
|
||||
const BC4EncoderPtr _chan1_encoder;
|
||||
};
|
||||
} // namespace quicktex::s3tc
|
1
quicktex/s3tc/bc5/__init__.py
Normal file
1
quicktex/s3tc/bc5/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from _quicktex._s3tc._bc5 import *
|
111
quicktex/s3tc/bc5/_bindings.cpp
Normal file
111
quicktex/s3tc/bc5/_bindings.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
/* Quicktex 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 "../../_bindings.h"
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../../Decoder.h"
|
||||
#include "../../Encoder.h"
|
||||
#include "BC5Decoder.h"
|
||||
#include "BC5Encoder.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
namespace quicktex::bindings {
|
||||
|
||||
using namespace quicktex::s3tc;
|
||||
using namespace quicktex::s3tc;
|
||||
|
||||
void InitBC5(py::module_ &s3tc) {
|
||||
auto bc5 = s3tc.def_submodule("_bc5", "internal bc5 module");
|
||||
|
||||
// region BC5Block
|
||||
auto bc5_block = BindBlock<BC5Block>(bc5, "BC5Block");
|
||||
bc5_block.doc() = "A single BC5 block.";
|
||||
|
||||
bc5_block.def(py::init<>());
|
||||
bc5_block.def(py::init<BC4Block, BC4Block>(), "chan0_block"_a, "chan1_block"_a, R"doc(
|
||||
Create a new BC5Block out of two BC4 blocks.
|
||||
|
||||
:param BC4Block chan0_block: The BC4 block used for the first channel.
|
||||
:param BC4Block chan1_block: The BC1 block used for the second channel.
|
||||
)doc");
|
||||
|
||||
bc5_block.def_readwrite("chan0_block", &BC5Block::chan0_block, "The BC4 block used for the first channel.");
|
||||
bc5_block.def_readwrite("chan1_block", &BC5Block::chan1_block, "The BC4 block used for the second channel.");
|
||||
bc5_block.def_property("blocks", &BC5Block::GetBlocks, &BC5Block::SetBlocks, "The BC4 and BC1 blocks that make up this block as a 2-tuple.");
|
||||
// endregion
|
||||
|
||||
// region BC5Texture
|
||||
auto bc5_texture = BindBlockTexture<BC5Block>(bc5, "BC5Texture");
|
||||
bc5_texture.doc() = "A texture comprised of BC5 blocks.";
|
||||
// endregion
|
||||
|
||||
// region BC5Encoder
|
||||
py::class_<BC5Encoder> bc5_encoder(bc5, "BC5Encoder", R"doc(
|
||||
Encodes dual-channel textures to BC5.
|
||||
)doc");
|
||||
|
||||
bc5_encoder.def(py::init<uint8_t, uint8_t>(), py::arg("chan0") = 0, py::arg("chan1") = 1, R"doc(
|
||||
Create a new BC5 encoder with the specified channels
|
||||
|
||||
:param int chan0: the first channel that will be read from. 0 to 3 inclusive. Default: 0 (red).
|
||||
:param int chan1: the second channel that will be read from. 0 to 3 inclusive. Default: 1 (green).
|
||||
)doc");
|
||||
|
||||
bc5_encoder.def("encode", &BC5Encoder::Encode, "texture"_a, R"doc(
|
||||
Encode a raw texture into a new BC5Texture using the encoder's current settings.
|
||||
|
||||
:param RawTexture texture: Input texture to encode.
|
||||
:returns: A new BC5Texture with the same dimension as the input.
|
||||
)doc");
|
||||
|
||||
bc5_encoder.def_property_readonly("channels", &BC5Encoder::GetChannels, "A 2-tuple of channels that will be read from. 0 to 3 inclusive. Readonly.");
|
||||
bc5_encoder.def_property_readonly("bc4_encoders", &BC5Encoder::GetBC4Encoders,
|
||||
"2-tuple of internal :py:class:`~quicktex.s3tc.bc4.BC4Encoder` s used for each channel. Readonly.");
|
||||
// endregion
|
||||
|
||||
// region BC5Decoder
|
||||
py::class_<BC5Decoder> bc5_decoder(bc5, "BC5Decoder", R"doc(
|
||||
Decodes BC4 textures to two channels.
|
||||
)doc");
|
||||
|
||||
bc5_decoder.def(py::init<uint8_t, uint8_t>(), py::arg("chan0") = 0, py::arg("chan1") = 1, R"doc(
|
||||
Create a new BC5 decoder with the specified channels
|
||||
|
||||
:param int chan0: the first channel that will be written to. 0 to 3 inclusive. Default: 0 (red).
|
||||
:param int chan1: the second channel that will be written to. 0 to 3 inclusive. Default: 1 (green).
|
||||
)doc");
|
||||
|
||||
bc5_decoder.def("decode", &BC5Decoder::Decode, "texture"_a, R"doc(
|
||||
Decode a BC5 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");
|
||||
|
||||
bc5_decoder.def_property_readonly("channels", &BC5Decoder::GetChannels, "A 2-tuple of channels that will be written to. 0 to 3 inclusive. Readonly.");
|
||||
bc5_decoder.def_property_readonly("bc4_decoders", &BC5Decoder::GetBC4Decoders,
|
||||
"2-tuple of internal :py:class:`~quicktex.s3tc.bc4.BC4Decoder` s used for each channel. Readonly.");
|
||||
// endregion
|
||||
}
|
||||
} // namespace quicktex::bindings
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -24,69 +24,10 @@
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "util.h"
|
||||
#include "../../util.h"
|
||||
#include "../../Color.h"
|
||||
|
||||
namespace rgbcx {
|
||||
|
||||
/*
|
||||
Interpolator::Interpolator() {
|
||||
PrepSingleColorTables(_single_match5, _single_match5_half, 5);
|
||||
PrepSingleColorTables(_single_match5, _single_match5_half, 6);
|
||||
}
|
||||
|
||||
void Interpolator::PrepSingleColorTables(const MatchListPtr &matchTable, const MatchListPtr &matchTableHalf, int len) {
|
||||
int size = 1 << len;
|
||||
|
||||
assert((len == 5 && size == Size5) || (len == 6 && size == size6));
|
||||
|
||||
const uint8_t *expand = (len == 5) ? &Expand5[0] : &Expand6[0];
|
||||
|
||||
bool ideal = IsIdeal();
|
||||
bool use_e = useExpandedInMatch();
|
||||
|
||||
for (int i = 0; i < match_count; i++) {
|
||||
int lowest_error = 256;
|
||||
int lowest_half_error = 256;
|
||||
|
||||
for (int low = 0; low < size; low++) {
|
||||
const int low_e = expand[low];
|
||||
const int low_val = use_e ? low_e : low;
|
||||
|
||||
for (int high = 0; high < size; high++) {
|
||||
const int high_e = expand[high];
|
||||
const int high_val = use_e ? high_e : high;
|
||||
|
||||
int v = (len == 5) ? Interpolate5(high_val, low_val) : Interpolate6(high_val, low_val);
|
||||
int v_half = (len == 5) ? InterpolateHalf5(low_val, high_val) : InterpolateHalf6(low_val, high_val);
|
||||
|
||||
int error = PrepSingleColorTableEntry(matchTable, v, i, low, high, low_e, high_e, lowest_error, false, ideal);
|
||||
int half_error = PrepSingleColorTableEntry(matchTableHalf, v, i, low, high, low_e, high_e, lowest_error, true, ideal);
|
||||
|
||||
if (error < lowest_error) lowest_error = error;
|
||||
if (half_error < lowest_half_error) lowest_half_error = half_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int Interpolator::PrepSingleColorTableEntry(const MatchListPtr &matchTable, int v, int i, int low, int high, int low_e, int high_e, int lowest_error, bool half,
|
||||
bool ideal) {
|
||||
int e = iabs(v - i);
|
||||
|
||||
// We only need to factor in 3% error in BC1 ideal mode.
|
||||
if (ideal) e += (iabs(high_e - low_e) * 3) / 100;
|
||||
|
||||
// Favor equal endpoints, for lower error on actual GPU's which approximate the interpolation.
|
||||
if ((e < lowest_error) || (e == lowest_error && low == high)) {
|
||||
assert(e <= UINT8_MAX);
|
||||
|
||||
auto &entry = (*matchTable)[i];
|
||||
entry.low = low;
|
||||
entry.high = high;
|
||||
entry.error = e;
|
||||
}
|
||||
|
||||
return e;
|
||||
}*/
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
// region Interpolator implementation
|
||||
std::unique_ptr<Interpolator> Interpolator::MakeInterpolator(Interpolator::Type type) {
|
||||
@ -109,19 +50,24 @@ uint8_t Interpolator::Interpolate6(uint8_t v0, uint8_t v1) const { return Interp
|
||||
uint8_t Interpolator::InterpolateHalf5(uint8_t v0, uint8_t v1) const { return InterpolateHalf8(scale5To8(v0), scale5To8(v1)); }
|
||||
uint8_t Interpolator::InterpolateHalf6(uint8_t v0, uint8_t v1) const { return InterpolateHalf8(scale6To8(v0), scale6To8(v1)); }
|
||||
|
||||
std::array<Color, 4> Interpolator::InterpolateBC1(uint16_t low, uint16_t high) const {
|
||||
auto colors = std::array<Color, 4>();
|
||||
colors[0] = Color::Unpack565(low);
|
||||
colors[1] = Color::Unpack565(high);
|
||||
std::array<Color, 4> Interpolator::Interpolate565BC1(uint16_t low, uint16_t high, bool allow_3color) const {
|
||||
bool use_3color = allow_3color && (high >= low);
|
||||
return InterpolateBC1(Color::Unpack565Unscaled(low), Color::Unpack565Unscaled(high), use_3color);
|
||||
}
|
||||
|
||||
if (low > high) {
|
||||
// 4-color mode
|
||||
colors[2] = InterpolateColor24(colors[0], colors[1]);
|
||||
colors[3] = InterpolateColor24(colors[1], colors[0]);
|
||||
} else {
|
||||
std::array<Color, 4> Interpolator::InterpolateBC1(Color low, Color high, bool use_3color) const {
|
||||
auto colors = std::array<Color, 4>();
|
||||
colors[0] = low.ScaleFrom565();
|
||||
colors[1] = high.ScaleFrom565();
|
||||
|
||||
if (use_3color) {
|
||||
// 3-color mode
|
||||
colors[2] = InterpolateHalfColor24(colors[0], colors[1]);
|
||||
colors[3] = Color(0, 0, 0, 0); // transparent black
|
||||
} else {
|
||||
// 4-color mode
|
||||
colors[2] = InterpolateColor24(colors[0], colors[1]);
|
||||
colors[3] = InterpolateColor24(colors[1], colors[0]);
|
||||
}
|
||||
|
||||
return colors;
|
||||
@ -147,7 +93,7 @@ uint8_t InterpolatorNvidia::Interpolate5(uint8_t v0, uint8_t v1) const {
|
||||
|
||||
uint8_t InterpolatorNvidia::Interpolate6(uint8_t v0, uint8_t v1) const {
|
||||
assert(v0 < 64 && v1 < 64);
|
||||
const int gdiff = (int) v1 - v0;
|
||||
const int gdiff = (int)v1 - v0;
|
||||
return static_cast<uint8_t>((256 * v0 + (gdiff / 4) + 128 + gdiff * 80) >> 8);
|
||||
}
|
||||
|
||||
@ -158,25 +104,23 @@ uint8_t InterpolatorNvidia::InterpolateHalf5(uint8_t v0, uint8_t v1) const {
|
||||
|
||||
uint8_t InterpolatorNvidia::InterpolateHalf6(uint8_t v0, uint8_t v1) const {
|
||||
assert(v0 < 64 && v1 < 64);
|
||||
const int gdiff = (int) v1 - v0;
|
||||
const int gdiff = (int)v1 - v0;
|
||||
return static_cast<uint8_t>((256 * v0 + gdiff / 4 + 128 + gdiff * 128) >> 8);
|
||||
}
|
||||
|
||||
std::array<Color, 4> InterpolatorNvidia::InterpolateBC1(uint16_t low, uint16_t high) const {
|
||||
std::array<Color, 4> InterpolatorNvidia::InterpolateBC1(Color low, Color high, bool use_3color) const {
|
||||
// Nvidia is special and interpolation cant be done with 8-bit values, so we need to override the default behavior
|
||||
std::array<Color, 4> colors;
|
||||
auto low565 = Color::Unpack565Unscaled(low);
|
||||
auto high565 = Color::Unpack565Unscaled(high);
|
||||
colors[0] = low565.ScaleFrom565();
|
||||
colors[1] = high565.ScaleFrom565();
|
||||
colors[0] = low.ScaleFrom565();
|
||||
colors[1] = high.ScaleFrom565();
|
||||
|
||||
if (low > high) {
|
||||
if (!use_3color) {
|
||||
// 4-color mode
|
||||
colors[2] = InterpolateColor565(low565, high565);
|
||||
colors[3] = InterpolateColor565(high565, low565);
|
||||
colors[2] = InterpolateColor565(low, high);
|
||||
colors[3] = InterpolateColor565(high, low);
|
||||
} else {
|
||||
// 3-color mode
|
||||
colors[2] = InterpolateHalfColor565(low565, high565);
|
||||
colors[2] = InterpolateHalfColor565(low, high);
|
||||
colors[3] = Color(0, 0, 0, 0); // transparent black
|
||||
}
|
||||
|
||||
@ -194,4 +138,4 @@ uint8_t InterpolatorAMD::Interpolate8(uint8_t v0, uint8_t v1) const { return (v0
|
||||
|
||||
uint8_t InterpolatorAMD::InterpolateHalf8(uint8_t v0, uint8_t v1) const { return (v0 + v1 + 1) >> 1; }
|
||||
// endregion
|
||||
} // namespace rgbcx
|
||||
} // namespace quicktex::s3tc
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -22,9 +22,9 @@
|
||||
#include <cstdint> // for uint8_t, uint16_t
|
||||
#include <memory> // for unique_ptr
|
||||
|
||||
#include "Color.h" // for Color
|
||||
#include "../../Color.h" // for Color
|
||||
|
||||
namespace rgbcx {
|
||||
namespace quicktex::s3tc {
|
||||
|
||||
class Interpolator {
|
||||
public:
|
||||
@ -52,6 +52,16 @@ class Interpolator {
|
||||
*/
|
||||
virtual uint8_t Interpolate6(uint8_t v0, uint8_t v1) const;
|
||||
|
||||
/**
|
||||
* Performs a 2/3 interpolation of a pair of 8-bit values to produce an 8-bit value
|
||||
* Output is approximately (2v0 + v1)/3.
|
||||
* Output is not guranteed to be accurate for the given interpolator if CanInterpolate8Bit() is false
|
||||
* @param v0 The first 8-bit value
|
||||
* @param v1 The second 8-bit value
|
||||
* @return The interpolated value
|
||||
*/
|
||||
virtual uint8_t Interpolate8(uint8_t v0, uint8_t v1) const;
|
||||
|
||||
/**
|
||||
* Performs a 1/2 interpolation of a pair of 5-bit values to produce an 8-bit value
|
||||
* Output is approximately (v0 + v1)/2, with v0 and v1 first extended to 8 bits.
|
||||
@ -70,13 +80,33 @@ class Interpolator {
|
||||
*/
|
||||
virtual uint8_t InterpolateHalf6(uint8_t v0, uint8_t v1) const;
|
||||
|
||||
/**
|
||||
* Performs a 1/2 interpolation of a pair of 8-bit values to produce an 8-bit value
|
||||
* Output is approximately (v0 + v1)/2.
|
||||
* Output is not guranteed to be accurate for the given interpolator if CanInterpolate8Bit() is false
|
||||
* @param v0 The first 8-bit value
|
||||
* @param v1 The second 8-bit value
|
||||
* @return The interpolated value
|
||||
*/
|
||||
virtual uint8_t InterpolateHalf8(uint8_t v0, uint8_t v1) const;
|
||||
|
||||
/**
|
||||
* Generates the 4 colors for a BC1 block from the given 5:6:5-packed colors
|
||||
* @param low first 5:6:5 color for the block
|
||||
* @param high second 5:6:5 color for the block
|
||||
* @return and array of 4 Color values, with indices matching BC1 selectors
|
||||
* @param allow_3color if true, a different interpolation mode will be used if high >= low
|
||||
* @return an array of 4 Color values, with indices matching BC1 selectors
|
||||
*/
|
||||
virtual std::array<Color, 4> InterpolateBC1(uint16_t low, uint16_t high) const;
|
||||
std::array<Color, 4> Interpolate565BC1(uint16_t low, uint16_t high, bool allow_3color = true) const;
|
||||
|
||||
/**
|
||||
* Generates the 4 colors for a BC1 block from the given
|
||||
* @param low the first color for the block, as a seperated 5:6:5 Color object
|
||||
* @param high the second color for the block, as a seperated 5:6:5 Color object
|
||||
* @param use_3color if the 3-color interpolation mode should be used
|
||||
* @return an array of 4 Color values, with indices matching BC1 selectors
|
||||
*/
|
||||
virtual std::array<Color, 4> InterpolateBC1(Color low, Color high, bool use_3color) const;
|
||||
|
||||
/**
|
||||
* Gets the type of an interpolator
|
||||
@ -84,6 +114,8 @@ class Interpolator {
|
||||
*/
|
||||
virtual Type GetType() const noexcept { return Type::Ideal; }
|
||||
|
||||
virtual bool CanInterpolate8Bit() const noexcept { return true; }
|
||||
|
||||
/**
|
||||
* Checks if the interpolator uses an ideal algorithm
|
||||
* @return true if the interpolator is ideal, false otherwise.
|
||||
@ -94,21 +126,6 @@ class Interpolator {
|
||||
}
|
||||
|
||||
private:
|
||||
virtual uint8_t Interpolate8(uint8_t v0, uint8_t v1) const;
|
||||
virtual uint8_t InterpolateHalf8(uint8_t v0, uint8_t v1) const;
|
||||
|
||||
// constexpr static auto Expand5 = ExpandArray<Size5, scale5To8>();
|
||||
// constexpr static auto Expand6 = ExpandArray<size6, scale6To8>();
|
||||
//
|
||||
// // match tables used for single-color blocks
|
||||
// using MatchList = std::array<MatchEntry, match_count>;
|
||||
// using MatchListPtr = std::shared_ptr<MatchList>;
|
||||
//
|
||||
// const MatchListPtr _single_match5 = {std::make_shared<MatchList>()};
|
||||
// const MatchListPtr _single_match6 = {std::make_shared<MatchList>()};
|
||||
// const MatchListPtr _single_match5_half = {std::make_shared<MatchList>()};
|
||||
// const MatchListPtr _single_match6_half = {std::make_shared<MatchList>()};
|
||||
|
||||
Color InterpolateColor24(const Color &c0, const Color &c1) const {
|
||||
return Color(Interpolate8(c0.r, c1.r), Interpolate8(c0.g, c1.g), Interpolate8(c0.b, c1.b));
|
||||
}
|
||||
@ -116,33 +133,29 @@ class Interpolator {
|
||||
Color InterpolateHalfColor24(const Color &c0, const Color &c1) const {
|
||||
return Color(InterpolateHalf8(c0.r, c1.r), InterpolateHalf8(c0.g, c1.g), InterpolateHalf8(c0.b, c1.b));
|
||||
}
|
||||
|
||||
// virtual constexpr bool useExpandedInMatch() noexcept { return true; }
|
||||
//
|
||||
// void PrepSingleColorTables(const MatchListPtr &matchTable, const MatchListPtr &matchTableHalf, int len);
|
||||
//
|
||||
// int PrepSingleColorTableEntry(const MatchListPtr &matchTable, int v, int i, int low, int high, int low_e, int high_e, int lowest_error, bool half,
|
||||
// bool ideal);
|
||||
};
|
||||
|
||||
class InterpolatorRound : public Interpolator {
|
||||
class InterpolatorRound final : public Interpolator {
|
||||
public:
|
||||
uint8_t Interpolate5(uint8_t v0, uint8_t v1) const override;
|
||||
uint8_t Interpolate6(uint8_t v0, uint8_t v1) const override;
|
||||
Type GetType() const noexcept override { return Type::IdealRound; }
|
||||
virtual uint8_t Interpolate5(uint8_t v0, uint8_t v1) const override;
|
||||
virtual uint8_t Interpolate6(uint8_t v0, uint8_t v1) const override;
|
||||
virtual uint8_t Interpolate8(uint8_t v0, uint8_t v1) const override;
|
||||
|
||||
private:
|
||||
uint8_t Interpolate8(uint8_t v0, uint8_t v1) const override;
|
||||
virtual Type GetType() const noexcept override { return Type::IdealRound; }
|
||||
};
|
||||
|
||||
class InterpolatorNvidia : public Interpolator {
|
||||
class InterpolatorNvidia final : public Interpolator {
|
||||
public:
|
||||
uint8_t Interpolate5(uint8_t v0, uint8_t v1) const override;
|
||||
uint8_t Interpolate6(uint8_t v0, uint8_t v1) const override;
|
||||
uint8_t InterpolateHalf5(uint8_t v0, uint8_t v1) const override;
|
||||
uint8_t InterpolateHalf6(uint8_t v0, uint8_t v1) const override;
|
||||
std::array<Color, 4> InterpolateBC1(uint16_t low, uint16_t high) const override;
|
||||
Type GetType() const noexcept override { return Type::Nvidia; }
|
||||
virtual uint8_t Interpolate5(uint8_t v0, uint8_t v1) const override;
|
||||
virtual uint8_t Interpolate6(uint8_t v0, uint8_t v1) const override;
|
||||
|
||||
virtual uint8_t InterpolateHalf5(uint8_t v0, uint8_t v1) const override;
|
||||
virtual uint8_t InterpolateHalf6(uint8_t v0, uint8_t v1) const override;
|
||||
|
||||
virtual std::array<Color, 4> InterpolateBC1(Color low, Color high, bool use_3color) const override;
|
||||
|
||||
virtual Type GetType() const noexcept override { return Type::Nvidia; }
|
||||
virtual bool CanInterpolate8Bit() const noexcept override { return false; }
|
||||
|
||||
private:
|
||||
Color InterpolateColor565(const Color &c0, const Color &c1) const {
|
||||
@ -154,16 +167,16 @@ class InterpolatorNvidia : public Interpolator {
|
||||
}
|
||||
};
|
||||
|
||||
class InterpolatorAMD : public Interpolator {
|
||||
class InterpolatorAMD final : public Interpolator {
|
||||
public:
|
||||
uint8_t Interpolate5(uint8_t v0, uint8_t v1) const override;
|
||||
uint8_t Interpolate6(uint8_t v0, uint8_t v1) const override;
|
||||
uint8_t InterpolateHalf5(uint8_t v0, uint8_t v1) const override;
|
||||
uint8_t InterpolateHalf6(uint8_t v0, uint8_t v1) const override;
|
||||
Type GetType() const noexcept override { return Type::AMD; }
|
||||
virtual uint8_t Interpolate5(uint8_t v0, uint8_t v1) const override;
|
||||
virtual uint8_t Interpolate6(uint8_t v0, uint8_t v1) const override;
|
||||
virtual uint8_t Interpolate8(uint8_t v0, uint8_t v1) const override;
|
||||
|
||||
private:
|
||||
uint8_t Interpolate8(uint8_t v0, uint8_t v1) const override;
|
||||
uint8_t InterpolateHalf8(uint8_t v0, uint8_t v1) const override;
|
||||
virtual uint8_t InterpolateHalf5(uint8_t v0, uint8_t v1) const override;
|
||||
virtual uint8_t InterpolateHalf6(uint8_t v0, uint8_t v1) const override;
|
||||
virtual uint8_t InterpolateHalf8(uint8_t v0, uint8_t v1) const override;
|
||||
|
||||
virtual Type GetType() const noexcept override { return Type::AMD; }
|
||||
};
|
||||
} // namespace rgbcx
|
||||
} // namespace quicktex::s3tc
|
3
quicktex/s3tc/interpolator/__init__.py
Normal file
3
quicktex/s3tc/interpolator/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""Classes representing various methods of interpolating BC1-3 blocks"""
|
||||
|
||||
from _quicktex._s3tc._interpolator import *
|
68
quicktex/s3tc/interpolator/_bindings.cpp
Normal file
68
quicktex/s3tc/interpolator/_bindings.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
/* Quicktex 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 <pybind11/pybind11.h>
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "Interpolator.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
namespace quicktex::bindings {
|
||||
|
||||
using namespace quicktex::s3tc;
|
||||
using InterpolatorPtr = std::shared_ptr<Interpolator>;
|
||||
|
||||
void InitInterpolator(py::module_ &s3tc) {
|
||||
auto interpolator = s3tc.def_submodule("_interpolator", "internal interpolator module");
|
||||
|
||||
// Interpolator
|
||||
py::class_<Interpolator, std::shared_ptr<Interpolator>> ideal(
|
||||
interpolator, "Interpolator", R"doc(
|
||||
Interpolator base class representing the ideal interpolation mode, with no rounding for colors 2 and 3.
|
||||
This matches the `D3D10 docs`_ on BC1.
|
||||
|
||||
.. _D3D10 docs: https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression
|
||||
)doc");
|
||||
|
||||
// InterpolatorRound
|
||||
py::class_<InterpolatorRound, std::shared_ptr<InterpolatorRound>> round(interpolator, "InterpolatorRound", ideal, R"doc(
|
||||
Base: :py:class:`~quicktex.s3tc.interpolator.Interpolator`
|
||||
|
||||
Interpolator class representing the ideal rounding interpolation mode.
|
||||
Round colors 2 and 3. Matches the AMD Compressonator tool and the `D3D9 docs`_ on DXT1.
|
||||
|
||||
.. _D3D9 docs: https://docs.microsoft.com/en-us/windows/win32/direct3d9/opaque-and-1-bit-alpha-textures
|
||||
)doc");
|
||||
|
||||
// InterpolatorNvidia
|
||||
py::class_<InterpolatorNvidia, std::shared_ptr<InterpolatorNvidia>> nvidia(interpolator, "InterpolatorNvidia", ideal, R"doc(
|
||||
Base: :py:class:`~quicktex.s3tc.interpolator.Interpolator`
|
||||
|
||||
Interpolator class representing the Nvidia GPU interpolation mode.
|
||||
)doc");
|
||||
|
||||
// InterpolatorAMD
|
||||
py::class_<InterpolatorAMD, std::shared_ptr<InterpolatorAMD>> amd(interpolator, "InterpolatorAMD", ideal, R"doc(
|
||||
Base: :py:class:`~quicktex.s3tc.interpolator.Interpolator`
|
||||
|
||||
Interpolator class representing the AMD GPU interpolation mode.
|
||||
)doc");
|
||||
}
|
||||
} // namespace quicktex::bindings
|
@ -1,4 +1,4 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
/* Quicktex 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
|
||||
@ -21,9 +21,10 @@
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "ndebug.h"
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#define UINT5_MAX 0x1FU // 31
|
||||
#define UINT6_MAX 0x3FU // 63
|
||||
@ -39,21 +40,21 @@ template <typename S> constexpr auto iabs(S i) {
|
||||
|
||||
/**
|
||||
* Unpacks an unsigned integer into an array of smaller integers.
|
||||
* @tparam I Input data type. Must be an unsigned integral type large enough to hold C * S bits.
|
||||
* @tparam I Input data type. Must be an unsigned integral type large enough to hold C * N bits.
|
||||
* @tparam O Output data type. must be an unsigned integral type large enough to hold C bits..
|
||||
* @tparam S Number of bits in each value.
|
||||
* @tparam C Number of values to unpack.
|
||||
* @param packed Packed integer input of type I.
|
||||
* @return Unpacked std::array of type O and size C.
|
||||
*/
|
||||
template <typename I, typename O, size_t S, size_t C> constexpr auto Unpack(I packed) noexcept(ndebug) {
|
||||
template <typename I, typename O, size_t S, size_t C> constexpr std::array<O, C> Unpack(I packed) {
|
||||
// type checking
|
||||
static_assert(std::is_unsigned<I>::value, "Packed input type must be unsigned");
|
||||
static_assert(std::is_unsigned<O>::value, "Unpacked output type must be unsigned");
|
||||
static_assert(std::numeric_limits<I>::digits >= (C * S), "Packed input type must be big enough to represent the number of bits multiplied by count");
|
||||
static_assert(std::numeric_limits<O>::digits >= S, "Unpacked output type must be big enough to represent the number of bits");
|
||||
|
||||
constexpr O mask = (1U << S) - 1U; // maximum value representable by S bits
|
||||
constexpr O mask = (1U << S) - 1U; // maximum value representable by N bits
|
||||
std::array<O, C> vals; // output values array of size C
|
||||
|
||||
for (unsigned i = 0; i < C; i++) {
|
||||
@ -67,13 +68,13 @@ template <typename I, typename O, size_t S, size_t C> constexpr auto Unpack(I pa
|
||||
/**
|
||||
* Packs an array of unsigned integers into a single integer.
|
||||
* @tparam I Input data type. Must be an unsigned integral type large enough to hold C bits.
|
||||
* @tparam O Output data type. must be an unsigned integral type large enough to hold C * S bits.
|
||||
* @tparam O Output data type. must be an unsigned integral type large enough to hold C * N bits.
|
||||
* @tparam S Number of bits in each value.
|
||||
* @tparam C Number of values to unpack.
|
||||
* @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 O 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");
|
||||
@ -97,6 +98,16 @@ template <size_t Size, int Op(int)> constexpr std::array<uint8_t, Size> ExpandAr
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename Seq, typename Fn> constexpr auto MapArray(const Seq &input, Fn op) {
|
||||
using I = typename Seq::value_type;
|
||||
using O = decltype(op(std::declval<I>()));
|
||||
constexpr size_t N = std::tuple_size<Seq>::value;
|
||||
|
||||
std::array<O, N> output;
|
||||
for (unsigned i = 0; i < N; i++) { output[i] = op(input[i]); }
|
||||
return output;
|
||||
}
|
||||
|
||||
template <typename S> constexpr S scale8To5(S v) {
|
||||
auto v2 = v * 31 + 128;
|
||||
return static_cast<S>((v2 + (v2 >> 8)) >> 8);
|
||||
@ -125,7 +136,7 @@ template <typename S> constexpr S minimum(S a, S b, S c, S d) { return minimum(m
|
||||
|
||||
template <typename T> constexpr T square(T a) { return a * a; }
|
||||
|
||||
constexpr float clampf(float value, float low, float high) {
|
||||
constexpr float clampf(float value, float low = 0.0f, float high = 1.0f) {
|
||||
if (value < low)
|
||||
value = low;
|
||||
else if (value > high)
|
||||
@ -147,3 +158,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;
|
||||
}
|
15
setup.py
Normal file
15
setup.py
Normal file
@ -0,0 +1,15 @@
|
||||
import sys
|
||||
|
||||
try:
|
||||
from skbuild import setup
|
||||
except ImportError:
|
||||
print(
|
||||
"Please update pip, you need pip 10 or greater,\n"
|
||||
" or you need to install the PEP 518 requirements in pyproject.toml yourself",
|
||||
file=sys.stderr,
|
||||
)
|
||||
raise
|
||||
|
||||
setup(
|
||||
cmake_install_dir='.'
|
||||
)
|
@ -1,85 +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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "../Color.h"
|
||||
#include "../util.h"
|
||||
|
||||
namespace rgbcx {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
class BC1Block {
|
||||
public:
|
||||
using UnpackedSelectors = std::array<std::array<uint8_t, 4>, 4>;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
UnpackedSelectors UnpackSelectors() const {
|
||||
UnpackedSelectors 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) {
|
||||
for (unsigned i = 0; i < 4; i++) { selectors[i] = Pack<uint8_t, uint8_t, 2, 4>(unpacked[i]); }
|
||||
}
|
||||
|
||||
constexpr static inline size_t EndpointSize = 2;
|
||||
constexpr static inline size_t SelectorSize = 4;
|
||||
constexpr static inline uint8_t SelectorBits = 2;
|
||||
constexpr static inline uint8_t SelectorValues = 1 << SelectorBits;
|
||||
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, 4> selectors;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
} // namespace rgbcx
|
@ -1,117 +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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "../BC1/BC1Block.h"
|
||||
#include "../Color.h"
|
||||
#include "../util.h"
|
||||
|
||||
namespace rgbcx {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
class BC4Block {
|
||||
public:
|
||||
using UnpackedSelectors = std::array<std::array<uint8_t, 4>, 4>;
|
||||
|
||||
inline uint32_t GetLowAlpha() const { return low_alpha; }
|
||||
inline uint32_t GetHighAlpha() const { return high_alpha; }
|
||||
inline bool Is6Alpha() const { return GetLowAlpha() <= GetHighAlpha(); }
|
||||
|
||||
inline uint64_t GetSelectorBits() const {
|
||||
auto packed = Pack<uint8_t, uint64_t, 8, 6>(selectors);
|
||||
assert(packed <= SelectorBitsMax);
|
||||
return packed;
|
||||
}
|
||||
|
||||
void SetSelectorBits(uint64_t packed) {
|
||||
assert(packed <= SelectorBitsMax);
|
||||
selectors = Unpack<uint64_t, uint8_t, 8, 6>(packed);
|
||||
}
|
||||
|
||||
UnpackedSelectors UnpackSelectors() const {
|
||||
UnpackedSelectors unpacked;
|
||||
auto rows = Unpack<uint64_t, uint16_t, 12, 4>(GetSelectorBits());
|
||||
for (unsigned i = 0; i < 4; i++) {
|
||||
auto row = Unpack<uint16_t, uint8_t, SelectorBits, 4>(rows[i]);
|
||||
unpacked[i] = row;
|
||||
}
|
||||
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
void PackSelectors(const UnpackedSelectors& unpacked) {
|
||||
std::array<uint16_t, 4> rows;
|
||||
for (unsigned i = 0; i < 4; i++) { rows[i] = Pack<uint8_t, uint16_t, SelectorBits, 4>(unpacked[i]); }
|
||||
auto packed = Pack<uint16_t, uint64_t, 12, 4>(rows);
|
||||
SetSelectorBits(packed);
|
||||
}
|
||||
|
||||
inline uint32_t GetSelector(uint32_t x, uint32_t y, uint64_t selector_bits) const {
|
||||
assert((x < 4U) && (y < 4U));
|
||||
return (selector_bits >> (((y * 4) + x) * SelectorBits)) & (SelectorMask);
|
||||
}
|
||||
|
||||
static inline std::array<uint8_t, 8> GetValues6(uint32_t l, uint32_t h) {
|
||||
return {static_cast<uint8_t>(l),
|
||||
static_cast<uint8_t>(h),
|
||||
static_cast<uint8_t>((l * 4 + h) / 5),
|
||||
static_cast<uint8_t>((l * 3 + h * 2) / 5),
|
||||
static_cast<uint8_t>((l * 2 + h * 3) / 5),
|
||||
static_cast<uint8_t>((l + h * 4) / 5),
|
||||
0,
|
||||
255};
|
||||
}
|
||||
|
||||
static inline std::array<uint8_t, 8> GetValues8(uint32_t l, uint32_t h) {
|
||||
return {static_cast<uint8_t>(l),
|
||||
static_cast<uint8_t>(h),
|
||||
static_cast<uint8_t>((l * 6 + h) / 7),
|
||||
static_cast<uint8_t>((l * 5 + h * 2) / 7),
|
||||
static_cast<uint8_t>((l * 4 + h * 3) / 7),
|
||||
static_cast<uint8_t>((l * 3 + h * 4) / 7),
|
||||
static_cast<uint8_t>((l * 2 + h * 5) / 7),
|
||||
static_cast<uint8_t>((l + h * 6) / 7)};
|
||||
}
|
||||
|
||||
static inline std::array<uint8_t, 8> GetValues(uint32_t l, uint32_t h) {
|
||||
if (l > h)
|
||||
return GetValues8(l, h);
|
||||
else
|
||||
return GetValues6(l, h);
|
||||
}
|
||||
|
||||
constexpr static inline size_t EndpointSize = 1;
|
||||
constexpr static inline size_t SelectorSize = 6;
|
||||
constexpr static inline uint8_t SelectorBits = 3;
|
||||
constexpr static inline uint8_t SelectorValues = 1 << SelectorBits;
|
||||
constexpr static inline uint8_t SelectorMask = SelectorValues - 1;
|
||||
constexpr static inline uint64_t SelectorBitsMax = (1ULL << (8U * SelectorSize)) - 1U;
|
||||
|
||||
uint8_t low_alpha;
|
||||
uint8_t high_alpha;
|
||||
std::array<uint8_t, SelectorSize> selectors;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
} // namespace rgbcx
|
@ -1,43 +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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "../BlockDecoder.h"
|
||||
#include "../BlockView.h"
|
||||
#include "../ndebug.h"
|
||||
#include "BC4Block.h"
|
||||
|
||||
namespace rgbcx {
|
||||
class BC4Decoder : public BlockDecoder<BC4Block, 4, 4> {
|
||||
public:
|
||||
BC4Decoder(uint8_t channel = 3) : _channel(channel) { assert(channel < 4U); }
|
||||
|
||||
void DecodeBlock(Color4x4 dest, BC4Block *const block) const noexcept(ndebug) override { DecodeBlock(dest.GetChannel(_channel), block); }
|
||||
void DecodeBlock(Color4x4 dest, BC4Block *const block, uint8_t channel) const noexcept(ndebug) { DecodeBlock(dest.GetChannel(channel), block); }
|
||||
void DecodeBlock(Byte4x4 dest, BC4Block *const block) const noexcept(ndebug);
|
||||
|
||||
constexpr uint8_t GetChannel() const { return _channel; }
|
||||
|
||||
private:
|
||||
const uint8_t _channel;
|
||||
};
|
||||
} // namespace rgbcx
|
@ -1,107 +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 "BC4Encoder.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace rgbcx {
|
||||
void BC4Encoder::EncodeBlock(Byte4x4 pixels, BC4Block *const dest) const noexcept(ndebug) {
|
||||
auto bytes = pixels.Flatten();
|
||||
auto minmax = std::minmax_element(bytes.begin(), bytes.end());
|
||||
|
||||
uint8_t min_v = *minmax.first;
|
||||
uint8_t max_v = *minmax.second;
|
||||
|
||||
dest->high_alpha = min_v;
|
||||
dest->low_alpha = max_v;
|
||||
|
||||
if (max_v == min_v) {
|
||||
dest->SetSelectorBits(0);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t delta = max_v - min_v;
|
||||
|
||||
// min_v is now 0. Compute thresholds between values by scaling max_v. It's x14 because we're adding two x7 scale factors.
|
||||
const int t0 = delta * 13;
|
||||
const int t1 = delta * 11;
|
||||
const int t2 = delta * 9;
|
||||
const int t3 = delta * 7;
|
||||
const int t4 = delta * 5;
|
||||
const int t5 = delta * 3;
|
||||
const int t6 = delta * 1;
|
||||
|
||||
// BC4 floors in its divisions, which we compensate for with the 4 bias.
|
||||
// This function is optimal for all possible inputs (i.e. it outputs the same results as checking all 8 values and choosing the closest one).
|
||||
const int bias = 4 - min_v * 14;
|
||||
|
||||
static const uint32_t s_tran0[8] = {1U, 7U, 6U, 5U, 4U, 3U, 2U, 0U};
|
||||
static const uint32_t s_tran1[8] = {1U << 3U, 7U << 3U, 6U << 3U, 5U << 3U, 4U << 3U, 3U << 3U, 2U << 3U, 0U << 3U};
|
||||
static const uint32_t s_tran2[8] = {1U << 6U, 7U << 6U, 6U << 6U, 5U << 6U, 4U << 6U, 3U << 6U, 2U << 6U, 0U << 6U};
|
||||
static const uint32_t s_tran3[8] = {1U << 9U, 7U << 9U, 6U << 9U, 5U << 9U, 4U << 9U, 3U << 9U, 2U << 9U, 0U << 9U};
|
||||
|
||||
uint64_t a0, a1, a2, a3;
|
||||
{
|
||||
const int v0 = bytes[0] * 14 + bias;
|
||||
const int v1 = bytes[1] * 14 + bias;
|
||||
const int v2 = bytes[2] * 14 + bias;
|
||||
const int v3 = bytes[3] * 14 + bias;
|
||||
a0 = s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)];
|
||||
a1 = s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)];
|
||||
a2 = s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)];
|
||||
a3 = s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)];
|
||||
}
|
||||
|
||||
{
|
||||
const int v0 = bytes[4] * 14 + bias;
|
||||
const int v1 = bytes[5] * 14 + bias;
|
||||
const int v2 = bytes[6] * 14 + bias;
|
||||
const int v3 = bytes[7] * 14 + bias;
|
||||
a0 |= (uint64_t)(s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)] << 12U);
|
||||
a1 |= (uint64_t)(s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)] << 12U);
|
||||
a2 |= (uint64_t)(s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)] << 12U);
|
||||
a3 |= (uint64_t)(s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)] << 12U);
|
||||
}
|
||||
|
||||
{
|
||||
const int v0 = bytes[8] * 14 + bias;
|
||||
const int v1 = bytes[9] * 14 + bias;
|
||||
const int v2 = bytes[10] * 14 + bias;
|
||||
const int v3 = bytes[11] * 14 + bias;
|
||||
a0 |= (((uint64_t)s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)]) << 24U);
|
||||
a1 |= (((uint64_t)s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)]) << 24U);
|
||||
a2 |= (((uint64_t)s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)]) << 24U);
|
||||
a3 |= (((uint64_t)s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)]) << 24U);
|
||||
}
|
||||
|
||||
{
|
||||
const int v0 = bytes[12] * 14 + bias;
|
||||
const int v1 = bytes[13] * 14 + bias;
|
||||
const int v2 = bytes[14] * 14 + bias;
|
||||
const int v3 = bytes[15] * 14 + bias;
|
||||
a0 |= (((uint64_t)s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)]) << 36U);
|
||||
a1 |= (((uint64_t)s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)]) << 36U);
|
||||
a2 |= (((uint64_t)s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)]) << 36U);
|
||||
a3 |= (((uint64_t)s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)]) << 36U);
|
||||
}
|
||||
|
||||
dest->SetSelectorBits(a0 | a1 | a2 | a3);
|
||||
}
|
||||
} // namespace rgbcx
|
@ -1,73 +0,0 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich 2020 <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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "BlockView.h"
|
||||
#include "ndebug.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace rgbcx {
|
||||
|
||||
template <class B, size_t M, size_t N> class BlockDecoder {
|
||||
public:
|
||||
using DecodedBlock = ColorBlockView<M, N>;
|
||||
using EncodedBlock = B;
|
||||
|
||||
BlockDecoder() noexcept = default;
|
||||
virtual ~BlockDecoder() 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) {
|
||||
assert(image_width % N == 0);
|
||||
assert(image_width % M == 0);
|
||||
|
||||
unsigned block_width = image_width / N;
|
||||
unsigned block_height = image_height / M;
|
||||
|
||||
auto blocks = reinterpret_cast<B *>(encoded);
|
||||
|
||||
// from experimentation, multithreading this using OpenMP 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
|
||||
for (unsigned y = 0; y < block_height; y++) {
|
||||
for (unsigned x = 0; x < block_width; x++) {
|
||||
unsigned pixel_x = x * N;
|
||||
unsigned pixel_y = y * M;
|
||||
|
||||
assert(pixel_x >= 0);
|
||||
assert(pixel_y >= 0);
|
||||
assert(pixel_y + M <= image_height);
|
||||
assert(pixel_x + N <= image_width);
|
||||
|
||||
unsigned top_left = pixel_x + (pixel_y * image_width);
|
||||
auto dest = DecodedBlock(&decoded[top_left], image_width);
|
||||
|
||||
DecodeBlock(dest, &blocks[x + block_width * y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace rgbcx
|
@ -1,68 +0,0 @@
|
||||
/* Python-rgbcx Texture Compression Library
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich 2020 <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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "BlockView.h"
|
||||
|
||||
namespace rgbcx {
|
||||
|
||||
template <class B, size_t M, size_t N> class BlockEncoder {
|
||||
public:
|
||||
using DecodedBlock = ColorBlockView<M, N>;
|
||||
using EncodedBlock = B;
|
||||
|
||||
BlockEncoder() noexcept = default;
|
||||
virtual ~BlockEncoder() noexcept = default;
|
||||
|
||||
virtual void EncodeBlock(DecodedBlock pixels, EncodedBlock *dest) const = 0;
|
||||
|
||||
void EncodeImage(uint8_t *encoded, Color *decoded, unsigned image_width, unsigned image_height) {
|
||||
assert(image_width % N == 0);
|
||||
assert(image_width % M == 0);
|
||||
|
||||
unsigned block_width = image_width / N;
|
||||
unsigned block_height = image_height / M;
|
||||
|
||||
auto blocks = reinterpret_cast<B *>(encoded);
|
||||
|
||||
// from experimentation, multithreading this using OpenMP 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
|
||||
for (unsigned y = 0; y < block_height; y++) {
|
||||
for (unsigned x = 0; x < block_width; x++) {
|
||||
unsigned pixel_x = x * N;
|
||||
unsigned pixel_y = y * M;
|
||||
|
||||
assert(pixel_x >= 0);
|
||||
assert(pixel_y >= 0);
|
||||
assert(pixel_y + M <= image_height);
|
||||
assert(pixel_x + N <= image_width);
|
||||
|
||||
unsigned top_left = pixel_x + (pixel_y * image_width);
|
||||
auto src = DecodedBlock(&decoded[top_left], image_width);
|
||||
|
||||
EncodeBlock(src, &blocks[x + block_width * y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace rgbcx
|
116
src/BlockView.h
116
src/BlockView.h
@ -1,116 +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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
#include "Color.h"
|
||||
#include "ndebug.h"
|
||||
|
||||
namespace rgbcx {
|
||||
template <typename S, size_t N> class RowView {
|
||||
public:
|
||||
RowView(S *start, int pixel_stride = 1) : start(start), pixel_stride(pixel_stride) {}
|
||||
|
||||
constexpr S &operator[](size_t index) noexcept(ndebug) {
|
||||
assert(index < N);
|
||||
return start[index * pixel_stride];
|
||||
}
|
||||
constexpr const S &operator[](size_t index) const noexcept(ndebug) {
|
||||
assert(index < N);
|
||||
return start[index * pixel_stride];
|
||||
}
|
||||
|
||||
constexpr int Size() noexcept { return N; }
|
||||
|
||||
S *const start;
|
||||
const int pixel_stride;
|
||||
};
|
||||
|
||||
template <typename S, size_t M, size_t N> class BlockView {
|
||||
public:
|
||||
using Row = RowView<S, N>;
|
||||
|
||||
BlockView(S *start, int row_stride = N, int pixel_stride = 1) : start(start), row_stride(row_stride), pixel_stride(pixel_stride) {}
|
||||
|
||||
constexpr Row operator[](unsigned index) noexcept(ndebug) {
|
||||
assert(index < M);
|
||||
return RowView<S, N>(&start[row_stride * (int)index], pixel_stride);
|
||||
}
|
||||
|
||||
constexpr int Width() noexcept { return N; }
|
||||
constexpr int Height() noexcept { return M; }
|
||||
constexpr int Size() noexcept { return N * M; }
|
||||
|
||||
constexpr S &Get(unsigned x, unsigned y) noexcept(ndebug) {
|
||||
assert(x < N);
|
||||
assert(y < M);
|
||||
return start[(row_stride * (int)y) + (pixel_stride * (int)x)];
|
||||
}
|
||||
|
||||
constexpr S Get(unsigned x, unsigned y) const noexcept(ndebug) {
|
||||
assert(x < N);
|
||||
assert(y < M);
|
||||
return start[(row_stride * (int)y) + (pixel_stride * (int)x)];
|
||||
}
|
||||
|
||||
constexpr void Set(unsigned x, unsigned y, S value) noexcept(ndebug) {
|
||||
assert(x < N);
|
||||
assert(y < M);
|
||||
start[(row_stride * (int)y) + (pixel_stride * (int)x)] = value;
|
||||
}
|
||||
|
||||
constexpr std::array<S, M * N> Flatten() noexcept {
|
||||
std::array<S, M * N> result;
|
||||
for (int x = 0; x < N; x++) {
|
||||
for (int y = 0; y < M; y++) { result[x + (N * y)] = start[(row_stride * y) + (pixel_stride * x)]; }
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
S *const start;
|
||||
const int row_stride;
|
||||
const int pixel_stride;
|
||||
};
|
||||
|
||||
template <size_t M, size_t N> class ColorBlockView : public BlockView<Color, M, N> {
|
||||
public:
|
||||
using Base = BlockView<Color, M, N>;
|
||||
using ChannelView = BlockView<uint8_t, M, N>;
|
||||
|
||||
ColorBlockView(Color *start, int row_stride = N, int pixel_stride = 1) : Base(start, row_stride, pixel_stride) {}
|
||||
|
||||
constexpr ChannelView GetChannel(uint8_t index) noexcept(ndebug) {
|
||||
assert(index < 4U);
|
||||
auto channelStart = reinterpret_cast<uint8_t *>(Base::start) + index;
|
||||
return ChannelView(channelStart, Base::row_stride * 4, Base::pixel_stride * 4);
|
||||
}
|
||||
|
||||
void SetRGB(unsigned x, unsigned y, Color value) noexcept(ndebug) { Base::Get(x, y).SetRGB(value); }
|
||||
};
|
||||
|
||||
using Color4x4 = ColorBlockView<4, 4>;
|
||||
using Byte4x4 = BlockView<uint8_t, 4, 4>;
|
||||
|
||||
} // namespace rgbcx
|
@ -1,80 +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 "Color.h"
|
||||
|
||||
#include <algorithm> // for max, min
|
||||
|
||||
#include "util.h" // for scale5To8, scale8To5, assert5bit, scale6To8
|
||||
|
||||
// region Color implementation
|
||||
Color::Color() { SetRGBA(0, 0, 0, 0xFF); }
|
||||
|
||||
Color::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { SetRGBA(r, g, b, a); }
|
||||
|
||||
uint16_t Color::Pack565Unscaled(uint8_t r, uint8_t g, uint8_t b) {
|
||||
assert5bit(r);
|
||||
assert6bit(g);
|
||||
assert5bit(b);
|
||||
return static_cast<uint16_t>(b | (g << 5) | (r << 11));
|
||||
}
|
||||
|
||||
uint16_t Color::Pack565(uint8_t r, uint8_t g, uint8_t b) { return Pack565Unscaled(scale8To5(r), scale8To6(g), scale8To5(b)); }
|
||||
|
||||
Color Color::Unpack565(uint16_t Packed) {
|
||||
uint8_t r = static_cast<uint8_t>(scale5To8((Packed >> 11) & 0x1FU));
|
||||
uint8_t g = static_cast<uint8_t>(scale6To8((Packed >> 5) & 0x3FU));
|
||||
uint8_t b = static_cast<uint8_t>(scale5To8(Packed & 0x1FU));
|
||||
|
||||
return Color(r, g, b);
|
||||
}
|
||||
|
||||
Color Color::Unpack565Unscaled(uint16_t Packed) {
|
||||
uint8_t r = (Packed >> 11) & 0x1F;
|
||||
uint8_t g = (Packed >> 5) & 0x3F;
|
||||
uint8_t b = Packed & 0x1F;
|
||||
|
||||
return Color(r, g, b);
|
||||
}
|
||||
|
||||
void Color::SetRGBA(uint8_t vr, uint8_t vg, uint8_t vb, uint8_t va = 0xFF) {
|
||||
r = vr;
|
||||
g = vg;
|
||||
b = vb;
|
||||
a = va;
|
||||
}
|
||||
|
||||
void Color::SetRGB(uint8_t vr, uint8_t vg, uint8_t vb) {
|
||||
r = vr;
|
||||
g = vg;
|
||||
b = vb;
|
||||
}
|
||||
|
||||
Color Color::min(const Color &a, const Color &b) { return Color(std::min(a[0], b[0]), std::min(a[1], b[1]), std::min(a[2], b[2]), std::min(a[3], b[3])); }
|
||||
|
||||
Color Color::max(const Color &a, const Color &b) { return Color(std::max(a[0], b[0]), std::max(a[1], b[1]), std::max(a[2], b[2]), std::max(a[3], b[3])); }
|
||||
|
||||
uint16_t Color::pack565() { return Pack565(r, g, b); }
|
||||
|
||||
uint16_t Color::pack565Unscaled() { return Pack565Unscaled(r, g, b); }
|
||||
|
||||
Color Color::ScaleTo565() const { return Color(scale8To5(r), scale8To6(g), scale8To5(b)); }
|
||||
Color Color::ScaleFrom565() const { return Color(scale5To8(r), scale6To8(g), scale5To8(b)); }
|
||||
|
||||
// endregion
|
72
src/Color.h
72
src/Color.h
@ -1,72 +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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <assert.h> // for assert
|
||||
#include <stddef.h> // for size_t
|
||||
|
||||
#include <cstdint> // for uint8_t, uint16_t
|
||||
|
||||
#pragma pack(push, 1)
|
||||
class Color {
|
||||
public:
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
|
||||
Color();
|
||||
|
||||
Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 0xFF);
|
||||
|
||||
static uint16_t Pack565Unscaled(uint8_t r, uint8_t g, uint8_t b);
|
||||
static uint16_t Pack565(uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
static Color Unpack565Unscaled(uint16_t Packed);
|
||||
static Color Unpack565(uint16_t Packed);
|
||||
|
||||
bool operator==(const Color &Rhs) const { return r == Rhs.r && g == Rhs.g && b == Rhs.b && a == Rhs.a; }
|
||||
|
||||
uint8_t operator[](size_t index) const {
|
||||
assert(index < 4);
|
||||
return reinterpret_cast<const uint8_t *>(this)[index];
|
||||
}
|
||||
uint8_t &operator[](size_t index) {
|
||||
assert(index < 4);
|
||||
return reinterpret_cast<uint8_t *>(this)[index];
|
||||
}
|
||||
|
||||
void SetRGBA(uint8_t vr, uint8_t vg, uint8_t vb, uint8_t va);
|
||||
void SetRGBA(const Color &other) { SetRGBA(other.r, other.g, other.b, other.a); }
|
||||
|
||||
void SetRGB(uint8_t vr, uint8_t vg, uint8_t vb);
|
||||
void SetRGB(const Color &other) { SetRGB(other.r, other.g, other.b); }
|
||||
|
||||
uint16_t pack565();
|
||||
uint16_t pack565Unscaled();
|
||||
|
||||
Color ScaleTo565() const;
|
||||
Color ScaleFrom565() const;
|
||||
|
||||
static Color min(const Color &A, const Color &B);
|
||||
static Color max(const Color &A, const Color &B);
|
||||
|
||||
int get_luma() const { return (13938U * r + 46869U * g + 4729U * b + 32768U) >> 16U; } // REC709 weightings
|
||||
};
|
||||
#pragma pack(pop)
|
@ -1,22 +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 "Image.h"
|
||||
|
||||
namespace rgbcx {} // namespace rgbcx
|
26
src/ndebug.h
26
src/ndebug.h
@ -1,26 +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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef NDEBUG // asserts disabled
|
||||
constexpr bool ndebug = true;
|
||||
#else // asserts enabled
|
||||
constexpr bool ndebug = false;
|
||||
#endif
|
2512
src/rgbcx.cpp
2512
src/rgbcx.cpp
File diff suppressed because it is too large
Load Diff
246
src/rgbcx.h
246
src/rgbcx.h
@ -1,246 +0,0 @@
|
||||
// rgbcx.h v1.12
|
||||
// High-performance scalar BC1-5 encoders. Public Domain or MIT license (you choose - see below), written by Richard Geldreich 2020 <richgel99@gmail.com>.
|
||||
//
|
||||
// Influential references:
|
||||
// http://sjbrown.co.uk/2006/01/19/dxt-compression-techniques/
|
||||
// https://github.com/nothings/stb/blob/master/stb_dxt.h
|
||||
// https://gist.github.com/castano/c92c7626f288f9e99e158520b14a61cf
|
||||
// https://github.com/castano/icbc/blob/master/icbc.h
|
||||
// http://www.humus.name/index.php?page=3D&ID=79
|
||||
//
|
||||
// Instructions:
|
||||
//
|
||||
// The library MUST be initialized by calling this function at least once before using any encoder or decoder functions:
|
||||
//
|
||||
// void rgbcx::init(bc1_approx_mode mode = cBC1Ideal);
|
||||
//
|
||||
// This function manipulates global state, so it is not thread safe.
|
||||
// You can call it multiple times to change the global BC1 approximation mode.
|
||||
// Important: BC1/3 textures encoded using non-ideal BC1 approximation modes should only be sampled on parts from that vendor.
|
||||
// If you encode for AMD, average error on AMD parts will go down, but average error on NVidia parts will go up and vice versa.
|
||||
// If in doubt, encode in ideal BC1 mode.
|
||||
//
|
||||
// Call these functions to encode BC1-5:
|
||||
// void rgbcx::encode_bc1(uint32_t level, void* pDst, const uint8_t* pPixels, bool allow_3color, bool use_transparent_texels_for_black);
|
||||
// void rgbcx::encode_bc3(uint32_t level, void* pDst, const uint8_t* pPixels);
|
||||
// void rgbcx::encode_bc4(void* pDst, const uint8_t* pPixels, uint32_t stride = 4);
|
||||
// void rgbcx::encode_bc5(void* pDst, const uint8_t* pPixels, uint32_t chan0 = 0, uint32_t chan1 = 1, uint32_t stride = 4);
|
||||
//
|
||||
// - level ranges from MIN_LEVEL to MAX_LEVEL. The higher the level, the slower the encoder goes, but the higher the average quality.
|
||||
// levels [0,4] are fast and compete against stb_dxt (default and HIGHQUAL). The remaining levels compete against squish/NVTT/icbc and icbc HQ.
|
||||
// If in doubt just use level 10, set allow_3color to true and use_transparent_texels_for_black to false, and adjust as needed.
|
||||
//
|
||||
// - pDst is a pointer to the 8-byte (BC1/4) or 16-byte (BC3/5) destination block.
|
||||
//
|
||||
// - pPixels is a pointer to the 32-bpp pixels, in either RGBX or RGBA format (R is first in memory).
|
||||
// Alpha is always ignored by encode_bc1().
|
||||
//
|
||||
// - allow_3color: If true the encoder will use 3-color blocks. This flag is ignored unless level is >= 5 (because lower levels compete against stb_dxt and it
|
||||
// doesn't support 3-color blocks). Do not enable on BC3-5 textures. 3-color block usage slows down encoding.
|
||||
//
|
||||
// - use_transparent_texels_for_black: If true the encoder will use 3-color block transparent black pixels to code very dark or black texels. Your engine/shader
|
||||
// MUST ignore the sampled alpha value for textures encoded in this mode. This is how NVidia's classic "nvdxt" encoder (used by many original Xbox titles) used
|
||||
// to work by default on DXT1C textures. It increases average quality substantially (because dark texels/black are very common) and is highly recommended. Do
|
||||
// not enable on BC3-5 textures.
|
||||
//
|
||||
// - stride is the source pixel stride, in bytes. It's typically 4.
|
||||
//
|
||||
// - chan0 and chan1 are the source channels. Typically they will be 0 and 1.
|
||||
//
|
||||
// All encoding and decoding functions are thread-safe.
|
||||
//
|
||||
// To reduce the compiled size of the encoder, set #define RGBCX_USE_SMALLER_TABLES to 1 before including this header.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "BC1/BC1Block.h"
|
||||
#include "BC3/BC3Block.h"
|
||||
#include "BC4/BC4Block.h"
|
||||
#include "BC5/BC5Block.h"
|
||||
#include "Interpolator.h"
|
||||
|
||||
// By default, the table used to accelerate cluster fit on 4 color blocks uses a 969x128 entry table.
|
||||
// To reduce the executable size, set RGBCX_USE_SMALLER_TABLES to 1, which selects the smaller 969x32 entry table.
|
||||
#ifndef RGBCX_USE_SMALLER_TABLES
|
||||
#define RGBCX_USE_SMALLER_TABLES 0
|
||||
#endif
|
||||
|
||||
namespace rgbcx {
|
||||
enum class bc1_approx_mode {
|
||||
// The default mode. No rounding for 4-color colors 2,3. My older tools/compressors use this mode.
|
||||
// This matches the D3D10 docs on BC1.
|
||||
cBC1Ideal = 0,
|
||||
|
||||
// NVidia GPU mode.
|
||||
cBC1NVidia = 1,
|
||||
|
||||
// AMD GPU mode.
|
||||
cBC1AMD = 2,
|
||||
|
||||
// This mode matches AMD Compressonator's output. It rounds 4-color colors 2,3 (not 3-color color 2).
|
||||
// This matches the D3D9 docs on DXT1.
|
||||
cBC1IdealRound4 = 3
|
||||
};
|
||||
|
||||
// init() MUST be called once before using the BC1 encoder.
|
||||
// This function may be called multiple times to change the BC1 approximation mode.
|
||||
// This function initializes global state, so don't call it while other threads inside the encoder.
|
||||
// Important: If you encode textures for a specific vendor's GPU's, beware that using that texture data on other GPU's may result in ugly artifacts.
|
||||
// Encode to cBC1Ideal unless you know the texture data will only be deployed or used on a specific vendor's GPU.
|
||||
void init(bc1_approx_mode mode = bc1_approx_mode::cBC1Ideal);
|
||||
|
||||
// Optimally encodes a solid color block to BC1 format.
|
||||
void encode_bc1_solid_block(void *pDst, uint32_t fr, uint32_t fg, uint32_t fb, bool allow_3color);
|
||||
|
||||
// BC1 low-level API encoder flags. You can ignore this if you use the simple level API.
|
||||
enum {
|
||||
// Try to improve quality using the most likely total orderings.
|
||||
// The total_orderings_to_try parameter will then control the number of total orderings to try for 4 color blocks, and the
|
||||
// total_orderings_to_try3 parameter will control the number of total orderings to try for 3 color blocks (if they are enabled).
|
||||
cEncodeBC1UseLikelyTotalOrderings = 2,
|
||||
|
||||
// Use 2 least squares pass, instead of one (same as stb_dxt's HIGHQUAL option).
|
||||
// Recommended if you're enabling cEncodeBC1UseLikelyTotalOrderings.
|
||||
cEncodeBC1TwoLeastSquaresPasses = 4,
|
||||
|
||||
// cEncodeBC1Use3ColorBlocksForBlackPixels allows the BC1 encoder to use 3-color blocks for blocks containing black or very dark pixels.
|
||||
// You shader/engine MUST ignore the alpha channel on textures encoded with this flag.
|
||||
// Average quality goes up substantially for my 100 texture corpus (~.5 dB), so it's worth using if you can.
|
||||
// Note the BC1 encoder does not actually support transparency in 3-color mode.
|
||||
// Don't set when encoding to BC3.
|
||||
cEncodeBC1Use3ColorBlocksForBlackPixels = 8,
|
||||
|
||||
// If cEncodeBC1Use3ColorBlocks is set, the encoder can use 3-color mode for a small but noticeable gain in average quality, but lower perf.
|
||||
// If you also specify the cEncodeBC1UseLikelyTotalOrderings flag, set the total_orderings_to_try3 paramter to the number of total orderings to try.
|
||||
// Don't set when encoding to BC3.
|
||||
cEncodeBC1Use3ColorBlocks = 16,
|
||||
|
||||
// cEncodeBC1Iterative will greatly increase encode time, but is very slightly higher quality.
|
||||
// Same as squish's iterative cluster fit option. Not really worth the tiny boost in quality, unless you just don't care about perf. at all.
|
||||
cEncodeBC1Iterative = 32,
|
||||
|
||||
// cEncodeBC1BoundingBox enables a fast all-integer PCA approximation on 4-color blocks.
|
||||
// At level 0 options (no other flags), this is ~15% faster, and higher *average* quality.
|
||||
cEncodeBC1BoundingBox = 64,
|
||||
|
||||
// Use a slightly lower quality, but ~30% faster MSE evaluation function for 4-color blocks.
|
||||
cEncodeBC1UseFasterMSEEval = 128,
|
||||
|
||||
// Examine all colors to compute selectors/MSE (slower than default)
|
||||
cEncodeBC1UseFullMSEEval = 256,
|
||||
|
||||
// Use 2D least squares+inset+optimal rounding (the method used in Humus's GPU texture encoding demo), instead of PCA.
|
||||
// Around 18% faster, very slightly lower average quality to better (depends on the content).
|
||||
cEncodeBC1Use2DLS = 512,
|
||||
|
||||
// Use 6 power iterations vs. 4 for PCA.
|
||||
cEncodeBC1Use6PowerIters = 2048,
|
||||
|
||||
// Check all total orderings - *very* slow. The encoder is not designed to be used in this way.
|
||||
cEncodeBC1Exhaustive = 8192,
|
||||
|
||||
// Try 2 different ways of choosing the initial endpoints.
|
||||
cEncodeBC1TryAllInitialEndponts = 16384,
|
||||
|
||||
// Same as cEncodeBC1BoundingBox, but implemented using integer math (faster, slightly less quality)
|
||||
cEncodeBC1BoundingBoxInt = 32768,
|
||||
|
||||
// Try refining the final endpoints by examining nearby colors.
|
||||
cEncodeBC1EndpointSearchRoundsShift = 22,
|
||||
cEncodeBC1EndpointSearchRoundsMask = 1023U << cEncodeBC1EndpointSearchRoundsShift,
|
||||
};
|
||||
|
||||
// DEFAULT_TOTAL_ORDERINGS_TO_TRY is around 3x faster than libsquish at slightly higher average quality. 10-16 is a good range to start to compete against
|
||||
// libsquish.
|
||||
const uint32_t DEFAULT_TOTAL_ORDERINGS_TO_TRY = 10;
|
||||
|
||||
const uint32_t DEFAULT_TOTAL_ORDERINGS_TO_TRY3 = 1;
|
||||
|
||||
// Encodes a 4x4 block of RGBX (X=ignored) pixels to BC1 format.
|
||||
// This is the simplified interface for BC1 encoding, which accepts a level parameter and converts that to the best overall flags.
|
||||
// The pixels are in RGBA format, where R is first in memory. The BC1 encoder completely ignores the alpha channel (i.e. there is no punchthrough alpha
|
||||
// support). This is the recommended function to use for BC1 encoding, becuase it configures the encoder for you in the best possible way (on average). Note
|
||||
// that the 3 color modes won't be used at all until level 5 or higher. No transparency supported, however if you set use_transparent_texels_for_black to true
|
||||
// the encoder will use transparent selectors on very dark/black texels to reduce MSE.
|
||||
const uint32_t MIN_LEVEL = 0, MAX_LEVEL = 18;
|
||||
void encode_bc1(uint32_t level, void *pDst, const uint8_t *pPixels, bool allow_3color, bool use_transparent_texels_for_black);
|
||||
|
||||
// Low-level interface for BC1 encoding.
|
||||
// Always returns a 4 color block, unless cEncodeBC1Use3ColorBlocksForBlackPixels or cEncodeBC1Use3ColorBlock flags are specified.
|
||||
// total_orderings_to_try controls the perf. vs. quality tradeoff on 4-color blocks when the cEncodeBC1UseLikelyTotalOrderings flag is used. It must range
|
||||
// between [MIN_TOTAL_ORDERINGS, MAX_TOTAL_ORDERINGS4]. total_orderings_to_try3 controls the perf. vs. quality tradeoff on 3-color bocks when the
|
||||
// cEncodeBC1UseLikelyTotalOrderings and the cEncodeBC1Use3ColorBlocks flags are used. Valid range is [0,MAX_TOTAL_ORDERINGS3] (0=disabled).
|
||||
void encode_bc1(void *pDst, const uint8_t *pPixels, uint32_t flags = 0, uint32_t total_orderings_to_try = DEFAULT_TOTAL_ORDERINGS_TO_TRY,
|
||||
uint32_t total_orderings_to_try3 = DEFAULT_TOTAL_ORDERINGS_TO_TRY3);
|
||||
|
||||
// Encodes a 4x4 block of RGBA pixels to BC3 format.
|
||||
// There are two encode_bc3() functions.
|
||||
// The first is the recommended function, which accepts a level parameter.
|
||||
// The second is a low-level version that allows fine control over BC1 encoding.
|
||||
void encode_bc3(uint32_t level, BC3Block *pDst, const uint8_t *pPixels);
|
||||
void encode_bc3(BC3Block *pDst, const uint8_t *pPixels, uint32_t flags = 0, uint32_t total_orderings_to_try = DEFAULT_TOTAL_ORDERINGS_TO_TRY);
|
||||
|
||||
// Encodes a single channel to BC4.
|
||||
// stride is the source pixel stride in bytes.
|
||||
void encode_bc4(void *pDst, const uint8_t *pPixels, uint32_t stride = 4);
|
||||
|
||||
// Encodes two channels to BC5.
|
||||
// chan0/chan1 control which channels, stride is the source pixel stride in bytes.
|
||||
void encode_bc5(BC5Block *pDst, const uint8_t *pPixels, uint32_t chan0 = 0, uint32_t chan1 = 1, uint32_t stride = 4);
|
||||
|
||||
// Decompression functions.
|
||||
|
||||
// Returns true if the block uses 3 color punchthrough alpha mode.
|
||||
bool unpack_bc1(const void *pBlock_bits, void *pPixels, bool set_alpha = true, bc1_approx_mode mode = bc1_approx_mode::cBC1Ideal);
|
||||
|
||||
void unpack_bc4(const void *pBlock_bits, uint8_t *pPixels, uint32_t stride = 4);
|
||||
|
||||
// Returns true if the block uses 3 color punchthrough alpha mode.
|
||||
bool unpack_bc3(const void *pBlock_bits, void *pPixels, bc1_approx_mode mode = bc1_approx_mode::cBC1Ideal);
|
||||
|
||||
void unpack_bc5(const void *pBlock_bits, void *pPixels, uint32_t chan0 = 0, uint32_t chan1 = 1, uint32_t stride = 4);
|
||||
} // namespace rgbcx
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright(c) 2020 Richard Geldreich, Jr.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files(the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions :
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain(www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non - commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain.We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors.We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
@ -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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BC1/BC1Decoder.h"
|
||||
#include "BC3/BC3Decoder.h"
|
||||
#include "BC4/BC4Decoder.h"
|
||||
#include "BC5/BC5Decoder.h"
|
1308
src/tables.cpp
1308
src/tables.cpp
File diff suppressed because it is too large
Load Diff
66
src/tables.h
66
src/tables.h
@ -1,66 +0,0 @@
|
||||
// rgbcx.h v1.12
|
||||
// High-performance scalar BC1-5 encoders. Public Domain or MIT license (you choose - see below), written by Richard Geldreich 2020 <richgel99@gmail.com>.
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
const uint32_t MIN_TOTAL_ORDERINGS = 1;
|
||||
const uint32_t MAX_TOTAL_ORDERINGS3 = 32;
|
||||
|
||||
#if RGBCX_USE_SMALLER_TABLES
|
||||
const uint32_t MAX_TOTAL_ORDERINGS4 = 32;
|
||||
#else
|
||||
const uint32_t MAX_TOTAL_ORDERINGS4 = 128;
|
||||
#endif
|
||||
|
||||
const uint32_t NUM_UNIQUE_TOTAL_ORDERINGS4 = 969;
|
||||
extern const uint8_t g_unique_total_orders4[NUM_UNIQUE_TOTAL_ORDERINGS4][4];
|
||||
|
||||
const uint32_t NUM_UNIQUE_TOTAL_ORDERINGS3 = 153;
|
||||
extern const uint8_t g_unique_total_orders3[NUM_UNIQUE_TOTAL_ORDERINGS3][3];
|
||||
|
||||
extern const uint16_t g_best_total_orderings4[NUM_UNIQUE_TOTAL_ORDERINGS4][MAX_TOTAL_ORDERINGS4];
|
||||
|
||||
extern const uint8_t g_best_total_orderings3[NUM_UNIQUE_TOTAL_ORDERINGS3][32];
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright(c) 2020 Richard Geldreich, Jr.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files(the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions :
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain(www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non - commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain.We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors.We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
Binary file not shown.
Before Width: | Height: | Size: 1.3 MiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user