Compare commits
1 Commits
dependabot
...
scikit-bui
Author | SHA1 | Date | |
---|---|---|---|
1aa08a0cda |
@ -1,8 +0,0 @@
|
||||
# git-blame ignored revisions
|
||||
# To configure, run
|
||||
# git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
# Requires Git > 2.23
|
||||
# See https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-fileltfilegt
|
||||
|
||||
# Migrate code style to Black
|
||||
cb84f32edab717389d03a3855aa5bd4d0db1ae3c
|
10
.github/dependabot.yml
vendored
@ -1,10 +0,0 @@
|
||||
# Set update schedule for GitHub Actions
|
||||
version: 2
|
||||
updates:
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
target-branch: "dev"
|
||||
schedule:
|
||||
# Check for updates to GitHub Actions every weekday
|
||||
interval: "daily"
|
73
.github/workflows/python-package.yml
vendored
@ -11,14 +11,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# Whether to checkout submodules: `true` to checkout submodules or `recursive` to
|
||||
# recursively checkout submodules.
|
||||
submodules: 'true'
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4.5.0
|
||||
with:
|
||||
python-version: '3.x'
|
||||
uses: actions/setup-python@v2
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@ -40,67 +40,80 @@ jobs:
|
||||
run: python -m twine check dist/*
|
||||
|
||||
- name: Upload SDist
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: dist/*.tar.gz
|
||||
|
||||
build-wheels:
|
||||
name: Build Wheels on ${{ matrix.os }} ${{ matrix.arch[0] }}
|
||||
name: Build Wheels for ${{ matrix.os }}-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ macos-11, windows-latest, ubuntu-latest ]
|
||||
arch: [ ['x86', 'x86_64', 'AMD64', 'x86_64' ] ] #[suffix, mac, windows, linux] arch names
|
||||
os: [ macos-latest, windows-latest, ubuntu-latest ]
|
||||
arch: [ x86_64 ]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
arch: [ 'ARM', 'arm64', 'ARM64', 'aarch64' ]
|
||||
- arch: arm64
|
||||
os: macos-latest
|
||||
- arch: aarch64
|
||||
os: ubuntu-latest
|
||||
qemu: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# Whether to checkout submodules: `true` to checkout submodules or `recursive` to
|
||||
# recursively checkout submodules.
|
||||
submodules: 'true'
|
||||
|
||||
- name: Install libomp
|
||||
if: runner.os == 'macOS'
|
||||
# openMP isnt part of core apple clang for some reason?
|
||||
# libomp is in homebrew, which works for end users but its not a fat binary
|
||||
# so we have to install it manually
|
||||
# compiled dylibs courtesy of https://mac.r-project.org/openmp/ and mirrored on my own server
|
||||
run: |
|
||||
wget https://pileof.rocks/openmp-13.0.0-darwin21-Release.tar.gz
|
||||
sudo tar fvxz openmp-*.tar.gz -C /
|
||||
run: brew install libomp
|
||||
|
||||
- name: Install QEMU
|
||||
# install QEMU if building for arm linux
|
||||
uses: docker/setup-qemu-action@v2
|
||||
if: runner.os == 'linux' && matrix.arch[3] == 'aarch64'
|
||||
uses: docker/setup-qemu-action@v1
|
||||
if: matrix.qemu
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Install test images
|
||||
run: git clone https://git.pileof.rocks/drewcassidy/quicktex-test-images.git tests/images
|
||||
|
||||
- name: Build wheels
|
||||
uses: pypa/cibuildwheel@v2.11.2
|
||||
uses: joerick/cibuildwheel@v2.3.1
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: "10.15"
|
||||
CIBW_ARCHS_LINUX: ${{ matrix.arch[3] }}
|
||||
CIBW_BUILD: "cp{37,38,39,310}-*"
|
||||
CIBW_ARCHS_MACOS: ${{ matrix.arch }}
|
||||
CIBW_ARCHS_LINUX: ${{ matrix.arch }}
|
||||
CIBW_ARCHS_WINDOWS: "auto64"
|
||||
CIBW_MANYLINUX_X86_64_IMAGE: "manylinux2014"
|
||||
CIBW_MANYLINUX_AARCH64_IMAGE: "manylinux2014"
|
||||
CIBW_TEST_EXTRAS: "tests"
|
||||
CIBW_TEST_COMMAND: nosetests {project}/tests -d
|
||||
CIBW_TEST_SKIP: "*-macosx_arm64"
|
||||
|
||||
- name: Upload Wheels
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
publish:
|
||||
name: Publish to PyPI and Github
|
||||
needs: [ build-wheels, build-sdist ]
|
||||
needs: [build-wheels, build-sdist]
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3 # just need the changelog
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
# Whether to checkout submodules: `true` to checkout submodules or `recursive` to
|
||||
# recursively checkout submodules.
|
||||
submodules: 'true'
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4.5.0
|
||||
with:
|
||||
python-version: '3.x'
|
||||
uses: actions/setup-python@v2
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@ -113,7 +126,7 @@ jobs:
|
||||
echo "$(yaclog show -mb)" >> RELEASE.md
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: artifact
|
||||
path: dist
|
||||
|
6
.gitignore
vendored
@ -5,8 +5,6 @@ build/
|
||||
*.egg-info
|
||||
*.pyc
|
||||
*.pyi
|
||||
*.whl
|
||||
*.tar.gz
|
||||
|
||||
#sphinx
|
||||
docs/_build/
|
||||
@ -14,6 +12,9 @@ docs/_build/
|
||||
#mypy
|
||||
out
|
||||
|
||||
# Test images
|
||||
tests/images/
|
||||
|
||||
# IDEs
|
||||
**/.idea
|
||||
|
||||
@ -33,3 +34,4 @@ compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
cmake-build-*
|
||||
_skbuild
|
||||
|
4
.gitmodules
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
[submodule "extern/pybind11"]
|
||||
path = extern/pybind11
|
||||
url = https://github.com/pybind/pybind11.git
|
||||
branch = stable
|
@ -1,28 +0,0 @@
|
||||
# .readthedocs.yaml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the version of Python and other tools you might need
|
||||
build:
|
||||
os: ubuntu-20.04
|
||||
tools:
|
||||
python: "3.10"
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
|
||||
# If using Sphinx, optionally build your docs in additional formats such as PDF
|
||||
formats:
|
||||
- pdf
|
||||
|
||||
# Give python build instructions
|
||||
python:
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
extra_requirements:
|
||||
- docs
|
20
CHANGELOG.md
@ -2,26 +2,6 @@
|
||||
|
||||
All notable changes to this project will be documented in this file
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated Pybind11 to version 3.10, adding Python 3.11 support
|
||||
|
||||
|
||||
## 0.1.3 - 2022-04-13
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed quicktex not compiling for python 3.10 on Windows
|
||||
|
||||
### Changed
|
||||
|
||||
- Reworked CI job, adding wheels for ARM macOS, ARM Linux, and x86 musl Linux.
|
||||
- Added wheels for python 3.10
|
||||
- Added a more useful error message when importing quicktex on macOS when libomp.dylib isn't installed
|
||||
|
||||
|
||||
## 0.1.2 - 2022-03-27
|
||||
|
||||
### Fixed
|
||||
|
@ -1,11 +1,23 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
cmake_minimum_required(VERSION 3.17)
|
||||
include(CheckIPOSupported)
|
||||
include(tools/CompilerWarnings.cmake)
|
||||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||
|
||||
project(quicktex)
|
||||
|
||||
if (SKBUILD)
|
||||
# Scikit-Build does not add your site-packages to the search path
|
||||
# automatically, so we need to add it _or_ the pybind11 specific directory
|
||||
# here.
|
||||
execute_process(
|
||||
COMMAND "${PYTHON_EXECUTABLE}" -c
|
||||
"import pybind11; print(pybind11.get_cmake_dir())"
|
||||
OUTPUT_VARIABLE _tmp_dir
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ECHO STDOUT)
|
||||
list(APPEND CMAKE_PREFIX_PATH "${_tmp_dir}")
|
||||
endif ()
|
||||
|
||||
# Find dependencies
|
||||
find_package(Python COMPONENTS Interpreter Development.Module)
|
||||
find_package(Python COMPONENTS Interpreter Development)
|
||||
find_package(pybind11 CONFIG REQUIRED)
|
||||
find_package(OpenMP)
|
||||
|
||||
@ -30,6 +42,8 @@ file(GLOB HEADER_FILES
|
||||
"quicktex/s3tc/interpolator/*.h"
|
||||
)
|
||||
|
||||
file(GLOB TEST_FILES "tests/*.cpp")
|
||||
|
||||
file(GLOB_RECURSE PYTHON_FILES "src/**/*.py")
|
||||
|
||||
# Organize source files together for some IDEs
|
||||
@ -40,24 +54,36 @@ pybind11_add_module(_quicktex
|
||||
${SOURCE_FILES}
|
||||
${HEADER_FILES})
|
||||
|
||||
# Set Quicktex version info
|
||||
add_executable(test_quicktex
|
||||
${SOURCE_FILES}
|
||||
${HEADER_FILES}
|
||||
${TEST_FILES})
|
||||
|
||||
target_link_libraries(test_quicktex PRIVATE pybind11::embed)
|
||||
|
||||
target_compile_definitions(test_quicktex PRIVATE -DCUSTOM_SYS_PATH="${CMAKE_HOME_DIRECTORY}/env/lib/python3.9/site-packages")
|
||||
target_compile_definitions(_quicktex PRIVATE VERSION_INFO=${QUICKTEX_VERSION_INFO})
|
||||
|
||||
# enable openMP if available
|
||||
if (OpenMP_CXX_FOUND)
|
||||
target_link_libraries(_quicktex PUBLIC OpenMP::OpenMP_CXX)
|
||||
target_link_libraries(test_quicktex PUBLIC OpenMP::OpenMP_CXX)
|
||||
endif ()
|
||||
|
||||
# Set module features, like C/C++ standards
|
||||
target_compile_features(_quicktex PUBLIC cxx_std_17 c_std_11)
|
||||
target_compile_features(test_quicktex PUBLIC cxx_std_17 c_std_11)
|
||||
|
||||
# Set compiler warnings
|
||||
set_project_warnings(_quicktex)
|
||||
set_project_warnings(test_quicktex)
|
||||
|
||||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||
|
||||
# Clang-specific
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -stdlib=libc++ -fsanitize=undefined")
|
||||
set(PROJECT_WARNINGS ${CLANG_WARNINGS})
|
||||
endif ()
|
||||
|
||||
install(TARGETS _quicktex LIBRARY DESTINATION quicktex)
|
||||
|
@ -1,25 +0,0 @@
|
||||
# Development
|
||||
|
||||
This document outlines how to set up a development environment for Quicktex. Documentation on writing Python extension modules is sparse, so I hope this document is useful for other projects as well. The [Coding Patterns for Python Extensions](https://pythonextensionpatterns.readthedocs.io/en/latest/) site has some useful information and will be linked to often in this document.
|
||||
|
||||
## Setting up Debug Python
|
||||
|
||||
Many development tools require debug symbols to function, and since the front-end for accessing an extension module is Python, that usually means adding debug symbols to Python. [This Page](https://pythonextensionpatterns.readthedocs.io/en/latest/debugging/debug_python.html) has some instructions on building python with debug symbols.
|
||||
|
||||
If you plan to use DTrace, enable the `--with-dtrace` flag when running `configure`.
|
||||
|
||||
It's useful for this debug python to have SSL enabled so that packages can be installed using pip. Enable SSL with the `--with-openssl` flag when running `configure`. If you are on macOS and installed OpenSSL through Homebrew, you may need to use `--with-openssl=$(brew --prefix openssl)` to help the compiler find it.
|
||||
|
||||
### Installing Debug Python
|
||||
|
||||
You can keep the resulting binary in your local copy of the cpython repo and symlink to it, but I like to install it somewhere like `/opt/python-debug/`. The install location is set in the `configure` tool using the `--prefix` flag, and installation is done by running `make install`
|
||||
|
||||
### Mixing Debug and Release Python
|
||||
|
||||
The debug build of python is slow (It may be possible to build with debug symbols but full optimization, I have not looked into it). If you already have a venv setup for your project, you can just symlink the debug python binary into `env/bin` with a full name like `python3.9d`. Make sure that the debug build has the same minor version (e.g '3.9') as the version you made the virtual environment with to maintain ABI compatibility.
|
||||
|
||||
## Profiling with Dtrace
|
||||
|
||||
DTrace is the default program profiler on macOS and other Unix systems, but it's also available for use on Windows and Linux. Using DTrace requires building Python with DTrace hooks as seen above.
|
||||
|
||||
Your extension module does not need a full debug build to profile, but it does need frame pointers to see the stack trace at each sample, as well as debug symbols to give functions names. The cmake build type `RelWithDebInfo` handles this automatically.
|
1
MANIFEST.in
Normal file
@ -0,0 +1 @@
|
||||
graft extern
|
@ -1,2 +0,0 @@
|
||||
```{include} ../CHANGELOG.md
|
||||
```
|
@ -13,13 +13,12 @@
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('..'))
|
||||
from datetime import date
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'Quicktex'
|
||||
copyright = f'{date.today().year}, Andrew Cassidy'
|
||||
copyright = '2021, Andrew Cassidy'
|
||||
author = 'Andrew Cassidy'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
@ -29,14 +28,11 @@ author = 'Andrew Cassidy'
|
||||
# ones.
|
||||
extensions = [
|
||||
'myst_parser',
|
||||
'sphinx_click',
|
||||
'sphinx_rtd_theme',
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.intersphinx',
|
||||
]
|
||||
|
||||
myst_heading_anchors = 2
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
@ -74,5 +70,5 @@ autodoc_default_options = {
|
||||
# should be linked to in this documentation.
|
||||
intersphinx_mapping = {
|
||||
'python': ('https://docs.python.org/3', None),
|
||||
'PIL': ('https://pillow.readthedocs.io/en/stable/', None),
|
||||
'PIL': ('https://pillow.readthedocs.io/en/stable/', None)
|
||||
}
|
||||
|
@ -1,2 +0,0 @@
|
||||
```{include} ../DEVELOPMENT.md
|
||||
```
|
@ -1,7 +0,0 @@
|
||||
# Command Reference
|
||||
|
||||
```{eval-rst}
|
||||
.. click:: quicktex.__main__:main
|
||||
:prog: quicktex
|
||||
:nested: full
|
||||
```
|
@ -1,84 +0,0 @@
|
||||
# Getting Started
|
||||
|
||||
## Installation
|
||||
|
||||
Install and update using [pip](https://pip.pypa.io/en/stable/quickstart/):
|
||||
|
||||
```shell
|
||||
pip install -U quicktex
|
||||
```
|
||||
|
||||
If you are on macOS, you need to install openMP to allow multithreading, since it does not ship with the built-in Clang.
|
||||
This can be done easily
|
||||
using [homebrew](https://brew.sh). This is not required if building from source, but highly recommended.
|
||||
|
||||
```shell
|
||||
brew install libomp
|
||||
```
|
||||
|
||||
If you want, you can also install from source. First clone the [git repo](https://github.com/drewcassidy/quicktex) and
|
||||
install it with:
|
||||
|
||||
```shell
|
||||
pip install .
|
||||
```
|
||||
|
||||
and setuptools will take care of any dependencies for you.
|
||||
|
||||
The package also makes tests, stub generation, and docs available. To install the
|
||||
required dependencies for them, install with options like so:
|
||||
|
||||
```shell
|
||||
pip install quicktex[tests,stubs,docs]
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
For detailed documentation on the {command}`quicktex` command and its subcommands see the {doc}`commands`.
|
||||
|
||||
### Examples
|
||||
|
||||
#### Encoding a file
|
||||
|
||||
To encode a file in place, use the {command}`encode` command
|
||||
|
||||
```shell
|
||||
quicktex encode auto bun.png # chooses format based on alpha
|
||||
quicktex encode bc3 bun.png # encodes as bc3
|
||||
```
|
||||
|
||||
the auto subcommand automatically chooses between bc1 and bc3 for your image depending on the contents of its alpha
|
||||
channel. Quicktex supports reading from all image formats supported by [pillow](https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html).
|
||||
|
||||
By default, Quicktex converts in place, meaning the above command will produce a `bun.dds` file alongside the png. If
|
||||
you want to replace the png, use the `-r` flag to remove it after converting.
|
||||
|
||||
if you want to specify an output filename or directory use the `-o` flag.
|
||||
|
||||
```shell
|
||||
quicktex encode auto -o rabbit.dds bun.png # produces rabbit.dds
|
||||
quicktex.encode auto -o textures/ bun.png # produces textures/bun.dds, if textures/ exists
|
||||
```
|
||||
|
||||
#### Encoding multiple files
|
||||
|
||||
quicktex is also able to convert multiple files at once, for example, to encode every png file in the images folder,
|
||||
use:
|
||||
|
||||
```shell
|
||||
quicktex encode auto images/*.png # encodes in-place
|
||||
quicktex encode auto -o textures/ images/*.png # encodes to the textures/ directory
|
||||
```
|
||||
|
||||
please note that globbing is an operation performed by your shell and is not supported by the built in windows `cmd.exe`
|
||||
. If you are on Windows, please use Powershell or any posix-ish shell like [fish](https://fishshell.com).
|
||||
|
||||
#### Decoding files
|
||||
|
||||
decoding is performed exactly the same as encoding, except without having to specify a format. The output image format
|
||||
is set using the `-x` flag, and defaults to png. Quicktex supports writing to all image formats supported by [pillow](https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html)
|
||||
|
||||
```shell
|
||||
quicktex decode bun.dds # produces bun.png
|
||||
quicktex decode -x .tga bun2.dds # produces bun.tga
|
||||
```
|
@ -1,10 +0,0 @@
|
||||
# Handbook
|
||||
|
||||
```{toctree}
|
||||
---
|
||||
maxdepth: 3
|
||||
---
|
||||
|
||||
commands
|
||||
getting_started
|
||||
```
|
@ -1,35 +0,0 @@
|
||||
# Welcome to Quicktex's Documentation
|
||||
|
||||
[](https://quicktex.readthedocs.io/en/latest/?badge=latest)
|
||||
[](https://github.com/drewcassidy/quicktex/actions/workflows/python-package.yml)
|
||||
[](https://badge.fury.io/py/quicktex)
|
||||
|
||||
Quicktex is a Python library for encoding, decoding, and manipulating compressed textures. It uses a backend written in
|
||||
C++ for superior performance, as well as an extensive API for low-level access to the texture data. The compression
|
||||
engine is based in [rgbcx](https://github.com/richgel999/bc7enc).
|
||||
|
||||
```{toctree}
|
||||
---
|
||||
maxdepth: 2
|
||||
caption: Contents
|
||||
---
|
||||
|
||||
handbook/index
|
||||
reference/index
|
||||
```
|
||||
|
||||
```{toctree}
|
||||
---
|
||||
maxdepth: 1
|
||||
---
|
||||
|
||||
development
|
||||
Changelog <changelog>
|
||||
License <license>
|
||||
```
|
||||
|
||||
## Indices and tables
|
||||
|
||||
* {ref}`genindex`
|
||||
* {ref}`modindex`
|
||||
* {ref}`search`
|
15
docs/index.rst
Normal file
@ -0,0 +1,15 @@
|
||||
Welcome to Quicktex's documentation!
|
||||
========================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
reference/index.rst
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
@ -1,2 +0,0 @@
|
||||
```{include} ../LICENSE.md
|
||||
```
|
10
docs/reference/conversion.rst
Normal file
@ -0,0 +1,10 @@
|
||||
.. py:currentmodule:: quicktex
|
||||
|
||||
Conversion
|
||||
============
|
||||
|
||||
.. autoclass:: BlockEncoder
|
||||
:members:
|
||||
|
||||
.. autoclass:: BlockDecoder
|
||||
:members:
|
@ -1,9 +1,10 @@
|
||||
API Reference
|
||||
=============
|
||||
Reference
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
conversion.rst
|
||||
dds.rst
|
||||
image_utils.rst
|
||||
formats/index.rst
|
||||
|
@ -3,8 +3,9 @@ requires = [
|
||||
"setuptools>=61",
|
||||
"setuptools_scm>=6.2",
|
||||
"wheel",
|
||||
"cmake>=3.18",
|
||||
"pybind11~=2.10",
|
||||
"pybind11>=2.9.0",
|
||||
"cmake>=3.22",
|
||||
"scikit-build>0.13",
|
||||
"ninja; sys_platform != 'win32'",
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
||||
@ -13,6 +14,7 @@ build-backend = "setuptools.build_meta"
|
||||
name = "quicktex"
|
||||
description = "A fast block compression library for python"
|
||||
readme = "README.md"
|
||||
license = { file = "LICENSE.md" }
|
||||
authors = [{ name = "Andrew Cassidy", email = "drewcassidy@me.com" }]
|
||||
|
||||
classifiers = [
|
||||
@ -36,19 +38,13 @@ dependencies = ["Pillow", "click"]
|
||||
dynamic = ["version"]
|
||||
|
||||
[project.optional-dependencies]
|
||||
tests = ["parameterized", "pytest"]
|
||||
docs = [
|
||||
"Sphinx >= 3.5",
|
||||
"sphinx-click >= 2.7",
|
||||
"sphinx-rtd-theme",
|
||||
"myst-parser >= 0.14",
|
||||
]
|
||||
tests = ["nose", "parameterized"]
|
||||
docs = ["sphinx", "myst-parser", "sphinx-rtd-theme"]
|
||||
stubs = ["pybind11-stubgen"]
|
||||
|
||||
[project.urls]
|
||||
Docs = "https://quicktex.readthedocs.io/en/"
|
||||
Source = "https://github.com/drewcassidy/quicktex"
|
||||
Changelog = "https://github.com/drewcassidy/quicktex/blob/main/CHANGELOG.md"
|
||||
repository = "https://github.com/drewcassidy/quicktex"
|
||||
changelog = "https://github.com/drewcassidy/quicktex/blob/main/CHANGELOG.md"
|
||||
|
||||
[project.scripts]
|
||||
quicktex = "quicktex.__main__:main"
|
||||
@ -60,26 +56,3 @@ package-data = { '*' = ['py.typed', '*.pyi'] } # include stubs
|
||||
package-dir = { '' = '.' } # without this line, C++ source files get included in the bdist
|
||||
|
||||
[tool.setuptools_scm]
|
||||
|
||||
[tool.cibuildwheel]
|
||||
build = "cp*" # only build wheels for cpython.
|
||||
build-frontend = "build"
|
||||
test-command = "pytest {project}/tests --verbose"
|
||||
test-extras = ["tests"]
|
||||
|
||||
[tool.cibuildwheel.macos]
|
||||
archs = ["x86_64", "universal2"] # build fat binaries, or x86-64 for python 3.7
|
||||
skip = ["cp{38,39,31*}-macosx_x86_64"] # skip x86-only builds where fat binaries are supported
|
||||
|
||||
[tool.cibuildwheel.windows]
|
||||
archs = ["auto64"] # arm64 windows builds not yet supported
|
||||
|
||||
[tool.cibuildwheel.linux]
|
||||
skip = ["cp37-musllinux*", "*musllinux_aarch64*"] # skip targets without available Pillow wheels
|
||||
manylinux-x86_64-image = "manylinux2014"
|
||||
manylinux-aarch64-image = "manylinux2014"
|
||||
|
||||
[tool.black]
|
||||
line-length = 120 # 80-column is stupid
|
||||
target-version = ['py37', 'py38', 'py39', 'py310', 'py310']
|
||||
skip-string-normalization = true
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich 2020 <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich 2020 <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,8 +1,2 @@
|
||||
try:
|
||||
from _quicktex import *
|
||||
from _quicktex import __version__
|
||||
except ImportError as e:
|
||||
if 'libomp.dylib' in e.msg:
|
||||
print('\033[41m\033[01mERROR: LIBOMP NOT FOUND! PLEASE INSTALL IT WITH \033[04m`brew install libomp`\033[0m')
|
||||
print('original error message:')
|
||||
raise e
|
||||
from _quicktex import *
|
||||
from _quicktex import __version__
|
||||
|
@ -1,7 +1,6 @@
|
||||
import click
|
||||
|
||||
from quicktex.cli.decode import decode
|
||||
from quicktex.cli.encode import encode
|
||||
from quicktex.cli.decode import decode
|
||||
|
||||
|
||||
@click.group()
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
@ -96,9 +96,9 @@ template <typename T> T BufferToTexture(py::buffer buf, int width, int height) {
|
||||
auto dst_size = output.NBytes();
|
||||
|
||||
if (info.format != py::format_descriptor<uint8_t>::format()) throw std::runtime_error("Incompatible format in python buffer: expected a byte array.");
|
||||
if (info.size < (Py_ssize_t)dst_size) std::runtime_error("Incompatible format in python buffer: Input data is smaller than texture size.");
|
||||
if (info.size < (ssize_t)dst_size) std::runtime_error("Incompatible format in python buffer: Input data is smaller than texture size.");
|
||||
if (info.ndim == 1) {
|
||||
if (info.shape[0] < (Py_ssize_t)dst_size) throw std::runtime_error("Incompatible format in python buffer: 1-D buffer has incorrect length.");
|
||||
if (info.shape[0] < (ssize_t)dst_size) throw std::runtime_error("Incompatible format in python buffer: 1-D buffer has incorrect length.");
|
||||
if (info.strides[0] != 1) throw std::runtime_error("Incompatible format in python buffer: 1-D buffer is not contiguous.");
|
||||
} else {
|
||||
throw std::runtime_error("Incompatible format in python buffer: Incorrect number of dimensions.");
|
||||
@ -115,9 +115,9 @@ template <typename T> T BufferToPOD(py::buffer buf) {
|
||||
auto info = buf.request(false);
|
||||
|
||||
if (info.format != py::format_descriptor<uint8_t>::format()) throw std::runtime_error("Incompatible format in python buffer: expected a byte array.");
|
||||
if (info.size < (Py_ssize_t)sizeof(T)) std::runtime_error("Incompatible format in python buffer: Input data is smaller than texture size.");
|
||||
if (info.size < (ssize_t)sizeof(T)) std::runtime_error("Incompatible format in python buffer: Input data is smaller than texture size.");
|
||||
if (info.ndim == 1) {
|
||||
if (info.shape[0] < (Py_ssize_t)sizeof(T)) throw std::runtime_error("Incompatible format in python buffer: 1-D buffer has incorrect length.");
|
||||
if (info.shape[0] < (ssize_t)sizeof(T)) throw std::runtime_error("Incompatible format in python buffer: 1-D buffer has incorrect length.");
|
||||
if (info.strides[0] != 1) throw std::runtime_error("Incompatible format in python buffer: 1-D buffer is not contiguous.");
|
||||
} else {
|
||||
throw std::runtime_error("Incompatible format in python buffer: Incorrect number of dimensions.");
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich 2020 <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
import pathlib
|
||||
from typing import List
|
||||
|
||||
import click
|
||||
from PIL import Image
|
||||
from typing import List
|
||||
import pathlib
|
||||
import click
|
||||
|
||||
|
||||
def get_decoded_extensions(feature: str = 'open') -> List[str]:
|
||||
|
@ -1,49 +1,28 @@
|
||||
import os.path
|
||||
|
||||
import click
|
||||
from PIL import Image
|
||||
|
||||
import quicktex.cli.common as common
|
||||
import os.path
|
||||
import quicktex.dds as dds
|
||||
import quicktex.cli.common as common
|
||||
from PIL import Image
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option(
|
||||
'-f/-F', '--flip/--no-flip', default=True, show_default=True, help="Vertically flip image after converting."
|
||||
)
|
||||
@click.option('-f/-F', '--flip/--no-flip', default=True, show_default=True, help="Vertically flip image after converting.")
|
||||
@click.option('-r', '--remove', is_flag=True, help="Remove input images after converting.")
|
||||
@click.option(
|
||||
'-s',
|
||||
'--suffix',
|
||||
type=str,
|
||||
default='',
|
||||
help="Suffix to append to output file(s). Ignored if output is a single file.",
|
||||
)
|
||||
@click.option(
|
||||
'-x',
|
||||
'--extension',
|
||||
callback=common.validate_decoded_extension,
|
||||
type=str,
|
||||
default='.png',
|
||||
show_default=True,
|
||||
help="Extension to use for output. Ignored if output is a single file. Output filetype is deduced from this",
|
||||
)
|
||||
@click.option(
|
||||
'-o',
|
||||
'--output',
|
||||
type=click.Path(writable=True),
|
||||
default=None,
|
||||
help="Output file or directory. If outputting to a file, input filenames must be only a single item. By default, files are decoded in place.",
|
||||
)
|
||||
@click.option('-s', '--suffix', type=str, default='', help="Suffix to append to output file(s). Ignored if output is a single file.")
|
||||
@click.option('-x', '--extension',
|
||||
callback=common.validate_decoded_extension,
|
||||
type=str, default='.png', show_default=True,
|
||||
help="Extension to use for output. Ignored if output is a single file. Output filetype is deduced from this")
|
||||
@click.option('-o', '--output',
|
||||
type=click.Path(writable=True), default=None,
|
||||
help="Output file or directory. If outputting to a file, input filenames must be only a single item. By default, files are decoded in place.")
|
||||
@click.argument('filenames', nargs=-1, type=click.Path(exists=True, readable=True, dir_okay=False))
|
||||
def decode(flip, remove, suffix, extension, output, filenames):
|
||||
"""Decode DDS files to images."""
|
||||
|
||||
path_pairs = common.path_pairs(filenames, output, suffix, extension)
|
||||
|
||||
with click.progressbar(
|
||||
path_pairs, show_eta=False, show_pos=True, item_show_func=lambda x: str(x[0]) if x else ''
|
||||
) as bar:
|
||||
with click.progressbar(path_pairs, show_eta=False, show_pos=True, item_show_func=lambda x: str(x[0]) if x else '') as bar:
|
||||
for inpath, outpath in bar:
|
||||
if inpath.suffix != '.dds':
|
||||
raise click.BadArgumentUsage(f"Input file '{inpath}' is not a DDS file.")
|
||||
|
@ -1,14 +1,13 @@
|
||||
import os
|
||||
|
||||
import click
|
||||
from PIL import Image
|
||||
|
||||
import quicktex.cli.common as common
|
||||
import quicktex.dds as dds
|
||||
import os
|
||||
import pathlib
|
||||
import quicktex.s3tc.bc1
|
||||
import quicktex.s3tc.bc3
|
||||
import quicktex.s3tc.bc4
|
||||
import quicktex.s3tc.bc5
|
||||
import quicktex.dds as dds
|
||||
import quicktex.cli.common as common
|
||||
from PIL import Image
|
||||
|
||||
|
||||
@click.group()
|
||||
@ -17,31 +16,17 @@ def encode():
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option(
|
||||
'-f/-F', '--flip/--no-flip', default=True, show_default=True, help="Vertically flip image before converting."
|
||||
)
|
||||
@click.option('-f/-F', '--flip/--no-flip', default=True, show_default=True, help="Vertically flip image before converting.")
|
||||
@click.option('-r', '--remove', is_flag=True, help="Remove input images after converting.")
|
||||
@click.option(
|
||||
'-s',
|
||||
'--suffix',
|
||||
type=str,
|
||||
default='',
|
||||
help="Suffix to append to output file(s). Ignored if output is a single file.",
|
||||
)
|
||||
@click.option(
|
||||
'-o',
|
||||
'--output',
|
||||
type=click.Path(writable=True),
|
||||
default=None,
|
||||
help="Output file or directory. If outputting to a file, input filenames must be only a single item. By default, files are decoded in place.",
|
||||
)
|
||||
@click.option('-s', '--suffix', type=str, default='', help="Suffix to append to output file(s). Ignored if output is a single file.")
|
||||
@click.option('-o', '--output',
|
||||
type=click.Path(writable=True), default=None,
|
||||
help="Output file or directory. If outputting to a file, input filenames must be only a single item. By default, files are decoded in place.")
|
||||
@click.argument('filenames', nargs=-1, type=click.Path(exists=True, readable=True, dir_okay=False))
|
||||
def encode_format(encoder, four_cc, flip, remove, suffix, output, filenames):
|
||||
path_pairs = common.path_pairs(filenames, output, suffix, '.dds')
|
||||
|
||||
with click.progressbar(
|
||||
path_pairs, show_eta=False, show_pos=True, item_show_func=lambda x: str(x[0]) if x else ''
|
||||
) as bar:
|
||||
with click.progressbar(path_pairs, show_eta=False, show_pos=True, item_show_func=lambda x: str(x[0]) if x else '') as bar:
|
||||
for inpath, outpath in bar:
|
||||
image = Image.open(inpath)
|
||||
|
||||
@ -55,44 +40,17 @@ def encode_format(encoder, four_cc, flip, remove, suffix, output, filenames):
|
||||
|
||||
|
||||
@click.command('auto')
|
||||
@click.option(
|
||||
'-l',
|
||||
'--level',
|
||||
type=click.IntRange(0, 18),
|
||||
default=18,
|
||||
help='Quality level to use. Higher values = higher quality, but slower.',
|
||||
)
|
||||
@click.option(
|
||||
'-b/-B',
|
||||
'--black/--no-black',
|
||||
help='[BC1 only] Enable 3-color mode for blocks containing black or very dark pixels. --3color must also be enabled for this to work.'
|
||||
' (Important: engine/shader MUST ignore decoded texture alpha if this flag is enabled!)',
|
||||
)
|
||||
@click.option(
|
||||
'-3/-4',
|
||||
'--3color/--4color',
|
||||
'threecolor',
|
||||
default=True,
|
||||
help='[BC1 only] Enable 3-color mode for non-black pixels. Higher quality, but slightly slower.',
|
||||
)
|
||||
@click.option(
|
||||
'-f/-F', '--flip/--no-flip', default=True, show_default=True, help="Vertically flip image before converting."
|
||||
)
|
||||
@click.option('-l', '--level', type=click.IntRange(0, 18), default=18, help='Quality level to use. Higher values = higher quality, but slower.')
|
||||
@click.option('-b/-B', '--black/--no-black',
|
||||
help='[BC1 only] Enable 3-color mode for blocks containing black or very dark pixels. --3color must also be enabled for this to work.'
|
||||
' (Important: engine/shader MUST ignore decoded texture alpha if this flag is enabled!)')
|
||||
@click.option('-3/-4', '--3color/--4color', 'threecolor', default=True, help='[BC1 only] Enable 3-color mode for non-black pixels. Higher quality, but slightly slower.')
|
||||
@click.option('-f/-F', '--flip/--no-flip', default=True, show_default=True, help="Vertically flip image before converting.")
|
||||
@click.option('-r', '--remove', is_flag=True, help="Remove input images after converting.")
|
||||
@click.option(
|
||||
'-s',
|
||||
'--suffix',
|
||||
type=str,
|
||||
default='',
|
||||
help="Suffix to append to output file(s). Ignored if output is a single file.",
|
||||
)
|
||||
@click.option(
|
||||
'-o',
|
||||
'--output',
|
||||
type=click.Path(writable=True),
|
||||
default=None,
|
||||
help="Output file or directory. If outputting to a file, input filenames must be only a single item. By default, files are decoded in place.",
|
||||
)
|
||||
@click.option('-s', '--suffix', type=str, default='', help="Suffix to append to output file(s). Ignored if output is a single file.")
|
||||
@click.option('-o', '--output',
|
||||
type=click.Path(writable=True), default=None,
|
||||
help="Output file or directory. If outputting to a file, input filenames must be only a single item. By default, files are decoded in place.")
|
||||
@click.argument('filenames', nargs=-1, type=click.Path(exists=True, readable=True, dir_okay=False))
|
||||
def encode_auto(level, black, threecolor, flip, remove, suffix, output, filenames):
|
||||
"""Encode images to BC1 or BC3, with the format chosen based on each image's alpha channel."""
|
||||
@ -109,9 +67,7 @@ def encode_auto(level, black, threecolor, flip, remove, suffix, output, filename
|
||||
bc3_encoder = quicktex.s3tc.bc3.BC3Encoder(level)
|
||||
path_pairs = common.path_pairs(filenames, output, suffix, '.dds')
|
||||
|
||||
with click.progressbar(
|
||||
path_pairs, show_eta=False, show_pos=True, item_show_func=lambda x: str(x[0]) if x else ''
|
||||
) as bar:
|
||||
with click.progressbar(path_pairs, show_eta=False, show_pos=True, item_show_func=lambda x: str(x[0]) if x else '') as bar:
|
||||
for inpath, outpath in bar:
|
||||
image = Image.open(inpath)
|
||||
|
||||
@ -134,26 +90,11 @@ def encode_auto(level, black, threecolor, flip, remove, suffix, output, filename
|
||||
|
||||
|
||||
@click.command('bc1')
|
||||
@click.option(
|
||||
'-l',
|
||||
'--level',
|
||||
type=click.IntRange(0, 18),
|
||||
default=18,
|
||||
help='Quality level to use. Higher values = higher quality, but slower.',
|
||||
)
|
||||
@click.option(
|
||||
'-b/-B',
|
||||
'--black/--no-black',
|
||||
help='Enable 3-color mode for blocks containing black or very dark pixels. --3color must also be enabled for this to work.'
|
||||
' (Important: engine/shader MUST ignore decoded texture alpha if this flag is enabled!)',
|
||||
)
|
||||
@click.option(
|
||||
'-3/-4',
|
||||
'--3color/--4color',
|
||||
'threecolor',
|
||||
default=True,
|
||||
help='Enable 3-color mode for non-black pixels. Higher quality, but slightly slower.',
|
||||
)
|
||||
@click.option('-l', '--level', type=click.IntRange(0, 18), default=18, help='Quality level to use. Higher values = higher quality, but slower.')
|
||||
@click.option('-b/-B', '--black/--no-black',
|
||||
help='Enable 3-color mode for blocks containing black or very dark pixels. --3color must also be enabled for this to work.'
|
||||
' (Important: engine/shader MUST ignore decoded texture alpha if this flag is enabled!)')
|
||||
@click.option('-3/-4', '--3color/--4color', 'threecolor', default=True, help='Enable 3-color mode for non-black pixels. Higher quality, but slightly slower.')
|
||||
def encode_bc1(level, black, threecolor, **kwargs):
|
||||
"""Encode images to BC1 (RGB, no alpha)."""
|
||||
color_mode = quicktex.s3tc.bc1.BC1Encoder.ColorMode
|
||||
@ -168,13 +109,7 @@ def encode_bc1(level, black, threecolor, **kwargs):
|
||||
|
||||
|
||||
@click.command('bc3')
|
||||
@click.option(
|
||||
'-l',
|
||||
'--level',
|
||||
type=click.IntRange(0, 18),
|
||||
default=18,
|
||||
help='Quality level to use. Higher values = higher quality, but slower.',
|
||||
)
|
||||
@click.option('-l', '--level', type=click.IntRange(0, 18), default=18, help='Quality level to use. Higher values = higher quality, but slower.')
|
||||
def encode_bc3(level, **kwargs):
|
||||
"""Encode images to BC4 (RGBA, 8-bit interpolated alpha)."""
|
||||
encode_format.callback(quicktex.s3tc.bc3.BC3Encoder(level), 'DXT5', **kwargs)
|
||||
|
@ -4,14 +4,12 @@ import enum
|
||||
import os
|
||||
import struct
|
||||
import typing
|
||||
|
||||
from PIL import Image
|
||||
|
||||
import quicktex.image_utils
|
||||
import quicktex.s3tc.bc1 as bc1
|
||||
import quicktex.s3tc.bc3 as bc3
|
||||
import quicktex.s3tc.bc4 as bc4
|
||||
import quicktex.s3tc.bc5 as bc5
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class DDSFormat:
|
||||
@ -167,28 +165,8 @@ class DDSFile:
|
||||
file.write(DDSFile.magic)
|
||||
|
||||
# WRITE HEADER
|
||||
file.write(
|
||||
struct.pack(
|
||||
'<7I44x',
|
||||
DDSFile.header_bytes,
|
||||
int(self.flags),
|
||||
self.size[1],
|
||||
self.size[0],
|
||||
self.pitch,
|
||||
self.depth,
|
||||
self.mipmap_count,
|
||||
)
|
||||
)
|
||||
file.write(
|
||||
struct.pack(
|
||||
'<2I4s5I',
|
||||
32,
|
||||
int(self.pf_flags),
|
||||
bytes(self.four_cc, 'ascii'),
|
||||
self.pixel_size,
|
||||
*self.pixel_bitmasks,
|
||||
)
|
||||
)
|
||||
file.write(struct.pack('<7I44x', DDSFile.header_bytes, int(self.flags), self.size[1], self.size[0], self.pitch, self.depth, self.mipmap_count))
|
||||
file.write(struct.pack('<2I4s5I', 32, int(self.pf_flags), bytes(self.four_cc, 'ascii'), self.pixel_size, *self.pixel_bitmasks))
|
||||
file.write(struct.pack('<4I4x', *self.caps))
|
||||
|
||||
assert file.tell() == 4 + DDSFile.header_bytes, 'error writing file: incorrect header size'
|
||||
|
@ -1,9 +1,8 @@
|
||||
"""Various utilities for working with Pillow images"""
|
||||
|
||||
import math
|
||||
from typing import List, Tuple, Optional
|
||||
|
||||
from PIL import Image
|
||||
from typing import List, Tuple, Optional
|
||||
import math
|
||||
|
||||
|
||||
def mip_sizes(dimensions: Tuple[int, int], mip_count: Optional[int] = None) -> List[Tuple[int, int]]:
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
@ -58,7 +58,7 @@ template <size_t N> class OrderTable {
|
||||
static bool Generate() {
|
||||
static_assert(N == 4 || N == 3);
|
||||
|
||||
std::scoped_lock{table_mutex};
|
||||
table_mutex.lock();
|
||||
if (!generated) {
|
||||
hashes = new std::array<Hash, HashCount>();
|
||||
factors = new std::array<Vector4, OrderCount>();
|
||||
@ -85,6 +85,8 @@ template <size_t N> class OrderTable {
|
||||
|
||||
generated = true;
|
||||
}
|
||||
table_mutex.unlock();
|
||||
|
||||
assert(generated);
|
||||
return true;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com> and licenced under the public domain
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021-2022 Andrew Cassidy <drewcassidy@me.com>
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
|
120
setup.py
@ -1,111 +1,15 @@
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pybind11
|
||||
from setuptools import setup, Extension
|
||||
from setuptools.command.build_ext import build_ext
|
||||
try:
|
||||
from skbuild import setup
|
||||
except ImportError:
|
||||
print(
|
||||
"Please update pip, you need pip 10 or greater,\n"
|
||||
" or you need to install the PEP 518 requirements in pyproject.toml yourself",
|
||||
file=sys.stderr,
|
||||
)
|
||||
raise
|
||||
|
||||
project_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
|
||||
# A CMakeExtension needs a sourcedir instead of a file list.
|
||||
# The name must be the _single_ output extension from the CMake build.
|
||||
# If you need multiple extensions, see scikit-build.
|
||||
class CMakeExtension(Extension):
|
||||
def __init__(self, name, sourcedir=""):
|
||||
Extension.__init__(self, name, sources=[])
|
||||
self.sourcedir = os.path.abspath(sourcedir)
|
||||
|
||||
|
||||
class CMakeBuild(build_ext):
|
||||
def build_extension(self, ext):
|
||||
from setuptools_scm import get_version
|
||||
|
||||
version = get_version(root='.', relative_to=__file__)
|
||||
|
||||
extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
|
||||
|
||||
# required for auto-detection of auxiliary "native" libs
|
||||
if not extdir.endswith(os.path.sep):
|
||||
extdir += os.path.sep
|
||||
|
||||
cfg = "Debug" if self.debug else "RelWithDebInfo"
|
||||
if 'QUICKTEX_DEBUG' in os.environ:
|
||||
cfg = "Debug"
|
||||
|
||||
# CMake lets you override the generator - we need to check this.
|
||||
# Can be set with Conda-Build, for example.
|
||||
cmake_generator = os.environ.get("CMAKE_GENERATOR", "")
|
||||
|
||||
# Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON
|
||||
cmake_args = [
|
||||
"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={}".format(extdir),
|
||||
"-Dpybind11_DIR={}".format(pybind11.get_cmake_dir()),
|
||||
"-DPython_EXECUTABLE={}".format(sys.executable),
|
||||
"-DPython_ROOT_DIR={}".format(os.path.dirname(sys.executable)),
|
||||
"-DQUICKTEX_VERSION_INFO={}".format(version), # include version info in module
|
||||
"-DQUICKTEX_MODULE_ONLY=TRUE", # only build the module, not the wrapper
|
||||
"-DCMAKE_BUILD_TYPE={}".format(cfg), # not used on MSVC, but no harm
|
||||
# clear cached make program binary, see https://github.com/pypa/setuptools/issues/2912
|
||||
"-U",
|
||||
"CMAKE_MAKE_PROGRAM",
|
||||
]
|
||||
build_args = []
|
||||
|
||||
if self.compiler.compiler_type != "msvc":
|
||||
# Using Ninja-build since it a) is available as a wheel and b)
|
||||
# multithreads automatically. MSVC would require all variables be
|
||||
# exported for Ninja to pick it up, which is a little tricky to do.
|
||||
# Users can override the generator with CMAKE_GENERATOR in CMake
|
||||
# 3.15+.
|
||||
if not cmake_generator:
|
||||
cmake_args += ["-GNinja"]
|
||||
|
||||
else:
|
||||
# Single config generators are handled "normally"
|
||||
single_config = any(x in cmake_generator for x in {"NMake", "Ninja"})
|
||||
|
||||
# CMake allows an arch-in-generator style for backward compatibility
|
||||
contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"})
|
||||
|
||||
# Convert distutils Windows platform specifiers to CMake -A arguments
|
||||
plat_to_cmake = {"win32": "Win32", "win-amd64": "x64", "win-arm32": "ARM", "win-arm64": "ARM64"}
|
||||
|
||||
# Specify the arch if using MSVC generator, but only if it doesn't
|
||||
# contain a backward-compatibility arch spec already in the
|
||||
# generator name.
|
||||
if not single_config and not contains_arch:
|
||||
cmake_args += ["-A", plat_to_cmake[self.plat_name]]
|
||||
|
||||
# Multi-config generators have a different way to specify configs
|
||||
if not single_config:
|
||||
cmake_args += ["-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}".format(cfg.upper(), extdir)]
|
||||
build_args += ["--config", cfg]
|
||||
|
||||
if sys.platform.startswith("darwin"):
|
||||
# Cross-compile support for macOS - respect ARCHFLAGS if set
|
||||
archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", ""))
|
||||
if archs:
|
||||
cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))]
|
||||
|
||||
# Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level
|
||||
# across all generators.
|
||||
if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ:
|
||||
# self.parallel is a Python 3 only way to set parallel jobs by hand
|
||||
# using -j in the build_ext call, not supported by pip or PyPA-build.
|
||||
if hasattr(self, "parallel") and self.parallel:
|
||||
# CMake 3.12+ only.
|
||||
build_args += ["-j{}".format(self.parallel)]
|
||||
|
||||
if not os.path.exists(self.build_temp):
|
||||
os.makedirs(self.build_temp)
|
||||
|
||||
subprocess.check_call(["cmake", ext.sourcedir] + cmake_args, cwd=self.build_temp)
|
||||
subprocess.check_call(["cmake", "--build", ".", "--target", ext.name] + build_args, cwd=self.build_temp)
|
||||
|
||||
|
||||
# The information here can also be placed in setup.cfg - better separation of
|
||||
# logic and declaration, and simpler if you include description/version in a file.
|
||||
setup(use_scm_version=True, ext_modules=[CMakeExtension("_quicktex")], cmdclass={"build_ext": CMakeBuild})
|
||||
setup(
|
||||
cmake_install_dir='.'
|
||||
)
|
||||
|
@ -1,3 +1,9 @@
|
||||
import os.path
|
||||
|
||||
# Some checks to run before tests can begin
|
||||
images_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'images')
|
||||
|
||||
assert os.path.isdir(images_path), 'test images repo not present'
|
||||
assert os.path.isfile(os.path.join(images_path, '__init__.py')), 'images __init__.py not present, is the test image repo present?'
|
||||
bp_size = os.path.getsize(os.path.join(images_path, 'Boilerplate.png'))
|
||||
assert bp_size == 955989, 'Boilerplate.png is the wrong size, is the test image repo checked out with LFS enabled?'
|
||||
|
Before Width: | Height: | Size: 934 KiB |
Before Width: | Height: | Size: 375 KiB |
@ -1,32 +0,0 @@
|
||||
from PIL import Image
|
||||
from quicktex.s3tc.bc1 import BC1Block
|
||||
from quicktex.s3tc.bc4 import BC4Block
|
||||
from quicktex import RawTexture
|
||||
import os.path
|
||||
|
||||
image_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
|
||||
class BC1Blocks:
|
||||
class Entry:
|
||||
def __init__(self, filename, block):
|
||||
path = os.path.join(image_path, 'bc1', filename)
|
||||
self.image = Image.open(path).convert('RGBA')
|
||||
self.texture = RawTexture.frombytes(self.image.tobytes('raw', 'RGBA'), *self.image.size)
|
||||
self.block = block
|
||||
|
||||
greyscale = Entry('greyscale_unpacked.png', BC1Block.frombytes(b'\xFF\xFF\x49\x4A\x78\x78\x78\x78'))
|
||||
three_color = Entry('3color_unpacked.png', BC1Block.frombytes(b'\xE0\x07\x00\xF8\x29\x29\x29\x29'))
|
||||
three_color_black = Entry('3color_black_unpacked.png', BC1Block.frombytes(b'\xE0\x07\x00\xF8\x27\x27\x27\x27'))
|
||||
|
||||
|
||||
class BC4Blocks:
|
||||
class Entry:
|
||||
def __init__(self, filename, block):
|
||||
path = os.path.join(image_path, 'bc4', filename)
|
||||
self.image = Image.open(path).convert('RGBA')
|
||||
self.texture = RawTexture.frombytes(self.image.tobytes('raw', 'RGBA'), *self.image.size)
|
||||
self.block = block
|
||||
|
||||
six_value = Entry('6value.png', BC4Block(8, 248, [[0, 1, 2, 3]] * 2 + [[4, 5, 6, 7]] * 2))
|
||||
eight_value = Entry('8value.png', BC4Block(240, 16, [[0, 1, 2, 3]] * 2 + [[4, 5, 6, 7]] * 2))
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 513 B |
Before Width: | Height: | Size: 509 B |
44
tests/run_tests.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
/* Quicktex Texture Compression Library
|
||||
Copyright (C) 2021 Andrew Cassidy <drewcassidy@me.com>
|
||||
Partially derived from rgbcx.h written by Richard Geldreich <richgel99@gmail.com>
|
||||
and licenced under the public domain
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// This file allows for easy debugging in CLion or other IDEs that dont natively support cross-debugging between Python and C++
|
||||
|
||||
#include <pybind11/embed.h>
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
namespace py = pybind11;
|
||||
using namespace pybind11::literals;
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
#define MACRO_STRINGIFY(x) STRINGIFY(x)
|
||||
|
||||
int main() {
|
||||
py::scoped_interpreter guard{};
|
||||
|
||||
py::module_ site = py::module_::import("site");
|
||||
|
||||
site.attr("addsitedir")(CUSTOM_SYS_PATH);
|
||||
|
||||
py::module_ nose = py::module_::import("nose");
|
||||
py::module_ tests = py::module_::import("tests");
|
||||
py::list argv(1);
|
||||
nose.attr("runmodule")("name"_a = "tests.test_bc1", "exit"_a = false);
|
||||
}
|
@ -1,206 +1,213 @@
|
||||
import math
|
||||
|
||||
import pytest
|
||||
import unittest
|
||||
import nose
|
||||
import os.path
|
||||
from parameterized import parameterized, parameterized_class
|
||||
import quicktex
|
||||
from quicktex.s3tc.bc1 import BC1Block, BC1Texture, BC1Encoder, BC1Decoder
|
||||
from tests.images import BC1Blocks, image_path
|
||||
from PIL import Image, ImageChops
|
||||
|
||||
from quicktex.s3tc.bc1 import BC1Block, BC1Texture, BC1Encoder, BC1Decoder
|
||||
from .images import BC1Blocks
|
||||
|
||||
in_endpoints = ((253, 254, 255), (65, 70, 67)) # has some small changes that should encode the same in 5:6:5
|
||||
in_endpoints = ((253, 254, 255), (65, 70, 67)) # has some small changes that should encode the same
|
||||
out_endpoints = ((255, 255, 255, 255), (66, 69, 66, 255))
|
||||
selectors = [[0, 2, 3, 1]] * 4
|
||||
block_bytes = b'\xff\xff\x28\x42\x78\x78\x78\x78'
|
||||
|
||||
|
||||
class TestBC1Block:
|
||||
class TestBC1Block(unittest.TestCase):
|
||||
"""Tests for the BC1Block class"""
|
||||
|
||||
def test_size(self):
|
||||
"""Test the size and dimensions of BC1Block"""
|
||||
assert BC1Block.nbytes == 8
|
||||
assert BC1Block.width == 4
|
||||
assert BC1Block.height == 4
|
||||
assert BC1Block.size == (4, 4)
|
||||
self.assertEqual(BC1Block.nbytes, 8, 'incorrect block size')
|
||||
self.assertEqual(BC1Block.width, 4, 'incorrect block width')
|
||||
self.assertEqual(BC1Block.height, 4, 'incorrect block width')
|
||||
self.assertEqual(BC1Block.size, (4, 4), 'incorrect block dimensions')
|
||||
|
||||
def test_buffer(self):
|
||||
"""Test the buffer protocol of BC1Block"""
|
||||
block = BC1Block()
|
||||
mv = memoryview(block)
|
||||
mv[:] = block_bytes
|
||||
|
||||
assert not mv.readonly
|
||||
assert mv.c_contiguous
|
||||
assert mv.ndim == 1
|
||||
assert mv.nbytes == 8
|
||||
assert mv.format == 'B'
|
||||
assert mv.tobytes() == block_bytes
|
||||
assert mv.tobytes() == block.tobytes()
|
||||
self.assertFalse(mv.readonly, 'buffer is readonly')
|
||||
self.assertTrue(mv.c_contiguous, 'buffer is not contiguous')
|
||||
self.assertEqual(mv.ndim, 1, 'buffer is multidimensional')
|
||||
self.assertEqual(mv.nbytes, BC1Block.nbytes, 'buffer is the wrong size')
|
||||
self.assertEqual(mv.format, 'B', 'buffer has the wrong format')
|
||||
|
||||
mv[:] = block_bytes
|
||||
self.assertEqual(mv.tobytes(), block_bytes, 'incorrect buffer data')
|
||||
|
||||
def test_constructor(self):
|
||||
"""Test constructing a block out of endpoints and selectors"""
|
||||
block = BC1Block(*in_endpoints, selectors)
|
||||
assert block.tobytes() == block_bytes
|
||||
assert block.selectors == selectors
|
||||
assert block.endpoints == out_endpoints
|
||||
assert not block.is_3color
|
||||
self.assertEqual(block.tobytes(), block_bytes, 'incorrect block bytes')
|
||||
self.assertEqual(block.selectors, selectors, 'incorrect selectors')
|
||||
self.assertEqual(block.endpoints, out_endpoints, 'incorrect endpoints')
|
||||
self.assertFalse(block.is_3color, 'incorrect color mode')
|
||||
|
||||
def test_frombytes(self):
|
||||
"""Test constructing a block out of raw data"""
|
||||
block = BC1Block.frombytes(block_bytes)
|
||||
assert block.tobytes() == block_bytes
|
||||
assert block.selectors == selectors
|
||||
assert block.endpoints == out_endpoints
|
||||
assert not block.is_3color
|
||||
self.assertEqual(block.tobytes(), block_bytes, 'incorrect block bytes')
|
||||
self.assertEqual(block.selectors, selectors, 'incorrect selectors')
|
||||
self.assertEqual(block.endpoints, out_endpoints, 'incorrect endpoints')
|
||||
self.assertFalse(block.is_3color, 'incorrect color mode')
|
||||
|
||||
def test_eq(self):
|
||||
"""Test equality between two identical blocks"""
|
||||
block1 = BC1Block.frombytes(block_bytes)
|
||||
block2 = BC1Block.frombytes(block_bytes)
|
||||
assert block1 == block2
|
||||
self.assertEqual(block1, block2, 'identical blocks not equal')
|
||||
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
@pytest.mark.parametrize('w', [7, 8, 9])
|
||||
@pytest.mark.parametrize('h', [7, 8, 9])
|
||||
class TestBC1Texture:
|
||||
def test_dimensions(self, w, h):
|
||||
"""Test dimensions of BC1Texture in pixels, blocks, and bytes"""
|
||||
tex = BC1Texture(w, h)
|
||||
wb = math.ceil(w / 4)
|
||||
hb = math.ceil(h / 4)
|
||||
@parameterized_class(
|
||||
("name", "w", "h", "wb", "hb"), [
|
||||
("8x8", 8, 8, 2, 2),
|
||||
("9x9", 9, 9, 3, 3),
|
||||
("7x7", 7, 7, 2, 2),
|
||||
("7x9", 7, 9, 2, 3)
|
||||
])
|
||||
class TestBC1Texture(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tex = BC1Texture(self.w, self.h)
|
||||
self.nbytes = self.wb * self.hb * BC1Block.nbytes
|
||||
|
||||
assert tex.nbytes == BC1Block.nbytes * wb * hb # block width x block height
|
||||
assert len(tex.tobytes()) == tex.nbytes
|
||||
def test_size(self):
|
||||
"""Test size of BC1Texture in bytes"""
|
||||
self.assertEqual(self.tex.nbytes, self.nbytes, 'incorrect texture size')
|
||||
self.assertEqual(len(self.tex.tobytes()), self.nbytes, 'incorrect texture size from tobytes')
|
||||
|
||||
assert tex.width == w
|
||||
assert tex.height == h
|
||||
assert tex.size == (w, h)
|
||||
def test_dimensions(self):
|
||||
"""Test dimensions of BC1Texture in pixels"""
|
||||
self.assertEqual(self.tex.width, self.w, 'incorrect texture width')
|
||||
self.assertEqual(self.tex.height, self.h, 'incorrect texture height')
|
||||
self.assertEqual(self.tex.size, (self.w, self.h), 'incorrect texture dimensions')
|
||||
|
||||
assert tex.width_blocks == wb
|
||||
assert tex.height_blocks == hb
|
||||
assert tex.size_blocks == (wb, hb)
|
||||
def test_dimensions_blocks(self):
|
||||
"""Test dimensions of BC1Texture in blocks"""
|
||||
self.assertEqual(self.tex.width_blocks, self.wb, 'incorrect texture width_blocks')
|
||||
self.assertEqual(self.tex.height_blocks, self.hb, 'incorrect texture width_blocks')
|
||||
self.assertEqual(self.tex.size_blocks, (self.wb, self.hb), 'incorrect texture dimensions_blocks')
|
||||
|
||||
def test_blocks(self, w, h):
|
||||
def test_blocks(self):
|
||||
"""Test getting and setting blocks to BC1Texture"""
|
||||
tex = BC1Texture(w, h)
|
||||
blocks = [[BC1Block.frombytes(bytes([x, y] + [0] * 6)) for x in range(self.wb)] for y in range(self.hb)]
|
||||
for x in range(self.wb):
|
||||
for y in range(self.hb):
|
||||
self.tex[x, y] = blocks[y][x]
|
||||
|
||||
# generate garbage blocks with the x and y index in the first 2 bytes
|
||||
blocks = [
|
||||
[BC1Block.frombytes(bytes([x, y] + [0] * 6)) for x in range(tex.width_blocks)]
|
||||
for y in range(tex.height_blocks)
|
||||
]
|
||||
# assign those blocks to the texture
|
||||
for x in range(tex.width_blocks):
|
||||
for y in range(tex.height_blocks):
|
||||
tex[x, y] = blocks[y][x]
|
||||
b = self.tex.tobytes()
|
||||
for x in range(self.wb):
|
||||
for y in range(self.hb):
|
||||
index = (x + (y * self.wb)) * BC1Block.nbytes
|
||||
tb = self.tex[x, y]
|
||||
fb = BC1Block.frombytes(b[index:index + BC1Block.nbytes])
|
||||
self.assertEqual(tb, blocks[y][x], 'incorrect block read from texture')
|
||||
self.assertEqual(fb, blocks[y][x], 'incorrect block read from texture bytes')
|
||||
|
||||
# get the blocks and analyze
|
||||
b = tex.tobytes()
|
||||
for x in range(tex.width_blocks):
|
||||
for y in range(tex.height_blocks):
|
||||
index = (x + (y * tex.width_blocks)) * BC1Block.nbytes
|
||||
tb = tex[x, y]
|
||||
fb = BC1Block.frombytes(b[index : index + BC1Block.nbytes])
|
||||
assert tb == blocks[y][x]
|
||||
assert fb == blocks[y][x]
|
||||
self.assertEqual(self.tex[-1, -1], self.tex[self.wb - 1, self.hb - 1], 'incorrect negative subscripting')
|
||||
|
||||
def text_subscript(self, w, h):
|
||||
"""Test BC1Texture subscripting for blocks"""
|
||||
tex = BC1Texture(w, h)
|
||||
with self.assertRaises(IndexError):
|
||||
thing = self.tex[self.wb, self.hb]
|
||||
with self.assertRaises(IndexError):
|
||||
thing = self.tex[-1 - self.wb, -1 - self.hb]
|
||||
|
||||
# ensure negative wraparound works
|
||||
assert tex[-1, -1] == tex[tex.width_blocks - 1, tex.height_blocks - 1]
|
||||
|
||||
with pytest.raises(IndexError):
|
||||
_ = tex[tex.width_blocks, tex.height_blocks]
|
||||
with pytest.raises(IndexError):
|
||||
_ = tex[-1 - tex.width_blocks, -1 - tex.height_blocks]
|
||||
|
||||
def test_buffer(self, w, h):
|
||||
def test_buffer(self):
|
||||
"""Test the buffer protocol of BC1Texture"""
|
||||
tex = BC1Texture(w, h)
|
||||
mv = memoryview(tex)
|
||||
mv = memoryview(self.tex)
|
||||
|
||||
data = block_bytes * tex.width_blocks * tex.height_blocks
|
||||
self.assertFalse(mv.readonly, 'buffer is readonly')
|
||||
self.assertTrue(mv.c_contiguous, 'buffer is not contiguous')
|
||||
self.assertEqual(mv.nbytes, self.nbytes, 'buffer is the wrong size')
|
||||
self.assertEqual(mv.format, 'B', 'buffer has the wrong format')
|
||||
|
||||
data = block_bytes * self.wb * self.hb
|
||||
mv[:] = data
|
||||
|
||||
assert not mv.readonly
|
||||
assert mv.c_contiguous
|
||||
assert mv.nbytes == tex.nbytes
|
||||
assert mv.format == 'B'
|
||||
assert mv.tobytes() == data
|
||||
self.assertEqual(mv.tobytes(), data, 'incorrect buffer data')
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'color_mode',
|
||||
[BC1Encoder.ColorMode.FourColor, BC1Encoder.ColorMode.ThreeColor, BC1Encoder.ColorMode.ThreeColorBlack],
|
||||
)
|
||||
class TestBC1Encoder:
|
||||
@parameterized_class(
|
||||
("name", "color_mode"), [
|
||||
("4Color", BC1Encoder.ColorMode.FourColor),
|
||||
("3Color", BC1Encoder.ColorMode.ThreeColor),
|
||||
("3Color_Black", BC1Encoder.ColorMode.ThreeColorBlack),
|
||||
])
|
||||
class TestBC1Encoder(unittest.TestCase):
|
||||
"""Test BC1Encoder"""
|
||||
|
||||
def test_block_4color(self, color_mode):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.bc1_encoder = BC1Encoder(5, cls.color_mode)
|
||||
|
||||
def test_block_4color(self):
|
||||
"""Test encoder output with 4 color greyscale test block"""
|
||||
encoder = BC1Encoder(color_mode=color_mode)
|
||||
out_tex = encoder.encode(BC1Blocks.greyscale.texture)
|
||||
out_tex = self.bc1_encoder.encode(BC1Blocks.greyscale.texture)
|
||||
|
||||
self.assertEqual(out_tex.size_blocks, (1, 1), 'encoded texture has multiple blocks')
|
||||
|
||||
out_block = out_tex[0, 0]
|
||||
|
||||
assert out_tex.size_blocks == (1, 1)
|
||||
self.assertFalse(out_block.is_3color, 'returned 3color mode for greyscale test block')
|
||||
self.assertEqual(out_block, BC1Blocks.greyscale.block, 'encoded block is incorrect')
|
||||
|
||||
assert not out_block.is_3color
|
||||
assert out_block == BC1Blocks.greyscale.block
|
||||
|
||||
def test_block_3color(self, color_mode):
|
||||
def test_block_3color(self):
|
||||
"""Test encoder output with 3 color test block"""
|
||||
encoder = BC1Encoder(color_mode=color_mode)
|
||||
out_tex = encoder.encode(BC1Blocks.three_color.texture)
|
||||
out_tex = self.bc1_encoder.encode(BC1Blocks.three_color.texture)
|
||||
|
||||
self.assertEqual(out_tex.size_blocks, (1, 1), 'encoded texture has multiple blocks')
|
||||
|
||||
out_block = out_tex[0, 0]
|
||||
|
||||
assert out_tex.size_blocks == (1, 1)
|
||||
|
||||
if encoder.color_mode != BC1Encoder.ColorMode.FourColor:
|
||||
# we only care about the selectors if we are in 3 color mode
|
||||
assert out_block.is_3color
|
||||
assert out_block == BC1Blocks.three_color.block
|
||||
if self.color_mode != BC1Encoder.ColorMode.FourColor: # we only care about the selectors if we are in 3 color mode
|
||||
self.assertTrue(out_block.is_3color, 'returned 4-color block for 3 color test block')
|
||||
self.assertEqual(out_block, BC1Blocks.three_color.block, 'encoded block is incorrect')
|
||||
else:
|
||||
assert not out_block.is_3color
|
||||
self.assertFalse(out_block.is_3color, 'returned 3-color block in 4-color mode')
|
||||
|
||||
def test_block_3color_black(self, color_mode):
|
||||
def test_block_3color_black(self):
|
||||
"""Test encoder output with 3 color test block with black pixels"""
|
||||
encoder = BC1Encoder(color_mode=color_mode)
|
||||
out_tex = encoder.encode(BC1Blocks.three_color_black.texture)
|
||||
out_tex = self.bc1_encoder.encode(BC1Blocks.three_color_black.texture)
|
||||
|
||||
self.assertEqual(out_tex.size_blocks, (1, 1), 'encoded texture has multiple blocks')
|
||||
|
||||
out_block = out_tex[0, 0]
|
||||
|
||||
assert out_tex.size_blocks == (1, 1)
|
||||
|
||||
has_black = 3 in [j for row in out_block.selectors for j in row]
|
||||
|
||||
if color_mode == BC1Encoder.ColorMode.ThreeColorBlack:
|
||||
# we only care about the selectors if we are in 3 color black mode
|
||||
assert out_block.is_3color
|
||||
assert has_black
|
||||
assert out_block == BC1Blocks.three_color_black.block
|
||||
elif color_mode == BC1Encoder.ColorMode.ThreeColor:
|
||||
assert not (has_black and out_block.is_3color)
|
||||
if self.color_mode == BC1Encoder.ColorMode.ThreeColorBlack: # we only care about the selectors if we are in 3 color black mode
|
||||
self.assertTrue(out_block.is_3color, 'returned 4-color block for 3 color test block with black')
|
||||
self.assertTrue(has_black, 'block does not have black pixels as expected')
|
||||
self.assertEqual(out_block, BC1Blocks.three_color_black.block, "encoded block is incorrect")
|
||||
elif self.color_mode == BC1Encoder.ColorMode.ThreeColor:
|
||||
self.assertFalse(has_black and out_block.is_3color, 'returned 3color block with black pixels')
|
||||
else:
|
||||
assert not out_block.is_3color
|
||||
self.assertFalse(out_block.is_3color, 'returned 3-color block in 4-color mode')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('texture', [BC1Blocks.greyscale, BC1Blocks.three_color, BC1Blocks.three_color_black])
|
||||
class TestBC1Decoder:
|
||||
class TestBC1Decoder(unittest.TestCase):
|
||||
"""Test BC1Decoder"""
|
||||
|
||||
def test_block(self, texture):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.bc1_decoder = BC1Decoder()
|
||||
|
||||
@parameterized.expand([
|
||||
("4color", BC1Blocks.greyscale.block, BC1Blocks.greyscale.image),
|
||||
("3color", BC1Blocks.three_color.block, BC1Blocks.three_color.image),
|
||||
("3color_black", BC1Blocks.three_color_black.block, BC1Blocks.three_color_black.image)
|
||||
])
|
||||
def test_block(self, _, block, image):
|
||||
"""Test decoder output for a single block"""
|
||||
block = texture.block
|
||||
image = texture.image
|
||||
decoder = BC1Decoder()
|
||||
in_tex = BC1Texture(4, 4)
|
||||
in_tex[0, 0] = block
|
||||
out_tex = decoder.decode(in_tex)
|
||||
out_tex = self.bc1_decoder.decode(in_tex)
|
||||
|
||||
assert out_tex.size == (4, 4)
|
||||
self.assertEqual(out_tex.size, (4, 4), 'decoded texture has incorrect dimensions')
|
||||
|
||||
out_img = Image.frombytes('RGBA', (4, 4), out_tex.tobytes())
|
||||
img_diff = ImageChops.difference(out_img, image).convert('L')
|
||||
img_hist = img_diff.histogram()
|
||||
assert img_hist[0] == 16
|
||||
self.assertEqual(16, img_hist[0], 'decoded block is incorrect')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
nose.main()
|
||||
|