Compare commits

...
Author SHA1 Message Date
Sungun Park aa0160f8ba fix compile errors 2026-04-29 12:00:41 -07:00
Sungun Park 8c037123b3 Update basis_universal to v2.1.0 2026-04-29 11:20:51 -07:00
Mathias Agopian 9c27bfa3e1 utils: Improve StructureOfArrays (#9936)
- Added `copyRange()` to `StructureOfArrays` to efficiently copy a range
  of elements from another SoA of the same type. It uses `std::copy_n`
  which leverages `memcpy` for trivially copyable types.
- Added `operator[]` to `StructureOfArrays` returning `IteratorValueRef`.
- Added `operator=` for `Structure` (tuple) to `IteratorValueRef` to
  allow direct assignment: `soa[i] = tuple`.
- Added unit tests for these new methods in `test_StructureOfArrays.cpp`.
2026-04-29 09:06:26 -07:00
Filament Bot 25a12b47c9 [automated] Updating /docs due to commit c481025
Full commit hash is c48102504e

DOCS_ALLOW_DIRECT_EDITS
2026-04-29 06:44:13 +00:00
Powei Feng 052e95eb2f mesa: update mesa version and ensure linux build (#9938)
- Update mesa version to 25.0.3 so that it builds with clang-17.
 - Make sure that abseil checks do not block compilation (this
   was due to unknown incompatiblity that appeared when trying to
   build abseil on linux and clang-17).
2026-04-29 04:36:54 +00:00
100 changed files with 150558 additions and 28932 deletions
+3 -2
View File
@@ -21,7 +21,7 @@ fi
OS_NAME=$(uname -s)
LLVM_VERSION=${GITHUB_LLVM_VERSION-17}
MESA_VERSION=${GITHUB_MESA_VERSION-24.2.1}
MESA_VERSION=${GITHUB_MESA_VERSION-25.0.6}
ORIG_DIR=$(pwd)
MESA_DIR=${MESA_DIR-${ORIG_DIR}/mesa}
@@ -87,7 +87,8 @@ if [[ "$OS_NAME" == "Linux" ]]; then
elif [[ "$OS_NAME" == "Darwin" ]]; then
if command -v brew > /dev/null 2>&1; then
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=true brew install autoconf automake libx11 libxext libxrandr \
llvm@${LLVM_VERSION} ninja meson pkg-config libxshmfence
llvm@${LLVM_VERSION} ninja meson pkg-config libxshmfence \
glslang
brew link --overwrite llvm@${LLVM_VERSION}
# For reasons unknown, this is necessary for pkg-config to find homebrew's packages
LOCAL_PKG_CONFIG_PATH="/opt/homebrew/lib/pkgconfig:$PKG_CONFIG_PATH"
+1 -1
View File
@@ -1,7 +1,7 @@
GITHUB_CLANG_VERSION=17
GITHUB_CMAKE_VERSION=3.22.1
GITHUB_NINJA_VERSION=1.10.2
GITHUB_MESA_VERSION=24.2.1
GITHUB_MESA_VERSION=25.0.6
GITHUB_LLVM_VERSION=17
GITHUB_NDK_VERSION=29.0.14206865
GITHUB_EMSDK_VERSION=5.0.4
+2 -2
View File
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.71.1'
implementation 'com.google.android.filament:filament-android:1.71.2'
}
</code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -195,7 +195,7 @@ dependencies {
</div>
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
<p>iOS projects can use CocoaPods to install the latest release:</p>
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.71.1'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.71.2'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
+3 -3
View File
@@ -169,7 +169,7 @@ BasisEncoder* Builder::build() {
// an annotation that gets stored in the KTX2 file, which enables the app to choose the right
// format when it loads and transcodes the texture. Technically however, the transcoder
// SHOULD know about this, since in some scenarios it needs to interpolate between colors.
params.m_ktx2_srgb_transfer_func = !mImpl->linear;
params.m_ktx2_and_basis_srgb_transfer_function = !mImpl->linear;
// Select the same quality that the basis tool selects by default (midpoint of range).
params.m_quality_level = 128;
@@ -181,7 +181,7 @@ BasisEncoder* Builder::build() {
params.m_read_source_images = false;
// We do not want basis to write the file, we want to manually dump "get_output_ktx2_file()"
params.m_write_output_basis_files = false;
params.m_write_output_basis_or_ktx2_files = false;
basisu::basis_compressor* encoder = new basisu::basis_compressor();
@@ -238,7 +238,7 @@ bool BasisEncoder::encode() {
case basis_compressor::cECFailedFrontEnd:
puts("Compressor frontend stage failed!");
break;
case basis_compressor::cECFailedFontendExtract:
case basis_compressor::cECFailedFrontendExtract:
puts("Compressor frontend data extraction failed!");
break;
case basis_compressor::cECFailedBackend:
+4 -4
View File
@@ -135,7 +135,7 @@ static Result transcodeImageLevel(ktx2_transcoder& transcoder,
if (formatInfo.isCompressed) {
const uint32_t qwordsPerBlock = basisu::get_qwords_per_block(destFormat);
const size_t byteCount = (size_t)sizeof(uint64_t) * (size_t)qwordsPerBlock * (size_t)levelInfo.m_total_blocks;
if (qwordsPerBlock != 0 && levelInfo.m_total_blocks != 0 &&
byteCount / qwordsPerBlock / sizeof(uint64_t) != levelInfo.m_total_blocks) {
return Result::COMPRESSED_TRANSCODE_FAILURE;
@@ -156,13 +156,13 @@ static Result transcodeImageLevel(ktx2_transcoder& transcoder,
const uint32_t rowCount = levelInfo.m_orig_height;
const uint32_t bytesPerPix = basis_get_bytes_per_block_or_pixel(formatInfo.basisFormat);
if (bytesPerPix == 0) {
return Result::UNCOMPRESSED_TRANSCODE_FAILURE;
}
const size_t byteCount = (size_t)bytesPerPix * (size_t)levelInfo.m_orig_width * (size_t)rowCount;
if (levelInfo.m_orig_width != 0 &&
byteCount / bytesPerPix / levelInfo.m_orig_width != rowCount) {
return Result::UNCOMPRESSED_TRANSCODE_FAILURE;
@@ -400,7 +400,7 @@ Texture* Ktx2Reader::createTexture(ktx2_transcoder* transcoder, const void* data
if (!info.isSupported || info.transferFunction != transfer) {
continue;
}
if (!basis_is_format_supported(info.basisFormat, transcoder->get_format())) {
if (!basis_is_format_supported(info.basisFormat, transcoder->get_basis_tex_format())) {
continue;
}
const uint32_t layerIndex = 0;
+108 -40
View File
@@ -55,10 +55,19 @@ public:
static constexpr size_t getArrayCount() noexcept { return kArrayCount; }
// Size needed to store "size" array elements
static size_t getNeededSize(size_t size) noexcept {
static size_t getNeededSize(size_t const size) noexcept {
return getOffset(kArrayCount - 1, size) + sizeof(TypeAt<kArrayCount - 1>) * size;
}
template<typename OtherAllocator>
void copyRange(size_t destOffset,
const StructureOfArraysBase<OtherAllocator, Elements...>& src, size_t srcOffset, size_t count) {
assert(destOffset + count <= mCapacity);
assert(srcOffset + count <= src.mSize);
Copier<OtherAllocator> copier{this, src, destOffset, srcOffset, count};
const_cast<StructureOfArraysBase*>(this)->for_each_index(mArrays, copier);
}
// --------------------------------------------------------------------------------------------
class IteratorValue;
@@ -77,21 +86,28 @@ public:
friend class IteratorValue;
friend iterator;
friend const_iterator;
friend class StructureOfArraysBase;
StructureOfArraysBase* const UTILS_RESTRICT soa;
size_t const index;
IteratorValueRef(StructureOfArraysBase* soa, size_t index) : soa(soa), index(index) { }
IteratorValueRef(StructureOfArraysBase* soa, size_t const index) : soa(soa), index(index) { }
// assigns a value_type to a reference (i.e. assigns to what's pointed to by the reference)
template<size_t ... Is>
IteratorValueRef& assign(IteratorValue const& rhs, std::index_sequence<Is...>);
template<size_t ... Is>
IteratorValueRef& assign(Structure const& rhs, std::index_sequence<Is...>);
template<size_t ... Is>
IteratorValueRef& assign(Structure&& rhs, std::index_sequence<Is...>) noexcept;
// assigns a value_type to a reference (i.e. assigns to what's pointed to by the reference)
template<size_t ... Is>
IteratorValueRef& assign(IteratorValue&& rhs, std::index_sequence<Is...>) noexcept;
// objects pointed to by reference can be swapped, so provide the special swap() function.
friend void swap(IteratorValueRef lhs, IteratorValueRef rhs) {
friend void swap(IteratorValueRef const lhs, IteratorValueRef const rhs) {
lhs.soa->swap(lhs.index, rhs.index);
}
@@ -99,6 +115,9 @@ public:
// references can be created by copy-assignment only
IteratorValueRef(IteratorValueRef const& rhs) noexcept : soa(rhs.soa), index(rhs.index) { }
IteratorValueRef& operator=(Structure const& rhs);
IteratorValueRef& operator=(Structure&& rhs) noexcept;
// copy the content of a reference to the content of this one
IteratorValueRef& operator=(IteratorValueRef const& rhs);
@@ -120,6 +139,10 @@ public:
template<size_t I> TypeAt<I>& get() { return soa->elementAt<I>(index); }
};
IteratorValueRef operator[](size_t index) noexcept {
return {this, index};
}
/*
* The value_type of iterator. This is basically the "structure" of the SoA.
@@ -175,7 +198,7 @@ public:
CVQualifiedSOAPointer soa; // don't use restrict, can have aliases if multiple iterators are created
size_t index;
Iterator(CVQualifiedSOAPointer soa, size_t index) : soa(soa), index(index) {}
Iterator(CVQualifiedSOAPointer soa, size_t const index) : soa(soa), index(index) {}
public:
using value_type = IteratorValue;
@@ -196,10 +219,10 @@ public:
Iterator& operator++() { ++index; return *this; }
Iterator& operator--() { --index; return *this; }
Iterator& operator+=(size_t n) { index += n; return *this; }
Iterator& operator-=(size_t n) { index -= n; return *this; }
Iterator operator+(size_t n) const { return { soa, index + n }; }
Iterator operator-(size_t n) const { return { soa, index - n }; }
Iterator& operator+=(size_t const n) { index += n; return *this; }
Iterator& operator-=(size_t const n) { index -= n; return *this; }
Iterator operator+(size_t const n) const { return { soa, index + n }; }
Iterator operator-(size_t const n) const { return { soa, index - n }; }
difference_type operator-(Iterator const& rhs) const { return index - rhs.index; }
bool operator==(Iterator const& rhs) const { return (index == rhs.index); }
bool operator!=(Iterator const& rhs) const { return (index != rhs.index); }
@@ -222,7 +245,7 @@ public:
StructureOfArraysBase() = default;
explicit StructureOfArraysBase(size_t capacity) {
explicit StructureOfArraysBase(size_t const capacity) {
setCapacity(capacity);
}
@@ -270,7 +293,7 @@ public:
// set the capacity of the array. the capacity cannot be smaller than the current size,
// the call is a no-op in that case.
UTILS_NOINLINE
void setCapacity(size_t capacity) {
void setCapacity(size_t const capacity) {
// allocate enough space for "capacity" elements of each array
// capacity cannot change when optional storage is specified
if (capacity >= mSize) {
@@ -292,7 +315,7 @@ public:
}
}
void ensureCapacity(size_t needed) {
void ensureCapacity(size_t const needed) {
if (UTILS_UNLIKELY(needed > mCapacity)) {
// not enough space, increase the capacity
const size_t capacity = (needed > SIZE_MAX / 3) ? needed : (needed * 3 + 1) / 2;
@@ -305,7 +328,7 @@ public:
// If the arrays don't have enough capacity, the capacity is increased accordingly
// (the capacity is set to 3/2 of the asked size).
UTILS_NOINLINE
void resize(size_t needed) {
void resize(size_t const needed) {
ensureCapacity(needed);
resizeNoCheck(needed);
if (needed <= mCapacity) {
@@ -318,7 +341,7 @@ public:
}
inline void swap(size_t i, size_t j) noexcept {
void swap(size_t i, size_t j) noexcept {
forEach([i, j](auto p) {
using std::swap;
swap(p[i], p[j]);
@@ -326,7 +349,7 @@ public:
}
// remove and destroy the last element of each array
inline void pop_back() noexcept {
void pop_back() noexcept {
if (mSize) {
destroy_each(mSize - 1, mSize);
mSize--;
@@ -483,80 +506,97 @@ public:
using Type = typename SoA::template TypeAt<E>;
UTILS_ALWAYS_INLINE Field& operator = (Field&& rhs) noexcept {
soa.elementAt<E>(i) = soa.elementAt<E>(rhs.i);
soa.template elementAt<E>(i) = soa.template elementAt<E>(rhs.i);
return *this;
}
// auto-conversion to the field's type
UTILS_ALWAYS_INLINE operator Type&() noexcept {
return soa.elementAt<E>(i);
return soa.template elementAt<E>(i);
}
UTILS_ALWAYS_INLINE operator Type const&() const noexcept {
return soa.elementAt<E>(i);
return soa.template elementAt<E>(i);
}
// dereferencing the selected field
UTILS_ALWAYS_INLINE Type& operator ->() noexcept {
return soa.elementAt<E>(i);
return soa.template elementAt<E>(i);
}
UTILS_ALWAYS_INLINE Type const& operator ->() const noexcept {
return soa.elementAt<E>(i);
return soa.template elementAt<E>(i);
}
// address-of the selected field
UTILS_ALWAYS_INLINE Type* operator &() noexcept {
return &soa.elementAt<E>(i);
return &soa.template elementAt<E>(i);
}
UTILS_ALWAYS_INLINE Type const* operator &() const noexcept {
return &soa.elementAt<E>(i);
return &soa.template elementAt<E>(i);
}
// assignment to the field
UTILS_ALWAYS_INLINE Type const& operator = (Type const& other) noexcept {
return (soa.elementAt<E>(i) = other);
return (soa.template elementAt<E>(i) = other);
}
UTILS_ALWAYS_INLINE Type const& operator = (Type&& other) noexcept {
return (soa.elementAt<E>(i) = std::forward<Type>(other));
return (soa.template elementAt<E>(i) = std::forward<Type>(other));
}
// comparisons
UTILS_ALWAYS_INLINE bool operator==(Type const& other) const {
return (soa.elementAt<E>(i) == other);
return (soa.template elementAt<E>(i) == other);
}
UTILS_ALWAYS_INLINE bool operator!=(Type const& other) const {
return (soa.elementAt<E>(i) != other);
return (soa.template elementAt<E>(i) != other);
}
// calling the field
template <typename ... ARGS>
UTILS_ALWAYS_INLINE decltype(auto) operator()(ARGS&& ... args) noexcept {
return soa.elementAt<E>(i)(std::forward<ARGS>(args)...);
return soa.template elementAt<E>(i)(std::forward<ARGS>(args)...);
}
template <typename ... ARGS>
UTILS_ALWAYS_INLINE decltype(auto) operator()(ARGS&& ... args) const noexcept {
return soa.elementAt<E>(i)(std::forward<ARGS>(args)...);
return soa.template elementAt<E>(i)(std::forward<ARGS>(args)...);
}
};
private:
template<typename OtherAllocator>
struct Copier {
StructureOfArraysBase* dest;
const StructureOfArraysBase<OtherAllocator, Elements...>& src;
size_t destOffset;
size_t srcOffset;
size_t count;
template<size_t I>
void operator()(TypeAt<I>* destPtr) const {
using ElementType = TypeAt<I>;
ElementType const* const s = std::get<I>(src.mArrays) + srcOffset;
ElementType* const d = destPtr + destOffset;
std::copy_n(s, count, d);
}
};
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline std::enable_if_t<I == sizeof...(Tp), void>
std::enable_if_t<I == sizeof...(Tp), void>
for_each(std::tuple<Tp...>&, FuncT) {}
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline std::enable_if_t<I < sizeof...(Tp), void>
std::enable_if_t<I < sizeof...(Tp), void>
for_each(std::tuple<Tp...>& t, FuncT f) {
f(I, std::get<I>(t));
for_each<I + 1, FuncT, Tp...>(t, f);
}
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline std::enable_if_t<I == sizeof...(Tp), void>
std::enable_if_t<I == sizeof...(Tp), void>
for_each_index(std::tuple<Tp...>&, FuncT) {}
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline std::enable_if_t<I < sizeof...(Tp), void>
std::enable_if_t<I < sizeof...(Tp), void>
for_each_index(std::tuple<Tp...>& t, FuncT f) {
f.template operator()<I>(std::get<I>(t));
for_each_index<I + 1, FuncT, Tp...>(t, f);
}
inline void resizeNoCheck(size_t needed) noexcept {
void resizeNoCheck(size_t const needed) noexcept {
assert(mCapacity >= needed);
if (needed < mSize) {
// we shrink the arrays
@@ -570,12 +610,12 @@ private:
}
// this calculates the offset adjusted for all data alignment of a given array
static inline size_t getOffset(size_t index, size_t capacity) noexcept {
static size_t getOffset(size_t index, size_t const capacity) noexcept {
auto offsets = getOffsets(capacity);
return offsets[index];
}
static inline std::array<size_t, kArrayCount> getOffsets(size_t capacity) noexcept {
static std::array<size_t, kArrayCount> getOffsets(size_t capacity) noexcept {
// compute the required size of each array
const size_t sizes[] = { (sizeof(Elements) * capacity)... };
@@ -618,7 +658,7 @@ private:
});
}
void move_each(void* buffer, size_t capacity) noexcept {
void move_each(void* buffer, size_t const capacity) noexcept {
auto offsets = getOffsets(capacity);
size_t index = 0;
if (mSize) {
@@ -653,7 +693,7 @@ private:
// update the pointers
for_each(mArrays, [buffer, &offsets](size_t i, auto&& p) {
using Type = std::remove_reference_t<decltype(p)>;
p = Type((char*)buffer + offsets[i]);
p = Type(static_cast<char*>(buffer) + offsets[i]);
});
}
@@ -668,7 +708,6 @@ private:
template<typename Allocator, typename... Elements>
inline
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::operator=(
IteratorValueRef const& rhs) {
@@ -676,16 +715,28 @@ StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::operator=(
}
template<typename Allocator, typename... Elements>
inline
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::operator=(
IteratorValueRef&& rhs) noexcept {
return operator=(IteratorValue(rhs));
}
template<typename Allocator, typename... Elements>
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::operator=(
Structure const& rhs) {
return assign(rhs, std::make_index_sequence<kArrayCount>());
}
template<typename Allocator, typename... Elements>
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::operator=(
Structure&& rhs) noexcept {
return assign(std::move(rhs), std::make_index_sequence<kArrayCount>());
}
template<typename Allocator, typename... Elements>
template<size_t... Is>
inline
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::assign(
IteratorValue const& rhs, std::index_sequence<Is...>) {
@@ -696,7 +747,24 @@ StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::assign(
template<typename Allocator, typename... Elements>
template<size_t... Is>
inline
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::assign(
Structure const& rhs, std::index_sequence<Is...>) {
auto UTILS_UNUSED l = {(soa->template elementAt<Is>(index) = std::get<Is>(rhs), 0)...};
return *this;
}
template<typename Allocator, typename... Elements>
template<size_t... Is>
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::assign(
Structure&& rhs, std::index_sequence<Is...>) noexcept {
auto UTILS_UNUSED l = {(soa->template elementAt<Is>(index) = std::move(std::get<Is>(rhs)), 0)...};
return *this;
}
template<typename Allocator, typename... Elements>
template<size_t... Is>
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::assign(
IteratorValue&& rhs, std::index_sequence<Is...>) noexcept {
+46 -2
View File
@@ -19,6 +19,11 @@
#include <utils/StructureOfArrays.h>
#include <math/vec4.h>
#include <cstdint>
#include <cstdlib>
#include <memory>
#include <utility>
using namespace filament::math;
using namespace utils;
@@ -71,7 +76,7 @@ TEST(StructureOfArraysTest, Iterator) {
EXPECT_EQ(soa.elementAt<0>(2), 3.0f);
for (size_t i = 0; i < 8; i++) {
soa.elementAt<0>(i) = (float)std::rand();
soa.elementAt<0>(i) = float(std::rand());
soa.elementAt<1>(i) = soa.elementAt<0>(i) * 2;
soa.elementAt<2>(i) = soa.elementAt<0>(i) * 4;
}
@@ -103,7 +108,7 @@ TEST(StructureOfArraysTest, Simple) {
soa.elementAt<2>(i) = i * 4;
}
size_t capacity = soa.capacity();
size_t const capacity = soa.capacity();
// check that each array doesn't overlap the previous one
EXPECT_TRUE((void*)soa.data<1>() >= (void*)(soa.data<0>() + capacity));
@@ -183,3 +188,42 @@ TEST(StructureOfArraysTest, MoveOnly) {
EXPECT_EQ(*soa.elementAt<1>(1).get(), 2);
}
TEST(StructureOfArraysTest, CopyRange) {
StructureOfArrays<float, double, TestFloat4> soa1;
StructureOfArrays<float, double, TestFloat4> soa2;
soa1.setCapacity(10);
soa1.resize(5);
for (size_t i = 0; i < 5; i++) {
soa1.elementAt<0>(i) = i;
soa1.elementAt<1>(i) = i * 2;
soa1.elementAt<2>(i) = i * 4;
}
soa2.setCapacity(10);
soa2.resize(5);
soa2.copyRange(1, soa1, 1, 3);
EXPECT_EQ(soa2.elementAt<0>(1), 1.0f);
EXPECT_EQ(soa2.elementAt<1>(1), 2.0);
EXPECT_EQ(soa2.elementAt<2>(1), TestFloat4{ 4.0f });
EXPECT_EQ(soa2.elementAt<0>(3), 3.0f);
EXPECT_EQ(soa2.elementAt<1>(3), 6.0);
EXPECT_EQ(soa2.elementAt<2>(3), TestFloat4{ 12.0f });
}
TEST(StructureOfArraysTest, AssignTuple) {
StructureOfArrays<float, double, TestFloat4> soa;
soa.setCapacity(10);
soa.resize(5);
auto tuple = std::make_tuple(1.0f, 2.0, TestFloat4{4.0f});
soa[2] = tuple;
EXPECT_EQ(soa.elementAt<0>(2), 1.0f);
EXPECT_EQ(soa.elementAt<1>(2), 2.0);
EXPECT_EQ(soa.elementAt<2>(2), TestFloat4{ 4.0f });
}
+4
View File
@@ -3,6 +3,10 @@ option(FILAMENT_USE_EXTERNAL_ABSL "Use find_package to locate an external Abseil
if (FILAMENT_USE_EXTERNAL_ABSL)
find_package(absl CONFIG QUIET)
else()
# This will skip checks that sometimes fail due to enviornment incompatibility on Linxu. Abseil can
# still be built without this.
set(ABSL_INTERNAL_AT_LEAST_CXX17 TRUE CACHE BOOL "Forced in wrapper")
set(ABSL_INTERNAL_AT_LEAST_CXX20 TRUE CACHE BOOL "Forced in wrapper")
add_subdirectory(../ "${CMAKE_CURRENT_BINARY_DIR}/abseil")
endif()
+1 -1
View File
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2019-2026 Binomial LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+428 -203
View File
@@ -1,227 +1,489 @@
# basis_universal
Basis Universal Supercompressed GPU Texture Codec
<!-- Copyright 2016-2026 Binomial LLC -->
<!-- SPDX-License-Identifier: Apache-2.0 -->
<sub>(This software is available under a free and permissive license (Apache 2.0), but like many other large open source projects it needs financial support to sustain its continued security, bug fixes and improvements. In addition to maintenance and stability there are many desirable features and new interchange formats we would like to add. If your company is using Basis Universal in a product, please consider reaching out.)</sub>
# basis_universal v2.1
An LDR/HDR portable GPU supercompressed texture transcoding system.
Businesses: support continued development and maintenance via invoiced technical support, maintenance, or sponsoring contracts:
<br>&nbsp;&nbsp;_E-mail: info @ binomial dot info_ or contact us on [Twitter](https://twitter.com/_binomial)
Also see the [Sponsors](https://github.com/BinomialLLC/basis_universal/wiki/Sponsors-and-Supporters) wiki page.
[![Build status](https://img.shields.io/appveyor/build/BinomialLLC/basis-universal/master.svg)](https://ci.appveyor.com/project/BinomialLLC/basis-universal)
----
[![Build status](https://ci.appveyor.com/api/projects/status/87eb0o96pjho4sh0?svg=true)](https://ci.appveyor.com/project/BinomialLLC/basis-universal)
Intro
-----
Basis Universal is a ["supercompressed"](http://gamma.cs.unc.edu/GST/gst.pdf) GPU texture data interchange system that supports two highly compressed intermediate file formats (.basis or the [.KTX2 open standard from the Khronos Group](https://github.khronos.org/KTX-Specification/)) that can be quickly transcoded to a [very wide variety](https://github.com/BinomialLLC/basis_universal/wiki/OpenGL-texture-format-enums-table) of GPU compressed and uncompressed pixel formats: ASTC 4x4 L/LA/RGB/RGBA, PVRTC1 4bpp RGB/RGBA, PVRTC2 RGB/RGBA, BC7 mode 6 RGB, BC7 mode 5 RGB/RGBA, BC1-5 RGB/RGBA/X/XY, ETC1 RGB, ETC2 RGBA, ATC RGB/RGBA, ETC2 EAC R11 and RG11, FXT1 RGB, and uncompressed raster image formats 8888/565/4444.
Basis Universal™ v2.1 is an open source [supercompressed](http://gamma.cs.unc.edu/GST/gst.pdf) LDR/HDR GPU compressed texture interchange system from Binomial LLC that supports two intermediate file formats: the [.KTX2 open standard from the Khronos Group](https://registry.khronos.org/KTX/specs/2.0/ktxspec.v2.html), and our own ".basis" file format. These file formats support rapid transcoding to virtually any compressed [GPU texture format](https://grokipedia.com/page/texture_compression) released over the past quarter century.
The system now supports two modes: a high quality mode which is internally based off the [UASTC compressed texture format](https://richg42.blogspot.com/2020/01/uastc-block-format-encoding.html), and the original lower quality mode which is based off a subset of ETC1 called "ETC1S". UASTC is for extremely high quality (similar to BC7 quality) textures, and ETC1S is for very small files. The ETC1S system includes built-in data compression, while the UASTC system includes an optional Rate Distortion Optimization (RDO) post-process stage that conditions the encoded UASTC texture data in the .basis file so it can be more effectively LZ compressed by the end user. More technical details about UASTC integration are [here](https://github.com/BinomialLLC/basis_universal/wiki/UASTC-implementation-details).
## GPU Textures are Infrastructure
Basis files support non-uniform texture arrays, so cubemaps, volume textures, texture arrays, mipmap levels, video sequences, or arbitrary texture "tiles" can be stored in a single file. The compressor is able to exploit color and pattern correlations across the entire file, so multiple images with mipmaps can be stored very efficiently in a single file.
Our overall goal is to simplify the encoding and efficient distribution of *portable* LDR and HDR GPU texture, image, and short [texture video](https://github.com/BinomialLLC/basis_universal/wiki/Encoding-ETC1S-and-XUASTC-LDR-Texture-Video) content in a way that is compatible with any GPU or rendering/graphics API.
The system's bitrate depends on the quality setting and image content, but common usable ETC1S bitrates are .3-1.25 bits/texel. ETC1S .basis files are typically 10-25% smaller than using RDO texture compression of the internal texture data stored in the .basis file followed by LZMA. For UASTC files, the bitrate is fixed at 8bpp, but with RDO post-processing and user-provided LZ compression on the .basis file the effective bitrate can be as low as 2bpp for video or for individual textures approximately 4-6bpp.
The system supports seven modes (or codecs). In the order they were implemented:
1. **ETC1S**: A supercompressed subset of ETC1 designed for very fast transcoding to other LDR texture formats, low/medium quality but high compression, slightly faster transcoding to other LDR texture formats vs. libjpeg.
2. **UASTC LDR 4x4 (with or without RDO)**: Custom ASTC 4x4-like format designed for very fast transcoding to other LDR texture formats, high quality
3. **UASTC HDR 4x4**: Standard ASTC HDR 4x4 texture data, but constrained for very fast transcoding to BC6H
4. **ASTC HDR 6x6 (with or without RDO)**: Standard ASTC HDR 6x6
5. **UASTC HDR 6x6 Intermediate ("GPU Photo HDR")**: Supercompressed ASTC HDR 6x6
6. **ASTC LDR 4x4-12x12 (all 14 standard ASTC block sizes, with or without basic windowed RDO)**: Standard ASTC LDR 4x4-12x12
7. **XUASTC LDR 4x4-12x12 (all 14 standard ASTC block sizes, "GPU Photo LDR/SDR")**: Latent-space supercompressed ASTC LDR with Weight Grid DCT ([Discrete Cosine Transform](https://grokipedia.com/page/Discrete_cosine_transform)) for very high quality, extreme bitrate scalability, optional adaptive deblocking (CPU or using a [simple GPU pixel shader](https://github.com/BinomialLLC/basis_universal/tree/master/shader_deblocking) compatible with mipmapping and filtering), three entropy coding profiles (Zstd, arithmetic or hybrid). See [JPEG for ASTC](https://github.com/BinomialLLC/basis_universal/wiki/JPEG-for-ASTC), and the [ASTC and XUASTC LDR Usage Guide](https://github.com/BinomialLLC/basis_universal/wiki/ASTC-and-XUASTC-LDR-Usage-Guide).
The .basis and .KTX2 transcoders have been fuzz tested using [zzuf](https://www.linux.com/news/fuzz-testing-zzuf).
The C/C++ encoder and transcoder libraries can be compiled to native code or WebAssembly (web or WASI), and all encoder/transcoder features can be accessed from JavaScript via a C++ wrapper library which optionally supports [WASM multithreading](https://web.dev/articles/webassembly-threads) for fast encoding in the browser. [WASM WASI](https://wasi.dev/) builds, for the command line tool and the encoder/transcoder as a WASI module using a pure C API, are also supported.
So far, we've compiled the code using MSVC 2019, under Ubuntu 18.04 and 20 x64 using cmake with either clang 3.8 or gcc 5.4, and emscripten 1.35 to asm.js. (Be sure to use this version or later of emcc, as earlier versions fail with internal errors/exceptions during compilation.)
Full Python support for encoding/transcoding is now available, supporting native or WASM modules, but is still in the early stages of development.
Basis Universal supports "skip blocks" in ETC1S compressed texture arrays, which makes it useful for basic [compressed texture video](http://gamma.cs.unc.edu/MPTC/) applications. Note that Basis Universal is still at heart a GPU texture compression system, not a dedicated video codec, so bitrates will be larger than even MPEG1.
1/10/21 release notes:
License/Legal
-------------
For v1.13, we've added numerous ETC1S encoder optimizations designed to greatly speed up single threaded encoding time, as well as greatly reducing overall CPU utilization when multithreading is enabled. For benchmarking, we're using "-q 128 -no_multithreading -mip_fast". The encoder now uses approximately 1/3rd as much total CPU time for the same PSNR. The encoder can now optionally utilize SSE 4.1 - see the "-no_sse" command line option.
The reference encoder library, transcoder, and most specification documents in this repo (unless otherwise explictly indicated) are Copyright © 2016–2026 Binomial LLC. All rights reserved except as granted under the [Apache 2.0 LICENSE](https://github.com/BinomialLLC/basis_universal/blob/master/LICENSE). Basis Universal™ is a trademark of Binomial LLC. KTX™ is a trademark of [The Khronos Group Inc.](https://www.khronos.org/ktx/) See our Apache 2.0 [NOTICE file](https://github.com/BinomialLLC/basis_universal/wiki/NOTICE). If you modify the Basis Universal reference source code, specifications, or wiki documents and redistribute the files, you must cause any modified files to carry prominent notices stating that you changed the files (see Apache 2.0 §4(b)).
[Release Notes](https://github.com/BinomialLLC/basis_universal/wiki/Release-Notes)
See our [DEP5 file](https://github.com/BinomialLLC/basis_universal/blob/master/.reuse/dep5) for the complete list of software and their licenses in this repo. The encoder library is Apache 2.0, but it utilizes some open source 3rd party modules (in 'encoder/3rdparty' and in the 'Zstd' directory) to load [.QOI](https://qoiformat.org/), [.DDS](https://github.com/DeanoC/tiny_dds), [.EXR](https://github.com/syoyo/tinyexr) images, to handle [Zstd](https://github.com/facebook/zstd) compression, and to unpack ASTC texture blocks. See the [LICENSES](https://github.com/BinomialLLC/basis_universal/tree/master/LICENSES) folder. The transcoder utilizes no 3rd party libraries or dependencies, other than Zstd (which is optional but limits the transcoder to non-Zstd utilizing codecs).
### The first texture (and possibly image/video) compression codec developed without "Lena"
Links
-----
We retired Lena years ago. No testing is done with this image:
- [Wiki/Specifications](https://github.com/BinomialLLC/basis_universal/wiki)
- [Release Notes](https://github.com/BinomialLLC/basis_universal/wiki/Release-Notes)
- [Live Compression/Transcoding Testbed](https://subquantumtech.com/xu/ktx2_encode_test/) - A WASM64 compatible browser is recommended (such as Chrome/Edge/Firefox), especially for XUASTC LDR compression, but it works under plain WASM too (with resolution limits due to less available memory).
- [Live WebGL Examples](https://subquantumtech.com/xu/)
- [JavaScript API/WASM/WebGL info](https://github.com/BinomialLLC/basis_universal/tree/master/webgl)
- [XUASTC LDR Specification](https://github.com/BinomialLLC/basis_universal/wiki/XUASTC-LDR-Specification-v1.0)
https://www.losinglena.com/
### UASTC HDR 4x4/6x6 Specific Links:
### Quick Introduction
- [UASTC HDR 4x4 Example Images](https://github.com/BinomialLLC/basis_universal/wiki/UASTC-HDR-Examples)
- [UASTC HDR 6x6 Example Images](https://github.com/BinomialLLC/basis_universal/wiki/ASTC-HDR-6x6-Example-Images)
- [UASTC HDR 6x6 Support Notes](https://github.com/BinomialLLC/basis_universal/wiki/UASTC-HDR-6x6-Support-Notes)
- [Quick comparison of ARM's astcenc HDR 6x6 encoder vs. ours](https://github.com/richgel999/junkdrawer/wiki/ASTC-HDR-6x6-Encoder-Comparisons)
Probably the most important concept to understand about Basis Universal before using it: The system supports **two** very different universal texture modes: The original "ETC1S" mode is low/medium quality, but the resulting file sizes are very small because the system has built-in compression for ETC1S texture format files. This is the command line encoding tool's default mode. ETC1S textures work best on images, photos, map data, or albedo/specular/etc. textures, but don't work as well on normal maps.
----
There's the second "UASTC" mode, which is significantly higher quality (comparable to BC7 and highest quality LDR ASTC 4x4), and is usable on all texture types including complex normal maps. UASTC mode purposely does not have built-in file compression like ETC1S mode does, so the resulting files are quite large (8-bits/texel - same as BC7) compared to ETC1S mode. The UASTC encoder has an optional Rate Distortion Optimization (RDO) encoding mode (implemented as a post-process over the encoded UASTC texture data), which conditions the output texture data in a way that results in better lossless compression when UASTC .basis files are compressed with Deflate/Zstd, etc. In UASTC mode, you must losslessly compress .basis files yourself. .KTX2 files have built-in lossless compression support using [Zstandard](https://facebook.github.io/zstd/), which is used by default on UASTC textures.
Supported LDR GPU Texture Formats
---------------------------------
Basis Universal is not an image compression codec, but a GPU texture compression codec. It can be used just like an image compression codec, but that's not the only use case. Here's a [good intro](http://renderingpipeline.com/2012/07/texture-compression/) to GPU texture compression. If you're looking to primarily use the system as an image compression codec on sRGB photographic content, use the default ETC1S mode, because it has built-in compression.
ETC1S, UASTC LDR 4x4, XUASTC LDR 4x4-12x12 and ASTC LDR 4x4-12x12 files can be transcoded to:
- ASTC LDR 4x4 L/LA/RGB/RGBA 8bpp
- ASTC LDR 4x4-12x12 (XUASTC/ASTC), 0.89-8bpp
- BC1-5 RGB/RGBA/X/XY
- BC7 RGB/RGBA
- ETC1 RGB, ETC2 RGBA, and ETC2 EAC R11/RG11
- PVRTC1 4bpp RGB/RGBA and PVRTC2 RGB/RGBA
- ATC RGB/RGBA and FXT1 RGB
- Uncompressed LDR raster image formats: 8888/565/4444
**The "-q X" option controls the output quality in ETC1S mode.** The default is quality level 128. "-q 255" will increase quality quite a bit. If you want even higher quality, try "-max_selectors 16128 -max_endpoints 16128" instead of -q. -q internally tries to set the codebook sizes (or the # of quantization intervals for endpoints/selectors) for you. You need to experiment with the quality level on your content.
Supported HDR GPU Texture Formats
---------------------------------
For tangent space normal maps, you should separate X into RGB and Y into Alpha, and provide the compressor with 32-bit/pixel input images. Or use the "-separate_rg_to_color_alpha" command line option which does this for you. The internal texture format that Basis Universal uses (ETC1S) doesn't handle tangent space normal maps encoded into RGB well. You need to separate the channels and recover Z in the pixel shader using z=sqrt(1-x^2-y^2).
UASTC HDR 4x4, ASTC HDR 6x6, and UASTC HDR 6x6 files can be transcoded to:
- ASTC HDR 4x4 (8bpp, UASTC HDR 4x4 only)
- ASTC HDR 6x6 RGB (3.56bpp, ASTC HDR 6x6 or UASTC HDR 6x6 intermediate only)
- BC6H RGB (8bpp, either UASTC HDR 4x4 or UASTC HDR 6x6)
- Uncompressed HDR raster image formats: RGB_16F/RGBA_16F (half float/FP16 RGB, 48 or 64bpp), or 32-bit/pixel shared exponent [RGB_9E5](https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_shared_exponent.txt)
### License and 3rd party code dependencies
----
Detailed legal, license, and IP information is [here](https://github.com/BinomialLLC/basis_universal/wiki/Legal-IP-License-Information). Basis Universal itself uses the Apache 2.0 licenses, but it also utilizes some optional BSD code (Zstandard). The supported texture formats are [open Khronos Group standards](https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html).
Supported Texture Compression/Supercompression Modes
----------------------------------------------------
All C/C++ code dependencies are present inside the Basis Universal repo itself to simplify building.
1. **[ETC1S](https://github.com/BinomialLLC/basis_universal/wiki/.basis-File-Format-and-ETC1S-Texture-Video-Specification)**: A roughly .3-3bpp low to medium quality supercompressed mode based on a subset of [ETC1](https://en.wikipedia.org/wiki/Ericsson_Texture_Compression) called "ETC1S". This mode supports variable quality vs. file size levels (like JPEG), alpha channels, built-in compression, and texture arrays optionally compressed as a video sequence using skip blocks ([Conditional Replenishment](https://en.wikipedia.org/wiki/MPEG-1)). This mode can be rapidly transcoded to all of the supported LDR texture formats.
The encoder optionally uses Zstandard's single source file compressor (in zstd/zstd.c) to support compressing supercompressed KTX2 files. The stand-alone transcoder (in the "transcoder" directory) is a single .cpp source file library which has no 3rd party code dependencies apart from zstd/zstddeclib.c, which is also technically optional. It's only used for decompressing UASTC KTX2 files that use Zstandard.
2. **[UASTC LDR 4x4](https://github.com/BinomialLLC/basis_universal/wiki/UASTC-LDR-4x4-Texture-Specification)**: An 8 bits/pixel LDR high quality mode. UASTC LDR is a 19 mode subset of the standard [ASTC LDR](https://en.wikipedia.org/wiki/Adaptive_scalable_texture_compression) 4x4 (8bpp) texture format, but with a custom block format containing transcoding hints. Transcoding UASTC LDR to ASTC LDR and BC7 is particularly fast and simple, because UASTC LDR is a common subset of both BC7 and ASTC. The transcoders for the other texture formats are accelerated by several format-specific hint bits present in each UASTC LDR block.
### Command Line Compression Tool
This mode supports an optional [Rate-Distortion Optimized (RDO)](https://en.wikipedia.org/wiki/Rate%E2%80%93distortion_optimization) post-process stage that conditions the encoded UASTC LDR texture data in the .KTX2/.basis file so it can be more effectively LZ compressed. More details [here](https://github.com/BinomialLLC/basis_universal/wiki/UASTC-implementation-details).
The command line tool used to create, validate, and transcode/unpack .basis/.KTX2 files is named "basisu". Run basisu without any parameters for help.
Here is the [UASTC LDR 4x4 specification document](https://github.com/BinomialLLC/basis_universal/wiki/UASTC-LDR-4x4-Texture-Specification).
The library and command line tool have no other 3rd party dependencies (that are not already in the repo), so it's pretty easy to build.
3. **[UASTC HDR 4x4](https://github.com/BinomialLLC/basis_universal/wiki/UASTC-HDR-4x4-Texture-Specification)**: An 8 bits/pixel HDR high quality mode. This is a 24 mode subset of the standard [ASTC HDR](https://en.wikipedia.org/wiki/Adaptive_scalable_texture_compression) 4x4 (8bpp) texture format. It's designed to be high quality, supporting the 27 partition patterns in common between BC6H and ASTC, and fast to transcode with very little loss (typically a fraction of a dB PSNR) to the BC6H HDR texture format. Notably, **UASTC HDR 4x4 data is 100% standard ASTC texture data**, so no transcoding at all is required on devices or APIs that support ASTC HDR. This mode can also be transcoded to various 32-64bpp uncompressed HDR texture/image formats.
To build basisu (without SSE 4.1 support - the default):
Here is the [UASTC HDR 4x4 specification document](https://github.com/BinomialLLC/basis_universal/wiki/UASTC-HDR-4x4-Texture-Specification-v1.0), and here are some compressed [example images](https://github.com/BinomialLLC/basis_universal/wiki/UASTC-HDR-Examples).
4. **ASTC HDR 6x6 or RDO ASTC HDR 6x6**: A 3.56 bits/pixel (or less with RDO+Zstd) HDR high quality mode. Just like mode #3, **ASTC HDR 6x6 data is 100% standard ASTC texture data**. Here's a [page with details](https://github.com/BinomialLLC/basis_universal/wiki/UASTC-HDR-6x6-Support-Notes). The current encoder supports weight grid upsampling, 1-3 subsets, single or dual planes, CEM's 7 and 11, and all unique ASTC partition patterns.
The ASTC HDR decoder, used in the transcoder module, supports the entire ASTC HDR format.
5. **UASTC HDR 6x6 Intermediate ("GPU Photo HDR")**: A custom compressed intermediate format that can be rapidly transcoded to ASTC HDR 6x6, BC6H, and various uncompressed HDR formats. The custom compressed file format is [described here](https://github.com/BinomialLLC/basis_universal/wiki/UASTC-HDR-6x6-Intermediate-File-Format-(Basis-GPU-Photo-6x6)). The format supports 75 unique ASTC configurations, weight grid upsampling, 1-3 subsets, single or dual planes, CEM's 7 and 11, and all unique ASTC partition patterns. One of the first HDR GPU texture codecs supporting the [delta E ITP (ICtCp) colorspace metric](https://www.portrait.com/resource-center/about-deltae-e/) and perceptual saliency maps.
6. **Standard ASTC LDR-4x4-12x12**. Supports all standard 14 ASTC block sizes. Transcodable from any ASTC block size to any other supported LDR texture format with adaptive deblocking, including BC7 using the [bc7f "one-shot" analytical BC7 encoder](https://github.com/BinomialLLC/basis_universal/wiki/Transcoder-Internals-Analytical-Real-Time-Encoders) (supporting all BC7 modes/features) and ETC1 (using etc1f, which also supports the entire ETC1 format).
The ASTC LDR decoder, used in the transcoder module, supports the entire standard ASTC LDR format (i.e. not just ASTC texture blocks generated using our encoder). The ASTC LDR transcoder can transcode any block size ASTC (4x4 - 12x12) to the other LDR texture formats.
7. **XUASTC LDR 4x4-12x12 ("GPU Photo LDR/SDR")**: Supercompressed ASTC with **Weight Grid DCT**, supporting all 14 standard ASTC block sizes, with adaptive deblocking when transcoding to other texture/pixel formats. Bitrates range from approximately 0.3–5.7 bpp, depending on content, profile, block size, windowed RDO, and Weight Grid DCT quality settings. Typical XUASTC LDR 4×4 (**8 bpp in memory**) transmission/on-disk bitrate with Weight Grid DCT (where it is least effective) is **1.15–3.5 bpp (typical ≈2.25 bpp)**, with larger block sizes achieving even lower usable bitrates, down to approximately 0.3 bpp. Like ASTC LDR, the XUASTC LDR transcoder can transcode any block size ASTC (4x4 - 12x12) to the other LDR texture formats, but with additional block-size specific optimizations.
Supports three profiles: context-based range/arithmetic coding (for higher compression ratios), Zstd (for faster and simpler transcoding), or a hybrid profile using both approaches. Transcodable to all other supported LDR texture formats, including fully featured (all 8 modes, all dual-plane channel configurations, all mode settings) BC7. Certain common block sizes (4×4, 6×6, and 8×6) have specializations for particularly fast transcoding directly to BC7, bypassing analytical BC7 encoding (using [bc7f](https://github.com/BinomialLLC/basis_universal/wiki/Transcoder-Internals-Analytical-Real-Time-Encoders)) entirely for the most common ASTC configurations (solid color and single-subset CEMs).
Weight Grid DCT can be disabled; however, supercompression remains available with optional, configurable windowed RDO. Compatible with all major image and texture content types, including photographic images, lightmaps, albedo/specular textures, various types of normal maps, luminance-only maps, and geospatial mapping signals.
Supports adaptive deblocking when transcoding from larger block sizes; this can be disabled using a transcoder flag.
One interesting use of XUASTC LDR which works with any of the 14 block sizes: the efficient distribution of texture content compressed to very low bitrates vs. older systems, resulting in game-changing download time reductions. Using the larger XUASTC block sizes (beyond 6x6) with Weight Grid DCT and adaptive deblocking on either the CPU or [GPU using a simple shader](https://github.com/BinomialLLC/basis_universal/tree/master/shader_deblocking), **any developer can now distribute texture and image content destined for BC7 at .35-1.5 bpp**, and **cache the transcoded BC7 data on a modern Gen 4 or 5 (10+ GB/sec.) SSD**.
XUASTC LDR supports the following ASTC configurations: L/LA/RGB/RGBA CEMs; base+scale or RGB/RGBA direct; base+offset CEMs; Blue Contraction encoding; 1–3 subsets; all partition patterns; and single- or dual-plane modes. Here is the [XUASTC LDR specification](https://github.com/BinomialLLC/basis_universal/wiki/XUASTC-LDR-Specification-v1.0). Also see the [ASTC and XUASTC LDR Usage Guide](https://github.com/BinomialLLC/basis_universal/wiki/ASTC-and-XUASTC-LDR-Usage-Guide).
Notes:
- Mode #1 (ETC1S) has special support and optimizations for basic temporal supercompression ([texture video](https://github.com/BinomialLLC/basis_universal/wiki/Encoding-ETC1S-and-XUASTC-LDR-Texture-Video)).
- Modes #3 (UASTC HDR 4x4) and #4 (RDO ASTC HDR 6x6), and #6 (ASTC LDR 4x4-12x12) output 100% standard ASTC texture data (with or without RDO), like any other ASTC encoder. The .KTX2 files are just plain textures.
- The other modes (#1, #2, #5, #7) output compressed data in various custom supercompressed formats, which our transcoder library can convert in real-time to various GPU texture or pixel formats.
- Modes #4 (ASTC HDR 6x6) and #5 (UASTC HDR 6x6) internally use the same unified ASTC HDR 6x6 encoder.
- Modes #6 (ASTC LDR 4x4-12x12) and #7 (XUASTC LDR 4x4-12x12) internally use the same unified ASTC LDR ASTC encoder.
### Other Features
Both .basis and .KTX2 files support mipmap levels, texture arrays, cubemaps, cubemap arrays, and texture video, in all modes. Additionally, .basis files support non-uniform texture arrays, where each image in the file can have a different resolution or number of mipmap levels.
In ETC1S mode, the compressor is able to exploit color and pattern correlations across all the images in the entire file using global endpoint/selector codebooks, so multiple images with mipmaps can be stored efficiently in a single file. The ETC1S mode also supports skip blocks (Conditional Replenishment) for short video sequences, to prevent sending blocks which haven't changed relative to the previous frame.
The LDR image formats supported for reading are .PNG, [.DDS with mipmaps](https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide), .TGA, .QOI, and .JPG. The HDR image formats supported for reading are .EXR, .HDR, and .DDS with mipmaps. The library can write .basis, .KTX2, .DDS, .KTX (v1), .ASTC, .OUT, .EXR, and .PNG files.
The system now supports loading basic 2D .DDS files with optional mipmaps, but the .DDS file must be in one of the supported uncompressed formats: 24bpp RGB, 32bpp RGBA/BGRA, half-float RGBA, or float RGBA. Using .DDS files allows the user to control exactly how the mipmaps are generated before compression.
----
Building (Native)
-----------------
The encoding library and command line tool have no required 3rd party dependencies that are not already in the repo itself. The transcoder is a single .cpp source file (in `transcoder/basisu_transcoder.cpp`) which has no 3rd party dependencies.
We build and test under:
- Windows x86/x64 using Visual Studio 2026, MSVC or clang
- Windows ARM using Visual Studio 2022 ARM 17.13.0
- Ubuntu Linux 24.04.3 LTS (noble) with gcc 13.3.0 or clang 18.1.3
- macOS (M1) with clang 16.0.0
- Arch Linux ARM, on a [Pinebook Pro](https://pine64.org/devices/pinebook_pro/), with gcc 12.1.
- Ubuntu Linux 24.04 on RISC-V (Orange PI RV2)
- cmake: 3.28.3, emcc: 4.0.19
Under Windows with Visual Studio you can use the included `basisu.sln` file. Alternatively, you can use cmake to create new VS solution/project files.
To build, first [install cmake](https://cmake.org/), then:
```
cmake CMakeLists.txt
make
```
To build with SSE 4.1 support on x86/x64 systems (encoding is roughly 15-30% faster):
```
cmake -D SSE=TRUE CMakeLists.txt
mkdir build
cd build
cmake ..
make
```
For Visual Studio 2019, you can now either use the CMakeLists.txt file or the included `basisu.sln` file. Earlier versions of Visual Studio (particularly 2017) should work but aren't actively tested. We develop with the most up to date version of 2019.
To build with SSE 4.1 support on x86/x64 systems (ETC1S encoding is roughly 15-30% faster), add `-DBASISU_SSE=TRUE` to the cmake command line. Add `-DBASISU_OPENCL=TRUE` to build with (optional) OpenCL support. Use `-DCMAKE_BUILD_TYPE=Debug` to build in debug. To build 32-bit executables, add `-DBASISU_BUILD_X64=FALSE`.
To compress a sRGB PNG/BMP/TGA/JPEG image to an ETC1S .KTX2 file:
After building, the native command line tool used to create, validate, and transcode/unpack .KTX2/.basis files is `bin/basisu`.
`basisu -ktx2 x.png`
*Note we use C++17 for compiling the software. Anything later is too new for us. Compiling the software with a newer C++ version is not supported by us yet.*
To compress a sRGB PNG/BMP/TGA/JPEG image to an UASTC .KTX2 file:
----
`basisu -ktx2 -uastc x.png`
Running the Precompiled WASM WASI Executables
---------------------------------------------
To compress a sRGB PNG/BMP/TGA/JPEG image to an RDO UASTC .KTX2 file with mipmaps:
For smaller images/textures (~4 megatexels or less), there are precompiled, secure, cross-platform 32-bit .WASM WASI executables checked into the `bin` directory: `basisu_mt.wasm` (multithreaded) and `basisu_st.wasm` (single threaded). Quick testing - ETC1S/UASTC LDR 4x4 (all platforms) - multithreaded and single threaded, using [wasmtime](https://wasmtime.dev/):
`basisu -ktx2 -uastc -uastc_rdo_l 1.0 -mipmap x.png`
Tested with wasmtime v39.0.0:
To compress a sRGB PNG/BMP/TGA/JPEG image to an ETC1S .basis file:
```
cd bin
wasmtime run --dir=. --dir=../test_files --wasm threads=yes --wasi threads=yes ./basisu_mt.wasm -test
wasmtime run --dir=. --dir=../test_files ./basisu_st.wasm -test
```
`basisu x.png`
For newer versions of wasmtime such as v42.0.1 add `--wasm shared-memory=yes`:
To compress a image to a higher quality UASTC .basis file:
```
wasmtime run --dir=. --dir=../test_files --wasm threads=yes --wasm shared-memory=yes --wasi threads=yes ./basisu_mt.wasm -test
```
`basisu -uastc -uastc_level 2 x.png`
See the `runwt.sh`, `runwt.bat`, `runw.sh`, or `runw.bat` scripts for examples on how to run the WASM executables using wasmtime. Windows example for XUASTC LDR 6x6 compression using the arithmetic profile, with Weight Grid DCT level 70:
To compress a image to a higher quality UASTC .basis file with RDO post processing, so the .basis file is more compressible:
```
cd bin
runwt.bat ../test_files/tough.png -xuastc_ldr_6x6 -quality 70 -xuastc_arith
runwt.bat tough.ktx2
```
`basisu -uastc -uastc_level 2 -uastc_rdo_l .75 x.png`
Linux/macOS:
-uastc_level X ranges from 0-4 and controls the UASTC encoder's performance vs. quality tradeoff. Level 0 is very fast, but low quality, level 2 is the default quality, while level 3 is the highest practical quality. Level 4 is impractically slow, but highest quality.
```
cd bin
chmod +x runwt.sh
./runwt.sh ../test_files/tough.png -xuastc_ldr_6x6 -quality 70 -xuastc_arith
./runwt.sh tough.ktx2
```
-uastc_rdo_l X controls the rate distortion stage's quality setting. The lower this value, the higher the quality, but the larger the compressed file size. Good values to try are between .2-3.0. The default is 1.0. RDO post-processing is currently pretty slow, but we'll be optimizing it over time.
Unfortunately, 32-bit WASM WASI executables have tradeoffs vs. native executables: Limited memory, and slower performance (somewhat mitigatable using WASM threading, which we support). **32-bit WASM WASI memory constraints limit the maximum image/texture size that can be compressed to ASTC LDR or XUASTC LDR to around 4 megapixels.** (The other codecs have lower memory requirements.) For Web, we support both WASM and WASM64 (with or without threading), which greatly improves the WASM memory situation. As far as we know as of 2/2026, wasmtime supports WASM64, but the WASI SDK still [doesn't officially support the wasm64-wasi target](https://github.com/WebAssembly/wasi-sdk/issues/212), but once it does we'll support it.
UASTC texture video is supported and has been tested. In RDO mode with 7zip LZMA, we've seen average bitrates between 1-2 bpp. ETC1S mode is recommended for texture video, which gets bitrates around .25-.3 bpp.
Building (WASM WASI)
--------------------
Note that basisu defaults to sRGB colorspace metrics. If the input is a normal map, or some other type of non-sRGB (non-photographic) texture content, be sure to use -linear to avoid extra unnecessary artifacts. (Note: Currently, UASTC mode always uses linear colorspace metrics. sRGB and angulate metrics are comming soon.)
To build the WASM WASI executables, you will need the [WASM WASI SDK](https://github.com/WebAssembly/wasi-sdk) installed. The `WASI_SDK_PATH` environment variable must be set to the correct path where the SDK is installed.
To add automatically generated mipmaps to the .basis file, at a higher than default quality level (which ranges from [1,255]):
Multithreaded:
```
mkdir build_wasm_mt
cd build_wasm_mt
cmake -DCMAKE_TOOLCHAIN_FILE=$WASI_SDK_PATH/share/cmake/wasi-sdk-pthread.cmake -DCMAKE_BUILD_TYPE=Release -DBASISU_WASM_THREADING=ON ..
make
```
`basisu -mipmap -q 190 x.png`
Single threaded:
```
mkdir build_wasm_st
cd build_wasm_st
cmake -DCMAKE_TOOLCHAIN_FILE=$WASI_SDK_PATH/share/cmake/wasi-sdk.cmake -DCMAKE_BUILD_TYPE=Release -DBASISU_WASM_THREADING=OFF ..
make
```
There are several mipmap options that allow you to change the filter kernel, the filter colorspace for the RGB channels (linear vs. sRGB), the smallest mipmap dimension, etc. The tool also supports generating cubemap files, 2D/cubemap texture arrays, etc.
The WASM WASI executables will be placed in the `bin` directory. These platform-independent executables are fully functional, and can be executed using a WASM WASI runtime such as [wasmtime](https://github.com/bytecodealliance/wasmtime).
To create a slightly higher quality ETC1S .basis file (one with better codebooks) at the default quality level (128) - note this is much slower to encode:
----
### Testing the Codec
The command line tool includes some automated LDR/HDR encoding/transcoding tests:
```
cd ../bin
basisu -test
basisu -test_hdr_4x4
basisu -test_hdr_6x6
basisu -test_hdr_6x6i
basisu -test_xuastc_ldr
```
To test the codec in OpenCL mode (must have OpenCL libs/headers/drivers installed and have compiled OpenCL support in by running cmake with `-DBASISU_OPENCL=TRUE`):
```
basisu -test -opencl
```
----
Compressing and Unpacking .KTX2/.basis Files
--------------------------------------------
- To compress an LDR sRGB PNG/QOI/TGA/JPEG/DDS image to a supercompressed XUASTC LDR 6x6 .KTX2 file, at quality level 75 (**valid quality levels 1-100, where higher values=higher quality**), effort level 4 (**valid effort levels 0-10, higher values=slower compression, default effort is 3**):
`basisu -xuastc_ldr_6x6 -quality 75 -effort 4 x.png`
`-quality 100` disables Weight Grid DCT, leaving just lossless supercompression of ASTC. An alias for `-xuastc_ldr_6x6` is `-ldr_6x6i` (where 'i'="intermediate"). All **[14 standard ASTC block sizes](https://developer.nvidia.com/astc-texture-compression-for-game-assets) are supported, from 4x4-12x12**: 4x4, 5x4, 5x5, 6x5, 6x6, 8x5, 8x6, 10x5, 10x6, 8x8, 10x8, 10x10, 12x10 and 12x12. The **XUASTC LDR to BC7 transcoder has special optimizations for several common block sizes: 4x4, 6x6 and 8x6**. When transcoding XUASTC LDR at these particular block sizes, most XUASTC blocks are *directly* transcoded to BC7 (i.e. directly from the XUASTC latent to the BC7 latent), skipping the real-time analytical bc7f encoding step.
More XUASTC LDR specific options (many of these also apply to standard ASTC - see our [ASTC/XUASTC Usage Guide](https://github.com/BinomialLLC/basis_universal/wiki/ASTC-and-XUASTC-LDR-Usage-Guide)):
- The options `-xuastc_arith`, `-xuastc_zstd` (the default), and `-xuastc_hybrid` control the **XUASTC LDR profile used**. The arithmetic profile trades off transcoding throughput for roughly 5-18% better compression vs. the Zstd profile, and the hybrid profile is a balance between the two.
- `-ts` or `-srgb` enables the **sRGB profile (the default)**, and `-tl` or `-linear` **enables the linear profile**. Ideally this setting will match how the ASTC texture is sampled by the GPU. Use linear on normal maps.
- `-weights X Y Z W` sets the unsigned integer **channel error weights**, used to favor certain channels during compression.
- Another set of XUASTC specific options overrides the **windowed RDO behavior** (windowed or bounded RDO is a separate and optional perceptual optimization vs. Weight Grid DCT): `-xy` enables and `-xyd` disables windowed RDO. By default, if Weight Grid DCT is not enabled (i.e. `-quality` isn't specified, or is set to 100), windowed RDO is disabled. Windowed RDO is automatically enabled if the quality level is less than 100, unless `-xyd` is specified. Also see the tool's [help text](https://github.com/BinomialLLC/basis_universal/blob/master/cmd_help/cmd_help.txt) for additional windowed RDO options: `-ls_min_psnr`, `-ls_min_alpha_psnr`, `-ls_thresh_psnr`, `-ls_thresh_alpha_psnr`, etc.
- `-xs` disables 2-3 subset usage, and `-xp` disables dual plane usage (slightly higher compression, faster direct transcoding to BC7 will occur more often)
- `-higher_quality_transcoding`: Permits slower but higher quality transcoding
- `-no_deblocking`: Disables adaptive deblocking on ASTC block sizes > 8x6 (faster)
- `-force_deblocking`: Always use adaptive deblocking filter, even for block sizes <= 8x6 (slower)
- `-stronger_deblocking`: Use stronger deblocking when it's enabled (same performance)
- `-fast_xuastc_ldr_bc7_transcoding` and `-no_fast_xuastc_ldr_bc7_transcoding`: Controls faster direct XUASTC->BC7 transcoding (defaults to enabled, which is slightly lower quality)
- To compress an LDR sRGB image to a standard ASTC LDR 6x6 .KTX2 file, using effort level 4 (valid effort levels 0-10):
`basisu -astc_ldr_6x6 -effort 4 x.png`
An alias for `-astc_ldr_6x6` is `-ldr_6x6`.
Just like XUASTC LDR, all 14 standard ASTC block sizes are supported, from 4x4-12x12. Internally the XUASTC LDR encoder is used, but standard ASTC block data is output, instead of supercompressed XUASTC LDR. Most XUASTC LDR options also work in ASTC LDR mode.
- To compress an LDR sRGB image to an ETC1S .KTX2 file, at quality level 100 (the highest):
`basisu -quality 100 x.png`
- For a linear LDR image, in ETC1S mode, at default quality (`-quality 50`, or the older `-q 128`):
`basisu -linear x.png`
- To compress to UASTC LDR 4x4, which is much higher quality than ETC1S, but lower maximum quality vs. ASTC/XUASTC LDR 4x4:
`basisu -uastc x.png`
- To compress an [.EXR](https://en.wikipedia.org/wiki/OpenEXR), [Radiance .HDR](https://paulbourke.net/dataformats/pic/), or .DDS HDR image to a UASTC HDR 4x4 .KTX2 file:
`basisu x.exr`
- To compress a standard ASTC HDR 6x6 file (~3.56 bpp):
```
basisu -hdr_6x6 x.exr
basisu -hdr_6x6 -lambda 500 x.exr
basisu -hdr_6x6_level 5 -lambda 500 x.exr
```
- To compress a UASTC HDR 6x6i file (using the compressed intermediate format) for smaller files (~1.75-3.0 bpp):
```
basisu -hdr_6x6i x.exr
basisu -hdr_6x6i -lambda 500 x.exr
basisu -hdr_6x6i_level 5 -lambda 500 x.exr
```
Note the unified `-quality` and `-effort` options work in HDR, too. These examples use the older non-unified options, which allow more direct/precise control.
Be aware that the .EXR reader we use is [TinyEXR's](https://github.com/syoyo/tinyexr), which doesn't support all possible .EXR compression modes. Tools like [ImageMagick](https://imagemagick.org/) can be used to create .EXR files that TinyEXR can read.
Alternatively, LDR images (such as .PNG) can be compressed to an HDR format by specifying `-hdr`, `-hdr_6x6`, or `-hdr_6x6i`. By default LDR images, when compressed to an HDR format, are first upconverted to HDR by converting them from sRGB to linear light and scaled to 100 [nits - candela per sq. meter, cd/m²](https://grokipedia.com/page/Candela_per_square_metre). The sRGB conversion step can be disabled by specifying `-hdr_ldr_no_srgb_to_linear`, and the normalized RGB linear light to nit multiplier can be changed by specifying `-hdr_ldr_upconversion_nit_multiplier X`.
Note: If you're compressing LDR/SDR image files to an HDR format, the codec's default behavior is to convert the 8-bit image data to linear light (by undoing the sRGB transfer function). It then multiplies the linear light RGB values by the LDR->HDR upconversion multiplier, which is in nits. In previous versions of the codec, this multiplier was effectively 1 nit, but it now defaults to 100 nits in all modes. (The typical luminance of LDR monitors is 80-100 nits.) To change this, use the "-hdr_ldr_upconversion_nit_multiplier X" command line option. (This is done because the HDR 6x6 codecs function internally in the [ICtCp HDR colorspace](https://en.wikipedia.org/wiki/ICtCp). LDR/SDR images must be upconverted to linear light HDR images scaled to a proper max. luminance based on how the image data will be displayed on actual SDR/HDR monitors.)
### Some Useful Command Line Options
- All codecs now support simple unified "quality" and "effort" settings. `-effort X` [0,10] controls how much of the search space (and how slowly) compression proceeds, and `-quality X` [1,100] controls the quality vs. bitrate tradeoff. Internally these settings will be mapped to each codec's specific configuration settings. Almost all the older settings still work, however. Previously, `-q X`, where X ranged from [1,255], controlled the ETC1S quality setting. This option is still available, but `-quality` is preferred now.
- `-debug` causes the encoder to print internal and developer-oriented verbose debug information.
- `-stats` to see various quality (PSNR) statistics.
- `-linear`: ETC1S defaults to sRGB colorspace metrics, UASTC LDR currently always uses linear metrics, and UASTC HDR defaults to weighted RGB metrics (with 2,3,1 weights). If the input is a normal map, or some other type of non-sRGB (non-photographic) texture content, be sure to use `-linear` to avoid extra unnecessary artifacts. (Angular normal map metrics for UASTC LDR/HDR are definitely doable and on our TODO list.)
- Specifying `-opencl` enables OpenCL mode, which currently only accelerates ETC1S encoding if it's been enabled at compile time.
- The compressor is multithreaded by default, which can be disabled using the `-no_multithreading` command line option. The transcoder is currently single threaded, although it is thread safe (i.e. it supports decompressing multiple texture slices in parallel).
More Example Command Lines
--------------------------
- To compress an sRGB PNG/QOI/TGA/JPEG/DDS image to an RDO (Rate-Distortion Optimization) UASTC LDR .KTX2 file with mipmaps:
`basisu -uastc -uastc_rdo_l 1.0 -mipmap x.png`
`-uastc_rdo_l X` controls the RDO ([Rate-Distortion Optimization](https://en.wikipedia.org/wiki/Rate%E2%80%93distortion_optimization)) quality setting. The lower this value, the higher the quality, but the larger the compressed file size. Good values to try are between .2-3.0. The default is 1.0.
- To add automatically generated mipmaps to an ETC1S .KTX2 file:
`basisu -mipmap -quality 75 x.png`
There are several mipmap options to change the filter kernel, the filter colorspace for the RGB channels (linear vs. sRGB), the smallest mipmap dimension, etc. The tool also supports generating cubemap files, 2D/cubemap texture arrays, etc. To bypass the automatic mipmap generator, you can create LDR or HDR uncompressed [.DDS texture files](https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide) and feed them to the compressor.
- To create a slightly higher quality ETC1S .KTX2 file (one with higher quality endpoint/selector codebooks) at the default quality level (128) - note this is much slower to encode:
`basisu -comp_level 2 x.png`
On some rare images (ones with blue sky gradients come to bind), you may need to increase the ETC1S `-comp_level` setting. This controls the amount of overall effort the encoder uses to optimize the ETC1S codebooks (palettes) and compressed data stream. Higher comp_level's are *significantly* slower, and shouldn't be used unless necessary:
On some rare images (ones with blue sky gradients come to mind), you may need to increase the ETC1S `-comp_level` setting, which ranges from 1 to 6. This controls the amount of overall effort the encoder uses to optimize the ETC1S codebooks and the compressed data stream. Higher -comp_level's are *significantly* slower.
`basisu -ktx2 x.png -comp_level 5 -q 255`
- To manually set the ETC1S codebook sizes (instead of using -quality, or the older -q options), with a higher codebook generation level (this is useful with texture video):
Or try:
`basisu -ktx2 x.png -comp_level 5 -max_endpoints 16128 -max_selectors 16128`
`basisu x.png -comp_level 2 -max_endpoints 16128 -max_selectors 16128`
Note `-comp_level`'s 3-4 are almost as good as 5 and are a lot faster.
- To [tonemap](https://en.wikipedia.org/wiki/Tone_mapping) an HDR .EXR or .HDR image file to multiple LDR .PNG files at different exposures, using the Reinhard tonemap operator:
The compressor is multithreaded by default, but this can be disabled using the `-no_multithreading` command line option. The transcoder is currently single threaded although it supports multithreading decompression of multiple texture slices in parallel.
`basisu -tonemap x.exr`
### Unpacking .basis/.KTX2 files to .PNG/.KTX files
- To compare two LDR images and print PSNR statistics:
You can either use the command line tool or [call the transcoder directly](https://github.com/BinomialLLC/basis_universal/wiki/How-to-Use-and-Configure-the-Transcoder) from JavaScript or C/C++ code to decompress .basis/.KTX2 files to GPU texture data or uncompressed images.
`basisu -compare a.png b.png`
To use the command line tool to unpack a .basis or .KTX2 file to multiple .png/.ktx files:
- To compare two HDR .EXR/.HDR images and print FP16 PSNR statistics:
`basisu x.basis`
`basisu -compare_hdr a.exr b.exr`
Use the `-no_ktx` and `-etc1_only` options to unpack to less files. `-info` and `-validate` will just display file information and not output any files. The output .KTX1 files are currently in the KTX1 file format, not KTX2.
See the help text for a complete listing of the tool's command line options. The command line tool is just a thin wrapper on top of the encoder library.
The mipmapped or cubemap .KTX files will be in a wide variety of compressed GPU texture formats (PVRTC1 4bpp, ETC1-2, BC1-5, BC7, etc.), and to my knowledge there is no single .KTX viewer tool that correctly and reliably supports every GPU texture format that we support. BC1-5 and BC7 files are viewable using AMD's Compressonator, ETC1/2 using Mali's Texture Compression Tool, and PVRTC1 using Imagination Tech's PVRTexTool. Links:
Unpacking .KTX2/.basis files to .PNG/.EXR/.KTX/.DDS files
---------------------------------------------------------
[Mali Texture Compression Tool](https://developer.arm.com/tools-and-software/graphics-and-gaming/graphics-development-tools/mali-texture-compression-tool)
You can either use the command line tool or [call the transcoder directly](https://github.com/BinomialLLC/basis_universal/wiki/How-to-Use-and-Configure-the-Transcoder) from JavaScript or C/C++ code to decompress .KTX2/.basis files to GPU texture data or uncompressed image data. To unpack a .KTX2 or .basis file to multiple .png/.exr/.ktx/.dds files:
[Compressonator](https://gpuopen.com/gaming-product/compressonator/)
`basisu x.ktx2`
[PVRTexTool](https://www.imgtec.com/developers/powervr-sdk-tools/pvrtextool/)
Use the `-no_ktx` and `-etc1_only`/`-format_only` options to unpack to less files.
After compression, the compressor transcodes all slices in the output .basis file to validate that the file decompresses correctly. It also validates all header, compressed data, and slice data CRC16's.
`-info` and `-validate` will just display file information and not output any files.
For best quality, you must **supply basisu with original uncompressed source images**. Any other type of lossy compression applied before basisu (including ETC1/BC1-5, BC7, JPEG, etc.) will cause multi-generational artifacts to appear in the final output textures.
The written mipmapped, cubemap, or texture array .KTX/.DDS files will be in a wide variety of compressed GPU texture formats (PVRTC1 4bpp, ETC1-2, BC1-5, BC7, etc.), and to our knowledge there is unfortunately (as of 2024) still no single .KTX or .DDS viewer tool that correctly and reliably supports every GPU texture format that we support. BC1-5 and BC7 files are viewable using AMD's Compressonator, ETC1/2 using Mali's Texture Compression Tool, and PVRTC1 using Imagination Tech's PVRTexTool. [RenderDoc](https://renderdoc.org/) has a useful texture file viewer for many formats. The macOS *Finder* app supports previewing .EXR, .ASTC and .KTX files in various GPU formats, including ASTC LDR/ HDR. The Windows 11 Explorer can preview .DDS files. The [online OpenHDR Viewer](https://viewer.openhdr.org/) is useful for viewing .EXR/.HDR image files.
For the maximum possible achievable ETC1S mode quality with the current format and encoder (completely ignoring encoding speed!), use:
----
`basisu x.png -comp_level 5 -max_endpoints 16128 -max_selectors 16128 -no_selector_rdo -no_endpoint_rdo`
Pixel Shader Deblocking Sample: CPU + GPU Deblocking Everywhere
---------------------------------------------------------------
Level 5 is extremely slow, so unless you have a very powerful machine, levels 1-4 are recommended.
The [shader_deblocking sample](https://github.com/BinomialLLC/basis_universal/blob/master/shader_deblocking/README.md) in the repo demonstrates how to use a simple pixel shader to deblock sampled textures of any block size between 4x4-12x12, greatly reducing block artifacts. The sample shader is compatible with mipmapping and bilinear or trilinear filtering. Ultimately, shader deblocking enables the usage of larger ASTC block sizes, reducing bitrate and increasing transcoding speeds. Deblocking is a standard feature of modern image and video codecs, and there's no reason why it can't be used while sampling (or transcoding) GPU textures. Using larger ASTC block sizes can significantly reduce GPU memory bandwidth. If bandwidth is the bottleneck — as it often is — the modest ALU and texture sampling cost of deblocking can be effectively free.
Note that "-no_selector_rdo -no_endpoint_rdo" are optional. Using them hurts rate distortion performance, but increases quality. An alternative is to use -selector_rdo_thresh X and -endpoint_rdo_thresh, with X ranging from [1,2] (higher=lower quality/better compression - see the tool's help text).
XUASTC LDR's transcoder supports adaptive deblocking when transcoding to other (non-ASTC) formats like BC7, and GPU shader deblocking can be used for ASTC, resulting in a complete deblocking system for ASTC.
To compress small video sequences, say using tools like ffmpeg and VirtualDub:
----
`basisu -comp_level 2 -tex_type video -stats -debug -multifile_printf "pic%04u.png" -multifile_num 200 -multifile_first 1 -max_selectors 16128 -max_endpoints 16128 -endpoint_rdo_thresh 1.05 -selector_rdo_thresh 1.05`
Python Support
--------------
For video, the more cores your machine has, the better. Basis is intended for smaller videos of a few dozen seconds or so. If you are very patient and have a Threadripper or Xeon workstation, you should be able to encode up to a few thousand 720P frames. The "webgl_videotest" directory contains a very simple video viewer.
For texture video, use -comp_level 2 or 3. The default is 1, which isn't quite good enough for texture video. Higher comp_level's result in reduced ETC1S artifacts.
All key encoder and all transcoder functionality is now available from Python, but this is still in the early stages of development. See the README files in the python directory for how to build the native SO's/PYD's. The Python support module supports both native and WASM modules, which is used as a fallback if native libraries can't be loaded. Python support has been tested under Ubuntu Linux and Windows 11 so far.
The .basis file will contain multiple images (all using the same global codebooks), which you can retrieve using the transcoder's image API. The system now supports [conditional replenisment](https://en.wikipedia.org/wiki/MPEG-1) (CR, or "skip blocks"). CR can reduce the bitrate of some videos (highly dependent on how dynamic the content is) by over 50%. For videos using CR, the images must be requested from the transcoder in sequence from first to last, and random access is only allowed to I-Frames.
Example:
```
cd python
python3 -m tests.test_backend_loading
========== BACKEND LOADING TEST ==========
If you are doing rate distortion comparisons vs. other similar systems, be sure to experiment with increasing the endpoint RDO threshold (-endpoint_rdo_thresh X). This setting controls how aggressively the compressor's backend will combine together nearby blocks so they use the same block endpoint codebook vectors, for better coding efficiency. X defaults to a modest 1.5, which means the backend is allowed to increase the overall color distance by 1.5x while searching for merge candidates. The higher this setting, the better the compression, with the tradeoff of more block artifacts. Settings up to ~2.25 can work well, and make the codec more competitive. "-endpoint_rdo_thresh 1.75" is a good setting on many textures.
Testing native backend...
[Encoder] Using native backend
[OK] Native backend loaded
Hello from basisu_wasm_api.cpp version 200
Native get_version() ? 200
Native alloc() returned ptr = 190977024
Native free() OK
[OK] Native basic operations working.
For video, level 1 should result in decent results on most clips. For less banding, level 2 can make a big difference. This is still an active area of development, and quality/encoding perf. will improve over time.
Testing WASM backend...
[WASM Encoder] Loaded: /mnt/c/dev/xuastc4/python/basisu_py/wasm/basisu_module_st.wasm
[Encoder] Using WASM backend
[OK] WASM backend loaded
Hello from basisu_wasm_api.cpp version 200
WASM get_version() ? 200
WASM alloc() returned ptr = 26920160
WASM free() OK
[OK] WASM basic operations working.
To control the ETC1S encoder's quality vs. encoding speed tradeoff, see [ETC1S Compression Effort Levels](https://github.com/BinomialLLC/basis_universal/wiki/ETC1S-Compression-Effort-Levels).
========== DONE ==========
```
### More Examples
----
`basisu x.png`\
Compress sRGB image x.png to a ETC1S format x.basis file using default settings (multiple filenames OK). ETC1S format files are typically very small on disk (around .5-1.5 bits/texel).
WebGL Examples
--------------
`basisu -uastc x.png`\
Compress image x.png to a UASTC format x.basis file using default settings (multiple filenames OK). UASTC files are the same size as BC7 on disk (8-bpp). Be sure to compress UASTC .basis files yourself using Deflate, zstd, etc. To increase .basis file compressibility (trading off quality for smaller compressed files) use the "-uastc_rdo_q X" command line parameter.
The 'WebGL' directory contains several simple WebGL demos that use the transcoder and compressor compiled to [WASM](https://webassembly.org/) with [Emscripten](https://emscripten.org/). These demos are online [here](https://subquantumtech.com/xu/). See more details in the readme file [here](webgl/README.md).
`basisu -q 255 x.png`\
Compress sRGB image x.png to x.basis at max quality level achievable without manually setting the codebook sizes (multiple filenames OK)
`basisu x.basis`\
Unpack x.basis to PNG/KTX files (multiple filenames OK)
`basisu -validate -file x.basis`\
Validate x.basis (check header, check file CRC's, attempt to transcode all slices)
`basisu -unpack -file x.basis`\
Validates, transcodes and unpacks x.basis to mipmapped .KTX and RGB/A .PNG files (transcodes to all supported GPU texture formats)
`basisu -q 255 -file x.png -mipmap -debug -stats`\
Compress sRGB x.png to x.basis at quality level 255 with compressor debug output/statistics
`basisu -linear -max_endpoints 16128 -max_selectors 16128 -file x.png`\
Compress non-sRGB x.png to x.basis using the largest supported manually specified codebook sizes
`basisu -linear -global_sel_pal -no_hybrid_sel_cb -file x.png`\
Compress a non-sRGB image, use virtual selector codebooks for improved compression (but slower encoding)
`basisu -linear -global_sel_pal -file x.png`\
Compress a non-sRGB image, use hybrid selector codebooks for slightly improved compression (but slower encoding)
`basisu -tex_type video -comp_level 2 -framerate 20 -multifile_printf "x%02u.png" -multifile_first 1 -multifile_count 20 -selector_rdo_thresh 1.05 -endpoint_rdo_thresh 1.05`\
Compress a 20 sRGB source image video sequence (x01.png, x02.png, x03.png, etc.) to x01.basis
`basisu -comp_level 2 -q 255 -file x.png -mipmap -y_flip`\
Compress a mipmapped x.basis file from an sRGB image named x.png, Y flip each source image, set encoder to level 2 for slightly higher quality (but slower encoding).
### WebGL test
The "WebGL" directory contains three simple WebGL demos that use the transcoder and compressor compiled to wasm with [emscripten](https://emscripten.org/). See more details [here](webgl/README.md).
![Screenshot of 'texture' example running in a browser.](webgl/texture/preview.png)
![Screenshot of 'texture' example running in a browser.](webgl/texture_test/preview.png)
![Screenshot of 'gltf' example running in a browser.](webgl/gltf/preview.png)
![Screenshot of 'encode_test' example running in a browser.](webgl/encode_test/preview.png)
![Screenshot of 'encode_test' example running in a browser.](webgl/ktx2_encode_test/preview.png)
### Installation using the vcpkg dependency manager
----
Building the WASM Modules with [Emscripten](https://emscripten.org/)
--------------------------------------------------------------------
Both the transcoder and encoder may be compiled using Emscripten to WebAssembly and used on the web. A set of JavaScript wrappers to the codec, written in C++ with Emscripten extensions, is located in [`webgl/transcoding/basis_wrappers.cpp`](https://github.com/BinomialLLC/basis_universal/blob/master/webgl/transcoder/basis_wrappers.cpp). The JavaScript wrapper supports nearly all features and modes, including texture video. See the [README.md](https://github.com/BinomialLLC/basis_universal/tree/master/webgl) and CMakeLists.txt files in `webgl/transcoder` and `webgl/encoder`.
To build the WASM transcoder, after installing Emscripten:
```
cd webgl/transcoder/build
emcmake cmake ..
make
```
To build the WASM encoder:
```
cd webgl/encoder/build
emcmake cmake ..
make
```
There are several simple encoding/transcoding web demos, located in `webgl/ktx2_encode_test` and `webgl/texture_test`, that show how to use the encoder's and transcoder's JavaScript wrapper APIs. They are [live on the web here](https://subquantumtech.com/xu/).
----
Low-level C++ Encoder/Transcoder API Examples
---------------------------------------------
Some simple examples showing how to directly call the C++ encoder and transcoder library APIs are in [`example/example.cpp`](https://github.com/BinomialLLC/basis_universal/blob/master/example/example.cpp).
----
Installation using the vcpkg dependency manager
-----------------------------------------------
You can download and install Basis Universal using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
@@ -231,88 +493,51 @@ You can download and install Basis Universal using the [vcpkg](https://github.co
./vcpkg integrate install
vcpkg install basisu
The Basis Universal port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
The Basis Universal port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. (9/10/2024: UASTC HDR support is not available here yet.)
### WebAssembly Support Using Emscripten
---
Both the transcoder and now the compressor (as of 12/17/2020) may be compiled using emscripten to WebAssembly and used on the web. Currently, multithreading is not supported by the compressor when compiled with emscripten. A simple Web compression demo is in webgl/encode_test. All compressor features, including texture video, are supported and fully exposed.
Project Policies
----------------
To enable compression support compile the JavaScript wrappers in `webgl/transcoding/basis_wrappers.cpp` with `BASISU_SUPPORT_ENCODING` set to 1. See the webgl/encoding directory.
See our wiki page: [Project Policies: PRs, compiler warnings, release cadence etc.](https://github.com/BinomialLLC/basis_universal/wiki/Project-Policies:-PR's,-compiler-warnings,-release-cadence,-etc.).
### Low-level C++ encoder API
----
You can call the encoder directly, instead of using the command line tool. We'll be adding documentation and some examples by the end of the year. For now, some important notes:
KTX2 Support Status
-------------------
First, ALWAYS call ```basisu::basisu_encoder_init()``` to initialize the library. Otherwise, you'll get undefined behavior or black textures.
Note as of March 2026 we are working with Khronos on the exact details of how we embed XUASTC LDR supercompressed texture data into the KTX2 file format. KTX2 texture files using our previous codecs (including the recently added UASTC HDR 4x4 and UASTC HDR 6x6i formats) can now be interchanged with other KTX2 tools. See our [KTX2 technical information document](https://github.com/BinomialLLC/basis_universal/wiki/KTX2-File-Format-Support-Technical-Details) for more info.
Create a job pool, fill in the ```basis_compress_params``` struct, then call ```basisu::basis_compressor::init()```, then ```basisu::basis_compressor::process()```. Like this for UASTC:
Whenever possible, we keep full introspection/transcode compatibility with all of our previously written KTX2 files, even if during standardization a file format change is made. We don't expect how we embed XUASTC LDR into KTX2 in basisu v2.1 to change.
```
bool test()
{
basisu_encoder_init();
----
image img;
if (!load_image("test.png", img))
{
printf("Can't load image\n");
return false;
}
Repository Licensing with REUSE
-------------------------------
basis_compressor_params basisCompressorParams;
The repository has been updated to be compliant with the REUSE license
checking tool (https://reuse.software/). See the [.reuse](https://github.com/BinomialLLC/basis_universal/tree/master/.reuse) subdirectory.
basisCompressorParams.m_source_images.push_back(img);
basisCompressorParams.m_perceptual = false;
basisCompressorParams.m_mip_srgb = false;
External Links
--------------
basisCompressorParams.m_write_output_basis_files = true;
basisCompressorParams.m_out_filename = "test.basis";
- [btx - KTX2 Command Line Tool](https://github.com/BinomialLLC/KTX-Software-Binomial-Fork) - Our fork of KTX-Software with bug fixes, working HDR quality/effort controls, new options, and new codec integrations. This tool can validate, extract, and compress KTX2 files compatible with our project.
- [ARM's astcenc](https://github.com/ARM-software/astc-encoder) - Crucial official tool from ARM which can unpack ASTC format LDR/HDR .astc and .ktx files to .png or .exr for testing and verification purposes.
- [Online .EXR and .HDR Image File Viewer](https://viewer.openhdr.org/) - OpenHDR Viewer. Has a very well implemented tone mapper, auto-exposure, and HDR histogram.
- [Windows HDR + WCG Image Viewer](https://13thsymphony.github.io/hdrimageviewer/) - A true HDR image viewer for Windows which works on HDR monitors. Also see [the github repo](https://github.com/13thsymphony/HDRImageViewer).
- [AMD Compressonator](https://gpuopen.com/compressonator/) - .DDS viewer, can view .KTX files in some formats.
- [PVRTexTool](https://www.imgtec.com/developers/powervr-sdk-tools/pvrtextool/) - Can view .ASTC and .KTX files in some formats. (Note: .DDS viewer seems busted for BC1, doesn't support BC7 at all.)
- [Microsoft's DirectXTex](https://github.com/microsoft/DirectXTex) - Samples contain a basic .DDS viewer. (Note: May still have issues loading .DDS files with texture dimensions that aren't divisible by 4 texels.)
- [RenderDoc](https://renderdoc.org/) - Reliable viewer for LDR/HDR .DDS files in BC1-7 formats.
- [Paint.NET](https://www.getpaint.net/) - Windows app: built-in [.DDS file loading](https://github.com/0xC0000054/pdn-ddsfiletype-plus), supports BC1-7 and [cubemaps](https://github.com/0xC0000054/pdn-ddsfiletype-plus/wiki/Cube-Maps)
- [Mali Texture Compression Tool](https://community.arm.com/support-forums/f/graphics-gaming-and-vr-forum/52390/announcement-mali-texture-compression-tool-end-of-life) - Now deprecated.
- [Our GitHub wiki content statically mirrored as HTML](https://subquantumtech.com/basisu_wiki/Home.html), which lags behind the [live GitHub wiki](https://github.com/BinomialLLC/basis_universal/wiki)
For more useful links, papers, and tools/libraries, see the end of the [UASTC HDR 4x4 texture specification](https://github.com/BinomialLLC/basis_universal/wiki/UASTC-HDR-4x4-Texture-Specification#papersfurther-reading).
basisCompressorParams.m_uastc = true;
basisCompressorParams.m_rdo_uastc_multithreading = false;
basisCompressorParams.m_multithreading = false;
basisCompressorParams.m_debug = true;
basisCompressorParams.m_status_output = true;
basisCompressorParams.m_compute_stats = true;
basisu::job_pool jpool(1);
basisCompressorParams.m_pJob_pool = &jpool;
----
basisu::basis_compressor basisCompressor;
basisu::enable_debug_printf(true);
bool ok = basisCompressor.init(basisCompressorParams);
if (ok)
{
basisu::basis_compressor::error_code result = basisCompressor.process();
if (result == basisu::basis_compressor::cECSuccess)
printf("Success\n");
else
{
printf("Failure\n");
ok = false;
}
}
else
printf("Failure\n");
return ok;
}
```
The command line tool uses this API too, so you can always look at that to see what it does given a set of command line options.
### Repository Licensing with REUSE
The repository has been updated to be compliant with the REUSE licenese
checking tool (https://reuse.software/). This was done by adding the complete
text of all licenses used under the LICENSES/ directory and adding the
.reuse/dep5 file which specifies licenses for files which don't contain
them in a form which can be automatically parse by the reuse tool. REUSE
does not alter copyrights or licenses, simply captures information about
licensing to ensure the entire repository has explicit licensing information.
To ensure continued REUSE compliance, run `reuse lint` at the root of
a clean, checked-out repository periodically, or run it during CI tests
before any build artifacts have been created.
E-mail: info @ binomial dot info, or contact us on [Twitter](https://twitter.com/_binomial)
Here's the [Sponsors](https://github.com/BinomialLLC/basis_universal/wiki/Sponsors-and-Supporters) wiki page.
+3927 -2068
View File
File diff suppressed because it is too large Load Diff
+715
View File
@@ -0,0 +1,715 @@
// Don't edit directly - see python script in cmd_help directory.
"See project and wiki at: https://github.com/BinomialLLC/basis_universal\n"
"\n"
"The default processing mode is compression of one or more .PNG/.TGA/.JPG/.QOI/\n"
".DDS/.EXR/.HDR files to a LDR or HDR .KTX2 file. Alternate modes:\n"
"\n"
" -unpack: Use transcoder to unpack a .basis/.KTX2 file to one or more .KTX, \n"
" .DDS, .PNG, .ASTC, etc. files.\n"
"\n"
" -info: Display high-level information about a .basis/.KTX2 file\n"
" \n"
" -validate: Validate and display information about a .basis/.KTX2 file\n"
" \n"
" -compare: Compare two LDR PNG/BMP/TGA/JPG/QOI images specified with -file,\n"
" output PSNR and SSIM statistics and RGB/A delta images\n"
"\n"
" -compare_hdr: Compare two HDR .EXR/.HDR images specified with -file, output\n"
" PSNR statistics and RGB delta images\n"
"\n"
" -tonemap: Tonemap an HDR or EXR image to PNG at multiple exposures, use -file\n"
" to specify filename\n"
"\n"
" -peek_astc: Read an .astc file and calculate statistics (for testing/dev)\n"
"\n"
" -version or --version: Print version and exit\n"
"\n"
"--- Intro:\n"
"\n"
"This tool compresses LDR/SDR and HDR images and textures to a Basis Universal\n"
"supercompressed GPU texture, which can be written to supercompressed .basis or\n"
"standard .KTX2 files. It supports a number of SDR and HDR codecs, each with\n"
"different quality, transcoding performance, and bitrate tradeoffs. A SDR/HDR\n"
"mipmap generator is also included. This tool can also examine and\n"
"unpack .KTX2/.basis files to .PNG, .KTX (v1), .ASTC, or .DDS files using its\n"
"single source file transcoder library.\n"
"\n"
"The list of supported texture/supercompressed texture codecs:\n"
" -LDR: ETC1S (SDR default), RDO UASTC LDR 4x4, ASTC or XUASTC LDR 4x4-12x12\n"
" -HDR: UASTC HDR 4x4 (HDR default), RDO ASTC HDR 6x6, UASTC HDR 6x6\n"
" \n"
"RDO=Rate-Distortion Optimization. Two key parameters (quality and effort) have\n"
"been unified across all the codecs:\n"
"\n"
"- The -quality X parameter, where X ranges from [0, 100], controls the\n"
"compression quality vs. bitrate (output file size) tradeoff for those codecs\n"
"supporting supercompression or RDO (Rate-distortion optimization). 100=max\n"
"quality, and lower levels produce smaller files with more distortion.\n"
"\n"
"- The -effort X parameter, where X ranges from [0, 10], controls the\n"
"compression speed (and max CPU usage) vs. max achievable quality tradeoff.\n"
"Low efforts result in more distortion/artifacts, but faster compression. Lower\n"
"efforts result in less utilization of the underlying GPU block format's\n"
"capabilities.\n"
"\n"
"Key Definitions: \n"
"\"Quality\" controls the explicit tradeoff between output distortion and output\n"
"file bitrate (in bits per pixel or target). At max quality (100) each\n"
"compressor will output the lowest distortion it's capable of at its currently\n"
"configured effort level.\n"
"\n"
"\"Effort\" controls how intensely a compressor uses the CPU to focus on each\n"
"block format's encoding capabilities. Low effort levels only target a set of\n"
"core or basic capabilities (specific to each output target format), while\n"
"higher effort levels allow each compressor to explore more of each target's\n"
"features (at the cost of higher CPU time). Lower effort levels result in more\n"
"brittle compression (higher distortion on tough image/texture features).\n"
"Effort=0 fastest compression, effort=10=extremely slow.\n"
"\n"
"Unless an explicit mode is specified, if one or more files have the .basis\n"
"or .KTX2 extension this tool defaults to unpack mode.\n"
"\n"
"By default, the compressor assumes the input is in the sRGB colorspace (like\n"
"typical photos/albedo textures). If the input is NOT sRGB (like a normal map),\n"
"be sure to specify -linear for less artifacts. Depending on the content type,\n"
"some experimentation may be needed.\n"
"\n"
"The TinyEXR library is used to read .EXR images. Crucially, this small library\n"
"does not support all .EXR compression methods. For unsupported images, you can\n"
"use a tool like ImageMagick to convert them to uncompressed .EXR.\n"
"\n"
"For .DDS source files: Mipmapped or plain 2D textures (but not cubemaps) are\n"
"supported. Only uncompressed 32-bit RGBA/BGRA, half float RGBA, or float\n"
"RGBA .DDS files are supported. In -tex_array mode, if a .DDS file is specified,\n"
"all source files must be in .DDS format.\n"
"\n"
"Filenames prefixed with a @ symbol are read as filename listing files. Listing\n"
"text files specify which actual filenames to process (one filename per line).\n"
"\n"
"--- High-Level Texture Mode (Codec) Selection:\n"
"\n"
" 1. -etc1s: Encode to supercompressed ETC1S LDR (the default for SDR/LDR\n"
" inputs). Roughly .8-2.5 bpp. Supports temporal texture supercompression\n"
" (texture video) with skip blocks (Conditional Replenishment), with global\n"
" codebooks shared across all frames.\n"
"\n"
" 2. -uastc/-uastc_ldr: UASTC LDR 4x4. Encode to UASTC LDR 4x4, a custom high\n"
" quality virtual texture format designed for fast transcoding to numerous\n"
" GPU texture formats. Roughly 5-8 bpp. Supports RDO encoding using -lambda X\n"
" option - see options below. In this mode the multi-target compressor\n"
" optimizes for a balance of transcoded ASTC 4x4 LDR and BC7 quality.\n"
"\n"
" 3. -hdr/-hdr_4x4: UASTC HDR 4x4. Encode input as UASTC HDR 4x4 (the default if\n"
" any input file has the .EXR or .HDR extension, or if any .DDS file is HDR).\n"
" Output is standard, but constrained, ASTC HDR 4x4. Roughly 5-8 bpp. In this\n"
" mode the dual-target compressor optimizes for a balance of transcoded ASTC\n"
" 4x4 HDR and BC6H quality.\n"
"\n"
" 4. -hdr_6x6: ASTC HDR 6x6. Encode input as RDO or highest quality standard\n"
" ASTC HDR 6x6. Use -quality (preferred) or -lambda X (low-level, try\n"
" 100-20000 or higher) option to enable RDO ASTC HDR 6x6, where x controls\n"
" the quality vs. size tradeoff. Roughly 1.2-3.2 bpp.\n"
"\n"
" 5. -hdr_6x6i: UASTC HDR 6x6. Encode input as supercompressed UASTC HDR 6x6\n"
" intermediate. Use -quality (preferred) or -lambda X (low-level, try\n"
" 100-20000 or higher) option to enable RDO UASTC HDR 6x6, where x controls\n"
" the quality vs. size tradeoff. Roughly 1-3.2 bpp.\n"
"\n"
" 6. XUASTC LDR 4x4-12x12: -ldr_4x4i, -ldr_5x4i, -ldr_5x5i, -ldr_6x5i,\n"
" -ldr_6x6i, -ldr_8x5i, -ldr_8x6i, -ldr_10x5i, -ldr_10x6i, -ldr_8x8i,\n"
" -ldr_10x8i, -ldr_10x10i, -ldr_12x10i, -ldr_12x12i:\n"
" Compress to supercompressed XUASTC LDR/SDR using the specific\n"
" ASTC block size. See additional ASTC/XUASTC LDR specific options\n"
" (-effort, -quality, -xy, -ts, -tl, etc.) below. Roughly .3-5.7 bpp\n"
"\n"
" 7. ASTC LDR 4x4-12x12: -ldr_4x4, -ldr_5x4, -ldr_5x5, -ldr_6x5, -ldr_6x6,\n"
" -ldr_8x5, -ldr_8x6, -ldr_10x5, -ldr_10x6, -ldr_8x8, -ldr_10x8,\n"
" -ldr_10x10, -ldr_12x10, -ldr_12x12:\n"
" Compress to standard or ZStd supercompressed ASTC LDR/SDR using\n"
" the specific ASTC block size. See additional ASTC LDR specific\n"
" options (-effort, -quality, -xy, -ts, -tl, etc.) below. .89-8 bpp before\n"
" ZStd compression.\n"
"\n"
"--- Tool Options:\n"
"\n"
" -ktx2: Write .KTX2 files (the default). By default, UASTC LDR/HDR 4x4 and ASTC\n"
" 6x6 files will be compressed using Zstandard unless -ktx2_no_zstandard is\n"
" specified.\n"
"\n"
" -basis: Write .basis files instead of .KTX2 files.\n"
"\n"
" -file filename.png/tga/jpg/qoi/exr/hdr: Input image filename, multiple images\n"
" are OK, use -file X for each input filename (prefixing input filenames\n"
" with -file is optional)\n"
"\n"
" -alpha_file filename.png/tga/jpg/qoi: Input alpha image filename, multiple\n"
" images are OK, use -file X for each input filename (must be paired\n"
" with -file), images converted to REC709 grayscale and used as input alpha\n"
"\n"
" -quiet or -no_status_output: Disable compressor's status output to stdout\n"
"\n"
" -output_file filename: Output .basis/.KTX2 filename\n"
"\n"
" -output_path: Output .basis/.KTX2 files to specified directory.\n"
"\n"
" -debug or -verbose: Enable codec debug print to stdout (slightly slower).\n"
"\n"
" -debug_images: Enable codec debug images (much slower).\n"
"\n"
" -stats: Compute and display image quality metrics (slightly to much slower).\n"
"\n"
" -individual: Process input images individually and output\n"
" multiple .basis/.KTX2 files (not as a texture array - this is now the default\n"
" as of v1.16)\n"
"\n"
" -parallel: Compress multiple textures simultaneously (one per thread), instead\n"
" of one at a time. Compatible with OpenCL mode. This is much faster, but in\n"
" OpenCL mode the driver is pushed harder, and the CLI output will be jumbled.\n"
"\n"
" -linear: Use linear colorspace metrics (instead of the default sRGB or scaled\n"
" RGB for HDR), write linear transfer function setting to KTX2/basis file, and\n"
" by default linear (not sRGB) mipmap filtering (unless overridden). Same\n"
" as -tl.\n"
"\n"
" -srgb: Use sRGB colorspace metrics, write sRGB transfer function setting to\n"
" KTX2/basis file, and by default use sRGB mipmap filtering (unless\n"
" overridden). Same as -ts.\n"
"\n"
" -tex_type <2d, 2darray, 3d, video, cubemap>: Set Basis file header's texture\n"
" type field. Cubemap arrays require multiples of 6 images, in X+, X-, Y+, Y-,\n"
" Z+, Z- order, each image must be the same resolutions. 2d=arbitrary 2D\n"
" images, 2darray=2D array, 3D=volume texture slices, video=video frames,\n"
" cubemap=array of faces. For 2darray/3d/cubemaps/video, each source image's\n"
" dimensions and # of mipmap levels must be the same. For video, the .basis\n"
" file will be written with the first frame being an I-Frame, and subsequent\n"
" frames being P-Frames (using conditional replenishment). Playback must always\n"
" occur in order from first to last image.\n"
"\n"
" -cubemap: same as -tex_type cubemap\n"
"\n"
" -tex_array: Process input images as a single texture array and write a\n"
" single .basis/.KTX2 file (the former default before v1.16)\n"
"\n"
" -fuzz_testing: Use with -validate: Disables CRC16 validation of file contents\n"
" before transcoding\n"
"\n"
" -multifile_printf: printf() format string to use to compose multiple filenames\n"
"\n"
" -multifile_first: The index of the first file to process, default is 0 (must\n"
" specify -multifile_printf and -multifile_num)\n"
"\n"
" -multifile_num: The total number of files to process.\n"
"\n"
" -opencl: Enable OpenCL usage (currently only accelerates ETC1S encoding)\n"
"\n"
" -opencl_serialize: Serialize all calls to the OpenCL driver (to work around\n"
" buggy drivers, only useful with -parallel)\n"
"\n"
"--- ETC1S specific options (-etc1s - the LDR/SDR default):\n"
"\n"
" -quality X and -effort X: Set quality (1-100) and effort (0-10) levels\n"
"\n"
" -q X: Low-level ETC1S quality level, 1-255, default is 128, lower=better\n"
" compression/lower quality/faster, higher=less compression/higher\n"
" quality/slower, default is 128. For even higher quality,\n"
" use -max_endpoints/-max_selectors. (-quality is preferred.)\n"
"\n"
" -comp_level X: Low-level ETC1S speed vs. quality tradeoff. Range is 0-6,\n"
" default is 1. Higher values=MUCH slower, but slightly higher quality. Higher\n"
" levels intended for videos. (-effort is preferred.)\n"
"\n"
" -max_endpoints X: ETC1S: Manually set the max number of color endpoint\n"
" clusters from 1-16128, use instead of -q\n"
"\n"
" -max_selectors X: ETC1S: Manually set the max number of color selector\n"
" clusters from 1-16128, use instead of -q\n"
"\n"
"--- UASTC LDR/HDR 4x4 specific options (-uastc or -uastc_ldr):\n"
"\n"
" -quality X and -effort X: Set quality (1-100) and effort (0-10) levels\n"
"\n"
" -uastc, -uastc_ldr or -uastc_ldr_4x4: Enable UASTC LDR 4x4 texture mode,\n"
" instead of the default ETC1S mode. Significantly higher texture quality, but\n"
" much larger (~8bpp) files. (Note that UASTC LDR 4x4 .basis files must be\n"
" losslessly compressed by the user.)\n"
"\n"
" -uastc_level: Set low-level UASTC LDR/HDR 4x4 encoding effort level. LDR Range\n"
" is [0,4], default is 2, higher=slower but higher quality. 0=fastest/lowest\n"
" quality, 3=slowest practical option, 4=impractically slow/highest achievable\n"
" quality. UASTC HDR 4x4 range is [0,4]: higher=slower, but higher quality. HDR\n"
" 4x4 default level=1.\n"
"\n"
" -uastc_rdo_l X: Enable UASTC LDR 4x4 RDO post-processing and set the low-level\n"
" UASTC LDR 4x4 RDO quality scalar (lambda) to X. Lower values=higher\n"
" quality/larger LZ compressed files, higher values=lower quality/smaller LZ\n"
" compressed files. Good range to try is [.25-10]. Note: Previous versons used\n"
" the -uastc_rdo_q option, which was removed because the RDO algorithm was\n"
" changed.\n"
"\n"
" -uastc_rdo_d X: Set UASTC LDR 4x4 RDO dictionary size in bytes. Default is\n"
" 4096, max is 65536. Lower values=faster, but less compression.\n"
"\n"
" -uastc_rdo_b X: Set UASTC LDR 4x4 RDO max smooth block error scale. Range is\n"
" [1,300]. Default is 10.0, 1.0=disabled. Larger values suppress more artifacts\n"
" (and allocate more bits) on smooth blocks.\n"
"\n"
" -uastc_rdo_s X: Set UASTC LDR 4x4 RDO max smooth block standard deviation.\n"
" Range is [.01,65536]. Default is 18.0. Larger values expand the range of\n"
" blocks considered smooth.\n"
"\n"
" -uastc_rdo_f: Don't favor simpler UASTC LDR 4x4 modes in RDO mode.\n"
"\n"
" -uastc_rdo_m: Disable RDO multithreading (slightly higher compression,\n"
" deterministic).\n"
"\n"
"--- UASTC HDR 4x4 specific options (-hdr or -hdr_4x4 - the HDR default):\n"
"\n"
" -hdr, -hdr_4x4, or -uastc_hdr_4x4: Enable UASTC HDR 4x4 mode\n"
"\n"
" -quality X and -effort X: Set quality (1-100) and effort (0-10) levels\n"
"\n"
" -uastc_level X: Sets the low-level UASTC HDR 4x4 compressor's effort level.\n"
" Valid range is [0,4]: higher=slower but higher quality. HDR\n"
" default=1. Level 0=fastest/lowest quality, 3=highest practical\n"
" setting, 4=exhaustive\n"
"\n"
" -hdr_uber_mode: Allow the UASTC HDR 4x4 encoder to try varying the CEM 11\n"
" selectors more for slightly higher quality (slower). This may negatively\n"
" impact BC6H quality, however.\n"
"\n"
" -hdr_ultra_quant: UASTC HDR 4x4: Try to find better quantized CEM 7/11\n"
" endpoint values (slower).\n"
"\n"
" -hdr_favor_astc: UASTC HDR 4x4: By default the dual-target UASTC HDR 4x4\n"
" encoder tries to strike a balance or even slightly favor BC6H quality. If\n"
" this option is specified, ASTC HDR 4x4 quality is favored instead.\n"
"\n"
"--- ASTC/UASTC HDR 6x6 specific options (-hdr_6x6 or -hdr_6x6i):\n"
"\n"
"Internally both modes use the same compressor which can generate either\n"
"standard ASTC HDR 6x6 (with optional RDO) or UASTC HDR 6x6 (supercompressed\n"
"with a custom format).\n"
"\n"
" -hdr_6x6 or -astc_hdr_6x6: Enable RDO ASTC HDR 6x6 mode\n"
"\n"
" -hdr_6x6i or -uastc_hdr_6x6: Enable UASTC HDR 6x6 mode\n"
"\n"
" -quality X and -effort X: Set quality (1-100) and effort (0-10) levels\n"
"\n"
" -lambda X: Low-level option to enable rate distortion optimization (RDO) and\n"
" directly control the HDR 6x6 compressor's lambda setting. The\n"
" higher this value, the lower the quality, but the smaller the file\n"
" size. Try 100-20000, or higher values on some images. Upconverted\n"
" SDR images can generally tolerate much higher lambda settings vs.\n"
" true HDR images.\n"
"\n"
" -hdr_6x6_level X: Low-level option to set the codec to 6x6 HDR mode (same\n"
" as -hdr_6x6) and controls encoder performance vs. max quality\n"
" tradeoff. X may range from [0,12]. Default level is 2. Higher\n"
" values result in better quality but slower encoding. Values above\n"
" 10 are extremely slow.\n"
"\n"
" -hdr_6x6i_level X: Low-level option to set the codec to 6x6 HDR intermediate\n"
" mode (same as -hdr_6x6i) and controls encoder performance vs. max\n"
" quality tradeoff. X may range from [0,12]. Default level is 2.\n"
"\n"
" -rec_2020: The input image's gamut is Rec. 2020 vs. the default Rec. 709 - for\n"
" accurate colorspace error calculations. This value will also be\n"
" written to the KTX2 file's header in the DFD.\n"
"\n"
" -hdr_6x6_jnd X, -hdr_6x6_extra_pats, -hdr_6x6_brute_force_pats,\n"
" -hdr_6x6_comp_levels X Y, or -hdr_6x6i_comp_levels X Y: Low-level\n"
" control over the encoder's configuration.\n"
"\n"
" -hdr_6x6i_16_compatibility: Write basisu v1.6 UASTC HDR 6x6i files (default)\n"
"\n"
" -hdr_6x6i_20_compatibility: Write basisu v2.0 UASTC HDR 6x6i files (slightly\n"
" lower bitrate, only compatible with v2.0+ transcoders however.)\n"
"\n"
"--- SDR/LDR->HDR upconversion options (only used when encoding to HDR formats\n"
"from an LDR/SDR source image):\n"
"\n"
" -hdr_ldr_no_srgb_to_linear: If specified, LDR images will NOT be converted to\n"
" normalized linear light (via a sRGB->Linear conversion) during SDR->HDR\n"
" upconversion before compressing as HDR.\n"
"\n"
" -hdr_ldr_upconversion_nit_multiplier X: Specify how many nits (candelas per\n"
" sq. meter) LDR/SDR images are converted to after converting to linear\n"
" light. Default is 100 nits. Note: Previous builds used 1 nit. Common\n"
" values are 80-100 nits.\n"
"\n"
"--- ASTC LDR/XUASTC LDR specific options (-ldr_4x4 or -ldr_4x4i, up to 12x12):\n"
"\n"
"Internally both modes (ASTC 4x4-12x12 and XUASTC 4x4-12x12) use the same\n"
"core compressor but with different outputs. All 14 standard ASTC block \n"
"sizes are supported (see the list below).\n"
"\n"
" -ldr_4x4-12x12 or -astc_ldr_4x4-12x12: Enable ASTC LDR 4x4-12x12 mode\n"
"\n"
" -ldr_4x4i-12x12 or -xuastc_ldr_4x4-12x12: Enable XUASTC LDR 4x4-12x12 mode\n"
"\n"
" -quality X: Enables lossy weight grid DCT and sets DCT quality level [1,100]\n"
" (defaults to no DCT). Higher=better quality, but higher bitrate. Good values\n"
" to try are 30-90. Default is no weight grid DCT.\n"
"\n"
" -effort X: Set encoder effort level [0,10]: Encoding speed tradeoff, \n"
" higher=slower but potentially higher overall quality. Default=3, 10=Insane.\n"
" \n"
" -xuastc_arith, -xuastc_hybrid, -xuastc_zstd: Set transcoding speed vs.\n"
" compression ratio tradeoff by selecting the output profile/syntax. Default\n"
" is -xuastc_zstd (fastest, lowest ratio). ZStd is fastest/lowest ratio, arith\n"
" is slowest/highest ratio (3-15% better vs. ZStd).\n"
"\n"
" -xy: Enables lossy supercompression using windowed/bounded RDO for extra\n"
" compression (default is lossless supercompression of the XUASTC texture data\n"
" unless DCT is enabled)\n"
"\n"
" -xyd: Disables lossy supercompression (default, but automatically enabled\n"
" if -quality less than 100 is specified )\n"
"\n"
" -xs: Force disable 2-3 subset usage in all effort levels (lower quality but\n"
" faster compression and faster transcoding to BC7 at certain block sizes)\n"
"\n"
" -xp: Force disable RGB dual plane usage in all effort levels (lower quality\n"
" but faster compression and faster transcoding to BC7 at certain block sizes)\n"
"\n"
" -ts: Use LDR sRGB ASTC decoding profile - the default. This parameter should\n"
" match how the developer will decode or sample the ASTC texture data. Inverse\n"
" of -tl. Same as -srgb.\n"
"\n"
" -tl: Use LDR Linear ASTC decoding profile. Inverse of -ts. Same as -linear.\n"
"\n"
" -weights X Y Z W: Set unsigned integer channel error weights. Defaults are\n"
" 1,1,1,1. Useful to favor certain channels during compression.\n"
"\n"
" -ls_min_psnr X, -ls_min_alpha_psnr X, -ls_thresh_psnr X, -ls_thresh_alpha_psnr\n"
" X, -ls_thresh_edge_psnr X, -ls_thresh_edge_alpha_psnr X: Windowed/bounded RDO\n"
" settings (Lossy supercompression must be enabled, or -xy.)\n"
"\n"
" -xuastc_blurring: Experimental - enable blurred block candidates (MUCH slower\n"
" compression, higher quality).\n"
"\n"
"These modes support all the standard ASTC block sizes. The larger the block\n"
"size, the lower the bitrate on disk and in memory, but the more noticeable the\n"
"artifacts. Some block sizes (4x4, 6x6, and 8x6) have specially optimized direct\n"
"transcoding paths to BC7. For faster direct BC7 transcoding at these block\n"
"sizes, disable RGB dual plane (-xp) and subset usage (-xs).\n"
"\n"
" Block Size Base/Memory Size\n"
" 1. 4x4 8.00 bpp\n"
" 2. 5x4 6.40 bpp\n"
" 3. 5x5 5.12 bpp\n"
" 4. 6x5 4.27 bpp\n"
" 5. 6x6 3.56 bpp\n"
" 6. 8x5 3.20 bpp\n"
" 7. 8x6 2.67 bpp\n"
" 8. 10x5 2.56 bpp\n"
" 9. 10x6 2.13 bpp\n"
" 10. 8x8 2.00 bpp\n"
" 11. 10x8 1.60 bpp\n"
" 12. 10x10 1.28 bpp\n"
" 13. 12x10 1.07 bpp\n"
" 14. 12x12 0.89 bpp\n"
"\n"
"--- More options:\n"
"\n"
" -test: Run an automated LDR ETC1S/UASTC LDR 4x4 encoding and transcoding test.\n"
" Returns EXIT_FAILURE on any failures.\n"
"\n"
" -test_hdr_4x4/-test_hdr_6x6/-test_hdr_6x6i: Run automated UASTC HDR encoding\n"
" and transcoding tests. Returns EXIT_FAILURE on any failures.\n"
"\n"
" -test_xuastc: Run an automated XUASTC LDR encoding and transcoding test.\n"
" Returns EXIT_FAILURE on any failures.\n"
"\n"
" -test_dir: Optional directory of test files. Defaults to \"../test_files\".\n"
"\n"
" -y_flip: Flip input images vertically before compression\n"
"\n"
" -normal_map: Tunes codec parameters for better quality on normal maps (linear\n"
" colorspace metrics, linear mipmap filtering, no selector RDO, no sRGB)\n"
"\n"
" -no_alpha: Always output non-alpha basis files, even if one or more inputs has\n"
" alpha\n"
"\n"
" -force_alpha: Always output alpha basis files, even if no inputs has alpha\n"
"\n"
" -separate_rg_to_color_alpha: Separate input R and G channels to RGB and A (for\n"
" tangent space XY normal maps)\n"
"\n"
" -swizzle rgba: Specify swizzle for the 4 input color channels using r, g, b\n"
" and a (the -separate_rg_to_color_alpha flag is equivalent to rrrg)\n"
"\n"
" -renorm: Renormalize each input image before any further\n"
" processing/compression\n"
"\n"
" -no_multithreading: Disable multithreading\n"
"\n"
" -max_threads X: Use at most X threads total when multithreading is enabled\n"
" (this includes the main thread)\n"
"\n"
" -wasi_threads: Set number of threads to use in WASI threading builds\n"
" (default=8, only used in WASI threading builds)\n"
"\n"
" -no_ktx: Disable KTX writing when unpacking (faster, less output files)\n"
"\n"
" -ktx_only: Only write KTX files when unpacking (faster, less output files)\n"
"\n"
" -write_out: Write 3dfx OUT files when unpacking FXT1 textures\n"
"\n"
" -format_only: Only unpack the specified format, by its numeric code.\n"
"\n"
" -etc1_only: Only unpack to ETC1, skipping the other texture formats\n"
" during -unpack\n"
"\n"
" -disable_hierarchical_endpoint_codebooks: Disable hierarchical endpoint\n"
" codebook usage, slower but higher quality on some compression levels\n"
"\n"
" -compare_ssim: Compute and display SSIM of image comparison (slow)\n"
"\n"
" -compare_plot: Display histogram plots in -compare mode\n"
"\n"
" -bench: UASTC benchmark mode, for development only\n"
"\n"
" -resample X Y: Resample all input textures to XxY pixels using a box filter\n"
"\n"
" -resample_factor X: Resample all input textures by scale factor X using a box\n"
" filter\n"
"\n"
" -no_sse: Forbid all SSE instruction set usage\n"
"\n"
" -validate_etc1s: Validate internal ETC1S compressor's data structures during\n"
" compression (slower, intended for development).\n"
"\n"
" -ktx2_animdata_duration X: Set KTX2animData duration field to integer value X\n"
" (only valid/useful for -tex_type video, default is 1)\n"
"\n"
" -ktx2_animdata_timescale X: Set KTX2animData timescale field to integer value\n"
" X (only valid/useful for -tex_type video, default is 15)\n"
"\n"
" -ktx2_animdata_loopcount X: Set KTX2animData loopcount field to integer value\n"
" X (only valid/useful for -tex_type video, default is 0)\n"
"\n"
" -framerate X: Set framerate in .basis header to X/frames sec.\n"
"\n"
" -ktx2_no_zstandard: Don't compress UASTC texture data using Zstandard -- store\n"
" it uncompressed instead.\n"
"\n"
" -ktx2_zstandard_level X: Set ZStandard compression level to X (see Zstandard\n"
" documentation, default level is 6)\n"
"\n"
" -tonemap_dither: Dither tonemapper's 8-bit/component output by adding a small\n"
" amount of white noise, only used with -tonemap mode\n"
"\n"
"--- Mipmap Generator Options:\n"
"\n"
"By default, SDR textures will be converted from sRGB to linear light before\n"
"mipmap filtering, then back to sRGB (for the RGB color channels) unless -linear\n"
"is specified. You can override this behavior with -mip_srgb/-mip_linear.\n"
"\n"
" -mipmap: Generate mipmaps for each source image\n"
"\n"
" -mip_srgb: Convert image to linear before filtering, then back to sRGB.\n"
" (This is set automatically by default, unless you override it.)\n"
"\n"
" -mip_linear: Keep image in linear light during mipmap filtering (i.e. do not\n"
" convert to/from sRGB for filtering purposes). (This is set automatically by\n"
" default, unless you override it.)\n"
"\n"
" -mip_scale X: Set mipmap filter kernel's scale, lower=sharper, higher=more\n"
" blurry, default is 1.0 (quite conservative).\n"
"\n"
" -mip_filter X: Set mipmap filter kernel, default is kaiser. Supported filters:\n"
" box, tent, bell, b-spline, mitchell, blackman, lanczos3, lanczos4, lanczos6,\n"
" lanczos12, kaiser, gaussian, catmullrom, quadratic_interp, quadratic_approx,\n"
" quadratic_mix\n"
"\n"
" -mip_renorm: Renormalize normal map to unit length vectors after filtering\n"
"\n"
" -mip_clamp: Use clamp addressing on borders, instead of wrapping\n"
"\n"
" -mip_fast: Use faster mipmap generation (resample from previous mip, not\n"
" always first/largest mip level). The default.\n"
"\n"
" -mip_slow: Always resample each mipmap level starting from the largest mipmap.\n"
" Higher quality, but slower. Opposite of -mip_fast. \n"
"\n"
" -mip_smallest X: Set smallest pixel dimension for generated mipmaps, default\n"
" is 1 pixel \n"
"\n"
"--- Transcoding Options (used while unpacking, validating after compression):\n"
"\n"
"These settings control the \"decode flags\" used while transcoding:\n"
"\n"
" -higher_quality_transcoding: Enable higher quality, but slower, transcoding\n"
"\n"
" -no_deblocking: Always disable adaptive deblocking filter on all block sizes\n"
" (XUASTC/ASTC LDR 4x4-12x12 only). By default only block sizes >8x6 are\n"
" deblocked while transcoding. (No deblocking ever occurs when transcoding to\n"
" ASTC: only when re-encoding ASTC to another format, to lower artifacts.)\n"
"\n"
" -force_deblocking: Always use adaptive deblocking filter, even for block sizes\n"
" <= 8x6 (XUASTC/ASTC LDR 4x4-12x12 only)\n"
"\n"
" -stronger_deblocking: Use stronger adaptive deblocking filtering (XUASTC/ASTC\n"
" LDR 4x4-12x12 only)\n"
"\n"
" -no_etc1s_chroma_filtering: Disable adaptive ETC1S transcode chroma filter,\n"
" for faster transcoding to BC7.\n"
"\n"
" -fast_xuastc_ldr_bc7_transcoding: Use much faster, but lower quality, XUASTC\n"
" LDR 4x4/6x6/8x6 direct BC7 transcoders (the default)\n"
"\n"
" -no_fast_xuastc_ldr_bc7_transcoding: Disable much faster, but slightly lower\n"
" quality, XUASTC LDR 4x4/6x6/8x6 direct BC7 transcoders\n"
"\n"
"--- Low-Level ETC1S backend endpoint/selector RDO codec options:\n"
"\n"
" -no_selector_rdo: Disable backend's selector rate distortion optimizations\n"
" (slightly faster, less noisy output, but lower quality per output bit)\n"
"\n"
" -selector_rdo_thresh X: Set selector RDO quality threshold, default is 1.25,\n"
" lower is higher quality but less quality per output bit (try 1.0-3.0)\n"
"\n"
" -no_endpoint_rdo: Disable backend's endpoint rate distortion optimizations\n"
" (slightly faster, less noisy output, but lower quality per output bit)\n"
"\n"
" -endpoint_rdo_thresh X: Set endpoint RDO quality threshold, default is 1.5,\n"
" lower is higher quality but less quality per output bit (try 1.0-3.0)\n"
"\n"
"--- Set various low-level fields in the Basis file header:\n"
"\n"
" -userdata0 X: Set 32-bit userdata0 field in Basis file header to X (X is a\n"
" signed 32-bit int)\n"
"\n"
" -userdata1 X: Set 32-bit userdata1 field in Basis file header to X (X is a\n"
" signed 32-bit int)\n"
"\n"
"--- Example LDR ETC1S/UASTC LDR 4x4 command lines:\n"
"\n"
" - basisu x.png : Compress sRGB image x.png to x.ktx2 using default settings\n"
" (multiple filenames OK, use -tex_array if you want a tex array vs. multiple\n"
" output files)\n"
"\n"
" - basisu -basis x.qoi : Compress sRGB image x.qoi to x.basis (supports 24-bit\n"
" or 32-bit .QOI files)\n"
"\n"
" - basisu x.ktx2 : Unpack x.basis to PNG/KTX files (multiple filenames OK)\n"
"\n"
" - basisu x.basis : Unpack x.basis to PNG/KTX files (multiple filenames OK)\n"
"\n"
" - basisu -uastc x.png -uastc_rdo_l 2.0 -ktx2 -stats : Compress to a\n"
" UASTC .KTX2 file with RDO (rate distortion optimization) to reduce .KTX2\n"
" compressed file size\n"
"\n"
" - basisu -file x.png -mipmap -y_flip : Compress a mipmapped x.ktx2 file from\n"
" an sRGB image named x.png, Y flip each source image\n"
"\n"
" - basisu -validate -file x.basis : Validate x.basis (check header, check file\n"
" CRC's, attempt to transcode all slices)\n"
"\n"
" - basisu -unpack -file x.basis : Validates, transcodes and unpacks x.basis to\n"
" mipmapped .KTX and RGB/A .PNG files (transcodes to all supported GPU texture\n"
" formats)\n"
"\n"
" - basisu -q 255 -file x.png -mipmap -debug -stats : Compress sRGB x.png to\n"
" x.ktx2 at quality level 255 with compressor debug output/statistics\n"
"\n"
" - basisu -linear -max_endpoints 16128 -max_selectors 16128 -file x.png :\n"
" Compress non-sRGB x.png to x.ktx2 using the largest supported manually\n"
" specified codebook sizes\n"
"\n"
" - basisu -basis -comp_level 2 -max_selectors 8192 -max_endpoints\n"
" 8192 -tex_type video -framerate 20 -multifile_printf\n"
" \"x%02u.png\" -multifile_first 1 -multifile_num 20 : Compress a 20 sRGB source\n"
" image video sequence (x01.png, x02.png, x03.png, etc.) to x01.basis\n"
"\n"
"--- Example UASTC HDR 4x4 command lines:\n"
"\n"
" - basisu x.exr : Compress a HDR .EXR (or .HDR) image to a UASTC HDR 4x4 .KTX2\n"
" file. LDR/SDR images will be upconverted to linear light HDR before\n"
" compression. See HDR upconversion options, above.\n"
"\n"
" - basisu -hdr_4x4 x.exr : Compress a HDR .EXR image to a UASTC HDR 4x4 .KTX2\n"
" file.\n"
"\n"
" - basisu x.hdr -uastc_level 0 : Compress a HDR .hdr image to a UASTC HDR\n"
" 4x4 .KTX2 file, fastest encoding but lowest quality\n"
"\n"
" - basisu -hdr x.png : Compress a LDR .PNG image to UASTC HDR 4x4 (image is\n"
" converted from sRGB to linear light first, use -hdr_ldr_no_srgb_to_linear to\n"
" disable)\n"
"\n"
" - basisu x.hdr -uastc_level 3 : Compress a HDR .hdr image to UASTC HDR 4x4 at\n"
" higher quality (-uastc_level 4 is highest quality, but very slow encoding)\n"
"\n"
" - basisu x.hdr -uastc_level 3 -mipmap -basis -stats -debug -debug_images :\n"
" Compress a HDR .hdr image to UASTC HDR 4x4, .basis output file, at higher\n"
" quality, generate mipmaps, output statistics and debug information, and write\n"
" tone mapped debug images\n"
"\n"
" - basisu x.hdr -stats -hdr_favor_astc -hdr_uber_mode -uastc_level 4 : Highest\n"
" achievable ASTC HDR 4x4 quality (very slow encoding, BC6H quality is traded\n"
" off)\n"
"\n"
"--- Example RDO ASTC/UASTC HDR 6x6 command lines:\n"
"\n"
" - basisu -hdr_6x6 x.exr : Compress a HDR .EXR (or .HDR) image to a UASTC HDR\n"
" 6x6 .KTX2 file. LDR/SDR images will be upconverted to linear light HDR before\n"
" compression. See HDR upconversion options, above.\n"
"\n"
" - basisu -lambda 1000 -hdr_6x6 x.exr : Compress a HDR .EXR (or .HDR) image to\n"
" a UASTC HDR 6x6 .KTX2 file with rate-distortion optimization (RDO), at lambda\n"
" level 1000.\n"
"\n"
" - basisu -hdr_6x6i x.exr : Compress a HDR .EXR image to a compressed\n"
" intermediate format UASTC HDR 6x6 .KTX2 file.\n"
"\n"
" - basisu -lambda 1000 -hdr_6x6i x.exr : Compress a HDR .EXR image to a\n"
" compressed intermediate format UASTC HDR 6x6 .KTX2 file with rate-distortion\n"
" optimization (RDO), at lambda level 1000.\n"
"\n"
"--- Example ASTC/XUASTC LDR 4x4-12x12 command lines:\n"
"\n"
" - basisu -ldr_6x6i -quality 75 -xuastc_arith test.png : Compress test.png to XUASTC\n"
" LDR 6x6 using weight grid DCT with setting 75 and the arith profile for\n"
" higher compression.\n"
"\n"
" - basisu -ldr_4x4 -effort 5 test.png : Compress test.png to ASTC LDR 4x4\n"
"\n"
" - basisu -mipmap -ldr_10x5i test.png : Compress test.png to XUASTC LDR 10x5,\n"
" using lossless ZStd supercompression, with mipmaps\n"
"\n"
"--- ETC1S Texture Video Notes: Use -comp_level 2 or higher for better codebook\n"
"generation, specify very large codebooks using -max_endpoints\n"
"and -max_selectors, and reduce the default endpoint RDO threshold\n"
"(-endpoint_rdo_thresh) to around 1.25. Videos may have mipmaps and alpha\n"
"channels. Videos must always be played back by the transcoder in first to last\n"
"image order. Video files currently use I-Frames on the first image, and\n"
"P-Frames using conditional replenishment on subsequent frames.\n"
"\n"
"--- Low-level ETC1S compression (Effort) Level (-comp_level X) Details \n"
"\n"
"This setting controls the ETC1S speed vs. quality tradeoff. (Use -q to control\n"
"the quality vs. compressed size tradeoff.):\n"
"\n"
" - Level 0: Fastest, but has marginal quality and can be brittle on complex\n"
"images. Avg. Y dB: 35.45\n"
"\n"
" - Level 1: Hierarchical codebook searching, faster ETC1S encoding. 36.87 dB,\n"
"~1.4x slower vs. level 0. (This is the default setting.)\n"
"\n"
" - Level 2: Use this or higher for video. Hierarchical codebook searching.\n"
"36.87 dB, ~1.4x slower vs. level 0. (This is the v1.12's default setting.)\n"
"\n"
" - Level 3: Full codebook searching. 37.13 dB, ~1.8x slower vs. level 0.\n"
"(Equivalent to the initial release's default settings.)\n"
"\n"
" - Level 4: Hierarchical codebook searching, codebook k-means iterations. 37.15\n"
"dB, ~4x slower vs. level 0\n"
"\n"
" - Level 5: Full codebook searching, codebook k-means iterations. 37.41 dB,\n"
"~5.5x slower vs. level 0.\n"
"\n"
" - Level 6: Full codebook searching, twice as many codebook k-means iterations,\n"
"best ETC1 endpoint opt. 37.43 dB, ~12x slower vs. level 0"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,45 @@
// File: android_astc_decomp.h
#ifndef _TCUASTCUTIL_HPP
#define _TCUASTCUTIL_HPP
/*-------------------------------------------------------------------------
* drawElements Quality Program Tester Core
* ----------------------------------------
*
* Copyright 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief ASTC Utilities.
*//*--------------------------------------------------------------------*/
#include <vector>
#include <stdint.h>
namespace basisu_astc
{
namespace astc
{
// Unpacks a single ASTC block to pDst
// If isSRGB is true, the spec requires the decoder to scale the LDR 8-bit endpoints to 16-bit before interpolation slightly differently,
// which will lead to different outputs. So be sure to set it correctly (ideally it should match whatever the encoder did).
bool decompress_ldr(uint8_t* pDst, const uint8_t* data, bool isSRGB, int blockWidth, int blockHeight);
bool decompress_hdr(float* pDstRGBA, const uint8_t* data, int blockWidth, int blockHeight);
bool is_hdr(const uint8_t* data, int blockWidth, int blockHeight, bool& is_hdr);
} // astc
} // basisu
#endif
+676
View File
@@ -0,0 +1,676 @@
/*
Copyright (c) 2021, Dominic Szablewski - https://phoboslab.org
SPDX-License-Identifier: MIT
QOI - The "Quite OK Image" format for fast, lossless image compression
-- About
QOI encodes and decodes images in a lossless format. Compared to stb_image and
stb_image_write QOI offers 20x-50x faster encoding, 3x-4x faster decoding and
20% better compression.
-- Synopsis
// Define `QOI_IMPLEMENTATION` in *one* C/C++ file before including this
// library to create the implementation.
#define QOI_IMPLEMENTATION
#include "qoi.h"
// Encode and store an RGBA buffer to the file system. The qoi_desc describes
// the input pixel data.
qoi_write("image_new.qoi", rgba_pixels, &(qoi_desc){
.width = 1920,
.height = 1080,
.channels = 4,
.colorspace = QOI_SRGB
});
// Load and decode a QOI image from the file system into a 32bbp RGBA buffer.
// The qoi_desc struct will be filled with the width, height, number of channels
// and colorspace read from the file header.
qoi_desc desc;
void *rgba_pixels = qoi_read("image.qoi", &desc, 4);
-- Documentation
This library provides the following functions;
- qoi_read -- read and decode a QOI file
- qoi_decode -- decode the raw bytes of a QOI image from memory
- qoi_write -- encode and write a QOI file
- qoi_encode -- encode an rgba buffer into a QOI image in memory
See the function declaration below for the signature and more information.
If you don't want/need the qoi_read and qoi_write functions, you can define
QOI_NO_STDIO before including this library.
This library uses malloc() and free(). To supply your own malloc implementation
you can define QOI_MALLOC and QOI_FREE before including this library.
This library uses memset() to zero-initialize the index. To supply your own
implementation you can define QOI_ZEROARR before including this library.
-- Data Format
A QOI file has a 14 byte header, followed by any number of data "chunks" and an
8-byte end marker.
struct qoi_header_t {
char magic[4]; // magic bytes "qoif"
uint32_t width; // image width in pixels (BE)
uint32_t height; // image height in pixels (BE)
uint8_t channels; // 3 = RGB, 4 = RGBA
uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
};
Images are encoded row by row, left to right, top to bottom. The decoder and
encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An
image is complete when all pixels specified by width * height have been covered.
Pixels are encoded as
- a run of the previous pixel
- an index into an array of previously seen pixels
- a difference to the previous pixel value in r,g,b
- full r,g,b or r,g,b,a values
The color channels are assumed to not be premultiplied with the alpha channel
("un-premultiplied alpha").
A running array[64] (zero-initialized) of previously seen pixel values is
maintained by the encoder and decoder. Each pixel that is seen by the encoder
and decoder is put into this array at the position formed by a hash function of
the color value. In the encoder, if the pixel value at the index matches the
current pixel, this index position is written to the stream as QOI_OP_INDEX.
The hash function for the index is:
index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64
Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The
bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All
values encoded in these data bits have the most significant bit on the left.
The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the
presence of an 8-bit tag first.
The byte stream's end is marked with 7 0x00 bytes followed a single 0x01 byte.
The possible chunks are:
.- QOI_OP_INDEX ----------.
| Byte[0] |
| 7 6 5 4 3 2 1 0 |
|-------+-----------------|
| 0 0 | index |
`-------------------------`
2-bit tag b00
6-bit index into the color index array: 0..63
A valid encoder must not issue 2 or more consecutive QOI_OP_INDEX chunks to the
same index. QOI_OP_RUN should be used instead.
.- QOI_OP_DIFF -----------.
| Byte[0] |
| 7 6 5 4 3 2 1 0 |
|-------+-----+-----+-----|
| 0 1 | dr | dg | db |
`-------------------------`
2-bit tag b01
2-bit red channel difference from the previous pixel between -2..1
2-bit green channel difference from the previous pixel between -2..1
2-bit blue channel difference from the previous pixel between -2..1
The difference to the current channel values are using a wraparound operation,
so "1 - 2" will result in 255, while "255 + 1" will result in 0.
Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as
0 (b00). 1 is stored as 3 (b11).
The alpha value remains unchanged from the previous pixel.
.- QOI_OP_LUMA -------------------------------------.
| Byte[0] | Byte[1] |
| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 |
|-------+-----------------+-------------+-----------|
| 1 0 | green diff | dr - dg | db - dg |
`---------------------------------------------------`
2-bit tag b10
6-bit green channel difference from the previous pixel -32..31
4-bit red channel difference minus green channel difference -8..7
4-bit blue channel difference minus green channel difference -8..7
The green channel is used to indicate the general direction of change and is
encoded in 6 bits. The red and blue channels (dr and db) base their diffs off
of the green channel difference and are encoded in 4 bits. I.e.:
dr_dg = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g)
db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_px.g)
The difference to the current channel values are using a wraparound operation,
so "10 - 13" will result in 253, while "250 + 7" will result in 1.
Values are stored as unsigned integers with a bias of 32 for the green channel
and a bias of 8 for the red and blue channel.
The alpha value remains unchanged from the previous pixel.
.- QOI_OP_RUN ------------.
| Byte[0] |
| 7 6 5 4 3 2 1 0 |
|-------+-----------------|
| 1 1 | run |
`-------------------------`
2-bit tag b11
6-bit run-length repeating the previous pixel: 1..62
The run-length is stored with a bias of -1. Note that the run-lengths 63 and 64
(b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and
QOI_OP_RGBA tags.
.- QOI_OP_RGB ------------------------------------------.
| Byte[0] | Byte[1] | Byte[2] | Byte[3] |
| 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 |
|-------------------------+---------+---------+---------|
| 1 1 1 1 1 1 1 0 | red | green | blue |
`-------------------------------------------------------`
8-bit tag b11111110
8-bit red channel value
8-bit green channel value
8-bit blue channel value
The alpha value remains unchanged from the previous pixel.
.- QOI_OP_RGBA ---------------------------------------------------.
| Byte[0] | Byte[1] | Byte[2] | Byte[3] | Byte[4] |
| 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 |
|-------------------------+---------+---------+---------+---------|
| 1 1 1 1 1 1 1 1 | red | green | blue | alpha |
`-----------------------------------------------------------------`
8-bit tag b11111111
8-bit red channel value
8-bit green channel value
8-bit blue channel value
8-bit alpha channel value
*/
/* -----------------------------------------------------------------------------
Header - Public functions */
#ifndef QOI_H
#define QOI_H
#ifdef __cplusplus
extern "C" {
#endif
/* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions.
It describes either the input format (for qoi_write and qoi_encode), or is
filled with the description read from the file header (for qoi_read and
qoi_decode).
The colorspace in this qoi_desc is an enum where
0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel
1 = all channels are linear
You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely
informative. It will be saved to the file header, but does not affect
how chunks are en-/decoded. */
#define QOI_SRGB 0
#define QOI_LINEAR 1
typedef struct {
unsigned int width;
unsigned int height;
unsigned char channels;
unsigned char colorspace;
} qoi_desc;
#ifndef QOI_NO_STDIO
/* Encode raw RGB or RGBA pixels into a QOI image and write it to the file
system. The qoi_desc struct must be filled with the image width, height,
number of channels (3 = RGB, 4 = RGBA) and the colorspace.
The function returns 0 on failure (invalid parameters, or fopen or malloc
failed) or the number of bytes written on success. */
int qoi_write(const char *filename, const void *data, const qoi_desc *desc);
/* Read and decode a QOI image from the file system. If channels is 0, the
number of channels from the file header is used. If channels is 3 or 4 the
output format will be forced into this number of channels.
The function either returns NULL on failure (invalid data, or malloc or fopen
failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
will be filled with the description from the file header.
The returned pixel data should be free()d after use. */
void *qoi_read(const char *filename, qoi_desc *desc, int channels);
#endif /* QOI_NO_STDIO */
/* Encode raw RGB or RGBA pixels into a QOI image in memory.
The function either returns NULL on failure (invalid parameters or malloc
failed) or a pointer to the encoded data on success. On success the out_len
is set to the size in bytes of the encoded data.
The returned qoi data should be free()d after use. */
void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len);
/* Decode a QOI image from memory.
The function either returns NULL on failure (invalid parameters or malloc
failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
is filled with the description from the file header.
The returned pixel data should be free()d after use. */
void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels);
#ifdef __cplusplus
}
#endif
#endif /* QOI_H */
/* -----------------------------------------------------------------------------
Implementation */
#ifdef QOI_IMPLEMENTATION
#include <stdlib.h>
#include <string.h>
#ifndef QOI_MALLOC
#define QOI_MALLOC(sz) malloc(sz)
#define QOI_FREE(p) free(p)
#endif
#ifndef QOI_ZEROARR
#define QOI_ZEROARR(a) memset((a),0,sizeof(a))
#endif
#define QOI_OP_INDEX 0x00 /* 00xxxxxx */
#define QOI_OP_DIFF 0x40 /* 01xxxxxx */
#define QOI_OP_LUMA 0x80 /* 10xxxxxx */
#define QOI_OP_RUN 0xc0 /* 11xxxxxx */
#define QOI_OP_RGB 0xfe /* 11111110 */
#define QOI_OP_RGBA 0xff /* 11111111 */
#define QOI_MASK_2 0xc0 /* 11000000 */
#define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11)
#define QOI_MAGIC \
(((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
((unsigned int)'i') << 8 | ((unsigned int)'f'))
#define QOI_HEADER_SIZE 14
/* 2GB is the max file size that this implementation can safely handle. We guard
against anything larger than that, assuming the worst case with 5 bytes per
pixel, rounded down to a nice clean value. 400 million pixels ought to be
enough for anybody. */
#define QOI_PIXELS_MAX ((unsigned int)400000000)
typedef union {
struct { unsigned char r, g, b, a; } rgba;
unsigned int v;
} qoi_rgba_t;
static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1};
static void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) {
bytes[(*p)++] = (uint8_t)((0xff000000 & v) >> 24);
bytes[(*p)++] = (uint8_t)((0x00ff0000 & v) >> 16);
bytes[(*p)++] = (uint8_t)((0x0000ff00 & v) >> 8);
bytes[(*p)++] = (uint8_t)((0x000000ff & v));
}
static unsigned int qoi_read_32(const unsigned char *bytes, size_t *p) {
unsigned int a = bytes[(*p)++];
unsigned int b = bytes[(*p)++];
unsigned int c = bytes[(*p)++];
unsigned int d = bytes[(*p)++];
return a << 24 | b << 16 | c << 8 | d;
}
void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
int i, max_size, p, run;
int px_len, px_end, px_pos, channels;
unsigned char *bytes;
const unsigned char *pixels;
qoi_rgba_t index[64];
qoi_rgba_t px, px_prev;
if (
data == NULL || out_len == NULL || desc == NULL ||
desc->width == 0 || desc->height == 0 ||
desc->channels < 3 || desc->channels > 4 ||
desc->colorspace > 1 ||
desc->height >= QOI_PIXELS_MAX / desc->width
) {
return NULL;
}
max_size =
desc->width * desc->height * (desc->channels + 1) +
QOI_HEADER_SIZE + sizeof(qoi_padding);
p = 0;
bytes = (unsigned char *) QOI_MALLOC(max_size);
if (!bytes) {
return NULL;
}
qoi_write_32(bytes, &p, QOI_MAGIC);
qoi_write_32(bytes, &p, desc->width);
qoi_write_32(bytes, &p, desc->height);
bytes[p++] = desc->channels;
bytes[p++] = desc->colorspace;
pixels = (const unsigned char *)data;
QOI_ZEROARR(index);
run = 0;
px_prev.rgba.r = 0;
px_prev.rgba.g = 0;
px_prev.rgba.b = 0;
px_prev.rgba.a = 255;
px = px_prev;
px_len = desc->width * desc->height * desc->channels;
px_end = px_len - desc->channels;
channels = desc->channels;
for (px_pos = 0; px_pos < px_len; px_pos += channels) {
px.rgba.r = pixels[px_pos + 0];
px.rgba.g = pixels[px_pos + 1];
px.rgba.b = pixels[px_pos + 2];
if (channels == 4) {
px.rgba.a = pixels[px_pos + 3];
}
if (px.v == px_prev.v) {
run++;
if (run == 62 || px_pos == px_end) {
bytes[p++] = (uint8_t)(QOI_OP_RUN | (run - 1));
run = 0;
}
}
else {
int index_pos;
if (run > 0) {
bytes[p++] = (uint8_t)(QOI_OP_RUN | (run - 1));
run = 0;
}
index_pos = QOI_COLOR_HASH(px) % 64;
if (index[index_pos].v == px.v) {
bytes[p++] = (uint8_t)(QOI_OP_INDEX | index_pos);
}
else {
index[index_pos] = px;
if (px.rgba.a == px_prev.rgba.a) {
signed char vr = px.rgba.r - px_prev.rgba.r;
signed char vg = px.rgba.g - px_prev.rgba.g;
signed char vb = px.rgba.b - px_prev.rgba.b;
signed char vg_r = vr - vg;
signed char vg_b = vb - vg;
if (
vr > -3 && vr < 2 &&
vg > -3 && vg < 2 &&
vb > -3 && vb < 2
) {
bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
}
else if (
vg_r > -9 && vg_r < 8 &&
vg > -33 && vg < 32 &&
vg_b > -9 && vg_b < 8
) {
bytes[p++] = QOI_OP_LUMA | (vg + 32);
bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8);
}
else {
bytes[p++] = QOI_OP_RGB;
bytes[p++] = px.rgba.r;
bytes[p++] = px.rgba.g;
bytes[p++] = px.rgba.b;
}
}
else {
bytes[p++] = QOI_OP_RGBA;
bytes[p++] = px.rgba.r;
bytes[p++] = px.rgba.g;
bytes[p++] = px.rgba.b;
bytes[p++] = px.rgba.a;
}
}
}
px_prev = px;
}
for (i = 0; i < (int)sizeof(qoi_padding); i++) {
bytes[p++] = qoi_padding[i];
}
*out_len = p;
return bytes;
}
void *qoi_decode(const void *data, size_t size, qoi_desc *desc, int channels) {
const unsigned char *bytes;
unsigned int header_magic;
unsigned char *pixels;
qoi_rgba_t index[64];
qoi_rgba_t px;
int px_len, px_pos;
size_t chunks_len, p = 0;
int run = 0;
if (
data == NULL || desc == NULL ||
(channels != 0 && channels != 3 && channels != 4) ||
size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding)
) {
return NULL;
}
bytes = (const unsigned char *)data;
header_magic = qoi_read_32(bytes, &p);
desc->width = qoi_read_32(bytes, &p);
desc->height = qoi_read_32(bytes, &p);
desc->channels = bytes[p++];
desc->colorspace = bytes[p++];
if (
desc->width == 0 || desc->height == 0 ||
desc->channels < 3 || desc->channels > 4 ||
desc->colorspace > 1 ||
header_magic != QOI_MAGIC ||
desc->height >= QOI_PIXELS_MAX / desc->width
) {
return NULL;
}
if (channels == 0) {
channels = desc->channels;
}
px_len = desc->width * desc->height * channels;
pixels = (unsigned char *) QOI_MALLOC(px_len);
if (!pixels) {
return NULL;
}
QOI_ZEROARR(index);
px.rgba.r = 0;
px.rgba.g = 0;
px.rgba.b = 0;
px.rgba.a = 255;
chunks_len = size - (int)sizeof(qoi_padding);
for (px_pos = 0; px_pos < px_len; px_pos += channels) {
if (run > 0) {
run--;
}
else if (p < chunks_len) {
int b1 = bytes[p++];
if (b1 == QOI_OP_RGB) {
px.rgba.r = bytes[p++];
px.rgba.g = bytes[p++];
px.rgba.b = bytes[p++];
}
else if (b1 == QOI_OP_RGBA) {
px.rgba.r = bytes[p++];
px.rgba.g = bytes[p++];
px.rgba.b = bytes[p++];
px.rgba.a = bytes[p++];
}
else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) {
px = index[b1];
}
else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) {
px.rgba.r += ((b1 >> 4) & 0x03) - 2;
px.rgba.g += ((b1 >> 2) & 0x03) - 2;
px.rgba.b += ( b1 & 0x03) - 2;
}
else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) {
int b2 = bytes[p++];
int vg = (b1 & 0x3f) - 32;
px.rgba.r += (uint8_t)(vg - 8 + ((b2 >> 4) & 0x0f));
px.rgba.g += (uint8_t)(vg);
px.rgba.b += (uint8_t)(vg - 8 + (b2 & 0x0f));
}
else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) {
run = (b1 & 0x3f);
}
index[QOI_COLOR_HASH(px) % 64] = px;
}
pixels[px_pos + 0] = px.rgba.r;
pixels[px_pos + 1] = px.rgba.g;
pixels[px_pos + 2] = px.rgba.b;
if (channels == 4) {
pixels[px_pos + 3] = px.rgba.a;
}
}
return pixels;
}
#ifndef QOI_NO_STDIO
#include <stdio.h>
int qoi_write(const char *filename, const void *data, const qoi_desc *desc) {
#ifdef _MSC_VER
FILE* f = NULL;
fopen_s(&f, filename, "wb");
#else
FILE *f = fopen(filename, "wb");
#endif
int size, err;
void *encoded;
if (!f) {
return 0;
}
encoded = qoi_encode(data, desc, &size);
if (!encoded) {
fclose(f);
return 0;
}
fwrite(encoded, 1, size, f);
fflush(f);
err = ferror(f);
fclose(f);
QOI_FREE(encoded);
return err ? 0 : size;
}
void *qoi_read(const char *filename, qoi_desc *desc, int channels) {
#ifdef _MSC_VER
FILE* f = NULL;
fopen_s(&f, filename, "rb");
#else
FILE *f = fopen(filename, "rb");
#endif
size_t size, bytes_read;
void *pixels, *data;
long sz;
if (!f) {
return NULL;
}
fseek(f, 0, SEEK_END);
sz = ftell(f);
if (sz <= 0)
{
fclose(f);
return NULL;
}
size = (size_t)sz;
if (size != (unsigned long)sz)
{
fclose(f);
return NULL;
}
if (fseek(f, 0, SEEK_SET) != 0) {
fclose(f);
return NULL;
}
data = QOI_MALLOC(size);
if (!data) {
fclose(f);
return NULL;
}
bytes_read = (int)fread(data, 1, size, f);
fclose(f);
pixels = (bytes_read != size) ? NULL : qoi_decode(data, bytes_read, desc, channels);
QOI_FREE(data);
return pixels;
}
#endif /* QOI_NO_STDIO */
#endif /* QOI_IMPLEMENTATION */
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
#if defined(_WIN32)
#ifndef NOMINMAX
#define NOMINMAX
#endif
#endif
#ifdef _MSC_VER
#pragma warning (disable:4530) // warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
#endif
#define TINYEXR_IMPLEMENTATION
#include "tinyexr.h"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+139
View File
@@ -0,0 +1,139 @@
// File: basisu_astc_hdr_6x6_enc.h
#pragma once
#include "basisu_enc.h"
#include "../transcoder/basisu_astc_hdr_core.h"
namespace astc_6x6_hdr
{
const uint32_t ASTC_HDR_6X6_DEF_USER_COMP_LEVEL = 2;
const uint32_t ASTC_HDR_6X6_MAX_USER_COMP_LEVEL = 12;
const uint32_t ASTC_HDR_6X6_MAX_COMP_LEVEL = 4;
const float LDR_BLACK_BIAS = 0.0f;// .49f;
// Note: This struct is copied several times, so do not place any heavyweight objects in here.
struct astc_hdr_6x6_global_config
{
// Important: The Delta ITP colorspace error metric we use internally makes several assumptions about the nature of the HDR RGB inputs supplied to the encoder.
// This encoder computes colorspace error in the ICtCp (or more accurately the delta E ITP, where CT is scaled by .5 vs. ICtCp to become T) colorspace, so getting this correct is important.
// By default the encoder assumes the input is in absolute luminance (in nits or candela per square meter, cd/m^2), specified as positive-only linear light RGB, using the REC 709 colorspace gamut (but NOT the sRGB transfer function, i.e. linear light).
// If the m_rec2020_bt2100_color_gamut flag is true, the input colorspace is treated as REC 2020/BT.2100 (which is wider than 709).
// For SDR/LDR->HDR upconversion, the REC 709 sRGB input should be converted to linear light (sRGB->linear) and the resulting normalized linear RGB values scaled by either 80 or 100 nits (the luminance of a typical SDR monitor).
// SDR upconversion to normalized [0,1] (i.e. non-absolute) luminances may work but is not supported because ITP errors will not be predicted correctly.
// 11/3/2025: This flag is always copied straight into the output KTX2 DFD colorspace, even for non-HDR formats.
// TODO: Move this parameter to reflect this.
bool m_rec2020_bt2100_color_gamut = false;
// levels 0-3 normal levels, 4=exhaustive
uint32_t m_master_comp_level = 0;
uint32_t m_highest_comp_level = 1;
float m_lambda = 0.0f;
bool m_extra_patterns_flag = false; // def to false, works in comp levels [1,4]
bool m_brute_force_partition_matching = false; // def to false
bool m_jnd_optimization = false; // defaults to false for HDR inputs, on SDR upconverted images this can default to enabled
float m_jnd_delta_itp_thresh = .75f;
bool m_force_one_strip = false;
bool m_gaussian1_fallback = true; // def to true, if this is disabled m_gaussian2_fallback should be disabled too
float m_gaussian1_strength = 1.45f;
bool m_gaussian2_fallback = true; // def to true, hopefully rarely kicks in
float m_gaussian2_strength = 1.83f;
// m_disable_delta_endpoint_usage may give a slight increase in RDO ASTC encoding efficiency. It's also faster.
bool m_disable_delta_endpoint_usage = false;
// Scale up Delta ITP errors for very dark pixels, assuming they will be brightly exposed > 1.0x.
// We don't know if the output will be exposed, or not. If heavily exposed, our JND calculations will not be conservative enough.
bool m_delta_itp_dark_adjustment = true;
bool m_debug_images = false;
std::string m_debug_image_prefix = "dbg_astc_hdr_6x6_devel_";
bool m_output_images = false;
std::string m_output_image_prefix = "dbg_astc_hdr_6x6_output_";
bool m_debug_output = false;
bool m_image_stats = false;
bool m_status_output = false;
//-------------------------------------------------------------------------------------
// Very low level/devel parameters - intended for development. Best not to change them.
//-------------------------------------------------------------------------------------
bool m_deblocking_flag = true;
float m_deblock_penalty_weight = .03f;
bool m_disable_twothree_subsets = false; // def to false
bool m_use_solid_blocks = true; // def to true
bool m_use_runs = true; // def to true
bool m_block_stat_optimizations_flag = true; // def to true
bool m_rdo_candidate_diversity_boost = true; // def to true
float m_rdo_candidate_diversity_boost_bit_window_weight = 1.2f;
bool m_favor_higher_compression = true; // utilize all modes
uint32_t m_num_reuse_xy_deltas = basist::astc_6x6_hdr::NUM_REUSE_XY_DELTAS;
// By default, for compatibility with KTX-Software (which uses v1.60), we write v1.6 compatible UASTC HDR 6x6i files.
// The transcoder is compatible with both variants. This setting impacts how 2x2 blocks are upsampled and the initial marker version.
// Eventually once KTX-Software upgrades to the latest version of basisu this will be defaulted to false.
// If this is false a v2.0 or later transcoder is required for UASTC HDR 6x6i.
bool m_write_basisu_1_6_compatible_files = true;
void print() const
{
basisu::fmt_debug_printf(" m_master_comp_level: {}, m_highest_comp_level: {}\n", m_master_comp_level, m_highest_comp_level);
basisu::fmt_debug_printf(" m_lambda: {}\n", m_lambda);
basisu::fmt_debug_printf(" m_rec2020_bt2100_color_gamut: {}\n", m_rec2020_bt2100_color_gamut);
basisu::fmt_debug_printf(" m_extra_patterns_flag: {}, m_brute_force_partition_matching: {}\n", m_extra_patterns_flag, m_brute_force_partition_matching);
basisu::fmt_debug_printf(" m_jnd_optimization: {}, m_jnd_delta_itp_thresh: {}\n", m_jnd_optimization, m_jnd_delta_itp_thresh);
basisu::fmt_debug_printf(" m_force_one_strip: {}\n", m_force_one_strip);
basisu::fmt_debug_printf(" m_gaussian1_fallback: {}, m_gaussian1_strength: {}\n", m_gaussian1_fallback, m_gaussian1_strength);
basisu::fmt_debug_printf(" m_gaussian2_fallback: {}, m_gaussian2_strength: {}\n", m_gaussian2_fallback, m_gaussian2_strength);
basisu::fmt_debug_printf(" m_disable_delta_endpoint_usage: {}\n", m_disable_delta_endpoint_usage);
basisu::fmt_debug_printf(" m_delta_itp_dark_adjustment: {}\n", m_delta_itp_dark_adjustment);
basisu::fmt_debug_printf(" m_debug_images: {}, m_debug_image_prefix: {}\n", m_debug_images, m_debug_image_prefix);
basisu::fmt_debug_printf(" m_output_images: {}, m_output_image_prefix: {}\n", m_output_images, m_output_image_prefix);
basisu::fmt_debug_printf(" m_image_stats: {}, m_status_output: {}\n", m_image_stats, m_status_output);
basisu::fmt_debug_printf(" m_deblocking_flag: {}, m_deblock_penalty_weight: {}\n", m_deblocking_flag, m_deblock_penalty_weight);
basisu::fmt_debug_printf(" m_disable_twothree_subsets: {}, m_use_solid_blocks: {}\n", m_disable_twothree_subsets, m_use_solid_blocks);
basisu::fmt_debug_printf(" m_use_runs: {}, m_block_stat_optimizations_flag: {}\n", m_use_runs, m_block_stat_optimizations_flag);
basisu::fmt_debug_printf(" m_rdo_candidate_diversity_boost: {}, m_rdo_candidate_diversity_boost_bit_window_weight: {}\n", m_rdo_candidate_diversity_boost, m_rdo_candidate_diversity_boost_bit_window_weight);
basisu::fmt_debug_printf(" m_favor_higher_compression: {}, m_num_reuse_xy_deltas: {}\n", m_favor_higher_compression, m_num_reuse_xy_deltas);
basisu::fmt_debug_printf(" m_write_basisu_1_6_compatible_files: {}\n", m_write_basisu_1_6_compatible_files);
}
astc_hdr_6x6_global_config()
{
}
void clear()
{
astc_hdr_6x6_global_config def;
std::swap(*this, def);
}
// Max level is ASTC_HDR_6X6_MAX_USER_COMP_LEVEL
void set_user_level(int level);
};
void global_init();
struct result_metrics
{
basisu::image_metrics m_im_astc_log2;
basisu::image_metrics m_im_astc_half;
basisu::image_metrics m_im_bc6h_log2;
basisu::image_metrics m_im_bc6h_half;
};
// The input image should be unpadded to 6x6 boundaries, i.e. the original unexpanded image.
bool compress_photo(const basisu::imagef& orig_src_img, const astc_hdr_6x6_global_config& global_cfg, basisu::job_pool* pJob_pool,
basisu::uint8_vec& intermediate_tex_data, basisu::uint8_vec& astc_tex_data, result_metrics& metrics);
} // namespace uastc_6x6_hdr
File diff suppressed because it is too large Load Diff
+433
View File
@@ -0,0 +1,433 @@
// File: basisu_astc_hdr_common.h
#pragma once
#include "basisu_enc.h"
#include "basisu_gpu_texture.h"
#include "../transcoder/basisu_astc_helpers.h"
#include "../transcoder/basisu_astc_hdr_core.h"
namespace basisu
{
const uint32_t MAX_ASTC_HDR_BLOCK_W = 6, MAX_ASTC_HDR_BLOCK_H = 6;
const uint32_t MAX_ASTC_HDR_ENC_BLOCK_PIXELS = 6 * 6;
const uint32_t MODE11_TOTAL_SUBMODES = 8; // plus an extra hidden submode, directly encoded, for direct, so really 9 (see tables 99/100 of the ASTC spec)
const uint32_t MODE7_TOTAL_SUBMODES = 6;
// [ise_range][0] = # levels
// [ise_range][1...] = lerp value [0,64]
// in ASTC order
// Supported ISE weight ranges: 0 to 11, 12 total
const uint32_t MIN_SUPPORTED_ISE_WEIGHT_INDEX = astc_helpers::BISE_2_LEVELS; // ISE 0=2 levels
const uint32_t MAX_SUPPORTED_ISE_WEIGHT_INDEX = astc_helpers::BISE_32_LEVELS; // ISE 11=32 levels
const uint32_t MIN_SUPPORTED_WEIGHT_LEVELS = 2;
const uint32_t MAX_SUPPORTED_WEIGHT_LEVELS = 32;
extern const uint8_t g_ise_weight_lerps[MAX_SUPPORTED_ISE_WEIGHT_INDEX + 1][33];
const float Q_LOG_BIAS_4x4 = .125f; // the original UASTC HDR 4x4 log bias
const float Q_LOG_BIAS_6x6 = 1.0f; // the log bias both encoders use now
const float LDR_TO_HDR_NITS = 100.0f;
extern vec4F g_astc_ls_weights_ise[MAX_SUPPORTED_ISE_WEIGHT_INDEX + 1][MAX_SUPPORTED_WEIGHT_LEVELS];
extern uint8_t g_map_astc_to_linear_order[MAX_SUPPORTED_ISE_WEIGHT_INDEX + 1][MAX_SUPPORTED_WEIGHT_LEVELS]; // [ise_range][astc_index] -> linear index
extern uint8_t g_map_linear_to_astc_order[MAX_SUPPORTED_ISE_WEIGHT_INDEX + 1][MAX_SUPPORTED_WEIGHT_LEVELS]; // [ise_range][linear_index] -> astc_index
struct astc_hdr_codec_base_options
{
float m_r_err_scale, m_g_err_scale;
float m_q_log_bias;
bool m_ultra_quant;
// If true, the ASTC HDR compressor is allowed to more aggressively vary weight indices for slightly higher compression in non-fastest mode. This will hurt BC6H quality, however.
bool m_allow_uber_mode;
bool m_mode7_full_s_optimization;
bool m_take_first_non_clamping_mode11_submode;
bool m_take_first_non_clamping_mode7_submode;
bool m_disable_weight_plane_optimization;
astc_hdr_codec_base_options() { init(); }
void init();
};
inline int get_bit(
int src_val, int src_bit)
{
assert(src_bit >= 0 && src_bit <= 31);
int bit = (src_val >> src_bit) & 1;
return bit;
}
inline void pack_bit(
int& dst, int dst_bit,
int src_val, int src_bit = 0)
{
assert(dst_bit >= 0 && dst_bit <= 31);
int bit = get_bit(src_val, src_bit);
dst |= (bit << dst_bit);
}
inline uint32_t get_max_qlog(uint32_t bits)
{
switch (bits)
{
case 7: return basist::MAX_QLOG7;
case 8: return basist::MAX_QLOG8;
case 9: return basist::MAX_QLOG9;
case 10: return basist::MAX_QLOG10;
case 11: return basist::MAX_QLOG11;
case 12: return basist::MAX_QLOG12;
case 16: return basist::MAX_QLOG16;
default: assert(0); break;
}
return 0;
}
#if 0
inline float get_max_qlog_val(uint32_t bits)
{
switch (bits)
{
case 7: return MAX_QLOG7_VAL;
case 8: return MAX_QLOG8_VAL;
case 9: return MAX_QLOG9_VAL;
case 10: return MAX_QLOG10_VAL;
case 11: return MAX_QLOG11_VAL;
case 12: return MAX_QLOG12_VAL;
case 16: return MAX_QLOG16_VAL;
default: assert(0); break;
}
return 0;
}
#endif
#if 0
// Input is the low 11 bits of the qlog
// Returns the 10-bit mantissa of the half float value
int qlog11_to_half_float_mantissa(int M)
{
assert(M <= 0x7FF);
int Mt;
if (M < 512)
Mt = 3 * M;
else if (M >= 1536)
Mt = 5 * M - 2048;
else
Mt = 4 * M - 512;
return (Mt >> 3);
}
#endif
// Input is the 10-bit mantissa of the half float value
// Output is the 11-bit qlog value
// Inverse of qlog11_to_half_float_mantissa()
inline int half_float_mantissa_to_qlog11(int hf)
{
int q0 = (hf * 8 + 2) / 3;
int q1 = (hf * 8 + 2048 + 4) / 5;
if (q0 < 512)
return q0;
else if (q1 >= 1536)
return q1;
int q2 = (hf * 8 + 512 + 2) / 4;
return q2;
}
inline int half_to_qlog16(int hf)
{
assert(!basist::half_is_signed((basist::half_float)hf) && !basist::is_half_inf_or_nan((basist::half_float)hf));
// extract 5 bits exponent, which is carried through to qlog16 unchanged
const int exp = (hf >> 10) & 0x1F;
// extract and invert the 10 bit mantissa to nearest qlog11 (should be lossless)
const int mantissa = half_float_mantissa_to_qlog11(hf & 0x3FF);
assert(mantissa <= 0x7FF);
// Now combine to qlog16, which is what ASTC HDR interpolates using the [0-64] weights.
uint32_t qlog16 = (exp << 11) | mantissa;
// should be a lossless operation
assert(astc_helpers::qlog16_to_half(qlog16) == hf);
return qlog16;
}
void interpolate_qlog12_colors(
const int e[2][3],
basist::half_float* pDecoded_half,
vec3F* pDecoded_float,
uint32_t n, uint32_t ise_weight_range);
bool get_astc_hdr_mode_11_block_colors(
const uint8_t* pEndpoints,
basist::half_float* pDecoded_half,
vec3F* pDecoded_float,
uint32_t n, uint32_t ise_weight_range, uint32_t ise_endpoint_range);
bool get_astc_hdr_mode_7_block_colors(
const uint8_t* pEndpoints,
basist::half_float* pDecoded_half,
vec3F* pDecoded_float,
uint32_t n, uint32_t ise_weight_range, uint32_t ise_endpoint_range);
// Fast high precision piecewise linear approximation of log2(bias+x).
// Half may be zero, positive or denormal. No NaN/Inf/negative.
BASISU_FORCE_INLINE double q(basist::half_float x, float log_bias)
{
union { float f; int32_t i; uint32_t u; } fi;
fi.f = fast_half_to_float_pos_not_inf_or_nan(x);
assert(fi.f >= 0.0f);
fi.f += log_bias;
return (double)fi.u; // approx log2f(fi.f), need to return double for the precision
}
BASISU_FORCE_INLINE uint32_t q2(basist::half_float x, float log_bias)
{
union { float f; int32_t i; uint32_t u; } fi;
fi.f = fast_half_to_float_pos_not_inf_or_nan(x);
assert(fi.f >= 0.0f);
fi.f += log_bias;
return fi.u;
}
double eval_selectors(
uint32_t num_pixels,
uint8_t* pWeights,
uint32_t ise_weight_range,
const basist::half_float* pBlock_pixels_half,
uint32_t num_weight_levels,
const basist::half_float* pDecoded_half,
const astc_hdr_codec_base_options& coptions,
uint32_t usable_selector_bitmask = UINT32_MAX);
double eval_selectors_dual_plane(
uint32_t channel_index,
uint32_t num_pixels,
uint8_t* pWeights0, uint8_t* pWeights1,
const basist::half_float* pBlock_pixels_half,
uint32_t num_weight_levels,
const basist::half_float* pDecoded_half,
const astc_hdr_codec_base_options& coptions,
uint32_t usable_selector_bitmask = UINT32_MAX);
double compute_block_error(uint32_t num_pixels, const basist::half_float* pOrig_block, const basist::half_float* pPacked_block, const astc_hdr_codec_base_options& coptions);
const uint32_t FIRST_MODE7_SUBMODE_INDEX = 0;
const uint32_t MAX_MODE7_SUBMODE_INDEX = 5;
bool pack_mode7(
const vec3F& high_color_q16, const float s_q16,
uint32_t ise_endpoint_range, uint8_t* pEndpoints,
uint32_t ise_weight_range, // only used for determining biasing during CEM 7 packing
const astc_hdr_codec_base_options& coptions,
int32_t first_submode, int32_t last_submode, bool ignore_clamping, uint32_t& submode_used);
bool try_mode7(
uint32_t num_pixels,
uint8_t* pEndpoints, uint8_t* pWeights, double& cur_block_error, uint32_t& submode_used,
const vec3F& high_color_q16, const float s_q16,
const basist::half_float block_pixels_half[][3],
uint32_t num_weight_levels, uint32_t ise_weight_range, const astc_hdr_codec_base_options& coptions,
uint32_t ise_endpoint_range,
int32_t first_submode = 0, int32_t last_submode = MAX_MODE7_SUBMODE_INDEX);
bool pack_mode11(
const vec3F& low_color_q16, const vec3F& high_color_q16,
uint32_t ise_endpoint_range, uint8_t* pEndpoints,
const astc_hdr_codec_base_options& coptions,
bool direct_only, int32_t first_submode, int32_t last_submode, bool ignore_clamping, uint32_t& submode_used);
bool try_mode11(uint32_t num_pixels,
uint8_t* pEndpoints, uint8_t* pWeights, double& cur_block_error, uint32_t& submode_used,
const vec3F& low_color_q16, const vec3F& high_color_q16,
const basist::half_float block_pixels_half[][3],
uint32_t num_weight_levels, uint32_t ise_weight_range, const astc_hdr_codec_base_options& coptions, bool direct_only, uint32_t ise_endpoint_range,
bool constrain_ise_weight_selectors,
int32_t first_submode, int32_t last_submode, bool ignore_clamping);
bool try_mode11_dual_plane(uint32_t channel_index, uint32_t num_pixels,
uint8_t* pEndpoints, uint8_t* pWeights0, uint8_t* pWeights1, double& cur_block_error, uint32_t& submode_used,
const vec3F& low_color_q16, const vec3F& high_color_q16,
const basist::half_float block_pixels_half[][3],
uint32_t num_weight_levels, uint32_t ise_weight_range, const astc_hdr_codec_base_options& coptions, bool direct_only, uint32_t ise_endpoint_range,
bool constrain_ise_weight_selectors,
int32_t first_submode, int32_t last_submode, bool ignore_clamping);
const int FIRST_MODE11_SUBMODE_INDEX = -1;
const int MAX_MODE11_SUBMODE_INDEX = 7;
enum opt_mode_t
{
cNoOpt,
cOrdinaryLeastSquares,
cWeightedLeastSquares,
cWeightedLeastSquaresHeavy,
cWeightedAverage
};
struct encode_astc_block_stats
{
uint32_t m_num_pixels;
vec3F m_mean_q16;
vec3F m_axis_q16;
void init(uint32_t num_pixels, const vec4F pBlock_pixels_q16[]);
};
double encode_astc_hdr_block_mode_11(
uint32_t num_pixels,
const basist::half_float pBlock_pixels_half[][3], const vec4F pBlock_pixels_q16[],
uint32_t ise_weight_range,
uint32_t& best_submode,
double cur_block_error,
uint8_t* blk_endpoints, uint8_t* blk_weights,
const astc_hdr_codec_base_options& coptions,
bool direct_only,
uint32_t ise_endpoint_range,
bool uber_mode,
bool constrain_ise_weight_selectors,
int32_t first_submode, int32_t last_submode, bool ignore_clamping,
opt_mode_t opt_mode,
const encode_astc_block_stats *pBlock_stats = nullptr);
double encode_astc_hdr_block_downsampled_mode_11(
uint32_t block_x, uint32_t block_y, uint32_t grid_x, uint32_t grid_y,
uint32_t ise_weight_range, uint32_t ise_endpoint_range,
uint32_t num_pixels, const basist::half_float pBlock_pixels_half[][3], const vec4F pBlock_pixels_q16[],
double cur_block_error,
int32_t first_submode, int32_t last_submode, bool ignore_clamping, opt_mode_t opt_mode,
uint8_t* pBlk_endpoints, uint8_t* pBlk_weights, uint32_t& best_submode,
const astc_hdr_codec_base_options& coptions,
const encode_astc_block_stats* pBlock_stats = nullptr);
double encode_astc_hdr_block_mode_11_dual_plane(
uint32_t num_pixels,
const basist::half_float pBlock_pixels_half[][3], const vec4F pBlock_pixels_q16[],
uint32_t channel_index, // 0-2
uint32_t ise_weight_range,
uint32_t& best_submode,
double cur_block_error,
uint8_t* blk_endpoints, uint8_t* blk_weights0, uint8_t* blk_weights1,
const astc_hdr_codec_base_options& coptions,
bool direct_only,
uint32_t ise_endpoint_range,
bool uber_mode,
bool constrain_ise_weight_selectors,
int32_t first_submode, int32_t last_submode,
bool ignore_clamping);
double encode_astc_hdr_block_mode_7(
uint32_t num_pixels,
const basist::half_float pBlock_pixels_half[][3], const vec4F pBlock_pixels_q16[],
uint32_t ise_weight_range,
uint32_t& best_submode,
double cur_block_error,
uint8_t* blk_endpoints, //[4]
uint8_t* blk_weights, // [num_pixels]
const astc_hdr_codec_base_options& coptions,
uint32_t ise_endpoint_range,
int first_submode = 0, int last_submode = MAX_MODE7_SUBMODE_INDEX,
const encode_astc_block_stats *pBlock_stats = nullptr);
//--------------------------------------------------------------------------------------------------------------------------
struct mode11_log_desc
{
int32_t m_submode;
int32_t m_maj_comp;
// Or R0, G0, B0 if maj_comp==3 (direct)
int32_t m_a; // positive
int32_t m_c; // positive
int32_t m_b0; // positive
// Or R1, G1, B1 if maj_comp==3 (direct)
int32_t m_b1; // positive
int32_t m_d0; // if not direct, is signed
int32_t m_d1; // if not direct, is signed
// limits if not direct
int32_t m_a_bits, m_c_bits, m_b_bits, m_d_bits;
int32_t m_max_a_val, m_max_c_val, m_max_b_val, m_min_d_val, m_max_d_val;
void clear() { clear_obj(*this); }
bool is_direct() const { return m_maj_comp == 3; }
};
//--------------------------------------------------------------------------------------------------------------------------
bool pack_astc_mode7_submode(uint32_t submode, uint8_t* pEndpoints, const vec3F& rgb_q16, float s_q16, int& max_clamp_mag, uint32_t ise_weight_range, bool early_out_if_clamped, int max_clamp_mag_accept_thresh);
bool pack_astc_mode11_submode(uint32_t submode, uint8_t* pEndpoints, int val_q[2][3], int& max_clamp_mag, bool early_out_if_clamped = false, int max_clamp_mag_accept_thresh = 0);
bool pack_astc_mode11_submode(uint32_t submode, uint8_t* pEndpoints, const vec3F& low_q16, const vec3F& high_q16, int& max_clamp_mag, bool early_out_if_clamped = false, int max_clamp_mag_accept_thresh = 0);
void pack_astc_mode11_direct(uint8_t* pEndpoints, vec3F l_q16, vec3F h_q16);
bool pack_mode11(mode11_log_desc& desc, uint8_t* pEndpoints);
void unpack_mode11(const uint8_t* pEndpoints, mode11_log_desc& desc);
void decode_cem_11_config(const uint8_t* pEndpoints, int& submode_index, int& maj_index);
void decode_cem_7_config(const uint8_t* pEndpoints, int& submode_index, int& maj_index);
void dequantize_astc_weights(uint32_t n, const uint8_t* pSrc_ise_vals, uint32_t from_ise_range, uint8_t* pDst_raw_weights);
const float* get_6x6_downsample_matrix(uint32_t grid_width, uint32_t grid_height);
const float* get_8x6_downsample_matrix(uint32_t grid_width, uint32_t grid_height);
void compute_upsample_matrix(basisu::vector2D<float>& upsample_matrix, uint32_t block_width, uint32_t block_height, uint32_t grid_width, uint32_t grid_height);
void compute_upsample_matrix_transposed(basisu::vector<float>& unweighted_downsample_matrix, uint32_t block_width, uint32_t block_height, uint32_t grid_width, uint32_t grid_height);
void compute_diag_AtA_vector(uint32_t block_width, uint32_t block_height, uint32_t grid_width, uint32_t grid_height, const vector2D<float>& upsample_matrix, float* pDst_vec);
void downsample_weight_grid(
const float* pMatrix_weights,
uint32_t bx, uint32_t by, // source/from dimension (block size)
uint32_t wx, uint32_t wy, // dest/to dimension (grid size)
const uint8_t* pSrc_weights, // these are dequantized weights, NOT ISE symbols, [by][bx]
uint8_t* pDst_weights); // [wy][wx]
void downsample_ise_weights(
uint32_t weight_ise_range, uint32_t quant_weight_ise_range,
uint32_t block_w, uint32_t block_h,
uint32_t grid_w, uint32_t grid_h,
const uint8_t* pSrc_weights, uint8_t* pDst_weights);
void downsample_ise_weights_dual_plane(
uint32_t dequant_weight_ise_range, uint32_t quant_weight_ise_range,
uint32_t block_w, uint32_t block_h,
uint32_t grid_w, uint32_t grid_h,
const uint8_t* pSrc_weights0, const uint8_t* pSrc_weights1,
uint8_t* pDst_weights);
bool refine_endpoints(
uint32_t cem,
uint32_t endpoint_ise_range,
uint8_t* pEndpoint_vals, // the endpoints to optimize
uint32_t block_w, uint32_t block_h, // block dimensions
uint32_t grid_w, uint32_t grid_h, const uint8_t* pWeights, uint32_t weight_ise_range, // weight grid
uint32_t num_pixels, const basist::half_float pBlock_pixels_half[][3], const vec4F pBlock_pixels_q16[],
const uint8_t* pPixel_block_ofs, // maps this subset's pixels to block offsets
astc_hdr_codec_base_options& coptions, opt_mode_t opt_mode);
extern bool g_astc_hdr_enc_initialized;
// This MUST be called before encoding any blocks.
void astc_hdr_enc_init();
} // namespace basisu
File diff suppressed because it is too large Load Diff
+445
View File
@@ -0,0 +1,445 @@
// File: basisu_astc_ldr_common.h
#pragma once
#include "basisu_enc.h"
#include "basisu_gpu_texture.h"
#include <array>
namespace basisu
{
namespace astc_ldr
{
const uint32_t ASTC_LDR_MAX_BLOCK_WIDTH = astc_helpers::MAX_BLOCK_DIM; // 12
const uint32_t ASTC_LDR_MAX_BLOCK_HEIGHT = astc_helpers::MAX_BLOCK_DIM; // 12
const uint32_t ASTC_LDR_MAX_BLOCK_PIXELS = astc_helpers::MAX_BLOCK_PIXELS; // 144
const uint32_t ASTC_LDR_MAX_RAW_WEIGHTS = astc_helpers::MAX_WEIGHT_INTERPOLANT_VALUE + 1; // 65
const uint32_t WEIGHT_REFINER_MAX_PASSES = 17;
inline basist::color_rgba convert_to_basist_color_rgba(const color_rgba& c)
{
return basist::color_rgba(c.r, c.g, c.b, c.a);
}
struct cem_encode_params
{
uint32_t m_comp_weights[4];
bool m_decode_mode_srgb; // todo: store astc_helpers::cDecodeModeSRGB8 : astc_helpers::cDecodeModeLDR8 instead, also the alpha mode for srgb because the decoders are broken
const uint8_t* m_pForced_weight_vals0;
const uint8_t* m_pForced_weight_vals1;
uint32_t m_max_ls_passes, m_total_weight_refine_passes;
bool m_worst_weight_nudging_flag;
bool m_endpoint_refinement_flag;
cem_encode_params()
{
init();
}
void init()
{
m_comp_weights[0] = 1;
m_comp_weights[1] = 1;
m_comp_weights[2] = 1;
m_comp_weights[3] = 1;
m_decode_mode_srgb = true;
m_pForced_weight_vals0 = nullptr;
m_pForced_weight_vals1 = nullptr;
m_max_ls_passes = 3;
m_total_weight_refine_passes = 0;
m_worst_weight_nudging_flag = false;
m_endpoint_refinement_flag = false;
}
float get_total_comp_weights() const
{
return (float)(m_comp_weights[0] + m_comp_weights[1] + m_comp_weights[2] + m_comp_weights[3]);
}
};
struct pixel_stats_t
{
uint32_t m_num_pixels;
color_rgba m_pixels[ASTC_LDR_MAX_BLOCK_PIXELS];
vec4F m_pixels_f[ASTC_LDR_MAX_BLOCK_PIXELS];
color_rgba m_min, m_max;
vec4F m_min_f, m_max_f;
vec4F m_mean_f;
// Always 3D, ignoring alpha
vec3F m_mean_rel_axis3;
vec3F m_zero_rel_axis3;
// Always 4D
vec4F m_mean_rel_axis4;
bool m_has_alpha;
stats<float> m_rgba_stats[4];
void clear()
{
clear_obj(*this);
}
void init(uint32_t num_pixels, const color_rgba* pPixels);
}; // struct struct pixel_stats
void global_init();
void bit_transfer_signed_enc(int& a, int& b);
void bit_transfer_signed_dec(int& a, int& b); // transfers MSB from a to b, a is then [-32,31]
color_rgba blue_contract_enc(color_rgba orig, bool& did_clamp, int encoded_b);
int quant_preserve2(uint32_t ise_range, uint32_t v);
uint32_t get_colors(const color_rgba& l, const color_rgba& h, uint32_t weight_ise_index, color_rgba* pColors, bool decode_mode_srgb);
uint32_t get_colors_raw_weights(const color_rgba& l, const color_rgba& h, color_rgba* pColors, bool decode_mode_srgb);
void decode_endpoints_ise20(uint32_t cem_index, const uint8_t* pEndpoint_vals, color_rgba& l, color_rgba& h); // assume BISE 20
void decode_endpoints(uint32_t cem_index, const uint8_t* pEndpoint_vals, uint32_t endpoint_ise_index, color_rgba& l, color_rgba& h, float* pScale = nullptr);
uint32_t get_colors(uint32_t cem_index, const uint8_t* pEndpoint_vals, uint32_t endpoint_ise_index, uint32_t weight_ise_index, color_rgba* pColors, bool decode_mode_srgb);
uint32_t get_colors_raw_weights(uint32_t cem_index, const uint8_t* pEndpoint_vals, uint32_t endpoint_ise_index, color_rgba* pColors, bool decode_mode_srgb);
//int apply_delta_to_bise_endpoint_val(uint32_t endpoint_ise_range, int ise_val, int delta);
int apply_delta_to_bise_weight_val(uint32_t weight_ise_range, int ise_val, int delta);
uint64_t eval_solution(
const pixel_stats_t& pixel_stats,
uint32_t total_weights, const color_rgba* pWeight_colors,
uint8_t* pWeight_vals, uint32_t weight_ise_index,
const cem_encode_params& params);
uint64_t eval_solution(
const pixel_stats_t& pixel_stats,
uint32_t cem_index,
const uint8_t* pEndpoint_vals, uint32_t endpoint_ise_index,
uint8_t* pWeight_vals, uint32_t weight_ise_index,
const cem_encode_params& params);
uint64_t eval_solution_dp(
uint32_t ccs_index,
const pixel_stats_t& pixel_stats,
uint32_t total_weights, const color_rgba* pWeight_colors,
uint8_t* pWeight_vals0, uint8_t* pWeight_vals1, uint32_t weight_ise_index,
const cem_encode_params& params);
uint64_t eval_solution_dp(
const pixel_stats_t& pixel_stats,
uint32_t cem_index, uint32_t ccs_index,
const uint8_t* pEndpoint_vals, uint32_t endpoint_ise_index,
uint8_t* pWeight_vals0, uint8_t* pWeight_vals1, uint32_t weight_ise_index,
const cem_encode_params& params);
//bool cem8_or_12_used_blue_contraction(uint32_t cem_index, const uint8_t* pEndpoint_vals, uint32_t endpoint_ise_index);
//bool cem9_or_13_used_blue_contraction(uint32_t cem_index, const uint8_t* pEndpoint_vals, uint32_t endpoint_ise_index);
//bool used_blue_contraction(uint32_t cem_index, const uint8_t* pEndpoint_vals, uint32_t endpoint_ise_index);
uint64_t cem_encode_pixels(
uint32_t cem_index, int ccs_index,
const pixel_stats_t& pixel_stats, const cem_encode_params& enc_params,
uint32_t endpoint_ise_range, uint32_t weight_ise_range,
uint8_t* pEndpoint_vals, uint8_t* pWeight_vals0, uint8_t* pWeight_vals1, uint64_t cur_blk_error,
bool use_blue_contraction, bool* pBase_ofs_clamped_flag);
// TODO: Rename, confusing vs. std::vector or basisu::vector or vec4F etc.
struct partition_pattern_vec
{
uint32_t m_width, m_height;
uint8_t m_parts[ASTC_LDR_MAX_BLOCK_PIXELS];
partition_pattern_vec();
partition_pattern_vec(const partition_pattern_vec& other);
partition_pattern_vec(uint32_t width, uint32_t height, const uint8_t* pParts = nullptr);
void init(uint32_t width, uint32_t height, const uint8_t* pParts = nullptr);
void init_part_hist();
void clear();
partition_pattern_vec& operator= (const partition_pattern_vec& rhs);
uint32_t get_width() const { return m_width; }
uint32_t get_height() const { return m_height; }
uint32_t get_total() const { return m_width * m_height; }
uint8_t operator[] (uint32_t i) const { assert(i < get_total()); return m_parts[i]; }
uint8_t& operator[] (uint32_t i) { assert(i < get_total()); return m_parts[i]; }
uint8_t operator() (uint32_t x, uint32_t y) const { assert((x < m_width) && (y < m_height)); return m_parts[x + y * m_width]; }
uint8_t& operator() (uint32_t x, uint32_t y) { assert((x < m_width) && (y < m_height)); return m_parts[x + y * m_width]; }
int get_squared_distance(const partition_pattern_vec& other) const;
float get_distance(const partition_pattern_vec& other) const
{
return sqrtf((float)get_squared_distance(other));
}
enum { cMaxPermute2Index = 1 };
partition_pattern_vec get_permuted2(uint32_t permute_index) const;
enum { cMaxPermute3Index = 5 };
partition_pattern_vec get_permuted3(uint32_t permute_index) const;
partition_pattern_vec get_canonicalized() const;
bool operator== (const partition_pattern_vec& rhs) const
{
if ((m_width != rhs.m_width) || (m_height != rhs.m_height))
return false;
return memcmp(m_parts, rhs.m_parts, get_total()) == 0;
}
operator size_t() const
{
return basist::hash_hsieh(m_parts, get_total());
}
};
struct vp_tree_node
{
partition_pattern_vec m_vantage_point;
uint32_t m_point_index;
float m_dist;
int m_inner_node, m_outer_node;
};
const uint32_t NUM_PART3_MAPPINGS = 6;
extern uint8_t g_part3_mapping[NUM_PART3_MAPPINGS][3];
class vp_tree
{
public:
vp_tree()
{
}
void clear()
{
m_nodes.clear();
}
// This requires no redundant patterns, i.e. all must be unique.
bool init(uint32_t n, const partition_pattern_vec* pUnique_pats);
struct result
{
uint32_t m_pat_index;
uint32_t m_mapping_index;
float m_dist;
bool operator< (const result& rhs) const { return m_dist < rhs.m_dist; }
bool operator> (const result& rhs) const { return m_dist > rhs.m_dist; }
};
class result_queue
{
enum { MaxSupportedSize = 512 + 1 };
public:
result_queue() :
m_cur_size(0)
{
}
size_t get_size() const
{
return m_cur_size;
}
bool empty() const
{
return !m_cur_size;
}
typedef std::array<result, MaxSupportedSize + 1> result_array_type;
const result_array_type& get_elements() const { return m_elements; }
result_array_type& get_elements() { return m_elements; }
void clear()
{
m_cur_size = 0;
}
void reserve(uint32_t n)
{
BASISU_NOTE_UNUSED(n);
}
const result& top() const
{
assert(m_cur_size);
return m_elements[1];
}
bool insert(const result& val, uint32_t max_size)
{
assert(max_size < MaxSupportedSize);
if (m_cur_size >= MaxSupportedSize)
return false;
m_elements[++m_cur_size] = val;
up_heap(m_cur_size);
if (m_cur_size > max_size)
pop();
return true;
}
bool pop()
{
if (m_cur_size == 0)
return false;
m_elements[1] = m_elements[m_cur_size--];
down_heap(1);
return true;
}
float get_highest_dist() const
{
if (!m_cur_size)
return 0.0f;
return top().m_dist;
}
private:
result_array_type m_elements;
size_t m_cur_size;
void up_heap(size_t index)
{
while ((index > 1) && (m_elements[index] > m_elements[index >> 1]))
{
std::swap(m_elements[index], m_elements[index >> 1]);
index >>= 1;
}
}
void down_heap(size_t index)
{
for (; ; )
{
size_t largest = index, left_child = 2 * index, right_child = 2 * index + 1;
if ((left_child <= m_cur_size) && (m_elements[left_child] > m_elements[largest]))
largest = left_child;
if ((right_child <= m_cur_size) && (m_elements[right_child] > m_elements[largest]))
largest = right_child;
if (largest == index)
break;
std::swap(m_elements[index], m_elements[largest]);
index = largest;
}
}
};
void find_nearest(uint32_t num_subsets, const partition_pattern_vec& desired_pat, result_queue& results, uint32_t max_results) const;
private:
basisu::vector<vp_tree_node> m_nodes;
void find_nearest_at_node(int node_index, uint32_t num_desired_pats, const partition_pattern_vec* pDesired_pats, result_queue& results, uint32_t max_results) const;
void find_nearest_at_node_non_recursive(int init_node_index, uint32_t num_desired_pats, const partition_pattern_vec* pDesired_pats, result_queue& results, uint32_t max_results) const;
// returns the index of the new node, or -1 on error
int create_node(uint32_t n, const partition_pattern_vec* pUnique_pats, const uint_vec& pat_indices);
// returns the pattern index of the vantage point (-1 on error), and the optimal split distance
std::pair<int, float> find_best_vantage_point(uint32_t num_unique_pats, const partition_pattern_vec* pUnique_pats, const uint_vec& pat_indices);
};
typedef basisu::hash_map<partition_pattern_vec, std::pair<uint32_t, uint32_t > > partition_hash_map;
struct partition_pattern_hist
{
uint8_t m_hist[4];
partition_pattern_hist() { clear(); }
void clear() { clear_obj(m_hist); }
};
struct partitions_data
{
uint32_t m_width, m_height, m_num_partitions;
partition_pattern_vec m_partition_pats[astc_helpers::NUM_PARTITION_PATTERNS]; // indexed by unique index, NOT the 10-bit ASTC seed/pattern index
partition_pattern_hist m_partition_pat_histograms[astc_helpers::NUM_PARTITION_PATTERNS]; // indexed by unique index, histograms of each pattern
// ASTC seed to unique index and vice versa
int16_t m_part_seed_to_unique_index[astc_helpers::NUM_PARTITION_PATTERNS];
int16_t m_unique_index_to_part_seed[astc_helpers::NUM_PARTITION_PATTERNS];
// Total number of unique patterns
uint32_t m_total_unique_patterns;
// VP tree used to rapidly find nearby/similar patterns.
vp_tree m_part_vp_tree;
void init(uint32_t num_partitions, uint32_t block_width, uint32_t block_height, bool init_vp_tree = true);
};
float surrogate_quant_endpoint_val(float e, uint32_t num_endpoint_levels, uint32_t flags);
vec4F surrogate_quant_endpoint(const vec4F& e, uint32_t num_endpoint_levels, uint32_t flags);
float surrogate_evaluate_rgba_sp(const pixel_stats_t& ps, const vec4F& l, const vec4F& h, float* pWeights0, uint32_t num_weight_levels, const cem_encode_params& enc_params, uint32_t flags);
float surrogate_evaluate_rgba_dp(uint32_t ccs_index, const pixel_stats_t& ps, const vec4F& l, const vec4F& h, float* pWeights0, float* pWeights1, uint32_t num_weight_levels, const cem_encode_params& enc_params, uint32_t flags);
enum
{
cFlagDisableQuant = 1,
cFlagNoError = 2
}
;
float cem_surrogate_encode_pixels(
uint32_t cem_index, int ccs_index,
const pixel_stats_t& pixel_stats, const cem_encode_params& enc_params,
uint32_t endpoint_ise_range, uint32_t weight_ise_range,
vec4F& low_endpoint, vec4F& high_endpoint, float& s, float* pWeights0, float* pWeights1, uint32_t flags = 0);
#if 0
bool requantize_ise_endpoints(uint32_t cem,
uint32_t src_ise_endpoint_range, const uint8_t* pSrc_endpoints,
uint32_t dst_ise_endpoint_range, uint8_t* pDst_endpoints);
uint32_t get_base_cem_without_alpha(uint32_t cem);
bool pack_base_offset(
uint32_t cem_index, uint32_t dst_ise_endpoint_range, uint8_t* pPacked_endpoints,
const color_rgba& l, const color_rgba& h,
bool use_blue_contraction, bool auto_disable_blue_contraction_if_clamped,
bool& blue_contraction_clamped_flag, bool& base_ofs_clamped_flag, bool& endpoints_swapped);
bool convert_endpoints_across_cems(
uint32_t prev_cem, uint32_t prev_endpoint_ise_range, const uint8_t* pPrev_endpoints,
uint32_t dst_cem, uint32_t dst_endpoint_ise_range, uint8_t* pDst_endpoints,
bool always_repack,
bool use_blue_contraction, bool auto_disable_blue_contraction_if_clamped,
bool& blue_contraction_clamped_flag, bool& base_ofs_clamped_flag);
#endif
} // namespace astc_ldr
} // namespace basisu
File diff suppressed because it is too large Load Diff
+118
View File
@@ -0,0 +1,118 @@
// File: basisu_astc_ldr_encode.cpp
#pragma once
#include "basisu_enc.h"
#include "../transcoder/basisu_astc_helpers.h"
namespace basisu {
namespace astc_ldr {
void encoder_init();
const int EFFORT_LEVEL_MIN = 0, EFFORT_LEVEL_MAX = 10, EFFORT_LEVEL_DEF = 3;
const int DCT_QUALITY_MIN = 1, DCT_QUALITY_MAX = 100;
struct astc_ldr_encode_config
{
astc_ldr_encode_config()
{
}
void clear()
{
*this = astc_ldr_encode_config();
}
// ASTC LDR block dimensions. Must be a valid ASTC block dimension. Any supported from 4x4-12x12, including unequal dimensions.
uint32_t m_astc_block_width = 6;
uint32_t m_astc_block_height = 6;
// If true, the encoder assumes all ASTC blocks will be decompressed using sRGB vs. LDR8 mode. This corresponds to astcenc's -cs vs. cl color profiles.
// This should match how the texture is later decoded by the GPU for maximum quality. This bit is stored into the output file.
bool m_astc_decode_mode_srgb = true;
// If true, trade off some compression (3-10%) for faster decompression.
// If false, favor highest compression, but slower decompression.
//bool m_use_faster_format = true;
basist::astc_ldr_t::xuastc_ldr_syntax m_compressed_syntax = basist::astc_ldr_t::xuastc_ldr_syntax::cFullArith;
// Encoder CPU effort vs. quality. [0,10], higher=better.
// 0=extremely fast but very brittle (no subsets)
// 1=first 2 subset effort level
// 10=extremely high CPU requirements.
uint32_t m_effort_level = 3;
// Weight grid DCT quality [1,100] - higher=better quality (JPEG-style).
float m_dct_quality = 85;
// true=use weight grid DCT, false=always use DPCM
bool m_use_dct = false;
// true=use lossy supercompression, false=supercompression stage is always lossless.
bool m_lossy_supercompression = false;
// Channel weights used to compute RGBA colorspace L2 errors. Must be >= 1.
uint32_t m_comp_weights[4] = { 1, 1, 1, 1 };
// Lossy supercompression stage parameters for RGB vs. RGBA image inputs.
// (Bounded RDO - explictly not Lagrangian.)
float m_replacement_min_psnr = 35.0f; // if the block's base PSNR is less than this, it cannot be changed
float m_psnr_trial_diff_thresh = 1.5f; // reject candidates if their PSNR is lower than m_replacement_min_psnr-m_psnr_trial_diff_thresh
float m_psnr_trial_diff_thresh_edge = 1.0f; // edge variant
// Lossy supercompression settings - alpha texture variants
float m_replacement_min_psnr_alpha = 38.0f;
float m_psnr_trial_diff_thresh_alpha = .75f;
float m_psnr_trial_diff_thresh_edge_alpha = .5f;
// If true, try encoding blurred blocks, in addition to unblurred, for superpass 1 and 2.
// Higher quality, but massively slower and not yet tuned/refined.
bool m_block_blurring_p1 = false, m_block_blurring_p2 = false;
// If true, no matter what effort level subset usage will be disabled.
bool m_force_disable_subsets = false;
// If true, no matter what effort level RGB dual plane usage will be disabled.
bool m_force_disable_rgb_dual_plane = false;
bool m_debug_images = false;
bool m_debug_output = false;
std::string m_debug_file_prefix;
void debug_print() const
{
fmt_debug_printf("ASTC block dimensions: {}x{}\n", m_astc_block_width, m_astc_block_height);
fmt_debug_printf("ASTC decode profile mode sRGB: {}\n", m_astc_decode_mode_srgb);
fmt_debug_printf("Syntax: {}\n", (uint32_t)m_compressed_syntax);
fmt_debug_printf("Effort level: {}\n", m_effort_level);
fmt_debug_printf("Use DCT: {}\n", m_use_dct);
fmt_debug_printf("DCT quality level (1-100): {}\n", m_dct_quality);
fmt_debug_printf("Comp weights: {} {} {} {}\n", m_comp_weights[0], m_comp_weights[1], m_comp_weights[2], m_comp_weights[3]);
fmt_debug_printf("Block blurring: {} {}\n", m_block_blurring_p1, m_block_blurring_p2);
fmt_debug_printf("Force disable subsets: {}\n", m_force_disable_subsets);
fmt_debug_printf("Force disable RGB dual plane: {}\n", m_force_disable_rgb_dual_plane);
fmt_debug_printf("\nLossy supercompression: {}\n", m_lossy_supercompression);
fmt_debug_printf("m_replacement_min_psnr: {}\n", m_replacement_min_psnr);
fmt_debug_printf("m_psnr_trial_diff_thresh: {}\n", m_psnr_trial_diff_thresh);
fmt_debug_printf("m_psnr_trial_diff_thresh_edge: {}\n", m_psnr_trial_diff_thresh_edge);
fmt_debug_printf("m_replacement_min_psnr_alpha: {}\n", m_replacement_min_psnr_alpha);
fmt_debug_printf("m_psnr_trial_diff_thresh_alpha: {}\n", m_psnr_trial_diff_thresh_alpha);
fmt_debug_printf("m_psnr_trial_diff_thresh_edge_alpha: {}\n", m_psnr_trial_diff_thresh_edge_alpha);
fmt_debug_printf("m_debug_images: {}\n", m_debug_images);
}
};
bool compress_image(
const image& orig_img, uint8_vec &comp_data, vector2D<astc_helpers::log_astc_block>& coded_blocks,
const astc_ldr_encode_config& global_cfg,
job_pool& job_pool);
void deblock_filter(uint32_t filter_block_width, uint32_t filter_block_height, const image& src_img, image& dst_img, bool stronger_filtering = false, int SKIP_THRESH = 24);
} // namespace astc_ldr
} // namespace basisu
+2 -2
View File
@@ -1,5 +1,5 @@
// basisu_backend.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -666,7 +666,7 @@ namespace basisu
if (m_params.m_debug_images)
{
image gi_unpacked;
gi.unpack(gi_unpacked);
gi.unpack(gi_unpacked, false);
char buf[256];
#ifdef _WIN32
+10 -11
View File
@@ -1,5 +1,5 @@
// basisu_backend.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -103,8 +103,8 @@ namespace basisu
{
clear();
}
uint32_t m_endpoint_predictor;
uint32_t m_endpoint_predictor;
int m_endpoint_index;
int m_selector_index;
@@ -115,10 +115,10 @@ namespace basisu
void clear()
{
m_endpoint_predictor = 0;
m_endpoint_index = 0;
m_selector_index = 0;
m_selector_history_buf_index = 0;
m_is_cr_target = false;
}
@@ -137,7 +137,7 @@ namespace basisu
color_rgba m_color5;
uint32_t m_inten5;
bool m_color5_valid;
void clear()
{
clear_obj(*this);
@@ -153,7 +153,7 @@ namespace basisu
float m_endpoint_rdo_quality_thresh;
float m_selector_rdo_quality_thresh;
uint32_t m_compression_level;
bool m_used_global_codebooks;
bool m_validate;
@@ -285,7 +285,7 @@ namespace basisu
basisu_backend_params m_params;
basisu_backend_slice_desc_vec m_slices;
basisu_backend_output m_output;
etc1_endpoint_palette_entry_vec m_endpoint_palette;
etc1_selector_palette_entry_vec m_selector_palette;
@@ -331,12 +331,12 @@ namespace basisu
return slice.m_first_block_index + block_y * slice.m_num_blocks_x + block_x;
}
uint32_t get_total_blocks(uint32_t slice_index) const
{
return m_slices[slice_index].m_num_blocks_x * m_slices[slice_index].m_num_blocks_y;
}
uint32_t get_total_blocks() const
{
uint32_t total_blocks = 0;
@@ -406,4 +406,3 @@ namespace basisu
};
} // namespace basisu
+8 -8
View File
@@ -1,5 +1,5 @@
// basisu_basis_file.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -27,14 +27,14 @@ namespace basisu
m_header.m_data_size = m_total_file_size - sizeof(basist::basis_file_header);
m_header.m_total_slices = (uint32_t)encoder_output.m_slice_desc.size();
m_header.m_total_images = 0;
for (uint32_t i = 0; i < encoder_output.m_slice_desc.size(); i++)
m_header.m_total_images = maximum<uint32_t>(m_header.m_total_images, encoder_output.m_slice_desc[i].m_source_file_index + 1);
m_header.m_tex_format = (int)encoder_output.m_tex_format;
m_header.m_flags = 0;
if (encoder_output.m_etc1s)
{
assert(encoder_output.m_tex_format == basist::basis_tex_format::cETC1S);
@@ -51,7 +51,7 @@ namespace basisu
m_header.m_flags = m_header.m_flags | basist::cBASISHeaderFlagUsesGlobalCodebook;
if (encoder_output.m_srgb)
m_header.m_flags = m_header.m_flags | basist::cBASISHeaderFlagSRGB;
for (uint32_t i = 0; i < encoder_output.m_slice_desc.size(); i++)
{
if (encoder_output.m_slice_desc[i].m_alpha)
@@ -108,7 +108,7 @@ namespace basisu
m_images_descs[i].m_image_index = slice_descs[i].m_source_file_index;
m_images_descs[i].m_level_index = slice_descs[i].m_mip_index;
if (slice_descs[i].m_alpha)
m_images_descs[i].m_flags = m_images_descs[i].m_flags | basist::cSliceDescFlagsHasAlpha;
if (slice_descs[i].m_iframe)
@@ -186,7 +186,7 @@ namespace basisu
pHeader->m_data_size = m_total_file_size - sizeof(basist::basis_file_header);
pHeader->m_data_crc16 = basist::crc16(&m_comp_data[0] + sizeof(basist::basis_file_header), m_total_file_size - sizeof(basist::basis_file_header), 0);
pHeader->m_header_crc16 = basist::crc16(&pHeader->m_data_size, sizeof(basist::basis_file_header) - BASISU_OFFSETOF(basist::basis_file_header, m_data_size), 0);
pHeader->m_sig = basist::basis_file_header::cBASISSigValue;
@@ -242,7 +242,7 @@ namespace basisu
m_tables_file_ofs = 0;
m_first_image_file_ofs = m_slice_descs_file_ofs + sizeof(basist::basis_slice_desc) * (uint32_t)slice_descs.size();
}
uint64_t total_file_size = m_first_image_file_ofs;
for (uint32_t i = 0; i < encoder_output.m_slice_image_data.size(); i++)
total_file_size += encoder_output.m_slice_image_data[i].size();
+1 -1
View File
@@ -1,5 +1,5 @@
// basisu_basis_file.h
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
+62 -59
View File
@@ -1,5 +1,5 @@
// File: basisu_bc7enc.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -100,24 +100,24 @@ static void astc_init()
{
if (!astc_is_valid_endpoint_range(range))
continue;
const uint32_t levels = astc_get_levels(range);
uint32_t vals[256];
// TODO
for (uint32_t i = 0; i < levels; i++)
vals[i] = (unquant_astc_endpoint_val(i, range) << 8) | i;
std::sort(vals, vals + levels);
for (uint32_t i = 0; i < levels; i++)
{
uint32_t order = vals[i] & 0xFF;
uint32_t unq = vals[i] >> 8;
g_astc_sorted_order_unquant[range][i].m_unquant = (uint8_t)unq;
g_astc_sorted_order_unquant[range][i].m_index = (uint8_t)order;
} // i
#if 0
@@ -186,7 +186,7 @@ static inline uint32_t astc_interpolate_linear(uint32_t l, uint32_t h, uint32_t
void bc7enc_compress_block_init()
{
astc_init();
// BC7 666.1
for (int c = 0; c < 256; c++)
{
@@ -224,11 +224,11 @@ void bc7enc_compress_block_init()
for (uint32_t l = 0; l < 16; l++)
{
uint32_t low = (l << 4) | l;
for (uint32_t h = 0; h < 16; h++)
{
uint32_t high = (h << 4) | h;
const int k = astc_interpolate_linear(low, high, g_bc7_weights3[BC7ENC_ASTC_4BIT_3BIT_OPTIMAL_INDEX]);
const int err = (k - c) * (k - c);
@@ -240,9 +240,9 @@ void bc7enc_compress_block_init()
}
} // h
} // l
g_astc_4bit_3bit_optimal_endpoints[c] = best;
} // c
// ASTC [0,15] 2-bit
@@ -253,11 +253,11 @@ void bc7enc_compress_block_init()
for (uint32_t l = 0; l < 16; l++)
{
uint32_t low = (l << 4) | l;
for (uint32_t h = 0; h < 16; h++)
{
uint32_t high = (h << 4) | h;
const int k = astc_interpolate_linear(low, high, g_bc7_weights2[BC7ENC_ASTC_4BIT_2BIT_OPTIMAL_INDEX]);
const int err = (k - c) * (k - c);
@@ -269,9 +269,9 @@ void bc7enc_compress_block_init()
}
} // h
} // l
g_astc_4bit_2bit_optimal_endpoints[c] = best;
} // c
// ASTC range 7 [0,11] 2-bit
@@ -282,11 +282,11 @@ void bc7enc_compress_block_init()
for (uint32_t l = 0; l < 12; l++)
{
uint32_t low = g_astc_sorted_order_unquant[7][l].m_unquant;
for (uint32_t h = 0; h < 12; h++)
{
uint32_t high = g_astc_sorted_order_unquant[7][h].m_unquant;
const int k = astc_interpolate_linear(low, high, g_bc7_weights2[BC7ENC_ASTC_RANGE7_2BIT_OPTIMAL_INDEX]);
const int err = (k - c) * (k - c);
@@ -298,9 +298,9 @@ void bc7enc_compress_block_init()
}
} // h
} // l
g_astc_range7_2bit_optimal_endpoints[c] = best;
} // c
// ASTC range 13 [0,47] 4-bit
@@ -311,11 +311,11 @@ void bc7enc_compress_block_init()
for (uint32_t l = 0; l < 48; l++)
{
uint32_t low = g_astc_sorted_order_unquant[13][l].m_unquant;
for (uint32_t h = 0; h < 48; h++)
{
uint32_t high = g_astc_sorted_order_unquant[13][h].m_unquant;
const int k = astc_interpolate_linear(low, high, g_astc_weights4[BC7ENC_ASTC_RANGE13_4BIT_OPTIMAL_INDEX]);
const int err = (k - c) * (k - c);
@@ -327,9 +327,9 @@ void bc7enc_compress_block_init()
}
} // h
} // l
g_astc_range13_4bit_optimal_endpoints[c] = best;
} // c
// ASTC range 13 [0,47] 2-bit
@@ -340,11 +340,11 @@ void bc7enc_compress_block_init()
for (uint32_t l = 0; l < 48; l++)
{
uint32_t low = g_astc_sorted_order_unquant[13][l].m_unquant;
for (uint32_t h = 0; h < 48; h++)
{
uint32_t high = g_astc_sorted_order_unquant[13][h].m_unquant;
const int k = astc_interpolate_linear(low, high, g_bc7_weights2[BC7ENC_ASTC_RANGE13_2BIT_OPTIMAL_INDEX]);
const int err = (k - c) * (k - c);
@@ -356,9 +356,9 @@ void bc7enc_compress_block_init()
}
} // h
} // l
g_astc_range13_2bit_optimal_endpoints[c] = best;
} // c
// ASTC range 11 [0,31] 5-bit
@@ -393,14 +393,15 @@ void bc7enc_compress_block_init()
static void compute_least_squares_endpoints_rgba(uint32_t N, const uint8_t *pSelectors, const bc7enc_vec4F* pSelector_weights, bc7enc_vec4F* pXl, bc7enc_vec4F* pXh, const color_quad_u8 *pColors)
{
// Least squares using normal equations: http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf
// Least squares using normal equations: http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf
// https://web.archive.org/web/20150319232457/http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf
// I did this in matrix form first, expanded out all the ops, then optimized it a bit.
double z00 = 0.0f, z01 = 0.0f, z10 = 0.0f, z11 = 0.0f;
double q00_r = 0.0f, q10_r = 0.0f, t_r = 0.0f;
double q00_g = 0.0f, q10_g = 0.0f, t_g = 0.0f;
double q00_b = 0.0f, q10_b = 0.0f, t_b = 0.0f;
double q00_a = 0.0f, q10_a = 0.0f, t_a = 0.0f;
for (uint32_t i = 0; i < N; i++)
{
const uint32_t sel = pSelectors[i];
@@ -648,7 +649,7 @@ static uint64_t pack_astc_4bit_3bit_to_one_color(const color_cell_compressor_par
{
uint32_t low = (pResults->m_low_endpoint.m_c[i] << 4) | pResults->m_low_endpoint.m_c[i];
uint32_t high = (pResults->m_high_endpoint.m_c[i] << 4) | pResults->m_high_endpoint.m_c[i];
p.m_c[i] = (uint8_t)astc_interpolate_linear(low, high, g_bc7_weights3[BC7ENC_ASTC_4BIT_3BIT_OPTIMAL_INDEX]);
}
p.m_c[3] = 255;
@@ -687,10 +688,10 @@ static uint64_t pack_astc_4bit_2bit_to_one_color_rgba(const color_cell_compresso
{
uint32_t low = (pResults->m_low_endpoint.m_c[i] << 4) | pResults->m_low_endpoint.m_c[i];
uint32_t high = (pResults->m_high_endpoint.m_c[i] << 4) | pResults->m_high_endpoint.m_c[i];
p.m_c[i] = (uint8_t)astc_interpolate_linear(low, high, g_bc7_weights2[BC7ENC_ASTC_4BIT_2BIT_OPTIMAL_INDEX]);
}
uint64_t total_err = 0;
for (uint32_t i = 0; i < pParams->m_num_pixels; i++)
total_err += compute_color_distance_rgba(&p, &pParams->m_pPixels[i], pParams->m_perceptual, pParams->m_weights);
@@ -726,7 +727,7 @@ static uint64_t pack_astc_range7_2bit_to_one_color(const color_cell_compressor_p
{
uint32_t low = g_astc_sorted_order_unquant[7][pResults->m_low_endpoint.m_c[i]].m_unquant;
uint32_t high = g_astc_sorted_order_unquant[7][pResults->m_high_endpoint.m_c[i]].m_unquant;
p.m_c[i] = (uint8_t)astc_interpolate_linear(low, high, g_bc7_weights2[BC7ENC_ASTC_RANGE7_2BIT_OPTIMAL_INDEX]);
}
p.m_c[3] = 255;
@@ -747,7 +748,7 @@ static uint64_t pack_astc_range13_2bit_to_one_color(const color_cell_compressor_
const endpoint_err *pEr = &g_astc_range13_2bit_optimal_endpoints[r];
const endpoint_err *pEg = &g_astc_range13_2bit_optimal_endpoints[g];
const endpoint_err *pEb = &g_astc_range13_2bit_optimal_endpoints[b];
color_quad_u8_set(&pResults->m_low_endpoint, pEr->m_lo, pEg->m_lo, pEb->m_lo, 47);
color_quad_u8_set(&pResults->m_high_endpoint, pEr->m_hi, pEg->m_hi, pEb->m_hi, 47);
pResults->m_pbits[0] = 0;
@@ -766,10 +767,10 @@ static uint64_t pack_astc_range13_2bit_to_one_color(const color_cell_compressor_
{
uint32_t low = g_astc_sorted_order_unquant[13][pResults->m_low_endpoint.m_c[i]].m_unquant;
uint32_t high = g_astc_sorted_order_unquant[13][pResults->m_high_endpoint.m_c[i]].m_unquant;
p.m_c[i] = (uint8_t)astc_interpolate_linear(low, high, g_bc7_weights2[BC7ENC_ASTC_RANGE13_2BIT_OPTIMAL_INDEX]);
}
uint64_t total_err = 0;
for (uint32_t i = 0; i < pParams->m_num_pixels; i++)
total_err += compute_color_distance_rgb(&p, &pParams->m_pPixels[i], pParams->m_perceptual, pParams->m_weights);
@@ -878,18 +879,18 @@ static uint64_t evaluate_solution(const color_quad_u8 *pLow, const color_quad_u8
const int dr = actualMaxColor.m_c[0] - lr;
const int dg = actualMaxColor.m_c[1] - lg;
const int db = actualMaxColor.m_c[2] - lb;
uint64_t total_err = 0;
if (pParams->m_pForce_selectors)
{
for (uint32_t i = 0; i < pParams->m_num_pixels; i++)
{
const color_quad_u8* pC = &pParams->m_pPixels[i];
const uint8_t sel = pParams->m_pForce_selectors[i];
assert(sel < N);
total_err += (pParams->m_has_alpha ? compute_color_distance_rgba : compute_color_distance_rgb)(&weightedColors[sel], pC, pParams->m_perceptual, pParams->m_weights);
pResults->m_pSelectors_temp[i] = sel;
@@ -930,7 +931,7 @@ static uint64_t evaluate_solution(const color_quad_u8 *pLow, const color_quad_u8
--best_sel;
}
total_err += err1;
pResults->m_pSelectors_temp[i] = (uint8_t)best_sel;
}
}
@@ -1027,7 +1028,7 @@ static uint64_t evaluate_solution(const color_quad_u8 *pLow, const color_quad_u8
memcpy(pResults->m_pSelectors, pResults->m_pSelectors_temp, sizeof(pResults->m_pSelectors[0]) * pParams->m_num_pixels);
}
return total_err;
}
@@ -1257,7 +1258,7 @@ static uint64_t find_optimal_solution(uint32_t mode, bc7enc_vec4F xl, bc7enc_vec
}
}
}
fixDegenerateEndpoints(mode, &bestMinColor, &bestMaxColor, &xl, &xh, iscalep >> 1, 0);
if ((pResults->m_best_overall_err == UINT64_MAX) || color_quad_u8_notequals(&bestMinColor, &pResults->m_low_endpoint) || color_quad_u8_notequals(&bestMaxColor, &pResults->m_high_endpoint) || (best_pbits[0] != pResults->m_pbits[0]) || (best_pbits[1] != pResults->m_pbits[1]))
@@ -1296,24 +1297,26 @@ void check_best_overall_error(const color_cell_compressor_params *pParams, color
colors[n-1].m_c[c] = g_astc_unquant[pParams->m_astc_endpoint_range][pResults->m_astc_high_endpoint.m_c[c]].m_unquant;
assert(colors[n-1].m_c[c] == g_astc_sorted_order_unquant[pParams->m_astc_endpoint_range][pResults->m_high_endpoint.m_c[c]].m_unquant);
}
for (uint32_t i = 1; i < pParams->m_num_selector_weights - 1; i++)
for (uint32_t c = 0; c < 4; c++)
colors[i].m_c[c] = (uint8_t)astc_interpolate_linear(colors[0].m_c[c], colors[n - 1].m_c[c], pParams->m_pSelector_weights[i]);
#ifdef _DEBUG
uint64_t total_err = 0;
for (uint32_t p = 0; p < pParams->m_num_pixels; p++)
{
const color_quad_u8 &orig = pParams->m_pPixels[p];
const color_quad_u8 &packed = colors[pResults->m_pSelectors[p]];
if (pParams->m_has_alpha)
total_err += compute_color_distance_rgba(&orig, &packed, pParams->m_perceptual, pParams->m_weights);
else
total_err += compute_color_distance_rgb(&orig, &packed, pParams->m_perceptual, pParams->m_weights);
}
assert(total_err == pResults->m_best_overall_err);
#endif
// HACK HACK
//if (total_err != pResults->m_best_overall_err)
// printf("X");
@@ -1416,12 +1419,12 @@ uint64_t color_cell_compression(uint32_t mode, const color_cell_compressor_param
bc7enc_vec4F color = vec4F_from_color(&pParams->m_pPixels[i]);
meanColor = vec4F_add(&meanColor, &color);
}
bc7enc_vec4F meanColorScaled = vec4F_mul(&meanColor, 1.0f / (float)(pParams->m_num_pixels));
meanColor = vec4F_mul(&meanColor, 1.0f / (float)(pParams->m_num_pixels * 255.0f));
vec4F_saturate_in_place(&meanColor);
if (pParams->m_has_alpha)
{
// Use incremental PCA for RGBA PCA, because it's simple.
@@ -1484,7 +1487,7 @@ uint64_t color_cell_compression(uint32_t mode, const color_cell_compressor_param
vec4F_set(&axis, xr, xg, xb, 0);
}
}
if (vec4F_dot(&axis, &axis) < .5f)
{
if (pParams->m_perceptual)
@@ -1493,7 +1496,7 @@ uint64_t color_cell_compression(uint32_t mode, const color_cell_compressor_param
vec4F_set(&axis, 1.0f, 1.0f, 1.0f, pParams->m_has_alpha ? 1.0f : 0);
vec4F_normalize_in_place(&axis);
}
bc7enc_vec4F minColor, maxColor;
float l = 1e+9f, h = -1e+9f;
@@ -1518,7 +1521,7 @@ uint64_t color_cell_compression(uint32_t mode, const color_cell_compressor_param
bc7enc_vec4F c1 = vec4F_add(&meanColor, &b1);
minColor = vec4F_saturate(&c0);
maxColor = vec4F_saturate(&c1);
bc7enc_vec4F whiteVec;
vec4F_set_scalar(&whiteVec, 1.0f);
if (vec4F_dot(&minColor, &whiteVec) > vec4F_dot(&maxColor, &whiteVec))
@@ -1542,7 +1545,7 @@ uint64_t color_cell_compression(uint32_t mode, const color_cell_compressor_param
// First find a solution using the block's PCA.
if (!find_optimal_solution(mode, minColor, maxColor, pParams, pResults))
return 0;
for (uint32_t i = 0; i < pComp_params->m_least_squares_passes; i++)
{
// Now try to refine the solution using least squares by computing the optimal endpoints from the current selectors.
@@ -1556,11 +1559,11 @@ uint64_t color_cell_compression(uint32_t mode, const color_cell_compressor_param
xl = vec4F_mul(&xl, (1.0f / 255.0f));
xh = vec4F_mul(&xh, (1.0f / 255.0f));
if (!find_optimal_solution(mode, xl, xh, pParams, pResults))
return 0;
}
if ((!pParams->m_pForce_selectors) && (pComp_params->m_uber_level > 0))
{
// In uber level 1, try varying the selectors a little, somewhat like cluster fit would. First try incrementing the minimum selectors,
@@ -1597,7 +1600,7 @@ uint64_t color_cell_compression(uint32_t mode, const color_cell_compressor_param
xl = vec4F_mul(&xl, (1.0f / 255.0f));
xh = vec4F_mul(&xh, (1.0f / 255.0f));
if (!find_optimal_solution(mode, xl, xh, pParams, pResults))
return 0;
@@ -1616,7 +1619,7 @@ uint64_t color_cell_compression(uint32_t mode, const color_cell_compressor_param
xl = vec4F_mul(&xl, (1.0f / 255.0f));
xh = vec4F_mul(&xh, (1.0f / 255.0f));
if (!find_optimal_solution(mode, xl, xh, pParams, pResults))
return 0;
@@ -1673,7 +1676,7 @@ uint64_t color_cell_compression(uint32_t mode, const color_cell_compressor_param
}
}
}
if (!pParams->m_pForce_selectors)
{
// Try encoding the partition as a single color by using the optimal single colors tables to encode the block to its mean.
@@ -1754,13 +1757,13 @@ uint64_t color_cell_compression(uint32_t mode, const color_cell_compressor_param
#if BC7ENC_CHECK_OVERALL_ERROR
check_best_overall_error(pParams, pResults);
#endif
return pResults->m_best_overall_err;
}
uint64_t color_cell_compression_est_astc(
uint32_t num_weights, uint32_t num_comps, const uint32_t *pWeight_table,
uint32_t num_pixels, const color_quad_u8* pPixels,
uint32_t num_pixels, const color_quad_u8* pPixels,
uint64_t best_err_so_far, const uint32_t weights[4])
{
assert(num_comps == 3 || num_comps == 4);
@@ -1807,7 +1810,7 @@ uint64_t color_cell_compression_est_astc(
color_quad_u8_set(&lowColor, lr, lg, lb, la);
color_quad_u8_set(&highColor, hr, hg, hb, ha);
// Place endpoints at bbox diagonals and compute interpolated colors
// Place endpoints at bbox diagonals and compute interpolated colors
color_quad_u8 weightedColors[32];
weightedColors[0] = lowColor;
+7 -7
View File
@@ -1,5 +1,5 @@
// File: basisu_bc7enc.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@ namespace basisu
#define BC7ENC_TRUE (1)
#define BC7ENC_FALSE (0)
typedef struct { float m_c[4]; } bc7enc_vec4F;
extern const float g_bc7_weights1x[2 * 4];
@@ -36,9 +36,9 @@ namespace basisu
extern const float g_astc_weights4x[16 * 4];
extern const float g_astc_weights5x[32 * 4];
extern const float g_astc_weights_3levelsx[3 * 4];
extern basist::astc_quant_bin g_astc_sorted_order_unquant[basist::BC7ENC_TOTAL_ASTC_RANGES][256]; // [sorted unquantized order]
struct color_cell_compressor_params
{
uint32_t m_num_pixels;
@@ -94,12 +94,12 @@ namespace basisu
};
uint64_t color_cell_compression(uint32_t mode, const color_cell_compressor_params* pParams, color_cell_compressor_results* pResults, const bc7enc_compress_block_params* pComp_params);
uint64_t color_cell_compression_est_astc(
uint32_t num_weights, uint32_t num_comps, const uint32_t* pWeight_table,
uint32_t num_pixels, const basist::color_quad_u8* pPixels,
uint64_t best_err_so_far, const uint32_t weights[4]);
inline void bc7enc_compress_block_params_init_linear_weights(bc7enc_compress_block_params* p)
{
p->m_perceptual = BC7ENC_FALSE;
@@ -128,5 +128,5 @@ namespace basisu
// bc7enc_compress_block_init() MUST be called before calling bc7enc_compress_block() (or you'll get artifacts).
void bc7enc_compress_block_init();
} // namespace basisu
File diff suppressed because it is too large Load Diff
+539 -85
View File
@@ -1,5 +1,5 @@
// basisu_comp.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -18,9 +18,12 @@
#include "basisu_basis_file.h"
#include "../transcoder/basisu_transcoder.h"
#include "basisu_uastc_enc.h"
#include "basisu_uastc_hdr_4x4_enc.h"
#include "basisu_astc_hdr_6x6_enc.h"
#include "basisu_astc_ldr_encode.h"
#define BASISU_LIB_VERSION 116
#define BASISU_LIB_VERSION_STRING "1.16"
#define BASISU_LIB_VERSION 210
#define BASISU_LIB_VERSION_STRING "2.10"
#ifndef BASISD_SUPPORT_KTX2
#error BASISD_SUPPORT_KTX2 is undefined
@@ -50,11 +53,18 @@ namespace basisu
const float BASISU_DEFAULT_HYBRID_SEL_CB_QUALITY_THRESH = 2.0f;
const uint32_t BASISU_MAX_IMAGE_DIMENSION = 16384;
const uint32_t BASISU_QUALITY_MIN = 1;
// The original ETC1S specific (non-unified) quality level
const uint32_t BASISU_QUALITY_MIN = 1; // note 0 is also technically valid in the code/API for ETC1S; the difference in quality is tiny (both result in very small codebooks)
const uint32_t BASISU_QUALITY_MAX = 255;
const uint32_t BASISU_MAX_ENDPOINT_CLUSTERS = basisu_frontend::cMaxEndpointClusters;
const uint32_t BASISU_MAX_SELECTOR_CLUSTERS = basisu_frontend::cMaxSelectorClusters;
// [1,100] are also the valid unified quality levels
const uint32_t BASISU_XUASTC_QUALITY_MIN = 1;
const uint32_t BASISU_XUASTC_QUALITY_MAX = 100;
const uint32_t BASISU_MAX_SLICES = 0xFFFFFF;
const int BASISU_RDO_UASTC_DICT_SIZE_DEFAULT = 4096; // 32768;
@@ -75,12 +85,17 @@ namespace basisu
m_height = 0;
m_basis_rgb_avg_psnr = 0.0f;
m_basis_rgb_avg_log2_psnr = 0.0f;
m_basis_rgba_avg_psnr = 0.0f;
m_basis_a_avg_psnr = 0.0f;
m_basis_luma_709_psnr = 0.0f;
m_basis_luma_601_psnr = 0.0f;
m_basis_luma_709_ssim = 0.0f;
m_basis_rgb_avg_bc6h_psnr = 0.0f;
m_basis_rgb_avg_bc6h_log2_psnr = 0.0f;
m_bc7_rgb_avg_psnr = 0.0f;
m_bc7_rgba_avg_psnr = 0.0f;
m_bc7_a_avg_psnr = 0.0f;
@@ -92,21 +107,29 @@ namespace basisu
m_best_etc1s_luma_709_psnr = 0.0f;
m_best_etc1s_luma_601_psnr = 0.0f;
m_best_etc1s_luma_709_ssim = 0.0f;
m_opencl_failed = false;
}
std::string m_filename;
uint32_t m_width;
uint32_t m_height;
// .basis compressed (ETC1S or UASTC statistics)
// .basis/.ktx2 compressed (LDR: ETC1S or UASTC statistics, HDR: transcoded BC6H statistics)
float m_basis_rgb_avg_psnr;
float m_basis_rgb_avg_log2_psnr;
float m_basis_rgba_avg_psnr;
float m_basis_a_avg_psnr;
float m_basis_luma_709_psnr;
float m_basis_luma_601_psnr;
float m_basis_luma_709_ssim;
// BC7 statistics
// UASTC HDR only.
float m_basis_rgb_avg_bc6h_psnr;
float m_basis_rgb_avg_bc6h_log2_psnr;
// LDR: BC7 statistics
float m_bc7_rgb_avg_psnr;
float m_bc7_rgba_avg_psnr;
float m_bc7_a_avg_psnr;
@@ -114,11 +137,24 @@ namespace basisu
float m_bc7_luma_601_psnr;
float m_bc7_luma_709_ssim;
// Highest achievable quality ETC1S statistics
// LDR: Highest achievable quality ETC1S statistics, for development/comparison
float m_best_etc1s_rgb_avg_psnr;
float m_best_etc1s_luma_709_psnr;
float m_best_etc1s_luma_601_psnr;
float m_best_etc1s_luma_709_ssim;
bool m_opencl_failed;
};
enum class hdr_modes
{
// standard but constrained ASTC HDR 4x4 tex data that can be rapidly transcoded to BC6H
cUASTC_HDR_4X4,
// standard RDO optimized or non-RDO (highest quality) ASTC HDR 6x6 tex data that can be rapidly re-encoded to BC6H
cASTC_HDR_6X6,
// a custom intermediate format based off ASTC HDR that can be rapidly decoded straight to ASTC HDR or re-encoded to BC6H
cUASTC_HDR_6X6_INTERMEDIATE,
cTotal
};
template<bool def>
@@ -202,29 +238,41 @@ namespace basisu
bool m_changed;
};
// Low-level direct compressor parameters.
// Also see basis_compress() below for a simplified C-style interface.
struct basis_compressor_params
{
basis_compressor_params() :
m_compression_level((int)BASISU_DEFAULT_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_COMPRESSION_LEVEL),
m_xuastc_or_astc_ldr_basis_tex_format(-1, -1, INT_MAX),
// Note the ETC1S default compression/effort level is 2, not the command line default of 1.
m_etc1s_compression_level((int)BASISU_DEFAULT_ETC1S_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_ETC1S_COMPRESSION_LEVEL),
m_selector_rdo_thresh(BASISU_DEFAULT_SELECTOR_RDO_THRESH, 0.0f, 1e+10f),
m_endpoint_rdo_thresh(BASISU_DEFAULT_ENDPOINT_RDO_THRESH, 0.0f, 1e+10f),
m_mip_scale(1.0f, .000125f, 4.0f),
m_mip_smallest_dimension(1, 1, 16384),
m_max_endpoint_clusters(512),
m_max_selector_clusters(512),
m_etc1s_max_endpoint_clusters(0),
m_etc1s_max_selector_clusters(0),
m_quality_level(-1),
m_pack_uastc_flags(cPackUASTCLevelDefault),
m_rdo_uastc_quality_scalar(1.0f, 0.001f, 50.0f),
m_rdo_uastc_dict_size(BASISU_RDO_UASTC_DICT_SIZE_DEFAULT, BASISU_RDO_UASTC_DICT_SIZE_MIN, BASISU_RDO_UASTC_DICT_SIZE_MAX),
m_rdo_uastc_max_smooth_block_error_scale(UASTC_RDO_DEFAULT_SMOOTH_BLOCK_MAX_ERROR_SCALE, 1.0f, 300.0f),
m_rdo_uastc_smooth_block_max_std_dev(UASTC_RDO_DEFAULT_MAX_SMOOTH_BLOCK_STD_DEV, .01f, 65536.0f),
m_rdo_uastc_max_allowed_rms_increase_ratio(UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO, .01f, 100.0f),
m_rdo_uastc_skip_block_rms_thresh(UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH, .01f, 100.0f),
m_pack_uastc_ldr_4x4_flags(cPackUASTCLevelDefault),
m_rdo_uastc_ldr_4x4_quality_scalar(1.0f, 0.001f, 50.0f),
m_rdo_uastc_ldr_4x4_dict_size(BASISU_RDO_UASTC_DICT_SIZE_DEFAULT, BASISU_RDO_UASTC_DICT_SIZE_MIN, BASISU_RDO_UASTC_DICT_SIZE_MAX),
m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale(UASTC_RDO_DEFAULT_SMOOTH_BLOCK_MAX_ERROR_SCALE, 1.0f, 300.0f),
m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev(UASTC_RDO_DEFAULT_MAX_SMOOTH_BLOCK_STD_DEV, .01f, 65536.0f),
m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio(UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO, .01f, 100.0f),
m_rdo_uastc_ldr_4x4_skip_block_rms_thresh(UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH, .01f, 100.0f),
m_resample_width(0, 1, 16384),
m_resample_height(0, 1, 16384),
m_resample_factor(0.0f, .00125f, 100.0f),
m_ktx2_uastc_supercompression(basist::KTX2_SS_NONE),
m_ktx2_zstd_supercompression_level(6, INT_MIN, INT_MAX),
m_transcode_flags(0, 0, UINT32_MAX),
m_ldr_hdr_upconversion_nit_multiplier(0.0f, 0.0f, basist::MAX_HALF_FLOAT),
m_ldr_hdr_upconversion_black_bias(0.0f, 0.0f, 1.0f),
m_xuastc_ldr_effort_level(astc_ldr::EFFORT_LEVEL_DEF, astc_ldr::EFFORT_LEVEL_MIN, astc_ldr::EFFORT_LEVEL_MAX),
m_xuastc_ldr_syntax((int)basist::astc_ldr_t::xuastc_ldr_syntax::cFullZStd, (int)basist::astc_ldr_t::xuastc_ldr_syntax::cFullArith, (int)basist::astc_ldr_t::xuastc_ldr_syntax::cFullZStd),
m_ls_min_psnr(35.0f, 0.0f, 100.0f), m_ls_min_alpha_psnr(38.0f, 0.0f, 100.0f),
m_ls_thresh_psnr(1.5f, 0.0f, 100.0f), m_ls_thresh_alpha_psnr(0.75f, 0.0f, 100.0f),
m_ls_thresh_edge_psnr(1.0f, 0.0f, 100.00f), m_ls_thresh_edge_alpha_psnr(0.5f, 0.0f, 100.00f),
m_pJob_pool(nullptr)
{
clear();
@@ -232,7 +280,13 @@ namespace basisu
void clear()
{
m_format_mode = basist::basis_tex_format::cETC1S;
m_uastc.clear();
m_hdr.clear();
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4;
m_xuastc_or_astc_ldr_basis_tex_format = -1;
m_use_opencl.clear();
m_status_output.clear();
@@ -252,9 +306,10 @@ namespace basisu
m_no_selector_rdo.clear();
m_selector_rdo_thresh.clear();
m_read_source_images.clear();
m_write_output_basis_files.clear();
m_compression_level.clear();
m_write_output_basis_or_ktx2_files.clear();
m_etc1s_compression_level.clear();
m_compute_stats.clear();
m_print_stats.clear();
m_check_for_alpha.clear();
m_force_alpha.clear();
m_multithreading.clear();
@@ -279,8 +334,8 @@ namespace basisu
m_mip_fast.clear();
m_mip_smallest_dimension.clear();
m_max_endpoint_clusters = 0;
m_max_selector_clusters = 0;
m_etc1s_max_endpoint_clusters = 0;
m_etc1s_max_selector_clusters = 0;
m_quality_level = -1;
m_tex_type = basist::cBASISTexType2D;
@@ -288,15 +343,15 @@ namespace basisu
m_userdata1 = 0;
m_us_per_frame = 0;
m_pack_uastc_flags = cPackUASTCLevelDefault;
m_rdo_uastc.clear();
m_rdo_uastc_quality_scalar.clear();
m_rdo_uastc_max_smooth_block_error_scale.clear();
m_rdo_uastc_smooth_block_max_std_dev.clear();
m_rdo_uastc_max_allowed_rms_increase_ratio.clear();
m_rdo_uastc_skip_block_rms_thresh.clear();
m_rdo_uastc_favor_simpler_modes_in_rdo_mode.clear();
m_rdo_uastc_multithreading.clear();
m_pack_uastc_ldr_4x4_flags = cPackUASTCLevelDefault;
m_rdo_uastc_ldr_4x4.clear();
m_rdo_uastc_ldr_4x4_quality_scalar.clear();
m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale.clear();
m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev.clear();
m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio.clear();
m_rdo_uastc_ldr_4x4_skip_block_rms_thresh.clear();
m_rdo_uastc_ldr_4x4_favor_simpler_modes_in_rdo_mode.clear();
m_rdo_uastc_ldr_4x4_multithreading.clear();
m_resample_width.clear();
m_resample_height.clear();
@@ -308,31 +363,215 @@ namespace basisu
m_ktx2_uastc_supercompression = basist::KTX2_SS_NONE;
m_ktx2_key_values.clear();
m_ktx2_zstd_supercompression_level.clear();
m_ktx2_srgb_transfer_func.clear();
m_ktx2_and_basis_srgb_transfer_function.clear();
m_validate_output_data.clear();
m_transcode_flags.clear();
m_ldr_hdr_upconversion_srgb_to_linear.clear();
m_hdr_favor_astc.clear();
m_uastc_hdr_4x4_options.init();
m_astc_hdr_6x6_options.clear();
m_ldr_hdr_upconversion_nit_multiplier.clear();
m_ldr_hdr_upconversion_black_bias.clear();
m_xuastc_ldr_effort_level.clear();
m_xuastc_ldr_use_dct.clear();
m_xuastc_ldr_use_lossy_supercompression.clear();
m_xuastc_ldr_force_disable_subsets.clear();
m_xuastc_ldr_force_disable_rgb_dual_plane.clear();
m_xuastc_ldr_syntax.clear();
m_ls_min_psnr.clear();
m_ls_min_alpha_psnr.clear();
m_ls_thresh_psnr.clear();
m_ls_thresh_alpha_psnr.clear();
m_ls_thresh_edge_psnr.clear();
m_ls_thresh_edge_alpha_psnr.clear();
for (uint32_t i = 0; i < 4; i++)
m_xuastc_ldr_channel_weights[i] = 1;
m_xuastc_ldr_blurring.clear();
m_pJob_pool = nullptr;
}
// True to generate UASTC .basis file data, otherwise ETC1S.
// Configures the compressor's mode by setting the proper parameters (which were preserved for backwards compatibility with old code).
// This is by far the preferred way of controlling which codec mode the compressor will select.
void set_format_mode(basist::basis_tex_format mode)
{
m_format_mode = mode;
switch (mode)
{
case basist::basis_tex_format::cETC1S:
{
// ETC1S
m_xuastc_or_astc_ldr_basis_tex_format = -1;
m_hdr = false;
m_uastc = false;
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter
break;
}
case basist::basis_tex_format::cUASTC_LDR_4x4:
{
// UASTC LDR 4x4
m_xuastc_or_astc_ldr_basis_tex_format = -1;
m_hdr = false;
m_uastc = true;
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter
break;
}
case basist::basis_tex_format::cUASTC_HDR_4x4:
{
// UASTC HDR 4x4
m_xuastc_or_astc_ldr_basis_tex_format = -1;
m_hdr = true;
m_uastc = true;
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4;
break;
}
case basist::basis_tex_format::cASTC_HDR_6x6:
{
// ASTC HDR 6x6
m_xuastc_or_astc_ldr_basis_tex_format = -1;
m_hdr = true;
m_uastc = true;
m_hdr_mode = hdr_modes::cASTC_HDR_6X6;
break;
}
case basist::basis_tex_format::cUASTC_HDR_6x6_INTERMEDIATE:
{
// UASTC HDR 6x6
m_xuastc_or_astc_ldr_basis_tex_format = -1;
m_hdr = true;
m_uastc = true;
m_hdr_mode = hdr_modes::cUASTC_HDR_6X6_INTERMEDIATE;
break;
}
case basist::basis_tex_format::cXUASTC_LDR_4x4:
case basist::basis_tex_format::cXUASTC_LDR_5x4:
case basist::basis_tex_format::cXUASTC_LDR_5x5:
case basist::basis_tex_format::cXUASTC_LDR_6x5:
case basist::basis_tex_format::cXUASTC_LDR_6x6:
case basist::basis_tex_format::cXUASTC_LDR_8x5:
case basist::basis_tex_format::cXUASTC_LDR_8x6:
case basist::basis_tex_format::cXUASTC_LDR_10x5:
case basist::basis_tex_format::cXUASTC_LDR_10x6:
case basist::basis_tex_format::cXUASTC_LDR_8x8:
case basist::basis_tex_format::cXUASTC_LDR_10x8:
case basist::basis_tex_format::cXUASTC_LDR_10x10:
case basist::basis_tex_format::cXUASTC_LDR_12x10:
case basist::basis_tex_format::cXUASTC_LDR_12x12:
case basist::basis_tex_format::cASTC_LDR_4x4:
case basist::basis_tex_format::cASTC_LDR_5x4:
case basist::basis_tex_format::cASTC_LDR_5x5:
case basist::basis_tex_format::cASTC_LDR_6x5:
case basist::basis_tex_format::cASTC_LDR_6x6:
case basist::basis_tex_format::cASTC_LDR_8x5:
case basist::basis_tex_format::cASTC_LDR_8x6:
case basist::basis_tex_format::cASTC_LDR_10x5:
case basist::basis_tex_format::cASTC_LDR_10x6:
case basist::basis_tex_format::cASTC_LDR_8x8:
case basist::basis_tex_format::cASTC_LDR_10x8:
case basist::basis_tex_format::cASTC_LDR_10x10:
case basist::basis_tex_format::cASTC_LDR_12x10:
case basist::basis_tex_format::cASTC_LDR_12x12:
{
// ASTC LDR 4x4-12x12 or XUASTC LDR 4x4-12x12
m_xuastc_or_astc_ldr_basis_tex_format = (int)mode;
m_hdr = false;
m_uastc = true;
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter
break;
}
default:
assert(0);
break;
}
}
// Like set_format_mode() but also sets the effort and quality parameters appropriately for the selected mode.
// "Effort" (perf. vs. highest achievable quality) and "quality" (quality vs. bitrate) parameters are now mode dependent.
// Effort ranges from [0,10] and quality ranges from [1,100], unless they are -1 in which case you get the codec's default settings.
bool set_format_mode_and_effort(basist::basis_tex_format mode, int effort = -1, bool set_defaults = true);
bool set_format_mode_and_quality_effort(basist::basis_tex_format mode, int quality = -1, int effort = -1, bool set_defaults = true);
// Sets all the sRGB-related options (m_perceptual, m_mip_srgb, m_ktx2_and_basis_srgb_transfer_function) to the specified value.
void set_srgb_options(bool srgb_flag)
{
m_perceptual = srgb_flag;
m_mip_srgb = srgb_flag;
m_ktx2_and_basis_srgb_transfer_function = srgb_flag;
}
// Simpler helpers - I wish this was easier, but backwards API compat is also valuable.
bool is_etc1s() const
{
return !m_uastc;
}
bool is_uastc_ldr_4x4() const
{
return m_uastc && !m_hdr && (m_xuastc_or_astc_ldr_basis_tex_format == -1);
}
bool is_uastc_hdr_4x4() const
{
return m_uastc && m_hdr && (m_hdr_mode == hdr_modes::cUASTC_HDR_4X4);
}
// By default we generate LDR ETC1S data.
// Ideally call set_format_mode() above instead of directly manipulating the below fields. These individual parameters are for backwards API compatibility.
// - If m_uastc is false you get ETC1S (the default).
// - If m_uastc is true, and m_hdr is not true, and m_xuastc_or_astc_ldr_basis_tex_format==-1, we generate UASTC 4x4 LDR data (8bpp with or without RDO).
// - If m_uastc is true, and m_hdr is not true, and m_xuastc_or_astc_ldr_basis_tex_format!=-1, we generate XUASTC 4x4-12x12 or ASTC 4x4-12x12 LDR data, controlled by m_xuastc_or_astc_ldr_basis_tex_format.
// - If m_uastc is true and m_hdr is true, we generate 4x4 or 6x6 HDR data, controlled by m_hdr_mode.
// True to generate UASTC .basis/.KTX2 file data, otherwise ETC1S.
// Should be true for any non-ETC1S format (UASTC 4x4 LDR, UASTC 4x4 HDR, RDO ASTC 6x6 HDR, UASTC 6x6 HDR, or ASTC/XUASTC LDR 4x4-12x12).
// Note: Ideally call set_format_mode() or set_format_mode_and_quality_effort() above instead.
// Many of these individual parameters are for backwards API compatibility.
bool_param<false> m_uastc;
// Set m_hdr to true to switch to UASTC HDR mode. m_hdr_mode then controls which format is output.
// m_hdr_mode then controls which format is output (4x4, 6x6, or 6x6 intermediate).
// Note: Ideally call set_format_mode() instead. This is for backwards API compatibility.
bool_param<false> m_hdr;
// If m_hdr is true, this specifies which mode we operate in (currently UASTC 4x4 HDR or ASTC 6x6 HDR). Defaults to UASTC 4x4 HDR for backwards compatibility.
// Note: Ideally call set_format_mode() instead. This is for backwards API compatibility.
hdr_modes m_hdr_mode;
// If not -1: Generate XUASTC or ASTC LDR 4x4-12x12 files in the specified basis_tex_format (which also sets the ASTC block size). If -1 (the default), don't generate XUASTC/ASTC LDR files.
// m_uastc must also be set to true if this is not -1.
// Note: Ideally call set_format_mode() instead.
param<int> m_xuastc_or_astc_ldr_basis_tex_format; // enum basis_tex_format
// True to enable OpenCL if it's available. The compressor will fall back to CPU encoding if something goes wrong.
bool_param<false> m_use_opencl;
// If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG images to read.
// Otherwise, the compressor processes the images in m_source_images.
// If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG etc. images to read.
// Otherwise, the compressor processes the images in m_source_images or m_source_images_hdr.
basisu::vector<std::string> m_source_filenames;
basisu::vector<std::string> m_source_alpha_filenames;
// An array of 2D LDR/SDR source images.
basisu::vector<image> m_source_images;
// An array of 2D HDR source images.
basisu::vector<imagef> m_source_images_hdr;
// Stores mipmaps starting from level 1. Level 0 is still stored in m_source_images, as usual.
// If m_source_mipmaps isn't empty, automatic mipmap generation isn't done. m_source_mipmaps.size() MUST equal m_source_images.size() or the compressor returns an error.
// The compressor applies the user-provided swizzling (in m_swizzle) to these images.
basisu::vector< basisu::vector<image> > m_source_mipmap_images;
basisu::vector< basisu::vector<imagef> > m_source_mipmap_images_hdr;
// Filename of the output basis file
// Filename of the output basis/ktx2 file
std::string m_out_filename;
// The params are done this way so we can detect when the user has explictly changed them.
@@ -345,17 +584,21 @@ namespace basisu
// Output debug information during compression
bool_param<false> m_debug;
// Low-level ETC1S data validation during encoding (slower/development).
bool_param<false> m_validate_etc1s;
// m_debug_images is pretty slow
bool_param<false> m_debug_images;
// ETC1S compression level, from 0 to BASISU_MAX_COMPRESSION_LEVEL (higher is slower).
// ETC1S compression effort level, from 0 to BASISU_MAX_ETC1S_COMPRESSION_LEVEL (higher is slower).
// This parameter controls numerous internal encoding speed vs. compression efficiency/performance tradeoffs.
// Note this is NOT the same as the ETC1S quality level, and most users shouldn't change this.
param<int> m_compression_level;
param<int> m_etc1s_compression_level;
// Use perceptual sRGB colorspace metrics instead of linear
// Use perceptual sRGB colorspace metrics instead of linear.
// Note: You probably also want to set m_ktx2_srgb_transfer_func to match.
// Note: This member variable was previously called "m_perceptual".
bool_param<true> m_perceptual;
// Disable selector RDO, for faster compression but larger files
@@ -368,26 +611,33 @@ namespace basisu
// Read source images from m_source_filenames/m_source_alpha_filenames
bool_param<false> m_read_source_images;
// Write the output basis file to disk using m_out_filename
bool_param<false> m_write_output_basis_files;
// Write the output basis/ktx2 file to disk using m_out_filename
bool_param<false> m_write_output_basis_or_ktx2_files;
// Compute and display image metrics
bool_param<false> m_compute_stats;
// Print stats to stdout, if m_compute_stats is true.
bool_param<true> m_print_stats;
// Check to see if any input image has an alpha channel, if so then the output basis file will have alpha channels
// Check to see if any input image has an alpha channel, if so then the output basis/ktx2 file will have alpha channels
bool_param<true> m_check_for_alpha;
// Always put alpha slices in the output basis file, even when the input doesn't have alpha
// Always put alpha slices in the output basis/ktx2 file, even when the input doesn't have alpha
bool_param<false> m_force_alpha;
// True to enable multithreading in various compressors.
// Note currently, some compressors (like ASTC/XUASTC LDR) will utilize threading anyway if the job pool is more than one thread.
bool_param<true> m_multithreading;
// Split the R channel to RGB and the G channel to alpha, then write a basis file with alpha channels
char m_swizzle[4];
// Split the R channel to RGB and the G channel to alpha, then write a basis/ktx2 file with alpha channels
uint8_t m_swizzle[4];
// Renormalize normal map normals after loading image
bool_param<false> m_renormalize;
// If true the front end will not use 2 level endpoint codebook searching, for slightly higher quality but much slower execution.
// Note some m_compression_level's disable this automatically.
// Note some m_etc1s_compression_level's disable this automatically.
bool_param<false> m_disable_hierarchical_endpoint_codebooks;
// mipmap generation parameters
@@ -401,48 +651,112 @@ namespace basisu
bool_param<true> m_mip_fast;
param<int> m_mip_smallest_dimension;
// Codebook size (quality) control.
// If m_quality_level != -1, it controls the quality level. It ranges from [1,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX].
// ETC1S codebook size (quality) control.
// If m_quality_level (previously named m_etc1s_quality_level) != -1, it controls the quality level. It ranges from [1,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX].
// Otherwise m_max_endpoint_clusters/m_max_selector_clusters controls the codebook sizes directly.
uint32_t m_max_endpoint_clusters;
uint32_t m_max_selector_clusters;
int m_quality_level;
uint32_t m_etc1s_max_endpoint_clusters;
uint32_t m_etc1s_max_selector_clusters;
// Quality level (bitrate vs. distortion tradeoff) control for ETC1S or XUASTC LDR 4x4-12x12.
// ETC1S: Must set to [1,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX] to control quality vs. bitrate. If -1 (the default!), quality is controlled by m_etc1s_max_endpoint_clusters and m_etc1s_max_selector_clusters directly.
// XUASTC LDR: Must not be -1 for DCT.
int m_quality_level;
// m_tex_type, m_userdata0, m_userdata1, m_framerate - These fields go directly into the Basis file header.
// m_tex_type, m_userdata0, m_userdata1, m_framerate - These fields go directly into the .basis file header.
basist::basis_texture_type m_tex_type;
uint32_t m_userdata0;
uint32_t m_userdata1;
uint32_t m_us_per_frame;
// UASTC LDR 4x4 parameters
// cPackUASTCLevelDefault, etc.
uint32_t m_pack_uastc_flags;
bool_param<false> m_rdo_uastc;
param<float> m_rdo_uastc_quality_scalar;
param<int> m_rdo_uastc_dict_size;
param<float> m_rdo_uastc_max_smooth_block_error_scale;
param<float> m_rdo_uastc_smooth_block_max_std_dev;
param<float> m_rdo_uastc_max_allowed_rms_increase_ratio;
param<float> m_rdo_uastc_skip_block_rms_thresh;
bool_param<true> m_rdo_uastc_favor_simpler_modes_in_rdo_mode;
bool_param<true> m_rdo_uastc_multithreading;
uint32_t m_pack_uastc_ldr_4x4_flags;
bool_param<false> m_rdo_uastc_ldr_4x4;
param<float> m_rdo_uastc_ldr_4x4_quality_scalar; // RDO lambda for UASTC 4x4 LDR
param<int> m_rdo_uastc_ldr_4x4_dict_size;
param<float> m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale;
param<float> m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev;
param<float> m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio;
param<float> m_rdo_uastc_ldr_4x4_skip_block_rms_thresh;
bool_param<true> m_rdo_uastc_ldr_4x4_favor_simpler_modes_in_rdo_mode;
bool_param<true> m_rdo_uastc_ldr_4x4_multithreading;
// Resample input texture after loading
param<int> m_resample_width;
param<int> m_resample_height;
param<float> m_resample_factor;
// ETC1S global codebook control
const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks;
// KTX2 specific parameters.
// Internally, the compressor always creates a .basis file then it converts that lossless to KTX2.
// Internally, the compressor always creates a .basis file then it converts that losslessly to KTX2.
bool_param<false> m_create_ktx2_file;
basist::ktx2_supercompression m_ktx2_uastc_supercompression;
basist::ktx2_transcoder::key_value_vec m_ktx2_key_values;
param<int> m_ktx2_zstd_supercompression_level;
bool_param<false> m_ktx2_srgb_transfer_func;
// Note: The default for this parameter (which used to be "m_ktx2_srgb_transfer_func") used to be false, now setting this to true and renaming to m_ktx2_and_basis_srgb_transfer_function.
// Also see m_perceptual and m_mip_srgb, which should in most uses be the same.
// This also controls the XUASTC LDR ASTC decode profile (linear vs. sRGB) in the simulated decoder block.
// For XUASTC LDR, it's also still used when generating .basis files vs. .KTX2.
bool_param<true> m_ktx2_and_basis_srgb_transfer_function; // false = linear transfer function, true = sRGB transfer function
// HDR codec specific options
uastc_hdr_4x4_codec_options m_uastc_hdr_4x4_options;
astc_6x6_hdr::astc_hdr_6x6_global_config m_astc_hdr_6x6_options; // also UASTC HDR 6x6i
// True to try transcoding the generated output after compression to a few formats.
bool_param<false> m_validate_output_data;
// The flags to use while transcoding if m_validate_output_data
param<uint32_t> m_transcode_flags;
// LDR->HDR upconversion parameters.
//
// If true, LDR images (such as PNG) will be converted to normalized [0,1] linear light (via a sRGB->Linear conversion), or absolute luminance (nits or candelas per meter squared), and then processed as HDR.
// Otherwise, LDR images are assumed to already be in linear light (i.e. they don't use the sRGB transfer function).
bool_param<true> m_ldr_hdr_upconversion_srgb_to_linear;
// m_ldr_hdr_upconversion_nit_multiplier is only used when loading SDR/LDR images and compressing to an HDR output format.
// By default m_ldr_hdr_upconversion_nit_multiplier is 0. It's an override for the default, which is now 100.0 nits (LDR_TO_HDR_NITS).
// UASTC HDR 4x4: The default multiplier of 1.0 was previously used in this codec's original release. Note this encoder isn't dependent on absolute nits, unlike the ASTC 6x6 HDR encoder.
// RDO ASTC HDR 6x6/UASTC HDR 6x6i: These encoders expect inputs in absolute nits, so the LDR upconversion luminance multiplier default will be 100 nits. (Most SDR monitors were/are 80-100 nits or so.)
param<float> m_ldr_hdr_upconversion_nit_multiplier;
// The optional sRGB space bias to use during LDR->HDR upconversion. Should be between [0,.49] or so. Only applied on black (0.0) color components.
// Defaults to no bias (0.0f).
param<float> m_ldr_hdr_upconversion_black_bias;
// If true, ASTC HDR quality is favored more than BC6H quality by the dual target encoder. Otherwise it's a rough balance.
// UASTC HDR 4x4
bool_param<false> m_hdr_favor_astc;
// XUASTC LDR 4x4-12x12 specific options
param<int> m_xuastc_ldr_effort_level;
bool_param<false> m_xuastc_ldr_use_dct; // set the DCT quality above using m_quality_level, [1,100]
bool_param<false> m_xuastc_ldr_use_lossy_supercompression; // allows the compressor to introduce a bounded amount of distortion if doing so would make smaller files (actually ASTC or XUASTC)
bool_param<false> m_xuastc_ldr_force_disable_subsets; // disable 2-3 subset usage in all effort levels, faster encoding, faster transcoding to BC7, but lower quality)
bool_param<false> m_xuastc_ldr_force_disable_rgb_dual_plane; // disable RGB dual plane usage (still can use dual plane on alpha blocks), for faster transcoding to BC7 but lower quality
param<int> m_xuastc_ldr_syntax; // favor faster decompression over ratio, default is basist::astc_ldr_t::xuastc_ldr_syntax::cFullZstd (fastest transcoding but lower ratio)
uint32_t m_xuastc_ldr_channel_weights[4];
bool_param<false> m_xuastc_ldr_blurring; // experimental, not recommended, very slow
// XUASTC Lossy supercompression PSNR threshold parameters
param<float> m_ls_min_psnr, m_ls_min_alpha_psnr;
param<float> m_ls_thresh_psnr, m_ls_thresh_alpha_psnr;
param<float> m_ls_thresh_edge_psnr, m_ls_thresh_edge_alpha_psnr;
// Job pool, MUST not be nullptr;
job_pool *m_pJob_pool;
// Returns the current format mode as set by set_format_mode() above.
// Because of backwards API compatibility we don't use this directly yet, it's just here to aid the transition to the new API.
basist::basis_tex_format get_format_mode() const { return m_format_mode; }
private:
// This is set by set_format_mode() above. For backwards API compat we don't use it directly, it's just here to aid the transition to the new API.
basist::basis_tex_format m_format_mode;
};
// Important: basisu_encoder_init() MUST be called first before using this class.
@@ -465,12 +779,13 @@ namespace basisu
cECFailedValidating,
cECFailedEncodeUASTC,
cECFailedFrontEnd,
cECFailedFontendExtract,
cECFailedFrontendExtract,
cECFailedBackend,
cECFailedCreateBasisFile,
cECFailedWritingOutput,
cECFailedUASTCRDOPostProcess,
cECFailedCreateKTX2File
cECFailedCreateKTX2File,
cECFailedInvalidParameters
};
error_code process();
@@ -483,8 +798,14 @@ namespace basisu
const basisu::vector<image_stats> &get_stats() const { return m_stats; }
uint32_t get_basis_file_size() const { return m_basis_file_size; }
// Sum of all slice orig pixels. Intended for statistics display.
uint64_t get_total_slice_orig_texels() const { return m_total_slice_orig_texels; }
uint64_t get_basis_file_size() const { return m_basis_file_size; }
double get_basis_bits_per_texel() const { return m_basis_bits_per_texel; }
uint64_t get_ktx2_file_size() const { return m_ktx2_file_size; }
double get_ktx2_bits_per_texel() const { return m_ktx2_bits_per_texel; }
bool get_any_source_image_has_alpha() const { return m_any_source_image_has_alpha; }
@@ -492,22 +813,39 @@ namespace basisu
private:
basis_compressor_params m_params;
opencl_context_ptr m_pOpenCL_context;
// the output mode/codec
basist::basis_tex_format m_fmt_mode;
// the output mode/codec's block width/height
uint32_t m_fmt_mode_block_width;
uint32_t m_fmt_mode_block_height;
// Note these images are expanded if necessary (duplicating cols/rows) to account for block dimensions.
basisu::vector<image> m_slice_images;
basisu::vector<imagef> m_slice_images_hdr;
basisu::vector<image_stats> m_stats;
uint32_t m_basis_file_size;
uint64_t m_total_slice_orig_texels;
uint64_t m_basis_file_size;
double m_basis_bits_per_texel;
uint64_t m_ktx2_file_size;
double m_ktx2_bits_per_texel;
basisu_backend_slice_desc_vec m_slice_descs;
uint32_t m_total_blocks;
basisu_frontend m_frontend;
// These are 4x4 blocks.
pixel_block_vec m_source_blocks;
pixel_block_hdr_vec m_source_blocks_hdr;
basisu::vector<gpu_image> m_frontend_output_textures;
@@ -518,34 +856,62 @@ namespace basisu
basisu_file m_basis_file;
basisu::vector<gpu_image> m_decoded_output_textures;
basisu::vector<gpu_image> m_decoded_output_textures; // BC6H in HDR mode
basisu::vector<image> m_decoded_output_textures_unpacked;
basisu::vector<gpu_image> m_decoded_output_textures_bc7;
basisu::vector<image> m_decoded_output_textures_unpacked_bc7;
basisu::vector<imagef> m_decoded_output_textures_bc6h_hdr_unpacked; // BC6H in HDR mode
basisu::vector<gpu_image> m_decoded_output_textures_astc_hdr;
basisu::vector<imagef> m_decoded_output_textures_astc_hdr_unpacked;
uint8_vec m_output_basis_file;
uint8_vec m_output_ktx2_file;
basisu::vector<gpu_image> m_uastc_slice_textures;
basisu_backend_output m_uastc_backend_output;
// The amount the HDR input has to be scaled up in case it had to be rescaled to fit into half floats.
float m_hdr_image_scale;
// The upconversion multiplier used to load LDR images in HDR mode.
float m_ldr_to_hdr_upconversion_nit_multiplier;
// True if any loaded source images were LDR and upconverted to HDR.
bool m_upconverted_any_ldr_images;
bool m_any_source_image_has_alpha;
bool m_opencl_failed;
void check_for_hdr_inputs();
bool sanity_check_input_params();
void clean_hdr_image(imagef& src_img);
bool read_dds_source_images();
bool read_source_images();
bool extract_source_blocks();
bool process_frontend();
bool extract_frontend_texture_data();
bool process_backend();
bool create_basis_file_and_transcode();
bool write_hdr_debug_images(const char* pBasename, const imagef& img, uint32_t width, uint32_t height);
bool write_output_files_and_compute_stats();
error_code encode_slices_to_uastc();
error_code encode_slices_to_astc_6x6_hdr();
error_code encode_slices_to_uastc_4x4_hdr();
error_code encode_slices_to_uastc_4x4_ldr();
error_code encode_slices_to_xuastc_or_astc_ldr();
bool generate_mipmaps(const imagef& img, basisu::vector<imagef>& mips, bool has_alpha);
bool generate_mipmaps(const image &img, basisu::vector<image> &mips, bool has_alpha);
bool validate_texture_type_constraints();
bool validate_ktx2_constraints();
void get_dfd(uint8_vec& dfd, const basist::ktx2_header& hdr);
bool get_dfd(uint8_vec& dfd, const basist::ktx2_header& hdr);
bool create_ktx2_file();
bool pick_format_mode();
uint32_t get_block_width() const { return m_fmt_mode_block_width; }
uint32_t get_block_height() const { return m_fmt_mode_block_height; }
};
// Alternative simple C-style wrapper API around the basis_compressor class.
@@ -558,11 +924,18 @@ namespace basisu
// pImageRGBA: pointer to a 32-bpp RGBx or RGBA raster image, R first in memory, A last. Top scanline first in memory.
// width/height/pitch_in_pixels: dimensions of pImageRGBA
//
// flags_and_quality: Combination of the above flags logically OR'd with the ETC1S or UASTC level, i.e. "cFlagSRGB | cFlagGenMipsClamp | cFlagThreaded | 128" or "cFlagSRGB | cFlagGenMipsClamp | cFlagUASTC | cFlagThreaded | cPackUASTCLevelDefault".
// flags_and_quality: Combination of the above flags logically OR'd with the ETC1S or UASTC quality or effort level.
// Note: basis_compress2() variants below accept the new-style "quality_level" (0-100) and "effort_level" (0-10) parameters instead of packing them into flags_and_quality.
// In ETC1S mode, the lower 8-bits are the ETC1S quality level which ranges from [1,255] (higher=better quality/larger files)
// In UASTC mode, the lower 8-bits are the UASTC pack level (see cPackUASTCLevelFastest, etc.). Fastest/lowest quality is 0, so be sure to set it correctly.
// In UASTC LDR 4x4 mode, the lower 8-bits are the UASTC LDR/HDR pack or effort level (see cPackUASTCLevelFastest to cPackUASTCLevelVerySlow). Fastest/lowest quality is 0, so be sure to set it correctly. Valid values are [0,4] for both LDR/HDR.
// In UASTC HDR 4x4 mode, the lower 8-bits are the codec's effort level. Valid range is [uastc_hdr_4x4_codec_options::cMinLevel, uastc_hdr_4x4_codec_options::cMaxLevel]. Higher=better quality, but slower.
// In RDO ASTC HDR 6x6/UASTC HDR 6x6 mode, the lower 8-bits are the codec's effort level. Valid range is [0,astc_6x6_hdr::ASTC_HDR_6X6_MAX_USER_COMP_LEVEL]. Higher levels=better quality, but slower.
// In XUASTC/ASTC LDR 4x4-12x12 mode, the lower 8-bits are the compressor's effort level from [0,10] (astc_ldr_t::EFFORT_LEVEL_MIN, astc_ldr_t::EFFORT_LEVEL_MAX).
//
// uastc_rdo_quality: Float UASTC RDO quality level (0=no change, higher values lower quality but increase compressibility, initially try .5-1.5)
// float uastc_rdo_or_dct_quality:
// UASTC LDR 4x4 RDO quality level: RDO lambda setting - 0=no change/highest quality. Higher values lower quality but increase compressibility, initially try .5-1.5.
// RDO ASTC 6x6 HDR/UASTC 6x6 HDR: RDO lambda setting. 0=no change/highest quality. Higher values lower quality but increase compressibility, initially try 250-2000 (HDR) or 1000-10000 (LDR/SDR inputs upconverted to HDR).
// In XUASTC/ASTC LDR 4x4-12x12 mode, this is the [1,100] weight grid DCT quality level.
//
// pSize: Returns the output data's compressed size in bytes
//
@@ -574,36 +947,117 @@ namespace basisu
cFlagDebug = 1 << 10, // enable debug output
cFlagKTX2 = 1 << 11, // generate a KTX2 file
cFlagKTX2UASTCSuperCompression = 1 << 12, // use KTX2 Zstd supercompression on UASTC files
cFlagKTX2UASTCSuperCompression = 1 << 12, // use KTX2 Zstd supercompression on non-supercompressed formats that support it.
cFlagSRGB = 1 << 13, // input texture is sRGB, use perceptual colorspace metrics, also use sRGB filtering during mipmap gen, and also sets KTX2 output transfer func to sRGB
cFlagGenMipsClamp = 1 << 14, // generate mipmaps with clamp addressing
cFlagGenMipsWrap = 1 << 15, // generate mipmaps with wrap addressing
cFlagSRGB = 1 << 13, // input texture is sRGB, use perceptual colorspace metrics, also use sRGB filtering during mipmap gen, and also sets KTX2/.basis output transfer func to sRGB
cFlagGenMipsClamp = 1 << 14, // generate mipmaps with clamp addressing
cFlagGenMipsWrap = 1 << 15, // generate mipmaps with wrap addressing
cFlagYFlip = 1 << 16, // flip source image on Y axis before compression
cFlagYFlip = 1 << 16, // flip source image on Y axis before compression
cFlagUASTC = 1 << 17, // use UASTC compression vs. ETC1S
cFlagUASTCRDO = 1 << 18 // use RDO postprocessing when generating UASTC files (must set uastc_rdo_quality to the quality scalar)
// Note 11/18/2025: cFlagUASTCRDO flag is now ignored. Now if uastc_rdo_or_dct_quality>0 in UASTC LDR 4x4 mode, you automatically get RDO.
//cFlagUASTCRDO = 1 << 17, // use RDO postprocessing when generating UASTC LDR 4x4 files (must set uastc_rdo_or_dct_quality to the quality scalar)
cFlagPrintStats = 1 << 18, // print image stats to stdout
cFlagPrintStatus = 1 << 19, // print status to stdout
cFlagDebugImages = 1 << 20, // enable debug image generation (for development, slower)
cFlagREC2020 = 1 << 21, // treat input as REC 2020 vs. the default 709 (for codecs that support this, currently UASTC HDR and ASTC 6x6), bit is always placed into KTX2 DFD
cFlagValidateOutput = 1 << 22, // transcode the output after encoding for testing
// XUASTC LDR profile: full arith, hybrid or full zstd (see basist::astc_ldr_t::xuastc_ldr_syntax)
cFlagXUASTCLDRSyntaxFullArith = 0 << 23,
cFlagXUASTCLDRSyntaxHybrid = 1 << 23,
cFlagXUASTCLDRSyntaxFullZStd = 2 << 23,
cFlagXUASTCLDRSyntaxShift = 23,
cFlagXUASTCLDRSyntaxMask = 3,
// Texture Type: 2D, 2D Array, Cubemap Array, or Texture Video (see enum basis_texture_type). Defaults to plain 2D.
cFlagTextureType2D = 0 << 25,
cFlagTextureType2DArray = 1 << 25,
cFlagTextureTypeCubemapArray = 2 << 25,
cFlagTextureTypeVideoFrames = 3 << 25,
cFlagTextureTypeShift = 25,
cFlagTextureTypeMask = 3,
};
void* basis_compress_internal(
basist::basis_tex_format mode,
const basisu::vector<image>* pSource_images,
const basisu::vector<imagef>* pSource_images_hdr,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats,
int quality_level = -1, int effort_level = -1);
// This function accepts an array of source images.
// If more than one image is provided, it's assumed the images form a mipmap pyramid and automatic mipmap generation is disabled.
// Returns a pointer to the compressed .basis or .ktx2 file data. *pSize is the size of the compressed data.
// Important: The returned block MUST be manually freed using basis_free_data().
// basisu_encoder_init() MUST be called first!
// LDR version.
void* basis_compress(
basist::basis_tex_format mode,
const basisu::vector<image> &source_images,
uint32_t flags_and_quality, float uastc_rdo_quality,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats = nullptr);
// This function only accepts a single source image.
// HDR-only version.
// Important: The returned block MUST be manually freed using basis_free_data().
void* basis_compress(
basist::basis_tex_format mode,
const basisu::vector<imagef>& source_images_hdr,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats = nullptr);
// This function only accepts a single LDR source image. It's just a wrapper for basis_compress() above.
// Important: The returned block MUST be manually freed using basis_free_data().
void* basis_compress(
basist::basis_tex_format mode,
const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
uint32_t flags_and_quality, float uastc_rdo_quality,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats = nullptr);
// basis_compress2() variants accept the new unified quality_level and effort_level parameters instead of the old flags/float uastc_rdo_or_dct_quality parameter.
// quality_level must be [0,100], effort_level [0,10].
void* basis_compress2(
basist::basis_tex_format mode,
const basisu::vector<image>& source_images,
uint32_t flags_and_quality, int quality_level, int effort_level,
size_t* pSize,
image_stats* pStats = nullptr);
void* basis_compress2(
basist::basis_tex_format mode,
const basisu::vector<imagef>& source_images_hdr,
uint32_t flags_and_quality, int quality_level, int effort_level,
size_t* pSize,
image_stats* pStats = nullptr);
void* basis_compress2(
basist::basis_tex_format mode,
const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
uint32_t flags_and_quality, int quality_level, int effort_level,
size_t* pSize,
image_stats* pStats = nullptr);
// Frees the dynamically allocated file data returned by basis_compress().
// This MUST be called on the pointer returned by basis_compress() when you're done with it.
void basis_free_data(void* p);
// Runs a short benchmark using synthetic image data to time OpenCL encoding vs. CPU encoding, with multithreading enabled.
// Returns true if opencl is worth using on this system, otherwise false.
// If pOpenCL_failed is not null, it will be set to true if OpenCL encoding failed *on this particular machine/driver/BasisU version* and the encoder falled back to CPU encoding.
// basisu_encoder_init() MUST be called first. If OpenCL support wasn't enabled this always returns false.
bool basis_benchmark_etc1s_opencl(bool *pOpenCL_failed = nullptr);
// Parallel compression API
struct parallel_results
{
+2721 -103
View File
File diff suppressed because it is too large Load Diff
+1177 -233
View File
File diff suppressed because it is too large Load Diff
+10 -6
View File
@@ -1,5 +1,5 @@
// basis_etc.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -811,7 +811,8 @@ namespace basisu
#if defined(DEBUG) || defined(_DEBUG)
{
// sanity check the returned error
// Ultimate sanity check on the returned error.
// If this check fails, it likely means the SSE code diverged from C++ somehow, or there was an overflow somewhere.
color_rgba block_colors[4];
m_best_solution.m_coords.get_block_colors(block_colors);
@@ -826,7 +827,7 @@ namespace basisu
for (uint32_t i = 0; i < n; i++)
actual_error += color_distance(perceptual, pSrc_pixels[i], block_colors[pSelectors[i]], false);
assert(actual_error == m_best_solution.m_error);
}
#endif
@@ -1071,7 +1072,7 @@ namespace basisu
bool etc1_optimizer::check_for_redundant_solution(const etc1_solution_coordinates& coords)
{
// Hash first 3 bytes of color (RGB)
uint32_t kh = hash_hsieh((uint8_t*)&coords.m_unscaled_color.r, 3);
uint32_t kh = basist::hash_hsieh((uint8_t*)&coords.m_unscaled_color.r, 3);
uint32_t h0 = kh & cSolutionsTriedHashMask;
uint32_t h1 = (kh >> cSolutionsTriedHashBits) & cSolutionsTriedHashMask;
@@ -1177,7 +1178,7 @@ namespace basisu
uint64_t total_error = 0;
const color_rgba* pSrc_pixels = m_pParams->m_pSrc_pixels;
if (!g_cpu_supports_sse41)
{
for (uint32_t c = 0; c < n; c++)
@@ -1234,6 +1235,9 @@ namespace basisu
perceptual_distance_rgb_4_N_sse41((int64_t*)&total_error, pSelectors_to_use, block_colors, pSrc_pixels, n, trial_solution.m_error);
else
linear_distance_rgb_4_N_sse41((int64_t*)&total_error, pSelectors_to_use, block_colors, pSrc_pixels, n, trial_solution.m_error);
for (uint32_t i = 0; i < n; i++)
m_temp_selectors[i] = pSelectors_to_use[i];
}
else
{
@@ -1255,7 +1259,7 @@ namespace basisu
}
trial_solution.m_coords.m_unscaled_color = coords.m_unscaled_color;
trial_solution.m_coords.m_color4 = m_pParams->m_use_color4;
#if BASISU_DEBUG_ETC_ENCODER_DEEPER
printf("Eval done: %u error: %I64u best error so far: %I64u\n", (trial_solution.m_error < pBest_solution->m_error), trial_solution.m_error, pBest_solution->m_error);
#endif
+14 -14
View File
@@ -1,5 +1,5 @@
// basis_etc.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -76,7 +76,7 @@ namespace basisu
// 000 001 010 011 100 101 110 111
// 0 1 2 3 -4 -3 -2 -1
};
extern const int g_etc1_inten_tables[cETC1IntenModifierValues][cETC1SelectorValues];
extern const uint8_t g_etc1_to_selector_index[cETC1SelectorValues];
extern const uint8_t g_selector_index_to_etc1[cETC1SelectorValues];
@@ -92,7 +92,7 @@ namespace basisu
{
// big endian uint64:
// bit ofs: 56 48 40 32 24 16 8 0
// byte ofs: b0, b1, b2, b3, b4, b5, b6, b7
// byte ofs: b0, b1, b2, b3, b4, b5, b6, b7
union
{
uint64_t m_uint64;
@@ -275,7 +275,7 @@ namespace basisu
const uint32_t byte_bit_ofs = bit_index & 7;
const uint32_t mask = 1 << byte_bit_ofs;
const uint32_t lsb = etc1_val & 1;
const uint32_t msb = etc1_val >> 1;
@@ -510,7 +510,7 @@ namespace basisu
b.r = (base5_color.r << 3U) | (base5_color.r >> 2U);
b.g = (base5_color.g << 3U) | (base5_color.g >> 2U);
b.b = (base5_color.b << 3U) | (base5_color.b >> 2U);
const int* pInten_table = g_etc1_inten_tables[inten_table];
pBlock_colors[0].set(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);
@@ -646,7 +646,7 @@ namespace basisu
void set_block_color5_etc1s(const color_rgba &c_unscaled)
{
set_diff_bit(true);
set_base5_color(pack_color5(c_unscaled, false));
set_delta3_color(pack_delta3(0, 0, 0));
}
@@ -679,11 +679,11 @@ namespace basisu
int dr = c1_unscaled.r - c0_unscaled.r;
int dg = c1_unscaled.g - c0_unscaled.g;
int db = c1_unscaled.b - c0_unscaled.b;
dr = clamp<int>(dr, cETC1ColorDeltaMin, cETC1ColorDeltaMax);
dg = clamp<int>(dg, cETC1ColorDeltaMin, cETC1ColorDeltaMax);
db = clamp<int>(db, cETC1ColorDeltaMin, cETC1ColorDeltaMax);
set_delta3_color(pack_delta3(dr, dg, db));
return true;
@@ -785,12 +785,12 @@ namespace basisu
return static_cast<uint8_t>(x);
}
};
typedef basisu::vector<etc_block> etc_block_vec;
// Returns false if the unpack fails (could be bogus data or ETC2)
bool unpack_etc1(const etc_block& block, color_rgba *pDst, bool preserve_alpha = false);
enum basis_etc_quality
{
cETCQualityFast,
@@ -1077,13 +1077,13 @@ namespace basisu
enum { cSolutionsTriedHashBits = 10, cTotalSolutionsTriedHashSize = 1 << cSolutionsTriedHashBits, cSolutionsTriedHashMask = cTotalSolutionsTriedHashSize - 1 };
uint8_t m_solutions_tried[cTotalSolutionsTriedHashSize / 8];
void get_nearby_inten_tables(uint32_t idx, int &first_inten_table, int &last_inten_table)
{
first_inten_table = maximum<int>(idx - 1, 0);
last_inten_table = minimum<int>(cETC1IntenModifierValues, idx + 1);
}
bool check_for_redundant_solution(const etc1_solution_coordinates& coords);
bool evaluate_solution_slow(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution);
bool evaluate_solution_fast(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution);
@@ -1105,7 +1105,7 @@ namespace basisu
{
etc1_optimizer m_optimizer;
};
void pack_etc1_solid_color_init();
uint64_t pack_etc1_block_solid_color(etc_block& block, const uint8_t* pColor);
@@ -1177,5 +1177,5 @@ namespace basisu
uint64_t pack_eac_a8(pack_eac_a8_results& results, const uint8_t* pPixels, uint32_t num_pixels, uint32_t base_search_rad, uint32_t mul_search_rad, uint32_t table_mask = UINT32_MAX);
void pack_eac_a8(eac_a8_block* pBlock, const uint8_t* pPixels, uint32_t base_search_rad, uint32_t mul_search_rad, uint32_t table_mask = UINT32_MAX);
} // namespace basisu
+31 -109
View File
@@ -1,5 +1,5 @@
// basisu_frontend.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -353,9 +353,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -388,15 +386,11 @@ namespace basisu
m_block_selector_cluster_index[block_index] = best_index;
}
#ifndef __EMSCRIPTEN__
});
#endif
}
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
m_encoded_blocks.resize(m_total_blocks);
for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
@@ -425,9 +419,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index, pass] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -475,15 +467,11 @@ namespace basisu
} // block_index
#ifndef __EMSCRIPTEN__
});
#endif
}
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
m_endpoint_clusters.resize(0);
m_endpoint_clusters.resize(endpoints.size());
@@ -501,15 +489,14 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
const uint32_t block_endpoint_index = m_block_endpoint_clusters_indices[block_index][0];
etc_block trial_blk;
clear_obj(trial_blk);
trial_blk.set_block_color5_etc1s(m_endpoint_cluster_etc_params[block_endpoint_index].m_color_unscaled[0]);
trial_blk.set_inten_tables_etc1s(m_endpoint_cluster_etc_params[block_endpoint_index].m_inten_table[0]);
trial_blk.set_flip_bit(true);
@@ -535,15 +522,11 @@ namespace basisu
m_block_selector_cluster_index[block_index] = best_index;
}
#ifndef __EMSCRIPTEN__
});
#endif
}
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
m_encoded_blocks.resize(m_total_blocks);
for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
@@ -573,7 +556,7 @@ namespace basisu
debug_printf("introduce_special_selector_clusters\n");
uint32_t total_blocks_relocated = 0;
const uint32_t initial_selector_clusters = (uint32_t)m_selector_cluster_block_indices.size();
const uint32_t initial_selector_clusters = m_selector_cluster_block_indices.size_u32();
bool_vec block_relocated_flags(m_total_blocks);
@@ -595,7 +578,7 @@ namespace basisu
debug_printf("Introducing sel %u\n", sel);
const uint32_t new_selector_cluster_index = (uint32_t)m_optimized_cluster_selectors.size();
const uint32_t new_selector_cluster_index = m_optimized_cluster_selectors.size_u32();
m_optimized_cluster_selectors.push_back(blk);
@@ -675,7 +658,7 @@ namespace basisu
{
debug_printf("optimize_selector_codebook\n");
const uint32_t orig_total_selector_clusters = (uint32_t)m_optimized_cluster_selectors.size();
const uint32_t orig_total_selector_clusters = m_optimized_cluster_selectors.size_u32();
bool_vec selector_cluster_was_used(m_optimized_cluster_selectors.size());
for (uint32_t i = 0; i < m_total_blocks; i++)
@@ -765,7 +748,7 @@ namespace basisu
total_perms = 4;
else if (m_params.m_compression_level == 1)
total_perms = 16;
else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
else if (m_params.m_compression_level == BASISU_MAX_ETC1S_COMPRESSION_LEVEL)
total_perms = OPENCL_ENCODE_ETC1S_MAX_PERMS;
bool status = opencl_encode_etc1s_blocks(m_params.m_pOpenCL_context, m_etc1_blocks_etc1s.data(), m_params.m_perceptual, total_perms);
@@ -787,9 +770,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -803,7 +784,7 @@ namespace basisu
optimizer_params.m_quality = cETCQualityFast;
else if (m_params.m_compression_level == 1)
optimizer_params.m_quality = cETCQualityMedium;
else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
else if (m_params.m_compression_level == BASISU_MAX_ETC1S_COMPRESSION_LEVEL)
optimizer_params.m_quality = cETCQualityUber;
optimizer_params.m_num_src_pixels = 16;
@@ -830,15 +811,11 @@ namespace basisu
blk.set_selector(x, y, selectors[x + y * 4]);
}
#ifndef __EMSCRIPTEN__
});
#endif
}
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
} // use_cpu
@@ -859,9 +836,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job( [this, first_index, last_index, &training_vecs] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -883,15 +858,11 @@ namespace basisu
} // block_index;
#ifndef __EMSCRIPTEN__
} );
#endif
} // block_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
}
void basisu_frontend::generate_endpoint_clusters()
@@ -900,7 +871,7 @@ namespace basisu
const uint32_t parent_codebook_size = (m_params.m_max_endpoint_clusters >= 256) ? BASISU_ENDPOINT_PARENT_CODEBOOK_SIZE : 0;
uint32_t max_threads = 0;
max_threads = m_params.m_multithreaded ? minimum<int>(std::thread::hardware_concurrency(), cMaxCodebookCreationThreads) : 0;
max_threads = m_params.m_multithreaded ? minimum<int>(get_num_hardware_threads(), cMaxCodebookCreationThreads) : 0;
if (m_params.m_pJob_pool)
max_threads = minimum<int>((int)m_params.m_pJob_pool->get_total_threads(), max_threads);
@@ -970,7 +941,7 @@ namespace basisu
}
if (m_params.m_debug_stats)
debug_printf("Total endpoint clusters: %u, parent clusters: %u\n", (uint32_t)m_endpoint_clusters.size(), (uint32_t)m_endpoint_parent_clusters.size());
debug_printf("Total endpoint clusters: %u, parent clusters: %u\n", m_endpoint_clusters.size_u32(), m_endpoint_parent_clusters.size_u32());
}
// Iterate through each array of endpoint cluster block indices and set the m_block_endpoint_clusters_indices[][] array to indicaste which cluster index each block uses.
@@ -1040,11 +1011,9 @@ namespace basisu
for (uint32_t cluster_index_iter = 0; cluster_index_iter < m_endpoint_clusters.size(); cluster_index_iter += N)
{
const uint32_t first_index = cluster_index_iter;
const uint32_t last_index = minimum<uint32_t>((uint32_t)m_endpoint_clusters.size(), cluster_index_iter + N);
const uint32_t last_index = minimum<uint32_t>(m_endpoint_clusters.size_u32(), cluster_index_iter + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job( [this, first_index, last_index] {
#endif
for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
{
@@ -1112,15 +1081,11 @@ namespace basisu
}
} // cluster_index
#ifndef __EMSCRIPTEN__
} );
#endif
} // cluster_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
vector_sort(m_subblock_endpoint_quant_err_vec);
}
@@ -1131,19 +1096,19 @@ namespace basisu
generate_block_endpoint_clusters();
int num_new_endpoint_clusters = m_params.m_max_endpoint_clusters - (uint32_t)m_endpoint_clusters.size();
int num_new_endpoint_clusters = m_params.m_max_endpoint_clusters - m_endpoint_clusters.size_u32();
if (num_new_endpoint_clusters <= 0)
return;
compute_endpoint_subblock_error_vec();
const uint32_t num_orig_endpoint_clusters = (uint32_t)m_endpoint_clusters.size();
const uint32_t num_orig_endpoint_clusters = m_endpoint_clusters.size_u32();
std::unordered_set<uint32_t> training_vector_was_relocated;
uint_vec cluster_sizes(num_orig_endpoint_clusters);
for (uint32_t i = 0; i < num_orig_endpoint_clusters; i++)
cluster_sizes[i] = (uint32_t)m_endpoint_clusters[i].size();
cluster_sizes[i] = m_endpoint_clusters[i].size_u32();
std::unordered_set<uint32_t> ignore_cluster;
@@ -1259,7 +1224,7 @@ namespace basisu
// TODO: Get this working when step>0
if (m_params.m_pOpenCL_context && !step)
{
const uint32_t total_clusters = m_endpoint_clusters.size();
const uint32_t total_clusters = (uint32_t)m_endpoint_clusters.size();
basisu::vector<cl_pixel_cluster> pixel_clusters(total_clusters);
@@ -1342,7 +1307,7 @@ namespace basisu
} // cluster_indices_iter
uint32_t* pSorted = radix_sort(colors.size(), colors.data(), colors2.data(), 0, 3);
uint32_t* pSorted = radix_sort((uint32_t)colors.size(), colors.data(), colors2.data(), 0, 3);
const uint64_t first_pixel_index = input_pixels.size();
@@ -1474,7 +1439,7 @@ namespace basisu
uint32_t total_perms = 64;
if (m_params.m_compression_level <= 1)
total_perms = 16;
else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
else if (m_params.m_compression_level == BASISU_MAX_ETC1S_COMPRESSION_LEVEL)
total_perms = OPENCL_ENCODE_ETC1S_MAX_PERMS;
basisu::vector<etc_block> output_blocks(total_clusters);
@@ -1522,9 +1487,7 @@ namespace basisu
const uint32_t first_index = cluster_index_iter;
const uint32_t last_index = minimum<uint32_t>((uint32_t)m_endpoint_clusters.size(), cluster_index_iter + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index, step] {
#endif
for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
{
@@ -1556,7 +1519,7 @@ namespace basisu
{
etc1_optimizer optimizer;
etc1_solution_coordinates solutions[2];
//etc1_solution_coordinates solutions[2];
etc1_optimizer::params cluster_optimizer_params;
cluster_optimizer_params.m_num_src_pixels = total_pixels;
@@ -1567,7 +1530,7 @@ namespace basisu
if (m_params.m_compression_level <= 1)
cluster_optimizer_params.m_quality = cETCQualityMedium;
else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
else if (m_params.m_compression_level == BASISU_MAX_ETC1S_COMPRESSION_LEVEL)
cluster_optimizer_params.m_quality = cETCQualityUber;
etc1_optimizer::results cluster_optimizer_results;
@@ -1643,15 +1606,11 @@ namespace basisu
} // cluster_index
#ifndef __EMSCRIPTEN__
});
#endif
} // cluster_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
}
debug_printf("Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
@@ -1726,14 +1685,14 @@ namespace basisu
{
// For the OpenCL kernel, we order the parent endpoint clusters by smallest to largest for efficiency.
// We also prepare an array of block info structs that point into this new parent endpoint cluster array.
const uint32_t total_parent_clusters = m_endpoint_clusters_within_each_parent_cluster.size();
const uint32_t total_parent_clusters = (uint32_t)m_endpoint_clusters_within_each_parent_cluster.size();
basisu::vector<cl_block_info_struct> cl_block_info_structs(m_total_blocks);
// the size of each parent cluster, in total clusters
uint_vec parent_cluster_sizes(total_parent_clusters);
for (uint32_t i = 0; i < total_parent_clusters; i++)
parent_cluster_sizes[i] = m_endpoint_clusters_within_each_parent_cluster[i].size();
parent_cluster_sizes[i] = (uint32_t)m_endpoint_clusters_within_each_parent_cluster[i].size();
uint_vec first_parent_cluster_ofs(total_parent_clusters);
uint32_t cur_ofs = 0;
@@ -1818,9 +1777,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index, &best_cluster_indices, &block_clusters] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -1951,15 +1908,11 @@ namespace basisu
} // block_index
#ifndef __EMSCRIPTEN__
});
#endif
} // block_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
} // use_cpu
@@ -2104,9 +2057,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -2131,15 +2082,11 @@ namespace basisu
} // block_index
#ifndef __EMSCRIPTEN__
});
#endif
} // block_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
} // use_cpu
@@ -2204,9 +2151,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job( [this, first_index, last_index, &training_vecs] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -2233,15 +2178,11 @@ namespace basisu
} // block_index
#ifndef __EMSCRIPTEN__
} );
#endif
} // block_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
vec16F_clusterizer selector_clusterizer;
for (uint32_t i = 0; i < m_total_blocks; i++)
@@ -2252,7 +2193,7 @@ namespace basisu
debug_printf("Using selector parent codebook size %u\n", parent_codebook_size);
uint32_t max_threads = 0;
max_threads = m_params.m_multithreaded ? minimum<int>(std::thread::hardware_concurrency(), cMaxCodebookCreationThreads) : 0;
max_threads = m_params.m_multithreaded ? minimum<int>(get_num_hardware_threads(), cMaxCodebookCreationThreads) : 0;
if (m_params.m_pJob_pool)
max_threads = minimum<int>((int)m_params.m_pJob_pool->get_total_threads(), max_threads);
@@ -2328,8 +2269,6 @@ namespace basisu
m_optimized_cluster_selectors.resize(total_selector_clusters);
uint32_t total_clusters_processed = 0;
// For each selector codebook entry, and for each of the 4x4 selectors, determine which selector minimizes the error across all the blocks that use that quantized selector.
const uint32_t N = 256;
for (uint32_t cluster_index_iter = 0; cluster_index_iter < total_selector_clusters; cluster_index_iter += N)
@@ -2337,9 +2276,7 @@ namespace basisu
const uint32_t first_index = cluster_index_iter;
const uint32_t last_index = minimum<uint32_t>((uint32_t)total_selector_clusters, cluster_index_iter + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index, &total_clusters_processed, &total_selector_clusters] {
#endif
m_params.m_pJob_pool->add_job([this, first_index, last_index] {
for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
{
@@ -2349,6 +2286,7 @@ namespace basisu
continue;
uint64_t overall_best_err = 0;
(void)overall_best_err;
uint64_t total_err[4][4][4];
clear_obj(total_err);
@@ -2407,15 +2345,11 @@ namespace basisu
} // cluster_index
#ifndef __EMSCRIPTEN__
});
#endif
} // cluster_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
debug_printf("Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
@@ -2507,7 +2441,7 @@ namespace basisu
if ((m_params.m_pOpenCL_context) && m_use_hierarchical_selector_codebooks)
{
const uint32_t num_parent_clusters = m_selector_clusters_within_each_parent_cluster.size();
const uint32_t num_parent_clusters = m_selector_clusters_within_each_parent_cluster.size_u32();
basisu::vector<fosc_selector_struct> selector_structs;
selector_structs.reserve(m_optimized_cluster_selectors.size());
@@ -2535,7 +2469,7 @@ namespace basisu
selector_cluster_indices.push_back(selector_cluster_index);
}
cur_ofs += m_selector_clusters_within_each_parent_cluster[parent_index].size();
cur_ofs += m_selector_clusters_within_each_parent_cluster[parent_index].size_u32();
}
const uint32_t total_input_selectors = cur_ofs;
@@ -2550,7 +2484,7 @@ namespace basisu
block_structs[i].m_etc_color5_inten.a = (uint8_t)blk.get_inten_table(0);
block_structs[i].m_first_selector = parent_selector_cluster_offsets[parent_selector_cluster];
block_structs[i].m_num_selectors = m_selector_clusters_within_each_parent_cluster[parent_selector_cluster].size();
block_structs[i].m_num_selectors = m_selector_clusters_within_each_parent_cluster[parent_selector_cluster].size_u32();
}
uint_vec output_selector_cluster_indices(m_total_blocks);
@@ -2616,10 +2550,8 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job( [this, first_index, last_index, &unpacked_optimized_cluster_selectors] {
#endif
int prev_best_cluster_index = 0;
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
@@ -2757,15 +2689,11 @@ namespace basisu
} // block_index
#ifndef __EMSCRIPTEN__
} );
#endif
} // block_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
for (uint32_t i = 0; i < m_selector_cluster_block_indices.size(); i++)
{
@@ -2863,7 +2791,7 @@ namespace basisu
total_subblocks_examined += total_pixels / 8;
etc1_optimizer optimizer;
etc1_solution_coordinates solutions[2];
//etc1_solution_coordinates solutions[2];
etc1_optimizer::params cluster_optimizer_params;
cluster_optimizer_params.m_num_src_pixels = total_pixels;
@@ -3082,9 +3010,7 @@ namespace basisu
const uint32_t first_index = cluster_index_iter;
const uint32_t last_index = minimum<uint32_t>((uint32_t)new_endpoint_cluster_block_indices.size(), cluster_index_iter + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job( [this, first_index, last_index, &cluster_improved, &cluster_valid, &new_endpoint_cluster_block_indices, &pBlock_selector_indices ] {
#endif
for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
{
@@ -3130,7 +3056,7 @@ namespace basisu
{
etc1_optimizer optimizer;
etc1_solution_coordinates solutions[2];
//etc1_solution_coordinates solutions[2];
etc1_optimizer::params cluster_optimizer_params;
cluster_optimizer_params.m_num_src_pixels = total_pixels;
@@ -3140,7 +3066,7 @@ namespace basisu
cluster_optimizer_params.m_perceptual = m_params.m_perceptual;
cluster_optimizer_params.m_pForce_selectors = &force_selectors[0];
if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
if (m_params.m_compression_level == BASISU_MAX_ETC1S_COMPRESSION_LEVEL)
cluster_optimizer_params.m_quality = cETCQualityUber;
else
cluster_optimizer_params.m_quality = cETCQualitySlow;
@@ -3174,15 +3100,11 @@ namespace basisu
} // cluster_index
#ifndef __EMSCRIPTEN__
} );
#endif
} // cluster_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
uint32_t total_unused_clusters = 0;
uint32_t total_improved_clusters = 0;
@@ -3455,7 +3377,7 @@ namespace basisu
}
image img;
g.unpack(img);
g.unpack(img, false);
save_png(pFilename, img);
}
+6 -4
View File
@@ -1,5 +1,5 @@
// basisu_frontend.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -37,8 +37,10 @@ namespace basisu
uint32_t &operator[] (uint32_t i) { assert(i < 2); return m_comps[i]; }
};
const uint32_t BASISU_DEFAULT_COMPRESSION_LEVEL = 2;
const uint32_t BASISU_MAX_COMPRESSION_LEVEL = 6;
// rg [11/25/25] - The command line tool defaults to ETC1S level 1, but the API 2. Changing this breaks backwards compatibility for anyone using the API and our test suite.
const uint32_t BASISU_DEFAULT_ETC1S_COMPRESSION_LEVEL = 2;
const uint32_t BASISU_MAX_ETC1S_COMPRESSION_LEVEL = 6;
class basisu_frontend
{
@@ -72,7 +74,7 @@ namespace basisu
m_pSource_blocks(NULL),
m_max_endpoint_clusters(256),
m_max_selector_clusters(256),
m_compression_level(BASISU_DEFAULT_COMPRESSION_LEVEL),
m_compression_level(BASISU_DEFAULT_ETC1S_COMPRESSION_LEVEL),
m_perceptual(true),
m_debug_stats(false),
m_debug_images(false),
File diff suppressed because it is too large Load Diff
+50 -20
View File
@@ -1,5 +1,5 @@
// basisu_gpu_texture.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,6 +14,7 @@
// limitations under the License.
#pragma once
#include "../transcoder/basisu.h"
#include "../transcoder/basisu_astc_helpers.h"
#include "basisu_etc.h"
namespace basisu
@@ -48,6 +49,8 @@ namespace basisu
}
inline texture_format get_format() const { return m_fmt; }
inline bool is_hdr() const { return is_hdr_texture_format(m_fmt); }
inline bool is_ldr() const { return !is_hdr_texture_format(m_fmt); }
// Width/height in pixels
inline uint32_t get_pixel_width() const { return m_width; }
@@ -100,9 +103,15 @@ namespace basisu
m_blocks.resize(m_blocks_x * m_blocks_y * m_qwords_per_block);
}
bool unpack(image& img) const;
// Unpacks LDR textures only. Asserts and returns false otherwise.
// astc_srgb: true to use the ASTC sRGB decode profile, false for linear.
// For XUASTC LDR, this should match what was used during encoding. For ETC1S/UASTC LDR 4x4, this should be false.
bool unpack(image& img, bool astc_srgb) const;
// Unpacks HDR textures only. Asserts and returns false otherwise.
bool unpack_hdr(imagef& img) const;
void override_dimensions(uint32_t w, uint32_t h)
inline void override_dimensions(uint32_t w, uint32_t h)
{
m_width = w;
m_height = h;
@@ -116,39 +125,60 @@ namespace basisu
typedef basisu::vector<gpu_image> gpu_image_vec;
// KTX file writing
bool create_ktx_texture_file(uint8_vec &ktx_data, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag);
bool write_compressed_texture_file(const char *pFilename, const basisu::vector<gpu_image_vec>& g, bool cubemap_flag);
// KTX1 file writing - compatible with ARM's astcenc tool, and some other tools.
// Note astc_linear_flag used to be always effectively true in older code. It's ignored for ASTC HDR formats.
bool create_ktx_texture_file(uint8_vec &ktx_data, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool astc_srgb_flag);
inline bool write_compressed_texture_file(const char *pFilename, const gpu_image_vec &g)
{
basisu::vector<gpu_image_vec> a;
a.push_back(g);
return write_compressed_texture_file(pFilename, a, false);
}
bool does_dds_support_format(texture_format fmt);
bool write_dds_file(uint8_vec& dds_data, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format);
bool write_dds_file(const char* pFilename, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format);
bool write_compressed_texture_file(const char *pFilename, const gpu_image &g);
// Currently reads 2D 32bpp RGBA, 16-bit HALF RGBA, or 32-bit FLOAT RGBA, with or without mipmaps. No tex arrays or cubemaps, yet.
bool read_uncompressed_dds_file(const char* pFilename, basisu::vector<image>& ldr_mips, basisu::vector<imagef>& hdr_mips);
// Supports DDS and KTX
bool write_compressed_texture_file(const char *pFilename, const basisu::vector<gpu_image_vec>& g, bool cubemap_flag, bool use_srgb_format);
bool write_compressed_texture_file(const char* pFilename, const gpu_image_vec& g, bool use_srgb_format);
bool write_compressed_texture_file(const char *pFilename, const gpu_image &g, bool use_srgb_format);
bool write_3dfx_out_file(const char* pFilename, const gpu_image& gi);
// GPU texture block unpacking
// For ETC1, use in basisu_etc.h: bool unpack_etc1(const etc_block& block, color_rgba *pDst, bool preserve_alpha)
void unpack_etc2_eac(const void *pBlock_bits, color_rgba *pPixels);
bool unpack_bc1(const void *pBlock_bits, color_rgba *pPixels, bool set_alpha);
void unpack_bc4(const void *pBlock_bits, uint8_t *pPixels, uint32_t stride);
bool unpack_bc3(const void *pBlock_bits, color_rgba *pPixels);
void unpack_bc5(const void *pBlock_bits, color_rgba *pPixels);
#if 0
bool unpack_bc7_mode6(const void *pBlock_bits, color_rgba *pPixels);
bool unpack_bc7(const void* pBlock_bits, color_rgba* pPixels);
int determine_bc7_mode(const void* pBlock);
int determine_bc7_mode_4_index_mode(const void* pBlock);
int determine_bc7_mode_4_or_5_rotation(const void* pBlock);
bool unpack_bc7(const void* pBlock_bits, color_rgba* pPixels); // full format
#endif
bool unpack_bc6h(const void* pSrc_block, void* pDst_block, bool is_signed, uint32_t dest_pitch_in_halfs = 4 * 3); // full format, outputs HALF values, RGB texels only (not RGBA)
void unpack_atc(const void* pBlock_bits, color_rgba* pPixels);
// We only support CC_MIXED non-alpha blocks here because that's the only mode the transcoder uses at the moment.
bool unpack_fxt1(const void* p, color_rgba* pPixels);
// PVRTC2 is currently limited to only what our transcoder outputs (non-interpolated, hard_flag=1 modulation=0). In this mode, PVRTC2 looks much like BC1/ATC.
bool unpack_pvrtc2(const void* p, color_rgba* pPixels);
void unpack_etc2_eac_r(const void *p, color_rgba* pPixels, uint32_t c);
void unpack_etc2_eac_rg(const void* p, color_rgba* pPixels);
// unpack_block() is primarily intended to unpack texture data created by the transcoder.
// For some texture formats (like ETC2 RGB, PVRTC2, FXT1) it's not a complete implementation.
bool unpack_block(texture_format fmt, const void *pBlock, color_rgba *pPixels);
// For some texture formats (like ETC2 RGB, PVRTC2, FXT1) it's not yet a complete implementation.
// Unpacks LDR texture formats only.
bool unpack_block(texture_format fmt, const void *pBlock, color_rgba *pPixels, bool astc_srgb);
// Unpacks HDR texture formats only.
bool unpack_block_hdr(texture_format fmt, const void* pBlock, vec4F* pPixels);
bool read_astc_file(const uint8_t* pImage_data, size_t image_data_size, vector2D<astc_helpers::astc_block>& blocks, uint32_t& block_width, uint32_t& block_height, uint32_t& width, uint32_t& height);
bool read_astc_file(const char* pFilename, vector2D<astc_helpers::astc_block>& blocks, uint32_t& block_width, uint32_t& block_height, uint32_t& width, uint32_t& height);
bool write_astc_file(const char* pFilename, const void* pBlocks, uint32_t block_width, uint32_t block_height, uint32_t dim_x, uint32_t dim_y);
} // namespace basisu
+1 -1
View File
@@ -1,5 +1,5 @@
// basisu_kernels_declares.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
+57 -52
View File
@@ -1,5 +1,5 @@
// basisu_kernels_imp.h - Do not directly include
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,12 +17,17 @@ using namespace CPPSPMD;
namespace CPPSPMD_NAME(basisu_kernels_namespace)
{
static inline int64_t reduce_add64(const vint &x)
{
return (int64_t)VINT_EXTRACT(x, 0) + (int64_t)VINT_EXTRACT(x, 1) +
(int64_t)VINT_EXTRACT(x, 2) + (int64_t)VINT_EXTRACT(x, 3);
}
struct perceptual_distance_rgb_4_N : spmd_kernel
{
void _call(int64_t* pDistance,
const uint8_t* pSelectors,
const color_rgba* pBlock_colors,
const color_rgba* pSrc_pixels, uint32_t n,
const color_rgba* pSrc_pixels, uint32_t n,
int64_t early_out_err)
{
assert(early_out_err >= 0);
@@ -66,15 +71,15 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
vint dg = base_g - g;
vint db = base_b - b;
vint delta_l = dr * 27 + dg * 92 + db * 9;
vint delta_cr = dr * 128 - delta_l;
vint delta_cb = db * 128 - delta_l;
vint delta_l = dr * 14 + dg * 45 + db * 5;
vint delta_cr = dr * 64 - delta_l;
vint delta_cb = db * 64 - delta_l;
vint id = ((delta_l * delta_l) >> 7) +
((((delta_cr * delta_cr) >> 7) * 26) >> 7) +
((((delta_cb * delta_cb) >> 7) * 3) >> 7);
vint id = ((delta_l * delta_l) >> 5) +
((((delta_cr * delta_cr) >> 5) * 26) >> 7) +
((((delta_cb * delta_cb) >> 5) * 3) >> 7);
*pDistance += reduce_add(id);
*pDistance += reduce_add64(id);
if (*pDistance >= early_out_err)
return;
}
@@ -90,13 +95,13 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
int dg = base_g - g;
int db = base_b - b;
int delta_l = dr * 27 + dg * 92 + db * 9;
int delta_cr = dr * 128 - delta_l;
int delta_cb = db * 128 - delta_l;
int delta_l = dr * 14 + dg * 45 + db * 5;
int delta_cr = dr * 64 - delta_l;
int delta_cb = db * 64 - delta_l;
int id = ((delta_l * delta_l) >> 7) +
((((delta_cr * delta_cr) >> 7) * 26) >> 7) +
((((delta_cb * delta_cb) >> 7) * 3) >> 7);
int id = ((delta_l * delta_l) >> 5) +
((((delta_cr * delta_cr) >> 5) * 26) >> 7) +
((((delta_cb * delta_cb) >> 5) * 3) >> 7);
*pDistance += id;
if (*pDistance >= early_out_err)
@@ -110,7 +115,7 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
void _call(int64_t* pDistance,
const uint8_t* pSelectors,
const color_rgba* pBlock_colors,
const color_rgba* pSrc_pixels, uint32_t n,
const color_rgba* pSrc_pixels, uint32_t n,
int64_t early_out_err)
{
assert(early_out_err >= 0);
@@ -156,7 +161,7 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
vint id = dr * dr + dg * dg + db * db;
*pDistance += reduce_add(id);
*pDistance += reduce_add64(id);
if (*pDistance >= early_out_err)
return;
}
@@ -191,13 +196,13 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
vint dg = base_g - g;
vint db = base_b - b;
vint delta_l = dr * 27 + dg * 92 + db * 9;
vint delta_cr = dr * 128 - delta_l;
vint delta_cb = db * 128 - delta_l;
vint delta_l = dr * 14 + dg * 45 + db * 5;
vint delta_cr = dr * 64 - delta_l;
vint delta_cb = db * 64 - delta_l;
vint id = VINT_SHIFT_RIGHT(delta_l * delta_l, 7) +
VINT_SHIFT_RIGHT(VINT_SHIFT_RIGHT(delta_cr * delta_cr, 7) * 26, 7) +
VINT_SHIFT_RIGHT(VINT_SHIFT_RIGHT(delta_cb * delta_cb, 7) * 3, 7);
vint id = VINT_SHIFT_RIGHT(delta_l * delta_l, 5) +
VINT_SHIFT_RIGHT(VINT_SHIFT_RIGHT(delta_cr * delta_cr, 5) * 26, 7) +
VINT_SHIFT_RIGHT(VINT_SHIFT_RIGHT(delta_cb * delta_cb, 5) * 3, 7);
return id;
}
@@ -205,7 +210,7 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
void _call(int64_t* pDistance,
uint8_t* pSelectors,
const color_rgba* pBlock_colors,
const color_rgba* pSrc_pixels, uint32_t n,
const color_rgba* pSrc_pixels, uint32_t n,
int64_t early_out_err)
{
assert(early_out_err >= 0);
@@ -243,7 +248,7 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
__m128i vsels = shuffle_epi8(sels.m_value, shuf);
storeu_si32((void *)(pSelectors + i), vsels);
*pDistance += reduce_add(min_dist);
*pDistance += reduce_add64(min_dist);
if (*pDistance >= early_out_err)
return;
}
@@ -261,13 +266,13 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
int dg = base_g - g;
int db = base_b - b;
int delta_l = dr * 27 + dg * 92 + db * 9;
int delta_cr = dr * 128 - delta_l;
int delta_cb = db * 128 - delta_l;
int delta_l = dr * 14 + dg * 45 + db * 5;
int delta_cr = dr * 64 - delta_l;
int delta_cb = db * 64 - delta_l;
int id = ((delta_l * delta_l) >> 7) +
((((delta_cr * delta_cr) >> 7) * 26) >> 7) +
((((delta_cb * delta_cb) >> 7) * 3) >> 7);
int id = ((delta_l * delta_l) >> 5) +
((((delta_cr * delta_cr) >> 5) * 26) >> 7) +
((((delta_cb * delta_cb) >> 5) * 3) >> 7);
if (id < best_err)
{
best_err = id;
@@ -301,7 +306,7 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
void _call(int64_t* pDistance,
uint8_t* pSelectors,
const color_rgba* pBlock_colors,
const color_rgba* pSrc_pixels, uint32_t n,
const color_rgba* pSrc_pixels, uint32_t n,
int64_t early_out_err)
{
assert(early_out_err >= 0);
@@ -339,7 +344,7 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
__m128i vsels = shuffle_epi8(sels.m_value, shuf);
storeu_si32((void *)(pSelectors + i), vsels);
*pDistance += reduce_add(min_dist);
*pDistance += reduce_add64(min_dist);
if (*pDistance >= early_out_err)
return;
}
@@ -384,20 +389,20 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
vint dg = base_g - g;
vint db = base_b - b;
vint delta_l = dr * 27 + dg * 92 + db * 9;
vint delta_cr = dr * 128 - delta_l;
vint delta_cb = db * 128 - delta_l;
vint delta_l = dr * 14 + dg * 45 + db * 5;
vint delta_cr = dr * 64 - delta_l;
vint delta_cb = db * 64 - delta_l;
vint id = VINT_SHIFT_RIGHT(delta_l * delta_l, 7) +
VINT_SHIFT_RIGHT(VINT_SHIFT_RIGHT(delta_cr * delta_cr, 7) * 26, 7) +
VINT_SHIFT_RIGHT(VINT_SHIFT_RIGHT(delta_cb * delta_cb, 7) * 3, 7);
vint id = VINT_SHIFT_RIGHT(delta_l * delta_l, 5) +
VINT_SHIFT_RIGHT(VINT_SHIFT_RIGHT(delta_cr * delta_cr, 5) * 26, 7) +
VINT_SHIFT_RIGHT(VINT_SHIFT_RIGHT(delta_cb * delta_cb, 5) * 3, 7);
return id;
}
void _call(int64_t* pDistance,
const color_rgba* pBlock_colors,
const color_rgba* pSrc_pixels, uint32_t n,
const color_rgba* pSrc_pixels, uint32_t n,
int64_t early_out_error)
{
assert(early_out_error >= 0);
@@ -428,7 +433,7 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
vint min_dist = min(min(min(dist0, dist1), dist2), dist3);
*pDistance += reduce_add(min_dist);
*pDistance += reduce_add64(min_dist);
if (*pDistance > early_out_error)
return;
}
@@ -446,14 +451,14 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
int dg = base_g - g;
int db = base_b - b;
int delta_l = dr * 27 + dg * 92 + db * 9;
int delta_cr = dr * 128 - delta_l;
int delta_cb = db * 128 - delta_l;
int delta_l = dr * 14 + dg * 45 + db * 5;
int delta_cr = dr * 64 - delta_l;
int delta_cb = db * 64 - delta_l;
int id = ((delta_l * delta_l) >> 5) +
((((delta_cr * delta_cr) >> 5) * 26) >> 7) +
((((delta_cb * delta_cb) >> 5) * 3) >> 7);
int id = ((delta_l * delta_l) >> 7) +
((((delta_cr * delta_cr) >> 7) * 26) >> 7) +
((((delta_cb * delta_cb) >> 7) * 3) >> 7);
if (id < best_err)
{
best_err = id;
@@ -515,7 +520,7 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
vint min_dist = min(min(min(dist0, dist1), dist2), dist3);
*pDistance += reduce_add(min_dist);
*pDistance += reduce_add64(min_dist);
if (*pDistance > early_out_error)
return;
}
@@ -554,10 +559,10 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
uint32_t num_vecs, const void* pWeighted_vecs_void, const void* pOrigin_void, const uint32_t* pVec_indices, void* pMatrix16x16_void)
{
const std::pair<vec16F, uint64_t>* pWeighted_vecs = static_cast< const std::pair<vec16F, uint64_t> *>(pWeighted_vecs_void);
const float* pOrigin = static_cast<const float*>(pOrigin_void);
vfloat org0 = loadu_linear_all(pOrigin), org1 = loadu_linear_all(pOrigin + 4), org2 = loadu_linear_all(pOrigin + 8), org3 = loadu_linear_all(pOrigin + 12);
vfloat mat[16][4];
vfloat vzero(zero_vfloat());
@@ -577,7 +582,7 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
vfloat weight((float)pWeighted_vecs[vec_index].second);
vfloat vec[4] = { loadu_linear_all(pW) - org0, loadu_linear_all(pW + 4) - org1, loadu_linear_all(pW + 8) - org2, loadu_linear_all(pW + 12) - org3 };
vfloat wvec0 = vec[0] * weight, wvec1 = vec[1] * weight, wvec2 = vec[2] * weight, wvec3 = vec[3] * weight;
for (uint32_t j = 0; j < 16; j++)
+1 -18
View File
@@ -1,5 +1,5 @@
// basisu_kernels_sse.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -22,22 +22,6 @@
#include <intrin.h>
#endif
#if !defined(_MSC_VER)
#if __AVX__ || __AVX2__ || __AVX512F__
#error Please check your compiler options
#endif
#if CPPSPMD_SSE2
#if __SSE4_1__ || __SSE3__ || __SSE4_2__ || __SSSE3__
#error SSE4.1/SSE3/SSE4.2/SSSE3 cannot be enabled to use this file
#endif
#else
#if !__SSE4_1__ || !__SSE3__ || !__SSSE3__
#error Please check your compiler options
#endif
#endif
#endif
#include "cppspmd_sse.h"
#include "cppspmd_type_aliases.h"
@@ -158,4 +142,3 @@ void detect_sse41()
} // namespace basisu
#endif // #if BASISU_SUPPORT_SSE
File diff suppressed because it is too large Load Diff
+12 -13
View File
@@ -1,9 +1,9 @@
/* miniz.c v1.15 - deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt
Forked from the public domain/unlicense version at: https://code.google.com/archive/p/miniz/
Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
Forked from the public domain/unlicense version at: https://code.google.com/archive/p/miniz/
Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -497,7 +497,7 @@ size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void
// Compresses an image to a compressed PNG file in memory.
// On entry:
// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4.
// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4.
// The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory.
// level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL
// If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps).
@@ -799,7 +799,7 @@ mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len)
// This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.)
mz_uint64 a = 128ULL + (source_len * 110ULL) / 100ULL;
mz_uint64 b = 128ULL + (mz_uint64)source_len + ((source_len / (31 * 1024)) + 1ULL) * 5ULL;
mz_uint64 t = MZ_MAX(a, b);
if (((mz_ulong)t) != t)
t = (mz_ulong)(-1);
@@ -1377,8 +1377,8 @@ common_exit:
for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
}
r->m_check_adler32 = (s2 << 16) + s1;
if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32))
r->m_check_adler32 = (s2 << 16) + s1;
if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32))
status = TINFL_STATUS_ADLER32_MISMATCH;
}
return status;
@@ -1973,7 +1973,7 @@ static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahe
(TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
if (!probe_len)
{
*pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break;
*pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); break;
}
else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len)
{
@@ -2101,7 +2101,7 @@ static mz_bool tdefl_compress_fast(tdefl_compressor *d)
total_lz_bytes += cur_match_len;
lookahead_pos += cur_match_len;
dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE);
dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE);
cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;
MZ_ASSERT(lookahead_size >= cur_match_len);
lookahead_size -= cur_match_len;
@@ -2129,7 +2129,7 @@ static mz_bool tdefl_compress_fast(tdefl_compressor *d)
d->m_huff_count[0][lit]++;
lookahead_pos++;
dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE);
dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE);
cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
lookahead_size--;
@@ -2283,7 +2283,7 @@ static mz_bool tdefl_compress_normal(tdefl_compressor *d)
d->m_lookahead_pos += len_to_move;
MZ_ASSERT(d->m_lookahead_size >= len_to_move);
d->m_lookahead_size -= len_to_move;
d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE);
d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE);
// Check if it's time to flush the current LZ codes to the internal output buffer.
if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ||
( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) )
@@ -2528,4 +2528,3 @@ void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h,
} // namespace buminiz
#endif // MINIZ_HEADER_FILE_ONLY
File diff suppressed because it is too large Load Diff
+46 -36
View File
@@ -1,5 +1,5 @@
// basisu_opencl.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -31,7 +31,9 @@
#include <CL/cl.h>
#endif
#define BASISU_OPENCL_ASSERT_ON_ANY_ERRORS (1)
#ifndef BASISU_OPENCL_ASSERT_ON_ANY_ERRORS
#define BASISU_OPENCL_ASSERT_ON_ANY_ERRORS (0)
#endif
namespace basisu
{
@@ -54,10 +56,10 @@ namespace basisu
class ocl
{
public:
ocl()
ocl()
{
memset(&m_dev_fp_config, 0, sizeof(m_dev_fp_config));
m_ocl_mutex.lock();
m_ocl_mutex.unlock();
}
@@ -161,12 +163,12 @@ namespace basisu
deinit();
return false;
}
printf("OpenCL init time: %3.3f secs\n", tm.get_elapsed_secs());
return true;
}
bool deinit()
{
if (m_program)
@@ -364,7 +366,7 @@ namespace basisu
return obj;
}
bool destroy_buffer(cl_mem buf)
{
if (buf)
@@ -678,7 +680,7 @@ namespace basisu
cl_command_queue m_command_queue = nullptr;
cl_program m_program = nullptr;
cl_device_fp_config m_dev_fp_config;
bool m_use_mutex = false;
std::mutex m_ocl_mutex;
@@ -704,7 +706,7 @@ namespace basisu
private:
ocl* m_p;
};
cl_image_format get_image_format(uint32_t bytes_per_pixel, bool normalized)
{
cl_image_format fmt;
@@ -721,10 +723,10 @@ namespace basisu
return fmt;
}
};
// Library blobal state
ocl g_ocl;
bool opencl_init(bool force_serialization)
{
if (g_ocl.is_initialized())
@@ -753,11 +755,11 @@ namespace basisu
g_ocl.deinit();
return false;
}
pKernel_src = (char*)kernel_src.data();
kernel_src_size = kernel_src.size();
#endif
if (!kernel_src_size)
{
ocl_error_printf("opencl_init: Invalid OpenCL kernel source file \"%s\"\n", BASISU_OCL_KERNELS_FILENAME);
@@ -771,7 +773,7 @@ namespace basisu
g_ocl.deinit();
return false;
}
printf("OpenCL support initialized successfully\n");
return true;
@@ -789,7 +791,7 @@ namespace basisu
struct opencl_context
{
uint32_t m_ocl_total_pixel_blocks;
size_t m_ocl_total_pixel_blocks;
cl_mem m_ocl_pixel_blocks;
cl_command_queue m_command_queue;
@@ -816,10 +818,10 @@ namespace basisu
opencl_context* pContext = static_cast<opencl_context * >(calloc(sizeof(opencl_context), 1));
if (!pContext)
return nullptr;
// To avoid driver bugs in some drivers - serialize this. Likely not necessary, we don't know.
// https://community.intel.com/t5/OpenCL-for-CPU/Bug-report-clCreateKernelsInProgram-is-not-thread-safe/td-p/1159771
pContext->m_command_queue = g_ocl.create_command_queue();
if (!pContext->m_command_queue)
{
@@ -890,7 +892,7 @@ namespace basisu
g_ocl.destroy_kernel(pContext->m_ocl_refine_endpoint_clusterization_kernel);
g_ocl.destroy_command_queue(pContext->m_command_queue);
memset(pContext, 0, sizeof(opencl_context));
free(pContext);
@@ -907,7 +909,7 @@ namespace basisu
};
#pragma pack(pop)
bool opencl_set_pixel_blocks(opencl_context_ptr pContext, uint32_t total_blocks, const cl_pixel_block* pPixel_blocks)
bool opencl_set_pixel_blocks(opencl_context_ptr pContext, size_t total_blocks, const cl_pixel_block* pPixel_blocks)
{
if (!opencl_is_available())
return false;
@@ -938,9 +940,11 @@ namespace basisu
assert(pContext->m_ocl_pixel_blocks);
if (!pContext->m_ocl_pixel_blocks)
return false;
assert(pContext->m_ocl_total_pixel_blocks <= INT_MAX);
cl_encode_etc1s_param_struct ps;
ps.m_total_blocks = pContext->m_ocl_total_pixel_blocks;
ps.m_total_blocks = (int)pContext->m_ocl_total_pixel_blocks;
ps.m_perceptual = perceptual;
ps.m_total_perms = total_perms;
@@ -948,7 +952,7 @@ namespace basisu
cl_mem vars = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue , &ps, sizeof(ps));
cl_mem block_buf = g_ocl.alloc_write_buffer(sizeof(etc_block) * pContext->m_ocl_total_pixel_blocks);
if (!vars || !block_buf)
goto exit;
@@ -986,7 +990,7 @@ exit:
interval_timer tm;
tm.start();
cl_encode_etc1s_param_struct ps;
ps.m_total_blocks = total_clusters;
ps.m_perceptual = perceptual;
@@ -1003,7 +1007,7 @@ exit:
return false;
}
}
cl_mem vars = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue , &ps, sizeof(ps));
cl_mem input_clusters = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pClusters, (size_t)(sizeof(cl_pixel_cluster) * total_clusters));
cl_mem input_pixels = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pPixels, (size_t)(sizeof(color_rgba) * total_pixels));
@@ -1062,9 +1066,11 @@ exit:
assert(pContext->m_ocl_pixel_blocks);
if (!pContext->m_ocl_pixel_blocks)
return false;
assert(pContext->m_ocl_total_pixel_blocks <= INT_MAX);
cl_rec_param_struct ps;
ps.m_total_blocks = pContext->m_ocl_total_pixel_blocks;
ps.m_total_blocks = (int)pContext->m_ocl_total_pixel_blocks;
ps.m_perceptual = perceptual;
bool status = false;
@@ -1073,7 +1079,7 @@ exit:
cl_mem cluster_info = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pCluster_info, sizeof(cl_endpoint_cluster_struct) * total_clusters);
cl_mem sorted_block_indices = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pSorted_block_indices, sizeof(uint32_t) * pContext->m_ocl_total_pixel_blocks);
cl_mem output_buf = g_ocl.alloc_write_buffer(sizeof(uint32_t) * pContext->m_ocl_total_pixel_blocks);
if (!pixel_block_info || !cluster_info || !sorted_block_indices || !output_buf)
goto exit;
@@ -1087,7 +1093,7 @@ exit:
goto exit;
debug_printf("opencl_refine_endpoint_clusterization: Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
status = true;
exit:
@@ -1118,10 +1124,12 @@ exit:
if (!pContext->m_ocl_pixel_blocks)
return false;
assert(pContext->m_ocl_total_pixel_blocks <= INT_MAX);
fosc_param_struct ps;
ps.m_total_blocks = pContext->m_ocl_total_pixel_blocks;
ps.m_total_blocks = (int)pContext->m_ocl_total_pixel_blocks;
ps.m_perceptual = perceptual;
bool status = false;
cl_mem input_block_info = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pInput_block_info, sizeof(fosc_block_struct) * pContext->m_ocl_total_pixel_blocks);
@@ -1170,8 +1178,10 @@ exit:
if (!pContext->m_ocl_pixel_blocks)
return false;
assert(pContext->m_ocl_total_pixel_blocks <= INT_MAX);
ds_param_struct ps;
ps.m_total_blocks = pContext->m_ocl_total_pixel_blocks;
ps.m_total_blocks = (int)pContext->m_ocl_total_pixel_blocks;
ps.m_perceptual = perceptual;
bool status = false;
@@ -1192,9 +1202,9 @@ exit:
goto exit;
debug_printf("opencl_determine_selectors: Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
status = true;
exit:
g_ocl.destroy_buffer(input_etc_color5_intens);
g_ocl.destroy_buffer(output_blocks);
@@ -1202,7 +1212,7 @@ exit:
return status;
}
#else
#else
namespace basisu
{
// No OpenCL support - all dummy functions that return false;
@@ -1232,7 +1242,7 @@ namespace basisu
BASISU_NOTE_UNUSED(context);
}
bool opencl_set_pixel_blocks(opencl_context_ptr pContext, uint32_t total_blocks, const cl_pixel_block* pPixel_blocks)
bool opencl_set_pixel_blocks(opencl_context_ptr pContext, size_t total_blocks, const cl_pixel_block* pPixel_blocks)
{
BASISU_NOTE_UNUSED(pContext);
BASISU_NOTE_UNUSED(total_blocks);
@@ -1269,7 +1279,7 @@ namespace basisu
BASISU_NOTE_UNUSED(pPixel_weights);
BASISU_NOTE_UNUSED(perceptual);
BASISU_NOTE_UNUSED(total_perms);
return false;
}
+4 -4
View File
@@ -1,5 +1,5 @@
// basisu_opencl.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Note: Undefine or set BASISU_SUPPORT_OPENCL to 0 to completely OpenCL support.
//
@@ -43,7 +43,7 @@ namespace basisu
// Must match BASISU_ETC1_CLUSTER_FIT_ORDER_TABLE_SIZE
const uint32_t OPENCL_ENCODE_ETC1S_MAX_PERMS = 165;
bool opencl_set_pixel_blocks(opencl_context_ptr pContext, uint32_t total_blocks, const cl_pixel_block* pPixel_blocks);
bool opencl_set_pixel_blocks(opencl_context_ptr pContext, size_t total_blocks, const cl_pixel_block* pPixel_blocks);
bool opencl_encode_etc1s_blocks(opencl_context_ptr pContext, etc_block* pOutput_blocks, bool perceptual, uint32_t total_perms);
@@ -59,7 +59,7 @@ namespace basisu
bool opencl_encode_etc1s_pixel_clusters(
opencl_context_ptr pContext,
etc_block* pOutput_blocks,
etc_block* pOutput_blocks,
uint32_t total_clusters,
const cl_pixel_cluster *pClusters,
uint64_t total_pixels,
@@ -92,7 +92,7 @@ namespace basisu
uint32_t total_clusters,
const cl_endpoint_cluster_struct *pCluster_info,
const uint32_t *pSorted_block_indices,
uint32_t* pOutput_cluster_indices,
uint32_t* pOutput_cluster_indices,
bool perceptual);
// opencl_find_optimal_selector_clusters_for_each_block
+14 -14
View File
@@ -1,5 +1,5 @@
// basisu_pvrtc1_4.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -131,7 +131,7 @@ namespace basisu
uint32_t pvrtc4_swizzle_uv(uint32_t width, uint32_t height, uint32_t x, uint32_t y)
{
assert((x < width) && (y < height) && basisu::is_pow2(height) && basisu::is_pow2(width));
uint32_t min_d = width, max_v = y;
if (height < width)
{
@@ -148,7 +148,7 @@ namespace basisu
}
max_v >>= shift_ofs;
// OR in the rest of the bits from the largest dimension
swizzled |= (max_v << (2 * shift_ofs));
@@ -169,7 +169,7 @@ namespace basisu
r = (packed >> 10) & 31;
g = (packed >> 5) & 31;
b = (packed >> 1) & 15;
if (unpack)
{
b = (b << 1) | (b >> 3);
@@ -198,7 +198,7 @@ namespace basisu
{
a = (a << 1);
a = (a << 4) | a;
r = (r << 1) | (r >> 3);
g = (g << 1) | (g >> 3);
b = (b << 2) | (b >> 1);
@@ -272,7 +272,7 @@ namespace basisu
b = (packed >> 1) & 7;
a = a << 1;
r = (r << 1) | (r >> 3);
g = (g << 1) | (g >> 3);
b = (b << 2) | (b >> 1);
@@ -285,13 +285,13 @@ namespace basisu
b = packed & 15;
a = a << 1;
r = (r << 1) | (r >> 3);
g = (g << 1) | (g >> 3);
b = (b << 1) | (b >> 3);
}
}
assert((r < 32) && (g < 32) && (b < 32) && (a < 16));
return color_rgba(r, g, b, a);
@@ -305,12 +305,12 @@ namespace basisu
int block_x1 = block_x0 + 1;
int block_y0 = (static_cast<int>(y) - 2) >> 2;
int block_y1 = block_y0 + 1;
block_x0 = posmod(block_x0, m_block_width);
block_x1 = posmod(block_x1, m_block_width);
block_y0 = posmod(block_y0, m_block_height);
block_y1 = posmod(block_y1, m_block_height);
pColors[0] = interpolate(x, y, m_blocks(block_x0, block_y0).get_endpoint_5554(0), m_blocks(block_x1, block_y0).get_endpoint_5554(0), m_blocks(block_x0, block_y1).get_endpoint_5554(0), m_blocks(block_x1, block_y1).get_endpoint_5554(0));
pColors[3] = interpolate(x, y, m_blocks(block_x0, block_y0).get_endpoint_5554(1), m_blocks(block_x1, block_y0).get_endpoint_5554(1), m_blocks(block_x0, block_y1).get_endpoint_5554(1), m_blocks(block_x1, block_y1).get_endpoint_5554(1));
@@ -334,7 +334,7 @@ namespace basisu
return false;
}
color_rgba pvrtc4_image::get_pixel(uint32_t x, uint32_t y, uint32_t m) const
{
assert((x < m_width) && (y < m_height));
@@ -343,12 +343,12 @@ namespace basisu
int block_x1 = block_x0 + 1;
int block_y0 = (static_cast<int>(y) - 2) >> 2;
int block_y1 = block_y0 + 1;
block_x0 = posmod(block_x0, m_block_width);
block_x1 = posmod(block_x1, m_block_width);
block_y0 = posmod(block_y0, m_block_height);
block_y1 = posmod(block_y1, m_block_height);
if (get_block_uses_transparent_modulation(x >> 2, y >> 2))
{
if (m == 0)
@@ -471,7 +471,7 @@ namespace basisu
color_rgba color_1((int)colors[1][0], (int)colors[1][1], (int)colors[1][2], 0);
pvrtc4_block cur_blocks[3][3];
for (int y = -1; y <= 1; y++)
{
for (int x = -1; x <= 1; x++)
+27 -19
View File
@@ -1,5 +1,5 @@
// basisu_pvrtc1_4.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,14 +17,14 @@
namespace basisu
{
enum
{
PVRTC2_MIN_WIDTH = 16,
PVRTC2_MIN_HEIGHT = 8,
PVRTC4_MIN_WIDTH = 8,
PVRTC4_MIN_HEIGHT = 8
enum
{
PVRTC2_MIN_WIDTH = 16,
PVRTC2_MIN_HEIGHT = 8,
PVRTC4_MIN_WIDTH = 8,
PVRTC4_MIN_HEIGHT = 8
};
struct pvrtc4_block
{
uint32_t m_modulation;
@@ -56,9 +56,9 @@ namespace basisu
// Returns raw endpoint or 8888
color_rgba get_endpoint(uint32_t endpoint_index, bool unpack) const;
color_rgba get_endpoint_5554(uint32_t endpoint_index) const;
static uint32_t get_component_precision_in_bits(uint32_t c, uint32_t endpoint_index, bool opaque_endpoint)
{
static const uint32_t s_comp_prec[4][4] =
@@ -80,7 +80,7 @@ namespace basisu
};
return s_color_prec[open_range_check(endpoint_index, 2U) + (opaque_endpoint * 2)];
}
inline uint32_t get_modulation(uint32_t x, uint32_t y) const
{
assert((x < 4) && (y < 4));
@@ -121,7 +121,7 @@ namespace basisu
assert(endpoint_index < 2);
const uint32_t m = m_endpoints & 1;
uint32_t r = c[0], g = c[1], b = c[2], a = c[3];
uint32_t packed;
if (opaque_endpoint)
@@ -231,7 +231,15 @@ namespace basisu
inline void set_to_black()
{
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
memset(m_blocks.get_ptr(), 0, m_blocks.size_in_bytes());
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
}
inline bool get_block_uses_transparent_modulation(uint32_t bx, uint32_t by) const
@@ -243,7 +251,7 @@ namespace basisu
{
return m_blocks(bx, by).is_endpoint_opaque(endpoint_index);
}
color_rgba get_endpoint(uint32_t bx, uint32_t by, uint32_t endpoint_index, bool unpack) const
{
assert((bx < m_block_width) && (by < m_block_height));
@@ -255,12 +263,12 @@ namespace basisu
assert((x < m_width) && (y < m_height));
return m_blocks(x >> 2, y >> 2).get_modulation(x & 3, y & 3);
}
// Returns true if the block uses transparent modulation.
bool get_interpolated_colors(uint32_t x, uint32_t y, color_rgba* pColors) const;
color_rgba get_pixel(uint32_t x, uint32_t y, uint32_t m) const;
inline color_rgba get_pixel(uint32_t x, uint32_t y) const
{
assert((x < m_width) && (y < m_height));
@@ -445,12 +453,12 @@ namespace basisu
return total_error;
}
public:
public:
uint32_t m_width, m_height;
pvrtc4_block_vector2D m_blocks;
uint32_t m_block_width, m_block_height;
bool m_uses_alpha;
};
+19 -23
View File
@@ -1,5 +1,5 @@
// basisu_resampler_filters.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -20,8 +20,7 @@
namespace basisu
{
#define BOX_FILTER_SUPPORT (0.5f)
static float box_filter(float t) /* pulse/Fourier window */
float box_filter(float t) /* pulse/Fourier window */
{
// make_clist() calls the filter function with t inverted (pos = left, neg = right)
if ((t >= -0.5f) && (t < 0.5f))
@@ -30,8 +29,7 @@ namespace basisu
return 0.0f;
}
#define TENT_FILTER_SUPPORT (1.0f)
static float tent_filter(float t) /* box (*) box, bilinear/triangle */
float tent_filter(float t) /* box (*) box, bilinear/triangle */
{
if (t < 0.0f)
t = -t;
@@ -42,8 +40,7 @@ namespace basisu
return 0.0f;
}
#define BELL_SUPPORT (1.5f)
static float bell_filter(float t) /* box (*) box (*) box */
float bell_filter(float t) /* box (*) box (*) box */
{
if (t < 0.0f)
t = -t;
@@ -201,13 +198,12 @@ namespace basisu
return (0.0f);
}
#define GAUSSIAN_SUPPORT (1.25f)
static float gaussian_filter(float t) // with blackman window
float gaussian_filter(float t) // with blackman window
{
if (t < 0)
t = -t;
if (t < GAUSSIAN_SUPPORT)
return clean(exp(-2.0f * t * t) * sqrt(2.0f / M_PI) * blackman_exact_window(t / GAUSSIAN_SUPPORT));
if (t < BASISU_GAUSSIAN_FILTER_SUPPORT)
return clean(exp(-2.0f * t * t) * sqrt(2.0f / M_PI) * blackman_exact_window(t / BASISU_GAUSSIAN_FILTER_SUPPORT));
else
return 0.0f;
}
@@ -310,21 +306,21 @@ namespace basisu
const resample_filter g_resample_filters[] =
{
{ "box", box_filter, BOX_FILTER_SUPPORT },
{ "tent", tent_filter, TENT_FILTER_SUPPORT },
{ "bell", bell_filter, BELL_SUPPORT },
{ "box", box_filter, BASISU_BOX_FILTER_SUPPORT },
{ "tent", tent_filter, BASISU_TENT_FILTER_SUPPORT },
{ "bell", bell_filter, BASISU_BELL_FILTER_SUPPORT },
{ "b-spline", B_spline_filter, B_SPLINE_SUPPORT },
{ "mitchell", mitchell_filter, MITCHELL_SUPPORT },
{ "blackman", blackman_filter, BLACKMAN_SUPPORT },
{ "mitchell", mitchell_filter, MITCHELL_SUPPORT },
{ "blackman", blackman_filter, BLACKMAN_SUPPORT },
{ "lanczos3", lanczos3_filter, LANCZOS3_SUPPORT },
{ "lanczos4", lanczos4_filter, LANCZOS4_SUPPORT },
{ "lanczos6", lanczos6_filter, LANCZOS6_SUPPORT },
{ "lanczos12", lanczos12_filter, LANCZOS12_SUPPORT },
{ "kaiser", kaiser_filter, KAISER_SUPPORT },
{ "gaussian", gaussian_filter, GAUSSIAN_SUPPORT },
{ "catmullrom", catmull_rom_filter, CATMULL_ROM_SUPPORT },
{ "quadratic_interp", quadratic_interp_filter, QUADRATIC_SUPPORT },
{ "quadratic_approx", quadratic_approx_filter, QUADRATIC_SUPPORT },
{ "lanczos6", lanczos6_filter, LANCZOS6_SUPPORT },
{ "lanczos12", lanczos12_filter, LANCZOS12_SUPPORT },
{ "kaiser", kaiser_filter, KAISER_SUPPORT },
{ "gaussian", gaussian_filter, BASISU_GAUSSIAN_FILTER_SUPPORT },
{ "catmullrom", catmull_rom_filter, CATMULL_ROM_SUPPORT },
{ "quadratic_interp", quadratic_interp_filter, QUADRATIC_SUPPORT },
{ "quadratic_approx", quadratic_approx_filter, QUADRATIC_SUPPORT },
{ "quadratic_mix", quadratic_mix_filter, QUADRATIC_SUPPORT },
};
+4 -4
View File
@@ -1,5 +1,5 @@
// basisu_resampler.cpp
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -139,7 +139,7 @@ namespace basisu
n += (right - left + 1);
}
// Allocate memory for contributors.
// Allocate memory for contributors.
if ((n == 0) || ((Pcpool = (Contrib*)calloc(n, sizeof(Contrib))) == NULL))
{
@@ -573,7 +573,7 @@ namespace basisu
/* Don't deallocate a contibutor list
* if the user passed us one of their own.
*/
*/
if ((m_Pclist_x) && (!m_clist_x_forced))
{
@@ -840,5 +840,5 @@ namespace basisu
else
return g_resample_filters[filter_num].name;
}
} // namespace basisu
+3 -1
View File
@@ -1,5 +1,5 @@
// basisu_resampler.h
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -113,6 +113,8 @@ namespace basisu
Resample_Real filter_scale,
Resample_Real src_ofs);
static void free_clist(Contrib_List* p) { if (p) { free(p->p); free(p); } }
private:
Resampler();
Resampler(const Resampler &o);
+13 -1
View File
@@ -1,5 +1,5 @@
// basisu_resampler_filters.h
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -30,6 +30,18 @@ namespace basisu
extern const resample_filter g_resample_filters[];
extern const int g_num_resample_filters;
const float BASISU_BOX_FILTER_SUPPORT = 0.5f;
float box_filter(float t); /* pulse/Fourier window */
const float BASISU_TENT_FILTER_SUPPORT = 1.0f;
float tent_filter(float t); /* box (*) box, bilinear/triangle */
const float BASISU_GAUSSIAN_FILTER_SUPPORT = 1.25f;
float gaussian_filter(float t); // with blackman window
const float BASISU_BELL_FILTER_SUPPORT = 1.5f;
float bell_filter(float t); /* box (*) box (*) box */
int find_resample_filter(const char *pName);
} // namespace basisu
+6 -4
View File
@@ -1,5 +1,5 @@
// basisu_ssim.cpp
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@ namespace basisu
float g = (1.0f / (sqrtf((float)(2.0f * M_PI * sigma_sqr)))) * pow;
return g;
}
// size_x/y should be odd
void compute_gaussian_kernel(float *pDst, int size_x, int size_y, float sigma_sqr, uint32_t flags)
{
@@ -91,6 +91,8 @@ namespace basisu
void gaussian_filter(imagef &dst, const imagef &orig_img, uint32_t odd_filter_width, float sigma_sqr, bool wrapping, uint32_t width_divisor, uint32_t height_divisor)
{
assert(&dst != &orig_img);
assert(odd_filter_width && (odd_filter_width & 1));
odd_filter_width |= 1;
@@ -316,14 +318,14 @@ namespace basisu
return avg;
}
// Reference: https://ece.uwaterloo.ca/~z70wang/research/ssim/index.html
vec4F compute_ssim(const imagef &a, const imagef &b)
{
imagef axb, a_sq, b_sq, mu1, mu2, mu1_sq, mu2_sq, mu1_mu2, s1_sq, s2_sq, s12, smap, t1, t2, t3;
const float C1 = 6.50250f, C2 = 58.52250f;
pow_image(a, a_sq, vec4F(2));
pow_image(b, b_sq, vec4F(2));
mul_image(a, b, axb, vec4F(1.0f));
+1 -1
View File
@@ -1,5 +1,5 @@
// basisu_ssim.h
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
+26 -50
View File
@@ -1,5 +1,5 @@
// basisu_uastc_enc.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -13,11 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "basisu_uastc_enc.h"
#if BASISU_USE_ASTC_DECOMPRESS
#include "basisu_astc_decomp.h"
#endif
#include "3rdparty/android_astc_decomp.h"
#include "basisu_gpu_texture.h"
#include "basisu_bc7enc.h"
@@ -323,7 +319,15 @@ namespace basisu
const uint32_t comp_plane = (total_comps == 2) ? c : ((c == result.m_astc.m_ccs) ? 1 : 0);
if (comp_plane == plane_index)
std::swap(endpoints[c * 2 + 0], endpoints[c * 2 + 1]);
{
// shut up a useless gcc warning
assert((c * 2 + 1) < (int)sizeof(endpoints));
if ((c * 2 + 1) < (int)sizeof(endpoints))
{
std::swap(endpoints[c * 2 + 0], endpoints[c * 2 + 1]);
}
}
}
}
else
@@ -384,6 +388,7 @@ namespace basisu
}
uint32_t total_endpoint_bits = 0;
(void)total_endpoint_bits;
for (uint32_t i = 0; i < total_tq_values; i++)
{
@@ -428,6 +433,8 @@ namespace basisu
#endif
uint32_t total_weight_bits = 0;
(void)total_weight_bits;
const uint32_t plane_shift = (total_planes == 2) ? 1 : 0;
for (uint32_t i = 0; i < 16 * total_planes; i++)
{
@@ -2543,7 +2550,7 @@ namespace basisu
color_rgba tblock_bc1[4][4];
dxt1_block tbc1_block[8];
basist::encode_bc1(tbc1_block, (const uint8_t*)&decoded_uastc_block[0][0], 0);
unpack_block(texture_format::cBC1, tbc1_block, &tblock_bc1[0][0]);
unpack_block(texture_format::cBC1, tbc1_block, &tblock_bc1[0][0], false);
color_rgba tblock_hint0_bc1[4][4];
color_rgba tblock_hint1_bc1[4][4];
@@ -2580,7 +2587,7 @@ namespace basisu
{
transcode_uastc_to_bc1_hint1(ublock, (color32 (*)[4]) decoded_uastc_block, &b, false);
unpack_block(texture_format::cBC1, &b, &tblock_hint1_bc1[0][0]);
unpack_block(texture_format::cBC1, &b, &tblock_hint1_bc1[0][0], false);
}
// HINT0
@@ -2592,7 +2599,7 @@ namespace basisu
{
transcode_uastc_to_bc1_hint0(ublock, &b);
unpack_block(texture_format::cBC1, &b, &tblock_hint0_bc1[0][0]);
unpack_block(texture_format::cBC1, &b, &tblock_hint0_bc1[0][0], false);
}
// Compute block errors
@@ -3175,6 +3182,7 @@ namespace basisu
const bool favor_bc7_error = !favor_uastc_error && ((flags & cPackUASTCFavorBC7Error) != 0);
//const bool etc1_perceptual = true;
// TODO: This uses 64KB of stack space!
uastc_encode_results results[MAX_ENCODE_RESULTS];
level = clampi(level, cPackUASTCLevelFastest, cPackUASTCLevelVerySlow);
@@ -3447,7 +3455,7 @@ namespace basisu
encode_bc7_block(&bc7_data, &bc7_results);
color_rgba decoded_bc7_block[4][4];
unpack_block(texture_format::cBC7, &bc7_data, &decoded_bc7_block[0][0]);
unpack_block(texture_format::cBC7, &bc7_data, &decoded_bc7_block[0][0], false);
// Compute BC7 error
uint64_t total_bc7_la_err, total_bc7_rgb_err, total_bc7_rgba_err;
@@ -3567,7 +3575,6 @@ namespace basisu
success = basist::unpack_uastc(temp_block, (basist::color32 *)temp_block_unpacked, false);
VALIDATE(success);
#if BASISU_USE_ASTC_DECOMPRESS
// Now round trip to packed ASTC and back, then decode to pixels.
uint32_t astc_data[4];
@@ -3580,7 +3587,7 @@ namespace basisu
}
color_rgba decoded_astc_block[4][4];
success = basisu_astc::astc::decompress((uint8_t*)decoded_astc_block, (uint8_t*)&astc_data, false, 4, 4);
success = basisu_astc::astc::decompress_ldr((uint8_t*)decoded_astc_block, (uint8_t*)&astc_data, false, 4, 4);
VALIDATE(success);
for (uint32_t y = 0; y < 4; y++)
@@ -3595,7 +3602,6 @@ namespace basisu
VALIDATE(temp_block_unpacked[y][x].c[3] == decoded_uastc_block[y][x].a);
}
}
#endif
}
#endif
@@ -3789,8 +3795,9 @@ namespace basisu
{
uint64_t m_sel;
uint32_t m_ofs;
uint32_t m_pad; // avoid implicit padding for selector_bitsequence_hash
selector_bitsequence() { }
selector_bitsequence(uint32_t bit_ofs, uint64_t sel) : m_sel(sel), m_ofs(bit_ofs) { }
selector_bitsequence(uint32_t bit_ofs, uint64_t sel) : m_sel(sel), m_ofs(bit_ofs), m_pad(0) { }
bool operator== (const selector_bitsequence& other) const
{
return (m_ofs == other.m_ofs) && (m_sel == other.m_sel);
@@ -3811,35 +3818,10 @@ namespace basisu
{
std::size_t operator()(selector_bitsequence const& s) const noexcept
{
return static_cast<std::size_t>(hash_hsieh((uint8_t *)&s, sizeof(s)) ^ s.m_sel);
return basist::hash_hsieh((const uint8_t*)&s, sizeof(s));
}
};
class tracked_stat
{
public:
tracked_stat() { clear(); }
void clear() { m_num = 0; m_total = 0; m_total2 = 0; }
void update(uint32_t val) { m_num++; m_total += val; m_total2 += val * val; }
tracked_stat& operator += (uint32_t val) { update(val); return *this; }
uint32_t get_number_of_values() { return m_num; }
uint64_t get_total() const { return m_total; }
uint64_t get_total2() const { return m_total2; }
float get_average() const { return m_num ? (float)m_total / m_num : 0.0f; };
float get_std_dev() const { return m_num ? sqrtf((float)(m_num * m_total2 - m_total * m_total)) / m_num : 0.0f; }
float get_variance() const { float s = get_std_dev(); return s * s; }
private:
uint32_t m_num;
uint64_t m_total;
uint64_t m_total2;
};
static bool uastc_rdo_blocks(uint32_t first_index, uint32_t last_index, basist::uastc_block* pBlocks, const color_rgba* pBlock_pixels, const uastc_rdo_params& params, uint32_t flags,
uint32_t &total_skipped, uint32_t &total_refined, uint32_t &total_modified, uint32_t &total_smooth)
{
@@ -3898,7 +3880,7 @@ namespace basisu
basist::encode_bc7_block(&b7_block, &b7_results);
color_rgba decoded_b7_blk[4][4];
unpack_block(texture_format::cBC7, &b7_block, &decoded_b7_blk[0][0]);
unpack_block(texture_format::cBC7, &b7_block, &decoded_b7_blk[0][0], false);
uint64_t bc7_err = 0;
for (uint32_t i = 0; i < 16; i++)
@@ -4007,7 +3989,7 @@ namespace basisu
basist::encode_bc7_block(&trial_b7_block, &trial_b7_results);
color_rgba decoded_trial_b7_blk[4][4];
unpack_block(texture_format::cBC7, &trial_b7_block, &decoded_trial_b7_blk[0][0]);
unpack_block(texture_format::cBC7, &trial_b7_block, &decoded_trial_b7_blk[0][0], false);
uint64_t trial_bc7_err = 0;
for (uint32_t i = 0; i < 16; i++)
@@ -4151,9 +4133,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(num_blocks, block_index_iter + blocks_per_job);
#ifndef __EMSCRIPTEN__
pJob_pool->add_job([first_index, last_index, pBlocks, pBlock_pixels, &params, flags, &total_skipped, &total_modified, &total_refined, &total_smooth, &all_succeeded, &stat_mutex] {
#endif
uint32_t job_skipped = 0, job_modified = 0, job_refined = 0, job_smooth = 0;
@@ -4169,16 +4149,12 @@ namespace basisu
total_smooth += job_smooth;
}
#ifndef __EMSCRIPTEN__
}
);
#endif
} // block_index_iter
#ifndef __EMSCRIPTEN__
pJob_pool->wait_for_all();
#endif
status = all_succeeded;
}
+18 -18
View File
@@ -1,5 +1,5 @@
// basisu_uastc_enc.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -25,15 +25,15 @@ namespace basisu
{
// Fastest is the lowest quality, although it's stil substantially higher quality vs. BC1/ETC1. It supports 5 modes.
// The output may be somewhat blocky because this setting doesn't support 2/3-subset UASTC modes, but it should be less blocky vs. BC1/ETC1.
// This setting doesn't write BC1 hints, so BC1 transcoding will be slower.
// This setting doesn't write BC1 hints, so BC1 transcoding will be slower.
// Transcoded ETC1 quality will be lower because it only considers 2 hints out of 32.
// Avg. 43.45 dB
cPackUASTCLevelFastest = 0,
// Faster is ~3x slower than fastest. It supports 9 modes.
// Avg. 46.49 dB
cPackUASTCLevelFaster = 1,
// Default is ~5.5x slower than fastest. It supports 14 modes.
// Avg. 47.47 dB
cPackUASTCLevelDefault = 2,
@@ -42,7 +42,7 @@ namespace basisu
// Avg. 48.01 dB
cPackUASTCLevelSlower = 3,
// VerySlow is ~200x slower than fastest.
// VerySlow is ~200x slower than fastest.
// The best quality the codec is capable of, but you'll need to be patient or have a lot of cores.
// Avg. 48.24 dB
cPackUASTCLevelVerySlow = 4,
@@ -53,13 +53,13 @@ namespace basisu
// These flags allow you to favor only optimizing for lowest UASTC error, or lowest BC7 error.
cPackUASTCFavorUASTCError = 8,
cPackUASTCFavorBC7Error = 16,
cPackUASTCETC1FasterHints = 64,
cPackUASTCETC1FastestHints = 128,
cPackUASTCETC1DisableFlipAndIndividual = 256,
// Favor UASTC modes 0 and 10 more than the others (this is experimental, it's useful for RDO compression)
cPackUASTCFavorSimplerModes = 512,
cPackUASTCFavorSimplerModes = 512,
};
// pRGBAPixels: Pointer to source 4x4 block of RGBA pixels (R first in memory).
@@ -75,18 +75,18 @@ namespace basisu
color_rgba m_solid_color;
uint64_t m_astc_err;
};
void pack_uastc(basist::uastc_block& blk, const uastc_encode_results& result, const etc_block& etc1_blk, uint32_t etc1_bias, const eac_a8_block& etc_eac_a8_blk, bool bc1_hint0, bool bc1_hint1);
const uint32_t UASCT_RDO_DEFAULT_LZ_DICT_SIZE = 4096;
const float UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO = 10.0f;
const float UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH = 8.0f;
// The RDO encoder computes a smoothness factor, from [0,1], for each block. To do this it computes each block's maximum component variance, then it divides this by this factor and clamps the result.
// Larger values will result in more blocks being protected from too much distortion.
const float UASTC_RDO_DEFAULT_MAX_SMOOTH_BLOCK_STD_DEV = 18.0f;
// The RDO encoder can artifically boost the error of smooth blocks, in order to suppress distortions on smooth areas of the texture.
// The encoder will use this value as the maximum error scale to use on smooth blocks. The larger this value, the better smooth bocks will look. Set to 1.0 to disable this completely.
const float UASTC_RDO_DEFAULT_SMOOTH_BLOCK_MAX_ERROR_SCALE = 10.0f;
@@ -106,30 +106,30 @@ namespace basisu
m_skip_block_rms_thresh = UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH;
m_endpoint_refinement = true;
m_lz_literal_cost = 100;
m_max_smooth_block_std_dev = UASTC_RDO_DEFAULT_MAX_SMOOTH_BLOCK_STD_DEV;
m_smooth_block_max_error_scale = UASTC_RDO_DEFAULT_SMOOTH_BLOCK_MAX_ERROR_SCALE;
}
// m_lz_dict_size: Size of LZ dictionary to simulate in bytes. The larger this value, the slower the encoder but the higher the quality per LZ compressed bit.
uint32_t m_lz_dict_size;
// m_lambda: The post-processor tries to reduce distortion+rate*lambda (rate is approximate LZ bits and distortion is scaled MS error).
// Larger values push the postprocessor towards optimizing more for lower rate, and smaller values more for distortion. 0=minimal distortion.
float m_lambda;
// m_max_allowed_rms_increase_ratio: How much the RMS error of a block is allowed to increase before a trial is rejected. 1.0=no increase allowed, 1.05=5% increase allowed, etc.
float m_max_allowed_rms_increase_ratio;
// m_skip_block_rms_thresh: Blocks with this much RMS error or more are completely skipped by the RDO encoder.
// m_skip_block_rms_thresh: Blocks with this much RMS error or more are completely skipped by the RDO encoder.
float m_skip_block_rms_thresh;
// m_endpoint_refinement: If true, the post-process will attempt to refine the endpoints of blocks with modified selectors.
// m_endpoint_refinement: If true, the post-process will attempt to refine the endpoints of blocks with modified selectors.
bool m_endpoint_refinement;
float m_max_smooth_block_std_dev;
float m_smooth_block_max_error_scale;
uint32_t m_lz_literal_cost;
};
File diff suppressed because it is too large Load Diff
+180
View File
@@ -0,0 +1,180 @@
// basisu_uastc_hdr_4x4_enc.h
#pragma once
#include "basisu_enc.h"
#include "basisu_gpu_texture.h"
#include "../transcoder/basisu_astc_helpers.h"
#include "../transcoder/basisu_astc_hdr_core.h"
#include "basisu_astc_hdr_common.h"
namespace basisu
{
struct uastc_hdr_4x4_codec_options : astc_hdr_codec_base_options
{
float m_bc6h_err_weight;
bool m_use_solid;
bool m_use_mode11_part1;
bool m_mode11_uber_mode;
uint32_t m_first_mode11_weight_ise_range;
uint32_t m_last_mode11_weight_ise_range;
bool m_mode11_direct_only;
int32_t m_first_mode11_submode;
int32_t m_last_mode11_submode;
bool m_use_mode7_part1;
uint32_t m_first_mode7_part1_weight_ise_range;
uint32_t m_last_mode7_part1_weight_ise_range;
bool m_use_mode7_part2;
uint32_t m_mode7_part2_part_masks;
uint32_t m_first_mode7_part2_weight_ise_range;
uint32_t m_last_mode7_part2_weight_ise_range;
bool m_use_mode11_part2;
uint32_t m_mode11_part2_part_masks;
uint32_t m_first_mode11_part2_weight_ise_range;
uint32_t m_last_mode11_part2_weight_ise_range;
bool m_refine_weights;
uint32_t m_level;
bool m_use_estimated_partitions;
uint32_t m_max_estimated_partitions;
uastc_hdr_4x4_codec_options();
void init();
// TODO: set_quality_level() is preferred to configure the codec for transcoding purposes.
static const int cMinLevel = 0;
static const int cMaxLevel = 4;
static const int cDefaultLevel = 1;
void set_quality_level(int level);
private:
void set_quality_best();
void set_quality_normal();
void set_quality_fastest();
};
struct astc_hdr_4x4_pack_results
{
double m_best_block_error;
double m_bc6h_block_error; // note this is not used/set by the encoder, here for convienance
// Encoder results (logical ASTC block)
astc_helpers::log_astc_block m_best_blk;
// For statistical use
uint32_t m_best_submodes[2];
uint32_t m_best_pat_index;
bool m_constrained_weights;
bool m_improved_via_refinement_flag;
// Only valid if the block is solid
basist::astc_blk m_solid_blk;
// The BC6H transcoded block
basist::bc6h_block m_bc6h_block;
// Solid color/void extent flag
bool m_is_solid;
void clear()
{
m_best_block_error = 1e+30f;
m_bc6h_block_error = 1e+30f;
m_best_blk.clear();
m_best_blk.m_grid_width = 4;
m_best_blk.m_grid_height = 4;
m_best_blk.m_endpoint_ise_range = 20; // 0-255
clear_obj(m_best_submodes);
m_best_pat_index = 0;
m_constrained_weights = false;
clear_obj(m_bc6h_block);
m_is_solid = false;
m_improved_via_refinement_flag = false;
}
};
// Encodes a 4x4 ASTC HDR block given a 4x4 array of source block pixels/texels.
// Supports solid color blocks, mode 11 (all submodes), mode 7/1 partition (all submodes),
// and mode 7/2 partitions (all submodes) - 30 patterns, only the ones also in common with the BC6H format.
// The packed ASTC weight grid dimensions are currently always 4x4 texels, but may be also 3x3 in the future.
// This function is thread safe, i.e. it may be called from multiple encoding threads simultanously with different blocks.
//
// Parameters:
// pRGBPixels - An array of 48 (16 RGB) floats: the 4x4 block to pack
// pPacked_block - A pointer to the packed ASTC HDR block
// coptions - Codec options
// pInternal_results - An optional pointer to details about how the block was packed, for statistics/debugging purposes. May be nullptr.
//
// Requirements:
// astc_hdr_enc_init() MUST have been called first to initialized the codec.
// Input pixels are checked and cannot be NaN's, Inf's, signed, or too large (greater than MAX_HALF_FLOAT, or 65504).
// Normal values and denormals are okay.
bool astc_hdr_4x4_enc_block(
const float* pRGBPixels, const basist::half_float *pRGBPixelsHalf,
const uastc_hdr_4x4_codec_options& coptions,
basisu::vector<astc_hdr_4x4_pack_results> &all_results);
bool astc_hdr_4x4_pack_results_to_block(basist::astc_blk& dst_blk, const astc_hdr_4x4_pack_results& results);
bool astc_hdr_4x4_refine_weights(const basist::half_float* pSource_block, astc_hdr_4x4_pack_results& cur_results, const uastc_hdr_4x4_codec_options& coptions, float bc6h_weight, bool* pImproved_flag);
struct astc_hdr_4x4_block_stats
{
std::mutex m_mutex;
uint32_t m_total_blocks;
uint32_t m_total_2part, m_total_solid;
uint32_t m_total_mode7_1part, m_total_mode7_2part;
uint32_t m_total_mode11_1part, m_total_mode11_2part;
uint32_t m_total_mode11_1part_constrained_weights;
uint32_t m_weight_range_hist_7[11];
uint32_t m_weight_range_hist_7_2part[11];
uint32_t m_mode7_submode_hist[6];
uint32_t m_weight_range_hist_11[11];
uint32_t m_weight_range_hist_11_2part[11];
uint32_t m_mode11_submode_hist[9];
uint32_t m_part_hist[32];
uint32_t m_total_refined;
astc_hdr_4x4_block_stats() { clear(); }
void clear()
{
std::lock_guard<std::mutex> lck(m_mutex);
m_total_blocks = 0;
m_total_mode7_1part = 0, m_total_mode7_2part = 0, m_total_mode11_1part = 0, m_total_2part = 0, m_total_solid = 0, m_total_mode11_2part = 0;
m_total_mode11_1part_constrained_weights = 0;
m_total_refined = 0;
clear_obj(m_weight_range_hist_11);
clear_obj(m_weight_range_hist_11_2part);
clear_obj(m_weight_range_hist_7);
clear_obj(m_weight_range_hist_7_2part);
clear_obj(m_mode7_submode_hist);
clear_obj(m_mode11_submode_hist);
clear_obj(m_part_hist);
}
void update(const astc_hdr_4x4_pack_results& log_blk);
void print();
};
} // namespace basisu
+319
View File
@@ -0,0 +1,319 @@
// File: basisu_wasm_api.cpp - Simplified compression API for WASM WASI modules and Python native support.
// Also useable by plain C callers.
#include "basisu_comp.h"
#include "basisu_wasm_api.h"
using namespace basisu;
static inline uint64_t wasm_offset(void* p)
{
return (uint64_t)(uintptr_t)p;
}
static inline uint8_t* wasm_ptr(uint64_t offset)
{
return (uint8_t*)(uintptr_t)offset;
}
BU_WASM_EXPORT("bu_get_version")
uint32_t bu_get_version()
{
printf("Hello from basisu_wasm_api.cpp version %u\n", BASISU_LIB_VERSION);
return BASISU_LIB_VERSION;
}
BU_WASM_EXPORT("bu_enable_debug_printf")
void bu_enable_debug_printf(uint32_t flag)
{
enable_debug_printf(flag != 0);
}
BU_WASM_EXPORT("bu_init")
void bu_init()
{
basisu_encoder_init(false, false);
}
// Memory alloc/free — stubs
BU_WASM_EXPORT("bu_alloc")
uint64_t bu_alloc(uint64_t size)
{
void* p = malloc((size_t)size);
return wasm_offset(p);
}
BU_WASM_EXPORT("bu_free")
void bu_free(uint64_t ofs)
{
free(wasm_ptr(ofs));
}
const uint32_t COMP_PARAMS_MAGIC = 0x43504D50; // "CPMP"
struct comp_params
{
uint32_t m_magic = COMP_PARAMS_MAGIC;
comp_params()
{
clear();
}
void clear()
{
assert(m_magic == COMP_PARAMS_MAGIC);
m_comp_data.clear();
m_images.clear();
m_imagesf.clear();
m_stats.clear();
}
uint8_vec m_comp_data;
basisu::vector<image> m_images;
basisu::vector<imagef> m_imagesf;
image_stats m_stats;
};
BU_WASM_EXPORT("bu_new_comp_params")
uint64_t bu_new_comp_params()
{
comp_params* p = new comp_params;
return wasm_offset(p);
}
BU_WASM_EXPORT("bu_delete_comp_params")
wasm_bool_t bu_delete_comp_params(uint64_t params_ofs)
{
comp_params* p = (comp_params*)wasm_ptr(params_ofs);
if (!p)
return false;
assert(p->m_magic == COMP_PARAMS_MAGIC);
if (p->m_magic != COMP_PARAMS_MAGIC)
return false;
delete p;
return true;
}
BU_WASM_EXPORT("bu_comp_params_get_comp_data_size")
uint64_t bu_comp_params_get_comp_data_size(uint64_t params_ofs)
{
comp_params* pParams = (comp_params*)wasm_ptr(params_ofs);
if (!pParams)
return 0;
assert(pParams->m_magic == COMP_PARAMS_MAGIC);
if (pParams->m_magic != COMP_PARAMS_MAGIC)
return 0;
return pParams->m_comp_data.size();
}
BU_WASM_EXPORT("bu_comp_params_get_comp_data_ofs")
uint64_t bu_comp_params_get_comp_data_ofs(uint64_t params_ofs)
{
comp_params* pParams = (comp_params*)wasm_ptr(params_ofs);
if (!pParams)
return 0;
assert(pParams->m_magic == COMP_PARAMS_MAGIC);
if (pParams->m_magic != COMP_PARAMS_MAGIC)
return 0;
return wasm_offset(pParams->m_comp_data.get_ptr());
}
BU_WASM_EXPORT("bu_comp_params_clear")
wasm_bool_t bu_comp_params_clear(uint64_t params_ofs)
{
comp_params* pParams = (comp_params*)wasm_ptr(params_ofs);
if (!pParams)
return false;
assert(pParams->m_magic == COMP_PARAMS_MAGIC);
if (pParams->m_magic != COMP_PARAMS_MAGIC)
return false;
pParams->clear();
return true;
}
// Caller wants to give us a LDR/SDR 32bpp RGBA mipmap level (4 bytes per pixel)
BU_WASM_EXPORT("bu_comp_params_set_image_rgba32")
wasm_bool_t bu_comp_params_set_image_rgba32(
uint64_t params_ofs,
uint32_t image_index,
uint64_t img_data_ofs,
uint32_t width, uint32_t height,
uint32_t pitch_in_bytes)
{
if ((!width) || (!height) || (!pitch_in_bytes))
return false;
comp_params* pParams = (comp_params*)wasm_ptr(params_ofs);
if (!pParams)
return false;
assert(pParams->m_magic == COMP_PARAMS_MAGIC);
if (pParams->m_magic != COMP_PARAMS_MAGIC)
return false;
const uint8_t* pImage = wasm_ptr(img_data_ofs);
if (!pImage)
return false;
const uint32_t bytes_per_pixel = sizeof(color_rgba);
if (pitch_in_bytes < width * bytes_per_pixel)
return false;
if (image_index >= pParams->m_images.size())
{
if (!pParams->m_images.try_resize(image_index + 1))
return false;
}
basisu::image& dst_img = pParams->m_images[image_index];
dst_img.resize(width, height);
if (pitch_in_bytes == width * bytes_per_pixel)
{
memcpy(dst_img.get_ptr(), pImage, pitch_in_bytes * height);
}
else
{
for (uint32_t y = 0; y < height; y++)
{
const uint8_t* pSrc_row = pImage + y * pitch_in_bytes;
uint8_t* pDst_row = (uint8_t *)&dst_img(0, y);
memcpy(pDst_row, pSrc_row, width * bytes_per_pixel);
} // y
}
return true;
}
// Caller wants to give us a float RGBA mipmap level (4*4=16 bytes per pixel)
BU_WASM_EXPORT("bu_comp_params_set_image_float_rgba")
wasm_bool_t bu_comp_params_set_image_float_rgba(
uint64_t params_ofs,
uint32_t image_index,
uint64_t img_data_ofs,
uint32_t width, uint32_t height,
uint32_t pitch_in_bytes)
{
if ((!width) || (!height) || (!pitch_in_bytes))
return false;
comp_params* pParams = (comp_params*)wasm_ptr(params_ofs);
if (!pParams)
return false;
assert(pParams->m_magic == COMP_PARAMS_MAGIC);
if (pParams->m_magic != COMP_PARAMS_MAGIC)
return false;
const uint8_t* pImage = wasm_ptr(img_data_ofs);
if (!pImage)
return false;
const uint32_t bytes_per_pixel = sizeof(float) * 4;
if (pitch_in_bytes < width * bytes_per_pixel)
return false;
if (image_index >= pParams->m_images.size())
{
if (!pParams->m_imagesf.try_resize(image_index + 1))
return false;
}
basisu::imagef& dst_img = pParams->m_imagesf[image_index];
dst_img.resize(width, height);
if (pitch_in_bytes == width * bytes_per_pixel)
{
memcpy((void *)dst_img.get_ptr(), (const void *)pImage, pitch_in_bytes * height);
}
else
{
for (uint32_t y = 0; y < height; y++)
{
const uint8_t* pSrc_row = pImage + y * pitch_in_bytes;
uint8_t* pDst_row = (uint8_t*)&dst_img(0, y);
memcpy(pDst_row, pSrc_row, width * bytes_per_pixel);
} // y
}
return true;
}
BU_WASM_EXPORT("bu_compress_texture")
wasm_bool_t bu_compress_texture(
uint64_t params_ofs,
uint32_t desired_basis_tex_format, // basis_tex_format
int quality_level, int effort_level,
uint64_t flags_and_quality, float low_level_uastc_rdo_or_dct_quality)
{
//enable_debug_printf((flags_and_quality & cFlagDebug) != 0);
comp_params* pParams = (comp_params*)wasm_ptr(params_ofs);
if (!pParams)
return false;
assert(pParams->m_magic == COMP_PARAMS_MAGIC);
if (pParams->m_magic != COMP_PARAMS_MAGIC)
return false;
pParams->m_comp_data.clear();
if (desired_basis_tex_format >= (uint32_t)basist::basis_tex_format::cTotalFormats)
return false;
if (!pParams->m_images.size() && !pParams->m_imagesf.size())
return false;
if (pParams->m_images.size() && pParams->m_imagesf.size())
return false;
size_t comp_size = 0;
void* pComp_data = basis_compress_internal(
(basist::basis_tex_format)desired_basis_tex_format,
pParams->m_images.size() ? &pParams->m_images : nullptr,
pParams->m_imagesf.size() ? &pParams->m_imagesf : nullptr,
(uint32_t)flags_and_quality,
low_level_uastc_rdo_or_dct_quality,
&comp_size,
&pParams->m_stats,
quality_level,
effort_level);
if (!pComp_data)
return false;
if (!pParams->m_comp_data.try_resize(comp_size))
{
basis_free_data(pComp_data);
return false;
}
memcpy(pParams->m_comp_data.get_ptr(), pComp_data, comp_size);
basis_free_data(pComp_data);
return true;
}
+58
View File
@@ -0,0 +1,58 @@
// File: basisu_wasm_api.h
#pragma once
#include "basisu_wasm_api_common.h"
BU_WASM_EXPORT("bu_get_version")
uint32_t bu_get_version();
BU_WASM_EXPORT("bu_enable_debug_printf")
void bu_enable_debug_printf(uint32_t flag);
BU_WASM_EXPORT("bu_init")
void bu_init();
BU_WASM_EXPORT("bu_alloc")
uint64_t bu_alloc(uint64_t size);
BU_WASM_EXPORT("bu_free")
void bu_free(uint64_t ofs);
BU_WASM_EXPORT("bu_new_comp_params")
uint64_t bu_new_comp_params();
BU_WASM_EXPORT("bu_delete_comp_params")
wasm_bool_t bu_delete_comp_params(uint64_t params_ofs);
BU_WASM_EXPORT("bu_comp_params_get_comp_data_size")
uint64_t bu_comp_params_get_comp_data_size(uint64_t params_ofs);
BU_WASM_EXPORT("bu_comp_params_get_comp_data_ofs")
uint64_t bu_comp_params_get_comp_data_ofs(uint64_t params_ofs);
BU_WASM_EXPORT("bu_comp_params_clear")
wasm_bool_t bu_comp_params_clear(uint64_t params_ofs);
BU_WASM_EXPORT("bu_comp_params_set_image_rgba32")
wasm_bool_t bu_comp_params_set_image_rgba32(
uint64_t params_ofs,
uint32_t image_index,
uint64_t img_data_ofs,
uint32_t width, uint32_t height,
uint32_t pitch_in_bytes);
BU_WASM_EXPORT("bu_comp_params_set_image_float_rgba")
wasm_bool_t bu_comp_params_set_image_float_rgba(
uint64_t params_ofs,
uint32_t image_index,
uint64_t img_data_ofs,
uint32_t width, uint32_t height,
uint32_t pitch_in_bytes);
BU_WASM_EXPORT("bu_compress_texture")
wasm_bool_t bu_compress_texture(
uint64_t params_ofs,
uint32_t desired_basis_tex_format,
int quality_level, int effort_level,
uint64_t flags_and_quality,
float low_level_uastc_rdo_or_dct_quality);
+156
View File
@@ -0,0 +1,156 @@
// File: basisu_wasm_api_common.h
#pragma once
#include "stdint.h"
#if defined(__wasm__)
#if defined(__cplusplus)
#define BU_WASM_EXPORT(name) __attribute__((export_name(name))) extern "C"
#else
#define BU_WASM_EXPORT(name) __attribute__((export_name(name)))
#endif
#elif defined(__cplusplus)
#define BU_WASM_EXPORT(name) extern "C"
#else
#define BU_WASM_EXPORT(name)
#endif
// wasm_bool_t is an alias for uint32_t
typedef uint32_t wasm_bool_t;
// Compression constants
#define BU_QUALITY_MIN 0
#define BU_QUALITY_MAX 100
#define BU_EFFORT_MIN 0
#define BU_EFFORT_MAX 10
#define BU_EFFORT_SUPER_FAST = 0
#define BU_EFFORT_FAST = 2
#define BU_EFFORT_NORMAL = 5
#define BU_EFFORT_DEFAULT = 2
#define BU_EFFORT_SLOW = 8
#define BU_EFFORT_VERY_SLOW = 10
#define BU_COMP_FLAGS_NONE (0)
#define BU_COMP_FLAGS_USE_OPENCL (1 << 8 )
#define BU_COMP_FLAGS_THREADED (1 << 9 )
#define BU_COMP_FLAGS_DEBUG_OUTPUT (1 << 10)
#define BU_COMP_FLAGS_KTX2_OUTPUT (1 << 11)
#define BU_COMP_FLAGS_KTX2_UASTC_ZSTD (1 << 12)
#define BU_COMP_FLAGS_SRGB (1 << 13)
#define BU_COMP_FLAGS_GEN_MIPS_CLAMP (1 << 14)
#define BU_COMP_FLAGS_GEN_MIPS_WRAP (1 << 15)
#define BU_COMP_FLAGS_Y_FLIP (1 << 16)
#define BU_COMP_FLAGS_PRINT_STATS (1 << 18)
#define BU_COMP_FLAGS_PRINT_STATUS (1 << 19)
#define BU_COMP_FLAGS_DEBUG_IMAGES (1 << 20)
#define BU_COMP_FLAGS_REC2020 (1 << 21)
#define BU_COMP_FLAGS_VALIDATE_OUTPUT (1 << 22)
#define BU_COMP_FLAGS_XUASTC_LDR_FULL_ARITH (0)
#define BU_COMP_FLAGS_XUASTC_LDR_HYBRID (1 << 23)
#define BU_COMP_FLAGS_XUASTC_LDR_FULL_ZSTD (2 << 23)
#define BU_COMP_FLAGS_XUASTC_LDR_SYNTAX_SHIFT (23)
#define BU_COMP_FLAGS_XUASTC_LDR_SYNTAX_MASK (3)
#define BU_COMP_FLAGS_TEXTURE_TYPE_2D (0 << 25)
#define BU_COMP_FLAGS_TEXTURE_TYPE_2D_ARRAY (1 << 25)
#define BU_COMP_FLAGS_TEXTURE_TYPE_CUBEMAP_ARRAY (2 << 25)
#define BU_COMP_FLAGS_TEXTURE_TYPE_VIDEO_FRAMES (3 << 25)
#define BU_COMP_FLAGS_TEXTURE_TYPE_SHIFT (25)
#define BU_COMP_FLAGS_TEXTURE_TYPE_MASK (3)
#define BU_COMP_FLAGS_VERBOSE (BU_COMP_FLAGS_DEBUG_OUTPUT | BU_COMP_FLAGS_PRINT_STATS | BU_COMP_FLAGS_PRINT_STATUS)
// basist::basis_tex_format: the supported .ktx2 (and .basis) file format types
#define BTF_ETC1S 0
#define BTF_UASTC_LDR_4X4 1
#define BTF_UASTC_HDR_4X4 2
#define BTF_ASTC_HDR_6X6 3
#define BTF_UASTC_HDR_6X6 4
#define BTF_XUASTC_LDR_4X4 5
#define BTF_XUASTC_LDR_5X4 6
#define BTF_XUASTC_LDR_5X5 7
#define BTF_XUASTC_LDR_6X5 8
#define BTF_XUASTC_LDR_6X6 9
#define BTF_XUASTC_LDR_8X5 10
#define BTF_XUASTC_LDR_8X6 11
#define BTF_XUASTC_LDR_10X5 12
#define BTF_XUASTC_LDR_10X6 13
#define BTF_XUASTC_LDR_8X8 14
#define BTF_XUASTC_LDR_10X8 15
#define BTF_XUASTC_LDR_10X10 16
#define BTF_XUASTC_LDR_12X10 17
#define BTF_XUASTC_LDR_12X12 18
#define BTF_ASTC_LDR_4X4 19
#define BTF_ASTC_LDR_5X4 20
#define BTF_ASTC_LDR_5X5 21
#define BTF_ASTC_LDR_6X5 22
#define BTF_ASTC_LDR_6X6 23
#define BTF_ASTC_LDR_8X5 24
#define BTF_ASTC_LDR_8X6 25
#define BTF_ASTC_LDR_10X5 26
#define BTF_ASTC_LDR_10X6 27
#define BTF_ASTC_LDR_8X8 28
#define BTF_ASTC_LDR_10X8 29
#define BTF_ASTC_LDR_10X10 30
#define BTF_ASTC_LDR_12X10 31
#define BTF_ASTC_LDR_12X12 32
#define BTF_TOTAL_FORMATS 33
// Transcoding constants
// basist::transcoder_texture_format: the supported transcode GPU texture formats
#define TF_ETC1_RGB 0
#define TF_ETC2_RGBA 1
#define TF_BC1_RGB 2
#define TF_BC3_RGBA 3
#define TF_BC4_R 4
#define TF_BC5_RG 5
#define TF_BC7_RGBA 6
#define TF_PVRTC1_4_RGB 8
#define TF_PVRTC1_4_RGBA 9
#define TF_ASTC_LDR_4X4_RGBA 10
#define TF_ATC_RGB 11
#define TF_ATC_RGBA 12
#define TF_FXT1_RGB 17
#define TF_PVRTC2_4_RGB 18
#define TF_PVRTC2_4_RGBA 19
#define TF_ETC2_EAC_R11 20
#define TF_ETC2_EAC_RG11 21
#define TF_BC6H 22
#define TF_ASTC_HDR_4X4_RGBA 23
#define TF_RGBA32 13
#define TF_RGB565 14
#define TF_BGR565 15
#define TF_RGBA4444 16
#define TF_RGB_HALF 24
#define TF_RGBA_HALF 25
#define TF_RGB_9E5 26
#define TF_ASTC_HDR_6X6_RGBA 27
#define TF_ASTC_LDR_5X4_RGBA 28
#define TF_ASTC_LDR_5X5_RGBA 29
#define TF_ASTC_LDR_6X5_RGBA 30
#define TF_ASTC_LDR_6X6_RGBA 31
#define TF_ASTC_LDR_8X5_RGBA 32
#define TF_ASTC_LDR_8X6_RGBA 33
#define TF_ASTC_LDR_10X5_RGBA 34
#define TF_ASTC_LDR_10X6_RGBA 35
#define TF_ASTC_LDR_8X8_RGBA 36
#define TF_ASTC_LDR_10X8_RGBA 37
#define TF_ASTC_LDR_10X10_RGBA 38
#define TF_ASTC_LDR_12X10_RGBA 39
#define TF_ASTC_LDR_12X12_RGBA 40
#define TF_TOTAL_TEXTURE_FORMATS 41
// basist::basisu_decode_flags: Transcode decode flags (bt_ktx2_transcode_image_level decode_flags parameter, logically OR'd)
#define DECODE_FLAGS_PVRTC_DECODE_TO_NEXT_POW2 2
#define DECODE_FLAGS_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS 4
#define DECODE_FLAGS_BC1_FORBID_THREE_COLOR_BLOCKS 8
#define DECODE_FLAGS_OUTPUT_HAS_ALPHA_INDICES 16
#define DECODE_FLAGS_HIGH_QUALITY 32
#define DECODE_FLAGS_NO_ETC1S_CHROMA_FILTERING 64
#define DECODE_FLAGS_NO_DEBLOCK_FILTERING 128
#define DECODE_FLAGS_STRONGER_DEBLOCK_FILTERING 256
#define DECODE_FLAGS_FORCE_DEBLOCK_FILTERING 512
#define DECODE_FLAGS_XUASTC_LDR_DISABLE_FAST_BC7_TRANSCODING 1024
File diff suppressed because it is too large Load Diff
+216
View File
@@ -0,0 +1,216 @@
// File: basisu_wasm_transcoder_api.h - Transcoding API support for WASM WASI modules and Python native support.
#pragma once
#include "basisu_wasm_api_common.h"
// High-level functions
BU_WASM_EXPORT("bt_get_version")
uint32_t bt_get_version();
BU_WASM_EXPORT("bt_enable_debug_printf")
void bt_enable_debug_printf(uint32_t flag);
BU_WASM_EXPORT("bt_init")
void bt_init();
BU_WASM_EXPORT("bt_alloc")
uint64_t bt_alloc(uint64_t size);
BU_WASM_EXPORT("bt_free")
void bt_free(uint64_t ofs);
// basis_tex_format helpers
BU_WASM_EXPORT("bt_basis_tex_format_is_xuastc_ldr")
wasm_bool_t bt_basis_tex_format_is_xuastc_ldr(uint32_t basis_tex_fmt_u32);
BU_WASM_EXPORT("bt_basis_tex_format_is_astc_ldr")
wasm_bool_t bt_basis_tex_format_is_astc_ldr(uint32_t basis_tex_fmt_u32);
BU_WASM_EXPORT("bt_basis_tex_format_get_block_width")
uint32_t bt_basis_tex_format_get_block_width(uint32_t basis_tex_fmt_u32);
BU_WASM_EXPORT("bt_basis_tex_format_get_block_height")
uint32_t bt_basis_tex_format_get_block_height(uint32_t basis_tex_fmt_u32);
BU_WASM_EXPORT("bt_basis_tex_format_is_hdr")
wasm_bool_t bt_basis_tex_format_is_hdr(uint32_t basis_tex_format_u32);
BU_WASM_EXPORT("bt_basis_tex_format_is_ldr")
wasm_bool_t bt_basis_tex_format_is_ldr(uint32_t basis_tex_format_u32);
// transcoder_texture_format helpers
BU_WASM_EXPORT("bt_basis_get_bytes_per_block_or_pixel")
uint32_t bt_basis_get_bytes_per_block_or_pixel(uint32_t transcoder_texture_format_u32);
BU_WASM_EXPORT("bt_basis_transcoder_format_has_alpha")
wasm_bool_t bt_basis_transcoder_format_has_alpha(uint32_t transcoder_texture_format_u32);
BU_WASM_EXPORT("bt_basis_transcoder_format_is_hdr")
wasm_bool_t bt_basis_transcoder_format_is_hdr(uint32_t transcoder_texture_format_u32);
BU_WASM_EXPORT("bt_basis_transcoder_format_is_ldr")
wasm_bool_t bt_basis_transcoder_format_is_ldr(uint32_t transcoder_texture_format_u32);
BU_WASM_EXPORT("bt_basis_transcoder_texture_format_is_astc")
wasm_bool_t bt_basis_transcoder_texture_format_is_astc(uint32_t transcoder_texture_format_u32);
BU_WASM_EXPORT("bt_basis_transcoder_format_is_uncompressed")
wasm_bool_t bt_basis_transcoder_format_is_uncompressed(uint32_t transcoder_texture_format_u32);
BU_WASM_EXPORT("bt_basis_get_uncompressed_bytes_per_pixel")
uint32_t bt_basis_get_uncompressed_bytes_per_pixel(uint32_t transcoder_texture_format_u32);
BU_WASM_EXPORT("bt_basis_get_block_width")
uint32_t bt_basis_get_block_width(uint32_t transcoder_texture_format_u32);
BU_WASM_EXPORT("bt_basis_get_block_height")
uint32_t bt_basis_get_block_height(uint32_t transcoder_texture_format_u32);
BU_WASM_EXPORT("bt_basis_get_transcoder_texture_format_from_basis_tex_format")
uint32_t bt_basis_get_transcoder_texture_format_from_basis_tex_format(uint32_t basis_tex_format_u32);
BU_WASM_EXPORT("bt_basis_is_format_supported")
wasm_bool_t bt_basis_is_format_supported(uint32_t transcoder_texture_format_u32, uint32_t basis_tex_format_u32);
BU_WASM_EXPORT("bt_basis_compute_transcoded_image_size_in_bytes")
uint32_t bt_basis_compute_transcoded_image_size_in_bytes(uint32_t transcoder_texture_format_u32, uint32_t orig_width, uint32_t orig_height);
// Transcoding
BU_WASM_EXPORT("bt_ktx2_open")
uint64_t bt_ktx2_open(uint64_t data_mem_ofs, uint32_t data_len);
BU_WASM_EXPORT("bt_ktx2_close")
void bt_ktx2_close(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_width")
uint32_t bt_ktx2_get_width(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_height")
uint32_t bt_ktx2_get_height(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_levels")
uint32_t bt_ktx2_get_levels(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_faces")
uint32_t bt_ktx2_get_faces(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_layers")
uint32_t bt_ktx2_get_layers(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_basis_tex_format")
uint32_t bt_ktx2_get_basis_tex_format(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_is_etc1s")
wasm_bool_t bt_ktx2_is_etc1s(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_is_uastc_ldr_4x4")
wasm_bool_t bt_ktx2_is_uastc_ldr_4x4(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_is_hdr")
wasm_bool_t bt_ktx2_is_hdr(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_is_hdr_4x4")
wasm_bool_t bt_ktx2_is_hdr_4x4(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_is_hdr_6x6")
wasm_bool_t bt_ktx2_is_hdr_6x6(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_is_ldr")
wasm_bool_t bt_ktx2_is_ldr(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_is_astc_ldr")
wasm_bool_t bt_ktx2_is_astc_ldr(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_is_xuastc_ldr")
wasm_bool_t bt_ktx2_is_xuastc_ldr(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_block_width")
uint32_t bt_ktx2_get_block_width(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_block_height")
uint32_t bt_ktx2_get_block_height(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_has_alpha")
wasm_bool_t bt_ktx2_has_alpha(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_dfd_color_model")
uint32_t bt_ktx2_get_dfd_color_model(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_dfd_color_primaries")
uint32_t bt_ktx2_get_dfd_color_primaries(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_dfd_transfer_func")
uint32_t bt_ktx2_get_dfd_transfer_func(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_is_srgb")
wasm_bool_t bt_ktx2_is_srgb(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_dfd_flags")
uint32_t bt_ktx2_get_dfd_flags(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_dfd_total_samples")
uint32_t bt_ktx2_get_dfd_total_samples(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_dfd_channel_id0")
uint32_t bt_ktx2_get_dfd_channel_id0(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_dfd_channel_id1")
uint32_t bt_ktx2_get_dfd_channel_id1(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_is_video")
wasm_bool_t bt_ktx2_is_video(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_ldr_hdr_upconversion_nit_multiplier")
float bt_ktx2_get_ldr_hdr_upconversion_nit_multiplier(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_get_level_orig_width")
uint32_t bt_ktx2_get_level_orig_width(uint64_t handle, uint32_t level_index, uint32_t layer_index, uint32_t face_index);
BU_WASM_EXPORT("bt_ktx2_get_level_orig_height")
uint32_t bt_ktx2_get_level_orig_height(uint64_t handle, uint32_t level_index, uint32_t layer_index, uint32_t face_index);
BU_WASM_EXPORT("bt_ktx2_get_level_actual_width")
uint32_t bt_ktx2_get_level_actual_width(uint64_t handle, uint32_t level_index, uint32_t layer_index, uint32_t face_index);
BU_WASM_EXPORT("bt_ktx2_get_level_actual_height")
uint32_t bt_ktx2_get_level_actual_height(uint64_t handle, uint32_t level_index, uint32_t layer_index, uint32_t face_index);
BU_WASM_EXPORT("bt_ktx2_get_level_num_blocks_x")
uint32_t bt_ktx2_get_level_num_blocks_x(uint64_t handle, uint32_t level_index, uint32_t layer_index, uint32_t face_index);
BU_WASM_EXPORT("bt_ktx2_get_level_num_blocks_y")
uint32_t bt_ktx2_get_level_num_blocks_y(uint64_t handle, uint32_t level_index, uint32_t layer_index, uint32_t face_index);
BU_WASM_EXPORT("bt_ktx2_get_level_total_blocks")
uint32_t bt_ktx2_get_level_total_blocks(uint64_t handle, uint32_t level_index, uint32_t layer_index, uint32_t face_index);
BU_WASM_EXPORT("bt_ktx2_get_level_alpha_flag")
wasm_bool_t bt_ktx2_get_level_alpha_flag(uint64_t handle, uint32_t level_index, uint32_t layer_index, uint32_t face_index);
BU_WASM_EXPORT("bt_ktx2_get_level_iframe_flag")
wasm_bool_t bt_ktx2_get_level_iframe_flag(uint64_t handle, uint32_t level_index, uint32_t layer_index, uint32_t face_index);
BU_WASM_EXPORT("bt_ktx2_start_transcoding")
wasm_bool_t bt_ktx2_start_transcoding(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_create_transcode_state")
uint64_t bt_ktx2_create_transcode_state();
BU_WASM_EXPORT("bt_ktx2_destroy_transcode_state")
void bt_ktx2_destroy_transcode_state(uint64_t handle);
BU_WASM_EXPORT("bt_ktx2_transcode_image_level")
wasm_bool_t bt_ktx2_transcode_image_level(
uint64_t ktx2_handle, // handle to KTX2 file, see bt_ktx2_open()
uint32_t level_index, uint32_t layer_index, uint32_t face_index, // KTX2 level/layer/face to transcode
uint64_t output_block_mem_ofs, // allocate using bt_alloc()
uint32_t output_blocks_buf_size_in_blocks_or_pixels,
uint32_t transcoder_texture_format_u32, // target format, TF_ETC1_RGB etc.
uint32_t decode_flags, // DECODE_FLAGS_
uint32_t output_row_pitch_in_blocks_or_pixels, // can be 0
uint32_t output_rows_in_pixels, // can be 0
int channel0, int channel1, // both default to -1
uint64_t state_handle); // thread local state: can be 0, or bt_ktx2_create_transcode_state()
+4 -3
View File
@@ -1,7 +1,7 @@
// Do not include this header directly.
// Control flow functionality in common between all the headers.
//
// Copyright 2020-2021 Binomial LLC
// Copyright 2020-2024 Binomial LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -218,9 +218,10 @@ CPPSPMD_FORCE_INLINE void spmd_kernel::spmd_ifelse(const vbool& cond, const IfBo
exec_mask unexecuted_lanes = cond_exec & m_exec;
m_exec = pre_if_exec;
ifBody();
// 11/22/2025: changed to elseBody() here, simple bug, we use the macro variants of ifelse anyway
elseBody();
// Propagate any lanes that got disabled inside the if body into the exec mask outside the if body, but turn on any lanes that didn't execute inside the if body.
// Propagate any lanes that got disabled inside the else body into the exec mask outside the else body, but turn on any lanes that didn't execute inside the else body.
m_exec = m_exec | unexecuted_lanes;
check_masks();
+105 -105
View File
@@ -1,6 +1,6 @@
// Do not include this header directly.
//
// Copyright 2020-2021 Binomial LLC
// Copyright 2020-2024 Binomial LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,21 +15,21 @@
// limitations under the License.
// The general goal of these vectorized estimated math functions is scalability/performance.
// There are explictly no checks NaN's/Inf's on the input arguments. There are no assertions either.
// These are fast estimate functions - if you need more than that, use stdlib. Please do a proper
// There are explictly no checks NaN's/Inf's on the input arguments. There are no assertions either.
// These are fast estimate functions - if you need more than that, use stdlib. Please do a proper
// engineering analysis before relying on them.
// I have chosen functions written by others, ported them to CppSPMD, then measured their abs/rel errors.
// I compared each to the ones in DirectXMath and stdlib's for accuracy/performance.
CPPSPMD_FORCE_INLINE vfloat fmod_inv(const vfloat& a, const vfloat& b, const vfloat& b_inv)
{
vfloat c = frac(abs(a * b_inv)) * abs(b);
return spmd_ternaryf(a < 0, -c, c);
CPPSPMD_FORCE_INLINE vfloat fmod_inv(const vfloat& a, const vfloat& b, const vfloat& b_inv)
{
vfloat c = frac(abs(a * b_inv)) * abs(b);
return spmd_ternaryf(a < 0, -c, c);
}
CPPSPMD_FORCE_INLINE vfloat fmod_inv_p(const vfloat& a, const vfloat& b, const vfloat& b_inv)
{
return frac(a * b_inv) * b;
CPPSPMD_FORCE_INLINE vfloat fmod_inv_p(const vfloat& a, const vfloat& b, const vfloat& b_inv)
{
return frac(a * b_inv) * b;
}
// Avoids dividing by zero or very small values.
@@ -87,13 +87,13 @@ inline vfloat spmd_kernel::log2_est(vfloat v)
vint greater = ux1_i & 0x00400000; // true if signif > 1.5
SPMD_SIF(greater != 0)
{
// signif >= 1.5 so need to divide by 2. Accomplish this by stuffing exp = 126 which corresponds to an exponent of -1
// signif >= 1.5 so need to divide by 2. Accomplish this by stuffing exp = 126 which corresponds to an exponent of -1
store_all(ux2_i, (ux1_i & 0x007FFFFF) | 0x3f000000);
store_all(ux2_f, cast_vint_to_vfloat(ux2_i));
// 126 instead of 127 compensates for division by 2
store_all(fexp, vfloat(exp - 126));
store_all(fexp, vfloat(exp - 126));
}
SPMD_SELSE(greater != 0)
{
@@ -113,9 +113,9 @@ inline vfloat spmd_kernel::log2_est(vfloat v)
vfloat xm1 = signif;
vfloat xm1sqr = xm1 * xm1;
return fexp + ((a * (xm1sqr * xm1) + b * xm1sqr + c * xm1) / (xm1sqr + d * xm1 + e));
// fma lowers accuracy for SSE4.1 - no idea why (compiler reordering?)
//return fexp + ((vfma(a, (xm1sqr * xm1), vfma(b, xm1sqr, c * xm1))) / (xm1sqr + vfma(d, xm1, e)));
}
@@ -130,15 +130,15 @@ CPPSPMD_FORCE_INLINE void spmd_kernel::reduce_expb(vfloat& arg, vfloat& two_int_
{
// Assume we're using equation (2)
store_all(adjustment, 0);
// integer part of the input argument
vint int_arg = (vint)arg;
// if frac(arg) is in [0.5, 1.0]...
SPMD_SIF((arg - int_arg) > 0.5f)
SPMD_SIF((arg - int_arg) > 0.5f)
{
store(adjustment, 1);
// then change it to [0.0, 0.5]
store(arg, arg - 0.5f);
}
@@ -146,17 +146,17 @@ CPPSPMD_FORCE_INLINE void spmd_kernel::reduce_expb(vfloat& arg, vfloat& two_int_
// arg == just the fractional part
store_all(arg, arg - (vfloat)int_arg);
// Now compute 2** (int) arg.
// Now compute 2** (int) arg.
store_all(int_arg, min(int_arg + 127, 254));
store_all(two_int_a, cast_vint_to_vfloat(VINT_SHIFT_LEFT(int_arg, 23)));
}
/*
clang 9.0.0 for win /fp:precise release
f range : -50.0000000000000000 49.9999940395355225, vals : 16777216
exp2_est():
Total passed near - zero check : 16777216
Total sign diffs : 0
@@ -164,7 +164,7 @@ CPPSPMD_FORCE_INLINE void spmd_kernel::reduce_expb(vfloat& arg, vfloat& two_int_
max rel err: 0.0000015642030031
avg abs err: 10793794.4007573910057545
avg rel err: 0.0000003890893282
XMVectorExp2():
Total passed near-zero check: 16777216
Total sign diffs: 0
@@ -191,11 +191,11 @@ inline vfloat spmd_kernel::exp2_est(vfloat arg)
const vfloat P01 = +0.0576900723731f;
const vfloat Q00 = +20.8189237930062f;
const vfloat Q01 = +1.0f;
const vfloat sqrt2 = 1.4142135623730950488f; // sqrt(2) for scaling
const vfloat sqrt2 = 1.4142135623730950488f; // sqrt(2) for scaling
vfloat result = 0.0f;
// Return 0 if arg is too large.
// Return 0 if arg is too large.
// We're not introducing inf/nan's into calculations, or risk doing so by returning huge default values.
SPMD_IF(abs(arg) > 126.0f)
{
@@ -204,13 +204,13 @@ inline vfloat spmd_kernel::exp2_est(vfloat arg)
SPMD_END_IF
// 2**(int(a))
vfloat two_int_a;
vfloat two_int_a;
// set to 1 by reduce_expb
vint adjustment;
// 0 if arg is +; 1 if negative
vint negative = 0;
vint negative = 0;
// If the input is negative, invert it. At the end we'll take the reciprocal, since n**(-1) = 1/(n**x).
SPMD_SIF(arg < 0.0f)
@@ -232,15 +232,15 @@ inline vfloat spmd_kernel::exp2_est(vfloat arg)
// Q(x**2)
vfloat Q = vfma(Q01, (arg * arg), Q00);
// x*P(x**2)
vfloat x_P = arg * (vfma(P01, arg * arg, P00));
vfloat answer = (Q + x_P) / (Q - x_P);
// Now correct for the scaling factor of 2**(int(a))
store_all(answer, answer * two_int_a);
// If the result had a fractional part > 0.5, correct for that
store_all(answer, spmd_ternaryf(adjustment != 0, answer * sqrt2, answer));
@@ -295,35 +295,35 @@ inline vfloat spmd_kernel::sincos_est_a(vfloat a, bool sin_flag)
store_all(r1_x, sin_flag ? vfms(c1_w, a, c1_x) : c1_w * a);
store_all(r1_y, frac(r1_x));
store_all(r2_x, (vfloat)(r1_y < c1_x));
store_all(r1_y, frac(r1_x));
store_all(r2_y, (vfloat)(r1_y >= c1_y));
store_all(r2_z, (vfloat)(r1_y >= c1_z));
store_all(r2_x, (vfloat)(r1_y < c1_x));
store_all(r2_y, (vfloat)(r1_y >= c1_y));
store_all(r2_z, (vfloat)(r1_y >= c1_z));
store_all(r2_y, vfma(r2_x, c4_z, vfma(r2_y, c4_w, r2_z * c4_z)));
store_all(r0_x, c0_x - r1_y);
store_all(r0_y, c0_y - r1_y);
store_all(r0_z, c0_z - r1_y);
store_all(r0_x, c0_x - r1_y);
store_all(r0_y, c0_y - r1_y);
store_all(r0_z, c0_z - r1_y);
store_all(r0_x, r0_x * r0_x);
store_all(r0_y, r0_y * r0_y);
store_all(r0_z, r0_z * r0_z);
store_all(r1_x, vfma(c2_x, r0_x, c2_z));
store_all(r1_y, vfma(c2_y, r0_y, c2_w));
store_all(r1_z, vfma(c2_x, r0_z, c2_z));
store_all(r1_x, vfma(c2_x, r0_x, c2_z));
store_all(r1_y, vfma(c2_y, r0_y, c2_w));
store_all(r1_z, vfma(c2_x, r0_z, c2_z));
store_all(r1_x, vfma(r1_x, r0_x, c3_x));
store_all(r1_y, vfma(r1_y, r0_y, c3_y));
store_all(r1_z, vfma(r1_z, r0_z, c3_x));
store_all(r1_x, vfma(r1_x, r0_x, c3_z));
store_all(r1_y, vfma(r1_y, r0_y, c3_w));
store_all(r1_z, vfma(r1_z, r0_z, c3_z));
store_all(r1_x, vfma(r1_x, r0_x, c4_x));
store_all(r1_y, vfma(r1_y, r0_y, c4_y));
store_all(r1_z, vfma(r1_z, r0_z, c4_x));
@@ -347,9 +347,9 @@ CPPSPMD_FORCE_INLINE vfloat spmd_kernel::recip_est1(const vfloat& q)
vfloat l = spmd_ternaryf(q >= fMinThresh, q, cast_vint_to_vfloat(vint(mag)));
vint x_l = vint(mag) - cast_vfloat_to_vint(l);
vfloat rcp_l = cast_vint_to_vfloat(x_l);
return rcp_l * vfnma(rcp_l, q, 2.0f);
}
@@ -395,12 +395,12 @@ CPPSPMD_FORCE_INLINE vfloat spmd_kernel::atan2_est(vfloat y, vfloat x)
{
vfloat t1 = abs(y);
vfloat t3 = abs(x);
vfloat t0 = max(t3, t1);
store_all(t1, min(t3, t1));
store_all(t3, t1 / t0);
vfloat t4 = t3 * t3;
store_all(t0, vfma(-0.013480470f, t4, 0.057477314f));
store_all(t0, vfms(t0, t4, 0.121239071f));
@@ -452,7 +452,7 @@ CPPSPMD_FORCE_INLINE vfloat spmd_kernel::atan2_est(vfloat y, vfloat x)
max abs err: 0.8989131818294709
max rel err: 0.0573181403173166
avg rel err: 0.0000030791301203
Originally from:
http://www.ganssle.com/approx.htm
*/
@@ -495,7 +495,7 @@ inline vfloat spmd_kernel::tan_est(vfloat x)
vfloat z = tan82(y);
vfloat r;
vbool octant_one_or_two = (octant == 1) || (octant == 2);
// SPMD optimization - skip costly divide if we can
@@ -503,7 +503,7 @@ inline vfloat spmd_kernel::tan_est(vfloat x)
{
const float fDivThresh = .4371e-7f;
vfloat one_over_z = 1.0f / spmd_ternaryf(abs(z) > fDivThresh, z, spmd_ternaryf(z < 0.0f, -fDivThresh, fDivThresh));
vfloat b = spmd_ternaryf(octant_one_or_two, one_over_z, z);
store_all(r, spmd_ternaryf((octant & 2) != 0, -b, b));
}
@@ -511,7 +511,7 @@ inline vfloat spmd_kernel::tan_est(vfloat x)
{
store_all(r, spmd_ternaryf(octant == 0, z, -z));
}
// Small angle approximation, to decrease the max rel error near Pi.
SPMD_SIF(x >= (1.0f - .0003125f*4.0f))
{
@@ -523,25 +523,25 @@ inline vfloat spmd_kernel::tan_est(vfloat x)
}
inline void spmd_kernel::seed_rand(rand_context& x, vint seed)
{
store(x.a, 0xf1ea5eed);
store(x.b, seed ^ 0xd8487b1f);
store(x.c, seed ^ 0xdbadef9a);
store(x.d, seed);
for (int i = 0; i < 20; ++i)
(void)get_randu(x);
{
store(x.a, 0xf1ea5eed);
store(x.b, seed ^ 0xd8487b1f);
store(x.c, seed ^ 0xdbadef9a);
store(x.d, seed);
for (int i = 0; i < 20; ++i)
(void)get_randu(x);
}
// https://burtleburtle.net/bob/rand/smallprng.html
// Returns 32-bit unsigned random numbers.
inline vint spmd_kernel::get_randu(rand_context& x)
{
vint e = x.a - VINT_ROT(x.b, 27);
store(x.a, x.b ^ VINT_ROT(x.c, 17));
store(x.b, x.c + x.d);
store(x.c, x.d + e);
store(x.d, e + x.a);
return x.d;
{
vint e = x.a - VINT_ROT(x.b, 27);
store(x.a, x.b ^ VINT_ROT(x.c, 17));
store(x.b, x.c + x.d);
store(x.c, x.d + e);
store(x.d, e + x.a);
return x.d;
}
// Returns random numbers between [low, high), or low if low >= high
@@ -552,7 +552,7 @@ inline vint spmd_kernel::get_randi(rand_context& x, vint low, vint high)
vint range = high - low;
vint rnd_range = mulhiu(rnd, range);
return spmd_ternaryi(low < high, low + rnd_range, low);
}
@@ -637,25 +637,25 @@ CPPSPMD_FORCE_INLINE vint spmd_kernel::count_trailing_zeros(vint x)
{
// cast the least significant bit in v to a float
vfloat f = (vfloat)(x & -x);
// extract exponent and adjust
return VUINT_SHIFT_RIGHT(cast_vfloat_to_vint(f), 23) - 0x7F;
}
CPPSPMD_FORCE_INLINE vint spmd_kernel::count_set_bits(vint x)
{
vint v = x - (VUINT_SHIFT_RIGHT(x, 1) & 0x55555555);
vint v1 = (v & 0x33333333) + (VUINT_SHIFT_RIGHT(v, 2) & 0x33333333);
return VUINT_SHIFT_RIGHT(((v1 + VUINT_SHIFT_RIGHT(v1, 4) & 0xF0F0F0F) * 0x1010101), 24);
vint v = x - (VUINT_SHIFT_RIGHT(x, 1) & 0x55555555);
vint v1 = (v & 0x33333333) + (VUINT_SHIFT_RIGHT(v, 2) & 0x33333333);
return VUINT_SHIFT_RIGHT(((v1 + (VUINT_SHIFT_RIGHT(v1, 4) & 0xF0F0F0F)) * 0x1010101), 24);
}
CPPSPMD_FORCE_INLINE vint cmple_epu16(const vint &a, const vint &b)
{
return cmpeq_epi16(subs_epu16(a, b), vint(0));
CPPSPMD_FORCE_INLINE vint cmple_epu16(const vint &a, const vint &b)
{
return cmpeq_epi16(subs_epu16(a, b), vint(0));
}
CPPSPMD_FORCE_INLINE vint cmpge_epu16(const vint &a, const vint &b)
{
CPPSPMD_FORCE_INLINE vint cmpge_epu16(const vint &a, const vint &b)
{
return cmple_epu16(b, a);
}
@@ -679,29 +679,29 @@ CPPSPMD_FORCE_INLINE vint cmple_epi16(const vint &a, const vint &b)
return cmpge_epi16(b, a);
}
void spmd_kernel::print_vint(vint v)
{
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
printf("%i ", extract(v, i));
printf("\n");
void spmd_kernel::print_vint(vint v)
{
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
printf("%i ", extract(v, i));
printf("\n");
}
void spmd_kernel::print_vbool(vbool v)
{
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
printf("%i ", extract(v, i) ? 1 : 0);
printf("\n");
}
void spmd_kernel::print_vint_hex(vint v)
{
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
printf("0x%X ", extract(v, i));
printf("\n");
void spmd_kernel::print_vbool(vbool v)
{
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
printf("%i ", extract(v, i) ? 1 : 0);
printf("\n");
}
void spmd_kernel::print_active_lanes(const char *pPrefix)
{
void spmd_kernel::print_vint_hex(vint v)
{
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
printf("0x%X ", extract(v, i));
printf("\n");
}
void spmd_kernel::print_active_lanes(const char *pPrefix)
{
CPPSPMD_DECL(int, flags[PROGRAM_COUNT]);
memset(flags, 0, sizeof(flags));
storeu_linear(flags, vint(1));
@@ -709,17 +709,17 @@ void spmd_kernel::print_active_lanes(const char *pPrefix)
if (pPrefix)
printf("%s", pPrefix);
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
{
if (flags[i])
printf("%u ", i);
}
printf("\n");
}
void spmd_kernel::print_vfloat(vfloat v)
{
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
printf("%f ", extract(v, i));
printf("\n");
void spmd_kernel::print_vfloat(vfloat v)
{
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
printf("%f ", extract(v, i));
printf("\n");
}
+2 -3
View File
@@ -1,7 +1,7 @@
// Do not include this header directly.
// This header defines shared struct spmd_kernel helpers.
//
// Copyright 2020-2021 Binomial LLC
// Copyright 2020-2024 Binomial LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -54,7 +54,7 @@ CPPSPMD_FORCE_INLINE vfloat atan2_est(vfloat y, vfloat x);
CPPSPMD_FORCE_INLINE vfloat atan_est(vfloat x) { return atan2_est(x, vfloat(1.0f)); }
// Don't call this for angles close to 90/270!
// Don't call this for angles close to 90/270!
inline vfloat tan_est(vfloat x);
// https://burtleburtle.net/bob/rand/smallprng.html
@@ -86,4 +86,3 @@ void print_vbool(vbool v);
void print_vint_hex(vint v);
void print_active_lanes(const char *pPrefix);
void print_vfloat(vfloat v);
+81 -82
View File
@@ -131,8 +131,8 @@ CPPSPMD_DECL(const uint32_t, g_x_128[4]) = { UINT32_MAX, 0, 0, 0 };
CPPSPMD_DECL(const float, g_onef_128[4]) = { 1.0f, 1.0f, 1.0f, 1.0f };
CPPSPMD_DECL(const uint32_t, g_oneu_128[4]) = { 1, 1, 1, 1 };
CPPSPMD_DECL(const uint32_t, g_lane_masks_128[4][4]) =
{
CPPSPMD_DECL(const uint32_t, g_lane_masks_128[4][4]) =
{
{ UINT32_MAX, 0, 0, 0 },
{ 0, UINT32_MAX, 0, 0 },
{ 0, 0, UINT32_MAX, 0 },
@@ -237,7 +237,7 @@ inline __m128i shuffle_epi8(const __m128i& a, const __m128i& b)
// Just emulate _mm_shuffle_epi8. This is very slow, but what else can we do?
CPPSPMD_ALIGN(16) uint8_t av[16];
_mm_store_si128((__m128i*)av, a);
CPPSPMD_ALIGN(16) uint8_t bvi[16];
_mm_store_ps((float*)bvi, _mm_and_ps(_mm_castsi128_ps(b), _mm_castsi128_ps(_mm_set1_epi32(0x0F0F0F0F))));
@@ -247,7 +247,7 @@ inline __m128i shuffle_epi8(const __m128i& a, const __m128i& b)
result[1] = av[bvi[1]];
result[2] = av[bvi[2]];
result[3] = av[bvi[3]];
result[4] = av[bvi[4]];
result[5] = av[bvi[5]];
result[6] = av[bvi[6]];
@@ -266,9 +266,9 @@ inline __m128i shuffle_epi8(const __m128i& a, const __m128i& b)
return _mm_andnot_si128(_mm_cmplt_epi8(b, _mm_setzero_si128()), _mm_load_si128((__m128i*)result));
}
#else
CPPSPMD_FORCE_INLINE __m128i shuffle_epi8(const __m128i& a, const __m128i& b)
{
return _mm_shuffle_epi8(a, b);
CPPSPMD_FORCE_INLINE __m128i shuffle_epi8(const __m128i& a, const __m128i& b)
{
return _mm_shuffle_epi8(a, b);
}
#endif
@@ -387,7 +387,7 @@ struct spmd_kernel
typedef int int_t;
typedef vint vint_t;
typedef lint lint_t;
// Exec mask
struct exec_mask
{
@@ -399,7 +399,7 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE explicit exec_mask(const __m128i& mask) : m_mask(mask) { }
CPPSPMD_FORCE_INLINE void enable_lane(uint32_t lane) { m_mask = _mm_load_si128((const __m128i *)&g_lane_masks_128[lane][0]); }
static CPPSPMD_FORCE_INLINE exec_mask all_on() { return exec_mask{ _mm_load_si128((const __m128i*)g_allones_128) }; }
static CPPSPMD_FORCE_INLINE exec_mask all_off() { return exec_mask{ _mm_setzero_si128() }; }
@@ -422,20 +422,20 @@ struct spmd_kernel
friend CPPSPMD_FORCE_INLINE exec_mask operator^ (const exec_mask& a, const exec_mask& b);
friend CPPSPMD_FORCE_INLINE exec_mask operator& (const exec_mask& a, const exec_mask& b);
friend CPPSPMD_FORCE_INLINE exec_mask operator| (const exec_mask& a, const exec_mask& b);
exec_mask m_exec;
exec_mask m_kernel_exec;
exec_mask m_continue_mask;
#ifdef _DEBUG
bool m_in_loop;
#endif
CPPSPMD_FORCE_INLINE uint32_t get_movemask() const { return m_exec.get_movemask(); }
void init(const exec_mask& kernel_exec);
// Varying bool
struct vbool
{
__m128i m_value;
@@ -448,25 +448,25 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE explicit operator vfloat() const;
CPPSPMD_FORCE_INLINE explicit operator vint() const;
private:
vbool& operator=(const vbool&);
//vbool& operator=(const vbool&);
};
friend vbool operator!(const vbool& v);
CPPSPMD_FORCE_INLINE vbool& store(vbool& dst, const vbool& src)
{
dst.m_value = blendv_mask_epi32(dst.m_value, src.m_value, m_exec.m_mask);
return dst;
}
CPPSPMD_FORCE_INLINE vbool& store_all(vbool& dst, const vbool& src)
{
dst.m_value = src.m_value;
return dst;
}
// Varying float
struct vfloat
{
@@ -481,7 +481,7 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE explicit vfloat(int value) : m_value(_mm_set1_ps((float)value)) { }
private:
vfloat& operator=(const vfloat&);
//vfloat& operator=(const vfloat&);
};
CPPSPMD_FORCE_INLINE vfloat& store(vfloat& dst, const vfloat& src)
@@ -495,7 +495,7 @@ struct spmd_kernel
dst.m_value = blendv_mask_ps(dst.m_value, src.m_value, _mm_castsi128_ps(m_exec.m_mask));
return dst;
}
CPPSPMD_FORCE_INLINE vfloat& store_all(vfloat& dst, const vfloat& src)
{
dst.m_value = src.m_value;
@@ -514,7 +514,7 @@ struct spmd_kernel
float* m_pValue;
private:
float_lref& operator=(const float_lref&);
//float_lref& operator=(const float_lref&);
};
CPPSPMD_FORCE_INLINE const float_lref& store(const float_lref& dst, const vfloat& src)
@@ -536,7 +536,7 @@ struct spmd_kernel
_mm_storeu_ps(dst.m_pValue, blendv_mask_ps(_mm_loadu_ps(dst.m_pValue), src.m_value, _mm_castsi128_ps(m_exec.m_mask)));
return dst;
}
CPPSPMD_FORCE_INLINE const float_lref& store_all(const float_lref& dst, const vfloat& src)
{
_mm_storeu_ps(dst.m_pValue, src.m_value);
@@ -553,15 +553,15 @@ struct spmd_kernel
{
return vfloat{ _mm_and_ps(_mm_loadu_ps(src.m_pValue), _mm_castsi128_ps(m_exec.m_mask)) };
}
// Varying ref to floats
struct float_vref
{
__m128i m_vindex;
float* m_pValue;
private:
float_vref& operator=(const float_vref&);
//float_vref& operator=(const float_vref&);
};
// Varying ref to varying float
@@ -569,9 +569,9 @@ struct spmd_kernel
{
__m128i m_vindex;
vfloat* m_pValue;
private:
vfloat_vref& operator=(const vfloat_vref&);
//vfloat_vref& operator=(const vfloat_vref&);
};
// Varying ref to varying int
@@ -579,14 +579,14 @@ struct spmd_kernel
{
__m128i m_vindex;
vint* m_pValue;
private:
vint_vref& operator=(const vint_vref&);
//vint_vref& operator=(const vint_vref&);
};
CPPSPMD_FORCE_INLINE const float_vref& store(const float_vref& dst, const vfloat& src);
CPPSPMD_FORCE_INLINE const float_vref& store(const float_vref&& dst, const vfloat& src);
CPPSPMD_FORCE_INLINE const float_vref& store_all(const float_vref& dst, const vfloat& src);
CPPSPMD_FORCE_INLINE const float_vref& store_all(const float_vref&& dst, const vfloat& src);
@@ -624,9 +624,9 @@ struct spmd_kernel
int* m_pValue;
private:
int_lref& operator=(const int_lref&);
//int_lref& operator=(const int_lref&);
};
CPPSPMD_FORCE_INLINE const int_lref& store(const int_lref& dst, const vint& src)
{
int mask = _mm_movemask_ps(_mm_castsi128_ps(m_exec.m_mask));
@@ -663,7 +663,7 @@ struct spmd_kernel
int16_t* m_pValue;
private:
int16_lref& operator=(const int16_lref&);
//int16_lref& operator=(const int16_lref&);
};
CPPSPMD_FORCE_INLINE const int16_lref& store(const int16_lref& dst, const vint& src)
@@ -689,7 +689,7 @@ struct spmd_kernel
dst.m_pValue[i] = static_cast<int16_t>(stored[i]);
return dst;
}
CPPSPMD_FORCE_INLINE vint load(const int16_lref& src)
{
CPPSPMD_ALIGN(16) int values[4];
@@ -713,14 +713,14 @@ struct spmd_kernel
return vint{ t };
}
// Linear ref to constant ints
struct cint_lref
{
const int* m_pValue;
private:
cint_lref& operator=(const cint_lref&);
//cint_lref& operator=(const cint_lref&);
};
CPPSPMD_FORCE_INLINE vint load(const cint_lref& src)
@@ -734,7 +734,7 @@ struct spmd_kernel
{
return vint{ _mm_loadu_si128((const __m128i *)src.m_pValue) };
}
// Varying ref to ints
struct int_vref
{
@@ -742,7 +742,7 @@ struct spmd_kernel
int* m_pValue;
private:
int_vref& operator=(const int_vref&);
//int_vref& operator=(const int_vref&);
};
// Varying ref to constant ints
@@ -752,7 +752,7 @@ struct spmd_kernel
const int* m_pValue;
private:
cint_vref& operator=(const cint_vref&);
//cint_vref& operator=(const cint_vref&);
};
// Varying int
@@ -774,7 +774,7 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE explicit vint(const vfloat& other) : m_value(_mm_cvttps_epi32(other.m_value)) { }
CPPSPMD_FORCE_INLINE explicit operator vbool() const
CPPSPMD_FORCE_INLINE explicit operator vbool() const
{
return vbool{ _mm_xor_si128( _mm_load_si128((const __m128i*)g_allones_128), _mm_cmpeq_epi32(m_value, _mm_setzero_si128())) };
}
@@ -810,7 +810,7 @@ struct spmd_kernel
}
private:
vint& operator=(const vint&);
//vint& operator=(const vint&);
};
// Load/store linear int
@@ -837,7 +837,7 @@ struct spmd_kernel
{
_mm_store_si128((__m128i*)pDst, src.m_value);
}
CPPSPMD_FORCE_INLINE vint loadu_linear(const int *pSrc)
{
__m128i v = _mm_loadu_si128((const __m128i*)pSrc);
@@ -882,7 +882,7 @@ struct spmd_kernel
{
_mm_store_ps((float*)pDst, src.m_value);
}
CPPSPMD_FORCE_INLINE vfloat loadu_linear(const float *pSrc)
{
__m128 v = _mm_loadu_ps((const float*)pSrc);
@@ -901,7 +901,7 @@ struct spmd_kernel
{
return vfloat{ _mm_load_ps((float*)pSrc) };
}
CPPSPMD_FORCE_INLINE vint& store(vint& dst, const vint& src)
{
dst.m_value = blendv_mask_epi32(dst.m_value, src.m_value, m_exec.m_mask);
@@ -924,13 +924,13 @@ struct spmd_kernel
}
return dst;
}
CPPSPMD_FORCE_INLINE vint& store_all(vint& dst, const vint& src)
{
dst.m_value = src.m_value;
return dst;
}
CPPSPMD_FORCE_INLINE const int_vref& store_all(const int_vref& dst, const vint& src)
{
CPPSPMD_ALIGN(16) int vindex[4];
@@ -961,7 +961,7 @@ struct spmd_kernel
return vint{ _mm_castps_si128(_mm_and_ps(_mm_castsi128_ps(m_exec.m_mask), _mm_load_ps((const float*)values))) };
}
CPPSPMD_FORCE_INLINE vint load_all(const int_vref& src)
{
CPPSPMD_ALIGN(16) int values[4];
@@ -974,7 +974,7 @@ struct spmd_kernel
return vint{ _mm_castps_si128( _mm_load_ps((const float*)values)) };
}
CPPSPMD_FORCE_INLINE vint load(const cint_vref& src)
{
CPPSPMD_ALIGN(16) int values[4];
@@ -991,7 +991,7 @@ struct spmd_kernel
return vint{ _mm_castps_si128(_mm_and_ps(_mm_castsi128_ps(m_exec.m_mask), _mm_load_ps((const float*)values))) };
}
CPPSPMD_FORCE_INLINE vint load_all(const cint_vref& src)
{
CPPSPMD_ALIGN(16) int values[4];
@@ -1034,7 +1034,7 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE void store_strided(int *pDst, uint32_t stride, const vint &v)
{
int mask = _mm_movemask_ps(_mm_castsi128_ps(m_exec.m_mask));
if (mask & 1) pDst[0] = extract_x(v.m_value);
if (mask & 2) pDst[stride] = extract_y(v.m_value);
if (mask & 4) pDst[stride*2] = extract_z(v.m_value);
@@ -1070,7 +1070,7 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE vint load_strided(const int *pSrc, uint32_t stride)
{
int mask = _mm_movemask_ps(_mm_castsi128_ps(m_exec.m_mask));
#if CPPSPMD_SSE2
CPPSPMD_ALIGN(16) int vals[4] = { 0, 0, 0, 0 };
if (mask & 1) vals[0] = pSrc[0];
@@ -1119,7 +1119,7 @@ struct spmd_kernel
vals[2] = pSrc[stride * 2];
vals[3] = pSrc[stride * 3];
return vint{ _mm_load_si128((__m128i*)vals) };
#else
#else
const float* pSrcF = (const float*)pSrc;
__m128 v = _mm_load_ss(pSrcF);
v = _mm_insert_ps(v, _mm_load_ss(pSrcF + stride), 0x10);
@@ -1151,7 +1151,7 @@ struct spmd_kernel
{
// TODO: There's surely a better way
int mask = _mm_movemask_ps(_mm_castsi128_ps(m_exec.m_mask));
if (mask & 1) ((int *)(&dst.m_pValue[extract_x(dst.m_vindex)]))[0] = extract_x(_mm_castps_si128(src.m_value));
if (mask & 2) ((int *)(&dst.m_pValue[extract_y(dst.m_vindex)]))[1] = extract_y(_mm_castps_si128(src.m_value));
if (mask & 4) ((int *)(&dst.m_pValue[extract_z(dst.m_vindex)]))[2] = extract_z(_mm_castps_si128(src.m_value));
@@ -1179,7 +1179,7 @@ struct spmd_kernel
{
// TODO: There's surely a better way
int mask = _mm_movemask_ps(_mm_castsi128_ps(m_exec.m_mask));
if (mask & 1) ((int *)(&dst.m_pValue[extract_x(dst.m_vindex)]))[0] = extract_x(src.m_value);
if (mask & 2) ((int *)(&dst.m_pValue[extract_y(dst.m_vindex)]))[1] = extract_y(src.m_value);
if (mask & 4) ((int *)(&dst.m_pValue[extract_z(dst.m_vindex)]))[2] = extract_z(src.m_value);
@@ -1206,7 +1206,7 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE vint load_all(const vint_vref& src)
{
// TODO: There's surely a better way
__m128i k;
__m128i k = _mm_setzero_si128();
k = insert_x(k, ((int*)(&src.m_pValue[extract_x(src.m_vindex)]))[0]);
k = insert_y(k, ((int*)(&src.m_pValue[extract_y(src.m_vindex)]))[1]);
@@ -1215,7 +1215,7 @@ struct spmd_kernel
return vint{ k };
}
// Linear integer
struct lint
{
@@ -1235,7 +1235,7 @@ struct spmd_kernel
return vint{ m_value };
}
CPPSPMD_FORCE_INLINE int get_first_value() const
CPPSPMD_FORCE_INLINE int get_first_value() const
{
return _mm_cvtsi128_si32(m_value);
}
@@ -1261,7 +1261,7 @@ struct spmd_kernel
}
private:
lint& operator=(const lint&);
//lint& operator=(const lint&);
};
CPPSPMD_FORCE_INLINE lint& store_all(lint& dst, const lint& src)
@@ -1269,9 +1269,9 @@ struct spmd_kernel
dst.m_value = src.m_value;
return dst;
}
const lint program_index = lint{ _mm_set_epi32( 3, 2, 1, 0 ) };
// SPMD condition helpers
template<typename IfBody>
@@ -1298,7 +1298,7 @@ struct spmd_kernel
template<typename ForeachBody>
CPPSPMD_FORCE_INLINE void spmd_foreach(int begin, int end, const ForeachBody& foreachBody);
#ifdef _DEBUG
CPPSPMD_FORCE_INLINE void check_masks();
#else
@@ -1307,9 +1307,9 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE void spmd_break();
CPPSPMD_FORCE_INLINE void spmd_continue();
CPPSPMD_FORCE_INLINE void spmd_return();
template<typename UnmaskedBody>
CPPSPMD_FORCE_INLINE void spmd_unmasked(const UnmaskedBody& unmaskedBody);
@@ -1321,8 +1321,8 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE void swap(vfloat &a, vfloat &b) { vfloat temp = a; store(a, b); store(b, temp); }
CPPSPMD_FORCE_INLINE void swap(vbool &a, vbool &b) { vbool temp = a; store(a, b); store(b, temp); }
CPPSPMD_FORCE_INLINE float reduce_add(vfloat v)
{
CPPSPMD_FORCE_INLINE float reduce_add(vfloat v)
{
__m128 k3210 = _mm_castsi128_ps(blendv_mask_epi32(_mm_setzero_si128(), _mm_castps_si128(v.m_value), m_exec.m_mask));
__m128 temp = _mm_add_ps(_mm_shuffle_ps(k3210, k3210, _MM_SHUFFLE(0, 1, 2, 3)), k3210);
return _mm_cvtss_f32(_mm_add_ss(_mm_movehl_ps(temp, temp), temp));
@@ -1353,14 +1353,14 @@ using float_vref = spmd_kernel::float_vref;
using vfloat_vref = spmd_kernel::vfloat_vref;
using vint_vref = spmd_kernel::vint_vref;
CPPSPMD_FORCE_INLINE spmd_kernel::vbool::operator vfloat() const
{
return vfloat { _mm_and_ps( _mm_castsi128_ps(m_value), *(const __m128 *)g_onef_128 ) };
CPPSPMD_FORCE_INLINE spmd_kernel::vbool::operator vfloat() const
{
return vfloat { _mm_and_ps( _mm_castsi128_ps(m_value), *(const __m128 *)g_onef_128 ) };
}
// Returns UINT32_MAX's for true, 0 for false. (Should it return 1's?)
CPPSPMD_FORCE_INLINE spmd_kernel::vbool::operator vint() const
{
CPPSPMD_FORCE_INLINE spmd_kernel::vbool::operator vint() const
{
return vint { m_value };
}
@@ -1441,9 +1441,9 @@ CPPSPMD_FORCE_INLINE vfloat round_truncate(const vfloat& a)
{
__m128i abs_a = _mm_and_si128(_mm_castps_si128(a.m_value), _mm_set1_epi32(0x7FFFFFFFU) );
__m128i has_fractional = _mm_cmplt_epi32(abs_a, _mm_castps_si128(_mm_set1_ps(8388608.0f)));
__m128i ai = _mm_cvttps_epi32(a.m_value);
__m128 af = _mm_cvtepi32_ps(ai);
return vfloat{ blendv_mask_ps(a.m_value, af, _mm_castsi128_ps(has_fractional)) };
}
@@ -1466,11 +1466,11 @@ CPPSPMD_FORCE_INLINE vfloat ceil(const vfloat& a)
{
__m128i abs_a = _mm_and_si128(_mm_castps_si128(a.m_value), _mm_set1_epi32(0x7FFFFFFFU));
__m128i has_fractional = _mm_cmplt_epi32(abs_a, _mm_castps_si128(_mm_set1_ps(8388608.0f)));
__m128i ai = _mm_cvtps_epi32(a.m_value);
__m128 af = _mm_cvtepi32_ps(ai);
__m128 changed = _mm_cvtepi32_ps(_mm_castps_si128(_mm_cmplt_ps(af, a.m_value)));
af = _mm_sub_ps(af, changed);
return vfloat{ blendv_mask_ps(a.m_value, af, _mm_castsi128_ps(has_fractional)) };
@@ -1503,12 +1503,12 @@ CPPSPMD_FORCE_INLINE vfloat round_nearest(const vfloat& a)
__m128i sign_a = _mm_and_si128(_mm_castps_si128(a.m_value), _mm_set1_epi32(0x80000000U));
__m128 force_int = _mm_castsi128_ps(_mm_or_si128(no_fract_fp_bits, sign_a));
// Can't use individual _mm_add_ps/_mm_sub_ps - this will be optimized out with /fp:fast by clang and probably other compilers.
//__m128 temp1 = _mm_add_ps(a.m_value, force_int);
//__m128 temp2 = _mm_sub_ps(temp1, force_int);
__m128 temp2 = add_sub(a.m_value, force_int);
__m128i abs_a = _mm_and_si128(_mm_castps_si128(a.m_value), _mm_set1_epi32(0x7FFFFFFFU));
__m128i has_fractional = _mm_cmplt_epi32(abs_a, no_fract_fp_bits);
return vfloat{ blendv_mask_ps(a.m_value, temp2, _mm_castsi128_ps(has_fractional)) };
@@ -1824,7 +1824,7 @@ CPPSPMD_FORCE_INLINE vint vuint_shift_right(const vint& a, const vint& b)
#else
//vint inv_shift = 32 - b;
//vfloat f = cast_vint_to_vfloat(vint(_mm_slli_epi32(inv_shift.m_value, 23)) + cast_vfloat_to_vint(vfloat(1.0f)));
// Take float rep of 1.0f (0x3f800000), subtract (32<<23), subtract (shift<<23), cast to float.
vfloat f = cast_vint_to_vfloat(vint(_mm_sub_epi32(_mm_set1_epi32(0x4f800000), _mm_slli_epi32(b.m_value, 23))));
@@ -1843,7 +1843,7 @@ CPPSPMD_FORCE_INLINE vint vuint_shift_right_not_zero(const vint& a, const vint&
{
//vint inv_shift = 32 - b;
//vfloat f = cast_vint_to_vfloat(vint(_mm_slli_epi32(inv_shift.m_value, 23)) + cast_vfloat_to_vint(vfloat(1.0f)));
// Take float rep of 1.0f (0x3f800000), subtract (32<<23), subtract (shift<<23), cast to float.
vfloat f = cast_vint_to_vfloat(vint(_mm_sub_epi32(_mm_set1_epi32(0x4f800000), _mm_slli_epi32(b.m_value, 23))));
@@ -1887,7 +1887,7 @@ CPPSPMD_FORCE_INLINE vint operator>> (const vint& a, const vint& b)
// Shift left/right by a uniform immediate constant
#define VINT_SHIFT_LEFT(a, b) vint(_mm_slli_epi32( (a).m_value, (b) ) )
#define VINT_SHIFT_RIGHT(a, b) vint( _mm_srai_epi32( (a).m_value, (b) ) )
#define VINT_SHIFT_RIGHT(a, b) vint( _mm_srai_epi32( (a).m_value, (b) ) )
#define VUINT_SHIFT_RIGHT(a, b) vint( _mm_srli_epi32( (a).m_value, (b) ) )
#define VINT_ROT(x, k) (VINT_SHIFT_LEFT((x), (k)) | VUINT_SHIFT_RIGHT((x), 32 - (k)))
@@ -2102,4 +2102,3 @@ CPPSPMD_FORCE_INLINE const float_vref& spmd_kernel::store_all(const float_vref&&
#include "cppspmd_math.h"
} // namespace cppspmd_sse41
+1 -1
View File
@@ -1,7 +1,7 @@
// cppspmd_type_aliases.h
// Do not include this file directly
//
// Copyright 2020-2021 Binomial LLC
// Copyright 2020-2024 Binomial LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
+26 -8
View File
@@ -7,6 +7,7 @@
// Public Domain
//
// License 2:
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -24,6 +25,10 @@
// v2.00, March 20, 2020: Fuzzed with zzuf and afl. Fixed several issues, converted most assert()'s to run-time checks. Added chroma upsampling. Removed freq. domain upsampling. gcc/clang warnings.
//
#if defined(__wasi__)
#pragma message("__wasi__ defined in jpgd.cpp: note if a decode error occurs, the app will exit because wasi doesn't support longjmp yet.")
#endif
#include "jpgd.h"
#include <string.h>
#include <algorithm>
@@ -608,7 +613,14 @@ namespace jpgd {
{
m_error_code = status;
free_all_blocks();
#ifdef __wasi__
// HACK HACK for wasi's lack of longjmp support
fprintf(stderr, "jpeg_decoder::stop_decoding: JPEG decode failed with status: %i\n", (int)status);
exit(EXIT_FAILURE);
#else
longjmp(m_jmp_state, status);
#endif
}
void* jpeg_decoder::alloc(size_t nSize, bool zero)
@@ -947,15 +959,15 @@ namespace jpgd {
// Finds the next marker.
int jpeg_decoder::next_marker()
{
uint c, bytes;
uint c;// , bytes;
bytes = 0;
//bytes = 0;
do
{
do
{
bytes++;
//bytes++;
c = get_bits(8);
} while (c != 0xFF);
@@ -1303,7 +1315,7 @@ namespace jpgd {
int i;
jpgd_block_t* p;
jpgd_quant_t* q;
int mcu_row, mcu_block, row_block = 0;
int mcu_row, mcu_block;// , row_block = 0;
int component_num, component_id;
int block_x_mcu[JPGD_MAX_COMPONENTS];
@@ -1338,7 +1350,7 @@ namespace jpgd {
if (p[g_ZAG[i]])
p[g_ZAG[i]] = static_cast<jpgd_block_t>(p[g_ZAG[i]] * q[i]);
row_block++;
//row_block++;
if (m_comps_in_scan == 1)
block_x_mcu[component_id]++;
@@ -1425,7 +1437,7 @@ namespace jpgd {
// Decodes and dequantizes the next row of coefficients.
void jpeg_decoder::decode_next_row()
{
int row_block = 0;
//int row_block = 0;
for (int mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++)
{
@@ -1528,7 +1540,7 @@ namespace jpgd {
m_mcu_block_max_zag[mcu_block] = k;
row_block++;
//row_block++;
}
transform_mcu(mcu_row);
@@ -2071,8 +2083,10 @@ namespace jpgd {
int jpeg_decoder::decode_next_mcu_row()
{
#ifndef __wasi__
if (setjmp(m_jmp_state))
return JPGD_FAILED;
#endif
const bool chroma_y_filtering = (m_flags & cFlagLinearChromaFiltering) && ((m_scan_type == JPGD_YH2V2) || (m_scan_type == JPGD_YH1V2)) && (m_image_x_size >= 2) && (m_image_y_size >= 2);
if (chroma_y_filtering)
@@ -2987,8 +3001,10 @@ namespace jpgd {
jpeg_decoder::jpeg_decoder(jpeg_decoder_stream* pStream, uint32_t flags)
{
#ifndef __wasi__
if (setjmp(m_jmp_state))
return;
#endif
decode_init(pStream, flags);
}
@@ -3000,8 +3016,10 @@ namespace jpgd {
if (m_error_code)
return JPGD_FAILED;
#ifndef __wasi__
if (setjmp(m_jmp_state))
return JPGD_FAILED;
#endif
decode_start();
@@ -3146,7 +3164,7 @@ namespace jpgd {
for (int y = 0; y < image_height; y++)
{
const uint8* pScan_line;
const uint8* pScan_line = nullptr;
uint scan_line_len;
if (decoder.decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS)
{
+6 -1
View File
@@ -1,11 +1,13 @@
// jpgd.h - C++ class for JPEG decompression.
// Public domain, Rich Geldreich <richgel99@gmail.com>
// Dual licensed: Public domain, Rich Geldreich <richgel99@gmail.com>, or Apache 2.0 (see jpgd.cpp)
#ifndef JPEG_DECODER_H
#define JPEG_DECODER_H
#include <stdlib.h>
#include <stdio.h>
#ifndef __wasi__
#include <setjmp.h>
#endif
#include <assert.h>
#include <stdint.h>
@@ -191,7 +193,10 @@ namespace jpgd
char m_data[1];
};
// TODO: we can get rid of longjmp entirely
#ifndef __wasi__
jmp_buf m_jmp_state;
#endif
uint32_t m_flags;
mem_block* m_pMem_blocks;
int m_image_x_size;
+87 -85
View File
@@ -1,12 +1,12 @@
// pngreader.cpp - Public Domain - see unlicense at bottom of file.
//
// Notes:
// This is ancient code from ~1995 ported to C++. It was originally written for a
// DOS app with very limited memory. It's not as fast as it should be, but it works.
// The low-level PNG reader class was written assuming the PNG file could not fit
// Notes:
// This is ancient code from ~1995 ported to C++. It was originally written for a
// DOS app with very limited memory. It's not as fast as it should be, but it works.
// The low-level PNG reader class was written assuming the PNG file could not fit
// entirely into memory, which dictated how it was written/structured.
// It has been modified to use either zlib or miniz.
// It supports all PNG color types/bit depths/interlacing, however 16-bit/component
// It supports all PNG color types/bit depths/interlacing, however 16-bit/component
// images are converted to 8-bit.
// TRNS chunks are converted to alpha as needed.
// GAMA chunk is read, but not applied.
@@ -109,20 +109,20 @@ class png_memory_file : public png_file
public:
std::vector<uint8_t> m_buf;
uint64_t m_ofs;
png_memory_file() :
png_memory_file() :
png_file(),
m_ofs(0)
{
{
}
virtual ~png_memory_file()
{
{
}
std::vector<uint8_t>& get_buf() { return m_buf; }
const std::vector<uint8_t>& get_buf() const { return m_buf; }
void init()
{
m_ofs = 0;
@@ -163,7 +163,7 @@ public:
{
if ((sizeof(size_t) == sizeof(uint32_t)) && (new_size > 0x7FFFFFFFUL))
return 0;
m_buf.resize(new_size);
m_buf.resize((size_t)new_size);
}
memcpy(&m_buf[(size_t)m_ofs], pBuf, len);
@@ -178,11 +178,11 @@ public:
return 0;
uint64_t max_bytes = minimum<uint64_t>(len, m_buf.size() - m_ofs);
memcpy(pBuf, &m_buf[(size_t)m_ofs], max_bytes);
memcpy(pBuf, &m_buf[(size_t)m_ofs], (size_t)max_bytes);
m_ofs += max_bytes;
return max_bytes;
return (size_t)max_bytes;
}
};
@@ -249,11 +249,11 @@ public:
return 0;
uint64_t max_bytes = minimum<uint64_t>(len, m_buf_size - m_ofs);
memcpy(pBuf, &m_pBuf[(size_t)m_ofs], max_bytes);
memcpy(pBuf, &m_pBuf[(size_t)m_ofs], (size_t)max_bytes);
m_ofs += max_bytes;
return max_bytes;
return (size_t)max_bytes;
}
};
@@ -269,8 +269,8 @@ class png_cfile : public png_file
{
public:
FILE* m_pFile;
png_cfile() :
png_cfile() :
png_file(),
m_pFile(nullptr)
{
@@ -284,9 +284,9 @@ public:
bool init(const char *pFilename, const char *pMode)
{
close();
m_pFile = nullptr;
#ifdef _MSC_VER
fopen_s(&m_pFile, pFilename, pMode);
#else
@@ -333,17 +333,17 @@ public:
int64_t cur_ofs = ftell64(m_pFile);
if (cur_ofs < 0)
return 0;
if (fseek64(m_pFile, 0, SEEK_END) != 0)
return 0;
const int64_t cur_size = ftell64(m_pFile);
if (cur_size < 0)
return 0;
if (fseek64(m_pFile, cur_ofs, SEEK_SET) != 0)
return 0;
return cur_size;
}
@@ -379,13 +379,13 @@ public:
png_decoder();
~png_decoder();
// Scans the PNG file, but doesn't decode the IDAT data.
// Scans the PNG file, but doesn't decode the IDAT data.
// Returns 0 on success, or an error code.
// If the returned status is non-zero, or m_img_supported_flag==FALSE the image either the image is corrupted/not PNG or is unsupported in some way.
int png_scan(png_file *pFile);
// Decodes a single scanline of PNG image data.
// Returns a pointer to the scanline's pixel data and its size in bytes.
// Returns a pointer to the scanline's pixel data and its size in bytes.
// This data is only minimally processed from the internal PNG pixel data.
// The caller must use the ihdr, trns_flag and values, and the palette to actually decode the pixel data.
//
@@ -397,21 +397,21 @@ public:
//
// Returns 0 on success, a non-zero error code, or PNG_ALLDONE.
int png_decode(void** ppImg_ptr, uint32_t* pImg_len);
// Starts decoding. Returns 0 on success, otherwise an error code.
int png_decode_start();
// Deinitializes the decoder, freeing all allocations.
void png_decode_end();
png_file* m_pFile;
// Image's 24bpp palette - 3 bytes per entry
uint8_t m_plte_flag;
uint8_t m_img_pal[768];
int m_img_supported_flag;
ihdr_struct m_ihdr;
uint8_t m_chunk_flag;
@@ -442,7 +442,7 @@ public:
uint8_t m_gama_flag;
uint32_t m_gama_value;
uint8_t m_trns_flag;
uint32_t m_trns_value[256];
@@ -455,7 +455,7 @@ public:
uint32_t m_inflate_dst_buf_ofs;
int m_inflate_eof_flag;
uint8_t m_gamma_table[256];
int m_pass_x_size;
@@ -467,16 +467,16 @@ public:
int m_adam7_pass_size_y[7];
std::vector<uint8_t> m_adam7_image_buf;
int m_adam7_decoded_flag;
bool m_scanned_flag;
int m_terminate_status;
#define TEMP_BUF_SIZE (384)
uint8_t m_temp_buf[TEMP_BUF_SIZE * 4];
void clear();
void uninitialize();
int terminate(int status);
@@ -516,7 +516,7 @@ public:
void png_decoder::uninitialize()
{
m_pFile = nullptr;
for (int i = 0; i < PNG_MAX_ALLOC_BLOCKS; i++)
{
free(m_pMalloc_blocks[i]);
@@ -600,7 +600,7 @@ int png_decoder::fetch_next_chunk_data(uint8_t* buf, int bytes)
int status = block_read(buf, bytes);
if (status != 0)
return status;
#if PVPNG_IDAT_CRC_CHECKING
bool check_crc32 = true;
#else
@@ -609,7 +609,7 @@ int png_decoder::fetch_next_chunk_data(uint8_t* buf, int bytes)
#endif
if (check_crc32)
m_chunk_crc32 = buminiz::mz_crc32(m_chunk_crc32, buf, bytes);
m_chunk_crc32 = static_cast<uint32_t>(buminiz::mz_crc32(m_chunk_crc32, buf, bytes));
if ((m_chunk_left -= bytes) == 0)
{
@@ -680,7 +680,7 @@ int png_decoder::fetch_next_chunk_init()
if (status != 0)
return status;
}
int64_t n = block_read_dword();
if (n < 0)
return (int)n;
@@ -776,7 +776,7 @@ static void PixelDePack2(void* src, void* dst, int numbytes)
while (numbytes)
{
uint8_t v = *src8++;
for (uint32_t i = 0; i < 8; i++)
dst8[7 - i] = (v >> i) & 1;
@@ -933,7 +933,7 @@ static int unpack_true_8(uint8_t* src, uint8_t* dst, int pixels, png_decoder* pw
uint8_t r = src[i * 3 + 0];
uint8_t g = src[i * 3 + 1];
uint8_t b = src[i * 3 + 2];
dst[i * 4 + 0] = r;
dst[i * 4 + 1] = g;
dst[i * 4 + 2] = b;
@@ -982,7 +982,7 @@ static int unpack_true_16(uint8_t* src, uint8_t* dst, int pixels, png_decoder* p
dst[1] = src[2];
dst[2] = src[4];
dst[3] = 255;
dst += 4;
src += 6;
}
@@ -1180,15 +1180,15 @@ int png_decoder::decompress_line(uint32_t* bytes_decoded)
m_inflator.next_in = inflate_src_buf + m_inflate_src_buf_ofs;
m_inflator.avail_in = src_bytes_left;
m_inflator.next_out = m_pCur_line_buf + m_inflate_dst_buf_ofs;
m_inflator.avail_out = dst_bytes_left;
status = buminiz::mz_inflate2(&m_inflator, buminiz::MZ_NO_FLUSH, PVPNG_ADLER32_CHECKING);
const uint32_t src_bytes_consumed = src_bytes_left - m_inflator.avail_in;
const uint32_t dst_bytes_written = dst_bytes_left - m_inflator.avail_out;
m_inflate_src_buf_ofs += src_bytes_consumed;
m_inflate_dst_buf_ofs += dst_bytes_written;
@@ -1255,10 +1255,10 @@ int png_decoder::png_decode(void** ppImg_ptr, uint32_t* pImg_len)
{
if (m_pass_y_left == 0)
return PNG_ALLDONE;
*ppImg_ptr = &m_adam7_image_buf[(m_ihdr.m_height - m_pass_y_left) * m_dst_bytes_per_line];
*pImg_len = m_dst_bytes_per_line;
m_pass_y_left--;
return 0;
@@ -1282,7 +1282,7 @@ int png_decoder::png_decode(void** ppImg_ptr, uint32_t* pImg_len)
status = find_iend_chunk();
if (status < 0)
return status;
return PNG_ALLDONE;
}
@@ -1398,7 +1398,7 @@ int png_decoder::png_decode(void** ppImg_ptr, uint32_t* pImg_len)
if ((*m_pProcess_func)(m_pCur_line_buf + 1, m_pPro_line_buf, m_pass_x_size, this))
decoded_line = m_pPro_line_buf;
}
if (m_ihdr.m_ilace_type == 0)
{
*ppImg_ptr = decoded_line;
@@ -1499,17 +1499,17 @@ void png_decoder::png_decode_end()
int png_decoder::png_decode_start()
{
int status;
if (m_img_supported_flag != TRUE)
return terminate(m_img_supported_flag);
switch (m_ihdr.m_color_type)
{
case PNG_COLOR_TYPE_GREYSCALE:
{
if (m_ihdr.m_bit_depth == 16)
{
// This is a special case. We can't pass back 8-bit samples and let the caller decide on transparency because the PNG is 16-bits.
// This is a special case. We can't pass back 8-bit samples and let the caller decide on transparency because the PNG is 16-bits.
// So we expand to 8-bit Gray-Alpha and handle transparency during decoding.
// We don't do this with all grayscale cases because that would require more code to deal with 1/2/4bpp expansion.
m_dec_bytes_per_pixel = (m_ihdr.m_bit_depth + 7) / 8;
@@ -1534,7 +1534,7 @@ int png_decoder::png_decode_start()
m_pProcess_func = unpack_grey_2;
else if (m_ihdr.m_bit_depth == 4)
m_pProcess_func = unpack_grey_4;
else
else
m_pProcess_func = unpack_grey_8;
}
@@ -1626,8 +1626,8 @@ int png_decoder::png_decode_start()
if (m_ihdr.m_ilace_type == 1)
{
int i;
uint32_t total_lines, lines_processed;
//int i;
//uint32_t total_lines, lines_processed;
m_adam7_pass_size_x[0] = adam7_pass_size(m_ihdr.m_width, 0, 8);
m_adam7_pass_size_x[1] = adam7_pass_size(m_ihdr.m_width, 4, 8);
@@ -1644,17 +1644,19 @@ int png_decoder::png_decode_start()
m_adam7_pass_size_y[4] = adam7_pass_size(m_ihdr.m_height, 2, 4);
m_adam7_pass_size_y[5] = adam7_pass_size(m_ihdr.m_height, 0, 2);
m_adam7_pass_size_y[6] = adam7_pass_size(m_ihdr.m_height, 1, 2);
m_adam7_image_buf.resize(m_dst_bytes_per_line * m_ihdr.m_height);
m_adam7_pass_num = -1;
m_pass_y_left = 0;
#if 0
total_lines = lines_processed = 0;
for (i = 0; i < 7; i++)
total_lines += m_adam7_pass_size_y[i];
#endif
for (; ; )
{
@@ -1675,7 +1677,7 @@ int png_decoder::png_decode_start()
}
}
lines_processed++;
//lines_processed++;
}
m_adam7_decoded_flag = TRUE;
@@ -1686,7 +1688,7 @@ int png_decoder::png_decode_start()
m_pass_x_size = m_ihdr.m_width;
m_pass_y_left = m_ihdr.m_height;
}
return 0;
}
@@ -1779,13 +1781,13 @@ int png_decoder::read_ihdr_chunk()
if ((m_ihdr.m_height == 0) || (m_ihdr.m_height > MAX_SUPPORTED_RES))
return terminate(PNG_BAD_HEIGHT);
int v = fetch_next_chunk_byte();
if (v < 0)
int v = fetch_next_chunk_byte();
if (v < 0)
return v;
m_ihdr.m_bit_depth = (uint8_t)v;
v = fetch_next_chunk_byte();
if (v < 0)
v = fetch_next_chunk_byte();
if (v < 0)
return v;
m_ihdr.m_color_type = (uint8_t)v;
@@ -1812,7 +1814,7 @@ int png_decoder::read_ihdr_chunk()
if (m_ihdr.m_ilace_type > 1)
m_img_supported_flag = PNG_UNS_ILACE;
switch (m_ihdr.m_color_type)
{
case PNG_COLOR_TYPE_GREYSCALE:
@@ -1903,7 +1905,7 @@ int png_decoder::read_bkgd_chunk()
if (v < 0)
return v;
m_bkgd_value[1] = v;
v = fetch_next_chunk_word();
if (v < 0)
return v;
@@ -1922,7 +1924,7 @@ int png_decoder::read_gama_chunk()
return (int)v;
m_gama_value = (uint32_t)v;
return 0;
}
@@ -1962,12 +1964,12 @@ int png_decoder::read_trns_chunk()
if (v < 0)
return v;
m_trns_value[0] = v;
v = fetch_next_chunk_word();
if (v < 0)
return v;
m_trns_value[1] = v;
v = fetch_next_chunk_word();
if (v < 0)
return v;
@@ -2013,7 +2015,7 @@ int png_decoder::read_plte_chunk()
if (v < 0)
return v;
*p++ = (uint8_t)v;
v = fetch_next_chunk_byte();
if (v < 0)
return v;
@@ -2024,7 +2026,7 @@ int png_decoder::read_plte_chunk()
return v;
*p++ = (uint8_t)v;
}
return 0;
}
@@ -2142,7 +2144,7 @@ void png_decoder::clear()
m_inflate_dst_buf_ofs = 0;
m_inflate_eof_flag = FALSE;
clear_obj(m_trns_value);
m_pass_x_size = 0;
@@ -2152,18 +2154,18 @@ void png_decoder::clear()
m_adam7_pass_y = 0;
clear_obj(m_adam7_pass_size_x);
clear_obj(m_adam7_pass_size_y);
m_adam7_decoded_flag = FALSE;
m_scanned_flag = false;
m_terminate_status = 0;
}
int png_decoder::png_scan(png_file *pFile)
{
m_pFile = pFile;
m_img_supported_flag = TRUE;
m_terminate_status = 0;
@@ -2174,11 +2176,11 @@ int png_decoder::png_scan(png_file *pFile)
res = read_ihdr_chunk();
if (res != 0)
return res;
res = find_idat_chunk();
if (res != 0)
return res;
if (m_gama_flag)
calc_gamma_table();
@@ -2198,14 +2200,14 @@ int png_decoder::png_scan(png_file *pFile)
}
static inline uint8_t get_709_luma(uint32_t r, uint32_t g, uint32_t b)
{
{
return (uint8_t)((13938U * r + 46869U * g + 4729U * b + 32768U) >> 16U);
}
bool get_png_info(const void* pImage_buf, size_t buf_size, png_info &info)
{
memset(&info, 0, sizeof(info));
if ((!pImage_buf) || (buf_size < MIN_PNG_SIZE))
return false;
@@ -2254,7 +2256,7 @@ void* load_png(const void* pImage_buf, size_t buf_size, uint32_t desired_chans,
width = 0;
height = 0;
num_chans = 0;
if ((!pImage_buf) || (buf_size < MIN_PNG_SIZE))
{
assert(0);
@@ -2271,7 +2273,7 @@ void* load_png(const void* pImage_buf, size_t buf_size, uint32_t desired_chans,
mf.init(pImage_buf, buf_size);
png_decoder dec;
int status = dec.png_scan(&mf);
if ((status != 0) || (dec.m_img_supported_flag != TRUE))
return nullptr;
@@ -2318,11 +2320,11 @@ void* load_png(const void* pImage_buf, size_t buf_size, uint32_t desired_chans,
uint64_t total_size = (uint64_t)pitch * height;
if (total_size > 0x7FFFFFFFULL)
return nullptr;
uint8_t* pBuf = (uint8_t*)malloc((size_t)total_size);
if (!pBuf)
return nullptr;
if (dec.png_decode_start() != 0)
{
free(pBuf);
+1 -1
View File
@@ -19,7 +19,7 @@ namespace pv_png
{
uint32_t m_width;
uint32_t m_height;
uint32_t m_num_chans; // The number of channels, factoring in transparency. Ranges from [1-4].
uint32_t m_bit_depth; // PNG ihdr bit depth: 1, 2, 4, 8 or 16
+14 -7
View File
@@ -7,22 +7,29 @@ set(TRANSCODER_SRC
)
set(ENCODER_SRC
../encoder/3rdparty/android_astc_decomp.cpp
../encoder/3rdparty/tinyexr.cpp
../encoder/basisu_astc_hdr_6x6_enc.cpp
../encoder/basisu_astc_hdr_common.cpp
../encoder/basisu_astc_ldr_common.cpp
../encoder/basisu_astc_ldr_encode.cpp
../encoder/basisu_backend.cpp
../encoder/basisu_basis_file.cpp
../encoder/basisu_bc7enc.cpp
../encoder/basisu_comp.cpp
../encoder/basisu_enc.cpp
../encoder/basisu_etc.cpp
../encoder/basisu_frontend.cpp
../encoder/basisu_gpu_texture.cpp
../encoder/basisu_pvrtc1_4.cpp
../encoder/basisu_resampler.cpp
../encoder/basisu_resample_filters.cpp
../encoder/basisu_ssim.cpp
../encoder/basisu_uastc_enc.cpp
../encoder/basisu_bc7enc.cpp
../encoder/jpgd.cpp
../encoder/basisu_kernels_sse.cpp
../encoder/basisu_opencl.cpp
../encoder/basisu_pvrtc1_4.cpp
../encoder/basisu_resample_filters.cpp
../encoder/basisu_resampler.cpp
../encoder/basisu_ssim.cpp
../encoder/basisu_uastc_enc.cpp
../encoder/basisu_uastc_hdr_4x4_enc.cpp
../encoder/jpgd.cpp
../encoder/pvpngreader.cpp
)
+448 -105
View File
@@ -1,5 +1,5 @@
// basisu.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,39 +15,16 @@
// limitations under the License.
#pragma once
#ifndef BASISD_SUPPORT_XUASTC
#define BASISD_SUPPORT_XUASTC 1
#endif
#ifdef _MSC_VER
#pragma warning (disable : 4201)
#pragma warning (disable : 4127) // warning C4127: conditional expression is constant
#pragma warning (disable : 4530) // C++ exception handler used, but unwind semantics are not enabled.
// Slamming this off always for v1.16 because we've gotten rid of most std containers.
#ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
#define BASISU_NO_ITERATOR_DEBUG_LEVEL (1)
#endif
#ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
//#define _HAS_ITERATOR_DEBUGGING 0
#if defined(_DEBUG) || defined(DEBUG)
// This is madness, but we need to disable iterator debugging in debug builds or the encoder is unsable because MSVC's iterator debugging implementation is totally broken.
#ifndef _ITERATOR_DEBUG_LEVEL
#define _ITERATOR_DEBUG_LEVEL 1
#endif
#ifndef _SECURE_SCL
#define _SECURE_SCL 1
#endif
#else // defined(_DEBUG) || defined(DEBUG)
#ifndef _SECURE_SCL
#define _SECURE_SCL 0
#endif
#ifndef _ITERATOR_DEBUG_LEVEL
#define _ITERATOR_DEBUG_LEVEL 0
#endif
#endif // defined(_DEBUG) || defined(DEBUG)
#endif // BASISU_NO_ITERATOR_DEBUG_LEVEL
#endif // _MSC_VER
#include <stdlib.h>
@@ -66,9 +43,12 @@
#include <type_traits>
#include <assert.h>
#include <random>
#include <inttypes.h>
#include <cfloat>
#include "basisu_containers.h"
// We never use min/max macros, slam them to off.
#ifdef max
#undef max
#endif
@@ -83,6 +63,7 @@
// Set to one to enable debug printf()'s when any errors occur, for development/debugging. Especially useful for WebGL development.
#ifndef BASISU_FORCE_DEVEL_MESSAGES
// Do not check in as 1!
#define BASISU_FORCE_DEVEL_MESSAGES 0
#endif
@@ -114,26 +95,44 @@ namespace basisu
typedef basisu::vector<int16_t> int16_vec;
typedef basisu::vector<uint16_t> uint16_vec;
typedef basisu::vector<uint32_t> uint_vec;
typedef basisu::vector<size_t> size_t_vec;
typedef basisu::vector<uint64_t> uint64_vec;
typedef basisu::vector<int> int_vec;
typedef basisu::vector<bool> bool_vec;
typedef basisu::vector<float> float_vec;
typedef basisu::vector<double> double_vec;
void enable_debug_printf(bool enabled);
void debug_printf(const char *pFmt, ...);
void debug_puts(const char* p);
template <typename T> inline void clear_obj(T& obj) { memset(&obj, 0, sizeof(obj)); }
template <typename... Args>
inline void fmt_debug_printf(const char* pFmt, Args&&... args)
{
std::string res;
if (!fmt_variants(res, pFmt, fmt_variant_vec{ fmt_variant(std::forward<Args>(args))... }))
return;
debug_puts(res.c_str());
}
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
template <typename T> inline void clear_obj(T& obj) { memset((void *)&obj, 0, sizeof(obj)); }
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
constexpr double cPiD = 3.14159265358979323846264338327950288;
constexpr float REALLY_SMALL_FLOAT_VAL = .000000125f;
constexpr float SMALL_FLOAT_VAL = .0000125f;
constexpr float BIG_FLOAT_VAL = 1e+30f;
template <typename T0, typename T1> inline T0 lerp(T0 a, T0 b, T1 c) { return a + (b - a) * c; }
template <typename S> inline S maximum(S a, S b) { return (a > b) ? a : b; }
template <typename S> inline S maximum(S a, S b, S c) { return maximum(maximum(a, b), c); }
template <typename S> inline S maximum(S a, S b, S c, S d) { return maximum(maximum(maximum(a, b), c), d); }
template <typename S> inline S minimum(S a, S b) { return (a < b) ? a : b; }
template <typename S> inline S minimum(S a, S b, S c) { return minimum(minimum(a, b), c); }
template <typename S> inline S minimum(S a, S b, S c, S d) { return minimum(minimum(minimum(a, b), c), d); }
inline float clampf(float value, float low, float high) { if (value < low) value = low; else if (value > high) value = high; return value; }
inline float saturate(float value) { return clampf(value, 0, 1.0f); }
inline uint8_t minimumub(uint8_t a, uint8_t b) { return (a < b) ? a : b; }
@@ -146,10 +145,66 @@ namespace basisu
inline float maximumf(float a, float b) { return (a > b) ? a : b; }
inline int squarei(int i) { return i * i; }
inline float squaref(float i) { return i * i; }
inline double squared(double i) { return i * i; }
template<typename T> inline T square(T a) { return a * a; }
template<typename T> inline T sign(T a) { return (a < 0) ? (T)-1 : ((a == 0) ? (T)0 : (T)1); }
template <typename S> inline S clamp(S value, S low, S high) { return (value < low) ? low : ((value > high) ? high : value); }
inline int imod(int i, int d)
{
assert(i != INT_MIN);
if (i >= 0)
return i % d;
int r = (-i) % d;
return (r == 0) ? 0 : d - r;
}
inline uint8_t safe_cast_uint8(uint32_t x)
{
assert(x <= UINT8_MAX);
return (uint8_t)x;
}
inline int8_t safe_cast_int8(int32_t x)
{
assert((x >= INT8_MIN) && (x <= INT8_MAX));
return (int8_t)x;
}
inline uint16_t safe_cast_uint16(uint32_t x)
{
assert(x <= UINT16_MAX);
return (uint16_t)x;
}
inline int16_t safe_cast_int16(int32_t x)
{
assert((x >= INT16_MIN) && (x <= INT16_MAX));
return (int16_t)x;
}
inline bool equal_tol(float a, float b, float t) { return fabsf(a - b) <= ((maximum(fabsf(a), fabsf(b)) + 1.0f) * t); }
inline bool equal_tol(double a, double b, double t) { return fabs(a - b) <= ((maximum(fabs(a), fabs(b)) + 1.0f) * t); }
template <class T>
inline T prev_wrap(T i, T n)
{
T temp = i - 1;
if (temp < 0)
temp = n - 1;
return temp;
}
template <class T>
inline T next_wrap(T i, T n)
{
T temp = i + 1;
if (temp >= n)
temp = 0;
return temp;
}
inline uint32_t iabs(int32_t i) { return (i < 0) ? static_cast<uint32_t>(-i) : static_cast<uint32_t>(i); }
inline uint64_t iabs64(int64_t i) { return (i < 0) ? static_cast<uint64_t>(-i) : static_cast<uint64_t>(i); }
@@ -159,13 +214,56 @@ namespace basisu
inline bool is_pow2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }
inline bool is_pow2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }
template<typename T> inline T range_check(T v, T minv, T maxv) { assert(v >= minv && v <= maxv); BASISU_NOTE_UNUSED(minv); BASISU_NOTE_UNUSED(maxv); return v; }
template<typename T> inline T range_check(T v, T maxv) { assert(v <= maxv); BASISU_NOTE_UNUSED(maxv); return v; }
template<typename T> inline T open_range_check(T v, T minv, T maxv) { assert(v >= minv && v < maxv); BASISU_NOTE_UNUSED(minv); BASISU_NOTE_UNUSED(maxv); return v; }
template<typename T> inline T open_range_check(T v, T maxv) { assert(v < maxv); BASISU_NOTE_UNUSED(maxv); return v; }
// Open interval
inline bool is_in_bounds(int v, int l, int h)
{
return (v >= l) && (v < h);
}
// Closed interval
inline bool is_in_range(int v, int l, int h)
{
return (v >= l) && (v <= h);
}
inline bool is_in_range(float v, float l, float h)
{
return (v >= l) && (v <= h);
}
inline uint32_t total_bits(uint32_t v) { uint32_t l = 0; for ( ; v > 0U; ++l) v >>= 1; return l; }
template<typename T> inline T saturate(T val) { return clamp(val, 0.0f, 1.0f); }
inline uint32_t get_bit(uint32_t src, int ndx)
{
assert(is_in_bounds(ndx, 0, 32));
return (src >> ndx) & 1;
}
inline bool is_bit_set(uint32_t src, int ndx)
{
return get_bit(src, ndx) != 0;
}
inline uint32_t get_bits(uint32_t val, int low, int high)
{
const int num_bits = (high - low) + 1;
assert(is_in_range(num_bits, 1, 32));
val >>= low;
if (num_bits != 32)
val &= ((1u << num_bits) - 1);
return val;
}
template<typename T, typename R> inline void append_vector(T &vec, const R *pObjs, size_t n)
{
if (n)
@@ -260,6 +358,14 @@ namespace basisu
return (m != 0) ? (y - m) : m;
}
inline float posmodf(float x, float y)
{
float m = fmodf(x, y);
if (m < 0.0f)
m += y;
return m;
}
inline bool do_excl_ranges_overlap(int la, int ha, int lb, int hb)
{
assert(la < ha && lb < hb);
@@ -267,6 +373,11 @@ namespace basisu
return true;
}
static inline uint32_t read_le_word(const uint8_t* pBytes)
{
return (pBytes[1] << 8U) | (pBytes[0]);
}
static inline uint32_t read_le_dword(const uint8_t *pBytes)
{
return (pBytes[3] << 24U) | (pBytes[2] << 16U) | (pBytes[1] << 8U) | (pBytes[0]);
@@ -292,6 +403,7 @@ namespace basisu
inline packed_uint& operator= (uint64_t v)
{
// TODO: Add assert on truncation?
for (uint32_t i = 0; i < NumBytes; i++)
m_bytes[i] = static_cast<uint8_t>(v >> (i * 8));
return *this;
@@ -302,57 +414,67 @@ namespace basisu
memcpy(m_bytes, rhs.m_bytes, sizeof(m_bytes));
return *this;
}
inline uint64_t get_uint64() const
{
// Some compilers may warn about this code. It clearly cannot access beyond the end of the m_bytes struct here.
if constexpr (NumBytes == 1)
{
return m_bytes[0];
}
else if constexpr (NumBytes == 2)
{
return (m_bytes[1] << 8U) | m_bytes[0];
}
else if constexpr (NumBytes == 3)
{
return (m_bytes[2] << 16U) | (m_bytes[1] << 8U) | m_bytes[0];
}
else if constexpr (NumBytes == 4)
{
return read_le_dword(m_bytes);
}
else if constexpr (NumBytes == 5)
{
uint32_t l = read_le_dword(m_bytes);
uint32_t h = m_bytes[4];
return static_cast<uint64_t>(l) | (static_cast<uint64_t>(h) << 32U);
}
else if constexpr (NumBytes == 6)
{
uint32_t l = read_le_dword(m_bytes);
uint32_t h = (m_bytes[5] << 8U) | m_bytes[4];
return static_cast<uint64_t>(l) | (static_cast<uint64_t>(h) << 32U);
}
else if constexpr (NumBytes == 7)
{
uint32_t l = read_le_dword(m_bytes);
uint32_t h = (m_bytes[6] << 16U) | (m_bytes[5] << 8U) | m_bytes[4];
return static_cast<uint64_t>(l) | (static_cast<uint64_t>(h) << 32U);
}
else if constexpr (NumBytes == 8)
{
uint32_t l = read_le_dword(m_bytes);
uint32_t h = read_le_dword(m_bytes + 4);
return static_cast<uint64_t>(l) | (static_cast<uint64_t>(h) << 32U);
}
else
{
static_assert(NumBytes <= 8, "Invalid NumBytes");
return 0;
}
}
inline uint32_t get_uint32() const
{
static_assert(NumBytes <= sizeof(uint32_t), "packed_uint too large to use get_uint32");
return static_cast<uint32_t>(get_uint64());
}
inline operator uint32_t() const
{
switch (NumBytes)
{
case 1:
{
return m_bytes[0];
}
case 2:
{
return (m_bytes[1] << 8U) | m_bytes[0];
}
case 3:
{
return (m_bytes[2] << 16U) | (m_bytes[1] << 8U) | m_bytes[0];
}
case 4:
{
return read_le_dword(m_bytes);
}
case 5:
{
uint32_t l = read_le_dword(m_bytes);
uint32_t h = m_bytes[4];
return static_cast<uint64_t>(l) | (static_cast<uint64_t>(h) << 32U);
}
case 6:
{
uint32_t l = read_le_dword(m_bytes);
uint32_t h = (m_bytes[5] << 8U) | m_bytes[4];
return static_cast<uint64_t>(l) | (static_cast<uint64_t>(h) << 32U);
}
case 7:
{
uint32_t l = read_le_dword(m_bytes);
uint32_t h = (m_bytes[6] << 16U) | (m_bytes[5] << 8U) | m_bytes[4];
return static_cast<uint64_t>(l) | (static_cast<uint64_t>(h) << 32U);
}
case 8:
{
uint32_t l = read_le_dword(m_bytes);
uint32_t h = read_le_dword(m_bytes + 4);
return static_cast<uint64_t>(l) | (static_cast<uint64_t>(h) << 32U);
}
default:
{
assert(0);
return 0;
}
}
static_assert(NumBytes <= sizeof(uint32_t), "packed_uint too large to use operator uint32_t");
return static_cast<uint32_t>(get_uint64());
}
};
@@ -386,24 +508,28 @@ namespace basisu
static const uint8_t g_huffman_sorted_codelength_codes[] = { cHuffmanSmallZeroRunCode, cHuffmanBigZeroRunCode, cHuffmanSmallRepeatCode, cHuffmanBigRepeatCode, 0, 8, 7, 9, 6, 0xA, 5, 0xB, 4, 0xC, 3, 0xD, 2, 0xE, 1, 0xF, 0x10 };
const uint32_t cHuffmanTotalSortedCodelengthCodes = sizeof(g_huffman_sorted_codelength_codes) / sizeof(g_huffman_sorted_codelength_codes[0]);
// GPU texture formats
// GPU texture formats and various uncompressed texture formats.
enum class texture_format
{
cInvalidTextureFormat = -1,
// Block-based formats
cETC1, // ETC1
cETC1S, // ETC1 (subset: diff colors only, no subblocks)
cETC2_RGB, // ETC2 color block (basisu doesn't support ETC2 planar/T/H modes - just basic ETC1)
cETC2_RGBA, // ETC2 EAC alpha block followed by ETC2 color block
cETC2_ALPHA, // ETC2 EAC alpha block
cETC1, // ETC1
cETC1S, // ETC1 (subset: diff colors only, no subblocks)
cETC2_RGB, // ETC2 color block (basisu doesn't support ETC2 planar/T/H modes - just basic ETC1)
cETC2_RGBA, // ETC2 EAC alpha block followed by ETC2 color block
cETC2_ALPHA, // ETC2 EAC alpha block
cBC1, // DXT1
cBC3, // DXT5 (BC4/DXT5A block followed by a BC1/DXT1 block)
cBC4, // DXT5A
cBC5, // 3DC/DXN (two BC4/DXT5A blocks)
cBC6HSigned, // HDR
cBC6HUnsigned, // HDR
cBC7,
cASTC4x4, // LDR only
cASTC_LDR_4x4, // ASTC 4x4 LDR only
cASTC_HDR_4x4, // ASTC 4x4 HDR only (but may use LDR ASTC blocks internally, although our encoders don't do this)
cASTC_HDR_6x6, // ASTC 6x6 HDR only (but may use LDR ASTC blocks internally, although our encoders don't do this)
cPVRTC1_4_RGB,
cPVRTC1_4_RGBA,
cATC_RGB,
@@ -413,18 +539,128 @@ namespace basisu
cETC2_R11_EAC,
cETC2_RG11_EAC,
cUASTC4x4,
cUASTC_HDR_4x4,
cBC1_NV,
cBC1_AMD,
// Uncompressed/raw pixels
cRGBA32,
cRGB565,
cBGR565,
cRGBA4444,
cABGR4444
cABGR4444,
cRGBA_HALF,
cRGB_HALF,
cRGB_9E5,
// All remaining ASTC LDR block size variants (other than 4x4 which is above). There are 14 total ASTC block sizes, including 4x4.
cASTC_LDR_5x4,
cASTC_LDR_5x5,
cASTC_LDR_6x5,
cASTC_LDR_6x6,
cASTC_LDR_8x5,
cASTC_LDR_8x6,
cASTC_LDR_10x5,
cASTC_LDR_10x6,
cASTC_LDR_8x8,
cASTC_LDR_10x8,
cASTC_LDR_10x10,
cASTC_LDR_12x10,
cASTC_LDR_12x12
};
inline uint32_t get_bytes_per_block(texture_format fmt)
inline bool is_astc(texture_format fmt)
{
switch (fmt)
{
case texture_format::cASTC_HDR_4x4:
case texture_format::cASTC_HDR_6x6:
case texture_format::cASTC_LDR_4x4:
case texture_format::cASTC_LDR_5x4:
case texture_format::cASTC_LDR_5x5:
case texture_format::cASTC_LDR_6x5:
case texture_format::cASTC_LDR_6x6:
case texture_format::cASTC_LDR_8x5:
case texture_format::cASTC_LDR_8x6:
case texture_format::cASTC_LDR_10x5:
case texture_format::cASTC_LDR_10x6:
case texture_format::cASTC_LDR_8x8:
case texture_format::cASTC_LDR_10x8:
case texture_format::cASTC_LDR_10x10:
case texture_format::cASTC_LDR_12x10:
case texture_format::cASTC_LDR_12x12:
return true;
default:
break;
}
return false;
}
inline bool is_hdr_astc(texture_format fmt)
{
switch (fmt)
{
case texture_format::cASTC_HDR_4x4:
case texture_format::cASTC_HDR_6x6:
return true;
default:
break;
}
return false;
}
inline bool is_ldr_astc(texture_format fmt)
{
switch (fmt)
{
case texture_format::cASTC_LDR_4x4:
case texture_format::cASTC_LDR_5x4:
case texture_format::cASTC_LDR_5x5:
case texture_format::cASTC_LDR_6x5:
case texture_format::cASTC_LDR_6x6:
case texture_format::cASTC_LDR_8x5:
case texture_format::cASTC_LDR_8x6:
case texture_format::cASTC_LDR_10x5:
case texture_format::cASTC_LDR_10x6:
case texture_format::cASTC_LDR_8x8:
case texture_format::cASTC_LDR_10x8:
case texture_format::cASTC_LDR_10x10:
case texture_format::cASTC_LDR_12x10:
case texture_format::cASTC_LDR_12x12:
return true;
default:
break;
}
return false;
}
inline bool is_uncompressed_texture_format(texture_format fmt)
{
switch (fmt)
{
case texture_format::cRGBA32:
case texture_format::cRGB565:
case texture_format::cBGR565:
case texture_format::cRGBA4444:
case texture_format::cABGR4444:
case texture_format::cRGBA_HALF:
case texture_format::cRGB_HALF:
case texture_format::cRGB_9E5:
return true;
default:
break;
}
return false;
}
inline bool is_block_based_texture_format(texture_format fmt)
{
return !is_uncompressed_texture_format(fmt);
}
// This is bytes per block for GPU formats, or bytes per texel for uncompressed formats.
inline uint32_t get_bytes_per_block_or_pixel(texture_format fmt)
{
switch (fmt)
{
@@ -443,25 +679,56 @@ namespace basisu
case texture_format::cETC2_R11_EAC:
return 8;
case texture_format::cRGBA32:
return sizeof(uint32_t) * 16;
case texture_format::cRGB_9E5:
return sizeof(uint32_t);
case texture_format::cRGB_HALF:
return sizeof(uint16_t) * 3;
case texture_format::cRGBA_HALF:
return sizeof(uint16_t) * 4;
case texture_format::cRGB565:
case texture_format::cBGR565:
case texture_format::cRGBA4444:
case texture_format::cABGR4444:
return sizeof(uint16_t);
default:
break;
}
// Everything else is 16 bytes/block.
return 16;
}
// This is qwords per block for GPU formats, or not valid for uncompressed formats.
inline uint32_t get_qwords_per_block(texture_format fmt)
{
return get_bytes_per_block(fmt) >> 3;
assert(is_block_based_texture_format(fmt));
const uint32_t bytes_per_block = get_bytes_per_block_or_pixel(fmt);
return bytes_per_block >> 3;
}
inline uint32_t get_block_width(texture_format fmt)
{
BASISU_NOTE_UNUSED(fmt);
assert(is_block_based_texture_format(fmt));
switch (fmt)
{
case texture_format::cFXT1_RGB:
return 8;
case texture_format::cFXT1_RGB: return 8;
case texture_format::cASTC_HDR_6x6: return 6;
case texture_format::cASTC_LDR_5x4: return 5;
case texture_format::cASTC_LDR_5x5: return 5;
case texture_format::cASTC_LDR_6x5: return 6;
case texture_format::cASTC_LDR_6x6: return 6;
case texture_format::cASTC_LDR_8x5: return 8;
case texture_format::cASTC_LDR_8x6: return 8;
case texture_format::cASTC_LDR_10x5: return 10;
case texture_format::cASTC_LDR_10x6: return 10;
case texture_format::cASTC_LDR_8x8: return 8;
case texture_format::cASTC_LDR_10x8: return 10;
case texture_format::cASTC_LDR_10x10: return 10;
case texture_format::cASTC_LDR_12x10: return 12;
case texture_format::cASTC_LDR_12x12: return 12;
default:
break;
}
@@ -470,9 +737,85 @@ namespace basisu
inline uint32_t get_block_height(texture_format fmt)
{
BASISU_NOTE_UNUSED(fmt);
assert(is_block_based_texture_format(fmt));
switch (fmt)
{
case texture_format::cASTC_HDR_6x6: return 6;
case texture_format::cASTC_LDR_5x5: return 5;
case texture_format::cASTC_LDR_6x5: return 5;
case texture_format::cASTC_LDR_6x6: return 6;
case texture_format::cASTC_LDR_8x5: return 5;
case texture_format::cASTC_LDR_8x6: return 6;
case texture_format::cASTC_LDR_10x5: return 5;
case texture_format::cASTC_LDR_10x6: return 6;
case texture_format::cASTC_LDR_8x8: return 8;
case texture_format::cASTC_LDR_10x8: return 8;
case texture_format::cASTC_LDR_10x10: return 10;
case texture_format::cASTC_LDR_12x10: return 10;
case texture_format::cASTC_LDR_12x12: return 12;
default:
break;
}
return 4;
}
inline bool is_hdr_texture_format(texture_format fmt)
{
switch (fmt)
{
case texture_format::cASTC_HDR_4x4:
case texture_format::cUASTC_HDR_4x4:
case texture_format::cASTC_HDR_6x6:
case texture_format::cBC6HSigned:
case texture_format::cBC6HUnsigned:
case texture_format::cRGBA_HALF:
case texture_format::cRGB_HALF:
case texture_format::cRGB_9E5:
return true;
default:
break;
}
return false;
}
inline bool is_ldr_texture_format(texture_format fmt)
{
return !is_hdr_texture_format(fmt);
}
inline texture_format get_astc_ldr_texture_format(uint32_t width, uint32_t height)
{
#define BU_ASTC_LDR_MATCH_BLOCK_DIM(x, y, f) if ((width == (x)) && (height == (y))) return (f);
BU_ASTC_LDR_MATCH_BLOCK_DIM(4, 4, texture_format::cASTC_LDR_4x4);
BU_ASTC_LDR_MATCH_BLOCK_DIM(5, 4, texture_format::cASTC_LDR_5x4);
BU_ASTC_LDR_MATCH_BLOCK_DIM(5, 5, texture_format::cASTC_LDR_5x5);
BU_ASTC_LDR_MATCH_BLOCK_DIM(6, 5, texture_format::cASTC_LDR_6x5);
BU_ASTC_LDR_MATCH_BLOCK_DIM(6, 6, texture_format::cASTC_LDR_6x6);
BU_ASTC_LDR_MATCH_BLOCK_DIM(8, 5, texture_format::cASTC_LDR_8x5);
BU_ASTC_LDR_MATCH_BLOCK_DIM(8, 6, texture_format::cASTC_LDR_8x6);
BU_ASTC_LDR_MATCH_BLOCK_DIM(10, 5, texture_format::cASTC_LDR_10x5);
BU_ASTC_LDR_MATCH_BLOCK_DIM(10, 6, texture_format::cASTC_LDR_10x6);
BU_ASTC_LDR_MATCH_BLOCK_DIM(8, 8, texture_format::cASTC_LDR_8x8);
BU_ASTC_LDR_MATCH_BLOCK_DIM(10, 8, texture_format::cASTC_LDR_10x8);
BU_ASTC_LDR_MATCH_BLOCK_DIM(10, 10, texture_format::cASTC_LDR_10x10);
BU_ASTC_LDR_MATCH_BLOCK_DIM(12, 10, texture_format::cASTC_LDR_12x10);
BU_ASTC_LDR_MATCH_BLOCK_DIM(12, 12, texture_format::cASTC_LDR_12x12);
#undef BU_ASTC_LDR_MATCH_BLOCK_DIM
return texture_format::cInvalidTextureFormat;
}
inline bool is_valid_astc_block_size(uint32_t width, uint32_t height)
{
return get_astc_ldr_texture_format(width, height) != texture_format::cInvalidTextureFormat;
}
} // namespace basisu
+648
View File
@@ -0,0 +1,648 @@
const uint32_t BU_TOTAL_ASTC_CFGS = 10311;
const uint8_t s_astc_cfg_table[BU_TOTAL_ASTC_CFGS*3] = {
176,72,0,208,72,0,240,72,0,16,73,0,48,73,0,80,73,0,112,73,0,176,130,0,208,130,0,240,130,0,16,131,0,48,131,0,80,131,0,112,131,0,176,132,0,208,132,0,
240,132,0,16,133,0,48,133,0,80,133,0,112,133,0,176,134,0,208,134,0,240,134,0,16,135,0,48,135,0,80,135,0,112,135,0,176,194,0,208,194,0,240,194,0,16,195,0,
48,195,0,80,195,0,112,195,0,176,196,0,208,196,0,240,196,0,16,197,0,48,197,0,80,197,0,112,197,0,176,198,0,208,198,0,240,198,0,16,199,0,48,199,0,80,199,0,
112,199,0,176,2,1,208,2,1,240,2,1,16,3,1,48,3,1,80,3,1,112,3,1,176,4,1,208,4,1,240,4,1,16,5,1,48,5,1,80,5,1,112,5,1,176,6,1,
208,6,1,240,6,1,16,7,1,48,7,1,80,7,1,112,7,1,176,8,1,208,8,1,240,8,1,16,9,1,48,9,1,80,9,1,112,9,1,176,66,1,208,66,1,240,66,1,
16,67,1,48,67,1,80,67,1,112,67,1,176,68,1,208,68,1,240,68,1,16,69,1,48,69,1,80,69,1,112,69,1,176,70,1,208,70,1,240,70,1,16,71,1,48,71,1,
80,71,1,112,71,1,176,72,1,208,72,1,240,72,1,16,73,1,48,73,1,80,73,1,112,73,1,16,1,2,48,1,2,80,1,2,112,1,2,16,17,2,48,17,2,80,17,2,
112,17,2,16,33,2,48,33,2,80,33,2,112,33,2,16,65,2,48,65,2,80,65,2,112,65,2,80,72,2,112,72,2,144,72,2,176,72,2,208,72,2,240,72,2,16,73,2,
48,73,2,80,73,2,112,73,2,16,81,2,48,81,2,80,81,2,112,81,2,10,97,2,42,97,2,73,97,2,105,97,2,16,129,2,48,129,2,80,129,2,112,129,2,80,130,2,
112,130,2,144,130,2,176,130,2,208,130,2,240,130,2,16,131,2,48,131,2,80,131,2,112,131,2,80,132,2,112,132,2,144,132,2,176,132,2,208,132,2,240,132,2,16,133,2,
48,133,2,80,133,2,112,133,2,80,134,2,112,134,2,144,134,2,176,134,2,208,134,2,240,134,2,16,135,2,48,135,2,80,135,2,112,135,2,16,145,2,48,145,2,80,145,2,
112,145,2,10,161,2,42,161,2,73,161,2,105,161,2,16,193,2,48,193,2,80,193,2,112,193,2,80,194,2,112,194,2,144,194,2,176,194,2,208,194,2,240,194,2,16,195,2,
48,195,2,80,195,2,112,195,2,80,196,2,112,196,2,144,196,2,176,196,2,208,196,2,240,196,2,16,197,2,48,197,2,80,197,2,112,197,2,80,198,2,112,198,2,144,198,2,
176,198,2,208,198,2,240,198,2,16,199,2,48,199,2,80,199,2,112,199,2,10,209,2,42,209,2,73,209,2,105,209,2,4,225,2,36,225,2,67,225,2,99,225,2,16,1,3,
48,1,3,80,1,3,112,1,3,80,2,3,112,2,3,144,2,3,176,2,3,208,2,3,240,2,3,16,3,3,48,3,3,80,3,3,112,3,3,80,4,3,112,4,3,144,4,3,
176,4,3,208,4,3,240,4,3,16,5,3,48,5,3,80,5,3,112,5,3,80,6,3,112,6,3,144,6,3,176,6,3,208,6,3,240,6,3,16,7,3,48,7,3,80,7,3,
112,7,3,80,8,3,112,8,3,144,8,3,176,8,3,208,8,3,240,8,3,16,9,3,48,9,3,80,9,3,112,9,3,10,17,3,42,17,3,73,17,3,105,17,3,4,33,3,
36,33,3,67,33,3,99,33,3,16,65,3,48,65,3,80,65,3,112,65,3,80,66,3,112,66,3,144,66,3,176,66,3,208,66,3,240,66,3,15,67,3,45,67,3,76,67,3,
106,67,3,80,68,3,112,68,3,144,68,3,176,68,3,208,68,3,240,68,3,15,69,3,45,69,3,76,69,3,106,69,3,80,70,3,112,70,3,144,70,3,176,70,3,208,70,3,
240,70,3,15,71,3,45,71,3,76,71,3,106,71,3,80,72,3,112,72,3,144,72,3,176,72,3,208,72,3,240,72,3,15,73,3,45,73,3,76,73,3,106,73,3,6,81,3,
37,81,3,69,81,3,100,81,3,176,0,4,208,0,4,240,0,4,16,1,4,48,1,4,80,1,4,112,1,4,176,16,4,208,16,4,240,16,4,16,17,4,48,17,4,80,17,4,
112,17,4,176,32,4,208,32,4,240,32,4,16,33,4,48,33,4,80,33,4,112,33,4,176,64,4,208,64,4,240,64,4,16,65,4,48,65,4,80,65,4,112,65,4,48,72,4,
80,72,4,112,72,4,144,72,4,176,72,4,208,72,4,240,72,4,16,73,4,48,73,4,80,73,4,109,73,4,176,80,4,208,80,4,240,80,4,16,81,4,48,81,4,79,81,4,
110,81,4,170,96,4,202,96,4,233,96,4,8,97,4,40,97,4,71,97,4,102,97,4,176,128,4,208,128,4,240,128,4,16,129,4,48,129,4,80,129,4,112,129,4,48,130,4,
80,130,4,112,130,4,144,130,4,176,130,4,208,130,4,240,130,4,16,131,4,48,131,4,80,131,4,109,131,4,48,132,4,80,132,4,112,132,4,144,132,4,176,132,4,208,132,4,
240,132,4,16,133,4,48,133,4,80,133,4,109,133,4,48,134,4,80,134,4,112,134,4,144,134,4,176,134,4,208,134,4,240,134,4,16,135,4,48,135,4,80,135,4,109,135,4,
176,144,4,208,144,4,240,144,4,16,145,4,48,145,4,79,145,4,110,145,4,170,160,4,202,160,4,233,160,4,8,161,4,40,161,4,71,161,4,102,161,4,176,192,4,208,192,4,
240,192,4,16,193,4,48,193,4,80,193,4,112,193,4,48,194,4,80,194,4,112,194,4,144,194,4,176,194,4,208,194,4,240,194,4,14,195,4,43,195,4,73,195,4,102,195,4,
48,196,4,80,196,4,112,196,4,144,196,4,176,196,4,208,196,4,240,196,4,14,197,4,43,197,4,73,197,4,102,197,4,48,198,4,80,198,4,112,198,4,144,198,4,176,198,4,
208,198,4,240,198,4,14,199,4,43,199,4,73,199,4,102,199,4,170,208,4,202,208,4,233,208,4,8,209,4,40,209,4,71,209,4,102,209,4,164,224,4,196,224,4,227,224,4,
3,225,4,34,225,4,66,225,4,97,225,4,176,0,5,208,0,5,240,0,5,16,1,5,48,1,5,80,1,5,112,1,5,48,2,5,80,2,5,112,2,5,144,2,5,176,2,5,
208,2,5,240,2,5,14,3,5,43,3,5,73,3,5,102,3,5,48,4,5,80,4,5,112,4,5,144,4,5,176,4,5,208,4,5,240,4,5,14,5,5,43,5,5,73,5,5,
102,5,5,48,6,5,80,6,5,112,6,5,144,6,5,176,6,5,208,6,5,240,6,5,14,7,5,43,7,5,73,7,5,102,7,5,48,8,5,80,8,5,112,8,5,144,8,5,
176,8,5,208,8,5,240,8,5,14,9,5,43,9,5,73,9,5,102,9,5,170,16,5,202,16,5,233,16,5,8,17,5,40,17,5,71,17,5,102,17,5,164,32,5,196,32,5,
227,32,5,3,33,5,34,33,5,66,33,5,97,33,5,176,64,5,208,64,5,240,64,5,16,65,5,48,65,5,80,65,5,112,65,5,48,66,5,80,66,5,112,66,5,144,66,5,
175,66,5,204,66,5,235,66,5,9,67,5,38,67,5,69,67,5,99,67,5,48,68,5,80,68,5,112,68,5,144,68,5,175,68,5,204,68,5,235,68,5,9,69,5,38,69,5,
69,69,5,99,69,5,48,70,5,80,70,5,112,70,5,144,70,5,175,70,5,204,70,5,235,70,5,9,71,5,38,71,5,69,71,5,99,71,5,48,72,5,80,72,5,112,72,5,
144,72,5,175,72,5,204,72,5,235,72,5,9,73,5,38,73,5,69,73,5,99,73,5,166,80,5,197,80,5,229,80,5,4,81,5,36,81,5,67,81,5,99,81,5,112,0,6,
144,0,6,176,0,6,208,0,6,240,0,6,16,1,6,48,1,6,80,1,6,112,1,6,112,16,6,144,16,6,176,16,6,208,16,6,240,16,6,16,17,6,48,17,6,80,17,6,
112,17,6,112,32,6,144,32,6,176,32,6,208,32,6,240,32,6,16,33,6,48,33,6,80,33,6,112,33,6,112,64,6,144,64,6,176,64,6,208,64,6,240,64,6,16,65,6,
48,65,6,80,65,6,112,65,6,48,72,6,80,72,6,112,72,6,144,72,6,176,72,6,208,72,6,240,72,6,13,73,6,40,73,6,68,73,6,112,80,6,144,80,6,176,80,6,
208,80,6,239,80,6,14,81,6,44,81,6,76,81,6,106,81,6,106,96,6,138,96,6,169,96,6,200,96,6,231,96,6,6,97,6,37,97,6,69,97,6,100,97,6,112,128,6,
144,128,6,176,128,6,208,128,6,240,128,6,16,129,6,48,129,6,80,129,6,112,129,6,48,130,6,80,130,6,112,130,6,144,130,6,176,130,6,208,130,6,240,130,6,13,131,6,
40,131,6,68,131,6,48,132,6,80,132,6,112,132,6,144,132,6,176,132,6,208,132,6,240,132,6,13,133,6,40,133,6,68,133,6,48,134,6,80,134,6,112,134,6,144,134,6,
176,134,6,208,134,6,240,134,6,13,135,6,40,135,6,68,135,6,112,144,6,144,144,6,176,144,6,208,144,6,239,144,6,14,145,6,44,145,6,76,145,6,106,145,6,106,160,6,
138,160,6,169,160,6,200,160,6,231,160,6,6,161,6,37,161,6,69,161,6,100,161,6,112,192,6,144,192,6,176,192,6,208,192,6,240,192,6,16,193,6,48,193,6,80,193,6,
112,193,6,48,194,6,80,194,6,112,194,6,144,194,6,176,194,6,205,194,6,234,194,6,6,195,6,35,195,6,64,195,6,48,196,6,80,196,6,112,196,6,144,196,6,176,196,6,
205,196,6,234,196,6,6,197,6,35,197,6,64,197,6,48,198,6,80,198,6,112,198,6,144,198,6,176,198,6,205,198,6,234,198,6,6,199,6,35,199,6,64,199,6,106,208,6,
138,208,6,169,208,6,200,208,6,231,208,6,6,209,6,37,209,6,69,209,6,100,209,6,100,224,6,132,224,6,163,224,6,195,224,6,226,224,6,1,225,6,33,225,6,64,225,6,
96,225,6,112,0,7,144,0,7,176,0,7,208,0,7,240,0,7,16,1,7,48,1,7,80,1,7,112,1,7,48,2,7,80,2,7,112,2,7,144,2,7,176,2,7,205,2,7,
234,2,7,6,3,7,35,3,7,64,3,7,48,4,7,80,4,7,112,4,7,144,4,7,176,4,7,205,4,7,234,4,7,6,5,7,35,5,7,64,5,7,48,6,7,80,6,7,
112,6,7,144,6,7,176,6,7,205,6,7,234,6,7,6,7,7,35,7,7,64,7,7,48,8,7,80,8,7,112,8,7,144,8,7,176,8,7,205,8,7,234,8,7,6,9,7,
35,9,7,64,9,7,106,16,7,138,16,7,169,16,7,200,16,7,231,16,7,6,17,7,37,17,7,69,17,7,100,17,7,100,32,7,132,32,7,163,32,7,195,32,7,226,32,7,
1,33,7,33,33,7,64,33,7,96,33,7,112,64,7,144,64,7,176,64,7,208,64,7,240,64,7,16,65,7,48,65,7,80,65,7,111,65,7,48,66,7,80,66,7,111,66,7,
141,66,7,170,66,7,199,66,7,230,66,7,3,67,7,32,67,7,48,68,7,80,68,7,111,68,7,141,68,7,170,68,7,199,68,7,230,68,7,3,69,7,32,69,7,48,70,7,
80,70,7,111,70,7,141,70,7,170,70,7,199,70,7,230,70,7,3,71,7,32,71,7,48,72,7,80,72,7,111,72,7,141,72,7,170,72,7,199,72,7,230,72,7,3,73,7,
32,73,7,102,80,7,133,80,7,164,80,7,196,80,7,227,80,7,3,81,7,34,81,7,65,81,7,97,81,7,80,0,8,112,0,8,144,0,8,176,0,8,208,0,8,240,0,8,
16,1,8,48,1,8,80,1,8,112,1,8,80,16,8,112,16,8,144,16,8,176,16,8,208,16,8,240,16,8,16,17,8,48,17,8,80,17,8,112,17,8,80,32,8,112,32,8,
144,32,8,176,32,8,208,32,8,240,32,8,16,33,8,47,33,8,77,33,8,107,33,8,80,64,8,112,64,8,144,64,8,176,64,8,208,64,8,240,64,8,16,65,8,48,65,8,
80,65,8,112,65,8,16,72,8,48,72,8,80,72,8,112,72,8,144,72,8,176,72,8,205,72,8,232,72,8,1,73,8,80,80,8,112,80,8,144,80,8,175,80,8,206,80,8,
236,80,8,11,81,8,41,81,8,72,81,8,102,81,8,74,96,8,105,96,8,136,96,8,167,96,8,198,96,8,229,96,8,4,97,8,35,97,8,66,97,8,97,97,8,80,128,8,
112,128,8,144,128,8,176,128,8,208,128,8,240,128,8,16,129,8,48,129,8,80,129,8,112,129,8,16,130,8,48,130,8,80,130,8,112,130,8,144,130,8,176,130,8,205,130,8,
232,130,8,1,131,8,16,132,8,48,132,8,80,132,8,112,132,8,144,132,8,176,132,8,205,132,8,232,132,8,1,133,8,16,134,8,48,134,8,80,134,8,112,134,8,144,134,8,
176,134,8,205,134,8,232,134,8,1,135,8,80,144,8,112,144,8,144,144,8,175,144,8,206,144,8,236,144,8,11,145,8,41,145,8,72,145,8,102,145,8,74,160,8,105,160,8,
136,160,8,167,160,8,198,160,8,229,160,8,4,161,8,35,161,8,66,161,8,97,161,8,80,192,8,112,192,8,144,192,8,176,192,8,208,192,8,240,192,8,16,193,8,48,193,8,
80,193,8,112,193,8,16,194,8,48,194,8,80,194,8,112,194,8,143,194,8,170,194,8,198,194,8,227,194,8,16,196,8,48,196,8,80,196,8,112,196,8,143,196,8,170,196,8,
198,196,8,227,196,8,16,198,8,48,198,8,80,198,8,112,198,8,143,198,8,170,198,8,198,198,8,227,198,8,74,208,8,105,208,8,136,208,8,167,208,8,198,208,8,229,208,8,
4,209,8,35,209,8,66,209,8,97,209,8,68,224,8,99,224,8,131,224,8,162,224,8,193,224,8,225,224,8,0,225,8,32,225,8,80,0,9,112,0,9,144,0,9,176,0,9,
208,0,9,240,0,9,16,1,9,48,1,9,80,1,9,112,1,9,16,2,9,48,2,9,80,2,9,112,2,9,143,2,9,170,2,9,198,2,9,227,2,9,16,4,9,48,4,9,
80,4,9,112,4,9,143,4,9,170,4,9,198,4,9,227,4,9,16,6,9,48,6,9,80,6,9,112,6,9,143,6,9,170,6,9,198,6,9,227,6,9,16,8,9,48,8,9,
80,8,9,112,8,9,143,8,9,170,8,9,198,8,9,227,8,9,74,16,9,105,16,9,136,16,9,167,16,9,198,16,9,229,16,9,4,17,9,35,17,9,66,17,9,97,17,9,
68,32,9,99,32,9,131,32,9,162,32,9,193,32,9,225,32,9,0,33,9,32,33,9,80,64,9,112,64,9,144,64,9,176,64,9,208,64,9,240,64,9,15,65,9,46,65,9,
76,65,9,107,65,9,16,66,9,48,66,9,79,66,9,108,66,9,137,66,9,166,66,9,195,66,9,224,66,9,16,68,9,48,68,9,79,68,9,108,68,9,137,68,9,166,68,9,
195,68,9,224,68,9,16,70,9,48,70,9,79,70,9,108,70,9,137,70,9,166,70,9,195,70,9,224,70,9,16,72,9,48,72,9,79,72,9,108,72,9,137,72,9,166,72,9,
195,72,9,224,72,9,70,80,9,101,80,9,132,80,9,163,80,9,195,80,9,226,80,9,1,81,9,32,81,9,64,81,9,80,0,10,112,0,10,144,0,10,176,0,10,208,0,10,
240,0,10,16,1,10,48,1,10,80,1,10,112,1,10,80,16,10,112,16,10,144,16,10,176,16,10,208,16,10,240,16,10,16,17,10,48,17,10,80,17,10,109,17,10,80,32,10,
112,32,10,144,32,10,176,32,10,208,32,10,240,32,10,13,33,10,43,33,10,73,33,10,102,33,10,80,64,10,112,64,10,144,64,10,176,64,10,208,64,10,240,64,10,16,65,10,
48,65,10,80,65,10,112,65,10,16,72,10,48,72,10,80,72,10,112,72,10,144,72,10,170,72,10,195,72,10,80,80,10,112,80,10,143,80,10,173,80,10,203,80,10,234,80,10,
8,81,10,38,81,10,68,81,10,99,81,10,73,96,10,104,96,10,135,96,10,166,96,10,197,96,10,228,96,10,2,97,10,33,97,10,64,97,10,80,128,10,112,128,10,144,128,10,
176,128,10,208,128,10,240,128,10,16,129,10,48,129,10,80,129,10,112,129,10,16,130,10,48,130,10,80,130,10,112,130,10,144,130,10,170,130,10,195,130,10,16,132,10,48,132,10,
80,132,10,112,132,10,144,132,10,170,132,10,195,132,10,16,134,10,48,134,10,80,134,10,112,134,10,144,134,10,170,134,10,195,134,10,80,144,10,112,144,10,143,144,10,173,144,10,
203,144,10,234,144,10,8,145,10,38,145,10,68,145,10,99,145,10,73,160,10,104,160,10,135,160,10,166,160,10,197,160,10,228,160,10,2,161,10,33,161,10,64,161,10,80,192,10,
112,192,10,144,192,10,176,192,10,208,192,10,240,192,10,16,193,10,48,193,10,79,193,10,108,193,10,16,194,10,48,194,10,80,194,10,109,194,10,138,194,10,164,194,10,16,196,10,
48,196,10,80,196,10,109,196,10,138,196,10,164,196,10,16,198,10,48,198,10,80,198,10,109,198,10,138,198,10,164,198,10,73,208,10,104,208,10,135,208,10,166,208,10,197,208,10,
228,208,10,2,209,10,33,209,10,64,209,10,67,224,10,99,224,10,130,224,10,161,224,10,192,224,10,224,224,10,80,0,11,112,0,11,144,0,11,176,0,11,208,0,11,240,0,11,
16,1,11,48,1,11,79,1,11,108,1,11,16,2,11,48,2,11,80,2,11,109,2,11,138,2,11,164,2,11,16,4,11,48,4,11,80,4,11,109,4,11,138,4,11,164,4,11,
16,6,11,48,6,11,80,6,11,109,6,11,138,6,11,164,6,11,16,8,11,48,8,11,80,8,11,109,8,11,138,8,11,164,8,11,73,16,11,104,16,11,135,16,11,166,16,11,
197,16,11,228,16,11,2,17,11,33,17,11,64,17,11,67,32,11,99,32,11,130,32,11,161,32,11,192,32,11,224,32,11,80,64,11,112,64,11,144,64,11,176,64,11,208,64,11,
238,64,11,12,65,11,42,65,11,73,65,11,103,65,11,16,66,11,48,66,11,76,66,11,104,66,11,133,66,11,161,66,11,16,68,11,48,68,11,76,68,11,104,68,11,133,68,11,
161,68,11,16,70,11,48,70,11,76,70,11,104,70,11,133,70,11,161,70,11,16,72,11,48,72,11,76,72,11,104,72,11,133,72,11,161,72,11,69,80,11,100,80,11,131,80,11,
162,80,11,193,80,11,225,80,11,0,81,11,48,0,12,80,0,12,112,0,12,144,0,12,176,0,12,208,0,12,240,0,12,16,1,12,48,1,12,80,1,12,112,1,12,48,16,12,
80,16,12,112,16,12,144,16,12,176,16,12,208,16,12,240,16,12,16,17,12,45,17,12,74,17,12,102,17,12,48,32,12,80,32,12,112,32,12,144,32,12,176,32,12,206,32,12,
236,32,12,9,33,12,38,33,12,68,33,12,97,33,12,48,64,12,80,64,12,112,64,12,144,64,12,176,64,12,208,64,12,240,64,12,16,65,12,48,65,12,80,65,12,111,65,12,
16,72,12,48,72,12,80,72,12,112,72,12,138,72,12,161,72,12,48,80,12,80,80,12,111,80,12,141,80,12,171,80,12,201,80,12,231,80,12,5,81,12,35,81,12,65,81,12,
42,96,12,72,96,12,103,96,12,134,96,12,164,96,12,195,96,12,226,96,12,0,97,12,48,128,12,80,128,12,112,128,12,144,128,12,176,128,12,208,128,12,240,128,12,16,129,12,
48,129,12,80,129,12,111,129,12,16,130,12,48,130,12,80,130,12,112,130,12,138,130,12,161,130,12,16,132,12,48,132,12,80,132,12,112,132,12,138,132,12,161,132,12,16,134,12,
48,134,12,80,134,12,112,134,12,138,134,12,161,134,12,48,144,12,80,144,12,111,144,12,141,144,12,171,144,12,201,144,12,231,144,12,5,145,12,35,145,12,65,145,12,42,160,12,
72,160,12,103,160,12,134,160,12,164,160,12,195,160,12,226,160,12,0,161,12,48,192,12,80,192,12,112,192,12,144,192,12,176,192,12,208,192,12,240,192,12,15,193,12,44,193,12,
74,193,12,103,193,12,16,194,12,48,194,12,78,194,12,105,194,12,132,194,12,16,196,12,48,196,12,78,196,12,105,196,12,132,196,12,16,198,12,48,198,12,78,198,12,105,198,12,
132,198,12,42,208,12,72,208,12,103,208,12,134,208,12,164,208,12,195,208,12,226,208,12,0,209,12,36,224,12,67,224,12,98,224,12,129,224,12,160,224,12,48,0,13,80,0,13,
112,0,13,144,0,13,176,0,13,208,0,13,240,0,13,15,1,13,44,1,13,74,1,13,103,1,13,16,2,13,48,2,13,78,2,13,105,2,13,132,2,13,16,4,13,48,4,13,
78,4,13,105,4,13,132,4,13,16,6,13,48,6,13,78,6,13,105,6,13,132,6,13,16,8,13,48,8,13,78,8,13,105,8,13,132,8,13,42,16,13,72,16,13,103,16,13,
134,16,13,164,16,13,195,16,13,226,16,13,0,17,13,36,32,13,67,32,13,98,32,13,129,32,13,160,32,13,48,64,13,80,64,13,112,64,13,144,64,13,175,64,13,205,64,13,
236,64,13,9,65,13,39,65,13,70,65,13,99,65,13,16,66,13,45,66,13,73,66,13,100,66,13,129,66,13,16,68,13,45,68,13,73,68,13,100,68,13,129,68,13,16,70,13,
45,70,13,73,70,13,100,70,13,129,70,13,16,72,13,45,72,13,73,72,13,100,72,13,129,72,13,37,80,13,68,80,13,99,80,13,130,80,13,161,80,13,192,80,13,48,0,14,
80,0,14,112,0,14,144,0,14,176,0,14,208,0,14,240,0,14,16,1,14,48,1,14,80,1,14,112,1,14,48,16,14,80,16,14,112,16,14,144,16,14,176,16,14,208,16,14,
240,16,14,12,17,14,39,17,14,68,17,14,48,32,14,80,32,14,112,32,14,144,32,14,174,32,14,203,32,14,233,32,14,5,33,14,34,33,14,64,33,14,48,64,14,80,64,14,
112,64,14,144,64,14,176,64,14,208,64,14,240,64,14,16,65,14,48,65,14,77,65,14,103,65,14,16,72,14,48,72,14,80,72,14,106,72,14,131,72,14,48,80,14,79,80,14,
109,80,14,139,80,14,169,80,14,198,80,14,228,80,14,2,81,14,32,81,14,41,96,14,71,96,14,102,96,14,133,96,14,163,96,14,193,96,14,224,96,14,48,128,14,80,128,14,
112,128,14,144,128,14,176,128,14,208,128,14,240,128,14,16,129,14,48,129,14,77,129,14,103,129,14,16,130,14,48,130,14,80,130,14,106,130,14,131,130,14,16,132,14,48,132,14,
80,132,14,106,132,14,131,132,14,16,134,14,48,134,14,80,134,14,106,134,14,131,134,14,48,144,14,79,144,14,109,144,14,139,144,14,169,144,14,198,144,14,228,144,14,2,145,14,
32,145,14,41,160,14,71,160,14,102,160,14,133,160,14,163,160,14,193,160,14,224,160,14,48,192,14,80,192,14,112,192,14,144,192,14,176,192,14,208,192,14,239,192,14,11,193,14,
40,193,14,70,193,14,98,193,14,16,194,14,48,194,14,74,194,14,100,194,14,16,196,14,48,196,14,74,196,14,100,196,14,16,198,14,48,198,14,74,198,14,100,198,14,41,208,14,
71,208,14,102,208,14,133,208,14,163,208,14,193,208,14,224,208,14,35,224,14,66,224,14,97,224,14,128,224,14,48,0,15,80,0,15,112,0,15,144,0,15,176,0,15,208,0,15,
239,0,15,11,1,15,40,1,15,70,1,15,98,1,15,16,2,15,48,2,15,74,2,15,100,2,15,16,4,15,48,4,15,74,4,15,100,4,15,16,6,15,48,6,15,74,6,15,
100,6,15,16,8,15,48,8,15,74,8,15,100,8,15,41,16,15,71,16,15,102,16,15,133,16,15,163,16,15,193,16,15,224,16,15,35,32,15,66,32,15,97,32,15,128,32,15,
48,64,15,80,64,15,112,64,15,144,64,15,173,64,15,203,64,15,233,64,15,6,65,15,36,65,15,66,65,15,96,65,15,16,66,15,43,66,15,70,66,15,97,66,15,16,68,15,
43,68,15,70,68,15,97,68,15,16,70,15,43,70,15,70,70,15,97,70,15,16,72,15,43,72,15,70,72,15,97,72,15,37,80,15,67,80,15,98,80,15,129,80,15,160,80,15,
48,0,16,80,0,16,112,0,16,144,0,16,176,0,16,208,0,16,240,0,16,16,1,16,48,1,16,80,1,16,48,16,16,80,16,16,112,16,16,144,16,16,176,16,16,208,16,16,
236,16,16,6,17,16,33,17,16,48,32,16,80,32,16,112,32,16,143,32,16,171,32,16,200,32,16,229,32,16,1,33,16,48,64,16,80,64,16,112,64,16,144,64,16,176,64,16,
208,64,16,240,64,16,15,65,16,42,65,16,70,65,16,16,72,16,48,72,16,77,72,16,99,72,16,48,80,16,78,80,16,107,80,16,137,80,16,166,80,16,196,80,16,226,80,16,
40,96,16,70,96,16,101,96,16,131,96,16,161,96,16,192,96,16,48,128,16,80,128,16,112,128,16,144,128,16,176,128,16,208,128,16,240,128,16,15,129,16,42,129,16,70,129,16,
16,130,16,48,130,16,77,130,16,99,130,16,16,132,16,48,132,16,77,132,16,99,132,16,16,134,16,48,134,16,77,134,16,99,134,16,48,144,16,78,144,16,107,144,16,137,144,16,
166,144,16,196,144,16,226,144,16,40,160,16,70,160,16,101,160,16,131,160,16,161,160,16,192,160,16,48,192,16,80,192,16,112,192,16,144,192,16,176,192,16,206,192,16,235,192,16,
7,193,16,36,193,16,65,193,16,16,194,16,46,194,16,70,194,16,16,196,16,46,196,16,70,196,16,16,198,16,46,198,16,70,198,16,40,208,16,70,208,16,101,208,16,131,208,16,
161,208,16,192,208,16,35,224,16,65,224,16,96,224,16,128,224,16,48,0,17,80,0,17,112,0,17,144,0,17,176,0,17,206,0,17,235,0,17,7,1,17,36,1,17,65,1,17,
16,2,17,46,2,17,70,2,17,16,4,17,46,4,17,70,4,17,16,6,17,46,6,17,70,6,17,16,8,17,46,8,17,70,8,17,40,16,17,70,16,17,101,16,17,131,16,17,
161,16,17,192,16,17,35,32,17,65,32,17,96,32,17,128,32,17,48,64,17,80,64,17,112,64,17,142,64,17,171,64,17,200,64,17,230,64,17,3,65,17,33,65,17,16,66,17,
41,66,17,67,66,17,16,68,17,41,68,17,67,68,17,16,70,17,41,70,17,67,70,17,16,72,17,41,72,17,67,72,17,36,80,17,67,80,17,97,80,17,128,80,17,48,0,18,
80,0,18,112,0,18,144,0,18,176,0,18,208,0,18,240,0,18,16,1,18,46,1,18,48,16,18,80,16,18,112,16,18,144,16,18,176,16,18,202,16,18,230,16,18,0,17,18,
48,32,18,80,32,18,111,32,18,140,32,18,168,32,18,196,32,18,225,32,18,48,64,18,80,64,18,112,64,18,144,64,18,176,64,18,208,64,18,239,64,18,9,65,18,35,65,18,
16,72,18,48,72,18,71,72,18,47,80,18,76,80,18,105,80,18,135,80,18,164,80,18,193,80,18,39,96,18,69,96,18,99,96,18,130,96,18,160,96,18,48,128,18,80,128,18,
112,128,18,144,128,18,176,128,18,208,128,18,239,128,18,9,129,18,35,129,18,16,130,18,48,130,18,71,130,18,16,132,18,48,132,18,71,132,18,16,134,18,48,134,18,71,134,18,
47,144,18,76,144,18,105,144,18,135,144,18,164,144,18,193,144,18,39,160,18,69,160,18,99,160,18,130,160,18,160,160,18,48,192,18,80,192,18,112,192,18,144,192,18,174,192,18,
202,192,18,231,192,18,3,193,18,16,194,18,43,194,18,66,194,18,16,196,18,43,196,18,66,196,18,16,198,18,43,198,18,66,198,18,39,208,18,69,208,18,99,208,18,130,208,18,
160,208,18,34,224,18,65,224,18,96,224,18,48,0,19,80,0,19,112,0,19,144,0,19,174,0,19,202,0,19,231,0,19,3,1,19,16,2,19,43,2,19,66,2,19,16,4,19,
43,4,19,66,4,19,16,6,19,43,6,19,66,6,19,16,8,19,43,8,19,66,8,19,39,16,19,69,16,19,99,16,19,130,16,19,160,16,19,34,32,19,65,32,19,96,32,19,
48,64,19,80,64,19,110,64,19,140,64,19,169,64,19,198,64,19,227,64,19,0,65,19,16,66,19,38,66,19,64,66,19,16,68,19,38,68,19,64,68,19,16,70,19,38,70,19,
64,70,19,16,72,19,38,72,19,64,72,19,35,80,19,66,80,19,96,80,19,16,0,20,48,0,20,80,0,20,112,0,20,144,0,20,176,0,20,208,0,20,240,0,20,14,1,20,
16,16,20,48,16,20,80,16,20,112,16,20,144,16,20,172,16,20,198,16,20,225,16,20,16,32,20,48,32,20,80,32,20,109,32,20,138,32,20,165,32,20,193,32,20,16,64,20,
48,64,20,80,64,20,112,64,20,144,64,20,176,64,20,207,64,20,234,64,20,3,65,20,16,72,20,48,72,20,65,72,20,16,80,20,46,80,20,75,80,20,104,80,20,133,80,20,
162,80,20,10,96,20,39,96,20,68,96,20,98,96,20,129,96,20,16,128,20,48,128,20,80,128,20,112,128,20,144,128,20,176,128,20,207,128,20,234,128,20,3,129,20,16,130,20,
48,130,20,65,130,20,16,132,20,48,132,20,65,132,20,16,134,20,48,134,20,65,134,20,16,144,20,46,144,20,75,144,20,104,144,20,133,144,20,162,144,20,10,160,20,39,160,20,
68,160,20,98,160,20,129,160,20,16,192,20,48,192,20,80,192,20,112,192,20,144,192,20,171,192,20,199,192,20,228,192,20,16,194,20,40,194,20,16,196,20,40,196,20,16,198,20,
40,198,20,10,208,20,39,208,20,68,208,20,98,208,20,129,208,20,4,224,20,34,224,20,64,224,20,16,0,21,48,0,21,80,0,21,112,0,21,144,0,21,171,0,21,199,0,21,
228,0,21,16,2,21,40,2,21,16,4,21,40,4,21,16,6,21,40,6,21,16,8,21,40,8,21,10,16,21,39,16,21,68,16,21,98,16,21,129,16,21,4,32,21,34,32,21,
64,32,21,16,64,21,48,64,21,79,64,21,108,64,21,138,64,21,166,64,21,195,64,21,225,64,21,15,66,21,36,66,21,15,68,21,36,68,21,15,70,21,36,70,21,15,72,21,
36,72,21,6,80,21,35,80,21,65,80,21,96,80,21,16,1,22,48,1,22,80,1,22,112,1,22,16,17,22,48,17,22,80,17,22,112,17,22,16,33,22,48,33,22,80,33,22,
112,33,22,16,65,22,48,65,22,80,65,22,112,65,22,80,72,22,112,72,22,144,72,22,176,72,22,208,72,22,240,72,22,16,73,22,48,73,22,80,73,22,112,73,22,16,81,22,
48,81,22,80,81,22,112,81,22,10,97,22,42,97,22,73,97,22,105,97,22,16,129,22,48,129,22,80,129,22,112,129,22,80,130,22,112,130,22,144,130,22,176,130,22,208,130,22,
240,130,22,16,131,22,48,131,22,80,131,22,112,131,22,80,132,22,112,132,22,144,132,22,176,132,22,208,132,22,240,132,22,16,133,22,48,133,22,80,133,22,112,133,22,80,134,22,
112,134,22,144,134,22,176,134,22,208,134,22,240,134,22,16,135,22,48,135,22,80,135,22,112,135,22,16,145,22,48,145,22,80,145,22,112,145,22,10,161,22,42,161,22,73,161,22,
105,161,22,16,193,22,48,193,22,80,193,22,112,193,22,80,194,22,112,194,22,144,194,22,176,194,22,208,194,22,240,194,22,16,195,22,48,195,22,80,195,22,112,195,22,80,196,22,
112,196,22,144,196,22,176,196,22,208,196,22,240,196,22,16,197,22,48,197,22,80,197,22,112,197,22,80,198,22,112,198,22,144,198,22,176,198,22,208,198,22,240,198,22,16,199,22,
48,199,22,80,199,22,112,199,22,10,209,22,42,209,22,73,209,22,105,209,22,4,225,22,36,225,22,67,225,22,99,225,22,16,1,23,48,1,23,80,1,23,112,1,23,80,2,23,
112,2,23,144,2,23,176,2,23,208,2,23,240,2,23,16,3,23,48,3,23,80,3,23,112,3,23,80,4,23,112,4,23,144,4,23,176,4,23,208,4,23,240,4,23,16,5,23,
48,5,23,80,5,23,112,5,23,80,6,23,112,6,23,144,6,23,176,6,23,208,6,23,240,6,23,16,7,23,48,7,23,80,7,23,112,7,23,80,8,23,112,8,23,144,8,23,
176,8,23,208,8,23,240,8,23,16,9,23,48,9,23,80,9,23,112,9,23,10,17,23,42,17,23,73,17,23,105,17,23,4,33,23,36,33,23,67,33,23,99,33,23,16,65,23,
48,65,23,80,65,23,112,65,23,80,66,23,112,66,23,144,66,23,176,66,23,208,66,23,240,66,23,15,67,23,45,67,23,76,67,23,106,67,23,80,68,23,112,68,23,144,68,23,
176,68,23,208,68,23,240,68,23,15,69,23,45,69,23,76,69,23,106,69,23,80,70,23,112,70,23,144,70,23,176,70,23,208,70,23,240,70,23,15,71,23,45,71,23,76,71,23,
106,71,23,80,72,23,112,72,23,144,72,23,176,72,23,208,72,23,240,72,23,15,73,23,45,73,23,76,73,23,106,73,23,6,81,23,37,81,23,69,81,23,100,81,23,144,0,24,
176,0,24,208,0,24,240,0,24,16,1,24,48,1,24,80,1,24,112,1,24,144,16,24,176,16,24,208,16,24,240,16,24,16,17,24,48,17,24,80,17,24,112,17,24,144,32,24,
176,32,24,208,32,24,240,32,24,16,33,24,48,33,24,80,33,24,112,33,24,144,64,24,176,64,24,208,64,24,240,64,24,16,65,24,48,65,24,80,65,24,112,65,24,48,72,24,
80,72,24,112,72,24,144,72,24,176,72,24,208,72,24,240,72,24,16,73,24,47,73,24,75,73,24,102,73,24,144,80,24,176,80,24,208,80,24,240,80,24,15,81,24,46,81,24,
77,81,24,108,81,24,138,96,24,170,96,24,201,96,24,232,96,24,7,97,24,39,97,24,70,97,24,101,97,24,144,128,24,176,128,24,208,128,24,240,128,24,16,129,24,48,129,24,
80,129,24,112,129,24,48,130,24,80,130,24,112,130,24,144,130,24,176,130,24,208,130,24,240,130,24,16,131,24,47,131,24,75,131,24,102,131,24,48,132,24,80,132,24,112,132,24,
144,132,24,176,132,24,208,132,24,240,132,24,16,133,24,47,133,24,75,133,24,102,133,24,48,134,24,80,134,24,112,134,24,144,134,24,176,134,24,208,134,24,240,134,24,16,135,24,
47,135,24,75,135,24,102,135,24,144,144,24,176,144,24,208,144,24,240,144,24,15,145,24,46,145,24,77,145,24,108,145,24,138,160,24,170,160,24,201,160,24,232,160,24,7,161,24,
39,161,24,70,161,24,101,161,24,144,192,24,176,192,24,208,192,24,240,192,24,16,193,24,48,193,24,80,193,24,112,193,24,48,194,24,80,194,24,112,194,24,144,194,24,176,194,24,
208,194,24,238,194,24,10,195,24,39,195,24,69,195,24,97,195,24,48,196,24,80,196,24,112,196,24,144,196,24,176,196,24,208,196,24,238,196,24,10,197,24,39,197,24,69,197,24,
97,197,24,48,198,24,80,198,24,112,198,24,144,198,24,176,198,24,208,198,24,238,198,24,10,199,24,39,199,24,69,199,24,97,199,24,138,208,24,170,208,24,201,208,24,232,208,24,
7,209,24,39,209,24,70,209,24,101,209,24,132,224,24,164,224,24,195,224,24,227,224,24,2,225,24,34,225,24,65,225,24,97,225,24,144,0,25,176,0,25,208,0,25,240,0,25,
16,1,25,48,1,25,80,1,25,112,1,25,48,2,25,80,2,25,112,2,25,144,2,25,176,2,25,208,2,25,238,2,25,10,3,25,39,3,25,69,3,25,97,3,25,48,4,25,
80,4,25,112,4,25,144,4,25,176,4,25,208,4,25,238,4,25,10,5,25,39,5,25,69,5,25,97,5,25,48,6,25,80,6,25,112,6,25,144,6,25,176,6,25,208,6,25,
238,6,25,10,7,25,39,7,25,69,7,25,97,7,25,48,8,25,80,8,25,112,8,25,144,8,25,176,8,25,208,8,25,238,8,25,10,9,25,39,9,25,69,9,25,97,9,25,
138,16,25,170,16,25,201,16,25,232,16,25,7,17,25,39,17,25,70,17,25,101,17,25,132,32,25,164,32,25,195,32,25,227,32,25,2,33,25,34,33,25,65,33,25,97,33,25,
144,64,25,176,64,25,208,64,25,240,64,25,16,65,25,48,65,25,80,65,25,112,65,25,48,66,25,80,66,25,112,66,25,143,66,25,172,66,25,202,66,25,232,66,25,6,67,25,
35,67,25,65,67,25,48,68,25,80,68,25,112,68,25,143,68,25,172,68,25,202,68,25,232,68,25,6,69,25,35,69,25,65,69,25,48,70,25,80,70,25,112,70,25,143,70,25,
172,70,25,202,70,25,232,70,25,6,71,25,35,71,25,65,71,25,48,72,25,80,72,25,112,72,25,143,72,25,172,72,25,202,72,25,232,72,25,6,73,25,35,73,25,65,73,25,
134,80,25,165,80,25,196,80,25,228,80,25,3,81,25,35,81,25,66,81,25,98,81,25,80,0,26,112,0,26,144,0,26,176,0,26,208,0,26,240,0,26,16,1,26,48,1,26,
80,1,26,112,1,26,80,16,26,112,16,26,144,16,26,176,16,26,208,16,26,240,16,26,16,17,26,48,17,26,80,17,26,112,17,26,80,32,26,112,32,26,144,32,26,176,32,26,
208,32,26,240,32,26,16,33,26,47,33,26,77,33,26,107,33,26,80,64,26,112,64,26,144,64,26,176,64,26,208,64,26,240,64,26,16,65,26,48,65,26,80,65,26,112,65,26,
16,72,26,48,72,26,80,72,26,112,72,26,144,72,26,176,72,26,205,72,26,232,72,26,1,73,26,80,80,26,112,80,26,144,80,26,175,80,26,206,80,26,236,80,26,11,81,26,
41,81,26,72,81,26,102,81,26,74,96,26,105,96,26,136,96,26,167,96,26,198,96,26,229,96,26,4,97,26,35,97,26,66,97,26,97,97,26,80,128,26,112,128,26,144,128,26,
176,128,26,208,128,26,240,128,26,16,129,26,48,129,26,80,129,26,112,129,26,16,130,26,48,130,26,80,130,26,112,130,26,144,130,26,176,130,26,205,130,26,232,130,26,1,131,26,
16,132,26,48,132,26,80,132,26,112,132,26,144,132,26,176,132,26,205,132,26,232,132,26,1,133,26,16,134,26,48,134,26,80,134,26,112,134,26,144,134,26,176,134,26,205,134,26,
232,134,26,1,135,26,80,144,26,112,144,26,144,144,26,175,144,26,206,144,26,236,144,26,11,145,26,41,145,26,72,145,26,102,145,26,74,160,26,105,160,26,136,160,26,167,160,26,
198,160,26,229,160,26,4,161,26,35,161,26,66,161,26,97,161,26,80,192,26,112,192,26,144,192,26,176,192,26,208,192,26,240,192,26,16,193,26,48,193,26,80,193,26,112,193,26,
16,194,26,48,194,26,80,194,26,112,194,26,143,194,26,170,194,26,198,194,26,227,194,26,16,196,26,48,196,26,80,196,26,112,196,26,143,196,26,170,196,26,198,196,26,227,196,26,
16,198,26,48,198,26,80,198,26,112,198,26,143,198,26,170,198,26,198,198,26,227,198,26,74,208,26,105,208,26,136,208,26,167,208,26,198,208,26,229,208,26,4,209,26,35,209,26,
66,209,26,97,209,26,68,224,26,99,224,26,131,224,26,162,224,26,193,224,26,225,224,26,0,225,26,32,225,26,80,0,27,112,0,27,144,0,27,176,0,27,208,0,27,240,0,27,
16,1,27,48,1,27,80,1,27,112,1,27,16,2,27,48,2,27,80,2,27,112,2,27,143,2,27,170,2,27,198,2,27,227,2,27,16,4,27,48,4,27,80,4,27,112,4,27,
143,4,27,170,4,27,198,4,27,227,4,27,16,6,27,48,6,27,80,6,27,112,6,27,143,6,27,170,6,27,198,6,27,227,6,27,16,8,27,48,8,27,80,8,27,112,8,27,
143,8,27,170,8,27,198,8,27,227,8,27,74,16,27,105,16,27,136,16,27,167,16,27,198,16,27,229,16,27,4,17,27,35,17,27,66,17,27,97,17,27,68,32,27,99,32,27,
131,32,27,162,32,27,193,32,27,225,32,27,0,33,27,32,33,27,80,64,27,112,64,27,144,64,27,176,64,27,208,64,27,240,64,27,15,65,27,46,65,27,76,65,27,107,65,27,
16,66,27,48,66,27,79,66,27,108,66,27,137,66,27,166,66,27,195,66,27,224,66,27,16,68,27,48,68,27,79,68,27,108,68,27,137,68,27,166,68,27,195,68,27,224,68,27,
16,70,27,48,70,27,79,70,27,108,70,27,137,70,27,166,70,27,195,70,27,224,70,27,16,72,27,48,72,27,79,72,27,108,72,27,137,72,27,166,72,27,195,72,27,224,72,27,
70,80,27,101,80,27,132,80,27,163,80,27,195,80,27,226,80,27,1,81,27,32,81,27,64,81,27,48,0,28,80,0,28,112,0,28,144,0,28,176,0,28,208,0,28,240,0,28,
16,1,28,48,1,28,80,1,28,112,1,28,48,16,28,80,16,28,112,16,28,144,16,28,176,16,28,208,16,28,240,16,28,16,17,28,48,17,28,78,17,28,106,17,28,48,32,28,
80,32,28,112,32,28,144,32,28,176,32,28,208,32,28,238,32,28,11,33,28,41,33,28,71,33,28,100,33,28,48,64,28,80,64,28,112,64,28,144,64,28,176,64,28,208,64,28,
240,64,28,16,65,28,48,65,28,80,65,28,112,65,28,16,72,28,48,72,28,80,72,28,112,72,28,143,72,28,166,72,28,48,80,28,80,80,28,112,80,28,142,80,28,172,80,28,
202,80,28,233,80,28,6,81,28,36,81,28,67,81,28,97,81,28,42,96,28,73,96,28,104,96,28,135,96,28,165,96,28,196,96,28,227,96,28,1,97,28,32,97,28,48,128,28,
80,128,28,112,128,28,144,128,28,176,128,28,208,128,28,240,128,28,16,129,28,48,129,28,80,129,28,112,129,28,16,130,28,48,130,28,80,130,28,112,130,28,143,130,28,166,130,28,
16,132,28,48,132,28,80,132,28,112,132,28,143,132,28,166,132,28,16,134,28,48,134,28,80,134,28,112,134,28,143,134,28,166,134,28,48,144,28,80,144,28,112,144,28,142,144,28,
172,144,28,202,144,28,233,144,28,6,145,28,36,145,28,67,145,28,97,145,28,42,160,28,73,160,28,104,160,28,135,160,28,165,160,28,196,160,28,227,160,28,1,161,28,32,161,28,
48,192,28,80,192,28,112,192,28,144,192,28,176,192,28,208,192,28,240,192,28,16,193,28,47,193,28,77,193,28,106,193,28,16,194,28,48,194,28,80,194,28,107,194,28,135,194,28,
161,194,28,16,196,28,48,196,28,80,196,28,107,196,28,135,196,28,161,196,28,16,198,28,48,198,28,80,198,28,107,198,28,135,198,28,161,198,28,42,208,28,73,208,28,104,208,28,
135,208,28,165,208,28,196,208,28,227,208,28,1,209,28,32,209,28,36,224,28,67,224,28,98,224,28,130,224,28,161,224,28,192,224,28,48,0,29,80,0,29,112,0,29,144,0,29,
176,0,29,208,0,29,240,0,29,16,1,29,47,1,29,77,1,29,106,1,29,16,2,29,48,2,29,80,2,29,107,2,29,135,2,29,161,2,29,16,4,29,48,4,29,80,4,29,
107,4,29,135,4,29,161,4,29,16,6,29,48,6,29,80,6,29,107,6,29,135,6,29,161,6,29,16,8,29,48,8,29,80,8,29,107,8,29,135,8,29,161,8,29,42,16,29,
73,16,29,104,16,29,135,16,29,165,16,29,196,16,29,227,16,29,1,17,29,32,17,29,36,32,29,67,32,29,98,32,29,130,32,29,161,32,29,192,32,29,48,64,29,80,64,29,
112,64,29,144,64,29,176,64,29,207,64,29,237,64,29,11,65,29,41,65,29,71,65,29,101,65,29,16,66,29,47,66,29,74,66,29,102,66,29,131,66,29,16,68,29,47,68,29,
74,68,29,102,68,29,131,68,29,16,70,29,47,70,29,74,70,29,102,70,29,131,70,29,16,72,29,47,72,29,74,72,29,102,72,29,131,72,29,38,80,29,68,80,29,100,80,29,
131,80,29,162,80,29,193,80,29,224,80,29,48,0,30,80,0,30,112,0,30,144,0,30,176,0,30,208,0,30,240,0,30,16,1,30,48,1,30,80,1,30,112,1,30,48,16,30,
80,16,30,112,16,30,144,16,30,176,16,30,208,16,30,240,16,30,12,17,30,39,17,30,68,17,30,48,32,30,80,32,30,112,32,30,144,32,30,174,32,30,203,32,30,233,32,30,
5,33,30,34,33,30,64,33,30,48,64,30,80,64,30,112,64,30,144,64,30,176,64,30,208,64,30,240,64,30,16,65,30,48,65,30,77,65,30,103,65,30,16,72,30,48,72,30,
80,72,30,106,72,30,131,72,30,48,80,30,79,80,30,109,80,30,139,80,30,169,80,30,198,80,30,228,80,30,2,81,30,32,81,30,41,96,30,71,96,30,102,96,30,133,96,30,
163,96,30,193,96,30,224,96,30,48,128,30,80,128,30,112,128,30,144,128,30,176,128,30,208,128,30,240,128,30,16,129,30,48,129,30,77,129,30,103,129,30,16,130,30,48,130,30,
80,130,30,106,130,30,131,130,30,16,132,30,48,132,30,80,132,30,106,132,30,131,132,30,16,134,30,48,134,30,80,134,30,106,134,30,131,134,30,48,144,30,79,144,30,109,144,30,
139,144,30,169,144,30,198,144,30,228,144,30,2,145,30,32,145,30,41,160,30,71,160,30,102,160,30,133,160,30,163,160,30,193,160,30,224,160,30,48,192,30,80,192,30,112,192,30,
144,192,30,176,192,30,208,192,30,239,192,30,11,193,30,40,193,30,70,193,30,98,193,30,16,194,30,48,194,30,74,194,30,100,194,30,16,196,30,48,196,30,74,196,30,100,196,30,
16,198,30,48,198,30,74,198,30,100,198,30,41,208,30,71,208,30,102,208,30,133,208,30,163,208,30,193,208,30,224,208,30,35,224,30,66,224,30,97,224,30,128,224,30,48,0,31,
80,0,31,112,0,31,144,0,31,176,0,31,208,0,31,239,0,31,11,1,31,40,1,31,70,1,31,98,1,31,16,2,31,48,2,31,74,2,31,100,2,31,16,4,31,48,4,31,
74,4,31,100,4,31,16,6,31,48,6,31,74,6,31,100,6,31,16,8,31,48,8,31,74,8,31,100,8,31,41,16,31,71,16,31,102,16,31,133,16,31,163,16,31,193,16,31,
224,16,31,35,32,31,66,32,31,97,32,31,128,32,31,48,64,31,80,64,31,112,64,31,144,64,31,173,64,31,203,64,31,233,64,31,6,65,31,36,65,31,66,65,31,96,65,31,
16,66,31,43,66,31,70,66,31,97,66,31,16,68,31,43,68,31,70,68,31,97,68,31,16,70,31,43,70,31,70,70,31,97,70,31,16,72,31,43,72,31,70,72,31,97,72,31,
37,80,31,67,80,31,98,80,31,129,80,31,160,80,31,48,0,32,80,0,32,112,0,32,144,0,32,176,0,32,208,0,32,240,0,32,16,1,32,48,1,32,48,16,32,80,16,32,
112,16,32,144,16,32,176,16,32,205,16,32,233,16,32,3,17,32,48,32,32,80,32,32,112,32,32,142,32,32,170,32,32,198,32,32,227,32,32,48,64,32,80,64,32,112,64,32,
144,64,32,176,64,32,208,64,32,240,64,32,12,65,32,39,65,32,16,72,32,48,72,32,74,72,32,48,80,32,77,80,32,106,80,32,136,80,32,165,80,32,195,80,32,224,80,32,
40,96,32,70,96,32,100,96,32,131,96,32,161,96,32,48,128,32,80,128,32,112,128,32,144,128,32,176,128,32,208,128,32,240,128,32,12,129,32,39,129,32,16,130,32,48,130,32,
74,130,32,16,132,32,48,132,32,74,132,32,16,134,32,48,134,32,74,134,32,48,144,32,77,144,32,106,144,32,136,144,32,165,144,32,195,144,32,224,144,32,40,160,32,70,160,32,
100,160,32,131,160,32,161,160,32,48,192,32,80,192,32,112,192,32,144,192,32,176,192,32,204,192,32,233,192,32,5,193,32,34,193,32,16,194,32,44,194,32,68,194,32,16,196,32,
44,196,32,68,196,32,16,198,32,44,198,32,68,198,32,40,208,32,70,208,32,100,208,32,131,208,32,161,208,32,35,224,32,65,224,32,96,224,32,48,0,33,80,0,33,112,0,33,
144,0,33,176,0,33,204,0,33,233,0,33,5,1,33,34,1,33,16,2,33,44,2,33,68,2,33,16,4,33,44,4,33,68,4,33,16,6,33,44,6,33,68,6,33,16,8,33,
44,8,33,68,8,33,40,16,33,70,16,33,100,16,33,131,16,33,161,16,33,35,32,33,65,32,33,96,32,33,48,64,33,80,64,33,111,64,33,141,64,33,170,64,33,199,64,33,
229,64,33,2,65,33,16,66,33,39,66,33,65,66,33,16,68,33,39,68,33,65,68,33,16,70,33,39,70,33,65,70,33,16,72,33,39,72,33,65,72,33,36,80,33,66,80,33,
97,80,33,128,80,33,16,0,34,48,0,34,80,0,34,112,0,34,144,0,34,176,0,34,208,0,34,240,0,34,14,1,34,16,16,34,48,16,34,80,16,34,112,16,34,144,16,34,
172,16,34,198,16,34,225,16,34,16,32,34,48,32,34,80,32,34,109,32,34,138,32,34,165,32,34,193,32,34,16,64,34,48,64,34,80,64,34,112,64,34,144,64,34,176,64,34,
207,64,34,234,64,34,3,65,34,16,72,34,48,72,34,65,72,34,16,80,34,46,80,34,75,80,34,104,80,34,133,80,34,162,80,34,10,96,34,39,96,34,68,96,34,98,96,34,
129,96,34,16,128,34,48,128,34,80,128,34,112,128,34,144,128,34,176,128,34,207,128,34,234,128,34,3,129,34,16,130,34,48,130,34,65,130,34,16,132,34,48,132,34,65,132,34,
16,134,34,48,134,34,65,134,34,16,144,34,46,144,34,75,144,34,104,144,34,133,144,34,162,144,34,10,160,34,39,160,34,68,160,34,98,160,34,129,160,34,16,192,34,48,192,34,
80,192,34,112,192,34,144,192,34,171,192,34,199,192,34,228,192,34,16,194,34,40,194,34,16,196,34,40,196,34,16,198,34,40,198,34,10,208,34,39,208,34,68,208,34,98,208,34,
129,208,34,4,224,34,34,224,34,64,224,34,16,0,35,48,0,35,80,0,35,112,0,35,144,0,35,171,0,35,199,0,35,228,0,35,16,2,35,40,2,35,16,4,35,40,4,35,
16,6,35,40,6,35,16,8,35,40,8,35,10,16,35,39,16,35,68,16,35,98,16,35,129,16,35,4,32,35,34,32,35,64,32,35,16,64,35,48,64,35,79,64,35,108,64,35,
138,64,35,166,64,35,195,64,35,225,64,35,15,66,35,36,66,35,15,68,35,36,68,35,15,70,35,36,70,35,15,72,35,36,72,35,6,80,35,35,80,35,65,80,35,96,80,35,
16,0,36,48,0,36,80,0,36,112,0,36,144,0,36,176,0,36,208,0,36,16,16,36,48,16,36,80,16,36,112,16,36,141,16,36,165,16,36,16,32,36,48,32,36,78,32,36,
106,32,36,134,32,36,161,32,36,16,64,36,48,64,36,80,64,36,112,64,36,144,64,36,174,64,36,199,64,36,16,72,36,40,72,36,16,80,36,44,80,36,73,80,36,101,80,36,
130,80,36,10,96,36,37,96,36,67,96,36,97,96,36,16,128,36,48,128,36,80,128,36,112,128,36,144,128,36,174,128,36,199,128,36,16,130,36,40,130,36,16,132,36,40,132,36,
16,134,36,40,134,36,16,144,36,44,144,36,73,144,36,101,144,36,130,144,36,10,160,36,37,160,36,67,160,36,97,160,36,16,192,36,48,192,36,80,192,36,112,192,36,140,192,36,
167,192,36,194,192,36,16,194,36,35,194,36,16,196,36,35,196,36,16,198,36,35,198,36,10,208,36,37,208,36,67,208,36,97,208,36,4,224,36,33,224,36,16,0,37,48,0,37,
80,0,37,112,0,37,140,0,37,167,0,37,194,0,37,16,2,37,35,2,37,16,4,37,35,4,37,16,6,37,35,6,37,16,8,37,35,8,37,10,16,37,37,16,37,67,16,37,
97,16,37,4,32,37,33,32,37,16,64,37,48,64,37,77,64,37,106,64,37,135,64,37,163,64,37,192,64,37,12,66,37,32,66,37,12,68,37,32,68,37,12,70,37,32,70,37,
12,72,37,32,72,37,5,80,37,34,80,37,64,80,37,16,0,38,48,0,38,80,0,38,112,0,38,144,0,38,176,0,38,16,16,38,48,16,38,80,16,38,109,16,38,135,16,38,
16,32,38,48,32,38,75,32,38,102,32,38,130,32,38,16,64,38,48,64,38,80,64,38,112,64,38,144,64,38,167,64,38,16,72,38,33,72,38,16,80,38,43,80,38,70,80,38,
99,80,38,128,80,38,9,96,38,36,96,38,65,96,38,16,128,38,48,128,38,80,128,38,112,128,38,144,128,38,167,128,38,16,130,38,33,130,38,16,132,38,33,132,38,16,134,38,
33,134,38,16,144,38,43,144,38,70,144,38,99,144,38,128,144,38,9,160,38,36,160,38,65,160,38,16,192,38,48,192,38,80,192,38,108,192,38,136,192,38,162,192,38,16,194,38,
16,196,38,16,198,38,9,208,38,36,208,38,65,208,38,3,224,38,32,224,38,16,0,39,48,0,39,80,0,39,108,0,39,136,0,39,162,0,39,16,2,39,16,4,39,16,6,39,
16,8,39,9,16,39,36,16,39,65,16,39,3,32,39,32,32,39,16,64,39,47,64,39,75,64,39,103,64,39,132,64,39,160,64,39,10,66,39,10,68,39,10,70,39,10,72,39,
4,80,39,33,80,39,16,0,40,48,0,40,80,0,40,112,0,40,144,0,40,16,16,40,48,16,40,80,16,40,104,16,40,129,16,40,16,32,40,47,32,40,72,32,40,99,32,40,
16,64,40,48,64,40,80,64,40,112,64,40,138,64,40,16,80,40,41,80,40,68,80,40,96,80,40,8,96,40,35,96,40,64,96,40,16,128,40,48,128,40,80,128,40,112,128,40,
138,128,40,16,144,40,41,144,40,68,144,40,96,144,40,8,160,40,35,160,40,64,160,40,16,192,40,48,192,40,78,192,40,105,192,40,132,192,40,8,208,40,35,208,40,64,208,40,
3,224,40,16,0,41,48,0,41,78,0,41,105,0,41,132,0,41,8,16,41,35,16,41,64,16,41,3,32,41,16,64,41,45,64,41,73,64,41,100,64,41,129,64,41,4,80,41,
32,80,41,16,0,42,48,0,42,80,0,42,112,0,42,144,0,42,16,16,42,48,16,42,76,16,42,99,16,42,16,32,42,44,32,42,69,32,42,16,64,42,48,64,42,80,64,42,
108,64,42,132,64,42,15,80,42,39,80,42,66,80,42,7,96,42,34,96,42,16,128,42,48,128,42,80,128,42,108,128,42,132,128,42,15,144,42,39,144,42,66,144,42,7,160,42,
34,160,42,16,192,42,48,192,42,75,192,42,101,192,42,128,192,42,7,208,42,34,208,42,2,224,42,16,0,43,48,0,43,75,0,43,101,0,43,128,0,43,7,16,43,34,16,43,
2,32,43,16,64,43,44,64,43,70,64,43,98,64,43,3,80,43,176,0,44,208,0,44,240,0,44,16,1,44,48,1,44,80,1,44,112,1,44,176,16,44,208,16,44,240,16,44,
16,17,44,48,17,44,80,17,44,112,17,44,176,32,44,208,32,44,240,32,44,16,33,44,48,33,44,80,33,44,112,33,44,176,64,44,208,64,44,240,64,44,16,65,44,48,65,44,
80,65,44,112,65,44,48,72,44,80,72,44,112,72,44,144,72,44,176,72,44,208,72,44,240,72,44,16,73,44,48,73,44,80,73,44,109,73,44,176,80,44,208,80,44,240,80,44,
16,81,44,48,81,44,79,81,44,110,81,44,170,96,44,202,96,44,233,96,44,8,97,44,40,97,44,71,97,44,102,97,44,176,128,44,208,128,44,240,128,44,16,129,44,48,129,44,
80,129,44,112,129,44,48,130,44,80,130,44,112,130,44,144,130,44,176,130,44,208,130,44,240,130,44,16,131,44,48,131,44,80,131,44,109,131,44,48,132,44,80,132,44,112,132,44,
144,132,44,176,132,44,208,132,44,240,132,44,16,133,44,48,133,44,80,133,44,109,133,44,48,134,44,80,134,44,112,134,44,144,134,44,176,134,44,208,134,44,240,134,44,16,135,44,
48,135,44,80,135,44,109,135,44,176,144,44,208,144,44,240,144,44,16,145,44,48,145,44,79,145,44,110,145,44,170,160,44,202,160,44,233,160,44,8,161,44,40,161,44,71,161,44,
102,161,44,176,192,44,208,192,44,240,192,44,16,193,44,48,193,44,80,193,44,112,193,44,48,194,44,80,194,44,112,194,44,144,194,44,176,194,44,208,194,44,240,194,44,14,195,44,
43,195,44,73,195,44,102,195,44,48,196,44,80,196,44,112,196,44,144,196,44,176,196,44,208,196,44,240,196,44,14,197,44,43,197,44,73,197,44,102,197,44,48,198,44,80,198,44,
112,198,44,144,198,44,176,198,44,208,198,44,240,198,44,14,199,44,43,199,44,73,199,44,102,199,44,170,208,44,202,208,44,233,208,44,8,209,44,40,209,44,71,209,44,102,209,44,
164,224,44,196,224,44,227,224,44,3,225,44,34,225,44,66,225,44,97,225,44,176,0,45,208,0,45,240,0,45,16,1,45,48,1,45,80,1,45,112,1,45,48,2,45,80,2,45,
112,2,45,144,2,45,176,2,45,208,2,45,240,2,45,14,3,45,43,3,45,73,3,45,102,3,45,48,4,45,80,4,45,112,4,45,144,4,45,176,4,45,208,4,45,240,4,45,
14,5,45,43,5,45,73,5,45,102,5,45,48,6,45,80,6,45,112,6,45,144,6,45,176,6,45,208,6,45,240,6,45,14,7,45,43,7,45,73,7,45,102,7,45,48,8,45,
80,8,45,112,8,45,144,8,45,176,8,45,208,8,45,240,8,45,14,9,45,43,9,45,73,9,45,102,9,45,170,16,45,202,16,45,233,16,45,8,17,45,40,17,45,71,17,45,
102,17,45,164,32,45,196,32,45,227,32,45,3,33,45,34,33,45,66,33,45,97,33,45,176,64,45,208,64,45,240,64,45,16,65,45,48,65,45,80,65,45,112,65,45,48,66,45,
80,66,45,112,66,45,144,66,45,175,66,45,204,66,45,235,66,45,9,67,45,38,67,45,69,67,45,99,67,45,48,68,45,80,68,45,112,68,45,144,68,45,175,68,45,204,68,45,
235,68,45,9,69,45,38,69,45,69,69,45,99,69,45,48,70,45,80,70,45,112,70,45,144,70,45,175,70,45,204,70,45,235,70,45,9,71,45,38,71,45,69,71,45,99,71,45,
48,72,45,80,72,45,112,72,45,144,72,45,175,72,45,204,72,45,235,72,45,9,73,45,38,73,45,69,73,45,99,73,45,166,80,45,197,80,45,229,80,45,4,81,45,36,81,45,
67,81,45,99,81,45,80,0,46,112,0,46,144,0,46,176,0,46,208,0,46,240,0,46,16,1,46,48,1,46,80,1,46,112,1,46,80,16,46,112,16,46,144,16,46,176,16,46,
208,16,46,240,16,46,16,17,46,48,17,46,80,17,46,112,17,46,80,32,46,112,32,46,144,32,46,176,32,46,208,32,46,240,32,46,16,33,46,47,33,46,77,33,46,107,33,46,
80,64,46,112,64,46,144,64,46,176,64,46,208,64,46,240,64,46,16,65,46,48,65,46,80,65,46,112,65,46,16,72,46,48,72,46,80,72,46,112,72,46,144,72,46,176,72,46,
205,72,46,232,72,46,1,73,46,80,80,46,112,80,46,144,80,46,175,80,46,206,80,46,236,80,46,11,81,46,41,81,46,72,81,46,102,81,46,74,96,46,105,96,46,136,96,46,
167,96,46,198,96,46,229,96,46,4,97,46,35,97,46,66,97,46,97,97,46,80,128,46,112,128,46,144,128,46,176,128,46,208,128,46,240,128,46,16,129,46,48,129,46,80,129,46,
112,129,46,16,130,46,48,130,46,80,130,46,112,130,46,144,130,46,176,130,46,205,130,46,232,130,46,1,131,46,16,132,46,48,132,46,80,132,46,112,132,46,144,132,46,176,132,46,
205,132,46,232,132,46,1,133,46,16,134,46,48,134,46,80,134,46,112,134,46,144,134,46,176,134,46,205,134,46,232,134,46,1,135,46,80,144,46,112,144,46,144,144,46,175,144,46,
206,144,46,236,144,46,11,145,46,41,145,46,72,145,46,102,145,46,74,160,46,105,160,46,136,160,46,167,160,46,198,160,46,229,160,46,4,161,46,35,161,46,66,161,46,97,161,46,
80,192,46,112,192,46,144,192,46,176,192,46,208,192,46,240,192,46,16,193,46,48,193,46,80,193,46,112,193,46,16,194,46,48,194,46,80,194,46,112,194,46,143,194,46,170,194,46,
198,194,46,227,194,46,16,196,46,48,196,46,80,196,46,112,196,46,143,196,46,170,196,46,198,196,46,227,196,46,16,198,46,48,198,46,80,198,46,112,198,46,143,198,46,170,198,46,
198,198,46,227,198,46,74,208,46,105,208,46,136,208,46,167,208,46,198,208,46,229,208,46,4,209,46,35,209,46,66,209,46,97,209,46,68,224,46,99,224,46,131,224,46,162,224,46,
193,224,46,225,224,46,0,225,46,32,225,46,80,0,47,112,0,47,144,0,47,176,0,47,208,0,47,240,0,47,16,1,47,48,1,47,80,1,47,112,1,47,16,2,47,48,2,47,
80,2,47,112,2,47,143,2,47,170,2,47,198,2,47,227,2,47,16,4,47,48,4,47,80,4,47,112,4,47,143,4,47,170,4,47,198,4,47,227,4,47,16,6,47,48,6,47,
80,6,47,112,6,47,143,6,47,170,6,47,198,6,47,227,6,47,16,8,47,48,8,47,80,8,47,112,8,47,143,8,47,170,8,47,198,8,47,227,8,47,74,16,47,105,16,47,
136,16,47,167,16,47,198,16,47,229,16,47,4,17,47,35,17,47,66,17,47,97,17,47,68,32,47,99,32,47,131,32,47,162,32,47,193,32,47,225,32,47,0,33,47,32,33,47,
80,64,47,112,64,47,144,64,47,176,64,47,208,64,47,240,64,47,15,65,47,46,65,47,76,65,47,107,65,47,16,66,47,48,66,47,79,66,47,108,66,47,137,66,47,166,66,47,
195,66,47,224,66,47,16,68,47,48,68,47,79,68,47,108,68,47,137,68,47,166,68,47,195,68,47,224,68,47,16,70,47,48,70,47,79,70,47,108,70,47,137,70,47,166,70,47,
195,70,47,224,70,47,16,72,47,48,72,47,79,72,47,108,72,47,137,72,47,166,72,47,195,72,47,224,72,47,70,80,47,101,80,47,132,80,47,163,80,47,195,80,47,226,80,47,
1,81,47,32,81,47,64,81,47,48,0,48,80,0,48,112,0,48,144,0,48,176,0,48,208,0,48,240,0,48,16,1,48,48,1,48,80,1,48,112,1,48,48,16,48,80,16,48,
112,16,48,144,16,48,176,16,48,208,16,48,240,16,48,16,17,48,45,17,48,74,17,48,102,17,48,48,32,48,80,32,48,112,32,48,144,32,48,176,32,48,206,32,48,236,32,48,
9,33,48,38,33,48,68,33,48,97,33,48,48,64,48,80,64,48,112,64,48,144,64,48,176,64,48,208,64,48,240,64,48,16,65,48,48,65,48,80,65,48,111,65,48,16,72,48,
48,72,48,80,72,48,112,72,48,138,72,48,161,72,48,48,80,48,80,80,48,111,80,48,141,80,48,171,80,48,201,80,48,231,80,48,5,81,48,35,81,48,65,81,48,42,96,48,
72,96,48,103,96,48,134,96,48,164,96,48,195,96,48,226,96,48,0,97,48,48,128,48,80,128,48,112,128,48,144,128,48,176,128,48,208,128,48,240,128,48,16,129,48,48,129,48,
80,129,48,111,129,48,16,130,48,48,130,48,80,130,48,112,130,48,138,130,48,161,130,48,16,132,48,48,132,48,80,132,48,112,132,48,138,132,48,161,132,48,16,134,48,48,134,48,
80,134,48,112,134,48,138,134,48,161,134,48,48,144,48,80,144,48,111,144,48,141,144,48,171,144,48,201,144,48,231,144,48,5,145,48,35,145,48,65,145,48,42,160,48,72,160,48,
103,160,48,134,160,48,164,160,48,195,160,48,226,160,48,0,161,48,48,192,48,80,192,48,112,192,48,144,192,48,176,192,48,208,192,48,240,192,48,15,193,48,44,193,48,74,193,48,
103,193,48,16,194,48,48,194,48,78,194,48,105,194,48,132,194,48,16,196,48,48,196,48,78,196,48,105,196,48,132,196,48,16,198,48,48,198,48,78,198,48,105,198,48,132,198,48,
42,208,48,72,208,48,103,208,48,134,208,48,164,208,48,195,208,48,226,208,48,0,209,48,36,224,48,67,224,48,98,224,48,129,224,48,160,224,48,48,0,49,80,0,49,112,0,49,
144,0,49,176,0,49,208,0,49,240,0,49,15,1,49,44,1,49,74,1,49,103,1,49,16,2,49,48,2,49,78,2,49,105,2,49,132,2,49,16,4,49,48,4,49,78,4,49,
105,4,49,132,4,49,16,6,49,48,6,49,78,6,49,105,6,49,132,6,49,16,8,49,48,8,49,78,8,49,105,8,49,132,8,49,42,16,49,72,16,49,103,16,49,134,16,49,
164,16,49,195,16,49,226,16,49,0,17,49,36,32,49,67,32,49,98,32,49,129,32,49,160,32,49,48,64,49,80,64,49,112,64,49,144,64,49,175,64,49,205,64,49,236,64,49,
9,65,49,39,65,49,70,65,49,99,65,49,16,66,49,45,66,49,73,66,49,100,66,49,129,66,49,16,68,49,45,68,49,73,68,49,100,68,49,129,68,49,16,70,49,45,70,49,
73,70,49,100,70,49,129,70,49,16,72,49,45,72,49,73,72,49,100,72,49,129,72,49,37,80,49,68,80,49,99,80,49,130,80,49,161,80,49,192,80,49,48,0,50,80,0,50,
112,0,50,144,0,50,176,0,50,208,0,50,240,0,50,16,1,50,48,1,50,80,1,50,48,16,50,80,16,50,112,16,50,144,16,50,176,16,50,208,16,50,236,16,50,6,17,50,
33,17,50,48,32,50,80,32,50,112,32,50,143,32,50,171,32,50,200,32,50,229,32,50,1,33,50,48,64,50,80,64,50,112,64,50,144,64,50,176,64,50,208,64,50,240,64,50,
15,65,50,42,65,50,70,65,50,16,72,50,48,72,50,77,72,50,99,72,50,48,80,50,78,80,50,107,80,50,137,80,50,166,80,50,196,80,50,226,80,50,40,96,50,70,96,50,
101,96,50,131,96,50,161,96,50,192,96,50,48,128,50,80,128,50,112,128,50,144,128,50,176,128,50,208,128,50,240,128,50,15,129,50,42,129,50,70,129,50,16,130,50,48,130,50,
77,130,50,99,130,50,16,132,50,48,132,50,77,132,50,99,132,50,16,134,50,48,134,50,77,134,50,99,134,50,48,144,50,78,144,50,107,144,50,137,144,50,166,144,50,196,144,50,
226,144,50,40,160,50,70,160,50,101,160,50,131,160,50,161,160,50,192,160,50,48,192,50,80,192,50,112,192,50,144,192,50,176,192,50,206,192,50,235,192,50,7,193,50,36,193,50,
65,193,50,16,194,50,46,194,50,70,194,50,16,196,50,46,196,50,70,196,50,16,198,50,46,198,50,70,198,50,40,208,50,70,208,50,101,208,50,131,208,50,161,208,50,192,208,50,
35,224,50,65,224,50,96,224,50,128,224,50,48,0,51,80,0,51,112,0,51,144,0,51,176,0,51,206,0,51,235,0,51,7,1,51,36,1,51,65,1,51,16,2,51,46,2,51,
70,2,51,16,4,51,46,4,51,70,4,51,16,6,51,46,6,51,70,6,51,16,8,51,46,8,51,70,8,51,40,16,51,70,16,51,101,16,51,131,16,51,161,16,51,192,16,51,
35,32,51,65,32,51,96,32,51,128,32,51,48,64,51,80,64,51,112,64,51,142,64,51,171,64,51,200,64,51,230,64,51,3,65,51,33,65,51,16,66,51,41,66,51,67,66,51,
16,68,51,41,68,51,67,68,51,16,70,51,41,70,51,67,70,51,16,72,51,41,72,51,67,72,51,36,80,51,67,80,51,97,80,51,128,80,51,16,0,52,48,0,52,80,0,52,
112,0,52,144,0,52,176,0,52,208,0,52,240,0,52,14,1,52,16,16,52,48,16,52,80,16,52,112,16,52,144,16,52,172,16,52,198,16,52,225,16,52,16,32,52,48,32,52,
80,32,52,109,32,52,138,32,52,165,32,52,193,32,52,16,64,52,48,64,52,80,64,52,112,64,52,144,64,52,176,64,52,207,64,52,234,64,52,3,65,52,16,72,52,48,72,52,
65,72,52,16,80,52,46,80,52,75,80,52,104,80,52,133,80,52,162,80,52,10,96,52,39,96,52,68,96,52,98,96,52,129,96,52,16,128,52,48,128,52,80,128,52,112,128,52,
144,128,52,176,128,52,207,128,52,234,128,52,3,129,52,16,130,52,48,130,52,65,130,52,16,132,52,48,132,52,65,132,52,16,134,52,48,134,52,65,134,52,16,144,52,46,144,52,
75,144,52,104,144,52,133,144,52,162,144,52,10,160,52,39,160,52,68,160,52,98,160,52,129,160,52,16,192,52,48,192,52,80,192,52,112,192,52,144,192,52,171,192,52,199,192,52,
228,192,52,16,194,52,40,194,52,16,196,52,40,196,52,16,198,52,40,198,52,10,208,52,39,208,52,68,208,52,98,208,52,129,208,52,4,224,52,34,224,52,64,224,52,16,0,53,
48,0,53,80,0,53,112,0,53,144,0,53,171,0,53,199,0,53,228,0,53,16,2,53,40,2,53,16,4,53,40,4,53,16,6,53,40,6,53,16,8,53,40,8,53,10,16,53,
39,16,53,68,16,53,98,16,53,129,16,53,4,32,53,34,32,53,64,32,53,16,64,53,48,64,53,79,64,53,108,64,53,138,64,53,166,64,53,195,64,53,225,64,53,15,66,53,
36,66,53,15,68,53,36,68,53,15,70,53,36,70,53,15,72,53,36,72,53,6,80,53,35,80,53,65,80,53,96,80,53,16,0,54,48,0,54,80,0,54,112,0,54,144,0,54,
176,0,54,208,0,54,16,16,54,48,16,54,80,16,54,112,16,54,139,16,54,163,16,54,16,32,54,48,32,54,77,32,54,104,32,54,133,32,54,16,64,54,48,64,54,80,64,54,
112,64,54,144,64,54,172,64,54,196,64,54,16,72,54,38,72,54,16,80,54,44,80,54,72,80,54,100,80,54,129,80,54,9,96,54,37,96,54,66,96,54,96,96,54,16,128,54,
48,128,54,80,128,54,112,128,54,144,128,54,172,128,54,196,128,54,16,130,54,38,130,54,16,132,54,38,132,54,16,134,54,38,134,54,16,144,54,44,144,54,72,144,54,100,144,54,
129,144,54,9,160,54,37,160,54,66,160,54,96,160,54,16,192,54,48,192,54,80,192,54,110,192,54,139,192,54,165,192,54,192,192,54,16,194,54,33,194,54,16,196,54,33,196,54,
16,198,54,33,198,54,9,208,54,37,208,54,66,208,54,96,208,54,3,224,54,33,224,54,16,0,55,48,0,55,80,0,55,110,0,55,139,0,55,165,0,55,192,0,55,16,2,55,
33,2,55,16,4,55,33,4,55,16,6,55,33,6,55,16,8,55,33,8,55,9,16,55,37,16,55,66,16,55,96,16,55,3,32,55,33,32,55,16,64,55,48,64,55,76,64,55,
105,64,55,134,64,55,162,64,55,12,66,55,12,68,55,12,70,55,12,72,55,5,80,55,34,80,55,64,80,55,16,0,56,48,0,56,80,0,56,112,0,56,144,0,56,174,0,56,
16,16,56,48,16,56,80,16,56,106,16,56,131,16,56,16,32,56,47,32,56,73,32,56,100,32,56,16,64,56,48,64,56,80,64,56,112,64,56,140,64,56,163,64,56,16,72,56,
16,80,56,41,80,56,69,80,56,97,80,56,8,96,56,35,96,56,64,96,56,16,128,56,48,128,56,80,128,56,112,128,56,140,128,56,163,128,56,16,130,56,16,132,56,16,134,56,
16,144,56,41,144,56,69,144,56,97,144,56,8,160,56,35,160,56,64,160,56,16,192,56,48,192,56,79,192,56,106,192,56,133,192,56,14,194,56,14,196,56,14,198,56,8,208,56,
35,208,56,64,208,56,3,224,56,32,224,56,16,0,57,48,0,57,79,0,57,106,0,57,133,0,57,14,2,57,14,4,57,14,6,57,14,8,57,8,16,57,35,16,57,64,16,57,
3,32,57,32,32,57,16,64,57,46,64,57,73,64,57,101,64,57,130,64,57,9,66,57,9,68,57,9,70,57,9,72,57,4,80,57,32,80,57,16,0,58,48,0,58,80,0,58,
112,0,58,144,0,58,16,16,58,48,16,58,76,16,58,99,16,58,16,32,58,44,32,58,69,32,58,16,64,58,48,64,58,80,64,58,108,64,58,132,64,58,15,80,58,39,80,58,
66,80,58,7,96,58,34,96,58,16,128,58,48,128,58,80,128,58,108,128,58,132,128,58,15,144,58,39,144,58,66,144,58,7,160,58,34,160,58,16,192,58,48,192,58,75,192,58,
101,192,58,128,192,58,7,208,58,34,208,58,2,224,58,16,0,59,48,0,59,75,0,59,101,0,59,128,0,59,7,16,59,34,16,59,2,32,59,16,64,59,44,64,59,70,64,59,
98,64,59,3,80,59,16,0,60,48,0,60,80,0,60,112,0,60,16,16,60,48,16,60,70,16,60,16,32,60,41,32,60,65,32,60,16,64,60,48,64,60,79,64,60,100,64,60,
14,80,60,37,80,60,6,96,60,32,96,60,16,128,60,48,128,60,79,128,60,100,128,60,14,144,60,37,144,60,6,160,60,32,160,60,16,192,60,47,192,60,71,192,60,96,192,60,
6,208,60,32,208,60,1,224,60,16,0,61,47,0,61,71,0,61,96,0,61,6,16,61,32,16,61,1,32,61,16,64,61,41,64,61,67,64,61,3,80,61,16,0,62,48,0,62,
80,0,62,16,16,62,45,16,62,64,16,62,16,32,62,38,32,62,16,64,62,48,64,62,73,64,62,12,80,62,34,80,62,5,96,62,16,128,62,48,128,62,73,128,62,12,144,62,
34,144,62,5,160,62,16,192,62,44,192,62,67,192,62,5,208,62,1,224,62,16,0,63,44,0,63,67,0,63,5,16,63,1,32,63,16,64,63,39,64,63,64,64,63,2,80,63,
16,0,64,48,0,64,78,0,64,16,16,64,40,16,64,16,32,64,35,32,64,16,64,64,48,64,64,67,64,64,11,80,64,32,80,64,4,96,64,16,128,64,48,128,64,67,128,64,
11,144,64,32,144,64,4,160,64,16,192,64,41,192,64,4,208,64,0,224,64,16,0,65,41,0,65,4,16,65,0,32,65,15,64,65,36,64,65,1,80,65,112,0,66,144,0,66,
176,0,66,208,0,66,240,0,66,16,1,66,48,1,66,80,1,66,112,1,66,112,16,66,144,16,66,176,16,66,208,16,66,240,16,66,16,17,66,48,17,66,80,17,66,112,17,66,
112,32,66,144,32,66,176,32,66,208,32,66,240,32,66,16,33,66,48,33,66,80,33,66,112,33,66,112,64,66,144,64,66,176,64,66,208,64,66,240,64,66,16,65,66,48,65,66,
80,65,66,112,65,66,48,72,66,80,72,66,112,72,66,144,72,66,176,72,66,208,72,66,240,72,66,13,73,66,40,73,66,68,73,66,112,80,66,144,80,66,176,80,66,208,80,66,
239,80,66,14,81,66,44,81,66,76,81,66,106,81,66,106,96,66,138,96,66,169,96,66,200,96,66,231,96,66,6,97,66,37,97,66,69,97,66,100,97,66,112,128,66,144,128,66,
176,128,66,208,128,66,240,128,66,16,129,66,48,129,66,80,129,66,112,129,66,48,130,66,80,130,66,112,130,66,144,130,66,176,130,66,208,130,66,240,130,66,13,131,66,40,131,66,
68,131,66,48,132,66,80,132,66,112,132,66,144,132,66,176,132,66,208,132,66,240,132,66,13,133,66,40,133,66,68,133,66,48,134,66,80,134,66,112,134,66,144,134,66,176,134,66,
208,134,66,240,134,66,13,135,66,40,135,66,68,135,66,112,144,66,144,144,66,176,144,66,208,144,66,239,144,66,14,145,66,44,145,66,76,145,66,106,145,66,106,160,66,138,160,66,
169,160,66,200,160,66,231,160,66,6,161,66,37,161,66,69,161,66,100,161,66,112,192,66,144,192,66,176,192,66,208,192,66,240,192,66,16,193,66,48,193,66,80,193,66,112,193,66,
48,194,66,80,194,66,112,194,66,144,194,66,176,194,66,205,194,66,234,194,66,6,195,66,35,195,66,64,195,66,48,196,66,80,196,66,112,196,66,144,196,66,176,196,66,205,196,66,
234,196,66,6,197,66,35,197,66,64,197,66,48,198,66,80,198,66,112,198,66,144,198,66,176,198,66,205,198,66,234,198,66,6,199,66,35,199,66,64,199,66,106,208,66,138,208,66,
169,208,66,200,208,66,231,208,66,6,209,66,37,209,66,69,209,66,100,209,66,100,224,66,132,224,66,163,224,66,195,224,66,226,224,66,1,225,66,33,225,66,64,225,66,96,225,66,
112,0,67,144,0,67,176,0,67,208,0,67,240,0,67,16,1,67,48,1,67,80,1,67,112,1,67,48,2,67,80,2,67,112,2,67,144,2,67,176,2,67,205,2,67,234,2,67,
6,3,67,35,3,67,64,3,67,48,4,67,80,4,67,112,4,67,144,4,67,176,4,67,205,4,67,234,4,67,6,5,67,35,5,67,64,5,67,48,6,67,80,6,67,112,6,67,
144,6,67,176,6,67,205,6,67,234,6,67,6,7,67,35,7,67,64,7,67,48,8,67,80,8,67,112,8,67,144,8,67,176,8,67,205,8,67,234,8,67,6,9,67,35,9,67,
64,9,67,106,16,67,138,16,67,169,16,67,200,16,67,231,16,67,6,17,67,37,17,67,69,17,67,100,17,67,100,32,67,132,32,67,163,32,67,195,32,67,226,32,67,1,33,67,
33,33,67,64,33,67,96,33,67,112,64,67,144,64,67,176,64,67,208,64,67,240,64,67,16,65,67,48,65,67,80,65,67,111,65,67,48,66,67,80,66,67,111,66,67,141,66,67,
170,66,67,199,66,67,230,66,67,3,67,67,32,67,67,48,68,67,80,68,67,111,68,67,141,68,67,170,68,67,199,68,67,230,68,67,3,69,67,32,69,67,48,70,67,80,70,67,
111,70,67,141,70,67,170,70,67,199,70,67,230,70,67,3,71,67,32,71,67,48,72,67,80,72,67,111,72,67,141,72,67,170,72,67,199,72,67,230,72,67,3,73,67,32,73,67,
102,80,67,133,80,67,164,80,67,196,80,67,227,80,67,3,81,67,34,81,67,65,81,67,97,81,67,48,0,68,80,0,68,112,0,68,144,0,68,176,0,68,208,0,68,240,0,68,
16,1,68,48,1,68,80,1,68,112,1,68,48,16,68,80,16,68,112,16,68,144,16,68,176,16,68,208,16,68,240,16,68,16,17,68,48,17,68,78,17,68,106,17,68,48,32,68,
80,32,68,112,32,68,144,32,68,176,32,68,208,32,68,238,32,68,11,33,68,41,33,68,71,33,68,100,33,68,48,64,68,80,64,68,112,64,68,144,64,68,176,64,68,208,64,68,
240,64,68,16,65,68,48,65,68,80,65,68,112,65,68,16,72,68,48,72,68,80,72,68,112,72,68,143,72,68,166,72,68,48,80,68,80,80,68,112,80,68,142,80,68,172,80,68,
202,80,68,233,80,68,6,81,68,36,81,68,67,81,68,97,81,68,42,96,68,73,96,68,104,96,68,135,96,68,165,96,68,196,96,68,227,96,68,1,97,68,32,97,68,48,128,68,
80,128,68,112,128,68,144,128,68,176,128,68,208,128,68,240,128,68,16,129,68,48,129,68,80,129,68,112,129,68,16,130,68,48,130,68,80,130,68,112,130,68,143,130,68,166,130,68,
16,132,68,48,132,68,80,132,68,112,132,68,143,132,68,166,132,68,16,134,68,48,134,68,80,134,68,112,134,68,143,134,68,166,134,68,48,144,68,80,144,68,112,144,68,142,144,68,
172,144,68,202,144,68,233,144,68,6,145,68,36,145,68,67,145,68,97,145,68,42,160,68,73,160,68,104,160,68,135,160,68,165,160,68,196,160,68,227,160,68,1,161,68,32,161,68,
48,192,68,80,192,68,112,192,68,144,192,68,176,192,68,208,192,68,240,192,68,16,193,68,47,193,68,77,193,68,106,193,68,16,194,68,48,194,68,80,194,68,107,194,68,135,194,68,
161,194,68,16,196,68,48,196,68,80,196,68,107,196,68,135,196,68,161,196,68,16,198,68,48,198,68,80,198,68,107,198,68,135,198,68,161,198,68,42,208,68,73,208,68,104,208,68,
135,208,68,165,208,68,196,208,68,227,208,68,1,209,68,32,209,68,36,224,68,67,224,68,98,224,68,130,224,68,161,224,68,192,224,68,48,0,69,80,0,69,112,0,69,144,0,69,
176,0,69,208,0,69,240,0,69,16,1,69,47,1,69,77,1,69,106,1,69,16,2,69,48,2,69,80,2,69,107,2,69,135,2,69,161,2,69,16,4,69,48,4,69,80,4,69,
107,4,69,135,4,69,161,4,69,16,6,69,48,6,69,80,6,69,107,6,69,135,6,69,161,6,69,16,8,69,48,8,69,80,8,69,107,8,69,135,8,69,161,8,69,42,16,69,
73,16,69,104,16,69,135,16,69,165,16,69,196,16,69,227,16,69,1,17,69,32,17,69,36,32,69,67,32,69,98,32,69,130,32,69,161,32,69,192,32,69,48,64,69,80,64,69,
112,64,69,144,64,69,176,64,69,207,64,69,237,64,69,11,65,69,41,65,69,71,65,69,101,65,69,16,66,69,47,66,69,74,66,69,102,66,69,131,66,69,16,68,69,47,68,69,
74,68,69,102,68,69,131,68,69,16,70,69,47,70,69,74,70,69,102,70,69,131,70,69,16,72,69,47,72,69,74,72,69,102,72,69,131,72,69,38,80,69,68,80,69,100,80,69,
131,80,69,162,80,69,193,80,69,224,80,69,48,0,70,80,0,70,112,0,70,144,0,70,176,0,70,208,0,70,240,0,70,16,1,70,48,1,70,80,1,70,48,16,70,80,16,70,
112,16,70,144,16,70,176,16,70,208,16,70,236,16,70,6,17,70,33,17,70,48,32,70,80,32,70,112,32,70,143,32,70,171,32,70,200,32,70,229,32,70,1,33,70,48,64,70,
80,64,70,112,64,70,144,64,70,176,64,70,208,64,70,240,64,70,15,65,70,42,65,70,70,65,70,16,72,70,48,72,70,77,72,70,99,72,70,48,80,70,78,80,70,107,80,70,
137,80,70,166,80,70,196,80,70,226,80,70,40,96,70,70,96,70,101,96,70,131,96,70,161,96,70,192,96,70,48,128,70,80,128,70,112,128,70,144,128,70,176,128,70,208,128,70,
240,128,70,15,129,70,42,129,70,70,129,70,16,130,70,48,130,70,77,130,70,99,130,70,16,132,70,48,132,70,77,132,70,99,132,70,16,134,70,48,134,70,77,134,70,99,134,70,
48,144,70,78,144,70,107,144,70,137,144,70,166,144,70,196,144,70,226,144,70,40,160,70,70,160,70,101,160,70,131,160,70,161,160,70,192,160,70,48,192,70,80,192,70,112,192,70,
144,192,70,176,192,70,206,192,70,235,192,70,7,193,70,36,193,70,65,193,70,16,194,70,46,194,70,70,194,70,16,196,70,46,196,70,70,196,70,16,198,70,46,198,70,70,198,70,
40,208,70,70,208,70,101,208,70,131,208,70,161,208,70,192,208,70,35,224,70,65,224,70,96,224,70,128,224,70,48,0,71,80,0,71,112,0,71,144,0,71,176,0,71,206,0,71,
235,0,71,7,1,71,36,1,71,65,1,71,16,2,71,46,2,71,70,2,71,16,4,71,46,4,71,70,4,71,16,6,71,46,6,71,70,6,71,16,8,71,46,8,71,70,8,71,
40,16,71,70,16,71,101,16,71,131,16,71,161,16,71,192,16,71,35,32,71,65,32,71,96,32,71,128,32,71,48,64,71,80,64,71,112,64,71,142,64,71,171,64,71,200,64,71,
230,64,71,3,65,71,33,65,71,16,66,71,41,66,71,67,66,71,16,68,71,41,68,71,67,68,71,16,70,71,41,70,71,67,70,71,16,72,71,41,72,71,67,72,71,36,80,71,
67,80,71,97,80,71,128,80,71,16,0,72,48,0,72,80,0,72,112,0,72,144,0,72,176,0,72,208,0,72,240,0,72,16,16,72,48,16,72,80,16,72,112,16,72,144,16,72,
170,16,72,195,16,72,16,32,72,48,32,72,80,32,72,108,32,72,137,32,72,164,32,72,16,64,72,48,64,72,80,64,72,112,64,72,144,64,72,176,64,72,204,64,72,231,64,72,
16,72,72,45,72,72,16,80,72,46,80,72,74,80,72,103,80,72,132,80,72,161,80,72,10,96,72,38,96,72,68,96,72,98,96,72,128,96,72,16,128,72,48,128,72,80,128,72,
112,128,72,144,128,72,176,128,72,204,128,72,231,128,72,16,130,72,45,130,72,16,132,72,45,132,72,16,134,72,45,134,72,16,144,72,46,144,72,74,144,72,103,144,72,132,144,72,
161,144,72,10,160,72,38,160,72,68,160,72,98,160,72,128,160,72,16,192,72,48,192,72,80,192,72,112,192,72,143,192,72,170,192,72,197,192,72,226,192,72,16,194,72,38,194,72,
16,196,72,38,196,72,16,198,72,38,198,72,10,208,72,38,208,72,68,208,72,98,208,72,128,208,72,4,224,72,33,224,72,64,224,72,16,0,73,48,0,73,80,0,73,112,0,73,
143,0,73,170,0,73,197,0,73,226,0,73,16,2,73,38,2,73,16,4,73,38,4,73,16,6,73,38,6,73,16,8,73,38,8,73,10,16,73,38,16,73,68,16,73,98,16,73,
128,16,73,4,32,73,33,32,73,64,32,73,16,64,73,48,64,73,79,64,73,107,64,73,137,64,73,165,64,73,194,64,73,224,64,73,14,66,73,35,66,73,14,68,73,35,68,73,
14,70,73,35,70,73,14,72,73,35,72,73,6,80,73,35,80,73,65,80,73,16,0,74,48,0,74,80,0,74,112,0,74,144,0,74,176,0,74,16,16,74,48,16,74,80,16,74,
109,16,74,135,16,74,16,32,74,48,32,74,75,32,74,102,32,74,130,32,74,16,64,74,48,64,74,80,64,74,112,64,74,144,64,74,167,64,74,16,72,74,33,72,74,16,80,74,
43,80,74,70,80,74,99,80,74,128,80,74,9,96,74,36,96,74,65,96,74,16,128,74,48,128,74,80,128,74,112,128,74,144,128,74,167,128,74,16,130,74,33,130,74,16,132,74,
33,132,74,16,134,74,33,134,74,16,144,74,43,144,74,70,144,74,99,144,74,128,144,74,9,160,74,36,160,74,65,160,74,16,192,74,48,192,74,80,192,74,108,192,74,136,192,74,
162,192,74,16,194,74,16,196,74,16,198,74,9,208,74,36,208,74,65,208,74,3,224,74,32,224,74,16,0,75,48,0,75,80,0,75,108,0,75,136,0,75,162,0,75,16,2,75,
16,4,75,16,6,75,16,8,75,9,16,75,36,16,75,65,16,75,3,32,75,32,32,75,16,64,75,47,64,75,75,64,75,103,64,75,132,64,75,160,64,75,10,66,75,10,68,75,
10,70,75,10,72,75,4,80,75,33,80,75,16,0,76,48,0,76,80,0,76,112,0,76,144,0,76,16,16,76,48,16,76,77,16,76,100,16,76,16,32,76,45,32,76,70,32,76,
96,32,76,16,64,76,48,64,76,80,64,76,109,64,76,135,64,76,16,80,76,40,80,76,67,80,76,8,96,76,34,96,76,16,128,76,48,128,76,80,128,76,109,128,76,135,128,76,
16,144,76,40,144,76,67,144,76,8,160,76,34,160,76,16,192,76,48,192,76,76,192,76,102,192,76,130,192,76,8,208,76,34,208,76,2,224,76,16,0,77,48,0,77,76,0,77,
102,0,77,130,0,77,8,16,77,34,16,77,2,32,77,16,64,77,44,64,77,71,64,77,99,64,77,4,80,77,32,80,77,16,0,78,48,0,78,80,0,78,112,0,78,16,16,78,
48,16,78,70,16,78,16,32,78,41,32,78,65,32,78,16,64,78,48,64,78,79,64,78,100,64,78,14,80,78,37,80,78,6,96,78,32,96,78,16,128,78,48,128,78,79,128,78,
100,128,78,14,144,78,37,144,78,6,160,78,32,160,78,16,192,78,47,192,78,71,192,78,96,192,78,6,208,78,32,208,78,1,224,78,16,0,79,47,0,79,71,0,79,96,0,79,
6,16,79,32,16,79,1,32,79,16,64,79,41,64,79,67,64,79,3,80,79,16,0,80,48,0,80,80,0,80,16,16,80,44,16,80,16,32,80,37,32,80,16,64,80,48,64,80,
71,64,80,12,80,80,34,80,80,5,96,80,16,128,80,48,128,80,71,128,80,12,144,80,34,144,80,5,160,80,16,192,80,43,192,80,66,192,80,5,208,80,1,224,80,16,0,81,
43,0,81,66,0,81,5,16,81,1,32,81,16,64,81,38,64,81,64,64,81,2,80,81,16,0,82,48,0,82,16,16,82,38,16,82,16,32,82,33,32,82,16,64,82,47,64,82,
10,80,82,4,96,82,16,128,82,47,128,82,10,144,82,4,160,82,16,192,82,39,192,82,4,208,82,0,224,82,16,0,83,39,0,83,4,16,83,0,32,83,15,64,83,35,64,83,
1,80,83,16,0,84,48,0,84,16,16,84,32,16,84,14,32,84,16,64,84,41,64,84,8,80,84,3,96,84,16,128,84,41,128,84,8,144,84,3,160,84,16,192,84,35,192,84,
3,208,84,16,0,85,35,0,85,3,16,85,13,64,85,32,64,85,0,80,85,16,0,86,46,0,86,16,16,86,11,32,86,16,64,86,35,64,86,6,80,86,1,96,86,16,128,86,
35,128,86,6,144,86,1,160,86,16,192,86,1,208,86,16,0,87,1,16,87,11,64,87,80,0,88,112,0,88,144,0,88,176,0,88,208,0,88,240,0,88,16,1,88,48,1,88,
80,1,88,112,1,88,80,16,88,112,16,88,144,16,88,176,16,88,208,16,88,240,16,88,16,17,88,48,17,88,80,17,88,112,17,88,80,32,88,112,32,88,144,32,88,176,32,88,
208,32,88,240,32,88,16,33,88,47,33,88,77,33,88,107,33,88,80,64,88,112,64,88,144,64,88,176,64,88,208,64,88,240,64,88,16,65,88,48,65,88,80,65,88,112,65,88,
16,72,88,48,72,88,80,72,88,112,72,88,144,72,88,176,72,88,205,72,88,232,72,88,1,73,88,80,80,88,112,80,88,144,80,88,175,80,88,206,80,88,236,80,88,11,81,88,
41,81,88,72,81,88,102,81,88,74,96,88,105,96,88,136,96,88,167,96,88,198,96,88,229,96,88,4,97,88,35,97,88,66,97,88,97,97,88,80,128,88,112,128,88,144,128,88,
176,128,88,208,128,88,240,128,88,16,129,88,48,129,88,80,129,88,112,129,88,16,130,88,48,130,88,80,130,88,112,130,88,144,130,88,176,130,88,205,130,88,232,130,88,1,131,88,
16,132,88,48,132,88,80,132,88,112,132,88,144,132,88,176,132,88,205,132,88,232,132,88,1,133,88,16,134,88,48,134,88,80,134,88,112,134,88,144,134,88,176,134,88,205,134,88,
232,134,88,1,135,88,80,144,88,112,144,88,144,144,88,175,144,88,206,144,88,236,144,88,11,145,88,41,145,88,72,145,88,102,145,88,74,160,88,105,160,88,136,160,88,167,160,88,
198,160,88,229,160,88,4,161,88,35,161,88,66,161,88,97,161,88,80,192,88,112,192,88,144,192,88,176,192,88,208,192,88,240,192,88,16,193,88,48,193,88,80,193,88,112,193,88,
16,194,88,48,194,88,80,194,88,112,194,88,143,194,88,170,194,88,198,194,88,227,194,88,16,196,88,48,196,88,80,196,88,112,196,88,143,196,88,170,196,88,198,196,88,227,196,88,
16,198,88,48,198,88,80,198,88,112,198,88,143,198,88,170,198,88,198,198,88,227,198,88,74,208,88,105,208,88,136,208,88,167,208,88,198,208,88,229,208,88,4,209,88,35,209,88,
66,209,88,97,209,88,68,224,88,99,224,88,131,224,88,162,224,88,193,224,88,225,224,88,0,225,88,32,225,88,80,0,89,112,0,89,144,0,89,176,0,89,208,0,89,240,0,89,
16,1,89,48,1,89,80,1,89,112,1,89,16,2,89,48,2,89,80,2,89,112,2,89,143,2,89,170,2,89,198,2,89,227,2,89,16,4,89,48,4,89,80,4,89,112,4,89,
143,4,89,170,4,89,198,4,89,227,4,89,16,6,89,48,6,89,80,6,89,112,6,89,143,6,89,170,6,89,198,6,89,227,6,89,16,8,89,48,8,89,80,8,89,112,8,89,
143,8,89,170,8,89,198,8,89,227,8,89,74,16,89,105,16,89,136,16,89,167,16,89,198,16,89,229,16,89,4,17,89,35,17,89,66,17,89,97,17,89,68,32,89,99,32,89,
131,32,89,162,32,89,193,32,89,225,32,89,0,33,89,32,33,89,80,64,89,112,64,89,144,64,89,176,64,89,208,64,89,240,64,89,15,65,89,46,65,89,76,65,89,107,65,89,
16,66,89,48,66,89,79,66,89,108,66,89,137,66,89,166,66,89,195,66,89,224,66,89,16,68,89,48,68,89,79,68,89,108,68,89,137,68,89,166,68,89,195,68,89,224,68,89,
16,70,89,48,70,89,79,70,89,108,70,89,137,70,89,166,70,89,195,70,89,224,70,89,16,72,89,48,72,89,79,72,89,108,72,89,137,72,89,166,72,89,195,72,89,224,72,89,
70,80,89,101,80,89,132,80,89,163,80,89,195,80,89,226,80,89,1,81,89,32,81,89,64,81,89,48,0,90,80,0,90,112,0,90,144,0,90,176,0,90,208,0,90,240,0,90,
16,1,90,48,1,90,80,1,90,112,1,90,48,16,90,80,16,90,112,16,90,144,16,90,176,16,90,208,16,90,240,16,90,12,17,90,39,17,90,68,17,90,48,32,90,80,32,90,
112,32,90,144,32,90,174,32,90,203,32,90,233,32,90,5,33,90,34,33,90,64,33,90,48,64,90,80,64,90,112,64,90,144,64,90,176,64,90,208,64,90,240,64,90,16,65,90,
48,65,90,77,65,90,103,65,90,16,72,90,48,72,90,80,72,90,106,72,90,131,72,90,48,80,90,79,80,90,109,80,90,139,80,90,169,80,90,198,80,90,228,80,90,2,81,90,
32,81,90,41,96,90,71,96,90,102,96,90,133,96,90,163,96,90,193,96,90,224,96,90,48,128,90,80,128,90,112,128,90,144,128,90,176,128,90,208,128,90,240,128,90,16,129,90,
48,129,90,77,129,90,103,129,90,16,130,90,48,130,90,80,130,90,106,130,90,131,130,90,16,132,90,48,132,90,80,132,90,106,132,90,131,132,90,16,134,90,48,134,90,80,134,90,
106,134,90,131,134,90,48,144,90,79,144,90,109,144,90,139,144,90,169,144,90,198,144,90,228,144,90,2,145,90,32,145,90,41,160,90,71,160,90,102,160,90,133,160,90,163,160,90,
193,160,90,224,160,90,48,192,90,80,192,90,112,192,90,144,192,90,176,192,90,208,192,90,239,192,90,11,193,90,40,193,90,70,193,90,98,193,90,16,194,90,48,194,90,74,194,90,
100,194,90,16,196,90,48,196,90,74,196,90,100,196,90,16,198,90,48,198,90,74,198,90,100,198,90,41,208,90,71,208,90,102,208,90,133,208,90,163,208,90,193,208,90,224,208,90,
35,224,90,66,224,90,97,224,90,128,224,90,48,0,91,80,0,91,112,0,91,144,0,91,176,0,91,208,0,91,239,0,91,11,1,91,40,1,91,70,1,91,98,1,91,16,2,91,
48,2,91,74,2,91,100,2,91,16,4,91,48,4,91,74,4,91,100,4,91,16,6,91,48,6,91,74,6,91,100,6,91,16,8,91,48,8,91,74,8,91,100,8,91,41,16,91,
71,16,91,102,16,91,133,16,91,163,16,91,193,16,91,224,16,91,35,32,91,66,32,91,97,32,91,128,32,91,48,64,91,80,64,91,112,64,91,144,64,91,173,64,91,203,64,91,
233,64,91,6,65,91,36,65,91,66,65,91,96,65,91,16,66,91,43,66,91,70,66,91,97,66,91,16,68,91,43,68,91,70,68,91,97,68,91,16,70,91,43,70,91,70,70,91,
97,70,91,16,72,91,43,72,91,70,72,91,97,72,91,37,80,91,67,80,91,98,80,91,129,80,91,160,80,91,16,0,92,48,0,92,80,0,92,112,0,92,144,0,92,176,0,92,
208,0,92,240,0,92,14,1,92,16,16,92,48,16,92,80,16,92,112,16,92,144,16,92,172,16,92,198,16,92,225,16,92,16,32,92,48,32,92,80,32,92,109,32,92,138,32,92,
165,32,92,193,32,92,16,64,92,48,64,92,80,64,92,112,64,92,144,64,92,176,64,92,207,64,92,234,64,92,3,65,92,16,72,92,48,72,92,65,72,92,16,80,92,46,80,92,
75,80,92,104,80,92,133,80,92,162,80,92,10,96,92,39,96,92,68,96,92,98,96,92,129,96,92,16,128,92,48,128,92,80,128,92,112,128,92,144,128,92,176,128,92,207,128,92,
234,128,92,3,129,92,16,130,92,48,130,92,65,130,92,16,132,92,48,132,92,65,132,92,16,134,92,48,134,92,65,134,92,16,144,92,46,144,92,75,144,92,104,144,92,133,144,92,
162,144,92,10,160,92,39,160,92,68,160,92,98,160,92,129,160,92,16,192,92,48,192,92,80,192,92,112,192,92,144,192,92,171,192,92,199,192,92,228,192,92,16,194,92,40,194,92,
16,196,92,40,196,92,16,198,92,40,198,92,10,208,92,39,208,92,68,208,92,98,208,92,129,208,92,4,224,92,34,224,92,64,224,92,16,0,93,48,0,93,80,0,93,112,0,93,
144,0,93,171,0,93,199,0,93,228,0,93,16,2,93,40,2,93,16,4,93,40,4,93,16,6,93,40,6,93,16,8,93,40,8,93,10,16,93,39,16,93,68,16,93,98,16,93,
129,16,93,4,32,93,34,32,93,64,32,93,16,64,93,48,64,93,79,64,93,108,64,93,138,64,93,166,64,93,195,64,93,225,64,93,15,66,93,36,66,93,15,68,93,36,68,93,
15,70,93,36,70,93,15,72,93,36,72,93,6,80,93,35,80,93,65,80,93,96,80,93,16,0,94,48,0,94,80,0,94,112,0,94,144,0,94,176,0,94,16,16,94,48,16,94,
80,16,94,109,16,94,135,16,94,16,32,94,48,32,94,75,32,94,102,32,94,130,32,94,16,64,94,48,64,94,80,64,94,112,64,94,144,64,94,167,64,94,16,72,94,33,72,94,
16,80,94,43,80,94,70,80,94,99,80,94,128,80,94,9,96,94,36,96,94,65,96,94,16,128,94,48,128,94,80,128,94,112,128,94,144,128,94,167,128,94,16,130,94,33,130,94,
16,132,94,33,132,94,16,134,94,33,134,94,16,144,94,43,144,94,70,144,94,99,144,94,128,144,94,9,160,94,36,160,94,65,160,94,16,192,94,48,192,94,80,192,94,108,192,94,
136,192,94,162,192,94,16,194,94,16,196,94,16,198,94,9,208,94,36,208,94,65,208,94,3,224,94,32,224,94,16,0,95,48,0,95,80,0,95,108,0,95,136,0,95,162,0,95,
16,2,95,16,4,95,16,6,95,16,8,95,9,16,95,36,16,95,65,16,95,3,32,95,32,32,95,16,64,95,47,64,95,75,64,95,103,64,95,132,64,95,160,64,95,10,66,95,
10,68,95,10,70,95,10,72,95,4,80,95,33,80,95,16,0,96,48,0,96,80,0,96,112,0,96,144,0,96,16,16,96,48,16,96,76,16,96,99,16,96,16,32,96,44,32,96,
69,32,96,16,64,96,48,64,96,80,64,96,108,64,96,132,64,96,15,80,96,39,80,96,66,80,96,7,96,96,34,96,96,16,128,96,48,128,96,80,128,96,108,128,96,132,128,96,
15,144,96,39,144,96,66,144,96,7,160,96,34,160,96,16,192,96,48,192,96,75,192,96,101,192,96,128,192,96,7,208,96,34,208,96,2,224,96,16,0,97,48,0,97,75,0,97,
101,0,97,128,0,97,7,16,97,34,16,97,2,32,97,16,64,97,44,64,97,70,64,97,98,64,97,3,80,97,16,0,98,48,0,98,80,0,98,16,16,98,47,16,98,67,16,98,
16,32,98,39,32,98,16,64,98,48,64,98,76,64,98,13,80,98,35,80,98,6,96,98,16,128,98,48,128,98,76,128,98,13,144,98,35,144,98,6,160,98,16,192,98,45,192,98,
69,192,98,6,208,98,1,224,98,16,0,99,45,0,99,69,0,99,6,16,99,1,32,99,16,64,99,40,64,99,66,64,99,2,80,99,16,0,100,48,0,100,78,0,100,16,16,100,
40,16,100,16,32,100,35,32,100,16,64,100,48,64,100,67,64,100,11,80,100,32,80,100,4,96,100,16,128,100,48,128,100,67,128,100,11,144,100,32,144,100,4,160,100,16,192,100,
41,192,100,4,208,100,0,224,100,16,0,101,41,0,101,4,16,101,0,32,101,15,64,101,36,64,101,1,80,101,16,0,102,48,0,102,16,16,102,33,16,102,14,32,102,16,64,102,
42,64,102,9,80,102,3,96,102,16,128,102,42,128,102,9,144,102,3,160,102,16,192,102,36,192,102,3,208,102,16,0,103,36,0,103,3,16,103,13,64,103,33,64,103,0,80,103,
16,0,104,46,0,104,16,16,104,11,32,104,16,64,104,35,64,104,6,80,104,1,96,104,16,128,104,35,128,104,6,144,104,1,160,104,16,192,104,1,208,104,16,0,105,1,16,105,
11,64,105,80,0,110,112,0,110,144,0,110,176,0,110,208,0,110,240,0,110,16,1,110,48,1,110,80,1,110,112,1,110,80,16,110,112,16,110,144,16,110,176,16,110,208,16,110,
240,16,110,16,17,110,48,17,110,80,17,110,109,17,110,80,32,110,112,32,110,144,32,110,176,32,110,208,32,110,240,32,110,13,33,110,43,33,110,73,33,110,102,33,110,80,64,110,
112,64,110,144,64,110,176,64,110,208,64,110,240,64,110,16,65,110,48,65,110,80,65,110,112,65,110,16,72,110,48,72,110,80,72,110,112,72,110,144,72,110,170,72,110,195,72,110,
80,80,110,112,80,110,143,80,110,173,80,110,203,80,110,234,80,110,8,81,110,38,81,110,68,81,110,99,81,110,73,96,110,104,96,110,135,96,110,166,96,110,197,96,110,228,96,110,
2,97,110,33,97,110,64,97,110,80,128,110,112,128,110,144,128,110,176,128,110,208,128,110,240,128,110,16,129,110,48,129,110,80,129,110,112,129,110,16,130,110,48,130,110,80,130,110,
112,130,110,144,130,110,170,130,110,195,130,110,16,132,110,48,132,110,80,132,110,112,132,110,144,132,110,170,132,110,195,132,110,16,134,110,48,134,110,80,134,110,112,134,110,144,134,110,
170,134,110,195,134,110,80,144,110,112,144,110,143,144,110,173,144,110,203,144,110,234,144,110,8,145,110,38,145,110,68,145,110,99,145,110,73,160,110,104,160,110,135,160,110,166,160,110,
197,160,110,228,160,110,2,161,110,33,161,110,64,161,110,80,192,110,112,192,110,144,192,110,176,192,110,208,192,110,240,192,110,16,193,110,48,193,110,79,193,110,108,193,110,16,194,110,
48,194,110,80,194,110,109,194,110,138,194,110,164,194,110,16,196,110,48,196,110,80,196,110,109,196,110,138,196,110,164,196,110,16,198,110,48,198,110,80,198,110,109,198,110,138,198,110,
164,198,110,73,208,110,104,208,110,135,208,110,166,208,110,197,208,110,228,208,110,2,209,110,33,209,110,64,209,110,67,224,110,99,224,110,130,224,110,161,224,110,192,224,110,224,224,110,
80,0,111,112,0,111,144,0,111,176,0,111,208,0,111,240,0,111,16,1,111,48,1,111,79,1,111,108,1,111,16,2,111,48,2,111,80,2,111,109,2,111,138,2,111,164,2,111,
16,4,111,48,4,111,80,4,111,109,4,111,138,4,111,164,4,111,16,6,111,48,6,111,80,6,111,109,6,111,138,6,111,164,6,111,16,8,111,48,8,111,80,8,111,109,8,111,
138,8,111,164,8,111,73,16,111,104,16,111,135,16,111,166,16,111,197,16,111,228,16,111,2,17,111,33,17,111,64,17,111,67,32,111,99,32,111,130,32,111,161,32,111,192,32,111,
224,32,111,80,64,111,112,64,111,144,64,111,176,64,111,208,64,111,238,64,111,12,65,111,42,65,111,73,65,111,103,65,111,16,66,111,48,66,111,76,66,111,104,66,111,133,66,111,
161,66,111,16,68,111,48,68,111,76,68,111,104,68,111,133,68,111,161,68,111,16,70,111,48,70,111,76,70,111,104,70,111,133,70,111,161,70,111,16,72,111,48,72,111,76,72,111,
104,72,111,133,72,111,161,72,111,69,80,111,100,80,111,131,80,111,162,80,111,193,80,111,225,80,111,0,81,111,48,0,112,80,0,112,112,0,112,144,0,112,176,0,112,208,0,112,
240,0,112,16,1,112,48,1,112,48,16,112,80,16,112,112,16,112,144,16,112,176,16,112,205,16,112,233,16,112,3,17,112,48,32,112,80,32,112,112,32,112,142,32,112,170,32,112,
198,32,112,227,32,112,48,64,112,80,64,112,112,64,112,144,64,112,176,64,112,208,64,112,240,64,112,12,65,112,39,65,112,16,72,112,48,72,112,74,72,112,48,80,112,77,80,112,
106,80,112,136,80,112,165,80,112,195,80,112,224,80,112,40,96,112,70,96,112,100,96,112,131,96,112,161,96,112,48,128,112,80,128,112,112,128,112,144,128,112,176,128,112,208,128,112,
240,128,112,12,129,112,39,129,112,16,130,112,48,130,112,74,130,112,16,132,112,48,132,112,74,132,112,16,134,112,48,134,112,74,134,112,48,144,112,77,144,112,106,144,112,136,144,112,
165,144,112,195,144,112,224,144,112,40,160,112,70,160,112,100,160,112,131,160,112,161,160,112,48,192,112,80,192,112,112,192,112,144,192,112,176,192,112,204,192,112,233,192,112,5,193,112,
34,193,112,16,194,112,44,194,112,68,194,112,16,196,112,44,196,112,68,196,112,16,198,112,44,198,112,68,198,112,40,208,112,70,208,112,100,208,112,131,208,112,161,208,112,35,224,112,
65,224,112,96,224,112,48,0,113,80,0,113,112,0,113,144,0,113,176,0,113,204,0,113,233,0,113,5,1,113,34,1,113,16,2,113,44,2,113,68,2,113,16,4,113,44,4,113,
68,4,113,16,6,113,44,6,113,68,6,113,16,8,113,44,8,113,68,8,113,40,16,113,70,16,113,100,16,113,131,16,113,161,16,113,35,32,113,65,32,113,96,32,113,48,64,113,
80,64,113,111,64,113,141,64,113,170,64,113,199,64,113,229,64,113,2,65,113,16,66,113,39,66,113,65,66,113,16,68,113,39,68,113,65,68,113,16,70,113,39,70,113,65,70,113,
16,72,113,39,72,113,65,72,113,36,80,113,66,80,113,97,80,113,128,80,113,16,0,114,48,0,114,80,0,114,112,0,114,144,0,114,176,0,114,208,0,114,16,16,114,48,16,114,
80,16,114,112,16,114,139,16,114,163,16,114,16,32,114,48,32,114,77,32,114,104,32,114,133,32,114,16,64,114,48,64,114,80,64,114,112,64,114,144,64,114,172,64,114,196,64,114,
16,72,114,38,72,114,16,80,114,44,80,114,72,80,114,100,80,114,129,80,114,9,96,114,37,96,114,66,96,114,96,96,114,16,128,114,48,128,114,80,128,114,112,128,114,144,128,114,
172,128,114,196,128,114,16,130,114,38,130,114,16,132,114,38,132,114,16,134,114,38,134,114,16,144,114,44,144,114,72,144,114,100,144,114,129,144,114,9,160,114,37,160,114,66,160,114,
96,160,114,16,192,114,48,192,114,80,192,114,110,192,114,139,192,114,165,192,114,192,192,114,16,194,114,33,194,114,16,196,114,33,196,114,16,198,114,33,198,114,9,208,114,37,208,114,
66,208,114,96,208,114,3,224,114,33,224,114,16,0,115,48,0,115,80,0,115,110,0,115,139,0,115,165,0,115,192,0,115,16,2,115,33,2,115,16,4,115,33,4,115,16,6,115,
33,6,115,16,8,115,33,8,115,9,16,115,37,16,115,66,16,115,96,16,115,3,32,115,33,32,115,16,64,115,48,64,115,76,64,115,105,64,115,134,64,115,162,64,115,12,66,115,
12,68,115,12,70,115,12,72,115,5,80,115,34,80,115,64,80,115,16,0,116,48,0,116,80,0,116,112,0,116,144,0,116,16,16,116,48,16,116,77,16,116,100,16,116,16,32,116,
45,32,116,70,32,116,96,32,116,16,64,116,48,64,116,80,64,116,109,64,116,135,64,116,16,80,116,40,80,116,67,80,116,8,96,116,34,96,116,16,128,116,48,128,116,80,128,116,
109,128,116,135,128,116,16,144,116,40,144,116,67,144,116,8,160,116,34,160,116,16,192,116,48,192,116,76,192,116,102,192,116,130,192,116,8,208,116,34,208,116,2,224,116,16,0,117,
48,0,117,76,0,117,102,0,117,130,0,117,8,16,117,34,16,117,2,32,117,16,64,117,44,64,117,71,64,117,99,64,117,4,80,117,32,80,117,16,0,118,48,0,118,80,0,118,
16,16,118,47,16,118,67,16,118,16,32,118,39,32,118,16,64,118,48,64,118,76,64,118,13,80,118,35,80,118,6,96,118,16,128,118,48,128,118,76,128,118,13,144,118,35,144,118,
6,160,118,16,192,118,45,192,118,69,192,118,6,208,118,1,224,118,16,0,119,45,0,119,69,0,119,6,16,119,1,32,119,16,64,119,40,64,119,66,64,119,2,80,119,16,0,120,
48,0,120,16,16,120,39,16,120,16,32,120,34,32,120,16,64,120,48,64,120,10,80,120,4,96,120,16,128,120,48,128,120,10,144,120,4,160,120,16,192,120,40,192,120,4,208,120,
0,224,120,16,0,121,40,0,121,4,16,121,0,32,121,15,64,121,36,64,121,1,80,121,16,0,122,48,0,122,16,16,122,13,32,122,16,64,122,39,64,122,8,80,122,2,96,122,
16,128,122,39,128,122,8,144,122,2,160,122,16,192,122,34,192,122,2,208,122,16,0,123,34,0,123,2,16,123,12,64,123,32,64,123,0,80,123,16,0,124,16,16,124,10,32,124,
16,64,124,5,80,124,1,96,124,16,128,124,5,144,124,1,160,124,16,192,124,1,208,124,16,0,125,1,16,125,10,64,125,48,0,132,80,0,132,112,0,132,144,0,132,176,0,132,
208,0,132,240,0,132,16,1,132,48,1,132,80,1,132,112,1,132,48,16,132,80,16,132,112,16,132,144,16,132,176,16,132,208,16,132,240,16,132,16,17,132,45,17,132,74,17,132,
102,17,132,48,32,132,80,32,132,112,32,132,144,32,132,176,32,132,206,32,132,236,32,132,9,33,132,38,33,132,68,33,132,97,33,132,48,64,132,80,64,132,112,64,132,144,64,132,
176,64,132,208,64,132,240,64,132,16,65,132,48,65,132,80,65,132,111,65,132,16,72,132,48,72,132,80,72,132,112,72,132,138,72,132,161,72,132,48,80,132,80,80,132,111,80,132,
141,80,132,171,80,132,201,80,132,231,80,132,5,81,132,35,81,132,65,81,132,42,96,132,72,96,132,103,96,132,134,96,132,164,96,132,195,96,132,226,96,132,0,97,132,48,128,132,
80,128,132,112,128,132,144,128,132,176,128,132,208,128,132,240,128,132,16,129,132,48,129,132,80,129,132,111,129,132,16,130,132,48,130,132,80,130,132,112,130,132,138,130,132,161,130,132,
16,132,132,48,132,132,80,132,132,112,132,132,138,132,132,161,132,132,16,134,132,48,134,132,80,134,132,112,134,132,138,134,132,161,134,132,48,144,132,80,144,132,111,144,132,141,144,132,
171,144,132,201,144,132,231,144,132,5,145,132,35,145,132,65,145,132,42,160,132,72,160,132,103,160,132,134,160,132,164,160,132,195,160,132,226,160,132,0,161,132,48,192,132,80,192,132,
112,192,132,144,192,132,176,192,132,208,192,132,240,192,132,15,193,132,44,193,132,74,193,132,103,193,132,16,194,132,48,194,132,78,194,132,105,194,132,132,194,132,16,196,132,48,196,132,
78,196,132,105,196,132,132,196,132,16,198,132,48,198,132,78,198,132,105,198,132,132,198,132,42,208,132,72,208,132,103,208,132,134,208,132,164,208,132,195,208,132,226,208,132,0,209,132,
36,224,132,67,224,132,98,224,132,129,224,132,160,224,132,48,0,133,80,0,133,112,0,133,144,0,133,176,0,133,208,0,133,240,0,133,15,1,133,44,1,133,74,1,133,103,1,133,
16,2,133,48,2,133,78,2,133,105,2,133,132,2,133,16,4,133,48,4,133,78,4,133,105,4,133,132,4,133,16,6,133,48,6,133,78,6,133,105,6,133,132,6,133,16,8,133,
48,8,133,78,8,133,105,8,133,132,8,133,42,16,133,72,16,133,103,16,133,134,16,133,164,16,133,195,16,133,226,16,133,0,17,133,36,32,133,67,32,133,98,32,133,129,32,133,
160,32,133,48,64,133,80,64,133,112,64,133,144,64,133,175,64,133,205,64,133,236,64,133,9,65,133,39,65,133,70,65,133,99,65,133,16,66,133,45,66,133,73,66,133,100,66,133,
129,66,133,16,68,133,45,68,133,73,68,133,100,68,133,129,68,133,16,70,133,45,70,133,73,70,133,100,70,133,129,70,133,16,72,133,45,72,133,73,72,133,100,72,133,129,72,133,
37,80,133,68,80,133,99,80,133,130,80,133,161,80,133,192,80,133,16,0,134,48,0,134,80,0,134,112,0,134,144,0,134,176,0,134,208,0,134,240,0,134,14,1,134,16,16,134,
48,16,134,80,16,134,112,16,134,144,16,134,172,16,134,198,16,134,225,16,134,16,32,134,48,32,134,80,32,134,109,32,134,138,32,134,165,32,134,193,32,134,16,64,134,48,64,134,
80,64,134,112,64,134,144,64,134,176,64,134,207,64,134,234,64,134,3,65,134,16,72,134,48,72,134,65,72,134,16,80,134,46,80,134,75,80,134,104,80,134,133,80,134,162,80,134,
10,96,134,39,96,134,68,96,134,98,96,134,129,96,134,16,128,134,48,128,134,80,128,134,112,128,134,144,128,134,176,128,134,207,128,134,234,128,134,3,129,134,16,130,134,48,130,134,
65,130,134,16,132,134,48,132,134,65,132,134,16,134,134,48,134,134,65,134,134,16,144,134,46,144,134,75,144,134,104,144,134,133,144,134,162,144,134,10,160,134,39,160,134,68,160,134,
98,160,134,129,160,134,16,192,134,48,192,134,80,192,134,112,192,134,144,192,134,171,192,134,199,192,134,228,192,134,16,194,134,40,194,134,16,196,134,40,196,134,16,198,134,40,198,134,
10,208,134,39,208,134,68,208,134,98,208,134,129,208,134,4,224,134,34,224,134,64,224,134,16,0,135,48,0,135,80,0,135,112,0,135,144,0,135,171,0,135,199,0,135,228,0,135,
16,2,135,40,2,135,16,4,135,40,4,135,16,6,135,40,6,135,16,8,135,40,8,135,10,16,135,39,16,135,68,16,135,98,16,135,129,16,135,4,32,135,34,32,135,64,32,135,
16,64,135,48,64,135,79,64,135,108,64,135,138,64,135,166,64,135,195,64,135,225,64,135,15,66,135,36,66,135,15,68,135,36,68,135,15,70,135,36,70,135,15,72,135,36,72,135,
6,80,135,35,80,135,65,80,135,96,80,135,16,0,136,48,0,136,80,0,136,112,0,136,144,0,136,174,0,136,16,16,136,48,16,136,80,16,136,106,16,136,131,16,136,16,32,136,
47,32,136,73,32,136,100,32,136,16,64,136,48,64,136,80,64,136,112,64,136,140,64,136,163,64,136,16,72,136,16,80,136,41,80,136,69,80,136,97,80,136,8,96,136,35,96,136,
64,96,136,16,128,136,48,128,136,80,128,136,112,128,136,140,128,136,163,128,136,16,130,136,16,132,136,16,134,136,16,144,136,41,144,136,69,144,136,97,144,136,8,160,136,35,160,136,
64,160,136,16,192,136,48,192,136,79,192,136,106,192,136,133,192,136,14,194,136,14,196,136,14,198,136,8,208,136,35,208,136,64,208,136,3,224,136,32,224,136,16,0,137,48,0,137,
79,0,137,106,0,137,133,0,137,14,2,137,14,4,137,14,6,137,14,8,137,8,16,137,35,16,137,64,16,137,3,32,137,32,32,137,16,64,137,46,64,137,73,64,137,101,64,137,
130,64,137,9,66,137,9,68,137,9,70,137,9,72,137,4,80,137,32,80,137,16,0,138,48,0,138,80,0,138,112,0,138,16,16,138,48,16,138,70,16,138,16,32,138,41,32,138,
65,32,138,16,64,138,48,64,138,79,64,138,100,64,138,14,80,138,37,80,138,6,96,138,32,96,138,16,128,138,48,128,138,79,128,138,100,128,138,14,144,138,37,144,138,6,160,138,
32,160,138,16,192,138,47,192,138,71,192,138,96,192,138,6,208,138,32,208,138,1,224,138,16,0,139,47,0,139,71,0,139,96,0,139,6,16,139,32,16,139,1,32,139,16,64,139,
41,64,139,67,64,139,3,80,139,16,0,140,48,0,140,78,0,140,16,16,140,40,16,140,16,32,140,35,32,140,16,64,140,48,64,140,67,64,140,11,80,140,32,80,140,4,96,140,
16,128,140,48,128,140,67,128,140,11,144,140,32,144,140,4,160,140,16,192,140,41,192,140,4,208,140,0,224,140,16,0,141,41,0,141,4,16,141,0,32,141,15,64,141,36,64,141,
1,80,141,16,0,142,48,0,142,16,16,142,13,32,142,16,64,142,39,64,142,8,80,142,2,96,142,16,128,142,39,128,142,8,144,142,2,160,142,16,192,142,34,192,142,2,208,142,
16,0,143,34,0,143,2,16,143,12,64,143,32,64,143,0,80,143,16,0,144,16,16,144,9,32,144,16,64,144,5,80,144,0,96,144,16,128,144,5,144,144,0,160,144,15,192,144,
0,208,144,15,0,145,0,16,145,9,64,145,48,0,154,80,0,154,112,0,154,144,0,154,176,0,154,208,0,154,240,0,154,16,1,154,48,1,154,80,1,154,112,1,154,48,16,154,
80,16,154,112,16,154,144,16,154,176,16,154,208,16,154,240,16,154,12,17,154,39,17,154,68,17,154,48,32,154,80,32,154,112,32,154,144,32,154,174,32,154,203,32,154,233,32,154,
5,33,154,34,33,154,64,33,154,48,64,154,80,64,154,112,64,154,144,64,154,176,64,154,208,64,154,240,64,154,16,65,154,48,65,154,77,65,154,103,65,154,16,72,154,48,72,154,
80,72,154,106,72,154,131,72,154,48,80,154,79,80,154,109,80,154,139,80,154,169,80,154,198,80,154,228,80,154,2,81,154,32,81,154,41,96,154,71,96,154,102,96,154,133,96,154,
163,96,154,193,96,154,224,96,154,48,128,154,80,128,154,112,128,154,144,128,154,176,128,154,208,128,154,240,128,154,16,129,154,48,129,154,77,129,154,103,129,154,16,130,154,48,130,154,
80,130,154,106,130,154,131,130,154,16,132,154,48,132,154,80,132,154,106,132,154,131,132,154,16,134,154,48,134,154,80,134,154,106,134,154,131,134,154,48,144,154,79,144,154,109,144,154,
139,144,154,169,144,154,198,144,154,228,144,154,2,145,154,32,145,154,41,160,154,71,160,154,102,160,154,133,160,154,163,160,154,193,160,154,224,160,154,48,192,154,80,192,154,112,192,154,
144,192,154,176,192,154,208,192,154,239,192,154,11,193,154,40,193,154,70,193,154,98,193,154,16,194,154,48,194,154,74,194,154,100,194,154,16,196,154,48,196,154,74,196,154,100,196,154,
16,198,154,48,198,154,74,198,154,100,198,154,41,208,154,71,208,154,102,208,154,133,208,154,163,208,154,193,208,154,224,208,154,35,224,154,66,224,154,97,224,154,128,224,154,48,0,155,
80,0,155,112,0,155,144,0,155,176,0,155,208,0,155,239,0,155,11,1,155,40,1,155,70,1,155,98,1,155,16,2,155,48,2,155,74,2,155,100,2,155,16,4,155,48,4,155,
74,4,155,100,4,155,16,6,155,48,6,155,74,6,155,100,6,155,16,8,155,48,8,155,74,8,155,100,8,155,41,16,155,71,16,155,102,16,155,133,16,155,163,16,155,193,16,155,
224,16,155,35,32,155,66,32,155,97,32,155,128,32,155,48,64,155,80,64,155,112,64,155,144,64,155,173,64,155,203,64,155,233,64,155,6,65,155,36,65,155,66,65,155,96,65,155,
16,66,155,43,66,155,70,66,155,97,66,155,16,68,155,43,68,155,70,68,155,97,68,155,16,70,155,43,70,155,70,70,155,97,70,155,16,72,155,43,72,155,70,72,155,97,72,155,
37,80,155,67,80,155,98,80,155,129,80,155,160,80,155,16,0,156,48,0,156,80,0,156,112,0,156,144,0,156,176,0,156,208,0,156,16,16,156,48,16,156,80,16,156,112,16,156,
141,16,156,165,16,156,16,32,156,48,32,156,78,32,156,106,32,156,134,32,156,161,32,156,16,64,156,48,64,156,80,64,156,112,64,156,144,64,156,174,64,156,199,64,156,16,72,156,
40,72,156,16,80,156,44,80,156,73,80,156,101,80,156,130,80,156,10,96,156,37,96,156,67,96,156,97,96,156,16,128,156,48,128,156,80,128,156,112,128,156,144,128,156,174,128,156,
199,128,156,16,130,156,40,130,156,16,132,156,40,132,156,16,134,156,40,134,156,16,144,156,44,144,156,73,144,156,101,144,156,130,144,156,10,160,156,37,160,156,67,160,156,97,160,156,
16,192,156,48,192,156,80,192,156,112,192,156,140,192,156,167,192,156,194,192,156,16,194,156,35,194,156,16,196,156,35,196,156,16,198,156,35,198,156,10,208,156,37,208,156,67,208,156,
97,208,156,4,224,156,33,224,156,16,0,157,48,0,157,80,0,157,112,0,157,140,0,157,167,0,157,194,0,157,16,2,157,35,2,157,16,4,157,35,4,157,16,6,157,35,6,157,
16,8,157,35,8,157,10,16,157,37,16,157,67,16,157,97,16,157,4,32,157,33,32,157,16,64,157,48,64,157,77,64,157,106,64,157,135,64,157,163,64,157,192,64,157,12,66,157,
32,66,157,12,68,157,32,68,157,12,70,157,32,70,157,12,72,157,32,72,157,5,80,157,34,80,157,64,80,157,16,0,158,48,0,158,80,0,158,112,0,158,144,0,158,16,16,158,
48,16,158,76,16,158,99,16,158,16,32,158,44,32,158,69,32,158,16,64,158,48,64,158,80,64,158,108,64,158,132,64,158,15,80,158,39,80,158,66,80,158,7,96,158,34,96,158,
16,128,158,48,128,158,80,128,158,108,128,158,132,128,158,15,144,158,39,144,158,66,144,158,7,160,158,34,160,158,16,192,158,48,192,158,75,192,158,101,192,158,128,192,158,7,208,158,
34,208,158,2,224,158,16,0,159,48,0,159,75,0,159,101,0,159,128,0,159,7,16,159,34,16,159,2,32,159,16,64,159,44,64,159,70,64,159,98,64,159,3,80,159,16,0,160,
48,0,160,80,0,160,16,16,160,44,16,160,16,32,160,37,32,160,16,64,160,48,64,160,71,64,160,12,80,160,34,80,160,5,96,160,16,128,160,48,128,160,71,128,160,12,144,160,
34,144,160,5,160,160,16,192,160,43,192,160,66,192,160,5,208,160,1,224,160,16,0,161,43,0,161,66,0,161,5,16,161,1,32,161,16,64,161,38,64,161,64,64,161,2,80,161,
16,0,162,48,0,162,16,16,162,33,16,162,14,32,162,16,64,162,42,64,162,9,80,162,3,96,162,16,128,162,42,128,162,9,144,162,3,160,162,16,192,162,36,192,162,3,208,162,
16,0,163,36,0,163,3,16,163,13,64,163,33,64,163,0,80,163,16,0,164,16,16,164,10,32,164,16,64,164,5,80,164,1,96,164,16,128,164,5,144,164,1,160,164,16,192,164,
1,208,164,16,0,165,1,16,165,10,64,165,48,0,176,80,0,176,112,0,176,144,0,176,176,0,176,208,0,176,240,0,176,16,1,176,48,1,176,80,1,176,48,16,176,80,16,176,
112,16,176,144,16,176,176,16,176,208,16,176,236,16,176,6,17,176,33,17,176,48,32,176,80,32,176,112,32,176,143,32,176,171,32,176,200,32,176,229,32,176,1,33,176,48,64,176,
80,64,176,112,64,176,144,64,176,176,64,176,208,64,176,240,64,176,15,65,176,42,65,176,70,65,176,16,72,176,48,72,176,77,72,176,99,72,176,48,80,176,78,80,176,107,80,176,
137,80,176,166,80,176,196,80,176,226,80,176,40,96,176,70,96,176,101,96,176,131,96,176,161,96,176,192,96,176,48,128,176,80,128,176,112,128,176,144,128,176,176,128,176,208,128,176,
240,128,176,15,129,176,42,129,176,70,129,176,16,130,176,48,130,176,77,130,176,99,130,176,16,132,176,48,132,176,77,132,176,99,132,176,16,134,176,48,134,176,77,134,176,99,134,176,
48,144,176,78,144,176,107,144,176,137,144,176,166,144,176,196,144,176,226,144,176,40,160,176,70,160,176,101,160,176,131,160,176,161,160,176,192,160,176,48,192,176,80,192,176,112,192,176,
144,192,176,176,192,176,206,192,176,235,192,176,7,193,176,36,193,176,65,193,176,16,194,176,46,194,176,70,194,176,16,196,176,46,196,176,70,196,176,16,198,176,46,198,176,70,198,176,
40,208,176,70,208,176,101,208,176,131,208,176,161,208,176,192,208,176,35,224,176,65,224,176,96,224,176,128,224,176,48,0,177,80,0,177,112,0,177,144,0,177,176,0,177,206,0,177,
235,0,177,7,1,177,36,1,177,65,1,177,16,2,177,46,2,177,70,2,177,16,4,177,46,4,177,70,4,177,16,6,177,46,6,177,70,6,177,16,8,177,46,8,177,70,8,177,
40,16,177,70,16,177,101,16,177,131,16,177,161,16,177,192,16,177,35,32,177,65,32,177,96,32,177,128,32,177,48,64,177,80,64,177,112,64,177,142,64,177,171,64,177,200,64,177,
230,64,177,3,65,177,33,65,177,16,66,177,41,66,177,67,66,177,16,68,177,41,68,177,67,68,177,16,70,177,41,70,177,67,70,177,16,72,177,41,72,177,67,72,177,36,80,177,
67,80,177,97,80,177,128,80,177,16,0,178,48,0,178,80,0,178,112,0,178,144,0,178,176,0,178,16,16,178,48,16,178,80,16,178,109,16,178,135,16,178,16,32,178,48,32,178,
75,32,178,102,32,178,130,32,178,16,64,178,48,64,178,80,64,178,112,64,178,144,64,178,167,64,178,16,72,178,33,72,178,16,80,178,43,80,178,70,80,178,99,80,178,128,80,178,
9,96,178,36,96,178,65,96,178,16,128,178,48,128,178,80,128,178,112,128,178,144,128,178,167,128,178,16,130,178,33,130,178,16,132,178,33,132,178,16,134,178,33,134,178,16,144,178,
43,144,178,70,144,178,99,144,178,128,144,178,9,160,178,36,160,178,65,160,178,16,192,178,48,192,178,80,192,178,108,192,178,136,192,178,162,192,178,16,194,178,16,196,178,16,198,178,
9,208,178,36,208,178,65,208,178,3,224,178,32,224,178,16,0,179,48,0,179,80,0,179,108,0,179,136,0,179,162,0,179,16,2,179,16,4,179,16,6,179,16,8,179,9,16,179,
36,16,179,65,16,179,3,32,179,32,32,179,16,64,179,47,64,179,75,64,179,103,64,179,132,64,179,160,64,179,10,66,179,10,68,179,10,70,179,10,72,179,4,80,179,33,80,179,
16,0,180,48,0,180,80,0,180,112,0,180,16,16,180,48,16,180,70,16,180,16,32,180,41,32,180,65,32,180,16,64,180,48,64,180,79,64,180,100,64,180,14,80,180,37,80,180,
6,96,180,32,96,180,16,128,180,48,128,180,79,128,180,100,128,180,14,144,180,37,144,180,6,160,180,32,160,180,16,192,180,47,192,180,71,192,180,96,192,180,6,208,180,32,208,180,
1,224,180,16,0,181,47,0,181,71,0,181,96,0,181,6,16,181,32,16,181,1,32,181,16,64,181,41,64,181,67,64,181,3,80,181,16,0,182,48,0,182,16,16,182,38,16,182,
16,32,182,33,32,182,16,64,182,47,64,182,10,80,182,4,96,182,16,128,182,47,128,182,10,144,182,4,160,182,16,192,182,39,192,182,4,208,182,0,224,182,16,0,183,39,0,183,
4,16,183,0,32,183,15,64,183,35,64,183,1,80,183,16,0,184,46,0,184,16,16,184,11,32,184,16,64,184,35,64,184,6,80,184,1,96,184,16,128,184,35,128,184,6,144,184,
1,160,184,16,192,184,1,208,184,16,0,185,1,16,185,11,64,185,48,0,198,80,0,198,112,0,198,144,0,198,176,0,198,208,0,198,240,0,198,16,1,198,46,1,198,48,16,198,
80,16,198,112,16,198,144,16,198,176,16,198,202,16,198,230,16,198,0,17,198,48,32,198,80,32,198,111,32,198,140,32,198,168,32,198,196,32,198,225,32,198,48,64,198,80,64,198,
112,64,198,144,64,198,176,64,198,208,64,198,239,64,198,9,65,198,35,65,198,16,72,198,48,72,198,71,72,198,47,80,198,76,80,198,105,80,198,135,80,198,164,80,198,193,80,198,
39,96,198,69,96,198,99,96,198,130,96,198,160,96,198,48,128,198,80,128,198,112,128,198,144,128,198,176,128,198,208,128,198,239,128,198,9,129,198,35,129,198,16,130,198,48,130,198,
71,130,198,16,132,198,48,132,198,71,132,198,16,134,198,48,134,198,71,134,198,47,144,198,76,144,198,105,144,198,135,144,198,164,144,198,193,144,198,39,160,198,69,160,198,99,160,198,
130,160,198,160,160,198,48,192,198,80,192,198,112,192,198,144,192,198,174,192,198,202,192,198,231,192,198,3,193,198,16,194,198,43,194,198,66,194,198,16,196,198,43,196,198,66,196,198,
16,198,198,43,198,198,66,198,198,39,208,198,69,208,198,99,208,198,130,208,198,160,208,198,34,224,198,65,224,198,96,224,198,48,0,199,80,0,199,112,0,199,144,0,199,174,0,199,
202,0,199,231,0,199,3,1,199,16,2,199,43,2,199,66,2,199,16,4,199,43,4,199,66,4,199,16,6,199,43,6,199,66,6,199,16,8,199,43,8,199,66,8,199,39,16,199,
69,16,199,99,16,199,130,16,199,160,16,199,34,32,199,65,32,199,96,32,199,48,64,199,80,64,199,110,64,199,140,64,199,169,64,199,198,64,199,227,64,199,0,65,199,16,66,199,
38,66,199,64,66,199,16,68,199,38,68,199,64,68,199,16,70,199,38,70,199,64,70,199,16,72,199,38,72,199,64,72,199,35,80,199,66,80,199,96,80,199,16,0,200,48,0,200,
80,0,200,112,0,200,144,0,200,16,16,200,48,16,200,80,16,200,104,16,200,129,16,200,16,32,200,47,32,200,72,32,200,99,32,200,16,64,200,48,64,200,80,64,200,112,64,200,
138,64,200,16,80,200,41,80,200,68,80,200,96,80,200,8,96,200,35,96,200,64,96,200,16,128,200,48,128,200,80,128,200,112,128,200,138,128,200,16,144,200,41,144,200,68,144,200,
96,144,200,8,160,200,35,160,200,64,160,200,16,192,200,48,192,200,78,192,200,105,192,200,132,192,200,8,208,200,35,208,200,64,208,200,3,224,200,16,0,201,48,0,201,78,0,201,
105,0,201,132,0,201,8,16,201,35,16,201,64,16,201,3,32,201,16,64,201,45,64,201,73,64,201,100,64,201,129,64,201,4,80,201,32,80,201,16,0,202,48,0,202,80,0,202,
16,16,202,45,16,202,64,16,202,16,32,202,38,32,202,16,64,202,48,64,202,73,64,202,12,80,202,34,80,202,5,96,202,16,128,202,48,128,202,73,128,202,12,144,202,34,144,202,
5,160,202,16,192,202,44,192,202,67,192,202,5,208,202,1,224,202,16,0,203,44,0,203,67,0,203,5,16,203,1,32,203,16,64,203,39,64,203,64,64,203,2,80,203,16,0,204,
48,0,204,16,16,204,32,16,204,14,32,204,16,64,204,41,64,204,8,80,204,3,96,204,16,128,204,41,128,204,8,144,204,3,160,204,16,192,204,35,192,204,3,208,204,16,0,205,
35,0,205,3,16,205,13,64,205,32,64,205,0,80,205,16,0,220,48,0,220,80,0,220,112,0,220,144,0,220,176,0,220,208,0,220,240,0,220,14,1,220,16,16,220,48,16,220,
80,16,220,112,16,220,144,16,220,172,16,220,198,16,220,225,16,220,16,32,220,48,32,220,80,32,220,109,32,220,138,32,220,165,32,220,193,32,220,16,64,220,48,64,220,80,64,220,
112,64,220,144,64,220,176,64,220,207,64,220,234,64,220,3,65,220,16,72,220,48,72,220,65,72,220,16,80,220,46,80,220,75,80,220,104,80,220,133,80,220,162,80,220,10,96,220,
39,96,220,68,96,220,98,96,220,129,96,220,16,128,220,48,128,220,80,128,220,112,128,220,144,128,220,176,128,220,207,128,220,234,128,220,3,129,220,16,130,220,48,130,220,65,130,220,
16,132,220,48,132,220,65,132,220,16,134,220,48,134,220,65,134,220,16,144,220,46,144,220,75,144,220,104,144,220,133,144,220,162,144,220,10,160,220,39,160,220,68,160,220,98,160,220,
129,160,220,16,192,220,48,192,220,80,192,220,112,192,220,144,192,220,171,192,220,199,192,220,228,192,220,16,194,220,40,194,220,16,196,220,40,196,220,16,198,220,40,198,220,10,208,220,
39,208,220,68,208,220,98,208,220,129,208,220,4,224,220,34,224,220,64,224,220,16,0,221,48,0,221,80,0,221,112,0,221,144,0,221,171,0,221,199,0,221,228,0,221,16,2,221,
40,2,221,16,4,221,40,4,221,16,6,221,40,6,221,16,8,221,40,8,221,10,16,221,39,16,221,68,16,221,98,16,221,129,16,221,4,32,221,34,32,221,64,32,221,16,64,221,
48,64,221,79,64,221,108,64,221,138,64,221,166,64,221,195,64,221,225,64,221,15,66,221,36,66,221,15,68,221,36,68,221,15,70,221,36,70,221,15,72,221,36,72,221,6,80,221,
35,80,221,65,80,221,96,80,221,16,0,222,48,0,222,80,0,222,112,0,222,144,0,222,16,16,222,48,16,222,76,16,222,99,16,222,16,32,222,44,32,222,69,32,222,16,64,222,
48,64,222,80,64,222,108,64,222,132,64,222,15,80,222,39,80,222,66,80,222,7,96,222,34,96,222,16,128,222,48,128,222,80,128,222,108,128,222,132,128,222,15,144,222,39,144,222,
66,144,222,7,160,222,34,160,222,16,192,222,48,192,222,75,192,222,101,192,222,128,192,222,7,208,222,34,208,222,2,224,222,16,0,223,48,0,223,75,0,223,101,0,223,128,0,223,
7,16,223,34,16,223,2,32,223,16,64,223,44,64,223,70,64,223,98,64,223,3,80,223,16,0,224,48,0,224,78,0,224,16,16,224,40,16,224,16,32,224,35,32,224,16,64,224,
48,64,224,67,64,224,11,80,224,32,80,224,4,96,224,16,128,224,48,128,224,67,128,224,11,144,224,32,144,224,4,160,224,16,192,224,41,192,224,4,208,224,0,224,224,16,0,225,
41,0,225,4,16,225,0,32,225,15,64,225,36,64,225,1,80,225,16,0,226,46,0,226,16,16,226,11,32,226,16,64,226,35,64,226,6,80,226,1,96,226,16,128,226,35,128,226,
6,144,226,1,160,226,16,192,226,1,208,226,16,0,227,1,16,227,11,64,227
};
+211
View File
@@ -0,0 +1,211 @@
// File: basisu_astc_hdr_core.h
#pragma once
namespace basist
{
struct astc_blk
{
uint8_t m_vals[16];
};
// ASTC_HDR_MAX_VAL is the maximum color component value that can be encoded.
// If the input has values higher than this, they need to be linearly scaled so all values are between [0,ASTC_HDR_MAX_VAL], and the linear scaling inverted in the shader.
const float ASTC_HDR_MAX_VAL = 65216.0f; // actually MAX_QLOG12_VAL
// Maximum usable QLOG encodings, and their floating point equivalent values, that don't result in NaN/Inf's.
const uint32_t MAX_QLOG7 = 123;
//const float MAX_QLOG7_VAL = 55296.0f;
const uint32_t MAX_QLOG8 = 247;
//const float MAX_QLOG8_VAL = 60416.0f;
const uint32_t MAX_QLOG9 = 495;
//const float MAX_QLOG9_VAL = 62976.0f;
const uint32_t MAX_QLOG10 = 991;
//const float MAX_QLOG10_VAL = 64256.0f;
const uint32_t MAX_QLOG11 = 1983;
//const float MAX_QLOG11_VAL = 64896.0f;
const uint32_t MAX_QLOG12 = 3967;
//const float MAX_QLOG12_VAL = 65216.0f;
const uint32_t MAX_QLOG16 = 63487;
const float MAX_QLOG16_VAL = 65504.0f;
// TODO: Should be called something like "NUM_MODE11_ENDPOINT_VALUES"
const uint32_t NUM_MODE11_ENDPOINTS = 6, NUM_MODE7_ENDPOINTS = 4;
// This is not lossless
inline half_float qlog_to_half(uint32_t qlog, uint32_t bits)
{
assert((bits >= 7U) && (bits <= 16U));
assert(qlog < (1U << bits));
int C = qlog << (16 - bits);
return astc_helpers::qlog16_to_half(C);
}
void astc_hdr_core_init();
void decode_mode7_to_qlog12_ise20(
const uint8_t* pEndpoints,
int e[2][3],
int* pScale);
bool decode_mode7_to_qlog12(
const uint8_t* pEndpoints,
int e[2][3],
int* pScale,
uint32_t ise_endpoint_range);
void decode_mode11_to_qlog12_ise20(
const uint8_t* pEndpoints,
int e[2][3]);
bool decode_mode11_to_qlog12(
const uint8_t* pEndpoints,
int e[2][3],
uint32_t ise_endpoint_range);
bool transcode_bc6h_1subset(half_float h_e[3][2], const astc_helpers::log_astc_block& best_blk, bc6h_block& transcoded_bc6h_blk);
bool transcode_bc6h_2subsets(uint32_t common_part_index, const astc_helpers::log_astc_block& best_blk, bc6h_block& transcoded_bc6h_blk);
bool astc_hdr_transcode_to_bc6h(const astc_blk& src_blk, bc6h_block& dst_blk);
bool astc_hdr_transcode_to_bc6h(const astc_helpers::log_astc_block& log_blk, bc6h_block& dst_blk);
namespace astc_6x6_hdr
{
const uint32_t MAX_ASTC_HDR_6X6_DIM = 32768;
const int32_t REUSE_MAX_BUFFER_ROWS = 5; // 1+-(-4), so we need to buffer 5 rows total
struct block_mode_desc
{
bool m_dp;
uint32_t m_cem;
uint32_t m_num_partitions;
uint32_t m_grid_x;
uint32_t m_grid_y;
// the coding ISE ranges (which may not be valid ASTC ranges for this configuration)
uint32_t m_endpoint_ise_range;
uint32_t m_weight_ise_range;
// the physical/output ASTC decompression ISE ranges (i.e. what the decompressor must output)
uint32_t m_transcode_endpoint_ise_range;
uint32_t m_transcode_weight_ise_range;
uint32_t m_flags;
int m_dp_channel;
};
// Lack of level flag indicates level 3+
const uint32_t BASIST_HDR_6X6_LEVEL0 = 1;
const uint32_t BASIST_HDR_6X6_LEVEL1 = 2;
const uint32_t BASIST_HDR_6X6_LEVEL2 = 4;
const uint32_t TOTAL_BLOCK_MODE_DECS = 75;
extern const block_mode_desc g_block_mode_descs[TOTAL_BLOCK_MODE_DECS];
const uint32_t UASTC_6x6_HDR_SIG0 = 0xABCD; // original release (v1.6)
const uint32_t UASTC_6x6_HDR_SIG1 = 0xABCE; // 2x2->4x4 weight grid upsampling change, not backwards compatible with older decoders
void copy_weight_grid(bool dual_plane, uint32_t grid_x, uint32_t grid_y, const uint8_t* transcode_weights, astc_helpers::log_astc_block& decomp_blk, bool orig_behavior);
enum class encoding_type
{
cInvalid = -1,
cRun = 0,
cSolid = 1,
cReuse = 2,
cBlock = 3,
cTotal
};
const uint32_t REUSE_XY_DELTA_BITS = 5;
const uint32_t NUM_REUSE_XY_DELTAS = 1 << REUSE_XY_DELTA_BITS;
struct reuse_xy_delta
{
int8_t m_x, m_y;
};
extern const reuse_xy_delta g_reuse_xy_deltas[NUM_REUSE_XY_DELTAS];
const uint32_t RUN_CODE = 0b000, RUN_CODE_LEN = 3;
const uint32_t SOLID_CODE = 0b100, SOLID_CODE_LEN = 3;
const uint32_t REUSE_CODE = 0b10, REUSE_CODE_LEN = 2;
const uint32_t BLOCK_CODE = 0b1, BLOCK_CODE_LEN = 1;
enum class endpoint_mode
{
cInvalid = -1,
cRaw = 0,
cUseLeft,
cUseUpper,
cUseLeftDelta,
cUseUpperDelta,
cTotal
};
enum class block_mode
{
cInvalid = -1,
cBMTotalModes = TOTAL_BLOCK_MODE_DECS
};
const uint32_t NUM_ENDPOINT_DELTA_BITS = 5;
const uint32_t NUM_UNIQUE_PARTITIONS2 = 521;
extern const uint32_t g_part2_unique_index_to_seed[NUM_UNIQUE_PARTITIONS2];
const uint32_t NUM_UNIQUE_PARTITIONS3 = 333;
extern const uint32_t g_part3_unique_index_to_seed[NUM_UNIQUE_PARTITIONS3];
bool decode_values(basist::bitwise_decoder& decoder, uint32_t total_values, uint32_t ise_range, uint8_t* pValues);
void requantize_astc_weights(uint32_t n, const uint8_t* pSrc_ise_vals, uint32_t from_ise_range, uint8_t* pDst_ise_vals, uint32_t to_ise_range);
void requantize_ise_endpoints(uint32_t cem, uint32_t src_ise_endpoint_range, const uint8_t* pSrc_endpoints, uint32_t dst_ise_endpoint_range, uint8_t* pDst_endpoints);
const uint32_t BC6H_NUM_DIFF_ENDPOINT_MODES_TO_TRY_2 = 2;
const uint32_t BC6H_NUM_DIFF_ENDPOINT_MODES_TO_TRY_4 = 4;
const uint32_t BC6H_NUM_DIFF_ENDPOINT_MODES_TO_TRY_9 = 9;
struct fast_bc6h_params
{
uint32_t m_num_diff_endpoint_modes_to_try;
uint32_t m_max_2subset_pats_to_try;
bool m_hq_ls;
bool m_brute_force_weight4_assignment;
fast_bc6h_params()
{
init();
}
void init()
{
m_hq_ls = true;
m_num_diff_endpoint_modes_to_try = BC6H_NUM_DIFF_ENDPOINT_MODES_TO_TRY_2;
m_max_2subset_pats_to_try = 1;
m_brute_force_weight4_assignment = false;
}
};
// Encodes to BC6H (unsigned variant).
// pPixels: pointer to 16 RGB half-float/FP16 values (48 total half-floats), in raster order.
// Max encodable value is (in float) basist::MAX_HALF_FLOAT.
void fast_encode_bc6h(const basist::half_float* pPixels, basist::bc6h_block* pBlock, const fast_bc6h_params &params);
bool decode_6x6_hdr(const uint8_t* pComp_data, uint32_t comp_data_size, basisu::vector2D<astc_helpers::astc_block>& decoded_blocks, uint32_t& width, uint32_t& height);
} // namespace astc_6x6_hdr
} // namespace basist
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+257
View File
@@ -0,0 +1,257 @@
static const uint8_t g_etc1_mod_tabs[255][8] = {
{0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,1,1,},
{0,0,0,0,0,0,0,1,},
{0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,1,},
{0,0,0,0,0,0,1,1,},
{0,0,0,0,0,1,1,1,},
{0,0,0,0,0,1,1,1,},
{0,0,0,0,1,1,1,1,},
{0,0,0,0,1,1,1,1,},
{0,0,0,0,1,1,1,1,},
{0,0,0,0,1,1,1,1,},
{0,0,0,1,1,1,1,1,},
{0,0,0,1,1,1,1,1,},
{0,0,0,1,1,1,1,1,},
{0,0,0,1,1,1,1,2,},
{0,0,0,1,1,1,2,2,},
{0,0,0,1,1,1,2,2,},
{0,0,0,1,1,2,2,2,},
{0,0,1,1,1,2,2,2,},
{0,0,1,1,1,2,2,2,},
{0,0,1,1,1,2,2,2,},
{0,0,1,1,1,2,2,2,},
{0,0,1,1,2,2,2,2,},
{0,0,1,1,2,2,2,2,},
{0,0,1,1,2,2,2,2,},
{0,0,1,1,2,2,2,2,},
{0,0,1,1,2,2,2,2,},
{0,0,1,1,2,2,2,3,},
{0,0,1,1,2,2,3,3,},
{0,0,1,2,2,2,3,3,},
{0,0,1,2,2,2,3,3,},
{0,0,1,2,2,2,3,3,},
{0,1,1,2,2,3,3,3,},
{0,1,1,2,2,3,3,3,},
{0,1,1,2,2,3,3,3,},
{0,1,1,2,2,3,3,3,},
{0,1,1,2,2,3,3,3,},
{0,1,1,2,2,3,3,3,},
{0,1,1,2,2,3,3,3,},
{0,1,1,2,3,3,3,3,},
{0,1,1,2,3,3,3,3,},
{0,1,1,2,3,3,3,3,},
{0,1,2,2,3,3,3,3,},
{0,1,2,2,3,3,3,4,},
{0,1,2,2,3,3,3,4,},
{0,1,2,2,3,3,4,4,},
{0,1,2,2,3,3,4,4,},
{0,1,2,2,3,3,4,4,},
{0,1,2,2,3,3,4,4,},
{0,1,2,2,3,3,4,4,},
{0,1,2,2,3,4,4,4,},
{0,1,2,3,3,4,4,4,},
{0,1,2,3,3,4,4,4,},
{0,1,2,3,3,4,4,4,},
{0,1,2,3,3,4,4,4,},
{0,1,2,3,3,4,4,4,},
{0,1,2,3,3,4,4,4,},
{0,1,2,3,3,4,4,4,},
{0,1,2,3,3,4,4,4,},
{0,1,2,3,3,4,4,4,},
{0,1,2,3,3,4,4,4,},
{0,1,2,3,4,4,4,4,},
{0,1,2,3,4,4,4,5,},
{0,1,2,3,4,4,4,5,},
{0,1,2,3,4,4,4,5,},
{0,1,2,3,4,4,5,5,},
{0,1,2,3,4,4,5,5,},
{0,1,2,3,4,4,5,5,},
{0,1,2,3,4,4,5,5,},
{0,2,2,3,4,4,5,5,},
{0,2,2,3,4,4,5,5,},
{0,2,2,3,4,4,5,5,},
{0,2,3,3,4,5,5,5,},
{0,2,3,3,4,5,5,5,},
{0,2,3,3,4,5,5,5,},
{0,2,3,3,4,5,5,5,},
{1,2,3,3,4,5,5,5,},
{1,2,3,3,4,5,5,5,},
{1,2,3,4,4,5,5,5,},
{1,2,3,4,4,5,5,5,},
{1,2,3,4,4,5,5,5,},
{1,2,3,4,4,5,5,5,},
{1,2,3,4,4,5,5,5,},
{1,2,3,4,4,5,5,5,},
{1,2,3,4,4,5,5,5,},
{1,2,3,4,4,5,5,5,},
{1,2,3,4,4,5,5,6,},
{1,2,3,4,5,5,5,6,},
{1,2,3,4,5,5,5,6,},
{1,2,3,4,5,5,5,6,},
{1,2,3,4,5,5,6,6,},
{1,2,3,4,5,5,6,6,},
{1,2,3,4,5,5,6,6,},
{1,2,3,4,5,5,6,6,},
{1,2,3,4,5,5,6,6,},
{1,2,3,4,5,5,6,6,},
{1,2,3,4,5,5,6,6,},
{1,2,3,4,5,5,6,6,},
{1,2,3,4,5,5,6,6,},
{1,2,3,4,5,6,6,6,},
{1,2,3,4,5,6,6,6,},
{1,2,3,4,5,6,6,6,},
{1,2,3,4,5,6,6,6,},
{1,2,3,4,5,6,6,6,},
{1,2,3,4,5,6,6,6,},
{1,2,3,4,5,6,6,6,},
{1,2,3,4,5,6,6,6,},
{1,2,4,4,5,6,6,6,},
{1,2,4,4,5,6,6,6,},
{1,2,4,4,5,6,6,6,},
{1,2,4,4,5,6,6,6,},
{1,2,4,5,5,6,6,6,},
{1,2,4,5,5,6,6,6,},
{1,3,4,5,5,6,6,6,},
{1,3,4,5,5,6,6,6,},
{1,3,4,5,5,6,6,6,},
{1,3,4,5,5,6,6,6,},
{1,3,4,5,5,6,6,6,},
{1,3,4,5,5,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,6,},
{1,3,4,5,6,6,6,7,},
{1,3,4,5,6,6,6,7,},
{1,3,4,5,6,6,6,7,},
{1,3,4,5,6,6,6,7,},
{1,3,4,5,6,6,6,7,},
{1,3,4,5,6,6,6,7,},
{1,3,4,5,6,6,6,7,},
{1,3,4,5,6,6,7,7,},
{1,3,4,5,6,6,7,7,},
{1,3,4,5,6,6,7,7,},
{1,3,4,5,6,6,7,7,},
{1,3,4,5,6,6,7,7,},
{1,3,4,6,6,6,7,7,},
{1,3,5,6,6,6,7,7,},
{1,3,5,6,6,6,7,7,},
{1,3,5,6,6,6,7,7,},
{2,3,5,6,6,6,7,7,},
{2,3,5,6,6,6,7,7,},
{2,3,5,6,6,6,7,7,},
{2,3,5,6,6,6,7,7,},
{2,3,5,6,6,7,7,7,},
{2,3,5,6,6,7,7,7,},
{2,3,5,6,6,7,7,7,},
{2,3,5,6,6,7,7,7,},
{2,3,5,6,6,7,7,7,},
{2,3,5,6,6,7,7,7,},
{2,3,5,6,6,7,7,7,},
{2,3,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,6,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,5,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,4,6,6,7,7,7,7,},
{2,5,6,6,7,7,7,7,},
{2,5,6,6,7,7,7,7,},
{2,5,6,6,7,7,7,7,},
{2,5,6,6,7,7,7,7,},
{2,5,6,6,7,7,7,7,},
{2,5,6,7,7,7,7,7,},
{2,5,6,7,7,7,7,7,},
{2,5,6,7,7,7,7,7,},
{2,5,6,7,7,7,7,7,},
{2,5,6,7,7,7,7,7,},
{2,5,6,7,7,7,7,7,},
{2,5,6,7,7,7,7,7,},
{2,5,6,7,7,7,7,7,}
};
+119 -16
View File
@@ -1,5 +1,5 @@
// basis_file_headers.h
// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -21,10 +21,10 @@ namespace basist
enum basis_slice_desc_flags
{
cSliceDescFlagsHasAlpha = 1,
// Video only: Frame doesn't refer to previous frame (no usage of conditional replenishment pred symbols)
// Currently the first frame is always an I-Frame, all subsequent frames are P-Frames. This will eventually be changed to periodic I-Frames.
cSliceDescFlagsFrameIsIFrame = 2
cSliceDescFlagsFrameIsIFrame = 2
};
#pragma pack(push)
@@ -38,7 +38,7 @@ namespace basist
basisu::packed_uint<2> m_orig_width; // The original image width (may not be a multiple of 4 pixels)
basisu::packed_uint<2> m_orig_height; // The original image height (may not be a multiple of 4 pixels)
basisu::packed_uint<2> m_num_blocks_x; // The slice's block X dimensions. Each block is 4x4 pixels. The slice's pixel resolution may or may not be a power of 2.
basisu::packed_uint<2> m_num_blocks_x; // The slice's block X dimensions. Each block is 4x4 or 6x6 pixels. The slice's pixel resolution may or may not be a power of 2.
basisu::packed_uint<2> m_num_blocks_y; // The slice's block Y dimensions.
basisu::packed_uint<4> m_file_ofs; // Offset from the start of the file to the start of the slice's data
@@ -51,20 +51,20 @@ namespace basist
enum basis_header_flags
{
// Always set for ETC1S files. Not set for UASTC files.
cBASISHeaderFlagETC1S = 1,
cBASISHeaderFlagETC1S = 1,
// Set if the texture had to be Y flipped before encoding. The actual interpretation of this (is Y up or down?) is up to the user.
cBASISHeaderFlagYFlipped = 2,
cBASISHeaderFlagYFlipped = 2,
// Set if any slices contain alpha (for ETC1S, if the odd slices contain alpha data)
cBASISHeaderFlagHasAlphaSlices = 4,
cBASISHeaderFlagHasAlphaSlices = 4,
// For ETC1S files, this will be true if the file utilizes a codebook from another .basis file.
cBASISHeaderFlagUsesGlobalCodebook = 8,
cBASISHeaderFlagUsesGlobalCodebook = 8,
// Set if the texture data is sRGB, otherwise it's linear.
// In reality, we have no idea if the texture data is actually linear or sRGB. This is the m_perceptual parameter passed to the compressor.
cBASISHeaderFlagSRGB = 16,
cBASISHeaderFlagSRGB = 16,
};
// The image type field attempts to describe how to interpret the image data in a Basis file.
@@ -72,9 +72,9 @@ namespace basist
// We do make sure the various constraints are followed (2DArray/cubemap/videoframes/volume implies that each image has the same resolution and # of mipmap levels, etc., cubemap implies that the # of image slices is a multiple of 6)
enum basis_texture_type
{
cBASISTexType2D = 0, // An arbitrary array of 2D RGB or RGBA images with optional mipmaps, array size = # images, each image may have a different resolution and # of mipmap levels
cBASISTexType2D = 0, // An arbitrary array of 2D RGB or RGBA images with optional mipmaps, array size = # images, each image may have a different resolution and # of mipmap levels
cBASISTexType2DArray = 1, // An array of 2D RGB or RGBA images with optional mipmaps, array size = # images, each image has the same resolution and mipmap levels
cBASISTexTypeCubemapArray = 2, // an array of cubemap levels, total # of images must be divisable by 6, in X+, X-, Y+, Y-, Z+, Z- order, with optional mipmaps
cBASISTexTypeCubemapArray = 2, // an array of cubemap levels, total # of images must be divisable by 6, in X+, X-, Y+, Y-, Z+, Z- order, with optional mipmaps
cBASISTexTypeVideoFrames = 3, // An array of 2D video frames, with optional mipmaps, # frames = # images, each image has the same resolution and # of mipmap levels
cBASISTexTypeVolume = 4, // A 3D texture with optional mipmaps, Z dimension = # images, each image has the same resolution and # of mipmap levels
@@ -88,10 +88,113 @@ namespace basist
enum class basis_tex_format
{
// Original LDR formats
cETC1S = 0,
cUASTC4x4 = 1
cUASTC_LDR_4x4 = 1,
// HDR formats
cUASTC_HDR_4x4 = 2,
cASTC_HDR_6x6 = 3,
cUASTC_HDR_6x6_INTERMEDIATE = 4, // TODO: rename to UASTC_HDR_6x6
// XUASTC (supercompressed) LDR variants (the standard ASTC block sizes)
cXUASTC_LDR_4x4 = 5,
cXUASTC_LDR_5x4 = 6,
cXUASTC_LDR_5x5 = 7,
cXUASTC_LDR_6x5 = 8,
cXUASTC_LDR_6x6 = 9,
cXUASTC_LDR_8x5 = 10,
cXUASTC_LDR_8x6 = 11,
cXUASTC_LDR_10x5 = 12,
cXUASTC_LDR_10x6 = 13,
cXUASTC_LDR_8x8 = 14,
cXUASTC_LDR_10x8 = 15,
cXUASTC_LDR_10x10 = 16,
cXUASTC_LDR_12x10 = 17,
cXUASTC_LDR_12x12 = 18,
// Standard (non-supercompressed) ASTC LDR variants (the standard ASTC block sizes)
cASTC_LDR_4x4 = 19,
cASTC_LDR_5x4 = 20,
cASTC_LDR_5x5 = 21,
cASTC_LDR_6x5 = 22,
cASTC_LDR_6x6 = 23,
cASTC_LDR_8x5 = 24,
cASTC_LDR_8x6 = 25,
cASTC_LDR_10x5 = 26,
cASTC_LDR_10x6 = 27,
cASTC_LDR_8x8 = 28,
cASTC_LDR_10x8 = 29,
cASTC_LDR_10x10 = 30,
cASTC_LDR_12x10 = 31,
cASTC_LDR_12x12 = 32,
cTotalFormats
};
// True if the basis_tex_format is XUASTC LDR 4x4-12x12.
inline bool basis_tex_format_is_xuastc_ldr(basis_tex_format tex_fmt)
{
return ((uint32_t)tex_fmt >= (uint32_t)basis_tex_format::cXUASTC_LDR_4x4) && ((uint32_t)tex_fmt <= (uint32_t)basis_tex_format::cXUASTC_LDR_12x12);
}
// True if the basis_tex_format is ASTC LDR 4x4-12x12.
inline bool basis_tex_format_is_astc_ldr(basis_tex_format tex_fmt)
{
return ((uint32_t)tex_fmt >= (uint32_t)basis_tex_format::cASTC_LDR_4x4) && ((uint32_t)tex_fmt <= (uint32_t)basis_tex_format::cASTC_LDR_12x12);
}
inline void get_basis_tex_format_block_size(basis_tex_format tex_fmt, uint32_t &width, uint32_t &height)
{
switch (tex_fmt)
{
case basis_tex_format::cETC1S: width = 4; height = 4; break;
case basis_tex_format::cUASTC_LDR_4x4: width = 4; height = 4; break;
case basis_tex_format::cUASTC_HDR_4x4: width = 4; height = 4; break;
case basis_tex_format::cASTC_HDR_6x6: width = 6; height = 6; break;
case basis_tex_format::cUASTC_HDR_6x6_INTERMEDIATE: width = 6; height = 6; break;
case basis_tex_format::cXUASTC_LDR_4x4: width = 4; height = 4; break;
case basis_tex_format::cXUASTC_LDR_5x4: width = 5; height = 4; break;
case basis_tex_format::cXUASTC_LDR_5x5: width = 5; height = 5; break;
case basis_tex_format::cXUASTC_LDR_6x5: width = 6; height = 5; break;
case basis_tex_format::cXUASTC_LDR_6x6: width = 6; height = 6; break;
case basis_tex_format::cXUASTC_LDR_8x5: width = 8; height = 5; break;
case basis_tex_format::cXUASTC_LDR_8x6: width = 8; height = 6; break;
case basis_tex_format::cXUASTC_LDR_10x5: width = 10; height = 5; break;
case basis_tex_format::cXUASTC_LDR_10x6: width = 10; height = 6; break;
case basis_tex_format::cXUASTC_LDR_8x8: width = 8; height = 8; break;
case basis_tex_format::cXUASTC_LDR_10x8: width = 10; height = 8; break;
case basis_tex_format::cXUASTC_LDR_10x10: width = 10; height = 10; break;
case basis_tex_format::cXUASTC_LDR_12x10: width = 12; height = 10; break;
case basis_tex_format::cXUASTC_LDR_12x12: width = 12; height = 12; break;
case basis_tex_format::cASTC_LDR_4x4: width = 4; height = 4; break;
case basis_tex_format::cASTC_LDR_5x4: width = 5; height = 4; break;
case basis_tex_format::cASTC_LDR_5x5: width = 5; height = 5; break;
case basis_tex_format::cASTC_LDR_6x5: width = 6; height = 5; break;
case basis_tex_format::cASTC_LDR_6x6: width = 6; height = 6; break;
case basis_tex_format::cASTC_LDR_8x5: width = 8; height = 5; break;
case basis_tex_format::cASTC_LDR_8x6: width = 8; height = 6; break;
case basis_tex_format::cASTC_LDR_10x5: width = 10; height = 5; break;
case basis_tex_format::cASTC_LDR_10x6: width = 10; height = 6; break;
case basis_tex_format::cASTC_LDR_8x8: width = 8; height = 8; break;
case basis_tex_format::cASTC_LDR_10x8: width = 10; height = 8; break;
case basis_tex_format::cASTC_LDR_10x10: width = 10; height = 10; break;
case basis_tex_format::cASTC_LDR_12x10: width = 12; height = 10; break;
case basis_tex_format::cASTC_LDR_12x12: width = 12; height = 12; break;
default:
assert(0);
width = 0;
height = 0;
break;
}
}
struct basis_file_header
{
enum
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+509 -89
View File
@@ -1,5 +1,5 @@
// basisu_transcoder.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,6 +13,8 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also see basis_tex_format in basisu_file_headers.h (TODO: Perhaps move key definitions into here.)
#pragma once
// By default KTX2 support is enabled to simplify compilation. This implies the need for the Zstandard library (which we distribute as a single source file in the "zstd" directory) by default.
@@ -27,11 +29,6 @@
#define BASISD_SUPPORT_KTX2_ZSTD 1
#endif
// Set BASISU_FORCE_DEVEL_MESSAGES to 1 to enable debug printf()'s whenever an error occurs, for easier debugging during development.
#ifndef BASISU_FORCE_DEVEL_MESSAGES
#define BASISU_FORCE_DEVEL_MESSAGES 0
#endif
#include "basisu_transcoder_internal.h"
#include "basisu_transcoder_uastc.h"
#include "basisu_file_headers.h"
@@ -55,7 +52,7 @@ namespace basist
cTFETC2_RGBA = 1, // Opaque+alpha, ETC2_EAC_A8 block followed by a ETC1 block, alpha channel will be opaque for opaque .basis files
// BC1-5, BC7 (desktop, some mobile devices)
cTFBC1_RGB = 2, // Opaque only, no punchthrough alpha support yet, transcodes alpha slice if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
cTFBC1_RGB = 2, // Opaque only, no punchthrough alpha support yet, transcodes alpha slice if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
cTFBC3_RGBA = 3, // Opaque+alpha, BC4 followed by a BC1 block, alpha channel will be opaque for opaque .basis files
cTFBC4_R = 4, // Red only, alpha slice is transcoded to output if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
cTFBC5_RG = 5, // XY: Two BC4 blocks, X=R and Y=Alpha, .basis file should have alpha data (if not Y will be all 255's)
@@ -63,10 +60,11 @@ namespace basist
// PVRTC1 4bpp (mobile, PowerVR devices)
cTFPVRTC1_4_RGB = 8, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified, nearly lowest quality of any texture format.
cTFPVRTC1_4_RGBA = 9, // Opaque+alpha, most useful for simple opacity maps. If .basis file doesn't have alpha cTFPVRTC1_4_RGB will be used instead. Lowest quality of any supported texture format.
cTFPVRTC1_4_RGBA = 9, // Opaque+alpha, most useful for simple opacity maps. If .basis file doesn't have alpha cTFPVRTC1_4_RGB will be used instead. Lowest quality of any supported texture format.
// ASTC (mobile, Intel devices, hopefully all desktop GPU's one day)
cTFASTC_4x4_RGBA = 10, // Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files. Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.
// ASTC (mobile, some Intel CPU's, hopefully all desktop GPU's one day)
cTFASTC_LDR_4x4_RGBA = 10, // LDR. Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files.
// LDR: Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.
// ATC (mobile, Adreno devices, this is a niche format)
cTFATC_RGB = 11, // Opaque, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. ATI ATC (GL_ATC_RGB_AMD)
@@ -74,24 +72,51 @@ namespace basist
// FXT1 (desktop, Intel devices, this is a super obscure format)
cTFFXT1_RGB = 17, // Opaque only, uses exclusively CC_MIXED blocks. Notable for having a 8x4 block size. GL_3DFX_texture_compression_FXT1 is supported on Intel integrated GPU's (such as HD 630).
// Punch-through alpha is relatively easy to support, but full alpha is harder. This format is only here for completeness so opaque-only is fine for now.
// See the BASISU_USE_ORIGINAL_3DFX_FXT1_ENCODING macro in basisu_transcoder_internal.h.
// Punch-through alpha is relatively easy to support, but full alpha is harder. This format is only here for completeness so opaque-only is fine for now.
// See the BASISU_USE_ORIGINAL_3DFX_FXT1_ENCODING macro in basisu_transcoder_internal.h.
cTFPVRTC2_4_RGB = 18, // Opaque-only, almost BC1 quality, much faster to transcode and supports arbitrary texture dimensions (unlike PVRTC1 RGB).
cTFPVRTC2_4_RGBA = 19, // Opaque+alpha, slower to encode than cTFPVRTC2_4_RGB. Premultiplied alpha is highly recommended, otherwise the color channel can leak into the alpha channel on transparent blocks.
cTFPVRTC2_4_RGB = 18, // Opaque-only, almost BC1 quality, much faster to transcode and supports arbitrary texture dimensions (unlike PVRTC1 RGB).
cTFPVRTC2_4_RGBA = 19, // Opaque+alpha, slower to encode than cTFPVRTC2_4_RGB. Premultiplied alpha is highly recommended, otherwise the color channel can leak into the alpha channel on transparent blocks.
cTFETC2_EAC_R11 = 20, // R only (ETC2 EAC R11 unsigned)
cTFETC2_EAC_RG11 = 21, // RG only (ETC2 EAC RG11 unsigned), R=opaque.r, G=alpha - for tangent space normal maps
cTFETC2_EAC_R11 = 20, // R only (ETC2 EAC R11 unsigned)
cTFETC2_EAC_RG11 = 21, // RG only (ETC2 EAC RG11 unsigned), R=opaque.r, G=alpha - for tangent space normal maps
cTFBC6H = 22, // HDR, RGB only, unsigned
cTFASTC_HDR_4x4_RGBA = 23, // HDR, RGBA (currently UASTC HDR 4x4 encoders are only RGB), unsigned
// Uncompressed (raw pixel) formats
cTFRGBA32 = 13, // 32bpp RGBA image stored in raster (not block) order in memory, R is first byte, A is last byte.
cTFRGB565 = 14, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 11
cTFBGR565 = 15, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 0
// Note these uncompressed formats (RGBA32, 565, and 4444) can only be transcoded to from LDR input files (ETC1S or UASTC LDR).
cTFRGBA32 = 13, // 32bpp RGBA image stored in raster (not block) order in memory, R is first byte, A is last byte.
cTFRGB565 = 14, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 11
cTFBGR565 = 15, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 0
cTFRGBA4444 = 16, // 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 0
// Note these uncompressed formats (HALF and 9E5) can only be transcoded to from HDR input files (UASTC HDR 4x4 or ASTC HDR 6x6).
cTFRGB_HALF = 24, // 48bpp RGB half (16-bits/component, 3 components)
cTFRGBA_HALF = 25, // 64bpp RGBA half (16-bits/component, 4 components) (A will always currently 1.0, UASTC_HDR doesn't support alpha)
cTFRGB_9E5 = 26, // 32bpp RGB 9E5 (shared exponent, positive only, see GL_EXT_texture_shared_exponent)
cTFTotalTextureFormats = 22,
cTFASTC_HDR_6x6_RGBA = 27, // HDR, RGBA (currently our ASTC HDR 6x6 encodes are only RGB), unsigned
// Old enums for compatibility with code compiled against previous versions
// The remaining LDR ASTC block sizes, excluding 4x4 (which is above). There are 14 total valid ASTC LDR/HDR block sizes.
cTFASTC_LDR_5x4_RGBA = 28,
cTFASTC_LDR_5x5_RGBA = 29,
cTFASTC_LDR_6x5_RGBA = 30,
cTFASTC_LDR_6x6_RGBA = 31,
cTFASTC_LDR_8x5_RGBA = 32,
cTFASTC_LDR_8x6_RGBA = 33,
cTFASTC_LDR_10x5_RGBA = 34,
cTFASTC_LDR_10x6_RGBA = 35,
cTFASTC_LDR_8x8_RGBA = 36,
cTFASTC_LDR_10x8_RGBA = 37,
cTFASTC_LDR_10x10_RGBA = 38,
cTFASTC_LDR_12x10_RGBA = 39,
cTFASTC_LDR_12x12_RGBA = 40,
cTFTotalTextureFormats = 41,
// ----- The following are old/legacy enums for compatibility with code compiled against previous versions
cTFETC1 = cTFETC1_RGB,
cTFETC2 = cTFETC2_RGBA,
cTFBC1 = cTFBC1_RGB,
@@ -99,31 +124,45 @@ namespace basist
cTFBC4 = cTFBC4_R,
cTFBC5 = cTFBC5_RG,
// Previously, the caller had some control over which BC7 mode the transcoder output. We've simplified this due to UASTC, which supports numerous modes.
cTFBC7_M6_RGB = cTFBC7_RGBA, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. Highest quality of all the non-ETC1 formats.
cTFBC7_M5_RGBA = cTFBC7_RGBA, // Opaque+alpha, alpha channel will be opaque for opaque .basis files
// Previously, the caller had some control over which BC7 mode the transcoder output. We've simplified this due to UASTC LDR 4x4, which supports numerous modes.
cTFBC7_M6_RGB = cTFBC7_RGBA, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. Highest quality of all the non-ETC1 formats.
cTFBC7_M5_RGBA = cTFBC7_RGBA, // Opaque+alpha, alpha channel will be opaque for opaque .basis files
cTFBC7_M6_OPAQUE_ONLY = cTFBC7_RGBA,
cTFBC7_M5 = cTFBC7_RGBA,
cTFBC7_ALT = 7,
cTFASTC_4x4 = cTFASTC_4x4_RGBA,
cTFASTC_4x4 = cTFASTC_LDR_4x4_RGBA,
cTFATC_RGBA_INTERPOLATED_ALPHA = cTFATC_RGBA,
cTFASTC_4x4_RGBA = cTFASTC_LDR_4x4_RGBA
};
// For compressed texture formats, this returns the # of bytes per block. For uncompressed, it returns the # of bytes per pixel.
// NOTE: Previously, this function was called basis_get_bytes_per_block(), and it always returned 16*bytes_per_pixel for uncompressed formats which was confusing.
uint32_t basis_get_bytes_per_block_or_pixel(transcoder_texture_format fmt);
// Returns format's name in ASCII
// Returns the transcoder texture format's name in ASCII
const char* basis_get_format_name(transcoder_texture_format fmt);
// Returns basis texture format name in ASCII
const char* basis_get_tex_format_name(basis_tex_format fmt);
// Returns block format name in ASCII
const char* basis_get_block_format_name(block_format fmt);
// Returns true if the format supports an alpha channel.
bool basis_transcoder_format_has_alpha(transcoder_texture_format fmt);
// Returns true if the format is HDR.
bool basis_transcoder_format_is_hdr(transcoder_texture_format fmt);
// Returns true if the format is LDR.
inline bool basis_transcoder_format_is_ldr(transcoder_texture_format fmt) { return !basis_transcoder_format_is_hdr(fmt); }
// Returns true if the format is an LDR or HDR ASTC format.
bool basis_is_transcoder_texture_format_astc(transcoder_texture_format fmt);
// Returns the basisu::texture_format corresponding to the specified transcoder_texture_format.
basisu::texture_format basis_get_basisu_texture_format(transcoder_texture_format fmt);
@@ -137,22 +176,42 @@ namespace basist
uint32_t basis_get_uncompressed_bytes_per_pixel(transcoder_texture_format fmt);
// Returns the block width for the specified texture format, which is currently either 4 or 8 for FXT1.
uint32_t basis_get_block_width(transcoder_texture_format tex_type);
uint32_t basis_get_block_width(transcoder_texture_format fmt);
// Returns the block height for the specified texture format, which is currently always 4.
uint32_t basis_get_block_height(transcoder_texture_format tex_type);
uint32_t basis_get_block_height(transcoder_texture_format fmt);
// ASTC/XUASTC LDR formats only: Given a basis_tex_format (mode or codec), return the corresponding ASTC basisu::texture_format with the proper block size from 4x4-12x12.
basisu::texture_format basis_get_texture_format_from_xuastc_or_astc_ldr_basis_tex_format(basis_tex_format fmt);
// For any given basis_tex_format (mode or codec), return the LDR/HDR ASTC transcoder texture format with the proper block size.
transcoder_texture_format basis_get_transcoder_texture_format_from_basis_tex_format(basis_tex_format fmt);
// basis_get_transcoder_texture_format_from_xuastc_or_astc_ldr_basis_tex_format: same as basis_get_transcoder_texture_format_from_basis_tex_format (TODO: remove)
transcoder_texture_format basis_get_transcoder_texture_format_from_xuastc_or_astc_ldr_basis_tex_format(basis_tex_format fmt);
// Returns true if the specified format was enabled at compile time.
// Returns true if the specified format was enabled at compile time, and is supported for the specific basis/ktx2 texture format (ETC1S, UASTC, or UASTC HDR, or XUASTC LDR 4x4-12x12).
// For XUASTC the ASTC block size must match the transcoder_texture_format's ASTC block size.
bool basis_is_format_supported(transcoder_texture_format tex_type, basis_tex_format fmt = basis_tex_format::cETC1S);
// Returns the block width/height for the specified basis texture file format.
uint32_t basis_tex_format_get_block_width(basis_tex_format fmt);
uint32_t basis_tex_format_get_block_height(basis_tex_format fmt);
bool basis_tex_format_is_hdr(basis_tex_format fmt);
inline bool basis_tex_format_is_ldr(basis_tex_format fmt) { return !basis_tex_format_is_hdr(fmt); }
// Validates that the output buffer is large enough to hold the entire transcoded texture.
// For uncompressed texture formats, most input parameters are in pixels, not blocks. Blocks are 4x4 pixels.
// For uncompressed texture formats, most input parameters are in pixels, not blocks.
bool basis_validate_output_buffer_size(transcoder_texture_format target_format,
uint32_t output_blocks_buf_size_in_blocks_or_pixels,
uint32_t orig_width, uint32_t orig_height,
uint32_t output_row_pitch_in_blocks_or_pixels,
uint32_t output_rows_in_pixels,
uint32_t total_slice_blocks);
uint32_t output_rows_in_pixels);
// Computes the size in bytes of a transcoded image or texture, taking into account the format's block width/height and any minimum size PVRTC1 requirements required by OpenGL.
// Note the returned value is not necessarily the # of bytes a transcoder could write to the output buffer due to these minimum PVRTC1 requirements.
// (These PVRTC1 requirements are not ours, but OpenGL's.)
uint32_t basis_compute_transcoded_image_size_in_bytes(transcoder_texture_format target_format, uint32_t orig_width, uint32_t orig_height);
class basisu_transcoder;
@@ -183,7 +242,48 @@ namespace basist
}
};
// Low-level helper class that does the actual transcoding.
// Low-level helper classes that do the actual transcoding.
enum basisu_decode_flags
{
// 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.
cDecodeFlagsPVRTCDecodeToNextPow2 = 2,
// When decoding to an opaque texture format, if the basis file has alpha, decode the alpha slice instead of the color slice to the output texture format.
// This is primarily to allow decoding of textures with alpha to multiple ETC1 textures (one for color, another for alpha).
cDecodeFlagsTranscodeAlphaDataToOpaqueFormats = 4,
// Forbid usage of BC1 3 color blocks (we don't support BC1 punchthrough alpha yet).
// This flag is used internally when decoding to BC3.
cDecodeFlagsBC1ForbidThreeColorBlocks = 8,
// The output buffer contains alpha endpoint/selector indices.
// Used internally when decoding formats like ASTC that require both color and alpha data to be available when transcoding to the output format.
cDecodeFlagsOutputHasAlphaIndices = 16,
// Enable slower, but higher quality transcoding for some formats.
// For ASTC/XUASTC->BC7, this enables partially analytical encoding vs. fully analytical.
cDecodeFlagsHighQuality = 32,
// Disable ETC1S->BC7 adaptive chroma filtering, for much faster transcoding to BC7.
cDecodeFlagsNoETC1SChromaFiltering = 64,
// Disable deblock filtering for XUASTC LDR transcoding to non-ASTC formats.
// For ASTC 8x6 or smaller block sizes, deblocking is always disabled unless you force it on using cDecodeFlagsForceDeblockFiltering.
cDecodeFlagsNoDeblockFiltering = 128,
// More aggressive deblock filtering (only used when it's enabled)
cDecodeFlagsStrongerDeblockFiltering = 256,
// Always apply deblocking, even for smaller ASTC block sizes (4x4-8x6).
cDecodeFlagsForceDeblockFiltering = 512,
// By default XUASTC LDR 4x4, 6x6 and 8x6 are directly transcoded to BC7 without always requiring a full ASTC block unpack and analytical BC7 encode. This is 1.4x up to 3x faster in WASM.
// This trade offs some quality. The largest transcoding speed gain is achieved when the source XUASTC data isn't dual plane and only uses 1 subset. Otherwise the actual perf. gain is variable.
// To disable this optimization for all XUASTC block sizes and always use the fallback encoder, specify cDecodeFlagXUASTCLDRDisableFastBC7Transcoding.
cDecodeFlagXUASTCLDRDisableFastBC7Transcoding = 1024
};
// ETC1S
class basisu_lowlevel_etc1s_transcoder
{
friend class basisu_transcoder;
@@ -202,18 +302,18 @@ namespace basist
bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const bool is_video, const bool is_alpha_slice, const uint32_t level_index, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0);
basisu_transcoder_state* pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0, uint32_t decode_flags = 0);
bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0)
basisu_transcoder_state* pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0, uint32_t decode_flags = 0)
{
return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt, output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks,
header.m_tex_type == cBASISTexTypeVideoFrames, (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0, slice_desc.m_level_index,
slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels, pState,
astc_transcode_alpha,
pAlpha_blocks,
output_rows_in_pixels);
output_rows_in_pixels, decode_flags);
}
// Container independent transcoding
@@ -222,7 +322,7 @@ namespace basist
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint32_t rgb_offset, uint32_t rgb_length, uint32_t alpha_offset, uint32_t alpha_length,
uint64_t rgb_offset, uint32_t rgb_length, uint64_t alpha_offset, uint32_t alpha_length,
uint32_t decode_flags = 0,
bool basis_file_has_alpha_slices = false,
bool is_video = false,
@@ -260,33 +360,14 @@ namespace basist
basisu_transcoder_state m_def_state;
};
enum basisu_decode_flags
{
// 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.
cDecodeFlagsPVRTCDecodeToNextPow2 = 2,
// When decoding to an opaque texture format, if the basis file has alpha, decode the alpha slice instead of the color slice to the output texture format.
// This is primarily to allow decoding of textures with alpha to multiple ETC1 textures (one for color, another for alpha).
cDecodeFlagsTranscodeAlphaDataToOpaqueFormats = 4,
// Forbid usage of BC1 3 color blocks (we don't support BC1 punchthrough alpha yet).
// This flag is used internally when decoding to BC3.
cDecodeFlagsBC1ForbidThreeColorBlocks = 8,
// The output buffer contains alpha endpoint/selector indices.
// Used internally when decoding formats like ASTC that require both color and alpha data to be available when transcoding to the output format.
cDecodeFlagsOutputHasAlphaIndices = 16,
cDecodeFlagsHighQuality = 32
};
class basisu_lowlevel_uastc_transcoder
// UASTC LDR 4x4
class basisu_lowlevel_uastc_ldr_4x4_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_uastc_transcoder();
basisu_lowlevel_uastc_ldr_4x4_transcoder();
bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
@@ -307,7 +388,189 @@ namespace basist
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint32_t slice_offset, uint32_t slice_length,
uint64_t slice_offset, uint32_t slice_length,
uint32_t decode_flags = 0,
bool has_alpha = false,
bool is_video = false,
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr,
uint32_t output_rows_in_pixels = 0,
int channel0 = -1, int channel1 = -1);
};
#if BASISD_SUPPORT_XUASTC
// XUASTC LDR 4x4-12x12 or ASTC LDR 4x4-12x12
struct xuastc_decoded_image
{
uint32_t m_actual_block_width = 0, m_actual_block_height = 0, m_actual_width = 0, m_actual_height = 0;
bool m_actual_has_alpha = false, m_uses_srgb_astc_decode_mode = false;
bool decode(const uint8_t* pImage_data, uint32_t image_data_size,
astc_ldr_t::xuastc_decomp_image_init_callback_ptr pInit_callback, void* pInit_callback_data,
astc_ldr_t::xuastc_decomp_image_block_callback_ptr pBlock_callback, void* pBlock_callback_data)
{
const bool decomp_flag = astc_ldr_t::xuastc_ldr_decompress_image(pImage_data, image_data_size,
m_actual_block_width, m_actual_block_height,
m_actual_width, m_actual_height,
m_actual_has_alpha, m_uses_srgb_astc_decode_mode, basisu::g_debug_printf,
pInit_callback, pInit_callback_data,
pBlock_callback, pBlock_callback_data);
return decomp_flag;
}
void clear()
{
m_actual_block_width = 0;
m_actual_block_height = 0;
m_actual_width = 0;
m_actual_height = 0;
m_actual_has_alpha = false;
m_uses_srgb_astc_decode_mode = false;
}
};
#endif
// This is both ASTC LDR 4x4-12x12 and XUASTC LDR 4x4-12x12.
class basisu_lowlevel_xuastc_ldr_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_xuastc_ldr_transcoder();
bool transcode_slice(basis_tex_format src_format, bool use_astc_srgb_decode_profile, void* pDst_blocks, uint32_t src_num_blocks_x, uint32_t src_num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
bool transcode_slice(basis_tex_format src_format, bool use_astc_srgb_decode_profile, void* pDst_blocks, uint32_t src_num_blocks_x, uint32_t src_num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
{
return transcode_slice(src_format, use_astc_srgb_decode_profile, pDst_blocks, src_num_blocks_x, src_num_blocks_y, pImage_data, image_data_size, fmt,
output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
}
// Container independent transcoding
bool transcode_image(
basis_tex_format src_format, bool use_astc_srgb_decode_profile,
transcoder_texture_format target_format,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t src_num_blocks_x, uint32_t src_num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint64_t slice_offset, uint32_t slice_length,
uint32_t decode_flags = 0,
bool has_alpha = false,
bool is_video = false,
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr,
uint32_t output_rows_in_pixels = 0,
int channel0 = -1, int channel1 = -1);
};
// UASTC HDR 4x4
class basisu_lowlevel_uastc_hdr_4x4_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_uastc_hdr_4x4_transcoder();
bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
{
return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt,
output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
}
// Container independent transcoding
bool transcode_image(
transcoder_texture_format target_format,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint64_t slice_offset, uint32_t slice_length,
uint32_t decode_flags = 0,
bool has_alpha = false,
bool is_video = false,
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr,
uint32_t output_rows_in_pixels = 0,
int channel0 = -1, int channel1 = -1);
};
// ASTC HDR 6x6
class basisu_lowlevel_astc_hdr_6x6_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_astc_hdr_6x6_transcoder();
bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
{
return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt,
output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
}
// Container independent transcoding
bool transcode_image(
transcoder_texture_format target_format,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint64_t slice_offset, uint32_t slice_length,
uint32_t decode_flags = 0,
bool has_alpha = false,
bool is_video = false,
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr,
uint32_t output_rows_in_pixels = 0,
int channel0 = -1, int channel1 = -1);
};
// UASTC HDR 6x6 intermediate
class basisu_lowlevel_uastc_hdr_6x6_intermediate_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_uastc_hdr_6x6_intermediate_transcoder();
bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
{
return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt,
output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
}
// Container independent transcoding
bool transcode_image(
transcoder_texture_format target_format,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint64_t slice_offset, uint32_t slice_length,
uint32_t decode_flags = 0,
bool has_alpha = false,
bool is_video = false,
@@ -319,9 +582,11 @@ namespace basist
struct basisu_slice_info
{
// The image's ACTUAL dimensions in texels.
uint32_t m_orig_width;
uint32_t m_orig_height;
// The texture's dimensions in texels - always a multiple of the texture's underlying block size (4x4-12x12).
uint32_t m_width;
uint32_t m_height;
@@ -329,6 +594,9 @@ namespace basist
uint32_t m_num_blocks_y;
uint32_t m_total_blocks;
uint32_t m_block_width;
uint32_t m_block_height;
uint32_t m_compressed_size;
uint32_t m_slice_index; // the slice index in the .basis file
@@ -348,12 +616,17 @@ namespace basist
uint32_t m_image_index;
uint32_t m_total_levels;
// The image's ACTUAL dimensions in texels.
uint32_t m_orig_width;
uint32_t m_orig_height;
// The texture's dimensions in texels - always a multiple of the texture's underlying block size (4x4-12x12).
uint32_t m_width;
uint32_t m_height;
uint32_t m_block_width;
uint32_t m_block_height;
uint32_t m_num_blocks_x;
uint32_t m_num_blocks_y;
uint32_t m_total_blocks;
@@ -375,6 +648,9 @@ namespace basist
uint32_t m_width;
uint32_t m_height;
uint32_t m_block_width;
uint32_t m_block_height;
uint32_t m_num_blocks_x;
uint32_t m_num_blocks_y;
uint32_t m_total_blocks;
@@ -424,13 +700,17 @@ namespace basist
basis_tex_format m_tex_format; // ETC1S, UASTC, etc.
uint32_t m_block_width;
uint32_t m_block_height;
bool m_y_flipped; // true if the image was Y flipped
bool m_srgb; // true if the image is sRGB, false if linear
bool m_etc1s; // true if the file is ETC1S
bool m_has_alpha_slices; // true if the texture has alpha slices (for ETC1S: even slices RGB, odd slices alpha)
};
// High-level transcoder class which accepts .basis file data and allows the caller to query information about the file and transcode image levels to various texture formats.
// If you're just starting out this is the class you care about.
// If you're just starting out this is the class you care about (or see the KTX2 transcoder below).
class basisu_transcoder
{
basisu_transcoder(basisu_transcoder&);
@@ -452,7 +732,7 @@ namespace basist
// Note that the number of mipmap levels for each image may differ, and that images may have different resolutions.
uint32_t get_total_images(const void* pData, uint32_t data_size) const;
basis_tex_format get_tex_format(const void* pData, uint32_t data_size) const;
basis_tex_format get_basis_tex_format(const void* pData, uint32_t data_size) const;
// Returns the number of mipmap levels in an image.
uint32_t get_total_image_levels(const void* pData, uint32_t data_size, uint32_t image_index) const;
@@ -482,7 +762,7 @@ namespace basist
// It'll first find the slice(s) to transcode, then call transcode_slice() one or two times to decode both the color and alpha texture data (or RG texture data from two slices for BC5).
// If the .basis file doesn't have alpha slices, the output alpha blocks will be set to fully opaque (all 255's).
// Currently, to decode to PVRTC1 the basis texture's dimensions in pixels must be a power of 2, due to PVRTC1 format requirements.
// output_blocks_buf_size_in_blocks_or_pixels should be at least the image level's total_blocks (num_blocks_x * num_blocks_y), or the total number of output pixels if fmt==cTFRGBA32.
// output_blocks_buf_size_in_blocks_or_pixels should be at least the image level's total_blocks (num_blocks_x * num_blocks_y), or the total number of output pixels if fmt==cTFRGBA32 etc.
// output_row_pitch_in_blocks_or_pixels: Number of blocks or pixels per row. If 0, the transcoder uses the slice's num_blocks_x or orig_width (NOT num_blocks_x * 4). Ignored for PVRTC1 (due to texture swizzling).
// output_rows_in_pixels: Ignored unless fmt is uncompressed (cRGBA32, etc.). The total number of output rows in the output buffer. If 0, the transcoder assumes the slice's orig_height (NOT num_blocks_y * 4).
// Notes:
@@ -524,12 +804,16 @@ namespace basist
const basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() const { return m_lowlevel_etc1s_decoder; }
basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() { return m_lowlevel_etc1s_decoder; }
const basisu_lowlevel_uastc_transcoder& get_lowlevel_uastc_decoder() const { return m_lowlevel_uastc_decoder; }
basisu_lowlevel_uastc_transcoder& get_lowlevel_uastc_decoder() { return m_lowlevel_uastc_decoder; }
const basisu_lowlevel_uastc_ldr_4x4_transcoder& get_lowlevel_uastc_decoder() const { return m_lowlevel_uastc_ldr_4x4_decoder; }
basisu_lowlevel_uastc_ldr_4x4_transcoder& get_lowlevel_uastc_decoder() { return m_lowlevel_uastc_ldr_4x4_decoder; }
private:
mutable basisu_lowlevel_etc1s_transcoder m_lowlevel_etc1s_decoder;
mutable basisu_lowlevel_uastc_transcoder m_lowlevel_uastc_decoder;
mutable basisu_lowlevel_uastc_ldr_4x4_transcoder m_lowlevel_uastc_ldr_4x4_decoder;
mutable basisu_lowlevel_xuastc_ldr_transcoder m_lowlevel_xuastc_ldr_decoder;
mutable basisu_lowlevel_uastc_hdr_4x4_transcoder m_lowlevel_uastc_4x4_hdr_decoder;
mutable basisu_lowlevel_astc_hdr_6x6_transcoder m_lowlevel_astc_6x6_hdr_decoder;
mutable basisu_lowlevel_uastc_hdr_6x6_intermediate_transcoder m_lowlevel_astc_6x6_hdr_intermediate_decoder;
bool m_ready_to_transcode;
@@ -603,6 +887,21 @@ namespace basist
basisu::packed_uint<4> m_alpha_slice_byte_length;
};
// The initial v1.6 release (for backwards compatibility only with our older .KTX2 files)
struct ktx2_slice_offset_len_desc_orig
{
basisu::packed_uint<4> m_slice_byte_offset; // byte offset relative to the KTX2 mipmap level
basisu::packed_uint<4> m_slice_byte_length;
};
// The Khronos KTX2 spec standard
struct ktx2_slice_offset_len_desc_std
{
basisu::packed_uint<4> m_slice_byte_offset; // byte offset relative to the KTX2 mipmap level
basisu::packed_uint<4> m_slice_byte_length;
basisu::packed_uint<4> m_profile;
};
struct ktx2_animdata
{
basisu::packed_uint<4> m_duration;
@@ -612,10 +911,40 @@ namespace basist
#pragma pack(pop)
const uint32_t KTX2_VK_FORMAT_UNDEFINED = 0;
const uint32_t KTX2_KDF_DF_MODEL_UASTC = 166;
const uint32_t KTX2_KDF_DF_MODEL_ETC1S = 163;
// These are standard Vulkan texture VkFormat ID's, see https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkFormat.html
const uint32_t KTX2_FORMAT_ASTC_4x4_SFLOAT_BLOCK = 1000066000;
const uint32_t KTX2_FORMAT_ASTC_5x4_SFLOAT_BLOCK = 1000066001;
const uint32_t KTX2_FORMAT_ASTC_5x5_SFLOAT_BLOCK = 1000066002;
const uint32_t KTX2_FORMAT_ASTC_6x5_SFLOAT_BLOCK = 1000066003;
const uint32_t KTX2_FORMAT_ASTC_6x6_SFLOAT_BLOCK = 1000066004;
const uint32_t KTX2_FORMAT_ASTC_8x5_SFLOAT_BLOCK = 1000066005;
const uint32_t KTX2_FORMAT_ASTC_8x6_SFLOAT_BLOCK = 1000066006;
const uint32_t KTX2_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, KTX2_FORMAT_ASTC_4x4_SRGB_BLOCK = 158;
const uint32_t KTX2_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, KTX2_FORMAT_ASTC_5x4_SRGB_BLOCK = 160;
const uint32_t KTX2_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, KTX2_FORMAT_ASTC_5x5_SRGB_BLOCK = 162;
const uint32_t KTX2_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, KTX2_FORMAT_ASTC_6x5_SRGB_BLOCK = 164;
const uint32_t KTX2_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, KTX2_FORMAT_ASTC_6x6_SRGB_BLOCK = 166;
const uint32_t KTX2_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, KTX2_FORMAT_ASTC_8x5_SRGB_BLOCK = 168;
const uint32_t KTX2_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, KTX2_FORMAT_ASTC_8x6_SRGB_BLOCK = 170;
const uint32_t KTX2_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, KTX2_FORMAT_ASTC_10x5_SRGB_BLOCK = 174;
const uint32_t KTX2_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, KTX2_FORMAT_ASTC_10x6_SRGB_BLOCK = 176;
const uint32_t KTX2_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, KTX2_FORMAT_ASTC_8x8_SRGB_BLOCK = 172; // note the ASTC block size order is off in the vkFormat definitions
const uint32_t KTX2_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, KTX2_FORMAT_ASTC_10x8_SRGB_BLOCK = 178;
const uint32_t KTX2_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, KTX2_FORMAT_ASTC_10x10_SRGB_BLOCK = 180;
const uint32_t KTX2_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, KTX2_FORMAT_ASTC_12x10_SRGB_BLOCK = 182;
const uint32_t KTX2_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, KTX2_FORMAT_ASTC_12x12_SRGB_BLOCK = 184;
const uint32_t KTX2_KDF_DF_MODEL_ASTC = 162; // 0xA2
const uint32_t KTX2_KDF_DF_MODEL_ETC1S = 163; // 0xA3
const uint32_t KTX2_KDF_DF_MODEL_UASTC_LDR_4X4 = 166; // 0xA6
const uint32_t KTX2_KDF_DF_MODEL_UASTC_HDR_4X4 = 167; // 0xA7
const uint32_t KTX2_KDF_DF_MODEL_UASTC_HDR_6X6_INTERMEDIATE = 168; // 0xA8, TODO - coordinate with Khronos on this
const uint32_t KTX2_KDF_DF_MODEL_XUASTC_LDR_INTERMEDIATE = 169; // 0xA9, TODO - coordinate with Khronos on this
const uint32_t KTX2_IMAGE_IS_P_FRAME = 2;
const uint32_t KTX2_UASTC_BLOCK_SIZE = 16;
const uint32_t KTX2_UASTC_BLOCK_SIZE = 16; // also the block size for UASTC_HDR
const uint32_t KTX2_MAX_SUPPORTED_LEVEL_COUNT = 16; // this is an implementation specific constraint and can be increased
// The KTX2 transfer functions supported by KTX2
@@ -625,8 +954,11 @@ namespace basist
enum ktx2_supercompression
{
KTX2_SS_NONE = 0,
KTX2_SS_BASISLZ = 1,
KTX2_SS_ZSTANDARD = 2
KTX2_SS_BASISLZ = 1, // actually ETC1S
KTX2_SS_ZSTANDARD = 2,
KTX2_SS_DEFLATE = 3, // currently unsupported by us
KTX2_SS_UASTC_HDR_6x6I = 4, // UASTC HDR 6x6i (picked by Khronos, in KTX-Software as of 2/19/2026)
KTX2_SS_XUASTC_LDR = 5 // XUASTC LDR 4x4-12x12 (coordinate with Khronos, not in KTX-Software yet as of 2/19/2026)
};
extern const uint8_t g_ktx2_file_identifier[12];
@@ -719,18 +1051,22 @@ namespace basist
uint32_t m_layer_index;
uint32_t m_face_index;
// The image's actual (or the original source image's) width/height in pixels, which may not be divisible by 4 pixels.
// The image's ACTUAL (or the original source image's) width/height in pixels, which may not be divisible by the block size (4-12 pixels).
uint32_t m_orig_width;
uint32_t m_orig_height;
// The image's physical width/height, which will always be divisible by 4 pixels.
// The image's physical width/height, which will always be divisible by the format's block size (4-12 pixels).
uint32_t m_width;
uint32_t m_height;
// The texture's dimensions in 4x4 texel blocks.
// The texture's dimensions in 4x4-12x12 texel blocks.
uint32_t m_num_blocks_x;
uint32_t m_num_blocks_y;
// The format's block width/height (4-12).
uint32_t m_block_width;
uint32_t m_block_height;
// The total number of blocks
uint32_t m_total_blocks;
@@ -759,7 +1095,7 @@ namespace basist
// This class is quite similar to basisu_transcoder. It treats KTX2 files as a simple container for ETC1S/UASTC texture data.
// It does not support 1D or 3D textures.
// It only supports 2D and cubemap textures, with or without mipmaps, texture arrays of 2D/cubemap textures, and texture video files.
// It only supports raw non-supercompressed UASTC, ETC1S, UASTC+Zstd, or UASTC+zlib compressed files.
// It only supports our codec formats: ETC1S, UASTC LDR 4x4, UASTC HDR 4x4, etc.
// DFD (Data Format Descriptor) parsing is purposely as simple as possible.
// If you need to know how to interpret the texture channels you'll need to parse the DFD yourself after calling get_dfd().
class ktx2_transcoder
@@ -785,10 +1121,10 @@ namespace basist
// Returns the KTX2 level index array. There will be one entry for each mipmap level. Valid after init().
const basisu::vector<ktx2_level_index>& get_level_index() const { return m_levels; }
// Returns the texture's width in texels. Always non-zero, might not be divisible by 4. Valid after init().
// Returns the texture's width in texels. Always non-zero, might not be divisible by the block size. Valid after init().
uint32_t get_width() const { return m_header.m_pixel_width; }
// Returns the texture's height in texels. Always non-zero, might not be divisible by 4. Valid after init().
// Returns the texture's height in texels. Always non-zero, might not be divisible by the block size. Valid after init().
uint32_t get_height() const { return m_header.m_pixel_height; }
// Returns the texture's number of mipmap levels. Always returns 1 or higher. Valid after init().
@@ -800,12 +1136,46 @@ namespace basist
// Returns 0 or the number of layers in the texture array or texture video. Valid after init().
uint32_t get_layers() const { return m_header.m_layer_count; }
// Returns cETC1S or cUASTC4x4. Valid after init().
basist::basis_tex_format get_format() const { return m_format; }
// Returns cETC1S, cUASTC4x4, cUASTC_HDR_4x4, cASTC_HDR_6x6, cUASTC_HDR_6x6_INTERMEDIATE, etc. Valid after init().
basist::basis_tex_format get_basis_tex_format() const { return m_format; }
bool is_etc1s() const { return get_format() == basist::basis_tex_format::cETC1S; }
// ETC1S LDR 4x4
bool is_etc1s() const { return get_basis_tex_format() == basist::basis_tex_format::cETC1S; }
bool is_uastc() const { return get_format() == basist::basis_tex_format::cUASTC4x4; }
// UASTC LDR 4x4 (only)
bool is_uastc() const { return get_basis_tex_format() == basist::basis_tex_format::cUASTC_LDR_4x4; }
// Is ASTC HDR 4x4 or 6x6
bool is_hdr() const
{
return basis_tex_format_is_hdr(get_basis_tex_format());
}
bool is_ldr() const
{
return !is_hdr();
}
// is UASTC HDR 4x4 (which is also standard ASTC HDR 4x4 data)
bool is_hdr_4x4() const
{
return (get_basis_tex_format() == basist::basis_tex_format::cUASTC_HDR_4x4);
}
// is ASTC HDR 6x6 or UASTC HDR 6x6 intermediate (only)
bool is_hdr_6x6() const
{
return (get_basis_tex_format() == basist::basis_tex_format::cASTC_HDR_6x6) || (get_basis_tex_format() == basist::basis_tex_format::cUASTC_HDR_6x6_INTERMEDIATE);
}
// is ASTC LDR 4x4-12x12 (only)
bool is_astc_ldr() const { return basis_tex_format_is_astc_ldr(get_basis_tex_format()); }
// is XUASTC LDR 4x4-12x12 (only)
bool is_xuastc_ldr() const { return basis_tex_format_is_xuastc_ldr(get_basis_tex_format()); }
uint32_t get_block_width() const { return basis_tex_format_get_block_width(get_basis_tex_format()); }
uint32_t get_block_height() const { return basis_tex_format_get_block_height(get_basis_tex_format()); }
// Returns true if the ETC1S file has two planes (typically RGBA, or RRRG), or true if the UASTC file has alpha data. Valid after init().
uint32_t get_has_alpha() const { return m_has_alpha; }
@@ -824,6 +1194,8 @@ namespace basist
// Returns KTX2_KHR_DF_TRANSFER_LINEAR or KTX2_KHR_DF_TRANSFER_SRGB.
uint32_t get_dfd_transfer_func() const { return m_dfd_transfer_func; }
bool is_srgb() const { return (get_dfd_transfer_func() == KTX2_KHR_DF_TRANSFER_SRGB); }
uint32_t get_dfd_flags() const { return m_dfd_flags; }
// Returns 1 (ETC1S/UASTC) or 2 (ETC1S with an internal alpha channel).
@@ -838,10 +1210,12 @@ namespace basist
// Key value field data.
struct key_value
{
// The key field is UTF8 and always zero terminated.
// The key field is UTF8 and always zero terminated.
// In memory we always append a zero terminator to the key.
basisu::uint8_vec m_key;
// The value may be empty. It consists of raw bytes which may or may not be zero terminated.
// The value may be empty. In the KTX2 file it consists of raw bytes which may or may not be zero terminated.
// In memory we always append a zero terminator to the value.
basisu::uint8_vec m_value;
bool operator< (const key_value& rhs) const { return strcmp((const char*)m_key.data(), (const char *)rhs.m_key.data()) < 0; }
@@ -862,14 +1236,19 @@ namespace basist
// Returns the array of ETC1S image descriptors, which is only valid after get_etc1s_image_descs() is called.
const basisu::vector<ktx2_etc1s_image_desc>& get_etc1s_image_descs() const { return m_etc1s_image_descs; }
const basisu::vector<ktx2_slice_offset_len_desc_orig>& get_slice_offset_len_descs() const { return m_slice_offset_len_descs; }
// Must have called startTranscoding() first
uint32_t get_etc1s_image_descs_image_flags(uint32_t level_index, uint32_t layer_index, uint32_t face_index) const;
// is_video() is only valid after start_transcoding() is called.
// For ETC1S data, if this returns true you must currently transcode the file from first to last frame, in order, without skipping any frames.
bool is_video() const { return m_is_video; }
// Defaults to 0, only non-zero if the key existed in the source KTX2 file.
float get_ldr_hdr_upconversion_nit_multiplier() const { return m_ldr_hdr_upconversion_nit_multiplier; }
// start_transcoding() MUST be called before calling transcode_image().
// start_transcoding() MUST be called before calling transcode_image_level().
// This method decompresses the ETC1S global endpoint/selector codebooks, which is not free, so try to avoid calling it excessively.
bool start_transcoding();
@@ -901,29 +1280,70 @@ namespace basist
ktx2_etc1s_global_data_header m_etc1s_header;
basisu::vector<ktx2_etc1s_image_desc> m_etc1s_image_descs;
basisu::vector<ktx2_slice_offset_len_desc_orig> m_slice_offset_len_descs;
basist::basis_tex_format m_format;
uint32_t m_dfd_color_model;
ktx2_df_color_primaries m_dfd_color_prims;
uint32_t m_dfd_transfer_func;
// KTX2_KHR_DF_TRANSFER_LINEAR vs. KTX2_KHR_DF_TRANSFER_SRGB (for XUASTC LDR: which profile was used during encoding)
uint32_t m_dfd_transfer_func;
uint32_t m_dfd_flags;
uint32_t m_dfd_samples;
ktx2_df_channel_id m_dfd_chan0, m_dfd_chan1;
basist::basisu_lowlevel_etc1s_transcoder m_etc1s_transcoder;
basist::basisu_lowlevel_uastc_transcoder m_uastc_transcoder;
basist::basisu_lowlevel_uastc_ldr_4x4_transcoder m_uastc_ldr_transcoder;
basist::basisu_lowlevel_xuastc_ldr_transcoder m_xuastc_ldr_transcoder;
basist::basisu_lowlevel_uastc_hdr_4x4_transcoder m_uastc_hdr_transcoder;
basist::basisu_lowlevel_astc_hdr_6x6_transcoder m_astc_hdr_6x6_transcoder;
basist::basisu_lowlevel_uastc_hdr_6x6_intermediate_transcoder m_astc_hdr_6x6_intermediate_transcoder;
ktx2_transcoder_state m_def_transcoder_state;
bool m_has_alpha;
bool m_is_video;
float m_ldr_hdr_upconversion_nit_multiplier;
bool decompress_level_data(uint32_t level_index, basisu::uint8_vec& uncomp_data);
bool read_slice_offset_len_global_data(bool read_std_structs);
bool decompress_etc1s_global_data();
bool read_key_values();
};
// Replaces if the key already exists
inline void ktx2_add_key_value(ktx2_transcoder::key_value_vec& key_values, const std::string& key, const std::string& val)
{
assert(key.size());
basist::ktx2_transcoder::key_value* p = nullptr;
// Try to find an existing key
for (size_t i = 0; i < key_values.size(); i++)
{
if (strcmp((const char*)key_values[i].m_key.data(), key.c_str()) == 0)
{
p = &key_values[i];
break;
}
}
if (!p)
p = key_values.enlarge(1);
p->m_key.resize(0);
p->m_value.resize(0);
p->m_key.resize(key.size() + 1);
memcpy(p->m_key.data(), key.c_str(), key.size());
p->m_value.resize(val.size() + 1);
if (val.size())
memcpy(p->m_value.data(), val.c_str(), val.size());
}
#endif // BASISD_SUPPORT_KTX2
// Returns true if the transcoder was compiled with KTX2 support.
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
// Copyright (C) 2017-2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2017-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
// Copyright (C) 2017-2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2017-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
+11 -10
View File
@@ -5,14 +5,15 @@
namespace basist
{
struct color_quad_u8
{
uint8_t m_c[4];
{
uint8_t m_c[4];
};
const uint32_t TOTAL_UASTC_MODES = 19;
const uint32_t UASTC_MODE_INDEX_SOLID_COLOR = 8;
const uint32_t TOTAL_ASTC_BC7_COMMON_PARTITIONS2 = 30;
const uint32_t TOTAL_ASTC_BC6H_COMMON_PARTITIONS2 = 27; // BC6H only supports only 5-bit pattern indices, BC7 supports 4-bit or 6-bit
const uint32_t TOTAL_ASTC_BC7_COMMON_PARTITIONS3 = 11;
const uint32_t TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS = 19;
@@ -101,9 +102,9 @@ namespace basist
int m_ccs; // color component selector (dual plane only)
bool m_dual_plane; // true if dual plane
// Weight and endpoint BISE values.
// Weight and endpoint BISE values.
// Note these values are NOT linear, they must be BISE encoded. See Table 97 and Table 107.
uint8_t m_endpoints[18]; // endpoint values, in RR GG BB etc. order
uint8_t m_endpoints[18]; // endpoint values, in RR GG BB etc. order
uint8_t m_weights[64]; // weight index values, raster order, in P0 P1, P0 P1, etc. or P0, P0, P0, P0, etc. order
};
@@ -198,7 +199,7 @@ namespace basist
#ifdef _DEBUG
int astc_compute_texel_partition(int seed, int x, int y, int z, int partitioncount, bool small_block);
#endif
struct uastc_block
{
union
@@ -234,10 +235,10 @@ namespace basist
};
color32 apply_etc1_bias(const color32 &block_color, uint32_t bias, uint32_t limit, uint32_t subblock);
struct decoder_etc_block;
struct eac_block;
bool unpack_uastc(uint32_t mode, uint32_t common_pattern, const color32& solid_color, const astc_block_desc& astc, color32* pPixels, bool srgb);
bool unpack_uastc(const unpacked_uastc_block& unpacked_blk, color32* pPixels, bool srgb);
@@ -259,7 +260,7 @@ namespace basist
// Packs 16 scalar values to BC4. Same PSNR as stb_dxt's BC4 encoder, around 13% faster.
void encode_bc4(void* pDst, const uint8_t* pPixels, uint32_t stride);
void encode_bc1_solid_block(void* pDst, uint32_t fr, uint32_t fg, uint32_t fb);
enum
@@ -269,7 +270,7 @@ namespace basist
cEncodeBC1UseSelectors = 4,
};
void encode_bc1(void* pDst, const uint8_t* pPixels, uint32_t flags);
// Alternate PCA-free encoder, around 15% faster, same (or slightly higher) avg. PSNR
void encode_bc1_alt(void* pDst, const uint8_t* pPixels, uint32_t flags);
@@ -286,7 +287,7 @@ namespace basist
bool transcode_uastc_to_pvrtc1_4_rgb(const uastc_block* pSrc_blocks, void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, bool high_quality, bool from_alpha);
bool transcode_uastc_to_pvrtc1_4_rgba(const uastc_block* pSrc_blocks, void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, bool high_quality);
// uastc_init() MUST be called before using this module.
void uastc_init();
+22094 -11752
View File
File diff suppressed because it is too large Load Diff
+1145 -397
View File
File diff suppressed because it is too large Load Diff
+107
View File
@@ -0,0 +1,107 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under both the BSD-style license (found in the
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
* in the COPYING file in the root directory of this source tree).
* You may select, at your option, one of the above-listed licenses.
*/
#ifndef ZSTD_ERRORS_H_398273423
#define ZSTD_ERRORS_H_398273423
#if defined (__cplusplus)
extern "C" {
#endif
/* ===== ZSTDERRORLIB_API : control library symbols visibility ===== */
#ifndef ZSTDERRORLIB_VISIBLE
/* Backwards compatibility with old macro name */
# ifdef ZSTDERRORLIB_VISIBILITY
# define ZSTDERRORLIB_VISIBLE ZSTDERRORLIB_VISIBILITY
# elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
# define ZSTDERRORLIB_VISIBLE __attribute__ ((visibility ("default")))
# else
# define ZSTDERRORLIB_VISIBLE
# endif
#endif
#ifndef ZSTDERRORLIB_HIDDEN
# if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
# define ZSTDERRORLIB_HIDDEN __attribute__ ((visibility ("hidden")))
# else
# define ZSTDERRORLIB_HIDDEN
# endif
#endif
#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
# define ZSTDERRORLIB_API __declspec(dllexport) ZSTDERRORLIB_VISIBLE
#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
# define ZSTDERRORLIB_API __declspec(dllimport) ZSTDERRORLIB_VISIBLE /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
#else
# define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBLE
#endif
/*-*********************************************
* Error codes list
*-*********************************************
* Error codes _values_ are pinned down since v1.3.1 only.
* Therefore, don't rely on values if you may link to any version < v1.3.1.
*
* Only values < 100 are considered stable.
*
* note 1 : this API shall be used with static linking only.
* dynamic linking is not yet officially supported.
* note 2 : Prefer relying on the enum than on its value whenever possible
* This is the only supported way to use the error list < v1.3.1
* note 3 : ZSTD_isError() is always correct, whatever the library version.
**********************************************/
typedef enum {
ZSTD_error_no_error = 0,
ZSTD_error_GENERIC = 1,
ZSTD_error_prefix_unknown = 10,
ZSTD_error_version_unsupported = 12,
ZSTD_error_frameParameter_unsupported = 14,
ZSTD_error_frameParameter_windowTooLarge = 16,
ZSTD_error_corruption_detected = 20,
ZSTD_error_checksum_wrong = 22,
ZSTD_error_literals_headerWrong = 24,
ZSTD_error_dictionary_corrupted = 30,
ZSTD_error_dictionary_wrong = 32,
ZSTD_error_dictionaryCreation_failed = 34,
ZSTD_error_parameter_unsupported = 40,
ZSTD_error_parameter_combination_unsupported = 41,
ZSTD_error_parameter_outOfBound = 42,
ZSTD_error_tableLog_tooLarge = 44,
ZSTD_error_maxSymbolValue_tooLarge = 46,
ZSTD_error_maxSymbolValue_tooSmall = 48,
ZSTD_error_cannotProduce_uncompressedBlock = 49,
ZSTD_error_stabilityCondition_notRespected = 50,
ZSTD_error_stage_wrong = 60,
ZSTD_error_init_missing = 62,
ZSTD_error_memory_allocation = 64,
ZSTD_error_workSpace_tooSmall= 66,
ZSTD_error_dstSize_tooSmall = 70,
ZSTD_error_srcSize_wrong = 72,
ZSTD_error_dstBuffer_null = 74,
ZSTD_error_noForwardProgress_destFull = 80,
ZSTD_error_noForwardProgress_inputEmpty = 82,
/* following error codes are __NOT STABLE__, they can be removed or changed in future versions */
ZSTD_error_frameIndex_tooLarge = 100,
ZSTD_error_seekableIO = 102,
ZSTD_error_dstBuffer_wrong = 104,
ZSTD_error_srcBuffer_wrong = 105,
ZSTD_error_sequenceProducer_failed = 106,
ZSTD_error_externalSequences_invalid = 107,
ZSTD_error_maxCode = 120 /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */
} ZSTD_ErrorCode;
ZSTDERRORLIB_API const char* ZSTD_getErrorString(ZSTD_ErrorCode code); /**< Same as ZSTD_getErrorName, but using a `ZSTD_ErrorCode` enum argument */
#if defined (__cplusplus)
}
#endif
#endif /* ZSTD_ERRORS_H_398273423 */
+15340 -8131
View File
File diff suppressed because it is too large Load Diff
@@ -397,8 +397,8 @@ class GeometryAttribute {
// Make sure the floating point |in_value| fits within the range of
// values that integral type OutT is able to represent.
if (in_value < std::numeric_limits<OutT>::min() ||
in_value >= std::numeric_limits<OutT>::max()) {
if (in_value < static_cast<T>(std::numeric_limits<OutT>::min()) ||
in_value >= static_cast<T>(std::numeric_limits<OutT>::max())) {
return false;
}
}