Add ktx
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
build
|
||||
dist
|
||||
pyktx.egg-info
|
||||
venv
|
||||
*.iml
|
||||
.eggs
|
||||
__pycache__
|
||||
Release
|
||||
Debug
|
||||
|
||||
# Sphinx
|
||||
docs
|
||||
_build
|
||||
_templates
|
||||
@@ -0,0 +1,242 @@
|
||||
# Copyright 2025 The Khronos Group Inc.
|
||||
# Copyright 2023 Shukant Pal
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
option( KTX_PY_USE_VENV
|
||||
"Use a Python virtual environment. Needed for externally managed python installations."
|
||||
OFF
|
||||
)
|
||||
|
||||
find_package (Python3 COMPONENTS Interpreter)
|
||||
set(SOURCE_DIR ${CMAKE_SOURCE_DIR}/interface/python_binding)
|
||||
file(GLOB pyktx_py_src ${SOURCE_DIR}/pyktx/*.py)
|
||||
list(TRANSFORM pyktx_py_src REPLACE "${SOURCE_DIR}/pyktx/" "${KTX_BUILD_DIR}/interface/python_binding/docs/pyktx." OUTPUT_VARIABLE pyktx_py_rst_filenames)
|
||||
list(TRANSFORM pyktx_py_rst_filenames REPLACE ".py$" ".rst" OUTPUT_VARIABLE pyktx_py_rst)
|
||||
|
||||
set(PYTHON_EXECUTABLE_SYSTEM ${Python3_EXECUTABLE})
|
||||
if(DEFINED PYTHON AND NOT ${PYTHON} STREQUAL "")
|
||||
set(PYTHON_EXECUTABLE_SYSTEM ${PYTHON})
|
||||
message(STATUS "Override PYTHON with ${PYTHON}")
|
||||
endif()
|
||||
if (LINUX AND NOT Python3_FOUND)
|
||||
set(PYTHON_EXECUTABLE_SYSTEM python)
|
||||
message(STATUS "CMake failed to find python3. Will continue assuming it's on PATH")
|
||||
endif()
|
||||
if(KTX_PY_USE_VENV)
|
||||
set(PYTHON_VENV_DIR "${KTX_BUILD_DIR}/interface/python_binding")
|
||||
set(PYTHON_EXECUTABLE ${PYTHON_VENV_DIR}/bin/python3)
|
||||
message(STATUS "Using virtual environment for Python")
|
||||
else()
|
||||
set(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE_SYSTEM})
|
||||
endif()
|
||||
|
||||
# Convert Windows path to CMake path
|
||||
cmake_path(SET PYTHON_PATH ${PYTHON_EXECUTABLE})
|
||||
|
||||
set(LIBKTX_LIB_DIR ${KTX_BUILD_DIR}/$<CONFIG>)
|
||||
|
||||
if(KTX_PY_USE_VENV)
|
||||
add_custom_target( py-venv ALL
|
||||
COMMENT
|
||||
"Set up virtual environment for Python"
|
||||
)
|
||||
add_custom_command(
|
||||
TARGET py-venv
|
||||
PRE_BUILD
|
||||
COMMAND
|
||||
${PYTHON_EXECUTABLE_SYSTEM} -m venv ${PYTHON_VENV_DIR}
|
||||
COMMENT
|
||||
"Create virtual environment for Python"
|
||||
)
|
||||
endif()
|
||||
|
||||
add_custom_target( pyktx-deps ALL
|
||||
COMMENT
|
||||
"Python deps"
|
||||
)
|
||||
if(KTX_PY_USE_VENV)
|
||||
add_dependencies(pyktx-deps py-venv)
|
||||
endif()
|
||||
add_custom_command(
|
||||
TARGET pyktx-deps
|
||||
PRE_BUILD
|
||||
COMMAND
|
||||
${PYTHON_EXECUTABLE} -m pip install --no-warn-script-location -r ${SOURCE_DIR}/requirements.txt
|
||||
COMMENT
|
||||
"Install dependencies for pyktx build"
|
||||
)
|
||||
|
||||
add_custom_target( pyktx ALL
|
||||
DEPENDS
|
||||
ktx
|
||||
${pyktx_py_src}
|
||||
pyktx/ktx_texture.h
|
||||
pyktx/ktx_texture1.h
|
||||
pyktx/ktx_texture2.h
|
||||
pyktx/ktx_texture.c
|
||||
pyktx/ktx_texture1.c
|
||||
pyktx/ktx_texture2.c
|
||||
WORKING_DIRECTORY
|
||||
${SOURCE_DIR}
|
||||
COMMENT
|
||||
"Python distributions"
|
||||
)
|
||||
add_dependencies(pyktx pyktx-deps)
|
||||
|
||||
add_custom_command(
|
||||
TARGET pyktx
|
||||
PRE_BUILD
|
||||
COMMAND
|
||||
${PYTHON_EXECUTABLE} clean.py
|
||||
COMMENT
|
||||
"Clean up pyktx build artifacts"
|
||||
WORKING_DIRECTORY
|
||||
${SOURCE_DIR}
|
||||
)
|
||||
|
||||
# Normalize version number as the python toolchain does. Tweaks are reduced
|
||||
# to a, b or rc immediately following the patch number. We do because names
|
||||
# of the BYPRODUCTS in the following custom_command need to match the
|
||||
# names with normalized version numbers the python tools would produce.
|
||||
function(normalize_version _var fullver)
|
||||
string(REPLACE -alpha a normalized ${fullver})
|
||||
string(REPLACE -beta b normalized ${normalized})
|
||||
set(${_var} "${normalized}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
normalize_version(KTX_VERSION_NORMALIZED ${KTX_VERSION_FULL})
|
||||
set(DIST_DIR ${KTX_BUILD_DIR}/interface/python_binding/dist)
|
||||
set(SOURCE_ARCHIVE_BASENAME ${DIST_DIR}/pyktx-${KTX_VERSION_NORMALIZED})
|
||||
|
||||
add_custom_command(
|
||||
TARGET pyktx
|
||||
POST_BUILD
|
||||
BYPRODUCTS
|
||||
${SOURCE_ARCHIVE_BASENAME}.tar.gz
|
||||
${SOURCE_ARCHIVE_BASENAME}.zip
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E env
|
||||
LIBKTX_INCLUDE_DIR=${CMAKE_SOURCE_DIR}/include
|
||||
LIBKTX_LIB_DIR=${LIBKTX_LIB_DIR}
|
||||
LIBKTX_VERSION=${KTX_VERSION_NORMALIZED}
|
||||
# The build module by default builds in an isolated environment, i.e. it
|
||||
# requires a virtual env. I have not found a way via find_package to
|
||||
# ensure venv support is installed. This can be turned off with
|
||||
# `--no-isolation` but such builds have not been tested.
|
||||
${PYTHON_EXECUTABLE} -m build --sdist --outdir ${DIST_DIR}
|
||||
COMMENT
|
||||
"Build pyktx source package"
|
||||
WORKING_DIRECTORY
|
||||
${SOURCE_DIR}
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
TARGET pyktx
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E env
|
||||
LIBKTX_INCLUDE_DIR=${CMAKE_SOURCE_DIR}/include
|
||||
LIBKTX_LIB_DIR=${LIBKTX_LIB_DIR}
|
||||
LIBKTX_VERSION=${KTX_VERSION_NORMALIZED}
|
||||
# Ditto with sdist isolated environment comment.
|
||||
${PYTHON_EXECUTABLE} -m build --wheel --outdir ${DIST_DIR}
|
||||
COMMENT
|
||||
"Build pyktx wheel"
|
||||
WORKING_DIRECTORY
|
||||
${SOURCE_DIR}
|
||||
)
|
||||
|
||||
set(pyktx_egg_info
|
||||
${SOURCE_DIR}/pyktx.egg-info/dependency_links.txt
|
||||
${SOURCE_DIR}/pyktx.egg-info/PKG-INFO
|
||||
${SOURCE_DIR}/pyktx.egg-info/requires.txt
|
||||
${SOURCE_DIR}/pyktx.egg-info/SOURCES.txt
|
||||
${SOURCE_DIR}/pyktx.egg-info/top_level.txt)
|
||||
|
||||
add_test(NAME pyktx
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E env
|
||||
LIBKTX_INCLUDE_DIR=${CMAKE_SOURCE_DIR}/include
|
||||
LIBKTX_LIB_DIR=${LIBKTX_LIB_DIR}
|
||||
KTX_RUN_TESTS=ON
|
||||
DYLD_LIBRARY_PATH=${LIBKTX_LIB_DIR}:$ENV{DYLD_LIBRARY_PATH}
|
||||
LD_LIBRARY_PATH=${LIBKTX_LIB_DIR}:$ENV{LD_LIBRARY_PATH}
|
||||
${PYTHON_EXECUTABLE} buildscript.py
|
||||
WORKING_DIRECTORY
|
||||
${SOURCE_DIR}
|
||||
)
|
||||
|
||||
if(KTX_FEATURE_DOC)
|
||||
add_custom_target(pyktx.doc ALL
|
||||
DEPENDS
|
||||
pyktx-deps
|
||||
ktx # No way to avoid this autodoc needs compiled modules.
|
||||
COMMENT
|
||||
"Build Python documentation"
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
TARGET pyktx.doc
|
||||
POST_BUILD
|
||||
BYPRODUCTS
|
||||
${KTX_BUILD_DIR}/interface/python_binding/conf.py
|
||||
${KTX_BUILD_DIR}/interface/python_binding/index.rst
|
||||
${pyktx_py_rst}
|
||||
${KTX_BUILD_DIR}/interface/python_binding/docs/pyktx.rst
|
||||
${KTX_BUILD_DIR}/interface/python_binding/docs/pyktx.native.rst
|
||||
${pyktx_egg_info}
|
||||
COMMAND
|
||||
# This appears to build binaries of the native parts in the source pyktx
|
||||
# folder. Without the binaries, autodoc can't import the modules. It
|
||||
# prints warnings and fails to include any content in the documentation
|
||||
# for the native interfaces in the HTML files it creates.
|
||||
${CMAKE_COMMAND} -E env
|
||||
LIBKTX_INCLUDE_DIR=${CMAKE_SOURCE_DIR}/include
|
||||
LIBKTX_LIB_DIR=${LIBKTX_LIB_DIR}
|
||||
DYLD_LIBRARY_PATH=${LIBKTX_LIB_DIR}:$ENV{DYLD_LIBRARY_PATH}
|
||||
LD_LIBRARY_PATH=${LIBKTX_LIB_DIR}:$ENV{LD_LIBRARY_PATH}
|
||||
${PYTHON_EXECUTABLE} buildscript.py
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E copy
|
||||
index.rst conf.py ${KTX_BUILD_DIR}/interface/python_binding
|
||||
# Currently there are no static items to be included in the Sphinx generated
|
||||
# documentation. The infrastructure is here in case of future need. If
|
||||
# these command are removed, also remove html_static_path from conf.py.
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E make_directory
|
||||
${KTX_BUILD_DIR}/interface/python_binding/_static
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E copy_directory
|
||||
_static ${KTX_BUILD_DIR}/interface/python_binding/_static
|
||||
COMMAND
|
||||
# Sphinx Apidoc extracts docstrings from the source files and builds .rst files
|
||||
# in the docs directory of the python_binding section of the build directory.
|
||||
# `--separate` says to put the documentation for each module on its own page.
|
||||
${CMAKE_COMMAND} -E env
|
||||
LIBKTX_INCLUDE_DIR=${CMAKE_SOURCE_DIR}/include
|
||||
LIBKTX_LIB_DIR=${LIBKTX_LIB_DIR}
|
||||
${PYTHON_EXECUTABLE} -m sphinx.ext.apidoc -o ${KTX_BUILD_DIR}/interface/python_binding/docs ./pyktx --separate
|
||||
COMMAND
|
||||
# Sphinx uses autodoc to generate html files from the .rst files generated above.
|
||||
# These are expected to be in a docs subdirectory of the SOURCEDIR hence the
|
||||
# build directory appearing in SOURCEDIR. Sphinx invokes autodoc which wants to
|
||||
# import the modules hence libktx, must be built for this to fully succeed.
|
||||
#
|
||||
# Autodoc is configured by the copy of `conf.py` in the build directory (copied
|
||||
# there by one of the COMMANDs above). `conf.py` adds the python binding
|
||||
# section of the build directory to Python's `sys.path` so Autodoc can find the
|
||||
# modules. However they aren't there. The `pyktx` module is the directory
|
||||
# ${SOURCE_DIR}/pyktx and the .o files from compiling the native parts are
|
||||
# built there by buildscript.py when invoked above. ${SOURCE_DIR} appears in
|
||||
# Python's sys.path so autodoc finds them. There is nothing explicit in `conf.py`
|
||||
# to do this. It appears that Sphinx or autodoc itself adds its working directory
|
||||
# to `sys.path`.
|
||||
${CMAKE_COMMAND} -E env
|
||||
LIBKTX_INCLUDE_DIR=${CMAKE_SOURCE_DIR}/include
|
||||
LIBKTX_LIB_DIR=${LIBKTX_LIB_DIR}
|
||||
SPHINXBUILD=${PYTHON_PATH}\ -m\ sphinx
|
||||
make SOURCEDIR="${KTX_BUILD_DIR}/interface/python_binding" BUILDDIR="${KTX_BUILD_DIR}/interface/python_binding/docs/html/pyktx" html
|
||||
WORKING_DIRECTORY
|
||||
${SOURCE_DIR}
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,178 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2022 Shukant Pal
|
||||
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
include buildscript.py
|
||||
include pyktx/ktx_texture.c
|
||||
include pyktx/ktx_texture1.c
|
||||
include pyktx/ktx_texture2.c
|
||||
include pyktx/ktx_texture.h
|
||||
include pyktx/ktx_texture1.h
|
||||
include pyktx/ktx_texture2.h
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@@ -0,0 +1,35 @@
|
||||
Copyright (c) 2023, Shukant Pal and Contributors \
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Made with love by [Shukant Pal](https://www.shukantpal.com/about) on his way to learning Python.
|
||||
|
||||
# pyktx
|
||||
|
||||
This Python package provides a Pythonic interface to libktx. It uses CFFI to generate the C bindings.
|
||||
|
||||
## Usage
|
||||
|
||||
**You must have libktx installed on your system to use pyktx ordinarily. You can configure where libktx is installed using the `LIBKTX_INCLUDE_DIR` and `LIBKTX_LIB_DIR` environment variables too.**
|
||||
|
||||
To install libktx, download and run the appropriate installer from [our releases](https://github.com/KhronosGroup/KTX-Software/releases).
|
||||
|
||||
## Building
|
||||
|
||||
To build and test pyktx,
|
||||
|
||||
```bash
|
||||
# Set LIBKTX_INSTALL_DIR if you've installed libktx at the default system location.
|
||||
# Otherwise set LIBKTX_INCLUDE_DIR, LIBKTX_LIB_DIR to wherever you've built libktx.
|
||||
cd ${PROJECT_DIR}/interface/python_binding
|
||||
KTX_RUN_TESTS=ON python3 buildscript.py
|
||||
```
|
||||
|
||||
If you are on a POSIX system (macOS or Linux), make sure libktx is on your `DYLD_LIBRARY_PATH` and `LD_LIBRARY_PATH`.
|
||||
|
||||
> When building on macOS against a universal CPython binary, such as that installed with the Xcode command-line tools (/usr/bin/python3), ld will issue a warning
|
||||
>
|
||||
> ```
|
||||
> ld: warning: ignoring file '/usr/local/lib/libktx.4.3.0.dylib': found architecture 'arm64', required architecture 'x86_64'
|
||||
> ```
|
||||
>
|
||||
> 'arm64' and 'x86_64' may be reversed depending on the build machine architecture. This happens because libktx is not a universal binary so only supports the current platform architecture. The message can be ignored.
|
||||
@@ -0,0 +1,204 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from cffi import FFI
|
||||
import platform
|
||||
import sys
|
||||
import os
|
||||
import unittest
|
||||
|
||||
LIBKTX_INSTALL_DIR = os.getenv("LIBKTX_INSTALL_DIR")
|
||||
LIBKTX_INCLUDE_DIR = os.getenv("LIBKTX_INCLUDE_DIR")
|
||||
LIBKTX_LIB_DIR = os.getenv("LIBKTX_LIB_DIR")
|
||||
|
||||
if os.name == 'nt':
|
||||
if LIBKTX_INSTALL_DIR is None:
|
||||
LIBKTX_INSTALL_DIR = 'C:\\Program Files\\KTX-Software'
|
||||
if LIBKTX_INCLUDE_DIR is None:
|
||||
LIBKTX_INCLUDE_DIR = LIBKTX_INSTALL_DIR + '\\include'
|
||||
if LIBKTX_LIB_DIR is None:
|
||||
LIBKTX_LIB_DIR = LIBKTX_INSTALL_DIR + '\\lib'
|
||||
elif platform.system() == 'Darwin':
|
||||
if LIBKTX_INCLUDE_DIR is None:
|
||||
LIBKTX_INCLUDE_DIR = '/usr/local/include'
|
||||
if LIBKTX_LIB_DIR is None:
|
||||
LIBKTX_LIB_DIR = '/usr/local/lib'
|
||||
elif os.name == 'posix':
|
||||
if LIBKTX_INCLUDE_DIR is None:
|
||||
LIBKTX_INCLUDE_DIR = '/usr/include'
|
||||
if LIBKTX_LIB_DIR is None:
|
||||
LIBKTX_LIB_DIR = '/usr/lib'
|
||||
|
||||
ffibuilder = FFI()
|
||||
|
||||
ffibuilder.cdef(
|
||||
"""
|
||||
void free(void *ptr);
|
||||
|
||||
typedef struct ktxTexture ktxTexture;
|
||||
typedef struct ktxTextureCreateInfo ktxTextureCreateInfo;
|
||||
typedef struct ktxTexture1 ktxTexture1;
|
||||
typedef struct ktxHashList ktxHashList;
|
||||
typedef struct ktxHashListEntry ktxHashListEntry;
|
||||
|
||||
typedef struct {
|
||||
uint32_t error;
|
||||
ktxTexture* texture;
|
||||
} ktxTextureMixed;
|
||||
|
||||
typedef struct {
|
||||
void *bytes;
|
||||
size_t size;
|
||||
int error;
|
||||
} ktxWriteToMemory;
|
||||
|
||||
typedef struct {
|
||||
size_t offset;
|
||||
int error;
|
||||
} ktxImageOffset;
|
||||
|
||||
// Native library
|
||||
int ktxTexture_WriteToNamedFile(ktxTexture *, const char* const);
|
||||
uint32_t ktxTexture_GetElementSize(ktxTexture *);
|
||||
uint32_t ktxTexture_GetRowPitch(ktxTexture *, uint32_t level);
|
||||
size_t ktxTexture_GetImageSize(ktxTexture *, uint32_t level);
|
||||
size_t ktxTexture_GetDataSize(ktxTexture *);
|
||||
size_t ktxTexture_GetDataSizeUncompressed(ktxTexture *);
|
||||
uint8_t *ktxTexture_GetData(ktxTexture *);
|
||||
int ktxTexture_SetImageFromMemory(ktxTexture *,
|
||||
uint32_t level,
|
||||
uint32_t layer,
|
||||
uint32_t faceSlice,
|
||||
void *src,
|
||||
size_t srcSize);
|
||||
int ktxTexture2_TranscodeBasis(void *, int outputFormat, int transcodeFlags);
|
||||
int ktxTexture2_DeflateZstd(void *, uint32_t compressionLevel);
|
||||
uint32_t ktxTexture2_GetOETF(void *);
|
||||
bool ktxTexture2_GetPremultipliedAlpha(void *);
|
||||
bool ktxTexture2_NeedsTranscoding(void *);
|
||||
|
||||
int ktxHashList_AddKVPair(ktxHashList *, const char *key, unsigned int valueLen, const void *value);
|
||||
int ktxHashList_DeleteKVPair(ktxHashList *, const char *key);
|
||||
ktxHashListEntry *ktxHashList_Next(void *entry);
|
||||
|
||||
// Glue code
|
||||
ktxTextureMixed PY_ktxTexture_CreateFromNamedFile(const char* const filename,
|
||||
uint32_t create_flags);
|
||||
ktxWriteToMemory PY_ktxTexture_WriteToMemory(ktxTexture *);
|
||||
ktxImageOffset PY_ktxTexture_GetImageOffset(ktxTexture *,
|
||||
uint32_t level,
|
||||
uint32_t layer,
|
||||
uint32_t faceSlice);
|
||||
int PY_ktxTexture_get_classId(ktxTexture *);
|
||||
bool PY_ktxTexture_get_isArray(ktxTexture *);
|
||||
bool PY_ktxTexture_get_isCompressed(ktxTexture *);
|
||||
bool PY_ktxTexture_get_isCubemap(ktxTexture *);
|
||||
bool PY_ktxTexture_get_generateMipmaps(ktxTexture *);
|
||||
uint32_t PY_ktxTexture_get_baseWidth(ktxTexture *);
|
||||
uint32_t PY_ktxTexture_get_baseHeight(ktxTexture *);
|
||||
uint32_t PY_ktxTexture_get_baseDepth(ktxTexture *);
|
||||
uint32_t PY_ktxTexture_get_numDimensions(ktxTexture *);
|
||||
uint32_t PY_ktxTexture_get_numLevels(ktxTexture *);
|
||||
uint32_t PY_ktxTexture_get_numFaces(ktxTexture *);
|
||||
uint32_t PY_ktxTexture_get_kvDataLen(ktxTexture *);
|
||||
void *PY_ktxTexture_get_kvData(ktxTexture *);
|
||||
ktxHashListEntry *PY_ktxHashList_get_listHead(ktxHashList *list);
|
||||
ktxHashList *PY_ktxTexture_get_kvDataHead(ktxTexture *);
|
||||
ktxWriteToMemory PY_ktxHashList_FindValue(ktxHashList *, const char *key);
|
||||
ktxWriteToMemory PY_ktxHashListEntry_GetKey(ktxHashListEntry *);
|
||||
ktxWriteToMemory PY_ktxHashListEntry_GetValue(ktxHashListEntry *);
|
||||
|
||||
ktxTextureMixed PY_ktxTexture1_Create(uint32_t glInternalFormat,
|
||||
uint32_t vkFormat,
|
||||
uint32_t *pDfd,
|
||||
uint32_t baseWidth,
|
||||
uint32_t baseHeight,
|
||||
uint32_t baseDepth,
|
||||
uint32_t numDimensions,
|
||||
uint32_t numLevels,
|
||||
uint32_t numLayers,
|
||||
uint32_t numFaces,
|
||||
bool isArray,
|
||||
bool generateMipmaps,
|
||||
int storageAllocation);
|
||||
uint32_t PY_ktxTexture1_get_glFormat(void *);
|
||||
uint32_t PY_ktxTexture1_get_glInternalformat(void *);
|
||||
uint32_t PY_ktxTexture1_get_glBaseInternalformat(void *);
|
||||
uint32_t PY_ktxTexture1_get_glType(void *);
|
||||
|
||||
ktxTextureMixed PY_ktxTexture2_Create(uint32_t glInternalFormat,
|
||||
uint32_t vkFormat,
|
||||
uint32_t *pDfd,
|
||||
uint32_t baseWidth,
|
||||
uint32_t baseHeight,
|
||||
uint32_t baseDepth,
|
||||
uint32_t numDimensions,
|
||||
uint32_t numLevels,
|
||||
uint32_t numLayers,
|
||||
uint32_t numFaces,
|
||||
bool isArray,
|
||||
bool generateMipmaps,
|
||||
int storageAllocation);
|
||||
int PY_ktxTexture2_CompressAstcEx(void *texture,
|
||||
bool verbose,
|
||||
uint32_t threadCount,
|
||||
uint32_t blockDimension,
|
||||
uint32_t mode,
|
||||
uint32_t quality,
|
||||
bool normalMap,
|
||||
bool perceptual,
|
||||
char *inputSwizzle);
|
||||
int PY_ktxTexture2_CompressBasisEx(void *texture,
|
||||
bool uastc,
|
||||
bool verbose,
|
||||
bool noSSE,
|
||||
uint32_t threadCount,
|
||||
uint32_t compressionLevel,
|
||||
uint32_t qualityLevel,
|
||||
uint32_t maxEndpoints,
|
||||
float endpointRDOThreshold,
|
||||
uint32_t maxSelectors,
|
||||
float selectorRDOThreshold,
|
||||
char *inputSwizzle,
|
||||
bool normalMap,
|
||||
bool separateRGToRGB_A,
|
||||
bool preSwizzle,
|
||||
bool noEndpointRDO,
|
||||
bool noSelectorRDO,
|
||||
int uastcFlags,
|
||||
bool uastcRDO,
|
||||
float uastcRDOQualityScalar,
|
||||
uint32_t uastcRDODictSize,
|
||||
float uastcRDOMaxSmoothBlockErrorScale,
|
||||
float uastcRDOMaxSmoothBlockStdDev,
|
||||
bool uastcRDODontFavorSimplerModes,
|
||||
bool uastcRDONoMultithreading);
|
||||
uint32_t PY_ktxTexture2_get_vkFormat(void *);
|
||||
uint32_t PY_ktxTexture2_get_supercompressionScheme(void *);
|
||||
"""
|
||||
)
|
||||
|
||||
ffibuilder.set_source(
|
||||
"pyktx.native",
|
||||
"""
|
||||
#include <ktx.h>
|
||||
#include "ktx_texture.h"
|
||||
#include "ktx_texture1.h"
|
||||
#include "ktx_texture2.h"
|
||||
""",
|
||||
include_dirs=['pyktx']
|
||||
+ ([LIBKTX_INCLUDE_DIR] if LIBKTX_INCLUDE_DIR is not None else []),
|
||||
sources=['pyktx/ktx_texture.c', 'pyktx/ktx_texture1.c', 'pyktx/ktx_texture2.c'],
|
||||
libraries=['ktx'],
|
||||
library_dirs=([LIBKTX_LIB_DIR] if LIBKTX_LIB_DIR is not None else []),
|
||||
runtime_library_dirs=(([LIBKTX_LIB_DIR] if LIBKTX_LIB_DIR is not None else []) if os.name != 'nt' else None))
|
||||
|
||||
if __name__ == "__main__":
|
||||
ffibuilder.compile(verbose=True)
|
||||
|
||||
if 'KTX_RUN_TESTS' in os.environ and os.environ['KTX_RUN_TESTS'] == 'ON':
|
||||
suite = unittest.TestLoader().discover(os.path.join(os.path.dirname(__file__), 'tests'))
|
||||
result = unittest.TextTestRunner(verbosity=2).run(suite)
|
||||
|
||||
if not result.wasSuccessful():
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,17 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import os
|
||||
|
||||
for root, directories, files in os.walk('pyktx'):
|
||||
for file in files:
|
||||
if (file.endswith('.o') or
|
||||
file.endswith('.obj') or
|
||||
file.endswith('.lib') or
|
||||
file.endswith('.so') or
|
||||
file.endswith('.dylib') or
|
||||
file.endswith('.dll') or
|
||||
file.startswith('native')):
|
||||
path = os.path.join('pyktx', file)
|
||||
print(f"Deleting {path}")
|
||||
os.remove(path)
|
||||
@@ -0,0 +1,53 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# NOTA BENE
|
||||
# The compiled modules that autodoc is trying to load are in
|
||||
# <ktx-source>/interface/python_binding but when autodoc is run it is
|
||||
# given the build directory as its "source" directory so it uses the copy
|
||||
# of this file in the build directory. Therefore the following 3 lines add
|
||||
# the build directory to sys.path which does not help autodoc find the
|
||||
# modules. However something is adding <ktx-source>/interface/python_binding
|
||||
# to sys.path. My best guess is that sphinx/autodoc add its working
|
||||
# directory which is the source directory.
|
||||
current_dir = os.path.dirname(__file__)
|
||||
target_dir = os.path.abspath(os.path.join(current_dir, "."))
|
||||
sys.path.insert(0, target_dir)
|
||||
|
||||
#print("*******" + __file__ + "**********\n", file=sys.stderr)
|
||||
#print(sys.path, file=sys.stderr)
|
||||
|
||||
project = 'pyktx'
|
||||
copyright = '2025, Khronos Group, Inc. 2023, Shukant Pal'
|
||||
author = 'Shukant Pal, Mark Callow'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.napoleon',
|
||||
]
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_static_path = ['_static']
|
||||
@@ -0,0 +1,26 @@
|
||||
.. Copyright (c) 2023, Shukant Pal and Contributors
|
||||
.. SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
.. pyktx documentation master file, created by
|
||||
sphinx-quickstart on Mon Jul 31 20:45:16 2023.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to pyktx's documentation!
|
||||
=================================
|
||||
|
||||
Use your browser's back button to return to the other documentation.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
:glob:
|
||||
|
||||
docs/*
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
@@ -0,0 +1,38 @@
|
||||
:: Copyright (c) 2023, Shukant Pal and Contributors
|
||||
:: SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
|
||||
%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.https://www.sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
@@ -0,0 +1,7 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
native.*
|
||||
*.so
|
||||
*.o
|
||||
pyktx.egg-info
|
||||
@@ -0,0 +1,34 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import os
|
||||
|
||||
LIBKTX_INSTALL_DIR = os.getenv("LIBKTX_INSTALL_DIR")
|
||||
LIBKTX_LIB_DIR = os.getenv("LIBKTX_LIB_DIR")
|
||||
|
||||
if os.name == 'nt':
|
||||
if LIBKTX_INSTALL_DIR is None:
|
||||
LIBKTX_INSTALL_DIR = 'C:\\Program Files\\KTX-Software'
|
||||
if LIBKTX_LIB_DIR is None:
|
||||
LIBKTX_LIB_DIR = LIBKTX_INSTALL_DIR + '\\bin'
|
||||
os.add_dll_directory(os.path.normpath(LIBKTX_LIB_DIR))
|
||||
|
||||
from .gl_internalformat import *
|
||||
from .ktx_astc_params import *
|
||||
from .ktx_basis_params import *
|
||||
from .ktx_error_code import *
|
||||
from .ktx_hash_list import *
|
||||
from .ktx_pack_astc_block_dimension import *
|
||||
from .ktx_pack_astc_encoder_mode import *
|
||||
from .ktx_pack_astc_quality_levels import *
|
||||
from .ktx_pack_uastc_flag_bits import *
|
||||
from .ktx_supercmp_scheme import *
|
||||
from .ktx_texture import *
|
||||
from .ktx_texture1 import *
|
||||
from .ktx_texture2 import *
|
||||
from .ktx_texture_create_flag_bits import *
|
||||
from .ktx_texture_create_info import *
|
||||
from .ktx_texture_create_storage import *
|
||||
from .ktx_transcode_flag_bits import *
|
||||
from .ktx_transcode_fmt import *
|
||||
from .vk_format import *
|
||||
@@ -0,0 +1,104 @@
|
||||
# Copyright (c) 2021, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class GlInternalformat(IntEnum):
|
||||
"""OpenGL internalformat constants."""
|
||||
|
||||
R3_G3_B2 = 0x2A10
|
||||
RGB4 = 0x804F
|
||||
RGB5 = 0x8050
|
||||
RGB8 = 0x8051
|
||||
RGB10 = 0x8052
|
||||
RGB12 = 0x8053
|
||||
RGB16 = 0x8054
|
||||
RGBA2 = 0x8055
|
||||
RGBA4 = 0x8056
|
||||
RGB5_A1 = 0x8057
|
||||
RGBA8 = 0x8058
|
||||
RGB10_A2 = 0x8059
|
||||
RGBA12 = 0x805A
|
||||
RGBA16 = 0x805B
|
||||
|
||||
COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0
|
||||
COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1
|
||||
COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2
|
||||
COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3
|
||||
|
||||
SRGB_EXT = 0x8C40
|
||||
SRGB8_EXT = 0x8C41
|
||||
SRGB_ALPHA_EXT = 0x8C42
|
||||
SRGB8_ALPHA8_EXT = 0x8C43
|
||||
SLUMINANCE_ALPHA_EXT = 0x8C44
|
||||
SLUMINANCE8_ALPHA8_EXT = 0x8C45
|
||||
SLUMINANCE_EXT = 0x8C46
|
||||
SLUMINANCE8_EXT = 0x8C47
|
||||
COMPRESSED_SRGB_EXT = 0x8C48
|
||||
COMPRESSED_SRGB_ALPHA_EXT = 0x8C49
|
||||
COMPRESSED_SLUMINANCE_EXT = 0x8C4A
|
||||
COMPRESSED_SLUMINANCE_ALPHA_EXT = 0x8C4B
|
||||
COMPRESSED_SRGB_S3TC_DXT1_EXT = 0x8C4C
|
||||
COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D
|
||||
COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E
|
||||
COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F
|
||||
|
||||
COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00
|
||||
COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01
|
||||
COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02
|
||||
COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03
|
||||
|
||||
ATC_RGB_AMD = 0x8C92
|
||||
ATC_RGBA_EXPLICIT_ALPHA_AMD = 0x8C93
|
||||
ATC_RGBA_INTERPOLATED_ALPHA_AMD = 0x87EE
|
||||
|
||||
COMPRESSED_LUMINANCE_LATC1_EXT = 0x8C70
|
||||
COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT = 0x8C72
|
||||
COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT = 0x8C71
|
||||
COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT = 0x8C73
|
||||
|
||||
ETC1_RGB8_OES = 0x8D64
|
||||
|
||||
COMPRESSED_RGBA_BPTC_UNORM_EXT = 0x8E8C
|
||||
COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB = 0x8E8D
|
||||
COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB = 0x8E8E
|
||||
COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB = 0x8E8F
|
||||
|
||||
COMPRESSED_RGB8_ETC2 = 0x9274
|
||||
COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276
|
||||
COMPRESSED_RGBA8_ETC2_EAC = 0x9278
|
||||
COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279
|
||||
COMPRESSED_R11_EAC = 0x9270
|
||||
COMPRESSED_SIGNED_R11_EAC = 0x9271
|
||||
COMPRESSED_RG11_EAC = 0x9272
|
||||
COMPRESSED_SIGNED_RG11_EAC = 0x9273
|
||||
|
||||
COMPRESSED_RGBA_ASTC_4x4_KHR = 0x93B0
|
||||
COMPRESSED_RGBA_ASTC_5x4_KHR = 0x93B1
|
||||
COMPRESSED_RGBA_ASTC_5x5_KHR = 0x93B2
|
||||
COMPRESSED_RGBA_ASTC_6x5_KHR = 0x93B3
|
||||
COMPRESSED_RGBA_ASTC_6x6_KHR = 0x93B4
|
||||
COMPRESSED_RGBA_ASTC_8x5_KHR = 0x93B5
|
||||
COMPRESSED_RGBA_ASTC_8x6_KHR = 0x93B6
|
||||
COMPRESSED_RGBA_ASTC_8x8_KHR = 0x93B7
|
||||
COMPRESSED_RGBA_ASTC_10x5_KHR = 0x93B8
|
||||
COMPRESSED_RGBA_ASTC_10x6_KHR = 0x93B9
|
||||
COMPRESSED_RGBA_ASTC_10x8_KHR = 0x93BA
|
||||
COMPRESSED_RGBA_ASTC_10x10_KHR = 0x93BB
|
||||
COMPRESSED_RGBA_ASTC_12x10_KHR = 0x93BC
|
||||
COMPRESSED_RGBA_ASTC_12x12_KHR = 0x93BD
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR = 0x93D0
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR = 0x93D1
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR = 0x93D2
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR = 0x93D3
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR = 0x93D4
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR = 0x93D5
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR = 0x93D6
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR = 0x93D7
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR = 0x93D8
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR = 0x93D9
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR = 0x93DA
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR = 0x93DB
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR = 0x93DC
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR = 0x93DD
|
||||
@@ -0,0 +1,64 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from .ktx_pack_astc_block_dimension import KtxPackAstcBlockDimension
|
||||
from .ktx_pack_astc_encoder_mode import KtxPackAstcEncoderMode
|
||||
from .ktx_pack_astc_quality_levels import KtxPackAstcQualityLevels
|
||||
|
||||
swizzle_regex = re.compile('^[rgba01]{4}$')
|
||||
|
||||
|
||||
@dataclass
|
||||
class KtxAstcParams:
|
||||
"""Data for passing extended parameters to KtxTexture2.compress_astc()."""
|
||||
|
||||
verbose: bool = False
|
||||
"""If true, prints Astc encoder operation details to stdout. Not recommended for GUI apps."""
|
||||
|
||||
thread_count: int = 1
|
||||
"""Number of threads used for compression. Default is 1."""
|
||||
|
||||
block_dimension: KtxPackAstcBlockDimension = KtxPackAstcBlockDimension.D4x4
|
||||
"""Combinations of block dimensions that astcenc supports i.e. 6x6, 8x8, 6x5 etc"""
|
||||
|
||||
mode: KtxPackAstcEncoderMode = KtxPackAstcEncoderMode.DEFAULT
|
||||
"""Can be {ldr/hdr} from astcenc"""
|
||||
|
||||
quality_level: int = KtxPackAstcQualityLevels.FASTEST
|
||||
"""astcenc supports -fastest, -fast, -medium, -thorough, -exhaustive"""
|
||||
|
||||
normal_map: bool = False
|
||||
"""
|
||||
Tunes codec parameters for better quality on normal maps.
|
||||
|
||||
In this mode normals are compressed to X,Y components
|
||||
Discarding Z component, reader will need to generate Z
|
||||
component in shaders.
|
||||
"""
|
||||
|
||||
perceptual: bool = False
|
||||
"""
|
||||
The codec should optimize for perceptual error, instead of direct RMS error.
|
||||
|
||||
This aims to improves perceived image quality, but
|
||||
typically lowers the measured PSNR score. Perceptual methods are
|
||||
currently only available for normal maps and RGB color data.
|
||||
"""
|
||||
|
||||
input_swizzle: bytes = bytes([0, 0, 0, 0])
|
||||
"""
|
||||
A swizzle to provide as input to astcenc.
|
||||
|
||||
It must match the regular expression /^[rgba01]{4}$/.
|
||||
"""
|
||||
|
||||
def parse_swizzle(self, swizzle: str) -> None:
|
||||
if not swizzle_regex.match(swizzle):
|
||||
raise ValueError(swizzle
|
||||
+ " does not match the appropriate format "
|
||||
+ str(swizzle_regex)
|
||||
+ " for a swizzle")
|
||||
|
||||
self.input_swizzle = swizzle.encode('ascii')
|
||||
@@ -0,0 +1,172 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from dataclasses import dataclass
|
||||
from .ktx_pack_uastc_flag_bits import KtxPackUastcFlagBits
|
||||
|
||||
|
||||
@dataclass
|
||||
class KtxBasisParams:
|
||||
"""Data for passing extended params to KtxTexture2.compressBasis()."""
|
||||
|
||||
uastc: bool = False
|
||||
"""True to use UASTC base, false to use ETC1S base."""
|
||||
|
||||
verbose: bool = False
|
||||
"""If true, prints Basis Universal encoder operation details to stdout. Not recommended for GUI apps."""
|
||||
|
||||
no_sse: bool = False
|
||||
"""True to forbid use of the SSE instruction set. Ignored if CPU does not support SSE."""
|
||||
|
||||
thread_count: int = 1
|
||||
"""Number of threads used for compression. Default is 1."""
|
||||
|
||||
compression_level: int = 0
|
||||
"""
|
||||
Encoding speed vs. quality tradeoff. Range is [0,5].
|
||||
|
||||
Higher values are slower, but give higher quality. There is no default.
|
||||
Callers must explicitly set this value. Callers can use
|
||||
KTX_ETC1S_DEFAULT_COMPRESSION_LEVEL as a default value. Currently this is 2.
|
||||
"""
|
||||
|
||||
quality_level: int = 0
|
||||
"""
|
||||
Compression quality. Range is [1,255].
|
||||
|
||||
Lower gives better compression/lower quality/faster.
|
||||
Higher gives less compression/higher quality/slower.
|
||||
This automatically determines values for max_endpoints, max_selectors,
|
||||
endpoint_rdo_threshold, and selector_rdo_threshold for the target quality
|
||||
level. Setting these parameters overrides the values determined by quality_level
|
||||
which defaults to 128 if neither it nor both of max_endpoints and max_selectors
|
||||
have been set.
|
||||
|
||||
Both of max_endpoints and max_selectors must be set for them to have any effect.
|
||||
quality_level will only determine values for endpoint_rdo_threshold and selector_rdo_threshold
|
||||
when its value exceeds 128, otherwise their defaults will be used.
|
||||
"""
|
||||
|
||||
max_endpoints: int = 0
|
||||
"""
|
||||
Manually set the max number of color endpoint clusters.
|
||||
|
||||
Range is [1,16128]. Default is 0, unset. If this is set, max_selectors
|
||||
must also be set, otherwise the value will be ignored.
|
||||
"""
|
||||
|
||||
endpoint_rdo_threshold: int = 0
|
||||
"""
|
||||
Set endpoint RDO quality threshold. The default is 1.25.
|
||||
|
||||
Lower is higher quality but less quality per output bit (try [1.0,3.0].
|
||||
This will override the value chosen by quality_level.
|
||||
"""
|
||||
|
||||
max_selectors: int = 0
|
||||
"""
|
||||
Manually set the max number of color selector clusters. Range is [1,16128].
|
||||
|
||||
Default is 0, unset. If this is set, max_endpoints must also be set, otherwise
|
||||
the value will be ignored.
|
||||
"""
|
||||
|
||||
selector_rdo_threshold: int = 0
|
||||
"""
|
||||
Set selector RDO quality threshold. The default is 1.5.
|
||||
|
||||
Lower is higher quality but less quality per output bit (try [1.0,3.0]).
|
||||
This will override the value chosen by @c qualityLevel.
|
||||
"""
|
||||
|
||||
input_swizzle: bytes = bytes(4)
|
||||
"""
|
||||
A swizzle to apply before encoding.
|
||||
|
||||
It must match the regular expression /^[rgba01]{4}$/. If both this and
|
||||
pre_swizzle are specified KtxTexture2.compressBasis() will raise INVALID_OPERATION.
|
||||
"""
|
||||
|
||||
normal_map: bool = False
|
||||
"""
|
||||
Tunes codec parameters for better quality on normal maps (no
|
||||
selector RDO, no endpoint RDO) and sets the texture's DFD appropriately.
|
||||
|
||||
Only valid for linear textures.
|
||||
"""
|
||||
|
||||
pre_swizzle: bool = False
|
||||
"""
|
||||
If the texture has swizzle metadata, apply it before compressing.
|
||||
|
||||
Swizzling, like rabb may yield drastically different error metrics
|
||||
if done after supercompression.
|
||||
"""
|
||||
|
||||
separate_rg_to_rgb_a: bool = False
|
||||
"""
|
||||
This was and is a no-op.
|
||||
|
||||
2-component inputs have always been automatically separated
|
||||
using an "rrrg" input_swizzle.
|
||||
"""
|
||||
|
||||
no_endpoint_rdo: bool = False
|
||||
"""
|
||||
Disable endpoint rate distortion optimizations.
|
||||
|
||||
Slightly faster, less noisy output, but lower quality per output bit.
|
||||
"""
|
||||
|
||||
no_selector_rdo: bool = False
|
||||
"""
|
||||
Disable selector rate distortion optimizations.
|
||||
|
||||
Slightly faster, less noisy output, but lower quality per output bit.
|
||||
"""
|
||||
|
||||
uastc_flags: int = KtxPackUastcFlagBits.FASTEST
|
||||
"""
|
||||
A set of KtxPackUastcFlagBits controlling UASTC encoding.
|
||||
|
||||
The most important value is the level given in the
|
||||
least-significant 4 bits which selects a speed vs quality tradeoff.
|
||||
"""
|
||||
|
||||
uastc_rdo: bool = False
|
||||
"""Enable Rate Distortion Optimization (RDO) post-processing."""
|
||||
|
||||
uastc_rdo_quality_scalar: float = 0.
|
||||
"""
|
||||
UASTC RDO quality scalar (lambda).
|
||||
|
||||
Lower values yield higher quality/larger LZ compressed files, higher
|
||||
values yield lower quality/smaller LZ compressed files. A good range to
|
||||
try is [.2,4]. Full range is [.001,50.0]. Default is 1.0.
|
||||
"""
|
||||
|
||||
uastc_rdo_dict_size: int = 0
|
||||
"""
|
||||
UASTC RDO dictionary size in bytes. Default is 4096. Lower
|
||||
values=faster, but give less compression. Range is [64,65536].
|
||||
"""
|
||||
|
||||
uastc_rdo_max_smooth_block_error_scale: float = 10.
|
||||
"""
|
||||
UASTC RDO max smooth block error scale. Range is [1,300].
|
||||
Default is 10.0, 1.0 is disabled. Larger values suppress more
|
||||
artifacts (and allocate more bits) on smooth blocks.
|
||||
"""
|
||||
|
||||
uastc_rdo_max_smooth_block_std_dev: float = 18.
|
||||
"""
|
||||
UASTC RDO max smooth block standard deviation. Range is
|
||||
[.01,65536.0]. Default is 18.0. Larger values expand the range of
|
||||
blocks considered smooth.
|
||||
"""
|
||||
|
||||
uastc_rdo_dont_favor_simpler_modes: bool = False
|
||||
"""Do not favor simpler UASTC modes in RDO mode."""
|
||||
|
||||
uastc_rdo_no_multithreading: bool = False
|
||||
"""Disable RDO multithreading (slightly higher compression, deterministic)."""
|
||||
@@ -0,0 +1,91 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class KtxErrorCode(IntEnum):
|
||||
"""Error codes thrown by library functions."""
|
||||
|
||||
SUCCESS = 0
|
||||
"""Operation was successful."""
|
||||
|
||||
FILE_DATA_ERROR = 1
|
||||
"""The data in the file is inconsistent with the spec."""
|
||||
|
||||
FILE_ISPIPE = 2
|
||||
"""The file is a pipe or named pipe."""
|
||||
|
||||
FILE_OPEN_FAILED = 3
|
||||
"""The target file could not be opened."""
|
||||
|
||||
FILE_OVERFLOW = 4
|
||||
"""The operation would exceed the max file size."""
|
||||
|
||||
FILE_READ_ERROR = 5
|
||||
"""An error occurred while reading from the file."""
|
||||
|
||||
FILE_SEEK_ERROR = 6
|
||||
"""An error occurred while seeking in the file."""
|
||||
|
||||
FILE_UNEXPECTED_EOF = 7
|
||||
"""File does not have enough data to satisfy request."""
|
||||
|
||||
FILE_WRITE_ERROR = 8
|
||||
"""An error occurred while writing to the file."""
|
||||
|
||||
GL_ERROR = 9
|
||||
"""GL operations resulted in an error."""
|
||||
|
||||
INVALID_OPERATION = 10
|
||||
"""The operation is not allowed in the current state."""
|
||||
|
||||
INVALID_VALUE = 11
|
||||
"""A parameter value was not valid."""
|
||||
|
||||
NOT_FOUND = 12
|
||||
"""Requested key was not found"""
|
||||
|
||||
OUT_OF_MEMORY = 13
|
||||
"""Not enough memory to complete the operation."""
|
||||
|
||||
TRANSCODE_FAILED = 14
|
||||
"""Transcoding of block compressed texture failed."""
|
||||
|
||||
UNKNOWN_FILE_FORMAT = 15
|
||||
"""The file not a KTX file."""
|
||||
|
||||
UNSUPPORTED_TEXTURE_TYPE = 16
|
||||
"""The KTX file specifies an unsupported texture type."""
|
||||
|
||||
UNSUPPORTED_FEATURE = 17
|
||||
"""Feature not included in in-use library or not yet implemented."""
|
||||
|
||||
LIBRARY_NOT_LINKED = 18
|
||||
"""Library dependency (OpenGL or Vulkan) not linked into application."""
|
||||
|
||||
DECOMPRESS_LENGTH_ERROR = 19
|
||||
"""Decompressed byte count does not match expected byte size."""
|
||||
|
||||
DECOMPRESS_CHECKSUM_ERROR = 20
|
||||
"""Checksum mismatch when decompressing."""
|
||||
|
||||
ERROR_MAX_ENUM = LIBRARY_NOT_LINKED
|
||||
"""For safety checks."""
|
||||
|
||||
|
||||
class KtxError(Exception):
|
||||
"""Error thrown when native operation does not succeed."""
|
||||
|
||||
invocation: str
|
||||
"""The C library function called."""
|
||||
|
||||
code: KtxErrorCode
|
||||
"""The error code returned by libktx."""
|
||||
|
||||
def __init__(self, invocation: str, code: KtxErrorCode):
|
||||
self.invocation = invocation
|
||||
self.code = code
|
||||
|
||||
def __str__(self):
|
||||
return str(self.invocation) + " returned with " + str(self.code)
|
||||
@@ -0,0 +1,69 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from ctypes import c_buffer
|
||||
from .ktx_error_code import KtxErrorCode, KtxError
|
||||
from pyktx.native import ffi, lib
|
||||
from typing import Dict, Optional
|
||||
|
||||
|
||||
class KtxHashList:
|
||||
"""Opaque handle to a ktxHashList implemented in C."""
|
||||
|
||||
def __init__(self, ptr):
|
||||
self._ptr = ptr
|
||||
|
||||
def add_kv_pair(self, key: str, value: bytes) -> None:
|
||||
"""Add a key value pair to a hash list."""
|
||||
|
||||
error = lib.ktxHashList_AddKVPair(self._ptr, key.encode('ascii'), len(value), value)
|
||||
|
||||
if int(error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxHashList_AddKVPair', KtxErrorCode(error))
|
||||
|
||||
def delete_kv_pair(self, key: str) -> None:
|
||||
"""Delete a key value pair in a hash list."""
|
||||
|
||||
error = lib.ktxHashList_DeleteKVPair(self._ptr, key.encode('ascii'))
|
||||
|
||||
if int(error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxHashList_DeleteKVPair', KtxErrorCode(error))
|
||||
|
||||
def find_value(self, key: str) -> Optional[c_buffer]:
|
||||
"""Looks up a key in a hash list and returns the value."""
|
||||
|
||||
data = lib.PY_ktxHashList_FindValue(self._ptr, key.encode('ascii'))
|
||||
|
||||
if data.error == KtxErrorCode.NOT_FOUND:
|
||||
return None
|
||||
if data.error != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxHashList_FindValue', KtxErrorCode(data.error))
|
||||
|
||||
return ffi.buffer(data.bytes, data.size)
|
||||
|
||||
def copy(self) -> Dict[str, bytes]:
|
||||
"""Copy the hash list into a dict. This is recommended if you reading the hash list."""
|
||||
|
||||
kv_data = {}
|
||||
entry = lib.PY_ktxHashList_get_listHead(self._ptr)
|
||||
|
||||
while entry != ffi.NULL:
|
||||
key_data = lib.PY_ktxHashListEntry_GetKey(entry)
|
||||
value_data = lib.PY_ktxHashListEntry_GetValue(entry)
|
||||
|
||||
if int(key_data.error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxHashListEntry_GetKey', KtxErrorCode(key_data.error))
|
||||
if int(value_data.error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxHashListEntry_GetValue', KtxErrorCode(value_data.error))
|
||||
|
||||
key: str = ffi.buffer(key_data.bytes, key_data.size)[:].decode(encoding='utf-8')
|
||||
value = ffi.buffer(value_data.bytes, value_data.size)[:]
|
||||
|
||||
if key.endswith('\x00'):
|
||||
key = key[:-1]
|
||||
|
||||
kv_data[key] = value
|
||||
|
||||
entry = lib.ktxHashList_Next(entry)
|
||||
|
||||
return kv_data
|
||||
@@ -0,0 +1,85 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class KtxPackAstcBlockDimension(IntEnum):
|
||||
"""Options specifiying ASTC encoding block dimensions."""
|
||||
|
||||
D4x4 = 0
|
||||
"""8.00 bpp"""
|
||||
|
||||
D5x4 = 1
|
||||
"""6.40 bpp"""
|
||||
|
||||
D5x5 = 2
|
||||
"""5.12 bpp"""
|
||||
|
||||
D6x5 = 3
|
||||
"""4.27 bpp"""
|
||||
|
||||
D6x6 = 4
|
||||
"""3.56 bpp"""
|
||||
|
||||
D8x5 = 5
|
||||
"""3.20 bpp"""
|
||||
|
||||
D8x6 = 6
|
||||
"""2.67 bpp"""
|
||||
|
||||
D10x5 = 7
|
||||
"""2.56 bpp"""
|
||||
|
||||
D10x6 = 8
|
||||
"""2.13 bpp"""
|
||||
|
||||
D8x8 = 9
|
||||
"""2.00 bpp"""
|
||||
|
||||
D10x8 = 10
|
||||
"""1.60 bpp"""
|
||||
|
||||
D10x10 = 11
|
||||
"""1.28 bpp"""
|
||||
|
||||
D12x10 = 12
|
||||
"""1.07 bpp"""
|
||||
|
||||
D12x12 = 13
|
||||
"""0.89 bpp"""
|
||||
|
||||
|
||||
D3x3x3 = 14
|
||||
"""4.74 bpp"""
|
||||
|
||||
D4x3x3 = 15
|
||||
"""3.56 bpp"""
|
||||
|
||||
D4x4x3 = 16
|
||||
"""2.67 bpp"""
|
||||
|
||||
D4x4x4 = 17
|
||||
"""2.00 bpp"""
|
||||
|
||||
D5x4x4 = 18
|
||||
"""1.60 bpp"""
|
||||
|
||||
D5x5x4 = 19
|
||||
"""1.28 bpp"""
|
||||
|
||||
D5x5x5 = 20
|
||||
"""1.02 bpp"""
|
||||
|
||||
D6x5x5 = 21
|
||||
"""0.85 bpp"""
|
||||
|
||||
D6x6x5 = 22
|
||||
"""0.71 bpp"""
|
||||
|
||||
D6x6x6 = 23
|
||||
"""0.59 bpp"""
|
||||
|
||||
|
||||
DMAX = D6x6x6
|
||||
"Maximum supported blocks."
|
||||
@@ -0,0 +1,17 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class KtxPackAstcEncoderMode(IntEnum):
|
||||
"""
|
||||
Options specifying ASTC encoder profile mode.
|
||||
|
||||
This and function is used later to derive the profile.
|
||||
"""
|
||||
|
||||
DEFAULT = 0
|
||||
LDR = 1
|
||||
HDR = 2
|
||||
MAX = HDR
|
||||
@@ -0,0 +1,26 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class KtxPackAstcQualityLevels(IntEnum):
|
||||
"""Options specifiying ASTC encoding quality levels."""
|
||||
|
||||
FASTEST = 0
|
||||
"""Fastest compression."""
|
||||
|
||||
FAST = 10
|
||||
"""Fast compression."""
|
||||
|
||||
MEDIUM = 60
|
||||
"""Medium compression."""
|
||||
|
||||
THOROUGH = 98
|
||||
"""Slower compression."""
|
||||
|
||||
EXHAUSTIVE = 100
|
||||
"""Very slow compression."""
|
||||
|
||||
MAX = EXHAUSTIVE
|
||||
"""Maximum supported quality level."""
|
||||
@@ -0,0 +1,44 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class KtxPackUastcFlagBits(IntEnum):
|
||||
"""Flags specifiying UASTC encoding options."""
|
||||
|
||||
FASTEST = 0
|
||||
"""Fastest compression. 43.45dB."""
|
||||
|
||||
FASTER = 1
|
||||
"""Faster compression. 46.49dB."""
|
||||
|
||||
DEFAULT = 2
|
||||
"""Default compression. 47.47dB."""
|
||||
|
||||
SLOWER = 3
|
||||
"""Slower compression. 48.01dB."""
|
||||
|
||||
VERY_SLOW = 4
|
||||
"""Very slow compression. 48.24dB."""
|
||||
|
||||
MAX_LEVEL = VERY_SLOW
|
||||
"""Maximum supported quality level."""
|
||||
|
||||
LEVEL_MASK = 0xF
|
||||
"""Mask to extract the level from the other bits."""
|
||||
|
||||
FAVOR_UASTC_ERROR = 8
|
||||
"""Optimize for lowest UASTC error."""
|
||||
|
||||
FAVOR_BC7_ERROR = 16
|
||||
"""Optimize for lowest BC7 error."""
|
||||
|
||||
ETC1_FASTER_HINTS = 64
|
||||
"""Optimize for faster transcoding to ETC1."""
|
||||
|
||||
ETC1_FASTEST_HINTS = 128
|
||||
"""Optimize for fastest transcoding to ETC1."""
|
||||
|
||||
ETC1_DISABLE_FLIP_AND_INDIVIDUAL = 256
|
||||
"""Not documented in BasisU code."""
|
||||
@@ -0,0 +1,20 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class KtxSupercmpScheme(IntEnum):
|
||||
"""Enumerators identifying the supercompression scheme."""
|
||||
|
||||
NONE = 0
|
||||
"""No supercompression."""
|
||||
|
||||
BASIS_LZ = 1
|
||||
"""Basis LZ supercompression."""
|
||||
|
||||
ZSTD = 2
|
||||
"""ZStd supercompression."""
|
||||
|
||||
ZLIB = 3
|
||||
"""ZLIB supercompression."""
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Shukant Pal and Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <ktx.h>
|
||||
#include "ktx_texture.h"
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
ktxTextureMixed PY_ktxTexture_CreateFromNamedFile(const char* const filename, ktx_uint32_t createFlags)
|
||||
{
|
||||
ktxTextureMixed mixed;
|
||||
mixed.error = ktxTexture_CreateFromNamedFile(filename, createFlags, &mixed.texture);
|
||||
return mixed;
|
||||
}
|
||||
|
||||
ktxWriteToMemory PY_ktxTexture_WriteToMemory(ktxTexture *texture)
|
||||
{
|
||||
ktx_uint8_t *ppDstBytes = NULL;
|
||||
ktx_size_t pSize = 0;
|
||||
KTX_error_code error = ktxTexture_WriteToMemory(texture, &ppDstBytes, &pSize);
|
||||
|
||||
return (ktxWriteToMemory) {
|
||||
.bytes = ppDstBytes,
|
||||
.size = pSize,
|
||||
.error = error
|
||||
};
|
||||
}
|
||||
|
||||
ktxImageOffset PY_ktxTexture_GetImageOffset(ktxTexture *texture,
|
||||
ktx_uint32_t level,
|
||||
ktx_uint32_t layer,
|
||||
ktx_uint32_t faceSlice)
|
||||
{
|
||||
ktxImageOffset result;
|
||||
|
||||
result.error = ktxTexture_GetImageOffset(texture, level, layer, faceSlice, &result.offset);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ktxWriteToMemory PY_ktxHashList_FindValue(ktxHashList *list, const char *key)
|
||||
{
|
||||
unsigned int valueLen = 0;
|
||||
void *pValue = NULL;
|
||||
KTX_error_code err = ktxHashList_FindValue(list, key, &valueLen, &pValue);
|
||||
|
||||
return (ktxWriteToMemory) {
|
||||
.bytes = pValue,
|
||||
.size = valueLen,
|
||||
.error = err
|
||||
};
|
||||
}
|
||||
|
||||
ktxWriteToMemory PY_ktxHashListEntry_GetKey(ktxHashListEntry *entry)
|
||||
{
|
||||
unsigned int keyLen = 0;
|
||||
char *pKey = NULL;
|
||||
KTX_error_code err = ktxHashListEntry_GetKey(entry, &keyLen, &pKey);
|
||||
|
||||
return (ktxWriteToMemory) {
|
||||
.bytes = pKey,
|
||||
.size = keyLen,
|
||||
.error = err
|
||||
};
|
||||
}
|
||||
|
||||
ktxWriteToMemory PY_ktxHashListEntry_GetValue(ktxHashListEntry *entry)
|
||||
{
|
||||
unsigned int valueLen = 0;
|
||||
void *pValue = NULL;
|
||||
KTX_error_code err = ktxHashListEntry_GetValue(entry, &valueLen, &pValue);
|
||||
|
||||
return (ktxWriteToMemory) {
|
||||
.bytes = pValue,
|
||||
.size = valueLen,
|
||||
.error = err
|
||||
};
|
||||
}
|
||||
|
||||
KTX_IMPL(class_id, classId);
|
||||
KTX_IMPL(ktx_bool_t, isArray);
|
||||
KTX_IMPL(ktx_bool_t, isCompressed);
|
||||
KTX_IMPL(ktx_bool_t, isCubemap);
|
||||
KTX_IMPL(ktx_bool_t, generateMipmaps);
|
||||
KTX_IMPL(ktx_uint32_t, baseWidth);
|
||||
KTX_IMPL(ktx_uint32_t, baseHeight);
|
||||
KTX_IMPL(ktx_uint32_t, baseDepth);
|
||||
KTX_IMPL(ktx_uint32_t, numDimensions);
|
||||
KTX_IMPL(ktx_uint32_t, numLevels);
|
||||
KTX_IMPL(ktx_uint32_t, numFaces);
|
||||
KTX_IMPL(ktx_uint32_t, kvDataLen);
|
||||
KTX_IMPL(ktx_uint8_t *, kvData);
|
||||
|
||||
ktxHashList *PY_ktxTexture_get_kvDataHead(ktxTexture *texture)
|
||||
{
|
||||
return &texture->kvDataHead;
|
||||
}
|
||||
|
||||
ktxHashListEntry *PY_ktxHashList_get_listHead(ktxHashList *list)
|
||||
{
|
||||
return *list;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Shukant Pal and Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef KTX_TEXTURE_H_9E005417467F4F98A33ACF592FE1D6FE
|
||||
#define KTX_TEXTURE_H_9E005417467F4F98A33ACF592FE1D6FE
|
||||
|
||||
#include <ktx.h>
|
||||
|
||||
typedef struct {
|
||||
KTX_error_code error;
|
||||
ktxTexture *texture;
|
||||
} ktxTextureMixed;
|
||||
|
||||
typedef struct {
|
||||
void *bytes;
|
||||
ktx_size_t size;
|
||||
KTX_error_code error;
|
||||
} ktxWriteToMemory;
|
||||
|
||||
typedef struct {
|
||||
size_t offset;
|
||||
int error;
|
||||
} ktxImageOffset;
|
||||
|
||||
ktxTextureMixed PY_ktxTexture_CreateFromNamedFile(const char* const filename, ktx_uint32_t create_flags);
|
||||
ktxWriteToMemory PY_ktxTexture_WriteToMemory(ktxTexture *);
|
||||
ktxImageOffset PY_ktxTexture_GetImageOffset(ktxTexture *,
|
||||
ktx_uint32_t level,
|
||||
ktx_uint32_t layer,
|
||||
ktx_uint32_t faceSlice);
|
||||
ktxWriteToMemory PY_ktxHashList_FindValue(ktxHashList *, const char *key);
|
||||
ktxWriteToMemory PY_ktxHashListEntry_GetKey(ktxHashListEntry *);
|
||||
ktxWriteToMemory PY_ktxHashListEntry_GetValue(ktxHashListEntry *);
|
||||
|
||||
#define KTX_GETTER(type, prop) \
|
||||
type PY_ktxTexture_get_##prop(ktxTexture *texture)
|
||||
|
||||
KTX_GETTER(class_id, classId);
|
||||
KTX_GETTER(ktx_bool_t, isArray);
|
||||
KTX_GETTER(ktx_bool_t, isCompressed);
|
||||
KTX_GETTER(ktx_bool_t, isCubemap);
|
||||
KTX_GETTER(ktx_bool_t, generateMipmaps);
|
||||
KTX_GETTER(ktx_uint32_t, baseWidth);
|
||||
KTX_GETTER(ktx_uint32_t, baseHeight);
|
||||
KTX_GETTER(ktx_uint32_t, baseDepth);
|
||||
KTX_GETTER(ktx_uint32_t, numDimensions);
|
||||
KTX_GETTER(ktx_uint32_t, numLevels);
|
||||
KTX_GETTER(ktx_uint32_t, numFaces);
|
||||
KTX_GETTER(ktx_uint32_t, kvDataLen);
|
||||
KTX_GETTER(ktx_uint8_t *, kvData);
|
||||
KTX_GETTER(ktxHashList *, kvDataHead);
|
||||
ktxHashListEntry *PY_ktxHashList_get_listHead(ktxHashList *list);
|
||||
|
||||
#define KTX_IMPL(type, prop) \
|
||||
KTX_GETTER(type, prop) \
|
||||
{ \
|
||||
return texture->prop; \
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,230 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from ctypes import *
|
||||
from .ktx_error_code import KtxErrorCode, KtxError
|
||||
from .ktx_hash_list import KtxHashList
|
||||
from pyktx.native import ffi, lib
|
||||
from typing import Dict, Literal, Optional
|
||||
|
||||
|
||||
class KtxVersionMismatchError(Exception):
|
||||
"""Error thrown when reading a file with wrong KTX version."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class KtxTexture:
|
||||
"""
|
||||
Base class representing a texture.
|
||||
|
||||
ktxTextures should be created only by one of the static factory methods,
|
||||
and these fields should be considered read-only.
|
||||
"""
|
||||
|
||||
def __init__(self, ptr: c_uint64):
|
||||
self._ptr = ptr
|
||||
self.__kv_data_head = KtxHashList(lib.PY_ktxTexture_get_kvDataHead(ptr))
|
||||
|
||||
def __del__(self):
|
||||
lib.free(self._ptr)
|
||||
self._ptr = ffi.NULL
|
||||
|
||||
@property
|
||||
def class_id(self) -> int:
|
||||
"""Identify the class type. 1 for KtxTexture1, 2 for KtxTexture2."""
|
||||
|
||||
return lib.PY_ktxTexture_get_classId(self._ptr)
|
||||
|
||||
@property
|
||||
def is_array(self) -> bool:
|
||||
"""true if the texture is an array texture, i.e, a GL_TEXTURE_*_ARRAY target is to be used."""
|
||||
|
||||
return lib.PY_ktxTexture_get_isArray(self._ptr)
|
||||
|
||||
@property
|
||||
def is_compressed(self) -> bool:
|
||||
"""If the texture's format is a block compressed format."""
|
||||
|
||||
return lib.PY_ktxTexture_get_isCompressed(self._ptr)
|
||||
|
||||
@property
|
||||
def is_cubemap(self) -> bool:
|
||||
"""
|
||||
True if the texture is a cubemap or cubemap array.
|
||||
"""
|
||||
return lib.PY_ktxTexture_get_isCubemap(self._ptr)
|
||||
|
||||
@property
|
||||
def generate_mipmaps(self) -> bool:
|
||||
"""If mipmaps should be generated for the texture when uploading to graphics APIs."""
|
||||
|
||||
return lib.PY_ktxTexture_get_generateMipmaps(self._ptr)
|
||||
|
||||
@property
|
||||
def base_width(self) -> int:
|
||||
"""Width of the texture's base level."""
|
||||
|
||||
return lib.PY_ktxTexture_get_baseWidth(self._ptr)
|
||||
|
||||
@property
|
||||
def base_height(self) -> int:
|
||||
"""Height of the texture's base level."""
|
||||
|
||||
return lib.PY_ktxTexture_get_baseHeight(self._ptr)
|
||||
|
||||
@property
|
||||
def base_depth(self) -> int:
|
||||
"""Depth of the texture's base level."""
|
||||
|
||||
return lib.PY_ktxTexture_get_baseDepth(self._ptr)
|
||||
|
||||
@property
|
||||
def num_dimensions(self) -> int:
|
||||
"""Number of dimensions in the texture: 1, 2 or 3."""
|
||||
|
||||
return lib.PY_ktxTexture_get_numDimensions(self._ptr)
|
||||
|
||||
@property
|
||||
def num_levels(self) -> int:
|
||||
"""Number of mip levels in the texture."""
|
||||
|
||||
return lib.PY_ktxTexture_get_numLevels(self._ptr)
|
||||
|
||||
@property
|
||||
def num_faces(self) -> int:
|
||||
"""Number of faces: 6 for cube maps, 1 otherwise."""
|
||||
|
||||
return lib.PY_ktxTexture_get_numFaces(self._ptr)
|
||||
|
||||
@property
|
||||
def element_size(self) -> int:
|
||||
"""The element size of the texture's images."""
|
||||
|
||||
return lib.ktxTexture_GetElementSize(self._ptr)
|
||||
|
||||
@property
|
||||
def kv_data_raw(self) -> Optional[c_buffer]:
|
||||
"""
|
||||
The raw KV data buffer.
|
||||
|
||||
This is available only if RAW_KVDATA_BIT was used in create-flag bits.
|
||||
"""
|
||||
|
||||
buffer = lib.PY_ktxTexture_get_kvData(self._ptr)
|
||||
|
||||
if buffer == ffi.NULL:
|
||||
return None
|
||||
|
||||
return ffi.buffer(buffer, lib.PY_ktxTexture_get_kvDataLen(self._ptr))
|
||||
|
||||
@property
|
||||
def kv_data(self) -> KtxHashList:
|
||||
"""
|
||||
The metadata stored in the texture as a hash-list.
|
||||
|
||||
This is not available if SKIP_KVDATA_BIT was used in the create-flag bits.
|
||||
"""
|
||||
|
||||
return self.__kv_data_head
|
||||
|
||||
@property
|
||||
def data_size(self) -> int:
|
||||
"""The total size of the texture image data in bytes."""
|
||||
|
||||
return lib.ktxTexture_GetDataSize(self._ptr)
|
||||
|
||||
@property
|
||||
def data_size_uncompressed(self) -> int:
|
||||
"""Byte length of the texture's uncompressed image data."""
|
||||
|
||||
return lib.ktxTexture_GetDataSizeUncompressed(self._ptr)
|
||||
|
||||
def row_pitch(self, level: int) -> int:
|
||||
"""
|
||||
Return pitch between rows of a texture image level in bytes.
|
||||
|
||||
For uncompressed textures the pitch is the number of bytes between
|
||||
rows of texels. For compressed textures it is the number of bytes
|
||||
between rows of blocks. The value is padded to GL_UNPACK_ALIGNMENT,
|
||||
if necessary. For all currently known compressed formats padding will
|
||||
not be necessary.
|
||||
"""
|
||||
|
||||
return lib.ktxTexture_GetRowPitch(self._ptr, level)
|
||||
|
||||
def image_size(self, level: int) -> int:
|
||||
"""
|
||||
Calculate & return the size in bytes of an image at the specified mip level.
|
||||
|
||||
For arrays, this is the size of layer, for cubemaps, the size of a face and
|
||||
for 3D textures, the size of a depth slice.
|
||||
|
||||
The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT.
|
||||
"""
|
||||
|
||||
return lib.ktxTexture_GetImageSize(self._ptr, level)
|
||||
|
||||
def image_offset(self, level: int, layer: int, face_slice: int) -> int:
|
||||
"""
|
||||
Find the offset of an image within a ktxTexture's image data.
|
||||
|
||||
As there is no such thing as a 3D cubemap we make the 3rd location parameter
|
||||
do double duty.
|
||||
"""
|
||||
|
||||
data = lib.PY_ktxTexture_GetImageOffset(self._ptr, level, layer, face_slice)
|
||||
|
||||
if int(data.error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxTexture_GetImageOffset', KtxErrorCode(data.error))
|
||||
|
||||
return data.offset
|
||||
|
||||
def data(self) -> bytes:
|
||||
"""Return a buffer holding the texture image data."""
|
||||
|
||||
return ffi.buffer(lib.ktxTexture_GetData(self._ptr), self.data_size)
|
||||
|
||||
def set_image_from_memory(self, level: int, layer: int, face_slice: int, data: bytes):
|
||||
"""
|
||||
Set image for level, layer, faceSlice from an image in memory.
|
||||
|
||||
Uncompressed images in memory are expected to have their rows tightly packed
|
||||
as is the norm for most image file formats. The copied image is padded as
|
||||
necessary to achieve the KTX-specified row alignment. No padding is done if the
|
||||
ktxTexture's is_compressed field is true. Level, layer, face_slice rather than offset
|
||||
are specified to enable some validation.
|
||||
"""
|
||||
|
||||
error = KtxErrorCode(lib.ktxTexture_SetImageFromMemory(self._ptr, level, layer, face_slice, data, len(data)))
|
||||
|
||||
if int(error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxTexture_SetImageFromMemory', KtxErrorCode(int(error)))
|
||||
|
||||
def write_to_named_file(self, dst_name: str) -> None:
|
||||
"""Save this texture to a named file in KTX format."""
|
||||
|
||||
error = KtxErrorCode(lib.ktxTexture_WriteToNamedFile(self._ptr, dst_name.encode('ascii')))
|
||||
|
||||
if int(error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxTexture_WriteToNamedFile', KtxErrorCode(error))
|
||||
|
||||
def write_to_native_memory(self) -> Dict[Literal["bytes", "size"], int]:
|
||||
"""Write this KTX file to block of memory in KTX format. Recommended to use write_to_memory instead."""
|
||||
|
||||
data = lib.PY_ktxTexture_WriteToMemory(self._ptr)
|
||||
|
||||
if int(data.error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxTexture_WriteToMemory', KtxErrorCode(data.error))
|
||||
|
||||
return {"bytes": data.bytes, "size": data.size}
|
||||
|
||||
def write_to_memory(self) -> bytes:
|
||||
"""Write this KTX file to a buffer in KTX format."""
|
||||
|
||||
data = self.write_to_native_memory()
|
||||
native_buffer = ffi.buffer(data.get('bytes'), data.get('size'))
|
||||
buffer = native_buffer[:]
|
||||
|
||||
lib.free(data.get('bytes'))
|
||||
return buffer
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Shukant Pal and Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <ktx.h>
|
||||
#include "ktx_texture1.h"
|
||||
#include <stddef.h>
|
||||
|
||||
ktxTextureMixed PY_ktxTexture1_Create(ktx_uint32_t glInternalformat,
|
||||
ktx_uint32_t vkFormat,
|
||||
ktx_uint32_t *pDfd,
|
||||
ktx_uint32_t baseWidth,
|
||||
ktx_uint32_t baseHeight,
|
||||
ktx_uint32_t baseDepth,
|
||||
ktx_uint32_t numDimensions,
|
||||
ktx_uint32_t numLevels,
|
||||
ktx_uint32_t numLayers,
|
||||
ktx_uint32_t numFaces,
|
||||
ktx_bool_t isArray,
|
||||
ktx_bool_t generateMipmaps,
|
||||
ktxTextureCreateStorageEnum storageAllocation)
|
||||
{
|
||||
ktxTextureCreateInfo createInfo = {
|
||||
glInternalformat,
|
||||
vkFormat,
|
||||
pDfd,
|
||||
baseWidth,
|
||||
baseHeight,
|
||||
baseDepth,
|
||||
numDimensions,
|
||||
numLevels,
|
||||
numLayers,
|
||||
numFaces,
|
||||
isArray,
|
||||
generateMipmaps
|
||||
};
|
||||
|
||||
ktxTexture1* newTex = NULL;
|
||||
KTX_error_code err = ktxTexture1_Create(&createInfo,
|
||||
storageAllocation,
|
||||
&newTex);
|
||||
|
||||
return (ktxTextureMixed) {
|
||||
err,
|
||||
(ktxTexture*) newTex
|
||||
};
|
||||
}
|
||||
|
||||
KTX1_IMPL(ktx_uint32_t, glFormat)
|
||||
KTX1_IMPL(ktx_uint32_t, glInternalformat)
|
||||
KTX1_IMPL(ktx_uint32_t, glBaseInternalformat)
|
||||
KTX1_IMPL(ktx_uint32_t, glType)
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Shukant Pal and Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef KTX_TEXTURE1_H_9E005417467F4F98A33ACF592FE1D6FE
|
||||
#define KTX_TEXTURE1_H_9E005417467F4F98A33ACF592FE1D6FE
|
||||
|
||||
#include <ktx.h>
|
||||
#include "ktx_texture.h"
|
||||
|
||||
ktxTextureMixed PY_ktxTexture1_Create(ktx_uint32_t glInternalFormat,
|
||||
ktx_uint32_t vkFormat,
|
||||
ktx_uint32_t *pDfd,
|
||||
ktx_uint32_t baseWidth,
|
||||
ktx_uint32_t baseHeight,
|
||||
ktx_uint32_t baseDepth,
|
||||
ktx_uint32_t numDimensions,
|
||||
ktx_uint32_t numLevels,
|
||||
ktx_uint32_t numLayers,
|
||||
ktx_uint32_t numFaces,
|
||||
ktx_bool_t isArray,
|
||||
ktx_bool_t generateMipmaps,
|
||||
ktxTextureCreateStorageEnum storageAllocation);
|
||||
|
||||
#define KTX1_GETTER(type, prop) \
|
||||
type PY_ktxTexture1_get_##prop(ktxTexture1 *texture)
|
||||
|
||||
KTX1_GETTER(ktx_uint32_t, glFormat);
|
||||
KTX1_GETTER(ktx_uint32_t, glInternalformat);
|
||||
KTX1_GETTER(ktx_uint32_t, glBaseInternalformat);
|
||||
KTX1_GETTER(ktx_uint32_t, glType);
|
||||
|
||||
#define KTX1_IMPL(type, prop) \
|
||||
KTX1_GETTER(type, prop) \
|
||||
{ \
|
||||
return texture->prop; \
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,91 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from .gl_internalformat import GlInternalformat
|
||||
from .ktx_error_code import KtxErrorCode, KtxError
|
||||
from .ktx_texture import KtxTexture, KtxVersionMismatchError
|
||||
from .ktx_texture_create_flag_bits import KtxTextureCreateFlagBits
|
||||
from .ktx_texture_create_info import KtxTextureCreateInfo
|
||||
from .ktx_texture_create_storage import KtxTextureCreateStorage
|
||||
from pyktx.native import ffi, lib
|
||||
|
||||
|
||||
class KtxTexture1(KtxTexture):
|
||||
"""Class representing a KTX version 1 format texture."""
|
||||
|
||||
@staticmethod
|
||||
def create(create_info: KtxTextureCreateInfo, storage_allocation: KtxTextureCreateStorage) -> 'KtxTexture1':
|
||||
"""Create a new empty KtxTexture1."""
|
||||
|
||||
result = lib.PY_ktxTexture1_Create(create_info.gl_internal_format.value,
|
||||
create_info.vk_format.value,
|
||||
ffi.NULL,
|
||||
create_info.base_width,
|
||||
create_info.base_height,
|
||||
create_info.base_depth,
|
||||
create_info.num_dimensions,
|
||||
create_info.num_levels,
|
||||
create_info.num_layers,
|
||||
create_info.num_faces,
|
||||
create_info.is_array,
|
||||
create_info.generate_mipmaps,
|
||||
storage_allocation.value)
|
||||
|
||||
if int(result.error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxTexture1_Create', KtxErrorCode(result.error))
|
||||
|
||||
return KtxTexture1(result.texture)
|
||||
|
||||
@staticmethod
|
||||
def create_from_named_file(filename: str, create_flags: int = KtxTextureCreateFlagBits.NO_FLAGS) -> 'KtxTexture1':
|
||||
"""Create a KtxTexture1 from a named KTX file according to the file contents."""
|
||||
|
||||
result = lib.PY_ktxTexture_CreateFromNamedFile(filename.encode("ascii"), int(create_flags))
|
||||
|
||||
if int(result.error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxTexture1_CreateFromNamedFile', KtxErrorCode(result.error))
|
||||
|
||||
texture = KtxTexture1(result.texture)
|
||||
|
||||
if texture.class_id != 1:
|
||||
raise KtxVersionMismatchError('The provided file ' + filename + ' is not a KTX1 file')
|
||||
|
||||
return texture
|
||||
|
||||
@property
|
||||
def gl_format(self) -> int:
|
||||
"""
|
||||
Format of the texture data, e.g. GL_RGB.
|
||||
|
||||
You can find all OpenGL formats here: <https://registry.khronos.org/OpenGL/api/GL/glcorearb.h>
|
||||
"""
|
||||
return lib.PY_ktxTexture1_get_glFormat(self._ptr)
|
||||
|
||||
@property
|
||||
def gl_internalformat(self) -> int:
|
||||
"""
|
||||
Internal format of the texture data. See GlInternalformat.
|
||||
|
||||
You can find all OpenGL internal formats here: <https://registry.khronos.org/OpenGL/api/GL/glext.h>
|
||||
"""
|
||||
|
||||
return GlInternalformat(lib.PY_ktxTexture1_get_glInternalformat(self._ptr))
|
||||
|
||||
@property
|
||||
def gl_baseinternalformat(self) -> int:
|
||||
"""
|
||||
Base format of the texture data, e.g., GL_RGB.
|
||||
|
||||
You can find all OpenGL formats here: <https://registry.khronos.org/OpenGL/api/GL/glcorearb.h>
|
||||
"""
|
||||
|
||||
return lib.PY_ktxTexture1_get_glBaseInternalformat(self._ptr)
|
||||
|
||||
@property
|
||||
def gl_type(self) -> int:
|
||||
"""
|
||||
Type of the texture data, e.g, GL_UNSIGNED_BYTE.
|
||||
|
||||
You can find all OpenGL data types here: <https://registry.khronos.org/OpenGL/api/GL/glcorearb.h>
|
||||
"""
|
||||
return lib.PY_ktxTexture1_get_glType(self._ptr)
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Shukant Pal and Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <ktx.h>
|
||||
#include "ktx_texture2.h"
|
||||
#include <stddef.h>
|
||||
|
||||
ktxTextureMixed PY_ktxTexture2_Create(ktx_uint32_t glInternalformat,
|
||||
ktx_uint32_t vkFormat,
|
||||
ktx_uint32_t *pDfd,
|
||||
ktx_uint32_t baseWidth,
|
||||
ktx_uint32_t baseHeight,
|
||||
ktx_uint32_t baseDepth,
|
||||
ktx_uint32_t numDimensions,
|
||||
ktx_uint32_t numLevels,
|
||||
ktx_uint32_t numLayers,
|
||||
ktx_uint32_t numFaces,
|
||||
ktx_bool_t isArray,
|
||||
ktx_bool_t generateMipmaps,
|
||||
ktxTextureCreateStorageEnum storageAllocation)
|
||||
{
|
||||
ktxTextureCreateInfo createInfo = {
|
||||
glInternalformat,
|
||||
vkFormat,
|
||||
pDfd,
|
||||
baseWidth,
|
||||
baseHeight,
|
||||
baseDepth,
|
||||
numDimensions,
|
||||
numLevels,
|
||||
numLayers,
|
||||
numFaces,
|
||||
isArray,
|
||||
generateMipmaps
|
||||
};
|
||||
|
||||
ktxTexture2* newTex = NULL;
|
||||
KTX_error_code err = ktxTexture2_Create(&createInfo,
|
||||
storageAllocation,
|
||||
&newTex);
|
||||
|
||||
return (ktxTextureMixed) {
|
||||
err,
|
||||
(ktxTexture*) newTex
|
||||
};
|
||||
}
|
||||
|
||||
KTX_error_code PY_ktxTexture2_CompressAstcEx(ktxTexture2 *texture,
|
||||
ktx_bool_t verbose,
|
||||
ktx_uint32_t threadCount,
|
||||
ktx_uint32_t blockDimension,
|
||||
ktx_uint32_t mode,
|
||||
ktx_uint32_t qualityLevel,
|
||||
ktx_bool_t normalMap,
|
||||
ktx_bool_t perceptual,
|
||||
char *inputSwizzle)
|
||||
{
|
||||
ktxAstcParams params = {
|
||||
.structSize = sizeof(ktxAstcParams),
|
||||
.verbose = verbose,
|
||||
.threadCount = threadCount,
|
||||
.blockDimension = blockDimension,
|
||||
.mode = mode,
|
||||
.qualityLevel = qualityLevel,
|
||||
.normalMap = normalMap,
|
||||
.perceptual = perceptual,
|
||||
};
|
||||
|
||||
params.inputSwizzle[0] = inputSwizzle[0];
|
||||
params.inputSwizzle[1] = inputSwizzle[1];
|
||||
params.inputSwizzle[2] = inputSwizzle[2];
|
||||
params.inputSwizzle[3] = inputSwizzle[3];
|
||||
|
||||
KTX_error_code err = ktxTexture2_CompressAstcEx(texture, ¶ms);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
KTX_error_code PY_ktxTexture2_CompressBasisEx(ktxTexture2 *texture,
|
||||
ktx_bool_t uastc,
|
||||
ktx_bool_t verbose,
|
||||
ktx_bool_t noSSE,
|
||||
ktx_uint32_t threadCount,
|
||||
ktx_uint32_t compressionLevel,
|
||||
ktx_uint32_t qualityLevel,
|
||||
ktx_uint32_t maxEndpoints,
|
||||
float endpointRDOThreshold,
|
||||
ktx_uint32_t maxSelectors,
|
||||
float selectorRDOThreshold,
|
||||
char *inputSwizzle,
|
||||
ktx_bool_t normalMap,
|
||||
ktx_bool_t separateRGToRGB_A,
|
||||
ktx_bool_t preSwizzle,
|
||||
ktx_bool_t noEndpointRDO,
|
||||
ktx_bool_t noSelectorRDO,
|
||||
int uastcFlags,
|
||||
ktx_bool_t uastcRDO,
|
||||
float uastcRDOQualityScalar,
|
||||
ktx_uint32_t uastcRDODictSize,
|
||||
float uastcRDOMaxSmoothBlockErrorScale,
|
||||
float uastcRDOMaxSmoothBlockStdDev,
|
||||
ktx_bool_t uastcRDODontFavorSimplerModes,
|
||||
ktx_bool_t uastcRDONoMultithreading)
|
||||
{
|
||||
ktxBasisParams params = {
|
||||
.structSize = sizeof(ktxBasisParams),
|
||||
.uastc = uastc,
|
||||
.verbose = verbose,
|
||||
.noSSE = noSSE,
|
||||
.threadCount = threadCount,
|
||||
.compressionLevel = compressionLevel,
|
||||
.qualityLevel = qualityLevel,
|
||||
.maxEndpoints = maxEndpoints,
|
||||
.endpointRDOThreshold = endpointRDOThreshold,
|
||||
.maxSelectors = maxSelectors,
|
||||
.selectorRDOThreshold = selectorRDOThreshold,
|
||||
// inputSwizzle skipped here
|
||||
.normalMap = normalMap,
|
||||
.separateRGToRGB_A = separateRGToRGB_A,
|
||||
.preSwizzle = preSwizzle,
|
||||
.noEndpointRDO = noEndpointRDO,
|
||||
.noSelectorRDO = noSelectorRDO,
|
||||
.uastcFlags = uastcFlags,
|
||||
.uastcRDO = uastcRDO,
|
||||
.uastcRDOQualityScalar = uastcRDOQualityScalar,
|
||||
.uastcRDOMaxSmoothBlockErrorScale = uastcRDOMaxSmoothBlockErrorScale,
|
||||
.uastcRDOMaxSmoothBlockStdDev = uastcRDOMaxSmoothBlockStdDev,
|
||||
.uastcRDODontFavorSimplerModes = uastcRDODontFavorSimplerModes,
|
||||
.uastcRDONoMultithreading = uastcRDONoMultithreading
|
||||
};
|
||||
|
||||
params.inputSwizzle[0] = inputSwizzle[0];
|
||||
params.inputSwizzle[1] = inputSwizzle[1];
|
||||
params.inputSwizzle[2] = inputSwizzle[2];
|
||||
params.inputSwizzle[3] = inputSwizzle[3];
|
||||
|
||||
KTX_error_code err = ktxTexture2_CompressBasisEx(texture, ¶ms);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
KTX2_IMPL(ktx_uint32_t, vkFormat)
|
||||
KTX2_IMPL(ktx_uint32_t, supercompressionScheme)
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Shukant Pal and Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef KTX_TEXTURE2_H_9E005417467F4F98A33ACF592FE1D6FE
|
||||
#define KTX_TEXTURE2_H_9E005417467F4F98A33ACF592FE1D6FE
|
||||
|
||||
#include <ktx.h>
|
||||
#include "ktx_texture.h"
|
||||
|
||||
ktxTextureMixed PY_ktxTexture2_Create(ktx_uint32_t glInternalformat,
|
||||
ktx_uint32_t vkFormat,
|
||||
ktx_uint32_t *pDfd,
|
||||
ktx_uint32_t baseWidth,
|
||||
ktx_uint32_t baseHeight,
|
||||
ktx_uint32_t baseDepth,
|
||||
ktx_uint32_t numDimensions,
|
||||
ktx_uint32_t numLevels,
|
||||
ktx_uint32_t numLayers,
|
||||
ktx_uint32_t numFaces,
|
||||
ktx_bool_t isArray,
|
||||
ktx_bool_t generateMipmaps,
|
||||
ktxTextureCreateStorageEnum storageAllocation);
|
||||
|
||||
KTX_error_code PY_ktxTexture2_CompressAstcEx(ktxTexture2 *texture,
|
||||
ktx_bool_t verbose,
|
||||
ktx_uint32_t threadCount,
|
||||
ktx_uint32_t blockDimension,
|
||||
ktx_uint32_t mode,
|
||||
ktx_uint32_t quality,
|
||||
ktx_bool_t normalMap,
|
||||
ktx_bool_t perceptual,
|
||||
char *inputSwizzle);
|
||||
|
||||
KTX_error_code PY_ktxTexture2_CompressBasisEx(ktxTexture2 *texture,
|
||||
ktx_bool_t uastc,
|
||||
ktx_bool_t verbose,
|
||||
ktx_bool_t noSSE,
|
||||
ktx_uint32_t threadCount,
|
||||
ktx_uint32_t compressionLevel,
|
||||
ktx_uint32_t qualityLevel,
|
||||
ktx_uint32_t maxEndpoints,
|
||||
float endpointRDOThreshold,
|
||||
ktx_uint32_t maxSelectors,
|
||||
float selectorRDOThreshold,
|
||||
char *inputSwizzle,
|
||||
ktx_bool_t normalMap,
|
||||
ktx_bool_t separateRGToRGB_A,
|
||||
ktx_bool_t preSwizzle,
|
||||
ktx_bool_t noEndpointRDO,
|
||||
ktx_bool_t noSelectorRDO,
|
||||
int uastcFlags,
|
||||
ktx_bool_t uastcRDO,
|
||||
float uastcRDOQualityScalar,
|
||||
ktx_uint32_t uastcRDODictSize,
|
||||
float uastcRDOMaxSmoothBlockErrorScale,
|
||||
float uastcRDOMaxSmoothBlockStdDev,
|
||||
ktx_bool_t uastcRDODontFavorSimplerModes,
|
||||
ktx_bool_t uastcRDONoMultithreading);
|
||||
|
||||
#define KTX2_GETTER(type, prop) \
|
||||
type PY_ktxTexture2_get_##prop(ktxTexture2 *texture)
|
||||
|
||||
KTX2_GETTER(ktx_uint32_t, vkFormat);
|
||||
KTX2_GETTER(ktx_uint32_t, supercompressionScheme);
|
||||
|
||||
#define KTX2_IMPL(type, prop) \
|
||||
KTX2_GETTER(type, prop) \
|
||||
{ \
|
||||
return texture->prop; \
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,212 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from .ktx_astc_params import KtxAstcParams
|
||||
from .ktx_basis_params import KtxBasisParams
|
||||
from .ktx_error_code import KtxErrorCode, KtxError
|
||||
from .ktx_supercmp_scheme import KtxSupercmpScheme
|
||||
from .ktx_texture import KtxTexture, KtxVersionMismatchError
|
||||
from .ktx_texture_create_flag_bits import KtxTextureCreateFlagBits
|
||||
from .ktx_texture_create_info import KtxTextureCreateInfo
|
||||
from .ktx_texture_create_storage import KtxTextureCreateStorage
|
||||
from .ktx_transcode_fmt import KtxTranscodeFmt
|
||||
from pyktx.native import ffi, lib
|
||||
from typing import Union
|
||||
from .vk_format import VkFormat
|
||||
|
||||
|
||||
class KtxTexture2(KtxTexture):
|
||||
"""Class representing a KTX version 2 format texture."""
|
||||
|
||||
@staticmethod
|
||||
def create(create_info: KtxTextureCreateInfo, storage_allocation: KtxTextureCreateStorage) -> 'KtxTexture2':
|
||||
"""Create a new empty KtxTexture2."""
|
||||
|
||||
result = lib.PY_ktxTexture2_Create(0,
|
||||
create_info.vk_format.value,
|
||||
ffi.NULL,
|
||||
create_info.base_width,
|
||||
create_info.base_height,
|
||||
create_info.base_depth,
|
||||
create_info.num_dimensions,
|
||||
create_info.num_levels,
|
||||
create_info.num_layers,
|
||||
create_info.num_faces,
|
||||
create_info.is_array,
|
||||
create_info.generate_mipmaps,
|
||||
storage_allocation.value)
|
||||
|
||||
if int(result.error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxTexture2_Create', KtxErrorCode(result.error))
|
||||
|
||||
return KtxTexture2(result.texture)
|
||||
|
||||
@staticmethod
|
||||
def create_from_named_file(filename: str,
|
||||
create_flags: int = KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT) -> 'KtxTexture2':
|
||||
"""Create a KtxTexture2 from a named KTX file."""
|
||||
|
||||
result = lib.PY_ktxTexture_CreateFromNamedFile(filename.encode("ascii"), int(create_flags))
|
||||
|
||||
if int(result.error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxTexture2_CreateFromNamedFile', KtxErrorCode(result.error))
|
||||
|
||||
texture = KtxTexture2(result.texture)
|
||||
|
||||
if texture.class_id != 2:
|
||||
raise KtxVersionMismatchError('The provided file ' + filename + ' is not a KTX2 file')
|
||||
|
||||
return texture
|
||||
|
||||
@property
|
||||
def vk_format(self) -> VkFormat:
|
||||
"""VkFormat for texture."""
|
||||
|
||||
return VkFormat(lib.PY_ktxTexture2_get_vkFormat(self._ptr))
|
||||
|
||||
@property
|
||||
def supercompression_scheme(self) -> KtxSupercmpScheme:
|
||||
"""The supercompression scheme used to compress the texture data."""
|
||||
|
||||
return KtxSupercmpScheme(lib.PY_ktxTexture2_get_supercompressionScheme(self._ptr))
|
||||
|
||||
@property
|
||||
def oetf(self) -> int:
|
||||
"""The opto-electrical transfer function of the images."""
|
||||
|
||||
return lib.ktxTexture2_GetOETF(self._ptr)
|
||||
|
||||
@property
|
||||
def premultipled_alpha(self) -> bool:
|
||||
"""Whether the RGB components have been premultiplied by the alpha component."""
|
||||
|
||||
return lib.ktxTexture2_GetPremultipliedAlpha(self._ptr)
|
||||
|
||||
@property
|
||||
def needs_transcoding(self) -> bool:
|
||||
"""If the images are in a transcodable format."""
|
||||
|
||||
return lib.ktxTexture2_NeedsTranscoding(self._ptr)
|
||||
|
||||
def compress_astc(self, params: Union[int, KtxAstcParams]) -> None:
|
||||
"""
|
||||
Encode and compress a ktx texture with uncompressed images to ASTC.
|
||||
|
||||
The images are either encoded to ASTC block-compressed format. The
|
||||
encoded images replace the original images and the texture's fields
|
||||
including the dfd are modified to reflect the new state.
|
||||
|
||||
Such textures can be directly uploaded to a GPU via a graphics API.
|
||||
"""
|
||||
|
||||
if isinstance(params, int):
|
||||
quality = params
|
||||
params = KtxAstcParams()
|
||||
params.quality_level = quality
|
||||
|
||||
error = lib.PY_ktxTexture2_CompressAstcEx(self._ptr,
|
||||
params.verbose,
|
||||
params.thread_count,
|
||||
int(params.block_dimension),
|
||||
int(params.mode),
|
||||
params.quality_level,
|
||||
params.normal_map,
|
||||
params.perceptual,
|
||||
params.input_swizzle)
|
||||
|
||||
if int(error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxTexture2_compressAstcEx', KtxErrorCode(error))
|
||||
|
||||
def compress_basis(self, params: Union[int, KtxBasisParams]) -> None:
|
||||
"""
|
||||
Supercompress a KTX2 texture with uncompressed images.
|
||||
|
||||
The images are either encoded to ETC1S block-compressed format and supercompressed
|
||||
with Basis LZ or they are encoded to UASTC block-compressed format. UASTC format is
|
||||
selected by setting the uastc field of params to true. The encoded images replace
|
||||
the original images and the texture's fields including the DFD are modified to reflect
|
||||
the new state. Such textures must be transcoded to a desired target block compressed
|
||||
format before they can be uploaded to a GPU via a graphics API.
|
||||
"""
|
||||
|
||||
if isinstance(params, int):
|
||||
quality = params
|
||||
params = KtxBasisParams()
|
||||
params.quality_level = quality
|
||||
|
||||
error = lib.PY_ktxTexture2_CompressBasisEx(self._ptr,
|
||||
params.uastc,
|
||||
params.verbose,
|
||||
params.no_sse,
|
||||
params.thread_count,
|
||||
params.compression_level,
|
||||
params.quality_level,
|
||||
params.max_endpoints,
|
||||
params.endpoint_rdo_threshold,
|
||||
params.max_selectors,
|
||||
params.selector_rdo_threshold,
|
||||
params.input_swizzle,
|
||||
params.normal_map,
|
||||
params.pre_swizzle,
|
||||
params.separate_rg_to_rgb_a,
|
||||
params.no_endpoint_rdo,
|
||||
params.no_selector_rdo,
|
||||
params.uastc_flags,
|
||||
params.uastc_rdo,
|
||||
params.uastc_rdo_quality_scalar,
|
||||
params.uastc_rdo_dict_size,
|
||||
params.uastc_rdo_max_smooth_block_error_scale,
|
||||
params.uastc_rdo_max_smooth_block_std_dev,
|
||||
params.uastc_rdo_dont_favor_simpler_modes,
|
||||
params.uastc_rdo_no_multithreading)
|
||||
|
||||
if int(error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktxTexture2_CompressBasisEx', KtxErrorCode(error))
|
||||
|
||||
def deflate_zstd(self, compression_level: int) -> None:
|
||||
"""
|
||||
Deflate the data in a ktxTexture2 object using Zstandard.
|
||||
|
||||
The texture's level_index, data_size, dfd and supercompression_scheme will all
|
||||
be updated after successful deflation to reflect the deflated data.
|
||||
"""
|
||||
|
||||
if not 1 <= compression_level <= 22:
|
||||
raise ValueError("compression_level must be between 1 and 22")
|
||||
|
||||
error = lib.ktxTexture2_DeflateZstd(self._ptr, compression_level)
|
||||
|
||||
if int(error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktx2_DeflateZstd', KtxErrorCode(error))
|
||||
|
||||
def transcode_basis(self, output_format: KtxTranscodeFmt, transcode_flags: int = 0) -> None:
|
||||
"""
|
||||
Transcode a KTX2 texture with BasisLZ/ETC1S or UASTC images.
|
||||
|
||||
If the texture contains BasisLZ supercompressed images, inflates them from back to
|
||||
ETC1S then transcodes them to the specified block-compressed format. If the texture
|
||||
contains UASTC images, inflates them, if they have been supercompressed with zstd, then
|
||||
transcodes then to the specified format, The transcoded images replace the original images
|
||||
and the texture's fields including the dfd are modified to reflect the new format.
|
||||
|
||||
These types of textures must be transcoded to a desired target block-compressed format
|
||||
before they can be uploaded to a GPU via a graphics API.
|
||||
|
||||
The following block compressed transcode targets (KtxTranscodeFmt) are available: ETC1_RGB,
|
||||
ETC2_RGBA, BC1_RGB, BC3_RGBA, BC4_R, BC5_RG, BC7_RGBA, PVRTC1_4_RGB, PVRTC1_4_RGBA,
|
||||
PVRTC2_4_RGB, PVRTC2_4_RGBA, ASTC_4x4_RGBA, ETC2_EAC_R11, ETC2_EAC_RG11, ETC and BC1_OR_3.
|
||||
|
||||
ETC automatically selects between ETC1_RGB and ETC2_RGBA according to whether an alpha
|
||||
channel is available. BC1_OR_3 does likewise between BC1_RGB and BC3_RGBA. Note that if
|
||||
PVRTC1_4_RGBA or PVRTC2_4_RGBA is specified and there is no alpha channel PVRTC1_4_RGB
|
||||
or PVRTC2_4_RGB respectively will be selected.
|
||||
|
||||
Transcoding to ATC & FXT1 formats is not supported by libktx as there are no equivalent Vulkan formats.
|
||||
|
||||
The following uncompressed transcode targets are also available: RGBA32, RGB565, BGR565 and RGBA4444.
|
||||
"""
|
||||
|
||||
error = lib.ktxTexture2_TranscodeBasis(self._ptr, output_format, transcode_flags)
|
||||
|
||||
if int(error) != KtxErrorCode.SUCCESS:
|
||||
raise KtxError('ktx2_TranscodeBasis', KtxErrorCode(error))
|
||||
@@ -0,0 +1,19 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class KtxTextureCreateFlagBits(IntEnum):
|
||||
"""Flags for requesting services during creation."""
|
||||
|
||||
NO_FLAGS = 0x00
|
||||
|
||||
LOAD_IMAGE_DATA_BIT = 0x01
|
||||
"""Load the images from the KTX source."""
|
||||
|
||||
RAW_KVDATA_BIT = 0x02
|
||||
"""Load the raw key-value data instead of creating a KtxHashList from it."""
|
||||
|
||||
SKIP_KVDATA_BIT = 0x04
|
||||
"""Skip any key-value data. This overrides the RAW_KVDATA_BIT."""
|
||||
@@ -0,0 +1,45 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from dataclasses import dataclass
|
||||
from .gl_internalformat import GlInternalformat
|
||||
from typing import Optional
|
||||
from .vk_format import VkFormat
|
||||
|
||||
|
||||
@dataclass
|
||||
class KtxTextureCreateInfo:
|
||||
"""Data for passing texture information to KtxTexture1.create() and KtxTexture2.create()."""
|
||||
|
||||
gl_internal_format: Optional[GlInternalformat]
|
||||
"""Internal format for the texture, e.g., GlInteralformat.RGB8. Ignored when creating a KtxTexture2."""
|
||||
|
||||
base_width: int
|
||||
"""Width of the base level of the texture."""
|
||||
|
||||
base_height: int
|
||||
"""Height of the base level of the texture."""
|
||||
|
||||
base_depth: int
|
||||
"""Depth of the base level of the texture."""
|
||||
|
||||
vk_format: VkFormat = VkFormat.VK_FORMAT_UNDEFINED
|
||||
"""VkFormat for texture. Ignored when creating a KtxTexture1."""
|
||||
|
||||
num_dimensions: int = 2
|
||||
"""Number of dimensions in the texture, 1, 2 or 3."""
|
||||
|
||||
num_levels: int = 1
|
||||
"""Number of mip levels in the texture. Should be 1 if generateMipmaps is true."""
|
||||
|
||||
num_layers: int = 1
|
||||
"""Number of array layers in the texture."""
|
||||
|
||||
num_faces: int = 1
|
||||
"""Number of faces: 6 for cube maps, 1 otherwise."""
|
||||
|
||||
is_array: bool = False
|
||||
"""Set to true if the texture is to be an array texture. Means OpenGL will use a GL_TEXTURE_*_ARRAY target."""
|
||||
|
||||
generate_mipmaps: bool = False
|
||||
"""Set to true if mipmaps should be generated for the texture when loading into a 3D API."""
|
||||
@@ -0,0 +1,15 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class KtxTextureCreateStorage(IntEnum):
|
||||
"""Enum for requesting, or not, allocation of storage for images."""
|
||||
|
||||
NO = 0
|
||||
"""Don't allocate any image storage."""
|
||||
|
||||
ALLOC = 1
|
||||
"""Allocate image storage."""
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class KtxTranscodeFlagBits(IntEnum):
|
||||
"""Flags guiding transcoding of Basis Universal compressed textures."""
|
||||
|
||||
PVRTC_DECODE_TO_NEXT_POW2 = 2
|
||||
"""
|
||||
PVRTC1: decode non-pow2 ETC1S texture level to the next larger
|
||||
power of 2 (not implemented yet, but we're going to support it).
|
||||
Ignored if the slice's dimensions are already a power of 2.
|
||||
"""
|
||||
|
||||
TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS = 4
|
||||
"""
|
||||
When decoding to an opaque texture format, if the Basis data has
|
||||
alpha, decode the alpha slice instead of the color slice to the
|
||||
output texture format. Has no effect if there is no alpha data.
|
||||
"""
|
||||
|
||||
HIGH_QUALITY = 32
|
||||
"""
|
||||
Request higher quality transcode of UASTC to BC1, BC3, ETC2_EAC_R11 and
|
||||
ETC2_EAC_RG11. The flag is unused by other UASTC transcoders.
|
||||
"""
|
||||
@@ -0,0 +1,30 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class KtxTranscodeFmt(IntEnum):
|
||||
"""Enumerators for specifying the transcode target format."""
|
||||
|
||||
ETC1_RGB = 0
|
||||
ETC2_RGBA = 1
|
||||
BC1_RGB = 2
|
||||
BC3_RGBA = 3
|
||||
BC4_R = 4
|
||||
BC5_RG = 5
|
||||
BC7_RGBA = 6
|
||||
PVRTC1_4_RGB = 8
|
||||
PVRTC1_4_RGBA = 9
|
||||
ASTC_4x4_RGBA = 10
|
||||
PVRTC2_4_RGB = 18
|
||||
PVRTC2_4_RGBA = 19
|
||||
ETC2_EAC_R11 = 20
|
||||
ETC2_EAC_RG11 = 21
|
||||
RGBA32 = 13
|
||||
RGB565 = 14
|
||||
BGR565 = 15
|
||||
RGBA4444 = 16
|
||||
ETC = 22
|
||||
BC1_OR_3 = 23
|
||||
NO_SELECTION = 0x7fffffff
|
||||
@@ -0,0 +1,245 @@
|
||||
|
||||
#******************************* Do not edit. *****************************
|
||||
# Automatically generated from vulkan_core.h version 287 by mkvkformatfiles.
|
||||
#***************************************************************************
|
||||
|
||||
# Copyright 2015-2024 The Khronos Group Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
class VkFormat(IntEnum):
|
||||
"""Vulkan texture format constants."""
|
||||
|
||||
VK_FORMAT_UNDEFINED = 0
|
||||
VK_FORMAT_R4G4_UNORM_PACK8 = 1
|
||||
VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2
|
||||
VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3
|
||||
VK_FORMAT_R5G6B5_UNORM_PACK16 = 4
|
||||
VK_FORMAT_B5G6R5_UNORM_PACK16 = 5
|
||||
VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6
|
||||
VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7
|
||||
VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8
|
||||
VK_FORMAT_R8_UNORM = 9
|
||||
VK_FORMAT_R8_SNORM = 10
|
||||
VK_FORMAT_R8_UINT = 13
|
||||
VK_FORMAT_R8_SINT = 14
|
||||
VK_FORMAT_R8_SRGB = 15
|
||||
VK_FORMAT_R8G8_UNORM = 16
|
||||
VK_FORMAT_R8G8_SNORM = 17
|
||||
VK_FORMAT_R8G8_UINT = 20
|
||||
VK_FORMAT_R8G8_SINT = 21
|
||||
VK_FORMAT_R8G8_SRGB = 22
|
||||
VK_FORMAT_R8G8B8_UNORM = 23
|
||||
VK_FORMAT_R8G8B8_SNORM = 24
|
||||
VK_FORMAT_R8G8B8_UINT = 27
|
||||
VK_FORMAT_R8G8B8_SINT = 28
|
||||
VK_FORMAT_R8G8B8_SRGB = 29
|
||||
VK_FORMAT_B8G8R8_UNORM = 30
|
||||
VK_FORMAT_B8G8R8_SNORM = 31
|
||||
VK_FORMAT_B8G8R8_UINT = 34
|
||||
VK_FORMAT_B8G8R8_SINT = 35
|
||||
VK_FORMAT_B8G8R8_SRGB = 36
|
||||
VK_FORMAT_R8G8B8A8_UNORM = 37
|
||||
VK_FORMAT_R8G8B8A8_SNORM = 38
|
||||
VK_FORMAT_R8G8B8A8_UINT = 41
|
||||
VK_FORMAT_R8G8B8A8_SINT = 42
|
||||
VK_FORMAT_R8G8B8A8_SRGB = 43
|
||||
VK_FORMAT_B8G8R8A8_UNORM = 44
|
||||
VK_FORMAT_B8G8R8A8_SNORM = 45
|
||||
VK_FORMAT_B8G8R8A8_UINT = 48
|
||||
VK_FORMAT_B8G8R8A8_SINT = 49
|
||||
VK_FORMAT_B8G8R8A8_SRGB = 50
|
||||
VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51
|
||||
VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52
|
||||
VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55
|
||||
VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56
|
||||
VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57
|
||||
VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58
|
||||
VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59
|
||||
VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62
|
||||
VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63
|
||||
VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64
|
||||
VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65
|
||||
VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68
|
||||
VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69
|
||||
VK_FORMAT_R16_UNORM = 70
|
||||
VK_FORMAT_R16_SNORM = 71
|
||||
VK_FORMAT_R16_UINT = 74
|
||||
VK_FORMAT_R16_SINT = 75
|
||||
VK_FORMAT_R16_SFLOAT = 76
|
||||
VK_FORMAT_R16G16_UNORM = 77
|
||||
VK_FORMAT_R16G16_SNORM = 78
|
||||
VK_FORMAT_R16G16_UINT = 81
|
||||
VK_FORMAT_R16G16_SINT = 82
|
||||
VK_FORMAT_R16G16_SFLOAT = 83
|
||||
VK_FORMAT_R16G16B16_UNORM = 84
|
||||
VK_FORMAT_R16G16B16_SNORM = 85
|
||||
VK_FORMAT_R16G16B16_UINT = 88
|
||||
VK_FORMAT_R16G16B16_SINT = 89
|
||||
VK_FORMAT_R16G16B16_SFLOAT = 90
|
||||
VK_FORMAT_R16G16B16A16_UNORM = 91
|
||||
VK_FORMAT_R16G16B16A16_SNORM = 92
|
||||
VK_FORMAT_R16G16B16A16_UINT = 95
|
||||
VK_FORMAT_R16G16B16A16_SINT = 96
|
||||
VK_FORMAT_R16G16B16A16_SFLOAT = 97
|
||||
VK_FORMAT_R32_UINT = 98
|
||||
VK_FORMAT_R32_SINT = 99
|
||||
VK_FORMAT_R32_SFLOAT = 100
|
||||
VK_FORMAT_R32G32_UINT = 101
|
||||
VK_FORMAT_R32G32_SINT = 102
|
||||
VK_FORMAT_R32G32_SFLOAT = 103
|
||||
VK_FORMAT_R32G32B32_UINT = 104
|
||||
VK_FORMAT_R32G32B32_SINT = 105
|
||||
VK_FORMAT_R32G32B32_SFLOAT = 106
|
||||
VK_FORMAT_R32G32B32A32_UINT = 107
|
||||
VK_FORMAT_R32G32B32A32_SINT = 108
|
||||
VK_FORMAT_R32G32B32A32_SFLOAT = 109
|
||||
VK_FORMAT_R64_UINT = 110
|
||||
VK_FORMAT_R64_SINT = 111
|
||||
VK_FORMAT_R64_SFLOAT = 112
|
||||
VK_FORMAT_R64G64_UINT = 113
|
||||
VK_FORMAT_R64G64_SINT = 114
|
||||
VK_FORMAT_R64G64_SFLOAT = 115
|
||||
VK_FORMAT_R64G64B64_UINT = 116
|
||||
VK_FORMAT_R64G64B64_SINT = 117
|
||||
VK_FORMAT_R64G64B64_SFLOAT = 118
|
||||
VK_FORMAT_R64G64B64A64_UINT = 119
|
||||
VK_FORMAT_R64G64B64A64_SINT = 120
|
||||
VK_FORMAT_R64G64B64A64_SFLOAT = 121
|
||||
VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122
|
||||
VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123
|
||||
VK_FORMAT_D16_UNORM = 124
|
||||
VK_FORMAT_X8_D24_UNORM_PACK32 = 125
|
||||
VK_FORMAT_D32_SFLOAT = 126
|
||||
VK_FORMAT_S8_UINT = 127
|
||||
VK_FORMAT_D16_UNORM_S8_UINT = 128
|
||||
VK_FORMAT_D24_UNORM_S8_UINT = 129
|
||||
VK_FORMAT_D32_SFLOAT_S8_UINT = 130
|
||||
VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131
|
||||
VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132
|
||||
VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133
|
||||
VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134
|
||||
VK_FORMAT_BC2_UNORM_BLOCK = 135
|
||||
VK_FORMAT_BC2_SRGB_BLOCK = 136
|
||||
VK_FORMAT_BC3_UNORM_BLOCK = 137
|
||||
VK_FORMAT_BC3_SRGB_BLOCK = 138
|
||||
VK_FORMAT_BC4_UNORM_BLOCK = 139
|
||||
VK_FORMAT_BC4_SNORM_BLOCK = 140
|
||||
VK_FORMAT_BC5_UNORM_BLOCK = 141
|
||||
VK_FORMAT_BC5_SNORM_BLOCK = 142
|
||||
VK_FORMAT_BC6H_UFLOAT_BLOCK = 143
|
||||
VK_FORMAT_BC6H_SFLOAT_BLOCK = 144
|
||||
VK_FORMAT_BC7_UNORM_BLOCK = 145
|
||||
VK_FORMAT_BC7_SRGB_BLOCK = 146
|
||||
VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147
|
||||
VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148
|
||||
VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149
|
||||
VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150
|
||||
VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151
|
||||
VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152
|
||||
VK_FORMAT_EAC_R11_UNORM_BLOCK = 153
|
||||
VK_FORMAT_EAC_R11_SNORM_BLOCK = 154
|
||||
VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155
|
||||
VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156
|
||||
VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157
|
||||
VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158
|
||||
VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159
|
||||
VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160
|
||||
VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161
|
||||
VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162
|
||||
VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163
|
||||
VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164
|
||||
VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165
|
||||
VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166
|
||||
VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167
|
||||
VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168
|
||||
VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169
|
||||
VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170
|
||||
VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171
|
||||
VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172
|
||||
VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173
|
||||
VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174
|
||||
VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175
|
||||
VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176
|
||||
VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177
|
||||
VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178
|
||||
VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179
|
||||
VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180
|
||||
VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181
|
||||
VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182
|
||||
VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183
|
||||
VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184
|
||||
VK_FORMAT_G8B8G8R8_422_UNORM = 1000156000
|
||||
VK_FORMAT_B8G8R8G8_422_UNORM = 1000156001
|
||||
VK_FORMAT_R10X6_UNORM_PACK16 = 1000156007
|
||||
VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008
|
||||
VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 = 1000156009
|
||||
VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 = 1000156010
|
||||
VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 = 1000156011
|
||||
VK_FORMAT_R12X4_UNORM_PACK16 = 1000156017
|
||||
VK_FORMAT_R12X4G12X4_UNORM_2PACK16 = 1000156018
|
||||
VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 = 1000156019
|
||||
VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 = 1000156020
|
||||
VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 = 1000156021
|
||||
VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027
|
||||
VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028
|
||||
VK_FORMAT_A4R4G4B4_UNORM_PACK16 = 1000340000
|
||||
VK_FORMAT_A4B4G4R4_UNORM_PACK16 = 1000340001
|
||||
VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK = 1000066000
|
||||
VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK = 1000066001
|
||||
VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK = 1000066002
|
||||
VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK = 1000066003
|
||||
VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK = 1000066004
|
||||
VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK = 1000066005
|
||||
VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK = 1000066006
|
||||
VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK = 1000066007
|
||||
VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK = 1000066008
|
||||
VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK = 1000066009
|
||||
VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK = 1000066010
|
||||
VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK = 1000066011
|
||||
VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK = 1000066012
|
||||
VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK = 1000066013
|
||||
VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000
|
||||
VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001
|
||||
VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002
|
||||
VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003
|
||||
VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004
|
||||
VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005
|
||||
VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006
|
||||
VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007
|
||||
VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT = 1000288000
|
||||
VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT = 1000288001
|
||||
VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT = 1000288002
|
||||
VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT = 1000288003
|
||||
VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT = 1000288004
|
||||
VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT = 1000288005
|
||||
VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT = 1000288006
|
||||
VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT = 1000288007
|
||||
VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT = 1000288008
|
||||
VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT = 1000288009
|
||||
VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT = 1000288010
|
||||
VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT = 1000288011
|
||||
VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT = 1000288012
|
||||
VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT = 1000288013
|
||||
VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT = 1000288014
|
||||
VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT = 1000288015
|
||||
VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT = 1000288016
|
||||
VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT = 1000288017
|
||||
VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT = 1000288018
|
||||
VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT = 1000288019
|
||||
VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT = 1000288020
|
||||
VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT = 1000288021
|
||||
VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT = 1000288022
|
||||
VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT = 1000288023
|
||||
VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT = 1000288024
|
||||
VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT = 1000288025
|
||||
VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT = 1000288026
|
||||
VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT = 1000288027
|
||||
VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT = 1000288028
|
||||
VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT = 1000288029
|
||||
VK_FORMAT_R16G16_SFIXED5_NV = 1000464000
|
||||
VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR = 1000470000
|
||||
VK_FORMAT_A8_UNORM_KHR = 1000470001
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
build==1.0.3
|
||||
cffi==1.17.1
|
||||
setuptools==78.1.1
|
||||
wheel>=0.38.4
|
||||
sphinx==6.2.1
|
||||
sphinx-rtd-theme==1.2.2
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import os
|
||||
from setuptools import setup
|
||||
|
||||
__name__ = 'pyktx'
|
||||
__version__ = os.environ['LIBKTX_VERSION']
|
||||
|
||||
assert __version__ is not None
|
||||
|
||||
setup(
|
||||
name=__name__,
|
||||
version=__version__,
|
||||
description='A Python interface to the libktx library',
|
||||
author='Shukant Pal, Mark Callow',
|
||||
author_email='ktx-sw-maintainer@khronosgroup.org',
|
||||
cffi_modules=["buildscript.py:ffibuilder"],
|
||||
classifiers=[
|
||||
"Programming Language :: Python :: 3",
|
||||
],
|
||||
include_package_data=True,
|
||||
install_requires=["cffi>=1.15.1"],
|
||||
license="Apache 2.0",
|
||||
license_files=['LICENSE'],
|
||||
long_description_content_type="text/markdown",
|
||||
long_description="This Python package provides a Pythonic interface to libktx. It uses CFFI to generate the C bindings.",
|
||||
packages=['pyktx'],
|
||||
package_dir={'pyktx': 'pyktx'},
|
||||
setup_requires=["cffi>=1.15.1"],
|
||||
url='https://github.com/KhronosGroup/KTX-Software',
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
__test_images__ = os.environ['KTX_IMAGES_DIR'] \
|
||||
if 'KTX_IMAGES_DIR' in os.environ \
|
||||
else str((Path(__file__) / Path('../../../../tests/testimages')).resolve())
|
||||
@@ -0,0 +1,26 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from pyktx import *
|
||||
from test_config import __test_images__
|
||||
import unittest
|
||||
|
||||
|
||||
class TestKtxTexture(unittest.TestCase):
|
||||
def test_kv_data(self):
|
||||
test_ktx_file = os.path.join(__test_images__, 'astc_ldr_4x4_FlightHelmet_baseColor.ktx2')
|
||||
texture = KtxTexture2.create_from_named_file(test_ktx_file)
|
||||
|
||||
self.assertEqual(texture.kv_data.find_value('KTXorientation'), b'rd\x00')
|
||||
self.assertEqual(texture.kv_data.copy(), {
|
||||
'KTXorientation': b'rd\x00',
|
||||
'KTXwriter': b'toktx v4.0.__default__ / libktx v4.0.__default__\x00',
|
||||
'KTXwriterScParams': b'--encode astc --astc_blk_d 4x4\x00',
|
||||
})
|
||||
|
||||
for key in ['KTXorientation', 'KTXwriter', 'KTXwriterScParams']:
|
||||
texture.kv_data.delete_kv_pair(key)
|
||||
texture.kv_data.add_kv_pair('KTXwriter', b'pyktx v4.0.__default__ / libktx v4.0.__default__\x00')
|
||||
|
||||
self.assertEqual(texture.kv_data.find_value('KTXwriter'),
|
||||
b'pyktx v4.0.__default__ / libktx v4.0.__default__\x00')
|
||||
@@ -0,0 +1,60 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from pyktx import *
|
||||
from tempfile import NamedTemporaryFile
|
||||
from test_config import __test_images__
|
||||
import os
|
||||
import unittest
|
||||
|
||||
|
||||
class TestKtxTexture1(unittest.TestCase):
|
||||
def test_create_from_named_file(self):
|
||||
test_ktx_file = os.path.join(__test_images__, 'etc1.ktx')
|
||||
texture = KtxTexture1.create_from_named_file(test_ktx_file)
|
||||
|
||||
self.assertEqual(texture.gl_internalformat, GlInternalformat.ETC1_RGB8_OES)
|
||||
self.assertFalse(texture.is_array)
|
||||
self.assertFalse(texture.generate_mipmaps)
|
||||
self.assertEqual(texture.num_levels, 1)
|
||||
|
||||
def test_write_to_named_file(self):
|
||||
test_ktx_file = os.path.join(__test_images__, 'etc2-rgb.ktx')
|
||||
copy_file = NamedTemporaryFile(delete=False)
|
||||
copy_file.close()
|
||||
|
||||
texture = KtxTexture1.create_from_named_file(test_ktx_file, KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT)
|
||||
texture.write_to_named_file(copy_file.name)
|
||||
|
||||
with open(test_ktx_file, 'rb') as original, open(copy_file.name, 'rb') as copy:
|
||||
self.assertEqual(original.read(), copy.read())
|
||||
|
||||
os.unlink(copy_file.name)
|
||||
|
||||
def test_write_to_memory(self):
|
||||
test_ktx_file = os.path.join(__test_images__, 'etc2-rgba1.ktx')
|
||||
texture = KtxTexture1.create_from_named_file(test_ktx_file, KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT)
|
||||
texture_bytes = texture.write_to_memory()
|
||||
|
||||
with open(test_ktx_file, 'rb') as original:
|
||||
self.assertEqual(original.read(), texture_bytes)
|
||||
|
||||
def test_get_data(self):
|
||||
test_ktx_file = os.path.join(__test_images__, 'etc2-rgba1.ktx')
|
||||
texture = KtxTexture1.create_from_named_file(test_ktx_file, KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT)
|
||||
|
||||
data = texture.data()
|
||||
|
||||
with open(test_ktx_file, 'rb') as file:
|
||||
level_0_size = texture.image_size(0)
|
||||
self.assertEqual(file.read()[-level_0_size:], data[:])
|
||||
|
||||
def test_create(self):
|
||||
info = KtxTextureCreateInfo(
|
||||
gl_internal_format=GlInternalformat.COMPRESSED_RGBA_ASTC_4x4_KHR,
|
||||
base_width=10,
|
||||
base_height=10,
|
||||
base_depth=1)
|
||||
|
||||
texture = KtxTexture1.create(info, KtxTextureCreateStorage.ALLOC)
|
||||
texture.set_image_from_memory(0, 0, 0, bytes(texture.data_size))
|
||||
@@ -0,0 +1,104 @@
|
||||
# Copyright (c) 2023, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from pyktx import *
|
||||
from test_config import __test_images__
|
||||
import unittest
|
||||
|
||||
|
||||
class TestKtxTexture2(unittest.TestCase):
|
||||
def test_create_from_named_file(self):
|
||||
test_ktx_file = os.path.join(__test_images__, 'astc_ldr_4x4_FlightHelmet_baseColor.ktx2')
|
||||
texture = KtxTexture2.create_from_named_file(test_ktx_file)
|
||||
|
||||
self.assertEqual(texture.num_levels, 1)
|
||||
self.assertEqual(texture.num_faces, 1)
|
||||
self.assertEqual(texture.vk_format, VkFormat.VK_FORMAT_ASTC_4x4_SRGB_BLOCK)
|
||||
self.assertEqual(texture.base_width, 2048)
|
||||
self.assertEqual(texture.base_height, 2048)
|
||||
self.assertEqual(texture.supercompression_scheme, KtxSupercmpScheme.NONE)
|
||||
|
||||
def test_create_from_named_file_mipmapped(self):
|
||||
test_ktx_file = os.path.join(__test_images__, 'astc_mipmap_ldr_4x4_posx.ktx2')
|
||||
texture = KtxTexture2.create_from_named_file(test_ktx_file, KtxTextureCreateFlagBits.NO_FLAGS)
|
||||
|
||||
self.assertEqual(texture.num_levels, 12)
|
||||
self.assertEqual(texture.base_width, 2048)
|
||||
self.assertEqual(texture.base_height, 2048)
|
||||
|
||||
def test_get_image_size(self):
|
||||
test_ktx_file = os.path.join(__test_images__, 'astc_mipmap_ldr_4x4_posx.ktx2')
|
||||
texture = KtxTexture2.create_from_named_file(test_ktx_file)
|
||||
|
||||
self.assertEqual(texture.image_size(0), 4194304)
|
||||
|
||||
def test_get_image_offset(self):
|
||||
test_ktx_file = os.path.join(__test_images__, 'astc_mipmap_ldr_4x4_posx.ktx2')
|
||||
texture = KtxTexture2.create_from_named_file(test_ktx_file)
|
||||
|
||||
level11_offset = texture.image_offset(11, 0, 0)
|
||||
level0_offset = texture.image_offset(0, 0, 0)
|
||||
|
||||
self.assertEqual(level11_offset, 0)
|
||||
self.assertEqual(level0_offset - level11_offset, 0x155790 - 0x220)
|
||||
|
||||
def test_get_size(self):
|
||||
test_ktx_file = os.path.join(__test_images__, 'astc_mipmap_ldr_4x4_posx.ktx2')
|
||||
texture = KtxTexture2.create_from_named_file(test_ktx_file)
|
||||
|
||||
self.assertEqual(texture.num_levels, 12)
|
||||
|
||||
data_size = texture.data_size
|
||||
total_size = 0
|
||||
|
||||
for i in range(0, 12):
|
||||
total_size += texture.image_size(i)
|
||||
|
||||
self.assertEqual(total_size, data_size)
|
||||
|
||||
data = texture.data()
|
||||
self.assertEqual(len(data), data_size)
|
||||
|
||||
def test_get_data(self):
|
||||
test_ktx_file = os.path.join(__test_images__, 'astc_mipmap_ldr_4x4_posx.ktx2')
|
||||
texture = KtxTexture2.create_from_named_file(test_ktx_file)
|
||||
|
||||
self.assertEqual(texture.num_levels, 12)
|
||||
|
||||
level0_length = texture.image_size(0)
|
||||
|
||||
with open(test_ktx_file, 'rb') as file:
|
||||
data = texture.data()
|
||||
self.assertEqual(file.read()[-level0_length - 1:], data[-level0_length - 1:])
|
||||
|
||||
def test_compress_basis(self):
|
||||
test_ktx_file = os.path.join(__test_images__, 'arraytex_7_mipmap_reference_u.ktx2')
|
||||
texture = KtxTexture2.create_from_named_file(test_ktx_file, KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT)
|
||||
|
||||
self.assertEqual(texture.is_compressed, False)
|
||||
self.assertEqual(texture.supercompression_scheme, KtxSupercmpScheme.NONE)
|
||||
|
||||
texture.compress_basis(KtxBasisParams(quality_level=1))
|
||||
|
||||
self.assertEqual(texture.is_compressed, True)
|
||||
self.assertEqual(texture.supercompression_scheme, KtxSupercmpScheme.BASIS_LZ)
|
||||
|
||||
def test_transcode_basis(self):
|
||||
test_ktx_file = os.path.join(__test_images__, 'color_grid_basis.ktx2')
|
||||
texture = KtxTexture2.create_from_named_file(test_ktx_file, KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT)
|
||||
|
||||
texture.transcode_basis(KtxTranscodeFmt.ASTC_4x4_RGBA, 0)
|
||||
|
||||
self.assertEqual(texture.vk_format, VkFormat.VK_FORMAT_ASTC_4x4_SRGB_BLOCK)
|
||||
|
||||
def test_create(self):
|
||||
info = KtxTextureCreateInfo(
|
||||
gl_internal_format=None, # ignored
|
||||
base_width=10,
|
||||
base_height=10,
|
||||
base_depth=1,
|
||||
vk_format=VkFormat.VK_FORMAT_ASTC_4x4_SRGB_BLOCK)
|
||||
|
||||
texture = KtxTexture2.create(info, KtxTextureCreateStorage.ALLOC)
|
||||
self.assertEqual(texture.vk_format, VkFormat.VK_FORMAT_ASTC_4x4_SRGB_BLOCK)
|
||||
texture.set_image_from_memory(0, 0, 0, bytes(texture.data_size))
|
||||
Reference in New Issue
Block a user