368 Commits

Author SHA1 Message Date
c92d58d115 Rework ranges library
Better matches the standard library, and iterators moved to their own file
2022-07-05 22:51:25 -07:00
9b3c1d0ca3 Add subrange template 2022-07-03 19:08:15 -07:00
db24af730e Use int for sizes in matrix type 2022-07-03 11:56:37 -07:00
f77ea3be0f MSVC is a joke
For some reason index variables need to be signed?
2022-07-02 17:14:12 -07:00
6afe4851bd Address some of the more annoying gcc warnings 2022-07-02 17:02:28 -07:00
3a27a89155 oopsz 2022-07-01 20:11:30 -07:00
768248c20d Add option to enable sanitization in python module
this is off by default, since it requires juggling some platform-specific environment variables
2022-07-01 19:30:21 -07:00
0bd0c6846f you win this time, GCC 2022-06-30 23:46:24 -07:00
dcd9bf4287 Enable sanitizers in tests
These all throw a fit when pointed at cpython unfortunately
2022-06-30 23:39:42 -07:00
32a411634e Fix mistaken use of max() instead of max_element() 2022-06-30 21:54:55 -07:00
bac61eb0fe Try to make testing return better errors 2022-06-28 17:47:37 -07:00
2cfcd26a90 Sum and matrix multiply tests 2022-06-28 17:08:30 -07:00
3119ba1a6c oops 2022-06-28 16:28:04 -07:00
bfba3228f0 Last attempt 2022-06-28 16:22:29 -07:00
2d7aeeb2d8 attempt to make MSVC happy 2022-06-28 16:06:45 -07:00
3849303a9b Workaround for GCC
Technically this syntax isnt required, but GCC has a bug that hasnt been fixed since 2015. see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67274
2022-06-28 15:48:24 -07:00
c41e023735 Fix VecBase constructor to only take the scalar type 2022-06-28 15:04:08 -07:00
0ee45ba966 Add improved, generalized arithmetic matrix tests 2022-06-28 14:57:55 -07:00
73441d1ed3 MSVC stop being annoying 2022-06-26 20:17:56 -07:00
54d61e0bd3 Fix iterators maybe 2022-06-26 19:50:36 -07:00
487f05c90a Pass config to ctest for windows 2022-06-26 19:33:43 -07:00
b5a55f606c attempt 2 2022-06-26 19:27:25 -07:00
3ab354db74 Run ctest from build directory 2022-06-26 19:21:04 -07:00
ef8a41fe03 Run tests verbosely 2022-06-26 19:08:14 -07:00
598175739f try using ctest
im so close to giving up on windows support
2022-06-26 19:03:25 -07:00
963d985572 make MSVC shut up 2022-06-26 18:50:19 -07:00
143bde78d6 Fix running tests 2022-06-26 18:43:53 -07:00
a96aadc867 use gcc 10 2022-06-26 18:34:06 -07:00
29741447cd syntax error 2022-06-26 18:13:38 -07:00
9011718c09 add NOPYTHON option 2022-06-26 18:09:40 -07:00
8b2c240094 syntax error 2022-06-26 18:02:40 -07:00
c97daa21ec would help if i cloned the repo 2022-06-26 18:00:30 -07:00
f5defd2817 Move testing to its own step 2022-06-26 17:57:41 -07:00
49ba7e26b7 Run C tests in CI 2022-06-26 17:40:05 -07:00
bdd75ddddf oops 2022-06-26 17:27:47 -07:00
e528c12b2d Introduce custom map function 2022-06-26 17:16:02 -07:00
aec31a2fdc improved clamp and sum 2022-06-24 21:41:43 -07:00
17663f4871 First trial run using the Matrix type 2022-06-22 22:32:19 -07:00
f2352f10fd Smarter map function using variadics and chunking 2022-06-22 20:39:40 -07:00
3ceb028907 Attempt to batch some matrix ops 2022-06-22 00:39:36 -07:00
10ba6b2bd6 Remove utest 2022-06-20 18:42:31 -07:00
1c06cccd5c More vector unit tests 2022-06-19 18:33:54 -07:00
232fb6cb41 use position independent code 2022-06-18 17:14:48 -07:00
19df5df68d Rework project layout and tests 2022-06-18 17:06:17 -07:00
3756f31e20 matrix multiplication and transposition 2022-06-13 22:55:41 -07:00
2c59419bf0 Cleanup and replace Matrix.h with Vec.h
Mysteriously this also (perhaps temporarily) fixed a CPU usage issue in Clion? I guess I'll take it
2022-06-12 20:01:56 -07:00
f767525aa1 Improved matrix/vector class 2022-06-12 17:06:53 -07:00
59fefae3f7 A 2022-06-09 22:48:52 -07:00
0bcfd50a44 I s2g I will make Stallman have a Nice Time 2022-06-08 23:15:51 -07:00
527067839f GCC has a very toxic view on friendship as a concept tbh 2022-06-08 23:12:42 -07:00
a33cb8ea67 Add new vector ops with smarter type deduction 2022-06-08 23:07:43 -07:00
d293687424 Flesh out ranges library 2022-06-06 22:31:41 -07:00
6f075b6c1d Reorganize and add Window class 2022-06-05 17:12:04 -07:00
f88212af85 ColorSet extension/alias for Matrix 2022-06-02 23:45:53 -07:00
d3515c1db8 Add matrix template 2022-06-02 23:15:57 -07:00
20305d2ea9 tweaks and formatting 2022-06-02 22:10:38 -07:00
abfe0b8d10 Header file reorganization 2022-06-01 23:50:35 -07:00
f097f71ba9 Assorted cleanup 2022-05-31 23:09:20 -07:00
961c2b7134 Build on macOS 12
This should build on older versions, so long as you have llvm 13. But the homebrew version of llvm the macos 11 runner has only includes x86 dylibs which cant be linked against when building for arm.
2022-05-31 01:16:18 -07:00
9388406769 oops 2022-05-31 01:13:21 -07:00
7430dccd5b im going to break MSVC's kneecaps 2022-05-31 01:03:52 -07:00
fa0579ff03 thats not a dollar sign 2022-05-31 00:53:15 -07:00
9f7eb5fe57 Target LLVM 13 2022-05-31 00:49:30 -07:00
3b7164ffba Refactor pack() and unpack() 2022-05-30 22:41:17 -07:00
dae507acc9 Don't build wheels for musl
I don't understand the reason these don't compile
2022-05-29 20:37:49 -07:00
7eac371064 less iterators 2022-05-29 19:03:47 -07:00
53a6427dcc iterators are confusing 2022-05-29 16:51:35 -07:00
b9c7c7cf6e Fix broken constructor 2022-05-29 15:58:18 -07:00
debaa6b54d Add Vector template class
Also experimentally bump to C++20 just to see if it works on GCC 9.3
2022-05-29 15:54:55 -07:00
c96450b5fe Rename Color to prepare for refactor 2022-05-25 23:42:06 -07:00
fffa291765 Fix LeastSquares mode and add tests for every quality level 2022-05-24 22:57:51 -07:00
829b5312b5 Tweak compiler warnings 2022-05-24 21:33:39 -07:00
c57106e3b2 Skip version number checking with debug builds
Because I found a way to build the extension module directly which helps speed up development immensely
2022-05-24 20:47:55 -07:00
468414f339 Add arm whadd instructions for all sizes of integer 2022-05-23 23:42:14 -07:00
f9831b1f61 :unsmilebeale: 2022-05-23 00:46:23 -07:00
0046bef9d3 Cmake why 2022-05-23 00:41:46 -07:00
8f19ad6a1d I cant spell
This reverts commit ed10899601.
2022-05-23 00:37:28 -07:00
ed10899601 just never include windows.h
tis a silly OS
2022-05-23 00:35:12 -07:00
2588ebcaa3 its late ok? 2022-05-23 00:28:01 -07:00
345344eef3 cleanup and prevent windows macros from stepping on everything 2022-05-23 00:21:33 -07:00
bf35983b2d Add tests for c-level code 2022-05-22 22:20:30 -07:00
c2d4e9be4d Merge branch 'feature/pytest' into feature/simd 2022-05-22 21:02:34 -07:00
920059bea1 Migrate tests to pytest 2022-05-22 18:40:13 -07:00
daae86cf50 Switch to pytest 2022-05-22 16:50:24 -07:00
c05879f1c1 Fixes and tweaks to whadd 2022-05-22 16:38:54 -07:00
aa6bd9602d Add widening hadd 2022-05-21 21:23:22 -07:00
8c77356aca Begin integrating xsimd 2022-05-20 20:46:45 -07:00
79f77a24b2 Remove Highway 2022-05-20 20:18:18 -07:00
04fece2771 Move clang-cl detection to cmake and allow it when setting flags 2022-05-16 23:41:42 -07:00
7ba2225644 Fix typo 2022-05-16 23:18:54 -07:00
4b3e236275 Tell Cmake to use clang-cl when requested 2022-05-16 23:13:40 -07:00
b2523dbe19 Try something 2022-05-16 23:05:09 -07:00
bcdfcb95fb h 2022-05-16 21:33:33 -07:00
e5f1a45c6b *raspberry noise* 2022-05-16 21:32:01 -07:00
014f7063fd Build wheels for SSE4 and using clang on windows 2022-05-16 21:29:45 -07:00
74aaac00d7 Fix AVX2 bug and add cmake SIMD ISA selection
By default, quicktex builds with -march=native on x86, unless an environment variable requesting a specific ISA is set. This doesnt work on MSVC though because it's a shit compiler, so it just falls back to no flags.
2022-05-16 20:52:31 -07:00
8168d6e249 Add emulated 128-bit support and fix x86 2022-05-15 20:03:05 -07:00
f7b0cbe76b add widening horizontal add for s16 vectors 2022-05-15 18:08:36 -07:00
bc925d3949 Include Highway in build 2022-05-15 00:43:28 -07:00
7f75104d18 Spell external correctly and exclude all affinity designer files 2022-05-13 00:51:39 -07:00
643276660a Include submodules in sdists and github actions 2022-05-13 00:36:00 -07:00
9789ecd159 Add Highway dependency 2022-05-13 00:32:35 -07:00
5c87c82702 Add file documenting development environment setup 2022-05-11 23:22:51 -07:00
ddbeff43cb Update copyright year 2022-05-11 20:51:35 -07:00
5c94782876 Remove debug wrapper, now that I know how to use a debug python build 2022-05-10 22:08:01 -07:00
9eaaf901f3 Fix compilation of test wrapper 2022-05-08 16:06:36 -07:00
c79ffc8794 Bump docker/setup-qemu-action from 1 to 2
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 1 to 2.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-06 18:33:39 -07:00
55f0ced229 getting started instructions 2022-05-04 23:29:28 -07:00
eb7b259d53 Autogenerated command documentation from helpstrings 2022-05-04 23:03:06 -07:00
03801e2e1b New index page and remove broken page 2022-05-04 22:51:40 -07:00
3a28ec690c Merge remote-tracking branch 'origin/dependabot/github_actions/pypa/cibuildwheel-2.5.0' into dev 2022-05-02 22:42:11 -07:00
697f7243a0 Documentation nice-to-haves 2022-05-02 22:41:39 -07:00
22e1455ceb tell dependabot to target the dev branch 2022-05-02 22:26:05 -07:00
c13f64828f Bump pypa/cibuildwheel from 2.4.0 to 2.5.0
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.4.0 to 2.5.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.4.0...2.5.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-02 11:41:47 +00:00
9a57b096f5 Set min version for sphinx tools 2022-04-21 09:34:21 -07:00
82f079f1b6 Enable RTD building 2022-04-19 21:55:00 -07:00
2c72b7ad22 Ignore previous commit in blames 2022-04-18 19:56:41 -07:00
cb84f32eda Migrate code style to Black 2022-04-18 19:53:26 -07:00
b34fdf2316 Ignore wheels and sdists 2022-04-16 22:40:54 -07:00
ac4e5b2679 Release 0.1.3
### Fixed

- Fixed quicktex not compiling for python 3.10 on Windows

### Changed

- Reworked CI job, adding wheels for ARM macOS, ARM Linux, and x86 musl Linux.
- Added wheels for python 3.10
- Added a more useful error message when importing quicktex on macOS when libomp.dylib isn't installed
2022-04-12 19:21:06 -07:00
25e74b9b08 Bump actions/setup-python from 3.1.1 to 3.1.2
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v3.1.1...v3.1.2)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-12 18:24:37 -07:00
a881a0a36b Add more helpful error when importing without libomp installed
Also use non-shallow clones in ci
2022-04-11 23:05:20 -07:00
3fdfc3ecaa Pretty job names 2022-04-11 21:48:22 -07:00
b440543de3 why 2022-04-11 21:18:43 -07:00
23ed54c7a2 fix conditional 2022-04-11 21:15:15 -07:00
e7e8657100 fix syntax error 2022-04-11 21:11:19 -07:00
2a07db8c8f Run arm64 linux builds on their own job for faster CI 2022-04-11 21:09:50 -07:00
b8a80235f8 Bump actions/upload-artifact from 2 to 3
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-11 10:26:24 -07:00
4cac24798e Bump actions/download-artifact from 2 to 3
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-11 10:25:24 -07:00
9b6097373e Another attempt 2022-04-10 22:03:17 -07:00
b954ac6ccc Attempt to hint to cmake what python install to use correctly
Should fix my linux building issue?
2022-04-10 21:51:18 -07:00
593a0c3f46 Fix windows test command and install fat binaries for libomp 2022-04-10 21:08:07 -07:00
3b73bc8bce use Py_ssize_t to make msvc happy 2022-04-10 01:06:25 -07:00
abeb08fc81 Upgrade cibuildwheel to 2.4.0 2022-04-10 00:26:00 -07:00
77637f6abd Run tests in the right directory and skip linux for now 2022-04-10 00:22:13 -07:00
df6d5b1848 Use latest setuptools instead of pinning it 2022-04-10 00:09:44 -07:00
b5aea803d5 Use relative imports in tests 2022-04-10 00:04:08 -07:00
b80a6d2229 Fix arch selection and test command 2022-04-09 23:15:23 -07:00
dac7f07db4 Build for musl linux for platforms supported by Pillow
Specifically x64 for cpython 3.8-3.10
2022-04-09 22:54:06 -07:00
7dfefa3007 Skip musl linux wheel builds
Pillow appears to fail to compile on these without installing a bunch of dependencies, so... sorry alpine users
2022-04-09 22:40:05 -07:00
eaca455a08 syntax error 2022-04-09 22:24:35 -07:00
e5ccdbb4f4 Don't try to download test images 2022-04-09 22:23:00 -07:00
94d88c7e00 Remove nose dependence
Also move test images into the base repo because they're not very big anyways
2022-04-09 22:20:09 -07:00
9421a6d372 Merge branch 'main' into build-modernization 2022-04-09 20:39:43 -07:00
cab0eeebae Merge pull request #10 from drewcassidy/dependabot/github_actions/actions/checkout-3
Bump actions/checkout from 2 to 3
2022-04-09 20:21:43 -07:00
3d98b37a37 Merge pull request #13 from drewcassidy/dependabot/github_actions/actions/setup-python-3.1.1
Bump actions/setup-python from 2 to 3.1.1
2022-04-09 20:21:33 -07:00
654b6d628a Slightly modernize how project is compiled 2022-04-09 19:43:40 -07:00
1502c5318c Bump actions/setup-python from 2 to 3.1.1
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 3.1.1.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v2...v3.1.1)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-07 11:45:36 +00:00
421876ab0f Move more metadata to pyproject.toml 2022-04-03 20:41:02 -07:00
29590e0323 Move a good chunk of metadata to pyproject.toml 2022-04-03 16:22:47 -07:00
8b4e3c5746 Bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-03 20:15:09 +00:00
8e7b95609c Dependabot for gh actions 2022-04-03 13:12:29 -07:00
71379b7ae1 Revert to manylinux2014 2022-04-03 00:05:35 -07:00
d1346ca11d Enable macos cross-compiling in setup.py 2022-04-02 21:39:14 -07:00
e488dbcbff Fix builder version
and here I was thinking this was semver smh
2022-04-01 21:07:57 -07:00
1f7b45aa57 Compile for multiple architectures 2022-04-01 21:06:51 -07:00
70b7251eae Include build for python 3.10 (finally) 2022-04-01 20:27:00 -07:00
e8e0f4e29b Release 0.1.2
### Fixed

- Fixed sdist not including pybind
2022-03-27 14:29:57 -07:00
6d7f56476f Add manifest file 2022-03-27 14:28:17 -07:00
b4d2388615 Release 0.1.1
### Fixed

- Fixed alpha premultiplication when generating mipmaps
2021-09-28 20:42:13 -07:00
f7d57aa859 Fix alpha premultiplication being used when generating mipmaps 2021-09-28 20:40:01 -07:00
24b064e6b4 Version 0.1.0
### Added

- Began publishing to PyPI

### Changed

- Rewrote CI workflow to include ManyLinux2014 builds
- Reverted project to C++17 for better compiler compatibility
2021-05-09 19:46:51 -07:00
28930d89d5 update changelog 2021-05-09 19:46:05 -07:00
4f25e0f750 fix tests dir 2021-05-08 23:15:52 -07:00
433e728424 pytmon 2021-05-08 23:07:37 -07:00
4939171d5d Rework CI workflow 2021-05-08 23:06:13 -07:00
f0f132328c Dont require findpython mode 2021-05-08 19:54:16 -07:00
7a11901aa6 Fix brace expansion 2021-05-08 18:41:41 -07:00
cb8b251baf Put back matrix for os 2021-05-08 18:29:45 -07:00
463956b63b Fix environment variables..? 2021-05-08 18:22:31 -07:00
22cc5f6148 Fix environment variables 2021-05-08 18:18:14 -07:00
0478532cb9 Test using CIBW 2021-05-08 18:12:37 -07:00
f2873f3a38 Downgrade project to C++17 2021-05-08 16:50:05 -07:00
2618faadfc Fix cli module 2021-04-20 15:14:51 -07:00
38d3805add fix pfflags and mipmap count 2021-04-20 11:23:14 -07:00
f87226ad50 dont set -O3 since its the default anyways 2021-04-14 00:14:21 -07:00
8967bc2f1a use member initialization for constexpr constructors 2021-04-13 23:39:24 -07:00
b3d6585799 install for tests using pip 2021-04-13 20:48:19 -07:00
cde6ae006e dont install tests as a package 2021-04-13 20:32:56 -07:00
2b36086bf5 Move build requirements to pyproject.toml 2021-04-13 19:22:11 -07:00
faa7e4df08 Remove test images as a submodule
They were causing problems for users without LFS installed, and unnecessary except for tests
2021-04-13 01:15:59 -07:00
f353bf6e92 metadata and readme fixes 2021-04-12 22:11:34 -07:00
7954ec1de6 wtf is this "manylinux" nonsense 2021-04-12 21:39:10 -07:00
890d60cd82 Fix version number 2021-04-12 21:02:21 -07:00
3ebf07ae74 Only run jobs when relevent 2021-04-12 20:56:51 -07:00
4a8e416f1f I'm dumb, this is the default on all compilers 2021-04-12 20:42:30 -07:00
a07fd55014 gcc no like underscore 2021-04-12 20:28:17 -07:00
803dccae2d do it correctly this time 2021-04-12 20:26:03 -07:00
148fb36273 fix verbose usage again 2021-04-12 20:11:31 -07:00
b3f6350353 fix --verbose usage 2021-04-12 20:09:46 -07:00
d4c91e21b8 test CI-specific build rules 2021-04-12 20:07:04 -07:00
de2dc0c0a8 Actually clone the repo for sdist 2021-04-12 16:45:34 -07:00
96dc62a1b0 Add long description and use system-level build 2021-04-12 16:31:16 -07:00
c18cebb519 put all wheels in same directory 2021-04-12 16:12:31 -07:00
784f2393f1 fix test dependencies 2021-04-12 15:59:27 -07:00
83b4914bb0 split build and publish 2021-04-12 15:57:06 -07:00
72aeb54c18 fix syntax error 2021-04-12 15:31:49 -07:00
60a944500b Build and publish to test-pypi 2021-04-12 15:31:14 -07:00
f34824b45b remove redundant windows env setup 2021-04-12 14:53:44 -07:00
68e37ca836 use setuptools_scm for versioning 2021-04-12 02:23:57 -07:00
46a62b0dab add note to initialize submodules 2021-04-10 23:15:43 -07:00
9156ac199b remove broken unit test 2021-04-10 22:25:42 -07:00
69fdd6a4c8 fix typo in help text 2021-04-10 22:11:29 -07:00
d5a3f09a58 Add useful README 2021-04-10 21:52:17 -07:00
0152e99c44 Add encode command 2021-04-10 21:34:49 -07:00
560acb20ea Add __version__ 2021-04-10 12:15:20 -07:00
362c1c0715 dont try to run cli from __init__.py 2021-04-10 01:50:25 -07:00
4c6b683616 move common code to its own module and out of __init__.py, move command to __main__.py 2021-04-10 01:34:26 -07:00
b8aba4d382 move common code to its own module and out of __init__.py 2021-04-10 01:28:03 -07:00
1f0a577d7f document some things 2021-04-09 23:21:53 -07:00
76da50d003 More decode cleanup 2021-04-09 23:18:40 -07:00
8a85f5c920 decode cleanup 2021-04-09 02:19:58 -07:00
156880d430 Add decode command 2021-04-09 01:33:30 -07:00
8b6ea69300 DDS file class 2021-04-08 21:26:58 -07:00
09e05b7cde API changes 2021-04-08 16:04:21 -07:00
7149864173 tweaks 2021-04-07 23:39:50 -07:00
2fa4f59104 License comment update 2021-04-07 23:26:28 -07:00
ac16b14a92 formatting 2021-04-07 23:21:54 -07:00
7154857323 install openMP on macos but with the correct tap name 2021-04-07 22:36:21 -07:00
6e0c855470 install openMP on macos 2021-04-07 22:33:25 -07:00
253b9c76ba remove cmake workflow 2021-04-07 21:11:37 -07:00
a4a19505d7 workflow tweaks 2021-04-07 21:10:58 -07:00
f5bacd5940 remove upterm stuff 2021-04-07 21:01:39 -07:00
d77665a8a9 fix findpython 2021-04-07 20:58:52 -07:00
48e118042d enable ssh debugging 2021-04-07 19:55:43 -07:00
42c9110a40 enable ssh debugging 2021-04-07 19:50:04 -07:00
d100f10931 test in isolation mode??? 2021-04-07 19:45:38 -07:00
822633ca7e test in isolation mode? 2021-04-07 19:41:32 -07:00
88f555d076 run tests in tests directory? 2021-04-07 19:34:09 -07:00
b62d64d540 Windows stop being weird 2021-04-07 19:18:18 -07:00
497203e76a cross-platform testing
why not?
2021-04-07 19:16:45 -07:00
b7b5c5f843 disable coverage 2021-04-07 19:12:09 -07:00
d6b8a18e6f add submodule tests to make sure everything is present before proceeding 2021-04-07 19:10:22 -07:00
824292c9bd Fix submodule reference 2021-04-07 18:49:16 -07:00
ca98bbb1f7 use GCC on linux 2021-04-07 18:42:45 -07:00
d5c2dfb96b fix syntax error 2021-04-07 17:40:13 -07:00
aacceb6cff Fix submodule checkout 2021-04-07 17:39:01 -07:00
0348407acb use ninja for compile 2021-04-07 17:35:56 -07:00
6578729e1c Merge branch 'feature-cli' 2021-04-07 17:28:36 -07:00
93baaacf5f Create python-package.yml 2021-04-07 17:26:38 -07:00
2bb2b143f2 test docs 2021-04-07 17:07:03 -07:00
3c538f0843 test for BC4 2021-04-07 01:08:45 -07:00
b35c7d5ae9 test for index errors on BC1 2021-04-06 23:46:00 -07:00
fea4d6c2b1 improved rawtexture tests 2021-04-06 23:41:26 -07:00
f06fc809b2 Add BC1 block decoding tests and fix 2021-04-06 00:03:58 -07:00
50d411a349 Add BC1 block encoding tests 2021-04-05 23:07:33 -07:00
901dcc45df Add BC1 tests 2021-04-05 02:44:56 -07:00
8ab324a661 delete ndebug.h 2021-04-04 21:23:13 -07:00
9fdbaf2909 Use pybind11-generated docstring signatures 2021-04-03 23:53:22 -07:00
25cd3bba24 use pybind11-stubgen instead 2021-04-03 23:52:42 -07:00
190bb58367 fix stubgen and add pyi files to package 2021-04-03 22:03:50 -07:00
6e30ba871c stubgen cleanup 2021-04-03 21:17:06 -07:00
b800511fcb update gitignore 2021-04-03 21:04:54 -07:00
5d89684a6e add stub generation script 2021-04-03 21:03:24 -07:00
4fdf1085e0 bind encode/decode methods 2021-04-03 00:55:49 -07:00
424d635292 Fix interpolator container type 2021-04-03 00:36:41 -07:00
d25d8c3c56 Finish block/texture bindings 2021-04-02 19:24:54 -07:00
6831c3f6c9 Fix virtual overloading 2021-04-02 18:51:38 -07:00
639ce6ad14 Minor tweaks 2021-04-02 18:34:14 -07:00
831e86fda6 Fix broken constexpr 2021-04-02 18:25:37 -07:00
3d91ac2bed BC1Block/BC4Block cleanup 2021-04-02 01:01:01 -07:00
e1739b99d3 Fix map? 2021-04-01 23:26:16 -07:00
53605c983d Fix functor? 2021-04-01 22:45:40 -07:00
8cd870ed26 More block tweaks 2021-04-01 22:37:25 -07:00
0caa4fbc4c fix Color constexpr constructor 2021-04-01 16:50:02 -07:00
fa3fab4ca0 Block refactor 2021-04-01 16:37:15 -07:00
3852da6249 Bind BC1Block and BC1Texture 2021-03-31 20:26:40 -07:00
28b541d49a Improved subscripting 2021-03-30 02:36:27 -07:00
bd454d9d20 improved texture indexing 2021-03-29 22:56:25 -07:00
22bbdeb7b8 Add texture tests and minor api tweaks 2021-03-29 22:15:53 -07:00
0965f3958d enable buffer protocol 2021-03-29 19:58:46 -07:00
0b3e3b5cb0 update CMakeLists.txt 2021-03-29 19:38:57 -07:00
c70907ced2 Add texture creation from bytes 2021-03-29 19:38:37 -07:00
67751e87c7 Fix linking 2021-03-29 03:07:48 -07:00
caef02e57f IWYU pass 2021-03-28 03:44:29 -07:00
539980f75f fix parameter hiding of rgba 2021-03-28 03:24:11 -07:00
eea15dcc4e fix warning flags in gcc 2021-03-28 03:19:00 -07:00
8e758d5739 fix some warnings 2021-03-28 03:15:52 -07:00
c1bde8560e Disable sign conversion warnings
just tired of them, I know what I'm doing
2021-03-28 03:03:29 -07:00
fb56c6af04 Refactor to add Texture classes 2021-03-28 02:36:47 -07:00
c843871ac1 make Color have a constexpr constructor and add Block class 2021-03-25 02:07:17 -07:00
e5f60ec030 CLI scaffolding 2021-03-24 22:04:12 -07:00
2244b2117d rename some values for less confusion
From now on, "size" should refer to a value in bytes, whereas dimensions are in pixels or blocks and usually a 2-tuple
2021-03-24 21:49:46 -07:00
18e3089eb0 some util functions 2021-03-24 21:45:05 -07:00
98061239f9 ignore .so files 2021-03-23 21:45:05 -07:00
96497cac20 add Click as a dependency 2021-03-23 20:04:00 -07:00
5f13b841ad Extremely bad hardcoded DDS file reading 2021-03-23 16:27:31 -07:00
dae82c0662 fix error 2021-03-23 03:14:23 -07:00
8676a6c10f various DDSFile tweaks and documentation 2021-03-23 03:12:38 -07:00
c86eca293c various DDSFile tweaks 2021-03-23 02:23:48 -07:00
023be6d770 add dds module 2021-03-23 02:00:17 -07:00
3ed1e7dfa4 improved tests 2021-03-21 23:36:34 -07:00
f7821c3a70 Document base classes 2021-03-21 20:36:26 -07:00
7c91139775 document BlockEncoder and BlockDecoder 2021-03-21 02:10:28 -07:00
920bc8d9fe documentation refinement 2021-03-21 01:46:17 -07:00
882c970e27 Add better documentation for s3tc 2021-03-20 21:19:23 -07:00
63978703cd I'm so confused part 4 2021-03-19 14:38:59 -07:00
2a9dbe6a92 I'm so confused part 3 2021-03-19 14:36:45 -07:00
d39679adaf I'm so confused part 2 2021-03-19 14:24:29 -07:00
862500cec3 I'm so confused 2021-03-19 14:19:46 -07:00
6a85b43542 Try using Clang-11 2021-03-19 14:11:54 -07:00
9d4e4c6c2b attempted linux build fix
I am so confused
2021-03-19 14:01:09 -07:00
4ec588c8e3 Fix color includes 2021-03-19 13:47:54 -07:00
7a81bbb58d Fix last commit 2021-03-19 01:46:33 -07:00
5a272c954f make BC4 channel readonly 2021-03-19 01:39:21 -07:00
285e891bef Fix BC1 binding order 2021-03-18 02:45:47 -07:00
71119b9279 Improved bindings and dont use punchthrough in BC3 2021-03-18 02:34:31 -07:00
70ebb43017 Refactor and rebind constructors 2021-03-17 01:13:11 -07:00
64919ab55a Make interpolators available in python 2021-03-15 19:01:42 -07:00
d39e0c06f7 Basic documentation 2021-03-15 02:49:34 -07:00
8f1e76bb43 namespace and binding changes 2021-03-14 00:59:16 -08:00
f72b794d20 More waffling 2021-03-13 20:47:53 -08:00
ab65c0a7c8 Major reorganization and rename 2021-03-13 04:14:04 -08:00
c930d10cc4 Add sphinx docs directory 2021-03-12 19:51:42 -08:00
543601ce57 fix BC4Encoder includes 2021-03-11 19:33:53 -08:00
d686ec3c9f Add and bind the rest of the encoders 2021-03-11 19:29:11 -08:00
46d64139bd MSVC what the fuck is your problem 2021-03-11 02:29:08 -08:00
ab0d4b30af IWYU pass 2021-03-11 02:18:40 -08:00
b77a5acfb6 Decoder APIs refinement 2021-03-11 02:01:16 -08:00
18544645a2 Bind decoders 2021-03-10 04:15:09 -08:00
83d547dd8e Rearrange bindings 2021-03-10 03:49:06 -08:00
04d11112d4 Bind BC1Decoder 2021-03-10 03:42:31 -08:00
52d1185dac Parameterized tests using Nose 2021-03-10 01:15:04 -08:00
60ed47ab10 Bind BC1Encoder.ErrorMode 2021-03-09 01:25:44 -08:00
d182cbae65 Reorgamize tests 2021-03-09 01:25:20 -08:00
beb3d76a4b Remove unused files and move LUT to .cpp 2021-03-08 16:56:54 -08:00
4d82dee240 Major reorganization 2021-03-08 02:23:04 -08:00
d42eadcf86 More unit tests 2021-03-07 16:15:26 -08:00
a4c1f00014 Merge remote-tracking branch 'origin/main' into main 2021-03-07 16:14:20 -08:00
637e7b44bb test images submodule 2021-03-07 16:14:09 -08:00
36652b212e test images submodule 2021-03-07 16:13:47 -08:00
b63c26a45a More unit tests and bindings 2021-03-07 01:39:51 -08:00
e58871167e Fix hanging when creating more than 2 BC1Encoders 2021-03-07 00:52:45 -08:00
5056e07f37 Add unit tests 2021-03-06 22:41:42 -08:00
289a4fa001 Kinda working extension module 2021-03-06 14:18:08 -08:00
19028db209 Finally start testing pybind stuff 2021-03-04 02:21:16 -08:00
af7860c06a Make encoder constructable 2021-03-04 01:18:30 -08:00
be5a439b08 Stupid assert macro 2021-03-03 14:50:48 -08:00
9fbe5a2b22 more cleanup and bugfixes 2021-03-03 14:49:06 -08:00
277b38a548 cleanup and bugfixes 2021-03-03 14:36:29 -08:00
70c8169711 Add endpoint search refinement
In theory, this is all of the rgbcx feature set!
2021-03-03 04:19:40 -08:00
06c9bacab0 Get black 3-color blocks working 2021-03-03 02:04:48 -08:00
0924e43dc2 Fix errors in last commit 2021-03-02 18:32:47 -08:00
4eb8c397f9 Ground work for 3Color-black blocks 2021-03-02 18:27:35 -08:00
f7f5a10b66 Fix matrix inversion 2021-03-01 23:53:30 -08:00
ac62e1c262 Remove use of old Tables header 2021-03-01 23:13:26 -08:00
e75ae9c570 Fix circular dependency 2021-03-01 22:50:32 -08:00
5e2f790ee6 fix missing Histogram 2021-03-01 22:45:09 -08:00
05af708e84 IWYU pass 2021-03-01 22:41:31 -08:00
d0e66b31f7 Refactor order tables 2021-03-01 22:05:04 -08:00
f65008ccc3 3-color refinement 2021-02-28 21:00:29 -08:00
645549978d Basic 3-color support 2021-02-28 02:22:35 -08:00
63cf1745f9 Universal WriteBlock 2021-02-27 22:27:31 -08:00
28c9f857ea More templating 2021-02-27 21:47:22 -08:00
b4f180f5de Cleanup 2021-02-27 17:07:58 -08:00
193ac22eb5 Quick shortcut for all-black blocks since they are very common 2021-02-27 17:03:19 -08:00
9435003553 simplify EncodeBlockSingleColor 2021-02-27 16:54:06 -08:00
ed89203510 Add cluster fitting for 4-color blocks 2021-02-27 16:41:13 -08:00
ab752e51b6 In hindsight, SingleColorTable doesnt need to be a class at all 2021-02-26 22:38:37 -08:00
b57a3d2561 Util.h -> util.h 2021-02-26 22:22:20 -08:00
5b492fd4b5 Add ordertable and move single color tables to their own template class 2021-02-26 22:16:12 -08:00
68896aca1a Include climits 2021-02-23 19:46:58 -08:00
2e5cf991b0 Improved first-pass refinement 2021-02-23 19:44:36 -08:00
8acaf1ed96 Implement first chunk of refinement code 2021-02-22 02:05:14 -08:00
4d3a0c0b61 Fix min/max in block metrics 2021-02-21 23:50:09 -08:00
265a41bc2e IWYU pass 2021-02-21 23:29:47 -08:00
90ea798568 Include climits 2021-02-21 23:18:15 -08:00
534c4f7cfc First working (but still bad) BC1 encoder 2021-02-21 23:15:04 -08:00
167 changed files with 10543 additions and 18261 deletions

View File

@ -1,10 +1,11 @@
---
BasedOnStyle: google
IndentWidth: 4
ColumnLimit: 160
ColumnLimit: 120
AllowShortBlocksOnASingleLine: Always
AllowShortFunctionsOnASingleLine: All
AlwaysBreakTemplateDeclarations: MultiLine
#RequiresClausePositionStyle: SingleLine # requires Clang 15 :(
#AlignConsecutiveDeclarations: true
---

View File

@ -2,15 +2,15 @@ FormatStyle: google
Checks: '-*,clang-diagnostic-*,llvm-*,misc-*,-misc-unused-parameters,-misc-non-private-member-variables-in-classes,readability-identifier-naming,cppcoreguidelines-narrowing-conversions'
CheckOptions:
- { key: readability-identifier-naming.NamespaceCase, value: lower_case }
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
- { key: readability-identifier-naming.StructCase, value: CamelCase }
- { key: readability-identifier-naming.TemplateParameterCase, value: CamelCase }
- { key: readability-identifier-naming.FunctionCase, value: aNy_CasE }
- { key: readability-identifier-naming.VariableCase, value: lower_case }
- { key: readability-identifier-naming.MemberCase, value: lower_case }
- { key: readability-identifier-naming.PrivateMemberPrefix, value: _ }
- { key: readability-identifier-naming.ProtectedMemberPrefix, value: _ }
- { key: readability-identifier-naming.NamespaceCase, value: lower_case }
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
- { key: readability-identifier-naming.StructCase, value: CamelCase }
- { key: readability-identifier-naming.TemplateParameterCase, value: CamelCase }
- { key: readability-identifier-naming.FunctionCase, value: lower_case }
- { key: readability-identifier-naming.VariableCase, value: lower_case }
- { key: readability-identifier-naming.MemberCase, value: lower_case }
- { key: readability-identifier-naming.PrivateMemberPrefix, value: _ }
- { key: readability-identifier-naming.ProtectedMemberPrefix, value: _ }
- { key: readability-identifier-naming.EnumConstantCase, value: CamelCase }
- { key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase }
- { key: readability-identifier-naming.GlobalConstantCase, value: CamelCase }

8
.git-blame-ignore-revs Normal file
View File

@ -0,0 +1,8 @@
# git-blame ignored revisions
# To configure, run
# git config blame.ignoreRevsFile .git-blame-ignore-revs
# Requires Git > 2.23
# See https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-fileltfilegt
# Migrate code style to Black
cb84f32edab717389d03a3855aa5bd4d0db1ae3c

10
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,10 @@
# Set update schedule for GitHub Actions
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "dev"
schedule:
# Check for updates to GitHub Actions every weekday
interval: "daily"

View File

@ -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

185
.github/workflows/python-package.yml vendored Normal file
View File

@ -0,0 +1,185 @@
# 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:
test:
name: Run Unit Tests
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ macos-12, windows-latest, ubuntu-latest ]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: recursive
- name: Set up GCC
if: runner.os == 'linux'
uses: egor-tensin/setup-gcc@v1
with:
version: 10
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.12
with:
cmake-version: 'latest'
github-api-token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Python
uses: actions/setup-python@v3.1.2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install flake8 pybind11
- 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 C code
run: |
ls
cmake -S . -B build -DQUICKTEX_NOPYTHON=TRUE -DCMAKE_BUILD_TYPE=Debug
cmake --build build
- name: Test C code
run: |
ctest -V --test-dir build -C Debug
build-sdist:
name: Build SDist
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: recursive
- name: Set up Python
uses: actions/setup-python@v3.1.2
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install setuptools twine build
- name: Build SDist
run: python -m build --sdist
- name: Check metadata
run: python -m twine check dist/*
- name: Upload SDist
uses: actions/upload-artifact@v3
with:
path: dist/*.tar.gz
build-wheels:
name: Build Wheels on ${{ matrix.os }} ${{ matrix.arch[0] }}
runs-on: ${{ matrix.os }}
needs: test
strategy:
matrix:
os: [ macos-12, windows-latest, ubuntu-latest ]
arch: [ [ 'x86', 'x86_64', 'AMD64', 'x86_64' ] ] #[suffix, mac, windows, linux] arch names
include:
- os: ubuntu-latest
arch: [ 'ARM', 'arm64', 'ARM64', 'aarch64' ]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: recursive
- name: Install libomp
if: runner.os == 'macOS'
# openMP isnt part of core apple clang for some reason?
# libomp is in homebrew, which works for end users but its not a fat binary
# so we have to install it manually
# compiled dylibs courtesy of https://mac.r-project.org/openmp/ and mirrored on my own server
run: |
wget https://pileof.rocks/openmp-13.0.0-darwin21-Release.tar.gz
sudo tar fvxz openmp-*.tar.gz -C /
- name: Install QEMU
# install QEMU if building for arm linux
uses: docker/setup-qemu-action@v2
if: runner.os == 'linux' && matrix.arch[3] == 'aarch64'
with:
platforms: arm64
- name: Build wheels
uses: pypa/cibuildwheel@2.5.0
env:
MACOSX_DEPLOYMENT_TARGET: "10.9"
CIBW_ARCHS_LINUX: ${{ matrix.arch[3] }}
- name: Upload Wheels
uses: actions/upload-artifact@v3
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@v3 # just need the changelog
- name: Set up Python
uses: actions/setup-python@v3.1.2
- 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@v3
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 }}

18
.gitignore vendored
View File

@ -1,11 +1,26 @@
# Python
env/
dist/
build/
*.egg-info
*.pyc
*.pyi
*.whl
*.tar.gz
#sphinx
docs/_build/
#mypy
out
# IDEs
**/.idea
# cmake
# binaries
*.so
# cmake
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
@ -18,3 +33,4 @@ compile_commands.json
CTestTestfile.cmake
_deps
cmake-build-*
*.a

7
.gitmodules vendored
View File

@ -1,4 +1,3 @@
[submodule "extern/pybind11"]
path = extern/pybind11
url = https://github.com/pybind/pybind11.git
branch = stable
[submodule "external/xsimd"]
path = external/xsimd
url = https://github.com/xtensor-stack/xsimd.git

28
.readthedocs.yaml Normal file
View File

@ -0,0 +1,28 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-20.04
tools:
python: "3.10"
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
# If using Sphinx, optionally build your docs in additional formats such as PDF
formats:
- pdf
# Give python build instructions
python:
install:
- method: pip
path: .
extra_requirements:
- docs

48
CHANGELOG.md Normal file
View File

@ -0,0 +1,48 @@
# Changelog
All notable changes to this project will be documented in this file
## Unreleased
### Fixed
- Fixed LeastSquares endpoint mode producint incorrect results
## 0.1.3 - 2022-04-13
### Fixed
- Fixed quicktex not compiling for python 3.10 on Windows
### Changed
- Reworked CI job, adding wheels for ARM macOS, ARM Linux, and x86 musl Linux.
- Added wheels for python 3.10
- Added a more useful error message when importing quicktex on macOS when libomp.dylib isn't installed
## 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

View File

@ -1,44 +1,14 @@
cmake_minimum_required(VERSION 3.17)
include(CheckIPOSupported)
cmake_minimum_required(VERSION 3.18)
include(tools/CompilerWarnings.cmake)
include(tools/SIMDFlags.cmake)
set(CMAKE_VERBOSE_MAKEFILE ON)
project(python_rgbcx)
project(quicktex)
# Link to Pybind
add_subdirectory(extern/pybind11)
add_subdirectory(external/xsimd)
# 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")
add_subdirectory(quicktex)
add_subdirectory(tests)
# 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
${SOURCE_FILES}
${HEADER_FILES}
${PYTHON_FILES})
add_executable(test_rgbcx
${SOURCE_FILES}
${HEADER_FILES}
${TEST_FILES})
# 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)
set_project_warnings(python_rgbcx)
set_project_warnings(test_rgbcx)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
if (CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined")
set(PROJECT_WARNINGS ${CLANG_WARNINGS})
endif ()
enable_testing ()
add_test (NAME QuicktexTest COMMAND Test)

25
DEVELOPMENT.md Normal file
View File

@ -0,0 +1,25 @@
# Development
This document outlines how to set up a development environment for Quicktex. Documentation on writing Python extension modules is sparse, so I hope this document is useful for other projects as well. The [Coding Patterns for Python Extensions](https://pythonextensionpatterns.readthedocs.io/en/latest/) site has some useful information and will be linked to often in this document.
## Setting up Debug Python
Many development tools require debug symbols to function, and since the front-end for accessing an extension module is Python, that usually means adding debug symbols to Python. [This Page](https://pythonextensionpatterns.readthedocs.io/en/latest/debugging/debug_python.html) has some instructions on building python with debug symbols.
If you plan to use DTrace, enable the `--with-dtrace` flag when running `configure`.
It's useful for this debug python to have SSL enabled so that packages can be installed using pip. Enable SSL with the `--with-openssl` flag when running `configure`. If you are on macOS and installed OpenSSL through Homebrew, you may need to use `--with-openssl=$(brew --prefix openssl)` to help the compiler find it.
### Installing Debug Python
You can keep the resulting binary in your local copy of the cpython repo and symlink to it, but I like to install it somewhere like `/opt/python-debug/`. The install location is set in the `configure` tool using the `--prefix` flag, and installation is done by running `make install`
### Mixing Debug and Release Python
The debug build of python is slow (It may be possible to build with debug symbols but full optimization, I have not looked into it). If you already have a venv setup for your project, you can just symlink the debug python binary into `env/bin` with a full name like `python3.9d`. Make sure that the debug build has the same minor version (e.g '3.9') as the version you made the virtual environment with to maintain ABI compatibility.
## Profiling with Dtrace
DTrace is the default program profiler on macOS and other Unix systems, but it's also available for use on Windows and Linux. Using DTrace requires building Python with DTrace hooks as seen above.
Your extension module does not need a full debug build to profile, but it does need frame pointers to see the stack trace at each sample, as well as debug symbols to give functions names. The cmake build type `RelWithDebInfo` handles this automatically.

2
MANIFEST.in Normal file
View File

@ -0,0 +1,2 @@
graft external
global-exclude *.afdesign # this is currently the vast majority of the repo size

View File

@ -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.

2
docs/changelog.md Normal file
View File

@ -0,0 +1,2 @@
```{include} ../CHANGELOG.md
```

78
docs/conf.py Normal file
View File

@ -0,0 +1,78 @@
# 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('..'))
from datetime import date
# -- Project information -----------------------------------------------------
project = 'Quicktex'
copyright = f'{date.today().year}, 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_click',
'sphinx_rtd_theme',
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
]
myst_heading_anchors = 2
# 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),
}

2
docs/development.md Normal file
View File

@ -0,0 +1,2 @@
```{include} ../DEVELOPMENT.md
```

View File

@ -0,0 +1,7 @@
# Command Reference
```{eval-rst}
.. click:: quicktex.__main__:main
:prog: quicktex
:nested: full
```

View File

@ -0,0 +1,84 @@
# Getting Started
## Installation
Install and update using [pip](https://pip.pypa.io/en/stable/quickstart/):
```shell
pip install -U quicktex
```
If you are on macOS, you need to install openMP to allow multithreading, since it does not ship with the built-in Clang.
This can be done easily
using [homebrew](https://brew.sh). This is not required if building from source, but highly recommended.
```shell
brew install libomp
```
If you want, you can also install from source. First clone the [git repo](https://github.com/drewcassidy/quicktex) and
install it with:
```shell
pip install .
```
and setuptools will take care of any dependencies for you.
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 quicktex[tests,stubs,docs]
```
## Usage
For detailed documentation on the {command}`quicktex` command and its subcommands see the {doc}`commands`.
### Examples
#### Encoding a file
To encode a file in place, use the {command}`encode` command
```shell
quicktex encode auto bun.png # chooses format based on alpha
quicktex encode bc3 bun.png # encodes as bc3
```
the auto subcommand automatically chooses between bc1 and bc3 for your image depending on the contents of its alpha
channel. Quicktex supports reading from all image formats supported by [pillow](https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html).
By default, Quicktex converts in place, meaning the above command will produce a `bun.dds` file alongside the png. If
you want to replace the png, use the `-r` flag to remove it after converting.
if you want to specify an output filename or directory use the `-o` flag.
```shell
quicktex encode auto -o rabbit.dds bun.png # produces rabbit.dds
quicktex.encode auto -o textures/ bun.png # produces textures/bun.dds, if textures/ exists
```
#### Encoding multiple files
quicktex is also able to convert multiple files at once, for example, to encode every png file in the images folder,
use:
```shell
quicktex encode auto images/*.png # encodes in-place
quicktex encode auto -o textures/ images/*.png # encodes to the textures/ directory
```
please note that globbing is an operation performed by your shell and is not supported by the built in windows `cmd.exe`
. If you are on Windows, please use Powershell or any posix-ish shell like [fish](https://fishshell.com).
#### Decoding files
decoding is performed exactly the same as encoding, except without having to specify a format. The output image format
is set using the `-x` flag, and defaults to png. Quicktex supports writing to all image formats supported by [pillow](https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html)
```shell
quicktex decode bun.dds # produces bun.png
quicktex decode -x .tga bun2.dds # produces bun.tga
```

10
docs/handbook/index.md Normal file
View File

@ -0,0 +1,10 @@
# Handbook
```{toctree}
---
maxdepth: 3
---
commands
getting_started
```

35
docs/index.md Normal file
View File

@ -0,0 +1,35 @@
# Welcome to Quicktex's Documentation
[![Documentation Status](https://readthedocs.org/projects/quicktex/badge/?version=latest)](https://quicktex.readthedocs.io/en/latest/?badge=latest)
[![Python Package](https://github.com/drewcassidy/quicktex/actions/workflows/python-package.yml/badge.svg)](https://github.com/drewcassidy/quicktex/actions/workflows/python-package.yml)
[![PyPI version](https://badge.fury.io/py/quicktex.svg)](https://badge.fury.io/py/quicktex)
Quicktex is a Python library for encoding, decoding, and manipulating compressed textures. It uses a backend written in
C++ for superior performance, as well as an extensive API for low-level access to the texture data. The compression
engine is based in [rgbcx](https://github.com/richgel999/bc7enc).
```{toctree}
---
maxdepth: 2
caption: Contents
---
handbook/index
reference/index
```
```{toctree}
---
maxdepth: 1
---
development
Changelog <changelog>
License <license>
```
## Indices and tables
* {ref}`genindex`
* {ref}`modindex`
* {ref}`search`

2
docs/license.md Normal file
View File

@ -0,0 +1,2 @@
```{include} ../LICENSE.md
```

35
docs/make.bat Normal file
View 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

6
docs/reference/dds.rst Normal file
View File

@ -0,0 +1,6 @@
dds module
==========
.. automodule:: quicktex.dds
:members:

View File

@ -0,0 +1,7 @@
Formats
=======
.. toctree::
:maxdepth: 2
s3tc.rst

View 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:

View File

@ -0,0 +1,5 @@
image_utils module
==================
.. automodule:: quicktex.image_utils
:members:

9
docs/reference/index.rst Normal file
View File

@ -0,0 +1,9 @@
API Reference
=============
.. toctree::
:maxdepth: 2
dds.rst
image_utils.rst
formats/index.rst

1
extern/pybind11 vendored

Submodule extern/pybind11 deleted from 8de7772cc7

1
external/xsimd vendored Submodule

Submodule external/xsimd added at 1577b02d54

92
pyproject.toml Normal file
View File

@ -0,0 +1,92 @@
[build-system]
requires = [
"setuptools>=61",
"setuptools_scm>=6.2",
"wheel",
"cmake>=3.18",
"pybind11~=2.6.1",
"ninja; sys_platform != 'win32'",
]
build-backend = "setuptools.build_meta"
[project]
name = "quicktex"
description = "A fast block compression library for python"
readme = "README.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 = ["parameterized", "pytest"]
docs = [
"Sphinx >= 3.5",
"sphinx-click >= 2.7",
"sphinx-rtd-theme",
"myst-parser >= 0.14",
]
stubs = ["pybind11-stubgen"]
[project.urls]
Docs = "https://quicktex.readthedocs.io/en/"
Source = "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]
[tool.cibuildwheel]
build = "cp*" # only build wheels for cpython.
build-frontend = "build"
test-command = "pytest {project}/tests --verbose --full-trace --capture=tee-sys"
test-extras = ["tests"]
test-skip = "*-macosx_arm64 *-macosx_universal2:arm64" # skip testing on arm macOS because CIBW doesnt support it
environment = { QUICKTEX_SIMD_MODE = "SSE4" } # SSE4 has a 99% market share and was released under the Bush administration
[tool.cibuildwheel.macos]
archs = ["x86_64", "universal2"] # build fat binaries, or x86-64 for python 3.7
skip = ["cp{38,39,31*}-macosx_x86_64"] # skip x86-only builds where fat binaries are supported
[tool.cibuildwheel.windows]
archs = ["auto64"] # arm64 windows builds not yet supported
[tool.cibuildwheel.linux]
skip = ["*musllinux*"]
manylinux-x86_64-image = "manylinux2014"
manylinux-aarch64-image = "manylinux2014"
[tool.black]
line-length = 120 # 80-column is stupid
target-version = ['py37', 'py38', 'py39', 'py310']
skip-string-normalization = true
[tool.pytest.ini_options]
minversion = "7.0"
addopts = ["--full-trace", "--capture=tee-sys"]
testpaths = ["tests"]

71
quicktex/CMakeLists.txt Normal file
View File

@ -0,0 +1,71 @@
# Find dependencies
if (NOT QUICKTEX_NOPYTHON)
find_package(Python COMPONENTS Interpreter Development.Module)
find_package(pybind11 CONFIG REQUIRED)
endif ()
find_package(OpenMP)
#Collect source files
set(SOURCE_FILES
Matrix4x4.cpp OldColor.cpp
s3tc/bc1/BC1Block.cpp s3tc/bc1/BC1Decoder.cpp
s3tc/bc1/BC1Encoder.cpp s3tc/bc1/OrderTable.cpp s3tc/bc1/OrderTable4.cpp
s3tc/bc3/BC3Decoder.cpp s3tc/bc3/BC3Encoder.cpp
s3tc/bc4/BC4Block.cpp s3tc/bc4/BC4Decoder.cpp s3tc/bc4/BC4Encoder.cpp
s3tc/bc5/BC5Decoder.cpp s3tc/bc5/BC5Encoder.cpp
s3tc/interpolator/Interpolator.cpp
texture/RawTexture.cpp texture/Window.cpp test.cpp)
set(BINDING_FILES
_bindings.cpp
s3tc/_bindings.cpp
s3tc/bc1/_bindings.cpp
s3tc/bc3/_bindings.cpp
s3tc/bc4/_bindings.cpp
s3tc/bc5/_bindings.cpp
s3tc/interpolator/_bindings.cpp)
file(GLOB_RECURSE HEADER_FILES "**.h")
file(GLOB_RECURSE PYTHON_FILES "**.py")
# Organize source files together for some IDEs
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE_FILES} ${BINDING_FILES} ${HEADER_FILES} ${PYTHON_FILES})
# Declare implementation module
add_library(quicktex STATIC ${SOURCE_FILES} ${HEADER_FILES})
# Link openMP if available
if (OpenMP_CXX_FOUND)
target_link_libraries(quicktex PUBLIC OpenMP::OpenMP_CXX)
endif ()
# Link XSimd
target_link_libraries(quicktex PUBLIC xsimd)
# Set library features, like C/C++ standards
target_compile_features(quicktex PUBLIC cxx_std_20 c_std_11)
set_property(TARGET quicktex PROPERTY CXX_VISIBILITY_PRESET hidden)
set_property(TARGET quicktex PROPERTY POSITION_INDEPENDENT_CODE ON)
# Include source root for project-relative includes
target_include_directories(quicktex PUBLIC .)
# Set compiler warnings and SIMD flags
set_project_warnings(quicktex)
set_simd_flags(quicktex)
if (NOT QUICKTEX_NOPYTHON)
# Declare python module
pybind11_add_module(_quicktex ${BINDING_FILES} ${HEADER_FILES})
target_compile_definitions(_quicktex PRIVATE VERSION_INFO=${QUICKTEX_VERSION_INFO})
# Link python module with implementation
target_link_libraries(_quicktex PUBLIC quicktex)
if ((NOT MSVC) AND (CMAKE_BUILD_TYPE MATCHES Debug) AND ($ENV{QUICKTEX_SANITIZE}))
target_compile_options(_quicktex PUBLIC -fsanitize=address,undefined -fno-sanitize-recover=address,undefined -fno-omit-frame-pointer)
target_link_options(_quicktex PUBLIC -fsanitize=address,undefined -fno-sanitize-recover=address,undefined -fno-omit-frame-pointer)
endif ()
endif ()

77
quicktex/Color.h Normal file
View File

@ -0,0 +1,77 @@
/* 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 "Matrix.h"
#include "util/bitbash.h"
namespace quicktex {
using Color = Vec<uint8_t, 4>;
using ColorRGB = Vec<uint8_t, 3>;
constexpr size_t uint5_max = (1 << 5) - 1;
constexpr size_t uint6_max = (1 << 6) - 1;
template <size_t N> struct MidpointTable {
public:
constexpr MidpointTable() : _values() {
constexpr float fN = (float)N;
for (unsigned i = 0; i < N - 1; i++) { _values[i] = ((float)i / fN) + (0.5f / fN); }
_values[N - 1] = 1e+37f;
}
float operator[](size_t i) const {
assert(i < N);
return _values[i];
}
private:
float _values[N];
};
constexpr MidpointTable<32> Midpoints5bit;
constexpr MidpointTable<64> Midpoints6bit;
template <typename T> Vec<T, 3> scale_to_565(Vec<T, 3> unscaled) {
return Vec<T, 3>{scale_from_8<T, 5>(unscaled.r()), scale_from_8<T, 6>(unscaled.g()),
scale_from_8<T, 5>(unscaled.b())};
}
template <typename T> Vec<T, 3> scale_from_565(Vec<T, 3> scaled) {
return Vec<T, 3>{scale_to_8<T, 5>(scaled.r()), scale_to_8<T, 6>(scaled.g()), scale_to_8<T, 5>(scaled.b())};
}
template <typename T = int16_t> Vec<T, 3> precise_round_565(Vec<float, 3> &v) {
auto scaled = v * Vec<float, 3>{uint5_max, uint6_max, uint5_max}; // rescale by from (0,1) to (0,int_max)
auto rounded = (Vec<T, 3>)scaled; // downcast to integral type
rounded = rounded.clamp({0, 0, 0}, {uint5_max, uint6_max, uint5_max}); // clamp to avoid out of bounds float errors
// increment each channel if above the rounding point
if (v.r() > Midpoints5bit[rounded.r()]) rounded.r()++;
if (v.g() > Midpoints6bit[rounded.g()]) rounded.g()++;
if (v.b() > Midpoints5bit[rounded.b()]) rounded.b()++;
assert(rounded.r() <= uint5_max);
assert(rounded.g() <= uint6_max);
assert(rounded.b() <= uint5_max);
return rounded;
}
} // namespace quicktex

124
quicktex/ColorBlock.h Normal file
View File

@ -0,0 +1,124 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "OldColor.h"
#include "Vector4Int.h"
namespace quicktex {
using Coords = std::tuple<int, int>;
template <int N, int M> class ColorBlock {
public:
struct Metrics {
OldColor min;
OldColor max;
OldColor avg;
bool is_greyscale;
bool has_black;
Vector4Int sums;
};
static constexpr int Width = N;
static constexpr int Height = M;
constexpr OldColor 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 OldColor 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 OldColor &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 OldColor &value) {
if (i >= N * M || i < 0) throw std::invalid_argument("i value out of range");
_pixels[i] = value;
}
void GetRow(int y, OldColor *dst) const {
if (y >= Height || y < 0) throw std::invalid_argument("y value out of range");
std::memcpy(dst, &_pixels[N * y], N * sizeof(OldColor));
}
void SetRow(int y, const OldColor *src) {
if (y >= Height || y < 0) throw std::invalid_argument("y value out of range");
std::memcpy(&_pixels[N * y], src, N * sizeof(OldColor));
}
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 = OldColor(UINT8_MAX, UINT8_MAX, UINT8_MAX);
metrics.max = OldColor(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++) {
OldColor 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<OldColor, N * M> _pixels;
};
} // namespace quicktex

68
quicktex/Decoder.h Normal file
View File

@ -0,0 +1,68 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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/RawTexture.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.bwidth();
int blocks_y = encoded.bheight();
// 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.get_block(x, y);
auto pixels = DecodeBlock(block);
decoded.set_block<BlockWidth, BlockHeight>(x, y, pixels);
}
}
return decoded;
}
};
} // namespace quicktex

73
quicktex/Encoder.h Normal file
View File

@ -0,0 +1,73 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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/RawTexture.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);
unsigned blocks_x = encoded.bwidth();
unsigned blocks_y = encoded.bheight();
// 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 < (int)blocks_y; y++) {
for (int x = 0; x < (int)blocks_x; x++) {
// index variables have to be signed for MSVC for some reason
auto pixels = decoded.get_block<BlockWidth, BlockHeight>(x, y);
auto block = EncodeBlock(pixels);
encoded.set_block(x, y, block);
}
}
return encoded;
}
virtual size_t MTThreshold() const { return SIZE_MAX; };
};
} // namespace quicktex

457
quicktex/Matrix.h Normal file
View File

@ -0,0 +1,457 @@
/* 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 <cstdint>
#include <numeric>
#include <xsimd/xsimd.hpp>
#include "util/iterator.h"
#include "util/map.h"
#include "util/math.h"
#include "util/ranges.h"
namespace quicktex {
template <typename T, int M, int N> class Matrix;
template <typename T, int M> using Vec = Matrix<T, M, 1>;
// region helper concepts
template <typename L, typename R, typename Op>
concept operable = requires(L &l, R &r, Op &op) { op(l, r); };
template <typename V>
concept is_matrix = requires(V &v) {
V::width();
V::height();
V::value_type;
} && std::same_as < Matrix<typename V::value_type, V::height(), V::width()>,
std::remove_cvref_t < V >> ;
template <typename V> struct vector_stats {
static constexpr int width = 1;
static constexpr int height = 1;
static constexpr int dims = 0;
};
template <typename V>
requires is_matrix<V>
struct vector_stats<V> {
static constexpr int width = V::width;
static constexpr int height = V::height;
static constexpr int dims = V::dims;
};
template <typename V> constexpr int vector_width = vector_stats<V>::width;
template <typename V> constexpr int vector_height = vector_stats<V>::height;
template <typename V> constexpr int vector_dims = vector_stats<V>::dims;
// endregion
template <typename R, typename T, int N> class VecBase {
public:
constexpr VecBase(T scalar = T()) : _c{} { _c.fill(scalar); }
protected:
const R &_at(int index) const { return _c.at(index); }
R &_at(int index) { return _c.at(index); }
constexpr auto _begin() const { return _c.data(); }
constexpr auto _begin() { return _c.data(); }
constexpr auto _end() const { return _c.data() + N; }
constexpr auto _end() { return _c.data() + N; }
private:
std::array<R, N> _c;
};
template <typename T, int N, int M> using matrix_row_type = std::conditional_t<N <= 1, T, Vec<T, N>>;
template <typename T, int N, int M> using matrix_column_type = std::conditional_t<M <= 1, T, Vec<T, M>>;
/**
* A matrix of values that can be operated on
* @tparam T Scalar type
* @tparam N Width of the matrix
* @tparam M Height of the matrix
*/
template <typename T, int M, int N>
class Matrix : public VecBase<std::conditional_t<N == 1, T, VecBase<T, T, N>>, T, M> {
public:
using base = VecBase<std::conditional_t<N == 1, T, VecBase<T, T, N>>, T, M>;
using value_type = T;
using row_type = matrix_row_type<T, N, M>;
using column_type = matrix_column_type<T, N, M>;
using base::base;
// using base::begin;
// using base::end;
// using base::operator[];
// region constructors
/**
* Create a vector from an intializer list
* @param il values to populate with
*/
Matrix(std::initializer_list<row_type> il) : base() {
assert(il.size() == M); // ensure il is of the right size
std::copy_n(il.begin(), M, this->begin());
}
/**
* Create a vector from a scalar value
* @param scalar value to populate with
*/
// constexpr Matrix(const T &scalar) { std::fill(this->begin(), this->end(), scalar); }
/**
* Create a vector from an iterator
* @tparam II input iterator type
* @param input_iterator iterator to copy from
*/
template <typename II>
requires std::input_iterator<II> && std::convertible_to<std::iter_value_t<II>,
const row_type>
Matrix(const II input_iterator) : base() {
std::copy_n(input_iterator, M, this->begin());
}
/**
* Create a vector from a range type
* @tparam R Range type
* @param input_range Range to copy from
*/
template <typename R>
Matrix(const R &input_range)
requires range<R> && std::convertible_to<typename R::value_type, row_type>
: Matrix(input_range.begin()) {
assert(std::distance(input_range.begin(), input_range.end()) == M);
}
template <typename R = T>
requires(N == M)
static constexpr Matrix identity() {
Matrix result = Matrix(0);
for (int i = 0; i < N; i++) { result.element(i, i) = 1; }
return result;
}
// endregion
// region iterators and accessors
static constexpr int size() { return M; }
static constexpr int width = N;
static constexpr int height = M;
static constexpr int elements = N * M;
static constexpr int dims = ((width > 1) ? 1 : 0) + ((height > 1) ? 1 : 0);
const row_type &at(int index) const {
assert(index >= 0 && index < M);
return static_cast<const row_type &>(base::_at(index));
}
row_type &at(int index) {
assert(index >= 0 && index < M);
return static_cast<row_type &>(base::_at(index));
}
const row_type &operator[](int index) const { return at(index); }
row_type &operator[](int index) { return at(index); }
const row_type *begin() const { return static_cast<const row_type *>(base::_begin()); }
row_type *begin() { return static_cast<row_type *>(base::_begin()); }
const row_type *end() const { return static_cast<const row_type *>(base::_end()); }
row_type *end() { return static_cast<row_type *>(base::_end()); }
auto column_begin() const { return column_iterator(this, 0); }
auto column_end() const { return column_iterator(this, N); }
auto all_begin() const { return linear_iterator<const Matrix>(this, 0); }
auto all_begin() { return linear_iterator<Matrix>(this, 0); }
auto all_end() const { return linear_iterator<const Matrix>(this, N * M); }
auto all_end() { return linear_iterator<Matrix>(this, N * M); }
const row_type &get_row(int m) const { return static_cast<const row_type &>(this->at(m)); }
template <typename R> void set_row(int m, const R &value) { this->at(m) = value; }
template <typename S = T> column_type get_column(int n) const {
if constexpr (M == 1) {
return element(0, n);
} else {
column_type ret;
for (int m = 0; m < M; m++) { ret[m] = element(m, n); }
return ret;
}
}
void set_column(int n, const column_type &value) {
if constexpr (M == 1) {
element(0, n) = value;
} else {
for (int m = 0; m < M; m++) { element(m, n) = value[m]; }
}
}
// n/m accessors
const T &element(int m, int n) const {
if constexpr (N == 1) {
return this->at(m);
} else {
return this->at(m)[n];
}
}
T &element(int n, int m) { return const_cast<T &>(static_cast<const Matrix &>(*this).element(n, m)); }
// linear accessors
const T &element(int i) const { return element(i / N, i % N); }
T &element(int i) { return element(i / N, i % N); }
// RGBA accessors
const T &r() const { return (*this)[0]; }
T &r() { return this->at(0); }
template <typename S = T> std::enable_if_t<M >= 2, const S &> g() const { return this->at(1); }
template <typename S = T> std::enable_if_t<M >= 2, S &> g() { return this->at(1); }
template <typename S = T> std::enable_if_t<M >= 3, const S &> b() const { return this->at(2); }
template <typename S = T> std::enable_if_t<M >= 3, S &> b() { return this->at(2); }
template <typename S = T> std::enable_if_t<M >= 4, const S &> a() const { return this->at(3); }
template <typename S = T> std::enable_if_t<M >= 4, S &> a() { return this->at(3); }
// XYZW accessors
const T &x() const { return this->at(0); }
T &x() { return this->at(0); }
template <typename S = T> std::enable_if_t<M >= 2, const S &> y() const { return this->at(1); }
template <typename S = T> std::enable_if_t<M >= 2, S &> y() { return this->at(1); }
template <typename S = T> std::enable_if_t<M >= 3, const S &> z() const { return this->at(2); }
template <typename S = T> std::enable_if_t<M >= 3, S &> z() { return this->at(2); }
template <typename S = T> std::enable_if_t<M >= 4, const S &> w() const { return this->at(3); }
template <typename S = T> std::enable_if_t<M >= 4, S &> w() { return this->at(3); }
// endregion
template <typename R>
requires std::equality_comparable_with<T, R> bool
operator==(const Matrix<R, M, N> &rhs) const {
return size() == rhs.size() && std::equal(this->begin(), this->end(), rhs.begin());
};
// unary vector negation
template <typename S = T>
requires(!std::unsigned_integral<T>) && requires(T &t) { -t; }
Matrix operator-() const {
return map(std::negate(), *this);
};
// add vectors
template <typename R>
requires operable<R, T, std::plus<>>
Matrix operator+(const Matrix<R, M, N> &rhs) const {
return map(std::plus(), *this, rhs);
};
// subtract vectors
template <typename R>
requires operable<R, T, std::minus<>>
Matrix operator-(const Matrix<R, M, N> &rhs) const {
// we can't just add the negation because that's invalid for int types
return map(std::minus(), *this, rhs);
};
// multiply matrix with a matrix or column vector
template <typename R, int P>
requires(P == 1 || P == N) && operable<R, T, std::multiplies<>>
Matrix operator*(const Matrix<R, M, P> &rhs) const {
return map(std::multiplies(), *this, rhs);
};
// multiply matrix with a scalar
template <typename R>
requires operable<R, T, std::multiplies<>>
Matrix operator*(const R &rhs) const {
return map(std::multiplies(), *this, rhs);
};
// divides a matrix by a matrix or column vector
template <typename R, int NN>
requires(NN == 1 || NN == N) && operable<R, T, std::divides<>>
Matrix operator/(const Matrix<R, M, NN> &rhs) const {
return map(std::divides(), *this, rhs);
};
// divides a matrix by a scalar
template <typename R>
requires operable<R, T, std::divides<>>
Matrix operator/(const R &rhs) const {
return map(std::divides(), *this, rhs);
};
// add-assigns a matrix with a matrix
template <typename R>
requires operable<Matrix, R, std::plus<>>
Matrix &operator+=(const R &rhs) {
return *this = *this + rhs;
}
// subtract-assigns a matrix with a matrix
template <typename R>
requires operable<Matrix, R, std::minus<>>
Matrix &operator-=(const R &rhs) {
return *this = *this - rhs;
}
// multiply-assigns a matrix with a matrix, column vector, or a scalar
template <typename R>
requires operable<Matrix, R, std::multiplies<>>
Matrix &operator*=(const R &rhs) {
return *this = *this * rhs;
}
// divide-assigns a matrix by a matrix, column vector, or a scalar
template <typename R>
requires operable<Matrix, R, std::divides<>>
Matrix &operator/=(const R &rhs) {
return *this = *this / rhs;
}
// decay a 1x1 matrix to a scalar on demand
template <typename S = T>
requires(N == 1 && M == 1)
operator S &() {
return this->at(0);
}
template <typename S = T>
requires(N == 1 && M == 1)
operator const S &() const {
return this->at(0);
}
// sum up all columns
column_type hsum() const {
if constexpr (N == 1) { return *this; }
if constexpr (M == 1) { return sum(); }
for (int i = 0; i < M; i++) {}
return _map<column_type>([](auto row) { return quicktex::sum(row); }, *this);
}
// sum up all rows
row_type vsum() const {
if constexpr (N == 1) { return sum(); }
if constexpr (M == 1) { return *this; }
return std::accumulate(begin(), end(), row_type{});
}
// sum up all values
T sum() const {
// TODO: reintroduce SIMDing for this
return std::accumulate(all_begin(), all_end(), T(0));
}
template <typename R, int P>
requires operable<R, T, std::multiplies<>>
Matrix<T, M, P> mult(const Matrix<R, N, P> &rhs) const {
Matrix<T, M, P> res(0);
for (int p = 0; p < P; p++) {
// for each column of the RHS/Result
for (int m = 0; m < M; m++) {
// for each row of the LHS/Result
for (int n = 0; n < N; n++) { res.element(m, p) += element(m, n) * rhs.element(n, p); }
}
}
return res;
}
Matrix<T, N, M> transpose() const {
Matrix<T, N, M> res;
for (int m = 0; m < M; m++) { res.set_column(m, get_row(m)); }
return res;
}
template <typename R = T>
requires(N == M)
Matrix mirror() const {
Matrix result = *this;
for (int n = 0; n < N - 1; n++) {
for (int m = (n + 1); m < M; m++) { result.element(m, n) = result.element(n, m); }
}
return result;
}
// dot product of two compatible matrices
template <typename R>
requires(N == 1) && operable<T, R, std::multiplies<>> && operable<T, T, std::plus<>>
inline row_type dot(const Matrix<R, M, N> &rhs) const {
// technically this is Lt * R, but the vsum method is probably faster/more readable
// than allocationg a new transpose matrix
Matrix product = *this * rhs;
return product.vsum();
}
inline row_type sqr_mag() const { return dot(*this); }
inline Matrix abs() const {
return map([](auto c) { return quicktex::abs(c); }, *this);
}
inline Matrix clamp(T low, T high) {
return map([low, high](auto c) { return quicktex::clamp(c, low, high); }, *this);
}
inline Matrix clamp(const Matrix &low, const Matrix &high) {
return map([](auto c, auto l, auto h) { return quicktex::clamp(c, l, h); }, *this, low, high);
}
protected:
class column_iterator : public index_iterator_base<column_iterator, column_type> {
public:
using value_type = column_type;
using base = index_iterator_base<column_iterator, column_type>;
column_iterator(const Matrix *matrix = nullptr, int index = 0) : base(index), _matrix(matrix){};
column_type operator*() const { return _matrix->get_column(this->_index); }
const column_type *operator->() const { &(_matrix->get_column(this->_index)); }
friend bool operator==(const column_iterator &lhs, const column_iterator &rhs) {
return (lhs._matrix == rhs._matrix) && (lhs._index == rhs._index);
}
private:
const Matrix *_matrix;
};
template <typename V> class linear_iterator : public index_iterator_base<linear_iterator<V>, T> {
public:
using value_type = T;
using base = index_iterator_base<linear_iterator<V>, T>;
linear_iterator(V *matrix = nullptr, int index = 0) : base(index), _matrix(matrix){};
auto &operator*() { return _matrix->element(this->_index); }
auto *operator->() const { return &(_matrix->element(this->_index)); }
friend bool operator==(const linear_iterator &lhs, const linear_iterator &rhs) {
return (lhs._matrix == rhs._matrix) && (lhs._index == rhs._index);
}
private:
V *_matrix;
};
};
} // namespace quicktex

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
and licenced under the public domain
@ -19,7 +19,7 @@
#include "Matrix4x4.h"
namespace rgbcx {
namespace quicktex {
Matrix4x4 operator*(const Matrix4x4& lhs, const Matrix4x4& rhs) {
Matrix4x4 trans_rhs = rhs.Transpose(); // 🏳️‍⚧️
@ -47,4 +47,4 @@ void Matrix4x4::Mirror() {
}
}
} // namespace rgbcx
} // namespace quicktex

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
and licenced under the public domain
@ -20,11 +20,13 @@
#pragma once
#include <array>
#include <cassert>
#include <cstddef>
#include <functional>
#include "Vector4.h"
namespace rgbcx {
namespace quicktex {
class Matrix4x4 {
public:
@ -90,4 +92,4 @@ class Matrix4x4 {
std::array<Vector4, 4> _r;
};
} // namespace rgbcx
} // namespace quicktex

121
quicktex/OldColor.cpp Normal file
View File

@ -0,0 +1,121 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "OldColor.h"
#include <algorithm>
#include <stdexcept>
#include "Vector4.h"
#include "Vector4Int.h"
#include "util/bitbash.h"
#include "util/math.h" // for scale_to_8<5>, scale_from_8<5>, assert5bit, scale_to_8<6>
namespace quicktex {
OldColor::OldColor(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 OldColor::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 OldColor::Pack565(uint8_t r, uint8_t g, uint8_t b) {
return Pack565Unscaled(scale_from_8<5>(r), scale_from_8<6>(g), scale_from_8<5>(b));
}
OldColor OldColor::Unpack565Unscaled(uint16_t Packed) {
uint8_t r = (Packed >> 11) & 0x1F;
uint8_t g = (Packed >> 5) & 0x3F;
uint8_t b = Packed & 0x1F;
return OldColor(r, g, b);
}
OldColor OldColor::Unpack565(uint16_t Packed) {
uint8_t r = static_cast<uint8_t>(scale_to_8<5>((Packed >> 11) & 0x1FU));
uint8_t g = static_cast<uint8_t>(scale_to_8<6>((Packed >> 5) & 0x3FU));
uint8_t b = static_cast<uint8_t>(scale_to_8<5>(Packed & 0x1FU));
return OldColor(r, g, b);
}
OldColor OldColor::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)clamp<int>(trial_r, 0, UINT5_MAX);
uint8_t g = (uint8_t)clamp<int>(trial_g, 0, UINT6_MAX);
uint8_t b = (uint8_t)clamp<int>(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 OldColor(r, g, b);
}
void OldColor::SetRGB(uint8_t vr, uint8_t vg, uint8_t vb) {
r = vr;
g = vg;
b = vb;
}
size_t OldColor::MaxChannelRGB() {
if (r >= g && r >= b) return 0;
if (g >= b && g >= r) return 1;
return 2;
}
OldColor::operator Vector4() const { return Vector4(r, g, b, a); }
OldColor::operator Vector4Int() const { return Vector4Int(r, g, b, a); }
Vector4Int operator-(const OldColor &lhs, const OldColor &rhs) {
Vector4Int result;
for (unsigned i = 0; i < 4; i++) { result[i] = (int)lhs[i] - rhs[i]; }
return result;
}
uint16_t OldColor::Pack565() const { return Pack565(r, g, b); }
uint16_t OldColor::Pack565Unscaled() const { return Pack565Unscaled(r, g, b); }
OldColor OldColor::ScaleTo565() const { return OldColor(scale_from_8<5>(r), scale_from_8<6>(g), scale_from_8<5>(b)); }
OldColor OldColor::ScaleFrom565() const { return OldColor(scale_to_8<5>(r), scale_to_8<6>(g), scale_to_8<5>(b)); }
bool OldColor::operator==(const OldColor &Rhs) const { return r == Rhs.r && g == Rhs.g && b == Rhs.b && a == Rhs.a; }
bool OldColor::operator!=(const OldColor &Rhs) const { return !(Rhs == *this); }
} // namespace quicktex

114
quicktex/OldColor.h Normal file
View File

@ -0,0 +1,114 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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
#include "Matrix.h"
namespace quicktex {
class Vector4;
class Vector4Int;
#pragma pack(push, 1)
class OldColor {
public:
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
constexpr OldColor() : OldColor(0, 0, 0, 0xFF) {}
constexpr OldColor(uint8_t vr, uint8_t vg, uint8_t vb, uint8_t va = 0xFF) : r(vr), g(vg), b(vb), a(va) {}
OldColor(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 OldColor Unpack565Unscaled(uint16_t Packed);
static OldColor Unpack565(uint16_t Packed);
static OldColor PreciseRound565(Vector4 &v);
static OldColor Min(const OldColor &A, const OldColor &B);
static OldColor Max(const OldColor &A, const OldColor &B);
bool operator==(const OldColor &Rhs) const;
bool operator!=(const OldColor &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 OldColor &lhs, const OldColor &rhs);
void SetRGB(uint8_t vr, uint8_t vg, uint8_t vb);
void SetRGB(const OldColor &other) { SetRGB(other.r, other.g, other.b); }
uint16_t Pack565() const;
uint16_t Pack565Unscaled() const;
OldColor ScaleTo565() const;
OldColor 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
operator Vec<uint8_t, 4>() const { return {r, g, b, a}; }
OldColor(const Vec<uint8_t, 4> v) {
r = v.r();
g = v.g();
b = v.b();
a = v.a();
}
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

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
and licenced under the public domain
@ -20,15 +20,18 @@
#pragma once
#include <array>
#include <cmath>
#include <functional>
#include "Color.h"
#include "OldColor.h"
namespace rgbcx {
namespace quicktex {
class Vector4 {
public:
Vector4(float x = 0, float y = 0, float z = 0, float w = 0) {
Vector4() : Vector4(0) {}
Vector4(float x, float y, float z = 0, float w = 0) {
_c[0] = x;
_c[1] = y;
_c[2] = z;
@ -42,13 +45,13 @@ class Vector4 {
_c[3] = scalar;
}
Vector4(const Color &c) : Vector4(c.r, c.g, c.b, c.a) {}
Vector4(const OldColor &c) : Vector4(c.r, c.g, c.b, c.a) {}
static Vector4 FromColor(const Color &c) { return Vector4(c); }
static Vector4 FromColor(const OldColor &c) { return Vector4(c); }
static Vector4 FromColorRGB(const Color &c) { return Vector4(c.r, c.g, c.b); }
static Vector4 FromColorRGB(const OldColor &c) { return Vector4(c.r, c.g, c.b); }
static float Dot(Vector4 &lhs, Vector4 &rhs) {
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;
@ -83,8 +86,8 @@ class Vector4 {
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) { return Dot(*this, other); }
float MaxAbs(unsigned channels = 4) {
float Dot(Vector4 other) const { return Dot(*this, other); }
float MaxAbs(unsigned channels = 4) const {
assert(channels < 5);
assert(channels > 0);
float max = 0;
@ -95,14 +98,21 @@ class Vector4 {
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> friend Vector4 DoOp(const Vector4 &lhs, const Vector4 &rhs, Op f) {
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> friend Vector4 DoOp(const Vector4 &lhs, const float &rhs, Op f) {
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;
@ -111,4 +121,4 @@ class Vector4 {
std::array<float, 4> _c;
};
} // namespace rgbcx
} // namespace quicktex

120
quicktex/Vector4Int.h Normal file
View File

@ -0,0 +1,120 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "OldColor.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 OldColor &c) : Vector4Int(c.r, c.g, c.b, c.a) {}
static Vector4Int FromColor(const OldColor &c) { return Vector4Int(c); }
static Vector4Int FromColorRGB(const OldColor &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

8
quicktex/__init__.py Normal file
View File

@ -0,0 +1,8 @@
try:
from _quicktex import *
from _quicktex import __version__
except ImportError as e:
if 'libomp.dylib' in e.msg:
print('\033[41m\033[01mERROR: LIBOMP NOT FOUND! PLEASE INSTALL IT WITH \033[04m`brew install libomp`\033[0m')
print('original error message:')
raise e

17
quicktex/__main__.py Normal file
View File

@ -0,0 +1,17 @@
import click
from quicktex.cli.decode import decode
from quicktex.cli.encode import encode
@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()

84
quicktex/_bindings.cpp Normal file
View File

@ -0,0 +1,84 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "Decoder.h"
#include "Encoder.h"
#include "OldColor.h"
#include "_bindings.h"
#include "texture/RawTexture.h"
#include "texture/Texture.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
#ifdef NDEBUG
m.attr("_debug_build") = false;
#else
m.attr("_debug_build") = true;
#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_readonly("width", &Texture::width);
texture.def_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);
DefSubscript2DRef(
raw_texture, [](RawTexture &self, int x, int y) -> Color { return self.pixel(x, y); },
[](RawTexture &self, int x, int y, Color val) { self.pixel(x, y) = val; }, &RawTexture::Size);
InitS3TC(m);
}
} // namespace quicktex::bindings

342
quicktex/_bindings.h Normal file
View File

@ -0,0 +1,342 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include "OldColor.h"
#include "texture/BlockTexture.h"
#include "util/math.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<OldColor> {
public:
PYBIND11_TYPE_CASTER(OldColor, _("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(OldColor 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;
}
};
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... 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;
}
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 < (Py_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] < (Py_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 < (Py_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] < (Py_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);
}
// TODO: untangle this mess
template <typename Tpy, typename Getter, typename Setter, typename Extent>
void DefSubscript2DRef(Tpy t, Getter&& get, Setter&& set, Extent&& ext) {
using T = typename Tpy::type;
using V = typename std::remove_cvref_t<std::invoke_result_t<Getter, T&, int, int>>;
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 get(self, 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");
set(self, 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 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::bwidth, "The width of the texture in blocks.");
block_texture.def_property_readonly("height_blocks", &BTex::bheight, "The height of the texture in blocks.");
block_texture.def_property_readonly("size_blocks", &BTex::bsize, "The dimensions of the texture in blocks.");
DefSubscript2D(block_texture, &BTex::get_block, &BTex::set_block, &BTex::bsize);
return block_texture;
}
} // namespace quicktex::bindings

0
quicktex/cli/__init__.py Normal file
View File

63
quicktex/cli/common.py Normal file
View File

@ -0,0 +1,63 @@
import pathlib
from typing import List
import click
from PIL import Image
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]

63
quicktex/cli/decode.py Normal file
View File

@ -0,0 +1,63 @@
import os.path
import click
from PIL import Image
import quicktex.cli.common as common
import quicktex.dds as dds
@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()

204
quicktex/cli/encode.py Normal file
View File

@ -0,0 +1,204 @@
import os
import click
from PIL import Image
import quicktex.cli.common as common
import quicktex.dds as dds
import quicktex.s3tc.bc1
import quicktex.s3tc.bc3
import quicktex.s3tc.bc4
import quicktex.s3tc.bc5
@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)

302
quicktex/dds.py Normal file
View File

@ -0,0 +1,302 @@
from __future__ import annotations
import enum
import os
import struct
import typing
from PIL import Image
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
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

52
quicktex/image_utils.py Normal file
View File

@ -0,0 +1,52 @@
"""Various utilities for working with Pillow images"""
import math
from typing import List, Tuple, Optional
from PIL import Image
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
View File

View 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 *

View File

@ -0,0 +1,45 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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

View File

@ -0,0 +1,54 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "util/bitbash.h"
#include "util/map.h"
#include "util/math.h"
#include "util/ranges.h"
namespace quicktex::s3tc {
uint16_t BC1Block::GetColor0Raw() const { return pack<uint16_t>(_color0, 8); }
uint16_t BC1Block::GetColor1Raw() const { return pack<uint16_t>(_color1, 8); }
void BC1Block::SetColor0Raw(uint16_t c) { _color0 = unpack<uint8_t, EndpointSize>(c, 8); }
void BC1Block::SetColor1Raw(uint16_t c) { _color1 = unpack<uint8_t, EndpointSize>(c, 8); }
BC1Block::SelectorArray BC1Block::GetSelectors() const {
return map([](auto row) { return unpack<uint8_t, Width>(row, SelectorBits); }, _selectors);
}
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 = map([](auto row) { return pack<uint8_t>(row, SelectorBits, true); }, unpacked);
}
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

View File

@ -0,0 +1,127 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "OldColor.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<OldColor, OldColor>;
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(OldColor color0, OldColor 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);
OldColor GetColor0() const { return OldColor::Unpack565(GetColor0Raw()); }
OldColor GetColor1() const { return OldColor::Unpack565(GetColor1Raw()); }
ColorPair GetColors() const { return {GetColor0(), GetColor1()}; }
void SetColor0(OldColor c) { SetColor0Raw(c.Pack565()); }
void SetColor1(OldColor 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

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
and licenced under the public domain
@ -23,30 +23,32 @@
#include <cassert>
#include <cstdint>
#include "../BlockView.h"
#include "../Color.h"
#include "../ndebug.h"
#include "BC1Block.h"
#include "ColorBlock.h"
#include "OldColor.h"
#include "s3tc/bc1/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

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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,32 @@
#include <memory>
#include "../BlockDecoder.h"
#include "../BlockView.h"
#include "../Interpolator.h"
#include "../ndebug.h"
#include "BC1Block.h"
#include "ColorBlock.h"
#include "Decoder.h"
#include "s3tc/bc1/BC1Block.h"
#include "s3tc/interpolator/Interpolator.h"
#include "texture/BlockTexture.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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,200 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "ColorBlock.h"
#include "Encoder.h"
#include "OldColor.h"
#include "s3tc/bc1/BC1Block.h"
#include "s3tc/bc1/SingleColorTable.h"
#include "s3tc/interpolator/Interpolator.h"
#include "texture/BlockTexture.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 {
OldColor low;
OldColor 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(OldColor 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, OldColor color, bool is_3color = false) const;
void FindEndpointsSingleColor(EncodeResults &result, const CBlock &pixels, OldColor 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

View File

@ -0,0 +1,85 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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/math.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

View File

@ -0,0 +1,327 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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

View File

@ -0,0 +1,154 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "Histogram.h"
#include "Vector4.h"
#include "util/math.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 (abs(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

View File

@ -0,0 +1,94 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "s3tc/interpolator/Interpolator.h"
#include "util/bitbash.h"
#include "util/math.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 = scale_to_8<B>(low);
for (uint8_t high = 0; high < Size; high++) {
uint8_t high8 = scale_to_8<B>(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 = abs(value - (int)i);
// We only need to factor in 3% error in BC1 ideal mode.
if (ideal) new_error += (abs(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

View File

@ -0,0 +1,3 @@
"""Classes for encoding/decoding BC1 textures"""
from _quicktex._s3tc._bc1 import *

View File

@ -0,0 +1,218 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 <memory>
#include "s3tc/bc1/BC1Block.h"
#include "s3tc/bc1/BC1Decoder.h"
#include "s3tc/bc1/BC1Encoder.h"
#include "s3tc/interpolator/Interpolator.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<OldColor, OldColor, 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

View File

@ -0,0 +1,62 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "s3tc/bc1/BC1Block.h"
#include "s3tc/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

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
and licenced under the public domain
@ -19,16 +19,16 @@
#include "BC3Decoder.h"
#include "../BC1/BC1Decoder.h"
#include "../BC4/BC4Decoder.h"
#include "../BlockView.h"
#include "../ndebug.h"
#include "../../ColorBlock.h"
#include "BC3Block.h"
namespace rgbcx {
namespace quicktex::s3tc {
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);
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 rgbcx
} // namespace quicktex::s3tc

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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,34 @@
#include <memory>
#include "../BC1/BC1Decoder.h"
#include "../BC4/BC4Decoder.h"
#include "../BlockDecoder.h"
#include "../BlockView.h"
#include "../Interpolator.h"
#include "../ndebug.h"
#include "BC3Block.h"
#include "ColorBlock.h"
#include "Decoder.h"
#include "s3tc/bc1/BC1Decoder.h"
#include "s3tc/bc3/BC3Block.h"
#include "s3tc/bc4/BC4Decoder.h"
#include "s3tc/interpolator/Interpolator.h"
#include "texture/BlockTexture.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

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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,16 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "BC3Encoder.h"
#include "../BC1/BC1Block.h"
#include "../BC4/BC4Block.h"
#include "ColorBlock.h"
#include "s3tc/bc3/BC3Block.h"
namespace rgbcx {
#pragma pack(push, 1)
class BC3Block {
public:
BC4Block alpha_block;
BC1Block color_block;
};
#pragma pack(pop)
} // namespace rgbcx
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 quicktex::s3tc

View File

@ -0,0 +1,55 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "s3tc/bc1/BC1Encoder.h"
#include "s3tc/bc3/BC3Block.h"
#include "s3tc/bc4/BC4Encoder.h"
#include "s3tc/interpolator/Interpolator.h"
#include "texture/BlockTexture.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

View File

@ -0,0 +1 @@
from _quicktex._s3tc._bc3 import *

View File

@ -0,0 +1,125 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 <memory>
#include "s3tc/bc1/BC1Block.h"
#include "s3tc/bc3/BC3Block.h"
#include "s3tc/bc3/BC3Decoder.h"
#include "s3tc/bc3/BC3Encoder.h"
#include "s3tc/bc4/BC4Block.h"
#include "s3tc/interpolator/Interpolator.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

View File

@ -0,0 +1,74 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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/bitbash.h"
#include "util/map.h"
#include "util/math.h"
#include "util/ranges.h"
namespace quicktex::s3tc {
BC4Block::SelectorArray BC4Block::GetSelectors() const {
auto packed = pack<uint64_t>(_selectors, 8);
auto rows = unpack<uint16_t, Height>(packed, SelectorBits * Width);
return map([](auto row) { return unpack<uint8_t, Width>(row, SelectorBits); }, rows);
}
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 = map([](auto r) { return pack<uint16_t>(r, SelectorBits); }, unpacked);
auto packed = pack<uint64_t>(rows, SelectorBits * Width);
_selectors = unpack<uint8_t, SelectorSize>(packed, 8);
}
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

View File

@ -0,0 +1,107 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
and licenced under the public domain
@ -22,22 +22,31 @@
#include <array> // for array
#include <cassert> // for assert
#include "../BlockView.h" // for ColorBlock
#include "../ndebug.h" // for ndebug
#include "../../ColorBlock.h"
#include "../../OldColor.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

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
and licenced under the public domain
@ -19,25 +19,30 @@
#pragma once
#include <cassert>
#include <cstdint>
#include <stdexcept>
#include "../BlockEncoder.h"
#include "../BlockView.h"
#include "../ndebug.h"
#include "BC4Block.h"
#include "ColorBlock.h"
#include "Decoder.h"
#include "s3tc/bc4/BC4Block.h"
#include "texture/BlockTexture.h"
namespace rgbcx {
namespace quicktex::s3tc {
class BC4Encoder : public BlockEncoder<BC4Block, 4, 4> {
class BC4Decoder : public BlockDecoder<BlockTexture<BC4Block>> {
public:
BC4Encoder(const uint8_t channel) : _channel(channel) { assert(channel < 4); }
BC4Decoder(uint8_t channel = 3) {
if (channel >= 4U) 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);
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:
const uint8_t _channel;
uint8_t _channel;
};
} // namespace rgbcx
} // namespace quicktex::s3tc

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
and licenced under the public domain
@ -19,30 +19,30 @@
#include "BC4Encoder.h"
#include <algorithm> // for minmax_element
#include <array> // for array
#include <cstdint> // for uint8_t
#include <utility> // for pair
#include <algorithm>
#include <array>
#include <cstdint>
#include "BC4Block.h" // for BC4Block
#include "../../ColorBlock.h"
#include "../../OldColor.h"
#include "BC4Block.h"
namespace rgbcx {
void BC4Encoder::EncodeBlock(Byte4x4 pixels, BC4Block *const dest) const noexcept(ndebug) {
auto flattened = pixels.Flatten();
auto minmax = std::minmax_element(flattened.begin(), flattened.end());
namespace quicktex::s3tc {
BC4Block BC4Encoder::EncodeBlock(const ColorBlock<4, 4> &pixels) const {
uint8_t min = UINT8_MAX;
uint8_t max = 0;
uint8_t min = *minmax.first;
uint8_t max = *minmax.second;
dest->high_alpha = min;
dest->low_alpha = max;
if (max == min) {
dest->SetSelectorBits(0);
return;
for (int i = 0; i < 16; i++) {
auto value = pixels.Get(i)[_channel];
min = std::min(min, value);
max = std::max(max, value);
}
std::array<uint8_t, 16> selectors = {};
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.
@ -56,16 +56,19 @@ void BC4Encoder::EncodeBlock(Byte4x4 pixels, BC4Block *const dest) const noexcep
for (unsigned i = 0; i < 7; i++) thresholds[i] = delta * (1 + (2 * (int)i)) - bias;
// iterate over all values and calculate selectors
for (unsigned i = 0; i < 16; i++) {
int value = flattened[i] * 14; // multiply by demonimator
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];
// level = number of thresholds this value is greater than
unsigned level = 0;
for (unsigned c = 0; c < 7; c++) level += value >= thresholds[c];
selectors[i] = Levels[level];
selectors[(unsigned)y][(unsigned)x] = Levels[level];
}
}
dest->PackSelectors(selectors);
return BC4Block(max, min, selectors);
}
} // namespace rgbcx
} // namespace quicktex::s3tc

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
and licenced under the public domain
@ -19,7 +19,28 @@
#pragma once
#include "BC1/BC1Decoder.h"
#include "BC3/BC3Decoder.h"
#include "BC4/BC4Decoder.h"
#include "BC5/BC5Decoder.h"
#include <cstdint>
#include <stdexcept>
#include "ColorBlock.h"
#include "Encoder.h"
#include "s3tc/bc4/BC4Block.h"
#include "texture/BlockTexture.h"
namespace quicktex::s3tc {
class BC4Encoder : public BlockEncoder<BlockTexture<BC4Block>> {
public:
BC4Encoder(const uint8_t channel) {
if (channel >= 4) throw std::invalid_argument("Channel out of range");
_channel = channel;
}
BC4Block EncodeBlock(const ColorBlock<4, 4> &pixels) const override;
uint8_t GetChannel() const { return _channel; }
private:
uint8_t _channel;
};
} // namespace quicktex::s3tc

View File

@ -0,0 +1 @@
from _quicktex._s3tc._bc4 import *

View File

@ -0,0 +1,125 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 <cstdint>
#include "s3tc/bc4/BC4Block.h"
#include "s3tc/bc4/BC4Decoder.h"
#include "s3tc/bc4/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

View File

@ -0,0 +1,59 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "s3tc/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

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "../BlockView.h"
#include "../ndebug.h"
#include "BC5Block.h"
#include "ColorBlock.h"
#include "s3tc/bc5/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

View File

@ -0,0 +1,56 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "Decoder.h"
#include "s3tc/bc4/BC4Decoder.h"
#include "s3tc/bc5/BC5Block.h"
#include "texture/BlockTexture.h"
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), std::make_shared<BC4Decoder>(chan1)) {}
BC5Decoder(BC4DecoderPtr chan0_decoder, BC4DecoderPtr chan1_decoder)
: _chan0_decoder(chan0_decoder), _chan1_decoder(chan1_decoder) {}
ColorBlock<4, 4> DecodeBlock(const BC5Block &block) const override;
ChannelPair GetChannels() const { return ChannelPair(_chan0_decoder->GetChannel(), _chan1_decoder->GetChannel()); }
BC4DecoderPair GetBC4Decoders() const { return BC4DecoderPair(_chan0_decoder, _chan1_decoder); }
private:
const BC4DecoderPtr _chan0_decoder;
const BC4DecoderPtr _chan1_decoder;
};
} // namespace quicktex::s3tc

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
and licenced under the public domain
@ -17,10 +17,16 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "BC5Encoder.h"
#ifdef NDEBUG // asserts disabled
constexpr bool ndebug = true;
#else // asserts enabled
constexpr bool ndebug = false;
#endif
#include "ColorBlock.h"
#include "s3tc/bc4/BC4Block.h"
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

View File

@ -0,0 +1,55 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "s3tc/bc4/BC4Encoder.h"
#include "s3tc/bc5/BC5Block.h"
#include "texture/BlockTexture.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

View File

@ -0,0 +1 @@
from _quicktex._s3tc._bc5 import *

View File

@ -0,0 +1,116 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "s3tc/bc4/BC4Block.h"
#include "s3tc/bc5/BC5Block.h"
#include "s3tc/bc5/BC5Decoder.h"
#include "s3tc/bc5/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

View File

@ -0,0 +1,161 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "Interpolator.h"
#include <array>
#include <cassert>
#include <cstdint>
#include <stdexcept>
#include "OldColor.h"
#include "util/bitbash.h"
namespace quicktex::s3tc {
// region Interpolator implementation
std::unique_ptr<Interpolator> Interpolator::MakeInterpolator(Interpolator::Type type) {
switch (type) {
case Type::Ideal:
return std::make_unique<Interpolator>();
case Type::IdealRound:
return std::make_unique<InterpolatorRound>();
case Type::Nvidia:
return std::make_unique<InterpolatorNvidia>();
case Type::AMD:
return std::make_unique<InterpolatorAMD>();
default:
throw std::invalid_argument("Invalid interpolator type");
}
}
uint8_t Interpolator::Interpolate5(uint8_t v0, uint8_t v1) const {
return Interpolate8(scale_to_8<5>(v0), scale_to_8<5>(v1));
}
uint8_t Interpolator::Interpolate6(uint8_t v0, uint8_t v1) const {
return Interpolate8(scale_to_8<6>(v0), scale_to_8<6>(v1));
}
uint8_t Interpolator::InterpolateHalf5(uint8_t v0, uint8_t v1) const {
return InterpolateHalf8(scale_to_8<5>(v0), scale_to_8<5>(v1));
}
uint8_t Interpolator::InterpolateHalf6(uint8_t v0, uint8_t v1) const {
return InterpolateHalf8(scale_to_8<6>(v0), scale_to_8<6>(v1));
}
std::array<OldColor, 4> Interpolator::Interpolate565BC1(uint16_t low, uint16_t high, bool allow_3color) const {
bool use_3color = allow_3color && (high >= low);
return InterpolateBC1(OldColor::Unpack565Unscaled(low), OldColor::Unpack565Unscaled(high), use_3color);
}
std::array<OldColor, 4> Interpolator::InterpolateBC1(OldColor low, OldColor high, bool use_3color) const {
auto colors = std::array<OldColor, 4>();
colors[0] = low.ScaleFrom565();
colors[1] = high.ScaleFrom565();
if (use_3color) {
// 3-color mode
colors[2] = InterpolateHalfColor24(colors[0], colors[1]);
colors[3] = OldColor(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;
}
uint8_t Interpolator::Interpolate8(uint8_t v0, uint8_t v1) const { return (v0 * 2 + v1) / 3; }
uint8_t Interpolator::InterpolateHalf8(uint8_t v0, uint8_t v1) const { return (v0 + v1) / 2; }
// endregion
// region InterpolatorRound implementation
uint8_t InterpolatorRound::Interpolate5(uint8_t v0, uint8_t v1) const {
return Interpolate8(scale_to_8<5>(v0), scale_to_8<5>(v1));
}
uint8_t InterpolatorRound::Interpolate6(uint8_t v0, uint8_t v1) const {
return Interpolate8(scale_to_8<6>(v0), scale_to_8<6>(v1));
}
uint8_t InterpolatorRound::Interpolate8(uint8_t v0, uint8_t v1) const { return (v0 * 2 + v1 + 1) / 3; }
// endregion
// region InterpolatorNvidia implementation
uint8_t InterpolatorNvidia::Interpolate5(uint8_t v0, uint8_t v1) const {
assert(v0 < 32 && v1 < 32);
return ((2 * v0 + v1) * 22) / 8U;
}
uint8_t InterpolatorNvidia::Interpolate6(uint8_t v0, uint8_t v1) const {
assert(v0 < 64 && v1 < 64);
const int gdiff = (int)v1 - v0;
return static_cast<uint8_t>((256 * v0 + (gdiff / 4) + 128 + gdiff * 80) >> 8);
}
uint8_t InterpolatorNvidia::InterpolateHalf5(uint8_t v0, uint8_t v1) const {
assert(v0 < 32 && v1 < 32);
return ((v0 + v1) * 33) / 8U;
}
uint8_t InterpolatorNvidia::InterpolateHalf6(uint8_t v0, uint8_t v1) const {
assert(v0 < 64 && v1 < 64);
const int gdiff = (int)v1 - v0;
return static_cast<uint8_t>((256 * v0 + gdiff / 4 + 128 + gdiff * 128) >> 8);
}
std::array<OldColor, 4> InterpolatorNvidia::InterpolateBC1(OldColor low, OldColor 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<OldColor, 4> colors;
colors[0] = low.ScaleFrom565();
colors[1] = high.ScaleFrom565();
if (!use_3color) {
// 4-color mode
colors[2] = InterpolateColor565(low, high);
colors[3] = InterpolateColor565(high, low);
} else {
// 3-color mode
colors[2] = InterpolateHalfColor565(low, high);
colors[3] = OldColor(0, 0, 0, 0); // transparent black
}
return colors;
}
// endregion
// region InterpolatorAMD implementation
uint8_t InterpolatorAMD::Interpolate5(uint8_t v0, uint8_t v1) const {
return Interpolate8(scale_to_8<5>(v0), scale_to_8<5>(v1));
}
uint8_t InterpolatorAMD::Interpolate6(uint8_t v0, uint8_t v1) const {
return Interpolate8(scale_to_8<6>(v0), scale_to_8<6>(v1));
}
uint8_t InterpolatorAMD::InterpolateHalf5(uint8_t v0, uint8_t v1) const {
return InterpolateHalf8(scale_to_8<5>(v0), scale_to_8<5>(v1));
}
uint8_t InterpolatorAMD::InterpolateHalf6(uint8_t v0, uint8_t v1) const {
return InterpolateHalf8(scale_to_8<6>(v0), scale_to_8<6>(v1));
}
uint8_t InterpolatorAMD::Interpolate8(uint8_t v0, uint8_t v1) const { return (v0 * 43 + v1 * 21 + 32) >> 6; }
uint8_t InterpolatorAMD::InterpolateHalf8(uint8_t v0, uint8_t v1) const { return (v0 + v1 + 1) >> 1; }
// endregion
} // namespace quicktex::s3tc

View File

@ -1,5 +1,5 @@
/* Python-rgbcx Texture Compression Library
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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 "OldColor.h" // for Color
namespace rgbcx {
namespace quicktex::s3tc {
class Interpolator {
public:
@ -94,9 +94,19 @@ class Interpolator {
* 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<OldColor, 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<OldColor, 4> InterpolateBC1(OldColor low, OldColor high, bool use_3color) const;
/**
* Gets the type of an interpolator
@ -116,57 +126,57 @@ class Interpolator {
}
private:
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));
OldColor InterpolateColor24(const OldColor &c0, const OldColor &c1) const {
return OldColor(Interpolate8(c0.r, c1.r), Interpolate8(c0.g, c1.g), Interpolate8(c0.b, c1.b));
}
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));
OldColor InterpolateHalfColor24(const OldColor &c0, const OldColor &c1) const {
return OldColor(InterpolateHalf8(c0.r, c1.r), InterpolateHalf8(c0.g, c1.g), InterpolateHalf8(c0.b, c1.b));
}
};
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;
uint8_t Interpolate8(uint8_t v0, uint8_t v1) const override;
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;
Type GetType() const noexcept override { return Type::IdealRound; }
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;
virtual uint8_t Interpolate5(uint8_t v0, uint8_t v1) const override;
virtual 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;
virtual uint8_t InterpolateHalf5(uint8_t v0, uint8_t v1) const override;
virtual uint8_t InterpolateHalf6(uint8_t v0, uint8_t v1) const override;
std::array<Color, 4> InterpolateBC1(uint16_t low, uint16_t high) const override;
virtual std::array<OldColor, 4> InterpolateBC1(OldColor low, OldColor high, bool use_3color) const override;
Type GetType() const noexcept override { return Type::Nvidia; }
bool CanInterpolate8Bit() const noexcept override { return false; }
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 {
return Color(Interpolate5(c0.r, c1.r), Interpolate6(c0.g, c1.g), Interpolate5(c0.b, c1.b));
OldColor InterpolateColor565(const OldColor &c0, const OldColor &c1) const {
return OldColor(Interpolate5(c0.r, c1.r), Interpolate6(c0.g, c1.g), Interpolate5(c0.b, c1.b));
}
Color InterpolateHalfColor565(const Color &c0, const Color &c1) const {
return Color(InterpolateHalf5(c0.r, c1.r), InterpolateHalf6(c0.g, c1.g), InterpolateHalf5(c0.b, c1.b));
OldColor InterpolateHalfColor565(const OldColor &c0, const OldColor &c1) const {
return OldColor(InterpolateHalf5(c0.r, c1.r), InterpolateHalf6(c0.g, c1.g), InterpolateHalf5(c0.b, c1.b));
}
};
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 Interpolate8(uint8_t v0, uint8_t v1) const override;
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;
uint8_t InterpolateHalf5(uint8_t v0, uint8_t v1) const override;
uint8_t InterpolateHalf6(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;
Type GetType() const noexcept override { return Type::AMD; }
virtual Type GetType() const noexcept override { return Type::AMD; }
};
} // namespace rgbcx
} // namespace quicktex::s3tc

View File

@ -0,0 +1,3 @@
"""Classes representing various methods of interpolating BC1-3 blocks"""
from _quicktex._s3tc._interpolator import *

View File

@ -0,0 +1,68 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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

View File

@ -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,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <array>
#include <cstdint>
#include <xsimd/xsimd.hpp>
#include "../BC4/BC4Block.h"
#include "Matrix.h"
namespace rgbcx {
#pragma pack(push, 1)
class BC5Block {
public:
BC4Block chan0_block;
BC4Block chan1_block;
// Type your code here, or load an example.
namespace quicktex {
auto test(Matrix<float, 4, 1> a, Matrix<float, 4, 1> b, Matrix<float, 4, 1> c) {
return a * 7;
};
#pragma pack(pop)
} // namespace rgbcx
} // namespace quicktex

View File

@ -0,0 +1,70 @@
/* 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 <vector>
#include "Texture.h"
namespace quicktex {
template <typename B> class BlockTexture final : public Texture {
private:
std::vector<B> _blocks;
unsigned _width_b;
unsigned _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 w, int h) : Base(w, h) {
_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 unsigned bwidth() const { return _width_b; }
constexpr unsigned bheight() const { return _height_b; }
constexpr std::tuple<int, int> bsize() const { return std::tuple<int, int>(_width_b, _height_b); }
B get_block(unsigned x, unsigned y) const {
if (x >= _width_b) throw std::out_of_range("x value out of range.");
if (y >= _height_b) throw std::out_of_range("y value out of range.");
return _blocks.at(x + (y * _width_b));
}
void set_block(unsigned x, unsigned y, const B &val) {
if (x >= _width_b) throw std::out_of_range("x value out of range.");
if (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

View File

@ -0,0 +1,33 @@
/* 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 "RawTexture.h"
namespace quicktex {
Color RawTexture::pixel(unsigned x, unsigned y) const {
if (x >= width) throw std::invalid_argument("x value out of range.");
if (y >= height) throw std::invalid_argument("y value out of range.");
return _pixels.at(x + (y * width));
}
quicktex::Color& RawTexture::pixel(unsigned x, unsigned y) {
if (x >= width) throw std::invalid_argument("x value out of range.");
if (y >= height) throw std::invalid_argument("y value out of range.");
return _pixels.at(x + (y * width));
}
} // namespace quicktex

View File

@ -0,0 +1,97 @@
/* 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"
#include "OldColor.h"
#include "texture/Texture.h"
namespace quicktex {
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 w, int h) : Base(w, h), _pixels(w* h) {}
quicktex::Color pixel(unsigned x, unsigned y) const;
quicktex::Color &pixel(unsigned x, unsigned y);
quicktex::Color pixel_wrapped(unsigned x, unsigned y) const { return pixel(x % width, y % height); }
quicktex::Color &pixel_wrapped(unsigned x, unsigned y) { return pixel(x % width, y % height); }
size_t nbytes() const noexcept override { return static_cast<size_t>(width * height) * sizeof(quicktex::Color); }
template <int N, int M> quicktex::ColorBlock<N, M> get_block(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
quicktex::ColorBlock<N, M> block;
int pixel_x = block_x * N;
int pixel_y = block_y * M;
// 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, pixel((pixel_x + x) % width, (pixel_y + y) % height)); }
}
return block;
}
template <int N, int M> void set_block(int block_x, int block_y, const quicktex::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;
// 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++) { pixel((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<quicktex::Color> _pixels;
};
} // namespace quicktex

View File

@ -0,0 +1,62 @@
/* Quicktex Texture Compression Library
Copyright (C) 2021-2022 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"
#include "OldColor.h"
#include "Window.h"
namespace quicktex {
class Texture {
public:
const unsigned width;
const unsigned height;
virtual ~Texture() = default;
virtual std::tuple<unsigned, unsigned> Size() const { return {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(unsigned w, unsigned h) : width(w), height(h) {}
};
} // namespace quicktex

Some files were not shown because too many files have changed in this diff Show More