opengl-basisu 0.1.0a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opengl_basisu-0.1.0a1/CHANGELOG.md +8 -0
- opengl_basisu-0.1.0a1/LICENSE +60 -0
- opengl_basisu-0.1.0a1/MANIFEST.in +11 -0
- opengl_basisu-0.1.0a1/NOTICE +27 -0
- opengl_basisu-0.1.0a1/PKG-INFO +182 -0
- opengl_basisu-0.1.0a1/README.md +142 -0
- opengl_basisu-0.1.0a1/docs/API.md +101 -0
- opengl_basisu-0.1.0a1/pyproject.toml +173 -0
- opengl_basisu-0.1.0a1/setup.cfg +4 -0
- opengl_basisu-0.1.0a1/setup.py +77 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu/__init__.py +50 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu/_shim.hpp +230 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu/_transcoder.cpp +34958 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu/_transcoder.pyi +51 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu/_transcoder.pyx +204 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu/errors.py +17 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu/py.typed +0 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu/targets.py +368 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu/texture.py +307 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu.egg-info/PKG-INFO +182 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu.egg-info/SOURCES.txt +71 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu.egg-info/dependency_links.txt +1 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu.egg-info/requires.txt +11 -0
- opengl_basisu-0.1.0a1/src/opengl_basisu.egg-info/top_level.txt +1 -0
- opengl_basisu-0.1.0a1/tests/blocks.py +73 -0
- opengl_basisu-0.1.0a1/tests/fixtures/colours.json +72 -0
- opengl_basisu-0.1.0a1/tests/fixtures/etc1s_cube.ktx2 +0 -0
- opengl_basisu-0.1.0a1/tests/fixtures/etc1s_rgba.basis +0 -0
- opengl_basisu-0.1.0a1/tests/fixtures/etc1s_srgb_mips.ktx2 +0 -0
- opengl_basisu-0.1.0a1/tests/fixtures/uastc_hdr.ktx2 +0 -0
- opengl_basisu-0.1.0a1/tests/fixtures/uastc_odd.ktx2 +0 -0
- opengl_basisu-0.1.0a1/tests/fixtures/uastc_rgba.ktx2 +0 -0
- opengl_basisu-0.1.0a1/tests/samples.py +27 -0
- opengl_basisu-0.1.0a1/tests/test_errors.py +134 -0
- opengl_basisu-0.1.0a1/tests/test_open.py +97 -0
- opengl_basisu-0.1.0a1/tests/test_targets.py +93 -0
- opengl_basisu-0.1.0a1/tests/test_transcode.py +174 -0
- opengl_basisu-0.1.0a1/tools/make_fixtures.py +140 -0
- opengl_basisu-0.1.0a1/tools/vendor_basisu.py +85 -0
- opengl_basisu-0.1.0a1/tox.ini +56 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/LICENSE +201 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/NOTICE +15 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/VERSION +3 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu.h +821 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_astc_cfgs.inl +648 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_astc_hdr_core.h +211 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_astc_helpers.h +5335 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_containers.h +4354 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_containers_impl.h +818 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_dds_transcoder.inl +1372 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_etc1_mods.inl +258 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_file_headers.h +264 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_idct.h +1446 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder.cpp +45962 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder.h +1786 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder_internal.h +4044 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder_tables_astc.inc +481 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder_tables_astc_0_255.inc +481 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder_tables_atc_55.inc +481 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder_tables_atc_56.inc +481 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder_tables_bc7_m5_alpha.inc +49 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder_tables_bc7_m5_color.inc +481 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder_tables_dxt1_5.inc +494 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder_tables_dxt1_6.inc +494 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder_tables_pvrtc2_45.inc +481 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder_tables_pvrtc2_alpha_33.inc +481 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_transcoder_uastc.h +289 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_xbc7_decoder.h +1467 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/transcoder/basisu_xbc7_decoder.inl +1515 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/zstd/LICENSE +30 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/zstd/zstd.h +3198 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/zstd/zstd_errors.h +107 -0
- opengl_basisu-0.1.0a1/vendor/basis_universal/zstd/zstddeclib.c +22242 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0a1
|
|
4
|
+
|
|
5
|
+
- `open()` reads a KTX 2 file with an ETC1S or UASTC payload, or a `.basis` file, from a path or bytes.
|
|
6
|
+
- `Texture.transcode()` writes BC1, BC3, BC4, BC5, BC7, ETC1, ETC2, ASTC 4x4, RGBA8 or RGB565 as a NumPy array.
|
|
7
|
+
- `Target.info` carries each format's GL enums; `choose_target()` picks one from a context's extensions and version.
|
|
8
|
+
- Transcoder: basis_universal v2_50 (commit 9bebe167).
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Python wrapper Copyright (c) 2026 Mike C. Fletcher and Contributors
|
|
4
|
+
|
|
5
|
+
All rights reserved except as granted under the [Apache 2.0 license](https://github.com/BinomialLLC/basis_universal/blob/master/LICENSE).
|
|
6
|
+
|
|
7
|
+
NOTE ON AUTHORSHIP
|
|
8
|
+
|
|
9
|
+
This wrapper was generated by a large language model under human direction.
|
|
10
|
+
Jurisdictions differ on whether such material attracts copyright at all: some
|
|
11
|
+
require human authorship for a work to be protected, some extend protection to
|
|
12
|
+
computer-generated works, and in others the question is unsettled. Where no
|
|
13
|
+
copyright subsists in part or all of this software, the permission granted
|
|
14
|
+
above has nothing to operate on there and that material may be used freely;
|
|
15
|
+
where copyright does subsist, the terms above govern it. The disclaimer of
|
|
16
|
+
warranty and limitation of liability apply in either case.
|
|
17
|
+
|
|
18
|
+
Basis Universal™ Supercompressed GPU Texture Compression Library
|
|
19
|
+
|
|
20
|
+
Copyright © 2016–2026 Binomial LLC.
|
|
21
|
+
All rights reserved except as granted under the [Apache 2.0 license](https://github.com/BinomialLLC/basis_universal/blob/master/LICENSE).
|
|
22
|
+
"Basis Universal" is a trademark of Binomial LLC.
|
|
23
|
+
|
|
24
|
+
The documents in the Basis Universal wiki, and the Basis Universal library, example, and tool source code, fall under the Apache 2.0 license, unless otherwise explicitly indicated.
|
|
25
|
+
|
|
26
|
+
Redistributions or derivative works must include a readable copy of the attribution notices from this NOTICE file (see Apache License 2.0 §4(d)).
|
|
27
|
+
|
|
28
|
+
If you modify the Basis Universal source code, specifications, or wiki documents and redistribute the files, you must cause any modified files to carry prominent notices stating that you changed the files (see Apache 2.0 §4(b)).
|
|
29
|
+
|
|
30
|
+
**This software, documentation and specifications are provided "as is", without warranty of any kind (see Apache 2.0 §§7–8).**
|
|
31
|
+
|
|
32
|
+
For Zstandard software
|
|
33
|
+
|
|
34
|
+
Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
|
|
35
|
+
|
|
36
|
+
Redistribution and use in source and binary forms, with or without modification,
|
|
37
|
+
are permitted provided that the following conditions are met:
|
|
38
|
+
|
|
39
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
40
|
+
list of conditions and the following disclaimer.
|
|
41
|
+
|
|
42
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
43
|
+
this list of conditions and the following disclaimer in the documentation
|
|
44
|
+
and/or other materials provided with the distribution.
|
|
45
|
+
|
|
46
|
+
* Neither the name Facebook nor the names of its contributors may be used to
|
|
47
|
+
endorse or promote products derived from this software without specific
|
|
48
|
+
prior written permission.
|
|
49
|
+
|
|
50
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
51
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
52
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
53
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
54
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
55
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
56
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
57
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
58
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
59
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
60
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# The vendored transcoder is compiled by every source build, so the sdist
|
|
2
|
+
# carries all of it, with its licence and notice files.
|
|
3
|
+
graft vendor
|
|
4
|
+
include NOTICE
|
|
5
|
+
include tox.ini
|
|
6
|
+
include CHANGELOG.md
|
|
7
|
+
recursive-include docs *.md
|
|
8
|
+
recursive-include tests *.py
|
|
9
|
+
recursive-include tests/fixtures *.ktx2 *.basis *.json
|
|
10
|
+
recursive-include tools *.py
|
|
11
|
+
include src/opengl_basisu/_shim.hpp
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
opengl_basisu
|
|
2
|
+
Copyright (c) 2026 Mike C. Fletcher and Contributors
|
|
3
|
+
Licensed under the MIT License; see LICENSE.
|
|
4
|
+
|
|
5
|
+
The compiled extension contains third-party code, copied unmodified into
|
|
6
|
+
vendor/basis_universal from basis_universal, tag v2_50
|
|
7
|
+
(commit 9bebe16726b3a61c8c213eeee3b7cffb462ef34e,
|
|
8
|
+
https://github.com/BinomialLLC/basis_universal):
|
|
9
|
+
|
|
10
|
+
* The Basis Universal transcoder (vendor/basis_universal/transcoder).
|
|
11
|
+
Licensed under the Apache License, Version 2.0; see
|
|
12
|
+
vendor/basis_universal/LICENSE. Its attribution notice, reproduced as
|
|
13
|
+
Apache 2.0 section 4(d) requires:
|
|
14
|
+
|
|
15
|
+
Basis Universal(TM) Supercompressed GPU Texture Compression Library
|
|
16
|
+
Copyright (c) 2016-2026 Binomial LLC.
|
|
17
|
+
All rights reserved except as granted under the Apache 2.0 license.
|
|
18
|
+
"Basis Universal" is a trademark of Binomial LLC.
|
|
19
|
+
|
|
20
|
+
The full notice is vendor/basis_universal/NOTICE.
|
|
21
|
+
|
|
22
|
+
* The Zstandard single-file decoder (vendor/basis_universal/zstd).
|
|
23
|
+
Copyright (c) Meta Platforms, Inc. and affiliates. Zstandard is offered
|
|
24
|
+
under a choice of the BSD license or the GPLv2; this package takes it under
|
|
25
|
+
the BSD license in vendor/basis_universal/zstd/LICENSE.
|
|
26
|
+
|
|
27
|
+
No basis_universal encoder code is included.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: opengl_basisu
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: Basis Universal KTX 2 and .basis transcoding to GPU texture formats, as NumPy arrays
|
|
5
|
+
Author-email: "Mike C. Fletcher" <mcfletch@vrplumber.com>
|
|
6
|
+
License-Expression: MIT AND Apache-2.0 AND BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/mcfletch/opengl_basisu
|
|
8
|
+
Project-URL: Repository, https://github.com/mcfletch/opengl_basisu
|
|
9
|
+
Keywords: basis,basisu,ktx2,KHR_texture_basisu,etc1s,uastc,texture,compression,glTF,numpy,BC7,ASTC
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
20
|
+
Classifier: Programming Language :: C++
|
|
21
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Rendering
|
|
22
|
+
Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
License-File: NOTICE
|
|
27
|
+
License-File: vendor/basis_universal/LICENSE
|
|
28
|
+
License-File: vendor/basis_universal/NOTICE
|
|
29
|
+
License-File: vendor/basis_universal/zstd/LICENSE
|
|
30
|
+
Requires-Dist: numpy>=1.24
|
|
31
|
+
Provides-Extra: test
|
|
32
|
+
Requires-Dist: pytest; extra == "test"
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: opengl_basisu[test]; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy; extra == "dev"
|
|
36
|
+
Requires-Dist: ruff; extra == "dev"
|
|
37
|
+
Requires-Dist: tox>=4.0; extra == "dev"
|
|
38
|
+
Requires-Dist: Cython>=3.0; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# opengl_basisu: Basis Universal transcoding as NumPy arrays
|
|
42
|
+
|
|
43
|
+
Transcode a KTX 2 file with a Basis Universal payload (ETC1S or UASTC, the two
|
|
44
|
+
codecs glTF's `KHR_texture_basisu` allows), or a `.basis` file, to a GPU
|
|
45
|
+
texture format: BC1, BC3, BC4, BC5, BC7, ETC1, ETC2 or ASTC 4x4 blocks, or
|
|
46
|
+
RGBA8 or RGB565 pixels. Input is a path or bytes; output is a NumPy array with
|
|
47
|
+
the OpenGL enums to upload it, as integers. Nothing here imports OpenGL.
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import opengl_basisu
|
|
51
|
+
|
|
52
|
+
texture = opengl_basisu.open('albedo.ktx2')
|
|
53
|
+
texture.width, texture.height, texture.levels # 1024, 1024, 11
|
|
54
|
+
texture.codec, texture.srgb, texture.has_alpha # Codec.ETC1S, True, False
|
|
55
|
+
|
|
56
|
+
target = opengl_basisu.choose_target(
|
|
57
|
+
extensions, # the names the GL context reports
|
|
58
|
+
codec=texture.codec,
|
|
59
|
+
has_alpha=texture.has_alpha,
|
|
60
|
+
srgb=texture.srgb,
|
|
61
|
+
version=(4, 6),
|
|
62
|
+
)
|
|
63
|
+
for image in texture.transcode_levels(target):
|
|
64
|
+
if image.compressed:
|
|
65
|
+
glCompressedTexImage2D(
|
|
66
|
+
GL_TEXTURE_2D, image.level, image.gl_internal_format,
|
|
67
|
+
image.width, image.height, 0, image.nbytes, image.data,
|
|
68
|
+
)
|
|
69
|
+
else:
|
|
70
|
+
glTexImage2D(
|
|
71
|
+
GL_TEXTURE_2D, image.level, image.gl_internal_format,
|
|
72
|
+
image.width, image.height, 0, image.gl_format, image.gl_type, image.data,
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
NumPy is the only runtime dependency.
|
|
77
|
+
|
|
78
|
+
## Install
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
pip install opengl_basisu
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Wheels are published for CPython 3.10 to 3.14 on Linux, Windows and macOS
|
|
85
|
+
(arm64 and x86_64). A source build needs Cython and a C++17 compiler.
|
|
86
|
+
|
|
87
|
+
## What a texture reports
|
|
88
|
+
|
|
89
|
+
| Attribute | Meaning |
|
|
90
|
+
|---|---|
|
|
91
|
+
| `container` | `'ktx2'` or `'basis'` |
|
|
92
|
+
| `width`, `height` | level 0, in pixels |
|
|
93
|
+
| `levels` | mip levels, at least 1 |
|
|
94
|
+
| `faces` | 1, or 6 for a cube map |
|
|
95
|
+
| `layers`, `is_array` | array layers (at least 1), and whether the file declares an array |
|
|
96
|
+
| `codec` | a `Codec`: `ETC1S`, `UASTC_LDR_4x4`, or a newer basis_universal codec |
|
|
97
|
+
| `has_alpha` | the file stores alpha |
|
|
98
|
+
| `srgb` | the colour space is sRGB rather than linear |
|
|
99
|
+
| `is_video`, `y_flipped` | a video's frames are its layers; `y_flipped` is a `.basis` flag |
|
|
100
|
+
|
|
101
|
+
`texture.transcode(target, level=0, face=0, layer=0)` returns an `Image`.
|
|
102
|
+
`transcode_levels(target, face, layer)` returns the mip chain, largest first,
|
|
103
|
+
and `images(target)` yields every image in the file.
|
|
104
|
+
|
|
105
|
+
## Targets
|
|
106
|
+
|
|
107
|
+
| `Target` | Array | GL internal format (linear / sRGB) | Provided by |
|
|
108
|
+
|---|---|---|---|
|
|
109
|
+
| `BC1` | (rows, cols, 8) | `0x83F0` / `0x8C4C` | `GL_EXT_texture_compression_s3tc`; sRGB also `GL_EXT_texture_sRGB` |
|
|
110
|
+
| `BC3` | (rows, cols, 16) | `0x83F3` / `0x8C4F` | as BC1 |
|
|
111
|
+
| `BC4` | (rows, cols, 8) | `0x8DBB` / none | GL 3.0, `GL_ARB_texture_compression_rgtc` |
|
|
112
|
+
| `BC5` | (rows, cols, 16) | `0x8DBD` / none | GL 3.0, `GL_ARB_texture_compression_rgtc` |
|
|
113
|
+
| `BC7` | (rows, cols, 16) | `0x8E8C` / `0x8E8D` | GL 4.2, `GL_ARB_texture_compression_bptc`, `GL_EXT_texture_compression_bptc` |
|
|
114
|
+
| `ETC1` | (rows, cols, 8) | `0x9274` / `0x9275` (ETC2 RGB8) | GL 4.3, GLES 3.0, `GL_ARB_ES3_compatibility` |
|
|
115
|
+
| `ETC2` | (rows, cols, 16) | `0x9278` / `0x9279` | GL 4.3, GLES 3.0, `GL_ARB_ES3_compatibility` |
|
|
116
|
+
| `ASTC_4x4` | (rows, cols, 16) | `0x93B0` / `0x93D0` | GLES 3.2, `GL_KHR_texture_compression_astc_ldr` |
|
|
117
|
+
| `RGBA8` | (height, width, 4) uint8 | `GL_RGBA8` / `GL_SRGB8_ALPHA8` | always |
|
|
118
|
+
| `RGB565` | (height, width) uint16 | `GL_RGB565` / none | GL 4.1, GLES 2.0 |
|
|
119
|
+
|
|
120
|
+
Block arrays are uint8, one 4x4 block per `(row, column)`, rows of blocks top
|
|
121
|
+
to bottom; `rows` and `cols` round the pixel size up to whole blocks.
|
|
122
|
+
`Target.X.info` is the `TargetFormat` with these values, and
|
|
123
|
+
`TargetFormat.available(extensions, version, es, srgb)` answers whether a
|
|
124
|
+
context provides it.
|
|
125
|
+
|
|
126
|
+
`choose_target()` takes the extension names, version and `es` flag of a
|
|
127
|
+
context, and the texture's codec, alpha and colour space. It walks
|
|
128
|
+
`preferences(codec, es, channels)` and returns the first target the context
|
|
129
|
+
provides, or `RGBA8`. On desktop GL the BC formats come before ETC because
|
|
130
|
+
desktop drivers commonly decode ETC2 to RGBA8 at upload. UASTC prefers ASTC
|
|
131
|
+
4x4, which it transcodes to without re-encoding; ETC1S on GLES prefers ETC1
|
|
132
|
+
for the same reason. `channels='r'` or `'rg'` puts BC4 or BC5 first, for
|
|
133
|
+
single and two-channel data.
|
|
134
|
+
|
|
135
|
+
## Errors
|
|
136
|
+
|
|
137
|
+
- `BasisuError` (a `ValueError`): truncated or corrupt data, or bytes that are
|
|
138
|
+
neither KTX 2 nor `.basis`.
|
|
139
|
+
- `UnsupportedError` (a `BasisuError`): a KTX 2 file whose payload is not a
|
|
140
|
+
Basis Universal codec (an uncompressed or a plain BCn KTX 2, for example), or
|
|
141
|
+
a high dynamic range codec asked for an 8-bit target.
|
|
142
|
+
- `ValueError`: an unknown target name. `IndexError`: a level, face or layer
|
|
143
|
+
the texture does not have.
|
|
144
|
+
|
|
145
|
+
The test suite truncates every fixture at every length and transcodes 5,000
|
|
146
|
+
randomly corrupted copies of each; corrupt input raises. `docs/API.md` has the
|
|
147
|
+
AddressSanitizer run of the same tests.
|
|
148
|
+
|
|
149
|
+
## Limits
|
|
150
|
+
|
|
151
|
+
- High dynamic range codecs (UASTC HDR 4x4, ASTC HDR 6x6) open and report
|
|
152
|
+
their header, and have no target here.
|
|
153
|
+
- The layers of an ETC1S video are P-frames: transcode them in order from
|
|
154
|
+
layer 0.
|
|
155
|
+
- One `Texture` transcodes one image at a time (a lock serialises threads);
|
|
156
|
+
separate textures transcode in parallel with the GIL released.
|
|
157
|
+
- The transcoder addresses files of up to 4 GiB.
|
|
158
|
+
|
|
159
|
+
## The transcoder
|
|
160
|
+
|
|
161
|
+
The compiled extension is Binomial's
|
|
162
|
+
[basis_universal](https://github.com/BinomialLLC/basis_universal) transcoder,
|
|
163
|
+
tag `v2_50` (commit `9bebe167`), with the Zstandard decoder it bundles. Both
|
|
164
|
+
are copied unmodified into `vendor/basis_universal`; `vendor/basis_universal/VERSION`
|
|
165
|
+
records the tag and commit, and `tools/vendor_basisu.py` repeats the copy for
|
|
166
|
+
another tag. No encoder code is included.
|
|
167
|
+
|
|
168
|
+
## Test fixtures
|
|
169
|
+
|
|
170
|
+
`tests/fixtures` holds six files of under 600 bytes each, written by the
|
|
171
|
+
basis_universal `v2_50` encoder from flat-colour images by
|
|
172
|
+
`tools/make_fixtures.py`; its docstring has the build and the encoder flags
|
|
173
|
+
for each file. `tests/fixtures/colours.json` lists the colours the tests
|
|
174
|
+
compare against.
|
|
175
|
+
|
|
176
|
+
## Licence
|
|
177
|
+
|
|
178
|
+
This package is Apache License 2.0. The extension also contains the
|
|
179
|
+
basis_universal transcoder under the Apache License 2.0
|
|
180
|
+
(`vendor/basis_universal/LICENSE`, `vendor/basis_universal/NOTICE`) and the
|
|
181
|
+
Zstandard decoder under the BSD licence (`vendor/basis_universal/zstd/LICENSE`).
|
|
182
|
+
`NOTICE` lists both.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# opengl_basisu: Basis Universal transcoding as NumPy arrays
|
|
2
|
+
|
|
3
|
+
Transcode a KTX 2 file with a Basis Universal payload (ETC1S or UASTC, the two
|
|
4
|
+
codecs glTF's `KHR_texture_basisu` allows), or a `.basis` file, to a GPU
|
|
5
|
+
texture format: BC1, BC3, BC4, BC5, BC7, ETC1, ETC2 or ASTC 4x4 blocks, or
|
|
6
|
+
RGBA8 or RGB565 pixels. Input is a path or bytes; output is a NumPy array with
|
|
7
|
+
the OpenGL enums to upload it, as integers. Nothing here imports OpenGL.
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
import opengl_basisu
|
|
11
|
+
|
|
12
|
+
texture = opengl_basisu.open('albedo.ktx2')
|
|
13
|
+
texture.width, texture.height, texture.levels # 1024, 1024, 11
|
|
14
|
+
texture.codec, texture.srgb, texture.has_alpha # Codec.ETC1S, True, False
|
|
15
|
+
|
|
16
|
+
target = opengl_basisu.choose_target(
|
|
17
|
+
extensions, # the names the GL context reports
|
|
18
|
+
codec=texture.codec,
|
|
19
|
+
has_alpha=texture.has_alpha,
|
|
20
|
+
srgb=texture.srgb,
|
|
21
|
+
version=(4, 6),
|
|
22
|
+
)
|
|
23
|
+
for image in texture.transcode_levels(target):
|
|
24
|
+
if image.compressed:
|
|
25
|
+
glCompressedTexImage2D(
|
|
26
|
+
GL_TEXTURE_2D, image.level, image.gl_internal_format,
|
|
27
|
+
image.width, image.height, 0, image.nbytes, image.data,
|
|
28
|
+
)
|
|
29
|
+
else:
|
|
30
|
+
glTexImage2D(
|
|
31
|
+
GL_TEXTURE_2D, image.level, image.gl_internal_format,
|
|
32
|
+
image.width, image.height, 0, image.gl_format, image.gl_type, image.data,
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
NumPy is the only runtime dependency.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
pip install opengl_basisu
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Wheels are published for CPython 3.10 to 3.14 on Linux, Windows and macOS
|
|
45
|
+
(arm64 and x86_64). A source build needs Cython and a C++17 compiler.
|
|
46
|
+
|
|
47
|
+
## What a texture reports
|
|
48
|
+
|
|
49
|
+
| Attribute | Meaning |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `container` | `'ktx2'` or `'basis'` |
|
|
52
|
+
| `width`, `height` | level 0, in pixels |
|
|
53
|
+
| `levels` | mip levels, at least 1 |
|
|
54
|
+
| `faces` | 1, or 6 for a cube map |
|
|
55
|
+
| `layers`, `is_array` | array layers (at least 1), and whether the file declares an array |
|
|
56
|
+
| `codec` | a `Codec`: `ETC1S`, `UASTC_LDR_4x4`, or a newer basis_universal codec |
|
|
57
|
+
| `has_alpha` | the file stores alpha |
|
|
58
|
+
| `srgb` | the colour space is sRGB rather than linear |
|
|
59
|
+
| `is_video`, `y_flipped` | a video's frames are its layers; `y_flipped` is a `.basis` flag |
|
|
60
|
+
|
|
61
|
+
`texture.transcode(target, level=0, face=0, layer=0)` returns an `Image`.
|
|
62
|
+
`transcode_levels(target, face, layer)` returns the mip chain, largest first,
|
|
63
|
+
and `images(target)` yields every image in the file.
|
|
64
|
+
|
|
65
|
+
## Targets
|
|
66
|
+
|
|
67
|
+
| `Target` | Array | GL internal format (linear / sRGB) | Provided by |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| `BC1` | (rows, cols, 8) | `0x83F0` / `0x8C4C` | `GL_EXT_texture_compression_s3tc`; sRGB also `GL_EXT_texture_sRGB` |
|
|
70
|
+
| `BC3` | (rows, cols, 16) | `0x83F3` / `0x8C4F` | as BC1 |
|
|
71
|
+
| `BC4` | (rows, cols, 8) | `0x8DBB` / none | GL 3.0, `GL_ARB_texture_compression_rgtc` |
|
|
72
|
+
| `BC5` | (rows, cols, 16) | `0x8DBD` / none | GL 3.0, `GL_ARB_texture_compression_rgtc` |
|
|
73
|
+
| `BC7` | (rows, cols, 16) | `0x8E8C` / `0x8E8D` | GL 4.2, `GL_ARB_texture_compression_bptc`, `GL_EXT_texture_compression_bptc` |
|
|
74
|
+
| `ETC1` | (rows, cols, 8) | `0x9274` / `0x9275` (ETC2 RGB8) | GL 4.3, GLES 3.0, `GL_ARB_ES3_compatibility` |
|
|
75
|
+
| `ETC2` | (rows, cols, 16) | `0x9278` / `0x9279` | GL 4.3, GLES 3.0, `GL_ARB_ES3_compatibility` |
|
|
76
|
+
| `ASTC_4x4` | (rows, cols, 16) | `0x93B0` / `0x93D0` | GLES 3.2, `GL_KHR_texture_compression_astc_ldr` |
|
|
77
|
+
| `RGBA8` | (height, width, 4) uint8 | `GL_RGBA8` / `GL_SRGB8_ALPHA8` | always |
|
|
78
|
+
| `RGB565` | (height, width) uint16 | `GL_RGB565` / none | GL 4.1, GLES 2.0 |
|
|
79
|
+
|
|
80
|
+
Block arrays are uint8, one 4x4 block per `(row, column)`, rows of blocks top
|
|
81
|
+
to bottom; `rows` and `cols` round the pixel size up to whole blocks.
|
|
82
|
+
`Target.X.info` is the `TargetFormat` with these values, and
|
|
83
|
+
`TargetFormat.available(extensions, version, es, srgb)` answers whether a
|
|
84
|
+
context provides it.
|
|
85
|
+
|
|
86
|
+
`choose_target()` takes the extension names, version and `es` flag of a
|
|
87
|
+
context, and the texture's codec, alpha and colour space. It walks
|
|
88
|
+
`preferences(codec, es, channels)` and returns the first target the context
|
|
89
|
+
provides, or `RGBA8`. On desktop GL the BC formats come before ETC because
|
|
90
|
+
desktop drivers commonly decode ETC2 to RGBA8 at upload. UASTC prefers ASTC
|
|
91
|
+
4x4, which it transcodes to without re-encoding; ETC1S on GLES prefers ETC1
|
|
92
|
+
for the same reason. `channels='r'` or `'rg'` puts BC4 or BC5 first, for
|
|
93
|
+
single and two-channel data.
|
|
94
|
+
|
|
95
|
+
## Errors
|
|
96
|
+
|
|
97
|
+
- `BasisuError` (a `ValueError`): truncated or corrupt data, or bytes that are
|
|
98
|
+
neither KTX 2 nor `.basis`.
|
|
99
|
+
- `UnsupportedError` (a `BasisuError`): a KTX 2 file whose payload is not a
|
|
100
|
+
Basis Universal codec (an uncompressed or a plain BCn KTX 2, for example), or
|
|
101
|
+
a high dynamic range codec asked for an 8-bit target.
|
|
102
|
+
- `ValueError`: an unknown target name. `IndexError`: a level, face or layer
|
|
103
|
+
the texture does not have.
|
|
104
|
+
|
|
105
|
+
The test suite truncates every fixture at every length and transcodes 5,000
|
|
106
|
+
randomly corrupted copies of each; corrupt input raises. `docs/API.md` has the
|
|
107
|
+
AddressSanitizer run of the same tests.
|
|
108
|
+
|
|
109
|
+
## Limits
|
|
110
|
+
|
|
111
|
+
- High dynamic range codecs (UASTC HDR 4x4, ASTC HDR 6x6) open and report
|
|
112
|
+
their header, and have no target here.
|
|
113
|
+
- The layers of an ETC1S video are P-frames: transcode them in order from
|
|
114
|
+
layer 0.
|
|
115
|
+
- One `Texture` transcodes one image at a time (a lock serialises threads);
|
|
116
|
+
separate textures transcode in parallel with the GIL released.
|
|
117
|
+
- The transcoder addresses files of up to 4 GiB.
|
|
118
|
+
|
|
119
|
+
## The transcoder
|
|
120
|
+
|
|
121
|
+
The compiled extension is Binomial's
|
|
122
|
+
[basis_universal](https://github.com/BinomialLLC/basis_universal) transcoder,
|
|
123
|
+
tag `v2_50` (commit `9bebe167`), with the Zstandard decoder it bundles. Both
|
|
124
|
+
are copied unmodified into `vendor/basis_universal`; `vendor/basis_universal/VERSION`
|
|
125
|
+
records the tag and commit, and `tools/vendor_basisu.py` repeats the copy for
|
|
126
|
+
another tag. No encoder code is included.
|
|
127
|
+
|
|
128
|
+
## Test fixtures
|
|
129
|
+
|
|
130
|
+
`tests/fixtures` holds six files of under 600 bytes each, written by the
|
|
131
|
+
basis_universal `v2_50` encoder from flat-colour images by
|
|
132
|
+
`tools/make_fixtures.py`; its docstring has the build and the encoder flags
|
|
133
|
+
for each file. `tests/fixtures/colours.json` lists the colours the tests
|
|
134
|
+
compare against.
|
|
135
|
+
|
|
136
|
+
## Licence
|
|
137
|
+
|
|
138
|
+
This package is Apache License 2.0. The extension also contains the
|
|
139
|
+
basis_universal transcoder under the Apache License 2.0
|
|
140
|
+
(`vendor/basis_universal/LICENSE`, `vendor/basis_universal/NOTICE`) and the
|
|
141
|
+
Zstandard decoder under the BSD licence (`vendor/basis_universal/zstd/LICENSE`).
|
|
142
|
+
`NOTICE` lists both.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# opengl_basisu API
|
|
2
|
+
|
|
3
|
+
Everything below is importable from `opengl_basisu`.
|
|
4
|
+
|
|
5
|
+
## Reading a file
|
|
6
|
+
|
|
7
|
+
`open(source) -> Texture`
|
|
8
|
+
: `source` is a path (`str` or `os.PathLike`) or the file's bytes (`bytes`,
|
|
9
|
+
`bytearray`, `memoryview`; the data is copied). Raises `BasisuError` for data
|
|
10
|
+
that is not a KTX 2 or `.basis` file the transcoder accepts, and
|
|
11
|
+
`UnsupportedError` for a KTX 2 file whose `vkFormat` is set and whose payload
|
|
12
|
+
the transcoder does not read.
|
|
13
|
+
|
|
14
|
+
`container_of(data) -> 'ktx2' | 'basis' | None`
|
|
15
|
+
: The container named by the first 12 bytes, without parsing the rest.
|
|
16
|
+
|
|
17
|
+
`Texture`
|
|
18
|
+
: `container`, `width`, `height`, `levels`, `faces`, `layers`, `is_array`,
|
|
19
|
+
`codec`, `has_alpha`, `srgb`, `is_video`, `y_flipped`, as in the README.
|
|
20
|
+
|
|
21
|
+
`level_size(level=0) -> (width, height)` in pixels.
|
|
22
|
+
|
|
23
|
+
`supports(target) -> bool`: whether the codec transcodes to `target`.
|
|
24
|
+
|
|
25
|
+
`transcode(target, level=0, face=0, layer=0) -> Image`. `target` is a
|
|
26
|
+
`Target` or its value (`'bc7'`). Raises `IndexError` for an index out of
|
|
27
|
+
range, `ValueError` for an unknown target name, `UnsupportedError` for a
|
|
28
|
+
codec and target the transcoder does not pair (any HDR codec with any target
|
|
29
|
+
here), and `BasisuError` when the transcoder rejects the image data.
|
|
30
|
+
|
|
31
|
+
`transcode_levels(target, face=0, layer=0) -> list[Image]`, largest first.
|
|
32
|
+
|
|
33
|
+
`images(target) -> Iterator[Image]`, by layer, then face, then level.
|
|
34
|
+
|
|
35
|
+
`Image`
|
|
36
|
+
: A frozen dataclass: `target`, `width`, `height` (pixels), `level`, `face`,
|
|
37
|
+
`layer`, `srgb` and `data` (a NumPy array). Properties: `compressed`,
|
|
38
|
+
`gl_internal_format` (the sRGB enum when `srgb` and the target has one),
|
|
39
|
+
`gl_format` and `gl_type` (uncompressed targets only, else `None`) and
|
|
40
|
+
`nbytes` (the `imageSize` argument of `glCompressedTexImage2D`).
|
|
41
|
+
|
|
42
|
+
## Choosing a target
|
|
43
|
+
|
|
44
|
+
`Target`
|
|
45
|
+
: `BC1`, `BC3`, `BC4`, `BC5`, `BC7`, `ETC1`, `ETC2`, `ASTC_4x4`, `RGBA8`,
|
|
46
|
+
`RGB565`. A `str` enum: `Target('etc2') is Target.ETC2`. `Target.X.info` is
|
|
47
|
+
its `TargetFormat`.
|
|
48
|
+
|
|
49
|
+
`TargetFormat`
|
|
50
|
+
: `basis` (the transcoder's format number), `block` (`(4, 4)`, or `(1, 1)` for
|
|
51
|
+
pixels), `bytes_per_block` (per pixel when uncompressed), `channels`,
|
|
52
|
+
`gl_internal_format`, `gl_srgb_internal_format`, `extensions`, `gl_version`,
|
|
53
|
+
`gles_version`, `srgb_extensions`, `gl_format`, `gl_type`. Properties
|
|
54
|
+
`compressed` and `has_alpha`. `internal_format(srgb=False)` returns the enum
|
|
55
|
+
to upload with. `available(extensions, version=(0, 0), es=False, srgb=False)`
|
|
56
|
+
returns whether a context with those extensions and that version provides
|
|
57
|
+
the format.
|
|
58
|
+
|
|
59
|
+
`preferences(codec, es=False, channels='rgba') -> tuple[Target, ...]`
|
|
60
|
+
: The order `choose_target` tries, ending with `RGBA8`.
|
|
61
|
+
|
|
62
|
+
`choose_target(extensions, codec=Codec.ETC1S, has_alpha=False, version=(0, 0), es=False, srgb=False, channels='rgba') -> Target`
|
|
63
|
+
: The first target in `preferences` the context provides, skipping formats
|
|
64
|
+
without alpha when `has_alpha` is set and formats without an sRGB enum when
|
|
65
|
+
`srgb` is set. Returns `RGBA8` when nothing compressed is available.
|
|
66
|
+
|
|
67
|
+
`Codec`
|
|
68
|
+
: An `IntEnum` of the transcoder's `basis_tex_format` values. `Codec.X.is_hdr`
|
|
69
|
+
is true for `UASTC_HDR_4x4`, `ASTC_HDR_6x6` and `UASTC_HDR_6x6`.
|
|
70
|
+
|
|
71
|
+
## Errors
|
|
72
|
+
|
|
73
|
+
`BasisuError(ValueError)`, and its subclass `UnsupportedError`.
|
|
74
|
+
|
|
75
|
+
## Versions
|
|
76
|
+
|
|
77
|
+
`__version__` is this package's version. `BASISU_VERSION` is the
|
|
78
|
+
basis_universal tag compiled in; `vendor/basis_universal/VERSION` has the
|
|
79
|
+
commit.
|
|
80
|
+
|
|
81
|
+
## Sanitizer run
|
|
82
|
+
|
|
83
|
+
The fuzz tests in `tests/test_errors.py` corrupt and truncate every fixture.
|
|
84
|
+
To run them against an AddressSanitizer and UndefinedBehaviorSanitizer build,
|
|
85
|
+
copy the project to a scratch directory, change the non-Windows `CXX_FLAGS`
|
|
86
|
+
and `C_FLAGS` in its `setup.py` to `-O1 -g -fsanitize=address,undefined
|
|
87
|
+
-fno-omit-frame-pointer`, then:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
LDFLAGS=-fsanitize=address,undefined python setup.py build_ext --inplace
|
|
91
|
+
LD_PRELOAD="$(gcc -print-file-name=libasan.so) $(gcc -print-file-name=libubsan.so)" \
|
|
92
|
+
ASAN_OPTIONS=detect_leaks=0 \
|
|
93
|
+
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1:suppressions=ubsan.supp \
|
|
94
|
+
PYTHONPATH=src python -m pytest -s tests -o addopts=
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`ubsan.supp` holds one line, `alignment:basisu_transcoder.cpp`. The UASTC
|
|
98
|
+
bit reader in the transcoder loads 16-bit words from odd addresses inside a
|
|
99
|
+
block, which x86-64 and arm64 execute; UBSan reports each such load. With that
|
|
100
|
+
suppression, 30,000 corrupted copies of each fixture run clean on basis_universal
|
|
101
|
+
`v2_50`.
|