Add ktx
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
# Copyright 2017-2020 The Khronos Group Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set(LIB_TYPE STATIC)
|
||||
# Parent scope variables set in this context are not seen in it and, if there
|
||||
# is a way to explicitly reference one, I've not found it. So 2-step.
|
||||
set(BASISU_CBIND_LIBTYPE ${LIB_TYPE} PARENT_SCOPE)
|
||||
|
||||
add_library(obj_basisu_cbind ${LIB_TYPE}
|
||||
inc/basisu_c_binding.h
|
||||
src/basisu_c_binding.cpp
|
||||
)
|
||||
|
||||
# Using cxx_std_17 because VS 2022 raises a set but unused variable warning
|
||||
# and pre-c++-17 there is no 'unused' attribute. equivalent.
|
||||
target_compile_features(obj_basisu_cbind PUBLIC c_std_99 cxx_std_17)
|
||||
# Need this to make VS correctly define __cplusplus to the std in use.
|
||||
target_compile_options(
|
||||
obj_basisu_cbind
|
||||
PRIVATE
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/Zc:__cplusplus>
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
obj_basisu_cbind
|
||||
PUBLIC
|
||||
inc
|
||||
${KTX_BASISU_INCLUDE_DIRS}
|
||||
PRIVATE
|
||||
$<TARGET_PROPERTY:ktx,INCLUDE_DIRECTORIES>
|
||||
${PROJECT_SOURCE_DIR}/utils
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
obj_basisu_cbind
|
||||
PUBLIC
|
||||
KTX_BASISU_C_BINDINGS
|
||||
PRIVATE
|
||||
$<TARGET_PROPERTY:ktx,INTERFACE_COMPILE_DEFINITIONS>
|
||||
# We're only using this to read .basis files.
|
||||
BASISD_SUPPORT_KTX2_ZSTD=0
|
||||
BASISD_SUPPORT_KTX2=0
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(
|
||||
obj_basisu_cbind
|
||||
PRIVATE
|
||||
$<IF:$<STREQUAL:"${LIB_TYPE}","STATIC">,KHRONOS_STATIC,KTX_BASISU_API=__declspec\(dllexport\)>
|
||||
)
|
||||
elseif(APPLE)
|
||||
if(KTX_EMBED_BITCODE)
|
||||
target_compile_options(obj_basisu_cbind PRIVATE "-fembed-bitcode")
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 2019 Andreas Atteneder, All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(KHRONOS_STATIC)
|
||||
#define KTX_BASISU_API
|
||||
#elif defined(_WIN32)
|
||||
#if !defined(KTX_BASISU_API)
|
||||
#define KTX_BASISU_API __declspec(dllimport)
|
||||
#endif
|
||||
#elif defined(__ANDROID__)
|
||||
#define KTX_BASISU_API __attribute__((visibility("default")))
|
||||
#else
|
||||
#define KTX_BASISU_API
|
||||
#endif
|
||||
|
||||
#include <basisu_transcoder.h>
|
||||
|
||||
using namespace basist;
|
||||
|
||||
class basis_file
|
||||
{
|
||||
unsigned int m_magic = 0;
|
||||
basisu_transcoder m_transcoder;
|
||||
const uint8_t *m_file;
|
||||
uint32_t byteLength;
|
||||
basisu_file_info fileinfo;
|
||||
|
||||
public:
|
||||
basis_file()
|
||||
:
|
||||
m_transcoder()
|
||||
{}
|
||||
|
||||
bool open(const uint8_t *buffer, uint32_t newByteLength);
|
||||
void close();
|
||||
uint32_t getHasAlpha();
|
||||
uint32_t getNumImages();
|
||||
uint32_t getNumLevels(uint32_t image_index);
|
||||
uint32_t getImageWidth(uint32_t image_index, uint32_t level_index);
|
||||
uint32_t getImageHeight(uint32_t image_index, uint32_t level_index);
|
||||
uint32_t getYFlip();
|
||||
uint32_t getIsEtc1s();
|
||||
basis_texture_type getTextureType();
|
||||
uint32_t getImageTranscodedSizeInBytes(uint32_t image_index, uint32_t level_index, uint32_t format);
|
||||
uint32_t startTranscoding();
|
||||
uint32_t transcodeImage(void* dst, uint32_t dst_size, uint32_t image_index, uint32_t level_index, uint32_t format, uint32_t pvrtc_wrap_addressing, uint32_t get_alpha_for_opaque_formats);
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
KTX_BASISU_API void ktx_basisu_basis_init();
|
||||
#ifdef KTX_BASISU_C_BINDINGS
|
||||
KTX_BASISU_API basis_file* ktx_basisu_create_basis();
|
||||
KTX_BASISU_API uint32_t ktx_basisu_open_basis( basis_file* basis, const uint8_t * data, uint32_t length );
|
||||
KTX_BASISU_API void ktx_basisu_close_basis( basis_file* basis );
|
||||
KTX_BASISU_API void ktx_basisu_delete_basis( basis_file* basis );
|
||||
KTX_BASISU_API uint32_t ktx_basisu_getHasAlpha( basis_file* basis );
|
||||
KTX_BASISU_API uint32_t ktx_basisu_getNumImages( basis_file* basis );
|
||||
KTX_BASISU_API uint32_t ktx_basisu_getNumLevels( basis_file* basis, uint32_t image_index);
|
||||
KTX_BASISU_API uint32_t ktx_basisu_getImageWidth( basis_file* basis, uint32_t image_index, uint32_t level_index);
|
||||
KTX_BASISU_API uint32_t ktx_basisu_getImageHeight( basis_file* basis, uint32_t image_index, uint32_t level_index);
|
||||
KTX_BASISU_API uint32_t ktx_basisu_get_y_flip( basis_file* basis );
|
||||
KTX_BASISU_API uint32_t ktx_basisu_get_is_etc1s( basis_file* basis );
|
||||
KTX_BASISU_API basis_texture_type ktx_basisu_get_texture_type( basis_file* basis );
|
||||
KTX_BASISU_API uint32_t ktx_basisu_getImageTranscodedSizeInBytes( basis_file* basis, uint32_t image_index, uint32_t level_index, uint32_t format);
|
||||
KTX_BASISU_API uint32_t ktx_basisu_startTranscoding( basis_file* basis );
|
||||
KTX_BASISU_API uint32_t ktx_basisu_transcodeImage( basis_file* basis, void* dst, uint32_t dst_size, uint32_t image_index, uint32_t level_index, uint32_t format, uint32_t pvrtc_wrap_addressing, uint32_t get_alpha_for_opaque_formats);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
// Copyright 2019 Andreas Atteneder, All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <basisu_transcoder.h>
|
||||
|
||||
#include "basisu_c_binding.h"
|
||||
#include "unused.h"
|
||||
|
||||
using namespace basist;
|
||||
|
||||
#define MAGIC 0xDEADBEE1
|
||||
|
||||
bool basis_file::open(const uint8_t *buffer, uint32_t newByteLength) {
|
||||
m_file = buffer;
|
||||
byteLength = newByteLength;
|
||||
|
||||
if (!m_transcoder.validate_header(buffer, newByteLength)) {
|
||||
m_file = nullptr;
|
||||
byteLength = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_transcoder.get_file_info(m_file, byteLength, fileinfo))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialized after validation
|
||||
m_magic = MAGIC;
|
||||
return true;
|
||||
}
|
||||
|
||||
void basis_file::close() {
|
||||
assert(m_magic == MAGIC);
|
||||
m_file = nullptr;
|
||||
byteLength = 0;
|
||||
}
|
||||
|
||||
uint32_t basis_file::getHasAlpha() {
|
||||
assert(m_magic == MAGIC);
|
||||
if (m_magic != MAGIC)
|
||||
return 0;
|
||||
|
||||
return fileinfo.m_has_alpha_slices;
|
||||
}
|
||||
|
||||
uint32_t basis_file::getNumImages() {
|
||||
assert(m_magic == MAGIC);
|
||||
if (m_magic != MAGIC)
|
||||
return 0;
|
||||
|
||||
return m_transcoder.get_total_images(m_file, byteLength);
|
||||
}
|
||||
|
||||
uint32_t basis_file::getNumLevels(uint32_t image_index) {
|
||||
assert(m_magic == MAGIC);
|
||||
if (m_magic != MAGIC)
|
||||
return 0;
|
||||
|
||||
return fileinfo.m_image_mipmap_levels[image_index];
|
||||
}
|
||||
|
||||
uint32_t basis_file::getImageWidth(uint32_t image_index, uint32_t level_index) {
|
||||
assert(m_magic == MAGIC);
|
||||
if (m_magic != MAGIC)
|
||||
return 0;
|
||||
|
||||
uint32_t orig_width, orig_height, total_blocks;
|
||||
if (!m_transcoder.get_image_level_desc(m_file, byteLength,
|
||||
image_index, level_index,
|
||||
orig_width, orig_height,
|
||||
total_blocks))
|
||||
return 0;
|
||||
|
||||
return orig_width;
|
||||
}
|
||||
|
||||
uint32_t basis_file::getImageHeight(uint32_t image_index, uint32_t level_index) {
|
||||
assert(m_magic == MAGIC);
|
||||
if (m_magic != MAGIC)
|
||||
return 0;
|
||||
|
||||
uint32_t orig_width, orig_height, total_blocks;
|
||||
if (!m_transcoder.get_image_level_desc(m_file, byteLength,
|
||||
image_index, level_index,
|
||||
orig_width, orig_height,
|
||||
total_blocks))
|
||||
return 0;
|
||||
|
||||
return orig_height;
|
||||
}
|
||||
|
||||
uint32_t basis_file::getYFlip() {
|
||||
assert(m_magic == MAGIC);
|
||||
return fileinfo.m_y_flipped;
|
||||
}
|
||||
|
||||
uint32_t basis_file::getIsEtc1s() {
|
||||
assert(m_magic == MAGIC);
|
||||
return fileinfo.m_etc1s;
|
||||
}
|
||||
|
||||
basis_texture_type basis_file::getTextureType() {
|
||||
assert(m_magic == MAGIC);
|
||||
return fileinfo.m_tex_type;
|
||||
}
|
||||
|
||||
uint32_t basis_file::getImageTranscodedSizeInBytes(uint32_t image_index, uint32_t level_index, uint32_t format) {
|
||||
assert(m_magic == MAGIC);
|
||||
if (m_magic != MAGIC)
|
||||
return 0;
|
||||
|
||||
if (format >= (uint32_t) basist::transcoder_texture_format::cTFTotalTextureFormats)
|
||||
return 0;
|
||||
|
||||
uint32_t orig_width, orig_height, total_blocks;
|
||||
if (!m_transcoder.get_image_level_desc(m_file, (uint32_t)byteLength,
|
||||
image_index, level_index, orig_width,
|
||||
orig_height, total_blocks))
|
||||
return 0;
|
||||
|
||||
const transcoder_texture_format transcoder_format = static_cast<transcoder_texture_format>(format);
|
||||
|
||||
if (basis_transcoder_format_is_uncompressed(transcoder_format))
|
||||
{
|
||||
// Uncompressed formats are just plain raster images.
|
||||
const uint32_t bytes_per_pixel = basis_get_uncompressed_bytes_per_pixel(transcoder_format);
|
||||
const uint32_t bytes_per_line = orig_width * bytes_per_pixel;
|
||||
const uint32_t bytes_per_slice = bytes_per_line * orig_height;
|
||||
return bytes_per_slice;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compressed formats are 2D arrays of blocks.
|
||||
const uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(transcoder_format);
|
||||
|
||||
if (transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGB || transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGBA)
|
||||
{
|
||||
// For PVRTC1, Basis only writes (or requires) total_blocks * bytes_per_block. But GL requires extra padding for very small textures:
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt
|
||||
const uint32_t width = (orig_width + 3) & ~3;
|
||||
const uint32_t height = (orig_height + 3) & ~3;
|
||||
const uint32_t size_in_bytes = (std::max(8U, width) * std::max(8U, height) * 4 + 7) / 8;
|
||||
return size_in_bytes;
|
||||
}
|
||||
|
||||
return total_blocks * bytes_per_block;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t basis_file::startTranscoding() {
|
||||
assert(m_magic == MAGIC);
|
||||
if (m_magic != MAGIC)
|
||||
return 0;
|
||||
|
||||
return m_transcoder.start_transcoding(m_file, byteLength);
|
||||
}
|
||||
|
||||
uint32_t basis_file::transcodeImage(void* dst, uint32_t dst_size, uint32_t image_index, uint32_t level_index, uint32_t format, uint32_t /*pvrtc_wrap_addressing*/, uint32_t get_alpha_for_opaque_formats) {
|
||||
assert(m_magic == MAGIC);
|
||||
if (m_magic != MAGIC)
|
||||
return 0;
|
||||
|
||||
if (format >= (uint32_t) basist::transcoder_texture_format::cTFTotalTextureFormats)
|
||||
return 0;
|
||||
|
||||
const transcoder_texture_format transcoder_format = static_cast<transcoder_texture_format>(format);
|
||||
|
||||
uint32_t orig_width, orig_height, total_blocks;
|
||||
if (!m_transcoder.get_image_level_desc(m_file, byteLength, image_index, level_index, orig_width, orig_height, total_blocks))
|
||||
return 0;
|
||||
|
||||
uint32_t flags = get_alpha_for_opaque_formats ? cDecodeFlagsTranscodeAlphaDataToOpaqueFormats : 0;
|
||||
|
||||
uint32_t status;
|
||||
|
||||
if (basis_transcoder_format_is_uncompressed(transcoder_format))
|
||||
{
|
||||
MAYBE_UNUSED const uint32_t bytes_per_pixel = basis_get_uncompressed_bytes_per_pixel(transcoder_format);
|
||||
MAYBE_UNUSED const uint32_t bytes_per_line = orig_width * bytes_per_pixel;
|
||||
MAYBE_UNUSED const uint32_t bytes_per_slice = bytes_per_line * orig_height;
|
||||
|
||||
assert(bytes_per_slice <= dst_size);
|
||||
|
||||
status = m_transcoder.transcode_image_level(
|
||||
m_file, byteLength, image_index, level_index,
|
||||
dst, orig_width * orig_height,
|
||||
transcoder_format,
|
||||
flags,
|
||||
orig_width,
|
||||
nullptr,
|
||||
orig_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(transcoder_format);
|
||||
|
||||
MAYBE_UNUSED uint32_t required_size = total_blocks * bytes_per_block;
|
||||
|
||||
if (transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGB || transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGBA)
|
||||
{
|
||||
// For PVRTC1, Basis only writes (or requires) total_blocks * bytes_per_block. But GL requires extra padding for very small textures:
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt
|
||||
// The transcoder will clear the extra bytes followed the used blocks to 0.
|
||||
const uint32_t width = (orig_width + 3) & ~3;
|
||||
const uint32_t height = (orig_height + 3) & ~3;
|
||||
required_size = (std::max(8U, width) * std::max(8U, height) * 4 + 7) / 8;
|
||||
assert(required_size >= total_blocks * bytes_per_block);
|
||||
}
|
||||
|
||||
assert(required_size <= dst_size);
|
||||
|
||||
status = m_transcoder.transcode_image_level(
|
||||
m_file, byteLength, image_index, level_index,
|
||||
dst, dst_size / bytes_per_block,
|
||||
static_cast<basist::transcoder_texture_format>(format),
|
||||
flags);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
KTX_BASISU_API void ktx_basisu_basis_init()
|
||||
{
|
||||
basisu_transcoder_init();
|
||||
}
|
||||
|
||||
#ifdef KTX_BASISU_C_BINDINGS
|
||||
|
||||
KTX_BASISU_API basis_file* ktx_basisu_create_basis() {
|
||||
basis_file* new_basis = new basis_file();
|
||||
return new_basis;
|
||||
}
|
||||
|
||||
KTX_BASISU_API uint32_t ktx_basisu_open_basis( basis_file* basis, const uint8_t * data, uint32_t length ) {
|
||||
return basis->open(data,length);
|
||||
}
|
||||
|
||||
KTX_BASISU_API void ktx_basisu_close_basis( basis_file* basis ) {
|
||||
basis->close();
|
||||
}
|
||||
|
||||
KTX_BASISU_API void ktx_basisu_delete_basis( basis_file* basis ) {
|
||||
delete basis;
|
||||
}
|
||||
|
||||
KTX_BASISU_API uint32_t ktx_basisu_getHasAlpha( basis_file* basis ) {
|
||||
assert(basis!=nullptr);
|
||||
return (bool)basis->getHasAlpha();
|
||||
}
|
||||
|
||||
KTX_BASISU_API uint32_t ktx_basisu_getNumImages( basis_file* basis ) {
|
||||
return basis->getNumImages();
|
||||
}
|
||||
|
||||
KTX_BASISU_API uint32_t ktx_basisu_getNumLevels( basis_file* basis, uint32_t image_index) {
|
||||
return basis->getNumLevels(image_index);
|
||||
}
|
||||
|
||||
KTX_BASISU_API uint32_t ktx_basisu_getImageWidth( basis_file* basis, uint32_t image_index, uint32_t level_index) {
|
||||
return basis->getImageWidth(image_index,level_index);
|
||||
}
|
||||
|
||||
KTX_BASISU_API uint32_t ktx_basisu_getImageHeight( basis_file* basis, uint32_t image_index, uint32_t level_index) {
|
||||
return basis->getImageHeight(image_index,level_index);
|
||||
}
|
||||
|
||||
KTX_BASISU_API uint32_t ktx_basisu_get_y_flip( basis_file* basis ) {
|
||||
return basis->getYFlip();
|
||||
}
|
||||
|
||||
KTX_BASISU_API uint32_t ktx_basisu_get_is_etc1s( basis_file* basis ) {
|
||||
return basis->getIsEtc1s();
|
||||
}
|
||||
|
||||
KTX_BASISU_API basis_texture_type ktx_basisu_get_texture_type( basis_file* basis ) {
|
||||
return basis->getTextureType();
|
||||
}
|
||||
|
||||
KTX_BASISU_API uint32_t ktx_basisu_getImageTranscodedSizeInBytes( basis_file* basis, uint32_t image_index, uint32_t level_index, uint32_t format) {
|
||||
return basis->getImageTranscodedSizeInBytes(image_index,level_index,format);
|
||||
}
|
||||
|
||||
KTX_BASISU_API uint32_t ktx_basisu_startTranscoding( basis_file* basis ) {
|
||||
return basis->startTranscoding();
|
||||
}
|
||||
|
||||
KTX_BASISU_API uint32_t ktx_basisu_transcodeImage( basis_file* basis, void* dst, uint32_t dst_size, uint32_t image_index, uint32_t level_index, uint32_t format, uint32_t pvrtc_wrap_addressing, uint32_t get_alpha_for_opaque_formats) {
|
||||
return basis->transcodeImage(dst,dst_size,image_index,level_index,format,pvrtc_wrap_addressing,get_alpha_for_opaque_formats);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // END extern "C"
|
||||
@@ -0,0 +1,127 @@
|
||||
# Copyright (c) 2021, Shukant Pal and Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# This ugliness is to workaround that, unless JAVA_HOME is explicitly set,
|
||||
# FindJNI tries to find a Java framework even though recent Java versions,
|
||||
# such as we need, are not Frameworks so the search fails. This is still the
|
||||
# case in CMake 3.19.2. Even if this is fixed in the latest CMake, due to some
|
||||
# of our CI build environments we can't use it.
|
||||
if(DEFINED CMAKE_FIND_FRAMEWORK)
|
||||
set(SAVED_CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK})
|
||||
endif()
|
||||
set(CMAKE_FIND_FRAMEWORK NEVER)
|
||||
find_package(JNI 11 REQUIRED)
|
||||
if(DEFINED SAVED_CMAKE_FIND_FRAMEWORK)
|
||||
set(CMAKE_FIND_FRAMEWORK ${SAVED_CMAKE_FIND_FRAMEWORK})
|
||||
endif()
|
||||
if(WIN32)
|
||||
set(maven_cmd "mvn.cmd")
|
||||
else()
|
||||
set(maven_cmd "mvn")
|
||||
endif()
|
||||
find_program(MAVEN_EXECUTABLE ${maven_cmd}
|
||||
DOC "Location of `mvn` or (on Windows) `mvn.cmd` executable"
|
||||
PATHS $ENV{PATH}
|
||||
)
|
||||
|
||||
add_library(ktx-jni SHARED
|
||||
src/main/cpp/KtxTexture.cpp
|
||||
src/main/cpp/KtxTexture1.cpp
|
||||
src/main/cpp/KtxTexture2.cpp
|
||||
src/main/cpp/KtxErrorCode.cpp
|
||||
src/main/cpp/libktx-jni.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ktx-jni.manifest
|
||||
)
|
||||
|
||||
configure_file(src/main/cpp/ktx-jni.manifest.in ktx-jni.manifest)
|
||||
|
||||
target_include_directories(ktx-jni SYSTEM PRIVATE
|
||||
${JNI_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set_target_properties(ktx-jni PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY ${KTX_BUILD_DIR}/$<CONFIG>
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME "YES"
|
||||
)
|
||||
# The location of libktx_jni must be set explicitly in java.library.path.
|
||||
# This sets places to search for libktx when loading libktx_jni. Setting
|
||||
# "./" here, to say look in the same directory, does not work.
|
||||
if(APPLE)
|
||||
set_target_properties(ktx-jni PROPERTIES
|
||||
# @executable_path does not work as the executable here is the JVM.
|
||||
INSTALL_RPATH "@loader_path;/usr/local/${CMAKE_INSTALL_LIBDIR}"
|
||||
)
|
||||
elseif(LINUX)
|
||||
set_target_properties(ktx-jni PROPERTIES
|
||||
# Reportedly ld.so when loading a .so with a DT_RUNPATH of $ORIGIN
|
||||
# searches first in the directory of the .so then in the directory
|
||||
# of the application that is loading the first .so. See
|
||||
# https://stackoverflow.com/questions/23006930/the-shared-library-rpath-and-the-binary-rpath-priority/52647116#52647116
|
||||
INSTALL_RPATH "$ORIGIN;/usr/local/${CMAKE_INSTALL_LIBDIR}"
|
||||
)
|
||||
endif()
|
||||
set_code_sign(ktx-jni)
|
||||
|
||||
if(APPLE AND KTX_EMBED_BITCODE)
|
||||
target_compile_options(ktx-jni PRIVATE "-fembed-bitcode")
|
||||
endif()
|
||||
|
||||
target_include_directories(ktx-jni PRIVATE include)
|
||||
|
||||
target_link_libraries(ktx-jni ktx)
|
||||
|
||||
if(APPLE OR LINUX)
|
||||
install(TARGETS ktx-jni
|
||||
LIBRARY
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
COMPONENT jni
|
||||
)
|
||||
else()
|
||||
install(TARGETS ktx-jni LIBRARY
|
||||
RUNTIME
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
COMPONENT jni
|
||||
)
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${CMAKE_SOURCE_DIR}/interface/java_binding/target/libktx-${PROJECT_VERSION}-sources.jar
|
||||
${CMAKE_SOURCE_DIR}/interface/java_binding/target/libktx-${PROJECT_VERSION}.jar
|
||||
COMMAND
|
||||
${MAVEN_EXECUTABLE} --quiet -Drevision=${PROJECT_VERSION} -Dmaven.test.skip=true package
|
||||
DEPENDS
|
||||
ktx-jni
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/interface/java_binding
|
||||
)
|
||||
|
||||
add_custom_target( ktx-jar ALL
|
||||
DEPENDS
|
||||
${CMAKE_SOURCE_DIR}/interface/java_binding/target/libktx-${PROJECT_VERSION}-sources.jar
|
||||
${CMAKE_SOURCE_DIR}/interface/java_binding/target/libktx-${PROJECT_VERSION}.jar
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_SOURCE_DIR}/interface/java_binding
|
||||
COMMENT
|
||||
"Java wrapper target"
|
||||
)
|
||||
|
||||
install(FILES
|
||||
${CMAKE_SOURCE_DIR}/interface/java_binding/target/libktx-${PROJECT_VERSION}.jar
|
||||
TYPE LIB
|
||||
COMPONENT jni
|
||||
)
|
||||
|
||||
add_test( NAME Java-wrapper
|
||||
COMMAND ${MAVEN_EXECUTABLE} --quiet -Drevision=${PROJECT_VERSION} test
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/interface/java_binding
|
||||
)
|
||||
|
||||
set_tests_properties(
|
||||
Java-wrapper
|
||||
PROPERTIES
|
||||
ENVIRONMENT _JAVA_OPTIONS=-Djava.library.path=$<TARGET_GENEX_EVAL:ktx-jni,$<TARGET_PROPERTY:ktx-jni,LIBRARY_OUTPUT_DIRECTORY>>
|
||||
)
|
||||
|
||||
# vim:ai:ts=4:sts=4:sw=2:expandtab
|
||||
@@ -0,0 +1,74 @@
|
||||
Copyright (c) 2021, Shukant Pal and Contributors \
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Java bindings for [libktx](https://github.com/KhronosGroup/KTX-Software), made with love by [Shukant Pal](https://github.com/ShukantPal) originally for the [Texture Compression Tool](https://compressor.shukantpal.com).
|
||||
|
||||
The `libktx-jni` library is built by the CMake project in the repository root. This library glues the `libktx` API with the interfaces provided in this Java library. You'll need to install `libktx` and `libktx-jni` to use the bindings. These, together with the Java archive `libktx.jar` can be installed from the packages found on the [KTX Software Releases](https://github.com/KhronosGroup/KTX-Software/releases) page.
|
||||
|
||||
Note: Java does not support arrays with more than 2³² elements so you should not use this library for images larger than four gigabytes in size.
|
||||
|
||||
## Usage
|
||||
|
||||
The setup is as follows:
|
||||
|
||||
```java
|
||||
import org.khronos.ktx.KtxTexture2;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class App {
|
||||
static {
|
||||
// Load libktx-jni, which provides the JNI stubs for natively
|
||||
// implemented Java methods. This should also load libktx
|
||||
// automatically! If it doesn't, you may need to load libktx manually.
|
||||
System.loadLibrary("ktx-jni");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
KTXTexture2 texture = KTXTexture2.createFromNamedFile(
|
||||
Paths.get("exampleInput.ktx2").toAbsolutePath().toString());
|
||||
|
||||
// Do something special with the texture!
|
||||
|
||||
texture.writeToNamedFile(
|
||||
Paths.get("exampleOutput.ktx2").toAbsolutePath().toString());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
You must have Maven installed.
|
||||
|
||||
Pass `-DKTX_FEATURE_JNI=ON` when configuring the CMake build for `libktx` so that `libktx-jni` and `libktx.jar` are built as well.
|
||||
|
||||
This will place the libraries in a sub-directory of the build directory you
|
||||
configured with CMake corresponding to the configuration you are building, usually `Debug` or `Release` and the JAR in the `target` directory in `interfaces/java_binding`. When building your application, include this JAR in the build.
|
||||
|
||||
The installers install the JAR is the same library directory as `libktx` and `libktx-jni`. On GNU/Linux and macOS this is `/usr/local/lib`.
|
||||
|
||||
## Manually Build JAR
|
||||
|
||||
You must have Maven installed:
|
||||
|
||||
```
|
||||
mvn package
|
||||
```
|
||||
|
||||
The JAR is placed in the location described in the previous section.
|
||||
|
||||
## Run tests on macOS
|
||||
|
||||
It's tricky - I know.
|
||||
|
||||
```
|
||||
_JAVA_OPTIONS=-Djava.library.path=/usr/local/lib mvn test
|
||||
```
|
||||
|
||||
The path shown above is for the case when `libktx` and `libktx-jni` have been installed. If you have only built them then use
|
||||
|
||||
```
|
||||
_JAVA_OPTIONS=-Djava.library.path=/path/to/your/cmake/build/<config> mvn test
|
||||
```
|
||||
|
||||
where `<config>` is your build configuration, usually either `Debug` or `Release`.
|
||||
@@ -0,0 +1,226 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (c) 2021, Shukant Pal and Contributors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<properties>
|
||||
<revision>4.0.0-SNAPSHOT</revision>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<groupId>org.khronos</groupId>
|
||||
<artifactId>libktx</artifactId>
|
||||
<version>${revision}</version> <!-- property MUST be "revision". -->
|
||||
<name>libktx</name>
|
||||
<description>Java Bindings for Khronos KTX Library</description>
|
||||
<url>https://www.khronos.org/ktx/</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Shukant Pal</name>
|
||||
<email>shukantpal@outlook.com</email>
|
||||
<organization>Khronos Group</organization>
|
||||
<organizationUrl>http://www.khronos.org</organizationUrl>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/KhronosGroup/KTX-Software.git</connection>
|
||||
<developerConnection>scm:git:ssh://github.com:KhronosGroup/KTX-Software.git</developerConnection>
|
||||
<url>https://github.com/KhronosGroup/KTX-Software/tree/main</url>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
|
||||
<plugins>
|
||||
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</plugin>
|
||||
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</plugin>
|
||||
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
|
||||
<plugin>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<version>3.7.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>2.5.3</version>
|
||||
<configuration>
|
||||
<localCheckout>true</localCheckout>
|
||||
<pushChanges>false</pushChanges>
|
||||
<mavenExecutorId>forked-path</mavenExecutorId>
|
||||
<arguments>-Dgpg.passphrase=${gpg.passphrase}</arguments>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.scm</groupId>
|
||||
<artifactId>maven-scm-provider-gitexe</artifactId>
|
||||
<version>1.9.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>1.6.7</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>ossrh</serverId>
|
||||
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!--
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>properties-maven-plugin</artifactId>
|
||||
<version>1.0-alpha-1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>initialize</phase>
|
||||
<goals>
|
||||
<goal>read-project-properties</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<files>
|
||||
<file>version.properties</file>
|
||||
</files>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
-->
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<!-- GPG Signature on release -->
|
||||
<profile>
|
||||
<id>release-sign-artifacts</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>performRelease</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.6</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<!-- Uncomment when we have access to Maven Central-->
|
||||
<!--
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>ossrh</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>ossrh</id>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
-->
|
||||
</project>
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Khronos Group and Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.khronos.ktx;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class KtxUtilitiesTest {
|
||||
@Test
|
||||
public void testValidSwizzle() {
|
||||
char swizzle[] = new char[] { 'a', '1', 'r', '0' };
|
||||
char expected[] = swizzle;
|
||||
char actual[] = KtxUtilities.validateSwizzle(swizzle);
|
||||
assertArrayEquals(expected, actual, "Accepts valid swizzle and returns it");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullSwizzle() {
|
||||
char expected[] = new char[] { 0, 0, 0, 0 };
|
||||
char actual[] = KtxUtilities.validateSwizzle(null);
|
||||
assertArrayEquals(expected, actual, "Accepts null swizzle (to apply no swizzle), and returns a default");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultSwizzle() {
|
||||
char swizzle[] = new char[] { 0, 0, 0, 0 };
|
||||
char expected[] = swizzle;
|
||||
char actual[] = KtxUtilities.validateSwizzle(swizzle);
|
||||
assertArrayEquals(expected, actual, "Accepts default swizzle (all zeros)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidSwizzleLength() throws IOException {
|
||||
char swizzle[] = new char[] { 'a', 'b', 'r', 'g', 'r', 'g' };
|
||||
assertThrows(IllegalArgumentException.class, () -> KtxUtilities.validateSwizzle(swizzle),
|
||||
"Swizzle length != 4 expected to throw IllegalArgumentException");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidSwizzleChar() throws IOException {
|
||||
|
||||
char swizzle[] = new char[] { 'a', 'b', 'X', 'g' };
|
||||
assertThrows(IllegalArgumentException.class, () -> KtxUtilities.validateSwizzle(swizzle),
|
||||
"Invalid swizzle character expected to throw IllegalArgumentException");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Shukant Pal, robnugent, and Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.khronos.ktx.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.khronos.ktx.*;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ExtendWith({ KtxTestLibraryLoader.class })
|
||||
public class KtxParallelTest {
|
||||
private static final int NUM_THREADS = 2;
|
||||
private static final Logger logger = Logger.getLogger(KtxParallelTest.class.getCanonicalName());
|
||||
|
||||
@Test
|
||||
public void testParallelAstcConversion() throws InterruptedException {
|
||||
final Thread[] runThreads = new Thread[NUM_THREADS];
|
||||
|
||||
for (int i = 0; i < NUM_THREADS; i++) {
|
||||
final KtxTestRun run = new KtxTestRun(i);
|
||||
final Thread runThread = new Thread(run);
|
||||
runThread.setDaemon(false);
|
||||
runThread.start();
|
||||
|
||||
runThreads[i] = runThread;
|
||||
}
|
||||
|
||||
for (Thread thread : runThreads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
private static class KtxTestRun implements Runnable {
|
||||
private final int id;
|
||||
private final Random testRandomizer = new Random();
|
||||
|
||||
public KtxTestRun(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
// Repeatedly create a compress an image.
|
||||
for (int i = 0; i < 30; i++) {
|
||||
final int w = (testRandomizer.nextInt() % 512) + 1024;
|
||||
final int h = w;
|
||||
final int size = convertToASTC(w, h);
|
||||
|
||||
// Change level to INFO for logging
|
||||
logger.log(Level.FINE,id + " iteration: " + i + ", size: " + w + "x" + h + ", compressed data size is " + size);
|
||||
}
|
||||
}
|
||||
|
||||
public int convertToASTC(int w, int h) {
|
||||
// Create Uncompressed texture
|
||||
final KtxTextureCreateInfo info = new KtxTextureCreateInfo();
|
||||
info.setBaseWidth(w);
|
||||
info.setBaseHeight(h);
|
||||
info.setVkFormat(VkFormat.VK_FORMAT_R8G8B8_SRGB); // Uncompressed
|
||||
final KtxTexture2 t = KtxTexture2.create(info, KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
|
||||
// Pass the uncompressed data
|
||||
int bufferSize = w * h * 3;
|
||||
final byte[] rgbBA = new byte[bufferSize];
|
||||
t.setImageFromMemory(0, 0, 0, rgbBA);
|
||||
|
||||
// Compress the data
|
||||
final KtxAstcParams p = new KtxAstcParams();
|
||||
p.setBlockDimension(KtxPackAstcBlockDimension.D8x8);
|
||||
p.setMode(KtxPackAstcEncoderMode.LDR);
|
||||
p.setQualityLevel(KtxPackAstcQualityLevel.EXHAUSTIVE);
|
||||
final int rc = t.compressAstcEx(p);
|
||||
if (rc != KtxErrorCode.SUCCESS) {
|
||||
throw new RuntimeException("ASTC error " + rc);
|
||||
}
|
||||
final int retDataLen = (int) t.getDataSize();
|
||||
|
||||
// Free things up - segfault usually occurs inside this destroy() call
|
||||
t.destroy();
|
||||
|
||||
return retDataLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Shukant Pal and Contributors
|
||||
* Copyright (c) 2024, Khronos Group and Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.khronos.ktx.test;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.jupiter.api.extension.BeforeAllCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
|
||||
/**
|
||||
* A class that will be used for extending the unit tests, and allow using the
|
||||
* native <code>ktx</code> and <code>ktx-jni</code> libraries from <i>any</i>
|
||||
* directory for the tests.
|
||||
*
|
||||
* (Usually, this will be a local build output directory)
|
||||
*
|
||||
* It will check the <code>LIBKTX_BINARY_DIR<code> environment variable. If this
|
||||
* environment variable is a directory that contains the KTX JNI library, then
|
||||
* this library will be loaded.
|
||||
*
|
||||
* Otherwise, it will load the KTX JNI library that was installed globally with
|
||||
* the usual installation procedure.
|
||||
*/
|
||||
public class KtxTestLibraryLoader implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {
|
||||
private static boolean started = false;
|
||||
|
||||
@Override
|
||||
public void beforeAll(final ExtensionContext context) throws Exception {
|
||||
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
started = true;
|
||||
|
||||
String ktxJniLibrary = findKtxJniLibraryName();
|
||||
if (ktxJniLibrary != null) {
|
||||
System.load(ktxJniLibrary);
|
||||
} else {
|
||||
System.loadLibrary("ktx-jni");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find the name (full, absolute path) of the KTX JNI library that should
|
||||
* be loaded.
|
||||
*
|
||||
* This method will search for the library in the directory that is defined via
|
||||
* the <code>LIBKTX_BINARY_DIR</code> environment variable. If this variable is
|
||||
* not defined, or no suitable library can be found, then <code>null</code> is
|
||||
* returned.
|
||||
*
|
||||
* @return The KTX JNI library name
|
||||
*/
|
||||
private static String findKtxJniLibraryName() {
|
||||
String ktxDir = System.getenv("LIBKTX_BINARY_DIR");
|
||||
if (ktxDir == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Path ktxPath = Path.of(ktxDir);
|
||||
if (!ktxPath.isAbsolute() || !Files.exists(ktxPath) || !Files.isDirectory(ktxPath)) {
|
||||
System.out.println(
|
||||
"KTXTestLibraryLoader: The value of the LIBKTX_BINARY_DIR environment variable is invalid: "
|
||||
+ ktxDir);
|
||||
return null;
|
||||
}
|
||||
|
||||
String expectedKtxJniLibraryName = isRunningOnWindows() ? "ktx-jni" : "libktx-jni";
|
||||
|
||||
System.out.println("KTXTestLibraryLoader: Loading KTX libraries from " + ktxDir);
|
||||
File ktxDirFile = new File(ktxDir);
|
||||
for (File file : ktxDirFile.listFiles()) {
|
||||
if (!file.isFile()) {
|
||||
continue;
|
||||
}
|
||||
String[] tokens = file.getName().split("\\.");
|
||||
if (tokens.length == 2 && tokens[0].equals(expectedKtxJniLibraryName)) {
|
||||
|
||||
String ktxJniLibrary = file.getAbsolutePath();
|
||||
System.out.println("KTXTestLibraryLoader: Found " + expectedKtxJniLibraryName + " at " + ktxJniLibrary);
|
||||
return ktxJniLibrary;
|
||||
}
|
||||
}
|
||||
System.out
|
||||
.println("KTXTestLibraryLoader: Could not find " + expectedKtxJniLibraryName + " in given directory");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the <code>os.name</code> system property indicates that the
|
||||
* operating system is Windows.
|
||||
*
|
||||
* @return Whether the operating system is Windows
|
||||
*/
|
||||
private static boolean isRunningOnWindows() {
|
||||
String osName = System.getProperty("os.name");
|
||||
osName = osName.toLowerCase(Locale.ENGLISH);
|
||||
return osName.startsWith("windows");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// NOOP
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Shukant Pal and Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.khronos.ktx.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.khronos.ktx.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ExtendWith({ KtxTestLibraryLoader.class })
|
||||
public class KtxTexture1Test {
|
||||
@Test
|
||||
public void testCreateFromNamedFile() {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/etc1.ktx")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
KtxTexture1 texture = KtxTexture1.createFromNamedFile(testKtxFile.toString(),
|
||||
KtxTextureCreateFlagBits.NO_FLAGS);
|
||||
|
||||
assertNotNull(texture);
|
||||
assertEquals(texture.getGlInternalformat(), KtxInternalformat.GL_ETC1_RGB8_OES);
|
||||
assertEquals(texture.isArray(), false);
|
||||
assertEquals(texture.isCompressed(), true);
|
||||
assertEquals(texture.getGenerateMipmaps(), false);
|
||||
assertEquals(texture.getNumLevels(), 1);
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteToNamedFile() throws IOException {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/etc2-rgb.ktx")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
File copyFile = File.createTempFile("copyktx", ".ktx");
|
||||
|
||||
KtxTexture1 texture = KtxTexture1.createFromNamedFile(testKtxFile.toString(), KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT);
|
||||
assertNotNull(texture);
|
||||
|
||||
int result = texture.writeToNamedFile(copyFile.getAbsolutePath().toString());
|
||||
assertEquals(result, KtxErrorCode.SUCCESS);
|
||||
|
||||
byte[] original = Files.readAllBytes(testKtxFile);
|
||||
byte[] copy = Files.readAllBytes(copyFile.toPath());
|
||||
|
||||
assertArrayEquals(copy, original);
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteToMemory() throws IOException {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/etc2-rgba1.ktx")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
KtxTexture1 texture = KtxTexture1.createFromNamedFile(testKtxFile.toString(), KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT);
|
||||
assertNotNull(texture);
|
||||
|
||||
byte[] file = Files.readAllBytes(testKtxFile);
|
||||
byte[] data = texture.writeToMemory();
|
||||
|
||||
assertArrayEquals(file, data);
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetData() throws IOException {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/etc2-rgba1.ktx")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
KtxTexture1 texture = KtxTexture1.createFromNamedFile(testKtxFile.toString(), KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT);
|
||||
assertNotNull(texture);
|
||||
|
||||
byte[] data = texture.getData();
|
||||
byte[] file = Files.readAllBytes(testKtxFile);
|
||||
int level0Size = texture.getImageSize(0);
|
||||
|
||||
for (int i = 0; i < level0Size; i++) {
|
||||
assertEquals(data[data.length - 1 - i], file[file.length - 1 - i]);
|
||||
}
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() {
|
||||
KtxTextureCreateInfo info = new KtxTextureCreateInfo();
|
||||
|
||||
info.setGlInternalformat(KtxInternalformat.GL_COMPRESSED_RGBA_ASTC_4x4_KHR);
|
||||
info.setBaseWidth(10);
|
||||
info.setBaseHeight(10);
|
||||
|
||||
KtxTexture1 texture = KtxTexture1.create(info, KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
assertNotNull(texture);
|
||||
|
||||
byte[] imageData = new byte[10 * 10];
|
||||
texture.setImageFromMemory(0, 0, 0, imageData);
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,699 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Shukant Pal and Contributors
|
||||
* Copyright (c) 2024, Khronos Group and Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.khronos.ktx.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.khronos.ktx.KtxBasisParams;
|
||||
import org.khronos.ktx.KtxTextureCreateStorage;
|
||||
import org.khronos.ktx.KtxErrorCode;
|
||||
import org.khronos.ktx.KtxException;
|
||||
import org.khronos.ktx.KtxSupercmpScheme;
|
||||
import org.khronos.ktx.KtxTexture2;
|
||||
import org.khronos.ktx.KtxTextureCreateFlagBits;
|
||||
import org.khronos.ktx.KtxTextureCreateInfo;
|
||||
import org.khronos.ktx.KtxTranscodeFormat;
|
||||
import org.khronos.ktx.VkFormat;
|
||||
|
||||
@ExtendWith({ KtxTestLibraryLoader.class })
|
||||
public class KtxTexture2Test {
|
||||
|
||||
@Test
|
||||
public void testCreateFromNamedFile() {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/astc_ldr_4x4_FlightHelmet_baseColor.ktx2")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
KtxTexture2 texture = KtxTexture2.createFromNamedFile(testKtxFile.toString(),
|
||||
KtxTextureCreateFlagBits.NO_FLAGS);
|
||||
|
||||
assertNotNull(texture);
|
||||
assertEquals(texture.getNumLevels(), 1);
|
||||
assertEquals(texture.getNumFaces(), 1);
|
||||
assertEquals(texture.getVkFormat(), VkFormat.VK_FORMAT_ASTC_4x4_SRGB_BLOCK);
|
||||
assertEquals(texture.getBaseWidth(), 2048);
|
||||
assertEquals(texture.getBaseHeight(), 2048);
|
||||
assertEquals(texture.getSupercompressionScheme(), KtxSupercmpScheme.NONE);
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithInvalidParameters() {
|
||||
|
||||
KtxTextureCreateInfo info = new KtxTextureCreateInfo();
|
||||
info.setBaseWidth(128);
|
||||
info.setBaseHeight(128);
|
||||
info.setNumDimensions(-123); // Invalid!
|
||||
info.setVkFormat(VkFormat.VK_FORMAT_R8G8B8A8_SRGB);
|
||||
|
||||
assertThrows(KtxException.class,
|
||||
() -> KtxTexture2.create(info, KtxTextureCreateStorage.ALLOC_STORAGE),
|
||||
"Expected to throw NullPointerException");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFromMemoryBasic() {
|
||||
|
||||
// Create a texture, and write it to memory
|
||||
int sizeX = 512;
|
||||
int sizeY = 512;
|
||||
KtxTextureCreateInfo info = new KtxTextureCreateInfo();
|
||||
info.setBaseWidth(sizeX);
|
||||
info.setBaseHeight(sizeY);
|
||||
info.setVkFormat(VkFormat.VK_FORMAT_R8G8B8A8_SRGB);
|
||||
KtxTexture2 input = KtxTexture2.create(info, KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
byte[] inputMemoryArray = input.writeToMemory();
|
||||
|
||||
// Create the texture from the exact memory
|
||||
int createFlags = KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT;
|
||||
KtxTexture2 t = KtxTexture2.createFromMemory(ByteBuffer.wrap(inputMemoryArray), createFlags);
|
||||
|
||||
// Ensure that the image has the same basic properties
|
||||
// as the one that it was created from
|
||||
assertEquals(t.getBaseWidth(), 512);
|
||||
assertEquals(t.getBaseHeight(), 512);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFromMemoryWithPositionAndLimit() {
|
||||
|
||||
// Create a texture, and write it to memory
|
||||
int sizeX = 512;
|
||||
int sizeY = 512;
|
||||
KtxTextureCreateInfo info = new KtxTextureCreateInfo();
|
||||
info.setBaseWidth(sizeX);
|
||||
info.setBaseHeight(sizeY);
|
||||
info.setVkFormat(VkFormat.VK_FORMAT_R8G8B8A8_SRGB);
|
||||
KtxTexture2 input = KtxTexture2.create(info, KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
byte[] inputMemoryArray = input.writeToMemory();
|
||||
|
||||
// Create a byte buffer that is a bit larger than
|
||||
// the input memory, and put the input memory
|
||||
// into it, at position 50
|
||||
ByteBuffer largeBuffer = ByteBuffer.wrap(new byte[inputMemoryArray.length + 100]);
|
||||
largeBuffer.position(50);
|
||||
largeBuffer.put(inputMemoryArray);
|
||||
|
||||
// Set the position and limit of the buffer to
|
||||
// reflect the range that actually contains
|
||||
// the real input data
|
||||
largeBuffer.position(50);
|
||||
largeBuffer.limit(50 + inputMemoryArray.length);
|
||||
|
||||
// Create the texture from the exact memory
|
||||
int createFlags = KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT;
|
||||
KtxTexture2 t = KtxTexture2.createFromMemory(largeBuffer, createFlags);
|
||||
|
||||
// Ensure that the image has the same basic properties
|
||||
// as the one that it was created from
|
||||
assertEquals(t.getBaseWidth(), 512);
|
||||
assertEquals(t.getBaseHeight(), 512);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFromDirectMemoryWithPositionAndLimit() {
|
||||
|
||||
// Create a texture, and write it to memory
|
||||
int sizeX = 512;
|
||||
int sizeY = 512;
|
||||
KtxTextureCreateInfo info = new KtxTextureCreateInfo();
|
||||
info.setBaseWidth(sizeX);
|
||||
info.setBaseHeight(sizeY);
|
||||
info.setVkFormat(VkFormat.VK_FORMAT_R8G8B8A8_SRGB);
|
||||
KtxTexture2 input = KtxTexture2.create(info, KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
byte[] inputMemoryArray = input.writeToMemory();
|
||||
|
||||
// Create a DIRECT byte buffer that is a bit larger than
|
||||
// the input memory, and put the input memory
|
||||
// into it, at position 50
|
||||
ByteBuffer largeBuffer = ByteBuffer.allocateDirect(inputMemoryArray.length + 100);
|
||||
largeBuffer.position(50);
|
||||
largeBuffer.put(inputMemoryArray);
|
||||
|
||||
// Set the position and limit of the buffer to
|
||||
// reflect the range that actually contains
|
||||
// the real input data
|
||||
largeBuffer.position(50);
|
||||
largeBuffer.limit(50 + inputMemoryArray.length);
|
||||
|
||||
// Create the texture from the exact memory
|
||||
int createFlags = KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT;
|
||||
KtxTexture2 t = KtxTexture2.createFromMemory(largeBuffer, createFlags);
|
||||
|
||||
// Ensure that the image has the same basic properties
|
||||
// as the one that it was created from
|
||||
assertEquals(t.getBaseWidth(), 512);
|
||||
assertEquals(t.getBaseHeight(), 512);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateFromNamedFileWithNull() {
|
||||
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> KtxTexture2.createFromNamedFile(null, KtxTextureCreateFlagBits.NO_FLAGS),
|
||||
"Expected to throw NullPointerException");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFromNamedFileMipmapped() {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/astc_mipmap_ldr_4x4_posx.ktx2")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
KtxTexture2 texture = KtxTexture2.createFromNamedFile(testKtxFile.toString(),
|
||||
KtxTextureCreateFlagBits.NO_FLAGS);
|
||||
|
||||
assertNotNull(texture);
|
||||
assertEquals(texture.getNumLevels(), 12);
|
||||
assertEquals(texture.getBaseWidth(), 2048);
|
||||
assertEquals(texture.getBaseHeight(), 2048);
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetImageSize() {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/astc_mipmap_ldr_4x4_posx.ktx2")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
KtxTexture2 texture = KtxTexture2.createFromNamedFile(testKtxFile.toString(),
|
||||
KtxTextureCreateFlagBits.NO_FLAGS);
|
||||
|
||||
assertNotNull(texture);
|
||||
assertEquals( 4194304, texture.getImageSize(0));
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetImageOffset() {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/astc_mipmap_ldr_4x4_posx.ktx2")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
KtxTexture2 texture = KtxTexture2.createFromNamedFile(testKtxFile.toString(),
|
||||
KtxTextureCreateFlagBits.NO_FLAGS);
|
||||
|
||||
assertNotNull(texture);
|
||||
|
||||
long level11Offset = texture.getImageOffset(11, 0, 0);
|
||||
long level0Offset = texture.getImageOffset(0, 0, 0);
|
||||
|
||||
assertEquals(level11Offset, 0);
|
||||
// ktxinfo offsets are from start of file :)
|
||||
assertEquals(level0Offset - level11Offset, 0x155790 - 0x220);
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSize() {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/astc_mipmap_ldr_4x4_posx.ktx2")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
KtxTexture2 texture = KtxTexture2.createFromNamedFile(testKtxFile.toString(),
|
||||
KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT);
|
||||
|
||||
assertNotNull(texture);
|
||||
assertEquals(texture.getNumLevels(), 12);
|
||||
|
||||
long dataSize = texture.getDataSize();
|
||||
long totalSize = 0;
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
totalSize += texture.getImageSize(i);
|
||||
}
|
||||
|
||||
assertEquals(totalSize, dataSize);
|
||||
|
||||
byte[] data = texture.getData();
|
||||
|
||||
assertEquals(data.length, dataSize);
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetData() throws IOException {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/astc_mipmap_ldr_4x4_posx.ktx2")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
KtxTexture2 texture = KtxTexture2.createFromNamedFile(testKtxFile.toString(),
|
||||
KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT);
|
||||
|
||||
assertNotNull(texture);
|
||||
assertEquals(texture.getNumLevels(), 12);
|
||||
|
||||
byte[] file = Files.readAllBytes(testKtxFile);
|
||||
byte[] data = texture.getData();
|
||||
int level0Length = texture.getImageSize(0);
|
||||
|
||||
for (int i = 0; i < level0Length; i++) {
|
||||
assertEquals(file[file.length - i - 1], data[data.length - i - 1]);
|
||||
}
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompressBasis() {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/arraytex_7_mipmap_reference_u.ktx2")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
KtxTexture2 texture = KtxTexture2.createFromNamedFile(testKtxFile.toString(),
|
||||
KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT);
|
||||
|
||||
assertNotNull(texture);
|
||||
assertEquals(false, texture.isCompressed());
|
||||
assertEquals(KtxSupercmpScheme.NONE, texture.getSupercompressionScheme());
|
||||
|
||||
assertEquals(KtxErrorCode.SUCCESS, texture.compressBasis(1));
|
||||
|
||||
assertEquals(true, texture.isCompressed());
|
||||
assertEquals(KtxSupercmpScheme.BASIS_LZ, texture.getSupercompressionScheme());
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompressBasisEx() {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/arraytex_7_mipmap_reference_u.ktx2")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
KtxTexture2 texture = KtxTexture2.createFromNamedFile(testKtxFile.toString(),
|
||||
KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT);
|
||||
|
||||
assertNotNull(texture);
|
||||
assertEquals(false, texture.isCompressed());
|
||||
assertEquals(KtxSupercmpScheme.NONE, texture.getSupercompressionScheme());
|
||||
|
||||
assertEquals(KtxErrorCode.SUCCESS, texture.compressBasisEx(new KtxBasisParams()));
|
||||
|
||||
assertEquals(true, texture.isCompressed());
|
||||
assertEquals(KtxSupercmpScheme.BASIS_LZ, texture.getSupercompressionScheme());
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsingAfterDestroy() {
|
||||
KtxTextureCreateInfo info = new KtxTextureCreateInfo();
|
||||
info.setBaseWidth(16);
|
||||
info.setBaseHeight(16);
|
||||
info.setVkFormat(VkFormat.VK_FORMAT_ASTC_4x4_SRGB_BLOCK);
|
||||
KtxTexture2 texture = KtxTexture2.create(info, KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
|
||||
// Call destroy, and then try to call a function.
|
||||
// It should throw for ALL functions, and there
|
||||
// should be a test for EACH function, but...
|
||||
// I got stuff to do, you know...
|
||||
texture.destroy();
|
||||
assertThrows(IllegalStateException.class,
|
||||
() -> texture.getBaseDepth(),
|
||||
"Expected to throw IllegalStateException");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDestroyMultipleTimes() {
|
||||
KtxTextureCreateInfo info = new KtxTextureCreateInfo();
|
||||
info.setBaseWidth(16);
|
||||
info.setBaseHeight(16);
|
||||
info.setVkFormat(VkFormat.VK_FORMAT_ASTC_4x4_SRGB_BLOCK);
|
||||
KtxTexture2 texture = KtxTexture2.create(info, KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
|
||||
texture.destroy();
|
||||
texture.destroy();
|
||||
texture.destroy();
|
||||
|
||||
assertTrue(true, "Should be able to call destroy() multiple times");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCompressBasisExWithNull() {
|
||||
KtxTextureCreateInfo info = new KtxTextureCreateInfo();
|
||||
info.setBaseWidth(16);
|
||||
info.setBaseHeight(16);
|
||||
info.setVkFormat(VkFormat.VK_FORMAT_ASTC_4x4_SRGB_BLOCK);
|
||||
KtxTexture2 texture = KtxTexture2.create(info, KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> texture.compressBasisEx(null),
|
||||
"Expected to throw NullPointerException");
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testTranscodeBasis() {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/color_grid_basis.ktx2")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
KtxTexture2 texture = KtxTexture2.createFromNamedFile(testKtxFile.toString(),
|
||||
KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT);
|
||||
|
||||
assertNotNull(texture);
|
||||
|
||||
texture.transcodeBasis(KtxTranscodeFormat.ASTC_4x4_RGBA, 0);
|
||||
|
||||
assertEquals(VkFormat.VK_FORMAT_ASTC_4x4_SRGB_BLOCK, texture.getVkFormat());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() {
|
||||
KtxTextureCreateInfo info = new KtxTextureCreateInfo();
|
||||
|
||||
info.setBaseWidth(10);
|
||||
info.setBaseHeight(10);
|
||||
info.setVkFormat(VkFormat.VK_FORMAT_ASTC_4x4_SRGB_BLOCK);
|
||||
|
||||
KtxTexture2 texture = KtxTexture2.create(info, KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
assertNotNull(texture);
|
||||
|
||||
byte[] imageData = new byte[10 * 10];
|
||||
texture.setImageFromMemory(0, 0, 0, imageData);
|
||||
|
||||
texture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithNull() {
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> KtxTexture2.create(null, KtxTextureCreateStorage.ALLOC_STORAGE),
|
||||
"Expected to throw NullPointerException");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetImageFromMemoryWithNull() {
|
||||
KtxTextureCreateInfo info = new KtxTextureCreateInfo();
|
||||
info.setBaseWidth(16);
|
||||
info.setBaseHeight(16);
|
||||
info.setVkFormat(VkFormat.VK_FORMAT_R8G8B8A8_SRGB);
|
||||
KtxTexture2 t = KtxTexture2.create(info, KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> t.setImageFromMemory(0, 0, 0, null),
|
||||
"Expected to throw NullPointerException");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateFromMemoryWithInvalidMemory() {
|
||||
|
||||
byte[] invalidMemory = new byte[1000];
|
||||
int createFlags = KtxTextureCreateFlagBits.LOAD_IMAGE_DATA_BIT;
|
||||
|
||||
assertThrows(KtxException.class,
|
||||
() -> KtxTexture2.createFromMemory(ByteBuffer.wrap(invalidMemory), createFlags),
|
||||
"Expected to throw NullPointerException");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInputSwizzleBasisEx() throws IOException {
|
||||
|
||||
int sizeX = 32;
|
||||
int sizeY = 32;
|
||||
int outputFormat = KtxTranscodeFormat.RGBA32;
|
||||
int transcodeFlags = 0;
|
||||
|
||||
// Create the actual texture data:
|
||||
// - create RGBA pixels
|
||||
// - create texture
|
||||
// - compress with BRGA input swizzling
|
||||
// - obtain resulting RGBA values
|
||||
|
||||
// Create a RGBA pixels for an image filled with
|
||||
// 8 rows of red pixels
|
||||
// 8 rows of green pixels
|
||||
// 8 rows of blue pixels
|
||||
// 8 rows of white pixels
|
||||
byte[] input = new byte[sizeX * sizeY * 4];
|
||||
TestUtils.fillRows(input, sizeX, sizeY, 0, 8, 255, 0, 0, 255); // Red
|
||||
TestUtils.fillRows(input, sizeX, sizeY, 8, 16, 0, 255, 0, 255); // Green
|
||||
TestUtils.fillRows(input, sizeX, sizeY, 16, 24, 0, 0, 255, 255); // Blue
|
||||
TestUtils.fillRows(input, sizeX, sizeY, 24, 32, 255, 255, 255, 255); // White
|
||||
|
||||
// Create the input texture from the pixels
|
||||
KtxTextureCreateInfo inputInfo = new KtxTextureCreateInfo();
|
||||
inputInfo.setBaseWidth(sizeX);
|
||||
inputInfo.setBaseHeight(sizeY);
|
||||
inputInfo.setVkFormat(VkFormat.VK_FORMAT_R8G8B8A8_SRGB);
|
||||
KtxTexture2 inputTexture = KtxTexture2.create(inputInfo,
|
||||
KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
inputTexture.setImageFromMemory(0, 0, 0, input);
|
||||
|
||||
// Apply basis compression to the input, with an input swizzle BRGA,
|
||||
// so that
|
||||
// the former B channel becomes the R channel
|
||||
// the former R channel becomes the G channel
|
||||
// the former G channel becomes the B channel
|
||||
// the former A channel remains the A channel
|
||||
KtxBasisParams inputParams = new KtxBasisParams();
|
||||
inputParams.setUastc(false);
|
||||
inputParams.setInputSwizzle(new char[] { 'b', 'r', 'g', 'a' });
|
||||
inputTexture.compressBasisEx(inputParams);
|
||||
|
||||
// Transcode the input texture to RGBA32
|
||||
inputTexture.transcodeBasis(outputFormat, transcodeFlags);
|
||||
byte[] actualRgba = inputTexture.getData();
|
||||
|
||||
// Create the expected reference data:
|
||||
// - create RGBA pixels, swizzled with BRGA
|
||||
// - create texture
|
||||
// - compress without input swizzling
|
||||
// - obtain resulting RGBA values
|
||||
|
||||
// Create "golden" reference pixels, where a BRGA
|
||||
// swizzling was already applied
|
||||
byte[] gold = new byte[sizeX * sizeY * 4];
|
||||
TestUtils.fillRows(gold, sizeX, sizeY, 0, 8, 0, 255, 0, 255); // Green
|
||||
TestUtils.fillRows(gold, sizeX, sizeY, 8, 16, 0, 0, 255, 255); // Blue
|
||||
TestUtils.fillRows(gold, sizeX, sizeY, 16, 24, 255, 0, 0, 255); // Red
|
||||
TestUtils.fillRows(gold, sizeX, sizeY, 24, 32, 255, 255, 255, 255); // White
|
||||
|
||||
// Create the reference texture from the swizzled pixels
|
||||
KtxTextureCreateInfo goldInfo = new KtxTextureCreateInfo();
|
||||
goldInfo.setBaseWidth(sizeX);
|
||||
goldInfo.setBaseHeight(sizeY);
|
||||
goldInfo.setVkFormat(VkFormat.VK_FORMAT_R8G8B8A8_SRGB);
|
||||
KtxTexture2 goldTexture = KtxTexture2.create(goldInfo,
|
||||
KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
goldTexture.setImageFromMemory(0, 0, 0, gold);
|
||||
|
||||
// Apply basis compression to the reference, without swizzling
|
||||
KtxBasisParams goldParams = new KtxBasisParams();
|
||||
goldParams.setUastc(false);
|
||||
goldTexture.compressBasisEx(goldParams);
|
||||
|
||||
// Transcode the reference texture to RGBA32
|
||||
goldTexture.transcodeBasis(outputFormat, transcodeFlags);
|
||||
byte[] expectedRgba = goldTexture.getData();
|
||||
|
||||
// Compare the resulting data to the expected RGBA values.
|
||||
assertArrayEquals(expectedRgba, actualRgba);
|
||||
|
||||
inputTexture.destroy();
|
||||
goldTexture.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSupercompressionZstd() throws IOException {
|
||||
|
||||
int sizeX = 32;
|
||||
int sizeY = 32;
|
||||
|
||||
// Create a dummy texture
|
||||
KtxTextureCreateInfo info = new KtxTextureCreateInfo();
|
||||
info.setBaseWidth(sizeX);
|
||||
info.setBaseHeight(sizeY);
|
||||
info.setVkFormat(VkFormat.VK_FORMAT_R8G8B8A8_SRGB);
|
||||
KtxTexture2 t = KtxTexture2.create(info, KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
byte[] rgba = new byte[sizeX * sizeY * 4];
|
||||
t.setImageFromMemory(0, 0, 0, rgba);
|
||||
|
||||
// Apply default UASTC compression
|
||||
KtxBasisParams p = new KtxBasisParams();
|
||||
p.setUastc(true);
|
||||
t.compressBasisEx(p);
|
||||
|
||||
// The supercompression scheme should be NONE here
|
||||
int scBefore = t.getSupercompressionScheme();
|
||||
assertEquals(KtxSupercmpScheme.NONE, scBefore);
|
||||
|
||||
// Apply Zstd compression
|
||||
t.deflateZstd(10);
|
||||
|
||||
// The supercompression scheme should now be ZSTD
|
||||
int scAfter = t.getSupercompressionScheme();
|
||||
assertEquals(KtxSupercmpScheme.ZSTD, scAfter);
|
||||
|
||||
t.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSupercompressionZLIB() throws IOException {
|
||||
|
||||
int sizeX = 32;
|
||||
int sizeY = 32;
|
||||
|
||||
// Create a dummy texture
|
||||
KtxTextureCreateInfo info = new KtxTextureCreateInfo();
|
||||
info.setBaseWidth(sizeX);
|
||||
info.setBaseHeight(sizeY);
|
||||
info.setVkFormat(VkFormat.VK_FORMAT_R8G8B8A8_SRGB);
|
||||
KtxTexture2 t = KtxTexture2.create(info, KtxTextureCreateStorage.ALLOC_STORAGE);
|
||||
byte[] rgba = new byte[sizeX * sizeY * 4];
|
||||
t.setImageFromMemory(0, 0, 0, rgba);
|
||||
|
||||
// Apply default UASTC compression
|
||||
KtxBasisParams p = new KtxBasisParams();
|
||||
p.setUastc(true);
|
||||
t.compressBasisEx(p);
|
||||
|
||||
// The supercompression scheme should be NONE here
|
||||
int scBefore = t.getSupercompressionScheme();
|
||||
assertEquals(KtxSupercmpScheme.NONE, scBefore);
|
||||
|
||||
// Apply ZLIB compression
|
||||
t.deflateZLIB(10);
|
||||
|
||||
// The supercompression scheme should now be ZLIB
|
||||
int scAfter = t.getSupercompressionScheme();
|
||||
assertEquals(KtxSupercmpScheme.ZLIB, scAfter);
|
||||
|
||||
t.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindings() {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/astc_ldr_4x4_FlightHelmet_baseColor.ktx2")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
// The purpose of this test is to check the bindings for the 'native'
|
||||
// functions that only return a value. When the binding for one of
|
||||
// these functions is not implemented properly, then trying to call
|
||||
// it will cause an 'UnsatisfiedLinkError'.
|
||||
// This does not cover all 'native' functions: Some of them can only
|
||||
// sensibly be called in the context of the other tests.
|
||||
|
||||
KtxTexture2 texture = KtxTexture2.createFromNamedFile(testKtxFile.toString(),
|
||||
KtxTextureCreateFlagBits.NO_FLAGS);
|
||||
|
||||
// Native getter methods from the 'KtxTexture2' class
|
||||
texture.getOETF();
|
||||
texture.getPremultipliedAlpha();
|
||||
texture.needsTranscoding();
|
||||
texture.getSupercompressionScheme();
|
||||
texture.getVkFormat();
|
||||
|
||||
// Native getter methods from the 'KtxTexture' class
|
||||
texture.isArray();
|
||||
texture.isCubemap();
|
||||
texture.isCompressed();
|
||||
texture.getGenerateMipmaps();
|
||||
texture.getBaseWidth();
|
||||
texture.getBaseHeight();
|
||||
texture.getBaseDepth();
|
||||
texture.getNumDimensions();
|
||||
texture.getNumLevels();
|
||||
texture.getNumFaces();
|
||||
texture.getDataSize();
|
||||
texture.getDataSizeUncompressed();
|
||||
texture.getElementSize();
|
||||
texture.getRowPitch(0);
|
||||
texture.getImageSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGlUpload() {
|
||||
Path testKtxFile = Paths.get("")
|
||||
.resolve("../../tests/testimages/astc_ldr_4x4_FlightHelmet_baseColor.ktx2")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
KtxTexture2 ktxTexture = KtxTexture2.createFromNamedFile(testKtxFile.toString(),
|
||||
KtxTextureCreateFlagBits.NO_FLAGS);
|
||||
ktxTexture.transcodeBasis(KtxTranscodeFormat.BC1_RGB, 0);
|
||||
|
||||
// This test checks the error conditions that are supposed
|
||||
// to be handled by the JNI layer by throwing exceptions.
|
||||
// The test can NOT perform an actual, "valid" call that
|
||||
// causes ktxTexture_GLUpload to be called internally,
|
||||
// because that would require a GL context to be current.
|
||||
int texture0[] = { };
|
||||
int target0[] = { };
|
||||
int glError0[] = { };
|
||||
int texture1[] = { 0 };
|
||||
int target1[] = { 0 };
|
||||
int glError1[] = { 0 };
|
||||
|
||||
// Expect NullPointerException when target is null
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> {
|
||||
ktxTexture.glUpload(texture1, null, glError1);
|
||||
},
|
||||
"Expected to throw NullPointerException");
|
||||
|
||||
// Expect IllegalArgumentException when texture length is 0
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> {
|
||||
ktxTexture.glUpload(texture0, target1, glError1);
|
||||
},
|
||||
"Expected to throw NullPointerException");
|
||||
|
||||
// Expect IllegalArgumentException when target length is 0
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> {
|
||||
ktxTexture.glUpload(texture1, target0, glError1);
|
||||
},
|
||||
"Expected to throw NullPointerException");
|
||||
|
||||
// Expect IllegalArgumentException when glError length is 0
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> {
|
||||
ktxTexture.glUpload(texture1, target1, glError0);
|
||||
},
|
||||
"Expected to throw NullPointerException");
|
||||
|
||||
ktxTexture.destroy();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Khronos Group and Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.khronos.ktx.test;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Utilities for the test package
|
||||
*/
|
||||
class TestUtils {
|
||||
|
||||
/**
|
||||
* Fill the specified range of rows of the given RGBA pixels array with the
|
||||
* given RGBA components
|
||||
*
|
||||
* @param rgba The RGBA pixels array
|
||||
* @param sizeX The size of the image in x-direction
|
||||
* @param sizeY The size of the image in y-direction
|
||||
* @param minRow The minimum row, inclusive
|
||||
* @param maxRow The maximum row, exclusive
|
||||
* @param r The red component (in [0,255])
|
||||
* @param g The green component (in [0,255])
|
||||
* @param b The blue component (in [0,255])
|
||||
* @param a The alpha component (in [0,255])
|
||||
*/
|
||||
static void fillRows(byte rgba[], int sizeX, int sizeY,
|
||||
int minRow, int maxRow,
|
||||
int r, int g, int b, int a) {
|
||||
for (int y = minRow; y < maxRow; y++) {
|
||||
for (int x = 0; x < sizeX; x++) {
|
||||
int index = (y * sizeX) + x;
|
||||
rgba[index * 4 + 0] = (byte) r;
|
||||
rgba[index * 4 + 1] = (byte) g;
|
||||
rgba[index * 4 + 2] = (byte) b;
|
||||
rgba[index * 4 + 3] = (byte) a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a string representation of the RGBA components of the specified pixel
|
||||
* in the given RGBA pixels array.
|
||||
*
|
||||
* This is mainly intended for debugging. Some details of the resulting string
|
||||
* are not specified.
|
||||
*
|
||||
* @param rgba The RGBA pixels array
|
||||
* @param pixelIndex The pixel index
|
||||
* @return The string
|
||||
*/
|
||||
static String createRgbaString(byte rgba[], int pixelIndex) {
|
||||
byte r = rgba[pixelIndex * 4 + 0];
|
||||
byte g = rgba[pixelIndex * 4 + 1];
|
||||
byte b = rgba[pixelIndex * 4 + 2];
|
||||
byte a = rgba[pixelIndex * 4 + 3];
|
||||
int ir = Byte.toUnsignedInt(r);
|
||||
int ig = Byte.toUnsignedInt(g);
|
||||
int ib = Byte.toUnsignedInt(b);
|
||||
int ia = Byte.toUnsignedInt(a);
|
||||
String s = String.format(Locale.ENGLISH, "%3d, %3d, %3d, %3d", ir, ig, ib, ia);
|
||||
return s;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// -*- tab-width: 4; -*-
|
||||
// vi: set sw=2 ts=4 expandtab textwidth=80:
|
||||
|
||||
//
|
||||
// Copyright 2024 Khronos Group, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// Provide old name for backward compatibility.
|
||||
|
||||
Module.onRuntimeInitialized = function() {
|
||||
Module['ktxTexture'] = Module.texture;
|
||||
Module['ErrorCode'] = Module.error_code;
|
||||
Module['TranscodeTarget'] = Module.transcode_fmt;
|
||||
Module['TranscodeFlags'] = Module.transcode_flag_bits;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
// -*- tab-width: 4; -*-
|
||||
// vi: set sw=2 ts=4 expandtab textwidth=80:
|
||||
|
||||
//
|
||||
// Copyright 2019-2024 Khronos Group, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// Provide old module create function name for backward compatibility.
|
||||
// N.B. --pre-js and --post-js code is run inside the module creation
|
||||
// function so the variable holding that function is not available.
|
||||
// This has to be --extern-post-js.
|
||||
|
||||
var LIBKTX
|
||||
if (typeof createKtxReadModule === "function")
|
||||
LIBKTX = createKtxReadModule;
|
||||
else
|
||||
LIBKTX = createKtxModule;
|
||||
@@ -0,0 +1,801 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab textwidth=80: */
|
||||
|
||||
/*
|
||||
* Copyright 2019-2020 Khronos Group, Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* This transcoder is DEPRECATED. Use the container independent transcoder from
|
||||
* the Binomial LLC repo instead: https://github.com/BinomialLLC/basis_universal.
|
||||
*/
|
||||
|
||||
#include <emscripten/bind.h>
|
||||
#include "basisu/transcoder/basisu_transcoder.h"
|
||||
|
||||
using namespace emscripten;
|
||||
using namespace basist;
|
||||
|
||||
namespace msc {
|
||||
// Container independent image description. This was originally in a PR
|
||||
// submitted to the BasisU repo to add container independent transcoders.
|
||||
// However the code that was eventually added favored explicit parameters
|
||||
// over collecting them in a struct. To maintain backward compatibility for
|
||||
// msc_basisu_trancoder users, recreate the struct here.
|
||||
struct basisu_image_desc {
|
||||
uint32_t m_flags;
|
||||
uint32_t m_rgb_byte_offset;
|
||||
uint32_t m_rgb_byte_length;
|
||||
uint32_t m_alpha_byte_offset;
|
||||
uint32_t m_alpha_byte_length;
|
||||
uint32_t m_orig_width;
|
||||
uint32_t m_orig_height;
|
||||
uint32_t m_num_blocks_x;
|
||||
uint32_t m_num_blocks_y;
|
||||
uint32_t m_level;
|
||||
|
||||
basisu_image_desc() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
basisu_image_desc(basis_tex_format, uint32_t width, uint32_t height,
|
||||
uint32_t level)
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
m_orig_width = width;
|
||||
m_orig_height = height;
|
||||
// Current formats are all 4 x 4 so ignore tex format param.
|
||||
const uint32_t bw = 4, bh = 4;
|
||||
m_num_blocks_x = (m_orig_width + (bw - 1)) / bw;
|
||||
m_num_blocks_y = (m_orig_height + (bh - 1)) / bh;
|
||||
m_level = level;
|
||||
}
|
||||
|
||||
basisu_image_desc(const basis_slice_desc* pSlice_desc,
|
||||
const bool hasAlphaSlice,
|
||||
uint32_t level = 0) : m_level(level)
|
||||
{
|
||||
m_flags = pSlice_desc->m_flags & cSliceDescFlagsFrameIsIFrame;
|
||||
m_rgb_byte_offset = pSlice_desc->m_file_ofs;
|
||||
m_rgb_byte_length = pSlice_desc->m_file_size;
|
||||
m_orig_width = pSlice_desc->m_orig_width;
|
||||
m_orig_height = pSlice_desc->m_orig_height;
|
||||
m_num_blocks_x = pSlice_desc->m_num_blocks_x;
|
||||
m_num_blocks_y = pSlice_desc->m_num_blocks_y;
|
||||
if (hasAlphaSlice) {
|
||||
++pSlice_desc;
|
||||
m_alpha_byte_offset = pSlice_desc->m_file_ofs;
|
||||
m_alpha_byte_length = pSlice_desc->m_file_size;
|
||||
} else {
|
||||
m_alpha_byte_offset = 0;
|
||||
m_alpha_byte_length = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This is needed because the enum defining CDecode* is anonymous.
|
||||
enum TranscodeFlagBits {
|
||||
TranscodeAlphaDataToOpaqueFormats =
|
||||
cDecodeFlagsTranscodeAlphaDataToOpaqueFormats,
|
||||
HighQuality = cDecodeFlagsHighQuality
|
||||
};
|
||||
|
||||
class BasisTranscoderState: public basisu_transcoder_state {
|
||||
};
|
||||
|
||||
class TranscodedImage {
|
||||
public:
|
||||
TranscodedImage(size_t size) : image(size) { }
|
||||
|
||||
uint8_t* data() { return image.data(); }
|
||||
size_t size() { return image.size(); }
|
||||
|
||||
val get_typed_memory_view() {
|
||||
return val(typed_memory_view(image.size(), image.data()));
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> image;
|
||||
};
|
||||
|
||||
class ImageTranscoderHelper {
|
||||
// block size calculations
|
||||
static inline uint32_t getWidthInBlocks(uint32_t w, uint32_t bw)
|
||||
{
|
||||
return (w + (bw - 1)) / bw;
|
||||
}
|
||||
|
||||
static inline uint32_t getHeightInBlocks(uint32_t h, uint32_t bh)
|
||||
{
|
||||
return (h + (bh - 1)) / bh;
|
||||
} //
|
||||
|
||||
public:
|
||||
static size_t getTranscodedImageByteLength(transcoder_texture_format format,
|
||||
uint32_t width, uint32_t height)
|
||||
{
|
||||
uint32_t blockByteLength =
|
||||
basis_get_bytes_per_block_or_pixel(format);
|
||||
if (basis_transcoder_format_is_uncompressed(format)) {
|
||||
return width * height * blockByteLength;
|
||||
} else if (format == transcoder_texture_format::cTFPVRTC1_4_RGB
|
||||
|| format == transcoder_texture_format::cTFPVRTC1_4_RGBA) {
|
||||
// For PVRTC1, Basis only writes (or requires)
|
||||
// blockWidth * blockHeight * blockByteLength. But GL requires
|
||||
// extra padding for very small textures:
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt
|
||||
const uint32_t paddedWidth = (width + 3) & ~3;
|
||||
const uint32_t paddedHeight = (height + 3) & ~3;
|
||||
return (std::max(8U, paddedWidth)
|
||||
* std::max(8U, paddedHeight) * 4 + 7) / 8;
|
||||
} else {
|
||||
uint32_t blockWidth = getWidthInBlocks(width, basis_get_block_width(format));
|
||||
uint32_t blockHeight = getHeightInBlocks(height, basis_get_block_height(format));
|
||||
return blockWidth * blockHeight * blockByteLength;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class BasisLzEtc1sImageTranscoder : public basisu_lowlevel_etc1s_transcoder {
|
||||
public:
|
||||
BasisLzEtc1sImageTranscoder()
|
||||
: basisu_lowlevel_etc1s_transcoder() { }
|
||||
|
||||
// Yes, code in the following functions handling data coming in from
|
||||
// ArrayBuffers IS copying the data. Sigh! According to Alon Zakai:
|
||||
//
|
||||
// "There isn't a way to let compiled code access a new ArrayBuffer.
|
||||
// The compiled code has hardcoded access to the wasm Memory it was
|
||||
// instantiated with - all the pointers it can understand are
|
||||
// indexes into that Memory. It can't refer to anything else,
|
||||
// I'm afraid."
|
||||
//
|
||||
// "In the future using different address spaces or techniques with
|
||||
// reference types may open up some possibilities here."
|
||||
|
||||
bool decode_palettes(uint32_t num_endpoints, const val& jsEndpoints,
|
||||
uint32_t num_selectors, const val& jsSelectors)
|
||||
{
|
||||
std::vector<uint8_t> cEndpoints{}, cSelectors{};
|
||||
cEndpoints.resize(jsEndpoints["byteLength"].as<size_t>());
|
||||
val memory = val::module_property("HEAP8")["buffer"];
|
||||
val endpointsView = jsEndpoints["constructor"].new_(memory,
|
||||
reinterpret_cast<uintptr_t>(cEndpoints.data()),
|
||||
jsEndpoints["length"].as<uint32_t>());
|
||||
endpointsView.call<void>("set", jsEndpoints);
|
||||
|
||||
cSelectors.resize(jsSelectors["byteLength"].as<size_t>());
|
||||
// In case the resize caused HEAP8 to grow.
|
||||
memory = val::module_property("HEAP8")["buffer"];
|
||||
val selectorsView = jsSelectors["constructor"].new_(memory,
|
||||
reinterpret_cast<uintptr_t>(cSelectors.data()),
|
||||
jsSelectors["length"].as<uint32_t>());
|
||||
selectorsView.call<void>("set", jsSelectors);
|
||||
|
||||
return basisu_lowlevel_etc1s_transcoder::decode_palettes(num_endpoints,
|
||||
cEndpoints.data(),
|
||||
cEndpoints.size(),
|
||||
num_selectors,
|
||||
cSelectors.data(),
|
||||
cSelectors.size());
|
||||
}
|
||||
|
||||
bool decode_tables(const val& jsTableData)
|
||||
{
|
||||
std::vector<uint8_t> cTableData{};
|
||||
|
||||
cTableData.resize(jsTableData["byteLength"].as<size_t>());
|
||||
val memory = val::module_property("HEAP8")["buffer"];
|
||||
val TableDataView = jsTableData["constructor"].new_(memory,
|
||||
reinterpret_cast<uintptr_t>(cTableData.data()),
|
||||
jsTableData["length"].as<uint32_t>());
|
||||
TableDataView.call<void>("set", jsTableData);
|
||||
|
||||
return basisu_lowlevel_etc1s_transcoder::decode_tables(
|
||||
cTableData.data(),
|
||||
cTableData.size());
|
||||
}
|
||||
|
||||
// @~English
|
||||
// @brief Transcode a single BasisLZ supercompressed ETC1S image.
|
||||
//
|
||||
// @param[in] targetFormat the format to which to transcode the image.
|
||||
// This enum comes from Basis Universal.
|
||||
// @param[in] jsInSlices emscripten::val of a .subarray of the
|
||||
// ArrayBuffer holding the file data that
|
||||
// points to the first slice for this image.
|
||||
// An alpha slice, if it exists, always
|
||||
// immediately follows the rgb slice.
|
||||
// @param[in] imageDesc reference to a struct basisu_image_desc
|
||||
// giving information about the image.
|
||||
// @param[in] decodeFlags
|
||||
// an OR of basisu_decode_flags bits setting decode
|
||||
// options. The only one of general interest is
|
||||
// @c cDecodeFlagsTranscodeAlphaDataToOpaqueFormats.
|
||||
// This can be used when @p targetFormat lacks an
|
||||
// alpha component. When set the alpha slice is
|
||||
// transcoded into the RGB components of the target.
|
||||
//
|
||||
// @return An emscripten::val with 1 entries, @c transcodedImage. If
|
||||
// the transcode failed, @c transcodedImage will be undefined.
|
||||
//
|
||||
emscripten::val transcode_image(
|
||||
transcoder_texture_format targetFormat,
|
||||
const val& jsInSlices,
|
||||
basisu_image_desc& imageDesc,
|
||||
uint32_t decodeFlags = 0,
|
||||
bool isVideo = false)
|
||||
{
|
||||
// First of all copy in the deflated data.
|
||||
std::vector <uint8_t> deflatedSlices;
|
||||
uint32_t deflatedSlicesByteLength
|
||||
= jsInSlices["byteLength"].as<uint32_t>();
|
||||
deflatedSlices.resize(deflatedSlicesByteLength);
|
||||
val memory = val::module_property("HEAP8")["buffer"];
|
||||
|
||||
val memoryView = jsInSlices["constructor"].new_(memory,
|
||||
reinterpret_cast<uintptr_t>(deflatedSlices.data()),
|
||||
deflatedSlices.size());
|
||||
memoryView.call<void>("set", jsInSlices);
|
||||
|
||||
size_t tiByteLength =
|
||||
ImageTranscoderHelper::getTranscodedImageByteLength(targetFormat,
|
||||
imageDesc.m_orig_width,
|
||||
imageDesc.m_orig_height);
|
||||
TranscodedImage* dst = new TranscodedImage(tiByteLength);
|
||||
// ETC1S texel block dimensions
|
||||
const uint32_t bw = 4, bh = 4;
|
||||
uint32_t numBlocksX =( imageDesc.m_orig_width + (bw - 1)) / bw;
|
||||
uint32_t numBlocksY = (imageDesc.m_orig_height + (bh - 1)) / bh;
|
||||
|
||||
bool status = basisu_lowlevel_etc1s_transcoder::transcode_image(
|
||||
targetFormat,
|
||||
dst->data(),
|
||||
dst->size(),
|
||||
deflatedSlices.data(),
|
||||
deflatedSlices.size(),
|
||||
numBlocksX,
|
||||
numBlocksY,
|
||||
imageDesc.m_orig_width,
|
||||
imageDesc.m_orig_height,
|
||||
imageDesc.m_level,
|
||||
imageDesc.m_rgb_byte_offset,
|
||||
imageDesc.m_rgb_byte_length,
|
||||
imageDesc.m_alpha_byte_offset,
|
||||
imageDesc.m_alpha_byte_length,
|
||||
decodeFlags,
|
||||
imageDesc.m_alpha_byte_length != 0,
|
||||
isVideo
|
||||
// API currently doesn't have any
|
||||
// way to indicate if this is an
|
||||
// iFrame or pFrame.
|
||||
);
|
||||
val ret = val::object();
|
||||
if (status) {
|
||||
ret.set("transcodedImage", val(dst, allow_raw_pointers()));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
class UastcImageTranscoder : public basisu_lowlevel_uastc_transcoder {
|
||||
public:
|
||||
UastcImageTranscoder() : basisu_lowlevel_uastc_transcoder() { }
|
||||
|
||||
// @~English
|
||||
// @brief Transcode a single UASTC encoded image.
|
||||
//
|
||||
// @param[in] targetFormat the format to which to transcode the image.
|
||||
// This enum comes from Basis Universal.
|
||||
// @param[in] jsInSlices emscripten::val of a .subarray of the
|
||||
// ArrayBuffer holding the file data that
|
||||
// points to the the image to transcode.
|
||||
// @param[in] imageDesc reference to a struct basisu_image_desc
|
||||
// giving information about the image.
|
||||
// @param[in] decodeFlags
|
||||
// an OR of basisu_decode_flags bits setting decode
|
||||
// options. The only one of general interest is
|
||||
// @c cDecodeFlagsTranscodeAlphaDataToOpaqueFormats.
|
||||
// This can be used when @p targetFormat lacks an
|
||||
// alpha component. When set, the alpha components
|
||||
// are decoded into the RGB components of the target.
|
||||
//
|
||||
// @return An emscripten::val with 1 entries, @c transcodedImage. If
|
||||
// the transcode failed, @c transcodedImage will be undefined.
|
||||
//
|
||||
emscripten::val transcode_image(
|
||||
transcoder_texture_format targetFormat,
|
||||
const val& jsInImage,
|
||||
basisu_image_desc& imageDesc,
|
||||
uint32_t decodeFlags = 0,
|
||||
bool hasAlpha = false,
|
||||
bool isVideo = false)
|
||||
{
|
||||
// Copy in the deflated image.
|
||||
std::vector <uint8_t> deflatedImage;
|
||||
size_t deflatedImageByteLength
|
||||
= jsInImage["byteLength"].as<size_t>();
|
||||
deflatedImage.resize(deflatedImageByteLength);
|
||||
val memory = val::module_property("HEAP8")["buffer"];
|
||||
val memoryView = jsInImage["constructor"].new_(memory,
|
||||
reinterpret_cast<uintptr_t>(deflatedImage.data()),
|
||||
deflatedImageByteLength);
|
||||
memoryView.call<void>("set", jsInImage);
|
||||
|
||||
size_t tiByteLength =
|
||||
ImageTranscoderHelper::getTranscodedImageByteLength(targetFormat,
|
||||
imageDesc.m_orig_width,
|
||||
imageDesc.m_orig_height);
|
||||
TranscodedImage* dst = new TranscodedImage(tiByteLength);
|
||||
// UASTC texel block dimensions
|
||||
const uint32_t bw = 4, bh = 4;
|
||||
uint32_t numBlocksX =( imageDesc.m_orig_width + (bw - 1)) / bw;
|
||||
uint32_t numBlocksY = (imageDesc.m_orig_height + (bh - 1)) / bh;
|
||||
|
||||
bool status =
|
||||
basisu_lowlevel_uastc_transcoder::transcode_image(
|
||||
targetFormat,
|
||||
dst->data(),
|
||||
dst->size(),
|
||||
deflatedImage.data(),
|
||||
deflatedImage.size(),
|
||||
numBlocksX,
|
||||
numBlocksY,
|
||||
imageDesc.m_orig_width,
|
||||
imageDesc.m_orig_height,
|
||||
imageDesc.m_level,
|
||||
imageDesc.m_rgb_byte_offset,
|
||||
imageDesc.m_rgb_byte_length,
|
||||
decodeFlags,
|
||||
hasAlpha,
|
||||
isVideo
|
||||
// API currently doesn't have any
|
||||
// way to indicate if this is an
|
||||
// iFrame or pFrame.
|
||||
);
|
||||
val ret = val::object();
|
||||
if (status) {
|
||||
ret.set("transcodedImage", val(dst, allow_raw_pointers()));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @page msc_basis_transcoder Basis Universal Image Transcoder binding
|
||||
|
||||
@warning Deprecated. Use the container independent transcoder from
|
||||
the Binomial LLC repo instead:
|
||||
https://github.com/BinomialLLC/basis_universal.
|
||||
This JS wrapper was designed to use an underlying C++ API that
|
||||
accepted image info in a structure. The API actually added to
|
||||
basis_universal uses explicit parameters so users of this transcoder
|
||||
will be packing the info into a struct from which it will be
|
||||
immediately unpacked before the underlying transcoder is called.
|
||||
|
||||
## WebIDL for the binding
|
||||
|
||||
@code{.unparsed}
|
||||
void initTranscoders();
|
||||
|
||||
bool isFormatSupported(TranscodeTarget targetFormat, TextureFormat texFormat);
|
||||
|
||||
interface BasisTranscoderState {
|
||||
void BasisTranscoderState();
|
||||
};
|
||||
|
||||
interface TranscodedImage {
|
||||
ArrayBufferView get_typed_memory_view();
|
||||
};
|
||||
|
||||
interface TranscodeResult {
|
||||
TranscodedImage transcodedImage;
|
||||
};
|
||||
|
||||
interface BasisLzEtc1sImageTranscoder {
|
||||
void BasisLzEtc1sImageTranscoder();
|
||||
uint32_t getBytesPerBlock(TranscodeTarget format);
|
||||
bool decode_palettes(uint32_t num_endpoints,
|
||||
const Uint8Array endpoints,
|
||||
uint32_t num_selectors,
|
||||
const Uint8Array selectors);
|
||||
bool decode_tables(const Uint8Array tableData);
|
||||
TranscodeResult transcode_image(
|
||||
TranscodeTarget targetFormat,
|
||||
const Uint8Array jsInSlices,
|
||||
ImageInfo imageInfo,
|
||||
uint32_t decodeFlags = 0,
|
||||
bool isVideo = false);
|
||||
};
|
||||
|
||||
interface BasisUastcImageTranscoder {
|
||||
void BasisUastcImageTranscoder();
|
||||
uint32_t getBytesPerBlock(const TranscodeTarget format);
|
||||
TranscodeResult transcode_image(
|
||||
TranscodeTarget targetFormat,
|
||||
const Uint8Array jsInImage,
|
||||
basisu_image_desc& imageDesc,
|
||||
uint32_t decodeFlags = 0,
|
||||
bool hasAlpha = false,
|
||||
bool isVideo = false);
|
||||
|
||||
interface ImageInfo = {
|
||||
ImageInfo(TextureFormat texFormat, uint32_t width, uint32_t height,
|
||||
uint32_t level);
|
||||
attribute uint32_t flags;
|
||||
attribute long rgbByteOffset;
|
||||
attribute long rgbByteLength;
|
||||
attribute long alphaByteOffset;
|
||||
attribute long alphaByteLength;
|
||||
attribute uint32_t width;
|
||||
attribute uint32_t height;
|
||||
attribute uint32_t numBlocksX;
|
||||
attribute uint32_t numBlocksY;
|
||||
attribute uint32_t level;
|
||||
};
|
||||
|
||||
// Some targets may not be available depending on options used when compiling
|
||||
// the web assembly.
|
||||
enum TranscodeTarget = {
|
||||
"ETC1_RGB",
|
||||
"BC1_RGB",
|
||||
"BC4_R",
|
||||
"BC5_RG",
|
||||
"BC3_RGBA",
|
||||
"PVRTC1_4_RGB",
|
||||
"PVRTC1_4_RGBA",
|
||||
"BC7_RGBA",
|
||||
"BC7_M6_RGB", //Deprecated. Use BC7_RGBA.
|
||||
"BC7_M5_RGBA", //Deprecated. Use BC7_RGBA.
|
||||
"ETC2_RGBA",
|
||||
"ASTC_4x4_RGBA",
|
||||
"RGBA32",
|
||||
"RGB565",
|
||||
"BGR565",
|
||||
"RGBA4444",
|
||||
"PVRTC2_4_RGB",
|
||||
"PVRTC2_4_RGBA",
|
||||
"EAC_R11",
|
||||
"EAC_RG11"
|
||||
};
|
||||
|
||||
enum TextureFormat = {
|
||||
"ETC1S",
|
||||
"UASTC4x4",
|
||||
};
|
||||
|
||||
enum TranscodeFlagBits =
|
||||
"TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS",
|
||||
"HIGH_QUALITY",
|
||||
};
|
||||
|
||||
enum TranscodeFlagBits = {
|
||||
"TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS",
|
||||
"HIGH_QUALITY"
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
## How to use
|
||||
|
||||
Put msc_basis_transcoder.js and msc_basis_transcoder.wasm in a
|
||||
directory on your server. Create a script tag with
|
||||
msc_basis_tranacoder.js as the @c src as shown below, changing
|
||||
the path as necessary for the relative locations of your .html
|
||||
file and the script source. msc_basis_transcoder.js will
|
||||
automatically load msc_basis_transcoder.wasm.
|
||||
|
||||
### Create an instance of the MSC_TRANSCODER module
|
||||
|
||||
For example, add this to the .html file to initialize the transcoder and
|
||||
make it available on the main window.
|
||||
@code{.unparsed}
|
||||
<script src="msc_transcoder_wrapper.js"></script>
|
||||
<script type="text/javascript">
|
||||
MSC_TRANSCODER().then(module => {
|
||||
window.MSC_TRANSCODER = module;
|
||||
module.initTranscoders();
|
||||
// Call a function to begin loading or transcoding..
|
||||
</script>
|
||||
@endcode
|
||||
|
||||
@e After the module is initialized, invoke code that will directly or indirectly cause
|
||||
a function with code like the following to be executed.
|
||||
|
||||
## Somewhere in the loader/transcoder
|
||||
|
||||
Assume a KTX file is fetched via an XMLHttpRequest which deposits the data into
|
||||
a Uint8Array, "buData"...
|
||||
|
||||
@note The names of the data items used in the following code are those
|
||||
from the KTX2 specification but the actual data is not specific to that
|
||||
container format.
|
||||
|
||||
@code{.unparsed}
|
||||
const {
|
||||
BasisLzEtc1sImageTranscoder,
|
||||
BasisUastcImageTranscoder,
|
||||
TranscodeTarget
|
||||
} = MSC_TRANSCODER;
|
||||
|
||||
// Determine from the KTX2 header information in buData if
|
||||
// the data format is BasisU or Uastc.
|
||||
// supercompressionScheme value == 1, it's TextureFormat.ETC1S.
|
||||
// DFD colorModel == 166, it's TextureFormat.UASTC4x4.
|
||||
const texFormat = ...
|
||||
|
||||
// Determine appropriate transcode format from available targets,
|
||||
// info about the texture, e.g. texture.numComponents, and
|
||||
// expected use. Use values from TranscodeTarget.
|
||||
const targetFormat = ...
|
||||
if ( !MSC_TRANSCODER.isFormatSupported(targetFormat, texFormat) {
|
||||
throw new Error( ... );
|
||||
}
|
||||
|
||||
if (TextureFormat.UASTC4x4) {
|
||||
var result = transcodeUastc(targetFormat);
|
||||
} else {
|
||||
var result = transcodeEtc1s(targetFormat);
|
||||
}
|
||||
if ( result.transcodedImage === undefined ) {
|
||||
throw new Error( 'Unable to transcode image.' );
|
||||
}
|
||||
@endcode
|
||||
|
||||
This is the function for transcoding etc1s.
|
||||
|
||||
@code{.unparsed}
|
||||
transcodeEtc1s(targetFormat) {
|
||||
// Locate the supercompression global data and compresssed
|
||||
// mip level data within buData.
|
||||
|
||||
var bit = new BasisLzEtc1sImageTranscoder();
|
||||
|
||||
// Find the index of the starts of the endpoints, selectors and tables
|
||||
// data within buData...
|
||||
var endpointsStart = ...
|
||||
var selectorsStart = ...
|
||||
var tablesStart = ...
|
||||
// The numbers of endpoints & selectors and their byteLengths are items
|
||||
// within buData. They are in the header of a .ktx2 file's
|
||||
// supercompressionGlobalData and in the header of a .basis file.
|
||||
|
||||
var endpoints = new Uint8Array(buData, endpointsStart,
|
||||
endpointsByteLength);
|
||||
var selectors = new Uint8Array(buData, selectorsStart,
|
||||
selectorsByteLength);
|
||||
|
||||
bit.decodePalettes(numEndpoints, endpoints,
|
||||
numSelectors, selectors);
|
||||
|
||||
var tables = new UInt8Array(buData, tablesStart, tablesByteLength);
|
||||
bit.decodeTables(tables);
|
||||
|
||||
// Determine if the file contains a video sequence...
|
||||
var isVideo = ...
|
||||
|
||||
// Calculate the total number of images in the data
|
||||
var numImages = ...
|
||||
|
||||
// Set up a subarray pointing at the deflated image descriptions
|
||||
// in buData. This is for .ktx2 containers. The image descriptions
|
||||
// are located in supercompressionGlobalData. .basis containers will
|
||||
// require different code to locate the slice descriptions within
|
||||
// the file.
|
||||
var imageDescsStart = ...:
|
||||
// An imageDesc has 5 uint32 values.
|
||||
var imageDescs = new Uint32Data(buData, imageDescsStart,
|
||||
numImages * 5 * 4);
|
||||
var curImageIndex = 0;
|
||||
|
||||
// Pseudo code for processing the levels of a .ktx2 container...
|
||||
foreach level {
|
||||
var leveWidth = width of image at this level
|
||||
var levelHeight = height of image at this level
|
||||
imageInfo = new ImageInfo(TextureFormat::ETC1S, levelWidth, levelHeight,
|
||||
level);
|
||||
foreach image in level {
|
||||
// In KTX2 container locate the imageDesc for this image.
|
||||
var imageDesc = imageDescs[curImageIndex++];
|
||||
imageInfo.flags = imageDesc.imageFlags;
|
||||
imageInfo.rgbByteOffset = 0;
|
||||
imageInfo.rgbByteLength = imageDesc.rgbSliceByteLength;
|
||||
imageInfo.alphaByteOffset = imageDesc.alphaSliceByteOffset > 0 ? imageDesc.rgbSliceByteLength : 0;
|
||||
imageInfo.alphaByteLength = imageDesc.alphaSliceByteLength;
|
||||
// Determine the location in the ArrayBuffer of the start
|
||||
// of the deflated data for level.
|
||||
var levelOffset = ...
|
||||
// Make a .subarray of the rgb slice data.
|
||||
var levelData = new Uint8Array(
|
||||
buData,
|
||||
levelOffset + imageDesc.rgbSliceByteOffset,
|
||||
imageDesc.rgbSliceByteLength + imageDesc.alphaByteLength
|
||||
);
|
||||
var result = bit.transcodeImage(
|
||||
targetFormat,
|
||||
levelData,
|
||||
imageInfo,
|
||||
0,
|
||||
isVideo);
|
||||
if ( result.transcodedImage === undefined ) {
|
||||
throw new Error( ... );
|
||||
}
|
||||
let imgData = transcodedImage.get_typed_memory_view();
|
||||
|
||||
// Upload data in imgData to WebGL...
|
||||
|
||||
// Do not call delete() until data has been uploaded
|
||||
// or otherwise copied.
|
||||
transcodedImage.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// For .basis containers, it is necessary to locate the slice
|
||||
// description(s) for the image and set the values in imageInfo
|
||||
// from them. Use of the .basis-specific transcoder is recommended.
|
||||
// The definition of the basis_slice_desc struct makes it difficult
|
||||
// to create JS interface for it with embind.
|
||||
@endcode
|
||||
|
||||
This is the function for transcoding Uastc.
|
||||
|
||||
@code{.unparsed}
|
||||
transcodeUastc(targetFormat) {
|
||||
var uit = new UastcImageTranscoder();
|
||||
|
||||
// Determine if the data is supercompressed.
|
||||
var zstd = (supercompressionScheme == 2);
|
||||
|
||||
// Determine if the data has alpha.
|
||||
var hasAlpha = (Channel ID of sample[0] in DFD == 1);
|
||||
|
||||
var dctx;
|
||||
if (zstd) {
|
||||
// Initialize the zstd decoder. Zstd JS wrapper + wasm is
|
||||
// a separate package.
|
||||
dctx = ZSTD_createDCtx();
|
||||
}
|
||||
|
||||
// Pseudo code for processing the levels of a .ktx2 container...
|
||||
foreach level {
|
||||
// Determine the location in the ArrayBuffer buData of the
|
||||
// start of the deflated data for the level.
|
||||
var levelData = ...
|
||||
if (zstd) {
|
||||
// Inflate the level data
|
||||
levelData = ZSTD_decompressDCtx(dctx, levelData, ... );
|
||||
}
|
||||
|
||||
var levelWidth = width of image at this level
|
||||
var levelHeight = height of image at this level
|
||||
var depth = depth of texture at this level
|
||||
var levelImageCount = number of layers * number of faces * depth;
|
||||
var imageOffsetInLevel = 0;
|
||||
|
||||
var imageInfo = new ImageInfo(TextureFormat::UASTC4x4,
|
||||
levelWidth, levelHeight, level);
|
||||
var levelImageByteLength = imageInfo.numBlocksX * imageInfo.numBlocksY * DFD bytesPlane0;
|
||||
|
||||
foreach image in level {
|
||||
inImage = Uint8Array(levelData, imageOffsetInLevel, levelImageByteLength);
|
||||
imageInfo.flags = 0;
|
||||
imageInfo.rgbByteOffset = 0;
|
||||
imageInfo.rgbByteLength = levelImageByteLength;
|
||||
imageInfo.alphaByteOffset = 0;
|
||||
imageInfo.alphaByteLength = 0;
|
||||
|
||||
const {transcodedImage} = uit.transcodeImage(
|
||||
targetFormat,
|
||||
inImage,
|
||||
imageInfo,
|
||||
0,
|
||||
hasAlpha,
|
||||
isVideo);
|
||||
if ( result.transcodedImage === undefined ) {
|
||||
throw new Error( ... );
|
||||
}
|
||||
let imgData = transcodedImage.get_typed_memory_view();
|
||||
|
||||
// Upload data in imgData to WebGL...
|
||||
|
||||
// Do not call delete() until data has been uploaded
|
||||
// or otherwise copied.
|
||||
transcodedImage.delete();
|
||||
|
||||
imageOffsetInLevel += levelImageByteLength;
|
||||
}
|
||||
}
|
||||
// For .basis containers, as with ETC1S, it is necessary to locate
|
||||
// the slice description for the image and set the values in imageInfo
|
||||
// from it.
|
||||
}
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
EMSCRIPTEN_BINDINGS(ktx_wrappers)
|
||||
{
|
||||
enum_<transcoder_texture_format>("TranscodeTarget")
|
||||
.value("ETC1_RGB", transcoder_texture_format::cTFETC1_RGB)
|
||||
.value("BC1_RGB", transcoder_texture_format::cTFBC1_RGB)
|
||||
.value("BC4_R", transcoder_texture_format::cTFBC4_R)
|
||||
.value("BC5_RG", transcoder_texture_format::cTFBC5_RG)
|
||||
.value("BC3_RGBA", transcoder_texture_format::cTFBC3_RGBA)
|
||||
.value("PVRTC1_4_RGB", transcoder_texture_format::cTFPVRTC1_4_RGB)
|
||||
.value("PVRTC1_4_RGBA", transcoder_texture_format::cTFPVRTC1_4_RGBA)
|
||||
.value("BC7_RGBA", transcoder_texture_format::cTFBC7_RGBA)
|
||||
// Deprecated. Use BC7_RGBA.
|
||||
.value("BC7_M6_RGB", transcoder_texture_format::cTFBC7_M6_RGB)
|
||||
// Deprecated. Use BC7_RGBA.
|
||||
.value("BC7_M5_RGBA", transcoder_texture_format::cTFBC7_M5_RGBA)
|
||||
.value("ETC2_RGBA", transcoder_texture_format::cTFETC2_RGBA)
|
||||
.value("ASTC_4x4_RGBA", transcoder_texture_format::cTFASTC_4x4_RGBA)
|
||||
.value("RGBA32", transcoder_texture_format::cTFRGBA32)
|
||||
.value("RGB565", transcoder_texture_format::cTFRGB565)
|
||||
.value("BGR565", transcoder_texture_format::cTFBGR565)
|
||||
.value("RGBA4444", transcoder_texture_format::cTFRGBA4444)
|
||||
.value("PVRTC2_4_RGB", transcoder_texture_format::cTFPVRTC2_4_RGB)
|
||||
.value("PVRTC2_4_RGBA", transcoder_texture_format::cTFPVRTC2_4_RGBA)
|
||||
.value("EAC_R11", transcoder_texture_format::cTFETC2_EAC_R11)
|
||||
.value("EAC_RG11", transcoder_texture_format::cTFETC2_EAC_RG11)
|
||||
;
|
||||
|
||||
enum_<basis_tex_format>("TextureFormat")
|
||||
.value("ETC1S", basis_tex_format::cETC1S)
|
||||
.value("UASTC4x4", basis_tex_format::cUASTC4x4)
|
||||
;
|
||||
|
||||
enum_<msc::TranscodeFlagBits>("TranscodeFlagBits")
|
||||
.value("TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS",
|
||||
msc::TranscodeAlphaDataToOpaqueFormats)
|
||||
.value("HIGH_QUALITY", msc::HighQuality)
|
||||
;
|
||||
|
||||
function("initTranscoders", basisu_transcoder_init);
|
||||
function("isFormatSupported", basis_is_format_supported);
|
||||
|
||||
class_<msc::basisu_image_desc>("ImageInfo")
|
||||
.constructor<basis_tex_format,uint32_t,uint32_t,uint32_t>()
|
||||
.property("flags", &msc::basisu_image_desc::m_flags)
|
||||
.property("rgbByteOffset", &msc::basisu_image_desc::m_rgb_byte_offset)
|
||||
.property("rgbByteLength", &msc::basisu_image_desc::m_rgb_byte_length)
|
||||
.property("alphaByteOffset", &msc::basisu_image_desc::m_alpha_byte_offset)
|
||||
.property("alphaByteLength", &msc::basisu_image_desc::m_alpha_byte_length)
|
||||
.property("width", &msc::basisu_image_desc::m_orig_width)
|
||||
.property("height", &msc::basisu_image_desc::m_orig_height)
|
||||
.property("numBlocksX", &msc::basisu_image_desc::m_num_blocks_x)
|
||||
.property("numBlocksY", &msc::basisu_image_desc::m_num_blocks_y)
|
||||
.property("level", &msc::basisu_image_desc::m_level)
|
||||
;
|
||||
|
||||
class_<msc::BasisLzEtc1sImageTranscoder>("BasisLzEtc1sImageTranscoder")
|
||||
.constructor()
|
||||
.class_function("getBytesPerBlock", basis_get_bytes_per_block_or_pixel)
|
||||
.function("decodePalettes",
|
||||
&msc::BasisLzEtc1sImageTranscoder::decode_palettes)
|
||||
.function("decodeTables",
|
||||
&msc::BasisLzEtc1sImageTranscoder::decode_tables)
|
||||
.function("transcodeImage",
|
||||
&msc::BasisLzEtc1sImageTranscoder::transcode_image)
|
||||
;
|
||||
|
||||
class_<msc::UastcImageTranscoder>("UastcImageTranscoder")
|
||||
.constructor()
|
||||
.class_function("getBytesPerBlock", basis_get_bytes_per_block_or_pixel)
|
||||
.function("transcodeImage",
|
||||
&msc::UastcImageTranscoder::transcode_image)
|
||||
;
|
||||
|
||||
class_<basisu_transcoder_state>("BasisTranscoderState")
|
||||
.constructor()
|
||||
;
|
||||
|
||||
class_<msc::TranscodedImage>("TranscodedImage")
|
||||
.function( "get_typed_memory_view",
|
||||
&msc::TranscodedImage::get_typed_memory_view )
|
||||
;
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
|
||||
/***************************** 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
|
||||
*/
|
||||
|
||||
.value("UNDEFINED", VK_FORMAT_UNDEFINED)
|
||||
.value("R4G4_UNORM_PACK8", VK_FORMAT_R4G4_UNORM_PACK8)
|
||||
.value("R4G4B4A4_UNORM_PACK16", VK_FORMAT_R4G4B4A4_UNORM_PACK16)
|
||||
.value("B4G4R4A4_UNORM_PACK16", VK_FORMAT_B4G4R4A4_UNORM_PACK16)
|
||||
.value("R5G6B5_UNORM_PACK16", VK_FORMAT_R5G6B5_UNORM_PACK16)
|
||||
.value("B5G6R5_UNORM_PACK16", VK_FORMAT_B5G6R5_UNORM_PACK16)
|
||||
.value("R5G5B5A1_UNORM_PACK16", VK_FORMAT_R5G5B5A1_UNORM_PACK16)
|
||||
.value("B5G5R5A1_UNORM_PACK16", VK_FORMAT_B5G5R5A1_UNORM_PACK16)
|
||||
.value("A1R5G5B5_UNORM_PACK16", VK_FORMAT_A1R5G5B5_UNORM_PACK16)
|
||||
.value("R8_UNORM", VK_FORMAT_R8_UNORM)
|
||||
.value("R8_SNORM", VK_FORMAT_R8_SNORM)
|
||||
.value("R8_UINT", VK_FORMAT_R8_UINT)
|
||||
.value("R8_SINT", VK_FORMAT_R8_SINT)
|
||||
.value("R8_SRGB", VK_FORMAT_R8_SRGB)
|
||||
.value("R8G8_UNORM", VK_FORMAT_R8G8_UNORM)
|
||||
.value("R8G8_SNORM", VK_FORMAT_R8G8_SNORM)
|
||||
.value("R8G8_UINT", VK_FORMAT_R8G8_UINT)
|
||||
.value("R8G8_SINT", VK_FORMAT_R8G8_SINT)
|
||||
.value("R8G8_SRGB", VK_FORMAT_R8G8_SRGB)
|
||||
.value("R8G8B8_UNORM", VK_FORMAT_R8G8B8_UNORM)
|
||||
.value("R8G8B8_SNORM", VK_FORMAT_R8G8B8_SNORM)
|
||||
.value("R8G8B8_UINT", VK_FORMAT_R8G8B8_UINT)
|
||||
.value("R8G8B8_SINT", VK_FORMAT_R8G8B8_SINT)
|
||||
.value("R8G8B8_SRGB", VK_FORMAT_R8G8B8_SRGB)
|
||||
.value("B8G8R8_UNORM", VK_FORMAT_B8G8R8_UNORM)
|
||||
.value("B8G8R8_SNORM", VK_FORMAT_B8G8R8_SNORM)
|
||||
.value("B8G8R8_UINT", VK_FORMAT_B8G8R8_UINT)
|
||||
.value("B8G8R8_SINT", VK_FORMAT_B8G8R8_SINT)
|
||||
.value("B8G8R8_SRGB", VK_FORMAT_B8G8R8_SRGB)
|
||||
.value("R8G8B8A8_UNORM", VK_FORMAT_R8G8B8A8_UNORM)
|
||||
.value("R8G8B8A8_SNORM", VK_FORMAT_R8G8B8A8_SNORM)
|
||||
.value("R8G8B8A8_UINT", VK_FORMAT_R8G8B8A8_UINT)
|
||||
.value("R8G8B8A8_SINT", VK_FORMAT_R8G8B8A8_SINT)
|
||||
.value("R8G8B8A8_SRGB", VK_FORMAT_R8G8B8A8_SRGB)
|
||||
.value("B8G8R8A8_UNORM", VK_FORMAT_B8G8R8A8_UNORM)
|
||||
.value("B8G8R8A8_SNORM", VK_FORMAT_B8G8R8A8_SNORM)
|
||||
.value("B8G8R8A8_UINT", VK_FORMAT_B8G8R8A8_UINT)
|
||||
.value("B8G8R8A8_SINT", VK_FORMAT_B8G8R8A8_SINT)
|
||||
.value("B8G8R8A8_SRGB", VK_FORMAT_B8G8R8A8_SRGB)
|
||||
.value("A8B8G8R8_UNORM_PACK32", VK_FORMAT_A8B8G8R8_UNORM_PACK32)
|
||||
.value("A8B8G8R8_SNORM_PACK32", VK_FORMAT_A8B8G8R8_SNORM_PACK32)
|
||||
.value("A8B8G8R8_UINT_PACK32", VK_FORMAT_A8B8G8R8_UINT_PACK32)
|
||||
.value("A8B8G8R8_SINT_PACK32", VK_FORMAT_A8B8G8R8_SINT_PACK32)
|
||||
.value("A8B8G8R8_SRGB_PACK32", VK_FORMAT_A8B8G8R8_SRGB_PACK32)
|
||||
.value("A2R10G10B10_UNORM_PACK32", VK_FORMAT_A2R10G10B10_UNORM_PACK32)
|
||||
.value("A2R10G10B10_SNORM_PACK32", VK_FORMAT_A2R10G10B10_SNORM_PACK32)
|
||||
.value("A2R10G10B10_UINT_PACK32", VK_FORMAT_A2R10G10B10_UINT_PACK32)
|
||||
.value("A2R10G10B10_SINT_PACK32", VK_FORMAT_A2R10G10B10_SINT_PACK32)
|
||||
.value("A2B10G10R10_UNORM_PACK32", VK_FORMAT_A2B10G10R10_UNORM_PACK32)
|
||||
.value("A2B10G10R10_SNORM_PACK32", VK_FORMAT_A2B10G10R10_SNORM_PACK32)
|
||||
.value("A2B10G10R10_UINT_PACK32", VK_FORMAT_A2B10G10R10_UINT_PACK32)
|
||||
.value("A2B10G10R10_SINT_PACK32", VK_FORMAT_A2B10G10R10_SINT_PACK32)
|
||||
.value("R16_UNORM", VK_FORMAT_R16_UNORM)
|
||||
.value("R16_SNORM", VK_FORMAT_R16_SNORM)
|
||||
.value("R16_UINT", VK_FORMAT_R16_UINT)
|
||||
.value("R16_SINT", VK_FORMAT_R16_SINT)
|
||||
.value("R16_SFLOAT", VK_FORMAT_R16_SFLOAT)
|
||||
.value("R16G16_UNORM", VK_FORMAT_R16G16_UNORM)
|
||||
.value("R16G16_SNORM", VK_FORMAT_R16G16_SNORM)
|
||||
.value("R16G16_UINT", VK_FORMAT_R16G16_UINT)
|
||||
.value("R16G16_SINT", VK_FORMAT_R16G16_SINT)
|
||||
.value("R16G16_SFLOAT", VK_FORMAT_R16G16_SFLOAT)
|
||||
.value("R16G16B16_UNORM", VK_FORMAT_R16G16B16_UNORM)
|
||||
.value("R16G16B16_SNORM", VK_FORMAT_R16G16B16_SNORM)
|
||||
.value("R16G16B16_UINT", VK_FORMAT_R16G16B16_UINT)
|
||||
.value("R16G16B16_SINT", VK_FORMAT_R16G16B16_SINT)
|
||||
.value("R16G16B16_SFLOAT", VK_FORMAT_R16G16B16_SFLOAT)
|
||||
.value("R16G16B16A16_UNORM", VK_FORMAT_R16G16B16A16_UNORM)
|
||||
.value("R16G16B16A16_SNORM", VK_FORMAT_R16G16B16A16_SNORM)
|
||||
.value("R16G16B16A16_UINT", VK_FORMAT_R16G16B16A16_UINT)
|
||||
.value("R16G16B16A16_SINT", VK_FORMAT_R16G16B16A16_SINT)
|
||||
.value("R16G16B16A16_SFLOAT", VK_FORMAT_R16G16B16A16_SFLOAT)
|
||||
.value("R32_UINT", VK_FORMAT_R32_UINT)
|
||||
.value("R32_SINT", VK_FORMAT_R32_SINT)
|
||||
.value("R32_SFLOAT", VK_FORMAT_R32_SFLOAT)
|
||||
.value("R32G32_UINT", VK_FORMAT_R32G32_UINT)
|
||||
.value("R32G32_SINT", VK_FORMAT_R32G32_SINT)
|
||||
.value("R32G32_SFLOAT", VK_FORMAT_R32G32_SFLOAT)
|
||||
.value("R32G32B32_UINT", VK_FORMAT_R32G32B32_UINT)
|
||||
.value("R32G32B32_SINT", VK_FORMAT_R32G32B32_SINT)
|
||||
.value("R32G32B32_SFLOAT", VK_FORMAT_R32G32B32_SFLOAT)
|
||||
.value("R32G32B32A32_UINT", VK_FORMAT_R32G32B32A32_UINT)
|
||||
.value("R32G32B32A32_SINT", VK_FORMAT_R32G32B32A32_SINT)
|
||||
.value("R32G32B32A32_SFLOAT", VK_FORMAT_R32G32B32A32_SFLOAT)
|
||||
.value("R64_UINT", VK_FORMAT_R64_UINT)
|
||||
.value("R64_SINT", VK_FORMAT_R64_SINT)
|
||||
.value("R64_SFLOAT", VK_FORMAT_R64_SFLOAT)
|
||||
.value("R64G64_UINT", VK_FORMAT_R64G64_UINT)
|
||||
.value("R64G64_SINT", VK_FORMAT_R64G64_SINT)
|
||||
.value("R64G64_SFLOAT", VK_FORMAT_R64G64_SFLOAT)
|
||||
.value("R64G64B64_UINT", VK_FORMAT_R64G64B64_UINT)
|
||||
.value("R64G64B64_SINT", VK_FORMAT_R64G64B64_SINT)
|
||||
.value("R64G64B64_SFLOAT", VK_FORMAT_R64G64B64_SFLOAT)
|
||||
.value("R64G64B64A64_UINT", VK_FORMAT_R64G64B64A64_UINT)
|
||||
.value("R64G64B64A64_SINT", VK_FORMAT_R64G64B64A64_SINT)
|
||||
.value("R64G64B64A64_SFLOAT", VK_FORMAT_R64G64B64A64_SFLOAT)
|
||||
.value("B10G11R11_UFLOAT_PACK32", VK_FORMAT_B10G11R11_UFLOAT_PACK32)
|
||||
.value("E5B9G9R9_UFLOAT_PACK32", VK_FORMAT_E5B9G9R9_UFLOAT_PACK32)
|
||||
.value("D16_UNORM", VK_FORMAT_D16_UNORM)
|
||||
.value("X8_D24_UNORM_PACK32", VK_FORMAT_X8_D24_UNORM_PACK32)
|
||||
.value("D32_SFLOAT", VK_FORMAT_D32_SFLOAT)
|
||||
.value("S8_UINT", VK_FORMAT_S8_UINT)
|
||||
.value("D16_UNORM_S8_UINT", VK_FORMAT_D16_UNORM_S8_UINT)
|
||||
.value("D24_UNORM_S8_UINT", VK_FORMAT_D24_UNORM_S8_UINT)
|
||||
.value("D32_SFLOAT_S8_UINT", VK_FORMAT_D32_SFLOAT_S8_UINT)
|
||||
.value("BC1_RGB_UNORM_BLOCK", VK_FORMAT_BC1_RGB_UNORM_BLOCK)
|
||||
.value("BC1_RGB_SRGB_BLOCK", VK_FORMAT_BC1_RGB_SRGB_BLOCK)
|
||||
.value("BC1_RGBA_UNORM_BLOCK", VK_FORMAT_BC1_RGBA_UNORM_BLOCK)
|
||||
.value("BC1_RGBA_SRGB_BLOCK", VK_FORMAT_BC1_RGBA_SRGB_BLOCK)
|
||||
.value("BC2_UNORM_BLOCK", VK_FORMAT_BC2_UNORM_BLOCK)
|
||||
.value("BC2_SRGB_BLOCK", VK_FORMAT_BC2_SRGB_BLOCK)
|
||||
.value("BC3_UNORM_BLOCK", VK_FORMAT_BC3_UNORM_BLOCK)
|
||||
.value("BC3_SRGB_BLOCK", VK_FORMAT_BC3_SRGB_BLOCK)
|
||||
.value("BC4_UNORM_BLOCK", VK_FORMAT_BC4_UNORM_BLOCK)
|
||||
.value("BC4_SNORM_BLOCK", VK_FORMAT_BC4_SNORM_BLOCK)
|
||||
.value("BC5_UNORM_BLOCK", VK_FORMAT_BC5_UNORM_BLOCK)
|
||||
.value("BC5_SNORM_BLOCK", VK_FORMAT_BC5_SNORM_BLOCK)
|
||||
.value("BC6H_UFLOAT_BLOCK", VK_FORMAT_BC6H_UFLOAT_BLOCK)
|
||||
.value("BC6H_SFLOAT_BLOCK", VK_FORMAT_BC6H_SFLOAT_BLOCK)
|
||||
.value("BC7_UNORM_BLOCK", VK_FORMAT_BC7_UNORM_BLOCK)
|
||||
.value("BC7_SRGB_BLOCK", VK_FORMAT_BC7_SRGB_BLOCK)
|
||||
.value("ETC2_R8G8B8_UNORM_BLOCK", VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK)
|
||||
.value("ETC2_R8G8B8_SRGB_BLOCK", VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK)
|
||||
.value("ETC2_R8G8B8A1_UNORM_BLOCK", VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK)
|
||||
.value("ETC2_R8G8B8A1_SRGB_BLOCK", VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK)
|
||||
.value("ETC2_R8G8B8A8_UNORM_BLOCK", VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK)
|
||||
.value("ETC2_R8G8B8A8_SRGB_BLOCK", VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK)
|
||||
.value("EAC_R11_UNORM_BLOCK", VK_FORMAT_EAC_R11_UNORM_BLOCK)
|
||||
.value("EAC_R11_SNORM_BLOCK", VK_FORMAT_EAC_R11_SNORM_BLOCK)
|
||||
.value("EAC_R11G11_UNORM_BLOCK", VK_FORMAT_EAC_R11G11_UNORM_BLOCK)
|
||||
.value("EAC_R11G11_SNORM_BLOCK", VK_FORMAT_EAC_R11G11_SNORM_BLOCK)
|
||||
.value("ASTC_4x4_UNORM_BLOCK", VK_FORMAT_ASTC_4x4_UNORM_BLOCK)
|
||||
.value("ASTC_4x4_SRGB_BLOCK", VK_FORMAT_ASTC_4x4_SRGB_BLOCK)
|
||||
.value("ASTC_5x4_UNORM_BLOCK", VK_FORMAT_ASTC_5x4_UNORM_BLOCK)
|
||||
.value("ASTC_5x4_SRGB_BLOCK", VK_FORMAT_ASTC_5x4_SRGB_BLOCK)
|
||||
.value("ASTC_5x5_UNORM_BLOCK", VK_FORMAT_ASTC_5x5_UNORM_BLOCK)
|
||||
.value("ASTC_5x5_SRGB_BLOCK", VK_FORMAT_ASTC_5x5_SRGB_BLOCK)
|
||||
.value("ASTC_6x5_UNORM_BLOCK", VK_FORMAT_ASTC_6x5_UNORM_BLOCK)
|
||||
.value("ASTC_6x5_SRGB_BLOCK", VK_FORMAT_ASTC_6x5_SRGB_BLOCK)
|
||||
.value("ASTC_6x6_UNORM_BLOCK", VK_FORMAT_ASTC_6x6_UNORM_BLOCK)
|
||||
.value("ASTC_6x6_SRGB_BLOCK", VK_FORMAT_ASTC_6x6_SRGB_BLOCK)
|
||||
.value("ASTC_8x5_UNORM_BLOCK", VK_FORMAT_ASTC_8x5_UNORM_BLOCK)
|
||||
.value("ASTC_8x5_SRGB_BLOCK", VK_FORMAT_ASTC_8x5_SRGB_BLOCK)
|
||||
.value("ASTC_8x6_UNORM_BLOCK", VK_FORMAT_ASTC_8x6_UNORM_BLOCK)
|
||||
.value("ASTC_8x6_SRGB_BLOCK", VK_FORMAT_ASTC_8x6_SRGB_BLOCK)
|
||||
.value("ASTC_8x8_UNORM_BLOCK", VK_FORMAT_ASTC_8x8_UNORM_BLOCK)
|
||||
.value("ASTC_8x8_SRGB_BLOCK", VK_FORMAT_ASTC_8x8_SRGB_BLOCK)
|
||||
.value("ASTC_10x5_UNORM_BLOCK", VK_FORMAT_ASTC_10x5_UNORM_BLOCK)
|
||||
.value("ASTC_10x5_SRGB_BLOCK", VK_FORMAT_ASTC_10x5_SRGB_BLOCK)
|
||||
.value("ASTC_10x6_UNORM_BLOCK", VK_FORMAT_ASTC_10x6_UNORM_BLOCK)
|
||||
.value("ASTC_10x6_SRGB_BLOCK", VK_FORMAT_ASTC_10x6_SRGB_BLOCK)
|
||||
.value("ASTC_10x8_UNORM_BLOCK", VK_FORMAT_ASTC_10x8_UNORM_BLOCK)
|
||||
.value("ASTC_10x8_SRGB_BLOCK", VK_FORMAT_ASTC_10x8_SRGB_BLOCK)
|
||||
.value("ASTC_10x10_UNORM_BLOCK", VK_FORMAT_ASTC_10x10_UNORM_BLOCK)
|
||||
.value("ASTC_10x10_SRGB_BLOCK", VK_FORMAT_ASTC_10x10_SRGB_BLOCK)
|
||||
.value("ASTC_12x10_UNORM_BLOCK", VK_FORMAT_ASTC_12x10_UNORM_BLOCK)
|
||||
.value("ASTC_12x10_SRGB_BLOCK", VK_FORMAT_ASTC_12x10_SRGB_BLOCK)
|
||||
.value("ASTC_12x12_UNORM_BLOCK", VK_FORMAT_ASTC_12x12_UNORM_BLOCK)
|
||||
.value("ASTC_12x12_SRGB_BLOCK", VK_FORMAT_ASTC_12x12_SRGB_BLOCK)
|
||||
.value("G8B8G8R8_422_UNORM", VK_FORMAT_G8B8G8R8_422_UNORM)
|
||||
.value("B8G8R8G8_422_UNORM", VK_FORMAT_B8G8R8G8_422_UNORM)
|
||||
.value("R10X6_UNORM_PACK16", VK_FORMAT_R10X6_UNORM_PACK16)
|
||||
.value("R10X6G10X6_UNORM_2PACK16", VK_FORMAT_R10X6G10X6_UNORM_2PACK16)
|
||||
.value("R10X6G10X6B10X6A10X6_UNORM_4PACK16", VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16)
|
||||
.value("G10X6B10X6G10X6R10X6_422_UNORM_4PACK16", VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16)
|
||||
.value("B10X6G10X6R10X6G10X6_422_UNORM_4PACK16", VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16)
|
||||
.value("R12X4_UNORM_PACK16", VK_FORMAT_R12X4_UNORM_PACK16)
|
||||
.value("R12X4G12X4_UNORM_2PACK16", VK_FORMAT_R12X4G12X4_UNORM_2PACK16)
|
||||
.value("R12X4G12X4B12X4A12X4_UNORM_4PACK16", VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16)
|
||||
.value("G12X4B12X4G12X4R12X4_422_UNORM_4PACK16", VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16)
|
||||
.value("B12X4G12X4R12X4G12X4_422_UNORM_4PACK16", VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16)
|
||||
.value("G16B16G16R16_422_UNORM", VK_FORMAT_G16B16G16R16_422_UNORM)
|
||||
.value("B16G16R16G16_422_UNORM", VK_FORMAT_B16G16R16G16_422_UNORM)
|
||||
.value("A4R4G4B4_UNORM_PACK16", VK_FORMAT_A4R4G4B4_UNORM_PACK16)
|
||||
.value("A4B4G4R4_UNORM_PACK16", VK_FORMAT_A4B4G4R4_UNORM_PACK16)
|
||||
.value("ASTC_4x4_SFLOAT_BLOCK", VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK)
|
||||
.value("ASTC_5x4_SFLOAT_BLOCK", VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK)
|
||||
.value("ASTC_5x5_SFLOAT_BLOCK", VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK)
|
||||
.value("ASTC_6x5_SFLOAT_BLOCK", VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK)
|
||||
.value("ASTC_6x6_SFLOAT_BLOCK", VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK)
|
||||
.value("ASTC_8x5_SFLOAT_BLOCK", VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK)
|
||||
.value("ASTC_8x6_SFLOAT_BLOCK", VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK)
|
||||
.value("ASTC_8x8_SFLOAT_BLOCK", VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK)
|
||||
.value("ASTC_10x5_SFLOAT_BLOCK", VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK)
|
||||
.value("ASTC_10x6_SFLOAT_BLOCK", VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK)
|
||||
.value("ASTC_10x8_SFLOAT_BLOCK", VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK)
|
||||
.value("ASTC_10x10_SFLOAT_BLOCK", VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK)
|
||||
.value("ASTC_12x10_SFLOAT_BLOCK", VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK)
|
||||
.value("ASTC_12x12_SFLOAT_BLOCK", VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK)
|
||||
.value("PVRTC1_2BPP_UNORM_BLOCK_IMG", VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG)
|
||||
.value("PVRTC1_4BPP_UNORM_BLOCK_IMG", VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG)
|
||||
.value("PVRTC2_2BPP_UNORM_BLOCK_IMG", VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG)
|
||||
.value("PVRTC2_4BPP_UNORM_BLOCK_IMG", VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG)
|
||||
.value("PVRTC1_2BPP_SRGB_BLOCK_IMG", VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG)
|
||||
.value("PVRTC1_4BPP_SRGB_BLOCK_IMG", VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG)
|
||||
.value("PVRTC2_2BPP_SRGB_BLOCK_IMG", VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG)
|
||||
.value("PVRTC2_4BPP_SRGB_BLOCK_IMG", VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG)
|
||||
.value("ASTC_3x3x3_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT)
|
||||
.value("ASTC_3x3x3_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT)
|
||||
.value("ASTC_3x3x3_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT)
|
||||
.value("ASTC_4x3x3_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT)
|
||||
.value("ASTC_4x3x3_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT)
|
||||
.value("ASTC_4x3x3_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT)
|
||||
.value("ASTC_4x4x3_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT)
|
||||
.value("ASTC_4x4x3_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT)
|
||||
.value("ASTC_4x4x3_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT)
|
||||
.value("ASTC_4x4x4_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT)
|
||||
.value("ASTC_4x4x4_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT)
|
||||
.value("ASTC_4x4x4_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT)
|
||||
.value("ASTC_5x4x4_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT)
|
||||
.value("ASTC_5x4x4_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT)
|
||||
.value("ASTC_5x4x4_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT)
|
||||
.value("ASTC_5x5x4_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT)
|
||||
.value("ASTC_5x5x4_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT)
|
||||
.value("ASTC_5x5x4_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT)
|
||||
.value("ASTC_5x5x5_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT)
|
||||
.value("ASTC_5x5x5_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT)
|
||||
.value("ASTC_5x5x5_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT)
|
||||
.value("ASTC_6x5x5_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT)
|
||||
.value("ASTC_6x5x5_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT)
|
||||
.value("ASTC_6x5x5_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT)
|
||||
.value("ASTC_6x6x5_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT)
|
||||
.value("ASTC_6x6x5_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT)
|
||||
.value("ASTC_6x6x5_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT)
|
||||
.value("ASTC_6x6x6_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT)
|
||||
.value("ASTC_6x6x6_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT)
|
||||
.value("ASTC_6x6x6_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT)
|
||||
.value("R16G16_SFIXED5_NV", VK_FORMAT_R16G16_SFIXED5_NV)
|
||||
.value("A1B5G5R5_UNORM_PACK16_KHR", VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR)
|
||||
.value("A8_UNORM_KHR", VK_FORMAT_A8_UNORM_KHR)
|
||||
|
||||
@@ -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