Path: blob/master/thirdparty/basis_universal/transcoder/basisu_transcoder.cpp
20873 views
// basisu_transcoder.cpp1// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.1415#include "basisu_transcoder.h"16#include "basisu_containers_impl.h"1718#include "basisu_astc_hdr_core.h"1920#define BASISU_ASTC_HELPERS_IMPLEMENTATION21#include "basisu_astc_helpers.h"2223#include <limits.h>2425#if defined(_MSC_VER)26#include <intrin.h> // For __popcnt intrinsic27#endif2829#ifndef BASISD_IS_BIG_ENDIAN30// TODO: This doesn't work on OSX. How can this be so difficult?31//#if defined(__BIG_ENDIAN__) || defined(_BIG_ENDIAN) || defined(BIG_ENDIAN)32// #define BASISD_IS_BIG_ENDIAN (1)33//#else34#define BASISD_IS_BIG_ENDIAN (0)35//#endif36#endif3738#ifndef BASISD_USE_UNALIGNED_WORD_READS39#ifdef __EMSCRIPTEN__40// Can't use unaligned loads/stores with WebAssembly.41#define BASISD_USE_UNALIGNED_WORD_READS (0)42#elif defined(_M_AMD64) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)43#define BASISD_USE_UNALIGNED_WORD_READS (1)44#else45#define BASISD_USE_UNALIGNED_WORD_READS (0)46#endif47#endif4849// Using unaligned loads and stores causes errors when using UBSan. Jam it off.50#if defined(__has_feature)51#if __has_feature(undefined_behavior_sanitizer)52#undef BASISD_USE_UNALIGNED_WORD_READS53#define BASISD_USE_UNALIGNED_WORD_READS 054#endif55#endif5657#define BASISD_SUPPORTED_BASIS_VERSION (0x13)5859#ifndef BASISD_SUPPORT_KTX260#error Must have defined BASISD_SUPPORT_KTX261#endif6263#ifndef BASISD_SUPPORT_KTX2_ZSTD64#error Must have defined BASISD_SUPPORT_KTX2_ZSTD65#endif6667// Set to 1 for fuzz testing. This will disable all CRC16 checks on headers and compressed data.68#ifndef BASISU_NO_HEADER_OR_DATA_CRC16_CHECKS69#define BASISU_NO_HEADER_OR_DATA_CRC16_CHECKS 070#endif7172#ifndef BASISD_SUPPORT_DXT173#define BASISD_SUPPORT_DXT1 174#endif7576#ifndef BASISD_SUPPORT_DXT5A77#define BASISD_SUPPORT_DXT5A 178#endif7980// Disable all BC7 transcoders if necessary (useful when cross compiling to Javascript)81#if defined(BASISD_SUPPORT_BC7) && !BASISD_SUPPORT_BC782#ifndef BASISD_SUPPORT_BC7_MODE583#define BASISD_SUPPORT_BC7_MODE5 084#endif85#endif // !BASISD_SUPPORT_BC78687// BC7 mode 5 supports both opaque and opaque+alpha textures, and uses less memory BC1.88#ifndef BASISD_SUPPORT_BC7_MODE589#define BASISD_SUPPORT_BC7_MODE5 190#endif9192#ifndef BASISD_SUPPORT_PVRTC193#define BASISD_SUPPORT_PVRTC1 194#endif9596#ifndef BASISD_SUPPORT_ETC2_EAC_A897#define BASISD_SUPPORT_ETC2_EAC_A8 198#endif99100// Set BASISD_SUPPORT_UASTC to 0 to completely disable support for transcoding UASTC files.101#ifndef BASISD_SUPPORT_UASTC102#define BASISD_SUPPORT_UASTC 1103#endif104105#ifndef BASISD_SUPPORT_ASTC106#define BASISD_SUPPORT_ASTC 1107#endif108109// Note that if BASISD_SUPPORT_ATC is enabled, BASISD_SUPPORT_DXT5A should also be enabled for alpha support.110#ifndef BASISD_SUPPORT_ATC111#define BASISD_SUPPORT_ATC 1112#endif113114// Support for ETC2 EAC R11 and ETC2 EAC RG11115#ifndef BASISD_SUPPORT_ETC2_EAC_RG11116#define BASISD_SUPPORT_ETC2_EAC_RG11 1117#endif118119// If BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY is 1, opaque blocks will be transcoded to ASTC at slightly higher quality (higher than BC1), but the transcoder tables will be 2x as large.120// This impacts grayscale and grayscale+alpha textures the most.121#ifndef BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY122#ifdef __EMSCRIPTEN__123// Let's assume size matters more than quality when compiling with emscripten.124#define BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY 0125#else126// Compiling native, so an extra 64K lookup table is probably acceptable.127#define BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY 1128#endif129#endif130131#ifndef BASISD_SUPPORT_FXT1132#define BASISD_SUPPORT_FXT1 1133#endif134135#ifndef BASISD_SUPPORT_PVRTC2136#define BASISD_SUPPORT_PVRTC2 1137#endif138139#if BASISD_SUPPORT_PVRTC2140#if !BASISD_SUPPORT_ATC141#error BASISD_SUPPORT_ATC must be 1 if BASISD_SUPPORT_PVRTC2 is 1142#endif143#endif144145#if BASISD_SUPPORT_ATC146#if !BASISD_SUPPORT_DXT5A147#error BASISD_SUPPORT_DXT5A must be 1 if BASISD_SUPPORT_ATC is 1148#endif149#endif150151#ifndef BASISD_SUPPORT_UASTC_HDR152#define BASISD_SUPPORT_UASTC_HDR 1153#endif154155#define BASISD_WRITE_NEW_BC7_MODE5_TABLES 0156#define BASISD_WRITE_NEW_DXT1_TABLES 0157#define BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES 0158#define BASISD_WRITE_NEW_ASTC_TABLES 0159#define BASISD_WRITE_NEW_ATC_TABLES 0160#define BASISD_WRITE_NEW_ETC2_EAC_R11_TABLES 0161162#ifndef BASISD_ENABLE_DEBUG_FLAGS163#define BASISD_ENABLE_DEBUG_FLAGS 0164#endif165166// If KTX2 support is enabled, we may need Zstd for decompression of supercompressed UASTC files. Include this header.167#if BASISD_SUPPORT_KTX2168// If BASISD_SUPPORT_KTX2_ZSTD is 0, UASTC files compressed with Zstd cannot be loaded.169#if BASISD_SUPPORT_KTX2_ZSTD170// We only use two Zstd API's: ZSTD_decompress() and ZSTD_isError()171#include <zstd.h>172#endif173#endif174175#if BASISD_SUPPORT_UASTC_HDR176using namespace basist::astc_6x6_hdr;177#endif178179namespace basisu180{181bool g_debug_printf;182183void enable_debug_printf(bool enabled)184{185g_debug_printf = enabled;186}187188void debug_printf(const char* pFmt, ...)189{190#if BASISU_FORCE_DEVEL_MESSAGES191g_debug_printf = true;192#endif193if (g_debug_printf)194{195va_list args;196va_start(args, pFmt);197vprintf(pFmt, args);198va_end(args);199}200}201202void debug_puts(const char* p)203{204#if BASISU_FORCE_DEVEL_MESSAGES205g_debug_printf = true;206#endif207if (g_debug_printf)208{209//puts(p);210printf("%s", p);211}212}213} // namespace basisu214215namespace basist216{217#if BASISD_ENABLE_DEBUG_FLAGS218static uint32_t g_debug_flags = 0;219#endif220221uint32_t get_debug_flags()222{223#if BASISD_ENABLE_DEBUG_FLAGS224return g_debug_flags;225#else226return 0;227#endif228}229230void set_debug_flags(uint32_t f)231{232BASISU_NOTE_UNUSED(f);233#if BASISD_ENABLE_DEBUG_FLAGS234g_debug_flags = f;235#endif236}237238inline uint16_t byteswap_uint16(uint16_t v)239{240return static_cast<uint16_t>((v >> 8) | (v << 8));241}242243static inline int32_t clampi(int32_t value, int32_t low, int32_t high) { if (value < low) value = low; else if (value > high) value = high; return value; }244static inline float clampf(float value, float low, float high) { if (value < low) value = low; else if (value > high) value = high; return value; }245static inline float saturate(float value) { return clampf(value, 0, 1.0f); }246247static inline uint8_t mul_8(uint32_t v, uint32_t q) { v = v * q + 128; return (uint8_t)((v + (v >> 8)) >> 8); }248static inline int mul_8bit(int a, int b) { int t = a * b + 128; return (t + (t >> 8)) >> 8; }249static inline int lerp_8bit(int a, int b, int s) { assert(a >= 0 && a <= 255); assert(b >= 0 && b <= 255); assert(s >= 0 && s <= 255); return a + mul_8bit(b - a, s); }250251struct vec2F252{253float c[2];254255inline vec2F() {}256257inline vec2F(float s) { c[0] = s; c[1] = s; }258inline vec2F(float x, float y) { c[0] = x; c[1] = y; }259260inline void set(float x, float y) { c[0] = x; c[1] = y; }261262inline float dot(const vec2F& o) const { return (c[0] * o.c[0]) + (c[1] * o.c[1]); }263264inline float operator[] (uint32_t index) const { assert(index < 2); return c[index]; }265inline float& operator[] (uint32_t index) { assert(index < 2); return c[index]; }266267inline vec2F& clamp(float l, float h)268{269c[0] = basisu::clamp(c[0], l, h);270c[1] = basisu::clamp(c[1], l, h);271return *this;272}273274static vec2F lerp(const vec2F& a, const vec2F& b, float s)275{276vec2F res;277for (uint32_t i = 0; i < 2; i++)278res[i] = basisu::lerp(a[i], b[i], s);279return res;280}281};282283struct vec3F284{285float c[3];286287inline vec3F() {}288289inline vec3F(float s) { c[0] = s; c[1] = s; c[2] = s; }290inline vec3F(float x, float y, float z) { c[0] = x; c[1] = y; c[2] = z; }291292inline void set(float x, float y, float z) { c[0] = x; c[1] = y; c[2] = z; }293294inline float dot(const vec3F& o) const { return (c[0] * o.c[0]) + (c[1] * o.c[1]) + (c[2] * o.c[2]); }295296inline float operator[] (uint32_t index) const { assert(index < 3); return c[index]; }297inline float &operator[] (uint32_t index) { assert(index < 3); return c[index]; }298299inline vec3F& clamp(float l, float h)300{301c[0] = basisu::clamp(c[0], l, h);302c[1] = basisu::clamp(c[1], l, h);303c[2] = basisu::clamp(c[2], l, h);304return *this;305}306307static vec3F lerp(const vec3F& a, const vec3F& b, float s)308{309vec3F res;310for (uint32_t i = 0; i < 3; i++)311res[i] = basisu::lerp(a[i], b[i], s);312return res;313}314};315316uint16_t crc16(const void* r, size_t size, uint16_t crc)317{318crc = ~crc;319320const uint8_t* p = static_cast<const uint8_t*>(r);321for (; size; --size)322{323const uint16_t q = *p++ ^ (crc >> 8);324uint16_t k = (q >> 4) ^ q;325crc = (((crc << 8) ^ k) ^ (k << 5)) ^ (k << 12);326}327328return static_cast<uint16_t>(~crc);329}330331struct vec4F332{333float c[4];334335inline void set(float x, float y, float z, float w) { c[0] = x; c[1] = y; c[2] = z; c[3] = w; }336337float operator[] (uint32_t index) const { assert(index < 4); return c[index]; }338float& operator[] (uint32_t index) { assert(index < 4); return c[index]; }339};340341enum etc_constants342{343cETC1BytesPerBlock = 8U,344345cETC1SelectorBits = 2U,346cETC1SelectorValues = 1U << cETC1SelectorBits,347cETC1SelectorMask = cETC1SelectorValues - 1U,348349cETC1BlockShift = 2U,350cETC1BlockSize = 1U << cETC1BlockShift,351352cETC1LSBSelectorIndicesBitOffset = 0,353cETC1MSBSelectorIndicesBitOffset = 16,354355cETC1FlipBitOffset = 32,356cETC1DiffBitOffset = 33,357358cETC1IntenModifierNumBits = 3,359cETC1IntenModifierValues = 1 << cETC1IntenModifierNumBits,360cETC1RightIntenModifierTableBitOffset = 34,361cETC1LeftIntenModifierTableBitOffset = 37,362363// Base+Delta encoding (5 bit bases, 3 bit delta)364cETC1BaseColorCompNumBits = 5,365cETC1BaseColorCompMax = 1 << cETC1BaseColorCompNumBits,366367cETC1DeltaColorCompNumBits = 3,368cETC1DeltaColorComp = 1 << cETC1DeltaColorCompNumBits,369cETC1DeltaColorCompMax = 1 << cETC1DeltaColorCompNumBits,370371cETC1BaseColor5RBitOffset = 59,372cETC1BaseColor5GBitOffset = 51,373cETC1BaseColor5BBitOffset = 43,374375cETC1DeltaColor3RBitOffset = 56,376cETC1DeltaColor3GBitOffset = 48,377cETC1DeltaColor3BBitOffset = 40,378379// Absolute (non-delta) encoding (two 4-bit per component bases)380cETC1AbsColorCompNumBits = 4,381cETC1AbsColorCompMax = 1 << cETC1AbsColorCompNumBits,382383cETC1AbsColor4R1BitOffset = 60,384cETC1AbsColor4G1BitOffset = 52,385cETC1AbsColor4B1BitOffset = 44,386387cETC1AbsColor4R2BitOffset = 56,388cETC1AbsColor4G2BitOffset = 48,389cETC1AbsColor4B2BitOffset = 40,390391cETC1ColorDeltaMin = -4,392cETC1ColorDeltaMax = 3,393394// Delta3:395// 0 1 2 3 4 5 6 7396// 000 001 010 011 100 101 110 111397// 0 1 2 3 -4 -3 -2 -1398};399400#define DECLARE_ETC1_INTEN_TABLE(name, N) \401static const int name[cETC1IntenModifierValues][cETC1SelectorValues] = \402{ \403{ N * -8, N * -2, N * 2, N * 8 },{ N * -17, N * -5, N * 5, N * 17 },{ N * -29, N * -9, N * 9, N * 29 },{ N * -42, N * -13, N * 13, N * 42 }, \404{ N * -60, N * -18, N * 18, N * 60 },{ N * -80, N * -24, N * 24, N * 80 },{ N * -106, N * -33, N * 33, N * 106 },{ N * -183, N * -47, N * 47, N * 183 } \405};406407DECLARE_ETC1_INTEN_TABLE(g_etc1_inten_tables, 1);408DECLARE_ETC1_INTEN_TABLE(g_etc1_inten_tables16, 16);409DECLARE_ETC1_INTEN_TABLE(g_etc1_inten_tables48, 3 * 16);410411//const uint8_t g_etc1_to_selector_index[cETC1SelectorValues] = { 2, 3, 1, 0 };412const uint8_t g_selector_index_to_etc1[cETC1SelectorValues] = { 3, 2, 0, 1 };413414static const uint8_t g_etc_5_to_8[32] = { 0, 8, 16, 24, 33, 41, 49, 57, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140, 148, 156, 165, 173, 181, 189, 198, 206, 214, 222, 231, 239, 247, 255 };415416struct decoder_etc_block417{418// big endian uint64:419// bit ofs: 56 48 40 32 24 16 8 0420// byte ofs: b0, b1, b2, b3, b4, b5, b6, b7421union422{423uint64_t m_uint64;424425uint32_t m_uint32[2];426427uint8_t m_bytes[8];428429struct430{431signed m_dred2 : 3;432uint32_t m_red1 : 5;433434signed m_dgreen2 : 3;435uint32_t m_green1 : 5;436437signed m_dblue2 : 3;438uint32_t m_blue1 : 5;439440uint32_t m_flip : 1;441uint32_t m_diff : 1;442uint32_t m_cw2 : 3;443uint32_t m_cw1 : 3;444445uint32_t m_selectors;446} m_differential;447};448449inline void clear()450{451assert(sizeof(*this) == 8);452basisu::clear_obj(*this);453}454455inline void set_byte_bits(uint32_t ofs, uint32_t num, uint32_t bits)456{457assert((ofs + num) <= 64U);458assert(num && (num < 32U));459assert((ofs >> 3) == ((ofs + num - 1) >> 3));460assert(bits < (1U << num));461const uint32_t byte_ofs = 7 - (ofs >> 3);462const uint32_t byte_bit_ofs = ofs & 7;463const uint32_t mask = (1 << num) - 1;464m_bytes[byte_ofs] &= ~(mask << byte_bit_ofs);465m_bytes[byte_ofs] |= (bits << byte_bit_ofs);466}467468inline void set_flip_bit(bool flip)469{470m_bytes[3] &= ~1;471m_bytes[3] |= static_cast<uint8_t>(flip);472}473474inline void set_diff_bit(bool diff)475{476m_bytes[3] &= ~2;477m_bytes[3] |= (static_cast<uint32_t>(diff) << 1);478}479480// Sets intensity modifier table (0-7) used by subblock subblock_id (0 or 1)481inline void set_inten_table(uint32_t subblock_id, uint32_t t)482{483assert(subblock_id < 2);484assert(t < 8);485const uint32_t ofs = subblock_id ? 2 : 5;486m_bytes[3] &= ~(7 << ofs);487m_bytes[3] |= (t << ofs);488}489490// Selector "val" ranges from 0-3 and is a direct index into g_etc1_inten_tables.491inline void set_selector(uint32_t x, uint32_t y, uint32_t val)492{493assert((x | y | val) < 4);494const uint32_t bit_index = x * 4 + y;495496uint8_t* p = &m_bytes[7 - (bit_index >> 3)];497498const uint32_t byte_bit_ofs = bit_index & 7;499const uint32_t mask = 1 << byte_bit_ofs;500501static const uint8_t s_selector_index_to_etc1[4] = { 3, 2, 0, 1 };502const uint32_t etc1_val = s_selector_index_to_etc1[val];503504const uint32_t lsb = etc1_val & 1;505const uint32_t msb = etc1_val >> 1;506507p[0] &= ~mask;508p[0] |= (lsb << byte_bit_ofs);509510p[-2] &= ~mask;511p[-2] |= (msb << byte_bit_ofs);512}513514// Returned encoded selector value ranges from 0-3 (this is NOT a direct index into g_etc1_inten_tables, see get_selector())515inline uint32_t get_raw_selector(uint32_t x, uint32_t y) const516{517assert((x | y) < 4);518519const uint32_t bit_index = x * 4 + y;520const uint32_t byte_bit_ofs = bit_index & 7;521const uint8_t* p = &m_bytes[7 - (bit_index >> 3)];522const uint32_t lsb = (p[0] >> byte_bit_ofs) & 1;523const uint32_t msb = (p[-2] >> byte_bit_ofs) & 1;524const uint32_t val = lsb | (msb << 1);525526return val;527}528529// Returned selector value ranges from 0-3 and is a direct index into g_etc1_inten_tables.530inline uint32_t get_selector(uint32_t x, uint32_t y) const531{532static const uint8_t s_etc1_to_selector_index[cETC1SelectorValues] = { 2, 3, 1, 0 };533return s_etc1_to_selector_index[get_raw_selector(x, y)];534}535536inline void set_raw_selector_bits(uint32_t bits)537{538m_bytes[4] = static_cast<uint8_t>(bits);539m_bytes[5] = static_cast<uint8_t>(bits >> 8);540m_bytes[6] = static_cast<uint8_t>(bits >> 16);541m_bytes[7] = static_cast<uint8_t>(bits >> 24);542}543544inline bool are_all_selectors_the_same() const545{546uint32_t v = *reinterpret_cast<const uint32_t*>(&m_bytes[4]);547548if ((v == 0xFFFFFFFF) || (v == 0xFFFF) || (!v) || (v == 0xFFFF0000))549return true;550551return false;552}553554inline void set_raw_selector_bits(uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t byte3)555{556m_bytes[4] = byte0;557m_bytes[5] = byte1;558m_bytes[6] = byte2;559m_bytes[7] = byte3;560}561562inline uint32_t get_raw_selector_bits() const563{564return m_bytes[4] | (m_bytes[5] << 8) | (m_bytes[6] << 16) | (m_bytes[7] << 24);565}566567inline void set_base4_color(uint32_t idx, uint16_t c)568{569if (idx)570{571set_byte_bits(cETC1AbsColor4R2BitOffset, 4, (c >> 8) & 15);572set_byte_bits(cETC1AbsColor4G2BitOffset, 4, (c >> 4) & 15);573set_byte_bits(cETC1AbsColor4B2BitOffset, 4, c & 15);574}575else576{577set_byte_bits(cETC1AbsColor4R1BitOffset, 4, (c >> 8) & 15);578set_byte_bits(cETC1AbsColor4G1BitOffset, 4, (c >> 4) & 15);579set_byte_bits(cETC1AbsColor4B1BitOffset, 4, c & 15);580}581}582583inline void set_base5_color(uint16_t c)584{585set_byte_bits(cETC1BaseColor5RBitOffset, 5, (c >> 10) & 31);586set_byte_bits(cETC1BaseColor5GBitOffset, 5, (c >> 5) & 31);587set_byte_bits(cETC1BaseColor5BBitOffset, 5, c & 31);588}589590void set_delta3_color(uint16_t c)591{592set_byte_bits(cETC1DeltaColor3RBitOffset, 3, (c >> 6) & 7);593set_byte_bits(cETC1DeltaColor3GBitOffset, 3, (c >> 3) & 7);594set_byte_bits(cETC1DeltaColor3BBitOffset, 3, c & 7);595}596597void set_block_color4(const color32& c0_unscaled, const color32& c1_unscaled)598{599set_diff_bit(false);600601set_base4_color(0, pack_color4(c0_unscaled, false));602set_base4_color(1, pack_color4(c1_unscaled, false));603}604605void set_block_color5(const color32& c0_unscaled, const color32& c1_unscaled)606{607set_diff_bit(true);608609set_base5_color(pack_color5(c0_unscaled, false));610611int dr = c1_unscaled.r - c0_unscaled.r;612int dg = c1_unscaled.g - c0_unscaled.g;613int db = c1_unscaled.b - c0_unscaled.b;614615set_delta3_color(pack_delta3(dr, dg, db));616}617618bool set_block_color5_check(const color32& c0_unscaled, const color32& c1_unscaled)619{620set_diff_bit(true);621622set_base5_color(pack_color5(c0_unscaled, false));623624int dr = c1_unscaled.r - c0_unscaled.r;625int dg = c1_unscaled.g - c0_unscaled.g;626int db = c1_unscaled.b - c0_unscaled.b;627628if (((dr < cETC1ColorDeltaMin) || (dr > cETC1ColorDeltaMax)) ||629((dg < cETC1ColorDeltaMin) || (dg > cETC1ColorDeltaMax)) ||630((db < cETC1ColorDeltaMin) || (db > cETC1ColorDeltaMax)))631return false;632633set_delta3_color(pack_delta3(dr, dg, db));634635return true;636}637638inline uint32_t get_byte_bits(uint32_t ofs, uint32_t num) const639{640assert((ofs + num) <= 64U);641assert(num && (num <= 8U));642assert((ofs >> 3) == ((ofs + num - 1) >> 3));643const uint32_t byte_ofs = 7 - (ofs >> 3);644const uint32_t byte_bit_ofs = ofs & 7;645return (m_bytes[byte_ofs] >> byte_bit_ofs) & ((1 << num) - 1);646}647648inline uint16_t get_base5_color() const649{650const uint32_t r = get_byte_bits(cETC1BaseColor5RBitOffset, 5);651const uint32_t g = get_byte_bits(cETC1BaseColor5GBitOffset, 5);652const uint32_t b = get_byte_bits(cETC1BaseColor5BBitOffset, 5);653return static_cast<uint16_t>(b | (g << 5U) | (r << 10U));654}655656inline uint16_t get_base4_color(uint32_t idx) const657{658uint32_t r, g, b;659if (idx)660{661r = get_byte_bits(cETC1AbsColor4R2BitOffset, 4);662g = get_byte_bits(cETC1AbsColor4G2BitOffset, 4);663b = get_byte_bits(cETC1AbsColor4B2BitOffset, 4);664}665else666{667r = get_byte_bits(cETC1AbsColor4R1BitOffset, 4);668g = get_byte_bits(cETC1AbsColor4G1BitOffset, 4);669b = get_byte_bits(cETC1AbsColor4B1BitOffset, 4);670}671return static_cast<uint16_t>(b | (g << 4U) | (r << 8U));672}673674inline color32 get_base5_color_unscaled() const675{676return color32(m_differential.m_red1, m_differential.m_green1, m_differential.m_blue1, 255);677}678679inline bool get_flip_bit() const680{681return (m_bytes[3] & 1) != 0;682}683684inline bool get_diff_bit() const685{686return (m_bytes[3] & 2) != 0;687}688689inline uint32_t get_inten_table(uint32_t subblock_id) const690{691assert(subblock_id < 2);692const uint32_t ofs = subblock_id ? 2 : 5;693return (m_bytes[3] >> ofs) & 7;694}695696inline uint16_t get_delta3_color() const697{698const uint32_t r = get_byte_bits(cETC1DeltaColor3RBitOffset, 3);699const uint32_t g = get_byte_bits(cETC1DeltaColor3GBitOffset, 3);700const uint32_t b = get_byte_bits(cETC1DeltaColor3BBitOffset, 3);701return static_cast<uint16_t>(b | (g << 3U) | (r << 6U));702}703704void get_block_colors(color32* pBlock_colors, uint32_t subblock_index) const705{706color32 b;707708if (get_diff_bit())709{710if (subblock_index)711unpack_color5(b, get_base5_color(), get_delta3_color(), true, 255);712else713unpack_color5(b, get_base5_color(), true);714}715else716{717b = unpack_color4(get_base4_color(subblock_index), true, 255);718}719720const int* pInten_table = g_etc1_inten_tables[get_inten_table(subblock_index)];721722pBlock_colors[0].set_noclamp_rgba(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);723pBlock_colors[1].set_noclamp_rgba(clamp255(b.r + pInten_table[1]), clamp255(b.g + pInten_table[1]), clamp255(b.b + pInten_table[1]), 255);724pBlock_colors[2].set_noclamp_rgba(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);725pBlock_colors[3].set_noclamp_rgba(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);726}727728static uint16_t pack_color4(const color32& color, bool scaled, uint32_t bias = 127U)729{730return pack_color4(color.r, color.g, color.b, scaled, bias);731}732733static uint16_t pack_color4(uint32_t r, uint32_t g, uint32_t b, bool scaled, uint32_t bias = 127U)734{735if (scaled)736{737r = (r * 15U + bias) / 255U;738g = (g * 15U + bias) / 255U;739b = (b * 15U + bias) / 255U;740}741742r = basisu::minimum(r, 15U);743g = basisu::minimum(g, 15U);744b = basisu::minimum(b, 15U);745746return static_cast<uint16_t>(b | (g << 4U) | (r << 8U));747}748749static uint16_t pack_color5(const color32& color, bool scaled, uint32_t bias = 127U)750{751return pack_color5(color.r, color.g, color.b, scaled, bias);752}753754static uint16_t pack_color5(uint32_t r, uint32_t g, uint32_t b, bool scaled, uint32_t bias = 127U)755{756if (scaled)757{758r = (r * 31U + bias) / 255U;759g = (g * 31U + bias) / 255U;760b = (b * 31U + bias) / 255U;761}762763r = basisu::minimum(r, 31U);764g = basisu::minimum(g, 31U);765b = basisu::minimum(b, 31U);766767return static_cast<uint16_t>(b | (g << 5U) | (r << 10U));768}769770uint16_t pack_delta3(const color32& color)771{772return pack_delta3(color.r, color.g, color.b);773}774775uint16_t pack_delta3(int r, int g, int b)776{777assert((r >= cETC1ColorDeltaMin) && (r <= cETC1ColorDeltaMax));778assert((g >= cETC1ColorDeltaMin) && (g <= cETC1ColorDeltaMax));779assert((b >= cETC1ColorDeltaMin) && (b <= cETC1ColorDeltaMax));780if (r < 0) r += 8;781if (g < 0) g += 8;782if (b < 0) b += 8;783return static_cast<uint16_t>(b | (g << 3) | (r << 6));784}785786static void unpack_delta3(int& r, int& g, int& b, uint16_t packed_delta3)787{788r = (packed_delta3 >> 6) & 7;789g = (packed_delta3 >> 3) & 7;790b = packed_delta3 & 7;791if (r >= 4) r -= 8;792if (g >= 4) g -= 8;793if (b >= 4) b -= 8;794}795796static color32 unpack_color5(uint16_t packed_color5, bool scaled, uint32_t alpha)797{798uint32_t b = packed_color5 & 31U;799uint32_t g = (packed_color5 >> 5U) & 31U;800uint32_t r = (packed_color5 >> 10U) & 31U;801802if (scaled)803{804b = (b << 3U) | (b >> 2U);805g = (g << 3U) | (g >> 2U);806r = (r << 3U) | (r >> 2U);807}808809assert(alpha <= 255);810811return color32(cNoClamp, r, g, b, alpha);812}813814static void unpack_color5(uint32_t& r, uint32_t& g, uint32_t& b, uint16_t packed_color5, bool scaled)815{816color32 c(unpack_color5(packed_color5, scaled, 0));817r = c.r;818g = c.g;819b = c.b;820}821822static void unpack_color5(color32& result, uint16_t packed_color5, bool scaled)823{824result = unpack_color5(packed_color5, scaled, 255);825}826827static bool unpack_color5(color32& result, uint16_t packed_color5, uint16_t packed_delta3, bool scaled, uint32_t alpha)828{829int dr, dg, db;830unpack_delta3(dr, dg, db, packed_delta3);831832int r = ((packed_color5 >> 10U) & 31U) + dr;833int g = ((packed_color5 >> 5U) & 31U) + dg;834int b = (packed_color5 & 31U) + db;835836bool success = true;837if (static_cast<uint32_t>(r | g | b) > 31U)838{839success = false;840r = basisu::clamp<int>(r, 0, 31);841g = basisu::clamp<int>(g, 0, 31);842b = basisu::clamp<int>(b, 0, 31);843}844845if (scaled)846{847b = (b << 3U) | (b >> 2U);848g = (g << 3U) | (g >> 2U);849r = (r << 3U) | (r >> 2U);850}851852result.set_noclamp_rgba(r, g, b, basisu::minimum(alpha, 255U));853return success;854}855856static color32 unpack_color4(uint16_t packed_color4, bool scaled, uint32_t alpha)857{858uint32_t b = packed_color4 & 15U;859uint32_t g = (packed_color4 >> 4U) & 15U;860uint32_t r = (packed_color4 >> 8U) & 15U;861862if (scaled)863{864b = (b << 4U) | b;865g = (g << 4U) | g;866r = (r << 4U) | r;867}868869return color32(cNoClamp, r, g, b, basisu::minimum(alpha, 255U));870}871872static void unpack_color4(uint32_t& r, uint32_t& g, uint32_t& b, uint16_t packed_color4, bool scaled)873{874color32 c(unpack_color4(packed_color4, scaled, 0));875r = c.r;876g = c.g;877b = c.b;878}879880static void get_diff_subblock_colors(color32* pDst, uint16_t packed_color5, uint32_t table_idx)881{882assert(table_idx < cETC1IntenModifierValues);883const int* pInten_modifer_table = &g_etc1_inten_tables[table_idx][0];884885uint32_t r, g, b;886unpack_color5(r, g, b, packed_color5, true);887888const int ir = static_cast<int>(r), ig = static_cast<int>(g), ib = static_cast<int>(b);889890const int y0 = pInten_modifer_table[0];891pDst[0].set(clamp255(ir + y0), clamp255(ig + y0), clamp255(ib + y0), 255);892893const int y1 = pInten_modifer_table[1];894pDst[1].set(clamp255(ir + y1), clamp255(ig + y1), clamp255(ib + y1), 255);895896const int y2 = pInten_modifer_table[2];897pDst[2].set(clamp255(ir + y2), clamp255(ig + y2), clamp255(ib + y2), 255);898899const int y3 = pInten_modifer_table[3];900pDst[3].set(clamp255(ir + y3), clamp255(ig + y3), clamp255(ib + y3), 255);901}902903static int clamp255(int x)904{905if (x & 0xFFFFFF00)906{907if (x < 0)908x = 0;909else if (x > 255)910x = 255;911}912913return x;914}915916static void get_block_colors5(color32* pBlock_colors, const color32& base_color5, uint32_t inten_table)917{918color32 b(base_color5);919920b.r = (b.r << 3) | (b.r >> 2);921b.g = (b.g << 3) | (b.g >> 2);922b.b = (b.b << 3) | (b.b >> 2);923924const int* pInten_table = g_etc1_inten_tables[inten_table];925926pBlock_colors[0].set(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);927pBlock_colors[1].set(clamp255(b.r + pInten_table[1]), clamp255(b.g + pInten_table[1]), clamp255(b.b + pInten_table[1]), 255);928pBlock_colors[2].set(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);929pBlock_colors[3].set(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);930}931932static void get_block_color5(const color32& base_color5, uint32_t inten_table, uint32_t index, uint32_t& r, uint32_t &g, uint32_t &b)933{934assert(index < 4);935936uint32_t br = (base_color5.r << 3) | (base_color5.r >> 2);937uint32_t bg = (base_color5.g << 3) | (base_color5.g >> 2);938uint32_t bb = (base_color5.b << 3) | (base_color5.b >> 2);939940const int* pInten_table = g_etc1_inten_tables[inten_table];941942r = clamp255(br + pInten_table[index]);943g = clamp255(bg + pInten_table[index]);944b = clamp255(bb + pInten_table[index]);945}946947static void get_block_color5_r(const color32& base_color5, uint32_t inten_table, uint32_t index, uint32_t &r)948{949assert(index < 4);950951uint32_t br = (base_color5.r << 3) | (base_color5.r >> 2);952953const int* pInten_table = g_etc1_inten_tables[inten_table];954955r = clamp255(br + pInten_table[index]);956}957958static void get_block_colors5_g(int* pBlock_colors, const color32& base_color5, uint32_t inten_table)959{960const int g = (base_color5.g << 3) | (base_color5.g >> 2);961962const int* pInten_table = g_etc1_inten_tables[inten_table];963964pBlock_colors[0] = clamp255(g + pInten_table[0]);965pBlock_colors[1] = clamp255(g + pInten_table[1]);966pBlock_colors[2] = clamp255(g + pInten_table[2]);967pBlock_colors[3] = clamp255(g + pInten_table[3]);968}969970static void get_block_colors5_bounds(color32* pBlock_colors, const color32& base_color5, uint32_t inten_table, uint32_t l = 0, uint32_t h = 3)971{972color32 b(base_color5);973974b.r = (b.r << 3) | (b.r >> 2);975b.g = (b.g << 3) | (b.g >> 2);976b.b = (b.b << 3) | (b.b >> 2);977978const int* pInten_table = g_etc1_inten_tables[inten_table];979980pBlock_colors[0].set(clamp255(b.r + pInten_table[l]), clamp255(b.g + pInten_table[l]), clamp255(b.b + pInten_table[l]), 255);981pBlock_colors[1].set(clamp255(b.r + pInten_table[h]), clamp255(b.g + pInten_table[h]), clamp255(b.b + pInten_table[h]), 255);982}983984static void get_block_colors5_bounds_g(uint32_t* pBlock_colors, const color32& base_color5, uint32_t inten_table, uint32_t l = 0, uint32_t h = 3)985{986color32 b(base_color5);987988b.g = (b.g << 3) | (b.g >> 2);989990const int* pInten_table = g_etc1_inten_tables[inten_table];991992pBlock_colors[0] = clamp255(b.g + pInten_table[l]);993pBlock_colors[1] = clamp255(b.g + pInten_table[h]);994}995};996997enum dxt_constants998{999cDXT1SelectorBits = 2U, cDXT1SelectorValues = 1U << cDXT1SelectorBits, cDXT1SelectorMask = cDXT1SelectorValues - 1U,1000cDXT5SelectorBits = 3U, cDXT5SelectorValues = 1U << cDXT5SelectorBits, cDXT5SelectorMask = cDXT5SelectorValues - 1U,1001};10021003static const uint8_t g_etc1_x_selector_unpack[4][256] =1004{1005{10060, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,10070, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,10080, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,10090, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,10100, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,10110, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,10120, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,10130, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,1014},1015{10160, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,10172, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3,10180, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,10192, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3,10200, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,10212, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3,10220, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,10232, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3,1024},10251026{10270, 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,10280, 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,10292, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3,10302, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3,10310, 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,10320, 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,10332, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3,10342, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3,1035},10361037{10380, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,10390, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,10400, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,10410, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,10422, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,10432, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,10442, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,10452, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,1046}1047};10481049struct dxt1_block1050{1051enum { cTotalEndpointBytes = 2, cTotalSelectorBytes = 4 };10521053uint8_t m_low_color[cTotalEndpointBytes];1054uint8_t m_high_color[cTotalEndpointBytes];1055uint8_t m_selectors[cTotalSelectorBytes];10561057inline void clear() { basisu::clear_obj(*this); }10581059inline uint32_t get_high_color() const { return m_high_color[0] | (m_high_color[1] << 8U); }1060inline uint32_t get_low_color() const { return m_low_color[0] | (m_low_color[1] << 8U); }1061inline void set_low_color(uint16_t c) { m_low_color[0] = static_cast<uint8_t>(c & 0xFF); m_low_color[1] = static_cast<uint8_t>((c >> 8) & 0xFF); }1062inline void set_high_color(uint16_t c) { m_high_color[0] = static_cast<uint8_t>(c & 0xFF); m_high_color[1] = static_cast<uint8_t>((c >> 8) & 0xFF); }1063inline uint32_t get_selector(uint32_t x, uint32_t y) const { assert((x < 4U) && (y < 4U)); return (m_selectors[y] >> (x * cDXT1SelectorBits)) & cDXT1SelectorMask; }1064inline void set_selector(uint32_t x, uint32_t y, uint32_t val) { assert((x < 4U) && (y < 4U) && (val < 4U)); m_selectors[y] &= (~(cDXT1SelectorMask << (x * cDXT1SelectorBits))); m_selectors[y] |= (val << (x * cDXT1SelectorBits)); }10651066static uint16_t pack_color(const color32& color, bool scaled, uint32_t bias = 127U)1067{1068uint32_t r = color.r, g = color.g, b = color.b;1069if (scaled)1070{1071r = (r * 31U + bias) / 255U;1072g = (g * 63U + bias) / 255U;1073b = (b * 31U + bias) / 255U;1074}1075return static_cast<uint16_t>(basisu::minimum(b, 31U) | (basisu::minimum(g, 63U) << 5U) | (basisu::minimum(r, 31U) << 11U));1076}10771078static uint16_t pack_unscaled_color(uint32_t r, uint32_t g, uint32_t b) { return static_cast<uint16_t>(b | (g << 5U) | (r << 11U)); }1079};10801081struct dxt_selector_range1082{1083uint32_t m_low;1084uint32_t m_high;1085};10861087struct etc1_to_dxt1_56_solution1088{1089uint8_t m_lo;1090uint8_t m_hi;1091uint16_t m_err;1092};10931094#if BASISD_SUPPORT_DXT11095static dxt_selector_range g_etc1_to_dxt1_selector_ranges[] =1096{1097{ 0, 3 },10981099{ 1, 3 },1100{ 0, 2 },11011102{ 1, 2 },11031104{ 2, 3 },1105{ 0, 1 },1106};11071108const uint32_t NUM_ETC1_TO_DXT1_SELECTOR_RANGES = sizeof(g_etc1_to_dxt1_selector_ranges) / sizeof(g_etc1_to_dxt1_selector_ranges[0]);11091110static uint32_t g_etc1_to_dxt1_selector_range_index[4][4];11111112const uint32_t NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS = 10;1113static const uint8_t g_etc1_to_dxt1_selector_mappings[NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS][4] =1114{1115{ 0, 0, 1, 1 },1116{ 0, 0, 1, 2 },1117{ 0, 0, 1, 3 },1118{ 0, 0, 2, 3 },1119{ 0, 1, 1, 1 },1120{ 0, 1, 2, 2 },1121{ 0, 1, 2, 3 },1122{ 0, 2, 3, 3 },1123{ 1, 2, 2, 2 },1124{ 1, 2, 3, 3 },1125};11261127static uint8_t g_etc1_to_dxt1_selector_mappings_raw_dxt1_256[NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS][256];1128static uint8_t g_etc1_to_dxt1_selector_mappings_raw_dxt1_inv_256[NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS][256];11291130static const etc1_to_dxt1_56_solution g_etc1_to_dxt_6[32 * 8 * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS * NUM_ETC1_TO_DXT1_SELECTOR_RANGES] = {1131#include "basisu_transcoder_tables_dxt1_6.inc"1132};11331134static const etc1_to_dxt1_56_solution g_etc1_to_dxt_5[32 * 8 * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS * NUM_ETC1_TO_DXT1_SELECTOR_RANGES] = {1135#include "basisu_transcoder_tables_dxt1_5.inc"1136};1137#endif // BASISD_SUPPORT_DXT111381139#if BASISD_SUPPORT_DXT1 || BASISD_SUPPORT_UASTC1140// First saw the idea for optimal BC1 single-color block encoding using lookup tables in ryg_dxt.1141struct bc1_match_entry1142{1143uint8_t m_hi;1144uint8_t m_lo;1145};1146static bc1_match_entry g_bc1_match5_equals_1[256], g_bc1_match6_equals_1[256]; // selector 1, allow equals hi/lo1147static bc1_match_entry g_bc1_match5_equals_0[256], g_bc1_match6_equals_0[256]; // selector 0, allow equals hi/lo11481149static void prepare_bc1_single_color_table(bc1_match_entry* pTable, const uint8_t* pExpand, int size0, int size1, int sel)1150{1151for (int i = 0; i < 256; i++)1152{1153int lowest_e = 256;1154for (int lo = 0; lo < size0; lo++)1155{1156for (int hi = 0; hi < size1; hi++)1157{1158const int lo_e = pExpand[lo], hi_e = pExpand[hi];1159int e;11601161if (sel == 1)1162{1163// Selector 11164e = basisu::iabs(((hi_e * 2 + lo_e) / 3) - i);1165e += (basisu::iabs(hi_e - lo_e) * 3) / 100;1166}1167else1168{1169assert(sel == 0);11701171// Selector 01172e = basisu::iabs(hi_e - i);1173}11741175if (e < lowest_e)1176{1177pTable[i].m_hi = static_cast<uint8_t>(hi);1178pTable[i].m_lo = static_cast<uint8_t>(lo);11791180lowest_e = e;1181}11821183} // hi1184} // lo1185}1186}1187#endif11881189#if BASISD_WRITE_NEW_DXT1_TABLES1190static void create_etc1_to_dxt1_5_conversion_table()1191{1192FILE* pFile = nullptr;1193fopen_s(&pFile, "basisu_transcoder_tables_dxt1_5.inc", "w");11941195uint32_t n = 0;11961197for (int inten = 0; inten < 8; inten++)1198{1199for (uint32_t g = 0; g < 32; g++)1200{1201color32 block_colors[4];1202decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);12031204for (uint32_t sr = 0; sr < NUM_ETC1_TO_DXT1_SELECTOR_RANGES; sr++)1205{1206const uint32_t low_selector = g_etc1_to_dxt1_selector_ranges[sr].m_low;1207const uint32_t high_selector = g_etc1_to_dxt1_selector_ranges[sr].m_high;12081209for (uint32_t m = 0; m < NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS; m++)1210{1211uint32_t best_lo = 0;1212uint32_t best_hi = 0;1213uint64_t best_err = UINT64_MAX;12141215for (uint32_t hi = 0; hi <= 31; hi++)1216{1217for (uint32_t lo = 0; lo <= 31; lo++)1218{1219//if (lo == hi) continue;12201221uint32_t colors[4];12221223colors[0] = (lo << 3) | (lo >> 2);1224colors[3] = (hi << 3) | (hi >> 2);12251226colors[1] = (colors[0] * 2 + colors[3]) / 3;1227colors[2] = (colors[3] * 2 + colors[0]) / 3;12281229uint64_t total_err = 0;12301231for (uint32_t s = low_selector; s <= high_selector; s++)1232{1233int err = block_colors[s].g - colors[g_etc1_to_dxt1_selector_mappings[m][s]];12341235total_err += err * err;1236}12371238if (total_err < best_err)1239{1240best_err = total_err;1241best_lo = lo;1242best_hi = hi;1243}1244}1245}12461247assert(best_err <= 0xFFFF);12481249//table[g + inten * 32].m_solutions[sr][m].m_lo = static_cast<uint8_t>(best_lo);1250//table[g + inten * 32].m_solutions[sr][m].m_hi = static_cast<uint8_t>(best_hi);1251//table[g + inten * 32].m_solutions[sr][m].m_err = static_cast<uint16_t>(best_err);12521253//assert(best_lo != best_hi);1254fprintf(pFile, "{%u,%u,%u},", best_lo, best_hi, (uint32_t)best_err);1255n++;1256if ((n & 31) == 31)1257fprintf(pFile, "\n");1258} // m1259} // sr1260} // g1261} // inten12621263fclose(pFile);1264}12651266static void create_etc1_to_dxt1_6_conversion_table()1267{1268FILE* pFile = nullptr;1269fopen_s(&pFile, "basisu_transcoder_tables_dxt1_6.inc", "w");12701271uint32_t n = 0;12721273for (int inten = 0; inten < 8; inten++)1274{1275for (uint32_t g = 0; g < 32; g++)1276{1277color32 block_colors[4];1278decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);12791280for (uint32_t sr = 0; sr < NUM_ETC1_TO_DXT1_SELECTOR_RANGES; sr++)1281{1282const uint32_t low_selector = g_etc1_to_dxt1_selector_ranges[sr].m_low;1283const uint32_t high_selector = g_etc1_to_dxt1_selector_ranges[sr].m_high;12841285for (uint32_t m = 0; m < NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS; m++)1286{1287uint32_t best_lo = 0;1288uint32_t best_hi = 0;1289uint64_t best_err = UINT64_MAX;12901291for (uint32_t hi = 0; hi <= 63; hi++)1292{1293for (uint32_t lo = 0; lo <= 63; lo++)1294{1295//if (lo == hi) continue;12961297uint32_t colors[4];12981299colors[0] = (lo << 2) | (lo >> 4);1300colors[3] = (hi << 2) | (hi >> 4);13011302colors[1] = (colors[0] * 2 + colors[3]) / 3;1303colors[2] = (colors[3] * 2 + colors[0]) / 3;13041305uint64_t total_err = 0;13061307for (uint32_t s = low_selector; s <= high_selector; s++)1308{1309int err = block_colors[s].g - colors[g_etc1_to_dxt1_selector_mappings[m][s]];13101311total_err += err * err;1312}13131314if (total_err < best_err)1315{1316best_err = total_err;1317best_lo = lo;1318best_hi = hi;1319}1320}1321}13221323assert(best_err <= 0xFFFF);13241325//table[g + inten * 32].m_solutions[sr][m].m_lo = static_cast<uint8_t>(best_lo);1326//table[g + inten * 32].m_solutions[sr][m].m_hi = static_cast<uint8_t>(best_hi);1327//table[g + inten * 32].m_solutions[sr][m].m_err = static_cast<uint16_t>(best_err);13281329//assert(best_lo != best_hi);1330fprintf(pFile, "{%u,%u,%u},", best_lo, best_hi, (uint32_t)best_err);1331n++;1332if ((n & 31) == 31)1333fprintf(pFile, "\n");13341335} // m1336} // sr1337} // g1338} // inten13391340fclose(pFile);1341}1342#endif13431344#if BASISD_SUPPORT_UASTC || BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_ETC2_EAC_RG111345static const int8_t g_eac_modifier_table[16][8] =1346{1347{ -3, -6, -9, -15, 2, 5, 8, 14 },1348{ -3, -7, -10, -13, 2, 6, 9, 12 },1349{ -2, -5, -8, -13, 1, 4, 7, 12 },1350{ -2, -4, -6, -13, 1, 3, 5, 12 },1351{ -3, -6, -8, -12, 2, 5, 7, 11 },1352{ -3, -7, -9, -11, 2, 6, 8, 10 },1353{ -4, -7, -8, -11, 3, 6, 7, 10 },1354{ -3, -5, -8, -11, 2, 4, 7, 10 },13551356{ -2, -6, -8, -10, 1, 5, 7, 9 },1357{ -2, -5, -8, -10, 1, 4, 7, 9 },1358{ -2, -4, -8, -10, 1, 3, 7, 9 },1359{ -2, -5, -7, -10, 1, 4, 6, 9 },1360{ -3, -4, -7, -10, 2, 3, 6, 9 },1361{ -1, -2, -3, -10, 0, 1, 2, 9 }, // entry 131362{ -4, -6, -8, -9, 3, 5, 7, 8 },1363{ -3, -5, -7, -9, 2, 4, 6, 8 }1364};13651366// Used by ETC2 EAC A8 and ETC2 EAC R11/RG11.1367struct eac_block1368{1369uint16_t m_base : 8;13701371uint16_t m_table : 4;1372uint16_t m_multiplier : 4;13731374uint8_t m_selectors[6];13751376uint32_t get_selector(uint32_t x, uint32_t y) const1377{1378assert((x < 4) && (y < 4));13791380const uint32_t ofs = 45 - (y + x * 4) * 3;13811382const uint64_t pixels = get_selector_bits();13831384return (pixels >> ofs) & 7;1385}13861387void set_selector(uint32_t x, uint32_t y, uint32_t s)1388{1389assert((x < 4) && (y < 4) && (s < 8));13901391const uint32_t ofs = 45 - (y + x * 4) * 3;13921393uint64_t pixels = get_selector_bits();13941395pixels &= ~(7ULL << ofs);1396pixels |= (static_cast<uint64_t>(s) << ofs);13971398set_selector_bits(pixels);1399}14001401uint64_t get_selector_bits() const1402{1403uint64_t pixels = ((uint64_t)m_selectors[0] << 40) | ((uint64_t)m_selectors[1] << 32) |1404((uint64_t)m_selectors[2] << 24) |1405((uint64_t)m_selectors[3] << 16) | ((uint64_t)m_selectors[4] << 8) | m_selectors[5];1406return pixels;1407}14081409void set_selector_bits(uint64_t pixels)1410{1411m_selectors[0] = (uint8_t)(pixels >> 40);1412m_selectors[1] = (uint8_t)(pixels >> 32);1413m_selectors[2] = (uint8_t)(pixels >> 24);1414m_selectors[3] = (uint8_t)(pixels >> 16);1415m_selectors[4] = (uint8_t)(pixels >> 8);1416m_selectors[5] = (uint8_t)(pixels);1417}1418};14191420#endif // #if BASISD_SUPPORT_UASTC BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_ETC2_EAC_RG1114211422#if BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_ETC2_EAC_RG111423static const dxt_selector_range s_etc2_eac_selector_ranges[] =1424{1425{ 0, 3 },14261427{ 1, 3 },1428{ 0, 2 },14291430{ 1, 2 },1431};14321433const uint32_t NUM_ETC2_EAC_SELECTOR_RANGES = sizeof(s_etc2_eac_selector_ranges) / sizeof(s_etc2_eac_selector_ranges[0]);14341435struct etc1_g_to_eac_conversion1436{1437uint8_t m_base;1438uint8_t m_table_mul; // mul*16+table1439uint16_t m_trans; // translates ETC1 selectors to ETC2_EAC_A81440};1441#endif // BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_ETC2_EAC_RG1114421443#if BASISD_SUPPORT_ETC2_EAC_A814441445#if BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES1446struct pack_eac_a8_results1447{1448uint32_t m_base;1449uint32_t m_table;1450uint32_t m_multiplier;1451basisu::vector<uint8_t> m_selectors;1452basisu::vector<uint8_t> m_selectors_temp;1453};14541455static uint64_t pack_eac_a8_exhaustive(pack_eac_a8_results& results, const uint8_t* pPixels, uint32_t num_pixels)1456{1457results.m_selectors.resize(num_pixels);1458results.m_selectors_temp.resize(num_pixels);14591460uint64_t best_err = UINT64_MAX;14611462for (uint32_t base_color = 0; base_color < 256; base_color++)1463{1464for (uint32_t multiplier = 1; multiplier < 16; multiplier++)1465{1466for (uint32_t table = 0; table < 16; table++)1467{1468uint64_t total_err = 0;14691470for (uint32_t i = 0; i < num_pixels; i++)1471{1472const int a = pPixels[i];14731474uint32_t best_s_err = UINT32_MAX;1475uint32_t best_s = 0;1476for (uint32_t s = 0; s < 8; s++)1477{1478int v = (int)multiplier * g_eac_modifier_table[table][s] + (int)base_color;1479if (v < 0)1480v = 0;1481else if (v > 255)1482v = 255;14831484uint32_t err = abs(a - v);1485if (err < best_s_err)1486{1487best_s_err = err;1488best_s = s;1489}1490}14911492results.m_selectors_temp[i] = static_cast<uint8_t>(best_s);14931494total_err += best_s_err * best_s_err;1495if (total_err >= best_err)1496break;1497}14981499if (total_err < best_err)1500{1501best_err = total_err;1502results.m_base = base_color;1503results.m_multiplier = multiplier;1504results.m_table = table;1505results.m_selectors.swap(results.m_selectors_temp);1506}15071508} // table15091510} // multiplier15111512} // base_color15131514return best_err;1515}1516#endif // BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES15171518static1519#if !BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES1520const1521#endif1522etc1_g_to_eac_conversion s_etc1_g_to_etc2_a8[32 * 8][NUM_ETC2_EAC_SELECTOR_RANGES] =1523{1524{ { 0,1,3328 },{ 0,1,3328 },{ 0,1,256 },{ 0,1,256 } },1525{ { 0,226,3936 },{ 0,226,3936 },{ 0,81,488 },{ 0,81,488 } },1526{ { 6,178,4012 },{ 6,178,4008 },{ 0,146,501 },{ 0,130,496 } },1527{ { 14,178,4012 },{ 14,178,4008 },{ 8,146,501 },{ 6,82,496 } },1528{ { 23,178,4012 },{ 23,178,4008 },{ 17,146,501 },{ 3,228,496 } },1529{ { 31,178,4012 },{ 31,178,4008 },{ 25,146,501 },{ 11,228,496 } },1530{ { 39,178,4012 },{ 39,178,4008 },{ 33,146,501 },{ 19,228,496 } },1531{ { 47,178,4012 },{ 47,178,4008 },{ 41,146,501 },{ 27,228,496 } },1532{ { 56,178,4012 },{ 56,178,4008 },{ 50,146,501 },{ 36,228,496 } },1533{ { 64,178,4012 },{ 64,178,4008 },{ 58,146,501 },{ 44,228,496 } },1534{ { 72,178,4012 },{ 72,178,4008 },{ 66,146,501 },{ 52,228,496 } },1535{ { 80,178,4012 },{ 80,178,4008 },{ 74,146,501 },{ 60,228,496 } },1536{ { 89,178,4012 },{ 89,178,4008 },{ 83,146,501 },{ 69,228,496 } },1537{ { 97,178,4012 },{ 97,178,4008 },{ 91,146,501 },{ 77,228,496 } },1538{ { 105,178,4012 },{ 105,178,4008 },{ 99,146,501 },{ 85,228,496 } },1539{ { 113,178,4012 },{ 113,178,4008 },{ 107,146,501 },{ 93,228,496 } },1540{ { 122,178,4012 },{ 122,178,4008 },{ 116,146,501 },{ 102,228,496 } },1541{ { 130,178,4012 },{ 130,178,4008 },{ 124,146,501 },{ 110,228,496 } },1542{ { 138,178,4012 },{ 138,178,4008 },{ 132,146,501 },{ 118,228,496 } },1543{ { 146,178,4012 },{ 146,178,4008 },{ 140,146,501 },{ 126,228,496 } },1544{ { 155,178,4012 },{ 155,178,4008 },{ 149,146,501 },{ 135,228,496 } },1545{ { 163,178,4012 },{ 163,178,4008 },{ 157,146,501 },{ 143,228,496 } },1546{ { 171,178,4012 },{ 171,178,4008 },{ 165,146,501 },{ 151,228,496 } },1547{ { 179,178,4012 },{ 179,178,4008 },{ 173,146,501 },{ 159,228,496 } },1548{ { 188,178,4012 },{ 188,178,4008 },{ 182,146,501 },{ 168,228,496 } },1549{ { 196,178,4012 },{ 196,178,4008 },{ 190,146,501 },{ 176,228,496 } },1550{ { 204,178,4012 },{ 204,178,4008 },{ 198,146,501 },{ 184,228,496 } },1551{ { 212,178,4012 },{ 212,178,4008 },{ 206,146,501 },{ 192,228,496 } },1552{ { 221,178,4012 },{ 221,178,4008 },{ 215,146,501 },{ 201,228,496 } },1553{ { 229,178,4012 },{ 229,178,4008 },{ 223,146,501 },{ 209,228,496 } },1554{ { 235,66,4012 },{ 221,100,4008 },{ 231,146,501 },{ 217,228,496 } },1555{ { 211,102,4085 },{ 118,31,4080 },{ 211,102,501 },{ 118,31,496 } },1556{ { 1,2,3328 },{ 1,2,3328 },{ 0,1,320 },{ 0,1,320 } },1557{ { 7,162,3905 },{ 7,162,3904 },{ 1,17,480 },{ 1,17,480 } },1558{ { 15,162,3906 },{ 15,162,3904 },{ 1,117,352 },{ 1,117,352 } },1559{ { 23,162,3906 },{ 23,162,3904 },{ 5,34,500 },{ 4,53,424 } },1560{ { 32,162,3906 },{ 32,162,3904 },{ 14,34,500 },{ 3,69,424 } },1561{ { 40,162,3906 },{ 40,162,3904 },{ 22,34,500 },{ 1,133,496 } },1562{ { 48,162,3906 },{ 48,162,3904 },{ 30,34,500 },{ 4,85,496 } },1563{ { 56,162,3906 },{ 56,162,3904 },{ 38,34,500 },{ 12,85,496 } },1564{ { 65,162,3906 },{ 65,162,3904 },{ 47,34,500 },{ 1,106,424 } },1565{ { 73,162,3906 },{ 73,162,3904 },{ 55,34,500 },{ 9,106,424 } },1566{ { 81,162,3906 },{ 81,162,3904 },{ 63,34,500 },{ 7,234,496 } },1567{ { 89,162,3906 },{ 89,162,3904 },{ 71,34,500 },{ 15,234,496 } },1568{ { 98,162,3906 },{ 98,162,3904 },{ 80,34,500 },{ 24,234,496 } },1569{ { 106,162,3906 },{ 106,162,3904 },{ 88,34,500 },{ 32,234,496 } },1570{ { 114,162,3906 },{ 114,162,3904 },{ 96,34,500 },{ 40,234,496 } },1571{ { 122,162,3906 },{ 122,162,3904 },{ 104,34,500 },{ 48,234,496 } },1572{ { 131,162,3906 },{ 131,162,3904 },{ 113,34,500 },{ 57,234,496 } },1573{ { 139,162,3906 },{ 139,162,3904 },{ 121,34,500 },{ 65,234,496 } },1574{ { 147,162,3906 },{ 147,162,3904 },{ 129,34,500 },{ 73,234,496 } },1575{ { 155,162,3906 },{ 155,162,3904 },{ 137,34,500 },{ 81,234,496 } },1576{ { 164,162,3906 },{ 164,162,3904 },{ 146,34,500 },{ 90,234,496 } },1577{ { 172,162,3906 },{ 172,162,3904 },{ 154,34,500 },{ 98,234,496 } },1578{ { 180,162,3906 },{ 180,162,3904 },{ 162,34,500 },{ 106,234,496 } },1579{ { 188,162,3906 },{ 188,162,3904 },{ 170,34,500 },{ 114,234,496 } },1580{ { 197,162,3906 },{ 197,162,3904 },{ 179,34,500 },{ 123,234,496 } },1581{ { 205,162,3906 },{ 205,162,3904 },{ 187,34,500 },{ 131,234,496 } },1582{ { 213,162,3906 },{ 213,162,3904 },{ 195,34,500 },{ 139,234,496 } },1583{ { 221,162,3906 },{ 221,162,3904 },{ 203,34,500 },{ 147,234,496 } },1584{ { 230,162,3906 },{ 230,162,3904 },{ 212,34,500 },{ 156,234,496 } },1585{ { 238,162,3906 },{ 174,106,4008 },{ 220,34,500 },{ 164,234,496 } },1586{ { 240,178,4001 },{ 182,106,4008 },{ 228,34,500 },{ 172,234,496 } },1587{ { 166,108,4085 },{ 115,31,4080 },{ 166,108,501 },{ 115,31,496 } },1588{ { 1,68,3328 },{ 1,68,3328 },{ 0,17,384 },{ 0,17,384 } },1589{ { 1,148,3904 },{ 1,148,3904 },{ 1,2,384 },{ 1,2,384 } },1590{ { 21,18,3851 },{ 21,18,3848 },{ 1,50,488 },{ 1,50,488 } },1591{ { 27,195,3851 },{ 29,18,3848 },{ 0,67,488 },{ 0,67,488 } },1592{ { 34,195,3907 },{ 38,18,3848 },{ 20,66,482 },{ 0,3,496 } },1593{ { 42,195,3907 },{ 46,18,3848 },{ 28,66,482 },{ 2,6,424 } },1594{ { 50,195,3907 },{ 54,18,3848 },{ 36,66,482 },{ 4,22,424 } },1595{ { 58,195,3907 },{ 62,18,3848 },{ 44,66,482 },{ 3,73,424 } },1596{ { 67,195,3907 },{ 71,18,3848 },{ 53,66,482 },{ 3,22,496 } },1597{ { 75,195,3907 },{ 79,18,3848 },{ 61,66,482 },{ 2,137,496 } },1598{ { 83,195,3907 },{ 87,18,3848 },{ 69,66,482 },{ 1,89,496 } },1599{ { 91,195,3907 },{ 95,18,3848 },{ 77,66,482 },{ 9,89,496 } },1600{ { 100,195,3907 },{ 104,18,3848 },{ 86,66,482 },{ 18,89,496 } },1601{ { 108,195,3907 },{ 112,18,3848 },{ 94,66,482 },{ 26,89,496 } },1602{ { 116,195,3907 },{ 120,18,3848 },{ 102,66,482 },{ 34,89,496 } },1603{ { 124,195,3907 },{ 128,18,3848 },{ 110,66,482 },{ 42,89,496 } },1604{ { 133,195,3907 },{ 137,18,3848 },{ 119,66,482 },{ 51,89,496 } },1605{ { 141,195,3907 },{ 145,18,3848 },{ 127,66,482 },{ 59,89,496 } },1606{ { 149,195,3907 },{ 153,18,3848 },{ 135,66,482 },{ 67,89,496 } },1607{ { 157,195,3907 },{ 161,18,3848 },{ 143,66,482 },{ 75,89,496 } },1608{ { 166,195,3907 },{ 170,18,3848 },{ 152,66,482 },{ 84,89,496 } },1609{ { 174,195,3907 },{ 178,18,3848 },{ 160,66,482 },{ 92,89,496 } },1610{ { 182,195,3907 },{ 186,18,3848 },{ 168,66,482 },{ 100,89,496 } },1611{ { 190,195,3907 },{ 194,18,3848 },{ 176,66,482 },{ 108,89,496 } },1612{ { 199,195,3907 },{ 203,18,3848 },{ 185,66,482 },{ 117,89,496 } },1613{ { 207,195,3907 },{ 211,18,3848 },{ 193,66,482 },{ 125,89,496 } },1614{ { 215,195,3907 },{ 219,18,3848 },{ 201,66,482 },{ 133,89,496 } },1615{ { 223,195,3907 },{ 227,18,3848 },{ 209,66,482 },{ 141,89,496 } },1616{ { 231,195,3907 },{ 168,89,4008 },{ 218,66,482 },{ 150,89,496 } },1617{ { 236,18,3907 },{ 176,89,4008 },{ 226,66,482 },{ 158,89,496 } },1618{ { 158,90,4085 },{ 103,31,4080 },{ 158,90,501 },{ 103,31,496 } },1619{ { 166,90,4085 },{ 111,31,4080 },{ 166,90,501 },{ 111,31,496 } },1620{ { 0,70,3328 },{ 0,70,3328 },{ 0,45,256 },{ 0,45,256 } },1621{ { 0,117,3904 },{ 0,117,3904 },{ 0,35,384 },{ 0,35,384 } },1622{ { 13,165,3905 },{ 13,165,3904 },{ 3,221,416 },{ 3,221,416 } },1623{ { 21,165,3906 },{ 21,165,3904 },{ 11,221,416 },{ 11,221,416 } },1624{ { 30,165,3906 },{ 30,165,3904 },{ 7,61,352 },{ 7,61,352 } },1625{ { 38,165,3906 },{ 38,165,3904 },{ 2,125,352 },{ 2,125,352 } },1626{ { 46,165,3906 },{ 46,165,3904 },{ 2,37,500 },{ 10,125,352 } },1627{ { 54,165,3906 },{ 54,165,3904 },{ 10,37,500 },{ 5,61,424 } },1628{ { 63,165,3906 },{ 63,165,3904 },{ 19,37,500 },{ 1,189,424 } },1629{ { 4,254,4012 },{ 71,165,3904 },{ 27,37,500 },{ 9,189,424 } },1630{ { 12,254,4012 },{ 79,165,3904 },{ 35,37,500 },{ 4,77,424 } },1631{ { 20,254,4012 },{ 87,165,3904 },{ 43,37,500 },{ 12,77,424 } },1632{ { 29,254,4012 },{ 96,165,3904 },{ 52,37,500 },{ 8,93,424 } },1633{ { 37,254,4012 },{ 104,165,3904 },{ 60,37,500 },{ 3,141,496 } },1634{ { 45,254,4012 },{ 112,165,3904 },{ 68,37,500 },{ 11,141,496 } },1635{ { 53,254,4012 },{ 120,165,3904 },{ 76,37,500 },{ 6,93,496 } },1636{ { 62,254,4012 },{ 129,165,3904 },{ 85,37,500 },{ 15,93,496 } },1637{ { 70,254,4012 },{ 137,165,3904 },{ 93,37,500 },{ 23,93,496 } },1638{ { 78,254,4012 },{ 145,165,3904 },{ 101,37,500 },{ 31,93,496 } },1639{ { 86,254,4012 },{ 153,165,3904 },{ 109,37,500 },{ 39,93,496 } },1640{ { 95,254,4012 },{ 162,165,3904 },{ 118,37,500 },{ 48,93,496 } },1641{ { 103,254,4012 },{ 170,165,3904 },{ 126,37,500 },{ 56,93,496 } },1642{ { 111,254,4012 },{ 178,165,3904 },{ 134,37,500 },{ 64,93,496 } },1643{ { 119,254,4012 },{ 186,165,3904 },{ 142,37,500 },{ 72,93,496 } },1644{ { 128,254,4012 },{ 195,165,3904 },{ 151,37,500 },{ 81,93,496 } },1645{ { 136,254,4012 },{ 203,165,3904 },{ 159,37,500 },{ 89,93,496 } },1646{ { 212,165,3906 },{ 136,77,4008 },{ 167,37,500 },{ 97,93,496 } },1647{ { 220,165,3394 },{ 131,93,4008 },{ 175,37,500 },{ 105,93,496 } },1648{ { 214,181,4001 },{ 140,93,4008 },{ 184,37,500 },{ 114,93,496 } },1649{ { 222,181,4001 },{ 148,93,4008 },{ 192,37,500 },{ 122,93,496 } },1650{ { 114,95,4085 },{ 99,31,4080 },{ 114,95,501 },{ 99,31,496 } },1651{ { 122,95,4085 },{ 107,31,4080 },{ 122,95,501 },{ 107,31,496 } },1652{ { 0,102,3840 },{ 0,102,3840 },{ 0,18,384 },{ 0,18,384 } },1653{ { 5,167,3904 },{ 5,167,3904 },{ 0,13,256 },{ 0,13,256 } },1654{ { 4,54,3968 },{ 4,54,3968 },{ 1,67,448 },{ 1,67,448 } },1655{ { 30,198,3850 },{ 30,198,3848 },{ 0,3,480 },{ 0,3,480 } },1656{ { 39,198,3850 },{ 39,198,3848 },{ 3,52,488 },{ 3,52,488 } },1657{ { 47,198,3851 },{ 47,198,3848 },{ 3,4,488 },{ 3,4,488 } },1658{ { 55,198,3851 },{ 55,198,3848 },{ 1,70,488 },{ 1,70,488 } },1659{ { 54,167,3906 },{ 63,198,3848 },{ 3,22,488 },{ 3,22,488 } },1660{ { 62,167,3906 },{ 72,198,3848 },{ 24,118,488 },{ 0,6,496 } },1661{ { 70,167,3906 },{ 80,198,3848 },{ 32,118,488 },{ 2,89,488 } },1662{ { 78,167,3906 },{ 88,198,3848 },{ 40,118,488 },{ 1,73,496 } },1663{ { 86,167,3906 },{ 96,198,3848 },{ 48,118,488 },{ 0,28,424 } },1664{ { 95,167,3906 },{ 105,198,3848 },{ 57,118,488 },{ 9,28,424 } },1665{ { 103,167,3906 },{ 113,198,3848 },{ 65,118,488 },{ 5,108,496 } },1666{ { 111,167,3906 },{ 121,198,3848 },{ 73,118,488 },{ 13,108,496 } },1667{ { 119,167,3906 },{ 129,198,3848 },{ 81,118,488 },{ 21,108,496 } },1668{ { 128,167,3906 },{ 138,198,3848 },{ 90,118,488 },{ 6,28,496 } },1669{ { 136,167,3906 },{ 146,198,3848 },{ 98,118,488 },{ 14,28,496 } },1670{ { 144,167,3906 },{ 154,198,3848 },{ 106,118,488 },{ 22,28,496 } },1671{ { 152,167,3906 },{ 162,198,3848 },{ 114,118,488 },{ 30,28,496 } },1672{ { 161,167,3906 },{ 171,198,3848 },{ 123,118,488 },{ 39,28,496 } },1673{ { 169,167,3906 },{ 179,198,3848 },{ 131,118,488 },{ 47,28,496 } },1674{ { 177,167,3906 },{ 187,198,3848 },{ 139,118,488 },{ 55,28,496 } },1675{ { 185,167,3906 },{ 195,198,3848 },{ 147,118,488 },{ 63,28,496 } },1676{ { 194,167,3906 },{ 120,12,4008 },{ 156,118,488 },{ 72,28,496 } },1677{ { 206,198,3907 },{ 116,28,4008 },{ 164,118,488 },{ 80,28,496 } },1678{ { 214,198,3907 },{ 124,28,4008 },{ 172,118,488 },{ 88,28,496 } },1679{ { 222,198,3395 },{ 132,28,4008 },{ 180,118,488 },{ 96,28,496 } },1680{ { 207,134,4001 },{ 141,28,4008 },{ 189,118,488 },{ 105,28,496 } },1681{ { 95,30,4085 },{ 86,31,4080 },{ 95,30,501 },{ 86,31,496 } },1682{ { 103,30,4085 },{ 94,31,4080 },{ 103,30,501 },{ 94,31,496 } },1683{ { 111,30,4085 },{ 102,31,4080 },{ 111,30,501 },{ 102,31,496 } },1684{ { 0,104,3840 },{ 0,104,3840 },{ 0,18,448 },{ 0,18,448 } },1685{ { 4,39,3904 },{ 4,39,3904 },{ 0,4,384 },{ 0,4,384 } },1686{ { 0,56,3968 },{ 0,56,3968 },{ 0,84,448 },{ 0,84,448 } },1687{ { 6,110,3328 },{ 6,110,3328 },{ 0,20,448 },{ 0,20,448 } },1688{ { 41,200,3850 },{ 41,200,3848 },{ 1,4,480 },{ 1,4,480 } },1689{ { 49,200,3850 },{ 49,200,3848 },{ 1,8,416 },{ 1,8,416 } },1690{ { 57,200,3851 },{ 57,200,3848 },{ 1,38,488 },{ 1,38,488 } },1691{ { 65,200,3851 },{ 65,200,3848 },{ 1,120,488 },{ 1,120,488 } },1692{ { 74,200,3851 },{ 74,200,3848 },{ 2,72,488 },{ 2,72,488 } },1693{ { 69,6,3907 },{ 82,200,3848 },{ 2,24,488 },{ 2,24,488 } },1694{ { 77,6,3907 },{ 90,200,3848 },{ 26,120,488 },{ 10,24,488 } },1695{ { 97,63,3330 },{ 98,200,3848 },{ 34,120,488 },{ 2,8,496 } },1696{ { 106,63,3330 },{ 107,200,3848 },{ 43,120,488 },{ 3,92,488 } },1697{ { 114,63,3330 },{ 115,200,3848 },{ 51,120,488 },{ 11,92,488 } },1698{ { 122,63,3330 },{ 123,200,3848 },{ 59,120,488 },{ 7,76,496 } },1699{ { 130,63,3330 },{ 131,200,3848 },{ 67,120,488 },{ 15,76,496 } },1700{ { 139,63,3330 },{ 140,200,3848 },{ 76,120,488 },{ 24,76,496 } },1701{ { 147,63,3330 },{ 148,200,3848 },{ 84,120,488 },{ 32,76,496 } },1702{ { 155,63,3330 },{ 156,200,3848 },{ 92,120,488 },{ 40,76,496 } },1703{ { 163,63,3330 },{ 164,200,3848 },{ 100,120,488 },{ 48,76,496 } },1704{ { 172,63,3330 },{ 173,200,3848 },{ 109,120,488 },{ 57,76,496 } },1705{ { 184,6,3851 },{ 181,200,3848 },{ 117,120,488 },{ 65,76,496 } },1706{ { 192,6,3851 },{ 133,28,3936 },{ 125,120,488 },{ 73,76,496 } },1707{ { 189,200,3907 },{ 141,28,3936 },{ 133,120,488 },{ 81,76,496 } },1708{ { 198,200,3907 },{ 138,108,4000 },{ 142,120,488 },{ 90,76,496 } },1709{ { 206,200,3907 },{ 146,108,4000 },{ 150,120,488 },{ 98,76,496 } },1710{ { 214,200,3395 },{ 154,108,4000 },{ 158,120,488 },{ 106,76,496 } },1711{ { 190,136,4001 },{ 162,108,4000 },{ 166,120,488 },{ 114,76,496 } },1712{ { 123,30,4076 },{ 87,15,4080 },{ 123,30,492 },{ 87,15,496 } },1713{ { 117,110,4084 },{ 80,31,4080 },{ 117,110,500 },{ 80,31,496 } },1714{ { 125,110,4084 },{ 88,31,4080 },{ 125,110,500 },{ 88,31,496 } },1715{ { 133,110,4084 },{ 96,31,4080 },{ 133,110,500 },{ 96,31,496 } },1716{ { 9,56,3904 },{ 9,56,3904 },{ 0,67,448 },{ 0,67,448 } },1717{ { 1,8,3904 },{ 1,8,3904 },{ 1,84,448 },{ 1,84,448 } },1718{ { 1,124,3904 },{ 1,124,3904 },{ 0,39,384 },{ 0,39,384 } },1719{ { 9,124,3904 },{ 9,124,3904 },{ 1,4,448 },{ 1,4,448 } },1720{ { 6,76,3904 },{ 6,76,3904 },{ 0,70,448 },{ 0,70,448 } },1721{ { 62,6,3859 },{ 62,6,3856 },{ 2,38,480 },{ 2,38,480 } },1722{ { 70,6,3859 },{ 70,6,3856 },{ 5,43,416 },{ 5,43,416 } },1723{ { 78,6,3859 },{ 78,6,3856 },{ 2,11,416 },{ 2,11,416 } },1724{ { 87,6,3859 },{ 87,6,3856 },{ 0,171,488 },{ 0,171,488 } },1725{ { 67,8,3906 },{ 95,6,3856 },{ 8,171,488 },{ 8,171,488 } },1726{ { 75,8,3907 },{ 103,6,3856 },{ 5,123,488 },{ 5,123,488 } },1727{ { 83,8,3907 },{ 111,6,3856 },{ 2,75,488 },{ 2,75,488 } },1728{ { 92,8,3907 },{ 120,6,3856 },{ 0,27,488 },{ 0,27,488 } },1729{ { 100,8,3907 },{ 128,6,3856 },{ 8,27,488 },{ 8,27,488 } },1730{ { 120,106,3843 },{ 136,6,3856 },{ 100,6,387 },{ 16,27,488 } },1731{ { 128,106,3843 },{ 144,6,3856 },{ 108,6,387 },{ 2,11,496 } },1732{ { 137,106,3843 },{ 153,6,3856 },{ 117,6,387 },{ 11,11,496 } },1733{ { 145,106,3843 },{ 161,6,3856 },{ 125,6,387 },{ 19,11,496 } },1734{ { 163,8,3851 },{ 137,43,3904 },{ 133,6,387 },{ 27,11,496 } },1735{ { 171,8,3851 },{ 101,11,4000 },{ 141,6,387 },{ 35,11,496 } },1736{ { 180,8,3851 },{ 110,11,4000 },{ 150,6,387 },{ 44,11,496 } },1737{ { 188,8,3851 },{ 118,11,4000 },{ 158,6,387 },{ 52,11,496 } },1738{ { 172,72,3907 },{ 126,11,4000 },{ 166,6,387 },{ 60,11,496 } },1739{ { 174,6,3971 },{ 134,11,4000 },{ 174,6,387 },{ 68,11,496 } },1740{ { 183,6,3971 },{ 143,11,4000 },{ 183,6,387 },{ 77,11,496 } },1741{ { 191,6,3971 },{ 151,11,4000 },{ 191,6,387 },{ 85,11,496 } },1742{ { 199,6,3971 },{ 159,11,4000 },{ 199,6,387 },{ 93,11,496 } },1743{ { 92,12,4084 },{ 69,15,4080 },{ 92,12,500 },{ 69,15,496 } },1744{ { 101,12,4084 },{ 78,15,4080 },{ 101,12,500 },{ 78,15,496 } },1745{ { 109,12,4084 },{ 86,15,4080 },{ 109,12,500 },{ 86,15,496 } },1746{ { 117,12,4084 },{ 79,31,4080 },{ 117,12,500 },{ 79,31,496 } },1747{ { 125,12,4084 },{ 87,31,4080 },{ 125,12,500 },{ 87,31,496 } },1748{ { 71,8,3602 },{ 71,8,3600 },{ 2,21,384 },{ 2,21,384 } },1749{ { 79,8,3611 },{ 79,8,3608 },{ 0,69,448 },{ 0,69,448 } },1750{ { 87,8,3611 },{ 87,8,3608 },{ 0,23,384 },{ 0,23,384 } },1751{ { 95,8,3611 },{ 95,8,3608 },{ 1,5,448 },{ 1,5,448 } },1752{ { 104,8,3611 },{ 104,8,3608 },{ 0,88,448 },{ 0,88,448 } },1753{ { 112,8,3611 },{ 112,8,3608 },{ 0,72,448 },{ 0,72,448 } },1754{ { 120,8,3611 },{ 121,8,3608 },{ 36,21,458 },{ 36,21,456 } },1755{ { 133,47,3091 },{ 129,8,3608 },{ 44,21,458 },{ 44,21,456 } },1756{ { 142,47,3091 },{ 138,8,3608 },{ 53,21,459 },{ 53,21,456 } },1757{ { 98,12,3850 },{ 98,12,3848 },{ 61,21,459 },{ 61,21,456 } },1758{ { 106,12,3850 },{ 106,12,3848 },{ 10,92,480 },{ 69,21,456 } },1759{ { 114,12,3851 },{ 114,12,3848 },{ 18,92,480 },{ 77,21,456 } },1760{ { 87,12,3906 },{ 87,12,3904 },{ 3,44,488 },{ 86,21,456 } },1761{ { 95,12,3906 },{ 95,12,3904 },{ 11,44,488 },{ 94,21,456 } },1762{ { 103,12,3906 },{ 103,12,3904 },{ 19,44,488 },{ 102,21,456 } },1763{ { 111,12,3907 },{ 111,12,3904 },{ 27,44,489 },{ 110,21,456 } },1764{ { 120,12,3907 },{ 120,12,3904 },{ 36,44,489 },{ 119,21,456 } },1765{ { 128,12,3907 },{ 128,12,3904 },{ 44,44,489 },{ 127,21,456 } },1766{ { 136,12,3907 },{ 136,12,3904 },{ 52,44,489 },{ 135,21,456 } },1767{ { 144,12,3907 },{ 144,12,3904 },{ 60,44,489 },{ 143,21,456 } },1768{ { 153,12,3907 },{ 153,12,3904 },{ 69,44,490 },{ 152,21,456 } },1769{ { 161,12,3395 },{ 149,188,3968 },{ 77,44,490 },{ 160,21,456 } },1770{ { 169,12,3395 },{ 198,21,3928 },{ 85,44,490 },{ 168,21,456 } },1771{ { 113,95,4001 },{ 201,69,3992 },{ 125,8,483 },{ 176,21,456 } },1772{ { 122,95,4001 },{ 200,21,3984 },{ 134,8,483 },{ 185,21,456 } },1773{ { 142,8,4067 },{ 208,21,3984 },{ 142,8,483 },{ 193,21,456 } },1774{ { 151,8,4067 },{ 47,15,4080 },{ 151,8,483 },{ 47,15,496 } },1775{ { 159,8,4067 },{ 55,15,4080 },{ 159,8,483 },{ 55,15,496 } },1776{ { 168,8,4067 },{ 64,15,4080 },{ 168,8,483 },{ 64,15,496 } },1777{ { 160,40,4075 },{ 72,15,4080 },{ 160,40,491 },{ 72,15,496 } },1778{ { 168,40,4075 },{ 80,15,4080 },{ 168,40,491 },{ 80,15,496 } },1779{ { 144,8,4082 },{ 88,15,4080 },{ 144,8,498 },{ 88,15,496 } }1780};1781#endif // BASISD_SUPPORT_ETC2_EAC_A817821783#if BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES1784static void create_etc2_eac_a8_conversion_table()1785{1786FILE* pFile = fopen("basisu_decoder_tables_etc2_eac_a8.inc", "w");17871788for (uint32_t inten = 0; inten < 8; inten++)1789{1790for (uint32_t base = 0; base < 32; base++)1791{1792color32 block_colors[4];1793decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(base, base, base, 255), false), inten);17941795fprintf(pFile, "{");17961797for (uint32_t sel_range = 0; sel_range < NUM_ETC2_EAC_SELECTOR_RANGES; sel_range++)1798{1799const uint32_t low_selector = s_etc2_eac_selector_ranges[sel_range].m_low;1800const uint32_t high_selector = s_etc2_eac_selector_ranges[sel_range].m_high;18011802// We have a ETC1 base color and intensity, and a used selector range from low_selector-high_selector.1803// Now find the best ETC2 EAC A8 base/table/multiplier that fits these colors.18041805uint8_t pixels[4];1806uint32_t num_pixels = 0;1807for (uint32_t s = low_selector; s <= high_selector; s++)1808pixels[num_pixels++] = block_colors[s].g;18091810pack_eac_a8_results pack_results;1811pack_eac_a8_exhaustive(pack_results, pixels, num_pixels);18121813etc1_g_to_eac_conversion& c = s_etc1_g_to_etc2_a8[base + inten * 32][sel_range];18141815c.m_base = pack_results.m_base;1816c.m_table_mul = pack_results.m_table * 16 + pack_results.m_multiplier;1817c.m_trans = 0;18181819for (uint32_t s = 0; s < 4; s++)1820{1821if ((s < low_selector) || (s > high_selector))1822continue;18231824uint32_t etc2_selector = pack_results.m_selectors[s - low_selector];18251826c.m_trans |= (etc2_selector << (s * 3));1827}18281829fprintf(pFile, "{%u,%u,%u}", c.m_base, c.m_table_mul, c.m_trans);1830if (sel_range < (NUM_ETC2_EAC_SELECTOR_RANGES - 1))1831fprintf(pFile, ",");1832}18331834fprintf(pFile, "},\n");1835}1836}18371838fclose(pFile);1839}1840#endif18411842#if BASISD_WRITE_NEW_ETC2_EAC_R11_TABLES1843struct pack_eac_r11_results1844{1845uint32_t m_base;1846uint32_t m_table;1847uint32_t m_multiplier;1848basisu::vector<uint8_t> m_selectors;1849basisu::vector<uint8_t> m_selectors_temp;1850};18511852static uint64_t pack_eac_r11_exhaustive(pack_eac_r11_results& results, const uint8_t* pPixels, uint32_t num_pixels)1853{1854results.m_selectors.resize(num_pixels);1855results.m_selectors_temp.resize(num_pixels);18561857uint64_t best_err = UINT64_MAX;18581859for (uint32_t base_color = 0; base_color < 256; base_color++)1860{1861for (uint32_t multiplier = 0; multiplier < 16; multiplier++)1862{1863for (uint32_t table = 0; table < 16; table++)1864{1865uint64_t total_err = 0;18661867for (uint32_t i = 0; i < num_pixels; i++)1868{1869// Convert 8-bit input to 11-bits1870const int a = (pPixels[i] * 2047 + 128) / 255;18711872uint32_t best_s_err = UINT32_MAX;1873uint32_t best_s = 0;1874for (uint32_t s = 0; s < 8; s++)1875{1876int v = (int)(multiplier ? (multiplier * 8) : 1) * g_eac_modifier_table[table][s] + (int)base_color * 8 + 4;1877if (v < 0)1878v = 0;1879else if (v > 2047)1880v = 2047;18811882uint32_t err = abs(a - v);1883if (err < best_s_err)1884{1885best_s_err = err;1886best_s = s;1887}1888}18891890results.m_selectors_temp[i] = static_cast<uint8_t>(best_s);18911892total_err += best_s_err * best_s_err;1893if (total_err >= best_err)1894break;1895}18961897if (total_err < best_err)1898{1899best_err = total_err;1900results.m_base = base_color;1901results.m_multiplier = multiplier;1902results.m_table = table;1903results.m_selectors.swap(results.m_selectors_temp);1904}19051906} // table19071908} // multiplier19091910} // base_color19111912return best_err;1913}19141915static void create_etc2_eac_r11_conversion_table()1916{1917FILE* pFile = nullptr;1918fopen_s(&pFile, "basisu_decoder_tables_etc2_eac_r11.inc", "w");19191920for (uint32_t inten = 0; inten < 8; inten++)1921{1922for (uint32_t base = 0; base < 32; base++)1923{1924color32 block_colors[4];1925decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(base, base, base, 255), false), inten);19261927fprintf(pFile, "{");19281929for (uint32_t sel_range = 0; sel_range < NUM_ETC2_EAC_SELECTOR_RANGES; sel_range++)1930{1931const uint32_t low_selector = s_etc2_eac_selector_ranges[sel_range].m_low;1932const uint32_t high_selector = s_etc2_eac_selector_ranges[sel_range].m_high;19331934// We have a ETC1 base color and intensity, and a used selector range from low_selector-high_selector.1935// Now find the best ETC2 EAC R11 base/table/multiplier that fits these colors.19361937uint8_t pixels[4];1938uint32_t num_pixels = 0;1939for (uint32_t s = low_selector; s <= high_selector; s++)1940pixels[num_pixels++] = block_colors[s].g;19411942pack_eac_r11_results pack_results;1943pack_eac_r11_exhaustive(pack_results, pixels, num_pixels);19441945etc1_g_to_eac_conversion c;19461947c.m_base = (uint8_t)pack_results.m_base;1948c.m_table_mul = (uint8_t)(pack_results.m_table * 16 + pack_results.m_multiplier);1949c.m_trans = 0;19501951for (uint32_t s = 0; s < 4; s++)1952{1953if ((s < low_selector) || (s > high_selector))1954continue;19551956uint32_t etc2_selector = pack_results.m_selectors[s - low_selector];19571958c.m_trans |= (etc2_selector << (s * 3));1959}19601961fprintf(pFile, "{%u,%u,%u}", c.m_base, c.m_table_mul, c.m_trans);1962if (sel_range < (NUM_ETC2_EAC_SELECTOR_RANGES - 1))1963fprintf(pFile, ",");1964}19651966fprintf(pFile, "},\n");1967}1968}19691970fclose(pFile);1971}1972#endif // BASISD_WRITE_NEW_ETC2_EAC_R11_TABLES19731974#if BASISD_WRITE_NEW_ASTC_TABLES1975static void create_etc1_to_astc_conversion_table_0_47();1976static void create_etc1_to_astc_conversion_table_0_255();1977#endif19781979#if BASISD_SUPPORT_ASTC1980static void transcoder_init_astc();1981#endif19821983#if BASISD_WRITE_NEW_BC7_MODE5_TABLES1984static void create_etc1_to_bc7_m5_color_conversion_table();1985static void create_etc1_to_bc7_m5_alpha_conversion_table();1986#endif19871988#if BASISD_SUPPORT_BC7_MODE51989static void transcoder_init_bc7_mode5();1990#endif19911992#if BASISD_WRITE_NEW_ATC_TABLES1993static void create_etc1s_to_atc_conversion_tables();1994#endif19951996#if BASISD_SUPPORT_ATC1997static void transcoder_init_atc();1998#endif19992000#if BASISD_SUPPORT_PVRTC22001static void transcoder_init_pvrtc2();2002#endif20032004#if BASISD_SUPPORT_UASTC2005void uastc_init();2006#endif20072008#if BASISD_SUPPORT_UASTC_HDR2009namespace astc_6x6_hdr2010{2011static void init_quantize_tables();2012static void fast_encode_bc6h_init();2013}2014#endif20152016#if BASISD_SUPPORT_BC7_MODE52017namespace bc7_mode_5_encoder2018{2019void encode_bc7_mode5_init();2020}2021#endif20222023static bool g_transcoder_initialized;20242025// Library global initialization. Requires ~9 milliseconds when compiled and executed natively on a Core i7 2.2 GHz.2026// If this is too slow, these computed tables can easilky be moved to be compiled in.2027void basisu_transcoder_init()2028{2029if (g_transcoder_initialized)2030{2031BASISU_DEVEL_ERROR("basisu_transcoder::basisu_transcoder_init: Called more than once\n");2032return;2033}20342035BASISU_DEVEL_ERROR("basisu_transcoder::basisu_transcoder_init: Initializing (this is not an error)\n");20362037#if BASISD_SUPPORT_UASTC2038uastc_init();2039#endif20402041#if BASISD_SUPPORT_UASTC_HDR2042// TODO: Examine this, optimize for startup time/mem utilization.2043astc_helpers::init_tables(false);20442045astc_hdr_core_init();2046#endif20472048#if BASISD_SUPPORT_ASTC2049transcoder_init_astc();2050#endif20512052#if BASISD_WRITE_NEW_ASTC_TABLES2053create_etc1_to_astc_conversion_table_0_47();2054create_etc1_to_astc_conversion_table_0_255();2055exit(0);2056#endif20572058#if BASISD_WRITE_NEW_BC7_MODE5_TABLES2059create_etc1_to_bc7_m5_color_conversion_table();2060create_etc1_to_bc7_m5_alpha_conversion_table();2061exit(0);2062#endif20632064#if BASISD_WRITE_NEW_DXT1_TABLES2065create_etc1_to_dxt1_5_conversion_table();2066create_etc1_to_dxt1_6_conversion_table();2067exit(0);2068#endif20692070#if BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES2071create_etc2_eac_a8_conversion_table();2072exit(0);2073#endif20742075#if BASISD_WRITE_NEW_ATC_TABLES2076create_etc1s_to_atc_conversion_tables();2077exit(0);2078#endif20792080#if BASISD_WRITE_NEW_ETC2_EAC_R11_TABLES2081create_etc2_eac_r11_conversion_table();2082exit(0);2083#endif20842085#if BASISD_SUPPORT_DXT1 || BASISD_SUPPORT_UASTC2086uint8_t bc1_expand5[32];2087for (int i = 0; i < 32; i++)2088bc1_expand5[i] = static_cast<uint8_t>((i << 3) | (i >> 2));2089prepare_bc1_single_color_table(g_bc1_match5_equals_1, bc1_expand5, 32, 32, 1);2090prepare_bc1_single_color_table(g_bc1_match5_equals_0, bc1_expand5, 1, 32, 0);20912092uint8_t bc1_expand6[64];2093for (int i = 0; i < 64; i++)2094bc1_expand6[i] = static_cast<uint8_t>((i << 2) | (i >> 4));2095prepare_bc1_single_color_table(g_bc1_match6_equals_1, bc1_expand6, 64, 64, 1);2096prepare_bc1_single_color_table(g_bc1_match6_equals_0, bc1_expand6, 1, 64, 0);20972098#if 02099for (uint32_t i = 0; i < 256; i++)2100{2101printf("%u %u %u\n", i, (i * 63 + 127) / 255, g_bc1_match6_equals_0[i].m_hi);2102}2103exit(0);2104#endif21052106#endif21072108#if BASISD_SUPPORT_DXT12109for (uint32_t i = 0; i < NUM_ETC1_TO_DXT1_SELECTOR_RANGES; i++)2110{2111uint32_t l = g_etc1_to_dxt1_selector_ranges[i].m_low;2112uint32_t h = g_etc1_to_dxt1_selector_ranges[i].m_high;2113g_etc1_to_dxt1_selector_range_index[l][h] = i;2114}21152116for (uint32_t sm = 0; sm < NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS; sm++)2117{2118uint8_t etc1_to_dxt1_selector_mappings_raw_dxt1[4];2119uint8_t etc1_to_dxt1_selector_mappings_raw_dxt1_inv[4];21202121for (uint32_t j = 0; j < 4; j++)2122{2123static const uint8_t s_linear_dxt1_to_dxt1[4] = { 0, 2, 3, 1 };2124static const uint8_t s_dxt1_inverted_xlat[4] = { 1, 0, 3, 2 };21252126etc1_to_dxt1_selector_mappings_raw_dxt1[j] = (uint8_t)s_linear_dxt1_to_dxt1[g_etc1_to_dxt1_selector_mappings[sm][j]];2127etc1_to_dxt1_selector_mappings_raw_dxt1_inv[j] = (uint8_t)s_dxt1_inverted_xlat[etc1_to_dxt1_selector_mappings_raw_dxt1[j]];2128}21292130for (uint32_t i = 0; i < 256; i++)2131{2132uint32_t k = 0, k_inv = 0;2133for (uint32_t s = 0; s < 4; s++)2134{2135k |= (etc1_to_dxt1_selector_mappings_raw_dxt1[(i >> (s * 2)) & 3] << (s * 2));2136k_inv |= (etc1_to_dxt1_selector_mappings_raw_dxt1_inv[(i >> (s * 2)) & 3] << (s * 2));2137}2138g_etc1_to_dxt1_selector_mappings_raw_dxt1_256[sm][i] = (uint8_t)k;2139g_etc1_to_dxt1_selector_mappings_raw_dxt1_inv_256[sm][i] = (uint8_t)k_inv;2140}2141}2142#endif21432144#if BASISD_SUPPORT_BC7_MODE52145transcoder_init_bc7_mode5();2146#endif21472148#if BASISD_SUPPORT_ATC2149transcoder_init_atc();2150#endif21512152#if BASISD_SUPPORT_PVRTC22153transcoder_init_pvrtc2();2154#endif21552156#if BASISD_SUPPORT_UASTC_HDR2157bc6h_enc_init();2158astc_6x6_hdr::init_quantize_tables();2159fast_encode_bc6h_init();2160#endif21612162#if BASISD_SUPPORT_BC7_MODE52163bc7_mode_5_encoder::encode_bc7_mode5_init();2164#endif21652166g_transcoder_initialized = true;2167}21682169#if BASISD_SUPPORT_DXT12170static void convert_etc1s_to_dxt1(dxt1_block* pDst_block, const endpoint *pEndpoints, const selector* pSelector, bool use_threecolor_blocks)2171{2172#if !BASISD_WRITE_NEW_DXT1_TABLES2173const uint32_t low_selector = pSelector->m_lo_selector;2174const uint32_t high_selector = pSelector->m_hi_selector;21752176const color32& base_color = pEndpoints->m_color5;2177const uint32_t inten_table = pEndpoints->m_inten5;21782179if (low_selector == high_selector)2180{2181uint32_t r, g, b;2182decoder_etc_block::get_block_color5(base_color, inten_table, low_selector, r, g, b);21832184uint32_t mask = 0xAA;2185uint32_t max16 = (g_bc1_match5_equals_1[r].m_hi << 11) | (g_bc1_match6_equals_1[g].m_hi << 5) | g_bc1_match5_equals_1[b].m_hi;2186uint32_t min16 = (g_bc1_match5_equals_1[r].m_lo << 11) | (g_bc1_match6_equals_1[g].m_lo << 5) | g_bc1_match5_equals_1[b].m_lo;21872188if ((!use_threecolor_blocks) && (min16 == max16))2189{2190// This is an annoying edge case that impacts BC3.2191// This is to guarantee that BC3 blocks never use punchthrough alpha (3 color) mode, which isn't supported on some (all?) GPU's.2192mask = 0;21932194// Make l > h2195if (min16 > 0)2196min16--;2197else2198{2199// l = h = 02200assert(min16 == max16 && max16 == 0);22012202max16 = 1;2203min16 = 0;2204mask = 0x55;2205}22062207assert(max16 > min16);2208}22092210if (max16 < min16)2211{2212std::swap(max16, min16);2213mask ^= 0x55;2214}22152216pDst_block->set_low_color(static_cast<uint16_t>(max16));2217pDst_block->set_high_color(static_cast<uint16_t>(min16));2218pDst_block->m_selectors[0] = static_cast<uint8_t>(mask);2219pDst_block->m_selectors[1] = static_cast<uint8_t>(mask);2220pDst_block->m_selectors[2] = static_cast<uint8_t>(mask);2221pDst_block->m_selectors[3] = static_cast<uint8_t>(mask);22222223return;2224}2225else if ((inten_table >= 7) && (pSelector->m_num_unique_selectors == 2) && (pSelector->m_lo_selector == 0) && (pSelector->m_hi_selector == 3))2226{2227color32 block_colors[4];22282229decoder_etc_block::get_block_colors5(block_colors, base_color, inten_table);22302231const uint32_t r0 = block_colors[0].r;2232const uint32_t g0 = block_colors[0].g;2233const uint32_t b0 = block_colors[0].b;22342235const uint32_t r1 = block_colors[3].r;2236const uint32_t g1 = block_colors[3].g;2237const uint32_t b1 = block_colors[3].b;22382239uint32_t max16 = (g_bc1_match5_equals_0[r0].m_hi << 11) | (g_bc1_match6_equals_0[g0].m_hi << 5) | g_bc1_match5_equals_0[b0].m_hi;2240uint32_t min16 = (g_bc1_match5_equals_0[r1].m_hi << 11) | (g_bc1_match6_equals_0[g1].m_hi << 5) | g_bc1_match5_equals_0[b1].m_hi;22412242uint32_t l = 0, h = 1;22432244if (min16 == max16)2245{2246// Make l > h2247if (min16 > 0)2248{2249min16--;22502251l = 0;2252h = 0;2253}2254else2255{2256// l = h = 02257assert(min16 == max16 && max16 == 0);22582259max16 = 1;2260min16 = 0;22612262l = 1;2263h = 1;2264}22652266assert(max16 > min16);2267}22682269if (max16 < min16)2270{2271std::swap(max16, min16);2272l = 1;2273h = 0;2274}22752276pDst_block->set_low_color((uint16_t)max16);2277pDst_block->set_high_color((uint16_t)min16);22782279for (uint32_t y = 0; y < 4; y++)2280{2281for (uint32_t x = 0; x < 4; x++)2282{2283uint32_t s = pSelector->get_selector(x, y);2284pDst_block->set_selector(x, y, (s == 3) ? h : l);2285}2286}22872288return;2289}22902291const uint32_t selector_range_table = g_etc1_to_dxt1_selector_range_index[low_selector][high_selector];22922293//[32][8][RANGES][MAPPING]2294const etc1_to_dxt1_56_solution* pTable_r = &g_etc1_to_dxt_5[(inten_table * 32 + base_color.r) * (NUM_ETC1_TO_DXT1_SELECTOR_RANGES * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS];2295const etc1_to_dxt1_56_solution* pTable_g = &g_etc1_to_dxt_6[(inten_table * 32 + base_color.g) * (NUM_ETC1_TO_DXT1_SELECTOR_RANGES * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS];2296const etc1_to_dxt1_56_solution* pTable_b = &g_etc1_to_dxt_5[(inten_table * 32 + base_color.b) * (NUM_ETC1_TO_DXT1_SELECTOR_RANGES * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS];22972298uint32_t best_err = UINT_MAX;2299uint32_t best_mapping = 0;23002301assert(NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS == 10);2302#define DO_ITER(m) { uint32_t total_err = pTable_r[m].m_err + pTable_g[m].m_err + pTable_b[m].m_err; if (total_err < best_err) { best_err = total_err; best_mapping = m; } }2303DO_ITER(0); DO_ITER(1); DO_ITER(2); DO_ITER(3); DO_ITER(4);2304DO_ITER(5); DO_ITER(6); DO_ITER(7); DO_ITER(8); DO_ITER(9);2305#undef DO_ITER23062307uint32_t l = dxt1_block::pack_unscaled_color(pTable_r[best_mapping].m_lo, pTable_g[best_mapping].m_lo, pTable_b[best_mapping].m_lo);2308uint32_t h = dxt1_block::pack_unscaled_color(pTable_r[best_mapping].m_hi, pTable_g[best_mapping].m_hi, pTable_b[best_mapping].m_hi);23092310const uint8_t* pSelectors_xlat_256 = &g_etc1_to_dxt1_selector_mappings_raw_dxt1_256[best_mapping][0];23112312if (l < h)2313{2314std::swap(l, h);2315pSelectors_xlat_256 = &g_etc1_to_dxt1_selector_mappings_raw_dxt1_inv_256[best_mapping][0];2316}23172318pDst_block->set_low_color(static_cast<uint16_t>(l));2319pDst_block->set_high_color(static_cast<uint16_t>(h));23202321if (l == h)2322{2323uint8_t mask = 0;23242325if (!use_threecolor_blocks)2326{2327// This is an annoying edge case that impacts BC3.23282329// Make l > h2330if (h > 0)2331h--;2332else2333{2334// l = h = 02335assert(l == h && h == 0);23362337h = 0;2338l = 1;2339mask = 0x55;2340}23412342assert(l > h);2343pDst_block->set_low_color(static_cast<uint16_t>(l));2344pDst_block->set_high_color(static_cast<uint16_t>(h));2345}23462347pDst_block->m_selectors[0] = mask;2348pDst_block->m_selectors[1] = mask;2349pDst_block->m_selectors[2] = mask;2350pDst_block->m_selectors[3] = mask;23512352return;2353}23542355pDst_block->m_selectors[0] = pSelectors_xlat_256[pSelector->m_selectors[0]];2356pDst_block->m_selectors[1] = pSelectors_xlat_256[pSelector->m_selectors[1]];2357pDst_block->m_selectors[2] = pSelectors_xlat_256[pSelector->m_selectors[2]];2358pDst_block->m_selectors[3] = pSelectors_xlat_256[pSelector->m_selectors[3]];2359#endif2360}23612362#if BASISD_ENABLE_DEBUG_FLAGS2363static void convert_etc1s_to_dxt1_vis(dxt1_block* pDst_block, const endpoint* pEndpoints, const selector* pSelector, bool use_threecolor_blocks)2364{2365convert_etc1s_to_dxt1(pDst_block, pEndpoints, pSelector, use_threecolor_blocks);23662367if (g_debug_flags & cDebugFlagVisBC1Sels)2368{2369uint32_t l = dxt1_block::pack_unscaled_color(31, 63, 31);2370uint32_t h = dxt1_block::pack_unscaled_color(0, 0, 0);2371pDst_block->set_low_color(static_cast<uint16_t>(l));2372pDst_block->set_high_color(static_cast<uint16_t>(h));2373}2374else if (g_debug_flags & cDebugFlagVisBC1Endpoints)2375{2376for (uint32_t y = 0; y < 4; y++)2377for (uint32_t x = 0; x < 4; x++)2378pDst_block->set_selector(x, y, (y < 2) ? 0 : 1);2379}2380}2381#endif2382#endif23832384#if BASISD_SUPPORT_FXT12385struct fxt1_block2386{2387union2388{2389struct2390{2391uint64_t m_t00 : 2;2392uint64_t m_t01 : 2;2393uint64_t m_t02 : 2;2394uint64_t m_t03 : 2;2395uint64_t m_t04 : 2;2396uint64_t m_t05 : 2;2397uint64_t m_t06 : 2;2398uint64_t m_t07 : 2;2399uint64_t m_t08 : 2;2400uint64_t m_t09 : 2;2401uint64_t m_t10 : 2;2402uint64_t m_t11 : 2;2403uint64_t m_t12 : 2;2404uint64_t m_t13 : 2;2405uint64_t m_t14 : 2;2406uint64_t m_t15 : 2;2407uint64_t m_t16 : 2;2408uint64_t m_t17 : 2;2409uint64_t m_t18 : 2;2410uint64_t m_t19 : 2;2411uint64_t m_t20 : 2;2412uint64_t m_t21 : 2;2413uint64_t m_t22 : 2;2414uint64_t m_t23 : 2;2415uint64_t m_t24 : 2;2416uint64_t m_t25 : 2;2417uint64_t m_t26 : 2;2418uint64_t m_t27 : 2;2419uint64_t m_t28 : 2;2420uint64_t m_t29 : 2;2421uint64_t m_t30 : 2;2422uint64_t m_t31 : 2;2423} m_lo;2424uint64_t m_lo_bits;2425uint8_t m_sels[8];2426};2427union2428{2429struct2430{2431#ifdef BASISU_USE_ORIGINAL_3DFX_FXT1_ENCODING2432uint64_t m_b1 : 5;2433uint64_t m_g1 : 5;2434uint64_t m_r1 : 5;2435uint64_t m_b0 : 5;2436uint64_t m_g0 : 5;2437uint64_t m_r0 : 5;2438uint64_t m_b3 : 5;2439uint64_t m_g3 : 5;2440uint64_t m_r3 : 5;2441uint64_t m_b2 : 5;2442uint64_t m_g2 : 5;2443uint64_t m_r2 : 5;2444#else2445uint64_t m_b0 : 5;2446uint64_t m_g0 : 5;2447uint64_t m_r0 : 5;2448uint64_t m_b1 : 5;2449uint64_t m_g1 : 5;2450uint64_t m_r1 : 5;2451uint64_t m_b2 : 5;2452uint64_t m_g2 : 5;2453uint64_t m_r2 : 5;2454uint64_t m_b3 : 5;2455uint64_t m_g3 : 5;2456uint64_t m_r3 : 5;2457#endif2458uint64_t m_alpha : 1;2459uint64_t m_glsb : 2;2460uint64_t m_mode : 1;2461} m_hi;2462uint64_t m_hi_bits;2463};2464};24652466static uint8_t conv_dxt1_to_fxt1_sels(uint32_t sels)2467{2468static uint8_t s_conv_table[16] = { 0, 3, 1, 2, 12, 15, 13, 14, 4, 7, 5, 6, 8, 11, 9, 10 };2469return s_conv_table[sels & 15] | (s_conv_table[sels >> 4] << 4);2470}24712472static void convert_etc1s_to_fxt1(void *pDst, const endpoint *pEndpoints, const selector *pSelectors, uint32_t fxt1_subblock)2473{2474fxt1_block* pBlock = static_cast<fxt1_block*>(pDst);24752476// CC_MIXED is basically DXT1 with different encoding tricks.2477// So transcode ETC1S to DXT1, then transcode that to FXT1 which is easy and nearly lossless.2478// (It's not completely lossless because FXT1 rounds in its color lerps while DXT1 doesn't, but it should be good enough.)2479dxt1_block blk;2480convert_etc1s_to_dxt1(&blk, pEndpoints, pSelectors, false);24812482const uint32_t l = blk.get_low_color();2483const uint32_t h = blk.get_high_color();24842485color32 color0((l >> 11) & 31, (l >> 5) & 63, l & 31, 255);2486color32 color1((h >> 11) & 31, (h >> 5) & 63, h & 31, 255);24872488uint32_t g0 = color0.g & 1;2489uint32_t g1 = color1.g & 1;24902491color0.g >>= 1;2492color1.g >>= 1;24932494blk.m_selectors[0] = conv_dxt1_to_fxt1_sels(blk.m_selectors[0]);2495blk.m_selectors[1] = conv_dxt1_to_fxt1_sels(blk.m_selectors[1]);2496blk.m_selectors[2] = conv_dxt1_to_fxt1_sels(blk.m_selectors[2]);2497blk.m_selectors[3] = conv_dxt1_to_fxt1_sels(blk.m_selectors[3]);24982499if ((blk.get_selector(0, 0) >> 1) != (g0 ^ g1))2500{2501std::swap(color0, color1);2502std::swap(g0, g1);25032504blk.m_selectors[0] ^= 0xFF;2505blk.m_selectors[1] ^= 0xFF;2506blk.m_selectors[2] ^= 0xFF;2507blk.m_selectors[3] ^= 0xFF;2508}25092510if (fxt1_subblock == 0)2511{2512pBlock->m_hi.m_mode = 1;2513pBlock->m_hi.m_alpha = 0;2514pBlock->m_hi.m_glsb = g1 | (g1 << 1);2515pBlock->m_hi.m_r0 = color0.r;2516pBlock->m_hi.m_g0 = color0.g;2517pBlock->m_hi.m_b0 = color0.b;2518pBlock->m_hi.m_r1 = color1.r;2519pBlock->m_hi.m_g1 = color1.g;2520pBlock->m_hi.m_b1 = color1.b;2521pBlock->m_hi.m_r2 = color0.r;2522pBlock->m_hi.m_g2 = color0.g;2523pBlock->m_hi.m_b2 = color0.b;2524pBlock->m_hi.m_r3 = color1.r;2525pBlock->m_hi.m_g3 = color1.g;2526pBlock->m_hi.m_b3 = color1.b;2527pBlock->m_sels[0] = blk.m_selectors[0];2528pBlock->m_sels[1] = blk.m_selectors[1];2529pBlock->m_sels[2] = blk.m_selectors[2];2530pBlock->m_sels[3] = blk.m_selectors[3];25312532static const uint8_t s_border_dup[4] = { 0, 85, 170, 255 };2533pBlock->m_sels[4] = s_border_dup[blk.m_selectors[0] >> 6];2534pBlock->m_sels[5] = s_border_dup[blk.m_selectors[1] >> 6];2535pBlock->m_sels[6] = s_border_dup[blk.m_selectors[2] >> 6];2536pBlock->m_sels[7] = s_border_dup[blk.m_selectors[3] >> 6];2537}2538else2539{2540pBlock->m_hi.m_glsb = (pBlock->m_hi.m_glsb & 1) | (g1 << 1);2541pBlock->m_hi.m_r2 = color0.r;2542pBlock->m_hi.m_g2 = color0.g;2543pBlock->m_hi.m_b2 = color0.b;2544pBlock->m_hi.m_r3 = color1.r;2545pBlock->m_hi.m_g3 = color1.g;2546pBlock->m_hi.m_b3 = color1.b;2547pBlock->m_sels[4] = blk.m_selectors[0];2548pBlock->m_sels[5] = blk.m_selectors[1];2549pBlock->m_sels[6] = blk.m_selectors[2];2550pBlock->m_sels[7] = blk.m_selectors[3];2551}2552}2553#endif // BASISD_SUPPORT_FXT12554#if BASISD_SUPPORT_DXT5A2555static dxt_selector_range s_dxt5a_selector_ranges[] =2556{2557{ 0, 3 },25582559{ 1, 3 },2560{ 0, 2 },25612562{ 1, 2 },2563};25642565const uint32_t NUM_DXT5A_SELECTOR_RANGES = sizeof(s_dxt5a_selector_ranges) / sizeof(s_dxt5a_selector_ranges[0]);25662567struct etc1_g_to_dxt5a_conversion2568{2569uint8_t m_lo, m_hi;2570uint16_t m_trans;2571};25722573static etc1_g_to_dxt5a_conversion g_etc1_g_to_dxt5a[32 * 8][NUM_DXT5A_SELECTOR_RANGES] =2574{2575{ { 8, 0, 393 },{ 8, 0, 392 },{ 2, 0, 9 },{ 2, 0, 8 }, }, { { 6, 16, 710 },{ 16, 6, 328 },{ 0, 10, 96 },{ 10, 6, 8 }, },2576{ { 28, 5, 1327 },{ 24, 14, 328 },{ 8, 18, 96 },{ 18, 14, 8 }, }, { { 36, 13, 1327 },{ 32, 22, 328 },{ 16, 26, 96 },{ 26, 22, 8 }, },2577{ { 45, 22, 1327 },{ 41, 31, 328 },{ 25, 35, 96 },{ 35, 31, 8 }, }, { { 53, 30, 1327 },{ 49, 39, 328 },{ 33, 43, 96 },{ 43, 39, 8 }, },2578{ { 61, 38, 1327 },{ 57, 47, 328 },{ 41, 51, 96 },{ 51, 47, 8 }, }, { { 69, 46, 1327 },{ 65, 55, 328 },{ 49, 59, 96 },{ 59, 55, 8 }, },2579{ { 78, 55, 1327 },{ 74, 64, 328 },{ 58, 68, 96 },{ 68, 64, 8 }, }, { { 86, 63, 1327 },{ 82, 72, 328 },{ 66, 76, 96 },{ 76, 72, 8 }, },2580{ { 94, 71, 1327 },{ 90, 80, 328 },{ 74, 84, 96 },{ 84, 80, 8 }, }, { { 102, 79, 1327 },{ 98, 88, 328 },{ 82, 92, 96 },{ 92, 88, 8 }, },2581{ { 111, 88, 1327 },{ 107, 97, 328 },{ 91, 101, 96 },{ 101, 97, 8 }, }, { { 119, 96, 1327 },{ 115, 105, 328 },{ 99, 109, 96 },{ 109, 105, 8 }, },2582{ { 127, 104, 1327 },{ 123, 113, 328 },{ 107, 117, 96 },{ 117, 113, 8 }, }, { { 135, 112, 1327 },{ 131, 121, 328 },{ 115, 125, 96 },{ 125, 121, 8 }, },2583{ { 144, 121, 1327 },{ 140, 130, 328 },{ 124, 134, 96 },{ 134, 130, 8 }, }, { { 152, 129, 1327 },{ 148, 138, 328 },{ 132, 142, 96 },{ 142, 138, 8 }, },2584{ { 160, 137, 1327 },{ 156, 146, 328 },{ 140, 150, 96 },{ 150, 146, 8 }, }, { { 168, 145, 1327 },{ 164, 154, 328 },{ 148, 158, 96 },{ 158, 154, 8 }, },2585{ { 177, 154, 1327 },{ 173, 163, 328 },{ 157, 167, 96 },{ 167, 163, 8 }, }, { { 185, 162, 1327 },{ 181, 171, 328 },{ 165, 175, 96 },{ 175, 171, 8 }, },2586{ { 193, 170, 1327 },{ 189, 179, 328 },{ 173, 183, 96 },{ 183, 179, 8 }, }, { { 201, 178, 1327 },{ 197, 187, 328 },{ 181, 191, 96 },{ 191, 187, 8 }, },2587{ { 210, 187, 1327 },{ 206, 196, 328 },{ 190, 200, 96 },{ 200, 196, 8 }, }, { { 218, 195, 1327 },{ 214, 204, 328 },{ 198, 208, 96 },{ 208, 204, 8 }, },2588{ { 226, 203, 1327 },{ 222, 212, 328 },{ 206, 216, 96 },{ 216, 212, 8 }, }, { { 234, 211, 1327 },{ 230, 220, 328 },{ 214, 224, 96 },{ 224, 220, 8 }, },2589{ { 243, 220, 1327 },{ 239, 229, 328 },{ 223, 233, 96 },{ 233, 229, 8 }, }, { { 251, 228, 1327 },{ 247, 237, 328 },{ 231, 241, 96 },{ 241, 237, 8 }, },2590{ { 239, 249, 3680 },{ 245, 249, 3648 },{ 239, 249, 96 },{ 249, 245, 8 }, }, { { 247, 253, 4040 },{ 255, 253, 8 },{ 247, 253, 456 },{ 255, 253, 8 }, },2591{ { 5, 17, 566 },{ 5, 17, 560 },{ 5, 0, 9 },{ 5, 0, 8 }, }, { { 25, 0, 313 },{ 25, 3, 328 },{ 13, 0, 49 },{ 13, 3, 8 }, },2592{ { 39, 0, 1329 },{ 33, 11, 328 },{ 11, 21, 70 },{ 21, 11, 8 }, }, { { 47, 7, 1329 },{ 41, 19, 328 },{ 29, 7, 33 },{ 29, 19, 8 }, },2593{ { 50, 11, 239 },{ 50, 28, 328 },{ 38, 16, 33 },{ 38, 28, 8 }, }, { { 92, 13, 2423 },{ 58, 36, 328 },{ 46, 24, 33 },{ 46, 36, 8 }, },2594{ { 100, 21, 2423 },{ 66, 44, 328 },{ 54, 32, 33 },{ 54, 44, 8 }, }, { { 86, 7, 1253 },{ 74, 52, 328 },{ 62, 40, 33 },{ 62, 52, 8 }, },2595{ { 95, 16, 1253 },{ 83, 61, 328 },{ 71, 49, 33 },{ 71, 61, 8 }, }, { { 103, 24, 1253 },{ 91, 69, 328 },{ 79, 57, 33 },{ 79, 69, 8 }, },2596{ { 111, 32, 1253 },{ 99, 77, 328 },{ 87, 65, 33 },{ 87, 77, 8 }, }, { { 119, 40, 1253 },{ 107, 85, 328 },{ 95, 73, 33 },{ 95, 85, 8 }, },2597{ { 128, 49, 1253 },{ 116, 94, 328 },{ 104, 82, 33 },{ 104, 94, 8 }, }, { { 136, 57, 1253 },{ 124, 102, 328 },{ 112, 90, 33 },{ 112, 102, 8 }, },2598{ { 144, 65, 1253 },{ 132, 110, 328 },{ 120, 98, 33 },{ 120, 110, 8 }, }, { { 152, 73, 1253 },{ 140, 118, 328 },{ 128, 106, 33 },{ 128, 118, 8 }, },2599{ { 161, 82, 1253 },{ 149, 127, 328 },{ 137, 115, 33 },{ 137, 127, 8 }, }, { { 169, 90, 1253 },{ 157, 135, 328 },{ 145, 123, 33 },{ 145, 135, 8 }, },2600{ { 177, 98, 1253 },{ 165, 143, 328 },{ 153, 131, 33 },{ 153, 143, 8 }, }, { { 185, 106, 1253 },{ 173, 151, 328 },{ 161, 139, 33 },{ 161, 151, 8 }, },2601{ { 194, 115, 1253 },{ 182, 160, 328 },{ 170, 148, 33 },{ 170, 160, 8 }, }, { { 202, 123, 1253 },{ 190, 168, 328 },{ 178, 156, 33 },{ 178, 168, 8 }, },2602{ { 210, 131, 1253 },{ 198, 176, 328 },{ 186, 164, 33 },{ 186, 176, 8 }, }, { { 218, 139, 1253 },{ 206, 184, 328 },{ 194, 172, 33 },{ 194, 184, 8 }, },2603{ { 227, 148, 1253 },{ 215, 193, 328 },{ 203, 181, 33 },{ 203, 193, 8 }, }, { { 235, 156, 1253 },{ 223, 201, 328 },{ 211, 189, 33 },{ 211, 201, 8 }, },2604{ { 243, 164, 1253 },{ 231, 209, 328 },{ 219, 197, 33 },{ 219, 209, 8 }, }, { { 183, 239, 867 },{ 239, 217, 328 },{ 227, 205, 33 },{ 227, 217, 8 }, },2605{ { 254, 214, 1329 },{ 248, 226, 328 },{ 236, 214, 33 },{ 236, 226, 8 }, }, { { 222, 244, 3680 },{ 234, 244, 3648 },{ 244, 222, 33 },{ 244, 234, 8 }, },2606{ { 230, 252, 3680 },{ 242, 252, 3648 },{ 252, 230, 33 },{ 252, 242, 8 }, }, { { 238, 250, 4040 },{ 255, 250, 8 },{ 238, 250, 456 },{ 255, 250, 8 }, },2607{ { 9, 29, 566 },{ 9, 29, 560 },{ 9, 0, 9 },{ 9, 0, 8 }, }, { { 17, 37, 566 },{ 17, 37, 560 },{ 17, 0, 9 },{ 17, 0, 8 }, },2608{ { 45, 0, 313 },{ 45, 0, 312 },{ 25, 0, 49 },{ 25, 7, 8 }, }, { { 14, 63, 2758 },{ 5, 53, 784 },{ 15, 33, 70 },{ 33, 15, 8 }, },2609{ { 71, 6, 1329 },{ 72, 4, 1328 },{ 42, 4, 33 },{ 42, 24, 8 }, }, { { 70, 3, 239 },{ 70, 2, 232 },{ 50, 12, 33 },{ 50, 32, 8 }, },2610{ { 0, 98, 2842 },{ 78, 10, 232 },{ 58, 20, 33 },{ 58, 40, 8 }, }, { { 97, 27, 1329 },{ 86, 18, 232 },{ 66, 28, 33 },{ 66, 48, 8 }, },2611{ { 0, 94, 867 },{ 95, 27, 232 },{ 75, 37, 33 },{ 75, 57, 8 }, }, { { 8, 102, 867 },{ 103, 35, 232 },{ 83, 45, 33 },{ 83, 65, 8 }, },2612{ { 12, 112, 867 },{ 111, 43, 232 },{ 91, 53, 33 },{ 91, 73, 8 }, }, { { 139, 2, 1253 },{ 119, 51, 232 },{ 99, 61, 33 },{ 99, 81, 8 }, },2613{ { 148, 13, 1253 },{ 128, 60, 232 },{ 108, 70, 33 },{ 108, 90, 8 }, }, { { 156, 21, 1253 },{ 136, 68, 232 },{ 116, 78, 33 },{ 116, 98, 8 }, },2614{ { 164, 29, 1253 },{ 144, 76, 232 },{ 124, 86, 33 },{ 124, 106, 8 }, }, { { 172, 37, 1253 },{ 152, 84, 232 },{ 132, 94, 33 },{ 132, 114, 8 }, },2615{ { 181, 46, 1253 },{ 161, 93, 232 },{ 141, 103, 33 },{ 141, 123, 8 }, }, { { 189, 54, 1253 },{ 169, 101, 232 },{ 149, 111, 33 },{ 149, 131, 8 }, },2616{ { 197, 62, 1253 },{ 177, 109, 232 },{ 157, 119, 33 },{ 157, 139, 8 }, }, { { 205, 70, 1253 },{ 185, 117, 232 },{ 165, 127, 33 },{ 165, 147, 8 }, },2617{ { 214, 79, 1253 },{ 194, 126, 232 },{ 174, 136, 33 },{ 174, 156, 8 }, }, { { 222, 87, 1253 },{ 202, 134, 232 },{ 182, 144, 33 },{ 182, 164, 8 }, },2618{ { 230, 95, 1253 },{ 210, 142, 232 },{ 190, 152, 33 },{ 190, 172, 8 }, }, { { 238, 103, 1253 },{ 218, 150, 232 },{ 198, 160, 33 },{ 198, 180, 8 }, },2619{ { 247, 112, 1253 },{ 227, 159, 232 },{ 207, 169, 33 },{ 207, 189, 8 }, }, { { 255, 120, 1253 },{ 235, 167, 232 },{ 215, 177, 33 },{ 215, 197, 8 }, },2620{ { 146, 243, 867 },{ 243, 175, 232 },{ 223, 185, 33 },{ 223, 205, 8 }, }, { { 184, 231, 3682 },{ 203, 251, 784 },{ 231, 193, 33 },{ 231, 213, 8 }, },2621{ { 193, 240, 3682 },{ 222, 240, 3648 },{ 240, 202, 33 },{ 240, 222, 8 }, }, { { 255, 210, 169 },{ 230, 248, 3648 },{ 248, 210, 33 },{ 248, 230, 8 }, },2622{ { 218, 238, 4040 },{ 255, 238, 8 },{ 218, 238, 456 },{ 255, 238, 8 }, }, { { 226, 246, 4040 },{ 255, 246, 8 },{ 226, 246, 456 },{ 255, 246, 8 }, },2623{ { 13, 42, 566 },{ 13, 42, 560 },{ 13, 0, 9 },{ 13, 0, 8 }, }, { { 50, 0, 329 },{ 50, 0, 328 },{ 21, 0, 9 },{ 21, 0, 8 }, },2624{ { 29, 58, 566 },{ 67, 2, 1352 },{ 3, 29, 70 },{ 29, 3, 8 }, }, { { 10, 79, 2758 },{ 76, 11, 1352 },{ 11, 37, 70 },{ 37, 11, 8 }, },2625{ { 7, 75, 790 },{ 7, 75, 784 },{ 20, 46, 70 },{ 46, 20, 8 }, }, { { 15, 83, 790 },{ 97, 1, 1328 },{ 28, 54, 70 },{ 54, 28, 8 }, },2626{ { 101, 7, 1329 },{ 105, 9, 1328 },{ 62, 0, 39 },{ 62, 36, 8 }, }, { { 99, 1, 239 },{ 99, 3, 232 },{ 1, 71, 98 },{ 70, 44, 8 }, },2627{ { 107, 11, 239 },{ 108, 12, 232 },{ 10, 80, 98 },{ 79, 53, 8 }, }, { { 115, 19, 239 },{ 116, 20, 232 },{ 18, 88, 98 },{ 87, 61, 8 }, },2628{ { 123, 27, 239 },{ 124, 28, 232 },{ 26, 96, 98 },{ 95, 69, 8 }, }, { { 131, 35, 239 },{ 132, 36, 232 },{ 34, 104, 98 },{ 103, 77, 8 }, },2629{ { 140, 44, 239 },{ 141, 45, 232 },{ 43, 113, 98 },{ 112, 86, 8 }, }, { { 148, 52, 239 },{ 149, 53, 232 },{ 51, 121, 98 },{ 120, 94, 8 }, },2630{ { 156, 60, 239 },{ 157, 61, 232 },{ 59, 129, 98 },{ 128, 102, 8 }, }, { { 164, 68, 239 },{ 165, 69, 232 },{ 67, 137, 98 },{ 136, 110, 8 }, },2631{ { 173, 77, 239 },{ 174, 78, 232 },{ 76, 146, 98 },{ 145, 119, 8 }, }, { { 181, 85, 239 },{ 182, 86, 232 },{ 84, 154, 98 },{ 153, 127, 8 }, },2632{ { 189, 93, 239 },{ 190, 94, 232 },{ 92, 162, 98 },{ 161, 135, 8 }, }, { { 197, 101, 239 },{ 198, 102, 232 },{ 100, 170, 98 },{ 169, 143, 8 }, },2633{ { 206, 110, 239 },{ 207, 111, 232 },{ 109, 179, 98 },{ 178, 152, 8 }, }, { { 214, 118, 239 },{ 215, 119, 232 },{ 117, 187, 98 },{ 186, 160, 8 }, },2634{ { 222, 126, 239 },{ 223, 127, 232 },{ 125, 195, 98 },{ 194, 168, 8 }, }, { { 230, 134, 239 },{ 231, 135, 232 },{ 133, 203, 98 },{ 202, 176, 8 }, },2635{ { 239, 143, 239 },{ 240, 144, 232 },{ 142, 212, 98 },{ 211, 185, 8 }, }, { { 247, 151, 239 },{ 180, 248, 784 },{ 150, 220, 98 },{ 219, 193, 8 }, },2636{ { 159, 228, 3682 },{ 201, 227, 3648 },{ 158, 228, 98 },{ 227, 201, 8 }, }, { { 181, 249, 3928 },{ 209, 235, 3648 },{ 166, 236, 98 },{ 235, 209, 8 }, },2637{ { 255, 189, 169 },{ 218, 244, 3648 },{ 175, 245, 98 },{ 244, 218, 8 }, }, { { 197, 226, 4040 },{ 226, 252, 3648 },{ 183, 253, 98 },{ 252, 226, 8 }, },2638{ { 205, 234, 4040 },{ 255, 234, 8 },{ 205, 234, 456 },{ 255, 234, 8 }, }, { { 213, 242, 4040 },{ 255, 242, 8 },{ 213, 242, 456 },{ 255, 242, 8 }, },2639{ { 18, 60, 566 },{ 18, 60, 560 },{ 18, 0, 9 },{ 18, 0, 8 }, }, { { 26, 68, 566 },{ 26, 68, 560 },{ 26, 0, 9 },{ 26, 0, 8 }, },2640{ { 34, 76, 566 },{ 34, 76, 560 },{ 34, 0, 9 },{ 34, 0, 8 }, }, { { 5, 104, 2758 },{ 98, 5, 1352 },{ 42, 0, 57 },{ 42, 6, 8 }, },2641{ { 92, 0, 313 },{ 93, 1, 312 },{ 15, 51, 70 },{ 51, 15, 8 }, }, { { 3, 101, 790 },{ 3, 101, 784 },{ 0, 59, 88 },{ 59, 23, 8 }, },2642{ { 14, 107, 790 },{ 11, 109, 784 },{ 31, 67, 70 },{ 67, 31, 8 }, }, { { 19, 117, 790 },{ 19, 117, 784 },{ 39, 75, 70 },{ 75, 39, 8 }, },2643{ { 28, 126, 790 },{ 28, 126, 784 },{ 83, 5, 33 },{ 84, 48, 8 }, }, { { 132, 0, 239 },{ 36, 134, 784 },{ 91, 13, 33 },{ 92, 56, 8 }, },2644{ { 142, 4, 239 },{ 44, 142, 784 },{ 99, 21, 33 },{ 100, 64, 8 }, }, { { 150, 12, 239 },{ 52, 150, 784 },{ 107, 29, 33 },{ 108, 72, 8 }, },2645{ { 159, 21, 239 },{ 61, 159, 784 },{ 116, 38, 33 },{ 117, 81, 8 }, }, { { 167, 29, 239 },{ 69, 167, 784 },{ 124, 46, 33 },{ 125, 89, 8 }, },2646{ { 175, 37, 239 },{ 77, 175, 784 },{ 132, 54, 33 },{ 133, 97, 8 }, }, { { 183, 45, 239 },{ 85, 183, 784 },{ 140, 62, 33 },{ 141, 105, 8 }, },2647{ { 192, 54, 239 },{ 94, 192, 784 },{ 149, 71, 33 },{ 150, 114, 8 }, }, { { 200, 62, 239 },{ 102, 200, 784 },{ 157, 79, 33 },{ 158, 122, 8 }, },2648{ { 208, 70, 239 },{ 110, 208, 784 },{ 165, 87, 33 },{ 166, 130, 8 }, }, { { 216, 78, 239 },{ 118, 216, 784 },{ 173, 95, 33 },{ 174, 138, 8 }, },2649{ { 225, 87, 239 },{ 127, 225, 784 },{ 182, 104, 33 },{ 183, 147, 8 }, }, { { 233, 95, 239 },{ 135, 233, 784 },{ 190, 112, 33 },{ 191, 155, 8 }, },2650{ { 241, 103, 239 },{ 143, 241, 784 },{ 198, 120, 33 },{ 199, 163, 8 }, }, { { 111, 208, 3682 },{ 151, 249, 784 },{ 206, 128, 33 },{ 207, 171, 8 }, },2651{ { 120, 217, 3682 },{ 180, 216, 3648 },{ 215, 137, 33 },{ 216, 180, 8 }, }, { { 128, 225, 3682 },{ 188, 224, 3648 },{ 223, 145, 33 },{ 224, 188, 8 }, },2652{ { 155, 253, 3928 },{ 196, 232, 3648 },{ 231, 153, 33 },{ 232, 196, 8 }, }, { { 144, 241, 3682 },{ 204, 240, 3648 },{ 239, 161, 33 },{ 240, 204, 8 }, },2653{ { 153, 250, 3682 },{ 213, 249, 3648 },{ 248, 170, 33 },{ 249, 213, 8 }, }, { { 179, 221, 4040 },{ 255, 221, 8 },{ 179, 221, 456 },{ 255, 221, 8 }, },2654{ { 187, 229, 4040 },{ 255, 229, 8 },{ 187, 229, 456 },{ 255, 229, 8 }, }, { { 195, 237, 4040 },{ 255, 237, 8 },{ 195, 237, 456 },{ 255, 237, 8 }, },2655{ { 24, 80, 566 },{ 24, 80, 560 },{ 24, 0, 9 },{ 24, 0, 8 }, }, { { 32, 88, 566 },{ 32, 88, 560 },{ 32, 0, 9 },{ 32, 0, 8 }, },2656{ { 40, 96, 566 },{ 40, 96, 560 },{ 40, 0, 9 },{ 40, 0, 8 }, }, { { 48, 104, 566 },{ 48, 104, 560 },{ 48, 0, 9 },{ 48, 0, 8 }, },2657{ { 9, 138, 2758 },{ 130, 7, 1352 },{ 9, 57, 70 },{ 57, 9, 8 }, }, { { 119, 0, 313 },{ 120, 0, 312 },{ 17, 65, 70 },{ 65, 17, 8 }, },2658{ { 0, 128, 784 },{ 128, 6, 312 },{ 25, 73, 70 },{ 73, 25, 8 }, }, { { 6, 137, 790 },{ 5, 136, 784 },{ 33, 81, 70 },{ 81, 33, 8 }, },2659{ { 42, 171, 2758 },{ 14, 145, 784 },{ 42, 90, 70 },{ 90, 42, 8 }, }, { { 50, 179, 2758 },{ 22, 153, 784 },{ 50, 98, 70 },{ 98, 50, 8 }, },2660{ { 58, 187, 2758 },{ 30, 161, 784 },{ 58, 106, 70 },{ 106, 58, 8 }, }, { { 191, 18, 1329 },{ 38, 169, 784 },{ 112, 9, 33 },{ 114, 66, 8 }, },2661{ { 176, 0, 239 },{ 47, 178, 784 },{ 121, 18, 33 },{ 123, 75, 8 }, }, { { 187, 1, 239 },{ 55, 186, 784 },{ 129, 26, 33 },{ 131, 83, 8 }, },2662{ { 195, 10, 239 },{ 63, 194, 784 },{ 137, 34, 33 },{ 139, 91, 8 }, }, { { 203, 18, 239 },{ 71, 202, 784 },{ 145, 42, 33 },{ 147, 99, 8 }, },2663{ { 212, 27, 239 },{ 80, 211, 784 },{ 154, 51, 33 },{ 156, 108, 8 }, }, { { 220, 35, 239 },{ 88, 219, 784 },{ 162, 59, 33 },{ 164, 116, 8 }, },2664{ { 228, 43, 239 },{ 96, 227, 784 },{ 170, 67, 33 },{ 172, 124, 8 }, }, { { 236, 51, 239 },{ 104, 235, 784 },{ 178, 75, 33 },{ 180, 132, 8 }, },2665{ { 245, 60, 239 },{ 113, 244, 784 },{ 187, 84, 33 },{ 189, 141, 8 }, }, { { 91, 194, 3680 },{ 149, 197, 3648 },{ 195, 92, 33 },{ 197, 149, 8 }, },2666{ { 99, 202, 3680 },{ 157, 205, 3648 },{ 203, 100, 33 },{ 205, 157, 8 }, }, { { 107, 210, 3680 },{ 165, 213, 3648 },{ 211, 108, 33 },{ 213, 165, 8 }, },2667{ { 119, 249, 3928 },{ 174, 222, 3648 },{ 220, 117, 33 },{ 222, 174, 8 }, }, { { 127, 255, 856 },{ 182, 230, 3648 },{ 228, 125, 33 },{ 230, 182, 8 }, },2668{ { 255, 135, 169 },{ 190, 238, 3648 },{ 236, 133, 33 },{ 238, 190, 8 }, }, { { 140, 243, 3680 },{ 198, 246, 3648 },{ 244, 141, 33 },{ 246, 198, 8 }, },2669{ { 151, 207, 4040 },{ 255, 207, 8 },{ 151, 207, 456 },{ 255, 207, 8 }, }, { { 159, 215, 4040 },{ 255, 215, 8 },{ 159, 215, 456 },{ 255, 215, 8 }, },2670{ { 167, 223, 4040 },{ 255, 223, 8 },{ 167, 223, 456 },{ 255, 223, 8 }, }, { { 175, 231, 4040 },{ 255, 231, 8 },{ 175, 231, 456 },{ 255, 231, 8 }, },2671{ { 33, 106, 566 },{ 33, 106, 560 },{ 33, 0, 9 },{ 33, 0, 8 }, }, { { 41, 114, 566 },{ 41, 114, 560 },{ 41, 0, 9 },{ 41, 0, 8 }, },2672{ { 49, 122, 566 },{ 49, 122, 560 },{ 49, 0, 9 },{ 49, 0, 8 }, }, { { 57, 130, 566 },{ 57, 130, 560 },{ 57, 0, 9 },{ 57, 0, 8 }, },2673{ { 66, 139, 566 },{ 66, 139, 560 },{ 66, 0, 9 },{ 66, 0, 8 }, }, { { 74, 147, 566 },{ 170, 7, 1352 },{ 8, 74, 70 },{ 74, 8, 8 }, },2674{ { 152, 0, 313 },{ 178, 15, 1352 },{ 0, 82, 80 },{ 82, 16, 8 }, }, { { 162, 0, 313 },{ 186, 23, 1352 },{ 24, 90, 70 },{ 90, 24, 8 }, },2675{ { 0, 171, 784 },{ 195, 32, 1352 },{ 33, 99, 70 },{ 99, 33, 8 }, }, { { 6, 179, 790 },{ 203, 40, 1352 },{ 41, 107, 70 },{ 107, 41, 8 }, },2676{ { 15, 187, 790 },{ 211, 48, 1352 },{ 115, 0, 41 },{ 115, 49, 8 }, }, { { 61, 199, 710 },{ 219, 56, 1352 },{ 57, 123, 70 },{ 123, 57, 8 }, },2677{ { 70, 208, 710 },{ 228, 65, 1352 },{ 66, 132, 70 },{ 132, 66, 8 }, }, { { 78, 216, 710 },{ 236, 73, 1352 },{ 74, 140, 70 },{ 140, 74, 8 }, },2678{ { 86, 224, 710 },{ 244, 81, 1352 },{ 145, 7, 33 },{ 148, 82, 8 }, }, { { 222, 8, 233 },{ 252, 89, 1352 },{ 153, 15, 33 },{ 156, 90, 8 }, },2679{ { 235, 0, 239 },{ 241, 101, 328 },{ 166, 6, 39 },{ 165, 99, 8 }, }, { { 32, 170, 3680 },{ 249, 109, 328 },{ 0, 175, 98 },{ 173, 107, 8 }, },2680{ { 40, 178, 3680 },{ 115, 181, 3648 },{ 8, 183, 98 },{ 181, 115, 8 }, }, { { 48, 186, 3680 },{ 123, 189, 3648 },{ 16, 191, 98 },{ 189, 123, 8 }, },2681{ { 57, 195, 3680 },{ 132, 198, 3648 },{ 25, 200, 98 },{ 198, 132, 8 }, }, { { 67, 243, 3928 },{ 140, 206, 3648 },{ 33, 208, 98 },{ 206, 140, 8 }, },2682{ { 76, 251, 3928 },{ 148, 214, 3648 },{ 41, 216, 98 },{ 214, 148, 8 }, }, { { 86, 255, 856 },{ 156, 222, 3648 },{ 49, 224, 98 },{ 222, 156, 8 }, },2683{ { 255, 93, 169 },{ 165, 231, 3648 },{ 58, 233, 98 },{ 231, 165, 8 }, }, { { 98, 236, 3680 },{ 173, 239, 3648 },{ 66, 241, 98 },{ 239, 173, 8 }, },2684{ { 108, 181, 4040 },{ 181, 247, 3648 },{ 74, 249, 98 },{ 247, 181, 8 }, }, { { 116, 189, 4040 },{ 255, 189, 8 },{ 116, 189, 456 },{ 255, 189, 8 }, },2685{ { 125, 198, 4040 },{ 255, 198, 8 },{ 125, 198, 456 },{ 255, 198, 8 }, }, { { 133, 206, 4040 },{ 255, 206, 8 },{ 133, 206, 456 },{ 255, 206, 8 }, },2686{ { 141, 214, 4040 },{ 255, 214, 8 },{ 141, 214, 456 },{ 255, 214, 8 }, }, { { 149, 222, 4040 },{ 255, 222, 8 },{ 149, 222, 456 },{ 255, 222, 8 }, },2687{ { 47, 183, 566 },{ 47, 183, 560 },{ 47, 0, 9 },{ 47, 0, 8 }, }, { { 55, 191, 566 },{ 55, 191, 560 },{ 55, 0, 9 },{ 55, 0, 8 }, },2688{ { 63, 199, 566 },{ 63, 199, 560 },{ 63, 0, 9 },{ 63, 0, 8 }, }, { { 71, 207, 566 },{ 71, 207, 560 },{ 71, 0, 9 },{ 71, 0, 8 }, },2689{ { 80, 216, 566 },{ 80, 216, 560 },{ 80, 0, 9 },{ 80, 0, 8 }, }, { { 88, 224, 566 },{ 88, 224, 560 },{ 88, 0, 9 },{ 88, 0, 8 }, },2690{ { 3, 233, 710 },{ 3, 233, 704 },{ 2, 96, 70 },{ 96, 2, 8 }, }, { { 11, 241, 710 },{ 11, 241, 704 },{ 10, 104, 70 },{ 104, 10, 8 }, },2691{ { 20, 250, 710 },{ 20, 250, 704 },{ 19, 113, 70 },{ 113, 19, 8 }, }, { { 27, 121, 3654 },{ 27, 121, 3648 },{ 27, 121, 70 },{ 121, 27, 8 }, },2692{ { 35, 129, 3654 },{ 35, 129, 3648 },{ 35, 129, 70 },{ 129, 35, 8 }, }, { { 43, 137, 3654 },{ 43, 137, 3648 },{ 43, 137, 70 },{ 137, 43, 8 }, },2693{ { 52, 146, 3654 },{ 52, 146, 3648 },{ 52, 146, 70 },{ 146, 52, 8 }, }, { { 60, 154, 3654 },{ 60, 154, 3648 },{ 60, 154, 70 },{ 154, 60, 8 }, },2694{ { 68, 162, 3654 },{ 68, 162, 3648 },{ 68, 162, 70 },{ 162, 68, 8 }, }, { { 76, 170, 3654 },{ 76, 170, 3648 },{ 76, 170, 70 },{ 170, 76, 8 }, },2695{ { 85, 179, 3654 },{ 85, 179, 3648 },{ 85, 179, 70 },{ 179, 85, 8 }, }, { { 93, 187, 3654 },{ 93, 187, 3648 },{ 93, 187, 70 },{ 187, 93, 8 }, },2696{ { 101, 195, 3654 },{ 101, 195, 3648 },{ 101, 195, 70 },{ 195, 101, 8 }, }, { { 109, 203, 3654 },{ 109, 203, 3648 },{ 109, 203, 70 },{ 203, 109, 8 }, },2697{ { 118, 212, 3654 },{ 118, 212, 3648 },{ 118, 212, 70 },{ 212, 118, 8 }, }, { { 126, 220, 3654 },{ 126, 220, 3648 },{ 126, 220, 70 },{ 220, 126, 8 }, },2698{ { 134, 228, 3654 },{ 134, 228, 3648 },{ 134, 228, 70 },{ 228, 134, 8 }, }, { { 5, 236, 3680 },{ 142, 236, 3648 },{ 5, 236, 96 },{ 236, 142, 8 }, },2699{ { 14, 245, 3680 },{ 151, 245, 3648 },{ 14, 245, 96 },{ 245, 151, 8 }, }, { { 23, 159, 4040 },{ 159, 253, 3648 },{ 23, 159, 456 },{ 253, 159, 8 }, },2700{ { 31, 167, 4040 },{ 255, 167, 8 },{ 31, 167, 456 },{ 255, 167, 8 }, }, { { 39, 175, 4040 },{ 255, 175, 8 },{ 39, 175, 456 },{ 255, 175, 8 }, },2701{ { 48, 184, 4040 },{ 255, 184, 8 },{ 48, 184, 456 },{ 255, 184, 8 }, }, { { 56, 192, 4040 },{ 255, 192, 8 },{ 56, 192, 456 },{ 255, 192, 8 }, },2702{ { 64, 200, 4040 },{ 255, 200, 8 },{ 64, 200, 456 },{ 255, 200, 8 }, },{ { 72, 208, 4040 },{ 255, 208, 8 },{ 72, 208, 456 },{ 255, 208, 8 }, },27032704};27052706struct dxt5a_block2707{2708uint8_t m_endpoints[2];27092710enum { cTotalSelectorBytes = 6 };2711uint8_t m_selectors[cTotalSelectorBytes];27122713inline void clear()2714{2715basisu::clear_obj(*this);2716}27172718inline uint32_t get_low_alpha() const2719{2720return m_endpoints[0];2721}27222723inline uint32_t get_high_alpha() const2724{2725return m_endpoints[1];2726}27272728inline void set_low_alpha(uint32_t i)2729{2730assert(i <= UINT8_MAX);2731m_endpoints[0] = static_cast<uint8_t>(i);2732}27332734inline void set_high_alpha(uint32_t i)2735{2736assert(i <= UINT8_MAX);2737m_endpoints[1] = static_cast<uint8_t>(i);2738}27392740inline bool is_alpha6_block() const { return get_low_alpha() <= get_high_alpha(); }27412742uint32_t get_endpoints_as_word() const { return m_endpoints[0] | (m_endpoints[1] << 8); }2743uint32_t get_selectors_as_word(uint32_t index) { assert(index < 3); return m_selectors[index * 2] | (m_selectors[index * 2 + 1] << 8); }27442745inline uint32_t get_selector(uint32_t x, uint32_t y) const2746{2747assert((x < 4U) && (y < 4U));27482749uint32_t selector_index = (y * 4) + x;2750uint32_t bit_index = selector_index * cDXT5SelectorBits;27512752uint32_t byte_index = bit_index >> 3;2753uint32_t bit_ofs = bit_index & 7;27542755uint32_t v = m_selectors[byte_index];2756if (byte_index < (cTotalSelectorBytes - 1))2757v |= (m_selectors[byte_index + 1] << 8);27582759return (v >> bit_ofs) & 7;2760}27612762inline void set_selector(uint32_t x, uint32_t y, uint32_t val)2763{2764assert((x < 4U) && (y < 4U) && (val < 8U));27652766uint32_t selector_index = (y * 4) + x;2767uint32_t bit_index = selector_index * cDXT5SelectorBits;27682769uint32_t byte_index = bit_index >> 3;2770uint32_t bit_ofs = bit_index & 7;27712772uint32_t v = m_selectors[byte_index];2773if (byte_index < (cTotalSelectorBytes - 1))2774v |= (m_selectors[byte_index + 1] << 8);27752776v &= (~(7 << bit_ofs));2777v |= (val << bit_ofs);27782779m_selectors[byte_index] = static_cast<uint8_t>(v);2780if (byte_index < (cTotalSelectorBytes - 1))2781m_selectors[byte_index + 1] = static_cast<uint8_t>(v >> 8);2782}27832784enum { cMaxSelectorValues = 8 };27852786static uint32_t get_block_values6(color32* pDst, uint32_t l, uint32_t h)2787{2788pDst[0].a = static_cast<uint8_t>(l);2789pDst[1].a = static_cast<uint8_t>(h);2790pDst[2].a = static_cast<uint8_t>((l * 4 + h) / 5);2791pDst[3].a = static_cast<uint8_t>((l * 3 + h * 2) / 5);2792pDst[4].a = static_cast<uint8_t>((l * 2 + h * 3) / 5);2793pDst[5].a = static_cast<uint8_t>((l + h * 4) / 5);2794pDst[6].a = 0;2795pDst[7].a = 255;2796return 6;2797}27982799static uint32_t get_block_values8(color32* pDst, uint32_t l, uint32_t h)2800{2801pDst[0].a = static_cast<uint8_t>(l);2802pDst[1].a = static_cast<uint8_t>(h);2803pDst[2].a = static_cast<uint8_t>((l * 6 + h) / 7);2804pDst[3].a = static_cast<uint8_t>((l * 5 + h * 2) / 7);2805pDst[4].a = static_cast<uint8_t>((l * 4 + h * 3) / 7);2806pDst[5].a = static_cast<uint8_t>((l * 3 + h * 4) / 7);2807pDst[6].a = static_cast<uint8_t>((l * 2 + h * 5) / 7);2808pDst[7].a = static_cast<uint8_t>((l + h * 6) / 7);2809return 8;2810}28112812static uint32_t get_block_values(color32* pDst, uint32_t l, uint32_t h)2813{2814if (l > h)2815return get_block_values8(pDst, l, h);2816else2817return get_block_values6(pDst, l, h);2818}2819};28202821static void convert_etc1s_to_dxt5a(dxt5a_block* pDst_block, const endpoint* pEndpoints, const selector* pSelector)2822{2823const uint32_t low_selector = pSelector->m_lo_selector;2824const uint32_t high_selector = pSelector->m_hi_selector;28252826const color32& base_color = pEndpoints->m_color5;2827const uint32_t inten_table = pEndpoints->m_inten5;28282829if (low_selector == high_selector)2830{2831uint32_t r;2832decoder_etc_block::get_block_color5_r(base_color, inten_table, low_selector, r);28332834pDst_block->set_low_alpha(r);2835pDst_block->set_high_alpha(r);2836pDst_block->m_selectors[0] = 0;2837pDst_block->m_selectors[1] = 0;2838pDst_block->m_selectors[2] = 0;2839pDst_block->m_selectors[3] = 0;2840pDst_block->m_selectors[4] = 0;2841pDst_block->m_selectors[5] = 0;2842return;2843}2844else if (pSelector->m_num_unique_selectors == 2)2845{2846color32 block_colors[4];28472848decoder_etc_block::get_block_colors5(block_colors, base_color, inten_table);28492850const uint32_t r0 = block_colors[low_selector].r;2851const uint32_t r1 = block_colors[high_selector].r;28522853pDst_block->set_low_alpha(r0);2854pDst_block->set_high_alpha(r1);28552856// TODO: Optimize this2857for (uint32_t y = 0; y < 4; y++)2858{2859for (uint32_t x = 0; x < 4; x++)2860{2861uint32_t s = pSelector->get_selector(x, y);2862pDst_block->set_selector(x, y, (s == high_selector) ? 1 : 0);2863}2864}28652866return;2867}28682869uint32_t selector_range_table = 0;2870for (selector_range_table = 0; selector_range_table < NUM_DXT5A_SELECTOR_RANGES; selector_range_table++)2871if ((low_selector == s_dxt5a_selector_ranges[selector_range_table].m_low) && (high_selector == s_dxt5a_selector_ranges[selector_range_table].m_high))2872break;2873if (selector_range_table >= NUM_DXT5A_SELECTOR_RANGES)2874selector_range_table = 0;28752876const etc1_g_to_dxt5a_conversion* pTable_entry = &g_etc1_g_to_dxt5a[base_color.r + inten_table * 32][selector_range_table];28772878pDst_block->set_low_alpha(pTable_entry->m_lo);2879pDst_block->set_high_alpha(pTable_entry->m_hi);28802881// TODO: Optimize this (like ETC1->BC1)2882for (uint32_t y = 0; y < 4; y++)2883{2884for (uint32_t x = 0; x < 4; x++)2885{2886uint32_t s = pSelector->get_selector(x, y);28872888uint32_t ds = (pTable_entry->m_trans >> (s * 3)) & 7;28892890pDst_block->set_selector(x, y, ds);2891}2892}2893}2894#endif //BASISD_SUPPORT_DXT5A28952896// PVRTC28972898#if BASISD_SUPPORT_PVRTC1 || BASISD_SUPPORT_UASTC2899static const uint16_t g_pvrtc_swizzle_table[256] =2900{29010x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015, 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055, 0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115, 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155,29020x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415, 0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455, 0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515, 0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555,29030x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015, 0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055, 0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115, 0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155,29040x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415, 0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455, 0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515, 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555,29050x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015, 0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055, 0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115, 0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155,29060x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415, 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455, 0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515, 0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555,29070x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015, 0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055, 0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115, 0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155,29080x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415, 0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455, 0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515, 0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x55552909};29102911// Note we can't use simple calculations to convert PVRTC1 encoded endpoint components to/from 8-bits, due to hardware approximations.2912static const uint8_t g_pvrtc_5[32] = { 0,8,16,24,33,41,49,57,66,74,82,90,99,107,115,123,132,140,148,156,165,173,181,189,198,206,214,222,231,239,247,255 };2913static const uint8_t g_pvrtc_4[16] = { 0,16,33,49,66,82,99,115,140,156,173,189,206,222,239,255 };2914static const uint8_t g_pvrtc_3[8] = { 0,33,74,107,148,181,222,255 };2915static const uint8_t g_pvrtc_alpha[9] = { 0,34,68,102,136,170,204,238,255 };29162917static const uint8_t g_pvrtc_5_floor[256] =2918{29190,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,29203,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,29217,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,292211,11,11,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,15,15,15,15,15,292315,15,15,15,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,19,19,19,19,292419,19,19,19,19,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,23,23,23,292523,23,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,27,27,292627,27,27,27,27,27,27,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,312927};29282929static const uint8_t g_pvrtc_5_ceil[256] =2930{29310,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,29324,4,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,8,8,8,8,8,8,29338,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,12,12,12,12,12,293412,12,12,12,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,16,16,16,16,293516,16,16,16,16,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,20,20,20,293620,20,20,20,20,20,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,24,24,293724,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,28,293828,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,312939};29402941static const uint8_t g_pvrtc_4_floor[256] =2942{29430,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,29441,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,29453,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,29465,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,29477,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,29489,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,294911,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,295013,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,152951};29522953static const uint8_t g_pvrtc_4_ceil[256] =2954{29550,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,29562,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,29574,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,29586,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,29598,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,296010,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,296112,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,296214,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,152963};29642965static const uint8_t g_pvrtc_3_floor[256] =2966{29670,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,29680,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,29691,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,29702,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,29713,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,29724,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,29735,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,29746,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,72975};29762977static const uint8_t g_pvrtc_3_ceil[256] =2978{29790,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,29801,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,29812,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,29823,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,29834,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,29845,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,29856,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,29867,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,72987};29882989static const uint8_t g_pvrtc_alpha_floor[256] =2990{29910,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,29920,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,29931,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,29942,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,29953,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,29964,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,29975,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,29986,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,82999};30003001static const uint8_t g_pvrtc_alpha_ceil[256] =3002{30030,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,30041,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,30052,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,30063,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,30074,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,30085,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,30096,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,30107,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,83011};30123013struct pvrtc4_block3014{3015uint32_t m_modulation;3016uint32_t m_endpoints;30173018pvrtc4_block() : m_modulation(0), m_endpoints(0) { }30193020inline bool operator== (const pvrtc4_block& rhs) const3021{3022return (m_modulation == rhs.m_modulation) && (m_endpoints == rhs.m_endpoints);3023}30243025inline void clear()3026{3027m_modulation = 0;3028m_endpoints = 0;3029}30303031inline bool get_block_uses_transparent_modulation() const3032{3033return (m_endpoints & 1) != 0;3034}30353036inline void set_block_uses_transparent_modulation(bool m)3037{3038m_endpoints = (m_endpoints & ~1U) | static_cast<uint32_t>(m);3039}30403041inline bool is_endpoint_opaque(uint32_t endpoint_index) const3042{3043static const uint32_t s_bitmasks[2] = { 0x8000U, 0x80000000U };3044return (m_endpoints & s_bitmasks[basisu::open_range_check(endpoint_index, 2U)]) != 0;3045}30463047inline void set_endpoint_opaque(uint32_t endpoint_index, bool opaque)3048{3049assert(endpoint_index < 2);3050static const uint32_t s_bitmasks[2] = { 0x8000U, 0x80000000U };3051if (opaque)3052m_endpoints |= s_bitmasks[endpoint_index];3053else3054m_endpoints &= ~s_bitmasks[endpoint_index];3055}30563057inline color32 get_endpoint_5554(uint32_t endpoint_index) const3058{3059assert(endpoint_index < 2);3060static const uint32_t s_endpoint_mask[2] = { 0xFFFE, 0xFFFF };3061uint32_t packed = (m_endpoints >> (basisu::open_range_check(endpoint_index, 2U) ? 16 : 0)) & s_endpoint_mask[endpoint_index];30623063uint32_t r, g, b, a;3064if (packed & 0x8000)3065{3066// opaque 554 or 5553067r = (packed >> 10) & 31;3068g = (packed >> 5) & 31;3069b = packed & 31;30703071if (!endpoint_index)3072b |= (b >> 4);30733074a = 0xF;3075}3076else3077{3078// translucent 4433 or 44433079r = (packed >> 7) & 0x1E;3080g = (packed >> 3) & 0x1E;3081b = (packed & 0xF) << 1;30823083r |= (r >> 4);3084g |= (g >> 4);30853086if (!endpoint_index)3087b |= (b >> 3);3088else3089b |= (b >> 4);30903091a = (packed >> 11) & 0xE;3092}30933094assert((r < 32) && (g < 32) && (b < 32) && (a < 16));30953096return color32(r, g, b, a);3097}30983099inline color32 get_endpoint_8888(uint32_t endpoint_index) const3100{3101assert(endpoint_index < 2);3102static const uint32_t s_endpoint_mask[2] = { 0xFFFE, 0xFFFF };3103uint32_t packed = (m_endpoints >> (basisu::open_range_check(endpoint_index, 2U) ? 16 : 0)) & s_endpoint_mask[endpoint_index];31043105uint32_t r, g, b, a;3106if (packed & 0x8000)3107{3108// opaque 554 or 5553109// 1RRRRRGGGGGBBBBM3110// 1RRRRRGGGGGBBBBB3111r = (packed >> 10) & 31;3112g = (packed >> 5) & 31;3113b = packed & 31;31143115r = g_pvrtc_5[r];3116g = g_pvrtc_5[g];31173118if (!endpoint_index)3119b = g_pvrtc_4[b >> 1];3120else3121b = g_pvrtc_5[b];31223123a = 255;3124}3125else3126{3127// translucent 4433 or 44433128// 0AAA RRRR GGGG BBBM3129// 0AAA RRRR GGGG BBBB3130r = (packed >> 8) & 0xF;3131g = (packed >> 4) & 0xF;3132b = packed & 0xF;3133a = (packed >> 12) & 7;31343135r = g_pvrtc_4[r];3136g = g_pvrtc_4[g];31373138if (!endpoint_index)3139b = g_pvrtc_3[b >> 1];3140else3141b = g_pvrtc_4[b];31423143a = g_pvrtc_alpha[a];3144}31453146return color32(r, g, b, a);3147}31483149inline uint32_t get_endpoint_l8(uint32_t endpoint_index) const3150{3151color32 c(get_endpoint_8888(endpoint_index));3152return c.r + c.g + c.b + c.a;3153}31543155inline uint32_t get_opaque_endpoint_l0() const3156{3157uint32_t packed = m_endpoints & 0xFFFE;31583159uint32_t r, g, b;3160assert(packed & 0x8000);31613162// opaque 554 or 5553163r = (packed >> 10) & 31;3164g = (packed >> 5) & 31;3165b = packed & 31;3166b |= (b >> 4);31673168return r + g + b;3169}31703171inline uint32_t get_opaque_endpoint_l1() const3172{3173uint32_t packed = m_endpoints >> 16;31743175uint32_t r, g, b;3176assert(packed & 0x8000);31773178// opaque 554 or 5553179r = (packed >> 10) & 31;3180g = (packed >> 5) & 31;3181b = packed & 31;31823183return r + g + b;3184}31853186static uint32_t get_component_precision_in_bits(uint32_t c, uint32_t endpoint_index, bool opaque_endpoint)3187{3188static const uint32_t s_comp_prec[4][4] =3189{3190// R0 G0 B0 A0 R1 G1 B1 A13191{ 4, 4, 3, 3 },{ 4, 4, 4, 3 }, // transparent endpoint31923193{ 5, 5, 4, 0 },{ 5, 5, 5, 0 } // opaque endpoint3194};3195return s_comp_prec[basisu::open_range_check(endpoint_index, 2U) + (opaque_endpoint * 2)][basisu::open_range_check(c, 4U)];3196}31973198static color32 get_color_precision_in_bits(uint32_t endpoint_index, bool opaque_endpoint)3199{3200static const color32 s_color_prec[4] =3201{3202color32(4, 4, 3, 3), color32(4, 4, 4, 3), // transparent endpoint3203color32(5, 5, 4, 0), color32(5, 5, 5, 0) // opaque endpoint3204};3205return s_color_prec[basisu::open_range_check(endpoint_index, 2U) + (opaque_endpoint * 2)];3206}32073208inline void set_opaque_endpoint_floor(uint32_t endpoint_index, const color32& c)3209{3210assert(endpoint_index < 2);3211const uint32_t m = m_endpoints & 1;32123213uint32_t r = g_pvrtc_5_floor[c[0]], g = g_pvrtc_5_floor[c[1]], b = c[2];32143215if (!endpoint_index)3216b = g_pvrtc_4_floor[b] << 1;3217else3218b = g_pvrtc_5_floor[b];32193220// rgba=555 here3221assert((r < 32) && (g < 32) && (b < 32));32223223// 1RRRRRGGGGGBBBBM3224// 1RRRRRGGGGGBBBBB32253226// opaque 554 or 5553227uint32_t packed = 0x8000 | (r << 10) | (g << 5) | b;3228if (!endpoint_index)3229packed = (packed & ~1) | m;32303231assert(packed <= 0xFFFF);32323233if (endpoint_index)3234m_endpoints = (m_endpoints & 0xFFFFU) | (packed << 16);3235else3236m_endpoints = (m_endpoints & 0xFFFF0000U) | packed;3237}32383239inline void set_opaque_endpoint_ceil(uint32_t endpoint_index, const color32& c)3240{3241assert(endpoint_index < 2);3242const uint32_t m = m_endpoints & 1;32433244uint32_t r = g_pvrtc_5_ceil[c[0]], g = g_pvrtc_5_ceil[c[1]], b = c[2];32453246if (!endpoint_index)3247b = g_pvrtc_4_ceil[b] << 1;3248else3249b = g_pvrtc_5_ceil[b];32503251// rgba=555 here3252assert((r < 32) && (g < 32) && (b < 32));32533254// 1RRRRRGGGGGBBBBM3255// 1RRRRRGGGGGBBBBB32563257// opaque 554 or 5553258uint32_t packed = 0x8000 | (r << 10) | (g << 5) | b;3259if (!endpoint_index)3260packed |= m;32613262assert(packed <= 0xFFFF);32633264if (endpoint_index)3265m_endpoints = (m_endpoints & 0xFFFFU) | (packed << 16);3266else3267m_endpoints = (m_endpoints & 0xFFFF0000U) | packed;3268}32693270// opaque endpoints: 554 or 5553271// transparent endpoints: 3443 or 34443272inline void set_endpoint_raw(uint32_t endpoint_index, const color32& c, bool opaque_endpoint)3273{3274assert(endpoint_index < 2);3275const uint32_t m = m_endpoints & 1;3276uint32_t r = c[0], g = c[1], b = c[2], a = c[3];32773278uint32_t packed;32793280if (opaque_endpoint)3281{3282if (!endpoint_index)3283{3284// 5543285// 1RRRRRGGGGGBBBBM3286assert((r < 32) && (g < 32) && (b < 16));3287packed = 0x8000 | (r << 10) | (g << 5) | (b << 1) | m;3288}3289else3290{3291// 5553292// 1RRRRRGGGGGBBBBB3293assert((r < 32) && (g < 32) && (b < 32));3294packed = 0x8000 | (r << 10) | (g << 5) | b;3295}3296}3297else3298{3299if (!endpoint_index)3300{3301// 34433302// 0AAA RRRR GGGG BBBM3303assert((r < 16) && (g < 16) && (b < 8) && (a < 8));3304packed = (a << 12) | (r << 8) | (g << 4) | (b << 1) | m;3305}3306else3307{3308// 34443309// 0AAA RRRR GGGG BBBB3310assert((r < 16) && (g < 16) && (b < 16) && (a < 8));3311packed = (a << 12) | (r << 8) | (g << 4) | b;3312}3313}33143315assert(packed <= 0xFFFF);33163317if (endpoint_index)3318m_endpoints = (m_endpoints & 0xFFFFU) | (packed << 16);3319else3320m_endpoints = (m_endpoints & 0xFFFF0000U) | packed;3321}33223323inline void set_endpoint_floor(uint32_t endpoint_index, const color32& c)3324{3325assert(endpoint_index < 2);33263327int a = g_pvrtc_alpha_floor[c.a];3328if (a == 8)3329{3330// 554 or 5553331uint32_t r = g_pvrtc_5_floor[c[0]], g = g_pvrtc_5_floor[c[1]], b = c[2];33323333if (!endpoint_index)3334b = g_pvrtc_4_floor[b];3335else3336b = g_pvrtc_5_floor[b];33373338set_endpoint_raw(endpoint_index, color32(r, g, b, a), true);3339}3340else3341{3342// 4433 or 44433343uint32_t r = g_pvrtc_4_floor[c[0]], g = g_pvrtc_4_floor[c[1]], b = c[2];33443345if (!endpoint_index)3346b = g_pvrtc_3_floor[b];3347else3348b = g_pvrtc_4_floor[b];33493350set_endpoint_raw(endpoint_index, color32(r, g, b, a), false);3351}3352}33533354inline void set_endpoint_ceil(uint32_t endpoint_index, const color32& c)3355{3356assert(endpoint_index < 2);33573358int a = g_pvrtc_alpha_ceil[c.a];3359if (a == 8)3360{3361// 554 or 5553362uint32_t r = g_pvrtc_5_ceil[c[0]], g = g_pvrtc_5_ceil[c[1]], b = c[2];33633364if (!endpoint_index)3365b = g_pvrtc_4_ceil[b];3366else3367b = g_pvrtc_5_ceil[b];33683369set_endpoint_raw(endpoint_index, color32(r, g, b, a), true);3370}3371else3372{3373// 4433 or 44433374uint32_t r = g_pvrtc_4_ceil[c[0]], g = g_pvrtc_4_ceil[c[1]], b = c[2];33753376if (!endpoint_index)3377b = g_pvrtc_3_ceil[b];3378else3379b = g_pvrtc_4_ceil[b];33803381set_endpoint_raw(endpoint_index, color32(r, g, b, a), false);3382}3383}33843385inline uint32_t get_modulation(uint32_t x, uint32_t y) const3386{3387assert((x < 4) && (y < 4));3388return (m_modulation >> ((y * 4 + x) * 2)) & 3;3389}33903391// Scaled by 83392inline const uint32_t* get_scaled_modulation_values(bool block_uses_transparent_modulation) const3393{3394static const uint32_t s_block_scales[2][4] = { { 0, 3, 5, 8 },{ 0, 4, 4, 8 } };3395return s_block_scales[block_uses_transparent_modulation];3396}33973398// Scaled by 83399inline uint32_t get_scaled_modulation(uint32_t x, uint32_t y) const3400{3401return get_scaled_modulation_values(get_block_uses_transparent_modulation())[get_modulation(x, y)];3402}34033404inline void set_modulation(uint32_t x, uint32_t y, uint32_t s)3405{3406assert((x < 4) && (y < 4) && (s < 4));3407uint32_t n = (y * 4 + x) * 2;3408m_modulation = (m_modulation & (~(3 << n))) | (s << n);3409assert(get_modulation(x, y) == s);3410}34113412// Assumes modulation was initialized to 03413inline void set_modulation_fast(uint32_t x, uint32_t y, uint32_t s)3414{3415assert((x < 4) && (y < 4) && (s < 4));3416uint32_t n = (y * 4 + x) * 2;3417m_modulation |= (s << n);3418assert(get_modulation(x, y) == s);3419}3420};34213422#if 03423static const uint8_t g_pvrtc_bilinear_weights[16][4] =3424{3425{ 4, 4, 4, 4 }, { 2, 6, 2, 6 }, { 8, 0, 8, 0 }, { 6, 2, 6, 2 },3426{ 2, 2, 6, 6 }, { 1, 3, 3, 9 }, { 4, 0, 12, 0 }, { 3, 1, 9, 3 },3427{ 8, 8, 0, 0 }, { 4, 12, 0, 0 }, { 16, 0, 0, 0 }, { 12, 4, 0, 0 },3428{ 6, 6, 2, 2 }, { 3, 9, 1, 3 }, { 12, 0, 4, 0 }, { 9, 3, 3, 1 },3429};3430#endif34313432struct pvrtc1_temp_block3433{3434decoder_etc_block m_etc1_block;3435uint32_t m_pvrtc_endpoints;3436};34373438static inline uint32_t get_opaque_endpoint_l0(uint32_t endpoints)3439{3440uint32_t packed = endpoints;34413442uint32_t r, g, b;3443assert(packed & 0x8000);34443445r = (packed >> 10) & 31;3446g = (packed >> 5) & 31;3447b = packed & 30;3448b |= (b >> 4);34493450return r + g + b;3451}34523453static inline uint32_t get_opaque_endpoint_l1(uint32_t endpoints)3454{3455uint32_t packed = endpoints >> 16;34563457uint32_t r, g, b;3458assert(packed & 0x8000);34593460r = (packed >> 10) & 31;3461g = (packed >> 5) & 31;3462b = packed & 31;34633464return r + g + b;3465}34663467static color32 get_endpoint_8888(uint32_t endpoints, uint32_t endpoint_index)3468{3469assert(endpoint_index < 2);3470static const uint32_t s_endpoint_mask[2] = { 0xFFFE, 0xFFFF };3471uint32_t packed = (endpoints >> (basisu::open_range_check(endpoint_index, 2U) ? 16 : 0)) & s_endpoint_mask[endpoint_index];34723473uint32_t r, g, b, a;3474if (packed & 0x8000)3475{3476// opaque 554 or 5553477// 1RRRRRGGGGGBBBBM3478// 1RRRRRGGGGGBBBBB3479r = (packed >> 10) & 31;3480g = (packed >> 5) & 31;3481b = packed & 31;34823483r = g_pvrtc_5[r];3484g = g_pvrtc_5[g];34853486if (!endpoint_index)3487b = g_pvrtc_4[b >> 1];3488else3489b = g_pvrtc_5[b];34903491a = 255;3492}3493else3494{3495// translucent 4433 or 44433496// 0AAA RRRR GGGG BBBM3497// 0AAA RRRR GGGG BBBB3498r = (packed >> 8) & 0xF;3499g = (packed >> 4) & 0xF;3500b = packed & 0xF;3501a = (packed >> 12) & 7;35023503r = g_pvrtc_4[r];3504g = g_pvrtc_4[g];35053506if (!endpoint_index)3507b = g_pvrtc_3[b >> 1];3508else3509b = g_pvrtc_4[b];35103511a = g_pvrtc_alpha[a];3512}35133514return color32(r, g, b, a);3515}35163517static uint32_t get_endpoint_l8(uint32_t endpoints, uint32_t endpoint_index)3518{3519color32 c(get_endpoint_8888(endpoints, endpoint_index));3520return c.r + c.g + c.b + c.a;3521}3522#endif35233524#if BASISD_SUPPORT_PVRTC13525// TODO: Support decoding a non-pow2 ETC1S texture into the next larger pow2 PVRTC texture.3526static void fixup_pvrtc1_4_modulation_rgb(const decoder_etc_block* pETC_Blocks, const uint32_t* pPVRTC_endpoints, void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y)3527{3528const uint32_t x_mask = num_blocks_x - 1;3529const uint32_t y_mask = num_blocks_y - 1;3530const uint32_t x_bits = basisu::total_bits(x_mask);3531const uint32_t y_bits = basisu::total_bits(y_mask);3532const uint32_t min_bits = basisu::minimum(x_bits, y_bits);3533//const uint32_t max_bits = basisu::maximum(x_bits, y_bits);3534const uint32_t swizzle_mask = (1 << (min_bits * 2)) - 1;35353536uint32_t block_index = 0;35373538// really 3x33539int e0[4][4], e1[4][4];35403541for (int y = 0; y < static_cast<int>(num_blocks_y); y++)3542{3543const uint32_t* pE_rows[3];35443545for (int ey = 0; ey < 3; ey++)3546{3547int by = y + ey - 1;35483549const uint32_t* pE = &pPVRTC_endpoints[(by & y_mask) * num_blocks_x];35503551pE_rows[ey] = pE;35523553for (int ex = 0; ex < 3; ex++)3554{3555int bx = 0 + ex - 1;35563557const uint32_t e = pE[bx & x_mask];35583559e0[ex][ey] = (get_opaque_endpoint_l0(e) * 255) / 31;3560e1[ex][ey] = (get_opaque_endpoint_l1(e) * 255) / 31;3561}3562}35633564const uint32_t y_swizzle = (g_pvrtc_swizzle_table[y >> 8] << 16) | g_pvrtc_swizzle_table[y & 0xFF];35653566for (int x = 0; x < static_cast<int>(num_blocks_x); x++, block_index++)3567{3568const decoder_etc_block& src_block = pETC_Blocks[block_index];35693570const uint32_t x_swizzle = (g_pvrtc_swizzle_table[x >> 8] << 17) | (g_pvrtc_swizzle_table[x & 0xFF] << 1);35713572uint32_t swizzled = x_swizzle | y_swizzle;3573if (num_blocks_x != num_blocks_y)3574{3575swizzled &= swizzle_mask;35763577if (num_blocks_x > num_blocks_y)3578swizzled |= ((x >> min_bits) << (min_bits * 2));3579else3580swizzled |= ((y >> min_bits) << (min_bits * 2));3581}35823583pvrtc4_block* pDst_block = static_cast<pvrtc4_block*>(pDst_blocks) + swizzled;3584pDst_block->m_endpoints = pPVRTC_endpoints[block_index];35853586uint32_t base_r = g_etc_5_to_8[src_block.m_differential.m_red1];3587uint32_t base_g = g_etc_5_to_8[src_block.m_differential.m_green1];3588uint32_t base_b = g_etc_5_to_8[src_block.m_differential.m_blue1];35893590const int* pInten_table48 = g_etc1_inten_tables48[src_block.m_differential.m_cw1];3591int by = (base_r + base_g + base_b) * 16;3592int block_colors_y_x16[4];3593block_colors_y_x16[0] = by + pInten_table48[2];3594block_colors_y_x16[1] = by + pInten_table48[3];3595block_colors_y_x16[2] = by + pInten_table48[1];3596block_colors_y_x16[3] = by + pInten_table48[0];35973598{3599const uint32_t ex = 2;3600int bx = x + ex - 1;3601bx &= x_mask;36023603#define DO_ROW(ey) \3604{ \3605const uint32_t e = pE_rows[ey][bx]; \3606e0[ex][ey] = (get_opaque_endpoint_l0(e) * 255) / 31; \3607e1[ex][ey] = (get_opaque_endpoint_l1(e) * 255) / 31; \3608}36093610DO_ROW(0);3611DO_ROW(1);3612DO_ROW(2);3613#undef DO_ROW3614}36153616uint32_t mod = 0;36173618uint32_t lookup_x[4];36193620#define DO_LOOKUP(lx) { \3621const uint32_t byte_ofs = 7 - (((lx) * 4) >> 3); \3622const uint32_t lsb_bits = src_block.m_bytes[byte_ofs] >> (((lx) & 1) * 4); \3623const uint32_t msb_bits = src_block.m_bytes[byte_ofs - 2] >> (((lx) & 1) * 4); \3624lookup_x[lx] = (lsb_bits & 0xF) | ((msb_bits & 0xF) << 4); }36253626DO_LOOKUP(0);3627DO_LOOKUP(1);3628DO_LOOKUP(2);3629DO_LOOKUP(3);3630#undef DO_LOOKUP36313632#define DO_PIX(lx, ly, w0, w1, w2, w3) \3633{ \3634int ca_l = a0 * w0 + a1 * w1 + a2 * w2 + a3 * w3; \3635int cb_l = b0 * w0 + b1 * w1 + b2 * w2 + b3 * w3; \3636int cl = block_colors_y_x16[g_etc1_x_selector_unpack[ly][lookup_x[lx]]]; \3637int dl = cb_l - ca_l; \3638int vl = cl - ca_l; \3639int p = vl * 16; \3640if (ca_l > cb_l) { p = -p; dl = -dl; } \3641uint32_t m = 0; \3642if (p > 3 * dl) m = (uint32_t)(1 << ((ly) * 8 + (lx) * 2)); \3643if (p > 8 * dl) m = (uint32_t)(2 << ((ly) * 8 + (lx) * 2)); \3644if (p > 13 * dl) m = (uint32_t)(3 << ((ly) * 8 + (lx) * 2)); \3645mod |= m; \3646}36473648{3649const uint32_t ex = 0, ey = 0;3650const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];3651const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];3652DO_PIX(0, 0, 4, 4, 4, 4);3653DO_PIX(1, 0, 2, 6, 2, 6);3654DO_PIX(0, 1, 2, 2, 6, 6);3655DO_PIX(1, 1, 1, 3, 3, 9);3656}36573658{3659const uint32_t ex = 1, ey = 0;3660const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];3661const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];3662DO_PIX(2, 0, 8, 0, 8, 0);3663DO_PIX(3, 0, 6, 2, 6, 2);3664DO_PIX(2, 1, 4, 0, 12, 0);3665DO_PIX(3, 1, 3, 1, 9, 3);3666}36673668{3669const uint32_t ex = 0, ey = 1;3670const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];3671const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];3672DO_PIX(0, 2, 8, 8, 0, 0);3673DO_PIX(1, 2, 4, 12, 0, 0);3674DO_PIX(0, 3, 6, 6, 2, 2);3675DO_PIX(1, 3, 3, 9, 1, 3);3676}36773678{3679const uint32_t ex = 1, ey = 1;3680const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];3681const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];3682DO_PIX(2, 2, 16, 0, 0, 0);3683DO_PIX(3, 2, 12, 4, 0, 0);3684DO_PIX(2, 3, 12, 0, 4, 0);3685DO_PIX(3, 3, 9, 3, 3, 1);3686}3687#undef DO_PIX36883689pDst_block->m_modulation = mod;36903691e0[0][0] = e0[1][0]; e0[1][0] = e0[2][0];3692e0[0][1] = e0[1][1]; e0[1][1] = e0[2][1];3693e0[0][2] = e0[1][2]; e0[1][2] = e0[2][2];36943695e1[0][0] = e1[1][0]; e1[1][0] = e1[2][0];3696e1[0][1] = e1[1][1]; e1[1][1] = e1[2][1];3697e1[0][2] = e1[1][2]; e1[1][2] = e1[2][2];36983699} // x3700} // y3701}37023703static void fixup_pvrtc1_4_modulation_rgba(3704const decoder_etc_block* pETC_Blocks,3705const uint32_t* pPVRTC_endpoints,3706void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, void *pAlpha_blocks,3707const endpoint* pEndpoints, const selector* pSelectors)3708{3709const uint32_t x_mask = num_blocks_x - 1;3710const uint32_t y_mask = num_blocks_y - 1;3711const uint32_t x_bits = basisu::total_bits(x_mask);3712const uint32_t y_bits = basisu::total_bits(y_mask);3713const uint32_t min_bits = basisu::minimum(x_bits, y_bits);3714//const uint32_t max_bits = basisu::maximum(x_bits, y_bits);3715const uint32_t swizzle_mask = (1 << (min_bits * 2)) - 1;37163717uint32_t block_index = 0;37183719// really 3x33720int e0[4][4], e1[4][4];37213722for (int y = 0; y < static_cast<int>(num_blocks_y); y++)3723{3724const uint32_t* pE_rows[3];37253726for (int ey = 0; ey < 3; ey++)3727{3728int by = y + ey - 1;37293730const uint32_t* pE = &pPVRTC_endpoints[(by & y_mask) * num_blocks_x];37313732pE_rows[ey] = pE;37333734for (int ex = 0; ex < 3; ex++)3735{3736int bx = 0 + ex - 1;37373738const uint32_t e = pE[bx & x_mask];37393740e0[ex][ey] = get_endpoint_l8(e, 0);3741e1[ex][ey] = get_endpoint_l8(e, 1);3742}3743}37443745const uint32_t y_swizzle = (g_pvrtc_swizzle_table[y >> 8] << 16) | g_pvrtc_swizzle_table[y & 0xFF];37463747for (int x = 0; x < static_cast<int>(num_blocks_x); x++, block_index++)3748{3749const decoder_etc_block& src_block = pETC_Blocks[block_index];37503751const uint16_t* pSrc_alpha_block = reinterpret_cast<const uint16_t*>(static_cast<const uint32_t*>(pAlpha_blocks) + x + (y * num_blocks_x));3752const endpoint* pAlpha_endpoints = &pEndpoints[pSrc_alpha_block[0]];3753const selector* pAlpha_selectors = &pSelectors[pSrc_alpha_block[1]];37543755const uint32_t x_swizzle = (g_pvrtc_swizzle_table[x >> 8] << 17) | (g_pvrtc_swizzle_table[x & 0xFF] << 1);37563757uint32_t swizzled = x_swizzle | y_swizzle;3758if (num_blocks_x != num_blocks_y)3759{3760swizzled &= swizzle_mask;37613762if (num_blocks_x > num_blocks_y)3763swizzled |= ((x >> min_bits) << (min_bits * 2));3764else3765swizzled |= ((y >> min_bits) << (min_bits * 2));3766}37673768pvrtc4_block* pDst_block = static_cast<pvrtc4_block*>(pDst_blocks) + swizzled;3769pDst_block->m_endpoints = pPVRTC_endpoints[block_index];37703771uint32_t base_r = g_etc_5_to_8[src_block.m_differential.m_red1];3772uint32_t base_g = g_etc_5_to_8[src_block.m_differential.m_green1];3773uint32_t base_b = g_etc_5_to_8[src_block.m_differential.m_blue1];37743775const int* pInten_table48 = g_etc1_inten_tables48[src_block.m_differential.m_cw1];3776int by = (base_r + base_g + base_b) * 16;3777int block_colors_y_x16[4];3778block_colors_y_x16[0] = basisu::clamp<int>(by + pInten_table48[0], 0, 48 * 255);3779block_colors_y_x16[1] = basisu::clamp<int>(by + pInten_table48[1], 0, 48 * 255);3780block_colors_y_x16[2] = basisu::clamp<int>(by + pInten_table48[2], 0, 48 * 255);3781block_colors_y_x16[3] = basisu::clamp<int>(by + pInten_table48[3], 0, 48 * 255);37823783uint32_t alpha_base_g = g_etc_5_to_8[pAlpha_endpoints->m_color5.g] * 16;3784const int* pInten_table16 = g_etc1_inten_tables16[pAlpha_endpoints->m_inten5];3785int alpha_block_colors_x16[4];3786alpha_block_colors_x16[0] = basisu::clamp<int>(alpha_base_g + pInten_table16[0], 0, 16 * 255);3787alpha_block_colors_x16[1] = basisu::clamp<int>(alpha_base_g + pInten_table16[1], 0, 16 * 255);3788alpha_block_colors_x16[2] = basisu::clamp<int>(alpha_base_g + pInten_table16[2], 0, 16 * 255);3789alpha_block_colors_x16[3] = basisu::clamp<int>(alpha_base_g + pInten_table16[3], 0, 16 * 255);37903791// clamp((base_r + base_g + base_b) * 16 + color_inten[s] * 48) + clamp(alpha_base_g * 16 + alpha_inten[as] * 16)37923793{3794const uint32_t ex = 2;3795int bx = x + ex - 1;3796bx &= x_mask;37973798#define DO_ROW(ey) \3799{ \3800const uint32_t e = pE_rows[ey][bx]; \3801e0[ex][ey] = get_endpoint_l8(e, 0); \3802e1[ex][ey] = get_endpoint_l8(e, 1); \3803}38043805DO_ROW(0);3806DO_ROW(1);3807DO_ROW(2);3808#undef DO_ROW3809}38103811uint32_t mod = 0;38123813#define DO_PIX(lx, ly, w0, w1, w2, w3) \3814{ \3815int ca_l = a0 * w0 + a1 * w1 + a2 * w2 + a3 * w3; \3816int cb_l = b0 * w0 + b1 * w1 + b2 * w2 + b3 * w3; \3817int cl = block_colors_y_x16[(src_block.m_bytes[4 + ly] >> (lx * 2)) & 3] + alpha_block_colors_x16[(pAlpha_selectors->m_selectors[ly] >> (lx * 2)) & 3]; \3818int dl = cb_l - ca_l; \3819int vl = cl - ca_l; \3820int p = vl * 16; \3821if (ca_l > cb_l) { p = -p; dl = -dl; } \3822uint32_t m = 0; \3823if (p > 3 * dl) m = (uint32_t)(1 << ((ly) * 8 + (lx) * 2)); \3824if (p > 8 * dl) m = (uint32_t)(2 << ((ly) * 8 + (lx) * 2)); \3825if (p > 13 * dl) m = (uint32_t)(3 << ((ly) * 8 + (lx) * 2)); \3826mod |= m; \3827}38283829{3830const uint32_t ex = 0, ey = 0;3831const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];3832const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];3833DO_PIX(0, 0, 4, 4, 4, 4);3834DO_PIX(1, 0, 2, 6, 2, 6);3835DO_PIX(0, 1, 2, 2, 6, 6);3836DO_PIX(1, 1, 1, 3, 3, 9);3837}38383839{3840const uint32_t ex = 1, ey = 0;3841const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];3842const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];3843DO_PIX(2, 0, 8, 0, 8, 0);3844DO_PIX(3, 0, 6, 2, 6, 2);3845DO_PIX(2, 1, 4, 0, 12, 0);3846DO_PIX(3, 1, 3, 1, 9, 3);3847}38483849{3850const uint32_t ex = 0, ey = 1;3851const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];3852const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];3853DO_PIX(0, 2, 8, 8, 0, 0);3854DO_PIX(1, 2, 4, 12, 0, 0);3855DO_PIX(0, 3, 6, 6, 2, 2);3856DO_PIX(1, 3, 3, 9, 1, 3);3857}38583859{3860const uint32_t ex = 1, ey = 1;3861const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];3862const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];3863DO_PIX(2, 2, 16, 0, 0, 0);3864DO_PIX(3, 2, 12, 4, 0, 0);3865DO_PIX(2, 3, 12, 0, 4, 0);3866DO_PIX(3, 3, 9, 3, 3, 1);3867}3868#undef DO_PIX38693870pDst_block->m_modulation = mod;38713872e0[0][0] = e0[1][0]; e0[1][0] = e0[2][0];3873e0[0][1] = e0[1][1]; e0[1][1] = e0[2][1];3874e0[0][2] = e0[1][2]; e0[1][2] = e0[2][2];38753876e1[0][0] = e1[1][0]; e1[1][0] = e1[2][0];3877e1[0][1] = e1[1][1]; e1[1][1] = e1[2][1];3878e1[0][2] = e1[1][2]; e1[1][2] = e1[2][2];38793880} // x3881} // y3882}3883#endif // BASISD_SUPPORT_PVRTC138843885#if BASISD_SUPPORT_BC7_MODE53886static dxt_selector_range g_etc1_to_bc7_m5_selector_ranges[] =3887{3888{ 0, 3 },3889{ 1, 3 },3890{ 0, 2 },3891{ 1, 2 },3892{ 2, 3 },3893{ 0, 1 },3894};38953896const uint32_t NUM_ETC1_TO_BC7_M5_SELECTOR_RANGES = sizeof(g_etc1_to_bc7_m5_selector_ranges) / sizeof(g_etc1_to_bc7_m5_selector_ranges[0]);38973898static uint32_t g_etc1_to_bc7_m5_selector_range_index[4][4];38993900const uint32_t NUM_ETC1_TO_BC7_M5_SELECTOR_MAPPINGS = 10;3901static const uint8_t g_etc1_to_bc7_m5_selector_mappings[NUM_ETC1_TO_BC7_M5_SELECTOR_MAPPINGS][4] =3902{3903{ 0, 0, 1, 1 },3904{ 0, 0, 1, 2 },3905{ 0, 0, 1, 3 },3906{ 0, 0, 2, 3 },3907{ 0, 1, 1, 1 },3908{ 0, 1, 2, 2 },3909{ 0, 1, 2, 3 },3910{ 0, 2, 3, 3 },3911{ 1, 2, 2, 2 },3912{ 1, 2, 3, 3 },3913};39143915struct etc1_to_bc7_m5_solution3916{3917uint8_t m_lo;3918uint8_t m_hi;3919uint16_t m_err;3920};39213922static const etc1_to_bc7_m5_solution g_etc1_to_bc7_m5_color[32 * 8 * NUM_ETC1_TO_BC7_M5_SELECTOR_MAPPINGS * NUM_ETC1_TO_BC7_M5_SELECTOR_RANGES] = {3923#include "basisu_transcoder_tables_bc7_m5_color.inc"3924};39253926static dxt_selector_range g_etc1_to_bc7_m5a_selector_ranges[] =3927{3928{ 0, 3 },3929{ 1, 3 },3930{ 0, 2 },3931{ 1, 2 },3932{ 2, 3 },3933{ 0, 1 }3934};39353936const uint32_t NUM_ETC1_TO_BC7_M5A_SELECTOR_RANGES = sizeof(g_etc1_to_bc7_m5a_selector_ranges) / sizeof(g_etc1_to_bc7_m5a_selector_ranges[0]);39373938static uint32_t g_etc1_to_bc7_m5a_selector_range_index[4][4];39393940struct etc1_g_to_bc7_m5a_conversion3941{3942uint8_t m_lo, m_hi;3943uint8_t m_trans;3944};39453946static etc1_g_to_bc7_m5a_conversion g_etc1_g_to_bc7_m5a[8 * 32 * NUM_ETC1_TO_BC7_M5A_SELECTOR_RANGES] =3947{3948#include "basisu_transcoder_tables_bc7_m5_alpha.inc"3949};39503951static inline uint32_t set_block_bits(uint8_t* pBytes, uint32_t val, uint32_t num_bits, uint32_t cur_ofs)3952{3953assert(num_bits < 32);3954assert(val < (1ULL << num_bits));39553956uint32_t mask = static_cast<uint32_t>((1ULL << num_bits) - 1);39573958while (num_bits)3959{3960const uint32_t n = basisu::minimum<uint32_t>(8 - (cur_ofs & 7), num_bits);39613962pBytes[cur_ofs >> 3] &= ~static_cast<uint8_t>(mask << (cur_ofs & 7));3963pBytes[cur_ofs >> 3] |= static_cast<uint8_t>(val << (cur_ofs & 7));39643965val >>= n;3966mask >>= n;39673968num_bits -= n;3969cur_ofs += n;3970}39713972return cur_ofs;3973}39743975struct bc7_mode_53976{3977union3978{3979struct3980{3981uint64_t m_mode : 6;3982uint64_t m_rot : 2;39833984uint64_t m_r0 : 7;3985uint64_t m_r1 : 7;3986uint64_t m_g0 : 7;3987uint64_t m_g1 : 7;3988uint64_t m_b0 : 7;3989uint64_t m_b1 : 7;3990uint64_t m_a0 : 8;3991uint64_t m_a1_0 : 6;39923993} m_lo;39943995uint64_t m_lo_bits;3996};39973998union3999{4000struct4001{4002uint64_t m_a1_1 : 2;40034004// bit 24005uint64_t m_c00 : 1;4006uint64_t m_c10 : 2;4007uint64_t m_c20 : 2;4008uint64_t m_c30 : 2;40094010uint64_t m_c01 : 2;4011uint64_t m_c11 : 2;4012uint64_t m_c21 : 2;4013uint64_t m_c31 : 2;40144015uint64_t m_c02 : 2;4016uint64_t m_c12 : 2;4017uint64_t m_c22 : 2;4018uint64_t m_c32 : 2;40194020uint64_t m_c03 : 2;4021uint64_t m_c13 : 2;4022uint64_t m_c23 : 2;4023uint64_t m_c33 : 2;40244025// bit 334026uint64_t m_a00 : 1;4027uint64_t m_a10 : 2;4028uint64_t m_a20 : 2;4029uint64_t m_a30 : 2;40304031uint64_t m_a01 : 2;4032uint64_t m_a11 : 2;4033uint64_t m_a21 : 2;4034uint64_t m_a31 : 2;40354036uint64_t m_a02 : 2;4037uint64_t m_a12 : 2;4038uint64_t m_a22 : 2;4039uint64_t m_a32 : 2;40404041uint64_t m_a03 : 2;4042uint64_t m_a13 : 2;4043uint64_t m_a23 : 2;4044uint64_t m_a33 : 2;40454046} m_hi;40474048uint64_t m_hi_bits;4049};4050};40514052#if BASISD_WRITE_NEW_BC7_MODE5_TABLES4053static void create_etc1_to_bc7_m5_color_conversion_table()4054{4055FILE* pFile = nullptr;4056fopen_s(&pFile, "basisu_transcoder_tables_bc7_m5_color.inc", "w");40574058uint32_t n = 0;40594060for (int inten = 0; inten < 8; inten++)4061{4062for (uint32_t g = 0; g < 32; g++)4063{4064color32 block_colors[4];4065decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);40664067for (uint32_t sr = 0; sr < NUM_ETC1_TO_BC7_M5_SELECTOR_RANGES; sr++)4068{4069const uint32_t low_selector = g_etc1_to_bc7_m5_selector_ranges[sr].m_low;4070const uint32_t high_selector = g_etc1_to_bc7_m5_selector_ranges[sr].m_high;40714072for (uint32_t m = 0; m < NUM_ETC1_TO_BC7_M5_SELECTOR_MAPPINGS; m++)4073{4074uint32_t best_lo = 0;4075uint32_t best_hi = 0;4076uint64_t best_err = UINT64_MAX;40774078for (uint32_t hi = 0; hi <= 127; hi++)4079{4080for (uint32_t lo = 0; lo <= 127; lo++)4081{4082uint32_t colors[4];40834084colors[0] = (lo << 1) | (lo >> 6);4085colors[3] = (hi << 1) | (hi >> 6);40864087colors[1] = (colors[0] * (64 - 21) + colors[3] * 21 + 32) / 64;4088colors[2] = (colors[0] * (64 - 43) + colors[3] * 43 + 32) / 64;40894090uint64_t total_err = 0;40914092for (uint32_t s = low_selector; s <= high_selector; s++)4093{4094int err = block_colors[s].g - colors[g_etc1_to_bc7_m5_selector_mappings[m][s]];40954096int err_scale = 1;4097// Special case when the intensity table is 7, low_selector is 0, and high_selector is 3. In this extreme case, it's likely the encoder is trying to strongly favor4098// the low/high selectors which are clamping to either 0 or 255.4099if (((inten == 7) && (low_selector == 0) && (high_selector == 3)) && ((s == 0) || (s == 3)))4100err_scale = 5;41014102total_err += (err * err) * err_scale;4103}41044105if (total_err < best_err)4106{4107best_err = total_err;4108best_lo = lo;4109best_hi = hi;4110}4111}4112}41134114best_err = basisu::minimum<uint32_t>(best_err, 0xFFFF);41154116fprintf(pFile, "{%u,%u,%u},", best_lo, best_hi, (uint32_t)best_err);4117n++;4118if ((n & 31) == 31)4119fprintf(pFile, "\n");4120} // m4121} // sr4122} // g4123} // inten41244125fclose(pFile);4126}41274128static void create_etc1_to_bc7_m5_alpha_conversion_table()4129{4130FILE* pFile = nullptr;4131fopen_s(&pFile, "basisu_transcoder_tables_bc7_m5_alpha.inc", "w");41324133uint32_t n = 0;41344135for (int inten = 0; inten < 8; inten++)4136{4137for (uint32_t g = 0; g < 32; g++)4138{4139color32 block_colors[4];4140decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);41414142for (uint32_t sr = 0; sr < NUM_ETC1_TO_BC7_M5A_SELECTOR_RANGES; sr++)4143{4144const uint32_t low_selector = g_etc1_to_bc7_m5a_selector_ranges[sr].m_low;4145const uint32_t high_selector = g_etc1_to_bc7_m5a_selector_ranges[sr].m_high;41464147uint32_t best_lo = 0;4148uint32_t best_hi = 0;4149uint64_t best_err = UINT64_MAX;4150uint32_t best_output_selectors = 0;41514152for (uint32_t hi = 0; hi <= 255; hi++)4153{4154for (uint32_t lo = 0; lo <= 255; lo++)4155{4156uint32_t colors[4];41574158colors[0] = lo;4159colors[3] = hi;41604161colors[1] = (colors[0] * (64 - 21) + colors[3] * 21 + 32) / 64;4162colors[2] = (colors[0] * (64 - 43) + colors[3] * 43 + 32) / 64;41634164uint64_t total_err = 0;4165uint32_t output_selectors = 0;41664167for (uint32_t s = low_selector; s <= high_selector; s++)4168{4169int best_mapping_err = INT_MAX;4170int best_k = 0;4171for (int k = 0; k < 4; k++)4172{4173int mapping_err = block_colors[s].g - colors[k];4174mapping_err *= mapping_err;41754176// Special case when the intensity table is 7, low_selector is 0, and high_selector is 3. In this extreme case, it's likely the encoder is trying to strongly favor4177// the low/high selectors which are clamping to either 0 or 255.4178if (((inten == 7) && (low_selector == 0) && (high_selector == 3)) && ((s == 0) || (s == 3)))4179mapping_err *= 5;41804181if (mapping_err < best_mapping_err)4182{4183best_mapping_err = mapping_err;4184best_k = k;4185}4186} // k41874188total_err += best_mapping_err;4189output_selectors |= (best_k << (s * 2));4190} // s41914192if (total_err < best_err)4193{4194best_err = total_err;4195best_lo = lo;4196best_hi = hi;4197best_output_selectors = output_selectors;4198}41994200} // lo4201} // hi42024203fprintf(pFile, "{%u,%u,%u},", best_lo, best_hi, best_output_selectors);4204n++;4205if ((n & 31) == 31)4206fprintf(pFile, "\n");42074208} // sr4209} // g4210} // inten42114212fclose(pFile);4213}4214#endif // BASISD_WRITE_NEW_BC7_MODE5_TABLES42154216struct bc7_m5_match_entry4217{4218uint8_t m_hi;4219uint8_t m_lo;4220};42214222static bc7_m5_match_entry g_bc7_m5_equals_1[256] =4223{4224{0,0},{1,0},{3,0},{4,0},{6,0},{7,0},{9,0},{10,0},{12,0},{13,0},{15,0},{16,0},{18,0},{20,0},{21,0},{23,0},4225{24,0},{26,0},{27,0},{29,0},{30,0},{32,0},{33,0},{35,0},{36,0},{38,0},{39,0},{41,0},{42,0},{44,0},{45,0},{47,0},4226{48,0},{50,0},{52,0},{53,0},{55,0},{56,0},{58,0},{59,0},{61,0},{62,0},{64,0},{65,0},{66,0},{68,0},{69,0},{71,0},4227{72,0},{74,0},{75,0},{77,0},{78,0},{80,0},{82,0},{83,0},{85,0},{86,0},{88,0},{89,0},{91,0},{92,0},{94,0},{95,0},4228{97,0},{98,0},{100,0},{101,0},{103,0},{104,0},{106,0},{107,0},{109,0},{110,0},{112,0},{114,0},{115,0},{117,0},{118,0},{120,0},4229{121,0},{123,0},{124,0},{126,0},{127,0},{127,1},{126,2},{126,3},{127,3},{127,4},{126,5},{126,6},{127,6},{127,7},{126,8},{126,9},4230{127,9},{127,10},{126,11},{126,12},{127,12},{127,13},{126,14},{125,15},{127,15},{126,16},{126,17},{127,17},{127,18},{126,19},{126,20},{127,20},4231{127,21},{126,22},{126,23},{127,23},{127,24},{126,25},{126,26},{127,26},{127,27},{126,28},{126,29},{127,29},{127,30},{126,31},{126,32},{127,32},4232{127,33},{126,34},{126,35},{127,35},{127,36},{126,37},{126,38},{127,38},{127,39},{126,40},{126,41},{127,41},{127,42},{126,43},{126,44},{127,44},4233{127,45},{126,46},{125,47},{127,47},{126,48},{126,49},{127,49},{127,50},{126,51},{126,52},{127,52},{127,53},{126,54},{126,55},{127,55},{127,56},4234{126,57},{126,58},{127,58},{127,59},{126,60},{126,61},{127,61},{127,62},{126,63},{125,64},{126,64},{126,65},{127,65},{127,66},{126,67},{126,68},4235{127,68},{127,69},{126,70},{126,71},{127,71},{127,72},{126,73},{126,74},{127,74},{127,75},{126,76},{125,77},{127,77},{126,78},{126,79},{127,79},4236{127,80},{126,81},{126,82},{127,82},{127,83},{126,84},{126,85},{127,85},{127,86},{126,87},{126,88},{127,88},{127,89},{126,90},{126,91},{127,91},4237{127,92},{126,93},{126,94},{127,94},{127,95},{126,96},{126,97},{127,97},{127,98},{126,99},{126,100},{127,100},{127,101},{126,102},{126,103},{127,103},4238{127,104},{126,105},{126,106},{127,106},{127,107},{126,108},{125,109},{127,109},{126,110},{126,111},{127,111},{127,112},{126,113},{126,114},{127,114},{127,115},4239{126,116},{126,117},{127,117},{127,118},{126,119},{126,120},{127,120},{127,121},{126,122},{126,123},{127,123},{127,124},{126,125},{126,126},{127,126},{127,127}4240};42414242static void transcoder_init_bc7_mode5()4243{4244#if 04245// This is a little too much work to do at init time, so precompute it.4246for (int i = 0; i < 256; i++)4247{4248int lowest_e = 256;4249for (int lo = 0; lo < 128; lo++)4250{4251for (int hi = 0; hi < 128; hi++)4252{4253const int lo_e = (lo << 1) | (lo >> 6);4254const int hi_e = (hi << 1) | (hi >> 6);42554256// Selector 14257int v = (lo_e * (64 - 21) + hi_e * 21 + 32) >> 6;4258int e = abs(v - i);42594260if (e < lowest_e)4261{4262g_bc7_m5_equals_1[i].m_hi = static_cast<uint8_t>(hi);4263g_bc7_m5_equals_1[i].m_lo = static_cast<uint8_t>(lo);42644265lowest_e = e;4266}42674268} // hi42694270} // lo42714272printf("{%u,%u},", g_bc7_m5_equals_1[i].m_hi, g_bc7_m5_equals_1[i].m_lo);4273if ((i & 15) == 15) printf("\n");4274}4275#endif42764277for (uint32_t i = 0; i < NUM_ETC1_TO_BC7_M5_SELECTOR_RANGES; i++)4278{4279uint32_t l = g_etc1_to_bc7_m5_selector_ranges[i].m_low;4280uint32_t h = g_etc1_to_bc7_m5_selector_ranges[i].m_high;4281g_etc1_to_bc7_m5_selector_range_index[l][h] = i;4282}42834284for (uint32_t i = 0; i < NUM_ETC1_TO_BC7_M5A_SELECTOR_RANGES; i++)4285{4286uint32_t l = g_etc1_to_bc7_m5a_selector_ranges[i].m_low;4287uint32_t h = g_etc1_to_bc7_m5a_selector_ranges[i].m_high;4288g_etc1_to_bc7_m5a_selector_range_index[l][h] = i;4289}4290}42914292static void convert_etc1s_to_bc7_m5_color(void* pDst, const endpoint* pEndpoints, const selector* pSelector)4293{4294bc7_mode_5* pDst_block = static_cast<bc7_mode_5*>(pDst);42954296// First ensure the block is cleared to all 0's4297static_cast<uint64_t*>(pDst)[0] = 0;4298static_cast<uint64_t*>(pDst)[1] = 0;42994300// Set alpha to 2554301pDst_block->m_lo.m_mode = 1 << 5;4302pDst_block->m_lo.m_a0 = 255;4303pDst_block->m_lo.m_a1_0 = 63;4304pDst_block->m_hi.m_a1_1 = 3;43054306const uint32_t low_selector = pSelector->m_lo_selector;4307const uint32_t high_selector = pSelector->m_hi_selector;43084309const uint32_t base_color_r = pEndpoints->m_color5.r;4310const uint32_t base_color_g = pEndpoints->m_color5.g;4311const uint32_t base_color_b = pEndpoints->m_color5.b;4312const uint32_t inten_table = pEndpoints->m_inten5;43134314if (pSelector->m_num_unique_selectors == 1)4315{4316// Solid color block - use precomputed tables and set selectors to 1.4317uint32_t r, g, b;4318decoder_etc_block::get_block_color5(pEndpoints->m_color5, inten_table, low_selector, r, g, b);43194320pDst_block->m_lo.m_r0 = g_bc7_m5_equals_1[r].m_lo;4321pDst_block->m_lo.m_g0 = g_bc7_m5_equals_1[g].m_lo;4322pDst_block->m_lo.m_b0 = g_bc7_m5_equals_1[b].m_lo;43234324pDst_block->m_lo.m_r1 = g_bc7_m5_equals_1[r].m_hi;4325pDst_block->m_lo.m_g1 = g_bc7_m5_equals_1[g].m_hi;4326pDst_block->m_lo.m_b1 = g_bc7_m5_equals_1[b].m_hi;43274328set_block_bits((uint8_t*)pDst, 0x2aaaaaab, 31, 66);4329return;4330}4331else if (pSelector->m_num_unique_selectors == 2)4332{4333// Only one or two unique selectors, so just switch to block truncation coding (BTC) to avoid quality issues on extreme blocks.4334color32 block_colors[4];43354336decoder_etc_block::get_block_colors5(block_colors, color32(base_color_r, base_color_g, base_color_b, 255), inten_table);43374338const uint32_t r0 = block_colors[low_selector].r;4339const uint32_t g0 = block_colors[low_selector].g;4340const uint32_t b0 = block_colors[low_selector].b;43414342const uint32_t r1 = block_colors[high_selector].r;4343const uint32_t g1 = block_colors[high_selector].g;4344const uint32_t b1 = block_colors[high_selector].b;43454346pDst_block->m_lo.m_r0 = r0 >> 1;4347pDst_block->m_lo.m_g0 = g0 >> 1;4348pDst_block->m_lo.m_b0 = b0 >> 1;43494350pDst_block->m_lo.m_r1 = r1 >> 1;4351pDst_block->m_lo.m_g1 = g1 >> 1;4352pDst_block->m_lo.m_b1 = b1 >> 1;43534354uint32_t output_low_selector = 0, output_bit_offset = 0, output_bits = 0;43554356for (uint32_t y = 0; y < 4; y++)4357{4358for (uint32_t x = 0; x < 4; x++)4359{4360uint32_t s = pSelector->get_selector(x, y);4361uint32_t os = (s == low_selector) ? output_low_selector : (3 ^ output_low_selector);43624363uint32_t num_bits = 2;43644365if ((x | y) == 0)4366{4367if (os & 2)4368{4369pDst_block->m_lo.m_r0 = r1 >> 1;4370pDst_block->m_lo.m_g0 = g1 >> 1;4371pDst_block->m_lo.m_b0 = b1 >> 1;43724373pDst_block->m_lo.m_r1 = r0 >> 1;4374pDst_block->m_lo.m_g1 = g0 >> 1;4375pDst_block->m_lo.m_b1 = b0 >> 1;43764377output_low_selector = 3;4378os = 0;4379}43804381num_bits = 1;4382}43834384output_bits |= (os << output_bit_offset);4385output_bit_offset += num_bits;4386}4387}43884389set_block_bits((uint8_t*)pDst, output_bits, 31, 66);4390return;4391}43924393const uint32_t selector_range_table = g_etc1_to_bc7_m5_selector_range_index[low_selector][high_selector];43944395//[32][8][RANGES][MAPPING]4396const etc1_to_bc7_m5_solution* pTable_r = &g_etc1_to_bc7_m5_color[(inten_table * 32 + base_color_r) * (NUM_ETC1_TO_BC7_M5_SELECTOR_RANGES * NUM_ETC1_TO_BC7_M5_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_BC7_M5_SELECTOR_MAPPINGS];4397const etc1_to_bc7_m5_solution* pTable_g = &g_etc1_to_bc7_m5_color[(inten_table * 32 + base_color_g) * (NUM_ETC1_TO_BC7_M5_SELECTOR_RANGES * NUM_ETC1_TO_BC7_M5_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_BC7_M5_SELECTOR_MAPPINGS];4398const etc1_to_bc7_m5_solution* pTable_b = &g_etc1_to_bc7_m5_color[(inten_table * 32 + base_color_b) * (NUM_ETC1_TO_BC7_M5_SELECTOR_RANGES * NUM_ETC1_TO_BC7_M5_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_BC7_M5_SELECTOR_MAPPINGS];43994400uint32_t best_err = UINT_MAX;4401uint32_t best_mapping = 0;44024403assert(NUM_ETC1_TO_BC7_M5_SELECTOR_MAPPINGS == 10);4404#define DO_ITER(m) { uint32_t total_err = pTable_r[m].m_err + pTable_g[m].m_err + pTable_b[m].m_err; if (total_err < best_err) { best_err = total_err; best_mapping = m; } }4405DO_ITER(0); DO_ITER(1); DO_ITER(2); DO_ITER(3); DO_ITER(4);4406DO_ITER(5); DO_ITER(6); DO_ITER(7); DO_ITER(8); DO_ITER(9);4407#undef DO_ITER44084409const uint8_t* pSelectors_xlat = &g_etc1_to_bc7_m5_selector_mappings[best_mapping][0];44104411uint32_t s_inv = 0;4412if (pSelectors_xlat[pSelector->get_selector(0, 0)] & 2)4413{4414pDst_block->m_lo.m_r0 = pTable_r[best_mapping].m_hi;4415pDst_block->m_lo.m_g0 = pTable_g[best_mapping].m_hi;4416pDst_block->m_lo.m_b0 = pTable_b[best_mapping].m_hi;44174418pDst_block->m_lo.m_r1 = pTable_r[best_mapping].m_lo;4419pDst_block->m_lo.m_g1 = pTable_g[best_mapping].m_lo;4420pDst_block->m_lo.m_b1 = pTable_b[best_mapping].m_lo;44214422s_inv = 3;4423}4424else4425{4426pDst_block->m_lo.m_r0 = pTable_r[best_mapping].m_lo;4427pDst_block->m_lo.m_g0 = pTable_g[best_mapping].m_lo;4428pDst_block->m_lo.m_b0 = pTable_b[best_mapping].m_lo;44294430pDst_block->m_lo.m_r1 = pTable_r[best_mapping].m_hi;4431pDst_block->m_lo.m_g1 = pTable_g[best_mapping].m_hi;4432pDst_block->m_lo.m_b1 = pTable_b[best_mapping].m_hi;4433}44344435uint32_t output_bits = 0, output_bit_ofs = 0;44364437for (uint32_t y = 0; y < 4; y++)4438{4439for (uint32_t x = 0; x < 4; x++)4440{4441const uint32_t s = pSelector->get_selector(x, y);44424443const uint32_t os = pSelectors_xlat[s] ^ s_inv;44444445output_bits |= (os << output_bit_ofs);44464447output_bit_ofs += (((x | y) == 0) ? 1 : 2);4448}4449}44504451set_block_bits((uint8_t*)pDst, output_bits, 31, 66);4452}44534454static void convert_etc1s_to_bc7_m5_alpha(void* pDst, const endpoint* pEndpoints, const selector* pSelector)4455{4456bc7_mode_5* pDst_block = static_cast<bc7_mode_5*>(pDst);44574458const uint32_t low_selector = pSelector->m_lo_selector;4459const uint32_t high_selector = pSelector->m_hi_selector;44604461const uint32_t base_color_r = pEndpoints->m_color5.r;4462const uint32_t inten_table = pEndpoints->m_inten5;44634464if (pSelector->m_num_unique_selectors == 1)4465{4466uint32_t r;4467decoder_etc_block::get_block_color5_r(pEndpoints->m_color5, inten_table, low_selector, r);44684469pDst_block->m_lo.m_a0 = r;4470pDst_block->m_lo.m_a1_0 = r & 63;4471pDst_block->m_hi.m_a1_1 = r >> 6;44724473return;4474}4475else if (pSelector->m_num_unique_selectors == 2)4476{4477// Only one or two unique selectors, so just switch to block truncation coding (BTC) to avoid quality issues on extreme blocks.4478int block_colors[4];44794480decoder_etc_block::get_block_colors5_g(block_colors, pEndpoints->m_color5, inten_table);44814482pDst_block->m_lo.m_a0 = block_colors[low_selector];4483pDst_block->m_lo.m_a1_0 = block_colors[high_selector] & 63;4484pDst_block->m_hi.m_a1_1 = block_colors[high_selector] >> 6;44854486uint32_t output_low_selector = 0, output_bit_offset = 0, output_bits = 0;44874488for (uint32_t y = 0; y < 4; y++)4489{4490for (uint32_t x = 0; x < 4; x++)4491{4492const uint32_t s = pSelector->get_selector(x, y);4493uint32_t os = (s == low_selector) ? output_low_selector : (3 ^ output_low_selector);44944495uint32_t num_bits = 2;44964497if ((x | y) == 0)4498{4499if (os & 2)4500{4501pDst_block->m_lo.m_a0 = block_colors[high_selector];4502pDst_block->m_lo.m_a1_0 = block_colors[low_selector] & 63;4503pDst_block->m_hi.m_a1_1 = block_colors[low_selector] >> 6;45044505output_low_selector = 3;4506os = 0;4507}45084509num_bits = 1;4510}45114512output_bits |= (os << output_bit_offset);4513output_bit_offset += num_bits;4514}4515}45164517set_block_bits((uint8_t*)pDst, output_bits, 31, 97);4518return;4519}45204521const uint32_t selector_range_table = g_etc1_to_bc7_m5a_selector_range_index[low_selector][high_selector];45224523const etc1_g_to_bc7_m5a_conversion* pTable = &g_etc1_g_to_bc7_m5a[inten_table * (32 * NUM_ETC1_TO_BC7_M5A_SELECTOR_RANGES) + base_color_r * NUM_ETC1_TO_BC7_M5A_SELECTOR_RANGES + selector_range_table];45244525pDst_block->m_lo.m_a0 = pTable->m_lo;4526pDst_block->m_lo.m_a1_0 = pTable->m_hi & 63;4527pDst_block->m_hi.m_a1_1 = pTable->m_hi >> 6;45284529uint32_t output_bit_offset = 0, output_bits = 0, selector_trans = pTable->m_trans;45304531for (uint32_t y = 0; y < 4; y++)4532{4533for (uint32_t x = 0; x < 4; x++)4534{4535const uint32_t s = pSelector->get_selector(x, y);4536uint32_t os = (selector_trans >> (s * 2)) & 3;45374538uint32_t num_bits = 2;45394540if ((x | y) == 0)4541{4542if (os & 2)4543{4544pDst_block->m_lo.m_a0 = pTable->m_hi;4545pDst_block->m_lo.m_a1_0 = pTable->m_lo & 63;4546pDst_block->m_hi.m_a1_1 = pTable->m_lo >> 6;45474548selector_trans ^= 0xFF;4549os ^= 3;4550}45514552num_bits = 1;4553}45544555output_bits |= (os << output_bit_offset);4556output_bit_offset += num_bits;4557}4558}45594560set_block_bits((uint8_t*)pDst, output_bits, 31, 97);4561}45624563static inline vec3F rgb_to_ycocg(const vec3F& rgb)4564{4565return vec3F(rgb.dot(vec3F(0.25f, 0.5f, 0.25f)), rgb.dot(vec3F(0.5f, 0.0f, -0.5f)), rgb.dot(vec3F(-0.25f, 0.5f, -0.25f)));4566}45674568static inline vec2F rgb_to_cocg(const vec3F& rgb)4569{4570return vec2F(rgb.dot(vec3F(0.5f, 0.0f, -0.5f)), rgb.dot(vec3F(-0.25f, 0.5f, -0.25f)));4571}45724573static inline vec3F ycocg_to_rgb(const vec3F& ycocg)4574{4575return vec3F(ycocg.dot(vec3F(1.0f, 1.0f, -1.0f)), ycocg.dot(vec3F(1.0f, 0.0f, 1.0f)), ycocg.dot(vec3F(1.0f, -1.0f, -1.0f)));4576}45774578static inline vec3F color32_to_vec3F(const color32& c)4579{4580return vec3F(c.r, c.g, c.b);4581}45824583static inline vec3F color5_to_ycocg(const endpoint& e)4584{4585const int r = (e.m_color5[0] << 3) | (e.m_color5[0] >> 2);4586const int g = (e.m_color5[1] << 3) | (e.m_color5[1] >> 2);4587const int b = (e.m_color5[2] << 3) | (e.m_color5[2] >> 2);4588return rgb_to_ycocg(vec3F((float)r, (float)g, (float)b));4589}45904591static inline vec2F color5_to_cocg(const endpoint& e)4592{4593const int r = (e.m_color5[0] << 3) | (e.m_color5[0] >> 2);4594const int g = (e.m_color5[1] << 3) | (e.m_color5[1] >> 2);4595const int b = (e.m_color5[2] << 3) | (e.m_color5[2] >> 2);4596return rgb_to_cocg(vec3F((float)r, (float)g, (float)b));4597}45984599static inline uint32_t bc7_7_to_8(uint32_t v)4600{4601assert(v < 128);4602return (v << 1) | (v >> 6);4603}46044605static inline uint32_t bc7_interp2(uint32_t l, uint32_t h, uint32_t w)4606{4607assert(w < 4);4608return (l * (64 - basist::g_bc7_weights2[w]) + h * basist::g_bc7_weights2[w] + 32) >> 6;4609}46104611static inline vec2F get_endpoint_cocg_clamped(int bx, int by, const basisu::vector2D<uint16_t>& decoded_endpoints, const endpoint* pEndpoints)4612{4613const uint32_t endpoint_index = decoded_endpoints.at_clamped(bx, by);4614return color5_to_cocg(pEndpoints[endpoint_index]);4615}46164617static void chroma_filter_bc7_mode5(const basisu::vector2D<uint16_t>& decoded_endpoints, void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t output_row_pitch_in_blocks_or_pixels, const endpoint *pEndpoints)4618{4619const bool hq_bc7_mode_5_encoder_mode = false;46204621const int CHROMA_THRESH = 10;46224623uint32_t total_filtered_blocks = 0;4624BASISU_NOTE_UNUSED(total_filtered_blocks);46254626for (int by = 0; by < (int)num_blocks_y; by++)4627{4628for (int bx = 0; bx < (int)num_blocks_x; bx++)4629{4630vec2F center_cocg(color5_to_cocg(pEndpoints[decoded_endpoints(bx, by)]));46314632//bool filter_flag = false;4633for (int dy = -1; dy <= 1; dy++)4634{4635const int oy = by + dy;4636if ((oy < 0) || (oy >= (int)num_blocks_y))4637continue;46384639for (int dx = -1; dx <= 1; dx++)4640{4641if ((dx | dy) == 0)4642continue;46434644const int ox = bx + dx;4645if ((ox < 0) || (ox >= (int)num_blocks_x))4646continue;46474648vec2F nearby_cocg(color5_to_cocg(pEndpoints[decoded_endpoints(ox, oy)]));46494650float delta_co = fabsf(nearby_cocg[0] - center_cocg[0]);4651float delta_cg = fabsf(nearby_cocg[1] - center_cocg[1]);46524653if ((delta_co > CHROMA_THRESH) || (delta_cg > CHROMA_THRESH))4654{4655//filter_flag = true;4656goto do_filter;4657}46584659} // dx4660} // dy46614662continue;46634664do_filter:;46654666total_filtered_blocks++;46674668bc7_mode_5* pDst_block = (bc7_mode_5*)(static_cast<uint8_t*>(pDst_blocks) + (bx + by * output_row_pitch_in_blocks_or_pixels) * sizeof(bc7_mode_5));46694670//memset(pDst_block, 0x80, 16);46714672int lr = bc7_7_to_8(pDst_block->m_lo.m_r0);4673int lg = bc7_7_to_8(pDst_block->m_lo.m_g0);4674int lb = bc7_7_to_8(pDst_block->m_lo.m_b0);46754676int hr = bc7_7_to_8(pDst_block->m_lo.m_r1);4677int hg = bc7_7_to_8(pDst_block->m_lo.m_g1);4678int hb = bc7_7_to_8(pDst_block->m_lo.m_b1);46794680float y_vals[4];4681for (uint32_t i = 0; i < 4; i++)4682{4683int cr = bc7_interp2(lr, hr, i);4684int cg = bc7_interp2(lg, hg, i);4685int cb = bc7_interp2(lb, hb, i);4686y_vals[i] = (float)cr * .25f + (float)cg * .5f + (float)cb * .25f;4687} // i46884689uint64_t sel_bits = pDst_block->m_hi_bits >> 2;46904691float block_y_vals[16]; // [y][x]4692float y_sum = 0.0f, y_sum_sq = 0.0f;46934694for (uint32_t i = 0; i < 16; i++)4695{4696uint32_t sel = sel_bits & (i ? 3 : 1);4697sel_bits >>= (i ? 2 : 1);4698float y = y_vals[sel];4699block_y_vals[i] = y;4700y_sum += y;4701y_sum_sq += y * y;47024703} // i47044705const float S = 1.0f / 16.0f;4706float y_var = (y_sum_sq * S) - basisu::squaref(y_sum * S);47074708// Don't bother if the block is too smooth.4709const float Y_VAR_SKIP_THRESH = 3.0f;4710if (y_var < Y_VAR_SKIP_THRESH)4711continue;47124713color32 block_to_pack[16];47144715for (int bpy = 0; bpy < 4; bpy++)4716{4717const int uby = by + ((bpy - 2) >> 2);47184719for (int bpx = 0; bpx < 4; bpx++)4720{4721const float fx = ((float)((bpx + 2) & 3) + .5f) * (1.0f / 4.0f);4722const float fy = ((float)((bpy + 2) & 3) + .5f) * (1.0f / 4.0f);47234724const int ubx = bx + ((bpx - 2) >> 2);47254726vec2F a(get_endpoint_cocg_clamped(ubx, uby, decoded_endpoints, pEndpoints));4727vec2F b(get_endpoint_cocg_clamped(ubx + 1, uby, decoded_endpoints, pEndpoints));4728vec2F c(get_endpoint_cocg_clamped(ubx, uby + 1, decoded_endpoints, pEndpoints));4729vec2F d(get_endpoint_cocg_clamped(ubx + 1, uby + 1, decoded_endpoints, pEndpoints));47304731assert((fx >= 0) && (fx <= 1.0f) && (fy >= 0) && (fy <= 1.0f));47324733// TODO: Could merge this into 4 muls on each corner by weights4734vec2F ab = vec2F::lerp(a, b, fx);4735vec2F cd = vec2F::lerp(c, d, fx);4736vec2F f = vec2F::lerp(ab, cd, fy);47374738vec3F final_ycocg(block_y_vals[bpx + bpy * 4], f[0], f[1]);47394740vec3F final_conv(ycocg_to_rgb(final_ycocg));4741final_conv.clamp(0.0f, 255.0f);47424743block_to_pack[bpx + bpy * 4].set_noclamp_rgba((int)(.5f + final_conv[0]), (int)(.5f + final_conv[1]), (int)(.5f + final_conv[2]), 255);47444745} // x4746} // y47474748bc7_mode_5_encoder::encode_bc7_mode_5_block(pDst_block, block_to_pack, hq_bc7_mode_5_encoder_mode);47494750} // bx4751} // by47524753//basisu::fmt_printf("Chroma thresh: {}, Total blocks to filter: {} out of {} {}\n", CHROMA_THRESH, total_filtered_blocks, num_blocks_x * num_blocks_y, (float)total_filtered_blocks * 100.0f / (num_blocks_x * num_blocks_y));4754}4755#endif // BASISD_SUPPORT_BC7_MODE547564757#if BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_UASTC4758static const uint8_t g_etc2_eac_a8_sel4[6] = { 0x92, 0x49, 0x24, 0x92, 0x49, 0x24 };4759#endif47604761#if BASISD_SUPPORT_ETC2_EAC_A84762static void convert_etc1s_to_etc2_eac_a8(eac_block* pDst_block, const endpoint* pEndpoints, const selector* pSelector)4763{4764const uint32_t low_selector = pSelector->m_lo_selector;4765const uint32_t high_selector = pSelector->m_hi_selector;47664767const color32& base_color = pEndpoints->m_color5;4768const uint32_t inten_table = pEndpoints->m_inten5;47694770if (low_selector == high_selector)4771{4772uint32_t r;4773decoder_etc_block::get_block_color5_r(base_color, inten_table, low_selector, r);47744775// Constant alpha block4776// Select table 13, use selector 4 (0), set multiplier to 1 and base color g4777pDst_block->m_base = r;4778pDst_block->m_table = 13;4779pDst_block->m_multiplier = 1;47804781// selectors are all 4's4782memcpy(pDst_block->m_selectors, g_etc2_eac_a8_sel4, sizeof(g_etc2_eac_a8_sel4));47834784return;4785}47864787uint32_t selector_range_table = 0;4788for (selector_range_table = 0; selector_range_table < NUM_ETC2_EAC_SELECTOR_RANGES; selector_range_table++)4789if ((low_selector == s_etc2_eac_selector_ranges[selector_range_table].m_low) && (high_selector == s_etc2_eac_selector_ranges[selector_range_table].m_high))4790break;4791if (selector_range_table >= NUM_ETC2_EAC_SELECTOR_RANGES)4792selector_range_table = 0;47934794const etc1_g_to_eac_conversion* pTable_entry = &s_etc1_g_to_etc2_a8[base_color.r + inten_table * 32][selector_range_table];47954796pDst_block->m_base = pTable_entry->m_base;4797pDst_block->m_table = pTable_entry->m_table_mul >> 4;4798pDst_block->m_multiplier = pTable_entry->m_table_mul & 15;47994800uint64_t selector_bits = 0;48014802for (uint32_t y = 0; y < 4; y++)4803{4804for (uint32_t x = 0; x < 4; x++)4805{4806uint32_t s = pSelector->get_selector(x, y);48074808uint32_t ds = (pTable_entry->m_trans >> (s * 3)) & 7;48094810const uint32_t dst_ofs = 45 - (y + x * 4) * 3;4811selector_bits |= (static_cast<uint64_t>(ds) << dst_ofs);4812}4813}48144815pDst_block->set_selector_bits(selector_bits);4816}4817#endif // BASISD_SUPPORT_ETC2_EAC_A848184819#if BASISD_SUPPORT_ETC2_EAC_RG114820static const etc1_g_to_eac_conversion s_etc1_g_to_etc2_r11[32 * 8][NUM_ETC2_EAC_SELECTOR_RANGES] =4821{4822{{0,1,3328},{0,1,3328},{0,16,457},{0,16,456}},4823{{0,226,3936},{0,226,3936},{0,17,424},{8,0,472}},4824{{6,178,4012},{6,178,4008},{0,146,501},{16,0,472}},4825{{14,178,4012},{14,178,4008},{8,146,501},{24,0,472}},4826{{23,178,4012},{23,178,4008},{17,146,501},{33,0,472}},4827{{31,178,4012},{31,178,4008},{25,146,501},{41,0,472}},4828{{39,178,4012},{39,178,4008},{33,146,501},{49,0,472}},4829{{47,178,4012},{47,178,4008},{41,146,501},{27,228,496}},4830{{56,178,4012},{56,178,4008},{50,146,501},{36,228,496}},4831{{64,178,4012},{64,178,4008},{58,146,501},{44,228,496}},4832{{72,178,4012},{72,178,4008},{66,146,501},{52,228,496}},4833{{80,178,4012},{80,178,4008},{74,146,501},{60,228,496}},4834{{89,178,4012},{89,178,4008},{83,146,501},{69,228,496}},4835{{97,178,4012},{97,178,4008},{91,146,501},{77,228,496}},4836{{105,178,4012},{105,178,4008},{99,146,501},{85,228,496}},4837{{113,178,4012},{113,178,4008},{107,146,501},{93,228,496}},4838{{122,178,4012},{122,178,4008},{116,146,501},{102,228,496}},4839{{130,178,4012},{130,178,4008},{124,146,501},{110,228,496}},4840{{138,178,4012},{138,178,4008},{132,146,501},{118,228,496}},4841{{146,178,4012},{146,178,4008},{140,146,501},{126,228,496}},4842{{155,178,4012},{155,178,4008},{149,146,501},{135,228,496}},4843{{163,178,4012},{163,178,4008},{157,146,501},{143,228,496}},4844{{171,178,4012},{171,178,4008},{165,146,501},{151,228,496}},4845{{179,178,4012},{179,178,4008},{173,146,501},{159,228,496}},4846{{188,178,4012},{188,178,4008},{182,146,501},{168,228,496}},4847{{196,178,4012},{196,178,4008},{190,146,501},{176,228,496}},4848{{204,178,4012},{204,178,4008},{198,146,501},{184,228,496}},4849{{212,178,4012},{212,178,4008},{206,146,501},{192,228,496}},4850{{221,178,4012},{221,178,4008},{215,146,501},{201,228,496}},4851{{229,178,4012},{229,178,4008},{223,146,501},{209,228,496}},4852{{235,66,4012},{221,100,4008},{231,146,501},{217,228,496}},4853{{211,102,4085},{254,32,4040},{211,102,501},{254,32,456}},4854{{0,2,3328},{0,2,3328},{0,1,320},{0,1,320}},4855{{7,162,3905},{7,162,3904},{0,17,480},{0,17,480}},4856{{15,162,3906},{15,162,3904},{1,117,352},{1,117,352}},4857{{23,162,3906},{23,162,3904},{5,34,500},{4,53,424}},4858{{32,162,3906},{32,162,3904},{14,34,500},{3,69,424}},4859{{40,162,3906},{40,162,3904},{22,34,500},{1,133,496}},4860{{48,162,3906},{48,162,3904},{30,34,500},{4,85,496}},4861{{56,162,3906},{56,162,3904},{38,34,500},{12,85,496}},4862{{65,162,3906},{65,162,3904},{47,34,500},{1,106,424}},4863{{73,162,3906},{73,162,3904},{55,34,500},{9,106,424}},4864{{81,162,3906},{81,162,3904},{63,34,500},{7,234,496}},4865{{89,162,3906},{89,162,3904},{71,34,500},{15,234,496}},4866{{98,162,3906},{98,162,3904},{80,34,500},{24,234,496}},4867{{106,162,3906},{106,162,3904},{88,34,500},{32,234,496}},4868{{114,162,3906},{114,162,3904},{96,34,500},{40,234,496}},4869{{122,162,3906},{122,162,3904},{104,34,500},{48,234,496}},4870{{131,162,3906},{131,162,3904},{113,34,500},{57,234,496}},4871{{139,162,3906},{139,162,3904},{121,34,500},{65,234,496}},4872{{147,162,3906},{147,162,3904},{129,34,500},{73,234,496}},4873{{155,162,3906},{155,162,3904},{137,34,500},{81,234,496}},4874{{164,162,3906},{164,162,3904},{146,34,500},{90,234,496}},4875{{172,162,3906},{172,162,3904},{154,34,500},{98,234,496}},4876{{180,162,3906},{180,162,3904},{162,34,500},{106,234,496}},4877{{188,162,3906},{188,162,3904},{170,34,500},{114,234,496}},4878{{197,162,3906},{197,162,3904},{179,34,500},{123,234,496}},4879{{205,162,3906},{205,162,3904},{187,34,500},{131,234,496}},4880{{213,162,3906},{213,162,3904},{195,34,500},{139,234,496}},4881{{221,162,3906},{221,162,3904},{203,34,500},{147,234,496}},4882{{230,162,3906},{230,162,3904},{212,34,500},{156,234,496}},4883{{238,162,3906},{174,106,4008},{220,34,500},{164,234,496}},4884{{240,178,4001},{182,106,4008},{228,34,500},{172,234,496}},4885{{166,108,4085},{115,31,4080},{166,108,501},{115,31,496}},4886{{1,68,3328},{1,68,3328},{0,1,384},{0,1,384}},4887{{1,51,3968},{1,51,3968},{0,2,384},{0,2,384}},4888{{21,18,3851},{21,18,3848},{1,50,488},{1,50,488}},4889{{26,195,3851},{29,18,3848},{0,67,488},{0,67,488}},4890{{35,195,3851},{38,18,3848},{12,115,488},{0,3,496}},4891{{43,195,3851},{46,18,3848},{20,115,488},{2,6,424}},4892{{51,195,3851},{54,18,3848},{36,66,482},{4,22,424}},4893{{59,195,3851},{62,18,3848},{44,66,482},{3,73,424}},4894{{68,195,3851},{71,18,3848},{53,66,482},{3,22,496}},4895{{76,195,3851},{79,18,3848},{61,66,482},{2,137,496}},4896{{84,195,3851},{87,18,3848},{69,66,482},{1,89,496}},4897{{92,195,3851},{95,18,3848},{77,66,482},{9,89,496}},4898{{101,195,3851},{104,18,3848},{86,66,482},{18,89,496}},4899{{109,195,3851},{112,18,3848},{94,66,482},{26,89,496}},4900{{117,195,3851},{120,18,3848},{102,66,482},{34,89,496}},4901{{125,195,3851},{128,18,3848},{110,66,482},{42,89,496}},4902{{134,195,3851},{137,18,3848},{119,66,482},{51,89,496}},4903{{141,195,3907},{145,18,3848},{127,66,482},{59,89,496}},4904{{149,195,3907},{153,18,3848},{135,66,482},{67,89,496}},4905{{157,195,3907},{161,18,3848},{143,66,482},{75,89,496}},4906{{166,195,3907},{170,18,3848},{152,66,482},{84,89,496}},4907{{174,195,3907},{178,18,3848},{160,66,482},{92,89,496}},4908{{182,195,3907},{186,18,3848},{168,66,482},{100,89,496}},4909{{190,195,3907},{194,18,3848},{176,66,482},{108,89,496}},4910{{199,195,3907},{203,18,3848},{185,66,482},{117,89,496}},4911{{207,195,3907},{211,18,3848},{193,66,482},{125,89,496}},4912{{215,195,3907},{219,18,3848},{201,66,482},{133,89,496}},4913{{223,195,3907},{227,18,3848},{209,66,482},{141,89,496}},4914{{232,195,3907},{168,89,4008},{218,66,482},{150,89,496}},4915{{236,18,3907},{176,89,4008},{226,66,482},{158,89,496}},4916{{158,90,4085},{103,31,4080},{158,90,501},{103,31,496}},4917{{166,90,4085},{111,31,4080},{166,90,501},{111,31,496}},4918{{0,70,3328},{0,70,3328},{0,17,448},{0,17,448}},4919{{0,117,3904},{0,117,3904},{0,35,384},{0,35,384}},4920{{13,165,3905},{13,165,3904},{2,211,480},{2,211,480}},4921{{21,165,3906},{21,165,3904},{1,51,488},{1,51,488}},4922{{30,165,3906},{30,165,3904},{7,61,352},{7,61,352}},4923{{38,165,3906},{38,165,3904},{2,125,352},{2,125,352}},4924{{46,165,3906},{46,165,3904},{1,37,500},{10,125,352}},4925{{54,165,3906},{54,165,3904},{9,37,500},{5,61,424}},4926{{63,165,3906},{63,165,3904},{18,37,500},{1,189,424}},4927{{71,165,3906},{71,165,3904},{26,37,500},{9,189,424}},4928{{79,165,3906},{79,165,3904},{34,37,500},{4,77,424}},4929{{87,165,3906},{87,165,3904},{42,37,500},{12,77,424}},4930{{96,165,3906},{96,165,3904},{51,37,500},{8,93,424}},4931{{104,165,3906},{104,165,3904},{59,37,500},{3,141,496}},4932{{112,165,3906},{112,165,3904},{68,37,500},{11,141,496}},4933{{120,165,3906},{120,165,3904},{76,37,500},{6,93,496}},4934{{129,165,3906},{129,165,3904},{85,37,500},{15,93,496}},4935{{70,254,4012},{137,165,3904},{93,37,500},{23,93,496}},4936{{145,165,3906},{145,165,3904},{101,37,500},{31,93,496}},4937{{86,254,4012},{153,165,3904},{109,37,500},{39,93,496}},4938{{163,165,3906},{162,165,3904},{118,37,500},{48,93,496}},4939{{171,165,3906},{170,165,3904},{126,37,500},{56,93,496}},4940{{179,165,3906},{178,165,3904},{134,37,500},{64,93,496}},4941{{187,165,3906},{187,165,3904},{142,37,500},{72,93,496}},4942{{196,165,3906},{196,165,3904},{151,37,500},{81,93,496}},4943{{204,165,3906},{204,165,3904},{159,37,500},{89,93,496}},4944{{212,165,3906},{136,77,4008},{167,37,500},{97,93,496}},4945{{220,165,3906},{131,93,4008},{175,37,500},{105,93,496}},4946{{214,181,4001},{140,93,4008},{184,37,500},{114,93,496}},4947{{222,181,4001},{148,93,4008},{192,37,500},{122,93,496}},4948{{115,95,4085},{99,31,4080},{115,95,501},{99,31,496}},4949{{123,95,4085},{107,31,4080},{123,95,501},{107,31,496}},4950{{0,102,3840},{0,102,3840},{0,18,384},{0,18,384}},4951{{5,167,3904},{5,167,3904},{0,13,256},{0,13,256}},4952{{4,54,3968},{4,54,3968},{1,67,448},{1,67,448}},4953{{30,198,3850},{30,198,3848},{0,3,480},{0,3,480}},4954{{39,198,3850},{39,198,3848},{3,52,488},{3,52,488}},4955{{47,198,3851},{47,198,3848},{3,4,488},{3,4,488}},4956{{55,198,3851},{55,198,3848},{1,70,488},{1,70,488}},4957{{53,167,3906},{63,198,3848},{3,22,488},{3,22,488}},4958{{62,167,3906},{72,198,3848},{24,118,488},{0,6,496}},4959{{70,167,3906},{80,198,3848},{32,118,488},{2,89,488}},4960{{78,167,3906},{88,198,3848},{40,118,488},{1,73,496}},4961{{86,167,3906},{96,198,3848},{48,118,488},{0,28,424}},4962{{95,167,3906},{105,198,3848},{57,118,488},{9,28,424}},4963{{103,167,3906},{113,198,3848},{65,118,488},{5,108,496}},4964{{111,167,3906},{121,198,3848},{73,118,488},{13,108,496}},4965{{119,167,3906},{129,198,3848},{81,118,488},{21,108,496}},4966{{128,167,3906},{138,198,3848},{90,118,488},{6,28,496}},4967{{136,167,3906},{146,198,3848},{98,118,488},{14,28,496}},4968{{145,167,3906},{154,198,3848},{106,118,488},{22,28,496}},4969{{153,167,3906},{162,198,3848},{114,118,488},{30,28,496}},4970{{162,167,3906},{171,198,3848},{123,118,488},{39,28,496}},4971{{170,167,3906},{179,198,3848},{131,118,488},{47,28,496}},4972{{178,167,3906},{187,198,3848},{139,118,488},{55,28,496}},4973{{186,167,3906},{195,198,3848},{147,118,488},{63,28,496}},4974{{194,167,3906},{120,12,4008},{156,118,488},{72,28,496}},4975{{206,198,3907},{116,28,4008},{164,118,488},{80,28,496}},4976{{214,198,3907},{124,28,4008},{172,118,488},{88,28,496}},4977{{222,198,3395},{132,28,4008},{180,118,488},{96,28,496}},4978{{207,134,4001},{141,28,4008},{189,118,488},{105,28,496}},4979{{95,30,4085},{86,31,4080},{95,30,501},{86,31,496}},4980{{103,30,4085},{94,31,4080},{103,30,501},{94,31,496}},4981{{111,30,4085},{102,31,4080},{111,30,501},{102,31,496}},4982{{0,104,3840},{0,104,3840},{0,18,448},{0,18,448}},4983{{4,39,3904},{4,39,3904},{0,4,384},{0,4,384}},4984{{0,56,3968},{0,56,3968},{0,84,448},{0,84,448}},4985{{6,110,3328},{6,110,3328},{0,20,448},{0,20,448}},4986{{41,200,3850},{41,200,3848},{1,4,480},{1,4,480}},4987{{49,200,3850},{49,200,3848},{1,8,416},{1,8,416}},4988{{57,200,3851},{57,200,3848},{1,38,488},{1,38,488}},4989{{65,200,3851},{65,200,3848},{1,120,488},{1,120,488}},4990{{74,200,3851},{74,200,3848},{2,72,488},{2,72,488}},4991{{68,6,3907},{82,200,3848},{2,24,488},{2,24,488}},4992{{77,6,3907},{90,200,3848},{26,120,488},{10,24,488}},4993{{97,63,3330},{98,200,3848},{34,120,488},{2,8,496}},4994{{106,63,3330},{107,200,3848},{43,120,488},{3,92,488}},4995{{114,63,3330},{115,200,3848},{51,120,488},{11,92,488}},4996{{122,63,3330},{123,200,3848},{59,120,488},{7,76,496}},4997{{130,63,3330},{131,200,3848},{67,120,488},{15,76,496}},4998{{139,63,3330},{140,200,3848},{76,120,488},{24,76,496}},4999{{147,63,3330},{148,200,3848},{84,120,488},{32,76,496}},5000{{155,63,3330},{156,200,3848},{92,120,488},{40,76,496}},5001{{164,63,3330},{164,200,3848},{100,120,488},{48,76,496}},5002{{173,63,3330},{173,200,3848},{109,120,488},{57,76,496}},5003{{184,6,3851},{181,200,3848},{117,120,488},{65,76,496}},5004{{192,6,3851},{133,28,3936},{125,120,488},{73,76,496}},5005{{189,200,3907},{141,28,3936},{133,120,488},{81,76,496}},5006{{198,200,3907},{138,108,4000},{142,120,488},{90,76,496}},5007{{206,200,3907},{146,108,4000},{150,120,488},{98,76,496}},5008{{214,200,3395},{154,108,4000},{158,120,488},{106,76,496}},5009{{190,136,4001},{162,108,4000},{166,120,488},{114,76,496}},5010{{123,30,4076},{87,15,4080},{123,30,492},{87,15,496}},5011{{117,110,4084},{80,31,4080},{117,110,500},{80,31,496}},5012{{125,110,4084},{88,31,4080},{125,110,500},{88,31,496}},5013{{133,110,4084},{96,31,4080},{133,110,500},{96,31,496}},5014{{9,56,3904},{9,56,3904},{0,67,448},{0,67,448}},5015{{1,8,3904},{1,8,3904},{1,84,448},{1,84,448}},5016{{1,124,3904},{1,124,3904},{0,39,384},{0,39,384}},5017{{9,124,3904},{9,124,3904},{1,4,448},{1,4,448}},5018{{6,76,3904},{6,76,3904},{0,70,448},{0,70,448}},5019{{62,6,3859},{62,6,3856},{2,38,480},{2,38,480}},5020{{70,6,3859},{70,6,3856},{5,43,416},{5,43,416}},5021{{78,6,3859},{78,6,3856},{2,11,416},{2,11,416}},5022{{87,6,3859},{87,6,3856},{0,171,488},{0,171,488}},5023{{67,8,3906},{95,6,3856},{8,171,488},{8,171,488}},5024{{75,8,3907},{103,6,3856},{5,123,488},{5,123,488}},5025{{83,8,3907},{111,6,3856},{2,75,488},{2,75,488}},5026{{92,8,3907},{120,6,3856},{0,27,488},{0,27,488}},5027{{100,8,3907},{128,6,3856},{8,27,488},{8,27,488}},5028{{120,106,3843},{136,6,3856},{99,6,387},{16,27,488}},5029{{128,106,3843},{144,6,3856},{107,6,387},{2,11,496}},5030{{137,106,3843},{153,6,3856},{117,6,387},{11,11,496}},5031{{145,106,3843},{161,6,3856},{125,6,387},{19,11,496}},5032{{163,8,3851},{137,43,3904},{133,6,387},{27,11,496}},5033{{171,8,3851},{145,43,3904},{141,6,387},{35,11,496}},5034{{180,8,3851},{110,11,4000},{150,6,387},{44,11,496}},5035{{188,8,3851},{118,11,4000},{158,6,387},{52,11,496}},5036{{172,72,3907},{126,11,4000},{166,6,387},{60,11,496}},5037{{174,6,3971},{134,11,4000},{174,6,387},{68,11,496}},5038{{183,6,3971},{143,11,4000},{183,6,387},{77,11,496}},5039{{191,6,3971},{151,11,4000},{191,6,387},{85,11,496}},5040{{199,6,3971},{159,11,4000},{199,6,387},{93,11,496}},5041{{92,12,4084},{69,15,4080},{92,12,500},{69,15,496}},5042{{101,12,4084},{78,15,4080},{101,12,500},{78,15,496}},5043{{110,12,4084},{86,15,4080},{110,12,500},{86,15,496}},5044{{118,12,4084},{79,31,4080},{118,12,500},{79,31,496}},5045{{126,12,4084},{87,31,4080},{126,12,500},{87,31,496}},5046{{71,8,3602},{71,8,3600},{2,21,384},{2,21,384}},5047{{79,8,3611},{79,8,3608},{0,69,448},{0,69,448}},5048{{87,8,3611},{87,8,3608},{0,23,384},{0,23,384}},5049{{95,8,3611},{95,8,3608},{1,5,448},{1,5,448}},5050{{104,8,3611},{104,8,3608},{0,88,448},{0,88,448}},5051{{112,8,3611},{112,8,3608},{0,72,448},{0,72,448}},5052{{120,8,3611},{121,8,3608},{36,21,458},{36,21,456}},5053{{133,47,3091},{129,8,3608},{44,21,458},{44,21,456}},5054{{142,47,3091},{138,8,3608},{53,21,459},{53,21,456}},5055{{98,12,3850},{98,12,3848},{61,21,459},{61,21,456}},5056{{106,12,3850},{106,12,3848},{10,92,480},{69,21,456}},5057{{114,12,3851},{114,12,3848},{18,92,480},{77,21,456}},5058{{123,12,3851},{123,12,3848},{3,44,488},{86,21,456}},5059{{95,12,3906},{95,12,3904},{11,44,488},{94,21,456}},5060{{103,12,3906},{103,12,3904},{19,44,488},{102,21,456}},5061{{111,12,3907},{111,12,3904},{27,44,489},{110,21,456}},5062{{120,12,3907},{120,12,3904},{36,44,489},{119,21,456}},5063{{128,12,3907},{128,12,3904},{44,44,489},{127,21,456}},5064{{136,12,3907},{136,12,3904},{52,44,489},{135,21,456}},5065{{144,12,3907},{144,12,3904},{60,44,490},{144,21,456}},5066{{153,12,3907},{153,12,3904},{69,44,490},{153,21,456}},5067{{161,12,3395},{149,188,3968},{77,44,490},{161,21,456}},5068{{169,12,3395},{199,21,3928},{85,44,490},{169,21,456}},5069{{113,95,4001},{202,69,3992},{125,8,483},{177,21,456}},5070{{122,95,4001},{201,21,3984},{134,8,483},{186,21,456}},5071{{143,8,4067},{209,21,3984},{142,8,483},{194,21,456}},5072{{151,8,4067},{47,15,4080},{151,8,483},{47,15,496}},5073{{159,8,4067},{55,15,4080},{159,8,483},{55,15,496}},5074{{168,8,4067},{64,15,4080},{168,8,483},{64,15,496}},5075{{160,40,4075},{72,15,4080},{160,40,491},{72,15,496}},5076{{168,40,4075},{80,15,4080},{168,40,491},{80,15,496}},5077{{144,8,4082},{88,15,4080},{144,8,498},{88,15,496}},5078};50795080static void convert_etc1s_to_etc2_eac_r11(eac_block* pDst_block, const endpoint* pEndpoints, const selector* pSelector)5081{5082const uint32_t low_selector = pSelector->m_lo_selector;5083const uint32_t high_selector = pSelector->m_hi_selector;50845085const color32& base_color = pEndpoints->m_color5;5086const uint32_t inten_table = pEndpoints->m_inten5;50875088if (low_selector == high_selector)5089{5090uint32_t r;5091decoder_etc_block::get_block_color5_r(base_color, inten_table, low_selector, r);50925093// Constant alpha block5094// Select table 13, use selector 4 (0), set multiplier to 1 and base color r5095pDst_block->m_base = r;5096pDst_block->m_table = 13;5097pDst_block->m_multiplier = 1;50985099// selectors are all 4's5100static const uint8_t s_etc2_eac_r11_sel4[6] = { 0x92, 0x49, 0x24, 0x92, 0x49, 0x24 };5101memcpy(pDst_block->m_selectors, s_etc2_eac_r11_sel4, sizeof(s_etc2_eac_r11_sel4));51025103return;5104}51055106uint32_t selector_range_table = 0;5107for (selector_range_table = 0; selector_range_table < NUM_ETC2_EAC_SELECTOR_RANGES; selector_range_table++)5108if ((low_selector == s_etc2_eac_selector_ranges[selector_range_table].m_low) && (high_selector == s_etc2_eac_selector_ranges[selector_range_table].m_high))5109break;5110if (selector_range_table >= NUM_ETC2_EAC_SELECTOR_RANGES)5111selector_range_table = 0;51125113const etc1_g_to_eac_conversion* pTable_entry = &s_etc1_g_to_etc2_r11[base_color.r + inten_table * 32][selector_range_table];51145115pDst_block->m_base = pTable_entry->m_base;5116pDst_block->m_table = pTable_entry->m_table_mul >> 4;5117pDst_block->m_multiplier = pTable_entry->m_table_mul & 15;51185119uint64_t selector_bits = 0;51205121for (uint32_t y = 0; y < 4; y++)5122{5123for (uint32_t x = 0; x < 4; x++)5124{5125uint32_t s = pSelector->get_selector(x, y);51265127uint32_t ds = (pTable_entry->m_trans >> (s * 3)) & 7;51285129const uint32_t dst_ofs = 45 - (y + x * 4) * 3;5130selector_bits |= (static_cast<uint64_t>(ds) << dst_ofs);5131}5132}51335134pDst_block->set_selector_bits(selector_bits);5135}5136#endif // BASISD_SUPPORT_ETC2_EAC_RG1151375138// ASTC5139struct etc1_to_astc_solution5140{5141uint8_t m_lo;5142uint8_t m_hi;5143uint16_t m_err;5144};51455146#if BASISD_SUPPORT_ASTC5147static dxt_selector_range g_etc1_to_astc_selector_ranges[] =5148{5149{ 0, 3 },51505151{ 1, 3 },5152{ 0, 2 },51535154{ 1, 2 },51555156{ 2, 3 },5157{ 0, 1 },5158};51595160const uint32_t NUM_ETC1_TO_ASTC_SELECTOR_RANGES = sizeof(g_etc1_to_astc_selector_ranges) / sizeof(g_etc1_to_astc_selector_ranges[0]);51615162static uint32_t g_etc1_to_astc_selector_range_index[4][4];51635164const uint32_t NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS = 10;5165static const uint8_t g_etc1_to_astc_selector_mappings[NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS][4] =5166{5167{ 0, 0, 1, 1 },5168{ 0, 0, 1, 2 },5169{ 0, 0, 1, 3 },5170{ 0, 0, 2, 3 },5171{ 0, 1, 1, 1 },5172{ 0, 1, 2, 2 },5173{ 0, 1, 2, 3 },5174{ 0, 2, 3, 3 },5175{ 1, 2, 2, 2 },5176{ 1, 2, 3, 3 },5177};51785179static const etc1_to_astc_solution g_etc1_to_astc[32 * 8 * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS * NUM_ETC1_TO_ASTC_SELECTOR_RANGES] = {5180#include "basisu_transcoder_tables_astc.inc"5181};51825183// The best selector mapping to use given a base base+inten table and used selector range for converting grayscale data.5184static uint8_t g_etc1_to_astc_best_grayscale_mapping[32][8][NUM_ETC1_TO_ASTC_SELECTOR_RANGES];51855186#if BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY5187static const etc1_to_astc_solution g_etc1_to_astc_0_255[32 * 8 * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS * NUM_ETC1_TO_ASTC_SELECTOR_RANGES] = {5188#include "basisu_transcoder_tables_astc_0_255.inc"5189};5190static uint8_t g_etc1_to_astc_best_grayscale_mapping_0_255[32][8][NUM_ETC1_TO_ASTC_SELECTOR_RANGES];5191#endif51925193static uint32_t g_ise_to_unquant[48];51945195#if BASISD_WRITE_NEW_ASTC_TABLES5196static void create_etc1_to_astc_conversion_table_0_47()5197{5198FILE* pFile = nullptr;5199fopen_s(&pFile, "basisu_transcoder_tables_astc.inc", "w");52005201uint32_t n = 0;52025203for (int inten = 0; inten < 8; inten++)5204{5205for (uint32_t g = 0; g < 32; g++)5206{5207color32 block_colors[4];5208decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);52095210for (uint32_t sr = 0; sr < NUM_ETC1_TO_ASTC_SELECTOR_RANGES; sr++)5211{5212const uint32_t low_selector = g_etc1_to_astc_selector_ranges[sr].m_low;5213const uint32_t high_selector = g_etc1_to_astc_selector_ranges[sr].m_high;52145215uint32_t mapping_best_low[NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];5216uint32_t mapping_best_high[NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];5217uint64_t mapping_best_err[NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];5218uint64_t highest_best_err = 0;52195220for (uint32_t m = 0; m < NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS; m++)5221{5222uint32_t best_lo = 0;5223uint32_t best_hi = 0;5224uint64_t best_err = UINT64_MAX;52255226for (uint32_t hi = 0; hi <= 47; hi++)5227{5228for (uint32_t lo = 0; lo <= 47; lo++)5229{5230uint32_t colors[4];52315232for (uint32_t s = 0; s < 4; s++)5233{5234uint32_t s_scaled = s | (s << 2) | (s << 4);5235if (s_scaled > 32)5236s_scaled++;52375238uint32_t c0 = g_ise_to_unquant[lo] | (g_ise_to_unquant[lo] << 8);5239uint32_t c1 = g_ise_to_unquant[hi] | (g_ise_to_unquant[hi] << 8);5240colors[s] = ((c0 * (64 - s_scaled) + c1 * s_scaled + 32) / 64) >> 8;5241}52425243uint64_t total_err = 0;52445245for (uint32_t s = low_selector; s <= high_selector; s++)5246{5247int err = block_colors[s].g - colors[g_etc1_to_astc_selector_mappings[m][s]];52485249int err_scale = 1;5250// Special case when the intensity table is 7, low_selector is 0, and high_selector is 3. In this extreme case, it's likely the encoder is trying to strongly favor5251// the low/high selectors which are clamping to either 0 or 255.5252if (((inten == 7) && (low_selector == 0) && (high_selector == 3)) && ((s == 0) || (s == 3)))5253err_scale = 8;52545255total_err += (err * err) * err_scale;5256}52575258if (total_err < best_err)5259{5260best_err = total_err;5261best_lo = lo;5262best_hi = hi;5263}5264}5265}52665267mapping_best_low[m] = best_lo;5268mapping_best_high[m] = best_hi;5269mapping_best_err[m] = best_err;5270highest_best_err = basisu::maximum(highest_best_err, best_err);52715272} // m52735274for (uint32_t m = 0; m < NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS; m++)5275{5276uint64_t err = mapping_best_err[m];52775278err = basisu::minimum<uint64_t>(err, 0xFFFF);52795280fprintf(pFile, "{%u,%u,%u},", mapping_best_low[m], mapping_best_high[m], (uint32_t)err);52815282n++;5283if ((n & 31) == 31)5284fprintf(pFile, "\n");5285} // m52865287} // sr5288} // g5289} // inten52905291fclose(pFile);5292}52935294static void create_etc1_to_astc_conversion_table_0_255()5295{5296FILE* pFile = nullptr;5297fopen_s(&pFile, "basisu_transcoder_tables_astc_0_255.inc", "w");52985299uint32_t n = 0;53005301for (int inten = 0; inten < 8; inten++)5302{5303for (uint32_t g = 0; g < 32; g++)5304{5305color32 block_colors[4];5306decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);53075308for (uint32_t sr = 0; sr < NUM_ETC1_TO_ASTC_SELECTOR_RANGES; sr++)5309{5310const uint32_t low_selector = g_etc1_to_astc_selector_ranges[sr].m_low;5311const uint32_t high_selector = g_etc1_to_astc_selector_ranges[sr].m_high;53125313uint32_t mapping_best_low[NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];5314uint32_t mapping_best_high[NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];5315uint64_t mapping_best_err[NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];5316uint64_t highest_best_err = 0;53175318for (uint32_t m = 0; m < NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS; m++)5319{5320uint32_t best_lo = 0;5321uint32_t best_hi = 0;5322uint64_t best_err = UINT64_MAX;53235324for (uint32_t hi = 0; hi <= 255; hi++)5325{5326for (uint32_t lo = 0; lo <= 255; lo++)5327{5328uint32_t colors[4];53295330for (uint32_t s = 0; s < 4; s++)5331{5332uint32_t s_scaled = s | (s << 2) | (s << 4);5333if (s_scaled > 32)5334s_scaled++;53355336uint32_t c0 = lo | (lo << 8);5337uint32_t c1 = hi | (hi << 8);5338colors[s] = ((c0 * (64 - s_scaled) + c1 * s_scaled + 32) / 64) >> 8;5339}53405341uint64_t total_err = 0;53425343for (uint32_t s = low_selector; s <= high_selector; s++)5344{5345int err = block_colors[s].g - colors[g_etc1_to_astc_selector_mappings[m][s]];53465347// Special case when the intensity table is 7, low_selector is 0, and high_selector is 3. In this extreme case, it's likely the encoder is trying to strongly favor5348// the low/high selectors which are clamping to either 0 or 255.5349int err_scale = 1;5350if (((inten == 7) && (low_selector == 0) && (high_selector == 3)) && ((s == 0) || (s == 3)))5351err_scale = 8;53525353total_err += (err * err) * err_scale;5354}53555356if (total_err < best_err)5357{5358best_err = total_err;5359best_lo = lo;5360best_hi = hi;5361}5362}5363}53645365mapping_best_low[m] = best_lo;5366mapping_best_high[m] = best_hi;5367mapping_best_err[m] = best_err;5368highest_best_err = basisu::maximum(highest_best_err, best_err);5369} // m53705371for (uint32_t m = 0; m < NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS; m++)5372{5373uint64_t err = mapping_best_err[m];53745375err = basisu::minimum<uint64_t>(err, 0xFFFF);53765377fprintf(pFile, "{%u,%u,%u},", mapping_best_low[m], mapping_best_high[m], (uint32_t)err);53785379n++;5380if ((n & 31) == 31)5381fprintf(pFile, "\n");5382} // m53835384} // sr5385} // g5386} // inten53875388fclose(pFile);5389}5390#endif53915392#endif53935394#if BASISD_SUPPORT_UASTC || BASISD_SUPPORT_ASTC5395// Table encodes 5 trits to 8 output bits. 3^5 entries.5396// Inverse of the trit bit manipulation process in https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#astc-integer-sequence-encoding5397static const uint8_t g_astc_trit_encode[243] = { 0, 1, 2, 4, 5, 6, 8, 9, 10, 16, 17, 18, 20, 21, 22, 24, 25, 26, 3, 7, 11, 19, 23, 27, 12, 13, 14, 32, 33, 34, 36, 37, 38, 40, 41, 42, 48, 49, 50, 52, 53, 54, 56, 57, 58, 35, 39,539843, 51, 55, 59, 44, 45, 46, 64, 65, 66, 68, 69, 70, 72, 73, 74, 80, 81, 82, 84, 85, 86, 88, 89, 90, 67, 71, 75, 83, 87, 91, 76, 77, 78, 128, 129, 130, 132, 133, 134, 136, 137, 138, 144, 145, 146, 148, 149, 150, 152, 153, 154,5399131, 135, 139, 147, 151, 155, 140, 141, 142, 160, 161, 162, 164, 165, 166, 168, 169, 170, 176, 177, 178, 180, 181, 182, 184, 185, 186, 163, 167, 171, 179, 183, 187, 172, 173, 174, 192, 193, 194, 196, 197, 198, 200, 201, 202,5400208, 209, 210, 212, 213, 214, 216, 217, 218, 195, 199, 203, 211, 215, 219, 204, 205, 206, 96, 97, 98, 100, 101, 102, 104, 105, 106, 112, 113, 114, 116, 117, 118, 120, 121, 122, 99, 103, 107, 115, 119, 123, 108, 109, 110, 224,5401225, 226, 228, 229, 230, 232, 233, 234, 240, 241, 242, 244, 245, 246, 248, 249, 250, 227, 231, 235, 243, 247, 251, 236, 237, 238, 28, 29, 30, 60, 61, 62, 92, 93, 94, 156, 157, 158, 188, 189, 190, 220, 221, 222, 31, 63, 95, 159,5402191, 223, 124, 125, 126 };54035404// Extracts bits [low,high]5405static inline uint32_t astc_extract_bits(uint32_t bits, int low, int high)5406{5407return (bits >> low) & ((1 << (high - low + 1)) - 1);5408}54095410// Writes bits to output in an endian safe way5411static inline void astc_set_bits(uint32_t* pOutput, int& bit_pos, uint32_t value, uint32_t total_bits)5412{5413uint8_t* pBytes = reinterpret_cast<uint8_t*>(pOutput);54145415while (total_bits)5416{5417const uint32_t bits_to_write = basisu::minimum<int>(total_bits, 8 - (bit_pos & 7));54185419pBytes[bit_pos >> 3] |= static_cast<uint8_t>(value << (bit_pos & 7));54205421bit_pos += bits_to_write;5422total_bits -= bits_to_write;5423value >>= bits_to_write;5424}5425}54265427// Encodes 5 values to output, usable for any range that uses trits and bits5428static void astc_encode_trits(uint32_t* pOutput, const uint8_t* pValues, int& bit_pos, int n)5429{5430// First extract the trits and the bits from the 5 input values5431int trits = 0, bits[5];5432const uint32_t bit_mask = (1 << n) - 1;5433for (int i = 0; i < 5; i++)5434{5435static const int s_muls[5] = { 1, 3, 9, 27, 81 };54365437const int t = pValues[i] >> n;54385439trits += t * s_muls[i];5440bits[i] = pValues[i] & bit_mask;5441}54425443// Encode the trits, by inverting the bit manipulations done by the decoder, converting 5 trits into 8-bits.5444// See https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#astc-integer-sequence-encoding54455446assert(trits < 243);5447const int T = g_astc_trit_encode[trits];54485449// Now interleave the 8 encoded trit bits with the bits to form the encoded output. See table 94.5450astc_set_bits(pOutput, bit_pos, bits[0] | (astc_extract_bits(T, 0, 1) << n) | (bits[1] << (2 + n)), n * 2 + 2);54515452astc_set_bits(pOutput, bit_pos, astc_extract_bits(T, 2, 3) | (bits[2] << 2) | (astc_extract_bits(T, 4, 4) << (2 + n)) | (bits[3] << (3 + n)) | (astc_extract_bits(T, 5, 6) << (3 + n * 2)) |5453(bits[4] << (5 + n * 2)) | (astc_extract_bits(T, 7, 7) << (5 + n * 3)), n * 3 + 6);5454}5455#endif // #if BASISD_SUPPORT_UASTC || BASISD_SUPPORT_ASTC54565457#if BASISD_SUPPORT_ASTC5458struct astc_block_params5459{5460// 2 groups of 5, but only a max of 8 are used (RRGGBBAA00)5461uint8_t m_endpoints[10];5462uint8_t m_weights[32];5463};54645465// Packs a single format ASTC block using Color Endpoint Mode 12 (LDR RGBA direct), endpoint BISE range 13, 2-bit weights (range 2).5466// We're always going to output blocks containing alpha, even if the input doesn't have alpha, for simplicity.5467// Each block always has 4x4 weights, uses range 13 BISE encoding on the endpoints (0-47), and each weight ranges from 0-3. This encoding should be roughly equal in quality vs. BC1 for color.5468// 8 total endpoints, stored as RGBA LH LH LH LH order, each ranging from 0-47.5469// Note the input [0,47] endpoint values are not linear - they are encoded as outlined in the ASTC spec:5470// https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#astc-endpoint-unquantization5471// 32 total weights, stored as 16 CA CA, each ranging from 0-3.5472static void astc_pack_block_cem_12_weight_range2(uint32_t *pOutput, const astc_block_params* pBlock)5473{5474uint8_t* pBytes = reinterpret_cast<uint8_t*>(pOutput);54755476// Write constant block mode, color component selector, number of partitions, color endpoint mode5477// https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#_block_mode5478pBytes[0] = 0x42; pBytes[1] = 0x84; pBytes[2] = 0x01; pBytes[3] = 0x00;5479pBytes[4] = 0x00; pBytes[5] = 0x00; pBytes[6] = 0x00; pBytes[7] = 0xc0;54805481pOutput[2] = 0;5482pOutput[3] = 0;54835484// Pack 8 endpoints (each ranging between [0,47]) using BISE starting at bit 175485int bit_pos = 17;5486astc_encode_trits(pOutput, pBlock->m_endpoints, bit_pos, 4);5487astc_encode_trits(pOutput, pBlock->m_endpoints + 5, bit_pos, 4);54885489// Pack 32 2-bit weights, which are stored from the top down into the block in opposite bit order.54905491for (uint32_t i = 0; i < 32; i++)5492{5493static const uint8_t s_reverse_bits[4] = { 0, 2, 1, 3 };5494const uint32_t ofs = 126 - (i * 2);5495pBytes[ofs >> 3] |= (s_reverse_bits[pBlock->m_weights[i]] << (ofs & 7));5496}5497}54985499// CEM mode 12 (LDR RGBA Direct), 8-bit endpoints, 1-bit weights5500// This ASTC mode is basically block truncation coding (BTC) using 1-bit weights and 8-bit/component endpoints - very convenient.5501static void astc_pack_block_cem_12_weight_range0(uint32_t* pOutput, const astc_block_params* pBlock)5502{5503uint8_t* pBytes = reinterpret_cast<uint8_t*>(pOutput);55045505// Write constant block mode, color component selector, number of partitions, color endpoint mode5506// https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#_block_mode5507pBytes[0] = 0x41; pBytes[1] = 0x84; pBytes[2] = 0x01; pBytes[3] = 0x00;5508pOutput[1] = 0;5509pBytes[8] = 0x00; pBytes[9] = 0x00; pBytes[10] = 0x00; pBytes[11] = 0xc0;5510pOutput[3] = 0;55115512// Pack 8 endpoints (each ranging between [0,255]) as 8-bits starting at bit 175513int bit_pos = 17;5514for (uint32_t i = 0; i < 8; i++)5515astc_set_bits(pOutput, bit_pos, pBlock->m_endpoints[i], 8);55165517// Pack 32 1-bit weights, which are stored from the top down into the block in opposite bit order.5518for (uint32_t i = 0; i < 32; i++)5519{5520const uint32_t ofs = 127 - i;5521pBytes[ofs >> 3] |= (pBlock->m_weights[i] << (ofs & 7));5522}5523}55245525#if BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY5526// Optional 8-bit endpoint packing functions.55275528// CEM mode 4 (LDR Luminance+Alpha Direct), 8-bit endpoints, 2 bit weights5529static void astc_pack_block_cem_4_weight_range2(uint32_t* pOutput, const astc_block_params* pBlock)5530{5531uint8_t* pBytes = reinterpret_cast<uint8_t*>(pOutput);55325533// Write constant block mode, color component selector, number of partitions, color endpoint mode5534// https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#_block_mode5535pBytes[0] = 0x42; pBytes[1] = 0x84; pBytes[2] = 0x00; pBytes[3] = 0x00;5536pBytes[4] = 0x00; pBytes[5] = 0x00; pBytes[6] = 0x00; pBytes[7] = 0xc0;55375538pOutput[2] = 0;5539pOutput[3] = 0;55405541// Pack 4 endpoints (each ranging between [0,255]) as 8-bits starting at bit 175542int bit_pos = 17;5543for (uint32_t i = 0; i < 4; i++)5544astc_set_bits(pOutput, bit_pos, pBlock->m_endpoints[i], 8);55455546// Pack 32 2-bit weights, which are stored from the top down into the block in opposite bit order.5547for (uint32_t i = 0; i < 32; i++)5548{5549static const uint8_t s_reverse_bits[4] = { 0, 2, 1, 3 };5550const uint32_t ofs = 126 - (i * 2);5551pBytes[ofs >> 3] |= (s_reverse_bits[pBlock->m_weights[i]] << (ofs & 7));5552}5553}55545555// CEM mode 8 (LDR RGB Direct), 8-bit endpoints, 2 bit weights5556static void astc_pack_block_cem_8_weight_range2(uint32_t* pOutput, const astc_block_params* pBlock)5557{5558uint8_t* pBytes = reinterpret_cast<uint8_t*>(pOutput);55595560// Write constant block mode, color component selector, number of partitions, color endpoint mode5561// https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#_block_mode5562pBytes[0] = 0x42; pBytes[1] = 0x00; pBytes[2] = 0x01; pBytes[3] = 0x00;55635564pOutput[1] = 0;5565pOutput[2] = 0;5566pOutput[3] = 0;55675568// Pack 6 endpoints (each ranging between [0,255]) as 8-bits starting at bit 175569int bit_pos = 17;5570for (uint32_t i = 0; i < 6; i++)5571astc_set_bits(pOutput, bit_pos, pBlock->m_endpoints[i], 8);55725573// Pack 16 2-bit weights, which are stored from the top down into the block in opposite bit order.5574for (uint32_t i = 0; i < 16; i++)5575{5576static const uint8_t s_reverse_bits[4] = { 0, 2, 1, 3 };5577const uint32_t ofs = 126 - (i * 2);5578pBytes[ofs >> 3] |= (s_reverse_bits[pBlock->m_weights[i]] << (ofs & 7));5579}5580}5581#endif55825583// Optimal quantized [0,47] entry to use given [0,255] input5584static uint8_t g_astc_single_color_encoding_0[256];55855586// Optimal quantized [0,47] low/high values given [0,255] input assuming a selector of 15587static struct5588{5589uint8_t m_lo, m_hi;5590} g_astc_single_color_encoding_1[256];55915592static void transcoder_init_astc()5593{5594for (uint32_t base_color = 0; base_color < 32; base_color++)5595{5596for (uint32_t inten_table = 0; inten_table < 8; inten_table++)5597{5598for (uint32_t range_index = 0; range_index < NUM_ETC1_TO_ASTC_SELECTOR_RANGES; range_index++)5599{5600const etc1_to_astc_solution* pTable_g = &g_etc1_to_astc[(inten_table * 32 + base_color) * (NUM_ETC1_TO_ASTC_SELECTOR_RANGES * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS) + range_index * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];56015602uint32_t best_mapping = 0;5603uint32_t best_err = UINT32_MAX;5604for (uint32_t mapping_index = 0; mapping_index < NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS; mapping_index++)5605{5606if (pTable_g[mapping_index].m_err < best_err)5607{5608best_err = pTable_g[mapping_index].m_err;5609best_mapping = mapping_index;5610}5611}56125613g_etc1_to_astc_best_grayscale_mapping[base_color][inten_table][range_index] = static_cast<uint8_t>(best_mapping);5614}5615}5616}56175618#if BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY5619for (uint32_t base_color = 0; base_color < 32; base_color++)5620{5621for (uint32_t inten_table = 0; inten_table < 8; inten_table++)5622{5623for (uint32_t range_index = 0; range_index < NUM_ETC1_TO_ASTC_SELECTOR_RANGES; range_index++)5624{5625const etc1_to_astc_solution* pTable_g = &g_etc1_to_astc_0_255[(inten_table * 32 + base_color) * (NUM_ETC1_TO_ASTC_SELECTOR_RANGES * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS) + range_index * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];56265627uint32_t best_mapping = 0;5628uint32_t best_err = UINT32_MAX;5629for (uint32_t mapping_index = 0; mapping_index < NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS; mapping_index++)5630{5631if (pTable_g[mapping_index].m_err < best_err)5632{5633best_err = pTable_g[mapping_index].m_err;5634best_mapping = mapping_index;5635}5636}56375638g_etc1_to_astc_best_grayscale_mapping_0_255[base_color][inten_table][range_index] = static_cast<uint8_t>(best_mapping);5639}5640}5641}5642#endif56435644for (uint32_t i = 0; i < NUM_ETC1_TO_ASTC_SELECTOR_RANGES; i++)5645{5646uint32_t l = g_etc1_to_astc_selector_ranges[i].m_low;5647uint32_t h = g_etc1_to_astc_selector_ranges[i].m_high;5648g_etc1_to_astc_selector_range_index[l][h] = i;5649}56505651// Endpoint dequantization, see:5652// https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#astc-endpoint-unquantization5653for (uint32_t trit = 0; trit < 3; trit++)5654{5655for (uint32_t bit = 0; bit < 16; bit++)5656{5657const uint32_t A = (bit & 1) ? 511 : 0;5658const uint32_t B = (bit >> 1) | ((bit >> 1) << 6);5659const uint32_t C = 22;5660const uint32_t D = trit;56615662uint32_t unq = D * C + B;5663unq = unq ^ A;5664unq = (A & 0x80) | (unq >> 2);56655666g_ise_to_unquant[bit | (trit << 4)] = unq;5667}5668}56695670// Compute table used for optimal single color encoding.5671for (int i = 0; i < 256; i++)5672{5673int lowest_e = INT_MAX;56745675for (int lo = 0; lo < 48; lo++)5676{5677for (int hi = 0; hi < 48; hi++)5678{5679const int lo_v = g_ise_to_unquant[lo];5680const int hi_v = g_ise_to_unquant[hi];56815682int l = lo_v | (lo_v << 8);5683int h = hi_v | (hi_v << 8);56845685int v = ((l * (64 - 21) + (h * 21) + 32) / 64) >> 8;56865687int e = abs(v - i);56885689if (e < lowest_e)5690{5691g_astc_single_color_encoding_1[i].m_hi = static_cast<uint8_t>(hi);5692g_astc_single_color_encoding_1[i].m_lo = static_cast<uint8_t>(lo);56935694lowest_e = e;5695}56965697} // hi5698} // lo5699}57005701for (int i = 0; i < 256; i++)5702{5703int lowest_e = INT_MAX;57045705for (int lo = 0; lo < 48; lo++)5706{5707const int lo_v = g_ise_to_unquant[lo];57085709int e = abs(lo_v - i);57105711if (e < lowest_e)5712{5713g_astc_single_color_encoding_0[i] = static_cast<uint8_t>(lo);57145715lowest_e = e;5716}5717} // lo5718}5719}57205721// Converts opaque or color+alpha ETC1S block to ASTC 4x4.5722// This function tries to use the best ASTC mode given the block's actual contents.5723static void convert_etc1s_to_astc_4x4(void* pDst_block, const endpoint* pEndpoints, const selector* pSelector,5724bool transcode_alpha, const endpoint *pEndpoint_codebook, const selector *pSelector_codebook)5725{5726astc_block_params blk;57275728blk.m_endpoints[8] = 0;5729blk.m_endpoints[9] = 0;57305731int constant_alpha_val = 255;5732int num_unique_alpha_selectors = 1;57335734if (transcode_alpha)5735{5736const selector& alpha_selectors = pSelector_codebook[((uint16_t*)pDst_block)[1]];57375738num_unique_alpha_selectors = alpha_selectors.m_num_unique_selectors;57395740if (num_unique_alpha_selectors == 1)5741{5742const endpoint& alpha_endpoint = pEndpoint_codebook[((uint16_t*)pDst_block)[0]];57435744const color32& alpha_base_color = alpha_endpoint.m_color5;5745const uint32_t alpha_inten_table = alpha_endpoint.m_inten5;57465747int alpha_block_colors[4];5748decoder_etc_block::get_block_colors5_g(alpha_block_colors, alpha_base_color, alpha_inten_table);57495750constant_alpha_val = alpha_block_colors[alpha_selectors.m_lo_selector];5751}5752}57535754const color32& base_color = pEndpoints->m_color5;5755const uint32_t inten_table = pEndpoints->m_inten5;57565757const uint32_t low_selector = pSelector->m_lo_selector;5758const uint32_t high_selector = pSelector->m_hi_selector;57595760// Handle solid color or BTC blocks, which can always be encoded from ETC1S to ASTC losslessly.5761if ((pSelector->m_num_unique_selectors == 1) && (num_unique_alpha_selectors == 1))5762{5763// Both color and alpha are constant, write a solid color block and exit.5764// See https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#astc-void-extent-blocks5765uint32_t r, g, b;5766decoder_etc_block::get_block_color5(base_color, inten_table, low_selector, r, g, b);57675768uint32_t* pOutput = static_cast<uint32_t*>(pDst_block);5769uint8_t* pBytes = reinterpret_cast<uint8_t*>(pDst_block);57705771pBytes[0] = 0xfc; pBytes[1] = 0xfd; pBytes[2] = 0xff; pBytes[3] = 0xff;57725773pOutput[1] = 0xffffffff;5774pOutput[2] = 0;5775pOutput[3] = 0;57765777int bit_pos = 64;5778astc_set_bits(pOutput, bit_pos, r | (r << 8), 16);5779astc_set_bits(pOutput, bit_pos, g | (g << 8), 16);5780astc_set_bits(pOutput, bit_pos, b | (b << 8), 16);5781astc_set_bits(pOutput, bit_pos, constant_alpha_val | (constant_alpha_val << 8), 16);57825783return;5784}5785else if ((pSelector->m_num_unique_selectors <= 2) && (num_unique_alpha_selectors <= 2))5786{5787// Both color and alpha use <= 2 unique selectors each.5788// Use block truncation coding, which is lossless with ASTC (8-bit endpoints, 1-bit weights).5789color32 block_colors[4];5790decoder_etc_block::get_block_colors5(block_colors, base_color, inten_table);57915792blk.m_endpoints[0] = block_colors[low_selector].r;5793blk.m_endpoints[2] = block_colors[low_selector].g;5794blk.m_endpoints[4] = block_colors[low_selector].b;57955796blk.m_endpoints[1] = block_colors[high_selector].r;5797blk.m_endpoints[3] = block_colors[high_selector].g;5798blk.m_endpoints[5] = block_colors[high_selector].b;57995800int s0 = blk.m_endpoints[0] + blk.m_endpoints[2] + blk.m_endpoints[4];5801int s1 = blk.m_endpoints[1] + blk.m_endpoints[3] + blk.m_endpoints[5];5802bool invert = false;5803if (s1 < s0)5804{5805std::swap(blk.m_endpoints[0], blk.m_endpoints[1]);5806std::swap(blk.m_endpoints[2], blk.m_endpoints[3]);5807std::swap(blk.m_endpoints[4], blk.m_endpoints[5]);5808invert = true;5809}58105811if (transcode_alpha)5812{5813const endpoint& alpha_endpoint = pEndpoint_codebook[((uint16_t*)pDst_block)[0]];5814const selector& alpha_selectors = pSelector_codebook[((uint16_t*)pDst_block)[1]];58155816const color32& alpha_base_color = alpha_endpoint.m_color5;5817const uint32_t alpha_inten_table = alpha_endpoint.m_inten5;58185819const uint32_t alpha_low_selector = alpha_selectors.m_lo_selector;5820const uint32_t alpha_high_selector = alpha_selectors.m_hi_selector;58215822int alpha_block_colors[4];5823decoder_etc_block::get_block_colors5_g(alpha_block_colors, alpha_base_color, alpha_inten_table);58245825blk.m_endpoints[6] = static_cast<uint8_t>(alpha_block_colors[alpha_low_selector]);5826blk.m_endpoints[7] = static_cast<uint8_t>(alpha_block_colors[alpha_high_selector]);58275828for (uint32_t y = 0; y < 4; y++)5829{5830for (uint32_t x = 0; x < 4; x++)5831{5832uint32_t s = alpha_selectors.get_selector(x, y);5833s = (s == alpha_high_selector) ? 1 : 0;58345835blk.m_weights[(x + y * 4) * 2 + 1] = static_cast<uint8_t>(s);5836} // x5837} // y5838}5839else5840{5841blk.m_endpoints[6] = 255;5842blk.m_endpoints[7] = 255;58435844for (uint32_t i = 0; i < 16; i++)5845blk.m_weights[i * 2 + 1] = 0;5846}58475848for (uint32_t y = 0; y < 4; y++)5849{5850for (uint32_t x = 0; x < 4; x++)5851{5852uint32_t s = pSelector->get_selector(x, y);58535854s = (s == high_selector) ? 1 : 0;58555856if (invert)5857s = 1 - s;58585859blk.m_weights[(x + y * 4) * 2] = static_cast<uint8_t>(s);5860} // x5861} // y58625863astc_pack_block_cem_12_weight_range0(reinterpret_cast<uint32_t*>(pDst_block), &blk);58645865return;5866}58675868// Either alpha and/or color use > 2 unique selectors each, so we must do something more complex.58695870#if BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY5871// The optional higher quality modes use 8-bits endpoints vs. [0,47] endpoints.58725873// If the block's base color is grayscale, all pixels are grayscale, so encode the block as Luminance+Alpha.5874if ((base_color.r == base_color.g) && (base_color.r == base_color.b))5875{5876if (transcode_alpha)5877{5878const endpoint& alpha_endpoint = pEndpoint_codebook[((uint16_t*)pDst_block)[0]];5879const selector& alpha_selectors = pSelector_codebook[((uint16_t*)pDst_block)[1]];58805881const color32& alpha_base_color = alpha_endpoint.m_color5;5882const uint32_t alpha_inten_table = alpha_endpoint.m_inten5;58835884const uint32_t alpha_low_selector = alpha_selectors.m_lo_selector;5885const uint32_t alpha_high_selector = alpha_selectors.m_hi_selector;58865887if (num_unique_alpha_selectors <= 2)5888{5889// Simple alpha block with only 1 or 2 unique values, so use BTC. This is lossless.5890int alpha_block_colors[4];5891decoder_etc_block::get_block_colors5_g(alpha_block_colors, alpha_base_color, alpha_inten_table);58925893blk.m_endpoints[2] = static_cast<uint8_t>(alpha_block_colors[alpha_low_selector]);5894blk.m_endpoints[3] = static_cast<uint8_t>(alpha_block_colors[alpha_high_selector]);58955896for (uint32_t i = 0; i < 16; i++)5897{5898uint32_t s = alpha_selectors.get_selector(i & 3, i >> 2);5899blk.m_weights[i * 2 + 1] = (s == alpha_high_selector) ? 3 : 0;5900}5901}5902else5903{5904// Convert ETC1S alpha5905const uint32_t alpha_selector_range_table = g_etc1_to_astc_selector_range_index[alpha_low_selector][alpha_high_selector];59065907//[32][8][RANGES][MAPPING]5908const etc1_to_astc_solution* pTable_g = &g_etc1_to_astc_0_255[(alpha_inten_table * 32 + alpha_base_color.g) * (NUM_ETC1_TO_ASTC_SELECTOR_RANGES * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS) + alpha_selector_range_table * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];59095910const uint32_t best_mapping = g_etc1_to_astc_best_grayscale_mapping_0_255[alpha_base_color.g][alpha_inten_table][alpha_selector_range_table];59115912blk.m_endpoints[2] = pTable_g[best_mapping].m_lo;5913blk.m_endpoints[3] = pTable_g[best_mapping].m_hi;59145915const uint8_t* pSelectors_xlat = &g_etc1_to_astc_selector_mappings[best_mapping][0];59165917for (uint32_t y = 0; y < 4; y++)5918{5919for (uint32_t x = 0; x < 4; x++)5920{5921uint32_t s = alpha_selectors.get_selector(x, y);5922uint32_t as = pSelectors_xlat[s];59235924blk.m_weights[(x + y * 4) * 2 + 1] = static_cast<uint8_t>(as);5925} // x5926} // y5927}5928}5929else5930{5931// No alpha slice - set output alpha to all 255's5932blk.m_endpoints[2] = 255;5933blk.m_endpoints[3] = 255;59345935for (uint32_t i = 0; i < 16; i++)5936blk.m_weights[i * 2 + 1] = 0;5937}59385939if (pSelector->m_num_unique_selectors <= 2)5940{5941// Simple color block with only 1 or 2 unique values, so use BTC. This is lossless.5942int block_colors[4];5943decoder_etc_block::get_block_colors5_g(block_colors, base_color, inten_table);59445945blk.m_endpoints[0] = static_cast<uint8_t>(block_colors[low_selector]);5946blk.m_endpoints[1] = static_cast<uint8_t>(block_colors[high_selector]);59475948for (uint32_t i = 0; i < 16; i++)5949{5950uint32_t s = pSelector->get_selector(i & 3, i >> 2);5951blk.m_weights[i * 2] = (s == high_selector) ? 3 : 0;5952}5953}5954else5955{5956// Convert ETC1S alpha5957const uint32_t selector_range_table = g_etc1_to_astc_selector_range_index[low_selector][high_selector];59585959//[32][8][RANGES][MAPPING]5960const etc1_to_astc_solution* pTable_g = &g_etc1_to_astc_0_255[(inten_table * 32 + base_color.g) * (NUM_ETC1_TO_ASTC_SELECTOR_RANGES * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];59615962const uint32_t best_mapping = g_etc1_to_astc_best_grayscale_mapping_0_255[base_color.g][inten_table][selector_range_table];59635964blk.m_endpoints[0] = pTable_g[best_mapping].m_lo;5965blk.m_endpoints[1] = pTable_g[best_mapping].m_hi;59665967const uint8_t* pSelectors_xlat = &g_etc1_to_astc_selector_mappings[best_mapping][0];59685969for (uint32_t y = 0; y < 4; y++)5970{5971for (uint32_t x = 0; x < 4; x++)5972{5973uint32_t s = pSelector->get_selector(x, y);5974uint32_t as = pSelectors_xlat[s];59755976blk.m_weights[(x + y * 4) * 2] = static_cast<uint8_t>(as);5977} // x5978} // y5979}59805981astc_pack_block_cem_4_weight_range2(reinterpret_cast<uint32_t*>(pDst_block), &blk);5982return;5983}59845985// The block isn't grayscale and it uses > 2 unique selectors for opaque and/or alpha.5986// Check for fully opaque blocks, if so use 8-bit endpoints for slightly higher opaque quality (higher than BC1, but lower than BC7 mode 6 opaque).5987if ((num_unique_alpha_selectors == 1) && (constant_alpha_val == 255))5988{5989// Convert ETC1S color5990const uint32_t selector_range_table = g_etc1_to_astc_selector_range_index[low_selector][high_selector];59915992//[32][8][RANGES][MAPPING]5993const etc1_to_astc_solution* pTable_r = &g_etc1_to_astc_0_255[(inten_table * 32 + base_color.r) * (NUM_ETC1_TO_ASTC_SELECTOR_RANGES * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];5994const etc1_to_astc_solution* pTable_g = &g_etc1_to_astc_0_255[(inten_table * 32 + base_color.g) * (NUM_ETC1_TO_ASTC_SELECTOR_RANGES * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];5995const etc1_to_astc_solution* pTable_b = &g_etc1_to_astc_0_255[(inten_table * 32 + base_color.b) * (NUM_ETC1_TO_ASTC_SELECTOR_RANGES * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];59965997uint32_t best_err = UINT_MAX;5998uint32_t best_mapping = 0;59996000assert(NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS == 10);6001#define DO_ITER(m) { uint32_t total_err = pTable_r[m].m_err + pTable_g[m].m_err + pTable_b[m].m_err; if (total_err < best_err) { best_err = total_err; best_mapping = m; } }6002DO_ITER(0); DO_ITER(1); DO_ITER(2); DO_ITER(3); DO_ITER(4);6003DO_ITER(5); DO_ITER(6); DO_ITER(7); DO_ITER(8); DO_ITER(9);6004#undef DO_ITER60056006blk.m_endpoints[0] = pTable_r[best_mapping].m_lo;6007blk.m_endpoints[1] = pTable_r[best_mapping].m_hi;60086009blk.m_endpoints[2] = pTable_g[best_mapping].m_lo;6010blk.m_endpoints[3] = pTable_g[best_mapping].m_hi;60116012blk.m_endpoints[4] = pTable_b[best_mapping].m_lo;6013blk.m_endpoints[5] = pTable_b[best_mapping].m_hi;60146015int s0 = blk.m_endpoints[0] + blk.m_endpoints[2] + blk.m_endpoints[4];6016int s1 = blk.m_endpoints[1] + blk.m_endpoints[3] + blk.m_endpoints[5];6017bool invert = false;60186019if (s1 < s0)6020{6021std::swap(blk.m_endpoints[0], blk.m_endpoints[1]);6022std::swap(blk.m_endpoints[2], blk.m_endpoints[3]);6023std::swap(blk.m_endpoints[4], blk.m_endpoints[5]);6024invert = true;6025}60266027const uint8_t* pSelectors_xlat = &g_etc1_to_astc_selector_mappings[best_mapping][0];60286029for (uint32_t y = 0; y < 4; y++)6030{6031for (uint32_t x = 0; x < 4; x++)6032{6033uint32_t s = pSelector->get_selector(x, y);6034uint32_t as = pSelectors_xlat[s];6035if (invert)6036as = 3 - as;60376038blk.m_weights[x + y * 4] = static_cast<uint8_t>(as);6039} // x6040} // y60416042// Now pack to ASTC6043astc_pack_block_cem_8_weight_range2(reinterpret_cast<uint32_t*>(pDst_block), &blk);6044return;6045}6046#endif //#if BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY60476048// Nothing else worked, so fall back to CEM Mode 12 (LDR RGBA Direct), [0,47] endpoints, weight range 2 (2-bit weights), dual planes.6049// This mode can handle everything, but at slightly less quality than BC1.6050if (transcode_alpha)6051{6052const endpoint& alpha_endpoint = pEndpoint_codebook[((uint16_t*)pDst_block)[0]];6053const selector& alpha_selectors = pSelector_codebook[((uint16_t*)pDst_block)[1]];60546055const color32& alpha_base_color = alpha_endpoint.m_color5;6056const uint32_t alpha_inten_table = alpha_endpoint.m_inten5;60576058const uint32_t alpha_low_selector = alpha_selectors.m_lo_selector;6059const uint32_t alpha_high_selector = alpha_selectors.m_hi_selector;60606061if (alpha_low_selector == alpha_high_selector)6062{6063// Solid alpha block - use precomputed tables.6064int alpha_block_colors[4];6065decoder_etc_block::get_block_colors5_g(alpha_block_colors, alpha_base_color, alpha_inten_table);60666067const uint32_t g = alpha_block_colors[alpha_low_selector];60686069blk.m_endpoints[6] = g_astc_single_color_encoding_1[g].m_lo;6070blk.m_endpoints[7] = g_astc_single_color_encoding_1[g].m_hi;60716072for (uint32_t i = 0; i < 16; i++)6073blk.m_weights[i * 2 + 1] = 1;6074}6075else if ((alpha_inten_table >= 7) && (alpha_selectors.m_num_unique_selectors == 2) && (alpha_low_selector == 0) && (alpha_high_selector == 3))6076{6077// Handle outlier case where only the two outer colors are used with inten table 7.6078color32 alpha_block_colors[4];60796080decoder_etc_block::get_block_colors5(alpha_block_colors, alpha_base_color, alpha_inten_table);60816082const uint32_t g0 = alpha_block_colors[0].g;6083const uint32_t g1 = alpha_block_colors[3].g;60846085blk.m_endpoints[6] = g_astc_single_color_encoding_0[g0];6086blk.m_endpoints[7] = g_astc_single_color_encoding_0[g1];60876088for (uint32_t y = 0; y < 4; y++)6089{6090for (uint32_t x = 0; x < 4; x++)6091{6092uint32_t s = alpha_selectors.get_selector(x, y);6093uint32_t as = (s == alpha_high_selector) ? 3 : 0;60946095blk.m_weights[(x + y * 4) * 2 + 1] = static_cast<uint8_t>(as);6096} // x6097} // y6098}6099else6100{6101// Convert ETC1S alpha6102const uint32_t alpha_selector_range_table = g_etc1_to_astc_selector_range_index[alpha_low_selector][alpha_high_selector];61036104//[32][8][RANGES][MAPPING]6105const etc1_to_astc_solution* pTable_g = &g_etc1_to_astc[(alpha_inten_table * 32 + alpha_base_color.g) * (NUM_ETC1_TO_ASTC_SELECTOR_RANGES * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS) + alpha_selector_range_table * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];61066107const uint32_t best_mapping = g_etc1_to_astc_best_grayscale_mapping[alpha_base_color.g][alpha_inten_table][alpha_selector_range_table];61086109blk.m_endpoints[6] = pTable_g[best_mapping].m_lo;6110blk.m_endpoints[7] = pTable_g[best_mapping].m_hi;61116112const uint8_t* pSelectors_xlat = &g_etc1_to_astc_selector_mappings[best_mapping][0];61136114for (uint32_t y = 0; y < 4; y++)6115{6116for (uint32_t x = 0; x < 4; x++)6117{6118uint32_t s = alpha_selectors.get_selector(x, y);6119uint32_t as = pSelectors_xlat[s];61206121blk.m_weights[(x + y * 4) * 2 + 1] = static_cast<uint8_t>(as);6122} // x6123} // y6124}6125}6126else6127{6128// No alpha slice - set output alpha to all 255's6129// 1 is 255 when dequantized6130blk.m_endpoints[6] = 1;6131blk.m_endpoints[7] = 1;61326133for (uint32_t i = 0; i < 16; i++)6134blk.m_weights[i * 2 + 1] = 0;6135}61366137if (low_selector == high_selector)6138{6139// Solid color block - use precomputed tables of optimal endpoints assuming selector weights are all 1.6140color32 block_colors[4];61416142decoder_etc_block::get_block_colors5(block_colors, base_color, inten_table);61436144const uint32_t r = block_colors[low_selector].r;6145const uint32_t g = block_colors[low_selector].g;6146const uint32_t b = block_colors[low_selector].b;61476148blk.m_endpoints[0] = g_astc_single_color_encoding_1[r].m_lo;6149blk.m_endpoints[1] = g_astc_single_color_encoding_1[r].m_hi;61506151blk.m_endpoints[2] = g_astc_single_color_encoding_1[g].m_lo;6152blk.m_endpoints[3] = g_astc_single_color_encoding_1[g].m_hi;61536154blk.m_endpoints[4] = g_astc_single_color_encoding_1[b].m_lo;6155blk.m_endpoints[5] = g_astc_single_color_encoding_1[b].m_hi;61566157int s0 = g_ise_to_unquant[blk.m_endpoints[0]] + g_ise_to_unquant[blk.m_endpoints[2]] + g_ise_to_unquant[blk.m_endpoints[4]];6158int s1 = g_ise_to_unquant[blk.m_endpoints[1]] + g_ise_to_unquant[blk.m_endpoints[3]] + g_ise_to_unquant[blk.m_endpoints[5]];6159bool invert = false;61606161if (s1 < s0)6162{6163std::swap(blk.m_endpoints[0], blk.m_endpoints[1]);6164std::swap(blk.m_endpoints[2], blk.m_endpoints[3]);6165std::swap(blk.m_endpoints[4], blk.m_endpoints[5]);6166invert = true;6167}61686169for (uint32_t i = 0; i < 16; i++)6170blk.m_weights[i * 2] = invert ? 2 : 1;6171}6172else if ((inten_table >= 7) && (pSelector->m_num_unique_selectors == 2) && (pSelector->m_lo_selector == 0) && (pSelector->m_hi_selector == 3))6173{6174// Handle outlier case where only the two outer colors are used with inten table 7.6175color32 block_colors[4];61766177decoder_etc_block::get_block_colors5(block_colors, base_color, inten_table);61786179const uint32_t r0 = block_colors[0].r;6180const uint32_t g0 = block_colors[0].g;6181const uint32_t b0 = block_colors[0].b;61826183const uint32_t r1 = block_colors[3].r;6184const uint32_t g1 = block_colors[3].g;6185const uint32_t b1 = block_colors[3].b;61866187blk.m_endpoints[0] = g_astc_single_color_encoding_0[r0];6188blk.m_endpoints[1] = g_astc_single_color_encoding_0[r1];61896190blk.m_endpoints[2] = g_astc_single_color_encoding_0[g0];6191blk.m_endpoints[3] = g_astc_single_color_encoding_0[g1];61926193blk.m_endpoints[4] = g_astc_single_color_encoding_0[b0];6194blk.m_endpoints[5] = g_astc_single_color_encoding_0[b1];61956196int s0 = g_ise_to_unquant[blk.m_endpoints[0]] + g_ise_to_unquant[blk.m_endpoints[2]] + g_ise_to_unquant[blk.m_endpoints[4]];6197int s1 = g_ise_to_unquant[blk.m_endpoints[1]] + g_ise_to_unquant[blk.m_endpoints[3]] + g_ise_to_unquant[blk.m_endpoints[5]];6198bool invert = false;61996200if (s1 < s0)6201{6202std::swap(blk.m_endpoints[0], blk.m_endpoints[1]);6203std::swap(blk.m_endpoints[2], blk.m_endpoints[3]);6204std::swap(blk.m_endpoints[4], blk.m_endpoints[5]);6205invert = true;6206}62076208for (uint32_t y = 0; y < 4; y++)6209{6210for (uint32_t x = 0; x < 4; x++)6211{6212uint32_t s = pSelector->get_selector(x, y);6213uint32_t as = (s == low_selector) ? 0 : 3;62146215if (invert)6216as = 3 - as;62176218blk.m_weights[(x + y * 4) * 2] = static_cast<uint8_t>(as);6219} // x6220} // y6221}6222else6223{6224// Convert ETC1S color6225const uint32_t selector_range_table = g_etc1_to_astc_selector_range_index[low_selector][high_selector];62266227//[32][8][RANGES][MAPPING]6228const etc1_to_astc_solution* pTable_r = &g_etc1_to_astc[(inten_table * 32 + base_color.r) * (NUM_ETC1_TO_ASTC_SELECTOR_RANGES * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];6229const etc1_to_astc_solution* pTable_g = &g_etc1_to_astc[(inten_table * 32 + base_color.g) * (NUM_ETC1_TO_ASTC_SELECTOR_RANGES * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];6230const etc1_to_astc_solution* pTable_b = &g_etc1_to_astc[(inten_table * 32 + base_color.b) * (NUM_ETC1_TO_ASTC_SELECTOR_RANGES * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS];62316232uint32_t best_err = UINT_MAX;6233uint32_t best_mapping = 0;62346235assert(NUM_ETC1_TO_ASTC_SELECTOR_MAPPINGS == 10);6236#define DO_ITER(m) { uint32_t total_err = pTable_r[m].m_err + pTable_g[m].m_err + pTable_b[m].m_err; if (total_err < best_err) { best_err = total_err; best_mapping = m; } }6237DO_ITER(0); DO_ITER(1); DO_ITER(2); DO_ITER(3); DO_ITER(4);6238DO_ITER(5); DO_ITER(6); DO_ITER(7); DO_ITER(8); DO_ITER(9);6239#undef DO_ITER62406241blk.m_endpoints[0] = pTable_r[best_mapping].m_lo;6242blk.m_endpoints[1] = pTable_r[best_mapping].m_hi;62436244blk.m_endpoints[2] = pTable_g[best_mapping].m_lo;6245blk.m_endpoints[3] = pTable_g[best_mapping].m_hi;62466247blk.m_endpoints[4] = pTable_b[best_mapping].m_lo;6248blk.m_endpoints[5] = pTable_b[best_mapping].m_hi;62496250int s0 = g_ise_to_unquant[blk.m_endpoints[0]] + g_ise_to_unquant[blk.m_endpoints[2]] + g_ise_to_unquant[blk.m_endpoints[4]];6251int s1 = g_ise_to_unquant[blk.m_endpoints[1]] + g_ise_to_unquant[blk.m_endpoints[3]] + g_ise_to_unquant[blk.m_endpoints[5]];6252bool invert = false;62536254if (s1 < s0)6255{6256std::swap(blk.m_endpoints[0], blk.m_endpoints[1]);6257std::swap(blk.m_endpoints[2], blk.m_endpoints[3]);6258std::swap(blk.m_endpoints[4], blk.m_endpoints[5]);6259invert = true;6260}62616262const uint8_t* pSelectors_xlat = &g_etc1_to_astc_selector_mappings[best_mapping][0];62636264for (uint32_t y = 0; y < 4; y++)6265{6266for (uint32_t x = 0; x < 4; x++)6267{6268uint32_t s = pSelector->get_selector(x, y);6269uint32_t as = pSelectors_xlat[s];6270if (invert)6271as = 3 - as;62726273blk.m_weights[(x + y * 4) * 2] = static_cast<uint8_t>(as);6274} // x6275} // y6276}62776278// Now pack to ASTC6279astc_pack_block_cem_12_weight_range2(reinterpret_cast<uint32_t *>(pDst_block), &blk);6280}6281#endif62826283#if BASISD_SUPPORT_ATC6284// ATC and PVRTC2 both use these tables.6285struct etc1s_to_atc_solution6286{6287uint8_t m_lo;6288uint8_t m_hi;6289uint16_t m_err;6290};62916292static dxt_selector_range g_etc1s_to_atc_selector_ranges[] =6293{6294{ 0, 3 },6295{ 1, 3 },6296{ 0, 2 },6297{ 1, 2 },6298{ 2, 3 },6299{ 0, 1 },6300};63016302const uint32_t NUM_ETC1S_TO_ATC_SELECTOR_RANGES = sizeof(g_etc1s_to_atc_selector_ranges) / sizeof(g_etc1s_to_atc_selector_ranges[0]);63036304static uint32_t g_etc1s_to_atc_selector_range_index[4][4];63056306const uint32_t NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS = 10;6307static const uint8_t g_etc1s_to_atc_selector_mappings[NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS][4] =6308{6309{ 0, 0, 1, 1 },6310{ 0, 0, 1, 2 },6311{ 0, 0, 1, 3 },6312{ 0, 0, 2, 3 },6313{ 0, 1, 1, 1 },6314{ 0, 1, 2, 2 },6315{ 0, 1, 2, 3 }, //6 - identity6316{ 0, 2, 3, 3 },6317{ 1, 2, 2, 2 },6318{ 1, 2, 3, 3 },6319};6320const uint32_t ATC_IDENTITY_SELECTOR_MAPPING_INDEX = 6;63216322#if BASISD_SUPPORT_PVRTC26323static const etc1s_to_atc_solution g_etc1s_to_pvrtc2_45[32 * 8 * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS * NUM_ETC1S_TO_ATC_SELECTOR_RANGES] = {6324#include "basisu_transcoder_tables_pvrtc2_45.inc"6325};63266327#if 06328static const etc1s_to_atc_solution g_etc1s_to_pvrtc2_alpha_33[32 * 8 * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS * NUM_ETC1S_TO_ATC_SELECTOR_RANGES] = {6329#include "basisu_transcoder_tables_pvrtc2_alpha_33.inc"6330};6331#endif63326333#endif63346335static const etc1s_to_atc_solution g_etc1s_to_atc_55[32 * 8 * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS * NUM_ETC1S_TO_ATC_SELECTOR_RANGES] = {6336#include "basisu_transcoder_tables_atc_55.inc"6337};63386339static const etc1s_to_atc_solution g_etc1s_to_atc_56[32 * 8 * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS * NUM_ETC1S_TO_ATC_SELECTOR_RANGES] = {6340#include "basisu_transcoder_tables_atc_56.inc"6341};63426343struct atc_match_entry6344{6345uint8_t m_lo;6346uint8_t m_hi;6347};6348static atc_match_entry g_pvrtc2_match45_equals_1[256], g_atc_match55_equals_1[256], g_atc_match56_equals_1[256]; // selector 16349static atc_match_entry g_pvrtc2_match4[256], g_atc_match5[256], g_atc_match6[256];63506351static void prepare_atc_single_color_table(atc_match_entry* pTable, int size0, int size1, int sel)6352{6353for (int i = 0; i < 256; i++)6354{6355int lowest_e = 256;6356for (int lo = 0; lo < size0; lo++)6357{6358int lo_e = lo;6359if (size0 == 16)6360{6361lo_e = (lo_e << 1) | (lo_e >> 3);6362lo_e = (lo_e << 3) | (lo_e >> 2);6363}6364else if (size0 == 32)6365lo_e = (lo_e << 3) | (lo_e >> 2);6366else6367lo_e = (lo_e << 2) | (lo_e >> 4);63686369for (int hi = 0; hi < size1; hi++)6370{6371int hi_e = hi;6372if (size1 == 16)6373{6374// This is only for PVRTC2 - expand to 5 then 86375hi_e = (hi_e << 1) | (hi_e >> 3);6376hi_e = (hi_e << 3) | (hi_e >> 2);6377}6378else if (size1 == 32)6379hi_e = (hi_e << 3) | (hi_e >> 2);6380else6381hi_e = (hi_e << 2) | (hi_e >> 4);63826383int e;63846385if (sel == 1)6386{6387// Selector 16388e = abs(((lo_e * 5 + hi_e * 3) / 8) - i);6389}6390else6391{6392assert(sel == 3);63936394// Selector 36395e = abs(hi_e - i);6396}63976398if (e < lowest_e)6399{6400pTable[i].m_lo = static_cast<uint8_t>(lo);6401pTable[i].m_hi = static_cast<uint8_t>(hi);64026403lowest_e = e;6404}64056406} // hi6407} // lo6408} // i6409}64106411static void transcoder_init_atc()6412{6413prepare_atc_single_color_table(g_pvrtc2_match45_equals_1, 16, 32, 1);6414prepare_atc_single_color_table(g_atc_match55_equals_1, 32, 32, 1);6415prepare_atc_single_color_table(g_atc_match56_equals_1, 32, 64, 1);64166417prepare_atc_single_color_table(g_pvrtc2_match4, 1, 16, 3);6418prepare_atc_single_color_table(g_atc_match5, 1, 32, 3);6419prepare_atc_single_color_table(g_atc_match6, 1, 64, 3);64206421for (uint32_t i = 0; i < NUM_ETC1S_TO_ATC_SELECTOR_RANGES; i++)6422{6423uint32_t l = g_etc1s_to_atc_selector_ranges[i].m_low;6424uint32_t h = g_etc1s_to_atc_selector_ranges[i].m_high;6425g_etc1s_to_atc_selector_range_index[l][h] = i;6426}6427}64286429struct atc_block6430{6431uint8_t m_lo[2];6432uint8_t m_hi[2];6433uint8_t m_sels[4];64346435void set_low_color(uint32_t r, uint32_t g, uint32_t b)6436{6437assert((r < 32) && (g < 32) && (b < 32));6438uint32_t x = (r << 10) | (g << 5) | b;6439m_lo[0] = x & 0xFF;6440m_lo[1] = (x >> 8) & 0xFF;6441}64426443void set_high_color(uint32_t r, uint32_t g, uint32_t b)6444{6445assert((r < 32) && (g < 64) && (b < 32));6446uint32_t x = (r << 11) | (g << 5) | b;6447m_hi[0] = x & 0xFF;6448m_hi[1] = (x >> 8) & 0xFF;6449}6450};64516452static void convert_etc1s_to_atc(void* pDst, const endpoint* pEndpoints, const selector* pSelector)6453{6454atc_block* pBlock = static_cast<atc_block*>(pDst);64556456const uint32_t low_selector = pSelector->m_lo_selector;6457const uint32_t high_selector = pSelector->m_hi_selector;64586459const color32& base_color = pEndpoints->m_color5;6460const uint32_t inten_table = pEndpoints->m_inten5;64616462if (low_selector == high_selector)6463{6464uint32_t r, g, b;6465decoder_etc_block::get_block_color5(base_color, inten_table, low_selector, r, g, b);64666467pBlock->set_low_color(g_atc_match55_equals_1[r].m_lo, g_atc_match56_equals_1[g].m_lo, g_atc_match55_equals_1[b].m_lo);6468pBlock->set_high_color(g_atc_match55_equals_1[r].m_hi, g_atc_match56_equals_1[g].m_hi, g_atc_match55_equals_1[b].m_hi);64696470pBlock->m_sels[0] = 0x55;6471pBlock->m_sels[1] = 0x55;6472pBlock->m_sels[2] = 0x55;6473pBlock->m_sels[3] = 0x55;64746475return;6476}6477else if ((inten_table >= 7) && (pSelector->m_num_unique_selectors == 2) && (pSelector->m_lo_selector == 0) && (pSelector->m_hi_selector == 3))6478{6479color32 block_colors[4];6480decoder_etc_block::get_block_colors5(block_colors, base_color, inten_table);64816482const uint32_t r0 = block_colors[0].r;6483const uint32_t g0 = block_colors[0].g;6484const uint32_t b0 = block_colors[0].b;64856486const uint32_t r1 = block_colors[3].r;6487const uint32_t g1 = block_colors[3].g;6488const uint32_t b1 = block_colors[3].b;64896490pBlock->set_low_color(g_atc_match5[r0].m_hi, g_atc_match5[g0].m_hi, g_atc_match5[b0].m_hi);6491pBlock->set_high_color(g_atc_match5[r1].m_hi, g_atc_match6[g1].m_hi, g_atc_match5[b1].m_hi);64926493pBlock->m_sels[0] = pSelector->m_selectors[0];6494pBlock->m_sels[1] = pSelector->m_selectors[1];6495pBlock->m_sels[2] = pSelector->m_selectors[2];6496pBlock->m_sels[3] = pSelector->m_selectors[3];64976498return;6499}65006501const uint32_t selector_range_table = g_etc1s_to_atc_selector_range_index[low_selector][high_selector];65026503//[32][8][RANGES][MAPPING]6504const etc1s_to_atc_solution* pTable_r = &g_etc1s_to_atc_55[(inten_table * 32 + base_color.r) * (NUM_ETC1S_TO_ATC_SELECTOR_RANGES * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS];6505const etc1s_to_atc_solution* pTable_g = &g_etc1s_to_atc_56[(inten_table * 32 + base_color.g) * (NUM_ETC1S_TO_ATC_SELECTOR_RANGES * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS];6506const etc1s_to_atc_solution* pTable_b = &g_etc1s_to_atc_55[(inten_table * 32 + base_color.b) * (NUM_ETC1S_TO_ATC_SELECTOR_RANGES * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS];65076508uint32_t best_err = UINT_MAX;6509uint32_t best_mapping = 0;65106511assert(NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS == 10);6512#define DO_ITER(m) { uint32_t total_err = pTable_r[m].m_err + pTable_g[m].m_err + pTable_b[m].m_err; if (total_err < best_err) { best_err = total_err; best_mapping = m; } }6513DO_ITER(0); DO_ITER(1); DO_ITER(2); DO_ITER(3); DO_ITER(4);6514DO_ITER(5); DO_ITER(6); DO_ITER(7); DO_ITER(8); DO_ITER(9);6515#undef DO_ITER65166517pBlock->set_low_color(pTable_r[best_mapping].m_lo, pTable_g[best_mapping].m_lo, pTable_b[best_mapping].m_lo);6518pBlock->set_high_color(pTable_r[best_mapping].m_hi, pTable_g[best_mapping].m_hi, pTable_b[best_mapping].m_hi);65196520if (ATC_IDENTITY_SELECTOR_MAPPING_INDEX == best_mapping)6521{6522pBlock->m_sels[0] = pSelector->m_selectors[0];6523pBlock->m_sels[1] = pSelector->m_selectors[1];6524pBlock->m_sels[2] = pSelector->m_selectors[2];6525pBlock->m_sels[3] = pSelector->m_selectors[3];6526}6527else6528{6529const uint8_t* pSelectors_xlat = &g_etc1s_to_atc_selector_mappings[best_mapping][0];65306531const uint32_t sel_bits0 = pSelector->m_selectors[0];6532const uint32_t sel_bits1 = pSelector->m_selectors[1];6533const uint32_t sel_bits2 = pSelector->m_selectors[2];6534const uint32_t sel_bits3 = pSelector->m_selectors[3];65356536uint32_t atc_sels0 = 0, atc_sels1 = 0, atc_sels2 = 0, atc_sels3 = 0;65376538#define DO_X(x) { \6539const uint32_t x_shift = (x) * 2; \6540atc_sels0 |= (pSelectors_xlat[(sel_bits0 >> x_shift) & 3] << x_shift); \6541atc_sels1 |= (pSelectors_xlat[(sel_bits1 >> x_shift) & 3] << x_shift); \6542atc_sels2 |= (pSelectors_xlat[(sel_bits2 >> x_shift) & 3] << x_shift); \6543atc_sels3 |= (pSelectors_xlat[(sel_bits3 >> x_shift) & 3] << x_shift); }65446545DO_X(0);6546DO_X(1);6547DO_X(2);6548DO_X(3);6549#undef DO_X65506551pBlock->m_sels[0] = (uint8_t)atc_sels0;6552pBlock->m_sels[1] = (uint8_t)atc_sels1;6553pBlock->m_sels[2] = (uint8_t)atc_sels2;6554pBlock->m_sels[3] = (uint8_t)atc_sels3;6555}6556}65576558#if BASISD_WRITE_NEW_ATC_TABLES6559static void create_etc1s_to_atc_conversion_tables()6560{6561// ATC 556562FILE* pFile = nullptr;6563fopen_s(&pFile, "basisu_transcoder_tables_atc_55.inc", "w");65646565uint32_t n = 0;65666567for (int inten = 0; inten < 8; inten++)6568{6569for (uint32_t g = 0; g < 32; g++)6570{6571color32 block_colors[4];6572decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);65736574for (uint32_t sr = 0; sr < NUM_ETC1S_TO_ATC_SELECTOR_RANGES; sr++)6575{6576const uint32_t low_selector = g_etc1s_to_atc_selector_ranges[sr].m_low;6577const uint32_t high_selector = g_etc1s_to_atc_selector_ranges[sr].m_high;65786579for (uint32_t m = 0; m < NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS; m++)6580{6581uint32_t best_lo = 0;6582uint32_t best_hi = 0;6583uint64_t best_err = UINT64_MAX;65846585for (uint32_t hi = 0; hi <= 31; hi++)6586{6587for (uint32_t lo = 0; lo <= 31; lo++)6588{6589uint32_t colors[4];65906591colors[0] = (lo << 3) | (lo >> 2);6592colors[3] = (hi << 3) | (hi >> 2);65936594colors[1] = (colors[0] * 5 + colors[3] * 3) / 8;6595colors[2] = (colors[3] * 5 + colors[0] * 3) / 8;65966597uint64_t total_err = 0;65986599for (uint32_t s = low_selector; s <= high_selector; s++)6600{6601int err = block_colors[s].g - colors[g_etc1s_to_atc_selector_mappings[m][s]];66026603int err_scale = 1;6604// Special case when the intensity table is 7, low_selector is 0, and high_selector is 3. In this extreme case, it's likely the encoder is trying to strongly favor6605// the low/high selectors which are clamping to either 0 or 255.6606if (((inten == 7) && (low_selector == 0) && (high_selector == 3)) && ((s == 0) || (s == 3)))6607err_scale = 5;66086609total_err += (err * err) * err_scale;6610}66116612if (total_err < best_err)6613{6614best_err = total_err;6615best_lo = lo;6616best_hi = hi;6617}6618}6619}66206621//assert(best_err <= 0xFFFF);6622best_err = basisu::minimum<uint32_t>(best_err, 0xFFFF);66236624fprintf(pFile, "{%u,%u,%u},", best_lo, best_hi, (uint32_t)best_err);6625n++;6626if ((n & 31) == 31)6627fprintf(pFile, "\n");6628} // m6629} // sr6630} // g6631} // inten66326633fclose(pFile);6634pFile = nullptr;66356636// ATC 566637fopen_s(&pFile, "basisu_transcoder_tables_atc_56.inc", "w");66386639n = 0;66406641for (int inten = 0; inten < 8; inten++)6642{6643for (uint32_t g = 0; g < 32; g++)6644{6645color32 block_colors[4];6646decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);66476648for (uint32_t sr = 0; sr < NUM_ETC1S_TO_ATC_SELECTOR_RANGES; sr++)6649{6650const uint32_t low_selector = g_etc1s_to_atc_selector_ranges[sr].m_low;6651const uint32_t high_selector = g_etc1s_to_atc_selector_ranges[sr].m_high;66526653for (uint32_t m = 0; m < NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS; m++)6654{6655uint32_t best_lo = 0;6656uint32_t best_hi = 0;6657uint64_t best_err = UINT64_MAX;66586659for (uint32_t hi = 0; hi <= 63; hi++)6660{6661for (uint32_t lo = 0; lo <= 31; lo++)6662{6663uint32_t colors[4];66646665colors[0] = (lo << 3) | (lo >> 2);6666colors[3] = (hi << 2) | (hi >> 4);66676668colors[1] = (colors[0] * 5 + colors[3] * 3) / 8;6669colors[2] = (colors[3] * 5 + colors[0] * 3) / 8;66706671uint64_t total_err = 0;66726673for (uint32_t s = low_selector; s <= high_selector; s++)6674{6675int err = block_colors[s].g - colors[g_etc1s_to_atc_selector_mappings[m][s]];66766677int err_scale = 1;6678// Special case when the intensity table is 7, low_selector is 0, and high_selector is 3. In this extreme case, it's likely the encoder is trying to strongly favor6679// the low/high selectors which are clamping to either 0 or 255.6680if (((inten == 7) && (low_selector == 0) && (high_selector == 3)) && ((s == 0) || (s == 3)))6681err_scale = 5;66826683total_err += (err * err) * err_scale;6684}66856686if (total_err < best_err)6687{6688best_err = total_err;6689best_lo = lo;6690best_hi = hi;6691}6692}6693}66946695//assert(best_err <= 0xFFFF);6696best_err = basisu::minimum<uint32_t>(best_err, 0xFFFF);66976698fprintf(pFile, "{%u,%u,%u},", best_lo, best_hi, (uint32_t)best_err);6699n++;6700if ((n & 31) == 31)6701fprintf(pFile, "\n");6702} // m6703} // sr6704} // g6705} // inten67066707fclose(pFile);67086709// PVRTC2 456710fopen_s(&pFile, "basisu_transcoder_tables_pvrtc2_45.inc", "w");67116712n = 0;67136714for (int inten = 0; inten < 8; inten++)6715{6716for (uint32_t g = 0; g < 32; g++)6717{6718color32 block_colors[4];6719decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);67206721for (uint32_t sr = 0; sr < NUM_ETC1S_TO_ATC_SELECTOR_RANGES; sr++)6722{6723const uint32_t low_selector = g_etc1s_to_atc_selector_ranges[sr].m_low;6724const uint32_t high_selector = g_etc1s_to_atc_selector_ranges[sr].m_high;67256726for (uint32_t m = 0; m < NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS; m++)6727{6728uint32_t best_lo = 0;6729uint32_t best_hi = 0;6730uint64_t best_err = UINT64_MAX;67316732for (uint32_t hi = 0; hi <= 31; hi++)6733{6734for (uint32_t lo = 0; lo <= 15; lo++)6735{6736uint32_t colors[4];67376738colors[0] = (lo << 1) | (lo >> 3);6739colors[0] = (colors[0] << 3) | (colors[0] >> 2);67406741colors[3] = (hi << 3) | (hi >> 2);67426743colors[1] = (colors[0] * 5 + colors[3] * 3) / 8;6744colors[2] = (colors[3] * 5 + colors[0] * 3) / 8;67456746uint64_t total_err = 0;67476748for (uint32_t s = low_selector; s <= high_selector; s++)6749{6750int err = block_colors[s].g - colors[g_etc1s_to_atc_selector_mappings[m][s]];67516752int err_scale = 1;6753// Special case when the intensity table is 7, low_selector is 0, and high_selector is 3. In this extreme case, it's likely the encoder is trying to strongly favor6754// the low/high selectors which are clamping to either 0 or 255.6755if (((inten == 7) && (low_selector == 0) && (high_selector == 3)) && ((s == 0) || (s == 3)))6756err_scale = 5;67576758total_err += (err * err) * err_scale;6759}67606761if (total_err < best_err)6762{6763best_err = total_err;6764best_lo = lo;6765best_hi = hi;6766}6767}6768}67696770//assert(best_err <= 0xFFFF);6771best_err = basisu::minimum<uint32_t>(best_err, 0xFFFF);67726773fprintf(pFile, "{%u,%u,%u},", best_lo, best_hi, (uint32_t)best_err);6774n++;6775if ((n & 31) == 31)6776fprintf(pFile, "\n");6777} // m6778} // sr6779} // g6780} // inten67816782fclose(pFile);67836784#if 06785// PVRTC2 346786fopen_s(&pFile, "basisu_transcoder_tables_pvrtc2_34.inc", "w");67876788n = 0;67896790for (int inten = 0; inten < 8; inten++)6791{6792for (uint32_t g = 0; g < 32; g++)6793{6794color32 block_colors[4];6795decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);67966797for (uint32_t sr = 0; sr < NUM_ETC1S_TO_ATC_SELECTOR_RANGES; sr++)6798{6799const uint32_t low_selector = g_etc1s_to_atc_selector_ranges[sr].m_low;6800const uint32_t high_selector = g_etc1s_to_atc_selector_ranges[sr].m_high;68016802for (uint32_t m = 0; m < NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS; m++)6803{6804uint32_t best_lo = 0;6805uint32_t best_hi = 0;6806uint64_t best_err = UINT64_MAX;68076808for (uint32_t hi = 0; hi <= 15; hi++)6809{6810for (uint32_t lo = 0; lo <= 7; lo++)6811{6812uint32_t colors[4];68136814colors[0] = (lo << 2) | (lo >> 1);6815colors[0] = (colors[0] << 3) | (colors[0] >> 2);68166817colors[3] = (hi << 1) | (hi >> 3);6818colors[3] = (colors[3] << 3) | (colors[3] >> 2);68196820colors[1] = (colors[0] * 5 + colors[3] * 3) / 8;6821colors[2] = (colors[3] * 5 + colors[0] * 3) / 8;68226823uint64_t total_err = 0;68246825for (uint32_t s = low_selector; s <= high_selector; s++)6826{6827int err = block_colors[s].g - colors[g_etc1s_to_atc_selector_mappings[m][s]];68286829int err_scale = 1;6830// Special case when the intensity table is 7, low_selector is 0, and high_selector is 3. In this extreme case, it's likely the encoder is trying to strongly favor6831// the low/high selectors which are clamping to either 0 or 255.6832if (((inten == 7) && (low_selector == 0) && (high_selector == 3)) && ((s == 0) || (s == 3)))6833err_scale = 5;68346835total_err += (err * err) * err_scale;6836}68376838if (total_err < best_err)6839{6840best_err = total_err;6841best_lo = lo;6842best_hi = hi;6843}6844}6845}68466847//assert(best_err <= 0xFFFF);6848best_err = basisu::minimum<uint32_t>(best_err, 0xFFFF);68496850fprintf(pFile, "{%u,%u,%u},", best_lo, best_hi, (uint32_t)best_err);6851n++;6852if ((n & 31) == 31)6853fprintf(pFile, "\n");6854} // m6855} // sr6856} // g6857} // inten68586859fclose(pFile);6860#endif6861#if 06862// PVRTC2 446863fopen_s(&pFile, "basisu_transcoder_tables_pvrtc2_44.inc", "w");68646865n = 0;68666867for (int inten = 0; inten < 8; inten++)6868{6869for (uint32_t g = 0; g < 32; g++)6870{6871color32 block_colors[4];6872decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);68736874for (uint32_t sr = 0; sr < NUM_ETC1S_TO_ATC_SELECTOR_RANGES; sr++)6875{6876const uint32_t low_selector = g_etc1s_to_atc_selector_ranges[sr].m_low;6877const uint32_t high_selector = g_etc1s_to_atc_selector_ranges[sr].m_high;68786879for (uint32_t m = 0; m < NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS; m++)6880{6881uint32_t best_lo = 0;6882uint32_t best_hi = 0;6883uint64_t best_err = UINT64_MAX;68846885for (uint32_t hi = 0; hi <= 15; hi++)6886{6887for (uint32_t lo = 0; lo <= 15; lo++)6888{6889uint32_t colors[4];68906891colors[0] = (lo << 1) | (lo >> 3);6892colors[0] = (colors[0] << 3) | (colors[0] >> 2);68936894colors[3] = (hi << 1) | (hi >> 3);6895colors[3] = (colors[3] << 3) | (colors[3] >> 2);68966897colors[1] = (colors[0] * 5 + colors[3] * 3) / 8;6898colors[2] = (colors[3] * 5 + colors[0] * 3) / 8;68996900uint64_t total_err = 0;69016902for (uint32_t s = low_selector; s <= high_selector; s++)6903{6904int err = block_colors[s].g - colors[g_etc1s_to_atc_selector_mappings[m][s]];69056906int err_scale = 1;6907// Special case when the intensity table is 7, low_selector is 0, and high_selector is 3. In this extreme case, it's likely the encoder is trying to strongly favor6908// the low/high selectors which are clamping to either 0 or 255.6909if (((inten == 7) && (low_selector == 0) && (high_selector == 3)) && ((s == 0) || (s == 3)))6910err_scale = 5;69116912total_err += (err * err) * err_scale;6913}69146915if (total_err < best_err)6916{6917best_err = total_err;6918best_lo = lo;6919best_hi = hi;6920}6921}6922}69236924//assert(best_err <= 0xFFFF);6925best_err = basisu::minimum<uint32_t>(best_err, 0xFFFF);69266927fprintf(pFile, "{%u,%u,%u},", best_lo, best_hi, (uint32_t)best_err);6928n++;6929if ((n & 31) == 31)6930fprintf(pFile, "\n");6931} // m6932} // sr6933} // g6934} // inten69356936fclose(pFile);6937#endif69386939// PVRTC2 alpha 336940fopen_s(&pFile, "basisu_transcoder_tables_pvrtc2_alpha_33.inc", "w");69416942n = 0;69436944for (int inten = 0; inten < 8; inten++)6945{6946for (uint32_t g = 0; g < 32; g++)6947{6948color32 block_colors[4];6949decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);69506951for (uint32_t sr = 0; sr < NUM_ETC1S_TO_ATC_SELECTOR_RANGES; sr++)6952{6953const uint32_t low_selector = g_etc1s_to_atc_selector_ranges[sr].m_low;6954const uint32_t high_selector = g_etc1s_to_atc_selector_ranges[sr].m_high;69556956for (uint32_t m = 0; m < NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS; m++)6957{6958uint32_t best_lo = 0;6959uint32_t best_hi = 0;6960uint64_t best_err = UINT64_MAX;69616962for (uint32_t hi = 0; hi <= 7; hi++)6963{6964for (uint32_t lo = 0; lo <= 7; lo++)6965{6966uint32_t colors[4];69676968colors[0] = (lo << 1);6969colors[0] = (colors[0] << 4) | colors[0];69706971colors[3] = (hi << 1) | 1;6972colors[3] = (colors[3] << 4) | colors[3];69736974colors[1] = (colors[0] * 5 + colors[3] * 3) / 8;6975colors[2] = (colors[3] * 5 + colors[0] * 3) / 8;69766977uint64_t total_err = 0;69786979for (uint32_t s = low_selector; s <= high_selector; s++)6980{6981int err = block_colors[s].g - colors[g_etc1s_to_atc_selector_mappings[m][s]];69826983int err_scale = 1;6984// Special case when the intensity table is 7, low_selector is 0, and high_selector is 3. In this extreme case, it's likely the encoder is trying to strongly favor6985// the low/high selectors which are clamping to either 0 or 255.6986if (((inten == 7) && (low_selector == 0) && (high_selector == 3)) && ((s == 0) || (s == 3)))6987err_scale = 5;69886989total_err += (err * err) * err_scale;6990}69916992if (total_err < best_err)6993{6994best_err = total_err;6995best_lo = lo;6996best_hi = hi;6997}6998}6999}70007001//assert(best_err <= 0xFFFF);7002best_err = basisu::minimum<uint32_t>(best_err, 0xFFFF);70037004fprintf(pFile, "{%u,%u,%u},", best_lo, best_hi, (uint32_t)best_err);7005n++;7006if ((n & 31) == 31)7007fprintf(pFile, "\n");7008} // m7009} // sr7010} // g7011} // inten70127013fclose(pFile);7014}7015#endif // BASISD_WRITE_NEW_ATC_TABLES70167017#endif // BASISD_SUPPORT_ATC70187019#if BASISD_SUPPORT_PVRTC27020struct pvrtc2_block7021{7022uint8_t m_modulation[4];70237024union7025{7026union7027{7028// Opaque mode: RGB colora=554 and colorb=5557029struct7030{7031uint32_t m_mod_flag : 1;7032uint32_t m_blue_a : 4;7033uint32_t m_green_a : 5;7034uint32_t m_red_a : 5;7035uint32_t m_hard_flag : 1;7036uint32_t m_blue_b : 5;7037uint32_t m_green_b : 5;7038uint32_t m_red_b : 5;7039uint32_t m_opaque_flag : 1;70407041} m_opaque_color_data;70427043// Transparent mode: RGBA colora=4433 and colorb=44437044struct7045{7046uint32_t m_mod_flag : 1;7047uint32_t m_blue_a : 3;7048uint32_t m_green_a : 4;7049uint32_t m_red_a : 4;7050uint32_t m_alpha_a : 3;7051uint32_t m_hard_flag : 1;7052uint32_t m_blue_b : 4;7053uint32_t m_green_b : 4;7054uint32_t m_red_b : 4;7055uint32_t m_alpha_b : 3;7056uint32_t m_opaque_flag : 1;70577058} m_trans_color_data;7059};70607061uint32_t m_color_data_bits;7062};70637064// 5547065void set_low_color(uint32_t r, uint32_t g, uint32_t b)7066{7067assert((r < 32) && (g < 32) && (b < 16));7068m_opaque_color_data.m_red_a = r;7069m_opaque_color_data.m_green_a = g;7070m_opaque_color_data.m_blue_a = b;7071}70727073// 5557074void set_high_color(uint32_t r, uint32_t g, uint32_t b)7075{7076assert((r < 32) && (g < 32) && (b < 32));7077m_opaque_color_data.m_red_b = r;7078m_opaque_color_data.m_green_b = g;7079m_opaque_color_data.m_blue_b = b;7080}70817082// 44337083void set_trans_low_color(uint32_t r, uint32_t g, uint32_t b, uint32_t a)7084{7085assert((r < 16) && (g < 16) && (b < 8) && (a < 8));7086m_trans_color_data.m_red_a = r;7087m_trans_color_data.m_green_a = g;7088m_trans_color_data.m_blue_a = b;7089m_trans_color_data.m_alpha_a = a;7090}70917092// 44437093void set_trans_high_color(uint32_t r, uint32_t g, uint32_t b, uint32_t a)7094{7095assert((r < 16) && (g < 16) && (b < 16) && (a < 8));7096m_trans_color_data.m_red_b = r;7097m_trans_color_data.m_green_b = g;7098m_trans_color_data.m_blue_b = b;7099m_trans_color_data.m_alpha_b = a;7100}7101};71027103static struct7104{7105uint8_t m_l, m_h;7106} g_pvrtc2_trans_match34[256];71077108static struct7109{7110uint8_t m_l, m_h;7111} g_pvrtc2_trans_match44[256];71127113static struct7114{7115uint8_t m_l, m_h;7116} g_pvrtc2_alpha_match33[256];71177118static struct7119{7120uint8_t m_l, m_h;7121} g_pvrtc2_alpha_match33_0[256];71227123static struct7124{7125uint8_t m_l, m_h;7126} g_pvrtc2_alpha_match33_3[256];71277128// PVRTC2 can be forced to look like a slightly weaker variant of ATC/BC1, so that's what we do here for simplicity.7129static void convert_etc1s_to_pvrtc2_rgb(void* pDst, const endpoint* pEndpoints, const selector* pSelector)7130{7131pvrtc2_block* pBlock = static_cast<pvrtc2_block*>(pDst);71327133pBlock->m_opaque_color_data.m_hard_flag = 1;7134pBlock->m_opaque_color_data.m_mod_flag = 0;7135pBlock->m_opaque_color_data.m_opaque_flag = 1;71367137const uint32_t low_selector = pSelector->m_lo_selector;7138const uint32_t high_selector = pSelector->m_hi_selector;71397140const color32& base_color = pEndpoints->m_color5;7141const uint32_t inten_table = pEndpoints->m_inten5;71427143if (low_selector == high_selector)7144{7145uint32_t r, g, b;7146decoder_etc_block::get_block_color5(base_color, inten_table, low_selector, r, g, b);71477148pBlock->set_low_color(g_atc_match55_equals_1[r].m_lo, g_atc_match55_equals_1[g].m_lo, g_pvrtc2_match45_equals_1[b].m_lo);7149pBlock->set_high_color(g_atc_match55_equals_1[r].m_hi, g_atc_match55_equals_1[g].m_hi, g_pvrtc2_match45_equals_1[b].m_hi);71507151pBlock->m_modulation[0] = 0x55;7152pBlock->m_modulation[1] = 0x55;7153pBlock->m_modulation[2] = 0x55;7154pBlock->m_modulation[3] = 0x55;71557156return;7157}7158else if ((inten_table >= 7) && (pSelector->m_num_unique_selectors == 2) && (pSelector->m_lo_selector == 0) && (pSelector->m_hi_selector == 3))7159{7160color32 block_colors[4];7161decoder_etc_block::get_block_colors5(block_colors, base_color, inten_table);71627163const uint32_t r0 = block_colors[0].r;7164const uint32_t g0 = block_colors[0].g;7165const uint32_t b0 = block_colors[0].b;71667167const uint32_t r1 = block_colors[3].r;7168const uint32_t g1 = block_colors[3].g;7169const uint32_t b1 = block_colors[3].b;71707171pBlock->set_low_color(g_atc_match5[r0].m_hi, g_atc_match5[g0].m_hi, g_pvrtc2_match4[b0].m_hi);7172pBlock->set_high_color(g_atc_match5[r1].m_hi, g_atc_match5[g1].m_hi, g_atc_match5[b1].m_hi);71737174pBlock->m_modulation[0] = pSelector->m_selectors[0];7175pBlock->m_modulation[1] = pSelector->m_selectors[1];7176pBlock->m_modulation[2] = pSelector->m_selectors[2];7177pBlock->m_modulation[3] = pSelector->m_selectors[3];71787179return;7180}71817182const uint32_t selector_range_table = g_etc1s_to_atc_selector_range_index[low_selector][high_selector];71837184//[32][8][RANGES][MAPPING]7185const etc1s_to_atc_solution* pTable_r = &g_etc1s_to_atc_55[(inten_table * 32 + base_color.r) * (NUM_ETC1S_TO_ATC_SELECTOR_RANGES * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS];7186const etc1s_to_atc_solution* pTable_g = &g_etc1s_to_atc_55[(inten_table * 32 + base_color.g) * (NUM_ETC1S_TO_ATC_SELECTOR_RANGES * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS];7187const etc1s_to_atc_solution* pTable_b = &g_etc1s_to_pvrtc2_45[(inten_table * 32 + base_color.b) * (NUM_ETC1S_TO_ATC_SELECTOR_RANGES * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS];71887189uint32_t best_err = UINT_MAX;7190uint32_t best_mapping = 0;71917192assert(NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS == 10);7193#define DO_ITER(m) { uint32_t total_err = pTable_r[m].m_err + pTable_g[m].m_err + pTable_b[m].m_err; if (total_err < best_err) { best_err = total_err; best_mapping = m; } }7194DO_ITER(0); DO_ITER(1); DO_ITER(2); DO_ITER(3); DO_ITER(4);7195DO_ITER(5); DO_ITER(6); DO_ITER(7); DO_ITER(8); DO_ITER(9);7196#undef DO_ITER71977198pBlock->set_low_color(pTable_r[best_mapping].m_lo, pTable_g[best_mapping].m_lo, pTable_b[best_mapping].m_lo);7199pBlock->set_high_color(pTable_r[best_mapping].m_hi, pTable_g[best_mapping].m_hi, pTable_b[best_mapping].m_hi);72007201if (ATC_IDENTITY_SELECTOR_MAPPING_INDEX == best_mapping)7202{7203pBlock->m_modulation[0] = pSelector->m_selectors[0];7204pBlock->m_modulation[1] = pSelector->m_selectors[1];7205pBlock->m_modulation[2] = pSelector->m_selectors[2];7206pBlock->m_modulation[3] = pSelector->m_selectors[3];7207}7208else7209{7210// TODO: We could make this faster using several precomputed 256 entry tables, like ETC1S->BC1 does.7211const uint8_t* pSelectors_xlat = &g_etc1s_to_atc_selector_mappings[best_mapping][0];72127213const uint32_t sel_bits0 = pSelector->m_selectors[0];7214const uint32_t sel_bits1 = pSelector->m_selectors[1];7215const uint32_t sel_bits2 = pSelector->m_selectors[2];7216const uint32_t sel_bits3 = pSelector->m_selectors[3];72177218uint32_t sels0 = 0, sels1 = 0, sels2 = 0, sels3 = 0;72197220#define DO_X(x) { \7221const uint32_t x_shift = (x) * 2; \7222sels0 |= (pSelectors_xlat[(sel_bits0 >> x_shift) & 3] << x_shift); \7223sels1 |= (pSelectors_xlat[(sel_bits1 >> x_shift) & 3] << x_shift); \7224sels2 |= (pSelectors_xlat[(sel_bits2 >> x_shift) & 3] << x_shift); \7225sels3 |= (pSelectors_xlat[(sel_bits3 >> x_shift) & 3] << x_shift); }72267227DO_X(0);7228DO_X(1);7229DO_X(2);7230DO_X(3);7231#undef DO_X72327233pBlock->m_modulation[0] = (uint8_t)sels0;7234pBlock->m_modulation[1] = (uint8_t)sels1;7235pBlock->m_modulation[2] = (uint8_t)sels2;7236pBlock->m_modulation[3] = (uint8_t)sels3;7237}7238}72397240static inline vec4F* vec4F_set_scalar(vec4F* pV, float x) { pV->c[0] = x; pV->c[1] = x; pV->c[2] = x; pV->c[3] = x; return pV; }7241static inline vec4F* vec4F_set(vec4F* pV, float x, float y, float z, float w) { pV->c[0] = x; pV->c[1] = y; pV->c[2] = z; pV->c[3] = w; return pV; }7242static inline vec4F* vec4F_saturate_in_place(vec4F* pV) { pV->c[0] = saturate(pV->c[0]); pV->c[1] = saturate(pV->c[1]); pV->c[2] = saturate(pV->c[2]); pV->c[3] = saturate(pV->c[3]); return pV; }7243static inline vec4F vec4F_saturate(const vec4F* pV) { vec4F res; res.c[0] = saturate(pV->c[0]); res.c[1] = saturate(pV->c[1]); res.c[2] = saturate(pV->c[2]); res.c[3] = saturate(pV->c[3]); return res; }7244static inline vec4F vec4F_from_color(const color32* pC) { vec4F res; vec4F_set(&res, pC->c[0], pC->c[1], pC->c[2], pC->c[3]); return res; }7245static inline vec4F vec4F_add(const vec4F* pLHS, const vec4F* pRHS) { vec4F res; vec4F_set(&res, pLHS->c[0] + pRHS->c[0], pLHS->c[1] + pRHS->c[1], pLHS->c[2] + pRHS->c[2], pLHS->c[3] + pRHS->c[3]); return res; }7246static inline vec4F vec4F_sub(const vec4F* pLHS, const vec4F* pRHS) { vec4F res; vec4F_set(&res, pLHS->c[0] - pRHS->c[0], pLHS->c[1] - pRHS->c[1], pLHS->c[2] - pRHS->c[2], pLHS->c[3] - pRHS->c[3]); return res; }7247static inline float vec4F_dot(const vec4F* pLHS, const vec4F* pRHS) { return pLHS->c[0] * pRHS->c[0] + pLHS->c[1] * pRHS->c[1] + pLHS->c[2] * pRHS->c[2] + pLHS->c[3] * pRHS->c[3]; }7248static inline vec4F vec4F_mul(const vec4F* pLHS, float s) { vec4F res; vec4F_set(&res, pLHS->c[0] * s, pLHS->c[1] * s, pLHS->c[2] * s, pLHS->c[3] * s); return res; }7249static inline vec4F* vec4F_normalize_in_place(vec4F* pV) { float s = pV->c[0] * pV->c[0] + pV->c[1] * pV->c[1] + pV->c[2] * pV->c[2] + pV->c[3] * pV->c[3]; if (s != 0.0f) { s = 1.0f / sqrtf(s); pV->c[0] *= s; pV->c[1] *= s; pV->c[2] *= s; pV->c[3] *= s; } return pV; }72507251static color32 convert_rgba_5554_to_8888(const color32& col)7252{7253return color32((col[0] << 3) | (col[0] >> 2), (col[1] << 3) | (col[1] >> 2), (col[2] << 3) | (col[2] >> 2), (col[3] << 4) | col[3]);7254}72557256static inline int sq(int x) { return x * x; }72577258// PVRTC2 is a slightly borked format for alpha: In Non-Interpolated mode, the way AlphaB8 is expanded from 4 to 8 bits means it can never be 0.7259// This is actually very bad, because on 100% transparent blocks which have non-trivial color pixels, part of the color channel will leak into alpha!7260// And there's nothing straightforward we can do because using the other modes is too expensive/complex. I can see why Apple didn't adopt it.7261static void convert_etc1s_to_pvrtc2_rgba(void* pDst, const endpoint* pEndpoints, const selector* pSelector, const endpoint* pEndpoint_codebook, const selector* pSelector_codebook)7262{7263pvrtc2_block* pBlock = static_cast<pvrtc2_block*>(pDst);72647265const endpoint& alpha_endpoint = pEndpoint_codebook[((uint16_t*)pBlock)[0]];7266const selector& alpha_selectors = pSelector_codebook[((uint16_t*)pBlock)[1]];72677268pBlock->m_opaque_color_data.m_hard_flag = 1;7269pBlock->m_opaque_color_data.m_mod_flag = 0;7270pBlock->m_opaque_color_data.m_opaque_flag = 0;72717272const int num_unique_alpha_selectors = alpha_selectors.m_num_unique_selectors;72737274const color32& alpha_base_color = alpha_endpoint.m_color5;7275const uint32_t alpha_inten_table = alpha_endpoint.m_inten5;72767277int constant_alpha_val = -1;72787279int alpha_block_colors[4];7280decoder_etc_block::get_block_colors5_g(alpha_block_colors, alpha_base_color, alpha_inten_table);72817282if (num_unique_alpha_selectors == 1)7283{7284constant_alpha_val = alpha_block_colors[alpha_selectors.m_lo_selector];7285}7286else7287{7288constant_alpha_val = alpha_block_colors[alpha_selectors.m_lo_selector];72897290for (uint32_t i = alpha_selectors.m_lo_selector + 1; i <= alpha_selectors.m_hi_selector; i++)7291{7292if (constant_alpha_val != alpha_block_colors[i])7293{7294constant_alpha_val = -1;7295break;7296}7297}7298}72997300if (constant_alpha_val >= 250)7301{7302// It's opaque enough, so don't bother trying to encode it as an alpha block.7303convert_etc1s_to_pvrtc2_rgb(pDst, pEndpoints, pSelector);7304return;7305}73067307const color32& base_color = pEndpoints->m_color5;7308const uint32_t inten_table = pEndpoints->m_inten5;73097310const uint32_t low_selector = pSelector->m_lo_selector;7311const uint32_t high_selector = pSelector->m_hi_selector;73127313const int num_unique_color_selectors = pSelector->m_num_unique_selectors;73147315// We need to reencode the block at the pixel level, unfortunately, from two ETC1S planes.7316// Do 4D incremental PCA, project all pixels to this hyperline, then quantize to packed endpoints and compute the modulation values.7317const int br = (base_color.r << 3) | (base_color.r >> 2);7318const int bg = (base_color.g << 3) | (base_color.g >> 2);7319const int bb = (base_color.b << 3) | (base_color.b >> 2);73207321color32 block_cols[4];7322for (uint32_t i = 0; i < 4; i++)7323{7324const int ci = g_etc1_inten_tables[inten_table][i];7325block_cols[i].set_clamped(br + ci, bg + ci, bb + ci, alpha_block_colors[i]);7326}73277328bool solid_color_block = true;7329if (num_unique_color_selectors > 1)7330{7331for (uint32_t i = low_selector + 1; i <= high_selector; i++)7332{7333if ((block_cols[low_selector].r != block_cols[i].r) || (block_cols[low_selector].g != block_cols[i].g) || (block_cols[low_selector].b != block_cols[i].b))7334{7335solid_color_block = false;7336break;7337}7338}7339}73407341if ((solid_color_block) && (constant_alpha_val >= 0))7342{7343// Constant color/alpha block.7344// This is more complex than it may seem because of the way color and alpha are packed in PVRTC2. We need to evaluate mod0, mod1 and mod3 encodings to find the best one.7345uint32_t r, g, b;7346decoder_etc_block::get_block_color5(base_color, inten_table, low_selector, r, g, b);73477348// Mod 07349uint32_t lr0 = (r * 15 + 128) / 255, lg0 = (g * 15 + 128) / 255, lb0 = (b * 7 + 128) / 255;7350uint32_t la0 = g_pvrtc2_alpha_match33_0[constant_alpha_val].m_l;73517352uint32_t cr0 = (lr0 << 1) | (lr0 >> 3);7353uint32_t cg0 = (lg0 << 1) | (lg0 >> 3);7354uint32_t cb0 = (lb0 << 2) | (lb0 >> 1);7355uint32_t ca0 = (la0 << 1);73567357cr0 = (cr0 << 3) | (cr0 >> 2);7358cg0 = (cg0 << 3) | (cg0 >> 2);7359cb0 = (cb0 << 3) | (cb0 >> 2);7360ca0 = (ca0 << 4) | ca0;73617362uint32_t err0 = sq(cr0 - r) + sq(cg0 - g) + sq(cb0 - b) + sq(ca0 - constant_alpha_val) * 2;73637364// If the alpha is < 3 or so we're kinda screwed. It's better to have some RGB error than it is to turn a 100% transparent area slightly opaque.7365if ((err0 == 0) || (constant_alpha_val < 3))7366{7367pBlock->set_trans_low_color(lr0, lg0, lb0, la0);7368pBlock->set_trans_high_color(0, 0, 0, 0);73697370pBlock->m_modulation[0] = 0;7371pBlock->m_modulation[1] = 0;7372pBlock->m_modulation[2] = 0;7373pBlock->m_modulation[3] = 0;7374return;7375}73767377// Mod 37378uint32_t lr3 = (r * 15 + 128) / 255, lg3 = (g * 15 + 128) / 255, lb3 = (b * 15 + 128) / 255;7379uint32_t la3 = g_pvrtc2_alpha_match33_3[constant_alpha_val].m_l;73807381uint32_t cr3 = (lr3 << 1) | (lr3 >> 3);7382uint32_t cg3 = (lg3 << 1) | (lg3 >> 3);7383uint32_t cb3 = (lb3 << 1) | (lb3 >> 3);7384uint32_t ca3 = (la3 << 1) | 1;73857386cr3 = (cr3 << 3) | (cr3 >> 2);7387cg3 = (cg3 << 3) | (cg3 >> 2);7388cb3 = (cb3 << 3) | (cb3 >> 2);7389ca3 = (ca3 << 4) | ca3;73907391uint32_t err3 = sq(cr3 - r) + sq(cg3 - g) + sq(cb3 - b) + sq(ca3 - constant_alpha_val) * 2;73927393// Mod 17394uint32_t lr1 = g_pvrtc2_trans_match44[r].m_l, lg1 = g_pvrtc2_trans_match44[g].m_l, lb1 = g_pvrtc2_trans_match34[b].m_l;7395uint32_t hr1 = g_pvrtc2_trans_match44[r].m_h, hg1 = g_pvrtc2_trans_match44[g].m_h, hb1 = g_pvrtc2_trans_match34[b].m_h;7396uint32_t la1 = g_pvrtc2_alpha_match33[constant_alpha_val].m_l, ha1 = g_pvrtc2_alpha_match33[constant_alpha_val].m_h;73977398uint32_t clr1 = (lr1 << 1) | (lr1 >> 3);7399uint32_t clg1 = (lg1 << 1) | (lg1 >> 3);7400uint32_t clb1 = (lb1 << 2) | (lb1 >> 1);7401uint32_t cla1 = (la1 << 1);74027403clr1 = (clr1 << 3) | (clr1 >> 2);7404clg1 = (clg1 << 3) | (clg1 >> 2);7405clb1 = (clb1 << 3) | (clb1 >> 2);7406cla1 = (cla1 << 4) | cla1;74077408uint32_t chr1 = (hr1 << 1) | (hr1 >> 3);7409uint32_t chg1 = (hg1 << 1) | (hg1 >> 3);7410uint32_t chb1 = (hb1 << 1) | (hb1 >> 3);7411uint32_t cha1 = (ha1 << 1) | 1;74127413chr1 = (chr1 << 3) | (chr1 >> 2);7414chg1 = (chg1 << 3) | (chg1 >> 2);7415chb1 = (chb1 << 3) | (chb1 >> 2);7416cha1 = (cha1 << 4) | cha1;74177418uint32_t r1 = (clr1 * 5 + chr1 * 3) / 8;7419uint32_t g1 = (clg1 * 5 + chg1 * 3) / 8;7420uint32_t b1 = (clb1 * 5 + chb1 * 3) / 8;7421uint32_t a1 = (cla1 * 5 + cha1 * 3) / 8;74227423uint32_t err1 = sq(r1 - r) + sq(g1 - g) + sq(b1 - b) + sq(a1 - constant_alpha_val) * 2;74247425if ((err1 < err0) && (err1 < err3))7426{7427pBlock->set_trans_low_color(lr1, lg1, lb1, la1);7428pBlock->set_trans_high_color(hr1, hg1, hb1, ha1);74297430pBlock->m_modulation[0] = 0x55;7431pBlock->m_modulation[1] = 0x55;7432pBlock->m_modulation[2] = 0x55;7433pBlock->m_modulation[3] = 0x55;7434}7435else if (err0 < err3)7436{7437pBlock->set_trans_low_color(lr0, lg0, lb0, la0);7438pBlock->set_trans_high_color(0, 0, 0, 0);74397440pBlock->m_modulation[0] = 0;7441pBlock->m_modulation[1] = 0;7442pBlock->m_modulation[2] = 0;7443pBlock->m_modulation[3] = 0;7444}7445else7446{7447pBlock->set_trans_low_color(0, 0, 0, 0);7448pBlock->set_trans_high_color(lr3, lg3, lb3, la3);74497450pBlock->m_modulation[0] = 0xFF;7451pBlock->m_modulation[1] = 0xFF;7452pBlock->m_modulation[2] = 0xFF;7453pBlock->m_modulation[3] = 0xFF;7454}74557456return;7457}74587459// It's a complex block with non-solid color and/or alpha pixels.7460vec4F minColor, maxColor;74617462if (solid_color_block)7463{7464// It's a solid color block.7465uint32_t low_a = block_cols[alpha_selectors.m_lo_selector].a;7466uint32_t high_a = block_cols[alpha_selectors.m_hi_selector].a;74677468const float S = 1.0f / 255.0f;7469vec4F_set(&minColor, block_cols[low_selector].r * S, block_cols[low_selector].g * S, block_cols[low_selector].b * S, low_a * S);7470vec4F_set(&maxColor, block_cols[low_selector].r * S, block_cols[low_selector].g * S, block_cols[low_selector].b * S, high_a * S);7471}7472else if (constant_alpha_val >= 0)7473{7474// It's a solid alpha block.7475const float S = 1.0f / 255.0f;7476vec4F_set(&minColor, block_cols[low_selector].r * S, block_cols[low_selector].g * S, block_cols[low_selector].b * S, constant_alpha_val * S);7477vec4F_set(&maxColor, block_cols[high_selector].r * S, block_cols[high_selector].g * S, block_cols[high_selector].b * S, constant_alpha_val * S);7478}7479// See if any of the block colors got clamped - if so the principle axis got distorted (it's no longer just the ETC1S luma axis).7480// To keep quality up we need to use full 4D PCA in this case.7481else if ((block_cols[low_selector].c[0] == 0) || (block_cols[high_selector].c[0] == 255) ||7482(block_cols[low_selector].c[1] == 0) || (block_cols[high_selector].c[1] == 255) ||7483(block_cols[low_selector].c[2] == 0) || (block_cols[high_selector].c[2] == 255) ||7484(block_cols[alpha_selectors.m_lo_selector].c[3] == 0) || (block_cols[alpha_selectors.m_hi_selector].c[3] == 255))7485{7486// Find principle component of RGBA colors treated as 4D vectors.7487color32 pixels[16];74887489uint32_t sum_r = 0, sum_g = 0, sum_b = 0, sum_a = 0;7490for (uint32_t i = 0; i < 16; i++)7491{7492color32 rgb(block_cols[pSelector->get_selector(i & 3, i >> 2)]);7493uint32_t a = block_cols[alpha_selectors.get_selector(i & 3, i >> 2)].a;74947495pixels[i].set(rgb.r, rgb.g, rgb.b, a);74967497sum_r += rgb.r;7498sum_g += rgb.g;7499sum_b += rgb.b;7500sum_a += a;7501}75027503vec4F meanColor;7504vec4F_set(&meanColor, (float)sum_r, (float)sum_g, (float)sum_b, (float)sum_a);7505vec4F meanColorScaled = vec4F_mul(&meanColor, 1.0f / 16.0f);75067507meanColor = vec4F_mul(&meanColor, 1.0f / (float)(16.0f * 255.0f));7508vec4F_saturate_in_place(&meanColor);75097510vec4F axis;7511vec4F_set_scalar(&axis, 0.0f);7512// Why this incremental method? Because it's stable and predictable. Covar+power method can require a lot of iterations to converge in 4D.7513for (uint32_t i = 0; i < 16; i++)7514{7515vec4F color = vec4F_from_color(&pixels[i]);7516color = vec4F_sub(&color, &meanColorScaled);7517vec4F a = vec4F_mul(&color, color.c[0]);7518vec4F b = vec4F_mul(&color, color.c[1]);7519vec4F c = vec4F_mul(&color, color.c[2]);7520vec4F d = vec4F_mul(&color, color.c[3]);7521vec4F n = i ? axis : color;7522vec4F_normalize_in_place(&n);7523axis.c[0] += vec4F_dot(&a, &n);7524axis.c[1] += vec4F_dot(&b, &n);7525axis.c[2] += vec4F_dot(&c, &n);7526axis.c[3] += vec4F_dot(&d, &n);7527}75287529vec4F_normalize_in_place(&axis);75307531if (vec4F_dot(&axis, &axis) < .5f)7532vec4F_set_scalar(&axis, .5f);75337534float l = 1e+9f, h = -1e+9f;75357536for (uint32_t i = 0; i < 16; i++)7537{7538vec4F color = vec4F_from_color(&pixels[i]);75397540vec4F q = vec4F_sub(&color, &meanColorScaled);7541float d = vec4F_dot(&q, &axis);75427543l = basisu::minimum(l, d);7544h = basisu::maximum(h, d);7545}75467547l *= (1.0f / 255.0f);7548h *= (1.0f / 255.0f);75497550vec4F b0 = vec4F_mul(&axis, l);7551vec4F b1 = vec4F_mul(&axis, h);7552vec4F c0 = vec4F_add(&meanColor, &b0);7553vec4F c1 = vec4F_add(&meanColor, &b1);7554minColor = vec4F_saturate(&c0);7555maxColor = vec4F_saturate(&c1);7556if (minColor.c[3] > maxColor.c[3])7557{7558// VS 2019 release Code Generator issue7559//std::swap(minColor, maxColor);75607561float a = minColor.c[0], b = minColor.c[1], c = minColor.c[2], d = minColor.c[3];7562minColor.c[0] = maxColor.c[0]; minColor.c[1] = maxColor.c[1]; minColor.c[2] = maxColor.c[2]; minColor.c[3] = maxColor.c[3];7563minColor.c[0] = maxColor.c[0]; minColor.c[1] = maxColor.c[1]; minColor.c[2] = maxColor.c[2]; minColor.c[3] = maxColor.c[3];7564maxColor.c[0] = a; maxColor.c[1] = b; maxColor.c[2] = c; maxColor.c[3] = d;7565}7566}7567else7568{7569// We know the RGB axis is luma, because it's an ETC1S block and none of the block colors got clamped. So we only need to use 2D PCA.7570// We project each LA vector onto two 2D lines with axes (1,1) and (1,-1) and find the largest projection to determine if axis A is flipped relative to L.7571uint32_t block_cols_l[4], block_cols_a[4];7572for (uint32_t i = 0; i < 4; i++)7573{7574block_cols_l[i] = block_cols[i].r + block_cols[i].g + block_cols[i].b;7575block_cols_a[i] = block_cols[i].a * 3;7576}75777578int p0_min = INT_MAX, p0_max = INT_MIN;7579int p1_min = INT_MAX, p1_max = INT_MIN;7580for (uint32_t y = 0; y < 4; y++)7581{7582const uint32_t cs = pSelector->m_selectors[y];7583const uint32_t as = alpha_selectors.m_selectors[y];75847585{7586const int l = block_cols_l[cs & 3];7587const int a = block_cols_a[as & 3];7588const int p0 = l + a; p0_min = basisu::minimum(p0_min, p0); p0_max = basisu::maximum(p0_max, p0);7589const int p1 = l - a; p1_min = basisu::minimum(p1_min, p1); p1_max = basisu::maximum(p1_max, p1);7590}7591{7592const int l = block_cols_l[(cs >> 2) & 3];7593const int a = block_cols_a[(as >> 2) & 3];7594const int p0 = l + a; p0_min = basisu::minimum(p0_min, p0); p0_max = basisu::maximum(p0_max, p0);7595const int p1 = l - a; p1_min = basisu::minimum(p1_min, p1); p1_max = basisu::maximum(p1_max, p1);7596}7597{7598const int l = block_cols_l[(cs >> 4) & 3];7599const int a = block_cols_a[(as >> 4) & 3];7600const int p0 = l + a; p0_min = basisu::minimum(p0_min, p0); p0_max = basisu::maximum(p0_max, p0);7601const int p1 = l - a; p1_min = basisu::minimum(p1_min, p1); p1_max = basisu::maximum(p1_max, p1);7602}7603{7604const int l = block_cols_l[cs >> 6];7605const int a = block_cols_a[as >> 6];7606const int p0 = l + a; p0_min = basisu::minimum(p0_min, p0); p0_max = basisu::maximum(p0_max, p0);7607const int p1 = l - a; p1_min = basisu::minimum(p1_min, p1); p1_max = basisu::maximum(p1_max, p1);7608}7609}76107611int dist0 = p0_max - p0_min;7612int dist1 = p1_max - p1_min;76137614const float S = 1.0f / 255.0f;76157616vec4F_set(&minColor, block_cols[low_selector].r * S, block_cols[low_selector].g * S, block_cols[low_selector].b * S, block_cols[alpha_selectors.m_lo_selector].a * S);7617vec4F_set(&maxColor, block_cols[high_selector].r * S, block_cols[high_selector].g * S, block_cols[high_selector].b * S, block_cols[alpha_selectors.m_hi_selector].a * S);76187619// See if the A component of the principle axis is flipped relative to L. If so, we need to flip either RGB or A bounds.7620if (dist1 > dist0)7621{7622std::swap(minColor.c[0], maxColor.c[0]);7623std::swap(minColor.c[1], maxColor.c[1]);7624std::swap(minColor.c[2], maxColor.c[2]);7625}7626}76277628// 4433 44437629color32 trialMinColor, trialMaxColor;76307631trialMinColor.set_clamped((int)(minColor.c[0] * 15.0f + .5f), (int)(minColor.c[1] * 15.0f + .5f), (int)(minColor.c[2] * 7.0f + .5f), (int)(minColor.c[3] * 7.0f + .5f));7632trialMaxColor.set_clamped((int)(maxColor.c[0] * 15.0f + .5f), (int)(maxColor.c[1] * 15.0f + .5f), (int)(maxColor.c[2] * 15.0f + .5f), (int)(maxColor.c[3] * 7.0f + .5f));76337634pBlock->set_trans_low_color(trialMinColor.r, trialMinColor.g, trialMinColor.b, trialMinColor.a);7635pBlock->set_trans_high_color(trialMaxColor.r, trialMaxColor.g, trialMaxColor.b, trialMaxColor.a);76367637color32 color_a((trialMinColor.r << 1) | (trialMinColor.r >> 3), (trialMinColor.g << 1) | (trialMinColor.g >> 3), (trialMinColor.b << 2) | (trialMinColor.b >> 1), trialMinColor.a << 1);7638color32 color_b((trialMaxColor.r << 1) | (trialMaxColor.r >> 3), (trialMaxColor.g << 1) | (trialMaxColor.g >> 3), (trialMaxColor.b << 1) | (trialMaxColor.b >> 3), (trialMaxColor.a << 1) | 1);76397640color32 color0(convert_rgba_5554_to_8888(color_a));7641color32 color3(convert_rgba_5554_to_8888(color_b));76427643const int lr = color0.r;7644const int lg = color0.g;7645const int lb = color0.b;7646const int la = color0.a;76477648const int axis_r = color3.r - lr;7649const int axis_g = color3.g - lg;7650const int axis_b = color3.b - lb;7651const int axis_a = color3.a - la;7652const int len_a = (axis_r * axis_r) + (axis_g * axis_g) + (axis_b * axis_b) + (axis_a * axis_a);76537654const int thresh01 = (len_a * 3) / 16;7655const int thresh12 = len_a >> 1;7656const int thresh23 = (len_a * 13) / 16;76577658if ((axis_r | axis_g | axis_b) == 0)7659{7660int ca_sel[4];76617662for (uint32_t i = 0; i < 4; i++)7663{7664int ca = (block_cols[i].a - la) * axis_a;7665ca_sel[i] = (ca >= thresh23) + (ca >= thresh12) + (ca >= thresh01);7666}76677668for (uint32_t y = 0; y < 4; y++)7669{7670const uint32_t a_sels = alpha_selectors.m_selectors[y];76717672uint32_t sel = ca_sel[a_sels & 3] | (ca_sel[(a_sels >> 2) & 3] << 2) | (ca_sel[(a_sels >> 4) & 3] << 4) | (ca_sel[a_sels >> 6] << 6);76737674pBlock->m_modulation[y] = (uint8_t)sel;7675}7676}7677else7678{7679int cy[4], ca[4];76807681for (uint32_t i = 0; i < 4; i++)7682{7683cy[i] = (block_cols[i].r - lr) * axis_r + (block_cols[i].g - lg) * axis_g + (block_cols[i].b - lb) * axis_b;7684ca[i] = (block_cols[i].a - la) * axis_a;7685}76867687for (uint32_t y = 0; y < 4; y++)7688{7689const uint32_t c_sels = pSelector->m_selectors[y];7690const uint32_t a_sels = alpha_selectors.m_selectors[y];76917692const int d0 = cy[c_sels & 3] + ca[a_sels & 3];7693const int d1 = cy[(c_sels >> 2) & 3] + ca[(a_sels >> 2) & 3];7694const int d2 = cy[(c_sels >> 4) & 3] + ca[(a_sels >> 4) & 3];7695const int d3 = cy[c_sels >> 6] + ca[a_sels >> 6];76967697uint32_t sel = ((d0 >= thresh23) + (d0 >= thresh12) + (d0 >= thresh01)) |7698(((d1 >= thresh23) + (d1 >= thresh12) + (d1 >= thresh01)) << 2) |7699(((d2 >= thresh23) + (d2 >= thresh12) + (d2 >= thresh01)) << 4) |7700(((d3 >= thresh23) + (d3 >= thresh12) + (d3 >= thresh01)) << 6);77017702pBlock->m_modulation[y] = (uint8_t)sel;7703}7704}7705}77067707static void transcoder_init_pvrtc2()7708{7709for (uint32_t v = 0; v < 256; v++)7710{7711int best_l = 0, best_h = 0, lowest_err = INT_MAX;77127713for (uint32_t l = 0; l < 8; l++)7714{7715uint32_t le = (l << 1);7716le = (le << 4) | le;77177718for (uint32_t h = 0; h < 8; h++)7719{7720uint32_t he = (h << 1) | 1;7721he = (he << 4) | he;77227723uint32_t m = (le * 5 + he * 3) / 8;77247725int err = (int)labs((int)v - (int)m);7726if (err < lowest_err)7727{7728lowest_err = err;7729best_l = l;7730best_h = h;7731}7732}7733}77347735g_pvrtc2_alpha_match33[v].m_l = (uint8_t)best_l;7736g_pvrtc2_alpha_match33[v].m_h = (uint8_t)best_h;7737}77387739for (uint32_t v = 0; v < 256; v++)7740{7741int best_l = 0, best_h = 0, lowest_err = INT_MAX;77427743for (uint32_t l = 0; l < 8; l++)7744{7745uint32_t le = (l << 1);7746le = (le << 4) | le;77477748int err = (int)labs((int)v - (int)le);7749if (err < lowest_err)7750{7751lowest_err = err;7752best_l = l;7753best_h = l;7754}7755}77567757g_pvrtc2_alpha_match33_0[v].m_l = (uint8_t)best_l;7758g_pvrtc2_alpha_match33_0[v].m_h = (uint8_t)best_h;7759}77607761for (uint32_t v = 0; v < 256; v++)7762{7763int best_l = 0, best_h = 0, lowest_err = INT_MAX;77647765for (uint32_t h = 0; h < 8; h++)7766{7767uint32_t he = (h << 1) | 1;7768he = (he << 4) | he;77697770int err = (int)labs((int)v - (int)he);7771if (err < lowest_err)7772{7773lowest_err = err;7774best_l = h;7775best_h = h;7776}7777}77787779g_pvrtc2_alpha_match33_3[v].m_l = (uint8_t)best_l;7780g_pvrtc2_alpha_match33_3[v].m_h = (uint8_t)best_h;7781}77827783for (uint32_t v = 0; v < 256; v++)7784{7785int best_l = 0, best_h = 0, lowest_err = INT_MAX;77867787for (uint32_t l = 0; l < 8; l++)7788{7789uint32_t le = (l << 2) | (l >> 1);7790le = (le << 3) | (le >> 2);77917792for (uint32_t h = 0; h < 16; h++)7793{7794uint32_t he = (h << 1) | (h >> 3);7795he = (he << 3) | (he >> 2);77967797uint32_t m = (le * 5 + he * 3) / 8;77987799int err = (int)labs((int)v - (int)m);7800if (err < lowest_err)7801{7802lowest_err = err;7803best_l = l;7804best_h = h;7805}7806}7807}78087809g_pvrtc2_trans_match34[v].m_l = (uint8_t)best_l;7810g_pvrtc2_trans_match34[v].m_h = (uint8_t)best_h;7811}78127813for (uint32_t v = 0; v < 256; v++)7814{7815int best_l = 0, best_h = 0, lowest_err = INT_MAX;78167817for (uint32_t l = 0; l < 16; l++)7818{7819uint32_t le = (l << 1) | (l >> 3);7820le = (le << 3) | (le >> 2);78217822for (uint32_t h = 0; h < 16; h++)7823{7824uint32_t he = (h << 1) | (h >> 3);7825he = (he << 3) | (he >> 2);78267827uint32_t m = (le * 5 + he * 3) / 8;78287829int err = (int)labs((int)v - (int)m);7830if (err < lowest_err)7831{7832lowest_err = err;7833best_l = l;7834best_h = h;7835}7836}7837}78387839g_pvrtc2_trans_match44[v].m_l = (uint8_t)best_l;7840g_pvrtc2_trans_match44[v].m_h = (uint8_t)best_h;7841}7842}7843#endif // BASISD_SUPPORT_PVRTC278447845//------------------------------------------------------------------------------------------------78467847// BC7 mode 5 RGB encoder78487849#if BASISD_SUPPORT_BC7_MODE57850namespace bc7_mode_5_encoder7851{7852static float g_mode5_rgba_midpoints[128];78537854void encode_bc7_mode5_init()7855{7856// Mode 5 endpoint midpoints7857for (uint32_t i = 0; i < 128; i++)7858{7859uint32_t vl = (i << 1);7860vl |= (vl >> 7);7861float lo = vl / 255.0f;78627863uint32_t vh = basisu::minimumi(127, i + 1) << 1;7864vh |= (vh >> 7);7865float hi = vh / 255.0f;78667867if (i == 127)7868g_mode5_rgba_midpoints[i] = 1e+15f;7869else7870g_mode5_rgba_midpoints[i] = (lo + hi) / 2.0f;7871}7872}78737874static inline uint32_t from_7(uint32_t v)7875{7876assert(v < 128);7877return (v << 1) | (v >> 6);7878}78797880static inline int to_7(float c)7881{7882assert((c >= 0) && (c <= 1.0f));78837884int vl = (int)(c * 127.0f);7885vl += (c > g_mode5_rgba_midpoints[vl]);7886return clampi(vl, 0, 127);7887}78887889static inline int to_7(int c8)7890{7891assert((c8 >= 0) && (c8 <= 255));78927893float c = (float)c8 * (1.0f / 255.0f);78947895int vl = (int)(c * 127.0f);7896vl += (c > g_mode5_rgba_midpoints[vl]);7897return clampi(vl, 0, 127);7898}78997900// This is usable with ASTC as well, which uses the same 2-bit interpolation weights.7901static inline uint32_t bc7_interp2(uint32_t l, uint32_t h, uint32_t w)7902{7903assert(w < 4);7904return (l * (64 - basist::g_bc7_weights2[w]) + h * basist::g_bc7_weights2[w] + 32) >> 6;7905}79067907static void eval_weights(7908const color32 *pPixels, uint8_t* pWeights,7909int lr, int lg, int lb,7910int hr, int hg, int hb)7911{7912lr = from_7(lr); lg = from_7(lg); lb = from_7(lb);7913hr = from_7(hr); hg = from_7(hg); hb = from_7(hb);79147915int cr[4], cg[4], cb[4];7916for (uint32_t i = 0; i < 4; i++)7917{7918cr[i] = (uint8_t)bc7_interp2(lr, hr, i);7919cg[i] = (uint8_t)bc7_interp2(lg, hg, i);7920cb[i] = (uint8_t)bc7_interp2(lb, hb, i);7921}79227923#if 07924for (uint32_t i = 0; i < 16; i++)7925{7926const int pr = pPixels[i].r, pg = pPixels[i].g, pb = pPixels[i].b;79277928uint32_t best_err = UINT32_MAX;7929uint32_t best_idx = 0;7930for (uint32_t j = 0; j < 4; j++)7931{7932uint32_t e = square(pr - cr[j]) + square(pg - cg[j]) + square(pb - cb[j]);7933if (e < best_err)7934{7935best_err = e;7936best_idx = j;7937}79387939pWeights[i] = (uint8_t)best_idx;7940}7941} // i7942#else7943int ar = cr[3] - cr[0], ag = cg[3] - cg[0], ab = cb[3] - cb[0];79447945int dots[4];7946for (uint32_t i = 0; i < 4; i++)7947dots[i] = (int)cr[i] * ar + (int)cg[i] * ag + (int)cb[i] * ab;79487949// seems very rare in LDR, so rare that it doesn't matter7950//assert(dots[0] <= dots[1]);7951//assert(dots[1] <= dots[2]);7952//assert(dots[2] <= dots[3]);79537954int t0 = dots[0] + dots[1], t1 = dots[1] + dots[2], t2 = dots[2] + dots[3];79557956ar *= 2; ag *= 2; ab *= 2;79577958for (uint32_t i = 0; i < 16; i += 4)7959{7960const int d0 = pPixels[i + 0].r * ar + pPixels[i + 0].g * ag + pPixels[i + 0].b * ab;7961const int d1 = pPixels[i + 1].r * ar + pPixels[i + 1].g * ag + pPixels[i + 1].b * ab;7962const int d2 = pPixels[i + 2].r * ar + pPixels[i + 2].g * ag + pPixels[i + 2].b * ab;7963const int d3 = pPixels[i + 3].r * ar + pPixels[i + 3].g * ag + pPixels[i + 3].b * ab;79647965pWeights[i + 0] = (d0 > t0) + (d0 >= t1) + (d0 >= t2);7966pWeights[i + 1] = (d1 > t0) + (d1 >= t1) + (d1 >= t2);7967pWeights[i + 2] = (d2 > t0) + (d2 >= t1) + (d2 >= t2);7968pWeights[i + 3] = (d3 > t0) + (d3 >= t1) + (d3 >= t2);7969}7970#endif7971}79727973static void pack_bc7_mode5_rgb_block(7974bc7_mode_5* pDst_block,7975int lr, int lg, int lb, int hr, int hg, int hb,7976const uint8_t* pWeights)7977{7978assert((lr >= 0) && (lr <= 127));7979assert((lg >= 0) && (lg <= 127));7980assert((lb >= 0) && (lb <= 127));7981assert((hr >= 0) && (hr <= 127));7982assert((hg >= 0) && (hg <= 127));7983assert((hb >= 0) && (hb <= 127));79847985pDst_block->m_lo_bits = 0;79867987uint8_t weight_inv = 0;7988if (pWeights[0] & 2)7989{7990std::swap(lr, hr);7991std::swap(lg, hg);7992std::swap(lb, hb);7993weight_inv = 3;7994}7995assert((pWeights[0] ^ weight_inv) <= 1);79967997pDst_block->m_lo.m_mode = 32;7998pDst_block->m_lo.m_r0 = lr;7999pDst_block->m_lo.m_r1 = hr;8000pDst_block->m_lo.m_g0 = lg;8001pDst_block->m_lo.m_g1 = hg;8002pDst_block->m_lo.m_b0 = lb;8003pDst_block->m_lo.m_b1 = hb;80048005pDst_block->m_lo.m_a0 = 255;8006pDst_block->m_lo.m_a1_0 = 63;80078008uint64_t sel_bits = 3;8009uint32_t cur_ofs = 2;8010for (uint32_t i = 0; i < 16; i++)8011{8012assert(pWeights[i] <= 3);8013sel_bits |= ((uint64_t)(weight_inv ^ pWeights[i])) << cur_ofs;8014cur_ofs += (i ? 2 : 1);8015}80168017pDst_block->m_hi_bits = sel_bits;8018}80198020// This table is: 9 * (w * w), 9 * ((1.0f - w) * w), 9 * ((1.0f - w) * (1.0f - w))8021// where w is [0,1/3,2/3,1]. 9 is the perfect multiplier.8022static const uint32_t g_weight_vals4[4] = { 0x000009, 0x010204, 0x040201, 0x090000 };80238024static inline bool compute_least_squares_endpoints4_rgb(8025const color32 *pColors, const uint8_t* pSelectors,8026int& lr, int& lg, int& lb, int& hr, int& hg, int& hb,8027int total_r, int total_g, int total_b)8028{8029uint32_t uq00_r = 0, uq00_g = 0, uq00_b = 0;8030uint32_t weight_accum = 0;8031for (uint32_t i = 0; i < 16; i++)8032{8033const uint8_t r = pColors[i].r, g = pColors[i].g, b = pColors[i].b;8034const uint8_t sel = pSelectors[i];80358036weight_accum += g_weight_vals4[sel];8037uq00_r += sel * r;8038uq00_g += sel * g;8039uq00_b += sel * b;8040}80418042int q10_r = total_r * 3 - uq00_r;8043int q10_g = total_g * 3 - uq00_g;8044int q10_b = total_b * 3 - uq00_b;80458046float z00 = (float)((weight_accum >> 16) & 0xFF);8047float z10 = (float)((weight_accum >> 8) & 0xFF);8048float z11 = (float)(weight_accum & 0xFF);8049float z01 = z10;80508051float det = z00 * z11 - z01 * z10;8052if (fabs(det) < 1e-8f)8053return false;80548055det = (3.0f / 255.0f) / det;80568057float iz00, iz01, iz10, iz11;8058iz00 = z11 * det;8059iz01 = -z01 * det;8060iz10 = -z10 * det;8061iz11 = z00 * det;80628063float fhr = basisu::clamp(iz00 * (float)uq00_r + iz01 * q10_r, 0.0f, 1.0f);8064float flr = basisu::clamp(iz10 * (float)uq00_r + iz11 * q10_r, 0.0f, 1.0f);80658066float fhg = basisu::clamp(iz00 * (float)uq00_g + iz01 * q10_g, 0.0f, 1.0f);8067float flg = basisu::clamp(iz10 * (float)uq00_g + iz11 * q10_g, 0.0f, 1.0f);80688069float fhb = basisu::clamp(iz00 * (float)uq00_b + iz01 * q10_b, 0.0f, 1.0f);8070float flb = basisu::clamp(iz10 * (float)uq00_b + iz11 * q10_b, 0.0f, 1.0f);80718072lr = to_7(flr); lg = to_7(flg); lb = to_7(flb);8073hr = to_7(fhr); hg = to_7(fhg); hb = to_7(fhb);80748075return true;8076}80778078void encode_bc7_mode_5_block(void* pDst_block, color32* pPixels, bool hq_mode)8079{8080assert(g_mode5_rgba_midpoints[1]);80818082int total_r = 0, total_g = 0, total_b = 0;80838084int min_r = 255, min_g = 255, min_b = 255;8085int max_r = 0, max_g = 0, max_b = 0;80868087for (uint32_t i = 0; i < 16; i++)8088{8089int r = pPixels[i].r, g = pPixels[i].g, b = pPixels[i].b;80908091total_r += r; total_g += g; total_b += b;80928093min_r = basisu::minimum(min_r, r); min_g = basisu::minimum(min_g, g); min_b = basisu::minimum(min_b, b);8094max_r = basisu::maximum(max_r, r); max_g = basisu::maximum(max_g, g); max_b = basisu::maximum(max_b, b);8095}80968097if ((min_r == max_r) && (min_g == max_g) && (min_b == max_b))8098{8099const int lr = g_bc7_m5_equals_1[min_r].m_lo, lg = g_bc7_m5_equals_1[min_g].m_lo, lb = g_bc7_m5_equals_1[min_b].m_lo;8100const int hr = g_bc7_m5_equals_1[min_r].m_hi, hg = g_bc7_m5_equals_1[min_g].m_hi, hb = g_bc7_m5_equals_1[min_b].m_hi;8101uint8_t solid_weights[16];8102memset(solid_weights, 1, 16);8103pack_bc7_mode5_rgb_block((bc7_mode_5*)pDst_block, lr, lg, lb, hr, hg, hb, solid_weights);8104return;8105}81068107int mean_r = (total_r + 8) >> 4, mean_g = (total_g + 8) >> 4, mean_b = (total_b + 8) >> 4;81088109// covar rows are:8110// 0, 1, 28111// 1, 3, 48112// 2, 4, 58113int icov[6] = { 0, 0, 0, 0, 0, 0 };81148115for (uint32_t i = 0; i < 16; i++)8116{8117int r = (int)pPixels[i].r - mean_r;8118int g = (int)pPixels[i].g - mean_g;8119int b = (int)pPixels[i].b - mean_b;8120icov[0] += r * r; icov[1] += r * g; icov[2] += r * b;8121icov[3] += g * g; icov[4] += g * b;8122icov[5] += b * b;8123}81248125int block_max_var = basisu::maximum(icov[0], icov[3], icov[5]); // not divided by 16, i.e. scaled by 1681268127// TODO: Tune this8128const int32_t SIMPLE_BLOCK_THRESH = 10 * 16;81298130if ((!hq_mode) && (block_max_var < SIMPLE_BLOCK_THRESH))8131{8132const int L = 16, H = 239;81338134int lr = to_7(lerp_8bit(min_r, max_r, L));8135int lg = to_7(lerp_8bit(min_g, max_g, L));8136int lb = to_7(lerp_8bit(min_b, max_b, L));81378138int hr = to_7(lerp_8bit(min_r, max_r, H));8139int hg = to_7(lerp_8bit(min_g, max_g, H));8140int hb = to_7(lerp_8bit(min_b, max_b, H));81418142uint8_t cur_weights[16];8143eval_weights(pPixels, cur_weights, lr, lg, lb, hr, hg, hb);81448145pack_bc7_mode5_rgb_block((bc7_mode_5*)pDst_block, lr, lg, lb, hr, hg, hb, cur_weights);8146return;8147}81488149float cov[6];8150for (uint32_t i = 0; i < 6; i++)8151cov[i] = (float)icov[i];81528153const float sc = 1.0f / (float)block_max_var;8154const float wx = sc * cov[0], wy = sc * cov[3], wz = sc * cov[5];81558156const float alt_xr = cov[0] * wx + cov[1] * wy + cov[2] * wz;8157const float alt_xg = cov[1] * wx + cov[3] * wy + cov[4] * wz;8158const float alt_xb = cov[2] * wx + cov[4] * wy + cov[5] * wz;81598160int saxis_r = 306, saxis_g = 601, saxis_b = 117;81618162float k = basisu::maximum(fabsf(alt_xr), fabsf(alt_xg), fabsf(alt_xb));8163if (fabs(k) >= basisu::SMALL_FLOAT_VAL)8164{8165float m = 2048.0f / k;8166saxis_r = (int)(alt_xr * m);8167saxis_g = (int)(alt_xg * m);8168saxis_b = (int)(alt_xb * m);8169}81708171saxis_r = (int)((uint32_t)saxis_r << 4U);8172saxis_g = (int)((uint32_t)saxis_g << 4U);8173saxis_b = (int)((uint32_t)saxis_b << 4U);81748175int low_dot = INT_MAX, high_dot = INT_MIN;81768177for (uint32_t i = 0; i < 16; i += 4)8178{8179int dot0 = ((pPixels[i].r * saxis_r + pPixels[i].g * saxis_g + pPixels[i].b * saxis_b) & ~0xF) + i;8180int dot1 = ((pPixels[i + 1].r * saxis_r + pPixels[i + 1].g * saxis_g + pPixels[i + 1].b * saxis_b) & ~0xF) + i + 1;8181int dot2 = ((pPixels[i + 2].r * saxis_r + pPixels[i + 2].g * saxis_g + pPixels[i + 2].b * saxis_b) & ~0xF) + i + 2;8182int dot3 = ((pPixels[i + 3].r * saxis_r + pPixels[i + 3].g * saxis_g + pPixels[i + 3].b * saxis_b) & ~0xF) + i + 3;81838184int min_d01 = basisu::minimum(dot0, dot1);8185int max_d01 = basisu::maximum(dot0, dot1);81868187int min_d23 = basisu::minimum(dot2, dot3);8188int max_d23 = basisu::maximum(dot2, dot3);81898190int min_d = basisu::minimum(min_d01, min_d23);8191int max_d = basisu::maximum(max_d01, max_d23);81928193low_dot = basisu::minimum(low_dot, min_d);8194high_dot = basisu::maximum(high_dot, max_d);8195}8196int low_c = low_dot & 15;8197int high_c = high_dot & 15;81988199int lr = to_7(pPixels[low_c].r), lg = to_7(pPixels[low_c].g), lb = to_7(pPixels[low_c].b);8200int hr = to_7(pPixels[high_c].r), hg = to_7(pPixels[high_c].g), hb = to_7(pPixels[high_c].b);82018202uint8_t cur_weights[16];8203eval_weights(pPixels, cur_weights, lr, lg, lb, hr, hg, hb);82048205if (compute_least_squares_endpoints4_rgb(8206pPixels, cur_weights,8207lr, lg, lb, hr, hg, hb,8208total_r, total_g, total_b))8209{8210eval_weights(pPixels, cur_weights, lr, lg, lb, hr, hg, hb);8211}82128213#if 08214lr = 0; lg = 0; lb = 0;8215hr = 0; hg = 0; hb = 0;8216#endif82178218pack_bc7_mode5_rgb_block((bc7_mode_5*)pDst_block, lr, lg, lb, hr, hg, hb, cur_weights);8219}82208221} // namespace bc7_mode_5_encoder82228223#endif // BASISD_SUPPORT_BC7_MODE582248225//------------------------------------------------------------------------------------------------82268227basisu_lowlevel_etc1s_transcoder::basisu_lowlevel_etc1s_transcoder() :8228m_pGlobal_codebook(nullptr),8229m_selector_history_buf_size(0)8230{8231}82328233bool basisu_lowlevel_etc1s_transcoder::decode_palettes(8234uint32_t num_endpoints, const uint8_t* pEndpoints_data, uint32_t endpoints_data_size,8235uint32_t num_selectors, const uint8_t* pSelectors_data, uint32_t selectors_data_size)8236{8237if (m_pGlobal_codebook)8238{8239BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 11\n");8240return false;8241}8242bitwise_decoder sym_codec;82438244huffman_decoding_table color5_delta_model0, color5_delta_model1, color5_delta_model2, inten_delta_model;82458246if (!sym_codec.init(pEndpoints_data, endpoints_data_size))8247{8248BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 0\n");8249return false;8250}82518252if (!sym_codec.read_huffman_table(color5_delta_model0))8253{8254BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 1\n");8255return false;8256}82578258if (!sym_codec.read_huffman_table(color5_delta_model1))8259{8260BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 1a\n");8261return false;8262}82638264if (!sym_codec.read_huffman_table(color5_delta_model2))8265{8266BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 2a\n");8267return false;8268}82698270if (!sym_codec.read_huffman_table(inten_delta_model))8271{8272BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 2b\n");8273return false;8274}82758276if (!color5_delta_model0.is_valid() || !color5_delta_model1.is_valid() || !color5_delta_model2.is_valid() || !inten_delta_model.is_valid())8277{8278BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 2b\n");8279return false;8280}82818282const bool endpoints_are_grayscale = sym_codec.get_bits(1) != 0;82838284m_local_endpoints.resize(num_endpoints);82858286color32 prev_color5(16, 16, 16, 0);8287uint32_t prev_inten = 0;82888289for (uint32_t i = 0; i < num_endpoints; i++)8290{8291uint32_t inten_delta = sym_codec.decode_huffman(inten_delta_model);8292m_local_endpoints[i].m_inten5 = static_cast<uint8_t>((inten_delta + prev_inten) & 7);8293prev_inten = m_local_endpoints[i].m_inten5;82948295for (uint32_t c = 0; c < (endpoints_are_grayscale ? 1U : 3U); c++)8296{8297int delta;8298if (prev_color5[c] <= basist::COLOR5_PAL0_PREV_HI)8299delta = sym_codec.decode_huffman(color5_delta_model0);8300else if (prev_color5[c] <= basist::COLOR5_PAL1_PREV_HI)8301delta = sym_codec.decode_huffman(color5_delta_model1);8302else8303delta = sym_codec.decode_huffman(color5_delta_model2);83048305int v = (prev_color5[c] + delta) & 31;83068307m_local_endpoints[i].m_color5[c] = static_cast<uint8_t>(v);83088309prev_color5[c] = static_cast<uint8_t>(v);8310}83118312if (endpoints_are_grayscale)8313{8314m_local_endpoints[i].m_color5[1] = m_local_endpoints[i].m_color5[0];8315m_local_endpoints[i].m_color5[2] = m_local_endpoints[i].m_color5[0];8316}8317}83188319sym_codec.stop();83208321m_local_selectors.resize(num_selectors);83228323if (!sym_codec.init(pSelectors_data, selectors_data_size))8324{8325BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 5\n");8326return false;8327}83288329basist::huffman_decoding_table delta_selector_pal_model;83308331const bool used_global_selector_cb = (sym_codec.get_bits(1) == 1);83328333if (used_global_selector_cb)8334{8335BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: global selector codebooks are unsupported\n");8336return false;8337}8338else8339{8340const bool used_hybrid_selector_cb = (sym_codec.get_bits(1) == 1);83418342if (used_hybrid_selector_cb)8343{8344BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: hybrid global selector codebooks are unsupported\n");8345return false;8346}83478348const bool used_raw_encoding = (sym_codec.get_bits(1) == 1);83498350if (used_raw_encoding)8351{8352for (uint32_t i = 0; i < num_selectors; i++)8353{8354for (uint32_t j = 0; j < 4; j++)8355{8356uint32_t cur_byte = sym_codec.get_bits(8);83578358for (uint32_t k = 0; k < 4; k++)8359m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);8360}83618362m_local_selectors[i].init_flags();8363}8364}8365else8366{8367if (!sym_codec.read_huffman_table(delta_selector_pal_model))8368{8369BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10\n");8370return false;8371}83728373if ((num_selectors > 1) && (!delta_selector_pal_model.is_valid()))8374{8375BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10a\n");8376return false;8377}83788379uint8_t prev_bytes[4] = { 0, 0, 0, 0 };83808381for (uint32_t i = 0; i < num_selectors; i++)8382{8383if (!i)8384{8385for (uint32_t j = 0; j < 4; j++)8386{8387uint32_t cur_byte = sym_codec.get_bits(8);8388prev_bytes[j] = static_cast<uint8_t>(cur_byte);83898390for (uint32_t k = 0; k < 4; k++)8391m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);8392}8393m_local_selectors[i].init_flags();8394continue;8395}83968397for (uint32_t j = 0; j < 4; j++)8398{8399int delta_byte = sym_codec.decode_huffman(delta_selector_pal_model);84008401uint32_t cur_byte = delta_byte ^ prev_bytes[j];8402prev_bytes[j] = static_cast<uint8_t>(cur_byte);84038404for (uint32_t k = 0; k < 4; k++)8405m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);8406}8407m_local_selectors[i].init_flags();8408}8409}8410}84118412sym_codec.stop();84138414return true;8415}84168417bool basisu_lowlevel_etc1s_transcoder::decode_tables(const uint8_t* pTable_data, uint32_t table_data_size)8418{8419basist::bitwise_decoder sym_codec;8420if (!sym_codec.init(pTable_data, table_data_size))8421{8422BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 0\n");8423return false;8424}84258426if (!sym_codec.read_huffman_table(m_endpoint_pred_model))8427{8428BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 1\n");8429return false;8430}84318432if (m_endpoint_pred_model.get_code_sizes().size() == 0)8433{8434BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 1a\n");8435return false;8436}84378438if (!sym_codec.read_huffman_table(m_delta_endpoint_model))8439{8440BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 2\n");8441return false;8442}84438444if (m_delta_endpoint_model.get_code_sizes().size() == 0)8445{8446BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 2a\n");8447return false;8448}84498450if (!sym_codec.read_huffman_table(m_selector_model))8451{8452BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 3\n");8453return false;8454}84558456if (m_selector_model.get_code_sizes().size() == 0)8457{8458BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 3a\n");8459return false;8460}84618462if (!sym_codec.read_huffman_table(m_selector_history_buf_rle_model))8463{8464BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 4\n");8465return false;8466}84678468if (m_selector_history_buf_rle_model.get_code_sizes().size() == 0)8469{8470BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 4a\n");8471return false;8472}84738474m_selector_history_buf_size = sym_codec.get_bits(13);8475// Check for bogus values.8476if (!m_selector_history_buf_size)8477{8478BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 5\n");8479return false;8480}84818482sym_codec.stop();84838484return true;8485}84868487bool basisu_lowlevel_etc1s_transcoder::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,8488uint32_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,8489basisu_transcoder_state* pState, bool transcode_alpha, void *pAlpha_blocks, uint32_t output_rows_in_pixels, uint32_t decode_flags)8490{8491// 'pDst_blocks' unused when disabling *all* hardware transcode options8492// (and 'bc1_allow_threecolor_blocks' when disabling DXT)8493BASISU_NOTE_UNUSED(pDst_blocks);8494BASISU_NOTE_UNUSED(bc1_allow_threecolor_blocks);8495BASISU_NOTE_UNUSED(transcode_alpha);8496BASISU_NOTE_UNUSED(pAlpha_blocks);84978498assert(g_transcoder_initialized);8499if (!g_transcoder_initialized)8500{8501BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: Transcoder not globally initialized.\n");8502return false;8503}85048505if (!pState)8506pState = &m_def_state;85078508const uint32_t total_blocks = num_blocks_x * num_blocks_y;85098510if (!output_row_pitch_in_blocks_or_pixels)8511{8512if (basis_block_format_is_uncompressed(fmt))8513output_row_pitch_in_blocks_or_pixels = orig_width;8514else8515{8516if (fmt == block_format::cFXT1_RGB)8517output_row_pitch_in_blocks_or_pixels = (orig_width + 7) / 8;8518else8519output_row_pitch_in_blocks_or_pixels = num_blocks_x;8520}8521}85228523if (basis_block_format_is_uncompressed(fmt))8524{8525if (!output_rows_in_pixels)8526output_rows_in_pixels = orig_height;8527}85288529basisu::vector<uint32_t>* pPrev_frame_indices = nullptr;8530if (is_video)8531{8532// TODO: Add check to make sure the caller hasn't tried skipping past p-frames8533//const bool alpha_flag = (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0;8534//const uint32_t level_index = slice_desc.m_level_index;85358536if (level_index >= basisu_transcoder_state::cMaxPrevFrameLevels)8537{8538BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: unsupported level_index\n");8539return false;8540}85418542pPrev_frame_indices = &pState->m_prev_frame_indices[is_alpha_slice][level_index];8543if (pPrev_frame_indices->size() < total_blocks)8544pPrev_frame_indices->resize(total_blocks);8545}85468547basist::bitwise_decoder sym_codec;85488549if (!sym_codec.init(pImage_data, image_data_size))8550{8551BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: sym_codec.init failed\n");8552return false;8553}85548555approx_move_to_front selector_history_buf(m_selector_history_buf_size);85568557uint32_t cur_selector_rle_count = 0;85588559decoder_etc_block block;8560memset(&block, 0, sizeof(block));85618562//block.set_flip_bit(true);8563// Setting the flip bit to false to be compatible with the Khronos KDFS.8564block.set_flip_bit(false);85658566block.set_diff_bit(true);85678568// Important: This MUST be freed before this function returns.8569void* pPVRTC_work_mem = nullptr;8570uint32_t* pPVRTC_endpoints = nullptr;8571if ((fmt == block_format::cPVRTC1_4_RGB) || (fmt == block_format::cPVRTC1_4_RGBA))8572{8573pPVRTC_work_mem = malloc(num_blocks_x * num_blocks_y * (sizeof(decoder_etc_block) + sizeof(uint32_t)));8574if (!pPVRTC_work_mem)8575{8576BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: malloc failed\n");8577return false;8578}8579pPVRTC_endpoints = (uint32_t*)&((decoder_etc_block*)pPVRTC_work_mem)[num_blocks_x * num_blocks_y];8580}85818582if (pState->m_block_endpoint_preds[0].size() < num_blocks_x)8583{8584pState->m_block_endpoint_preds[0].resize(num_blocks_x);8585pState->m_block_endpoint_preds[1].resize(num_blocks_x);8586}85878588uint32_t cur_pred_bits = 0;8589int prev_endpoint_pred_sym = 0;8590int endpoint_pred_repeat_count = 0;8591uint32_t prev_endpoint_index = 0;8592const endpoint_vec& endpoints = m_pGlobal_codebook ? m_pGlobal_codebook->m_local_endpoints : m_local_endpoints;8593const selector_vec& selectors = m_pGlobal_codebook ? m_pGlobal_codebook->m_local_selectors : m_local_selectors;8594if (!endpoints.size() || !selectors.size())8595{8596BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: global codebooks must be unpacked first\n");85978598if (pPVRTC_work_mem)8599free(pPVRTC_work_mem);86008601return false;8602}86038604const uint32_t SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX = (uint32_t)selectors.size();8605const uint32_t SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX = m_selector_history_buf_size + SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX;86068607#if BASISD_SUPPORT_BC7_MODE58608const bool bc7_chroma_filtering = ((decode_flags & cDecodeFlagsNoETC1SChromaFiltering) == 0) &&8609((fmt == block_format::cBC7_M5_COLOR) || (fmt == block_format::cBC7));86108611basisu::vector2D<uint16_t> decoded_endpoints;8612if (bc7_chroma_filtering)8613{8614if (!decoded_endpoints.try_resize(num_blocks_x, num_blocks_y))8615{8616BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: allocation failed\n");86178618if (pPVRTC_work_mem)8619free(pPVRTC_work_mem);86208621return false;8622}8623}8624#endif86258626for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)8627{8628const uint32_t cur_block_endpoint_pred_array = block_y & 1;86298630for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)8631{8632// Decode endpoint index predictor symbols8633if ((block_x & 1) == 0)8634{8635if ((block_y & 1) == 0)8636{8637if (endpoint_pred_repeat_count)8638{8639endpoint_pred_repeat_count--;8640cur_pred_bits = prev_endpoint_pred_sym;8641}8642else8643{8644cur_pred_bits = sym_codec.decode_huffman(m_endpoint_pred_model);8645if (cur_pred_bits == ENDPOINT_PRED_REPEAT_LAST_SYMBOL)8646{8647endpoint_pred_repeat_count = sym_codec.decode_vlc(ENDPOINT_PRED_COUNT_VLC_BITS) + ENDPOINT_PRED_MIN_REPEAT_COUNT - 1;86488649cur_pred_bits = prev_endpoint_pred_sym;8650}8651else8652{8653prev_endpoint_pred_sym = cur_pred_bits;8654}8655}86568657pState->m_block_endpoint_preds[cur_block_endpoint_pred_array ^ 1][block_x].m_pred_bits = (uint8_t)(cur_pred_bits >> 4);8658}8659else8660{8661cur_pred_bits = pState->m_block_endpoint_preds[cur_block_endpoint_pred_array][block_x].m_pred_bits;8662}8663}86648665// Decode endpoint index8666uint32_t endpoint_index, selector_index = 0;86678668const uint32_t pred = cur_pred_bits & 3;8669cur_pred_bits >>= 2;86708671if (pred == 0)8672{8673// Left8674if (!block_x)8675{8676BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (0)\n");8677if (pPVRTC_work_mem)8678free(pPVRTC_work_mem);8679return false;8680}86818682endpoint_index = prev_endpoint_index;8683}8684else if (pred == 1)8685{8686// Upper8687if (!block_y)8688{8689BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (1)\n");8690if (pPVRTC_work_mem)8691free(pPVRTC_work_mem);8692return false;8693}86948695endpoint_index = pState->m_block_endpoint_preds[cur_block_endpoint_pred_array ^ 1][block_x].m_endpoint_index;8696}8697else if (pred == 2)8698{8699if (is_video)8700{8701assert(pred == CR_ENDPOINT_PRED_INDEX);8702endpoint_index = (*pPrev_frame_indices)[block_x + block_y * num_blocks_x];8703selector_index = endpoint_index >> 16;8704endpoint_index &= 0xFFFFU;8705}8706else8707{8708// Upper left8709if ((!block_x) || (!block_y))8710{8711BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (2)\n");8712if (pPVRTC_work_mem)8713free(pPVRTC_work_mem);8714return false;8715}87168717endpoint_index = pState->m_block_endpoint_preds[cur_block_endpoint_pred_array ^ 1][block_x - 1].m_endpoint_index;8718}8719}8720else8721{8722// Decode and apply delta8723const uint32_t delta_sym = sym_codec.decode_huffman(m_delta_endpoint_model);87248725endpoint_index = delta_sym + prev_endpoint_index;8726if (endpoint_index >= endpoints.size())8727endpoint_index -= (int)endpoints.size();8728}87298730pState->m_block_endpoint_preds[cur_block_endpoint_pred_array][block_x].m_endpoint_index = (uint16_t)endpoint_index;87318732prev_endpoint_index = endpoint_index;87338734// Decode selector index8735if ((!is_video) || (pred != CR_ENDPOINT_PRED_INDEX))8736{8737int selector_sym;8738if (cur_selector_rle_count > 0)8739{8740cur_selector_rle_count--;87418742selector_sym = (int)selectors.size();8743}8744else8745{8746selector_sym = sym_codec.decode_huffman(m_selector_model);87478748if (selector_sym == static_cast<int>(SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX))8749{8750int run_sym = sym_codec.decode_huffman(m_selector_history_buf_rle_model);87518752if (run_sym == (SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1))8753cur_selector_rle_count = sym_codec.decode_vlc(7) + SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;8754else8755cur_selector_rle_count = run_sym + SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;87568757if (cur_selector_rle_count > total_blocks)8758{8759// The file is corrupted or we've got a bug.8760BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (3)\n");8761if (pPVRTC_work_mem)8762free(pPVRTC_work_mem);8763return false;8764}87658766selector_sym = (int)selectors.size();87678768cur_selector_rle_count--;8769}8770}87718772if (selector_sym >= (int)selectors.size())8773{8774assert(m_selector_history_buf_size > 0);87758776int history_buf_index = selector_sym - (int)selectors.size();87778778if (history_buf_index >= (int)selector_history_buf.size())8779{8780// The file is corrupted or we've got a bug.8781BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (4)\n");8782if (pPVRTC_work_mem)8783free(pPVRTC_work_mem);8784return false;8785}87868787selector_index = selector_history_buf[history_buf_index];87888789if (history_buf_index != 0)8790selector_history_buf.use(history_buf_index);8791}8792else8793{8794selector_index = selector_sym;87958796if (m_selector_history_buf_size)8797selector_history_buf.add(selector_index);8798}8799}88008801if ((endpoint_index >= endpoints.size()) || (selector_index >= selectors.size()))8802{8803// The file is corrupted or we've got a bug.8804BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (5)\n");8805if (pPVRTC_work_mem)8806free(pPVRTC_work_mem);8807return false;8808}88098810if (is_video)8811(*pPrev_frame_indices)[block_x + block_y * num_blocks_x] = endpoint_index | (selector_index << 16);88128813#if BASISD_ENABLE_DEBUG_FLAGS8814if ((g_debug_flags & cDebugFlagVisCRs) && ((fmt == block_format::cETC1) || (fmt == block_format::cBC1)))8815{8816if ((is_video) && (pred == 2))8817{8818decoder_etc_block* pDst_block = reinterpret_cast<decoder_etc_block*>(static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes);8819memset(pDst_block, 0xFF, 8);8820continue;8821}8822}8823#endif88248825const endpoint* pEndpoints = &endpoints[endpoint_index];8826const selector* pSelector = &selectors[selector_index];88278828switch (fmt)8829{8830case block_format::cETC1:8831{8832decoder_etc_block* pDst_block = reinterpret_cast<decoder_etc_block*>(static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes);88338834block.set_base5_color(decoder_etc_block::pack_color5(pEndpoints->m_color5, false));8835block.set_inten_table(0, pEndpoints->m_inten5);8836block.set_inten_table(1, pEndpoints->m_inten5);88378838pDst_block->m_uint32[0] = block.m_uint32[0];8839pDst_block->set_raw_selector_bits(pSelector->m_bytes[0], pSelector->m_bytes[1], pSelector->m_bytes[2], pSelector->m_bytes[3]);88408841break;8842}8843case block_format::cBC1:8844{8845#if BASISD_SUPPORT_DXT18846void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;8847#if BASISD_ENABLE_DEBUG_FLAGS8848if (g_debug_flags & (cDebugFlagVisBC1Sels | cDebugFlagVisBC1Endpoints))8849convert_etc1s_to_dxt1_vis(static_cast<dxt1_block*>(pDst_block), pEndpoints, pSelector, bc1_allow_threecolor_blocks);8850else8851#endif8852convert_etc1s_to_dxt1(static_cast<dxt1_block*>(pDst_block), pEndpoints, pSelector, bc1_allow_threecolor_blocks);8853#else8854assert(0);8855#endif8856break;8857}8858case block_format::cBC4:8859{8860#if BASISD_SUPPORT_DXT5A8861void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;8862convert_etc1s_to_dxt5a(static_cast<dxt5a_block*>(pDst_block), pEndpoints, pSelector);8863#else8864assert(0);8865#endif8866break;8867}8868case block_format::cPVRTC1_4_RGB:8869{8870#if BASISD_SUPPORT_PVRTC18871block.set_base5_color(decoder_etc_block::pack_color5(pEndpoints->m_color5, false));8872block.set_inten_table(0, pEndpoints->m_inten5);8873block.set_inten_table(1, pEndpoints->m_inten5);8874block.set_raw_selector_bits(pSelector->m_bytes[0], pSelector->m_bytes[1], pSelector->m_bytes[2], pSelector->m_bytes[3]);88758876((decoder_etc_block*)pPVRTC_work_mem)[block_x + block_y * num_blocks_x] = block;88778878const color32& base_color = pEndpoints->m_color5;8879const uint32_t inten_table = pEndpoints->m_inten5;88808881const uint32_t low_selector = pSelector->m_lo_selector;8882const uint32_t high_selector = pSelector->m_hi_selector;88838884// Get block's RGB bounding box8885color32 block_colors[2];8886decoder_etc_block::get_block_colors5_bounds(block_colors, base_color, inten_table, low_selector, high_selector);88878888assert(block_colors[0][0] <= block_colors[1][0]);8889assert(block_colors[0][1] <= block_colors[1][1]);8890assert(block_colors[0][2] <= block_colors[1][2]);88918892// Set PVRTC1 endpoints to floor/ceil of bounding box's coordinates.8893pvrtc4_block temp;8894temp.set_opaque_endpoint_floor(0, block_colors[0]);8895temp.set_opaque_endpoint_ceil(1, block_colors[1]);88968897pPVRTC_endpoints[block_x + block_y * num_blocks_x] = temp.m_endpoints;8898#else8899assert(0);8900#endif89018902break;8903}8904case block_format::cPVRTC1_4_RGBA:8905{8906#if BASISD_SUPPORT_PVRTC18907assert(pAlpha_blocks);89088909block.set_base5_color(decoder_etc_block::pack_color5(pEndpoints->m_color5, false));8910block.set_inten_table(0, pEndpoints->m_inten5);8911block.set_inten_table(1, pEndpoints->m_inten5);8912block.set_raw_selector_bits(pSelector->m_selectors[0], pSelector->m_selectors[1], pSelector->m_selectors[2], pSelector->m_selectors[3]);89138914((decoder_etc_block*)pPVRTC_work_mem)[block_x + block_y * num_blocks_x] = block;89158916// Get block's RGBA bounding box8917const color32& base_color = pEndpoints->m_color5;8918const uint32_t inten_table = pEndpoints->m_inten5;8919const uint32_t low_selector = pSelector->m_lo_selector;8920const uint32_t high_selector = pSelector->m_hi_selector;8921color32 block_colors[2];8922decoder_etc_block::get_block_colors5_bounds(block_colors, base_color, inten_table, low_selector, high_selector);89238924assert(block_colors[0][0] <= block_colors[1][0]);8925assert(block_colors[0][1] <= block_colors[1][1]);8926assert(block_colors[0][2] <= block_colors[1][2]);89278928const uint16_t* pAlpha_block = reinterpret_cast<uint16_t*>(static_cast<uint8_t*>(pAlpha_blocks) + (block_x + block_y * num_blocks_x) * sizeof(uint32_t));89298930const endpoint* pAlpha_endpoints = &endpoints[pAlpha_block[0]];8931const selector* pAlpha_selector = &selectors[pAlpha_block[1]];89328933const color32& alpha_base_color = pAlpha_endpoints->m_color5;8934const uint32_t alpha_inten_table = pAlpha_endpoints->m_inten5;8935const uint32_t alpha_low_selector = pAlpha_selector->m_lo_selector;8936const uint32_t alpha_high_selector = pAlpha_selector->m_hi_selector;8937uint32_t alpha_block_colors[2];8938decoder_etc_block::get_block_colors5_bounds_g(alpha_block_colors, alpha_base_color, alpha_inten_table, alpha_low_selector, alpha_high_selector);8939assert(alpha_block_colors[0] <= alpha_block_colors[1]);8940block_colors[0].a = (uint8_t)alpha_block_colors[0];8941block_colors[1].a = (uint8_t)alpha_block_colors[1];89428943// Set PVRTC1 endpoints to floor/ceil of bounding box's coordinates.8944pvrtc4_block temp;8945temp.set_endpoint_floor(0, block_colors[0]);8946temp.set_endpoint_ceil(1, block_colors[1]);89478948pPVRTC_endpoints[block_x + block_y * num_blocks_x] = temp.m_endpoints;8949#else8950assert(0);8951#endif89528953break;8954}8955case block_format::cBC7: // for more consistency with UASTC8956case block_format::cBC7_M5_COLOR:8957{8958#if BASISD_SUPPORT_BC7_MODE58959if (bc7_chroma_filtering)8960{8961assert(endpoint_index <= UINT16_MAX);8962decoded_endpoints(block_x, block_y) = (uint16_t)endpoint_index;8963}89648965void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;8966convert_etc1s_to_bc7_m5_color(pDst_block, pEndpoints, pSelector);8967#else8968assert(0);8969#endif8970break;8971}8972case block_format::cBC7_M5_ALPHA:8973{8974#if BASISD_SUPPORT_BC7_MODE58975void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;8976convert_etc1s_to_bc7_m5_alpha(pDst_block, pEndpoints, pSelector);8977#else8978assert(0);8979#endif8980break;8981}8982case block_format::cETC2_EAC_A8:8983{8984#if BASISD_SUPPORT_ETC2_EAC_A88985void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;8986convert_etc1s_to_etc2_eac_a8(static_cast<eac_block*>(pDst_block), pEndpoints, pSelector);8987#else8988assert(0);8989#endif8990break;8991}8992case block_format::cASTC_4x4:8993{8994#if BASISD_SUPPORT_ASTC8995void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;8996convert_etc1s_to_astc_4x4(pDst_block, pEndpoints, pSelector, transcode_alpha, &endpoints[0], &selectors[0]);8997#else8998assert(0);8999#endif9000break;9001}9002case block_format::cATC_RGB:9003{9004#if BASISD_SUPPORT_ATC9005void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;9006convert_etc1s_to_atc(pDst_block, pEndpoints, pSelector);9007#else9008assert(0);9009#endif9010break;9011}9012case block_format::cFXT1_RGB:9013{9014#if BASISD_SUPPORT_FXT19015const uint32_t fxt1_block_x = block_x >> 1;9016const uint32_t fxt1_block_y = block_y;9017const uint32_t fxt1_subblock = block_x & 1;90189019void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (fxt1_block_x + fxt1_block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;90209021convert_etc1s_to_fxt1(pDst_block, pEndpoints, pSelector, fxt1_subblock);9022#else9023assert(0);9024#endif9025break;9026}9027case block_format::cPVRTC2_4_RGB:9028{9029#if BASISD_SUPPORT_PVRTC29030void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;9031convert_etc1s_to_pvrtc2_rgb(pDst_block, pEndpoints, pSelector);9032#endif9033break;9034}9035case block_format::cPVRTC2_4_RGBA:9036{9037#if BASISD_SUPPORT_PVRTC29038assert(transcode_alpha);90399040void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;90419042convert_etc1s_to_pvrtc2_rgba(pDst_block, pEndpoints, pSelector, &endpoints[0], &selectors[0]);9043#endif9044break;9045}9046case block_format::cIndices:9047{9048uint16_t* pDst_block = reinterpret_cast<uint16_t *>(static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes);9049pDst_block[0] = static_cast<uint16_t>(endpoint_index);9050pDst_block[1] = static_cast<uint16_t>(selector_index);9051break;9052}9053case block_format::cA32:9054{9055assert(sizeof(uint32_t) == output_block_or_pixel_stride_in_bytes);9056uint8_t* pDst_pixels = static_cast<uint8_t*>(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint32_t);90579058const uint32_t max_x = basisu::minimum<int>(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);9059const uint32_t max_y = basisu::minimum<int>(4, (int)output_rows_in_pixels - (int)block_y * 4);90609061int colors[4];9062decoder_etc_block::get_block_colors5_g(colors, pEndpoints->m_color5, pEndpoints->m_inten5);90639064if (max_x == 4)9065{9066for (uint32_t y = 0; y < max_y; y++)9067{9068const uint32_t s = pSelector->m_selectors[y];90699070pDst_pixels[3] = static_cast<uint8_t>(colors[s & 3]);9071pDst_pixels[3+4] = static_cast<uint8_t>(colors[(s >> 2) & 3]);9072pDst_pixels[3+8] = static_cast<uint8_t>(colors[(s >> 4) & 3]);9073pDst_pixels[3+12] = static_cast<uint8_t>(colors[(s >> 6) & 3]);90749075pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint32_t);9076}9077}9078else9079{9080for (uint32_t y = 0; y < max_y; y++)9081{9082const uint32_t s = pSelector->m_selectors[y];90839084for (uint32_t x = 0; x < max_x; x++)9085pDst_pixels[3 + 4 * x] = static_cast<uint8_t>(colors[(s >> (x * 2)) & 3]);90869087pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint32_t);9088}9089}90909091break;9092}9093case block_format::cRGB32:9094{9095assert(sizeof(uint32_t) == output_block_or_pixel_stride_in_bytes);9096uint8_t* pDst_pixels = static_cast<uint8_t*>(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint32_t);90979098const uint32_t max_x = basisu::minimum<int>(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);9099const uint32_t max_y = basisu::minimum<int>(4, (int)output_rows_in_pixels - (int)block_y * 4);91009101color32 colors[4];9102decoder_etc_block::get_block_colors5(colors, pEndpoints->m_color5, pEndpoints->m_inten5);91039104for (uint32_t y = 0; y < max_y; y++)9105{9106const uint32_t s = pSelector->m_selectors[y];91079108for (uint32_t x = 0; x < max_x; x++)9109{9110const color32& c = colors[(s >> (x * 2)) & 3];91119112pDst_pixels[0 + 4 * x] = c.r;9113pDst_pixels[1 + 4 * x] = c.g;9114pDst_pixels[2 + 4 * x] = c.b;9115}91169117pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint32_t);9118}91199120break;9121}9122case block_format::cRGBA32:9123{9124assert(sizeof(uint32_t) == output_block_or_pixel_stride_in_bytes);9125uint8_t* pDst_pixels = static_cast<uint8_t*>(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint32_t);91269127const uint32_t max_x = basisu::minimum<int>(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);9128const uint32_t max_y = basisu::minimum<int>(4, (int)output_rows_in_pixels - (int)block_y * 4);91299130color32 colors[4];9131decoder_etc_block::get_block_colors5(colors, pEndpoints->m_color5, pEndpoints->m_inten5);91329133for (uint32_t y = 0; y < max_y; y++)9134{9135const uint32_t s = pSelector->m_selectors[y];91369137for (uint32_t x = 0; x < max_x; x++)9138{9139const color32& c = colors[(s >> (x * 2)) & 3];91409141pDst_pixels[0 + 4 * x] = c.r;9142pDst_pixels[1 + 4 * x] = c.g;9143pDst_pixels[2 + 4 * x] = c.b;9144pDst_pixels[3 + 4 * x] = 255;9145}91469147pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint32_t);9148}91499150break;9151}9152case block_format::cRGB565:9153case block_format::cBGR565:9154{9155assert(sizeof(uint16_t) == output_block_or_pixel_stride_in_bytes);9156uint8_t* pDst_pixels = static_cast<uint8_t*>(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint16_t);91579158const uint32_t max_x = basisu::minimum<int>(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);9159const uint32_t max_y = basisu::minimum<int>(4, (int)output_rows_in_pixels - (int)block_y * 4);91609161color32 colors[4];9162decoder_etc_block::get_block_colors5(colors, pEndpoints->m_color5, pEndpoints->m_inten5);91639164uint16_t packed_colors[4];9165if (fmt == block_format::cRGB565)9166{9167for (uint32_t i = 0; i < 4; i++)9168{9169packed_colors[i] = static_cast<uint16_t>((mul_8(colors[i].r, 31) << 11) | (mul_8(colors[i].g, 63) << 5) | mul_8(colors[i].b, 31));9170if (BASISD_IS_BIG_ENDIAN)9171packed_colors[i] = byteswap_uint16(packed_colors[i]);9172}9173}9174else9175{9176for (uint32_t i = 0; i < 4; i++)9177{9178packed_colors[i] = static_cast<uint16_t>((mul_8(colors[i].b, 31) << 11) | (mul_8(colors[i].g, 63) << 5) | mul_8(colors[i].r, 31));9179if (BASISD_IS_BIG_ENDIAN)9180packed_colors[i] = byteswap_uint16(packed_colors[i]);9181}9182}91839184for (uint32_t y = 0; y < max_y; y++)9185{9186const uint32_t s = pSelector->m_selectors[y];91879188for (uint32_t x = 0; x < max_x; x++)9189reinterpret_cast<uint16_t *>(pDst_pixels)[x] = packed_colors[(s >> (x * 2)) & 3];91909191pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint16_t);9192}91939194break;9195}9196case block_format::cRGBA4444_COLOR:9197{9198assert(sizeof(uint16_t) == output_block_or_pixel_stride_in_bytes);9199uint8_t* pDst_pixels = static_cast<uint8_t*>(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint16_t);92009201const uint32_t max_x = basisu::minimum<int>(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);9202const uint32_t max_y = basisu::minimum<int>(4, (int)output_rows_in_pixels - (int)block_y * 4);92039204color32 colors[4];9205decoder_etc_block::get_block_colors5(colors, pEndpoints->m_color5, pEndpoints->m_inten5);92069207uint16_t packed_colors[4];9208for (uint32_t i = 0; i < 4; i++)9209{9210packed_colors[i] = static_cast<uint16_t>((mul_8(colors[i].r, 15) << 12) | (mul_8(colors[i].g, 15) << 8) | (mul_8(colors[i].b, 15) << 4));9211}92129213for (uint32_t y = 0; y < max_y; y++)9214{9215const uint32_t s = pSelector->m_selectors[y];92169217for (uint32_t x = 0; x < max_x; x++)9218{9219uint16_t cur = reinterpret_cast<uint16_t*>(pDst_pixels)[x];9220if (BASISD_IS_BIG_ENDIAN)9221cur = byteswap_uint16(cur);92229223cur = (cur & 0xF) | packed_colors[(s >> (x * 2)) & 3];92249225if (BASISD_IS_BIG_ENDIAN)9226cur = byteswap_uint16(cur);92279228reinterpret_cast<uint16_t*>(pDst_pixels)[x] = cur;9229}92309231pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint16_t);9232}92339234break;9235}9236case block_format::cRGBA4444_COLOR_OPAQUE:9237{9238assert(sizeof(uint16_t) == output_block_or_pixel_stride_in_bytes);9239uint8_t* pDst_pixels = static_cast<uint8_t*>(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint16_t);92409241const uint32_t max_x = basisu::minimum<int>(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);9242const uint32_t max_y = basisu::minimum<int>(4, (int)output_rows_in_pixels - (int)block_y * 4);92439244color32 colors[4];9245decoder_etc_block::get_block_colors5(colors, pEndpoints->m_color5, pEndpoints->m_inten5);92469247uint16_t packed_colors[4];9248for (uint32_t i = 0; i < 4; i++)9249{9250packed_colors[i] = static_cast<uint16_t>((mul_8(colors[i].r, 15) << 12) | (mul_8(colors[i].g, 15) << 8) | (mul_8(colors[i].b, 15) << 4) | 0xF);9251if (BASISD_IS_BIG_ENDIAN)9252packed_colors[i] = byteswap_uint16(packed_colors[i]);9253}92549255for (uint32_t y = 0; y < max_y; y++)9256{9257const uint32_t s = pSelector->m_selectors[y];92589259for (uint32_t x = 0; x < max_x; x++)9260reinterpret_cast<uint16_t*>(pDst_pixels)[x] = packed_colors[(s >> (x * 2)) & 3];92619262pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint16_t);9263}92649265break;9266}9267case block_format::cRGBA4444_ALPHA:9268{9269assert(sizeof(uint16_t) == output_block_or_pixel_stride_in_bytes);9270uint8_t* pDst_pixels = static_cast<uint8_t*>(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint16_t);92719272const uint32_t max_x = basisu::minimum<int>(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);9273const uint32_t max_y = basisu::minimum<int>(4, (int)output_rows_in_pixels - (int)block_y * 4);92749275color32 colors[4];9276decoder_etc_block::get_block_colors5(colors, pEndpoints->m_color5, pEndpoints->m_inten5);92779278uint16_t packed_colors[4];9279for (uint32_t i = 0; i < 4; i++)9280{9281packed_colors[i] = mul_8(colors[i].g, 15);9282if (BASISD_IS_BIG_ENDIAN)9283packed_colors[i] = byteswap_uint16(packed_colors[i]);9284}92859286for (uint32_t y = 0; y < max_y; y++)9287{9288const uint32_t s = pSelector->m_selectors[y];92899290for (uint32_t x = 0; x < max_x; x++)9291{9292reinterpret_cast<uint16_t*>(pDst_pixels)[x] = packed_colors[(s >> (x * 2)) & 3];9293}92949295pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint16_t);9296}92979298break;9299}9300case block_format::cETC2_EAC_R11:9301{9302#if BASISD_SUPPORT_ETC2_EAC_RG119303void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;9304convert_etc1s_to_etc2_eac_r11(static_cast<eac_block*>(pDst_block), pEndpoints, pSelector);9305#else9306assert(0);9307#endif9308break;9309}9310default:9311{9312assert(0);9313break;9314}9315}93169317} // block_x93189319} // block_y93209321if (endpoint_pred_repeat_count != 0)9322{9323BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: endpoint_pred_repeat_count != 0. The file is corrupted or this is a bug\n");93249325if (pPVRTC_work_mem)9326free(pPVRTC_work_mem);93279328return false;9329}93309331//assert(endpoint_pred_repeat_count == 0);93329333#if BASISD_SUPPORT_PVRTC19334// PVRTC post process - create per-pixel modulation values.9335if (fmt == block_format::cPVRTC1_4_RGB)9336fixup_pvrtc1_4_modulation_rgb((decoder_etc_block*)pPVRTC_work_mem, pPVRTC_endpoints, pDst_blocks, num_blocks_x, num_blocks_y);9337else if (fmt == block_format::cPVRTC1_4_RGBA)9338fixup_pvrtc1_4_modulation_rgba((decoder_etc_block*)pPVRTC_work_mem, pPVRTC_endpoints, pDst_blocks, num_blocks_x, num_blocks_y, pAlpha_blocks, &endpoints[0], &selectors[0]);9339#endif // BASISD_SUPPORT_PVRTC193409341#if BASISD_SUPPORT_BC7_MODE59342if (bc7_chroma_filtering)9343{9344chroma_filter_bc7_mode5(decoded_endpoints, pDst_blocks, num_blocks_x, num_blocks_y, output_row_pitch_in_blocks_or_pixels, &endpoints[0]);9345}9346#endif93479348if (pPVRTC_work_mem)9349free(pPVRTC_work_mem);93509351return true;9352}93539354bool basis_validate_output_buffer_size(9355basis_tex_format source_format,9356transcoder_texture_format target_format,9357uint32_t output_blocks_buf_size_in_blocks_or_pixels,9358uint32_t orig_width, uint32_t orig_height,9359uint32_t output_row_pitch_in_blocks_or_pixels,9360uint32_t output_rows_in_pixels)9361{9362BASISU_NOTE_UNUSED(source_format);93639364if (basis_transcoder_format_is_uncompressed(target_format))9365{9366// Assume the output buffer is orig_width by orig_height9367if (!output_row_pitch_in_blocks_or_pixels)9368output_row_pitch_in_blocks_or_pixels = orig_width;93699370if (!output_rows_in_pixels)9371output_rows_in_pixels = orig_height;93729373// Now make sure the output buffer is large enough, or we'll overwrite memory.9374if (output_blocks_buf_size_in_blocks_or_pixels < (output_rows_in_pixels * output_row_pitch_in_blocks_or_pixels))9375{9376BASISU_DEVEL_ERROR("basis_validate_output_buffer_size: output_blocks_buf_size_in_blocks_or_pixels < (output_rows_in_pixels * output_row_pitch_in_blocks_or_pixels)\n");9377return false;9378}9379}9380else9381{9382const uint32_t dst_block_width = basis_get_block_width(target_format);9383const uint32_t dst_block_height = basis_get_block_height(target_format);9384//const uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(target_format);93859386// Take into account the destination format's block width/height.9387const uint32_t num_dst_blocks_x = (orig_width + dst_block_width - 1) / dst_block_width;9388const uint32_t num_dst_blocks_y = (orig_height + dst_block_height - 1) / dst_block_height;9389const uint32_t total_dst_blocks = num_dst_blocks_x * num_dst_blocks_y;93909391assert(total_dst_blocks);93929393// Note this only computes the # of blocks we will write during transcoding, but for PVRTC1 OpenGL may require more for very small textures.9394// basis_compute_transcoded_image_size_in_bytes() may return larger buffers.9395if (output_blocks_buf_size_in_blocks_or_pixels < total_dst_blocks)9396{9397BASISU_DEVEL_ERROR("basis_validate_output_buffer_size: output_blocks_buf_size_in_blocks_or_pixels is too small\n");9398return false;9399}9400}94019402return true;9403}94049405uint32_t basis_compute_transcoded_image_size_in_bytes(transcoder_texture_format target_format, uint32_t orig_width, uint32_t orig_height)9406{9407assert(orig_width && orig_height);94089409const uint32_t dst_block_width = basis_get_block_width(target_format);9410const uint32_t dst_block_height = basis_get_block_height(target_format);94119412if (basis_transcoder_format_is_uncompressed(target_format))9413{9414// Uncompressed formats are just plain raster images.9415const uint32_t bytes_per_pixel = basis_get_uncompressed_bytes_per_pixel(target_format);9416const uint32_t bytes_per_line = orig_width * bytes_per_pixel;9417const uint32_t bytes_per_slice = bytes_per_line * orig_height;9418return bytes_per_slice;9419}94209421// Compressed formats are 2D arrays of blocks.9422const uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(target_format);94239424if ((target_format == transcoder_texture_format::cTFPVRTC1_4_RGB) || (target_format == transcoder_texture_format::cTFPVRTC1_4_RGBA))9425{9426// For PVRTC1, Basis only writes (or requires) total_blocks * bytes_per_block. But GL requires extra padding for very small textures:9427// https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt9428const uint32_t width = (orig_width + 3) & ~3;9429const uint32_t height = (orig_height + 3) & ~3;9430const uint32_t size_in_bytes = (std::max(8U, width) * std::max(8U, height) * 4 + 7) / 8;9431return size_in_bytes;9432}94339434// Take into account the destination format's block width/height.9435const uint32_t num_dst_blocks_x = (orig_width + dst_block_width - 1) / dst_block_width;9436const uint32_t num_dst_blocks_y = (orig_height + dst_block_height - 1) / dst_block_height;9437const uint32_t total_dst_blocks = num_dst_blocks_x * num_dst_blocks_y;94389439assert(total_dst_blocks);94409441return total_dst_blocks * bytes_per_block;9442}94439444bool basisu_lowlevel_etc1s_transcoder::transcode_image(9445transcoder_texture_format target_format,9446void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,9447const uint8_t* pCompressed_data, uint32_t compressed_data_length,9448uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,9449uint64_t rgb_offset, uint32_t rgb_length, uint64_t alpha_offset, uint32_t alpha_length,9450uint32_t decode_flags,9451bool basis_file_has_alpha_slices,9452bool is_video,9453uint32_t output_row_pitch_in_blocks_or_pixels,9454basisu_transcoder_state* pState,9455uint32_t output_rows_in_pixels)9456{9457if (((uint64_t)rgb_offset + rgb_length) > (uint64_t)compressed_data_length)9458{9459BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: source data buffer too small (color)\n");9460return false;9461}94629463if (alpha_length)9464{9465if (((uint64_t)alpha_offset + alpha_length) > (uint64_t)compressed_data_length)9466{9467BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: source data buffer too small (alpha)\n");9468return false;9469}9470}9471else9472{9473assert(!basis_file_has_alpha_slices);9474}94759476if ((target_format == transcoder_texture_format::cTFPVRTC1_4_RGB) || (target_format == transcoder_texture_format::cTFPVRTC1_4_RGBA))9477{9478if ((!basisu::is_pow2(num_blocks_x * 4)) || (!basisu::is_pow2(num_blocks_y * 4)))9479{9480// PVRTC1 only supports power of 2 dimensions9481BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: PVRTC1 only supports power of 2 dimensions\n");9482return false;9483}9484}94859486if ((target_format == transcoder_texture_format::cTFPVRTC1_4_RGBA) && (!basis_file_has_alpha_slices))9487{9488// Switch to PVRTC1 RGB if the input doesn't have alpha.9489target_format = transcoder_texture_format::cTFPVRTC1_4_RGB;9490}94919492const bool transcode_alpha_data_to_opaque_formats = (decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;9493const uint32_t bytes_per_block_or_pixel = basis_get_bytes_per_block_or_pixel(target_format);9494const uint32_t total_slice_blocks = num_blocks_x * num_blocks_y;94959496if (!basis_validate_output_buffer_size(basis_tex_format::cETC1S, target_format, output_blocks_buf_size_in_blocks_or_pixels, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, output_rows_in_pixels))9497{9498BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: output buffer size too small\n");9499return false;9500}95019502bool status = false;95039504const uint8_t* pData = pCompressed_data + rgb_offset;9505uint32_t data_len = rgb_length;9506bool is_alpha_slice = false;95079508// If the caller wants us to transcode the mip level's alpha data, then use the next slice.9509if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))9510{9511pData = pCompressed_data + alpha_offset;9512data_len = alpha_length;9513is_alpha_slice = true;9514}95159516switch (target_format)9517{9518case transcoder_texture_format::cTFETC1_RGB:9519{9520//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9521status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cETC1, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);95229523if (!status)9524{9525BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ETC1 failed\n");9526}9527break;9528}9529case transcoder_texture_format::cTFBC1_RGB:9530{9531#if !BASISD_SUPPORT_DXT19532BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: BC1/DXT1 unsupported\n");9533return false;9534#else9535// status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9536status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cBC1, bytes_per_block_or_pixel, true, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9537if (!status)9538{9539BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC1 failed\n");9540}9541break;9542#endif9543}9544case transcoder_texture_format::cTFBC4_R:9545{9546#if !BASISD_SUPPORT_DXT5A9547BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: BC4/DXT5A unsupported\n");9548return false;9549#else9550//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9551status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cBC4, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9552if (!status)9553{9554BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC4 failed\n");9555}9556break;9557#endif9558}9559case transcoder_texture_format::cTFPVRTC1_4_RGB:9560{9561#if !BASISD_SUPPORT_PVRTC19562BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: PVRTC1 4 unsupported\n");9563return false;9564#else9565// output_row_pitch_in_blocks_or_pixels is actually ignored because we're transcoding to PVRTC1. (Print a dev warning if it's != 0?)9566//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9567status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cPVRTC1_4_RGB, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9568if (!status)9569{9570BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to PVRTC1 4 RGB failed\n");9571}9572break;9573#endif9574}9575case transcoder_texture_format::cTFPVRTC1_4_RGBA:9576{9577#if !BASISD_SUPPORT_PVRTC19578BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: PVRTC1 4 unsupported\n");9579return false;9580#else9581assert(basis_file_has_alpha_slices);9582assert(alpha_length);95839584// Temp buffer to hold alpha block endpoint/selector indices9585basisu::vector<uint32_t> temp_block_indices(total_slice_blocks);95869587// First transcode alpha data to temp buffer9588//status = transcode_slice(pData, data_size, slice_index + 1, &temp_block_indices[0], total_slice_blocks, block_format::cIndices, sizeof(uint32_t), decode_flags, pSlice_descs[slice_index].m_num_blocks_x, pState);9589status = transcode_slice(&temp_block_indices[0], num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cIndices, sizeof(uint32_t), false, is_video, true, level_index, orig_width, orig_height, num_blocks_x, pState, false, nullptr, 0, decode_flags);9590if (!status)9591{9592BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to PVRTC1 4 RGBA failed (0)\n");9593}9594else9595{9596// output_row_pitch_in_blocks_or_pixels is actually ignored because we're transcoding to PVRTC1. (Print a dev warning if it's != 0?)9597//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGBA, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState, &temp_block_indices[0]);9598status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cPVRTC1_4_RGBA, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, &temp_block_indices[0], 0, decode_flags);9599if (!status)9600{9601BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to PVRTC1 4 RGBA failed (1)\n");9602}9603}96049605break;9606#endif9607}9608case transcoder_texture_format::cTFBC7_RGBA:9609case transcoder_texture_format::cTFBC7_ALT:9610{9611#if !BASISD_SUPPORT_BC7_MODE59612BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: BC7 unsupported\n");9613return false;9614#else9615assert(bytes_per_block_or_pixel == 16);9616// We used to support transcoding just alpha to BC7 - but is that useful at all?96179618// First transcode the color slice. The cBC7_M5_COLOR transcoder will output opaque mode 5 blocks.9619//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7_M5_COLOR, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9620status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cBC7_M5_COLOR, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);96219622if ((status) && (basis_file_has_alpha_slices))9623{9624// Now transcode the alpha slice. The cBC7_M5_ALPHA transcoder will now change the opaque mode 5 blocks to blocks with alpha.9625//status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7_M5_ALPHA, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9626status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cBC7_M5_ALPHA, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9627}96289629if (!status)9630{9631BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC7 failed (0)\n");9632}96339634break;9635#endif9636}9637case transcoder_texture_format::cTFETC2_RGBA:9638{9639#if !BASISD_SUPPORT_ETC2_EAC_A89640BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: ETC2 EAC A8 unsupported\n");9641return false;9642#else9643assert(bytes_per_block_or_pixel == 16);96449645if (basis_file_has_alpha_slices)9646{9647// First decode the alpha data9648//status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_A8, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9649status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cETC2_EAC_A8, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9650}9651else9652{9653//write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_A8, 16, output_row_pitch_in_blocks_or_pixels);9654basisu_transcoder::write_opaque_alpha_blocks(num_blocks_x, num_blocks_y, pOutput_blocks, block_format::cETC2_EAC_A8, 16, output_row_pitch_in_blocks_or_pixels);9655status = true;9656}96579658if (status)9659{9660// Now decode the color data9661//status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9662status = transcode_slice((uint8_t *)pOutput_blocks + 8, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cETC1, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9663if (!status)9664{9665BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ETC2 RGB failed\n");9666}9667}9668else9669{9670BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ETC2 A failed\n");9671}9672break;9673#endif9674}9675case transcoder_texture_format::cTFBC3_RGBA:9676{9677#if !BASISD_SUPPORT_DXT19678BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: DXT1 unsupported\n");9679return false;9680#elif !BASISD_SUPPORT_DXT5A9681BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: DXT5A unsupported\n");9682return false;9683#else9684assert(bytes_per_block_or_pixel == 16);96859686// First decode the alpha data9687if (basis_file_has_alpha_slices)9688{9689//status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9690status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cBC4, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9691}9692else9693{9694basisu_transcoder::write_opaque_alpha_blocks(num_blocks_x, num_blocks_y, pOutput_blocks, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);9695status = true;9696}96979698if (status)9699{9700// Now decode the color data. Forbid 3 color blocks, which aren't allowed in BC3.9701//status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, 16, decode_flags | cDecodeFlagsBC1ForbidThreeColorBlocks, output_row_pitch_in_blocks_or_pixels, pState);9702status = transcode_slice((uint8_t *)pOutput_blocks + 8, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cBC1, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9703if (!status)9704{9705BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC3 RGB failed\n");9706}9707}9708else9709{9710BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC3 A failed\n");9711}97129713break;9714#endif9715}9716case transcoder_texture_format::cTFBC5_RG:9717{9718#if !BASISD_SUPPORT_DXT5A9719BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: DXT5A unsupported\n");9720return false;9721#else9722assert(bytes_per_block_or_pixel == 16);97239724//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,9725// 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,9726// basisu_transcoder_state* pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0);97279728// Decode the R data (actually the green channel of the color data slice in the basis file)9729//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9730status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cBC4, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9731if (status)9732{9733if (basis_file_has_alpha_slices)9734{9735// Decode the G data (actually the green channel of the alpha data slice in the basis file)9736//status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9737status = transcode_slice((uint8_t *)pOutput_blocks + 8, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cBC4, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9738if (!status)9739{9740BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC5 1 failed\n");9741}9742}9743else9744{9745basisu_transcoder::write_opaque_alpha_blocks(num_blocks_x, num_blocks_y, (uint8_t*)pOutput_blocks + 8, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);9746status = true;9747}9748}9749else9750{9751BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC5 channel 0 failed\n");9752}9753break;9754#endif9755}9756case transcoder_texture_format::cTFASTC_4x4_RGBA:9757{9758#if !BASISD_SUPPORT_ASTC9759BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: ASTC unsupported\n");9760return false;9761#else9762assert(bytes_per_block_or_pixel == 16);97639764if (basis_file_has_alpha_slices)9765{9766// First decode the alpha data to the output (we're using the output texture as a temp buffer here).9767//status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cIndices, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9768status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cIndices, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9769if (status)9770{9771// Now decode the color data and transcode to ASTC. The transcoder function will read the alpha selector data from the output texture as it converts and9772// transcode both the alpha and color data at the same time to ASTC.9773//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, 16, decode_flags | cDecodeFlagsOutputHasAlphaIndices, output_row_pitch_in_blocks_or_pixels, pState);9774status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cASTC_4x4, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, true, nullptr, output_rows_in_pixels, decode_flags);9775}9776}9777else9778//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9779status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cASTC_4x4, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);97809781if (!status)9782{9783BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ASTC failed (0)\n");9784}97859786break;9787#endif9788}9789case transcoder_texture_format::cTFATC_RGB:9790{9791#if !BASISD_SUPPORT_ATC9792BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: ATC unsupported\n");9793return false;9794#else9795//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cATC_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9796status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cATC_RGB, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9797if (!status)9798{9799BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ATC_RGB failed\n");9800}9801break;9802#endif9803}9804case transcoder_texture_format::cTFATC_RGBA:9805{9806#if !BASISD_SUPPORT_ATC9807BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: ATC unsupported\n");9808return false;9809#elif !BASISD_SUPPORT_DXT5A9810BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: DXT5A unsupported\n");9811return false;9812#else9813assert(bytes_per_block_or_pixel == 16);98149815// First decode the alpha data9816if (basis_file_has_alpha_slices)9817{9818//status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9819status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cBC4, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9820}9821else9822{9823basisu_transcoder::write_opaque_alpha_blocks(num_blocks_x, num_blocks_y, pOutput_blocks, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);9824status = true;9825}98269827if (status)9828{9829//status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cATC_RGB, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9830status = transcode_slice((uint8_t *)pOutput_blocks + 8, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cATC_RGB, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9831if (!status)9832{9833BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ATC RGB failed\n");9834}9835}9836else9837{9838BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ATC A failed\n");9839}9840break;9841#endif9842}9843case transcoder_texture_format::cTFPVRTC2_4_RGB:9844{9845#if !BASISD_SUPPORT_PVRTC29846BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: PVRTC2 unsupported\n");9847return false;9848#else9849//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9850status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cPVRTC2_4_RGB, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9851if (!status)9852{9853BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to cPVRTC2_4_RGB failed\n");9854}9855break;9856#endif9857}9858case transcoder_texture_format::cTFPVRTC2_4_RGBA:9859{9860#if !BASISD_SUPPORT_PVRTC29861BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: PVRTC2 unsupported\n");9862return false;9863#else9864if (basis_file_has_alpha_slices)9865{9866// First decode the alpha data to the output (we're using the output texture as a temp buffer here).9867//status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cIndices, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9868status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cIndices, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9869if (!status)9870{9871BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to failed\n");9872}9873else9874{9875// Now decode the color data and transcode to PVRTC2 RGBA.9876//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGBA, bytes_per_block_or_pixel, decode_flags | cDecodeFlagsOutputHasAlphaIndices, output_row_pitch_in_blocks_or_pixels, pState);9877status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cPVRTC2_4_RGBA, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, true, nullptr, output_rows_in_pixels, decode_flags);9878}9879}9880else9881//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9882status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cPVRTC2_4_RGB, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);98839884if (!status)9885{9886BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to cPVRTC2_4_RGBA failed\n");9887}98889889break;9890#endif9891}9892case transcoder_texture_format::cTFRGBA32:9893{9894// Raw 32bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.98959896// First decode the alpha data9897if (basis_file_has_alpha_slices)9898//status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cA32, sizeof(uint32_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);9899status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cA32, sizeof(uint32_t), false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9900else9901status = true;99029903if (status)9904{9905//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, basis_file_has_alpha_slices ? block_format::cRGB32 : block_format::cRGBA32, sizeof(uint32_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);9906status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, basis_file_has_alpha_slices ? block_format::cRGB32 : block_format::cRGBA32, sizeof(uint32_t), false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9907if (!status)9908{9909BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to RGBA32 RGB failed\n");9910}9911}9912else9913{9914BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to RGBA32 A failed\n");9915}99169917break;9918}9919case transcoder_texture_format::cTFRGB565:9920case transcoder_texture_format::cTFBGR565:9921{9922// Raw 16bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.99239924//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, (fmt == transcoder_texture_format::cTFRGB565) ? block_format::cRGB565 : block_format::cBGR565, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);9925status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, (target_format == transcoder_texture_format::cTFRGB565) ? block_format::cRGB565 : block_format::cBGR565, sizeof(uint16_t), false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9926if (!status)9927{9928BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to RGB565 RGB failed\n");9929}99309931break;9932}9933case transcoder_texture_format::cTFRGBA4444:9934{9935// Raw 16bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.99369937// First decode the alpha data9938if (basis_file_has_alpha_slices)9939//status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGBA4444_ALPHA, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);9940status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cRGBA4444_ALPHA, sizeof(uint16_t), false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9941else9942status = true;99439944if (status)9945{9946//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, basis_file_has_alpha_slices ? block_format::cRGBA4444_COLOR : block_format::cRGBA4444_COLOR_OPAQUE, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);9947status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, basis_file_has_alpha_slices ? block_format::cRGBA4444_COLOR : block_format::cRGBA4444_COLOR_OPAQUE, sizeof(uint16_t), false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9948if (!status)9949{9950BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to RGBA4444 RGB failed\n");9951}9952}9953else9954{9955BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to RGBA4444 A failed\n");9956}99579958break;9959}9960case transcoder_texture_format::cTFFXT1_RGB:9961{9962#if !BASISD_SUPPORT_FXT19963BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: FXT1 unsupported\n");9964return false;9965#else9966//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cFXT1_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9967status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cFXT1_RGB, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9968if (!status)9969{9970BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to FXT1_RGB failed\n");9971}9972break;9973#endif9974}9975case transcoder_texture_format::cTFETC2_EAC_R11:9976{9977#if !BASISD_SUPPORT_ETC2_EAC_RG119978BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: EAC_RG11 unsupported\n");9979return false;9980#else9981//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);9982status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);9983if (!status)9984{9985BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ETC2_EAC_R11 failed\n");9986}99879988break;9989#endif9990}9991case transcoder_texture_format::cTFETC2_EAC_RG11:9992{9993#if !BASISD_SUPPORT_ETC2_EAC_RG119994BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: EAC_RG11 unsupported\n");9995return false;9996#else9997assert(bytes_per_block_or_pixel == 16);99989999if (basis_file_has_alpha_slices)10000{10001// First decode the alpha data to G10002//status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10003status = transcode_slice((uint8_t *)pOutput_blocks + 8, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);10004}10005else10006{10007basisu_transcoder::write_opaque_alpha_blocks(num_blocks_x, num_blocks_y, (uint8_t*)pOutput_blocks + 8, block_format::cETC2_EAC_R11, 16, output_row_pitch_in_blocks_or_pixels);10008status = true;10009}1001010011if (status)10012{10013// Now decode the color data to R10014//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10015status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels, decode_flags);10016if (!status)10017{10018BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ETC2_EAC_R11 R failed\n");10019}10020}10021else10022{10023BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ETC2_EAC_R11 G failed\n");10024}1002510026break;10027#endif10028}10029default:10030{10031assert(0);10032BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: Invalid fmt\n");10033break;10034}10035}1003610037return status;10038}1003910040//------------------------------------------------------------------------------------------------1004110042basisu_lowlevel_uastc_ldr_4x4_transcoder::basisu_lowlevel_uastc_ldr_4x4_transcoder()10043{10044}1004510046bool basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_slice(10047void* 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,10048uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha,10049const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels,10050basisu_transcoder_state* pState, uint32_t output_rows_in_pixels, int channel0, int channel1, uint32_t decode_flags)10051{10052BASISU_NOTE_UNUSED(pState);10053BASISU_NOTE_UNUSED(bc1_allow_threecolor_blocks);1005410055assert(g_transcoder_initialized);10056if (!g_transcoder_initialized)10057{10058BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_slice: Transcoder not globally initialized.\n");10059return false;10060}1006110062#if BASISD_SUPPORT_UASTC10063const uint32_t total_blocks = num_blocks_x * num_blocks_y;1006410065if (!output_row_pitch_in_blocks_or_pixels)10066{10067if (basis_block_format_is_uncompressed(fmt))10068output_row_pitch_in_blocks_or_pixels = orig_width;10069else10070{10071if (fmt == block_format::cFXT1_RGB)10072output_row_pitch_in_blocks_or_pixels = (orig_width + 7) / 8;10073else10074output_row_pitch_in_blocks_or_pixels = num_blocks_x;10075}10076}1007710078if (basis_block_format_is_uncompressed(fmt))10079{10080if (!output_rows_in_pixels)10081output_rows_in_pixels = orig_height;10082}1008310084uint32_t total_expected_block_bytes = sizeof(uastc_block) * total_blocks;10085if (image_data_size < total_expected_block_bytes)10086{10087BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_slice: image_data_size < total_expected_block_bytes The file is corrupted or this is a bug.\n");10088return false;10089}1009010091const uastc_block* pSource_block = reinterpret_cast<const uastc_block *>(pImage_data);1009210093const bool high_quality = (decode_flags & cDecodeFlagsHighQuality) != 0;10094const bool from_alpha = has_alpha && (decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;1009510096bool status = false;10097if ((fmt == block_format::cPVRTC1_4_RGB) || (fmt == block_format::cPVRTC1_4_RGBA))10098{10099if (fmt == block_format::cPVRTC1_4_RGBA)10100transcode_uastc_to_pvrtc1_4_rgba((const uastc_block*)pImage_data, pDst_blocks, num_blocks_x, num_blocks_y, high_quality);10101else10102transcode_uastc_to_pvrtc1_4_rgb((const uastc_block *)pImage_data, pDst_blocks, num_blocks_x, num_blocks_y, high_quality, from_alpha);10103}10104else10105{10106for (uint32_t block_y = 0; block_y < num_blocks_y; ++block_y)10107{10108void* pDst_block = (uint8_t*)pDst_blocks + block_y * output_row_pitch_in_blocks_or_pixels * output_block_or_pixel_stride_in_bytes;1010910110for (uint32_t block_x = 0; block_x < num_blocks_x; ++block_x, ++pSource_block, pDst_block = (uint8_t *)pDst_block + output_block_or_pixel_stride_in_bytes)10111{10112switch (fmt)10113{10114case block_format::cUASTC_4x4:10115{10116memcpy(pDst_block, pSource_block, sizeof(uastc_block));10117status = true;10118break;10119}10120case block_format::cETC1:10121{10122if (from_alpha)10123status = transcode_uastc_to_etc1(*pSource_block, pDst_block, 3);10124else10125status = transcode_uastc_to_etc1(*pSource_block, pDst_block);10126break;10127}10128case block_format::cETC2_RGBA:10129{10130status = transcode_uastc_to_etc2_rgba(*pSource_block, pDst_block);10131break;10132}10133case block_format::cBC1:10134{10135status = transcode_uastc_to_bc1(*pSource_block, pDst_block, high_quality);10136break;10137}10138case block_format::cBC3:10139{10140status = transcode_uastc_to_bc3(*pSource_block, pDst_block, high_quality);10141break;10142}10143case block_format::cBC4:10144{10145if (channel0 < 0)10146channel0 = 0;10147status = transcode_uastc_to_bc4(*pSource_block, pDst_block, high_quality, channel0);10148break;10149}10150case block_format::cBC5:10151{10152if (channel0 < 0)10153channel0 = 0;10154if (channel1 < 0)10155channel1 = 3;10156status = transcode_uastc_to_bc5(*pSource_block, pDst_block, high_quality, channel0, channel1);10157break;10158}10159case block_format::cBC7:10160case block_format::cBC7_M5_COLOR: // for consistently with ETC1S10161{10162status = transcode_uastc_to_bc7(*pSource_block, pDst_block);10163break;10164}10165case block_format::cASTC_4x4:10166{10167status = transcode_uastc_to_astc(*pSource_block, pDst_block);10168break;10169}10170case block_format::cETC2_EAC_R11:10171{10172if (channel0 < 0)10173channel0 = 0;10174status = transcode_uastc_to_etc2_eac_r11(*pSource_block, pDst_block, high_quality, channel0);10175break;10176}10177case block_format::cETC2_EAC_RG11:10178{10179if (channel0 < 0)10180channel0 = 0;10181if (channel1 < 0)10182channel1 = 3;10183status = transcode_uastc_to_etc2_eac_rg11(*pSource_block, pDst_block, high_quality, channel0, channel1);10184break;10185}10186case block_format::cRGBA32:10187{10188color32 block_pixels[4][4];10189status = unpack_uastc(*pSource_block, (color32 *)block_pixels, false);1019010191assert(sizeof(uint32_t) == output_block_or_pixel_stride_in_bytes);10192uint8_t* pDst_pixels = static_cast<uint8_t*>(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint32_t);1019310194const uint32_t max_x = basisu::minimum<int>(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);10195const uint32_t max_y = basisu::minimum<int>(4, (int)output_rows_in_pixels - (int)block_y * 4);1019610197for (uint32_t y = 0; y < max_y; y++)10198{10199for (uint32_t x = 0; x < max_x; x++)10200{10201const color32& c = block_pixels[y][x];1020210203pDst_pixels[0 + 4 * x] = c.r;10204pDst_pixels[1 + 4 * x] = c.g;10205pDst_pixels[2 + 4 * x] = c.b;10206pDst_pixels[3 + 4 * x] = c.a;10207}1020810209pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint32_t);10210}1021110212break;10213}10214case block_format::cRGB565:10215case block_format::cBGR565:10216{10217color32 block_pixels[4][4];10218status = unpack_uastc(*pSource_block, (color32*)block_pixels, false);1021910220assert(sizeof(uint16_t) == output_block_or_pixel_stride_in_bytes);10221uint8_t* pDst_pixels = static_cast<uint8_t*>(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint16_t);1022210223const uint32_t max_x = basisu::minimum<int>(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);10224const uint32_t max_y = basisu::minimum<int>(4, (int)output_rows_in_pixels - (int)block_y * 4);1022510226for (uint32_t y = 0; y < max_y; y++)10227{10228for (uint32_t x = 0; x < max_x; x++)10229{10230const color32& c = block_pixels[y][x];1023110232const uint16_t packed = (fmt == block_format::cRGB565) ? static_cast<uint16_t>((mul_8(c.r, 31) << 11) | (mul_8(c.g, 63) << 5) | mul_8(c.b, 31)) :10233static_cast<uint16_t>((mul_8(c.b, 31) << 11) | (mul_8(c.g, 63) << 5) | mul_8(c.r, 31));1023410235pDst_pixels[x * 2 + 0] = (uint8_t)(packed & 0xFF);10236pDst_pixels[x * 2 + 1] = (uint8_t)((packed >> 8) & 0xFF);10237}1023810239pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint16_t);10240}1024110242break;10243}10244case block_format::cRGBA4444:10245{10246color32 block_pixels[4][4];10247status = unpack_uastc(*pSource_block, (color32*)block_pixels, false);1024810249assert(sizeof(uint16_t) == output_block_or_pixel_stride_in_bytes);10250uint8_t* pDst_pixels = static_cast<uint8_t*>(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint16_t);1025110252const uint32_t max_x = basisu::minimum<int>(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);10253const uint32_t max_y = basisu::minimum<int>(4, (int)output_rows_in_pixels - (int)block_y * 4);1025410255for (uint32_t y = 0; y < max_y; y++)10256{10257for (uint32_t x = 0; x < max_x; x++)10258{10259const color32& c = block_pixels[y][x];1026010261const uint16_t packed = static_cast<uint16_t>((mul_8(c.r, 15) << 12) | (mul_8(c.g, 15) << 8) | (mul_8(c.b, 15) << 4) | mul_8(c.a, 15));1026210263pDst_pixels[x * 2 + 0] = (uint8_t)(packed & 0xFF);10264pDst_pixels[x * 2 + 1] = (uint8_t)((packed >> 8) & 0xFF);10265}1026610267pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint16_t);10268}10269break;10270}10271default:10272assert(0);10273break;1027410275}1027610277if (!status)10278{10279BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_slice: Transcoder failed to unpack a UASTC block - this is a bug, or the data was corrupted\n");10280return false;10281}1028210283} // block_x1028410285} // block_y10286}1028710288return true;10289#else10290BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_slice: UASTC is unsupported\n");1029110292BASISU_NOTE_UNUSED(decode_flags);10293BASISU_NOTE_UNUSED(channel0);10294BASISU_NOTE_UNUSED(channel1);10295BASISU_NOTE_UNUSED(output_rows_in_pixels);10296BASISU_NOTE_UNUSED(output_row_pitch_in_blocks_or_pixels);10297BASISU_NOTE_UNUSED(output_block_or_pixel_stride_in_bytes);10298BASISU_NOTE_UNUSED(fmt);10299BASISU_NOTE_UNUSED(image_data_size);10300BASISU_NOTE_UNUSED(pImage_data);10301BASISU_NOTE_UNUSED(num_blocks_x);10302BASISU_NOTE_UNUSED(num_blocks_y);10303BASISU_NOTE_UNUSED(pDst_blocks);1030410305return false;10306#endif10307}1030810309bool basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image(10310transcoder_texture_format target_format,10311void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,10312const uint8_t* pCompressed_data, uint32_t compressed_data_length,10313uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,10314uint64_t slice_offset, uint32_t slice_length,10315uint32_t decode_flags,10316bool has_alpha,10317bool is_video,10318uint32_t output_row_pitch_in_blocks_or_pixels,10319basisu_transcoder_state* pState,10320uint32_t output_rows_in_pixels,10321int channel0, int channel1)10322{10323BASISU_NOTE_UNUSED(is_video);10324BASISU_NOTE_UNUSED(level_index);1032510326if (((uint64_t)slice_offset + slice_length) > (uint64_t)compressed_data_length)10327{10328BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: source data buffer too small\n");10329return false;10330}1033110332if ((target_format == transcoder_texture_format::cTFPVRTC1_4_RGB) || (target_format == transcoder_texture_format::cTFPVRTC1_4_RGBA))10333{10334if ((!basisu::is_pow2(num_blocks_x * 4)) || (!basisu::is_pow2(num_blocks_y * 4)))10335{10336// PVRTC1 only supports power of 2 dimensions10337BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: PVRTC1 only supports power of 2 dimensions\n");10338return false;10339}10340}1034110342if ((target_format == transcoder_texture_format::cTFPVRTC1_4_RGBA) && (!has_alpha))10343{10344// Switch to PVRTC1 RGB if the input doesn't have alpha.10345target_format = transcoder_texture_format::cTFPVRTC1_4_RGB;10346}1034710348const bool transcode_alpha_data_to_opaque_formats = (decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;10349const uint32_t bytes_per_block_or_pixel = basis_get_bytes_per_block_or_pixel(target_format);10350//const uint32_t total_slice_blocks = num_blocks_x * num_blocks_y;1035110352if (!basis_validate_output_buffer_size(basis_tex_format::cUASTC4x4, target_format, output_blocks_buf_size_in_blocks_or_pixels, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, output_rows_in_pixels))10353{10354BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: output buffer size too small\n");10355return false;10356}1035710358bool status = false;1035910360// UASTC4x410361switch (target_format)10362{10363case transcoder_texture_format::cTFETC1_RGB:10364{10365//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10366status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cETC1,10367bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);1036810369if (!status)10370{10371BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to ETC1 failed\n");10372}10373break;10374}10375case transcoder_texture_format::cTFETC2_RGBA:10376{10377//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_RGBA, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10378status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cETC2_RGBA,10379bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);10380if (!status)10381{10382BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to ETC2 failed\n");10383}10384break;10385}10386case transcoder_texture_format::cTFBC1_RGB:10387{10388// TODO: ETC1S allows BC1 from alpha channel. That doesn't seem actually useful, though.10389//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10390status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBC1,10391bytes_per_block_or_pixel, true, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);10392if (!status)10393{10394BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to BC1 failed\n");10395}10396break;10397}10398case transcoder_texture_format::cTFBC3_RGBA:10399{10400//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC3, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10401status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBC3,10402bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);10403if (!status)10404{10405BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to BC3 failed\n");10406}10407break;10408}10409case transcoder_texture_format::cTFBC4_R:10410{10411//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState,10412// nullptr, 0,10413// ((has_alpha) && (transcode_alpha_data_to_opaque_formats)) ? 3 : 0);10414status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBC4,10415bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels,10416((has_alpha) && (transcode_alpha_data_to_opaque_formats)) ? 3 : 0, -1, decode_flags);10417if (!status)10418{10419BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to BC4 failed\n");10420}10421break;10422}10423case transcoder_texture_format::cTFBC5_RG:10424{10425//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC5, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState,10426// nullptr, 0,10427// 0, 3);10428status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBC5,10429bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels,104300, 3, decode_flags);10431if (!status)10432{10433BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to BC5 failed\n");10434}10435break;10436}10437case transcoder_texture_format::cTFBC7_RGBA:10438case transcoder_texture_format::cTFBC7_ALT:10439{10440//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10441status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBC7,10442bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, -1, -1, decode_flags);10443if (!status)10444{10445BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to BC7 failed\n");10446}10447break;10448}10449case transcoder_texture_format::cTFPVRTC1_4_RGB:10450{10451//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10452status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cPVRTC1_4_RGB,10453bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, -1, -1, decode_flags);10454if (!status)10455{10456BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to PVRTC1 RGB 4bpp failed\n");10457}10458break;10459}10460case transcoder_texture_format::cTFPVRTC1_4_RGBA:10461{10462//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGBA, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10463status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cPVRTC1_4_RGBA,10464bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, -1, -1, decode_flags);10465if (!status)10466{10467BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to PVRTC1 RGBA 4bpp failed\n");10468}10469break;10470}10471case transcoder_texture_format::cTFASTC_4x4_RGBA:10472{10473//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10474status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cASTC_4x4,10475bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, -1, -1, decode_flags);10476if (!status)10477{10478BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to ASTC 4x4 failed\n");10479}10480break;10481}10482case transcoder_texture_format::cTFATC_RGB:10483case transcoder_texture_format::cTFATC_RGBA:10484{10485BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: UASTC->ATC currently unsupported\n");10486return false;10487}10488case transcoder_texture_format::cTFFXT1_RGB:10489{10490BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: UASTC->FXT1 currently unsupported\n");10491return false;10492}10493case transcoder_texture_format::cTFPVRTC2_4_RGB:10494{10495BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: UASTC->PVRTC2 currently unsupported\n");10496return false;10497}10498case transcoder_texture_format::cTFPVRTC2_4_RGBA:10499{10500BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: UASTC->PVRTC2 currently unsupported\n");10501return false;10502}10503case transcoder_texture_format::cTFETC2_EAC_R11:10504{10505//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState,10506// nullptr, 0,10507// ((has_alpha) && (transcode_alpha_data_to_opaque_formats)) ? 3 : 0);10508status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cETC2_EAC_R11,10509bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels,10510((has_alpha) && (transcode_alpha_data_to_opaque_formats)) ? 3 : 0, -1, decode_flags);10511if (!status)10512{10513BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to EAC R11 failed\n");10514}10515break;10516}10517case transcoder_texture_format::cTFETC2_EAC_RG11:10518{10519//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_RG11, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState,10520// nullptr, 0,10521// 0, 3);10522status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cETC2_EAC_RG11,10523bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels,105240, 3, decode_flags);10525if (!status)10526{10527BASISU_DEVEL_ERROR("basisu_basisu_lowlevel_uastc_ldr_4x4_transcodertranscoder::transcode_image: transcode_slice() to EAC RG11 failed\n");10528}10529break;10530}10531case transcoder_texture_format::cTFRGBA32:10532{10533//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGBA32, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10534status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGBA32,10535bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, -1, -1, decode_flags);10536if (!status)10537{10538BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to RGBA32 failed\n");10539}10540break;10541}10542case transcoder_texture_format::cTFRGB565:10543{10544//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGB565, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10545status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGB565,10546bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, -1, -1, decode_flags);10547if (!status)10548{10549BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to RGB565 failed\n");10550}10551break;10552}10553case transcoder_texture_format::cTFBGR565:10554{10555//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBGR565, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10556status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBGR565,10557bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, -1, -1, decode_flags);10558if (!status)10559{10560BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to RGB565 failed\n");10561}10562break;10563}10564case transcoder_texture_format::cTFRGBA4444:10565{10566//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGBA4444, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);10567status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGBA4444,10568bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, -1, -1, decode_flags);10569if (!status)10570{10571BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: transcode_slice() to RGBA4444 failed\n");10572}10573break;10574}10575default:10576{10577assert(0);10578BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_ldr_4x4_transcoder::transcode_image: Invalid format\n");10579break;10580}10581}1058210583return status;10584}1058510586//------------------------------------------------------------------------------------------------10587// UASTC HDR 4x41058810589basisu_lowlevel_uastc_hdr_4x4_transcoder::basisu_lowlevel_uastc_hdr_4x4_transcoder()10590{10591}1059210593bool basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_slice(10594void* 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,10595uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha,10596const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels,10597basisu_transcoder_state* pState, uint32_t output_rows_in_pixels, int channel0, int channel1, uint32_t decode_flags)10598{10599BASISU_NOTE_UNUSED(pState);10600BASISU_NOTE_UNUSED(bc1_allow_threecolor_blocks);10601BASISU_NOTE_UNUSED(has_alpha);10602BASISU_NOTE_UNUSED(channel0);10603BASISU_NOTE_UNUSED(channel1);10604BASISU_NOTE_UNUSED(decode_flags);10605BASISU_NOTE_UNUSED(orig_width);10606BASISU_NOTE_UNUSED(orig_height);1060710608assert(g_transcoder_initialized);10609if (!g_transcoder_initialized)10610{10611BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_slice: Transcoder not globally initialized.\n");10612return false;10613}1061410615#if BASISD_SUPPORT_UASTC_HDR10616const uint32_t total_blocks = num_blocks_x * num_blocks_y;1061710618if (!output_row_pitch_in_blocks_or_pixels)10619{10620if (basis_block_format_is_uncompressed(fmt))10621output_row_pitch_in_blocks_or_pixels = orig_width;10622else10623output_row_pitch_in_blocks_or_pixels = num_blocks_x;10624}1062510626if (basis_block_format_is_uncompressed(fmt))10627{10628if (!output_rows_in_pixels)10629output_rows_in_pixels = orig_height;10630}1063110632uint32_t total_expected_block_bytes = sizeof(astc_blk) * total_blocks;10633if (image_data_size < total_expected_block_bytes)10634{10635BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_slice: image_data_size < total_expected_block_bytes The file is corrupted or this is a bug.\n");10636return false;10637}1063810639const astc_blk* pSource_block = reinterpret_cast<const astc_blk*>(pImage_data);1064010641bool status = false;1064210643// TODO: Optimize pure memcpy() case.1064410645for (uint32_t block_y = 0; block_y < num_blocks_y; ++block_y)10646{10647void* pDst_block = (uint8_t*)pDst_blocks + block_y * output_row_pitch_in_blocks_or_pixels * output_block_or_pixel_stride_in_bytes;1064810649for (uint32_t block_x = 0; block_x < num_blocks_x; ++block_x, ++pSource_block, pDst_block = (uint8_t*)pDst_block + output_block_or_pixel_stride_in_bytes)10650{10651switch (fmt)10652{10653case block_format::cUASTC_HDR_4x4:10654case block_format::cASTC_HDR_4x4:10655{10656// Nothing to do, UASTC HDR 4x4 is just ASTC.10657memcpy(pDst_block, pSource_block, sizeof(uastc_block));10658status = true;10659break;10660}10661case block_format::cBC6H:10662{10663status = astc_hdr_transcode_to_bc6h(*pSource_block, *(bc6h_block *)pDst_block);10664break;10665}10666case block_format::cRGB_9E5:10667{10668astc_helpers::log_astc_block log_blk;10669status = astc_helpers::unpack_block(pSource_block, log_blk, 4, 4);10670if (status)10671{10672uint32_t* pDst_pixels = reinterpret_cast<uint32_t*>(10673static_cast<uint8_t*>(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint32_t)10674);1067510676uint32_t blk_texels[4][4];1067710678status = astc_helpers::decode_block(log_blk, blk_texels, 4, 4, astc_helpers::cDecodeModeRGB9E5);1067910680if (status)10681{10682const uint32_t max_x = basisu::minimum<int>(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);10683const uint32_t max_y = basisu::minimum<int>(4, (int)output_rows_in_pixels - (int)block_y * 4);1068410685for (uint32_t y = 0; y < max_y; y++)10686{10687memcpy(pDst_pixels, &blk_texels[y][0], sizeof(uint32_t) * max_x);1068810689pDst_pixels += output_row_pitch_in_blocks_or_pixels;10690} // y10691}10692}1069310694break;10695}10696case block_format::cRGBA_HALF:10697{10698astc_helpers::log_astc_block log_blk;10699status = astc_helpers::unpack_block(pSource_block, log_blk, 4, 4);10700if (status)10701{10702half_float* pDst_pixels = reinterpret_cast<half_float*>(10703static_cast<uint8_t*>(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(half_float) * 410704);1070510706half_float blk_texels[4][4][4];10707status = astc_helpers::decode_block(log_blk, blk_texels, 4, 4, astc_helpers::cDecodeModeHDR16);1070810709if (status)10710{10711const uint32_t max_x = basisu::minimum<int>(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);10712const uint32_t max_y = basisu::minimum<int>(4, (int)output_rows_in_pixels - (int)block_y * 4);1071310714for (uint32_t y = 0; y < max_y; y++)10715{10716for (uint32_t x = 0; x < max_x; x++)10717{10718pDst_pixels[0 + 4 * x] = blk_texels[y][x][0];10719pDst_pixels[1 + 4 * x] = blk_texels[y][x][1];10720pDst_pixels[2 + 4 * x] = blk_texels[y][x][2];10721pDst_pixels[3 + 4 * x] = blk_texels[y][x][3];10722} // x1072310724pDst_pixels += output_row_pitch_in_blocks_or_pixels * 4;10725} // y10726}10727}1072810729break;10730}10731case block_format::cRGB_HALF:10732{10733astc_helpers:: log_astc_block log_blk;10734status = astc_helpers::unpack_block(pSource_block, log_blk, 4, 4);10735if (status)10736{10737half_float* pDst_pixels =10738reinterpret_cast<half_float*>(static_cast<uint8_t*>(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(half_float) * 3);1073910740half_float blk_texels[4][4][4];10741status = astc_helpers::decode_block(log_blk, blk_texels, 4, 4, astc_helpers::cDecodeModeHDR16);10742if (status)10743{10744const uint32_t max_x = basisu::minimum<int>(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);10745const uint32_t max_y = basisu::minimum<int>(4, (int)output_rows_in_pixels - (int)block_y * 4);1074610747for (uint32_t y = 0; y < max_y; y++)10748{10749for (uint32_t x = 0; x < max_x; x++)10750{10751pDst_pixels[0 + 3 * x] = blk_texels[y][x][0];10752pDst_pixels[1 + 3 * x] = blk_texels[y][x][1];10753pDst_pixels[2 + 3 * x] = blk_texels[y][x][2];10754} // x1075510756pDst_pixels += output_row_pitch_in_blocks_or_pixels * 3;10757} // y10758}10759}1076010761break;10762}10763default:10764assert(0);10765break;1076610767}1076810769if (!status)10770{10771BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_slice: Transcoder failed to unpack a UASTC HDR block - this is a bug, or the data was corrupted\n");10772return false;10773}1077410775} // block_x1077610777} // block_y1077810779return true;10780#else10781BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_slice: UASTC_HDR is unsupported\n");1078210783BASISU_NOTE_UNUSED(decode_flags);10784BASISU_NOTE_UNUSED(channel0);10785BASISU_NOTE_UNUSED(channel1);10786BASISU_NOTE_UNUSED(output_rows_in_pixels);10787BASISU_NOTE_UNUSED(output_row_pitch_in_blocks_or_pixels);10788BASISU_NOTE_UNUSED(output_block_or_pixel_stride_in_bytes);10789BASISU_NOTE_UNUSED(fmt);10790BASISU_NOTE_UNUSED(image_data_size);10791BASISU_NOTE_UNUSED(pImage_data);10792BASISU_NOTE_UNUSED(num_blocks_x);10793BASISU_NOTE_UNUSED(num_blocks_y);10794BASISU_NOTE_UNUSED(pDst_blocks);1079510796return false;10797#endif10798}1079910800bool basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_image(10801transcoder_texture_format target_format,10802void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,10803const uint8_t* pCompressed_data, uint32_t compressed_data_length,10804uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,10805uint64_t slice_offset, uint32_t slice_length,10806uint32_t decode_flags,10807bool has_alpha,10808bool is_video,10809uint32_t output_row_pitch_in_blocks_or_pixels,10810basisu_transcoder_state* pState,10811uint32_t output_rows_in_pixels,10812int channel0, int channel1)10813{10814BASISU_NOTE_UNUSED(is_video);10815BASISU_NOTE_UNUSED(level_index);10816BASISU_NOTE_UNUSED(decode_flags);1081710818if (((uint64_t)slice_offset + slice_length) > (uint64_t)compressed_data_length)10819{10820BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_image: source data buffer too small\n");10821return false;10822}1082310824const uint32_t bytes_per_block_or_pixel = basis_get_bytes_per_block_or_pixel(target_format);10825//const uint32_t total_slice_blocks = num_blocks_x * num_blocks_y;1082610827if (!basis_validate_output_buffer_size(basis_tex_format::cUASTC_HDR_4x4, target_format, output_blocks_buf_size_in_blocks_or_pixels, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, output_rows_in_pixels))10828{10829BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_image: output buffer size too small\n");10830return false;10831}1083210833bool status = false;1083410835switch (target_format)10836{10837case transcoder_texture_format::cTFASTC_HDR_4x4_RGBA:10838{10839status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cASTC_HDR_4x4,10840bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);1084110842if (!status)10843{10844BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_image: transcode_slice() to ASTC_HDR failed\n");10845}10846break;10847}10848case transcoder_texture_format::cTFBC6H:10849{10850status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBC6H,10851bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);10852if (!status)10853{10854BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_image: transcode_slice() to BC6H failed\n");10855}10856break;10857}10858case transcoder_texture_format::cTFRGB_HALF:10859{10860status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGB_HALF,10861bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);10862if (!status)10863{10864BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_image: transcode_slice() to RGB_HALF failed\n");10865}10866break;10867}10868case transcoder_texture_format::cTFRGBA_HALF:10869{10870status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGBA_HALF,10871bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);10872if (!status)10873{10874BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_image: transcode_slice() to RGBA_HALF failed\n");10875}10876break;10877}10878case transcoder_texture_format::cTFRGB_9E5:10879{10880status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGB_9E5,10881bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);10882if (!status)10883{10884BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_image: transcode_slice() to RGBA_HALF failed\n");10885}10886break;10887}10888default:10889{10890assert(0);10891BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_hdr_4x4_transcoder::transcode_image: Invalid format\n");10892break;10893}10894}1089510896return status;10897}1089810899//------------------------------------------------------------------------------------------------10900// ASTC 6x6 HDR1090110902basisu_lowlevel_astc_hdr_6x6_transcoder::basisu_lowlevel_astc_hdr_6x6_transcoder()10903{10904}1090510906// num_blocks_x/num_blocks_y are source 6x6 blocks10907bool basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_slice(10908void* 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,10909uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha,10910const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels,10911basisu_transcoder_state* pState, uint32_t output_rows_in_pixels, int channel0, int channel1, uint32_t decode_flags)10912{10913BASISU_NOTE_UNUSED(pState);10914BASISU_NOTE_UNUSED(bc1_allow_threecolor_blocks);10915BASISU_NOTE_UNUSED(has_alpha);10916BASISU_NOTE_UNUSED(channel0);10917BASISU_NOTE_UNUSED(channel1);10918BASISU_NOTE_UNUSED(decode_flags);10919BASISU_NOTE_UNUSED(orig_width);10920BASISU_NOTE_UNUSED(orig_height);1092110922assert(g_transcoder_initialized);10923if (!g_transcoder_initialized)10924{10925BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_slice: Transcoder not globally initialized.\n");10926return false;10927}1092810929#if BASISD_SUPPORT_UASTC_HDR10930const uint32_t total_src_blocks = num_blocks_x * num_blocks_y;1093110932const uint32_t output_block_width = get_block_width(fmt);10933//const uint32_t output_block_height = get_block_height(fmt);1093410935if (!output_row_pitch_in_blocks_or_pixels)10936{10937if (basis_block_format_is_uncompressed(fmt))10938output_row_pitch_in_blocks_or_pixels = orig_width;10939else10940output_row_pitch_in_blocks_or_pixels = (orig_width + output_block_width - 1) / output_block_width;10941}1094210943if (basis_block_format_is_uncompressed(fmt))10944{10945if (!output_rows_in_pixels)10946output_rows_in_pixels = orig_height;10947}1094810949uint32_t total_expected_block_bytes = sizeof(astc_blk) * total_src_blocks;10950if (image_data_size < total_expected_block_bytes)10951{10952BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_slice: image_data_size < total_expected_block_bytes The file is corrupted or this is a bug.\n");10953return false;10954}1095510956const astc_blk* pSource_block = reinterpret_cast<const astc_blk*>(pImage_data);1095710958bool status = false;1095910960half_float unpacked_blocks[12][12][3]; // [y][x][c]1096110962assert(((orig_width + 5) / 6) == num_blocks_x);10963assert(((orig_height + 5) / 6) == num_blocks_y);1096410965if (fmt == block_format::cBC6H)10966{10967const uint32_t num_dst_blocks_x = (orig_width + 3) / 4;10968const uint32_t num_dst_blocks_y = (orig_height + 3) / 4;1096910970if (!output_row_pitch_in_blocks_or_pixels)10971{10972output_row_pitch_in_blocks_or_pixels = num_dst_blocks_x;10973}10974else if (output_row_pitch_in_blocks_or_pixels < num_dst_blocks_x)10975{10976BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_slice: output_row_pitch_in_blocks_or_pixels is too low\n");10977return false;10978}1097910980if (output_block_or_pixel_stride_in_bytes != sizeof(bc6h_block))10981{10982BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_slice: invalid output_block_or_pixel_stride_in_bytes\n");10983return false;10984}1098510986fast_bc6h_params bc6h_enc_params;10987const bool hq_flag = (decode_flags & cDecodeFlagsHighQuality) != 0;10988bc6h_enc_params.m_max_2subset_pats_to_try = hq_flag ? 1 : 0;1098910990for (uint32_t src_block_y = 0; src_block_y < num_blocks_y; src_block_y += 2)10991{10992const uint32_t num_inner_blocks_y = basisu::minimum<uint32_t>(2, num_blocks_y - src_block_y);1099310994for (uint32_t src_block_x = 0; src_block_x < num_blocks_x; src_block_x += 2)10995{10996const uint32_t num_inner_blocks_x = basisu::minimum<uint32_t>(2, num_blocks_x - src_block_x);1099710998for (uint32_t iy = 0; iy < num_inner_blocks_y; iy++)10999{11000for (uint32_t ix = 0; ix < num_inner_blocks_x; ix++)11001{11002const astc_blk* pS = pSource_block + (src_block_y + iy) * num_blocks_x + (src_block_x + ix);1100311004half_float blk_texels[6][6][4];1100511006astc_helpers::log_astc_block log_blk;11007status = astc_helpers::unpack_block(pS, log_blk, 6, 6);11008if (!status)11009{11010BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_slice: Transcoder failed to unpack a ASTC HDR block - this is a bug, or the data was corrupted\n");11011return false;11012}1101311014status = astc_helpers::decode_block(log_blk, blk_texels, 6, 6, astc_helpers::cDecodeModeHDR16);11015if (!status)11016{11017BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_slice: Transcoder failed to unpack a ASTC HDR block - this is a bug, or the data was corrupted\n");11018return false;11019}1102011021for (uint32_t y = 0; y < 6; y++)11022{11023for (uint32_t x = 0; x < 6; x++)11024{11025unpacked_blocks[iy * 6 + y][ix * 6 + x][0] = blk_texels[y][x][0];11026unpacked_blocks[iy * 6 + y][ix * 6 + x][1] = blk_texels[y][x][1];11027unpacked_blocks[iy * 6 + y][ix * 6 + x][2] = blk_texels[y][x][2];1102811029} // x11030} // y1103111032} // ix1103311034} // iy1103511036const uint32_t dst_x = src_block_x * 6;11037assert((dst_x & 3) == 0);11038const uint32_t dst_block_x = dst_x >> 2;1103911040const uint32_t dst_y = src_block_y * 6;11041assert((dst_y & 3) == 0);11042const uint32_t dst_block_y = dst_y >> 2;1104311044const uint32_t num_inner_dst_blocks_x = basisu::minimum<uint32_t>(3, num_dst_blocks_x - dst_block_x);11045const uint32_t num_inner_dst_blocks_y = basisu::minimum<uint32_t>(3, num_dst_blocks_y - dst_block_y);1104611047for (uint32_t dy = 0; dy < num_inner_dst_blocks_y; dy++)11048{11049for (uint32_t dx = 0; dx < num_inner_dst_blocks_x; dx++)11050{11051bc6h_block* pDst_block = (bc6h_block*)pDst_blocks + (dst_block_x + dx) + (dst_block_y + dy) * output_row_pitch_in_blocks_or_pixels;1105211053half_float src_pixels[4][4][3]; // [y][x][c]1105411055for (uint32_t y = 0; y < 4; y++)11056{11057const uint32_t src_pixel_y = basisu::minimum<uint32_t>(dy * 4 + y, num_inner_blocks_y * 6 - 1);1105811059for (uint32_t x = 0; x < 4; x++)11060{11061const uint32_t src_pixel_x = basisu::minimum<uint32_t>(dx * 4 + x, num_inner_blocks_x * 6 - 1);1106211063assert((src_pixel_y < 12) && (src_pixel_x < 12));1106411065src_pixels[y][x][0] = unpacked_blocks[src_pixel_y][src_pixel_x][0];11066src_pixels[y][x][1] = unpacked_blocks[src_pixel_y][src_pixel_x][1];11067src_pixels[y][x][2] = unpacked_blocks[src_pixel_y][src_pixel_x][2];1106811069} // x11070} // y1107111072astc_6x6_hdr::fast_encode_bc6h(&src_pixels[0][0][0], pDst_block, bc6h_enc_params);1107311074} // dx11075} // dy1107611077} // block_x1107811079} // block_y1108011081status = true;11082}11083else11084{11085for (uint32_t block_y = 0; block_y < num_blocks_y; ++block_y)11086{11087void* pDst_block = (uint8_t*)pDst_blocks + block_y * output_row_pitch_in_blocks_or_pixels * output_block_or_pixel_stride_in_bytes;1108811089for (uint32_t block_x = 0; block_x < num_blocks_x; ++block_x, ++pSource_block, pDst_block = (uint8_t*)pDst_block + output_block_or_pixel_stride_in_bytes)11090{11091switch (fmt)11092{11093case block_format::cASTC_HDR_6x6:11094{11095// Nothing to do, ASTC HDR 6x6 is just ASTC.11096// TODO: Optimize this copy11097memcpy(pDst_block, pSource_block, sizeof(astc_helpers::astc_block));11098status = true;11099break;11100}11101case block_format::cRGB_9E5:11102{11103astc_helpers::log_astc_block log_blk;11104status = astc_helpers::unpack_block(pSource_block, log_blk, 6, 6);11105if (status)11106{11107uint32_t* pDst_pixels = reinterpret_cast<uint32_t*>(11108static_cast<uint8_t*>(pDst_blocks) + (block_x * 6 + block_y * 6 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint32_t)11109);1111011111uint32_t blk_texels[6][6];1111211113status = astc_helpers::decode_block(log_blk, blk_texels, 6, 6, astc_helpers::cDecodeModeRGB9E5);1111411115if (status)11116{11117const uint32_t max_x = basisu::minimum<int>(6, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 6);11118const uint32_t max_y = basisu::minimum<int>(6, (int)output_rows_in_pixels - (int)block_y * 6);1111911120for (uint32_t y = 0; y < max_y; y++)11121{11122memcpy(pDst_pixels, &blk_texels[y][0], sizeof(uint32_t) * max_x);1112311124pDst_pixels += output_row_pitch_in_blocks_or_pixels;11125} // y11126}11127}1112811129break;11130}11131case block_format::cRGBA_HALF:11132{11133astc_helpers::log_astc_block log_blk;11134status = astc_helpers::unpack_block(pSource_block, log_blk, 6, 6);11135if (status)11136{11137half_float* pDst_pixels = reinterpret_cast<half_float*>(11138static_cast<uint8_t*>(pDst_blocks) + (block_x * 6 + block_y * 6 * output_row_pitch_in_blocks_or_pixels) * sizeof(half_float) * 411139);1114011141half_float blk_texels[6][6][4];11142status = astc_helpers::decode_block(log_blk, blk_texels, 6, 6, astc_helpers::cDecodeModeHDR16);1114311144if (status)11145{11146const uint32_t max_x = basisu::minimum<int>(6, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 6);11147const uint32_t max_y = basisu::minimum<int>(6, (int)output_rows_in_pixels - (int)block_y * 6);1114811149for (uint32_t y = 0; y < max_y; y++)11150{11151for (uint32_t x = 0; x < max_x; x++)11152{11153pDst_pixels[0 + 4 * x] = blk_texels[y][x][0];11154pDst_pixels[1 + 4 * x] = blk_texels[y][x][1];11155pDst_pixels[2 + 4 * x] = blk_texels[y][x][2];11156pDst_pixels[3 + 4 * x] = blk_texels[y][x][3];11157} // x1115811159pDst_pixels += output_row_pitch_in_blocks_or_pixels * 4;11160} // y11161}11162}1116311164break;11165}11166case block_format::cRGB_HALF:11167{11168astc_helpers::log_astc_block log_blk;11169status = astc_helpers::unpack_block(pSource_block, log_blk, 6, 6);11170if (status)11171{11172half_float* pDst_pixels =11173reinterpret_cast<half_float*>(static_cast<uint8_t*>(pDst_blocks) + (block_x * 6 + block_y * 6 * output_row_pitch_in_blocks_or_pixels) * sizeof(half_float) * 3);1117411175half_float blk_texels[6][6][4];11176status = astc_helpers::decode_block(log_blk, blk_texels, 6, 6, astc_helpers::cDecodeModeHDR16);11177if (status)11178{11179const uint32_t max_x = basisu::minimum<int>(6, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 6);11180const uint32_t max_y = basisu::minimum<int>(6, (int)output_rows_in_pixels - (int)block_y * 6);1118111182for (uint32_t y = 0; y < max_y; y++)11183{11184for (uint32_t x = 0; x < max_x; x++)11185{11186pDst_pixels[0 + 3 * x] = blk_texels[y][x][0];11187pDst_pixels[1 + 3 * x] = blk_texels[y][x][1];11188pDst_pixels[2 + 3 * x] = blk_texels[y][x][2];11189} // x1119011191pDst_pixels += output_row_pitch_in_blocks_or_pixels * 3;11192} // y11193}11194}1119511196break;11197}11198default:11199assert(0);11200break;1120111202}1120311204if (!status)11205{11206BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_slice: Transcoder failed to unpack a ASTC HDR block - this is a bug, or the data was corrupted\n");11207return false;11208}1120911210} // block_x1121111212} // block_y11213}1121411215return true;11216#else11217BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_slice: ASTC HDR is unsupported\n");1121811219BASISU_NOTE_UNUSED(decode_flags);11220BASISU_NOTE_UNUSED(channel0);11221BASISU_NOTE_UNUSED(channel1);11222BASISU_NOTE_UNUSED(output_rows_in_pixels);11223BASISU_NOTE_UNUSED(output_row_pitch_in_blocks_or_pixels);11224BASISU_NOTE_UNUSED(output_block_or_pixel_stride_in_bytes);11225BASISU_NOTE_UNUSED(fmt);11226BASISU_NOTE_UNUSED(image_data_size);11227BASISU_NOTE_UNUSED(pImage_data);11228BASISU_NOTE_UNUSED(num_blocks_x);11229BASISU_NOTE_UNUSED(num_blocks_y);11230BASISU_NOTE_UNUSED(pDst_blocks);1123111232return false;11233#endif11234}1123511236bool basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_image(11237transcoder_texture_format target_format,11238void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,11239const uint8_t* pCompressed_data, uint32_t compressed_data_length,11240uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,11241uint64_t slice_offset, uint32_t slice_length,11242uint32_t decode_flags,11243bool has_alpha,11244bool is_video,11245uint32_t output_row_pitch_in_blocks_or_pixels,11246basisu_transcoder_state* pState,11247uint32_t output_rows_in_pixels,11248int channel0, int channel1)11249{11250BASISU_NOTE_UNUSED(is_video);11251BASISU_NOTE_UNUSED(level_index);11252BASISU_NOTE_UNUSED(decode_flags);1125311254if (((uint64_t)slice_offset + slice_length) > (uint64_t)compressed_data_length)11255{11256BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_image: source data buffer too small\n");11257return false;11258}1125911260const uint32_t bytes_per_block_or_pixel = basis_get_bytes_per_block_or_pixel(target_format);11261//const uint32_t total_slice_blocks = num_blocks_x * num_blocks_y;1126211263if (!basis_validate_output_buffer_size(basis_tex_format::cASTC_HDR_6x6, target_format, output_blocks_buf_size_in_blocks_or_pixels, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, output_rows_in_pixels))11264{11265BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_image: output buffer size too small\n");11266return false;11267}1126811269bool status = false;1127011271switch (target_format)11272{11273case transcoder_texture_format::cTFASTC_HDR_6x6_RGBA:11274{11275status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cASTC_HDR_6x6,11276bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);1127711278if (!status)11279{11280BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_image: transcode_slice() to ASTC_HDR failed\n");11281}11282break;11283}11284case transcoder_texture_format::cTFBC6H:11285{11286status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBC6H,11287bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);11288if (!status)11289{11290BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_image: transcode_slice() to BC6H failed\n");11291}11292break;11293}11294case transcoder_texture_format::cTFRGB_HALF:11295{11296status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGB_HALF,11297bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);11298if (!status)11299{11300BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_image: transcode_slice() to RGB_HALF failed\n");11301}11302break;11303}11304case transcoder_texture_format::cTFRGBA_HALF:11305{11306status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGBA_HALF,11307bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);11308if (!status)11309{11310BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_image: transcode_slice() to RGBA_HALF failed\n");11311}11312break;11313}11314case transcoder_texture_format::cTFRGB_9E5:11315{11316status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGB_9E5,11317bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);11318if (!status)11319{11320BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_image: transcode_slice() to RGBA_HALF failed\n");11321}11322break;11323}11324default:11325{11326assert(0);11327BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_transcoder::transcode_image: Invalid format\n");11328break;11329}11330}1133111332return status;11333}1133411335//------------------------------------------------------------------------------------------------11336// ASTC 6x6 HDR intermediate1133711338basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder()11339{11340}1134111342// num_blocks_x/num_blocks_y are source 6x6 blocks11343bool basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_slice(11344void* 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,11345uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha,11346const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels,11347basisu_transcoder_state* pState, uint32_t output_rows_in_pixels, int channel0, int channel1, uint32_t decode_flags)11348{11349BASISU_NOTE_UNUSED(pState);11350BASISU_NOTE_UNUSED(bc1_allow_threecolor_blocks);11351BASISU_NOTE_UNUSED(has_alpha);11352BASISU_NOTE_UNUSED(channel0);11353BASISU_NOTE_UNUSED(channel1);11354BASISU_NOTE_UNUSED(decode_flags);11355BASISU_NOTE_UNUSED(orig_width);11356BASISU_NOTE_UNUSED(orig_height);1135711358assert(g_transcoder_initialized);11359if (!g_transcoder_initialized)11360{11361BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_slice: Transcoder not globally initialized.\n");11362return false;11363}1136411365#if BASISD_SUPPORT_UASTC_HDR1136611367// TODO: Optimize this1136811369basisu::vector2D<astc_helpers::astc_block> decoded_blocks;11370uint32_t dec_width = 0, dec_height = 0;11371bool dec_status = astc_6x6_hdr::decode_6x6_hdr(pImage_data, image_data_size, decoded_blocks, dec_width, dec_height);11372if (!dec_status)11373{11374BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_slice: decode_6x6_hdr() failed.\n");11375return false;11376}1137711378if ((dec_width != orig_width) || (dec_height != orig_height) ||11379(decoded_blocks.get_width() != num_blocks_x) || (decoded_blocks.get_height() != num_blocks_y))11380{11381BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_slice: unexpected decoded width/height\n");11382return false;11383}1138411385//const uint32_t total_src_blocks = num_blocks_x * num_blocks_y;1138611387const uint32_t output_block_width = get_block_width(fmt);11388//const uint32_t output_block_height = get_block_height(fmt);1138911390if (!output_row_pitch_in_blocks_or_pixels)11391{11392if (basis_block_format_is_uncompressed(fmt))11393output_row_pitch_in_blocks_or_pixels = orig_width;11394else11395output_row_pitch_in_blocks_or_pixels = (orig_width + output_block_width - 1) / output_block_width;11396}1139711398if (basis_block_format_is_uncompressed(fmt))11399{11400if (!output_rows_in_pixels)11401output_rows_in_pixels = orig_height;11402}1140311404const astc_blk* pSource_block = (const astc_blk *)decoded_blocks.get_ptr();1140511406bool status = false;1140711408half_float unpacked_blocks[12][12][3]; // [y][x][c]1140911410assert(((orig_width + 5) / 6) == num_blocks_x);11411assert(((orig_height + 5) / 6) == num_blocks_y);1141211413if (fmt == block_format::cBC6H)11414{11415const uint32_t num_dst_blocks_x = (orig_width + 3) / 4;11416const uint32_t num_dst_blocks_y = (orig_height + 3) / 4;1141711418if (!output_row_pitch_in_blocks_or_pixels)11419{11420output_row_pitch_in_blocks_or_pixels = num_dst_blocks_x;11421}11422else if (output_row_pitch_in_blocks_or_pixels < num_dst_blocks_x)11423{11424BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_slice: output_row_pitch_in_blocks_or_pixels is too low\n");11425return false;11426}1142711428if (output_block_or_pixel_stride_in_bytes != sizeof(bc6h_block))11429{11430BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_slice: invalid output_block_or_pixel_stride_in_bytes\n");11431return false;11432}1143311434fast_bc6h_params bc6h_enc_params;11435const bool hq_flag = (decode_flags & cDecodeFlagsHighQuality) != 0;11436bc6h_enc_params.m_max_2subset_pats_to_try = hq_flag ? 1 : 0;1143711438for (uint32_t src_block_y = 0; src_block_y < num_blocks_y; src_block_y += 2)11439{11440const uint32_t num_inner_blocks_y = basisu::minimum<uint32_t>(2, num_blocks_y - src_block_y);1144111442for (uint32_t src_block_x = 0; src_block_x < num_blocks_x; src_block_x += 2)11443{11444const uint32_t num_inner_blocks_x = basisu::minimum<uint32_t>(2, num_blocks_x - src_block_x);1144511446for (uint32_t iy = 0; iy < num_inner_blocks_y; iy++)11447{11448for (uint32_t ix = 0; ix < num_inner_blocks_x; ix++)11449{11450const astc_blk* pS = pSource_block + (src_block_y + iy) * num_blocks_x + (src_block_x + ix);1145111452half_float blk_texels[6][6][4];1145311454astc_helpers::log_astc_block log_blk;11455status = astc_helpers::unpack_block(pS, log_blk, 6, 6);11456if (!status)11457{11458BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_slice: Transcoder failed to unpack a ASTC HDR block - this is a bug, or the data was corrupted\n");11459return false;11460}1146111462status = astc_helpers::decode_block(log_blk, blk_texels, 6, 6, astc_helpers::cDecodeModeHDR16);11463if (!status)11464{11465BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_slice: Transcoder failed to unpack a ASTC HDR block - this is a bug, or the data was corrupted\n");11466return false;11467}1146811469for (uint32_t y = 0; y < 6; y++)11470{11471for (uint32_t x = 0; x < 6; x++)11472{11473unpacked_blocks[iy * 6 + y][ix * 6 + x][0] = blk_texels[y][x][0];11474unpacked_blocks[iy * 6 + y][ix * 6 + x][1] = blk_texels[y][x][1];11475unpacked_blocks[iy * 6 + y][ix * 6 + x][2] = blk_texels[y][x][2];11476} // x11477} // y1147811479} // ix1148011481} // iy1148211483const uint32_t dst_x = src_block_x * 6;11484assert((dst_x & 3) == 0);11485const uint32_t dst_block_x = dst_x >> 2;1148611487const uint32_t dst_y = src_block_y * 6;11488assert((dst_y & 3) == 0);11489const uint32_t dst_block_y = dst_y >> 2;1149011491const uint32_t num_inner_dst_blocks_x = basisu::minimum<uint32_t>(3, num_dst_blocks_x - dst_block_x);11492const uint32_t num_inner_dst_blocks_y = basisu::minimum<uint32_t>(3, num_dst_blocks_y - dst_block_y);1149311494for (uint32_t dy = 0; dy < num_inner_dst_blocks_y; dy++)11495{11496for (uint32_t dx = 0; dx < num_inner_dst_blocks_x; dx++)11497{11498bc6h_block* pDst_block = (bc6h_block*)pDst_blocks + (dst_block_x + dx) + (dst_block_y + dy) * output_row_pitch_in_blocks_or_pixels;1149911500half_float src_pixels[4][4][3]; // [y][x][c]1150111502for (uint32_t y = 0; y < 4; y++)11503{11504const uint32_t src_pixel_y = basisu::minimum<uint32_t>(dy * 4 + y, num_inner_blocks_y * 6 - 1);1150511506for (uint32_t x = 0; x < 4; x++)11507{11508const uint32_t src_pixel_x = basisu::minimum<uint32_t>(dx * 4 + x, num_inner_blocks_x * 6 - 1);1150911510assert((src_pixel_y < 12) && (src_pixel_x < 12));1151111512src_pixels[y][x][0] = unpacked_blocks[src_pixel_y][src_pixel_x][0];11513src_pixels[y][x][1] = unpacked_blocks[src_pixel_y][src_pixel_x][1];11514src_pixels[y][x][2] = unpacked_blocks[src_pixel_y][src_pixel_x][2];1151511516} // x11517} // y1151811519astc_6x6_hdr::fast_encode_bc6h(&src_pixels[0][0][0], pDst_block, bc6h_enc_params);1152011521} // dx11522} // dy1152311524} // block_x1152511526} // block_y1152711528status = true;11529}11530else11531{11532for (uint32_t block_y = 0; block_y < num_blocks_y; ++block_y)11533{11534void* pDst_block = (uint8_t*)pDst_blocks + block_y * output_row_pitch_in_blocks_or_pixels * output_block_or_pixel_stride_in_bytes;1153511536for (uint32_t block_x = 0; block_x < num_blocks_x; ++block_x, ++pSource_block, pDst_block = (uint8_t*)pDst_block + output_block_or_pixel_stride_in_bytes)11537{11538switch (fmt)11539{11540case block_format::cASTC_HDR_6x6:11541{11542// Nothing to do, ASTC HDR 6x6 is just ASTC.11543// TODO: Optimize this copy11544memcpy(pDst_block, pSource_block, sizeof(astc_helpers::astc_block));11545status = true;11546break;11547}11548case block_format::cRGB_9E5:11549{11550astc_helpers::log_astc_block log_blk;11551status = astc_helpers::unpack_block(pSource_block, log_blk, 6, 6);11552if (status)11553{11554uint32_t* pDst_pixels = reinterpret_cast<uint32_t*>(11555static_cast<uint8_t*>(pDst_blocks) + (block_x * 6 + block_y * 6 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint32_t)11556);1155711558uint32_t blk_texels[6][6];1155911560status = astc_helpers::decode_block(log_blk, blk_texels, 6, 6, astc_helpers::cDecodeModeRGB9E5);1156111562if (status)11563{11564const uint32_t max_x = basisu::minimum<int>(6, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 6);11565const uint32_t max_y = basisu::minimum<int>(6, (int)output_rows_in_pixels - (int)block_y * 6);1156611567for (uint32_t y = 0; y < max_y; y++)11568{11569memcpy(pDst_pixels, &blk_texels[y][0], sizeof(uint32_t) * max_x);1157011571pDst_pixels += output_row_pitch_in_blocks_or_pixels;11572} // y11573}11574}1157511576break;11577}11578case block_format::cRGBA_HALF:11579{11580astc_helpers::log_astc_block log_blk;11581status = astc_helpers::unpack_block(pSource_block, log_blk, 6, 6);11582if (status)11583{11584half_float* pDst_pixels = reinterpret_cast<half_float*>(11585static_cast<uint8_t*>(pDst_blocks) + (block_x * 6 + block_y * 6 * output_row_pitch_in_blocks_or_pixels) * sizeof(half_float) * 411586);1158711588half_float blk_texels[6][6][4];11589status = astc_helpers::decode_block(log_blk, blk_texels, 6, 6, astc_helpers::cDecodeModeHDR16);1159011591if (status)11592{11593const uint32_t max_x = basisu::minimum<int>(6, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 6);11594const uint32_t max_y = basisu::minimum<int>(6, (int)output_rows_in_pixels - (int)block_y * 6);1159511596for (uint32_t y = 0; y < max_y; y++)11597{11598for (uint32_t x = 0; x < max_x; x++)11599{11600pDst_pixels[0 + 4 * x] = blk_texels[y][x][0];11601pDst_pixels[1 + 4 * x] = blk_texels[y][x][1];11602pDst_pixels[2 + 4 * x] = blk_texels[y][x][2];11603pDst_pixels[3 + 4 * x] = blk_texels[y][x][3];11604} // x1160511606pDst_pixels += output_row_pitch_in_blocks_or_pixels * 4;11607} // y11608}11609}1161011611break;11612}11613case block_format::cRGB_HALF:11614{11615astc_helpers::log_astc_block log_blk;11616status = astc_helpers::unpack_block(pSource_block, log_blk, 6, 6);11617if (status)11618{11619half_float* pDst_pixels =11620reinterpret_cast<half_float*>(static_cast<uint8_t*>(pDst_blocks) + (block_x * 6 + block_y * 6 * output_row_pitch_in_blocks_or_pixels) * sizeof(half_float) * 3);1162111622half_float blk_texels[6][6][4];11623status = astc_helpers::decode_block(log_blk, blk_texels, 6, 6, astc_helpers::cDecodeModeHDR16);11624if (status)11625{11626const uint32_t max_x = basisu::minimum<int>(6, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 6);11627const uint32_t max_y = basisu::minimum<int>(6, (int)output_rows_in_pixels - (int)block_y * 6);1162811629for (uint32_t y = 0; y < max_y; y++)11630{11631for (uint32_t x = 0; x < max_x; x++)11632{11633pDst_pixels[0 + 3 * x] = blk_texels[y][x][0];11634pDst_pixels[1 + 3 * x] = blk_texels[y][x][1];11635pDst_pixels[2 + 3 * x] = blk_texels[y][x][2];11636} // x1163711638pDst_pixels += output_row_pitch_in_blocks_or_pixels * 3;11639} // y11640}11641}1164211643break;11644}11645default:11646assert(0);11647break;1164811649}1165011651if (!status)11652{11653BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_slice: Transcoder failed to unpack a ASTC HDR block - this is a bug, or the data was corrupted\n");11654return false;11655}1165611657} // block_x1165811659} // block_y11660}1166111662return true;11663#else11664BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_slice: ASTC HDR is unsupported\n");1166511666BASISU_NOTE_UNUSED(decode_flags);11667BASISU_NOTE_UNUSED(channel0);11668BASISU_NOTE_UNUSED(channel1);11669BASISU_NOTE_UNUSED(output_rows_in_pixels);11670BASISU_NOTE_UNUSED(output_row_pitch_in_blocks_or_pixels);11671BASISU_NOTE_UNUSED(output_block_or_pixel_stride_in_bytes);11672BASISU_NOTE_UNUSED(fmt);11673BASISU_NOTE_UNUSED(image_data_size);11674BASISU_NOTE_UNUSED(pImage_data);11675BASISU_NOTE_UNUSED(num_blocks_x);11676BASISU_NOTE_UNUSED(num_blocks_y);11677BASISU_NOTE_UNUSED(pDst_blocks);1167811679return false;11680#endif11681}1168211683bool basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_image(11684transcoder_texture_format target_format,11685void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,11686const uint8_t* pCompressed_data, uint32_t compressed_data_length,11687uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,11688uint64_t slice_offset, uint32_t slice_length,11689uint32_t decode_flags,11690bool has_alpha,11691bool is_video,11692uint32_t output_row_pitch_in_blocks_or_pixels,11693basisu_transcoder_state* pState,11694uint32_t output_rows_in_pixels,11695int channel0, int channel1)11696{11697BASISU_NOTE_UNUSED(is_video);11698BASISU_NOTE_UNUSED(level_index);11699BASISU_NOTE_UNUSED(decode_flags);1170011701if (((uint64_t)slice_offset + slice_length) > (uint64_t)compressed_data_length)11702{11703BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_image: source data buffer too small\n");11704return false;11705}1170611707const uint32_t bytes_per_block_or_pixel = basis_get_bytes_per_block_or_pixel(target_format);11708//const uint32_t total_slice_blocks = num_blocks_x * num_blocks_y;1170911710if (!basis_validate_output_buffer_size(basis_tex_format::cASTC_HDR_6x6, target_format, output_blocks_buf_size_in_blocks_or_pixels, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, output_rows_in_pixels))11711{11712BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_image: output buffer size too small\n");11713return false;11714}1171511716bool status = false;1171711718switch (target_format)11719{11720case transcoder_texture_format::cTFASTC_HDR_6x6_RGBA:11721{11722status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cASTC_HDR_6x6,11723bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);1172411725if (!status)11726{11727BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_image: transcode_slice() to ASTC_HDR failed\n");11728}11729break;11730}11731case transcoder_texture_format::cTFBC6H:11732{11733status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBC6H,11734bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);11735if (!status)11736{11737BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_image: transcode_slice() to BC6H failed\n");11738}11739break;11740}11741case transcoder_texture_format::cTFRGB_HALF:11742{11743status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGB_HALF,11744bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);11745if (!status)11746{11747BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_image: transcode_slice() to RGB_HALF failed\n");11748}11749break;11750}11751case transcoder_texture_format::cTFRGBA_HALF:11752{11753status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGBA_HALF,11754bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1, decode_flags);11755if (!status)11756{11757BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_image: transcode_slice() to RGBA_HALF failed\n");11758}11759break;11760}11761case transcoder_texture_format::cTFRGB_9E5:11762{11763status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGB_9E5,11764bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1 , decode_flags);11765if (!status)11766{11767BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_image: transcode_slice() to RGBA_HALF failed\n");11768}11769break;11770}11771default:11772{11773assert(0);11774BASISU_DEVEL_ERROR("basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder::transcode_image: Invalid format\n");11775break;11776}11777}1177811779return status;11780}1178111782//------------------------------------------------------------------------------------------------1178311784basisu_transcoder::basisu_transcoder() :11785m_ready_to_transcode(false)11786{11787}1178811789bool basisu_transcoder::validate_file_checksums(const void* pData, uint32_t data_size, bool full_validation) const11790{11791if (!validate_header(pData, data_size))11792return false;1179311794const basis_file_header* pHeader = reinterpret_cast<const basis_file_header*>(pData);1179511796#if !BASISU_NO_HEADER_OR_DATA_CRC16_CHECKS11797if (crc16(&pHeader->m_data_size, sizeof(basis_file_header) - BASISU_OFFSETOF(basis_file_header, m_data_size), 0) != pHeader->m_header_crc16)11798{11799BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: header CRC check failed\n");11800return false;11801}1180211803if (full_validation)11804{11805if (crc16(reinterpret_cast<const uint8_t*>(pData) + sizeof(basis_file_header), pHeader->m_data_size, 0) != pHeader->m_data_crc16)11806{11807BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: data CRC check failed\n");11808return false;11809}11810}11811#endif1181211813return true;11814}1181511816bool basisu_transcoder::validate_header_quick(const void* pData, uint32_t data_size) const11817{11818if (data_size <= sizeof(basis_file_header))11819return false;1182011821const basis_file_header* pHeader = reinterpret_cast<const basis_file_header*>(pData);1182211823if ((pHeader->m_sig != basis_file_header::cBASISSigValue) || (pHeader->m_ver != BASISD_SUPPORTED_BASIS_VERSION) || (pHeader->m_header_size != sizeof(basis_file_header)))11824{11825BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: header has an invalid signature, or file version is unsupported\n");11826return false;11827}1182811829uint32_t expected_file_size = sizeof(basis_file_header) + pHeader->m_data_size;11830if (data_size < expected_file_size)11831{11832BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: source buffer is too small\n");11833return false;11834}1183511836if ((!pHeader->m_total_slices) || (!pHeader->m_total_images))11837{11838BASISU_DEVEL_ERROR("basisu_transcoder::validate_header_quick: header is invalid\n");11839return false;11840}1184111842if ((pHeader->m_slice_desc_file_ofs >= data_size) ||11843((data_size - pHeader->m_slice_desc_file_ofs) < (sizeof(basis_slice_desc) * pHeader->m_total_slices))11844)11845{11846BASISU_DEVEL_ERROR("basisu_transcoder::validate_header_quick: passed in buffer is too small or data is corrupted\n");11847return false;11848}1184911850return true;11851}1185211853bool basisu_transcoder::validate_header(const void* pData, uint32_t data_size) const11854{11855if (data_size <= sizeof(basis_file_header))11856{11857BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: input source buffer is too small\n");11858return false;11859}1186011861const basis_file_header* pHeader = reinterpret_cast<const basis_file_header*>(pData);1186211863if ((pHeader->m_sig != basis_file_header::cBASISSigValue) || (pHeader->m_ver != BASISD_SUPPORTED_BASIS_VERSION) || (pHeader->m_header_size != sizeof(basis_file_header)))11864{11865BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: header has an invalid signature, or file version is unsupported\n");11866return false;11867}1186811869uint32_t expected_file_size = sizeof(basis_file_header) + pHeader->m_data_size;11870if (data_size < expected_file_size)11871{11872BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: input source buffer is too small, or header is corrupted\n");11873return false;11874}1187511876if ((!pHeader->m_total_images) || (!pHeader->m_total_slices))11877{11878BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid basis file (total images or slices are 0)\n");11879return false;11880}1188111882if (pHeader->m_total_images > pHeader->m_total_slices)11883{11884BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid basis file (too many images)\n");11885return false;11886}1188711888if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)11889{11890if (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices)11891{11892if (pHeader->m_total_slices & 1)11893{11894BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid alpha .basis file\n");11895return false;11896}11897}1189811899// This flag dates back to pre-Basis Universal, when .basis supported full ETC1 too.11900if ((pHeader->m_flags & cBASISHeaderFlagETC1S) == 0)11901{11902BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: Invalid .basis file (ETC1S check)\n");11903return false;11904}11905}11906else11907{11908if ((pHeader->m_flags & cBASISHeaderFlagETC1S) != 0)11909{11910BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: Invalid .basis file (ETC1S check)\n");11911return false;11912}11913}1191411915if ((pHeader->m_slice_desc_file_ofs >= data_size) ||11916((data_size - pHeader->m_slice_desc_file_ofs) < (sizeof(basis_slice_desc) * pHeader->m_total_slices))11917)11918{11919BASISU_DEVEL_ERROR("basisu_transcoder::validate_header_quick: passed in buffer is too small or data is corrupted\n");11920return false;11921}1192211923return true;11924}1192511926basis_texture_type basisu_transcoder::get_texture_type(const void* pData, uint32_t data_size) const11927{11928if (!validate_header_quick(pData, data_size))11929{11930BASISU_DEVEL_ERROR("basisu_transcoder::get_texture_type: header validation failed\n");11931return cBASISTexType2DArray;11932}1193311934const basis_file_header* pHeader = static_cast<const basis_file_header*>(pData);1193511936basis_texture_type btt = static_cast<basis_texture_type>(static_cast<uint8_t>(pHeader->m_tex_type));1193711938if (btt >= cBASISTexTypeTotal)11939{11940BASISU_DEVEL_ERROR("basisu_transcoder::validate_header_quick: header's texture type field is invalid\n");11941return cBASISTexType2DArray;11942}1194311944return btt;11945}1194611947bool basisu_transcoder::get_userdata(const void* pData, uint32_t data_size, uint32_t& userdata0, uint32_t& userdata1) const11948{11949if (!validate_header_quick(pData, data_size))11950{11951BASISU_DEVEL_ERROR("basisu_transcoder::get_userdata: header validation failed\n");11952return false;11953}1195411955const basis_file_header* pHeader = static_cast<const basis_file_header*>(pData);1195611957userdata0 = pHeader->m_userdata0;11958userdata1 = pHeader->m_userdata1;11959return true;11960}1196111962uint32_t basisu_transcoder::get_total_images(const void* pData, uint32_t data_size) const11963{11964if (!validate_header_quick(pData, data_size))11965{11966BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: header validation failed\n");11967return 0;11968}1196911970const basis_file_header* pHeader = static_cast<const basis_file_header*>(pData);1197111972return pHeader->m_total_images;11973}1197411975basis_tex_format basisu_transcoder::get_basis_tex_format(const void* pData, uint32_t data_size) const11976{11977if (!validate_header_quick(pData, data_size))11978{11979BASISU_DEVEL_ERROR("basisu_transcoder::get_basis_tex_format: header validation failed\n");11980return basis_tex_format::cETC1S;11981}1198211983const basis_file_header* pHeader = static_cast<const basis_file_header*>(pData);1198411985return (basis_tex_format)(uint32_t)pHeader->m_tex_format;11986}1198711988bool basisu_transcoder::get_image_info(const void* pData, uint32_t data_size, basisu_image_info& image_info, uint32_t image_index) const11989{11990if (!validate_header_quick(pData, data_size))11991{11992BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: header validation failed\n");11993return false;11994}1199511996int slice_index = find_first_slice_index(pData, data_size, image_index, 0);11997if (slice_index < 0)11998{11999BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: invalid slice index\n");12000return false;12001}1200212003const basis_file_header* pHeader = static_cast<const basis_file_header*>(pData);1200412005if (image_index >= pHeader->m_total_images)12006{12007BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: invalid image_index\n");12008return false;12009}1201012011const basis_slice_desc* pSlice_descs = reinterpret_cast<const basis_slice_desc*>(static_cast<const uint8_t*>(pData) + pHeader->m_slice_desc_file_ofs);1201212013uint32_t total_levels = 1;12014for (uint32_t i = slice_index + 1; i < pHeader->m_total_slices; i++)12015if (pSlice_descs[i].m_image_index == image_index)12016total_levels = basisu::maximum<uint32_t>(total_levels, pSlice_descs[i].m_level_index + 1);12017else12018break;1201912020if (total_levels > 16)12021{12022BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: invalid image_index\n");12023return false;12024}1202512026const basis_slice_desc& slice_desc = pSlice_descs[slice_index];1202712028image_info.m_image_index = image_index;12029image_info.m_total_levels = total_levels;1203012031image_info.m_alpha_flag = false;1203212033// For ETC1S, if anything has alpha all images have alpha. For UASTC, we only report alpha when the image actually has alpha.12034if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)12035image_info.m_alpha_flag = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0;12036else12037image_info.m_alpha_flag = (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0;1203812039image_info.m_iframe_flag = (slice_desc.m_flags & cSliceDescFlagsFrameIsIFrame) != 0;1204012041const uint32_t block_width = basis_tex_format_get_block_width((basis_tex_format)((uint32_t)pHeader->m_tex_format));12042const uint32_t block_height = basis_tex_format_get_block_height((basis_tex_format)((uint32_t)pHeader->m_tex_format));1204312044image_info.m_width = slice_desc.m_num_blocks_x * block_width;12045image_info.m_height = slice_desc.m_num_blocks_y * block_height;12046image_info.m_orig_width = slice_desc.m_orig_width;12047image_info.m_orig_height = slice_desc.m_orig_height;12048image_info.m_num_blocks_x = slice_desc.m_num_blocks_x;12049image_info.m_num_blocks_y = slice_desc.m_num_blocks_y;12050image_info.m_block_width = block_width;12051image_info.m_block_height = block_height;12052image_info.m_total_blocks = image_info.m_num_blocks_x * image_info.m_num_blocks_y;12053image_info.m_first_slice_index = slice_index;1205412055return true;12056}1205712058uint32_t basisu_transcoder::get_total_image_levels(const void* pData, uint32_t data_size, uint32_t image_index) const12059{12060if (!validate_header_quick(pData, data_size))12061{12062BASISU_DEVEL_ERROR("basisu_transcoder::get_total_image_levels: header validation failed\n");12063return false;12064}1206512066int slice_index = find_first_slice_index(pData, data_size, image_index, 0);12067if (slice_index < 0)12068{12069BASISU_DEVEL_ERROR("basisu_transcoder::get_total_image_levels: failed finding slice\n");12070return false;12071}1207212073const basis_file_header* pHeader = static_cast<const basis_file_header*>(pData);1207412075if (image_index >= pHeader->m_total_images)12076{12077BASISU_DEVEL_ERROR("basisu_transcoder::get_total_image_levels: invalid image_index\n");12078return false;12079}1208012081const basis_slice_desc* pSlice_descs = reinterpret_cast<const basis_slice_desc*>(static_cast<const uint8_t*>(pData) + pHeader->m_slice_desc_file_ofs);1208212083uint32_t total_levels = 1;12084for (uint32_t i = slice_index + 1; i < pHeader->m_total_slices; i++)12085if (pSlice_descs[i].m_image_index == image_index)12086total_levels = basisu::maximum<uint32_t>(total_levels, pSlice_descs[i].m_level_index + 1);12087else12088break;1208912090const uint32_t cMaxSupportedLevels = 16;12091if (total_levels > cMaxSupportedLevels)12092{12093BASISU_DEVEL_ERROR("basisu_transcoder::get_total_image_levels: invalid image levels!\n");12094return false;12095}1209612097return total_levels;12098}1209912100bool basisu_transcoder::get_image_level_desc(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, uint32_t& orig_width, uint32_t& orig_height, uint32_t& total_blocks) const12101{12102if (!validate_header_quick(pData, data_size))12103{12104BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_desc: header validation failed\n");12105return false;12106}1210712108int slice_index = find_first_slice_index(pData, data_size, image_index, level_index);12109if (slice_index < 0)12110{12111BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_desc: failed finding slice\n");12112return false;12113}1211412115const basis_file_header* pHeader = static_cast<const basis_file_header*>(pData);1211612117if (image_index >= pHeader->m_total_images)12118{12119BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_desc: invalid image_index\n");12120return false;12121}1212212123const basis_slice_desc* pSlice_descs = reinterpret_cast<const basis_slice_desc*>(static_cast<const uint8_t*>(pData) + pHeader->m_slice_desc_file_ofs);1212412125const basis_slice_desc& slice_desc = pSlice_descs[slice_index];1212612127orig_width = slice_desc.m_orig_width;12128orig_height = slice_desc.m_orig_height;12129total_blocks = slice_desc.m_num_blocks_x * slice_desc.m_num_blocks_y;1213012131return true;12132}1213312134bool basisu_transcoder::get_image_level_info(const void* pData, uint32_t data_size, basisu_image_level_info& image_info, uint32_t image_index, uint32_t level_index) const12135{12136if (!validate_header_quick(pData, data_size))12137{12138BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_info: validate_file_checksums failed\n");12139return false;12140}1214112142int slice_index = find_first_slice_index(pData, data_size, image_index, level_index);12143if (slice_index < 0)12144{12145BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_info: failed finding slice\n");12146return false;12147}1214812149const basis_file_header* pHeader = static_cast<const basis_file_header*>(pData);1215012151if (image_index >= pHeader->m_total_images)12152{12153BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_info: invalid image_index\n");12154return false;12155}1215612157const basis_slice_desc* pSlice_descs = reinterpret_cast<const basis_slice_desc*>(static_cast<const uint8_t*>(pData) + pHeader->m_slice_desc_file_ofs);1215812159const basis_slice_desc& slice_desc = pSlice_descs[slice_index];1216012161image_info.m_image_index = image_index;12162image_info.m_level_index = level_index;1216312164// For ETC1S, if anything has alpha all images have alpha. For UASTC, we only report alpha when the image actually has alpha.12165if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)12166image_info.m_alpha_flag = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0;12167else12168image_info.m_alpha_flag = (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0;1216912170const uint32_t block_width = basis_tex_format_get_block_width((basis_tex_format)((uint32_t)pHeader->m_tex_format));12171const uint32_t block_height = basis_tex_format_get_block_height((basis_tex_format)((uint32_t)pHeader->m_tex_format));1217212173image_info.m_iframe_flag = (slice_desc.m_flags & cSliceDescFlagsFrameIsIFrame) != 0;12174image_info.m_width = slice_desc.m_num_blocks_x * block_width;12175image_info.m_height = slice_desc.m_num_blocks_y * block_height;12176image_info.m_orig_width = slice_desc.m_orig_width;12177image_info.m_orig_height = slice_desc.m_orig_height;12178image_info.m_block_width = block_width;12179image_info.m_block_height = block_height;12180image_info.m_num_blocks_x = slice_desc.m_num_blocks_x;12181image_info.m_num_blocks_y = slice_desc.m_num_blocks_y;12182image_info.m_total_blocks = image_info.m_num_blocks_x * image_info.m_num_blocks_y;12183image_info.m_first_slice_index = slice_index;1218412185image_info.m_rgb_file_ofs = slice_desc.m_file_ofs;12186image_info.m_rgb_file_len = slice_desc.m_file_size;12187image_info.m_alpha_file_ofs = 0;12188image_info.m_alpha_file_len = 0;1218912190if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)12191{12192if (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices)12193{12194assert((slice_index + 1) < (int)pHeader->m_total_slices);12195image_info.m_alpha_file_ofs = pSlice_descs[slice_index + 1].m_file_ofs;12196image_info.m_alpha_file_len = pSlice_descs[slice_index + 1].m_file_size;12197}12198}1219912200return true;12201}1220212203bool basisu_transcoder::get_file_info(const void* pData, uint32_t data_size, basisu_file_info& file_info) const12204{12205if (!validate_file_checksums(pData, data_size, false))12206{12207BASISU_DEVEL_ERROR("basisu_transcoder::get_file_info: validate_file_checksums failed\n");12208return false;12209}1221012211const basis_file_header* pHeader = static_cast<const basis_file_header*>(pData);12212const basis_slice_desc* pSlice_descs = reinterpret_cast<const basis_slice_desc*>(static_cast<const uint8_t*>(pData) + pHeader->m_slice_desc_file_ofs);1221312214file_info.m_version = pHeader->m_ver;1221512216file_info.m_total_header_size = sizeof(basis_file_header) + pHeader->m_total_slices * sizeof(basis_slice_desc);1221712218file_info.m_total_selectors = pHeader->m_total_selectors;12219file_info.m_selector_codebook_ofs = pHeader->m_selector_cb_file_ofs;12220file_info.m_selector_codebook_size = pHeader->m_selector_cb_file_size;1222112222file_info.m_total_endpoints = pHeader->m_total_endpoints;12223file_info.m_endpoint_codebook_ofs = pHeader->m_endpoint_cb_file_ofs;12224file_info.m_endpoint_codebook_size = pHeader->m_endpoint_cb_file_size;1222512226file_info.m_tables_ofs = pHeader->m_tables_file_ofs;12227file_info.m_tables_size = pHeader->m_tables_file_size;1222812229file_info.m_tex_format = static_cast<basis_tex_format>(static_cast<int>(pHeader->m_tex_format));1223012231file_info.m_etc1s = (pHeader->m_tex_format == (int)basis_tex_format::cETC1S);1223212233file_info.m_y_flipped = (pHeader->m_flags & cBASISHeaderFlagYFlipped) != 0;12234file_info.m_has_alpha_slices = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0;1223512236const uint32_t total_slices = pHeader->m_total_slices;1223712238file_info.m_slice_info.resize(total_slices);1223912240file_info.m_slices_size = 0;1224112242file_info.m_tex_type = static_cast<basis_texture_type>(static_cast<uint8_t>(pHeader->m_tex_type));1224312244if (file_info.m_tex_type > cBASISTexTypeTotal)12245{12246BASISU_DEVEL_ERROR("basisu_transcoder::get_file_info: invalid texture type, file is corrupted\n");12247return false;12248}1224912250file_info.m_us_per_frame = pHeader->m_us_per_frame;12251file_info.m_userdata0 = pHeader->m_userdata0;12252file_info.m_userdata1 = pHeader->m_userdata1;1225312254file_info.m_image_mipmap_levels.resize(0);12255file_info.m_image_mipmap_levels.resize(pHeader->m_total_images);1225612257file_info.m_total_images = pHeader->m_total_images;1225812259const uint32_t block_width = basis_tex_format_get_block_width((basis_tex_format)((uint32_t)pHeader->m_tex_format));12260const uint32_t block_height = basis_tex_format_get_block_height((basis_tex_format)((uint32_t)pHeader->m_tex_format));12261file_info.m_block_width = block_width;12262file_info.m_block_height = block_height;1226312264for (uint32_t i = 0; i < total_slices; i++)12265{12266file_info.m_slices_size += pSlice_descs[i].m_file_size;1226712268basisu_slice_info& slice_info = file_info.m_slice_info[i];1226912270slice_info.m_orig_width = pSlice_descs[i].m_orig_width;12271slice_info.m_orig_height = pSlice_descs[i].m_orig_height;12272slice_info.m_width = pSlice_descs[i].m_num_blocks_x * block_width;12273slice_info.m_height = pSlice_descs[i].m_num_blocks_y * block_height;12274slice_info.m_num_blocks_x = pSlice_descs[i].m_num_blocks_x;12275slice_info.m_num_blocks_y = pSlice_descs[i].m_num_blocks_y;12276slice_info.m_block_width = block_width;12277slice_info.m_block_height = block_height;12278slice_info.m_total_blocks = slice_info.m_num_blocks_x * slice_info.m_num_blocks_y;12279slice_info.m_compressed_size = pSlice_descs[i].m_file_size;12280slice_info.m_slice_index = i;12281slice_info.m_image_index = pSlice_descs[i].m_image_index;12282slice_info.m_level_index = pSlice_descs[i].m_level_index;12283slice_info.m_unpacked_slice_crc16 = pSlice_descs[i].m_slice_data_crc16;12284slice_info.m_alpha_flag = (pSlice_descs[i].m_flags & cSliceDescFlagsHasAlpha) != 0;12285slice_info.m_iframe_flag = (pSlice_descs[i].m_flags & cSliceDescFlagsFrameIsIFrame) != 0;1228612287if (pSlice_descs[i].m_image_index >= pHeader->m_total_images)12288{12289BASISU_DEVEL_ERROR("basisu_transcoder::get_file_info: slice desc's image index is invalid\n");12290return false;12291}1229212293file_info.m_image_mipmap_levels[pSlice_descs[i].m_image_index] = basisu::maximum<uint32_t>(file_info.m_image_mipmap_levels[pSlice_descs[i].m_image_index], pSlice_descs[i].m_level_index + 1);1229412295if (file_info.m_image_mipmap_levels[pSlice_descs[i].m_image_index] > 16)12296{12297BASISU_DEVEL_ERROR("basisu_transcoder::get_file_info: slice mipmap level is invalid\n");12298return false;12299}12300}1230112302return true;12303}1230412305bool basisu_transcoder::start_transcoding(const void* pData, uint32_t data_size)12306{12307if (!validate_header_quick(pData, data_size))12308{12309BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: header validation failed\n");12310return false;12311}1231212313const basis_file_header* pHeader = reinterpret_cast<const basis_file_header*>(pData);12314const uint8_t* pDataU8 = static_cast<const uint8_t*>(pData);1231512316if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)12317{12318if (m_lowlevel_etc1s_decoder.m_local_endpoints.size())12319{12320m_lowlevel_etc1s_decoder.clear();12321}1232212323if (pHeader->m_flags & cBASISHeaderFlagUsesGlobalCodebook)12324{12325if (!m_lowlevel_etc1s_decoder.get_global_codebooks())12326{12327BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: File uses global codebooks, but set_global_codebooks() has not been called\n");12328return false;12329}12330if (!m_lowlevel_etc1s_decoder.get_global_codebooks()->get_endpoints().size())12331{12332BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: Global codebooks must be unpacked first by calling start_transcoding()\n");12333return false;12334}12335if ((m_lowlevel_etc1s_decoder.get_global_codebooks()->get_endpoints().size() != pHeader->m_total_endpoints) ||12336(m_lowlevel_etc1s_decoder.get_global_codebooks()->get_selectors().size() != pHeader->m_total_selectors))12337{12338BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: Global codebook size mismatch (wrong codebooks for file).\n");12339return false;12340}12341if (!pHeader->m_tables_file_size)12342{12343BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted (2)\n");12344return false;12345}12346if (pHeader->m_tables_file_ofs > data_size)12347{12348BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (4)\n");12349return false;12350}12351if (pHeader->m_tables_file_size > (data_size - pHeader->m_tables_file_ofs))12352{12353BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (5)\n");12354return false;12355}12356}12357else12358{12359if (!pHeader->m_endpoint_cb_file_size || !pHeader->m_selector_cb_file_size || !pHeader->m_tables_file_size)12360{12361BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted (0)\n");12362return false;12363}1236412365if ((pHeader->m_endpoint_cb_file_ofs > data_size) || (pHeader->m_selector_cb_file_ofs > data_size) || (pHeader->m_tables_file_ofs > data_size))12366{12367BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (1)\n");12368return false;12369}1237012371if (pHeader->m_endpoint_cb_file_size > (data_size - pHeader->m_endpoint_cb_file_ofs))12372{12373BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (2)\n");12374return false;12375}1237612377if (pHeader->m_selector_cb_file_size > (data_size - pHeader->m_selector_cb_file_ofs))12378{12379BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (3)\n");12380return false;12381}1238212383if (pHeader->m_tables_file_size > (data_size - pHeader->m_tables_file_ofs))12384{12385BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (3)\n");12386return false;12387}1238812389if (!m_lowlevel_etc1s_decoder.decode_palettes(12390pHeader->m_total_endpoints, pDataU8 + pHeader->m_endpoint_cb_file_ofs, pHeader->m_endpoint_cb_file_size,12391pHeader->m_total_selectors, pDataU8 + pHeader->m_selector_cb_file_ofs, pHeader->m_selector_cb_file_size))12392{12393BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: decode_palettes failed\n");12394return false;12395}12396}1239712398if (!m_lowlevel_etc1s_decoder.decode_tables(pDataU8 + pHeader->m_tables_file_ofs, pHeader->m_tables_file_size))12399{12400BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: decode_tables failed\n");12401return false;12402}12403}12404else12405{12406// Nothing special to do for UASTC/UASTC HDR.12407if (m_lowlevel_etc1s_decoder.m_local_endpoints.size())12408{12409m_lowlevel_etc1s_decoder.clear();12410}12411}1241212413m_ready_to_transcode = true;1241412415return true;12416}1241712418bool basisu_transcoder::stop_transcoding()12419{12420m_lowlevel_etc1s_decoder.clear();1242112422m_ready_to_transcode = false;1242312424return true;12425}1242612427bool basisu_transcoder::transcode_slice(const void* pData, uint32_t data_size, uint32_t slice_index, void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels, block_format fmt,12428uint32_t output_block_or_pixel_stride_in_bytes, uint32_t decode_flags, uint32_t output_row_pitch_in_blocks_or_pixels, basisu_transcoder_state* pState, void *pAlpha_blocks, uint32_t output_rows_in_pixels, int channel0, int channel1) const12429{12430if (!m_ready_to_transcode)12431{12432BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: must call start_transcoding first\n");12433return false;12434}1243512436if (decode_flags & cDecodeFlagsPVRTCDecodeToNextPow2)12437{12438// TODO: Not yet supported12439BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: cDecodeFlagsPVRTCDecodeToNextPow2 currently unsupported\n");12440return false;12441}1244212443if (!validate_header_quick(pData, data_size))12444{12445BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: header validation failed\n");12446return false;12447}1244812449const basis_file_header* pHeader = reinterpret_cast<const basis_file_header*>(pData);1245012451const uint8_t* pDataU8 = static_cast<const uint8_t*>(pData);1245212453if (slice_index >= pHeader->m_total_slices)12454{12455BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: slice_index >= pHeader->m_total_slices\n");12456return false;12457}1245812459const basis_slice_desc& slice_desc = reinterpret_cast<const basis_slice_desc*>(pDataU8 + pHeader->m_slice_desc_file_ofs)[slice_index];1246012461if (basis_block_format_is_uncompressed(fmt))12462{12463// Assume the output buffer is orig_width by orig_height12464if (!output_row_pitch_in_blocks_or_pixels)12465output_row_pitch_in_blocks_or_pixels = slice_desc.m_orig_width;1246612467if (!output_rows_in_pixels)12468output_rows_in_pixels = slice_desc.m_orig_height;1246912470// Now make sure the output buffer is large enough, or we'll overwrite memory.12471if (output_blocks_buf_size_in_blocks_or_pixels < (output_rows_in_pixels * output_row_pitch_in_blocks_or_pixels))12472{12473BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: output_blocks_buf_size_in_blocks_or_pixels < (output_rows_in_pixels * output_row_pitch_in_blocks_or_pixels)\n");12474return false;12475}12476}12477else if (fmt == block_format::cFXT1_RGB)12478{12479const uint32_t num_blocks_fxt1_x = (slice_desc.m_orig_width + 7) / 8;12480const uint32_t num_blocks_fxt1_y = (slice_desc.m_orig_height + 3) / 4;12481const uint32_t total_blocks_fxt1 = num_blocks_fxt1_x * num_blocks_fxt1_y;1248212483if (output_blocks_buf_size_in_blocks_or_pixels < total_blocks_fxt1)12484{12485BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: output_blocks_buf_size_in_blocks_or_pixels < total_blocks_fxt1\n");12486return false;12487}12488}12489else if (fmt == block_format::cASTC_HDR_6x6)12490{12491const uint32_t num_blocks_6x6_x = (slice_desc.m_orig_width + 5) / 6;12492const uint32_t num_blocks_6x6_y = (slice_desc.m_orig_height + 5) / 6;12493const uint32_t total_blocks_6x6 = num_blocks_6x6_x * num_blocks_6x6_y;1249412495if (output_blocks_buf_size_in_blocks_or_pixels < total_blocks_6x6)12496{12497BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: output_blocks_buf_size_in_blocks_or_pixels < total_blocks_6x6\n");12498return false;12499}12500}12501else12502{12503// must be a 4x4 pixel block format12504const uint32_t num_blocks_4x4_x = (slice_desc.m_orig_width + 3) / 4;12505const uint32_t num_blocks_4x4_y = (slice_desc.m_orig_height + 3) / 4;12506const uint32_t total_4x4_blocks = num_blocks_4x4_x * num_blocks_4x4_y;1250712508if (output_blocks_buf_size_in_blocks_or_pixels < total_4x4_blocks)12509{12510BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: output_blocks_buf_size_in_blocks_or_pixels < total_blocks\n");12511return false;12512}12513}1251412515if ((pHeader->m_tex_format == (uint32_t)basis_tex_format::cETC1S) || (pHeader->m_tex_format == (uint32_t)basis_tex_format::cUASTC4x4))12516{12517if ((fmt == block_format::cPVRTC1_4_RGB) || (fmt == block_format::cPVRTC1_4_RGBA))12518{12519if ((!basisu::is_pow2(slice_desc.m_num_blocks_x * 4)) || (!basisu::is_pow2(slice_desc.m_num_blocks_y * 4)))12520{12521// PVRTC1 only supports power of 2 dimensions12522BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: PVRTC1 only supports power of 2 dimensions\n");12523return false;12524}12525}12526}1252712528if (slice_desc.m_file_ofs > data_size)12529{12530BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: invalid slice_desc.m_file_ofs, or passed in buffer too small\n");12531return false;12532}1253312534const uint32_t data_size_left = data_size - slice_desc.m_file_ofs;12535if (data_size_left < slice_desc.m_file_size)12536{12537BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: invalid slice_desc.m_file_size, or passed in buffer too small\n");12538return false;12539}1254012541if (pHeader->m_tex_format == (int)basis_tex_format::cASTC_HDR_6x6)12542{12543return m_lowlevel_astc_6x6_hdr_decoder.transcode_slice(pOutput_blocks, slice_desc.m_num_blocks_x, slice_desc.m_num_blocks_y,12544pDataU8 + slice_desc.m_file_ofs, slice_desc.m_file_size,12545fmt, output_block_or_pixel_stride_in_bytes, (decode_flags & cDecodeFlagsBC1ForbidThreeColorBlocks) == 0, *pHeader, slice_desc, output_row_pitch_in_blocks_or_pixels, pState,12546output_rows_in_pixels, channel0, channel1, decode_flags);12547}12548else if (pHeader->m_tex_format == (int)basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE)12549{12550return m_lowlevel_astc_6x6_hdr_intermediate_decoder.transcode_slice(pOutput_blocks, slice_desc.m_num_blocks_x, slice_desc.m_num_blocks_y,12551pDataU8 + slice_desc.m_file_ofs, slice_desc.m_file_size,12552fmt, output_block_or_pixel_stride_in_bytes, (decode_flags & cDecodeFlagsBC1ForbidThreeColorBlocks) == 0, *pHeader, slice_desc, output_row_pitch_in_blocks_or_pixels, pState,12553output_rows_in_pixels, channel0, channel1, decode_flags);12554}12555else if (pHeader->m_tex_format == (int)basis_tex_format::cUASTC_HDR_4x4)12556{12557return m_lowlevel_uastc_4x4_hdr_decoder.transcode_slice(pOutput_blocks, slice_desc.m_num_blocks_x, slice_desc.m_num_blocks_y,12558pDataU8 + slice_desc.m_file_ofs, slice_desc.m_file_size,12559fmt, output_block_or_pixel_stride_in_bytes, (decode_flags & cDecodeFlagsBC1ForbidThreeColorBlocks) == 0, *pHeader, slice_desc, output_row_pitch_in_blocks_or_pixels, pState,12560output_rows_in_pixels, channel0, channel1, decode_flags);12561}12562else if (pHeader->m_tex_format == (int)basis_tex_format::cUASTC4x4)12563{12564return m_lowlevel_uastc_decoder.transcode_slice(pOutput_blocks, slice_desc.m_num_blocks_x, slice_desc.m_num_blocks_y,12565pDataU8 + slice_desc.m_file_ofs, slice_desc.m_file_size,12566fmt, output_block_or_pixel_stride_in_bytes, (decode_flags & cDecodeFlagsBC1ForbidThreeColorBlocks) == 0, *pHeader, slice_desc, output_row_pitch_in_blocks_or_pixels, pState,12567output_rows_in_pixels, channel0, channel1, decode_flags);12568}12569else12570{12571return m_lowlevel_etc1s_decoder.transcode_slice(pOutput_blocks, slice_desc.m_num_blocks_x, slice_desc.m_num_blocks_y,12572pDataU8 + slice_desc.m_file_ofs, slice_desc.m_file_size,12573fmt, output_block_or_pixel_stride_in_bytes, (decode_flags & cDecodeFlagsBC1ForbidThreeColorBlocks) == 0, *pHeader, slice_desc, output_row_pitch_in_blocks_or_pixels, pState,12574(decode_flags & cDecodeFlagsOutputHasAlphaIndices) != 0, pAlpha_blocks, output_rows_in_pixels);12575}12576}1257712578int basisu_transcoder::find_first_slice_index(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index) const12579{12580BASISU_NOTE_UNUSED(data_size);1258112582const basis_file_header* pHeader = reinterpret_cast<const basis_file_header*>(pData);12583const uint8_t* pDataU8 = static_cast<const uint8_t*>(pData);1258412585// For very large basis files this search could be painful12586// TODO: Binary search this12587for (uint32_t slice_iter = 0; slice_iter < pHeader->m_total_slices; slice_iter++)12588{12589const basis_slice_desc& slice_desc = reinterpret_cast<const basis_slice_desc*>(pDataU8 + pHeader->m_slice_desc_file_ofs)[slice_iter];12590if ((slice_desc.m_image_index == image_index) && (slice_desc.m_level_index == level_index))12591return slice_iter;12592}1259312594BASISU_DEVEL_ERROR("basisu_transcoder::find_first_slice_index: didn't find slice\n");1259512596return -1;12597}1259812599int basisu_transcoder::find_slice(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, bool alpha_data) const12600{12601if (!validate_header_quick(pData, data_size))12602{12603BASISU_DEVEL_ERROR("basisu_transcoder::find_slice: header validation failed\n");12604return false;12605}1260612607const basis_file_header* pHeader = reinterpret_cast<const basis_file_header*>(pData);12608const uint8_t* pDataU8 = static_cast<const uint8_t*>(pData);12609const basis_slice_desc* pSlice_descs = reinterpret_cast<const basis_slice_desc*>(pDataU8 + pHeader->m_slice_desc_file_ofs);1261012611// For very large basis files this search could be painful12612// TODO: Binary search this12613for (uint32_t slice_iter = 0; slice_iter < pHeader->m_total_slices; slice_iter++)12614{12615const basis_slice_desc& slice_desc = pSlice_descs[slice_iter];12616if ((slice_desc.m_image_index == image_index) && (slice_desc.m_level_index == level_index))12617{12618if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)12619{12620const bool slice_alpha = (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0;12621if (slice_alpha == alpha_data)12622return slice_iter;12623}12624else12625{12626return slice_iter;12627}12628}12629}1263012631BASISU_DEVEL_ERROR("basisu_transcoder::find_slice: didn't find slice\n");1263212633return -1;12634}1263512636void basisu_transcoder::write_opaque_alpha_blocks(12637uint32_t num_blocks_x, uint32_t num_blocks_y,12638void* pOutput_blocks, block_format fmt,12639uint32_t block_stride_in_bytes, uint32_t output_row_pitch_in_blocks_or_pixels)12640{12641// 'num_blocks_y', 'pOutput_blocks' & 'block_stride_in_bytes' unused12642// when disabling BASISD_SUPPORT_ETC2_EAC_A8 *and* BASISD_SUPPORT_DXT5A12643BASISU_NOTE_UNUSED(num_blocks_y);12644BASISU_NOTE_UNUSED(pOutput_blocks);12645BASISU_NOTE_UNUSED(block_stride_in_bytes);1264612647if (!output_row_pitch_in_blocks_or_pixels)12648output_row_pitch_in_blocks_or_pixels = num_blocks_x;1264912650if ((fmt == block_format::cETC2_EAC_A8) || (fmt == block_format::cETC2_EAC_R11))12651{12652#if BASISD_SUPPORT_ETC2_EAC_A812653eac_block blk;12654blk.m_base = 255;12655blk.m_multiplier = 1;12656blk.m_table = 13;1265712658// Selectors are all 4's12659memcpy(&blk.m_selectors, g_etc2_eac_a8_sel4, sizeof(g_etc2_eac_a8_sel4));1266012661for (uint32_t y = 0; y < num_blocks_y; y++)12662{12663uint32_t dst_ofs = y * output_row_pitch_in_blocks_or_pixels * block_stride_in_bytes;12664for (uint32_t x = 0; x < num_blocks_x; x++)12665{12666memcpy((uint8_t*)pOutput_blocks + dst_ofs, &blk, sizeof(blk));12667dst_ofs += block_stride_in_bytes;12668}12669}12670#endif12671}12672else if (fmt == block_format::cBC4)12673{12674#if BASISD_SUPPORT_DXT5A12675dxt5a_block blk;12676blk.m_endpoints[0] = 255;12677blk.m_endpoints[1] = 255;12678memset(blk.m_selectors, 0, sizeof(blk.m_selectors));1267912680for (uint32_t y = 0; y < num_blocks_y; y++)12681{12682uint32_t dst_ofs = y * output_row_pitch_in_blocks_or_pixels * block_stride_in_bytes;12683for (uint32_t x = 0; x < num_blocks_x; x++)12684{12685memcpy((uint8_t*)pOutput_blocks + dst_ofs, &blk, sizeof(blk));12686dst_ofs += block_stride_in_bytes;12687}12688}12689#endif12690}12691}1269212693bool basisu_transcoder::transcode_image_level(12694const void* pData, uint32_t data_size,12695uint32_t image_index, uint32_t level_index,12696void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,12697transcoder_texture_format fmt,12698uint32_t decode_flags, uint32_t output_row_pitch_in_blocks_or_pixels, basisu_transcoder_state *pState, uint32_t output_rows_in_pixels) const12699{12700const uint32_t bytes_per_block_or_pixel = basis_get_bytes_per_block_or_pixel(fmt);1270112702if (!m_ready_to_transcode)12703{12704BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: must call start_transcoding() first\n");12705return false;12706}1270712708//const bool transcode_alpha_data_to_opaque_formats = (decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;1270912710if (decode_flags & cDecodeFlagsPVRTCDecodeToNextPow2)12711{12712BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: cDecodeFlagsPVRTCDecodeToNextPow2 currently unsupported\n");12713// TODO: Not yet supported12714return false;12715}1271612717if (!validate_header_quick(pData, data_size))12718{12719BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: header validation failed\n");12720return false;12721}1272212723const basis_file_header* pHeader = reinterpret_cast<const basis_file_header*>(pData);1272412725const uint8_t* pDataU8 = static_cast<const uint8_t*>(pData);1272612727const basis_slice_desc* pSlice_descs = reinterpret_cast<const basis_slice_desc*>(pDataU8 + pHeader->m_slice_desc_file_ofs);1272812729const bool basis_file_has_alpha_slices = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0;1273012731int slice_index = find_first_slice_index(pData, data_size, image_index, level_index);12732if (slice_index < 0)12733{12734BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: failed finding slice index\n");12735// Unable to find the requested image/level12736return false;12737}1273812739if ((fmt == transcoder_texture_format::cTFPVRTC1_4_RGBA) && (!basis_file_has_alpha_slices))12740{12741// Switch to PVRTC1 RGB if the input doesn't have alpha.12742fmt = transcoder_texture_format::cTFPVRTC1_4_RGB;12743}1274412745if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)12746{12747if (pSlice_descs[slice_index].m_flags & cSliceDescFlagsHasAlpha)12748{12749BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file has out of order alpha slice\n");1275012751// The first slice shouldn't have alpha data in a properly formed basis file12752return false;12753}1275412755if (basis_file_has_alpha_slices)12756{12757// The alpha data should immediately follow the color data, and have the same resolution.12758if ((slice_index + 1U) >= pHeader->m_total_slices)12759{12760BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file has missing alpha slice\n");12761// basis file is missing the alpha slice12762return false;12763}1276412765// Basic sanity checks12766if ((pSlice_descs[slice_index + 1].m_flags & cSliceDescFlagsHasAlpha) == 0)12767{12768BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file has missing alpha slice (flag check)\n");12769// This slice should have alpha data12770return false;12771}1277212773if ((pSlice_descs[slice_index].m_num_blocks_x != pSlice_descs[slice_index + 1].m_num_blocks_x) || (pSlice_descs[slice_index].m_num_blocks_y != pSlice_descs[slice_index + 1].m_num_blocks_y))12774{12775BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file slice dimensions bad\n");12776// Alpha slice should have been the same res as the color slice12777return false;12778}12779}12780}1278112782bool status = false;1278312784if ((pHeader->m_tex_format == (int)basis_tex_format::cETC1S) || (pHeader->m_tex_format == (int)basis_tex_format::cUASTC4x4))12785{12786// Only do this on 4x4 LDR formats that supports transcoding to PVRTC1.12787const uint32_t total_slice_blocks = pSlice_descs[slice_index].m_num_blocks_x * pSlice_descs[slice_index].m_num_blocks_y;1278812789if (((fmt == transcoder_texture_format::cTFPVRTC1_4_RGB) || (fmt == transcoder_texture_format::cTFPVRTC1_4_RGBA)) && (output_blocks_buf_size_in_blocks_or_pixels > total_slice_blocks))12790{12791// The transcoder doesn't write beyond total_slice_blocks, so we need to clear the rest ourselves.12792// For GL usage, PVRTC1 4bpp image size is (max(width, 8)* max(height, 8) * 4 + 7) / 8.12793// However, for KTX and internally in Basis this formula isn't used, it's just ((width+3)/4) * ((height+3)/4) * bytes_per_block_or_pixel. This is all the transcoder actually writes to memory.12794memset(static_cast<uint8_t*>(pOutput_blocks) + total_slice_blocks * bytes_per_block_or_pixel, 0, (output_blocks_buf_size_in_blocks_or_pixels - total_slice_blocks) * bytes_per_block_or_pixel);12795}12796}1279712798if (pHeader->m_tex_format == (int)basis_tex_format::cASTC_HDR_6x6)12799{12800const basis_slice_desc* pSlice_desc = &pSlice_descs[slice_index];1280112802// Use the container independent image transcode method.12803status = m_lowlevel_astc_6x6_hdr_decoder.transcode_image(fmt,12804pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels,12805(const uint8_t*)pData, data_size, pSlice_desc->m_num_blocks_x, pSlice_desc->m_num_blocks_y, pSlice_desc->m_orig_width, pSlice_desc->m_orig_height, pSlice_desc->m_level_index,12806pSlice_desc->m_file_ofs, pSlice_desc->m_file_size,12807decode_flags, basis_file_has_alpha_slices, pHeader->m_tex_type == cBASISTexTypeVideoFrames, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);12808}12809else if (pHeader->m_tex_format == (int)basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE)12810{12811const basis_slice_desc* pSlice_desc = &pSlice_descs[slice_index];1281212813// Use the container independent image transcode method.12814status = m_lowlevel_astc_6x6_hdr_intermediate_decoder.transcode_image(fmt,12815pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels,12816(const uint8_t*)pData, data_size, pSlice_desc->m_num_blocks_x, pSlice_desc->m_num_blocks_y, pSlice_desc->m_orig_width, pSlice_desc->m_orig_height, pSlice_desc->m_level_index,12817pSlice_desc->m_file_ofs, pSlice_desc->m_file_size,12818decode_flags, basis_file_has_alpha_slices, pHeader->m_tex_type == cBASISTexTypeVideoFrames, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);12819}12820else if (pHeader->m_tex_format == (int)basis_tex_format::cUASTC_HDR_4x4)12821{12822const basis_slice_desc* pSlice_desc = &pSlice_descs[slice_index];1282312824// Use the container independent image transcode method.12825status = m_lowlevel_uastc_4x4_hdr_decoder.transcode_image(fmt,12826pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels,12827(const uint8_t*)pData, data_size, pSlice_desc->m_num_blocks_x, pSlice_desc->m_num_blocks_y, pSlice_desc->m_orig_width, pSlice_desc->m_orig_height, pSlice_desc->m_level_index,12828pSlice_desc->m_file_ofs, pSlice_desc->m_file_size,12829decode_flags, basis_file_has_alpha_slices, pHeader->m_tex_type == cBASISTexTypeVideoFrames, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);12830}12831else if (pHeader->m_tex_format == (int)basis_tex_format::cUASTC4x4)12832{12833const basis_slice_desc* pSlice_desc = &pSlice_descs[slice_index];1283412835// Use the container independent image transcode method.12836status = m_lowlevel_uastc_decoder.transcode_image(fmt,12837pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels,12838(const uint8_t*)pData, data_size, pSlice_desc->m_num_blocks_x, pSlice_desc->m_num_blocks_y, pSlice_desc->m_orig_width, pSlice_desc->m_orig_height, pSlice_desc->m_level_index,12839pSlice_desc->m_file_ofs, pSlice_desc->m_file_size,12840decode_flags, basis_file_has_alpha_slices, pHeader->m_tex_type == cBASISTexTypeVideoFrames, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);12841}12842else12843{12844// ETC1S12845const basis_slice_desc* pSlice_desc = &pSlice_descs[slice_index];12846const basis_slice_desc* pAlpha_slice_desc = basis_file_has_alpha_slices ? &pSlice_descs[slice_index + 1] : nullptr;1284712848assert((pSlice_desc->m_flags & cSliceDescFlagsHasAlpha) == 0);1284912850if (pAlpha_slice_desc)12851{12852// Basic sanity checks12853assert((pAlpha_slice_desc->m_flags & cSliceDescFlagsHasAlpha) != 0);12854assert(pSlice_desc->m_num_blocks_x == pAlpha_slice_desc->m_num_blocks_x);12855assert(pSlice_desc->m_num_blocks_y == pAlpha_slice_desc->m_num_blocks_y);12856assert(pSlice_desc->m_level_index == pAlpha_slice_desc->m_level_index);12857}1285812859// Use the container independent image transcode method.12860status = m_lowlevel_etc1s_decoder.transcode_image(fmt,12861pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels,12862(const uint8_t *)pData, data_size, pSlice_desc->m_num_blocks_x, pSlice_desc->m_num_blocks_y, pSlice_desc->m_orig_width, pSlice_desc->m_orig_height, pSlice_desc->m_level_index,12863pSlice_desc->m_file_ofs, pSlice_desc->m_file_size,12864(pAlpha_slice_desc != nullptr) ? (uint32_t)pAlpha_slice_desc->m_file_ofs : 0U, (pAlpha_slice_desc != nullptr) ? (uint32_t)pAlpha_slice_desc->m_file_size : 0U,12865decode_flags, basis_file_has_alpha_slices, pHeader->m_tex_type == cBASISTexTypeVideoFrames, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);1286612867} // if (pHeader->m_tex_format == (int)basis_tex_format::cUASTC4x4)1286812869if (!status)12870{12871BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: Returning false\n");12872}12873else12874{12875//BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: Returning true\n");12876}1287712878return status;12879}1288012881uint32_t basis_get_bytes_per_block_or_pixel(transcoder_texture_format fmt)12882{12883switch (fmt)12884{12885case transcoder_texture_format::cTFETC1_RGB:12886case transcoder_texture_format::cTFBC1_RGB:12887case transcoder_texture_format::cTFBC4_R:12888case transcoder_texture_format::cTFPVRTC1_4_RGB:12889case transcoder_texture_format::cTFPVRTC1_4_RGBA:12890case transcoder_texture_format::cTFATC_RGB:12891case transcoder_texture_format::cTFPVRTC2_4_RGB:12892case transcoder_texture_format::cTFPVRTC2_4_RGBA:12893case transcoder_texture_format::cTFETC2_EAC_R11:12894return 8;12895case transcoder_texture_format::cTFBC7_RGBA:12896case transcoder_texture_format::cTFBC7_ALT:12897case transcoder_texture_format::cTFBC6H:12898case transcoder_texture_format::cTFETC2_RGBA:12899case transcoder_texture_format::cTFBC3_RGBA:12900case transcoder_texture_format::cTFBC5_RG:12901case transcoder_texture_format::cTFASTC_4x4_RGBA:12902case transcoder_texture_format::cTFASTC_HDR_4x4_RGBA:12903case transcoder_texture_format::cTFASTC_HDR_6x6_RGBA:12904case transcoder_texture_format::cTFATC_RGBA:12905case transcoder_texture_format::cTFFXT1_RGB:12906case transcoder_texture_format::cTFETC2_EAC_RG11:12907return 16;12908case transcoder_texture_format::cTFRGBA32:12909case transcoder_texture_format::cTFRGB_9E5:12910return sizeof(uint32_t);12911case transcoder_texture_format::cTFRGB565:12912case transcoder_texture_format::cTFBGR565:12913case transcoder_texture_format::cTFRGBA4444:12914return sizeof(uint16_t);12915case transcoder_texture_format::cTFRGB_HALF:12916return sizeof(half_float) * 3;12917case transcoder_texture_format::cTFRGBA_HALF:12918return sizeof(half_float) * 4;12919default:12920assert(0);12921BASISU_DEVEL_ERROR("basis_get_basisu_texture_format: Invalid fmt\n");12922break;12923}12924return 0;12925}1292612927const char* basis_get_format_name(transcoder_texture_format fmt)12928{12929switch (fmt)12930{12931case transcoder_texture_format::cTFETC1_RGB: return "ETC1_RGB";12932case transcoder_texture_format::cTFBC1_RGB: return "BC1_RGB";12933case transcoder_texture_format::cTFBC4_R: return "BC4_R";12934case transcoder_texture_format::cTFPVRTC1_4_RGB: return "PVRTC1_4_RGB";12935case transcoder_texture_format::cTFPVRTC1_4_RGBA: return "PVRTC1_4_RGBA";12936case transcoder_texture_format::cTFBC7_RGBA: return "BC7_RGBA";12937case transcoder_texture_format::cTFBC7_ALT: return "BC7_RGBA";12938case transcoder_texture_format::cTFETC2_RGBA: return "ETC2_RGBA";12939case transcoder_texture_format::cTFBC3_RGBA: return "BC3_RGBA";12940case transcoder_texture_format::cTFBC5_RG: return "BC5_RG";12941case transcoder_texture_format::cTFASTC_4x4_RGBA: return "ASTC_RGBA";12942case transcoder_texture_format::cTFASTC_HDR_4x4_RGBA: return "ASTC_HDR_4X4_RGBA";12943case transcoder_texture_format::cTFASTC_HDR_6x6_RGBA: return "ASTC_HDR_6X6_RGBA";12944case transcoder_texture_format::cTFATC_RGB: return "ATC_RGB";12945case transcoder_texture_format::cTFATC_RGBA: return "ATC_RGBA";12946case transcoder_texture_format::cTFRGBA32: return "RGBA32";12947case transcoder_texture_format::cTFRGB565: return "RGB565";12948case transcoder_texture_format::cTFBGR565: return "BGR565";12949case transcoder_texture_format::cTFRGBA4444: return "RGBA4444";12950case transcoder_texture_format::cTFRGBA_HALF: return "RGBA_HALF";12951case transcoder_texture_format::cTFRGB_9E5: return "RGB_9E5";12952case transcoder_texture_format::cTFRGB_HALF: return "RGB_HALF";12953case transcoder_texture_format::cTFFXT1_RGB: return "FXT1_RGB";12954case transcoder_texture_format::cTFPVRTC2_4_RGB: return "PVRTC2_4_RGB";12955case transcoder_texture_format::cTFPVRTC2_4_RGBA: return "PVRTC2_4_RGBA";12956case transcoder_texture_format::cTFETC2_EAC_R11: return "ETC2_EAC_R11";12957case transcoder_texture_format::cTFETC2_EAC_RG11: return "ETC2_EAC_RG11";12958case transcoder_texture_format::cTFBC6H: return "BC6H";12959default:12960assert(0);12961BASISU_DEVEL_ERROR("basis_get_basisu_texture_format: Invalid fmt\n");12962break;12963}12964return "";12965}1296612967const char* basis_get_block_format_name(block_format fmt)12968{12969switch (fmt)12970{12971case block_format::cETC1: return "ETC1";12972case block_format::cBC1: return "BC1";12973case block_format::cPVRTC1_4_RGB: return "PVRTC1_4_RGB";12974case block_format::cPVRTC1_4_RGBA: return "PVRTC1_4_RGBA";12975case block_format::cBC7: return "BC7";12976case block_format::cETC2_RGBA: return "ETC2_RGBA";12977case block_format::cBC3: return "BC3";12978case block_format::cASTC_4x4: return "ASTC_4x4";12979case block_format::cATC_RGB: return "ATC_RGB";12980case block_format::cRGBA32: return "RGBA32";12981case block_format::cRGB565: return "RGB565";12982case block_format::cBGR565: return "BGR565";12983case block_format::cRGBA4444: return "RGBA4444";12984case block_format::cRGBA_HALF: return "RGBA_HALF";12985case block_format::cRGB_HALF: return "RGB_HALF";12986case block_format::cRGB_9E5: return "RGB_9E5";12987case block_format::cUASTC_4x4: return "UASTC_4x4";12988case block_format::cUASTC_HDR_4x4: return "UASTC_HDR_4x4";12989case block_format::cBC6H: return "BC6H";12990case block_format::cASTC_HDR_4x4: return "ASTC_HDR_4x4";12991case block_format::cASTC_HDR_6x6: return "ASTC_HDR_6x6";12992case block_format::cFXT1_RGB: return "FXT1_RGB";12993case block_format::cPVRTC2_4_RGB: return "PVRTC2_4_RGB";12994case block_format::cPVRTC2_4_RGBA: return "PVRTC2_4_RGBA";12995case block_format::cETC2_EAC_R11: return "ETC2_EAC_R11";12996case block_format::cETC2_EAC_RG11: return "ETC2_EAC_RG11";12997default:12998assert(0);12999BASISU_DEVEL_ERROR("basis_get_basisu_texture_format: Invalid fmt\n");13000break;13001}13002return "";13003}1300413005const char* basis_get_texture_type_name(basis_texture_type tex_type)13006{13007switch (tex_type)13008{13009case cBASISTexType2D: return "2D";13010case cBASISTexType2DArray: return "2D array";13011case cBASISTexTypeCubemapArray: return "cubemap array";13012case cBASISTexTypeVideoFrames: return "video";13013case cBASISTexTypeVolume: return "3D";13014default:13015assert(0);13016BASISU_DEVEL_ERROR("basis_get_texture_type_name: Invalid tex_type\n");13017break;13018}13019return "";13020}1302113022bool basis_transcoder_format_has_alpha(transcoder_texture_format fmt)13023{13024// TODO: Technically ASTC HDR does support alpha, but our ASTC HDR encoders don't yet support it. Unsure what to do here.13025switch (fmt)13026{13027case transcoder_texture_format::cTFETC2_RGBA:13028case transcoder_texture_format::cTFBC3_RGBA:13029case transcoder_texture_format::cTFASTC_4x4_RGBA:13030case transcoder_texture_format::cTFASTC_HDR_4x4_RGBA: // technically this ASTC HDR format supports alpha, but we currently don't exploit that in our encoders13031case transcoder_texture_format::cTFASTC_HDR_6x6_RGBA: // technically this ASTC HDR format supports alpha, but we currently don't exploit that in our encoders13032case transcoder_texture_format::cTFBC7_RGBA:13033case transcoder_texture_format::cTFBC7_ALT:13034case transcoder_texture_format::cTFPVRTC1_4_RGBA:13035case transcoder_texture_format::cTFPVRTC2_4_RGBA:13036case transcoder_texture_format::cTFATC_RGBA:13037case transcoder_texture_format::cTFRGBA32:13038case transcoder_texture_format::cTFRGBA4444:13039case transcoder_texture_format::cTFRGBA_HALF:13040return true;13041default:13042break;13043}13044return false;13045}1304613047bool basis_transcoder_format_is_hdr(transcoder_texture_format fmt)13048{13049switch (fmt)13050{13051case transcoder_texture_format::cTFASTC_HDR_4x4_RGBA:13052case transcoder_texture_format::cTFASTC_HDR_6x6_RGBA:13053case transcoder_texture_format::cTFBC6H:13054case transcoder_texture_format::cTFRGBA_HALF:13055case transcoder_texture_format::cTFRGB_HALF:13056case transcoder_texture_format::cTFRGB_9E5:13057return true;13058default:13059break;13060}13061return false;13062}1306313064basisu::texture_format basis_get_basisu_texture_format(transcoder_texture_format fmt)13065{13066switch (fmt)13067{13068case transcoder_texture_format::cTFETC1_RGB: return basisu::texture_format::cETC1;13069case transcoder_texture_format::cTFBC1_RGB: return basisu::texture_format::cBC1;13070case transcoder_texture_format::cTFBC4_R: return basisu::texture_format::cBC4;13071case transcoder_texture_format::cTFPVRTC1_4_RGB: return basisu::texture_format::cPVRTC1_4_RGB;13072case transcoder_texture_format::cTFPVRTC1_4_RGBA: return basisu::texture_format::cPVRTC1_4_RGBA;13073case transcoder_texture_format::cTFBC7_RGBA: return basisu::texture_format::cBC7;13074case transcoder_texture_format::cTFBC7_ALT: return basisu::texture_format::cBC7;13075case transcoder_texture_format::cTFETC2_RGBA: return basisu::texture_format::cETC2_RGBA;13076case transcoder_texture_format::cTFBC3_RGBA: return basisu::texture_format::cBC3;13077case transcoder_texture_format::cTFBC5_RG: return basisu::texture_format::cBC5;13078case transcoder_texture_format::cTFASTC_4x4_RGBA: return basisu::texture_format::cASTC_LDR_4x4;13079case transcoder_texture_format::cTFASTC_HDR_4x4_RGBA: return basisu::texture_format::cASTC_HDR_4x4;13080case transcoder_texture_format::cTFASTC_HDR_6x6_RGBA: return basisu::texture_format::cASTC_HDR_6x6;13081case transcoder_texture_format::cTFBC6H: return basisu::texture_format::cBC6HUnsigned;13082case transcoder_texture_format::cTFATC_RGB: return basisu::texture_format::cATC_RGB;13083case transcoder_texture_format::cTFATC_RGBA: return basisu::texture_format::cATC_RGBA_INTERPOLATED_ALPHA;13084case transcoder_texture_format::cTFRGBA32: return basisu::texture_format::cRGBA32;13085case transcoder_texture_format::cTFRGB565: return basisu::texture_format::cRGB565;13086case transcoder_texture_format::cTFBGR565: return basisu::texture_format::cBGR565;13087case transcoder_texture_format::cTFRGBA4444: return basisu::texture_format::cRGBA4444;13088case transcoder_texture_format::cTFRGBA_HALF: return basisu::texture_format::cRGBA_HALF;13089case transcoder_texture_format::cTFRGB_9E5: return basisu::texture_format::cRGB_9E5;13090case transcoder_texture_format::cTFRGB_HALF: return basisu::texture_format::cRGB_HALF;13091case transcoder_texture_format::cTFFXT1_RGB: return basisu::texture_format::cFXT1_RGB;13092case transcoder_texture_format::cTFPVRTC2_4_RGB: return basisu::texture_format::cPVRTC2_4_RGBA;13093case transcoder_texture_format::cTFPVRTC2_4_RGBA: return basisu::texture_format::cPVRTC2_4_RGBA;13094case transcoder_texture_format::cTFETC2_EAC_R11: return basisu::texture_format::cETC2_R11_EAC;13095case transcoder_texture_format::cTFETC2_EAC_RG11: return basisu::texture_format::cETC2_RG11_EAC;13096default:13097assert(0);13098BASISU_DEVEL_ERROR("basis_get_basisu_texture_format: Invalid fmt\n");13099break;13100}13101return basisu::texture_format::cInvalidTextureFormat;13102}1310313104bool basis_transcoder_format_is_uncompressed(transcoder_texture_format tex_type)13105{13106switch (tex_type)13107{13108case transcoder_texture_format::cTFRGBA32:13109case transcoder_texture_format::cTFRGB565:13110case transcoder_texture_format::cTFBGR565:13111case transcoder_texture_format::cTFRGBA4444:13112case transcoder_texture_format::cTFRGB_HALF:13113case transcoder_texture_format::cTFRGBA_HALF:13114case transcoder_texture_format::cTFRGB_9E5:13115return true;13116default:13117break;13118}13119return false;13120}1312113122bool basis_block_format_is_uncompressed(block_format blk_fmt)13123{13124switch (blk_fmt)13125{13126case block_format::cRGB32:13127case block_format::cRGBA32:13128case block_format::cA32:13129case block_format::cRGB565:13130case block_format::cBGR565:13131case block_format::cRGBA4444:13132case block_format::cRGBA4444_COLOR:13133case block_format::cRGBA4444_ALPHA:13134case block_format::cRGBA4444_COLOR_OPAQUE:13135case block_format::cRGBA_HALF:13136case block_format::cRGB_HALF:13137case block_format::cRGB_9E5:13138return true;13139default:13140break;13141}13142return false;13143}1314413145uint32_t basis_get_uncompressed_bytes_per_pixel(transcoder_texture_format fmt)13146{13147switch (fmt)13148{13149case transcoder_texture_format::cTFRGBA32:13150case transcoder_texture_format::cTFRGB_9E5:13151return sizeof(uint32_t);13152case transcoder_texture_format::cTFRGB565:13153case transcoder_texture_format::cTFBGR565:13154case transcoder_texture_format::cTFRGBA4444:13155return sizeof(uint16_t);13156case transcoder_texture_format::cTFRGB_HALF:13157return sizeof(half_float) * 3;13158case transcoder_texture_format::cTFRGBA_HALF:13159return sizeof(half_float) * 4;13160default:13161break;13162}13163return 0;13164}1316513166uint32_t basis_get_block_width(transcoder_texture_format tex_type)13167{13168switch (tex_type)13169{13170case transcoder_texture_format::cTFFXT1_RGB:13171return 8;13172case transcoder_texture_format::cTFASTC_HDR_6x6_RGBA:13173return 6;13174default:13175break;13176}13177return 4;13178}1317913180uint32_t basis_get_block_height(transcoder_texture_format tex_type)13181{13182switch (tex_type)13183{13184case transcoder_texture_format::cTFASTC_HDR_6x6_RGBA:13185return 6;13186default:13187break;13188}13189return 4;13190}1319113192uint32_t basis_tex_format_get_block_width(basis_tex_format fmt)13193{13194switch (fmt)13195{13196case basis_tex_format::cASTC_HDR_6x6:13197case basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE:13198return 6;13199default:13200break;13201}13202return 4;13203}1320413205uint32_t basis_tex_format_get_block_height(basis_tex_format fmt)13206{13207switch (fmt)13208{13209case basis_tex_format::cASTC_HDR_6x6:13210case basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE:13211return 6;13212default:13213break;13214}13215return 4;13216}1321713218bool basis_tex_format_is_hdr(basis_tex_format fmt)13219{13220switch (fmt)13221{13222case basis_tex_format::cUASTC_HDR_4x4:13223case basis_tex_format::cASTC_HDR_6x6:13224case basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE:13225return true;13226default:13227break;13228}13229return false;13230}1323113232bool basis_is_format_supported(transcoder_texture_format tex_type, basis_tex_format fmt)13233{13234if ((fmt == basis_tex_format::cASTC_HDR_6x6) || (fmt == basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE))13235{13236// RDO UASTC HDR 6x6, or our custom intermediate format13237#if BASISD_SUPPORT_UASTC_HDR13238switch (tex_type)13239{13240case transcoder_texture_format::cTFASTC_HDR_6x6_RGBA:13241case transcoder_texture_format::cTFBC6H:13242case transcoder_texture_format::cTFRGBA_HALF:13243case transcoder_texture_format::cTFRGB_HALF:13244case transcoder_texture_format::cTFRGB_9E5:13245return true;13246default:13247break;13248}13249#endif13250}13251else if (fmt == basis_tex_format::cUASTC_HDR_4x4)13252{13253// UASTC HDR 4x413254#if BASISD_SUPPORT_UASTC_HDR13255switch (tex_type)13256{13257case transcoder_texture_format::cTFASTC_HDR_4x4_RGBA:13258case transcoder_texture_format::cTFBC6H:13259case transcoder_texture_format::cTFRGBA_HALF:13260case transcoder_texture_format::cTFRGB_HALF:13261case transcoder_texture_format::cTFRGB_9E5:13262return true;13263default:13264break;13265}13266#endif13267}13268else if (fmt == basis_tex_format::cUASTC4x4)13269{13270// UASTC LDR 4x413271#if BASISD_SUPPORT_UASTC13272switch (tex_type)13273{13274// These niche formats aren't currently supported for UASTC - everything else is.13275case transcoder_texture_format::cTFPVRTC2_4_RGB:13276case transcoder_texture_format::cTFPVRTC2_4_RGBA:13277case transcoder_texture_format::cTFATC_RGB:13278case transcoder_texture_format::cTFATC_RGBA:13279case transcoder_texture_format::cTFFXT1_RGB:13280// UASTC LDR doesn't support transcoding to HDR formats13281case transcoder_texture_format::cTFASTC_HDR_4x4_RGBA:13282case transcoder_texture_format::cTFASTC_HDR_6x6_RGBA:13283case transcoder_texture_format::cTFBC6H:13284case transcoder_texture_format::cTFRGBA_HALF:13285case transcoder_texture_format::cTFRGB_HALF:13286case transcoder_texture_format::cTFRGB_9E5:13287return false;13288default:13289return true;13290}13291#endif13292}13293else13294{13295// ETC1S13296switch (tex_type)13297{13298// ETC1 and uncompressed are always supported.13299case transcoder_texture_format::cTFETC1_RGB:13300case transcoder_texture_format::cTFRGBA32:13301case transcoder_texture_format::cTFRGB565:13302case transcoder_texture_format::cTFBGR565:13303case transcoder_texture_format::cTFRGBA4444:13304return true;13305#if BASISD_SUPPORT_DXT113306case transcoder_texture_format::cTFBC1_RGB:13307return true;13308#endif13309#if BASISD_SUPPORT_DXT5A13310case transcoder_texture_format::cTFBC4_R:13311case transcoder_texture_format::cTFBC5_RG:13312return true;13313#endif13314#if BASISD_SUPPORT_DXT1 && BASISD_SUPPORT_DXT5A13315case transcoder_texture_format::cTFBC3_RGBA:13316return true;13317#endif13318#if BASISD_SUPPORT_PVRTC113319case transcoder_texture_format::cTFPVRTC1_4_RGB:13320case transcoder_texture_format::cTFPVRTC1_4_RGBA:13321return true;13322#endif13323#if BASISD_SUPPORT_BC7_MODE513324case transcoder_texture_format::cTFBC7_RGBA:13325case transcoder_texture_format::cTFBC7_ALT:13326return true;13327#endif13328#if BASISD_SUPPORT_ETC2_EAC_A813329case transcoder_texture_format::cTFETC2_RGBA:13330return true;13331#endif13332#if BASISD_SUPPORT_ASTC13333case transcoder_texture_format::cTFASTC_4x4_RGBA:13334return true;13335#endif13336#if BASISD_SUPPORT_ATC13337case transcoder_texture_format::cTFATC_RGB:13338case transcoder_texture_format::cTFATC_RGBA:13339return true;13340#endif13341#if BASISD_SUPPORT_FXT113342case transcoder_texture_format::cTFFXT1_RGB:13343return true;13344#endif13345#if BASISD_SUPPORT_PVRTC213346case transcoder_texture_format::cTFPVRTC2_4_RGB:13347case transcoder_texture_format::cTFPVRTC2_4_RGBA:13348return true;13349#endif13350#if BASISD_SUPPORT_ETC2_EAC_RG1113351case transcoder_texture_format::cTFETC2_EAC_R11:13352case transcoder_texture_format::cTFETC2_EAC_RG11:13353return true;13354#endif13355default:13356break;13357}13358}1335913360return false;13361}1336213363// ------------------------------------------------------------------------------------------------------13364// UASTC LDR 4x413365// ------------------------------------------------------------------------------------------------------1336613367#if BASISD_SUPPORT_UASTC13368const astc_bc7_common_partition2_desc g_astc_bc7_common_partitions2[TOTAL_ASTC_BC7_COMMON_PARTITIONS2] =13369{13370{ 0, 28, false }, { 1, 20, false }, { 2, 16, true }, { 3, 29, false },13371{ 4, 91, true }, { 5, 9, false }, { 6, 107, true }, { 7, 72, true },13372{ 8, 149, false }, { 9, 204, true }, { 10, 50, false }, { 11, 114, true },13373{ 12, 496, true }, { 13, 17, true }, { 14, 78, false }, { 15, 39, true },13374{ 17, 252, true }, { 18, 828, true }, { 19, 43, false }, { 20, 156, false },13375{ 21, 116, false }, { 22, 210, true }, { 23, 476, true }, { 24, 273, false },13376{ 25, 684, true }, { 26, 359, false }, { 29, 246, true }, { 32, 195, true },13377{ 33, 694, true }, { 52, 524, true }13378};1337913380const bc73_astc2_common_partition_desc g_bc7_3_astc2_common_partitions[TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS] =13381{13382{ 10, 36, 4 }, { 11, 48, 4 }, { 0, 61, 3 }, { 2, 137, 4 },13383{ 8, 161, 5 }, { 13, 183, 4 }, { 1, 226, 2 }, { 33, 281, 2 },13384{ 40, 302, 3 }, { 20, 307, 4 }, { 21, 479, 0 }, { 58, 495, 3 },13385{ 3, 593, 0 }, { 32, 594, 2 }, { 59, 605, 1 }, { 34, 799, 3 },13386{ 20, 812, 1 }, { 14, 988, 4 }, { 31, 993, 3 }13387};1338813389const astc_bc7_common_partition3_desc g_astc_bc7_common_partitions3[TOTAL_ASTC_BC7_COMMON_PARTITIONS3] =13390{13391{ 4, 260, 0 }, { 8, 74, 5 }, { 9, 32, 5 }, { 10, 156, 2 },13392{ 11, 183, 2 }, { 12, 15, 0 }, { 13, 745, 4 }, { 20, 0, 1 },13393{ 35, 335, 1 }, { 36, 902, 5 }, { 57, 254, 0 }13394};1339513396const uint8_t g_astc_to_bc7_partition_index_perm_tables[6][3] = { { 0, 1, 2 }, { 1, 2, 0 }, { 2, 0, 1 }, { 2, 1, 0 }, { 0, 2, 1 }, { 1, 0, 2 } };1339713398const uint8_t g_bc7_to_astc_partition_index_perm_tables[6][3] = { { 0, 1, 2 }, { 2, 0, 1 }, { 1, 2, 0 }, { 2, 1, 0 }, { 0, 2, 1 }, { 1, 0, 2 } };1339913400uint32_t bc7_convert_partition_index_3_to_2(uint32_t p, uint32_t k)13401{13402assert(k < 6);13403switch (k >> 1)13404{13405case 0:13406if (p <= 1)13407p = 0;13408else13409p = 1;13410break;13411case 1:13412if (p == 0)13413p = 0;13414else13415p = 1;13416break;13417case 2:13418if ((p == 0) || (p == 2))13419p = 0;13420else13421p = 1;13422break;13423}13424if (k & 1)13425p = 1 - p;13426return p;13427}1342813429static const uint8_t g_zero_pattern[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };1343013431const uint8_t g_astc_bc7_patterns2[TOTAL_ASTC_BC7_COMMON_PARTITIONS2][16] =13432{13433{ 0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1 }, { 0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1 }, { 1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0 }, { 0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1 },13434{ 1,1,1,1,1,1,1,0,1,1,1,0,1,1,0,0 }, { 0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1 }, { 1,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0 }, { 1,1,1,1,1,1,1,0,1,1,0,0,1,0,0,0 },13435{ 0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1 }, { 1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1 }, { 1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0 },13436{ 1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0 }, { 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1 }, { 1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0 },13437{ 1,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1 }, { 1,1,1,1,1,1,1,1,0,1,1,1,0,0,0,1 }, { 0,1,1,1,0,0,1,1,0,0,0,1,0,0,0,0 }, { 0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 },13438{ 0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0 }, { 1,1,1,1,1,1,1,1,0,1,1,1,0,0,1,1 }, { 1,0,0,0,1,1,0,0,1,1,0,0,1,1,1,0 }, { 0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0 },13439{ 1,1,1,1,0,1,1,1,0,1,1,1,0,0,1,1 }, { 0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0 }, { 1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1 }, { 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0 },13440{ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0 }, { 1,0,0,1,0,0,1,1,0,1,1,0,1,1,0,0 }13441};1344213443const uint8_t g_astc_bc7_patterns3[TOTAL_ASTC_BC7_COMMON_PARTITIONS3][16] =13444{13445{ 0,0,0,0,0,0,0,0,1,1,2,2,1,1,2,2 }, { 1,1,1,1,1,1,1,1,0,0,0,0,2,2,2,2 }, { 1,1,1,1,0,0,0,0,0,0,0,0,2,2,2,2 }, { 1,1,1,1,2,2,2,2,0,0,0,0,0,0,0,0 },13446{ 1,1,2,0,1,1,2,0,1,1,2,0,1,1,2,0 }, { 0,1,1,2,0,1,1,2,0,1,1,2,0,1,1,2 }, { 0,2,1,1,0,2,1,1,0,2,1,1,0,2,1,1 }, { 2,0,0,0,2,0,0,0,2,1,1,1,2,1,1,1 },13447{ 2,0,1,2,2,0,1,2,2,0,1,2,2,0,1,2 }, { 1,1,1,1,0,0,0,0,2,2,2,2,1,1,1,1 }, { 0,0,2,2,0,0,1,1,0,0,1,1,0,0,2,2 }13448};1344913450const uint8_t g_bc7_3_astc2_patterns2[TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS][16] =13451{13452{ 0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0 }, { 0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0 }, { 1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1 },13453{ 1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1 }, { 0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0 }, { 0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1 }, { 0,1,1,1,0,0,1,1,0,0,1,1,0,0,1,1 },13454{ 1,1,0,0,0,0,0,0,0,0,1,1,1,1,0,0 }, { 0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,0 }, { 1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0 },13455{ 0,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1 }, { 1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0 }, { 1,1,0,0,1,1,0,0,1,1,0,0,1,0,0,0 },13456{ 1,1,1,1,1,1,1,1,1,0,0,0,1,0,0,0 }, { 0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,0 }, { 1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0 }13457};1345813459const uint8_t g_astc_bc7_pattern2_anchors[TOTAL_ASTC_BC7_COMMON_PARTITIONS2][3] =13460{13461{ 0, 2 }, { 0, 3 }, { 1, 0 }, { 0, 3 }, { 7, 0 }, { 0, 2 }, { 3, 0 }, { 7, 0 },13462{ 0, 11 }, { 2, 0 }, { 0, 7 }, { 11, 0 }, { 3, 0 }, { 8, 0 }, { 0, 4 }, { 12, 0 },13463{ 1, 0 }, { 8, 0 }, { 0, 1 }, { 0, 2 }, { 0, 4 }, { 8, 0 }, { 1, 0 }, { 0, 2 },13464{ 4, 0 }, { 0, 1 }, { 4, 0 }, { 1, 0 }, { 4, 0 }, { 1, 0 }13465};1346613467const uint8_t g_astc_bc7_pattern3_anchors[TOTAL_ASTC_BC7_COMMON_PARTITIONS3][3] =13468{13469{ 0, 8, 10 }, { 8, 0, 12 }, { 4, 0, 12 }, { 8, 0, 4 }, { 3, 0, 2 }, { 0, 1, 3 }, { 0, 2, 1 }, { 1, 9, 0 }, { 1, 2, 0 }, { 4, 0, 8 }, { 0, 6, 2 }13470};1347113472const uint8_t g_bc7_3_astc2_patterns2_anchors[TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS][3] =13473{13474{ 0, 4 }, { 0, 2 }, { 2, 0 }, { 0, 7 }, { 8, 0 }, { 0, 1 }, { 0, 3 }, { 0, 1 }, { 2, 0 }, { 0, 1 }, { 0, 8 }, { 2, 0 }, { 0, 1 }, { 0, 7 }, { 12, 0 }, { 2, 0 }, { 9, 0 }, { 0, 2 }, { 4, 0 }13475};1347613477const uint32_t g_uastc_mode_huff_codes[TOTAL_UASTC_MODES + 1][2] =13478{13479{ 0x1, 4 },13480{ 0x35, 6 },13481{ 0x1D, 5 },13482{ 0x3, 5 },1348313484{ 0x13, 5 },13485{ 0xB, 5 },13486{ 0x1B, 5 },13487{ 0x7, 5 },1348813489{ 0x17, 5 },13490{ 0xF, 5 },13491{ 0x2, 3 },13492{ 0x0, 2 },1349313494{ 0x6, 3 },13495{ 0x1F, 5 },13496{ 0xD, 5 },13497{ 0x5, 7 },1349813499{ 0x15, 6 },13500{ 0x25, 6 },13501{ 0x9, 4 },13502{ 0x45, 7 } // future expansion13503};1350413505// If g_uastc_mode_huff_codes[] changes this table must be updated!13506static const uint8_t g_uastc_huff_modes[128] =13507{1350811,0,10,3,11,15,12,7,11,18,10,5,11,14,12,9,11,0,10,4,11,16,12,8,11,18,10,6,11,2,12,13,11,0,10,3,11,17,12,7,11,18,10,5,11,14,12,9,11,0,10,4,11,1,12,8,11,18,10,6,11,2,12,13,11,0,10,3,11,1350919,12,7,11,18,10,5,11,14,12,9,11,0,10,4,11,16,12,8,11,18,10,6,11,2,12,13,11,0,10,3,11,17,12,7,11,18,10,5,11,14,12,9,11,0,10,4,11,1,12,8,11,18,10,6,11,2,12,1313510};1351113512const uint8_t g_uastc_mode_weight_bits[TOTAL_UASTC_MODES] = { 4, 2, 3, 2, 2, 3, 2, 2, 0, 2, 4, 2, 3, 1, 2, 4, 2, 2, 5 };13513const uint8_t g_uastc_mode_weight_ranges[TOTAL_UASTC_MODES] = { 8, 2, 5, 2, 2, 5, 2, 2, 0, 2, 8, 2, 5, 0, 2, 8, 2, 2, 11 };13514const uint8_t g_uastc_mode_endpoint_ranges[TOTAL_UASTC_MODES] = { 19, 20, 8, 7, 12, 20, 18, 12, 0, 8, 13, 13, 19, 20, 20, 20, 20, 20, 11 };13515const uint8_t g_uastc_mode_subsets[TOTAL_UASTC_MODES] = { 1, 1, 2, 3, 2, 1, 1, 2, 0, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1 };13516const uint8_t g_uastc_mode_planes[TOTAL_UASTC_MODES] = { 1, 1, 1, 1, 1, 1, 2, 1, 0, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1 };13517const uint8_t g_uastc_mode_comps[TOTAL_UASTC_MODES] = { 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 3 };13518const uint8_t g_uastc_mode_has_etc1_bias[TOTAL_UASTC_MODES] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 };13519const uint8_t g_uastc_mode_has_bc1_hint0[TOTAL_UASTC_MODES] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };13520const uint8_t g_uastc_mode_has_bc1_hint1[TOTAL_UASTC_MODES] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 };13521const uint8_t g_uastc_mode_cem[TOTAL_UASTC_MODES] = { 8, 8, 8, 8, 8, 8, 8, 8, 0, 12, 12, 12, 12, 12, 12, 4, 4, 4, 8 };13522const uint8_t g_uastc_mode_has_alpha[TOTAL_UASTC_MODES] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 };13523const uint8_t g_uastc_mode_is_la[TOTAL_UASTC_MODES] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0 };13524const uint8_t g_uastc_mode_total_hint_bits[TOTAL_UASTC_MODES] = { 15, 15, 15, 15, 15, 15, 15, 15, 0, 23, 17, 17, 17, 23, 23, 23, 23, 23, 15 };1352513526// bits, trits, quints13527const int g_astc_bise_range_table[TOTAL_ASTC_RANGES][3] =13528{13529{ 1, 0, 0 }, // 0-1 013530{ 0, 1, 0 }, // 0-2 113531{ 2, 0, 0 }, // 0-3 213532{ 0, 0, 1 }, // 0-4 31353313534{ 1, 1, 0 }, // 0-5 413535{ 3, 0, 0 }, // 0-7 513536{ 1, 0, 1 }, // 0-9 613537{ 2, 1, 0 }, // 0-11 71353813539{ 4, 0, 0 }, // 0-15 813540{ 2, 0, 1 }, // 0-19 913541{ 3, 1, 0 }, // 0-23 1013542{ 5, 0, 0 }, // 0-31 111354313544{ 3, 0, 1 }, // 0-39 1213545{ 4, 1, 0 }, // 0-47 1313546{ 6, 0, 0 }, // 0-63 1413547{ 4, 0, 1 }, // 0-79 151354813549{ 5, 1, 0 }, // 0-95 1613550{ 7, 0, 0 }, // 0-127 1713551{ 5, 0, 1 }, // 0-159 1813552{ 6, 1, 0 }, // 0-191 191355313554{ 8, 0, 0 }, // 0-255 2013555};1355613557int astc_get_levels(int range)13558{13559assert(range < (int)BC7ENC_TOTAL_ASTC_RANGES);13560return (1 + 2 * g_astc_bise_range_table[range][1] + 4 * g_astc_bise_range_table[range][2]) << g_astc_bise_range_table[range][0];13561}1356213563// g_astc_unquant[] is the inverse of g_astc_sorted_order_unquant[]13564astc_quant_bin g_astc_unquant[BC7ENC_TOTAL_ASTC_RANGES][256]; // [ASTC encoded endpoint index]1356513566// Taken right from the ASTC spec.13567static struct13568{13569const char* m_pB_str;13570uint32_t m_c;13571} g_astc_endpoint_unquant_params[BC7ENC_TOTAL_ASTC_RANGES] =13572{13573{ "", 0 },13574{ "", 0 },13575{ "", 0 },13576{ "", 0 },13577{ "000000000", 204, }, // 0-513578{ "", 0 },13579{ "000000000", 113, }, // 0-913580{ "b000b0bb0", 93 }, // 0-1113581{ "", 0 },13582{ "b0000bb00", 54 }, // 0-1913583{ "cb000cbcb", 44 }, // 0-2313584{ "", 0 },13585{ "cb0000cbc", 26 }, // 0-3913586{ "dcb000dcb", 22 }, // 0-4713587{ "", 0 },13588{ "dcb0000dc", 13 }, // 0-7913589{ "edcb000ed", 11 }, // 0-9513590{ "", 0 },13591{ "edcb0000e", 6 }, // 0-15913592{ "fedcb000f", 5 }, // 0-19113593{ "", 0 },13594};1359513596bool astc_is_valid_endpoint_range(uint32_t range)13597{13598if ((g_astc_bise_range_table[range][1] == 0) && (g_astc_bise_range_table[range][2] == 0))13599return true;1360013601return g_astc_endpoint_unquant_params[range].m_c != 0;13602}1360313604uint32_t unquant_astc_endpoint(uint32_t packed_bits, uint32_t packed_trits, uint32_t packed_quints, uint32_t range)13605{13606assert(range < BC7ENC_TOTAL_ASTC_RANGES);1360713608const uint32_t bits = g_astc_bise_range_table[range][0];13609const uint32_t trits = g_astc_bise_range_table[range][1];13610const uint32_t quints = g_astc_bise_range_table[range][2];1361113612uint32_t val = 0;13613if ((!trits) && (!quints))13614{13615assert(!packed_trits && !packed_quints);1361613617int bits_left = 8;13618while (bits_left > 0)13619{13620uint32_t v = packed_bits;1362113622int n = basisu::minimumi(bits_left, bits);13623if (n < (int)bits)13624v >>= (bits - n);1362513626assert(v < (1U << n));1362713628val |= (v << (bits_left - n));13629bits_left -= n;13630}13631}13632else13633{13634const uint32_t A = (packed_bits & 1) ? 511 : 0;13635const uint32_t C = g_astc_endpoint_unquant_params[range].m_c;13636const uint32_t D = trits ? packed_trits : packed_quints;1363713638assert(C);1363913640uint32_t B = 0;13641for (uint32_t i = 0; i < 9; i++)13642{13643B <<= 1;1364413645char c = g_astc_endpoint_unquant_params[range].m_pB_str[i];13646if (c != '0')13647{13648c -= 'a';13649B |= ((packed_bits >> c) & 1);13650}13651}1365213653val = D * C + B;13654val = val ^ A;13655val = (A & 0x80) | (val >> 2);13656}1365713658return val;13659}1366013661uint32_t unquant_astc_endpoint_val(uint32_t packed_val, uint32_t range)13662{13663assert(range < BC7ENC_TOTAL_ASTC_RANGES);13664assert(packed_val < (uint32_t)astc_get_levels(range));1366513666const uint32_t bits = g_astc_bise_range_table[range][0];13667const uint32_t trits = g_astc_bise_range_table[range][1];13668const uint32_t quints = g_astc_bise_range_table[range][2];1366913670if ((!trits) && (!quints))13671return unquant_astc_endpoint(packed_val, 0, 0, range);13672else if (trits)13673return unquant_astc_endpoint(packed_val & ((1 << bits) - 1), packed_val >> bits, 0, range);13674else13675return unquant_astc_endpoint(packed_val & ((1 << bits) - 1), 0, packed_val >> bits, range);13676}1367713678// BC7 - Various BC7 tables/helpers13679const uint32_t g_bc7_weights1[2] = { 0, 64 };13680const uint32_t g_bc7_weights2[4] = { 0, 21, 43, 64 };13681const uint32_t g_bc7_weights3[8] = { 0, 9, 18, 27, 37, 46, 55, 64 };13682const uint32_t g_bc7_weights4[16] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };13683const uint32_t g_astc_weights4[16] = { 0, 4, 8, 12, 17, 21, 25, 29, 35, 39, 43, 47, 52, 56, 60, 64 };13684const uint32_t g_astc_weights5[32] = { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64 };13685const uint32_t g_astc_weights_3levels[3] = { 0, 32, 64 };1368613687const uint8_t g_bc7_partition1[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };1368813689const uint8_t g_bc7_partition2[64 * 16] =13690{136910,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1, 0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1, 0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1, 0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1, 0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1, 0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,136920,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1, 0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1, 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,136930,0,0,0,1,0,0,0,1,1,1,0,1,1,1,1, 0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,0, 0,1,1,1,0,0,1,1,0,0,0,1,0,0,0,0, 0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0, 0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0, 0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,136940,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0, 0,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0, 0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0, 0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0, 0,0,0,1,0,1,1,1,1,1,1,0,1,0,0,0, 0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0, 0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,0, 0,0,1,1,1,0,0,1,1,0,0,1,1,1,0,0,136950,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1, 0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1, 0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0, 0,0,1,1,0,0,1,1,1,1,0,0,1,1,0,0, 0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0, 0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0, 0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1, 0,1,0,1,1,0,1,0,1,0,1,0,0,1,0,1,136960,1,1,1,0,0,1,1,1,1,0,0,1,1,1,0, 0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0, 0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0, 0,0,1,1,1,0,1,1,1,1,0,1,1,1,0,0, 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, 0,0,1,1,1,1,0,0,1,1,0,0,0,0,1,1, 0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1, 0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,136970,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0, 0,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0, 0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0, 0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,0, 0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1, 0,1,1,0,0,0,1,1,1,0,0,1,1,1,0,0, 0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,0,136980,1,1,0,1,1,0,0,1,1,0,0,1,0,0,1, 0,1,1,0,0,0,1,1,0,0,1,1,1,0,0,1, 0,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1, 0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,1, 0,0,0,0,1,1,1,1,0,0,1,1,0,0,1,1, 0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0, 0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0, 0,1,0,0,0,1,0,0,0,1,1,1,0,1,1,113699};1370013701const uint8_t g_bc7_partition3[64 * 16] =13702{137030,0,1,1,0,0,1,1,0,2,2,1,2,2,2,2, 0,0,0,1,0,0,1,1,2,2,1,1,2,2,2,1, 0,0,0,0,2,0,0,1,2,2,1,1,2,2,1,1, 0,2,2,2,0,0,2,2,0,0,1,1,0,1,1,1, 0,0,0,0,0,0,0,0,1,1,2,2,1,1,2,2, 0,0,1,1,0,0,1,1,0,0,2,2,0,0,2,2, 0,0,2,2,0,0,2,2,1,1,1,1,1,1,1,1, 0,0,1,1,0,0,1,1,2,2,1,1,2,2,1,1,137040,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2, 0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2, 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2, 0,0,1,2,0,0,1,2,0,0,1,2,0,0,1,2, 0,1,1,2,0,1,1,2,0,1,1,2,0,1,1,2, 0,1,2,2,0,1,2,2,0,1,2,2,0,1,2,2, 0,0,1,1,0,1,1,2,1,1,2,2,1,2,2,2, 0,0,1,1,2,0,0,1,2,2,0,0,2,2,2,0,137050,0,0,1,0,0,1,1,0,1,1,2,1,1,2,2, 0,1,1,1,0,0,1,1,2,0,0,1,2,2,0,0, 0,0,0,0,1,1,2,2,1,1,2,2,1,1,2,2, 0,0,2,2,0,0,2,2,0,0,2,2,1,1,1,1, 0,1,1,1,0,1,1,1,0,2,2,2,0,2,2,2, 0,0,0,1,0,0,0,1,2,2,2,1,2,2,2,1, 0,0,0,0,0,0,1,1,0,1,2,2,0,1,2,2, 0,0,0,0,1,1,0,0,2,2,1,0,2,2,1,0,137060,1,2,2,0,1,2,2,0,0,1,1,0,0,0,0, 0,0,1,2,0,0,1,2,1,1,2,2,2,2,2,2, 0,1,1,0,1,2,2,1,1,2,2,1,0,1,1,0, 0,0,0,0,0,1,1,0,1,2,2,1,1,2,2,1, 0,0,2,2,1,1,0,2,1,1,0,2,0,0,2,2, 0,1,1,0,0,1,1,0,2,0,0,2,2,2,2,2, 0,0,1,1,0,1,2,2,0,1,2,2,0,0,1,1, 0,0,0,0,2,0,0,0,2,2,1,1,2,2,2,1,137070,0,0,0,0,0,0,2,1,1,2,2,1,2,2,2, 0,2,2,2,0,0,2,2,0,0,1,2,0,0,1,1, 0,0,1,1,0,0,1,2,0,0,2,2,0,2,2,2, 0,1,2,0,0,1,2,0,0,1,2,0,0,1,2,0, 0,0,0,0,1,1,1,1,2,2,2,2,0,0,0,0, 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0, 0,1,2,0,2,0,1,2,1,2,0,1,0,1,2,0, 0,0,1,1,2,2,0,0,1,1,2,2,0,0,1,1,137080,0,1,1,1,1,2,2,2,2,0,0,0,0,1,1, 0,1,0,1,0,1,0,1,2,2,2,2,2,2,2,2, 0,0,0,0,0,0,0,0,2,1,2,1,2,1,2,1, 0,0,2,2,1,1,2,2,0,0,2,2,1,1,2,2, 0,0,2,2,0,0,1,1,0,0,2,2,0,0,1,1, 0,2,2,0,1,2,2,1,0,2,2,0,1,2,2,1, 0,1,0,1,2,2,2,2,2,2,2,2,0,1,0,1, 0,0,0,0,2,1,2,1,2,1,2,1,2,1,2,1,137090,1,0,1,0,1,0,1,0,1,0,1,2,2,2,2, 0,2,2,2,0,1,1,1,0,2,2,2,0,1,1,1, 0,0,0,2,1,1,1,2,0,0,0,2,1,1,1,2, 0,0,0,0,2,1,1,2,2,1,1,2,2,1,1,2, 0,2,2,2,0,1,1,1,0,1,1,1,0,2,2,2, 0,0,0,2,1,1,1,2,1,1,1,2,0,0,0,2, 0,1,1,0,0,1,1,0,0,1,1,0,2,2,2,2, 0,0,0,0,0,0,0,0,2,1,1,2,2,1,1,2,137100,1,1,0,0,1,1,0,2,2,2,2,2,2,2,2, 0,0,2,2,0,0,1,1,0,0,1,1,0,0,2,2, 0,0,2,2,1,1,2,2,1,1,2,2,0,0,2,2, 0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,2, 0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,1, 0,2,2,2,1,2,2,2,0,2,2,2,1,2,2,2, 0,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2, 0,1,1,1,2,0,1,1,2,2,0,1,2,2,2,0,13711};1371213713const uint8_t g_bc7_table_anchor_index_second_subset[64] = { 15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15, 15, 2, 8, 2, 2, 8, 8,15, 2, 8, 2, 2, 8, 8, 2, 2, 15,15, 6, 8, 2, 8,15,15, 2, 8, 2, 2, 2,15,15, 6, 6, 2, 6, 8,15,15, 2, 2, 15,15,15,15,15, 2, 2,15 };1371413715const uint8_t g_bc7_table_anchor_index_third_subset_1[64] =13716{137173, 3,15,15, 8, 3,15,15, 8, 8, 6, 6, 6, 5, 3, 3, 3, 3, 8,15, 3, 3, 6,10, 5, 8, 8, 6, 8, 5,15,15, 8,15, 3, 5, 6,10, 8,15, 15, 3,15, 5,15,15,15,15, 3,15, 5, 5, 5, 8, 5,10, 5,10, 8,13,15,12, 3, 313718};1371913720const uint8_t g_bc7_table_anchor_index_third_subset_2[64] =13721{1372215, 8, 8, 3,15,15, 3, 8, 15,15,15,15,15,15,15, 8, 15, 8,15, 3,15, 8,15, 8, 3,15, 6,10,15,15,10, 8, 15, 3,15,10,10, 8, 9,10, 6,15, 8,15, 3, 6, 6, 8, 15, 3,15,15,15,15,15,15, 15,15,15,15, 3,15,15, 813723};1372413725const uint8_t g_bc7_num_subsets[8] = { 3, 2, 3, 2, 1, 1, 1, 2 };13726const uint8_t g_bc7_partition_bits[8] = { 4, 6, 6, 6, 0, 0, 0, 6 };13727const uint8_t g_bc7_color_index_bitcount[8] = { 3, 3, 2, 2, 2, 2, 4, 2 };1372813729const uint8_t g_bc7_mode_has_p_bits[8] = { 1, 1, 0, 1, 0, 0, 1, 1 };13730const uint8_t g_bc7_mode_has_shared_p_bits[8] = { 0, 1, 0, 0, 0, 0, 0, 0 };13731const uint8_t g_bc7_color_precision_table[8] = { 4, 6, 5, 7, 5, 7, 7, 5 };13732const int8_t g_bc7_alpha_precision_table[8] = { 0, 0, 0, 0, 6, 8, 7, 5 };1373313734const uint8_t g_bc7_alpha_index_bitcount[8] = { 0, 0, 0, 0, 3, 2, 4, 2 };1373513736endpoint_err g_bc7_mode_6_optimal_endpoints[256][2]; // [c][pbit]13737endpoint_err g_bc7_mode_5_optimal_endpoints[256]; // [c]1373813739static inline void bc7_set_block_bits(uint8_t* pBytes, uint32_t val, uint32_t num_bits, uint32_t* pCur_ofs)13740{13741assert((num_bits <= 32) && (val < (1ULL << num_bits)));13742while (num_bits)13743{13744const uint32_t n = basisu::minimumu(8 - (*pCur_ofs & 7), num_bits);13745pBytes[*pCur_ofs >> 3] |= (uint8_t)(val << (*pCur_ofs & 7));13746val >>= n;13747num_bits -= n;13748*pCur_ofs += n;13749}13750assert(*pCur_ofs <= 128);13751}1375213753// TODO: Optimize this.13754void encode_bc7_block(void* pBlock, const bc7_optimization_results* pResults)13755{13756const uint32_t best_mode = pResults->m_mode;1375713758const uint32_t total_subsets = g_bc7_num_subsets[best_mode];13759const uint32_t total_partitions = 1 << g_bc7_partition_bits[best_mode];13760//const uint32_t num_rotations = 1 << g_bc7_rotation_bits[best_mode];13761//const uint32_t num_index_selectors = (best_mode == 4) ? 2 : 1;1376213763const uint8_t* pPartition;13764if (total_subsets == 1)13765pPartition = &g_bc7_partition1[0];13766else if (total_subsets == 2)13767pPartition = &g_bc7_partition2[pResults->m_partition * 16];13768else13769pPartition = &g_bc7_partition3[pResults->m_partition * 16];1377013771uint8_t color_selectors[16];13772memcpy(color_selectors, pResults->m_selectors, 16);1377313774uint8_t alpha_selectors[16];13775memcpy(alpha_selectors, pResults->m_alpha_selectors, 16);1377613777color_quad_u8 low[3], high[3];13778memcpy(low, pResults->m_low, sizeof(low));13779memcpy(high, pResults->m_high, sizeof(high));1378013781uint32_t pbits[3][2];13782memcpy(pbits, pResults->m_pbits, sizeof(pbits));1378313784int anchor[3] = { -1, -1, -1 };1378513786for (uint32_t k = 0; k < total_subsets; k++)13787{13788uint32_t anchor_index = 0;13789if (k)13790{13791if ((total_subsets == 3) && (k == 1))13792anchor_index = g_bc7_table_anchor_index_third_subset_1[pResults->m_partition];13793else if ((total_subsets == 3) && (k == 2))13794anchor_index = g_bc7_table_anchor_index_third_subset_2[pResults->m_partition];13795else13796anchor_index = g_bc7_table_anchor_index_second_subset[pResults->m_partition];13797}1379813799anchor[k] = anchor_index;1380013801const uint32_t color_index_bits = get_bc7_color_index_size(best_mode, pResults->m_index_selector);13802const uint32_t num_color_indices = 1 << color_index_bits;1380313804if (color_selectors[anchor_index] & (num_color_indices >> 1))13805{13806for (uint32_t i = 0; i < 16; i++)13807if (pPartition[i] == k)13808color_selectors[i] = (uint8_t)((num_color_indices - 1) - color_selectors[i]);1380913810if (get_bc7_mode_has_seperate_alpha_selectors(best_mode))13811{13812for (uint32_t q = 0; q < 3; q++)13813{13814uint8_t t = low[k].m_c[q];13815low[k].m_c[q] = high[k].m_c[q];13816high[k].m_c[q] = t;13817}13818}13819else13820{13821color_quad_u8 tmp = low[k];13822low[k] = high[k];13823high[k] = tmp;13824}1382513826if (!g_bc7_mode_has_shared_p_bits[best_mode])13827{13828uint32_t t = pbits[k][0];13829pbits[k][0] = pbits[k][1];13830pbits[k][1] = t;13831}13832}1383313834if (get_bc7_mode_has_seperate_alpha_selectors(best_mode))13835{13836const uint32_t alpha_index_bits = get_bc7_alpha_index_size(best_mode, pResults->m_index_selector);13837const uint32_t num_alpha_indices = 1 << alpha_index_bits;1383813839if (alpha_selectors[anchor_index] & (num_alpha_indices >> 1))13840{13841for (uint32_t i = 0; i < 16; i++)13842if (pPartition[i] == k)13843alpha_selectors[i] = (uint8_t)((num_alpha_indices - 1) - alpha_selectors[i]);1384413845uint8_t t = low[k].m_c[3];13846low[k].m_c[3] = high[k].m_c[3];13847high[k].m_c[3] = t;13848}13849}13850}1385113852uint8_t* pBlock_bytes = (uint8_t*)(pBlock);13853memset(pBlock_bytes, 0, BC7ENC_BLOCK_SIZE);1385413855uint32_t cur_bit_ofs = 0;13856bc7_set_block_bits(pBlock_bytes, 1 << best_mode, best_mode + 1, &cur_bit_ofs);1385713858if ((best_mode == 4) || (best_mode == 5))13859bc7_set_block_bits(pBlock_bytes, pResults->m_rotation, 2, &cur_bit_ofs);1386013861if (best_mode == 4)13862bc7_set_block_bits(pBlock_bytes, pResults->m_index_selector, 1, &cur_bit_ofs);1386313864if (total_partitions > 1)13865bc7_set_block_bits(pBlock_bytes, pResults->m_partition, (total_partitions == 64) ? 6 : 4, &cur_bit_ofs);1386613867const uint32_t total_comps = (best_mode >= 4) ? 4 : 3;13868for (uint32_t comp = 0; comp < total_comps; comp++)13869{13870for (uint32_t subset = 0; subset < total_subsets; subset++)13871{13872bc7_set_block_bits(pBlock_bytes, low[subset].m_c[comp], (comp == 3) ? g_bc7_alpha_precision_table[best_mode] : g_bc7_color_precision_table[best_mode], &cur_bit_ofs);13873bc7_set_block_bits(pBlock_bytes, high[subset].m_c[comp], (comp == 3) ? g_bc7_alpha_precision_table[best_mode] : g_bc7_color_precision_table[best_mode], &cur_bit_ofs);13874}13875}1387613877if (g_bc7_mode_has_p_bits[best_mode])13878{13879for (uint32_t subset = 0; subset < total_subsets; subset++)13880{13881bc7_set_block_bits(pBlock_bytes, pbits[subset][0], 1, &cur_bit_ofs);13882if (!g_bc7_mode_has_shared_p_bits[best_mode])13883bc7_set_block_bits(pBlock_bytes, pbits[subset][1], 1, &cur_bit_ofs);13884}13885}1388613887for (uint32_t y = 0; y < 4; y++)13888{13889for (uint32_t x = 0; x < 4; x++)13890{13891int idx = x + y * 4;1389213893uint32_t n = pResults->m_index_selector ? get_bc7_alpha_index_size(best_mode, pResults->m_index_selector) : get_bc7_color_index_size(best_mode, pResults->m_index_selector);1389413895if ((idx == anchor[0]) || (idx == anchor[1]) || (idx == anchor[2]))13896n--;1389713898bc7_set_block_bits(pBlock_bytes, pResults->m_index_selector ? alpha_selectors[idx] : color_selectors[idx], n, &cur_bit_ofs);13899}13900}1390113902if (get_bc7_mode_has_seperate_alpha_selectors(best_mode))13903{13904for (uint32_t y = 0; y < 4; y++)13905{13906for (uint32_t x = 0; x < 4; x++)13907{13908int idx = x + y * 4;1390913910uint32_t n = pResults->m_index_selector ? get_bc7_color_index_size(best_mode, pResults->m_index_selector) : get_bc7_alpha_index_size(best_mode, pResults->m_index_selector);1391113912if ((idx == anchor[0]) || (idx == anchor[1]) || (idx == anchor[2]))13913n--;1391413915bc7_set_block_bits(pBlock_bytes, pResults->m_index_selector ? color_selectors[idx] : alpha_selectors[idx], n, &cur_bit_ofs);13916}13917}13918}1391913920assert(cur_bit_ofs == 128);13921}1392213923// ASTC13924static inline void astc_set_bits_1_to_9(uint32_t* pDst, int& bit_offset, uint32_t code, uint32_t codesize)13925{13926uint8_t* pBuf = reinterpret_cast<uint8_t*>(pDst);1392713928assert(codesize <= 9);13929if (codesize)13930{13931uint32_t byte_bit_offset = bit_offset & 7;13932uint32_t val = code << byte_bit_offset;1393313934uint32_t index = bit_offset >> 3;13935pBuf[index] |= (uint8_t)val;1393613937if (codesize > (8 - byte_bit_offset))13938pBuf[index + 1] |= (uint8_t)(val >> 8);1393913940bit_offset += codesize;13941}13942}1394313944void pack_astc_solid_block(void* pDst_block, const color32& color)13945{13946uint32_t r = color[0], g = color[1], b = color[2];13947uint32_t a = color[3];1394813949uint32_t* pOutput = static_cast<uint32_t*>(pDst_block);13950uint8_t* pBytes = reinterpret_cast<uint8_t*>(pDst_block);1395113952pBytes[0] = 0xfc; pBytes[1] = 0xfd; pBytes[2] = 0xff; pBytes[3] = 0xff;1395313954pOutput[1] = 0xffffffff;13955pOutput[2] = 0;13956pOutput[3] = 0;1395713958int bit_pos = 64;13959astc_set_bits(reinterpret_cast<uint32_t*>(pDst_block), bit_pos, r | (r << 8), 16);13960astc_set_bits(reinterpret_cast<uint32_t*>(pDst_block), bit_pos, g | (g << 8), 16);13961astc_set_bits(reinterpret_cast<uint32_t*>(pDst_block), bit_pos, b | (b << 8), 16);13962astc_set_bits(reinterpret_cast<uint32_t*>(pDst_block), bit_pos, a | (a << 8), 16);13963}1396413965// See 23.21 https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#_partition_pattern_generation13966#ifdef _DEBUG13967static inline uint32_t astc_hash52(uint32_t v)13968{13969uint32_t p = v;13970p ^= p >> 15; p -= p << 17; p += p << 7; p += p << 4;13971p ^= p >> 5; p += p << 16; p ^= p >> 7; p ^= p >> 3;13972p ^= p << 6; p ^= p >> 17;13973return p;13974}1397513976int astc_compute_texel_partition(int seed, int x, int y, int z, int partitioncount, bool small_block)13977{13978if (small_block)13979{13980x <<= 1; y <<= 1; z <<= 1;13981}13982seed += (partitioncount - 1) * 1024;13983uint32_t rnum = astc_hash52(seed);13984uint8_t seed1 = rnum & 0xF;13985uint8_t seed2 = (rnum >> 4) & 0xF;13986uint8_t seed3 = (rnum >> 8) & 0xF;13987uint8_t seed4 = (rnum >> 12) & 0xF;13988uint8_t seed5 = (rnum >> 16) & 0xF;13989uint8_t seed6 = (rnum >> 20) & 0xF;13990uint8_t seed7 = (rnum >> 24) & 0xF;13991uint8_t seed8 = (rnum >> 28) & 0xF;13992uint8_t seed9 = (rnum >> 18) & 0xF;13993uint8_t seed10 = (rnum >> 22) & 0xF;13994uint8_t seed11 = (rnum >> 26) & 0xF;13995uint8_t seed12 = ((rnum >> 30) | (rnum << 2)) & 0xF;1399613997seed1 *= seed1; seed2 *= seed2;13998seed3 *= seed3; seed4 *= seed4;13999seed5 *= seed5; seed6 *= seed6;14000seed7 *= seed7; seed8 *= seed8;14001seed9 *= seed9; seed10 *= seed10;14002seed11 *= seed11; seed12 *= seed12;1400314004int sh1, sh2, sh3;14005if (seed & 1)14006{14007sh1 = (seed & 2 ? 4 : 5); sh2 = (partitioncount == 3 ? 6 : 5);14008}14009else14010{14011sh1 = (partitioncount == 3 ? 6 : 5); sh2 = (seed & 2 ? 4 : 5);14012}14013sh3 = (seed & 0x10) ? sh1 : sh2;1401414015seed1 >>= sh1; seed2 >>= sh2; seed3 >>= sh1; seed4 >>= sh2;14016seed5 >>= sh1; seed6 >>= sh2; seed7 >>= sh1; seed8 >>= sh2;14017seed9 >>= sh3; seed10 >>= sh3; seed11 >>= sh3; seed12 >>= sh3;1401814019int a = seed1 * x + seed2 * y + seed11 * z + (rnum >> 14);14020int b = seed3 * x + seed4 * y + seed12 * z + (rnum >> 10);14021int c = seed5 * x + seed6 * y + seed9 * z + (rnum >> 6);14022int d = seed7 * x + seed8 * y + seed10 * z + (rnum >> 2);1402314024a &= 0x3F; b &= 0x3F; c &= 0x3F; d &= 0x3F;1402514026if (partitioncount < 4) d = 0;14027if (partitioncount < 3) c = 0;1402814029if (a >= b && a >= c && a >= d)14030return 0;14031else if (b >= c && b >= d)14032return 1;14033else if (c >= d)14034return 2;14035else14036return 3;14037}14038#endif1403914040static const uint8_t g_astc_quint_encode[125] =14041{140420, 1, 2, 3, 4, 8, 9, 10, 11, 12, 16, 17, 18, 19, 20, 24, 25, 26, 27, 28, 5, 13, 21, 29, 6, 32, 33, 34, 35, 36, 40, 41, 42, 43, 44, 48, 49, 50, 51, 52, 56, 57,1404358, 59, 60, 37, 45, 53, 61, 14, 64, 65, 66, 67, 68, 72, 73, 74, 75, 76, 80, 81, 82, 83, 84, 88, 89, 90, 91, 92, 69, 77, 85, 93, 22, 96, 97, 98, 99, 100, 104,14044105, 106, 107, 108, 112, 113, 114, 115, 116, 120, 121, 122, 123, 124, 101, 109, 117, 125, 30, 102, 103, 70, 71, 38, 110, 111, 78, 79, 46, 118, 119, 86, 87, 54,14045126, 127, 94, 95, 62, 39, 47, 55, 63, 3114046};1404714048// Encodes 3 values to output, usable for any range that uses quints and bits14049static inline void astc_encode_quints(uint32_t* pOutput, const uint8_t* pValues, int& bit_pos, int n)14050{14051// First extract the quints and the bits from the 3 input values14052int quints = 0, bits[3];14053const uint32_t bit_mask = (1 << n) - 1;14054for (int i = 0; i < 3; i++)14055{14056static const int s_muls[3] = { 1, 5, 25 };1405714058const int t = pValues[i] >> n;1405914060quints += t * s_muls[i];14061bits[i] = pValues[i] & bit_mask;14062}1406314064// Encode the quints, by inverting the bit manipulations done by the decoder, converting 3 quints into 7-bits.14065// See https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#astc-integer-sequence-encoding1406614067assert(quints < 125);14068const int T = g_astc_quint_encode[quints];1406914070// Now interleave the 7 encoded quint bits with the bits to form the encoded output. See table 95-96.14071astc_set_bits(pOutput, bit_pos, bits[0] | (astc_extract_bits(T, 0, 2) << n) | (bits[1] << (3 + n)) | (astc_extract_bits(T, 3, 4) << (3 + n * 2)) |14072(bits[2] << (5 + n * 2)) | (astc_extract_bits(T, 5, 6) << (5 + n * 3)), 7 + n * 3);14073}1407414075// Packs values using ASTC's BISE to output buffer.14076static void astc_pack_bise(uint32_t* pDst, const uint8_t* pSrc_vals, int bit_pos, int num_vals, int range)14077{14078uint32_t temp[5] = { 0, 0, 0, 0, 0 };1407914080const int num_bits = g_astc_bise_range_table[range][0];1408114082int group_size = 0;14083if (g_astc_bise_range_table[range][1])14084group_size = 5;14085else if (g_astc_bise_range_table[range][2])14086group_size = 3;1408714088if (group_size)14089{14090// Range has trits or quints - pack each group of 5 or 3 values14091const int total_groups = (group_size == 5) ? ((num_vals + 4) / 5) : ((num_vals + 2) / 3);1409214093for (int group_index = 0; group_index < total_groups; group_index++)14094{14095uint8_t vals[5] = { 0, 0, 0, 0, 0 };1409614097const int limit = basisu::minimum(group_size, num_vals - group_index * group_size);14098for (int i = 0; i < limit; i++)14099vals[i] = pSrc_vals[group_index * group_size + i];1410014101if (group_size == 5)14102astc_encode_trits(temp, vals, bit_pos, num_bits);14103else14104astc_encode_quints(temp, vals, bit_pos, num_bits);14105}14106}14107else14108{14109for (int i = 0; i < num_vals; i++)14110astc_set_bits_1_to_9(temp, bit_pos, pSrc_vals[i], num_bits);14111}1411214113pDst[0] |= temp[0]; pDst[1] |= temp[1];14114pDst[2] |= temp[2]; pDst[3] |= temp[3];14115}1411614117const uint32_t ASTC_BLOCK_MODE_BITS = 11;14118const uint32_t ASTC_PART_BITS = 2;14119const uint32_t ASTC_CEM_BITS = 4;14120const uint32_t ASTC_PARTITION_INDEX_BITS = 10;14121const uint32_t ASTC_CCS_BITS = 2;1412214123const uint32_t g_uastc_mode_astc_block_mode[TOTAL_UASTC_MODES] = { 0x242, 0x42, 0x53, 0x42, 0x42, 0x53, 0x442, 0x42, 0, 0x42, 0x242, 0x442, 0x53, 0x441, 0x42, 0x242, 0x42, 0x442, 0x253 };1412414125bool pack_astc_block(uint32_t* pDst, const astc_block_desc* pBlock, uint32_t uastc_mode)14126{14127assert(uastc_mode < TOTAL_UASTC_MODES);14128uint8_t* pDst_bytes = reinterpret_cast<uint8_t*>(pDst);1412914130const int total_weights = pBlock->m_dual_plane ? 32 : 16;1413114132// Set mode bits - see Table 146-14714133uint32_t mode = g_uastc_mode_astc_block_mode[uastc_mode];14134pDst_bytes[0] = (uint8_t)mode;14135pDst_bytes[1] = (uint8_t)(mode >> 8);1413614137memset(pDst_bytes + 2, 0, 16 - 2);1413814139int bit_pos = ASTC_BLOCK_MODE_BITS;1414014141// We only support 1-5 bit weight indices14142assert(!g_astc_bise_range_table[pBlock->m_weight_range][1] && !g_astc_bise_range_table[pBlock->m_weight_range][2]);14143const int bits_per_weight = g_astc_bise_range_table[pBlock->m_weight_range][0];1414414145// See table 143 - PART14146astc_set_bits_1_to_9(pDst, bit_pos, pBlock->m_subsets - 1, ASTC_PART_BITS);1414714148if (pBlock->m_subsets == 1)14149astc_set_bits_1_to_9(pDst, bit_pos, pBlock->m_cem, ASTC_CEM_BITS);14150else14151{14152// See table 14514153astc_set_bits(pDst, bit_pos, pBlock->m_partition_seed, ASTC_PARTITION_INDEX_BITS);1415414155// Table 150 - we assume all CEM's are equal, so write 2 0's along with the CEM14156astc_set_bits_1_to_9(pDst, bit_pos, (pBlock->m_cem << 2) & 63, ASTC_CEM_BITS + 2);14157}1415814159if (pBlock->m_dual_plane)14160{14161const int total_weight_bits = total_weights * bits_per_weight;1416214163// See Illegal Encodings 23.2414164// https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#_illegal_encodings14165assert((total_weight_bits >= 24) && (total_weight_bits <= 96));1416614167int ccs_bit_pos = 128 - total_weight_bits - ASTC_CCS_BITS;14168astc_set_bits_1_to_9(pDst, ccs_bit_pos, pBlock->m_ccs, ASTC_CCS_BITS);14169}1417014171const int num_cem_pairs = (1 + (pBlock->m_cem >> 2)) * pBlock->m_subsets;14172assert(num_cem_pairs <= 9);1417314174astc_pack_bise(pDst, pBlock->m_endpoints, bit_pos, num_cem_pairs * 2, g_uastc_mode_endpoint_ranges[uastc_mode]);1417514176// Write the weight bits in reverse bit order.14177switch (bits_per_weight)14178{14179case 1:14180{14181const uint32_t N = 1;14182for (int i = 0; i < total_weights; i++)14183{14184const uint32_t ofs = 128 - N - i;14185assert((ofs >> 3) < 16);14186pDst_bytes[ofs >> 3] |= (pBlock->m_weights[i] << (ofs & 7));14187}14188break;14189}14190case 2:14191{14192const uint32_t N = 2;14193for (int i = 0; i < total_weights; i++)14194{14195static const uint8_t s_reverse_bits2[4] = { 0, 2, 1, 3 };14196const uint32_t ofs = 128 - N - (i * N);14197assert((ofs >> 3) < 16);14198pDst_bytes[ofs >> 3] |= (s_reverse_bits2[pBlock->m_weights[i]] << (ofs & 7));14199}14200break;14201}14202case 3:14203{14204const uint32_t N = 3;14205for (int i = 0; i < total_weights; i++)14206{14207static const uint8_t s_reverse_bits3[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };1420814209const uint32_t ofs = 128 - N - (i * N);14210const uint32_t rev = s_reverse_bits3[pBlock->m_weights[i]] << (ofs & 7);1421114212uint32_t index = ofs >> 3;14213assert(index < 16);14214pDst_bytes[index++] |= rev & 0xFF;14215if (index < 16)14216pDst_bytes[index++] |= (rev >> 8);14217}14218break;14219}14220case 4:14221{14222const uint32_t N = 4;14223for (int i = 0; i < total_weights; i++)14224{14225static const uint8_t s_reverse_bits4[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };14226const int ofs = 128 - N - (i * N);14227assert(ofs >= 0 && (ofs >> 3) < 16);14228pDst_bytes[ofs >> 3] |= (s_reverse_bits4[pBlock->m_weights[i]] << (ofs & 7));14229}14230break;14231}14232case 5:14233{14234const uint32_t N = 5;14235for (int i = 0; i < total_weights; i++)14236{14237static const uint8_t s_reverse_bits5[32] = { 0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31 };1423814239const uint32_t ofs = 128 - N - (i * N);14240const uint32_t rev = s_reverse_bits5[pBlock->m_weights[i]] << (ofs & 7);1424114242uint32_t index = ofs >> 3;14243assert(index < 16);14244pDst_bytes[index++] |= rev & 0xFF;14245if (index < 16)14246pDst_bytes[index++] |= (rev >> 8);14247}1424814249break;14250}14251default:14252assert(0);14253break;14254}1425514256return true;14257}1425814259const uint8_t* get_anchor_indices(uint32_t subsets, uint32_t mode, uint32_t common_pattern, const uint8_t*& pPartition_pattern)14260{14261const uint8_t* pSubset_anchor_indices = g_zero_pattern;14262pPartition_pattern = g_zero_pattern;1426314264if (subsets >= 2)14265{14266if (subsets == 3)14267{14268pPartition_pattern = &g_astc_bc7_patterns3[common_pattern][0];14269pSubset_anchor_indices = &g_astc_bc7_pattern3_anchors[common_pattern][0];14270}14271else if (mode == 7)14272{14273pPartition_pattern = &g_bc7_3_astc2_patterns2[common_pattern][0];14274pSubset_anchor_indices = &g_bc7_3_astc2_patterns2_anchors[common_pattern][0];14275}14276else14277{14278pPartition_pattern = &g_astc_bc7_patterns2[common_pattern][0];14279pSubset_anchor_indices = &g_astc_bc7_pattern2_anchors[common_pattern][0];14280}14281}1428214283return pSubset_anchor_indices;14284}1428514286static inline uint32_t read_bit(const uint8_t* pBuf, uint32_t& bit_offset)14287{14288uint32_t byte_bits = pBuf[bit_offset >> 3] >> (bit_offset & 7);14289bit_offset += 1;14290return byte_bits & 1;14291}1429214293static inline uint32_t read_bits1_to_9(const uint8_t* pBuf, uint32_t& bit_offset, uint32_t codesize)14294{14295assert(codesize <= 9);14296if (!codesize)14297return 0;1429814299if ((BASISD_IS_BIG_ENDIAN) || (!BASISD_USE_UNALIGNED_WORD_READS) || (bit_offset >= 112))14300{14301const uint8_t* pBytes = &pBuf[bit_offset >> 3U];1430214303uint32_t byte_bit_offset = bit_offset & 7U;1430414305uint32_t bits = pBytes[0] >> byte_bit_offset;14306uint32_t bits_read = basisu::minimum<int>(codesize, 8 - byte_bit_offset);1430714308uint32_t bits_remaining = codesize - bits_read;14309if (bits_remaining)14310bits |= ((uint32_t)pBytes[1]) << bits_read;1431114312bit_offset += codesize;1431314314return bits & ((1U << codesize) - 1U);14315}1431614317uint32_t byte_bit_offset = bit_offset & 7U;14318const uint16_t w = *(const uint16_t *)(&pBuf[bit_offset >> 3U]);14319bit_offset += codesize;14320return (w >> byte_bit_offset) & ((1U << codesize) - 1U);14321}1432214323inline uint64_t read_bits64(const uint8_t* pBuf, uint32_t& bit_offset, uint32_t codesize)14324{14325assert(codesize <= 64U);14326uint64_t bits = 0;14327uint32_t total_bits = 0;1432814329while (total_bits < codesize)14330{14331uint32_t byte_bit_offset = bit_offset & 7U;14332uint32_t bits_to_read = basisu::minimum<int>(codesize - total_bits, 8U - byte_bit_offset);1433314334uint32_t byte_bits = pBuf[bit_offset >> 3U] >> byte_bit_offset;14335byte_bits &= ((1U << bits_to_read) - 1U);1433614337bits |= ((uint64_t)(byte_bits) << total_bits);1433814339total_bits += bits_to_read;14340bit_offset += bits_to_read;14341}1434214343return bits;14344}1434514346static inline uint32_t read_bits1_to_9_fst(const uint8_t* pBuf, uint32_t& bit_offset, uint32_t codesize)14347{14348assert(codesize <= 9);14349if (!codesize)14350return 0;14351assert(bit_offset < 112);1435214353if ((BASISD_IS_BIG_ENDIAN) || (!BASISD_USE_UNALIGNED_WORD_READS))14354{14355const uint8_t* pBytes = &pBuf[bit_offset >> 3U];1435614357uint32_t byte_bit_offset = bit_offset & 7U;1435814359uint32_t bits = pBytes[0] >> byte_bit_offset;14360uint32_t bits_read = basisu::minimum<int>(codesize, 8 - byte_bit_offset);1436114362uint32_t bits_remaining = codesize - bits_read;14363if (bits_remaining)14364bits |= ((uint32_t)pBytes[1]) << bits_read;1436514366bit_offset += codesize;1436714368return bits & ((1U << codesize) - 1U);14369}14370else14371{14372uint32_t byte_bit_offset = bit_offset & 7U;14373const uint16_t w = *(const uint16_t*)(&pBuf[bit_offset >> 3U]);14374bit_offset += codesize;14375return (w >> byte_bit_offset) & ((1U << codesize) - 1U);14376}14377}1437814379bool unpack_uastc(const uastc_block& blk, unpacked_uastc_block& unpacked, bool blue_contract_check, bool read_hints)14380{14381//memset(&unpacked, 0, sizeof(unpacked));1438214383#if 014384uint8_t table[128];14385memset(table, 0xFF, sizeof(table));1438614387{14388for (uint32_t mode = 0; mode <= TOTAL_UASTC_MODES; mode++)14389{14390const uint32_t code = g_uastc_mode_huff_codes[mode][0];14391const uint32_t codesize = g_uastc_mode_huff_codes[mode][1];1439214393table[code] = mode;1439414395uint32_t bits_left = 7 - codesize;14396for (uint32_t i = 0; i < (1 << bits_left); i++)14397table[code | (i << codesize)] = mode;14398}1439914400for (uint32_t i = 0; i < 128; i++)14401printf("%u,", table[i]);14402exit(0);14403}14404#endif1440514406const int mode = g_uastc_huff_modes[blk.m_bytes[0] & 127];14407if (mode >= (int)TOTAL_UASTC_MODES)14408return false;1440914410unpacked.m_mode = mode;14411unpacked.m_common_pattern = 0;1441214413uint32_t bit_ofs = g_uastc_mode_huff_codes[mode][1];1441414415if (mode == UASTC_MODE_INDEX_SOLID_COLOR)14416{14417unpacked.m_solid_color.r = (uint8_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 8);14418unpacked.m_solid_color.g = (uint8_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 8);14419unpacked.m_solid_color.b = (uint8_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 8);14420unpacked.m_solid_color.a = (uint8_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 8);1442114422if (read_hints)14423{14424unpacked.m_etc1_flip = false;14425unpacked.m_etc1_diff = read_bit(blk.m_bytes, bit_ofs) != 0;14426unpacked.m_etc1_inten0 = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 3);14427unpacked.m_etc1_inten1 = 0;14428unpacked.m_etc1_selector = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 2);14429unpacked.m_etc1_r = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 5);14430unpacked.m_etc1_g = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 5);14431unpacked.m_etc1_b = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 5);14432unpacked.m_etc1_bias = 0;14433unpacked.m_etc2_hints = 0;14434}1443514436return true;14437}1443814439if (read_hints)14440{14441if (g_uastc_mode_has_bc1_hint0[mode])14442unpacked.m_bc1_hint0 = read_bit(blk.m_bytes, bit_ofs) != 0;14443else14444unpacked.m_bc1_hint0 = false;1444514446if (g_uastc_mode_has_bc1_hint1[mode])14447unpacked.m_bc1_hint1 = read_bit(blk.m_bytes, bit_ofs) != 0;14448else14449unpacked.m_bc1_hint1 = false;1445014451unpacked.m_etc1_flip = read_bit(blk.m_bytes, bit_ofs) != 0;14452unpacked.m_etc1_diff = read_bit(blk.m_bytes, bit_ofs) != 0;14453unpacked.m_etc1_inten0 = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 3);14454unpacked.m_etc1_inten1 = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 3);1445514456if (g_uastc_mode_has_etc1_bias[mode])14457unpacked.m_etc1_bias = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 5);14458else14459unpacked.m_etc1_bias = 0;1446014461if (g_uastc_mode_has_alpha[mode])14462{14463unpacked.m_etc2_hints = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 8);14464//assert(unpacked.m_etc2_hints > 0);14465}14466else14467unpacked.m_etc2_hints = 0;14468}14469else14470bit_ofs += g_uastc_mode_total_hint_bits[mode];1447114472uint32_t subsets = 1;14473switch (mode)14474{14475case 2:14476case 4:14477case 7:14478case 9:14479case 16:14480unpacked.m_common_pattern = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 5);14481subsets = 2;14482break;14483case 3:14484unpacked.m_common_pattern = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 4);14485subsets = 3;14486break;14487default:14488break;14489}1449014491uint32_t part_seed = 0;14492switch (mode)14493{14494case 2:14495case 4:14496case 9:14497case 16:14498if (unpacked.m_common_pattern >= TOTAL_ASTC_BC7_COMMON_PARTITIONS2)14499return false;1450014501part_seed = g_astc_bc7_common_partitions2[unpacked.m_common_pattern].m_astc;14502break;14503case 3:14504if (unpacked.m_common_pattern >= TOTAL_ASTC_BC7_COMMON_PARTITIONS3)14505return false;1450614507part_seed = g_astc_bc7_common_partitions3[unpacked.m_common_pattern].m_astc;14508break;14509case 7:14510if (unpacked.m_common_pattern >= TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS)14511return false;1451214513part_seed = g_bc7_3_astc2_common_partitions[unpacked.m_common_pattern].m_astc2;14514break;14515default:14516break;14517}1451814519uint32_t total_planes = 1;14520switch (mode)14521{14522case 6:14523case 11:14524case 13:14525unpacked.m_astc.m_ccs = (int)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 2);14526total_planes = 2;14527break;14528case 17:14529unpacked.m_astc.m_ccs = 3;14530total_planes = 2;14531break;14532default:14533break;14534}1453514536unpacked.m_astc.m_dual_plane = (total_planes == 2);1453714538unpacked.m_astc.m_subsets = subsets;14539unpacked.m_astc.m_partition_seed = part_seed;1454014541const uint32_t total_comps = g_uastc_mode_comps[mode];1454214543const uint32_t weight_bits = g_uastc_mode_weight_bits[mode];1454414545unpacked.m_astc.m_weight_range = g_uastc_mode_weight_ranges[mode];1454614547const uint32_t total_values = total_comps * 2 * subsets;14548const uint32_t endpoint_range = g_uastc_mode_endpoint_ranges[mode];1454914550const uint32_t cem = g_uastc_mode_cem[mode];14551unpacked.m_astc.m_cem = cem;1455214553const uint32_t ep_bits = g_astc_bise_range_table[endpoint_range][0];14554const uint32_t ep_trits = g_astc_bise_range_table[endpoint_range][1];14555const uint32_t ep_quints = g_astc_bise_range_table[endpoint_range][2];1455614557uint32_t total_tqs = 0;14558uint32_t bundle_size = 0, mul = 0;14559if (ep_trits)14560{14561total_tqs = (total_values + 4) / 5;14562bundle_size = 5;14563mul = 3;14564}14565else if (ep_quints)14566{14567total_tqs = (total_values + 2) / 3;14568bundle_size = 3;14569mul = 5;14570}1457114572uint32_t tq_values[8];14573for (uint32_t i = 0; i < total_tqs; i++)14574{14575uint32_t num_bits = ep_trits ? 8 : 7;14576if (i == (total_tqs - 1))14577{14578uint32_t num_remaining = total_values - (total_tqs - 1) * bundle_size;14579if (ep_trits)14580{14581switch (num_remaining)14582{14583case 1: num_bits = 2; break;14584case 2: num_bits = 4; break;14585case 3: num_bits = 5; break;14586case 4: num_bits = 7; break;14587default: break;14588}14589}14590else if (ep_quints)14591{14592switch (num_remaining)14593{14594case 1: num_bits = 3; break;14595case 2: num_bits = 5; break;14596default: break;14597}14598}14599}1460014601tq_values[i] = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, num_bits);14602} // i1460314604uint32_t accum = 0;14605uint32_t accum_remaining = 0;14606uint32_t next_tq_index = 0;1460714608for (uint32_t i = 0; i < total_values; i++)14609{14610uint32_t value = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, ep_bits);1461114612if (total_tqs)14613{14614if (!accum_remaining)14615{14616assert(next_tq_index < total_tqs);14617accum = tq_values[next_tq_index++];14618accum_remaining = bundle_size;14619}1462014621// TODO: Optimize with tables14622uint32_t v = accum % mul;14623accum /= mul;14624accum_remaining--;1462514626value |= (v << ep_bits);14627}1462814629unpacked.m_astc.m_endpoints[i] = (uint8_t)value;14630}1463114632const uint8_t* pPartition_pattern;14633const uint8_t* pSubset_anchor_indices = get_anchor_indices(subsets, mode, unpacked.m_common_pattern, pPartition_pattern);1463414635#ifdef _DEBUG14636for (uint32_t i = 0; i < 16; i++)14637assert(pPartition_pattern[i] == astc_compute_texel_partition(part_seed, i & 3, i >> 2, 0, subsets, true));1463814639for (uint32_t subset_index = 0; subset_index < subsets; subset_index++)14640{14641uint32_t anchor_index = 0;1464214643for (uint32_t i = 0; i < 16; i++)14644{14645if (pPartition_pattern[i] == subset_index)14646{14647anchor_index = i;14648break;14649}14650}1465114652assert(pSubset_anchor_indices[subset_index] == anchor_index);14653}14654#endif1465514656#if 014657const uint32_t total_planes_shift = total_planes - 1;14658for (uint32_t i = 0; i < 16 * total_planes; i++)14659{14660uint32_t num_bits = weight_bits;14661for (uint32_t s = 0; s < subsets; s++)14662{14663if (pSubset_anchor_indices[s] == (i >> total_planes_shift))14664{14665num_bits--;14666break;14667}14668}1466914670unpacked.m_astc.m_weights[i] = (uint8_t)read_bits1_to_9(blk.m_bytes, bit_ofs, num_bits);14671}14672#endif1467314674if (mode == 18)14675{14676// Mode 18 is the only mode with more than 64 weight bits.14677for (uint32_t i = 0; i < 16; i++)14678unpacked.m_astc.m_weights[i] = (uint8_t)read_bits1_to_9(blk.m_bytes, bit_ofs, i ? weight_bits : (weight_bits - 1));14679}14680else14681{14682// All other modes have <= 64 weight bits.14683uint64_t bits;1468414685// Read the weight bits14686if ((BASISD_IS_BIG_ENDIAN) || (!BASISD_USE_UNALIGNED_WORD_READS))14687bits = read_bits64(blk.m_bytes, bit_ofs, basisu::minimum<int>(64, 128 - (int)bit_ofs));14688else14689{14690bits = blk.m_dwords[2];14691bits |= (((uint64_t)blk.m_dwords[3]) << 32U);1469214693if (bit_ofs >= 64U)14694bits >>= (bit_ofs - 64U);14695else14696{14697assert(bit_ofs >= 56U);1469814699uint32_t bits_needed = 64U - bit_ofs;14700bits <<= bits_needed;14701bits |= (blk.m_bytes[7] >> (8U - bits_needed));14702}14703}1470414705bit_ofs = 0;1470614707const uint32_t mask = (1U << weight_bits) - 1U;14708const uint32_t anchor_mask = (1U << (weight_bits - 1U)) - 1U;1470914710if (total_planes == 2)14711{14712// Dual plane modes always have a single subset, and the first 2 weights are anchors.1471314714unpacked.m_astc.m_weights[0] = (uint8_t)((uint32_t)(bits >> bit_ofs) & anchor_mask);14715bit_ofs += (weight_bits - 1);1471614717unpacked.m_astc.m_weights[1] = (uint8_t)((uint32_t)(bits >> bit_ofs) & anchor_mask);14718bit_ofs += (weight_bits - 1);1471914720for (uint32_t i = 2; i < 32; i++)14721{14722unpacked.m_astc.m_weights[i] = (uint8_t)((uint32_t)(bits >> bit_ofs) & mask);14723bit_ofs += weight_bits;14724}14725}14726else14727{14728if (subsets == 1)14729{14730// Specialize the single subset case.14731if (weight_bits == 4)14732{14733assert(bit_ofs == 0);1473414735// Specialize the most common case: 4-bit weights.14736unpacked.m_astc.m_weights[0] = (uint8_t)((uint32_t)(bits) & 7);14737unpacked.m_astc.m_weights[1] = (uint8_t)((uint32_t)(bits >> 3) & 15);14738unpacked.m_astc.m_weights[2] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 1)) & 15);14739unpacked.m_astc.m_weights[3] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 2)) & 15);1474014741unpacked.m_astc.m_weights[4] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 3)) & 15);14742unpacked.m_astc.m_weights[5] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 4)) & 15);14743unpacked.m_astc.m_weights[6] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 5)) & 15);14744unpacked.m_astc.m_weights[7] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 6)) & 15);1474514746unpacked.m_astc.m_weights[8] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 7)) & 15);14747unpacked.m_astc.m_weights[9] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 8)) & 15);14748unpacked.m_astc.m_weights[10] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 9)) & 15);14749unpacked.m_astc.m_weights[11] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 10)) & 15);1475014751unpacked.m_astc.m_weights[12] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 11)) & 15);14752unpacked.m_astc.m_weights[13] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 12)) & 15);14753unpacked.m_astc.m_weights[14] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 13)) & 15);14754unpacked.m_astc.m_weights[15] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 14)) & 15);14755}14756else14757{14758// First weight is always an anchor.14759unpacked.m_astc.m_weights[0] = (uint8_t)((uint32_t)(bits >> bit_ofs) & anchor_mask);14760bit_ofs += (weight_bits - 1);1476114762for (uint32_t i = 1; i < 16; i++)14763{14764unpacked.m_astc.m_weights[i] = (uint8_t)((uint32_t)(bits >> bit_ofs) & mask);14765bit_ofs += weight_bits;14766}14767}14768}14769else14770{14771const uint32_t a0 = pSubset_anchor_indices[0], a1 = pSubset_anchor_indices[1], a2 = pSubset_anchor_indices[2];1477214773for (uint32_t i = 0; i < 16; i++)14774{14775if ((i == a0) || (i == a1) || (i == a2))14776{14777unpacked.m_astc.m_weights[i] = (uint8_t)((uint32_t)(bits >> bit_ofs) & anchor_mask);14778bit_ofs += (weight_bits - 1);14779}14780else14781{14782unpacked.m_astc.m_weights[i] = (uint8_t)((uint32_t)(bits >> bit_ofs) & mask);14783bit_ofs += weight_bits;14784}14785}14786}14787}14788}1478914790if ((blue_contract_check) && (total_comps >= 3))14791{14792// We only need to disable ASTC Blue Contraction when we'll be packing to ASTC. The other transcoders don't care.14793bool invert_subset[3] = { false, false, false };14794bool any_flag = false;1479514796for (uint32_t subset_index = 0; subset_index < subsets; subset_index++)14797{14798const int s0 = g_astc_unquant[endpoint_range][unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + 0]].m_unquant +14799g_astc_unquant[endpoint_range][unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + 2]].m_unquant +14800g_astc_unquant[endpoint_range][unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + 4]].m_unquant;1480114802const int s1 = g_astc_unquant[endpoint_range][unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + 1]].m_unquant +14803g_astc_unquant[endpoint_range][unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + 3]].m_unquant +14804g_astc_unquant[endpoint_range][unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + 5]].m_unquant;1480514806if (s1 < s0)14807{14808for (uint32_t c = 0; c < total_comps; c++)14809std::swap(unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + c * 2 + 0], unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + c * 2 + 1]);1481014811invert_subset[subset_index] = true;14812any_flag = true;14813}14814}1481514816if (any_flag)14817{14818const uint32_t weight_mask = (1 << weight_bits) - 1;1481914820for (uint32_t i = 0; i < 16; i++)14821{14822uint32_t subset = pPartition_pattern[i];1482314824if (invert_subset[subset])14825{14826unpacked.m_astc.m_weights[i * total_planes] = (uint8_t)(weight_mask - unpacked.m_astc.m_weights[i * total_planes]);1482714828if (total_planes == 2)14829unpacked.m_astc.m_weights[i * total_planes + 1] = (uint8_t)(weight_mask - unpacked.m_astc.m_weights[i * total_planes + 1]);14830}14831}14832}14833}1483414835return true;14836}1483714838static const uint32_t* g_astc_weight_tables[6] = { nullptr, g_bc7_weights1, g_bc7_weights2, g_bc7_weights3, g_astc_weights4, g_astc_weights5 };1483914840bool unpack_uastc(uint32_t mode, uint32_t common_pattern, const color32& solid_color, const astc_block_desc& astc, color32* pPixels, bool srgb)14841{14842if (mode == UASTC_MODE_INDEX_SOLID_COLOR)14843{14844for (uint32_t i = 0; i < 16; i++)14845pPixels[i] = solid_color;14846return true;14847}1484814849color32 endpoints[3][2];1485014851const uint32_t total_subsets = g_uastc_mode_subsets[mode];14852const uint32_t total_comps = basisu::minimum<uint32_t>(4U, g_uastc_mode_comps[mode]);14853const uint32_t endpoint_range = g_uastc_mode_endpoint_ranges[mode];14854const uint32_t total_planes = g_uastc_mode_planes[mode];14855const uint32_t weight_bits = g_uastc_mode_weight_bits[mode];14856const uint32_t weight_levels = 1 << weight_bits;1485714858for (uint32_t subset_index = 0; subset_index < total_subsets; subset_index++)14859{14860if (total_comps == 2)14861{14862const uint32_t ll = g_astc_unquant[endpoint_range][astc.m_endpoints[subset_index * total_comps * 2 + 0 * 2 + 0]].m_unquant;14863const uint32_t lh = g_astc_unquant[endpoint_range][astc.m_endpoints[subset_index * total_comps * 2 + 0 * 2 + 1]].m_unquant;1486414865const uint32_t al = g_astc_unquant[endpoint_range][astc.m_endpoints[subset_index * total_comps * 2 + 1 * 2 + 0]].m_unquant;14866const uint32_t ah = g_astc_unquant[endpoint_range][astc.m_endpoints[subset_index * total_comps * 2 + 1 * 2 + 1]].m_unquant;1486714868endpoints[subset_index][0].set_noclamp_rgba(ll, ll, ll, al);14869endpoints[subset_index][1].set_noclamp_rgba(lh, lh, lh, ah);14870}14871else14872{14873for (uint32_t comp_index = 0; comp_index < total_comps; comp_index++)14874{14875endpoints[subset_index][0][comp_index] = g_astc_unquant[endpoint_range][astc.m_endpoints[subset_index * total_comps * 2 + comp_index * 2 + 0]].m_unquant;14876endpoints[subset_index][1][comp_index] = g_astc_unquant[endpoint_range][astc.m_endpoints[subset_index * total_comps * 2 + comp_index * 2 + 1]].m_unquant;14877}14878for (uint32_t comp_index = total_comps; comp_index < 4; comp_index++)14879{14880endpoints[subset_index][0][comp_index] = 255;14881endpoints[subset_index][1][comp_index] = 255;14882}14883}14884}1488514886color32 block_colors[3][32];1488714888const uint32_t* pWeights = g_astc_weight_tables[weight_bits];1488914890for (uint32_t subset_index = 0; subset_index < total_subsets; subset_index++)14891{14892for (uint32_t l = 0; l < weight_levels; l++)14893{14894if (total_comps == 2)14895{14896const uint8_t lc = (uint8_t)astc_interpolate(endpoints[subset_index][0][0], endpoints[subset_index][1][0], pWeights[l], srgb);14897const uint8_t ac = (uint8_t)astc_interpolate(endpoints[subset_index][0][3], endpoints[subset_index][1][3], pWeights[l], srgb);1489814899block_colors[subset_index][l].set(lc, lc, lc, ac);14900}14901else14902{14903uint32_t comp_index;14904for (comp_index = 0; comp_index < total_comps; comp_index++)14905block_colors[subset_index][l][comp_index] = (uint8_t)astc_interpolate(endpoints[subset_index][0][comp_index], endpoints[subset_index][1][comp_index], pWeights[l], srgb);1490614907for (; comp_index < 4; comp_index++)14908block_colors[subset_index][l][comp_index] = 255;14909}14910}14911}1491214913const uint8_t* pPartition_pattern = g_zero_pattern;1491414915if (total_subsets >= 2)14916{14917if (total_subsets == 3)14918pPartition_pattern = &g_astc_bc7_patterns3[common_pattern][0];14919else if (mode == 7)14920pPartition_pattern = &g_bc7_3_astc2_patterns2[common_pattern][0];14921else14922pPartition_pattern = &g_astc_bc7_patterns2[common_pattern][0];1492314924#ifdef _DEBUG14925for (uint32_t i = 0; i < 16; i++)14926{14927assert(pPartition_pattern[i] == (uint8_t)astc_compute_texel_partition(astc.m_partition_seed, i & 3, i >> 2, 0, total_subsets, true));14928}14929#endif14930}1493114932if (total_planes == 1)14933{14934if (total_subsets == 1)14935{14936for (uint32_t i = 0; i < 16; i++)14937{14938assert(astc.m_weights[i] < weight_levels);14939pPixels[i] = block_colors[0][astc.m_weights[i]];14940}14941}14942else14943{14944for (uint32_t i = 0; i < 16; i++)14945{14946assert(astc.m_weights[i] < weight_levels);14947pPixels[i] = block_colors[pPartition_pattern[i]][astc.m_weights[i]];14948}14949}14950}14951else14952{14953assert(total_subsets == 1);1495414955for (uint32_t i = 0; i < 16; i++)14956{14957const uint32_t subset_index = 0; // pPartition_pattern[i];1495814959const uint32_t weight_index0 = astc.m_weights[i * 2];14960const uint32_t weight_index1 = astc.m_weights[i * 2 + 1];1496114962assert(weight_index0 < weight_levels && weight_index1 < weight_levels);1496314964color32& c = pPixels[i];14965for (uint32_t comp = 0; comp < 4; comp++)14966{14967if ((int)comp == astc.m_ccs)14968c[comp] = block_colors[subset_index][weight_index1][comp];14969else14970c[comp] = block_colors[subset_index][weight_index0][comp];14971}14972}14973}1497414975return true;14976}1497714978bool unpack_uastc(const unpacked_uastc_block& unpacked_blk, color32* pPixels, bool srgb)14979{14980return unpack_uastc(unpacked_blk.m_mode, unpacked_blk.m_common_pattern, unpacked_blk.m_solid_color, unpacked_blk.m_astc, pPixels, srgb);14981}1498214983bool unpack_uastc(const uastc_block& blk, color32* pPixels, bool srgb)14984{14985unpacked_uastc_block unpacked_blk;1498614987if (!unpack_uastc(blk, unpacked_blk, false, false))14988return false;1498914990return unpack_uastc(unpacked_blk, pPixels, srgb);14991}1499214993// Determines the best shared pbits to use to encode xl/xh14994static void determine_shared_pbits(14995uint32_t total_comps, uint32_t comp_bits, float xl[4], float xh[4],14996color_quad_u8& bestMinColor, color_quad_u8& bestMaxColor, uint32_t best_pbits[2])14997{14998const uint32_t total_bits = comp_bits + 1;14999assert(total_bits >= 4 && total_bits <= 8);1500015001const int iscalep = (1 << total_bits) - 1;15002const float scalep = (float)iscalep;1500315004float best_err = 1e+9f;1500515006for (int p = 0; p < 2; p++)15007{15008color_quad_u8 xMinColor, xMaxColor;15009for (uint32_t c = 0; c < 4; c++)15010{15011xMinColor.m_c[c] = (uint8_t)(clampi(((int)((xl[c] * scalep - p) / 2.0f + .5f)) * 2 + p, p, iscalep - 1 + p));15012xMaxColor.m_c[c] = (uint8_t)(clampi(((int)((xh[c] * scalep - p) / 2.0f + .5f)) * 2 + p, p, iscalep - 1 + p));15013}1501415015color_quad_u8 scaledLow, scaledHigh;1501615017for (uint32_t i = 0; i < 4; i++)15018{15019scaledLow.m_c[i] = (xMinColor.m_c[i] << (8 - total_bits));15020scaledLow.m_c[i] |= (scaledLow.m_c[i] >> total_bits);15021assert(scaledLow.m_c[i] <= 255);1502215023scaledHigh.m_c[i] = (xMaxColor.m_c[i] << (8 - total_bits));15024scaledHigh.m_c[i] |= (scaledHigh.m_c[i] >> total_bits);15025assert(scaledHigh.m_c[i] <= 255);15026}1502715028float err = 0;15029for (uint32_t i = 0; i < total_comps; i++)15030err += basisu::squaref((scaledLow.m_c[i] / 255.0f) - xl[i]) + basisu::squaref((scaledHigh.m_c[i] / 255.0f) - xh[i]);1503115032if (err < best_err)15033{15034best_err = err;15035best_pbits[0] = p;15036best_pbits[1] = p;15037for (uint32_t j = 0; j < 4; j++)15038{15039bestMinColor.m_c[j] = xMinColor.m_c[j] >> 1;15040bestMaxColor.m_c[j] = xMaxColor.m_c[j] >> 1;15041}15042}15043}15044}1504515046// Determines the best unique pbits to use to encode xl/xh15047static void determine_unique_pbits(15048uint32_t total_comps, uint32_t comp_bits, float xl[4], float xh[4],15049color_quad_u8& bestMinColor, color_quad_u8& bestMaxColor, uint32_t best_pbits[2])15050{15051const uint32_t total_bits = comp_bits + 1;15052const int iscalep = (1 << total_bits) - 1;15053const float scalep = (float)iscalep;1505415055float best_err0 = 1e+9f;15056float best_err1 = 1e+9f;1505715058for (int p = 0; p < 2; p++)15059{15060color_quad_u8 xMinColor, xMaxColor;1506115062for (uint32_t c = 0; c < 4; c++)15063{15064xMinColor.m_c[c] = (uint8_t)(clampi(((int)((xl[c] * scalep - p) / 2.0f + .5f)) * 2 + p, p, iscalep - 1 + p));15065xMaxColor.m_c[c] = (uint8_t)(clampi(((int)((xh[c] * scalep - p) / 2.0f + .5f)) * 2 + p, p, iscalep - 1 + p));15066}1506715068color_quad_u8 scaledLow, scaledHigh;15069for (uint32_t i = 0; i < 4; i++)15070{15071scaledLow.m_c[i] = (xMinColor.m_c[i] << (8 - total_bits));15072scaledLow.m_c[i] |= (scaledLow.m_c[i] >> total_bits);15073assert(scaledLow.m_c[i] <= 255);1507415075scaledHigh.m_c[i] = (xMaxColor.m_c[i] << (8 - total_bits));15076scaledHigh.m_c[i] |= (scaledHigh.m_c[i] >> total_bits);15077assert(scaledHigh.m_c[i] <= 255);15078}1507915080float err0 = 0, err1 = 0;15081for (uint32_t i = 0; i < total_comps; i++)15082{15083err0 += basisu::squaref(scaledLow.m_c[i] - xl[i] * 255.0f);15084err1 += basisu::squaref(scaledHigh.m_c[i] - xh[i] * 255.0f);15085}1508615087if (err0 < best_err0)15088{15089best_err0 = err0;15090best_pbits[0] = p;1509115092bestMinColor.m_c[0] = xMinColor.m_c[0] >> 1;15093bestMinColor.m_c[1] = xMinColor.m_c[1] >> 1;15094bestMinColor.m_c[2] = xMinColor.m_c[2] >> 1;15095bestMinColor.m_c[3] = xMinColor.m_c[3] >> 1;15096}1509715098if (err1 < best_err1)15099{15100best_err1 = err1;15101best_pbits[1] = p;1510215103bestMaxColor.m_c[0] = xMaxColor.m_c[0] >> 1;15104bestMaxColor.m_c[1] = xMaxColor.m_c[1] >> 1;15105bestMaxColor.m_c[2] = xMaxColor.m_c[2] >> 1;15106bestMaxColor.m_c[3] = xMaxColor.m_c[3] >> 1;15107}15108}15109}1511015111bool transcode_uastc_to_astc(const uastc_block& src_blk, void* pDst)15112{15113unpacked_uastc_block unpacked_src_blk;15114if (!unpack_uastc(src_blk, unpacked_src_blk, true, false))15115return false;1511615117bool success = false;15118if (unpacked_src_blk.m_mode == UASTC_MODE_INDEX_SOLID_COLOR)15119{15120pack_astc_solid_block(pDst, unpacked_src_blk.m_solid_color);15121success = true;15122}15123else15124{15125success = pack_astc_block(static_cast<uint32_t*>(pDst), &unpacked_src_blk.m_astc, unpacked_src_blk.m_mode);15126}1512715128return success;15129}1513015131bool transcode_uastc_to_bc7(const unpacked_uastc_block& unpacked_src_blk, bc7_optimization_results& dst_blk)15132{15133memset(&dst_blk, 0, sizeof(dst_blk));1513415135const uint32_t mode = unpacked_src_blk.m_mode;1513615137const uint32_t endpoint_range = g_uastc_mode_endpoint_ranges[mode];15138const uint32_t total_comps = g_uastc_mode_comps[mode];1513915140switch (mode)15141{15142case 0:15143case 5:15144case 10:15145case 12:15146case 14:15147case 15:15148case 18:15149{15150// MODE 0: DualPlane: 0, WeightRange: 8 (16), Subsets: 1, EndpointRange: 19 (192) - BC7 MODE6 RGB15151// MODE 5: DualPlane: 0, WeightRange : 5 (8), Subsets : 1, EndpointRange : 20 (256) - BC7 MODE6 RGB15152// MODE 10 DualPlane: 0, WeightRange: 8 (16), Subsets: 1, EndpointRange: 13 (48) - BC7 MODE615153// MODE 12: DualPlane: 0, WeightRange : 5 (8), Subsets : 1, EndpointRange : 19 (192) - BC7 MODE615154// MODE 14: DualPlane: 0, WeightRange : 2 (4), Subsets : 1, EndpointRange : 20 (256) - BC7 MODE615155// MODE 18: DualPlane: 0, WeightRange : 11 (32), Subsets : 1, CEM : 8, EndpointRange : 11 (32) - BC7 MODE615156// MODE 15: DualPlane: 0, WeightRange : 8 (16), Subsets : 1, CEM : 4 (LA Direct), EndpointRange : 20 (256) - BC7 MODE615157dst_blk.m_mode = 6;1515815159float xl[4], xh[4];15160if (total_comps == 2)15161{15162xl[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[0]].m_unquant / 255.0f;15163xh[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[1]].m_unquant / 255.0f;1516415165xl[1] = xl[0];15166xh[1] = xh[0];1516715168xl[2] = xl[0];15169xh[2] = xh[0];1517015171xl[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[2]].m_unquant / 255.0f;15172xh[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[3]].m_unquant / 255.0f;15173}15174else15175{15176xl[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[0]].m_unquant / 255.0f;15177xl[1] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[2]].m_unquant / 255.0f;15178xl[2] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[4]].m_unquant / 255.0f;1517915180xh[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[1]].m_unquant / 255.0f;15181xh[1] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[3]].m_unquant / 255.0f;15182xh[2] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[5]].m_unquant / 255.0f;1518315184if (total_comps == 4)15185{15186xl[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[6]].m_unquant / 255.0f;15187xh[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[7]].m_unquant / 255.0f;15188}15189else15190{15191xl[3] = 1.0f;15192xh[3] = 1.0f;15193}15194}1519515196uint32_t best_pbits[2];15197color_quad_u8 bestMinColor, bestMaxColor;15198determine_unique_pbits((total_comps == 2) ? 4 : total_comps, 7, xl, xh, bestMinColor, bestMaxColor, best_pbits);1519915200dst_blk.m_low[0] = bestMinColor;15201dst_blk.m_high[0] = bestMaxColor;1520215203if (total_comps == 3)15204{15205dst_blk.m_low[0].m_c[3] = 127;15206dst_blk.m_high[0].m_c[3] = 127;15207}1520815209dst_blk.m_pbits[0][0] = best_pbits[0];15210dst_blk.m_pbits[0][1] = best_pbits[1];1521115212if (mode == 18)15213{15214const uint8_t s_bc7_5_to_4[32] = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15 };15215for (uint32_t i = 0; i < 16; i++)15216dst_blk.m_selectors[i] = s_bc7_5_to_4[unpacked_src_blk.m_astc.m_weights[i]];15217}15218else if (mode == 14)15219{15220const uint8_t s_bc7_2_to_4[4] = { 0, 5, 10, 15 };15221for (uint32_t i = 0; i < 16; i++)15222dst_blk.m_selectors[i] = s_bc7_2_to_4[unpacked_src_blk.m_astc.m_weights[i]];15223}15224else if ((mode == 5) || (mode == 12))15225{15226const uint8_t s_bc7_3_to_4[8] = { 0, 2, 4, 6, 9, 11, 13, 15 };15227for (uint32_t i = 0; i < 16; i++)15228dst_blk.m_selectors[i] = s_bc7_3_to_4[unpacked_src_blk.m_astc.m_weights[i]];15229}15230else15231{15232for (uint32_t i = 0; i < 16; i++)15233dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];15234}1523515236break;15237}15238case 1:15239{15240// DualPlane: 0, WeightRange : 2 (4), Subsets : 1, EndpointRange : 20 (256) - BC7 MODE315241// Mode 1 uses endpoint range 20 - no need to use ASTC dequant tables.15242dst_blk.m_mode = 3;1524315244float xl[4], xh[4];15245xl[0] = unpacked_src_blk.m_astc.m_endpoints[0] / 255.0f;15246xl[1] = unpacked_src_blk.m_astc.m_endpoints[2] / 255.0f;15247xl[2] = unpacked_src_blk.m_astc.m_endpoints[4] / 255.0f;15248xl[3] = 1.0f;1524915250xh[0] = unpacked_src_blk.m_astc.m_endpoints[1] / 255.0f;15251xh[1] = unpacked_src_blk.m_astc.m_endpoints[3] / 255.0f;15252xh[2] = unpacked_src_blk.m_astc.m_endpoints[5] / 255.0f;15253xh[3] = 1.0f;1525415255uint32_t best_pbits[2];15256color_quad_u8 bestMinColor, bestMaxColor;15257memset(&bestMinColor, 0, sizeof(bestMinColor));15258memset(&bestMaxColor, 0, sizeof(bestMaxColor));15259determine_unique_pbits(3, 7, xl, xh, bestMinColor, bestMaxColor, best_pbits);1526015261for (uint32_t i = 0; i < 3; i++)15262{15263dst_blk.m_low[0].m_c[i] = bestMinColor.m_c[i];15264dst_blk.m_high[0].m_c[i] = bestMaxColor.m_c[i];15265dst_blk.m_low[1].m_c[i] = bestMinColor.m_c[i];15266dst_blk.m_high[1].m_c[i] = bestMaxColor.m_c[i];15267}15268dst_blk.m_pbits[0][0] = best_pbits[0];15269dst_blk.m_pbits[0][1] = best_pbits[1];15270dst_blk.m_pbits[1][0] = best_pbits[0];15271dst_blk.m_pbits[1][1] = best_pbits[1];1527215273for (uint32_t i = 0; i < 16; i++)15274dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];1527515276break;15277}15278case 2:15279{15280// 2. DualPlane: 0, WeightRange : 5 (8), Subsets : 2, EndpointRange : 8 (16) - BC7 MODE115281dst_blk.m_mode = 1;15282dst_blk.m_partition = g_astc_bc7_common_partitions2[unpacked_src_blk.m_common_pattern].m_bc7;1528315284const bool invert_partition = g_astc_bc7_common_partitions2[unpacked_src_blk.m_common_pattern].m_invert;1528515286float xl[4], xh[4];15287xl[3] = 1.0f;15288xh[3] = 1.0f;1528915290for (uint32_t subset = 0; subset < 2; subset++)15291{15292for (uint32_t i = 0; i < 3; i++)15293{15294uint32_t v = unpacked_src_blk.m_astc.m_endpoints[i * 2 + subset * 6];15295v = (v << 4) | v;15296xl[i] = v / 255.0f;1529715298v = unpacked_src_blk.m_astc.m_endpoints[i * 2 + subset * 6 + 1];15299v = (v << 4) | v;15300xh[i] = v / 255.0f;15301}1530215303uint32_t best_pbits[2] = { 0, 0 };15304color_quad_u8 bestMinColor, bestMaxColor;15305memset(&bestMinColor, 0, sizeof(bestMinColor));15306memset(&bestMaxColor, 0, sizeof(bestMaxColor));15307determine_shared_pbits(3, 6, xl, xh, bestMinColor, bestMaxColor, best_pbits);1530815309const uint32_t bc7_subset_index = invert_partition ? (1 - subset) : subset;1531015311for (uint32_t i = 0; i < 3; i++)15312{15313dst_blk.m_low[bc7_subset_index].m_c[i] = bestMinColor.m_c[i];15314dst_blk.m_high[bc7_subset_index].m_c[i] = bestMaxColor.m_c[i];15315}1531615317dst_blk.m_pbits[bc7_subset_index][0] = best_pbits[0];15318} // subset1531915320for (uint32_t i = 0; i < 16; i++)15321dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];1532215323break;15324}15325case 3:15326{15327// DualPlane: 0, WeightRange : 2 (4), Subsets : 3, EndpointRange : 7 (12) - BC7 MODE215328dst_blk.m_mode = 2;15329dst_blk.m_partition = g_astc_bc7_common_partitions3[unpacked_src_blk.m_common_pattern].m_bc7;1533015331const uint32_t perm = g_astc_bc7_common_partitions3[unpacked_src_blk.m_common_pattern].m_astc_to_bc7_perm;1533215333for (uint32_t subset = 0; subset < 3; subset++)15334{15335for (uint32_t comp = 0; comp < 3; comp++)15336{15337uint32_t lo = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[comp * 2 + 0 + subset * 6]].m_unquant;15338uint32_t hi = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[comp * 2 + 1 + subset * 6]].m_unquant;1533915340// TODO: I think this can be improved by using tables like Basis Universal does with ETC1S conversion.15341lo = (lo * 31 + 127) / 255;15342hi = (hi * 31 + 127) / 255;1534315344const uint32_t bc7_subset_index = g_astc_to_bc7_partition_index_perm_tables[perm][subset];1534515346dst_blk.m_low[bc7_subset_index].m_c[comp] = (uint8_t)lo;15347dst_blk.m_high[bc7_subset_index].m_c[comp] = (uint8_t)hi;15348}15349}1535015351for (uint32_t i = 0; i < 16; i++)15352dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];1535315354break;15355}15356case 4:15357{15358// 4. DualPlane: 0, WeightRange: 2 (4), Subsets: 2, EndpointRange: 12 (40) - BC7 MODE315359dst_blk.m_mode = 3;15360dst_blk.m_partition = g_astc_bc7_common_partitions2[unpacked_src_blk.m_common_pattern].m_bc7;1536115362const bool invert_partition = g_astc_bc7_common_partitions2[unpacked_src_blk.m_common_pattern].m_invert;1536315364float xl[4], xh[4];15365xl[3] = 1.0f;15366xh[3] = 1.0f;1536715368for (uint32_t subset = 0; subset < 2; subset++)15369{15370for (uint32_t i = 0; i < 3; i++)15371{15372xl[i] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[i * 2 + subset * 6]].m_unquant / 255.0f;15373xh[i] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[i * 2 + subset * 6 + 1]].m_unquant / 255.0f;15374}1537515376uint32_t best_pbits[2] = { 0, 0 };15377color_quad_u8 bestMinColor, bestMaxColor;15378memset(&bestMinColor, 0, sizeof(bestMinColor));15379memset(&bestMaxColor, 0, sizeof(bestMaxColor));15380determine_unique_pbits(3, 7, xl, xh, bestMinColor, bestMaxColor, best_pbits);1538115382const uint32_t bc7_subset_index = invert_partition ? (1 - subset) : subset;1538315384for (uint32_t i = 0; i < 3; i++)15385{15386dst_blk.m_low[bc7_subset_index].m_c[i] = bestMinColor.m_c[i];15387dst_blk.m_high[bc7_subset_index].m_c[i] = bestMaxColor.m_c[i];15388}15389dst_blk.m_low[bc7_subset_index].m_c[3] = 127;15390dst_blk.m_high[bc7_subset_index].m_c[3] = 127;1539115392dst_blk.m_pbits[bc7_subset_index][0] = best_pbits[0];15393dst_blk.m_pbits[bc7_subset_index][1] = best_pbits[1];1539415395} // subset1539615397for (uint32_t i = 0; i < 16; i++)15398dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];1539915400break;15401}15402case 6:15403case 11:15404case 13:15405case 17:15406{15407// MODE 6: DualPlane: 1, WeightRange : 2 (4), Subsets : 1, EndpointRange : 18 (160) - BC7 MODE5 RGB15408// MODE 11: DualPlane: 1, WeightRange: 2 (4), Subsets: 1, EndpointRange: 13 (48) - BC7 MODE515409// MODE 13: DualPlane: 1, WeightRange: 0 (2), Subsets : 1, EndpointRange : 20 (256) - BC7 MODE515410// MODE 17: DualPlane: 1, WeightRange: 2 (4), Subsets: 1, CEM: 4 (LA Direct), EndpointRange: 20 (256) - BC7 MODE515411dst_blk.m_mode = 5;15412dst_blk.m_rotation = (unpacked_src_blk.m_astc.m_ccs + 1) & 3;1541315414if (total_comps == 2)15415{15416assert(unpacked_src_blk.m_astc.m_ccs == 3);1541715418dst_blk.m_low->m_c[0] = (uint8_t)((g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[0]].m_unquant * 127 + 127) / 255);15419dst_blk.m_high->m_c[0] = (uint8_t)((g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[1]].m_unquant * 127 + 127) / 255);1542015421dst_blk.m_low->m_c[1] = dst_blk.m_low->m_c[0];15422dst_blk.m_high->m_c[1] = dst_blk.m_high->m_c[0];1542315424dst_blk.m_low->m_c[2] = dst_blk.m_low->m_c[0];15425dst_blk.m_high->m_c[2] = dst_blk.m_high->m_c[0];1542615427dst_blk.m_low->m_c[3] = (uint8_t)(g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[2]].m_unquant);15428dst_blk.m_high->m_c[3] = (uint8_t)(g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[3]].m_unquant);15429}15430else15431{15432for (uint32_t astc_comp = 0; astc_comp < 4; astc_comp++)15433{15434uint32_t bc7_comp = astc_comp;15435// ASTC and BC7 handle dual plane component rotations differently:15436// ASTC: 2nd plane separately interpolates the CCS channel.15437// BC7: 2nd plane channel is swapped with alpha, 2nd plane controls alpha interpolation, then we swap alpha with the desired channel.15438if (astc_comp == (uint32_t)unpacked_src_blk.m_astc.m_ccs)15439bc7_comp = 3;15440else if (astc_comp == 3)15441bc7_comp = unpacked_src_blk.m_astc.m_ccs;1544215443uint32_t l = 255, h = 255;15444if (astc_comp < total_comps)15445{15446l = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[astc_comp * 2 + 0]].m_unquant;15447h = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[astc_comp * 2 + 1]].m_unquant;15448}1544915450if (bc7_comp < 3)15451{15452l = (l * 127 + 127) / 255;15453h = (h * 127 + 127) / 255;15454}1545515456dst_blk.m_low->m_c[bc7_comp] = (uint8_t)l;15457dst_blk.m_high->m_c[bc7_comp] = (uint8_t)h;15458}15459}1546015461if (mode == 13)15462{15463for (uint32_t i = 0; i < 16; i++)15464{15465dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i * 2] ? 3 : 0;15466dst_blk.m_alpha_selectors[i] = unpacked_src_blk.m_astc.m_weights[i * 2 + 1] ? 3 : 0;15467}15468}15469else15470{15471for (uint32_t i = 0; i < 16; i++)15472{15473dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i * 2];15474dst_blk.m_alpha_selectors[i] = unpacked_src_blk.m_astc.m_weights[i * 2 + 1];15475}15476}1547715478break;15479}15480case 7:15481{15482// DualPlane: 0, WeightRange : 2 (4), Subsets : 2, EndpointRange : 12 (40) - BC7 MODE215483dst_blk.m_mode = 2;15484dst_blk.m_partition = g_bc7_3_astc2_common_partitions[unpacked_src_blk.m_common_pattern].m_bc73;1548515486const uint32_t common_pattern_k = g_bc7_3_astc2_common_partitions[unpacked_src_blk.m_common_pattern].k;1548715488for (uint32_t bc7_part = 0; bc7_part < 3; bc7_part++)15489{15490const uint32_t astc_part = bc7_convert_partition_index_3_to_2(bc7_part, common_pattern_k);1549115492for (uint32_t c = 0; c < 3; c++)15493{15494dst_blk.m_low[bc7_part].m_c[c] = (g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[c * 2 + 0 + astc_part * 6]].m_unquant * 31 + 127) / 255;15495dst_blk.m_high[bc7_part].m_c[c] = (g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[c * 2 + 1 + astc_part * 6]].m_unquant * 31 + 127) / 255;15496}15497}1549815499for (uint32_t i = 0; i < 16; i++)15500dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];1550115502break;15503}15504case UASTC_MODE_INDEX_SOLID_COLOR:15505{15506// Void-Extent: Solid Color RGBA (BC7 MODE5 or MODE6)15507const color32& solid_color = unpacked_src_blk.m_solid_color;1550815509uint32_t best_err0 = g_bc7_mode_6_optimal_endpoints[solid_color.r][0].m_error + g_bc7_mode_6_optimal_endpoints[solid_color.g][0].m_error +15510g_bc7_mode_6_optimal_endpoints[solid_color.b][0].m_error + g_bc7_mode_6_optimal_endpoints[solid_color.a][0].m_error;1551115512uint32_t best_err1 = g_bc7_mode_6_optimal_endpoints[solid_color.r][1].m_error + g_bc7_mode_6_optimal_endpoints[solid_color.g][1].m_error +15513g_bc7_mode_6_optimal_endpoints[solid_color.b][1].m_error + g_bc7_mode_6_optimal_endpoints[solid_color.a][1].m_error;1551415515if (best_err0 > 0 && best_err1 > 0)15516{15517dst_blk.m_mode = 5;1551815519for (uint32_t c = 0; c < 3; c++)15520{15521dst_blk.m_low[0].m_c[c] = g_bc7_mode_5_optimal_endpoints[solid_color.c[c]].m_lo;15522dst_blk.m_high[0].m_c[c] = g_bc7_mode_5_optimal_endpoints[solid_color.c[c]].m_hi;15523}1552415525memset(dst_blk.m_selectors, BC7ENC_MODE_5_OPTIMAL_INDEX, 16);1552615527dst_blk.m_low[0].m_c[3] = solid_color.c[3];15528dst_blk.m_high[0].m_c[3] = solid_color.c[3];1552915530//memset(dst_blk.m_alpha_selectors, 0, 16);15531}15532else15533{15534dst_blk.m_mode = 6;1553515536uint32_t best_p = 0;15537if (best_err1 < best_err0)15538best_p = 1;1553915540for (uint32_t c = 0; c < 4; c++)15541{15542dst_blk.m_low[0].m_c[c] = g_bc7_mode_6_optimal_endpoints[solid_color.c[c]][best_p].m_lo;15543dst_blk.m_high[0].m_c[c] = g_bc7_mode_6_optimal_endpoints[solid_color.c[c]][best_p].m_hi;15544}1554515546dst_blk.m_pbits[0][0] = best_p;15547dst_blk.m_pbits[0][1] = best_p;15548memset(dst_blk.m_selectors, BC7ENC_MODE_6_OPTIMAL_INDEX, 16);15549}1555015551break;15552}15553case 9:15554case 16:15555{15556// 9. DualPlane: 0, WeightRange : 2 (4), Subsets : 2, EndpointRange : 8 (16) - BC7 MODE715557// 16. DualPlane: 0, WeightRange: 2 (4), Subsets: 2, CEM: 4 (LA Direct), EndpointRange: 20 (256) - BC7 MODE71555815559dst_blk.m_mode = 7;15560dst_blk.m_partition = g_astc_bc7_common_partitions2[unpacked_src_blk.m_common_pattern].m_bc7;1556115562const bool invert_partition = g_astc_bc7_common_partitions2[unpacked_src_blk.m_common_pattern].m_invert;1556315564for (uint32_t astc_subset = 0; astc_subset < 2; astc_subset++)15565{15566float xl[4], xh[4];1556715568if (total_comps == 2)15569{15570xl[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[0 + astc_subset * 4]].m_unquant / 255.0f;15571xh[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[1 + astc_subset * 4]].m_unquant / 255.0f;1557215573xl[1] = xl[0];15574xh[1] = xh[0];1557515576xl[2] = xl[0];15577xh[2] = xh[0];1557815579xl[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[2 + astc_subset * 4]].m_unquant / 255.0f;15580xh[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[3 + astc_subset * 4]].m_unquant / 255.0f;15581}15582else15583{15584xl[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[0 + astc_subset * 8]].m_unquant / 255.0f;15585xl[1] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[2 + astc_subset * 8]].m_unquant / 255.0f;15586xl[2] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[4 + astc_subset * 8]].m_unquant / 255.0f;15587xl[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[6 + astc_subset * 8]].m_unquant / 255.0f;1558815589xh[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[1 + astc_subset * 8]].m_unquant / 255.0f;15590xh[1] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[3 + astc_subset * 8]].m_unquant / 255.0f;15591xh[2] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[5 + astc_subset * 8]].m_unquant / 255.0f;15592xh[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[7 + astc_subset * 8]].m_unquant / 255.0f;15593}1559415595uint32_t best_pbits[2] = { 0, 0 };15596color_quad_u8 bestMinColor, bestMaxColor;15597memset(&bestMinColor, 0, sizeof(bestMinColor));15598memset(&bestMaxColor, 0, sizeof(bestMaxColor));15599determine_unique_pbits(4, 5, xl, xh, bestMinColor, bestMaxColor, best_pbits);1560015601const uint32_t bc7_subset_index = invert_partition ? (1 - astc_subset) : astc_subset;1560215603dst_blk.m_low[bc7_subset_index] = bestMinColor;15604dst_blk.m_high[bc7_subset_index] = bestMaxColor;1560515606dst_blk.m_pbits[bc7_subset_index][0] = best_pbits[0];15607dst_blk.m_pbits[bc7_subset_index][1] = best_pbits[1];15608} // astc_subset1560915610for (uint32_t i = 0; i < 16; i++)15611dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];1561215613break;15614}15615default:15616return false;15617}1561815619return true;15620}1562115622bool transcode_uastc_to_bc7(const uastc_block& src_blk, bc7_optimization_results& dst_blk)15623{15624unpacked_uastc_block unpacked_src_blk;15625if (!unpack_uastc(src_blk, unpacked_src_blk, false, false))15626return false;1562715628return transcode_uastc_to_bc7(unpacked_src_blk, dst_blk);15629}1563015631bool transcode_uastc_to_bc7(const uastc_block& src_blk, void* pDst)15632{15633bc7_optimization_results temp;15634if (!transcode_uastc_to_bc7(src_blk, temp))15635return false;1563615637encode_bc7_block(pDst, &temp);15638return true;15639}1564015641color32 apply_etc1_bias(const color32 &block_color, uint32_t bias, uint32_t limit, uint32_t subblock)15642{15643color32 result;1564415645for (uint32_t c = 0; c < 3; c++)15646{15647static const int s_divs[3] = { 1, 3, 9 };1564815649int delta = 0;1565015651switch (bias)15652{15653case 2: delta = subblock ? 0 : ((c == 0) ? -1 : 0); break;15654case 5: delta = subblock ? 0 : ((c == 1) ? -1 : 0); break;15655case 6: delta = subblock ? 0 : ((c == 2) ? -1 : 0); break;1565615657case 7: delta = subblock ? 0 : ((c == 0) ? 1 : 0); break;15658case 11: delta = subblock ? 0 : ((c == 1) ? 1 : 0); break;15659case 15: delta = subblock ? 0 : ((c == 2) ? 1 : 0); break;1566015661case 18: delta = subblock ? ((c == 0) ? -1 : 0) : 0; break;15662case 19: delta = subblock ? ((c == 1) ? -1 : 0) : 0; break;15663case 20: delta = subblock ? ((c == 2) ? -1 : 0) : 0; break;1566415665case 21: delta = subblock ? ((c == 0) ? 1 : 0) : 0; break;15666case 24: delta = subblock ? ((c == 1) ? 1 : 0) : 0; break;15667case 8: delta = subblock ? ((c == 2) ? 1 : 0) : 0; break;1566815669case 10: delta = -2; break;1567015671case 27: delta = subblock ? 0 : -1; break;15672case 28: delta = subblock ? -1 : 1; break;15673case 29: delta = subblock ? 1 : 0; break;15674case 30: delta = subblock ? -1 : 0; break;15675case 31: delta = subblock ? 0 : 1; break;1567615677default:15678delta = ((bias / s_divs[c]) % 3) - 1;15679break;15680}1568115682int v = block_color[c];15683if (v == 0)15684{15685if (delta == -2)15686v += 3;15687else15688v += delta + 1;15689}15690else if (v == (int)limit)15691{15692v += (delta - 1);15693}15694else15695{15696v += delta;15697if ((v < 0) || (v > (int)limit))15698v = (v - delta) - delta;15699}1570015701assert(v >= 0);15702assert(v <= (int)limit);1570315704result[c] = (uint8_t)v;15705}1570615707return result;15708}1570915710static void etc1_determine_selectors(decoder_etc_block& dst_blk, const color32* pSource_pixels, uint32_t first_subblock, uint32_t last_subblock)15711{15712static const uint8_t s_tran[4] = { 1, 0, 2, 3 };1571315714uint16_t l_bitmask = 0;15715uint16_t h_bitmask = 0;1571615717for (uint32_t subblock = first_subblock; subblock < last_subblock; subblock++)15718{15719color32 block_colors[4];15720dst_blk.get_block_colors(block_colors, subblock);1572115722uint32_t block_y[4];15723for (uint32_t i = 0; i < 4; i++)15724block_y[i] = block_colors[i][0] * 54 + block_colors[i][1] * 183 + block_colors[i][2] * 19;1572515726const uint32_t block_y01 = block_y[0] + block_y[1];15727const uint32_t block_y12 = block_y[1] + block_y[2];15728const uint32_t block_y23 = block_y[2] + block_y[3];1572915730// X0 X0 X0 X0 X1 X1 X1 X1 X2 X2 X2 X2 X3 X3 X3 X315731// Y0 Y1 Y2 Y3 Y0 Y1 Y2 Y3 Y0 Y1 Y2 Y3 Y0 Y1 Y2 Y31573215733if (dst_blk.get_flip_bit())15734{15735uint32_t ofs = subblock * 2;1573615737for (uint32_t y = 0; y < 2; y++)15738{15739for (uint32_t x = 0; x < 4; x++)15740{15741const color32& c = pSource_pixels[x + (subblock * 2 + y) * 4];15742const uint32_t l = c[0] * 108 + c[1] * 366 + c[2] * 38;1574315744uint32_t t = s_tran[(l < block_y01) + (l < block_y12) + (l < block_y23)];1574515746assert(ofs < 16);15747l_bitmask |= ((t & 1) << ofs);15748h_bitmask |= ((t >> 1) << ofs);15749ofs += 4;15750}1575115752ofs = (int)ofs + 1 - 4 * 4;15753}15754}15755else15756{15757uint32_t ofs = (subblock * 2) * 4;15758for (uint32_t x = 0; x < 2; x++)15759{15760for (uint32_t y = 0; y < 4; y++)15761{15762const color32& c = pSource_pixels[subblock * 2 + x + y * 4];15763const uint32_t l = c[0] * 108 + c[1] * 366 + c[2] * 38;1576415765uint32_t t = s_tran[(l < block_y01) + (l < block_y12) + (l < block_y23)];1576615767assert(ofs < 16);15768l_bitmask |= ((t & 1) << ofs);15769h_bitmask |= ((t >> 1) << ofs);15770++ofs;15771}15772}15773}15774}1577515776dst_blk.m_bytes[7] = (uint8_t)(l_bitmask);15777dst_blk.m_bytes[6] = (uint8_t)(l_bitmask >> 8);15778dst_blk.m_bytes[5] = (uint8_t)(h_bitmask);15779dst_blk.m_bytes[4] = (uint8_t)(h_bitmask >> 8);15780}1578115782static const uint8_t s_etc1_solid_selectors[4][4] = { { 255, 255, 255, 255 }, { 255, 255, 0, 0 }, { 0, 0, 0, 0 }, {0, 0, 255, 255 } };1578315784struct etc_coord215785{15786uint8_t m_x, m_y;15787};1578815789// [flip][subblock][pixel_index]15790const etc_coord2 g_etc1_pixel_coords[2][2][8] =15791{15792{15793{15794{ 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 },15795{ 1, 0 }, { 1, 1 }, { 1, 2 }, { 1, 3 }15796},15797{15798{ 2, 0 }, { 2, 1 }, { 2, 2 }, { 2, 3 },15799{ 3, 0 }, { 3, 1 }, { 3, 2 }, { 3, 3 }15800}15801},15802{15803{15804{ 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 },15805{ 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1 }15806},15807{15808{ 0, 2 }, { 1, 2 }, { 2, 2 }, { 3, 2 },15809{ 0, 3 }, { 1, 3 }, { 2, 3 }, { 3, 3 }15810},15811}15812};1581315814void transcode_uastc_to_etc1(unpacked_uastc_block& unpacked_src_blk, color32 block_pixels[4][4], void* pDst)15815{15816decoder_etc_block& dst_blk = *static_cast<decoder_etc_block*>(pDst);1581715818if (unpacked_src_blk.m_mode == UASTC_MODE_INDEX_SOLID_COLOR)15819{15820dst_blk.m_bytes[3] = (uint8_t)((unpacked_src_blk.m_etc1_diff << 1) | (unpacked_src_blk.m_etc1_inten0 << 5) | (unpacked_src_blk.m_etc1_inten0 << 2));1582115822if (unpacked_src_blk.m_etc1_diff)15823{15824dst_blk.m_bytes[0] = (uint8_t)(unpacked_src_blk.m_etc1_r << 3);15825dst_blk.m_bytes[1] = (uint8_t)(unpacked_src_blk.m_etc1_g << 3);15826dst_blk.m_bytes[2] = (uint8_t)(unpacked_src_blk.m_etc1_b << 3);15827}15828else15829{15830dst_blk.m_bytes[0] = (uint8_t)(unpacked_src_blk.m_etc1_r | (unpacked_src_blk.m_etc1_r << 4));15831dst_blk.m_bytes[1] = (uint8_t)(unpacked_src_blk.m_etc1_g | (unpacked_src_blk.m_etc1_g << 4));15832dst_blk.m_bytes[2] = (uint8_t)(unpacked_src_blk.m_etc1_b | (unpacked_src_blk.m_etc1_b << 4));15833}1583415835memcpy(dst_blk.m_bytes + 4, &s_etc1_solid_selectors[unpacked_src_blk.m_etc1_selector][0], 4);1583615837return;15838}1583915840const bool flip = unpacked_src_blk.m_etc1_flip != 0;15841const bool diff = unpacked_src_blk.m_etc1_diff != 0;1584215843dst_blk.m_bytes[3] = (uint8_t)((int)flip | (diff << 1) | (unpacked_src_blk.m_etc1_inten0 << 5) | (unpacked_src_blk.m_etc1_inten1 << 2));1584415845const uint32_t limit = diff ? 31 : 15;1584615847color32 block_colors[2];1584815849for (uint32_t subset = 0; subset < 2; subset++)15850{15851uint32_t avg_color[3];15852memset(avg_color, 0, sizeof(avg_color));1585315854for (uint32_t j = 0; j < 8; j++)15855{15856const etc_coord2& c = g_etc1_pixel_coords[flip][subset][j];1585715858avg_color[0] += block_pixels[c.m_y][c.m_x].r;15859avg_color[1] += block_pixels[c.m_y][c.m_x].g;15860avg_color[2] += block_pixels[c.m_y][c.m_x].b;15861} // j1586215863block_colors[subset][0] = (uint8_t)((avg_color[0] * limit + 1020) / (8 * 255));15864block_colors[subset][1] = (uint8_t)((avg_color[1] * limit + 1020) / (8 * 255));15865block_colors[subset][2] = (uint8_t)((avg_color[2] * limit + 1020) / (8 * 255));15866block_colors[subset][3] = 0;1586715868if (g_uastc_mode_has_etc1_bias[unpacked_src_blk.m_mode])15869{15870block_colors[subset] = apply_etc1_bias(block_colors[subset], unpacked_src_blk.m_etc1_bias, limit, subset);15871}1587215873} // subset1587415875if (diff)15876{15877int dr = block_colors[1].r - block_colors[0].r;15878int dg = block_colors[1].g - block_colors[0].g;15879int db = block_colors[1].b - block_colors[0].b;1588015881dr = basisu::clamp<int>(dr, cETC1ColorDeltaMin, cETC1ColorDeltaMax);15882dg = basisu::clamp<int>(dg, cETC1ColorDeltaMin, cETC1ColorDeltaMax);15883db = basisu::clamp<int>(db, cETC1ColorDeltaMin, cETC1ColorDeltaMax);1588415885if (dr < 0) dr += 8;15886if (dg < 0) dg += 8;15887if (db < 0) db += 8;1588815889dst_blk.m_bytes[0] = (uint8_t)((block_colors[0].r << 3) | dr);15890dst_blk.m_bytes[1] = (uint8_t)((block_colors[0].g << 3) | dg);15891dst_blk.m_bytes[2] = (uint8_t)((block_colors[0].b << 3) | db);15892}15893else15894{15895dst_blk.m_bytes[0] = (uint8_t)(block_colors[1].r | (block_colors[0].r << 4));15896dst_blk.m_bytes[1] = (uint8_t)(block_colors[1].g | (block_colors[0].g << 4));15897dst_blk.m_bytes[2] = (uint8_t)(block_colors[1].b | (block_colors[0].b << 4));15898}1589915900etc1_determine_selectors(dst_blk, &block_pixels[0][0], 0, 2);15901}1590215903bool transcode_uastc_to_etc1(const uastc_block& src_blk, void* pDst)15904{15905unpacked_uastc_block unpacked_src_blk;15906if (!unpack_uastc(src_blk, unpacked_src_blk, false))15907return false;1590815909color32 block_pixels[4][4];15910if (unpacked_src_blk.m_mode != UASTC_MODE_INDEX_SOLID_COLOR)15911{15912const bool unpack_srgb = false;15913if (!unpack_uastc(unpacked_src_blk, &block_pixels[0][0], unpack_srgb))15914return false;15915}1591615917transcode_uastc_to_etc1(unpacked_src_blk, block_pixels, pDst);1591815919return true;15920}1592115922static inline int gray_distance2(const uint8_t c, int y)15923{15924int gray_dist = (int)c - y;15925return gray_dist * gray_dist;15926}1592715928static bool pack_etc1_y_estimate_flipped(const uint8_t* pSrc_pixels,15929int& upper_avg, int& lower_avg, int& left_avg, int& right_avg)15930{15931int sums[2][2];1593215933#define GET_XY(x, y) pSrc_pixels[(x) + ((y) * 4)]1593415935sums[0][0] = GET_XY(0, 0) + GET_XY(0, 1) + GET_XY(1, 0) + GET_XY(1, 1);15936sums[1][0] = GET_XY(2, 0) + GET_XY(2, 1) + GET_XY(3, 0) + GET_XY(3, 1);15937sums[0][1] = GET_XY(0, 2) + GET_XY(0, 3) + GET_XY(1, 2) + GET_XY(1, 3);15938sums[1][1] = GET_XY(2, 2) + GET_XY(2, 3) + GET_XY(3, 2) + GET_XY(3, 3);1593915940upper_avg = (sums[0][0] + sums[1][0] + 4) / 8;15941lower_avg = (sums[0][1] + sums[1][1] + 4) / 8;15942left_avg = (sums[0][0] + sums[0][1] + 4) / 8;15943right_avg = (sums[1][0] + sums[1][1] + 4) / 8;1594415945#undef GET_XY15946#define GET_XY(x, y, a) gray_distance2(pSrc_pixels[(x) + ((y) * 4)], a)1594715948int upper_gray_dist = 0, lower_gray_dist = 0, left_gray_dist = 0, right_gray_dist = 0;15949for (uint32_t i = 0; i < 4; i++)15950{15951for (uint32_t j = 0; j < 2; j++)15952{15953upper_gray_dist += GET_XY(i, j, upper_avg);15954lower_gray_dist += GET_XY(i, 2 + j, lower_avg);15955left_gray_dist += GET_XY(j, i, left_avg);15956right_gray_dist += GET_XY(2 + j, i, right_avg);15957}15958}1595915960#undef GET_XY1596115962int upper_lower_sum = upper_gray_dist + lower_gray_dist;15963int left_right_sum = left_gray_dist + right_gray_dist;1596415965return upper_lower_sum < left_right_sum;15966}1596715968// Base Sel Table15969// XXXXX XX XXX15970static const uint16_t g_etc1_y_solid_block_configs[256] =15971{159720,781,64,161,260,192,33,131,96,320,65,162,261,193,34,291,97,224,66,163,262,194,35,549,98,4,67,653,164,195,523,36,99,5,578,68,165,353,196,37,135,100,324,69,166,354,197,38,295,101,228,70,167,15973355,198,39,553,102,8,71,608,168,199,527,40,103,9,582,72,169,357,200,41,139,104,328,73,170,358,201,42,299,105,232,74,171,359,202,43,557,106,12,75,612,172,203,531,44,107,13,586,76,173,361,15974204,45,143,108,332,77,174,362,205,46,303,109,236,78,175,363,206,47,561,110,16,79,616,176,207,535,48,111,17,590,80,177,365,208,49,147,112,336,81,178,366,209,50,307,113,240,82,179,367,210,1597551,565,114,20,83,620,180,211,539,52,115,21,594,84,181,369,212,53,151,116,340,85,182,370,213,54,311,117,244,86,183,371,214,55,569,118,24,87,624,184,215,543,56,119,25,598,88,185,373,216,57,15976155,120,344,89,186,374,217,58,315,121,248,90,187,375,218,59,573,122,28,91,628,188,219,754,60,123,29,602,92,189,377,220,61,159,124,348,93,190,378,221,62,319,125,252,94,191,379,222,63,882,12615977};1597815979// individual15980// table base sel0 sel1 sel2 sel315981static const uint16_t g_etc1_y_solid_block_4i_configs[256] =15982{159830xA000,0xA800,0x540B,0xAA01,0xAA01,0xFE00,0xFF00,0xFF00,0x8,0x5515,0x5509,0x5509,0xAA03,0x5508,0x5508,0x9508,0xA508,0xA908,0xAA08,0x5513,0xAA09,0xAA09,0xAA05,0xFF08,0xFF08,0x10,0x551D,0x5511,0x5511,159840xAA0B,0x5510,0x5510,0x9510,0xA510,0xA910,0xAA10,0x551B,0xAA11,0xAA11,0xAA0D,0xFF10,0xFF10,0x18,0x5525,0x5519,0x5519,0xAA13,0x5518,0x5518,0x9518,0xA518,0xA918,0xAA18,0x5523,0xAA19,0xAA19,0xAA15,159850xFF18,0xFF18,0x20,0x552D,0x5521,0x5521,0xAA1B,0x5520,0x5520,0x9520,0xA520,0xA920,0xAA20,0x552B,0xAA21,0xAA21,0xAA1D,0xFF20,0xFF20,0x28,0x5535,0x5529,0x5529,0xAA23,0x5528,0x5528,0x9528,0xA528,0xA928,159860xAA28,0x5533,0xAA29,0xAA29,0xAA25,0xFF28,0xFF28,0x30,0x553D,0x5531,0x5531,0xAA2B,0x5530,0x5530,0x9530,0xA530,0xA930,0xAA30,0x553B,0xAA31,0xAA31,0xAA2D,0xFF30,0xFF30,0x38,0x5545,0x5539,0x5539,0xAA33,159870x5538,0x5538,0x9538,0xA538,0xA938,0xAA38,0x5543,0xAA39,0xAA39,0xAA35,0xFF38,0xFF38,0x40,0x554D,0x5541,0x5541,0xAA3B,0x5540,0x5540,0x9540,0xA540,0xA940,0xAA40,0x554B,0xAA41,0xAA41,0xAA3D,0xFF40,0xFF40,159880x48,0x5555,0x5549,0x5549,0xAA43,0x5548,0x5548,0x9548,0xA548,0xA948,0xAA48,0x5553,0xAA49,0xAA49,0xAA45,0xFF48,0xFF48,0x50,0x555D,0x5551,0x5551,0xAA4B,0x5550,0x5550,0x9550,0xA550,0xA950,0xAA50,0x555B,159890xAA51,0xAA51,0xAA4D,0xFF50,0xFF50,0x58,0x5565,0x5559,0x5559,0xAA53,0x5558,0x5558,0x9558,0xA558,0xA958,0xAA58,0x5563,0xAA59,0xAA59,0xAA55,0xFF58,0xFF58,0x60,0x556D,0x5561,0x5561,0xAA5B,0x5560,0x5560,159900x9560,0xA560,0xA960,0xAA60,0x556B,0xAA61,0xAA61,0xAA5D,0xFF60,0xFF60,0x68,0x5575,0x5569,0x5569,0xAA63,0x5568,0x5568,0x9568,0xA568,0xA968,0xAA68,0x5573,0xAA69,0xAA69,0xAA65,0xFF68,0xFF68,0x70,0x557D,159910x5571,0x5571,0xAA6B,0x5570,0x5570,0x9570,0xA570,0xA970,0xAA70,0x557B,0xAA71,0xAA71,0xAA6D,0xFF70,0xFF70,0x78,0x78,0x5579,0x5579,0xAA73,0x5578,0x9578,0x2578,0xE6E,0x27815992};1599315994static const uint16_t g_etc1_y_solid_block_2i_configs[256] =15995{159960x416,0x800,0xA00,0x50B,0xA01,0xA01,0xF00,0xF00,0xF00,0x8,0x515,0x509,0x509,0xA03,0x508,0x508,0xF01,0xF01,0xA08,0xA08,0x513,0xA09,0xA09,0xA05,0xF08,0xF08,0x10,0x51D,0x511,0x511,0xA0B,0x510,0x510,0xF09,159970xF09,0xA10,0xA10,0x51B,0xA11,0xA11,0xA0D,0xF10,0xF10,0x18,0x525,0x519,0x519,0xA13,0x518,0x518,0xF11,0xF11,0xA18,0xA18,0x523,0xA19,0xA19,0xA15,0xF18,0xF18,0x20,0x52D,0x521,0x521,0xA1B,0x520,0x520,0xF19,159980xF19,0xA20,0xA20,0x52B,0xA21,0xA21,0xA1D,0xF20,0xF20,0x28,0x535,0x529,0x529,0xA23,0x528,0x528,0xF21,0xF21,0xA28,0xA28,0x533,0xA29,0xA29,0xA25,0xF28,0xF28,0x30,0x53D,0x531,0x531,0xA2B,0x530,0x530,0xF29,159990xF29,0xA30,0xA30,0x53B,0xA31,0xA31,0xA2D,0xF30,0xF30,0x38,0x545,0x539,0x539,0xA33,0x538,0x538,0xF31,0xF31,0xA38,0xA38,0x543,0xA39,0xA39,0xA35,0xF38,0xF38,0x40,0x54D,0x541,0x541,0xA3B,0x540,0x540,0xF39,160000xF39,0xA40,0xA40,0x54B,0xA41,0xA41,0xA3D,0xF40,0xF40,0x48,0x555,0x549,0x549,0xA43,0x548,0x548,0xF41,0xF41,0xA48,0xA48,0x553,0xA49,0xA49,0xA45,0xF48,0xF48,0x50,0x55D,0x551,0x551,0xA4B,0x550,0x550,0xF49,160010xF49,0xA50,0xA50,0x55B,0xA51,0xA51,0xA4D,0xF50,0xF50,0x58,0x565,0x559,0x559,0xA53,0x558,0x558,0xF51,0xF51,0xA58,0xA58,0x563,0xA59,0xA59,0xA55,0xF58,0xF58,0x60,0x56D,0x561,0x561,0xA5B,0x560,0x560,0xF59,160020xF59,0xA60,0xA60,0x56B,0xA61,0xA61,0xA5D,0xF60,0xF60,0x68,0x575,0x569,0x569,0xA63,0x568,0x568,0xF61,0xF61,0xA68,0xA68,0x573,0xA69,0xA69,0xA65,0xF68,0xF68,0x70,0x57D,0x571,0x571,0xA6B,0x570,0x570,0xF69,160030xF69,0xA70,0xA70,0x57B,0xA71,0xA71,0xA6D,0xF70,0xF70,0x78,0x78,0x579,0x579,0xA73,0x578,0x578,0xE6E,0x27816004};1600516006static const uint16_t g_etc1_y_solid_block_1i_configs[256] =16007{160080x0,0x116,0x200,0x200,0x10B,0x201,0x201,0x300,0x300,0x8,0x115,0x109,0x109,0x203,0x108,0x108,0x114,0x301,0x204,0x208,0x208,0x113,0x209,0x209,0x205,0x308,0x10,0x11D,0x111,0x111,0x20B,0x110,0x110,0x11C,0x309,160090x20C,0x210,0x210,0x11B,0x211,0x211,0x20D,0x310,0x18,0x125,0x119,0x119,0x213,0x118,0x118,0x124,0x311,0x214,0x218,0x218,0x123,0x219,0x219,0x215,0x318,0x20,0x12D,0x121,0x121,0x21B,0x120,0x120,0x12C,0x319,0x21C,160100x220,0x220,0x12B,0x221,0x221,0x21D,0x320,0x28,0x135,0x129,0x129,0x223,0x128,0x128,0x134,0x321,0x224,0x228,0x228,0x133,0x229,0x229,0x225,0x328,0x30,0x13D,0x131,0x131,0x22B,0x130,0x130,0x13C,0x329,0x22C,0x230,160110x230,0x13B,0x231,0x231,0x22D,0x330,0x38,0x145,0x139,0x139,0x233,0x138,0x138,0x144,0x331,0x234,0x238,0x238,0x143,0x239,0x239,0x235,0x338,0x40,0x14D,0x141,0x141,0x23B,0x140,0x140,0x14C,0x339,0x23C,0x240,0x240,160120x14B,0x241,0x241,0x23D,0x340,0x48,0x155,0x149,0x149,0x243,0x148,0x148,0x154,0x341,0x244,0x248,0x248,0x153,0x249,0x249,0x245,0x348,0x50,0x15D,0x151,0x151,0x24B,0x150,0x150,0x15C,0x349,0x24C,0x250,0x250,0x15B,160130x251,0x251,0x24D,0x350,0x58,0x165,0x159,0x159,0x253,0x158,0x158,0x164,0x351,0x254,0x258,0x258,0x163,0x259,0x259,0x255,0x358,0x60,0x16D,0x161,0x161,0x25B,0x160,0x160,0x16C,0x359,0x25C,0x260,0x260,0x16B,0x261,160140x261,0x25D,0x360,0x68,0x175,0x169,0x169,0x263,0x168,0x168,0x174,0x361,0x264,0x268,0x268,0x173,0x269,0x269,0x265,0x368,0x70,0x17D,0x171,0x171,0x26B,0x170,0x170,0x17C,0x369,0x26C,0x270,0x270,0x17B,0x271,0x271,160150x26D,0x370,0x78,0x78,0x179,0x179,0x273,0x178,0x178,0x26E,0x27816016};1601716018// We don't have any useful hints to accelerate single channel ETC1, so we need to real-time encode from scratch.16019bool transcode_uastc_to_etc1(const uastc_block& src_blk, void* pDst, uint32_t channel)16020{16021unpacked_uastc_block unpacked_src_blk;16022if (!unpack_uastc(src_blk, unpacked_src_blk, false))16023return false;1602416025#if 016026for (uint32_t individ = 0; individ < 2; individ++)16027{16028uint32_t overall_error = 0;1602916030for (uint32_t c = 0; c < 256; c++)16031{16032uint32_t best_err = UINT32_MAX;16033uint32_t best_individ = 0;16034uint32_t best_base = 0;16035uint32_t best_sels[4] = { 0,0,0,0 };16036uint32_t best_table = 0;1603716038const uint32_t limit = individ ? 16 : 32;1603916040for (uint32_t table = 0; table < 8; table++)16041{16042for (uint32_t base = 0; base < limit; base++)16043{16044uint32_t total_e = 0;16045uint32_t sels[4] = { 0,0,0,0 };1604616047const uint32_t N = 4;16048for (uint32_t i = 0; i < basisu::minimum<uint32_t>(N, (256 - c)); i++)16049{16050uint32_t best_sel_e = UINT32_MAX;16051uint32_t best_sel = 0;1605216053for (uint32_t sel = 0; sel < 4; sel++)16054{16055int val = individ ? ((base << 4) | base) : ((base << 3) | (base >> 2));16056val = clamp255(val + g_etc1_inten_tables[table][sel]);1605716058int e = iabs(val - clamp255(c + i));16059if (e < best_sel_e)16060{16061best_sel_e = e;16062best_sel = sel;16063}1606416065} // sel1606616067sels[i] = best_sel;16068total_e += best_sel_e * best_sel_e;1606916070} // i1607116072if (total_e < best_err)16073{16074best_err = total_e;16075best_individ = individ;16076best_base = base;16077memcpy(best_sels, sels, sizeof(best_sels));16078best_table = table;16079}1608016081} // base16082} // table1608316084//printf("%u: %u,%u,%u,%u,%u,%u,%u,%u\n", c, best_err, best_individ, best_table, best_base, best_sels[0], best_sels[1], best_sels[2], best_sels[3]);1608516086uint32_t encoded = best_table | (best_base << 3) |16087(best_sels[0] << 8) |16088(best_sels[1] << 10) |16089(best_sels[2] << 12) |16090(best_sels[3] << 14);1609116092printf("0x%X,", encoded);1609316094overall_error += best_err;16095} // c1609616097printf("\n");16098printf("Overall error: %u\n", overall_error);1609916100} // individ1610116102exit(0);16103#endif1610416105#if 016106for (uint32_t individ = 0; individ < 2; individ++)16107{16108uint32_t overall_error = 0;1610916110for (uint32_t c = 0; c < 256; c++)16111{16112uint32_t best_err = UINT32_MAX;16113uint32_t best_individ = 0;16114uint32_t best_base = 0;16115uint32_t best_sels[4] = { 0,0,0,0 };16116uint32_t best_table = 0;1611716118const uint32_t limit = individ ? 16 : 32;1611916120for (uint32_t table = 0; table < 8; table++)16121{16122for (uint32_t base = 0; base < limit; base++)16123{16124uint32_t total_e = 0;16125uint32_t sels[4] = { 0,0,0,0 };1612616127const uint32_t N = 1;16128for (uint32_t i = 0; i < basisu::minimum<uint32_t>(N, (256 - c)); i++)16129{16130uint32_t best_sel_e = UINT32_MAX;16131uint32_t best_sel = 0;1613216133for (uint32_t sel = 0; sel < 4; sel++)16134{16135int val = individ ? ((base << 4) | base) : ((base << 3) | (base >> 2));16136val = clamp255(val + g_etc1_inten_tables[table][sel]);1613716138int e = iabs(val - clamp255(c + i));16139if (e < best_sel_e)16140{16141best_sel_e = e;16142best_sel = sel;16143}1614416145} // sel1614616147sels[i] = best_sel;16148total_e += best_sel_e * best_sel_e;1614916150} // i1615116152if (total_e < best_err)16153{16154best_err = total_e;16155best_individ = individ;16156best_base = base;16157memcpy(best_sels, sels, sizeof(best_sels));16158best_table = table;16159}1616016161} // base16162} // table1616316164//printf("%u: %u,%u,%u,%u,%u,%u,%u,%u\n", c, best_err, best_individ, best_table, best_base, best_sels[0], best_sels[1], best_sels[2], best_sels[3]);1616516166uint32_t encoded = best_table | (best_base << 3) |16167(best_sels[0] << 8) |16168(best_sels[1] << 10) |16169(best_sels[2] << 12) |16170(best_sels[3] << 14);1617116172printf("0x%X,", encoded);1617316174overall_error += best_err;16175} // c1617616177printf("\n");16178printf("Overall error: %u\n", overall_error);1617916180} // individ1618116182exit(0);16183#endif1618416185decoder_etc_block& dst_blk = *static_cast<decoder_etc_block*>(pDst);1618616187if (unpacked_src_blk.m_mode == UASTC_MODE_INDEX_SOLID_COLOR)16188{16189const uint32_t y = unpacked_src_blk.m_solid_color[channel];16190const uint32_t encoded_config = g_etc1_y_solid_block_configs[y];1619116192const uint32_t base = encoded_config & 31;16193const uint32_t sel = (encoded_config >> 5) & 3;16194const uint32_t table = encoded_config >> 7;1619516196dst_blk.m_bytes[3] = (uint8_t)(2 | (table << 5) | (table << 2));1619716198dst_blk.m_bytes[0] = (uint8_t)(base << 3);16199dst_blk.m_bytes[1] = (uint8_t)(base << 3);16200dst_blk.m_bytes[2] = (uint8_t)(base << 3);1620116202memcpy(dst_blk.m_bytes + 4, &s_etc1_solid_selectors[sel][0], 4);16203return true;16204}1620516206color32 block_pixels[4][4];16207const bool unpack_srgb = false;16208if (!unpack_uastc(unpacked_src_blk, &block_pixels[0][0], unpack_srgb))16209return false;1621016211uint8_t block_y[4][4];16212for (uint32_t i = 0; i < 16; i++)16213((uint8_t*)block_y)[i] = ((color32*)block_pixels)[i][channel];1621416215int upper_avg, lower_avg, left_avg, right_avg;16216bool flip = pack_etc1_y_estimate_flipped(&block_y[0][0], upper_avg, lower_avg, left_avg, right_avg);1621716218// non-flipped: | |16219// vs.16220// flipped: --16221// --1622216223uint32_t low[2] = { 255, 255 }, high[2] = { 0, 0 };1622416225if (flip)16226{16227for (uint32_t y = 0; y < 2; y++)16228{16229for (uint32_t x = 0; x < 4; x++)16230{16231const uint32_t v = block_y[y][x];16232low[0] = basisu::minimum(low[0], v);16233high[0] = basisu::maximum(high[0], v);16234}16235}16236for (uint32_t y = 2; y < 4; y++)16237{16238for (uint32_t x = 0; x < 4; x++)16239{16240const uint32_t v = block_y[y][x];16241low[1] = basisu::minimum(low[1], v);16242high[1] = basisu::maximum(high[1], v);16243}16244}16245}16246else16247{16248for (uint32_t y = 0; y < 4; y++)16249{16250for (uint32_t x = 0; x < 2; x++)16251{16252const uint32_t v = block_y[y][x];16253low[0] = basisu::minimum(low[0], v);16254high[0] = basisu::maximum(high[0], v);16255}16256}16257for (uint32_t y = 0; y < 4; y++)16258{16259for (uint32_t x = 2; x < 4; x++)16260{16261const uint32_t v = block_y[y][x];16262low[1] = basisu::minimum(low[1], v);16263high[1] = basisu::maximum(high[1], v);16264}16265}16266}1626716268const uint32_t range[2] = { high[0] - low[0], high[1] - low[1] };1626916270dst_blk.m_bytes[3] = (uint8_t)((int)flip);1627116272if ((range[0] <= 3) && (range[1] <= 3))16273{16274// This is primarily for better gradients.16275dst_blk.m_bytes[0] = 0;16276dst_blk.m_bytes[1] = 0;16277dst_blk.m_bytes[2] = 0;1627816279uint16_t l_bitmask = 0, h_bitmask = 0;1628016281for (uint32_t subblock = 0; subblock < 2; subblock++)16282{16283const uint32_t encoded = (range[subblock] == 0) ? g_etc1_y_solid_block_1i_configs[low[subblock]] : ((range[subblock] < 2) ? g_etc1_y_solid_block_2i_configs[low[subblock]] : g_etc1_y_solid_block_4i_configs[low[subblock]]);1628416285const uint32_t table = encoded & 7;16286const uint32_t base = (encoded >> 3) & 31;16287assert(base <= 15);16288const uint32_t sels[4] = { (encoded >> 8) & 3, (encoded >> 10) & 3, (encoded >> 12) & 3, (encoded >> 14) & 3 };1628916290dst_blk.m_bytes[3] |= (uint8_t)(table << (subblock ? 2 : 5));1629116292const uint32_t sv = base << (subblock ? 0 : 4);16293dst_blk.m_bytes[0] |= (uint8_t)(sv);16294dst_blk.m_bytes[1] |= (uint8_t)(sv);16295dst_blk.m_bytes[2] |= (uint8_t)(sv);1629616297if (flip)16298{16299uint32_t ofs = subblock * 2;16300for (uint32_t y = 0; y < 2; y++)16301{16302for (uint32_t x = 0; x < 4; x++)16303{16304uint32_t t = block_y[y + subblock * 2][x];16305assert(t >= low[subblock] && t <= high[subblock]);16306t -= low[subblock];16307assert(t <= 3);1630816309t = g_selector_index_to_etc1[sels[t]];1631016311assert(ofs < 16);16312l_bitmask |= ((t & 1) << ofs);16313h_bitmask |= ((t >> 1) << ofs);16314ofs += 4;16315}1631616317ofs = (int)ofs + 1 - 4 * 4;16318}16319}16320else16321{16322uint32_t ofs = (subblock * 2) * 4;16323for (uint32_t x = 0; x < 2; x++)16324{16325for (uint32_t y = 0; y < 4; y++)16326{16327uint32_t t = block_y[y][x + subblock * 2];16328assert(t >= low[subblock] && t <= high[subblock]);16329t -= low[subblock];16330assert(t <= 3);1633116332t = g_selector_index_to_etc1[sels[t]];1633316334assert(ofs < 16);16335l_bitmask |= ((t & 1) << ofs);16336h_bitmask |= ((t >> 1) << ofs);16337++ofs;16338}16339}16340}16341} // subblock1634216343dst_blk.m_bytes[7] = (uint8_t)(l_bitmask);16344dst_blk.m_bytes[6] = (uint8_t)(l_bitmask >> 8);16345dst_blk.m_bytes[5] = (uint8_t)(h_bitmask);16346dst_blk.m_bytes[4] = (uint8_t)(h_bitmask >> 8);1634716348return true;16349}1635016351uint32_t y0 = ((flip ? upper_avg : left_avg) * 31 + 127) / 255;16352uint32_t y1 = ((flip ? lower_avg : right_avg) * 31 + 127) / 255;1635316354bool diff = true;1635516356int dy = y1 - y0;1635716358if ((dy < cETC1ColorDeltaMin) || (dy > cETC1ColorDeltaMax))16359{16360diff = false;1636116362y0 = ((flip ? upper_avg : left_avg) * 15 + 127) / 255;16363y1 = ((flip ? lower_avg : right_avg) * 15 + 127) / 255;1636416365dst_blk.m_bytes[0] = (uint8_t)(y1 | (y0 << 4));16366dst_blk.m_bytes[1] = (uint8_t)(y1 | (y0 << 4));16367dst_blk.m_bytes[2] = (uint8_t)(y1 | (y0 << 4));16368}16369else16370{16371dy = basisu::clamp<int>(dy, cETC1ColorDeltaMin, cETC1ColorDeltaMax);1637216373y1 = y0 + dy;1637416375if (dy < 0) dy += 8;1637616377dst_blk.m_bytes[0] = (uint8_t)((y0 << 3) | dy);16378dst_blk.m_bytes[1] = (uint8_t)((y0 << 3) | dy);16379dst_blk.m_bytes[2] = (uint8_t)((y0 << 3) | dy);1638016381dst_blk.m_bytes[3] |= 2;16382}1638316384const uint32_t base_y[2] = { diff ? ((y0 << 3) | (y0 >> 2)) : ((y0 << 4) | y0), diff ? ((y1 << 3) | (y1 >> 2)) : ((y1 << 4) | y1) };1638516386uint32_t enc_range[2];16387for (uint32_t subset = 0; subset < 2; subset++)16388{16389const int pos = basisu::iabs((int)high[subset] - (int)base_y[subset]);16390const int neg = basisu::iabs((int)base_y[subset] - (int)low[subset]);1639116392enc_range[subset] = basisu::maximum(pos, neg);16393}1639416395uint16_t l_bitmask = 0, h_bitmask = 0;16396for (uint32_t subblock = 0; subblock < 2; subblock++)16397{16398if ((!diff) && (range[subblock] <= 3))16399{16400const uint32_t encoded = (range[subblock] == 0) ? g_etc1_y_solid_block_1i_configs[low[subblock]] : ((range[subblock] < 2) ? g_etc1_y_solid_block_2i_configs[low[subblock]] : g_etc1_y_solid_block_4i_configs[low[subblock]]);1640116402const uint32_t table = encoded & 7;16403const uint32_t base = (encoded >> 3) & 31;16404assert(base <= 15);16405const uint32_t sels[4] = { (encoded >> 8) & 3, (encoded >> 10) & 3, (encoded >> 12) & 3, (encoded >> 14) & 3 };1640616407dst_blk.m_bytes[3] |= (uint8_t)(table << (subblock ? 2 : 5));1640816409const uint32_t mask = ~(0xF << (subblock ? 0 : 4));1641016411dst_blk.m_bytes[0] &= mask;16412dst_blk.m_bytes[1] &= mask;16413dst_blk.m_bytes[2] &= mask;1641416415const uint32_t sv = base << (subblock ? 0 : 4);16416dst_blk.m_bytes[0] |= (uint8_t)(sv);16417dst_blk.m_bytes[1] |= (uint8_t)(sv);16418dst_blk.m_bytes[2] |= (uint8_t)(sv);1641916420if (flip)16421{16422uint32_t ofs = subblock * 2;16423for (uint32_t y = 0; y < 2; y++)16424{16425for (uint32_t x = 0; x < 4; x++)16426{16427uint32_t t = block_y[y + subblock * 2][x];16428assert(t >= low[subblock] && t <= high[subblock]);16429t -= low[subblock];16430assert(t <= 3);1643116432t = g_selector_index_to_etc1[sels[t]];1643316434assert(ofs < 16);16435l_bitmask |= ((t & 1) << ofs);16436h_bitmask |= ((t >> 1) << ofs);16437ofs += 4;16438}1643916440ofs = (int)ofs + 1 - 4 * 4;16441}16442}16443else16444{16445uint32_t ofs = (subblock * 2) * 4;16446for (uint32_t x = 0; x < 2; x++)16447{16448for (uint32_t y = 0; y < 4; y++)16449{16450uint32_t t = block_y[y][x + subblock * 2];16451assert(t >= low[subblock] && t <= high[subblock]);16452t -= low[subblock];16453assert(t <= 3);1645416455t = g_selector_index_to_etc1[sels[t]];1645616457assert(ofs < 16);16458l_bitmask |= ((t & 1) << ofs);16459h_bitmask |= ((t >> 1) << ofs);16460++ofs;16461}16462}16463}1646416465continue;16466} // if1646716468uint32_t best_err = UINT32_MAX;16469uint8_t best_sels[8];16470uint32_t best_inten = 0;1647116472const int base = base_y[subblock];1647316474const int low_limit = -base;16475const int high_limit = 255 - base;1647616477assert(low_limit <= 0 && high_limit >= 0);1647816479uint32_t inten_table_mask = 0xFF;16480const uint32_t er = enc_range[subblock];16481// Each one of these tables is expensive to evaluate, so let's only examine the ones we know may be useful.16482if (er <= 51)16483{16484inten_table_mask = 0xF;1648516486if (er > 22)16487inten_table_mask &= ~(1 << 0);1648816489if ((er < 4) || (er > 39))16490inten_table_mask &= ~(1 << 1);1649116492if (er < 9)16493inten_table_mask &= ~(1 << 2);1649416495if (er < 12)16496inten_table_mask &= ~(1 << 3);16497}16498else16499{16500inten_table_mask &= ~((1 << 0) | (1 << 1));1650116502if (er > 60)16503inten_table_mask &= ~(1 << 2);1650416505if (er > 89)16506inten_table_mask &= ~(1 << 3);1650716508if (er > 120)16509inten_table_mask &= ~(1 << 4);1651016511if (er > 136)16512inten_table_mask &= ~(1 << 5);1651316514if (er > 174)16515inten_table_mask &= ~(1 << 6);16516}1651716518for (uint32_t inten = 0; inten < 8; inten++)16519{16520if ((inten_table_mask & (1 << inten)) == 0)16521continue;1652216523const int t0 = basisu::maximum(low_limit, g_etc1_inten_tables[inten][0]);16524const int t1 = basisu::maximum(low_limit, g_etc1_inten_tables[inten][1]);16525const int t2 = basisu::minimum(high_limit, g_etc1_inten_tables[inten][2]);16526const int t3 = basisu::minimum(high_limit, g_etc1_inten_tables[inten][3]);16527assert((t0 <= t1) && (t1 <= t2) && (t2 <= t3));1652816529const int tv[4] = { t2, t3, t1, t0 };1653016531const int thresh01 = t0 + t1;16532const int thresh12 = t1 + t2;16533const int thresh23 = t2 + t3;1653416535assert(thresh01 <= thresh12 && thresh12 <= thresh23);1653616537static const uint8_t s_table[4] = { 1, 0, 2, 3 };1653816539uint32_t total_err = 0;16540uint8_t sels[8];1654116542if (flip)16543{16544if (((int)high[subblock] - base) * 2 < thresh01)16545{16546memset(sels, 3, 8);1654716548for (uint32_t y = 0; y < 2; y++)16549{16550for (uint32_t x = 0; x < 4; x++)16551{16552const int delta = (int)block_y[y + subblock * 2][x] - base;1655316554const uint32_t c = 3;1655516556uint32_t e = basisu::iabs(tv[c] - delta);16557total_err += e * e;16558}16559if (total_err >= best_err)16560break;16561}16562}16563else if (((int)low[subblock] - base) * 2 >= thresh23)16564{16565memset(sels, 1, 8);1656616567for (uint32_t y = 0; y < 2; y++)16568{16569for (uint32_t x = 0; x < 4; x++)16570{16571const int delta = (int)block_y[y + subblock * 2][x] - base;1657216573const uint32_t c = 1;1657416575uint32_t e = basisu::iabs(tv[c] - delta);16576total_err += e * e;16577}16578if (total_err >= best_err)16579break;16580}16581}16582else16583{16584for (uint32_t y = 0; y < 2; y++)16585{16586for (uint32_t x = 0; x < 4; x++)16587{16588const int delta = (int)block_y[y + subblock * 2][x] - base;16589const int delta2 = delta * 2;1659016591uint32_t c = s_table[(delta2 < thresh01) + (delta2 < thresh12) + (delta2 < thresh23)];16592sels[y * 4 + x] = (uint8_t)c;1659316594uint32_t e = basisu::iabs(tv[c] - delta);16595total_err += e * e;16596}16597if (total_err >= best_err)16598break;16599}16600}16601}16602else16603{16604if (((int)high[subblock] - base) * 2 < thresh01)16605{16606memset(sels, 3, 8);1660716608for (uint32_t y = 0; y < 4; y++)16609{16610for (uint32_t x = 0; x < 2; x++)16611{16612const int delta = (int)block_y[y][x + subblock * 2] - base;1661316614const uint32_t c = 3;1661516616uint32_t e = basisu::iabs(tv[c] - delta);16617total_err += e * e;16618}16619if (total_err >= best_err)16620break;16621}16622}16623else if (((int)low[subblock] - base) * 2 >= thresh23)16624{16625memset(sels, 1, 8);1662616627for (uint32_t y = 0; y < 4; y++)16628{16629for (uint32_t x = 0; x < 2; x++)16630{16631const int delta = (int)block_y[y][x + subblock * 2] - base;1663216633const uint32_t c = 1;1663416635uint32_t e = basisu::iabs(tv[c] - delta);16636total_err += e * e;16637}16638if (total_err >= best_err)16639break;16640}16641}16642else16643{16644for (uint32_t y = 0; y < 4; y++)16645{16646for (uint32_t x = 0; x < 2; x++)16647{16648const int delta = (int)block_y[y][x + subblock * 2] - base;16649const int delta2 = delta * 2;1665016651uint32_t c = s_table[(delta2 < thresh01) + (delta2 < thresh12) + (delta2 < thresh23)];16652sels[y * 2 + x] = (uint8_t)c;1665316654uint32_t e = basisu::iabs(tv[c] - delta);16655total_err += e * e;16656}16657if (total_err >= best_err)16658break;16659}16660}16661}1666216663if (total_err < best_err)16664{16665best_err = total_err;16666best_inten = inten;16667memcpy(best_sels, sels, 8);16668}1666916670} // inten1667116672//g_inten_hist[best_inten][enc_range[subblock]]++;1667316674dst_blk.m_bytes[3] |= (uint8_t)(best_inten << (subblock ? 2 : 5));1667516676if (flip)16677{16678uint32_t ofs = subblock * 2;16679for (uint32_t y = 0; y < 2; y++)16680{16681for (uint32_t x = 0; x < 4; x++)16682{16683uint32_t t = best_sels[y * 4 + x];1668416685assert(ofs < 16);16686l_bitmask |= ((t & 1) << ofs);16687h_bitmask |= ((t >> 1) << ofs);16688ofs += 4;16689}1669016691ofs = (int)ofs + 1 - 4 * 4;16692}16693}16694else16695{16696uint32_t ofs = (subblock * 2) * 4;16697for (uint32_t x = 0; x < 2; x++)16698{16699for (uint32_t y = 0; y < 4; y++)16700{16701uint32_t t = best_sels[y * 2 + x];1670216703assert(ofs < 16);16704l_bitmask |= ((t & 1) << ofs);16705h_bitmask |= ((t >> 1) << ofs);16706++ofs;16707}16708}16709}1671016711} // subblock1671216713dst_blk.m_bytes[7] = (uint8_t)(l_bitmask);16714dst_blk.m_bytes[6] = (uint8_t)(l_bitmask >> 8);16715dst_blk.m_bytes[5] = (uint8_t)(h_bitmask);16716dst_blk.m_bytes[4] = (uint8_t)(h_bitmask >> 8);1671716718return true;16719}1672016721const uint32_t ETC2_EAC_MIN_VALUE_SELECTOR = 3, ETC2_EAC_MAX_VALUE_SELECTOR = 7;1672216723void transcode_uastc_to_etc2_eac_a8(unpacked_uastc_block& unpacked_src_blk, color32 block_pixels[4][4], void* pDst)16724{16725eac_block& dst = *static_cast<eac_block*>(pDst);16726const color32* pSrc_pixels = &block_pixels[0][0];1672716728if ((!g_uastc_mode_has_alpha[unpacked_src_blk.m_mode]) || (unpacked_src_blk.m_mode == UASTC_MODE_INDEX_SOLID_COLOR))16729{16730const uint32_t a = (unpacked_src_blk.m_mode == UASTC_MODE_INDEX_SOLID_COLOR) ? unpacked_src_blk.m_solid_color[3] : 255;1673116732dst.m_base = a;16733dst.m_table = 13;16734dst.m_multiplier = 1;1673516736memcpy(dst.m_selectors, g_etc2_eac_a8_sel4, sizeof(g_etc2_eac_a8_sel4));1673716738return;16739}1674016741uint32_t min_a = 255, max_a = 0;16742for (uint32_t i = 0; i < 16; i++)16743{16744min_a = basisu::minimum<uint32_t>(min_a, pSrc_pixels[i].a);16745max_a = basisu::maximum<uint32_t>(max_a, pSrc_pixels[i].a);16746}1674716748if (min_a == max_a)16749{16750dst.m_base = min_a;16751dst.m_table = 13;16752dst.m_multiplier = 1;1675316754memcpy(dst.m_selectors, g_etc2_eac_a8_sel4, sizeof(g_etc2_eac_a8_sel4));16755return;16756}1675716758const uint32_t table = unpacked_src_blk.m_etc2_hints & 0xF;16759const int multiplier = unpacked_src_blk.m_etc2_hints >> 4;1676016761assert(multiplier >= 1);1676216763dst.m_multiplier = multiplier;16764dst.m_table = table;1676516766const float range = (float)(g_eac_modifier_table[dst.m_table][ETC2_EAC_MAX_VALUE_SELECTOR] - g_eac_modifier_table[dst.m_table][ETC2_EAC_MIN_VALUE_SELECTOR]);16767const int center = (int)roundf(basisu::lerp((float)min_a, (float)max_a, (float)(0 - g_eac_modifier_table[dst.m_table][ETC2_EAC_MIN_VALUE_SELECTOR]) / range));1676816769dst.m_base = center;1677016771const int8_t* pTable = &g_eac_modifier_table[dst.m_table][0];1677216773uint32_t vals[8];16774for (uint32_t j = 0; j < 8; j++)16775vals[j] = clamp255(center + (pTable[j] * multiplier));1677616777uint64_t sels = 0;16778for (uint32_t i = 0; i < 16; i++)16779{16780const uint32_t a = block_pixels[i & 3][i >> 2].a;1678116782const uint32_t err0 = (basisu::iabs(vals[0] - a) << 3) | 0;16783const uint32_t err1 = (basisu::iabs(vals[1] - a) << 3) | 1;16784const uint32_t err2 = (basisu::iabs(vals[2] - a) << 3) | 2;16785const uint32_t err3 = (basisu::iabs(vals[3] - a) << 3) | 3;16786const uint32_t err4 = (basisu::iabs(vals[4] - a) << 3) | 4;16787const uint32_t err5 = (basisu::iabs(vals[5] - a) << 3) | 5;16788const uint32_t err6 = (basisu::iabs(vals[6] - a) << 3) | 6;16789const uint32_t err7 = (basisu::iabs(vals[7] - a) << 3) | 7;1679016791const uint32_t min_err = basisu::minimum(basisu::minimum(basisu::minimum(basisu::minimum(basisu::minimum(basisu::minimum(err0, err1, err2), err3), err4), err5), err6), err7);1679216793const uint64_t best_index = min_err & 7;16794sels |= (best_index << (45 - i * 3));16795}1679616797dst.set_selector_bits(sels);16798}1679916800bool transcode_uastc_to_etc2_rgba(const uastc_block& src_blk, void* pDst)16801{16802eac_block& dst_etc2_eac_a8_blk = *static_cast<eac_block*>(pDst);16803decoder_etc_block& dst_etc1_blk = static_cast<decoder_etc_block*>(pDst)[1];1680416805unpacked_uastc_block unpacked_src_blk;16806if (!unpack_uastc(src_blk, unpacked_src_blk, false))16807return false;1680816809color32 block_pixels[4][4];16810if (unpacked_src_blk.m_mode != UASTC_MODE_INDEX_SOLID_COLOR)16811{16812const bool unpack_srgb = false;16813if (!unpack_uastc(unpacked_src_blk, &block_pixels[0][0], unpack_srgb))16814return false;16815}1681616817transcode_uastc_to_etc2_eac_a8(unpacked_src_blk, block_pixels, &dst_etc2_eac_a8_blk);1681816819transcode_uastc_to_etc1(unpacked_src_blk, block_pixels, &dst_etc1_blk);1682016821return true;16822}1682316824static const uint8_t s_uastc5_to_bc1[32] = { 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1 };16825static const uint8_t s_uastc4_to_bc1[16] = { 0, 0, 0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 1, 1, 1 };16826static const uint8_t s_uastc3_to_bc1[8] = { 0, 0, 2, 2, 3, 3, 1, 1 };16827static const uint8_t s_uastc2_to_bc1[4] = { 0, 2, 3, 1 };16828static const uint8_t s_uastc1_to_bc1[2] = { 0, 1 };16829const uint8_t* s_uastc_to_bc1_weights[6] = { nullptr, s_uastc1_to_bc1, s_uastc2_to_bc1, s_uastc3_to_bc1, s_uastc4_to_bc1, s_uastc5_to_bc1 };1683016831void encode_bc4(void* pDst, const uint8_t* pPixels, uint32_t stride)16832{16833uint32_t min0_v, max0_v, min1_v, max1_v,min2_v, max2_v, min3_v, max3_v;1683416835{16836min0_v = max0_v = pPixels[0 * stride];16837min1_v = max1_v = pPixels[1 * stride];16838min2_v = max2_v = pPixels[2 * stride];16839min3_v = max3_v = pPixels[3 * stride];16840}1684116842{16843uint32_t v0 = pPixels[4 * stride]; min0_v = basisu::minimum(min0_v, v0); max0_v = basisu::maximum(max0_v, v0);16844uint32_t v1 = pPixels[5 * stride]; min1_v = basisu::minimum(min1_v, v1); max1_v = basisu::maximum(max1_v, v1);16845uint32_t v2 = pPixels[6 * stride]; min2_v = basisu::minimum(min2_v, v2); max2_v = basisu::maximum(max2_v, v2);16846uint32_t v3 = pPixels[7 * stride]; min3_v = basisu::minimum(min3_v, v3); max3_v = basisu::maximum(max3_v, v3);16847}1684816849{16850uint32_t v0 = pPixels[8 * stride]; min0_v = basisu::minimum(min0_v, v0); max0_v = basisu::maximum(max0_v, v0);16851uint32_t v1 = pPixels[9 * stride]; min1_v = basisu::minimum(min1_v, v1); max1_v = basisu::maximum(max1_v, v1);16852uint32_t v2 = pPixels[10 * stride]; min2_v = basisu::minimum(min2_v, v2); max2_v = basisu::maximum(max2_v, v2);16853uint32_t v3 = pPixels[11 * stride]; min3_v = basisu::minimum(min3_v, v3); max3_v = basisu::maximum(max3_v, v3);16854}1685516856{16857uint32_t v0 = pPixels[12 * stride]; min0_v = basisu::minimum(min0_v, v0); max0_v = basisu::maximum(max0_v, v0);16858uint32_t v1 = pPixels[13 * stride]; min1_v = basisu::minimum(min1_v, v1); max1_v = basisu::maximum(max1_v, v1);16859uint32_t v2 = pPixels[14 * stride]; min2_v = basisu::minimum(min2_v, v2); max2_v = basisu::maximum(max2_v, v2);16860uint32_t v3 = pPixels[15 * stride]; min3_v = basisu::minimum(min3_v, v3); max3_v = basisu::maximum(max3_v, v3);16861}1686216863const uint32_t min_v = basisu::minimum(min0_v, min1_v, min2_v, min3_v);16864const uint32_t max_v = basisu::maximum(max0_v, max1_v, max2_v, max3_v);1686516866uint8_t* pDst_bytes = static_cast<uint8_t*>(pDst);16867pDst_bytes[0] = (uint8_t)max_v;16868pDst_bytes[1] = (uint8_t)min_v;1686916870if (max_v == min_v)16871{16872memset(pDst_bytes + 2, 0, 6);16873return;16874}1687516876const uint32_t delta = max_v - min_v;1687716878// min_v is now 0. Compute thresholds between values by scaling max_v. It's x14 because we're adding two x7 scale factors.16879const int t0 = delta * 13;16880const int t1 = delta * 11;16881const int t2 = delta * 9;16882const int t3 = delta * 7;16883const int t4 = delta * 5;16884const int t5 = delta * 3;16885const int t6 = delta * 1;1688616887// BC4 floors in its divisions, which we compensate for with the 4 bias.16888// This function is optimal for all possible inputs (i.e. it outputs the same results as checking all 8 values and choosing the closest one).16889const int bias = 4 - min_v * 14;1689016891static const uint32_t s_tran0[8] = { 1U , 7U , 6U , 5U , 4U , 3U , 2U , 0U };16892static const uint32_t s_tran1[8] = { 1U << 3U, 7U << 3U, 6U << 3U, 5U << 3U, 4U << 3U, 3U << 3U, 2U << 3U, 0U << 3U };16893static const uint32_t s_tran2[8] = { 1U << 6U, 7U << 6U, 6U << 6U, 5U << 6U, 4U << 6U, 3U << 6U, 2U << 6U, 0U << 6U };16894static const uint32_t s_tran3[8] = { 1U << 9U, 7U << 9U, 6U << 9U, 5U << 9U, 4U << 9U, 3U << 9U, 2U << 9U, 0U << 9U };1689516896uint64_t a0, a1, a2, a3;16897{16898const int v0 = pPixels[0 * stride] * 14 + bias;16899const int v1 = pPixels[1 * stride] * 14 + bias;16900const int v2 = pPixels[2 * stride] * 14 + bias;16901const int v3 = pPixels[3 * stride] * 14 + bias;16902a0 = s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)];16903a1 = s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)];16904a2 = s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)];16905a3 = s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)];16906}1690716908{16909const int v0 = pPixels[4 * stride] * 14 + bias;16910const int v1 = pPixels[5 * stride] * 14 + bias;16911const int v2 = pPixels[6 * stride] * 14 + bias;16912const int v3 = pPixels[7 * stride] * 14 + bias;16913a0 |= (s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)] << 12U);16914a1 |= (s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)] << 12U);16915a2 |= (s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)] << 12U);16916a3 |= (s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)] << 12U);16917}1691816919{16920const int v0 = pPixels[8 * stride] * 14 + bias;16921const int v1 = pPixels[9 * stride] * 14 + bias;16922const int v2 = pPixels[10 * stride] * 14 + bias;16923const int v3 = pPixels[11 * stride] * 14 + bias;16924a0 |= (((uint64_t)s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)]) << 24U);16925a1 |= (((uint64_t)s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)]) << 24U);16926a2 |= (((uint64_t)s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)]) << 24U);16927a3 |= (((uint64_t)s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)]) << 24U);16928}1692916930{16931const int v0 = pPixels[12 * stride] * 14 + bias;16932const int v1 = pPixels[13 * stride] * 14 + bias;16933const int v2 = pPixels[14 * stride] * 14 + bias;16934const int v3 = pPixels[15 * stride] * 14 + bias;16935a0 |= (((uint64_t)s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)]) << 36U);16936a1 |= (((uint64_t)s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)]) << 36U);16937a2 |= (((uint64_t)s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)]) << 36U);16938a3 |= (((uint64_t)s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)]) << 36U);16939}1694016941const uint64_t f = a0 | a1 | a2 | a3;1694216943pDst_bytes[2] = (uint8_t)f;16944pDst_bytes[3] = (uint8_t)(f >> 8U);16945pDst_bytes[4] = (uint8_t)(f >> 16U);16946pDst_bytes[5] = (uint8_t)(f >> 24U);16947pDst_bytes[6] = (uint8_t)(f >> 32U);16948pDst_bytes[7] = (uint8_t)(f >> 40U);16949}1695016951static void bc1_find_sels(const color32 *pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16])16952{16953uint32_t block_r[4], block_g[4], block_b[4];1695416955block_r[0] = (lr << 3) | (lr >> 2); block_g[0] = (lg << 2) | (lg >> 4); block_b[0] = (lb << 3) | (lb >> 2);16956block_r[3] = (hr << 3) | (hr >> 2); block_g[3] = (hg << 2) | (hg >> 4); block_b[3] = (hb << 3) | (hb >> 2);16957block_r[1] = (block_r[0] * 2 + block_r[3]) / 3; block_g[1] = (block_g[0] * 2 + block_g[3]) / 3; block_b[1] = (block_b[0] * 2 + block_b[3]) / 3;16958block_r[2] = (block_r[3] * 2 + block_r[0]) / 3; block_g[2] = (block_g[3] * 2 + block_g[0]) / 3; block_b[2] = (block_b[3] * 2 + block_b[0]) / 3;1695916960int ar = block_r[3] - block_r[0], ag = block_g[3] - block_g[0], ab = block_b[3] - block_b[0];1696116962int dots[4];16963for (uint32_t i = 0; i < 4; i++)16964dots[i] = (int)block_r[i] * ar + (int)block_g[i] * ag + (int)block_b[i] * ab;1696516966int t0 = dots[0] + dots[1], t1 = dots[1] + dots[2], t2 = dots[2] + dots[3];1696716968ar *= 2; ag *= 2; ab *= 2;1696916970for (uint32_t i = 0; i < 16; i++)16971{16972const int d = pSrc_pixels[i].r * ar + pSrc_pixels[i].g * ag + pSrc_pixels[i].b * ab;16973static const uint8_t s_sels[4] = { 3, 2, 1, 0 };1697416975// Rounding matters here!16976// d <= t0: <=, not <, to the later LS step "sees" a wider range of selectors. It matters for quality.16977sels[i] = s_sels[(d <= t0) + (d < t1) + (d < t2)];16978}16979}1698016981static inline void bc1_find_sels_2(const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16])16982{16983uint32_t block_r[4], block_g[4], block_b[4];1698416985block_r[0] = (lr << 3) | (lr >> 2); block_g[0] = (lg << 2) | (lg >> 4); block_b[0] = (lb << 3) | (lb >> 2);16986block_r[3] = (hr << 3) | (hr >> 2); block_g[3] = (hg << 2) | (hg >> 4); block_b[3] = (hb << 3) | (hb >> 2);16987block_r[1] = (block_r[0] * 2 + block_r[3]) / 3; block_g[1] = (block_g[0] * 2 + block_g[3]) / 3; block_b[1] = (block_b[0] * 2 + block_b[3]) / 3;16988block_r[2] = (block_r[3] * 2 + block_r[0]) / 3; block_g[2] = (block_g[3] * 2 + block_g[0]) / 3; block_b[2] = (block_b[3] * 2 + block_b[0]) / 3;1698916990int ar = block_r[3] - block_r[0], ag = block_g[3] - block_g[0], ab = block_b[3] - block_b[0];1699116992int dots[4];16993for (uint32_t i = 0; i < 4; i++)16994dots[i] = (int)block_r[i] * ar + (int)block_g[i] * ag + (int)block_b[i] * ab;1699516996int t0 = dots[0] + dots[1], t1 = dots[1] + dots[2], t2 = dots[2] + dots[3];1699716998ar *= 2; ag *= 2; ab *= 2;1699917000static const uint8_t s_sels[4] = { 3, 2, 1, 0 };1700117002for (uint32_t i = 0; i < 16; i += 4)17003{17004const int d0 = pSrc_pixels[i+0].r * ar + pSrc_pixels[i+0].g * ag + pSrc_pixels[i+0].b * ab;17005const int d1 = pSrc_pixels[i+1].r * ar + pSrc_pixels[i+1].g * ag + pSrc_pixels[i+1].b * ab;17006const int d2 = pSrc_pixels[i+2].r * ar + pSrc_pixels[i+2].g * ag + pSrc_pixels[i+2].b * ab;17007const int d3 = pSrc_pixels[i+3].r * ar + pSrc_pixels[i+3].g * ag + pSrc_pixels[i+3].b * ab;1700817009sels[i+0] = s_sels[(d0 <= t0) + (d0 < t1) + (d0 < t2)];17010sels[i+1] = s_sels[(d1 <= t0) + (d1 < t1) + (d1 < t2)];17011sels[i+2] = s_sels[(d2 <= t0) + (d2 < t1) + (d2 < t2)];17012sels[i+3] = s_sels[(d3 <= t0) + (d3 < t1) + (d3 < t2)];17013}17014}1701517016static bool compute_least_squares_endpoints_rgb(const color32* pColors, const uint8_t* pSelectors, vec3F* pXl, vec3F* pXh)17017{17018// Derived from bc7enc16's LS function.17019// Least squares using normal equations: http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf17020// I did this in matrix form first, expanded out all the ops, then optimized it a bit.17021uint32_t uq00_r = 0, uq10_r = 0, ut_r = 0, uq00_g = 0, uq10_g = 0, ut_g = 0, uq00_b = 0, uq10_b = 0, ut_b = 0;1702217023// This table is: 9 * (w * w), 9 * ((1.0f - w) * w), 9 * ((1.0f - w) * (1.0f - w))17024// where w is [0,1/3,2/3,1]. 9 is the perfect multiplier.17025static const uint32_t s_weight_vals[4] = { 0x000009, 0x010204, 0x040201, 0x090000 };1702617027uint32_t weight_accum = 0;17028for (uint32_t i = 0; i < 16; i++)17029{17030const uint32_t r = pColors[i].c[0], g = pColors[i].c[1], b = pColors[i].c[2];17031const uint32_t sel = pSelectors[i];17032ut_r += r;17033ut_g += g;17034ut_b += b;17035weight_accum += s_weight_vals[sel];17036uq00_r += sel * r;17037uq00_g += sel * g;17038uq00_b += sel * b;17039}1704017041float q00_r = (float)uq00_r, q10_r = (float)uq10_r, t_r = (float)ut_r;17042float q00_g = (float)uq00_g, q10_g = (float)uq10_g, t_g = (float)ut_g;17043float q00_b = (float)uq00_b, q10_b = (float)uq10_b, t_b = (float)ut_b;1704417045q10_r = t_r * 3.0f - q00_r;17046q10_g = t_g * 3.0f - q00_g;17047q10_b = t_b * 3.0f - q00_b;1704817049float z00 = (float)((weight_accum >> 16) & 0xFF);17050float z10 = (float)((weight_accum >> 8) & 0xFF);17051float z11 = (float)(weight_accum & 0xFF);17052float z01 = z10;1705317054float det = z00 * z11 - z01 * z10;17055if (fabs(det) < 1e-8f)17056return false;1705717058det = 3.0f / det;1705917060float iz00, iz01, iz10, iz11;17061iz00 = z11 * det;17062iz01 = -z01 * det;17063iz10 = -z10 * det;17064iz11 = z00 * det;1706517066pXl->c[0] = iz00 * q00_r + iz01 * q10_r; pXh->c[0] = iz10 * q00_r + iz11 * q10_r;17067pXl->c[1] = iz00 * q00_g + iz01 * q10_g; pXh->c[1] = iz10 * q00_g + iz11 * q10_g;17068pXl->c[2] = iz00 * q00_b + iz01 * q10_b; pXh->c[2] = iz10 * q00_b + iz11 * q10_b;1706917070// Check and fix channel singularities - might not be needed, but is in UASTC's encoder.17071for (uint32_t c = 0; c < 3; c++)17072{17073if ((pXl->c[c] < 0.0f) || (pXh->c[c] > 255.0f))17074{17075uint32_t lo_v = UINT32_MAX, hi_v = 0;17076for (uint32_t i = 0; i < 16; i++)17077{17078lo_v = basisu::minimumu(lo_v, pColors[i].c[c]);17079hi_v = basisu::maximumu(hi_v, pColors[i].c[c]);17080}1708117082if (lo_v == hi_v)17083{17084pXl->c[c] = (float)lo_v;17085pXh->c[c] = (float)hi_v;17086}17087}17088}1708917090return true;17091}1709217093void encode_bc1_solid_block(void* pDst, uint32_t fr, uint32_t fg, uint32_t fb)17094{17095dxt1_block* pDst_block = static_cast<dxt1_block*>(pDst);1709617097uint32_t mask = 0xAA;17098uint32_t max16 = (g_bc1_match5_equals_1[fr].m_hi << 11) | (g_bc1_match6_equals_1[fg].m_hi << 5) | g_bc1_match5_equals_1[fb].m_hi;17099uint32_t min16 = (g_bc1_match5_equals_1[fr].m_lo << 11) | (g_bc1_match6_equals_1[fg].m_lo << 5) | g_bc1_match5_equals_1[fb].m_lo;1710017101if (min16 == max16)17102{17103// Always forbid 3 color blocks17104// This is to guarantee that BC3 blocks never use punchthrough alpha (3 color) mode, which isn't supported on some (all?) GPU's.17105mask = 0;1710617107// Make l > h17108if (min16 > 0)17109min16--;17110else17111{17112// l = h = 017113assert(min16 == max16 && max16 == 0);1711417115max16 = 1;17116min16 = 0;17117mask = 0x55;17118}1711917120assert(max16 > min16);17121}1712217123if (max16 < min16)17124{17125std::swap(max16, min16);17126mask ^= 0x55;17127}1712817129pDst_block->set_low_color(static_cast<uint16_t>(max16));17130pDst_block->set_high_color(static_cast<uint16_t>(min16));17131pDst_block->m_selectors[0] = static_cast<uint8_t>(mask);17132pDst_block->m_selectors[1] = static_cast<uint8_t>(mask);17133pDst_block->m_selectors[2] = static_cast<uint8_t>(mask);17134pDst_block->m_selectors[3] = static_cast<uint8_t>(mask);17135}1713617137static inline uint8_t to_5(uint32_t v) { v = v * 31 + 128; return (uint8_t)((v + (v >> 8)) >> 8); }17138static inline uint8_t to_6(uint32_t v) { v = v * 63 + 128; return (uint8_t)((v + (v >> 8)) >> 8); }1713917140// Good references: squish library, stb_dxt.17141void encode_bc1(void* pDst, const uint8_t* pPixels, uint32_t flags)17142{17143const color32* pSrc_pixels = (const color32*)pPixels;17144dxt1_block* pDst_block = static_cast<dxt1_block*>(pDst);1714517146int avg_r = -1, avg_g = 0, avg_b = 0;17147int lr = 0, lg = 0, lb = 0, hr = 0, hg = 0, hb = 0;17148uint8_t sels[16];1714917150const bool use_sels = (flags & cEncodeBC1UseSelectors) != 0;17151if (use_sels)17152{17153// Caller is jamming in their own selectors for us to try.17154const uint32_t s = pDst_block->m_selectors[0] | (pDst_block->m_selectors[1] << 8) | (pDst_block->m_selectors[2] << 16) | (pDst_block->m_selectors[3] << 24);1715517156static const uint8_t s_sel_tran[4] = { 0, 3, 1, 2 };1715717158for (uint32_t i = 0; i < 16; i++)17159sels[i] = s_sel_tran[(s >> (i * 2)) & 3];17160}17161else17162{17163const uint32_t fr = pSrc_pixels[0].r, fg = pSrc_pixels[0].g, fb = pSrc_pixels[0].b;1716417165uint32_t j;17166for (j = 1; j < 16; j++)17167if ((pSrc_pixels[j].r != fr) || (pSrc_pixels[j].g != fg) || (pSrc_pixels[j].b != fb))17168break;1716917170if (j == 16)17171{17172encode_bc1_solid_block(pDst, fr, fg, fb);17173return;17174}1717517176// Select 2 colors along the principle axis. (There must be a faster/simpler way.)17177int total_r = fr, total_g = fg, total_b = fb;17178int max_r = fr, max_g = fg, max_b = fb;17179int min_r = fr, min_g = fg, min_b = fb;17180for (uint32_t i = 1; i < 16; i++)17181{17182const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b;17183max_r = basisu::maximum(max_r, r); max_g = basisu::maximum(max_g, g); max_b = basisu::maximum(max_b, b);17184min_r = basisu::minimum(min_r, r); min_g = basisu::minimum(min_g, g); min_b = basisu::minimum(min_b, b);17185total_r += r; total_g += g; total_b += b;17186}1718717188avg_r = (total_r + 8) >> 4;17189avg_g = (total_g + 8) >> 4;17190avg_b = (total_b + 8) >> 4;1719117192int icov[6] = { 0, 0, 0, 0, 0, 0 };17193for (uint32_t i = 0; i < 16; i++)17194{17195int r = (int)pSrc_pixels[i].r - avg_r;17196int g = (int)pSrc_pixels[i].g - avg_g;17197int b = (int)pSrc_pixels[i].b - avg_b;17198icov[0] += r * r;17199icov[1] += r * g;17200icov[2] += r * b;17201icov[3] += g * g;17202icov[4] += g * b;17203icov[5] += b * b;17204}1720517206float cov[6];17207for (uint32_t i = 0; i < 6; i++)17208cov[i] = static_cast<float>(icov[i])* (1.0f / 255.0f);1720917210#if 017211// Seems silly to use full PCA to choose 2 colors. The diff in avg. PSNR between using PCA vs. not is small (~.025 difference).17212// TODO: Try 2 or 3 different normalized diagonal vectors, choose the one that results in the largest dot delta17213int saxis_r = max_r - min_r;17214int saxis_g = max_g - min_g;17215int saxis_b = max_b - min_b;17216#else17217float xr = (float)(max_r - min_r);17218float xg = (float)(max_g - min_g);17219float xb = (float)(max_b - min_b);17220//float xr = (float)(max_r - avg_r); // max-avg is nearly the same, and doesn't require computing min's17221//float xg = (float)(max_g - avg_g);17222//float xb = (float)(max_b - avg_b);17223for (uint32_t power_iter = 0; power_iter < 4; power_iter++)17224{17225float r = xr * cov[0] + xg * cov[1] + xb * cov[2];17226float g = xr * cov[1] + xg * cov[3] + xb * cov[4];17227float b = xr * cov[2] + xg * cov[4] + xb * cov[5];17228xr = r; xg = g; xb = b;17229}1723017231float k = basisu::maximum(fabsf(xr), fabsf(xg), fabsf(xb));17232int saxis_r = 306, saxis_g = 601, saxis_b = 117;17233if (k >= 2)17234{17235float m = 1024.0f / k;17236saxis_r = (int)(xr * m);17237saxis_g = (int)(xg * m);17238saxis_b = (int)(xb * m);17239}17240#endif1724117242int low_dot = INT_MAX, high_dot = INT_MIN, low_c = 0, high_c = 0;17243for (uint32_t i = 0; i < 16; i++)17244{17245int dot = pSrc_pixels[i].r * saxis_r + pSrc_pixels[i].g * saxis_g + pSrc_pixels[i].b * saxis_b;17246if (dot < low_dot)17247{17248low_dot = dot;17249low_c = i;17250}17251if (dot > high_dot)17252{17253high_dot = dot;17254high_c = i;17255}17256}1725717258lr = to_5(pSrc_pixels[low_c].r);17259lg = to_6(pSrc_pixels[low_c].g);17260lb = to_5(pSrc_pixels[low_c].b);1726117262hr = to_5(pSrc_pixels[high_c].r);17263hg = to_6(pSrc_pixels[high_c].g);17264hb = to_5(pSrc_pixels[high_c].b);1726517266bc1_find_sels(pSrc_pixels, lr, lg, lb, hr, hg, hb, sels);17267} // if (use_sels)1726817269const uint32_t total_ls_passes = (flags & cEncodeBC1HigherQuality) ? 3 : (flags & cEncodeBC1HighQuality ? 2 : 1);17270for (uint32_t ls_pass = 0; ls_pass < total_ls_passes; ls_pass++)17271{17272// This is where the real magic happens. We have an array of candidate selectors, so let's use least squares to compute the optimal low/high endpoint colors.17273vec3F xl, xh;17274if (!compute_least_squares_endpoints_rgb(pSrc_pixels, sels, &xl, &xh))17275{17276if (avg_r < 0)17277{17278int total_r = 0, total_g = 0, total_b = 0;17279for (uint32_t i = 0; i < 16; i++)17280{17281total_r += pSrc_pixels[i].r;17282total_g += pSrc_pixels[i].g;17283total_b += pSrc_pixels[i].b;17284}1728517286avg_r = (total_r + 8) >> 4;17287avg_g = (total_g + 8) >> 4;17288avg_b = (total_b + 8) >> 4;17289}1729017291// All selectors equal - treat it as a solid block which should always be equal or better.17292lr = g_bc1_match5_equals_1[avg_r].m_hi;17293lg = g_bc1_match6_equals_1[avg_g].m_hi;17294lb = g_bc1_match5_equals_1[avg_b].m_hi;1729517296hr = g_bc1_match5_equals_1[avg_r].m_lo;17297hg = g_bc1_match6_equals_1[avg_g].m_lo;17298hb = g_bc1_match5_equals_1[avg_b].m_lo;1729917300// In high/higher quality mode, let it try again in case the optimal tables have caused the sels to diverge.17301}17302else17303{17304lr = basisu::clamp((int)((xl.c[0]) * (31.0f / 255.0f) + .5f), 0, 31);17305lg = basisu::clamp((int)((xl.c[1]) * (63.0f / 255.0f) + .5f), 0, 63);17306lb = basisu::clamp((int)((xl.c[2]) * (31.0f / 255.0f) + .5f), 0, 31);1730717308hr = basisu::clamp((int)((xh.c[0]) * (31.0f / 255.0f) + .5f), 0, 31);17309hg = basisu::clamp((int)((xh.c[1]) * (63.0f / 255.0f) + .5f), 0, 63);17310hb = basisu::clamp((int)((xh.c[2]) * (31.0f / 255.0f) + .5f), 0, 31);17311}1731217313bc1_find_sels(pSrc_pixels, lr, lg, lb, hr, hg, hb, sels);17314}1731517316uint32_t lc16 = dxt1_block::pack_unscaled_color(lr, lg, lb);17317uint32_t hc16 = dxt1_block::pack_unscaled_color(hr, hg, hb);1731817319// Always forbid 3 color blocks17320if (lc16 == hc16)17321{17322uint8_t mask = 0;1732317324// Make l > h17325if (hc16 > 0)17326hc16--;17327else17328{17329// lc16 = hc16 = 017330assert(lc16 == hc16 && hc16 == 0);1733117332hc16 = 0;17333lc16 = 1;17334mask = 0x55; // select hc1617335}1733617337assert(lc16 > hc16);17338pDst_block->set_low_color(static_cast<uint16_t>(lc16));17339pDst_block->set_high_color(static_cast<uint16_t>(hc16));1734017341pDst_block->m_selectors[0] = mask;17342pDst_block->m_selectors[1] = mask;17343pDst_block->m_selectors[2] = mask;17344pDst_block->m_selectors[3] = mask;17345}17346else17347{17348uint8_t invert_mask = 0;17349if (lc16 < hc16)17350{17351std::swap(lc16, hc16);17352invert_mask = 0x55;17353}1735417355assert(lc16 > hc16);17356pDst_block->set_low_color((uint16_t)lc16);17357pDst_block->set_high_color((uint16_t)hc16);1735817359uint32_t packed_sels = 0;17360static const uint8_t s_sel_trans[4] = { 0, 2, 3, 1 };17361for (uint32_t i = 0; i < 16; i++)17362packed_sels |= ((uint32_t)s_sel_trans[sels[i]] << (i * 2));1736317364pDst_block->m_selectors[0] = (uint8_t)packed_sels ^ invert_mask;17365pDst_block->m_selectors[1] = (uint8_t)(packed_sels >> 8) ^ invert_mask;17366pDst_block->m_selectors[2] = (uint8_t)(packed_sels >> 16) ^ invert_mask;17367pDst_block->m_selectors[3] = (uint8_t)(packed_sels >> 24) ^ invert_mask;17368}17369}1737017371void encode_bc1_alt(void* pDst, const uint8_t* pPixels, uint32_t flags)17372{17373const color32* pSrc_pixels = (const color32*)pPixels;17374dxt1_block* pDst_block = static_cast<dxt1_block*>(pDst);1737517376int avg_r = -1, avg_g = 0, avg_b = 0;17377int lr = 0, lg = 0, lb = 0, hr = 0, hg = 0, hb = 0;17378uint8_t sels[16];1737917380const bool use_sels = (flags & cEncodeBC1UseSelectors) != 0;17381if (use_sels)17382{17383// Caller is jamming in their own selectors for us to try.17384const uint32_t s = pDst_block->m_selectors[0] | (pDst_block->m_selectors[1] << 8) | (pDst_block->m_selectors[2] << 16) | (pDst_block->m_selectors[3] << 24);1738517386static const uint8_t s_sel_tran[4] = { 0, 3, 1, 2 };1738717388for (uint32_t i = 0; i < 16; i++)17389sels[i] = s_sel_tran[(s >> (i * 2)) & 3];17390}17391else17392{17393const uint32_t fr = pSrc_pixels[0].r, fg = pSrc_pixels[0].g, fb = pSrc_pixels[0].b;1739417395uint32_t j;17396for (j = 1; j < 16; j++)17397if ((pSrc_pixels[j].r != fr) || (pSrc_pixels[j].g != fg) || (pSrc_pixels[j].b != fb))17398break;1739917400if (j == 16)17401{17402encode_bc1_solid_block(pDst, fr, fg, fb);17403return;17404}1740517406// Select 2 colors along the principle axis. (There must be a faster/simpler way.)17407int total_r = fr, total_g = fg, total_b = fb;17408int max_r = fr, max_g = fg, max_b = fb;17409int min_r = fr, min_g = fg, min_b = fb;17410uint32_t grayscale_flag = (fr == fg) && (fr == fb);17411for (uint32_t i = 1; i < 16; i++)17412{17413const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b;17414grayscale_flag &= ((r == g) && (r == b));17415max_r = basisu::maximum(max_r, r); max_g = basisu::maximum(max_g, g); max_b = basisu::maximum(max_b, b);17416min_r = basisu::minimum(min_r, r); min_g = basisu::minimum(min_g, g); min_b = basisu::minimum(min_b, b);17417total_r += r; total_g += g; total_b += b;17418}1741917420if (grayscale_flag)17421{17422// Grayscale blocks are a common enough case to specialize.17423if ((max_r - min_r) < 2)17424{17425lr = lb = hr = hb = to_5(fr);17426lg = hg = to_6(fr);17427}17428else17429{17430lr = lb = to_5(min_r);17431lg = to_6(min_r);1743217433hr = hb = to_5(max_r);17434hg = to_6(max_r);17435}17436}17437else17438{17439avg_r = (total_r + 8) >> 4;17440avg_g = (total_g + 8) >> 4;17441avg_b = (total_b + 8) >> 4;1744217443// Find the shortest vector from a AABB corner to the block's average color.17444// This is to help avoid outliers.1744517446uint32_t dist[3][2];17447dist[0][0] = basisu::square(min_r - avg_r) << 3; dist[0][1] = basisu::square(max_r - avg_r) << 3;17448dist[1][0] = basisu::square(min_g - avg_g) << 3; dist[1][1] = basisu::square(max_g - avg_g) << 3;17449dist[2][0] = basisu::square(min_b - avg_b) << 3; dist[2][1] = basisu::square(max_b - avg_b) << 3;1745017451uint32_t min_d0 = (dist[0][0] + dist[1][0] + dist[2][0]);17452uint32_t d4 = (dist[0][0] + dist[1][0] + dist[2][1]) | 4;17453min_d0 = basisu::minimum(min_d0, d4);1745417455uint32_t min_d1 = (dist[0][1] + dist[1][0] + dist[2][0]) | 1;17456uint32_t d5 = (dist[0][1] + dist[1][0] + dist[2][1]) | 5;17457min_d1 = basisu::minimum(min_d1, d5);1745817459uint32_t d2 = (dist[0][0] + dist[1][1] + dist[2][0]) | 2;17460min_d0 = basisu::minimum(min_d0, d2);1746117462uint32_t d3 = (dist[0][1] + dist[1][1] + dist[2][0]) | 3;17463min_d1 = basisu::minimum(min_d1, d3);1746417465uint32_t d6 = (dist[0][0] + dist[1][1] + dist[2][1]) | 6;17466min_d0 = basisu::minimum(min_d0, d6);1746717468uint32_t d7 = (dist[0][1] + dist[1][1] + dist[2][1]) | 7;17469min_d1 = basisu::minimum(min_d1, d7);1747017471uint32_t min_d = basisu::minimum(min_d0, min_d1);17472uint32_t best_i = min_d & 7;1747317474int delta_r = (best_i & 1) ? (max_r - avg_r) : (avg_r - min_r);17475int delta_g = (best_i & 2) ? (max_g - avg_g) : (avg_g - min_g);17476int delta_b = (best_i & 4) ? (max_b - avg_b) : (avg_b - min_b);1747717478// Note: if delta_r/g/b==0, we actually want to choose a single color, so the block average color optimization kicks in.17479uint32_t low_c = 0, high_c = 0;17480if ((delta_r | delta_g | delta_b) != 0)17481{17482// Now we have a smaller AABB going from the block's average color to a cornerpoint of the larger AABB.17483// Project all pixels colors along the 4 vectors going from a smaller AABB cornerpoint to the opposite cornerpoint, find largest projection.17484// One of these vectors will be a decent approximation of the block's PCA.17485const int saxis0_r = delta_r, saxis0_g = delta_g, saxis0_b = delta_b;1748617487int low_dot0 = INT_MAX, high_dot0 = INT_MIN;17488int low_dot1 = INT_MAX, high_dot1 = INT_MIN;17489int low_dot2 = INT_MAX, high_dot2 = INT_MIN;17490int low_dot3 = INT_MAX, high_dot3 = INT_MIN;1749117492//int low_c0, low_c1, low_c2, low_c3;17493//int high_c0, high_c1, high_c2, high_c3;1749417495for (uint32_t i = 0; i < 16; i++)17496{17497const int dotx = pSrc_pixels[i].r * saxis0_r;17498const int doty = pSrc_pixels[i].g * saxis0_g;17499const int dotz = pSrc_pixels[i].b * saxis0_b;1750017501const int dot0 = ((dotz + dotx + doty) << 4) + i;17502const int dot1 = ((dotz - dotx - doty) << 4) + i;17503const int dot2 = ((dotz - dotx + doty) << 4) + i;17504const int dot3 = ((dotz + dotx - doty) << 4) + i;1750517506if (dot0 < low_dot0)17507{17508low_dot0 = dot0;17509//low_c0 = i;17510}17511if ((dot0 ^ 15) > high_dot0)17512{17513high_dot0 = dot0 ^ 15;17514//high_c0 = i;17515}1751617517if (dot1 < low_dot1)17518{17519low_dot1 = dot1;17520//low_c1 = i;17521}17522if ((dot1 ^ 15) > high_dot1)17523{17524high_dot1 = dot1 ^ 15;17525//high_c1 = i;17526}1752717528if (dot2 < low_dot2)17529{17530low_dot2 = dot2;17531//low_c2 = i;17532}17533if ((dot2 ^ 15) > high_dot2)17534{17535high_dot2 = dot2 ^ 15;17536//high_c2 = i;17537}1753817539if (dot3 < low_dot3)17540{17541low_dot3 = dot3;17542//low_c3 = i;17543}17544if ((dot3 ^ 15) > high_dot3)17545{17546high_dot3 = dot3 ^ 15;17547//high_c3 = i;17548}17549}1755017551low_c = low_dot0 & 15;17552high_c = ~high_dot0 & 15;17553uint32_t r = (high_dot0 & ~15) - (low_dot0 & ~15);1755417555uint32_t tr = (high_dot1 & ~15) - (low_dot1 & ~15);17556if (tr > r) {17557low_c = low_dot1 & 15;17558high_c = ~high_dot1 & 15;17559r = tr;17560}1756117562tr = (high_dot2 & ~15) - (low_dot2 & ~15);17563if (tr > r) {17564low_c = low_dot2 & 15;17565high_c = ~high_dot2 & 15;17566r = tr;17567}1756817569tr = (high_dot3 & ~15) - (low_dot3 & ~15);17570if (tr > r) {17571low_c = low_dot3 & 15;17572high_c = ~high_dot3 & 15;17573}17574}1757517576lr = to_5(pSrc_pixels[low_c].r);17577lg = to_6(pSrc_pixels[low_c].g);17578lb = to_5(pSrc_pixels[low_c].b);1757917580hr = to_5(pSrc_pixels[high_c].r);17581hg = to_6(pSrc_pixels[high_c].g);17582hb = to_5(pSrc_pixels[high_c].b);17583}1758417585bc1_find_sels_2(pSrc_pixels, lr, lg, lb, hr, hg, hb, sels);17586} // if (use_sels)1758717588const uint32_t total_ls_passes = (flags & cEncodeBC1HigherQuality) ? 3 : (flags & cEncodeBC1HighQuality ? 2 : 1);17589for (uint32_t ls_pass = 0; ls_pass < total_ls_passes; ls_pass++)17590{17591int prev_lr = lr, prev_lg = lg, prev_lb = lb, prev_hr = hr, prev_hg = hg, prev_hb = hb;1759217593// This is where the real magic happens. We have an array of candidate selectors, so let's use least squares to compute the optimal low/high endpoint colors.17594vec3F xl, xh;17595if (!compute_least_squares_endpoints_rgb(pSrc_pixels, sels, &xl, &xh))17596{17597if (avg_r < 0)17598{17599int total_r = 0, total_g = 0, total_b = 0;17600for (uint32_t i = 0; i < 16; i++)17601{17602total_r += pSrc_pixels[i].r;17603total_g += pSrc_pixels[i].g;17604total_b += pSrc_pixels[i].b;17605}1760617607avg_r = (total_r + 8) >> 4;17608avg_g = (total_g + 8) >> 4;17609avg_b = (total_b + 8) >> 4;17610}1761117612// All selectors equal - treat it as a solid block which should always be equal or better.17613lr = g_bc1_match5_equals_1[avg_r].m_hi;17614lg = g_bc1_match6_equals_1[avg_g].m_hi;17615lb = g_bc1_match5_equals_1[avg_b].m_hi;1761617617hr = g_bc1_match5_equals_1[avg_r].m_lo;17618hg = g_bc1_match6_equals_1[avg_g].m_lo;17619hb = g_bc1_match5_equals_1[avg_b].m_lo;1762017621// In high/higher quality mode, let it try again in case the optimal tables have caused the sels to diverge.17622}17623else17624{17625lr = basisu::clamp((int)((xl.c[0]) * (31.0f / 255.0f) + .5f), 0, 31);17626lg = basisu::clamp((int)((xl.c[1]) * (63.0f / 255.0f) + .5f), 0, 63);17627lb = basisu::clamp((int)((xl.c[2]) * (31.0f / 255.0f) + .5f), 0, 31);1762817629hr = basisu::clamp((int)((xh.c[0]) * (31.0f / 255.0f) + .5f), 0, 31);17630hg = basisu::clamp((int)((xh.c[1]) * (63.0f / 255.0f) + .5f), 0, 63);17631hb = basisu::clamp((int)((xh.c[2]) * (31.0f / 255.0f) + .5f), 0, 31);17632}1763317634if ((prev_lr == lr) && (prev_lg == lg) && (prev_lb == lb) && (prev_hr == hr) && (prev_hg == hg) && (prev_hb == hb))17635break;1763617637bc1_find_sels_2(pSrc_pixels, lr, lg, lb, hr, hg, hb, sels);17638}1763917640uint32_t lc16 = dxt1_block::pack_unscaled_color(lr, lg, lb);17641uint32_t hc16 = dxt1_block::pack_unscaled_color(hr, hg, hb);1764217643// Always forbid 3 color blocks17644if (lc16 == hc16)17645{17646uint8_t mask = 0;1764717648// Make l > h17649if (hc16 > 0)17650hc16--;17651else17652{17653// lc16 = hc16 = 017654assert(lc16 == hc16 && hc16 == 0);1765517656hc16 = 0;17657lc16 = 1;17658mask = 0x55; // select hc1617659}1766017661assert(lc16 > hc16);17662pDst_block->set_low_color(static_cast<uint16_t>(lc16));17663pDst_block->set_high_color(static_cast<uint16_t>(hc16));1766417665pDst_block->m_selectors[0] = mask;17666pDst_block->m_selectors[1] = mask;17667pDst_block->m_selectors[2] = mask;17668pDst_block->m_selectors[3] = mask;17669}17670else17671{17672uint8_t invert_mask = 0;17673if (lc16 < hc16)17674{17675std::swap(lc16, hc16);17676invert_mask = 0x55;17677}1767817679assert(lc16 > hc16);17680pDst_block->set_low_color((uint16_t)lc16);17681pDst_block->set_high_color((uint16_t)hc16);1768217683uint32_t packed_sels = 0;17684static const uint8_t s_sel_trans[4] = { 0, 2, 3, 1 };17685for (uint32_t i = 0; i < 16; i++)17686packed_sels |= ((uint32_t)s_sel_trans[sels[i]] << (i * 2));1768717688pDst_block->m_selectors[0] = (uint8_t)packed_sels ^ invert_mask;17689pDst_block->m_selectors[1] = (uint8_t)(packed_sels >> 8) ^ invert_mask;17690pDst_block->m_selectors[2] = (uint8_t)(packed_sels >> 16) ^ invert_mask;17691pDst_block->m_selectors[3] = (uint8_t)(packed_sels >> 24) ^ invert_mask;17692}17693}1769417695// Scale the UASTC first subset endpoints and first plane's weight indices directly to BC1's - fastest.17696void transcode_uastc_to_bc1_hint0(const unpacked_uastc_block& unpacked_src_blk, void* pDst)17697{17698const uint32_t mode = unpacked_src_blk.m_mode;17699const astc_block_desc& astc_blk = unpacked_src_blk.m_astc;1770017701dxt1_block& b = *static_cast<dxt1_block*>(pDst);1770217703const uint32_t endpoint_range = g_uastc_mode_endpoint_ranges[mode];1770417705const uint32_t total_comps = g_uastc_mode_comps[mode];1770617707if (total_comps == 2)17708{17709const uint32_t l = g_astc_unquant[endpoint_range][astc_blk.m_endpoints[0]].m_unquant;17710const uint32_t h = g_astc_unquant[endpoint_range][astc_blk.m_endpoints[1]].m_unquant;1771117712b.set_low_color(dxt1_block::pack_color(color32(l, l, l, 255), true, 127));17713b.set_high_color(dxt1_block::pack_color(color32(h, h, h, 255), true, 127));17714}17715else17716{17717b.set_low_color(dxt1_block::pack_color(17718color32(g_astc_unquant[endpoint_range][astc_blk.m_endpoints[0]].m_unquant,17719g_astc_unquant[endpoint_range][astc_blk.m_endpoints[2]].m_unquant,17720g_astc_unquant[endpoint_range][astc_blk.m_endpoints[4]].m_unquant,17721255), true, 127)17722);1772317724b.set_high_color(dxt1_block::pack_color(17725color32(g_astc_unquant[endpoint_range][astc_blk.m_endpoints[1]].m_unquant,17726g_astc_unquant[endpoint_range][astc_blk.m_endpoints[3]].m_unquant,17727g_astc_unquant[endpoint_range][astc_blk.m_endpoints[5]].m_unquant,17728255), true, 127)17729);17730}1773117732if (b.get_low_color() == b.get_high_color())17733{17734// Always forbid 3 color blocks17735uint16_t lc16 = (uint16_t)b.get_low_color();17736uint16_t hc16 = (uint16_t)b.get_high_color();1773717738uint8_t mask = 0;1773917740// Make l > h17741if (hc16 > 0)17742hc16--;17743else17744{17745// lc16 = hc16 = 017746assert(lc16 == hc16 && hc16 == 0);1774717748hc16 = 0;17749lc16 = 1;17750mask = 0x55; // select hc1617751}1775217753assert(lc16 > hc16);17754b.set_low_color(static_cast<uint16_t>(lc16));17755b.set_high_color(static_cast<uint16_t>(hc16));1775617757b.m_selectors[0] = mask;17758b.m_selectors[1] = mask;17759b.m_selectors[2] = mask;17760b.m_selectors[3] = mask;17761}17762else17763{17764bool invert = false;17765if (b.get_low_color() < b.get_high_color())17766{17767std::swap(b.m_low_color[0], b.m_high_color[0]);17768std::swap(b.m_low_color[1], b.m_high_color[1]);17769invert = true;17770}1777117772const uint8_t* pTran = s_uastc_to_bc1_weights[g_uastc_mode_weight_bits[mode]];1777317774const uint32_t plane_shift = g_uastc_mode_planes[mode] - 1;1777517776uint32_t sels = 0;17777for (int i = 15; i >= 0; --i)17778{17779uint32_t s = pTran[astc_blk.m_weights[i << plane_shift]];1778017781if (invert)17782s ^= 1;1778317784sels = (sels << 2) | s;17785}17786b.m_selectors[0] = sels & 0xFF;17787b.m_selectors[1] = (sels >> 8) & 0xFF;17788b.m_selectors[2] = (sels >> 16) & 0xFF;17789b.m_selectors[3] = (sels >> 24) & 0xFF;17790}17791}1779217793// Scale the UASTC first plane's weight indices to BC1, use 1 or 2 least squares passes to compute endpoints - no PCA needed.17794void transcode_uastc_to_bc1_hint1(const unpacked_uastc_block& unpacked_src_blk, const color32 block_pixels[4][4], void* pDst, bool high_quality)17795{17796const uint32_t mode = unpacked_src_blk.m_mode;1779717798const astc_block_desc& astc_blk = unpacked_src_blk.m_astc;1779917800dxt1_block& b = *static_cast<dxt1_block*>(pDst);1780117802b.set_low_color(1);17803b.set_high_color(0);1780417805const uint8_t* pTran = s_uastc_to_bc1_weights[g_uastc_mode_weight_bits[mode]];1780617807const uint32_t plane_shift = g_uastc_mode_planes[mode] - 1;1780817809uint32_t sels = 0;17810for (int i = 15; i >= 0; --i)17811{17812sels <<= 2;17813sels |= pTran[astc_blk.m_weights[i << plane_shift]];17814}1781517816b.m_selectors[0] = sels & 0xFF;17817b.m_selectors[1] = (sels >> 8) & 0xFF;17818b.m_selectors[2] = (sels >> 16) & 0xFF;17819b.m_selectors[3] = (sels >> 24) & 0xFF;1782017821encode_bc1(&b, (const uint8_t*)&block_pixels[0][0].c[0], (high_quality ? cEncodeBC1HighQuality : 0) | cEncodeBC1UseSelectors);17822}1782317824bool transcode_uastc_to_bc1(const uastc_block& src_blk, void* pDst, bool high_quality)17825{17826unpacked_uastc_block unpacked_src_blk;17827if (!unpack_uastc(src_blk, unpacked_src_blk, false))17828return false;1782917830const uint32_t mode = unpacked_src_blk.m_mode;1783117832if (mode == UASTC_MODE_INDEX_SOLID_COLOR)17833{17834encode_bc1_solid_block(pDst, unpacked_src_blk.m_solid_color.r, unpacked_src_blk.m_solid_color.g, unpacked_src_blk.m_solid_color.b);17835return true;17836}1783717838if ((!high_quality) && (unpacked_src_blk.m_bc1_hint0))17839transcode_uastc_to_bc1_hint0(unpacked_src_blk, pDst);17840else17841{17842color32 block_pixels[4][4];17843const bool unpack_srgb = false;17844if (!unpack_uastc(unpacked_src_blk, &block_pixels[0][0], unpack_srgb))17845return false;1784617847if (unpacked_src_blk.m_bc1_hint1)17848transcode_uastc_to_bc1_hint1(unpacked_src_blk, block_pixels, pDst, high_quality);17849else17850encode_bc1(pDst, &block_pixels[0][0].r, high_quality ? cEncodeBC1HighQuality : 0);17851}1785217853return true;17854}1785517856static void write_bc4_solid_block(uint8_t* pDst, uint32_t a)17857{17858pDst[0] = (uint8_t)a;17859pDst[1] = (uint8_t)a;17860memset(pDst + 2, 0, 6);17861}1786217863bool transcode_uastc_to_bc3(const uastc_block& src_blk, void* pDst, bool high_quality)17864{17865unpacked_uastc_block unpacked_src_blk;17866if (!unpack_uastc(src_blk, unpacked_src_blk, false))17867return false;1786817869const uint32_t mode = unpacked_src_blk.m_mode;1787017871void* pBC4_block = pDst;17872dxt1_block* pBC1_block = &static_cast<dxt1_block*>(pDst)[1];1787317874if (mode == UASTC_MODE_INDEX_SOLID_COLOR)17875{17876write_bc4_solid_block(static_cast<uint8_t*>(pBC4_block), unpacked_src_blk.m_solid_color.a);17877encode_bc1_solid_block(pBC1_block, unpacked_src_blk.m_solid_color.r, unpacked_src_blk.m_solid_color.g, unpacked_src_blk.m_solid_color.b);17878return true;17879}1788017881color32 block_pixels[4][4];17882const bool unpack_srgb = false;17883if (!unpack_uastc(unpacked_src_blk, &block_pixels[0][0], unpack_srgb))17884return false;1788517886basist::encode_bc4(pBC4_block, &block_pixels[0][0].a, sizeof(color32));1788717888if ((!high_quality) && (unpacked_src_blk.m_bc1_hint0))17889transcode_uastc_to_bc1_hint0(unpacked_src_blk, pBC1_block);17890else17891{17892if (unpacked_src_blk.m_bc1_hint1)17893transcode_uastc_to_bc1_hint1(unpacked_src_blk, block_pixels, pBC1_block, high_quality);17894else17895encode_bc1(pBC1_block, &block_pixels[0][0].r, high_quality ? cEncodeBC1HighQuality : 0);17896}1789717898return true;17899}1790017901bool transcode_uastc_to_bc4(const uastc_block& src_blk, void* pDst, bool high_quality, uint32_t chan0)17902{17903BASISU_NOTE_UNUSED(high_quality);1790417905unpacked_uastc_block unpacked_src_blk;17906if (!unpack_uastc(src_blk, unpacked_src_blk, false))17907return false;1790817909const uint32_t mode = unpacked_src_blk.m_mode;1791017911void* pBC4_block = pDst;1791217913if (mode == UASTC_MODE_INDEX_SOLID_COLOR)17914{17915write_bc4_solid_block(static_cast<uint8_t*>(pBC4_block), unpacked_src_blk.m_solid_color.c[chan0]);17916return true;17917}1791817919color32 block_pixels[4][4];17920const bool unpack_srgb = false;17921if (!unpack_uastc(unpacked_src_blk, &block_pixels[0][0], unpack_srgb))17922return false;1792317924basist::encode_bc4(pBC4_block, &block_pixels[0][0].c[chan0], sizeof(color32));1792517926return true;17927}1792817929bool transcode_uastc_to_bc5(const uastc_block& src_blk, void* pDst, bool high_quality, uint32_t chan0, uint32_t chan1)17930{17931BASISU_NOTE_UNUSED(high_quality);1793217933unpacked_uastc_block unpacked_src_blk;17934if (!unpack_uastc(src_blk, unpacked_src_blk, false))17935return false;1793617937const uint32_t mode = unpacked_src_blk.m_mode;1793817939void* pBC4_block0 = pDst;17940void* pBC4_block1 = (uint8_t*)pDst + 8;1794117942if (mode == UASTC_MODE_INDEX_SOLID_COLOR)17943{17944write_bc4_solid_block(static_cast<uint8_t*>(pBC4_block0), unpacked_src_blk.m_solid_color.c[chan0]);17945write_bc4_solid_block(static_cast<uint8_t*>(pBC4_block1), unpacked_src_blk.m_solid_color.c[chan1]);17946return true;17947}1794817949color32 block_pixels[4][4];17950const bool unpack_srgb = false;17951if (!unpack_uastc(unpacked_src_blk, &block_pixels[0][0], unpack_srgb))17952return false;1795317954basist::encode_bc4(pBC4_block0, &block_pixels[0][0].c[chan0], sizeof(color32));17955basist::encode_bc4(pBC4_block1, &block_pixels[0][0].c[chan1], sizeof(color32));1795617957return true;17958}1795917960static const uint8_t s_etc2_eac_bit_ofs[16] = { 45, 33, 21, 9, 42, 30, 18, 6, 39, 27, 15, 3, 36, 24, 12, 0 };1796117962static void pack_eac_solid_block(eac_block& blk, uint32_t a)17963{17964blk.m_base = static_cast<uint8_t>(a);17965blk.m_table = 13;17966blk.m_multiplier = 0;1796717968memcpy(blk.m_selectors, g_etc2_eac_a8_sel4, sizeof(g_etc2_eac_a8_sel4));1796917970return;17971}1797217973// Only checks 4 tables.17974static void pack_eac(eac_block& blk, const uint8_t* pPixels, uint32_t stride)17975{17976uint32_t min_alpha = 255, max_alpha = 0;17977for (uint32_t i = 0; i < 16; i++)17978{17979const uint32_t a = pPixels[i * stride];17980if (a < min_alpha) min_alpha = a;17981if (a > max_alpha) max_alpha = a;17982}1798317984if (min_alpha == max_alpha)17985{17986pack_eac_solid_block(blk, min_alpha);17987return;17988}1798917990const uint32_t alpha_range = max_alpha - min_alpha;1799117992const uint32_t SINGLE_TABLE_THRESH = 5;17993if (alpha_range <= SINGLE_TABLE_THRESH)17994{17995// If alpha_range <= 5 table 13 is lossless17996int base = clamp255((int)max_alpha - 2);1799717998blk.m_base = base;17999blk.m_multiplier = 1;18000blk.m_table = 13;1800118002base -= 3;1800318004uint64_t packed_sels = 0;18005for (uint32_t i = 0; i < 16; i++)18006{18007const int a = pPixels[i * stride];1800818009static const uint8_t s_sels[6] = { 2, 1, 0, 4, 5, 6 };1801018011int sel = a - base;18012assert(sel >= 0 && sel <= 5);1801318014packed_sels |= (static_cast<uint64_t>(s_sels[sel]) << s_etc2_eac_bit_ofs[i]);18015}1801618017blk.set_selector_bits(packed_sels);1801818019return;18020}1802118022const uint32_t T0 = 2, T1 = 8, T2 = 11, T3 = 13;18023static const uint8_t s_tables[4] = { T0, T1, T2, T3 };1802418025int base[4], mul[4];18026uint32_t mul_or = 0;18027for (uint32_t i = 0; i < 4; i++)18028{18029const uint32_t table = s_tables[i];1803018031const float range = (float)(g_eac_modifier_table[table][ETC2_EAC_MAX_VALUE_SELECTOR] - g_eac_modifier_table[table][ETC2_EAC_MIN_VALUE_SELECTOR]);1803218033base[i] = clamp255((int)roundf(basisu::lerp((float)min_alpha, (float)max_alpha, (float)(0 - g_eac_modifier_table[table][ETC2_EAC_MIN_VALUE_SELECTOR]) / range)));18034mul[i] = clampi((int)roundf(alpha_range / range), 1, 15);18035mul_or |= mul[i];18036}1803718038uint32_t total_err[4] = { 0, 0, 0, 0 };18039uint8_t sels[4][16];1804018041for (uint32_t i = 0; i < 16; i++)18042{18043const int a = pPixels[i * stride];1804418045uint32_t l0 = UINT32_MAX, l1 = UINT32_MAX, l2 = UINT32_MAX, l3 = UINT32_MAX;1804618047if ((a < 7) || (a > (255 - 7)))18048{18049for (uint32_t s = 0; s < 8; s++)18050{18051const int v0 = clamp255(mul[0] * g_eac_modifier_table[T0][s] + base[0]);18052const int v1 = clamp255(mul[1] * g_eac_modifier_table[T1][s] + base[1]);18053const int v2 = clamp255(mul[2] * g_eac_modifier_table[T2][s] + base[2]);18054const int v3 = clamp255(mul[3] * g_eac_modifier_table[T3][s] + base[3]);1805518056l0 = basisu::minimum(l0, (basisu::iabs(v0 - a) << 3) | s);18057l1 = basisu::minimum(l1, (basisu::iabs(v1 - a) << 3) | s);18058l2 = basisu::minimum(l2, (basisu::iabs(v2 - a) << 3) | s);18059l3 = basisu::minimum(l3, (basisu::iabs(v3 - a) << 3) | s);18060}18061}18062else if (mul_or == 1)18063{18064const int a0 = base[0] - a, a1 = base[1] - a, a2 = base[2] - a, a3 = base[3] - a;1806518066for (uint32_t s = 0; s < 8; s++)18067{18068const int v0 = g_eac_modifier_table[T0][s] + a0;18069const int v1 = g_eac_modifier_table[T1][s] + a1;18070const int v2 = g_eac_modifier_table[T2][s] + a2;18071const int v3 = g_eac_modifier_table[T3][s] + a3;1807218073l0 = basisu::minimum(l0, (basisu::iabs(v0) << 3) | s);18074l1 = basisu::minimum(l1, (basisu::iabs(v1) << 3) | s);18075l2 = basisu::minimum(l2, (basisu::iabs(v2) << 3) | s);18076l3 = basisu::minimum(l3, (basisu::iabs(v3) << 3) | s);18077}18078}18079else18080{18081const int a0 = base[0] - a, a1 = base[1] - a, a2 = base[2] - a, a3 = base[3] - a;1808218083for (uint32_t s = 0; s < 8; s++)18084{18085const int v0 = mul[0] * g_eac_modifier_table[T0][s] + a0;18086const int v1 = mul[1] * g_eac_modifier_table[T1][s] + a1;18087const int v2 = mul[2] * g_eac_modifier_table[T2][s] + a2;18088const int v3 = mul[3] * g_eac_modifier_table[T3][s] + a3;1808918090l0 = basisu::minimum(l0, (basisu::iabs(v0) << 3) | s);18091l1 = basisu::minimum(l1, (basisu::iabs(v1) << 3) | s);18092l2 = basisu::minimum(l2, (basisu::iabs(v2) << 3) | s);18093l3 = basisu::minimum(l3, (basisu::iabs(v3) << 3) | s);18094}18095}1809618097sels[0][i] = l0 & 7;18098sels[1][i] = l1 & 7;18099sels[2][i] = l2 & 7;18100sels[3][i] = l3 & 7;1810118102total_err[0] += basisu::square<uint32_t>(l0 >> 3);18103total_err[1] += basisu::square<uint32_t>(l1 >> 3);18104total_err[2] += basisu::square<uint32_t>(l2 >> 3);18105total_err[3] += basisu::square<uint32_t>(l3 >> 3);18106}1810718108uint32_t min_err = total_err[0], min_index = 0;18109for (uint32_t i = 1; i < 4; i++)18110{18111if (total_err[i] < min_err)18112{18113min_err = total_err[i];18114min_index = i;18115}18116}1811718118blk.m_base = base[min_index];18119blk.m_multiplier = mul[min_index];18120blk.m_table = s_tables[min_index];1812118122uint64_t packed_sels = 0;18123const uint8_t* pSels = &sels[min_index][0];18124for (uint32_t i = 0; i < 16; i++)18125packed_sels |= (static_cast<uint64_t>(pSels[i]) << s_etc2_eac_bit_ofs[i]);1812618127blk.set_selector_bits(packed_sels);18128}1812918130// Checks all 16 tables. Around ~2 dB better vs. pack_eac(), ~1.2 dB less than near-optimal.18131static void pack_eac_high_quality(eac_block& blk, const uint8_t* pPixels, uint32_t stride)18132{18133uint32_t min_alpha = 255, max_alpha = 0;18134for (uint32_t i = 0; i < 16; i++)18135{18136const uint32_t a = pPixels[i * stride];18137if (a < min_alpha) min_alpha = a;18138if (a > max_alpha) max_alpha = a;18139}1814018141if (min_alpha == max_alpha)18142{18143pack_eac_solid_block(blk, min_alpha);18144return;18145}1814618147const uint32_t alpha_range = max_alpha - min_alpha;1814818149const uint32_t SINGLE_TABLE_THRESH = 5;18150if (alpha_range <= SINGLE_TABLE_THRESH)18151{18152// If alpha_range <= 5 table 13 is lossless18153int base = clamp255((int)max_alpha - 2);1815418155blk.m_base = base;18156blk.m_multiplier = 1;18157blk.m_table = 13;1815818159base -= 3;1816018161uint64_t packed_sels = 0;18162for (uint32_t i = 0; i < 16; i++)18163{18164const int a = pPixels[i * stride];1816518166static const uint8_t s_sels[6] = { 2, 1, 0, 4, 5, 6 };1816718168int sel = a - base;18169assert(sel >= 0 && sel <= 5);1817018171packed_sels |= (static_cast<uint64_t>(s_sels[sel]) << s_etc2_eac_bit_ofs[i]);18172}1817318174blk.set_selector_bits(packed_sels);1817518176return;18177}1817818179int base[16], mul[16];18180for (uint32_t table = 0; table < 16; table++)18181{18182const float range = (float)(g_eac_modifier_table[table][ETC2_EAC_MAX_VALUE_SELECTOR] - g_eac_modifier_table[table][ETC2_EAC_MIN_VALUE_SELECTOR]);1818318184base[table] = clamp255((int)roundf(basisu::lerp((float)min_alpha, (float)max_alpha, (float)(0 - g_eac_modifier_table[table][ETC2_EAC_MIN_VALUE_SELECTOR]) / range)));18185mul[table] = clampi((int)roundf(alpha_range / range), 1, 15);18186}1818718188uint32_t total_err[16];18189memset(total_err, 0, sizeof(total_err));1819018191uint8_t sels[16][16];1819218193for (uint32_t table = 0; table < 16; table++)18194{18195const int8_t* pTable = &g_eac_modifier_table[table][0];18196const int m = mul[table], b = base[table];1819718198uint32_t prev_l = 0, prev_a = UINT32_MAX;1819918200for (uint32_t i = 0; i < 16; i++)18201{18202const int a = pPixels[i * stride];1820318204if ((uint32_t)a == prev_a)18205{18206sels[table][i] = prev_l & 7;18207total_err[table] += basisu::square<uint32_t>(prev_l >> 3);18208}18209else18210{18211uint32_t l = basisu::iabs(clamp255(m * pTable[0] + b) - a) << 3;18212l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[1] + b) - a) << 3) | 1);18213l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[2] + b) - a) << 3) | 2);18214l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[3] + b) - a) << 3) | 3);18215l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[4] + b) - a) << 3) | 4);18216l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[5] + b) - a) << 3) | 5);18217l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[6] + b) - a) << 3) | 6);18218l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[7] + b) - a) << 3) | 7);1821918220sels[table][i] = l & 7;18221total_err[table] += basisu::square<uint32_t>(l >> 3);1822218223prev_l = l;18224prev_a = a;18225}18226}18227}1822818229uint32_t min_err = total_err[0], min_index = 0;18230for (uint32_t i = 1; i < 16; i++)18231{18232if (total_err[i] < min_err)18233{18234min_err = total_err[i];18235min_index = i;18236}18237}1823818239blk.m_base = base[min_index];18240blk.m_multiplier = mul[min_index];18241blk.m_table = min_index;1824218243uint64_t packed_sels = 0;18244const uint8_t* pSels = &sels[min_index][0];18245for (uint32_t i = 0; i < 16; i++)18246packed_sels |= (static_cast<uint64_t>(pSels[i]) << s_etc2_eac_bit_ofs[i]);1824718248blk.set_selector_bits(packed_sels);18249}1825018251bool transcode_uastc_to_etc2_eac_r11(const uastc_block& src_blk, void* pDst, bool high_quality, uint32_t chan0)18252{18253unpacked_uastc_block unpacked_src_blk;18254if (!unpack_uastc(src_blk, unpacked_src_blk, false))18255return false;1825618257const uint32_t mode = unpacked_src_blk.m_mode;1825818259if (mode == UASTC_MODE_INDEX_SOLID_COLOR)18260{18261pack_eac_solid_block(*static_cast<eac_block*>(pDst), unpacked_src_blk.m_solid_color.c[chan0]);18262return true;18263}1826418265color32 block_pixels[4][4];18266const bool unpack_srgb = false;18267if (!unpack_uastc(unpacked_src_blk, &block_pixels[0][0], unpack_srgb))18268return false;1826918270if (chan0 == 3)18271transcode_uastc_to_etc2_eac_a8(unpacked_src_blk, block_pixels, pDst);18272else18273(high_quality ? pack_eac_high_quality : pack_eac)(*static_cast<eac_block*>(pDst), &block_pixels[0][0].c[chan0], sizeof(color32));1827418275return true;18276}1827718278bool transcode_uastc_to_etc2_eac_rg11(const uastc_block& src_blk, void* pDst, bool high_quality, uint32_t chan0, uint32_t chan1)18279{18280unpacked_uastc_block unpacked_src_blk;18281if (!unpack_uastc(src_blk, unpacked_src_blk, false))18282return false;1828318284const uint32_t mode = unpacked_src_blk.m_mode;1828518286if (mode == UASTC_MODE_INDEX_SOLID_COLOR)18287{18288pack_eac_solid_block(static_cast<eac_block*>(pDst)[0], unpacked_src_blk.m_solid_color.c[chan0]);18289pack_eac_solid_block(static_cast<eac_block*>(pDst)[1], unpacked_src_blk.m_solid_color.c[chan1]);18290return true;18291}1829218293color32 block_pixels[4][4];18294const bool unpack_srgb = false;18295if (!unpack_uastc(unpacked_src_blk, &block_pixels[0][0], unpack_srgb))18296return false;1829718298if (chan0 == 3)18299transcode_uastc_to_etc2_eac_a8(unpacked_src_blk, block_pixels, &static_cast<eac_block*>(pDst)[0]);18300else18301(high_quality ? pack_eac_high_quality : pack_eac)(static_cast<eac_block*>(pDst)[0], &block_pixels[0][0].c[chan0], sizeof(color32));1830218303if (chan1 == 3)18304transcode_uastc_to_etc2_eac_a8(unpacked_src_blk, block_pixels, &static_cast<eac_block*>(pDst)[1]);18305else18306(high_quality ? pack_eac_high_quality : pack_eac)(static_cast<eac_block*>(pDst)[1], &block_pixels[0][0].c[chan1], sizeof(color32));18307return true;18308}1830918310// PVRTC118311static void fixup_pvrtc1_4_modulation_rgb(18312const uastc_block* pSrc_blocks,18313const uint32_t* pPVRTC_endpoints,18314void* pDst_blocks,18315uint32_t num_blocks_x, uint32_t num_blocks_y, bool from_alpha)18316{18317const uint32_t x_mask = num_blocks_x - 1;18318const uint32_t y_mask = num_blocks_y - 1;18319const uint32_t x_bits = basisu::total_bits(x_mask);18320const uint32_t y_bits = basisu::total_bits(y_mask);18321const uint32_t min_bits = basisu::minimum(x_bits, y_bits);18322//const uint32_t max_bits = basisu::maximum(x_bits, y_bits);18323const uint32_t swizzle_mask = (1 << (min_bits * 2)) - 1;1832418325uint32_t block_index = 0;1832618327// really 3x318328int e0[4][4], e1[4][4];1832918330for (int y = 0; y < static_cast<int>(num_blocks_y); y++)18331{18332const uint32_t* pE_rows[3];1833318334for (int ey = 0; ey < 3; ey++)18335{18336int by = y + ey - 1;1833718338const uint32_t* pE = &pPVRTC_endpoints[(by & y_mask) * num_blocks_x];1833918340pE_rows[ey] = pE;1834118342for (int ex = 0; ex < 3; ex++)18343{18344int bx = 0 + ex - 1;1834518346const uint32_t e = pE[bx & x_mask];1834718348e0[ex][ey] = (get_opaque_endpoint_l0(e) * 255) / 31;18349e1[ex][ey] = (get_opaque_endpoint_l1(e) * 255) / 31;18350}18351}1835218353const uint32_t y_swizzle = (g_pvrtc_swizzle_table[y >> 8] << 16) | g_pvrtc_swizzle_table[y & 0xFF];1835418355for (int x = 0; x < static_cast<int>(num_blocks_x); x++, block_index++)18356{18357const uastc_block& src_block = pSrc_blocks[block_index];1835818359color32 block_pixels[4][4];18360unpack_uastc(src_block, &block_pixels[0][0], false);18361if (from_alpha)18362{18363// Just set RGB to alpha to avoid adding complexity below.18364for (uint32_t i = 0; i < 16; i++)18365{18366const uint8_t a = ((color32*)block_pixels)[i].a;18367((color32*)block_pixels)[i].set(a, a, a, 255);18368}18369}1837018371const uint32_t x_swizzle = (g_pvrtc_swizzle_table[x >> 8] << 17) | (g_pvrtc_swizzle_table[x & 0xFF] << 1);1837218373uint32_t swizzled = x_swizzle | y_swizzle;18374if (num_blocks_x != num_blocks_y)18375{18376swizzled &= swizzle_mask;1837718378if (num_blocks_x > num_blocks_y)18379swizzled |= ((x >> min_bits) << (min_bits * 2));18380else18381swizzled |= ((y >> min_bits) << (min_bits * 2));18382}1838318384pvrtc4_block* pDst_block = static_cast<pvrtc4_block*>(pDst_blocks) + swizzled;18385pDst_block->m_endpoints = pPVRTC_endpoints[block_index];1838618387{18388const uint32_t ex = 2;18389int bx = x + ex - 1;18390bx &= x_mask;1839118392#define DO_ROW(ey) \18393{ \18394const uint32_t e = pE_rows[ey][bx]; \18395e0[ex][ey] = (get_opaque_endpoint_l0(e) * 255) / 31; \18396e1[ex][ey] = (get_opaque_endpoint_l1(e) * 255) / 31; \18397}1839818399DO_ROW(0);18400DO_ROW(1);18401DO_ROW(2);18402#undef DO_ROW18403}1840418405uint32_t mod = 0;1840618407#define DO_PIX(lx, ly, w0, w1, w2, w3) \18408{ \18409int ca_l = a0 * w0 + a1 * w1 + a2 * w2 + a3 * w3; \18410int cb_l = b0 * w0 + b1 * w1 + b2 * w2 + b3 * w3; \18411int cl = (block_pixels[ly][lx].r + block_pixels[ly][lx].g + block_pixels[ly][lx].b) * 16; \18412int dl = cb_l - ca_l; \18413int vl = cl - ca_l; \18414int p = vl * 16; \18415if (ca_l > cb_l) { p = -p; dl = -dl; } \18416uint32_t m = 0; \18417if (p > 3 * dl) m = (uint32_t)(1 << ((ly) * 8 + (lx) * 2)); \18418if (p > 8 * dl) m = (uint32_t)(2 << ((ly) * 8 + (lx) * 2)); \18419if (p > 13 * dl) m = (uint32_t)(3 << ((ly) * 8 + (lx) * 2)); \18420mod |= m; \18421}1842218423{18424const uint32_t ex = 0, ey = 0;18425const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];18426const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];18427DO_PIX(0, 0, 4, 4, 4, 4);18428DO_PIX(1, 0, 2, 6, 2, 6);18429DO_PIX(0, 1, 2, 2, 6, 6);18430DO_PIX(1, 1, 1, 3, 3, 9);18431}1843218433{18434const uint32_t ex = 1, ey = 0;18435const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];18436const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];18437DO_PIX(2, 0, 8, 0, 8, 0);18438DO_PIX(3, 0, 6, 2, 6, 2);18439DO_PIX(2, 1, 4, 0, 12, 0);18440DO_PIX(3, 1, 3, 1, 9, 3);18441}1844218443{18444const uint32_t ex = 0, ey = 1;18445const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];18446const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];18447DO_PIX(0, 2, 8, 8, 0, 0);18448DO_PIX(1, 2, 4, 12, 0, 0);18449DO_PIX(0, 3, 6, 6, 2, 2);18450DO_PIX(1, 3, 3, 9, 1, 3);18451}1845218453{18454const uint32_t ex = 1, ey = 1;18455const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];18456const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];18457DO_PIX(2, 2, 16, 0, 0, 0);18458DO_PIX(3, 2, 12, 4, 0, 0);18459DO_PIX(2, 3, 12, 0, 4, 0);18460DO_PIX(3, 3, 9, 3, 3, 1);18461}18462#undef DO_PIX1846318464pDst_block->m_modulation = mod;1846518466e0[0][0] = e0[1][0]; e0[1][0] = e0[2][0];18467e0[0][1] = e0[1][1]; e0[1][1] = e0[2][1];18468e0[0][2] = e0[1][2]; e0[1][2] = e0[2][2];1846918470e1[0][0] = e1[1][0]; e1[1][0] = e1[2][0];18471e1[0][1] = e1[1][1]; e1[1][1] = e1[2][1];18472e1[0][2] = e1[1][2]; e1[1][2] = e1[2][2];1847318474} // x18475} // y18476}1847718478static void fixup_pvrtc1_4_modulation_rgba(18479const uastc_block* pSrc_blocks,18480const uint32_t* pPVRTC_endpoints,18481void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y)18482{18483const uint32_t x_mask = num_blocks_x - 1;18484const uint32_t y_mask = num_blocks_y - 1;18485const uint32_t x_bits = basisu::total_bits(x_mask);18486const uint32_t y_bits = basisu::total_bits(y_mask);18487const uint32_t min_bits = basisu::minimum(x_bits, y_bits);18488//const uint32_t max_bits = basisu::maximum(x_bits, y_bits);18489const uint32_t swizzle_mask = (1 << (min_bits * 2)) - 1;1849018491uint32_t block_index = 0;1849218493// really 3x318494int e0[4][4], e1[4][4];1849518496for (int y = 0; y < static_cast<int>(num_blocks_y); y++)18497{18498const uint32_t* pE_rows[3];1849918500for (int ey = 0; ey < 3; ey++)18501{18502int by = y + ey - 1;1850318504const uint32_t* pE = &pPVRTC_endpoints[(by & y_mask) * num_blocks_x];1850518506pE_rows[ey] = pE;1850718508for (int ex = 0; ex < 3; ex++)18509{18510int bx = 0 + ex - 1;1851118512const uint32_t e = pE[bx & x_mask];1851318514e0[ex][ey] = get_endpoint_l8(e, 0);18515e1[ex][ey] = get_endpoint_l8(e, 1);18516}18517}1851818519const uint32_t y_swizzle = (g_pvrtc_swizzle_table[y >> 8] << 16) | g_pvrtc_swizzle_table[y & 0xFF];1852018521for (int x = 0; x < static_cast<int>(num_blocks_x); x++, block_index++)18522{18523const uastc_block& src_block = pSrc_blocks[block_index];1852418525color32 block_pixels[4][4];18526unpack_uastc(src_block, &block_pixels[0][0], false);1852718528const uint32_t x_swizzle = (g_pvrtc_swizzle_table[x >> 8] << 17) | (g_pvrtc_swizzle_table[x & 0xFF] << 1);1852918530uint32_t swizzled = x_swizzle | y_swizzle;18531if (num_blocks_x != num_blocks_y)18532{18533swizzled &= swizzle_mask;1853418535if (num_blocks_x > num_blocks_y)18536swizzled |= ((x >> min_bits) << (min_bits * 2));18537else18538swizzled |= ((y >> min_bits) << (min_bits * 2));18539}1854018541pvrtc4_block* pDst_block = static_cast<pvrtc4_block*>(pDst_blocks) + swizzled;18542pDst_block->m_endpoints = pPVRTC_endpoints[block_index];1854318544{18545const uint32_t ex = 2;18546int bx = x + ex - 1;18547bx &= x_mask;1854818549#define DO_ROW(ey) \18550{ \18551const uint32_t e = pE_rows[ey][bx]; \18552e0[ex][ey] = get_endpoint_l8(e, 0); \18553e1[ex][ey] = get_endpoint_l8(e, 1); \18554}1855518556DO_ROW(0);18557DO_ROW(1);18558DO_ROW(2);18559#undef DO_ROW18560}1856118562uint32_t mod = 0;1856318564#define DO_PIX(lx, ly, w0, w1, w2, w3) \18565{ \18566int ca_l = a0 * w0 + a1 * w1 + a2 * w2 + a3 * w3; \18567int cb_l = b0 * w0 + b1 * w1 + b2 * w2 + b3 * w3; \18568int cl = 16 * (block_pixels[ly][lx].r + block_pixels[ly][lx].g + block_pixels[ly][lx].b + block_pixels[ly][lx].a); \18569int dl = cb_l - ca_l; \18570int vl = cl - ca_l; \18571int p = vl * 16; \18572if (ca_l > cb_l) { p = -p; dl = -dl; } \18573uint32_t m = 0; \18574if (p > 3 * dl) m = (uint32_t)(1 << ((ly) * 8 + (lx) * 2)); \18575if (p > 8 * dl) m = (uint32_t)(2 << ((ly) * 8 + (lx) * 2)); \18576if (p > 13 * dl) m = (uint32_t)(3 << ((ly) * 8 + (lx) * 2)); \18577mod |= m; \18578}1857918580{18581const uint32_t ex = 0, ey = 0;18582const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];18583const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];18584DO_PIX(0, 0, 4, 4, 4, 4);18585DO_PIX(1, 0, 2, 6, 2, 6);18586DO_PIX(0, 1, 2, 2, 6, 6);18587DO_PIX(1, 1, 1, 3, 3, 9);18588}1858918590{18591const uint32_t ex = 1, ey = 0;18592const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];18593const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];18594DO_PIX(2, 0, 8, 0, 8, 0);18595DO_PIX(3, 0, 6, 2, 6, 2);18596DO_PIX(2, 1, 4, 0, 12, 0);18597DO_PIX(3, 1, 3, 1, 9, 3);18598}1859918600{18601const uint32_t ex = 0, ey = 1;18602const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];18603const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];18604DO_PIX(0, 2, 8, 8, 0, 0);18605DO_PIX(1, 2, 4, 12, 0, 0);18606DO_PIX(0, 3, 6, 6, 2, 2);18607DO_PIX(1, 3, 3, 9, 1, 3);18608}1860918610{18611const uint32_t ex = 1, ey = 1;18612const int a0 = e0[ex][ey], a1 = e0[ex + 1][ey], a2 = e0[ex][ey + 1], a3 = e0[ex + 1][ey + 1];18613const int b0 = e1[ex][ey], b1 = e1[ex + 1][ey], b2 = e1[ex][ey + 1], b3 = e1[ex + 1][ey + 1];18614DO_PIX(2, 2, 16, 0, 0, 0);18615DO_PIX(3, 2, 12, 4, 0, 0);18616DO_PIX(2, 3, 12, 0, 4, 0);18617DO_PIX(3, 3, 9, 3, 3, 1);18618}18619#undef DO_PIX1862018621pDst_block->m_modulation = mod;1862218623e0[0][0] = e0[1][0]; e0[1][0] = e0[2][0];18624e0[0][1] = e0[1][1]; e0[1][1] = e0[2][1];18625e0[0][2] = e0[1][2]; e0[1][2] = e0[2][2];1862618627e1[0][0] = e1[1][0]; e1[1][0] = e1[2][0];18628e1[0][1] = e1[1][1]; e1[1][1] = e1[2][1];18629e1[0][2] = e1[1][2]; e1[1][2] = e1[2][2];1863018631} // x18632} // y18633}1863418635bool 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)18636{18637BASISU_NOTE_UNUSED(high_quality);1863818639if ((!num_blocks_x) || (!num_blocks_y))18640return false;1864118642const uint32_t width = num_blocks_x * 4;18643const uint32_t height = num_blocks_y * 4;18644if (!basisu::is_pow2(width) || !basisu::is_pow2(height))18645return false;1864618647basisu::vector<uint32_t> temp_endpoints(num_blocks_x * num_blocks_y);1864818649for (uint32_t y = 0; y < num_blocks_y; y++)18650{18651for (uint32_t x = 0; x < num_blocks_x; x++)18652{18653color32 block_pixels[16];18654if (!unpack_uastc(pSrc_blocks[x + y * num_blocks_x], block_pixels, false))18655return false;1865618657// Get block's RGB bounding box18658color32 low_color(255, 255, 255, 255), high_color(0, 0, 0, 0);1865918660if (from_alpha)18661{18662uint32_t low_a = 255, high_a = 0;18663for (uint32_t i = 0; i < 16; i++)18664{18665low_a = basisu::minimum<uint32_t>(low_a, block_pixels[i].a);18666high_a = basisu::maximum<uint32_t>(high_a, block_pixels[i].a);18667}18668low_color.set(low_a, low_a, low_a, 255);18669high_color.set(high_a, high_a, high_a, 255);18670}18671else18672{18673for (uint32_t i = 0; i < 16; i++)18674{18675low_color = color32::comp_min(low_color, block_pixels[i]);18676high_color = color32::comp_max(high_color, block_pixels[i]);18677}18678}1867918680// Set PVRTC1 endpoints to floor/ceil of bounding box's coordinates.18681pvrtc4_block temp;18682temp.set_opaque_endpoint_floor(0, low_color);18683temp.set_opaque_endpoint_ceil(1, high_color);1868418685temp_endpoints[x + y * num_blocks_x] = temp.m_endpoints;18686}18687}1868818689fixup_pvrtc1_4_modulation_rgb(pSrc_blocks, &temp_endpoints[0], pDst_blocks, num_blocks_x, num_blocks_y, from_alpha);1869018691return true;18692}1869318694bool 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)18695{18696BASISU_NOTE_UNUSED(high_quality);1869718698if ((!num_blocks_x) || (!num_blocks_y))18699return false;1870018701const uint32_t width = num_blocks_x * 4;18702const uint32_t height = num_blocks_y * 4;18703if (!basisu::is_pow2(width) || !basisu::is_pow2(height))18704return false;1870518706basisu::vector<uint32_t> temp_endpoints(num_blocks_x * num_blocks_y);1870718708for (uint32_t y = 0; y < num_blocks_y; y++)18709{18710for (uint32_t x = 0; x < num_blocks_x; x++)18711{18712color32 block_pixels[16];18713if (!unpack_uastc(pSrc_blocks[x + y * num_blocks_x], block_pixels, false))18714return false;1871518716// Get block's RGBA bounding box18717color32 low_color(255, 255, 255, 255), high_color(0, 0, 0, 0);1871818719for (uint32_t i = 0; i < 16; i++)18720{18721low_color = color32::comp_min(low_color, block_pixels[i]);18722high_color = color32::comp_max(high_color, block_pixels[i]);18723}1872418725// Set PVRTC1 endpoints to floor/ceil of bounding box's coordinates.18726pvrtc4_block temp;18727temp.set_endpoint_floor(0, low_color);18728temp.set_endpoint_ceil(1, high_color);1872918730temp_endpoints[x + y * num_blocks_x] = temp.m_endpoints;18731}18732}1873318734fixup_pvrtc1_4_modulation_rgba(pSrc_blocks, &temp_endpoints[0], pDst_blocks, num_blocks_x, num_blocks_y);1873518736return true;18737}1873818739void uastc_init()18740{18741for (uint32_t range = 0; range < BC7ENC_TOTAL_ASTC_RANGES; range++)18742{18743if (!astc_is_valid_endpoint_range(range))18744continue;1874518746const uint32_t levels = astc_get_levels(range);1874718748uint32_t vals[256];18749for (uint32_t i = 0; i < levels; i++)18750vals[i] = (unquant_astc_endpoint_val(i, range) << 8) | i;1875118752std::sort(vals, vals + levels);1875318754for (uint32_t i = 0; i < levels; i++)18755{18756const uint32_t order = vals[i] & 0xFF;18757const uint32_t unq = vals[i] >> 8;1875818759g_astc_unquant[range][order].m_unquant = (uint8_t)unq;18760g_astc_unquant[range][order].m_index = (uint8_t)i;1876118762} // i18763}1876418765// TODO: Precompute?18766// BC7 777.118767for (int c = 0; c < 256; c++)18768{18769for (uint32_t lp = 0; lp < 2; lp++)18770{18771endpoint_err best;18772best.m_error = (uint16_t)UINT16_MAX;1877318774for (uint32_t l = 0; l < 128; l++)18775{18776const uint32_t low = (l << 1) | lp;1877718778for (uint32_t h = 0; h < 128; h++)18779{18780const uint32_t high = (h << 1) | lp;1878118782const int k = (low * (64 - g_bc7_weights4[BC7ENC_MODE_6_OPTIMAL_INDEX]) + high * g_bc7_weights4[BC7ENC_MODE_6_OPTIMAL_INDEX] + 32) >> 6;1878318784const int err = (k - c) * (k - c);18785if (err < best.m_error)18786{18787best.m_error = (uint16_t)err;18788best.m_lo = (uint8_t)l;18789best.m_hi = (uint8_t)h;18790}18791} // h18792} // l1879318794g_bc7_mode_6_optimal_endpoints[c][lp] = best;18795} // lp1879618797} // c1879818799// BC7 77718800for (int c = 0; c < 256; c++)18801{18802endpoint_err best;18803best.m_error = (uint16_t)UINT16_MAX;1880418805for (uint32_t l = 0; l < 128; l++)18806{18807const uint32_t low = (l << 1) | (l >> 6);1880818809for (uint32_t h = 0; h < 128; h++)18810{18811const uint32_t high = (h << 1) | (h >> 6);1881218813const int k = (low * (64 - g_bc7_weights2[BC7ENC_MODE_5_OPTIMAL_INDEX]) + high * g_bc7_weights2[BC7ENC_MODE_5_OPTIMAL_INDEX] + 32) >> 6;1881418815const int err = (k - c) * (k - c);18816if (err < best.m_error)18817{18818best.m_error = (uint16_t)err;18819best.m_lo = (uint8_t)l;18820best.m_hi = (uint8_t)h;18821}18822} // h18823} // l1882418825g_bc7_mode_5_optimal_endpoints[c] = best;1882618827} // c18828}1882918830#endif // #if BASISD_SUPPORT_UASTC1883118832// ------------------------------------------------------------------------------------------------------18833// KTX218834// ------------------------------------------------------------------------------------------------------1883518836#if BASISD_SUPPORT_KTX218837const uint8_t g_ktx2_file_identifier[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };1883818839ktx2_transcoder::ktx2_transcoder() :18840m_etc1s_transcoder()18841{18842clear();18843}1884418845void ktx2_transcoder::clear()18846{18847m_pData = nullptr;18848m_data_size = 0;1884918850memset((void *)&m_header, 0, sizeof(m_header));18851m_levels.clear();18852m_dfd.clear();18853m_key_values.clear();18854memset((void *)&m_etc1s_header, 0, sizeof(m_etc1s_header));18855m_etc1s_image_descs.clear();18856m_astc_6x6_intermediate_image_descs.clear();1885718858m_format = basist::basis_tex_format::cETC1S;1885918860m_dfd_color_model = 0;18861m_dfd_color_prims = KTX2_DF_PRIMARIES_UNSPECIFIED;18862m_dfd_transfer_func = 0;18863m_dfd_flags = 0;18864m_dfd_samples = 0;18865m_dfd_chan0 = KTX2_DF_CHANNEL_UASTC_RGB;18866m_dfd_chan1 = KTX2_DF_CHANNEL_UASTC_RGB;1886718868m_etc1s_transcoder.clear();1886918870m_def_transcoder_state.clear();1887118872m_has_alpha = false;18873m_is_video = false;18874m_ldr_hdr_upconversion_nit_multiplier = 0.0f;18875}1887618877bool ktx2_transcoder::init(const void* pData, uint32_t data_size)18878{18879clear();1888018881if (!pData)18882{18883BASISU_DEVEL_ERROR("ktx2_transcoder::init: pData is nullptr\n");18884assert(0);18885return false;18886}1888718888if (data_size <= sizeof(ktx2_header))18889{18890BASISU_DEVEL_ERROR("ktx2_transcoder::init: File is impossibly too small to be a valid KTX2 file\n");18891return false;18892}1889318894if (memcmp(pData, g_ktx2_file_identifier, sizeof(g_ktx2_file_identifier)) != 0)18895{18896BASISU_DEVEL_ERROR("ktx2_transcoder::init: KTX2 file identifier is not present\n");18897return false;18898}1889918900m_pData = static_cast<const uint8_t *>(pData);18901m_data_size = data_size;1890218903memcpy((void *)&m_header, pData, sizeof(m_header));1890418905// Check for supported VK formats. We may also need to parse the DFD.18906if ((m_header.m_vk_format != KTX2_VK_FORMAT_UNDEFINED) &&18907(m_header.m_vk_format != basist::KTX2_FORMAT_ASTC_4x4_SFLOAT_BLOCK) &&18908(m_header.m_vk_format != basist::KTX2_FORMAT_ASTC_6x6_SFLOAT_BLOCK))18909{18910BASISU_DEVEL_ERROR("ktx2_transcoder::init: KTX2 file must be in ETC1S or UASTC LDR/HDR format\n");18911return false;18912}1891318914// 3.3: "When format is VK_FORMAT_UNDEFINED, typeSize must equal 1."18915if (m_header.m_type_size != 1)18916{18917BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid type_size\n");18918return false;18919}1892018921// We only currently support 2D textures (plain, cubemapped, or texture array), which is by far the most common use case.18922// The BasisU library does not support 1D or 3D textures at all.18923if ((m_header.m_pixel_width < 1) || (m_header.m_pixel_height < 1) || (m_header.m_pixel_depth > 0))18924{18925BASISU_DEVEL_ERROR("ktx2_transcoder::init: Only 2D or cubemap textures are supported\n");18926return false;18927}1892818929// Face count must be 1 or 618930if ((m_header.m_face_count != 1) && (m_header.m_face_count != 6))18931{18932BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid face count, file is corrupted or invalid\n");18933return false;18934}1893518936if (m_header.m_face_count > 1)18937{18938// 3.4: Make sure cubemaps are square.18939if (m_header.m_pixel_width != m_header.m_pixel_height)18940{18941BASISU_DEVEL_ERROR("ktx2_transcoder::init: Cubemap is not square\n");18942return false;18943}18944}1894518946// 3.7 levelCount: "levelCount=0 is allowed, except for block-compressed formats"18947if (m_header.m_level_count < 1)18948{18949BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level count\n");18950return false;18951}1895218953// Sanity check the level count.18954if (m_header.m_level_count > KTX2_MAX_SUPPORTED_LEVEL_COUNT)18955{18956BASISU_DEVEL_ERROR("ktx2_transcoder::init: Too many levels or file is corrupted or invalid\n");18957return false;18958}1895918960if (m_header.m_supercompression_scheme > KTX2_SS_ZSTANDARD)18961{18962BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid/unsupported supercompression or file is corrupted or invalid\n");18963return false;18964}1896518966if (m_header.m_supercompression_scheme == KTX2_SS_BASISLZ)18967{18968#if 018969if (m_header.m_sgd_byte_length <= sizeof(ktx2_etc1s_global_data_header))18970{18971BASISU_DEVEL_ERROR("ktx2_transcoder::init: Supercompression global data is too small\n");18972return false;18973}18974#endif1897518976if (m_header.m_sgd_byte_offset.get_uint64() < sizeof(ktx2_header))18977{18978BASISU_DEVEL_ERROR("ktx2_transcoder::init: Supercompression global data offset is too low\n");18979return false;18980}1898118982if (m_header.m_sgd_byte_offset.get_uint64() + m_header.m_sgd_byte_length.get_uint64() > m_data_size)18983{18984BASISU_DEVEL_ERROR("ktx2_transcoder::init: Supercompression global data offset and/or length is too high\n");18985return false;18986}18987}1898818989if (!m_levels.try_resize(m_header.m_level_count))18990{18991BASISU_DEVEL_ERROR("ktx2_transcoder::init: Out of memory\n");18992return false;18993}1899418995const uint32_t level_index_size_in_bytes = basisu::maximum(1U, (uint32_t)m_header.m_level_count) * sizeof(ktx2_level_index);1899618997if ((sizeof(ktx2_header) + level_index_size_in_bytes) > m_data_size)18998{18999BASISU_DEVEL_ERROR("ktx2_transcoder::init: File is too small (can't read level index array)\n");19000return false;19001}1900219003memcpy((void *)&m_levels[0], m_pData + sizeof(ktx2_header), level_index_size_in_bytes);1900419005// Sanity check the level offsets and byte sizes19006for (uint32_t i = 0; i < m_levels.size(); i++)19007{19008if (m_levels[i].m_byte_offset.get_uint64() < sizeof(ktx2_header))19009{19010BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level offset (too low)\n");19011return false;19012}1901319014if (!m_levels[i].m_byte_length.get_uint64())19015{19016BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level byte length\n");19017}1901819019if ((m_levels[i].m_byte_offset.get_uint64() + m_levels[i].m_byte_length.get_uint64()) > m_data_size)19020{19021BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level offset and/or length\n");19022return false;19023}1902419025const uint64_t MAX_SANE_LEVEL_UNCOMP_SIZE = 2048ULL * 1024ULL * 1024ULL;1902619027if (m_levels[i].m_uncompressed_byte_length.get_uint64() >= MAX_SANE_LEVEL_UNCOMP_SIZE)19028{19029BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level offset (too large)\n");19030return false;19031}1903219033if (m_header.m_supercompression_scheme == KTX2_SS_BASISLZ)19034{19035if (m_levels[i].m_uncompressed_byte_length.get_uint64())19036{19037BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid uncompressed length (0)\n");19038return false;19039}19040}19041else if (m_header.m_supercompression_scheme >= KTX2_SS_ZSTANDARD)19042{19043if (!m_levels[i].m_uncompressed_byte_length.get_uint64())19044{19045BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid uncompressed length (1)\n");19046return false;19047}19048}19049}1905019051const uint32_t DFD_MINIMUM_SIZE = 44, DFD_MAXIMUM_SIZE = 60;19052if ((m_header.m_dfd_byte_length != DFD_MINIMUM_SIZE) && (m_header.m_dfd_byte_length != DFD_MAXIMUM_SIZE))19053{19054BASISU_DEVEL_ERROR("ktx2_transcoder::init: Unsupported DFD size\n");19055return false;19056}1905719058if (((m_header.m_dfd_byte_offset + m_header.m_dfd_byte_length) > m_data_size) || (m_header.m_dfd_byte_offset < sizeof(ktx2_header)))19059{19060BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid DFD offset and/or length\n");19061return false;19062}1906319064const uint8_t* pDFD = m_pData + m_header.m_dfd_byte_offset;1906519066if (!m_dfd.try_resize(m_header.m_dfd_byte_length))19067{19068BASISU_DEVEL_ERROR("ktx2_transcoder::init: Out of memory\n");19069return false;19070}1907119072memcpy(m_dfd.data(), pDFD, m_header.m_dfd_byte_length);1907319074// This is all hard coded for only ETC1S and UASTC.19075uint32_t dfd_total_size = basisu::read_le_dword(pDFD);1907619077// 3.10.3: Sanity check19078if (dfd_total_size != m_header.m_dfd_byte_length)19079{19080BASISU_DEVEL_ERROR("ktx2_transcoder::init: DFD size validation failed (1)\n");19081return false;19082}1908319084// 3.10.3: More sanity checking19085if (m_header.m_kvd_byte_length)19086{19087if (dfd_total_size != m_header.m_kvd_byte_offset - m_header.m_dfd_byte_offset)19088{19089BASISU_DEVEL_ERROR("ktx2_transcoder::init: DFD size validation failed (2)\n");19090return false;19091}19092}1909319094const uint32_t dfd_bits = basisu::read_le_dword(pDFD + 3 * sizeof(uint32_t));19095const uint32_t sample_channel0 = basisu::read_le_dword(pDFD + 7 * sizeof(uint32_t));1909619097m_dfd_color_model = dfd_bits & 255;19098m_dfd_color_prims = (ktx2_df_color_primaries)((dfd_bits >> 8) & 255);19099m_dfd_transfer_func = (dfd_bits >> 16) & 255;19100m_dfd_flags = (dfd_bits >> 24) & 255;1910119102// See 3.10.1.Restrictions19103if ((m_dfd_transfer_func != KTX2_KHR_DF_TRANSFER_LINEAR) && (m_dfd_transfer_func != KTX2_KHR_DF_TRANSFER_SRGB))19104{19105BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid DFD transfer function\n");19106return false;19107}1910819109if (m_dfd_color_model == KTX2_KDF_DF_MODEL_ETC1S)19110{19111if (m_header.m_vk_format != basist::KTX2_VK_FORMAT_UNDEFINED)19112{19113BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid header vkFormat\n");19114return false;19115}1911619117m_format = basist::basis_tex_format::cETC1S;1911819119// 3.10.2: "Whether the image has 1 or 2 slices can be determined from the DFD's sample count."19120// If m_has_alpha is true it may be 2-channel RRRG or 4-channel RGBA, but we let the caller deal with that.19121m_has_alpha = (m_header.m_dfd_byte_length == 60);1912219123m_dfd_samples = m_has_alpha ? 2 : 1;19124m_dfd_chan0 = (ktx2_df_channel_id)((sample_channel0 >> 24) & 15);1912519126if (m_has_alpha)19127{19128const uint32_t sample_channel1 = basisu::read_le_dword(pDFD + 11 * sizeof(uint32_t));19129m_dfd_chan1 = (ktx2_df_channel_id)((sample_channel1 >> 24) & 15);19130}19131}19132else if (m_dfd_color_model == KTX2_KDF_DF_MODEL_UASTC_LDR_4X4)19133{19134if (m_header.m_vk_format != basist::KTX2_VK_FORMAT_UNDEFINED)19135{19136BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid header vkFormat\n");19137return false;19138}1913919140m_format = basist::basis_tex_format::cUASTC4x4;1914119142m_dfd_samples = 1;19143m_dfd_chan0 = (ktx2_df_channel_id)((sample_channel0 >> 24) & 15);1914419145// We're assuming "DATA" means RGBA so it has alpha.19146m_has_alpha = (m_dfd_chan0 == KTX2_DF_CHANNEL_UASTC_RGBA) || (m_dfd_chan0 == KTX2_DF_CHANNEL_UASTC_RRRG);19147}19148else if (m_dfd_color_model == KTX2_KDF_DF_MODEL_UASTC_HDR_4X4)19149{19150// UASTC HDR 4x4 is standard ASTC HDR 4x4 texture data. Check the header's vkFormat.19151if (m_header.m_vk_format != basist::KTX2_FORMAT_ASTC_4x4_SFLOAT_BLOCK)19152{19153BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid header vkFormat\n");19154return false;19155}1915619157m_format = basist::basis_tex_format::cUASTC_HDR_4x4;1915819159m_dfd_samples = 1;19160m_dfd_chan0 = (ktx2_df_channel_id)((sample_channel0 >> 24) & 15);1916119162// We're assuming "DATA" means RGBA so it has alpha.19163// [11/26/2024] - changed to always false for now19164m_has_alpha = false;// (m_dfd_chan0 == KTX2_DF_CHANNEL_UASTC_RGBA) || (m_dfd_chan0 == KTX2_DF_CHANNEL_UASTC_RRRG);19165}19166else if (m_dfd_color_model == KTX2_KDF_DF_MODEL_ASTC)19167{19168// The DFD indicates plain ASTC texture data. We only support ASTC HDR 6x6 - check the header's vkFormat.19169if (m_header.m_vk_format != basist::KTX2_FORMAT_ASTC_6x6_SFLOAT_BLOCK)19170{19171BASISU_DEVEL_ERROR("ktx2_transcoder::init: DVD color model is ASTC, but the header's vkFormat isn't KTX2_FORMAT_ASTC_6x6_SFLOAT_BLOCK\n");19172return false;19173}1917419175m_format = basist::basis_tex_format::cASTC_HDR_6x6;1917619177m_dfd_samples = 1;19178m_dfd_chan0 = (ktx2_df_channel_id)((sample_channel0 >> 24) & 15);1917919180m_has_alpha = false;19181}19182else if (m_dfd_color_model == KTX2_KDF_DF_MODEL_ASTC_HDR_6X6_INTERMEDIATE)19183{19184// Custom variable block size ASTC HDR 6x6 texture data.19185if (m_header.m_vk_format != basist::KTX2_VK_FORMAT_UNDEFINED)19186{19187BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid header vkFormat\n");19188return false;19189}1919019191m_format = basist::basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE;1919219193m_dfd_samples = 1;19194m_dfd_chan0 = (ktx2_df_channel_id)((sample_channel0 >> 24) & 15);1919519196m_has_alpha = false;19197}19198else19199{19200// Unsupported DFD color model.19201BASISU_DEVEL_ERROR("ktx2_transcoder::init: Unsupported DFD color model\n");19202return false;19203}1920419205if (!read_key_values())19206{19207BASISU_DEVEL_ERROR("ktx2_transcoder::init: read_key_values() failed\n");19208return false;19209}1921019211// Check for a KTXanimData key19212for (uint32_t i = 0; i < m_key_values.size(); i++)19213{19214if (strcmp(reinterpret_cast<const char*>(m_key_values[i].m_key.data()), "KTXanimData") == 0)19215{19216m_is_video = true;19217break;19218}19219}1922019221m_ldr_hdr_upconversion_nit_multiplier = 0.0f;1922219223for (uint32_t i = 0; i < m_key_values.size(); i++)19224{19225if (strcmp(reinterpret_cast<const char*>(m_key_values[i].m_key.data()), "LDRUpconversionMultiplier") == 0)19226{19227m_ldr_hdr_upconversion_nit_multiplier = (float)atof(reinterpret_cast<const char*>(m_key_values[i].m_value.data()));1922819229if (std::isnan(m_ldr_hdr_upconversion_nit_multiplier) || std::isinf(m_ldr_hdr_upconversion_nit_multiplier) || (m_ldr_hdr_upconversion_nit_multiplier < 0.0f))19230m_ldr_hdr_upconversion_nit_multiplier = 0;1923119232break;19233}19234}1923519236return true;19237}1923819239uint32_t ktx2_transcoder::get_etc1s_image_descs_image_flags(uint32_t level_index, uint32_t layer_index, uint32_t face_index) const19240{19241const uint32_t etc1s_image_index =19242(level_index * basisu::maximum<uint32_t>(m_header.m_layer_count, 1) * m_header.m_face_count) +19243layer_index * m_header.m_face_count +19244face_index;1924519246if (etc1s_image_index >= get_etc1s_image_descs().size())19247{19248assert(0);19249return 0;19250}1925119252return get_etc1s_image_descs()[etc1s_image_index].m_image_flags;19253}1925419255const basisu::uint8_vec* ktx2_transcoder::find_key(const std::string& key_name) const19256{19257for (uint32_t i = 0; i < m_key_values.size(); i++)19258if (strcmp((const char *)m_key_values[i].m_key.data(), key_name.c_str()) == 0)19259return &m_key_values[i].m_value;1926019261return nullptr;19262}1926319264bool ktx2_transcoder::start_transcoding()19265{19266if (!m_pData)19267{19268BASISU_DEVEL_ERROR("ktx2_transcoder::start_transcoding: Must call init() first\n");19269return false;19270}1927119272if (m_header.m_supercompression_scheme == KTX2_SS_BASISLZ)19273{19274if (m_format == basis_tex_format::cETC1S)19275{19276// Check if we've already decompressed the ETC1S global data. If so don't unpack it again.19277if (!m_etc1s_transcoder.get_endpoints().empty())19278return true;1927919280if (!decompress_etc1s_global_data())19281{19282BASISU_DEVEL_ERROR("ktx2_transcoder::start_transcoding: decompress_etc1s_global_data() failed\n");19283return false;19284}1928519286if (!m_is_video)19287{19288// See if there are any P-frames. If so it must be a video, even if there wasn't a KTXanimData key.19289// Video cannot be a cubemap, and it must be a texture array.19290if ((m_header.m_face_count == 1) && (m_header.m_layer_count > 1))19291{19292for (uint32_t i = 0; i < m_etc1s_image_descs.size(); i++)19293{19294if (m_etc1s_image_descs[i].m_image_flags & KTX2_IMAGE_IS_P_FRAME)19295{19296m_is_video = true;19297break;19298}19299}19300}19301}19302}19303else if (m_format == basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE)19304{19305if (m_astc_6x6_intermediate_image_descs.size())19306return true;1930719308if (!read_astc_6x6_hdr_intermediate_global_data())19309{19310BASISU_DEVEL_ERROR("ktx2_transcoder::start_transcoding: read_astc_6x6_hdr_intermediate_global_data() failed\n");19311return false;19312}19313}19314else19315{19316BASISU_DEVEL_ERROR("ktx2_transcoder::start_transcoding: Invalid supercompression scheme and/or format\n");19317return false;19318}19319}19320else if (m_header.m_supercompression_scheme == KTX2_SS_ZSTANDARD)19321{19322#if !BASISD_SUPPORT_KTX2_ZSTD19323BASISU_DEVEL_ERROR("ktx2_transcoder::start_transcoding: File uses zstd supercompression, but zstd support was not enabled at compilation time (BASISD_SUPPORT_KTX2_ZSTD == 0)\n");19324return false;19325#endif19326}1932719328return true;19329}1933019331bool ktx2_transcoder::get_image_level_info(ktx2_image_level_info& level_info, uint32_t level_index, uint32_t layer_index, uint32_t face_index) const19332{19333if (level_index >= m_levels.size())19334{19335BASISU_DEVEL_ERROR("ktx2_transcoder::get_image_level_info: level_index >= m_levels.size()\n");19336return false;19337}1933819339if (m_header.m_face_count > 1)19340{19341if (face_index >= 6)19342{19343BASISU_DEVEL_ERROR("ktx2_transcoder::get_image_level_info: face_index >= 6\n");19344return false;19345}19346}19347else if (face_index != 0)19348{19349BASISU_DEVEL_ERROR("ktx2_transcoder::get_image_level_info: face_index != 0\n");19350return false;19351}1935219353if (layer_index >= basisu::maximum<uint32_t>(m_header.m_layer_count, 1))19354{19355BASISU_DEVEL_ERROR("ktx2_transcoder::get_image_level_info: layer_index >= maximum<uint32_t>(m_header.m_layer_count, 1)\n");19356return false;19357}1935819359const uint32_t level_width = basisu::maximum<uint32_t>(m_header.m_pixel_width >> level_index, 1);19360const uint32_t level_height = basisu::maximum<uint32_t>(m_header.m_pixel_height >> level_index, 1);1936119362const uint32_t block_width = get_block_width();19363const uint32_t block_height = get_block_height();1936419365const uint32_t num_blocks_x = (level_width + block_width - 1) / block_width;19366const uint32_t num_blocks_y = (level_height + block_height - 1) / block_height;1936719368level_info.m_face_index = face_index;19369level_info.m_layer_index = layer_index;19370level_info.m_level_index = level_index;19371level_info.m_orig_width = level_width;19372level_info.m_orig_height = level_height;19373level_info.m_width = num_blocks_x * block_width;19374level_info.m_height = num_blocks_y * block_height;19375level_info.m_block_width = block_width;19376level_info.m_block_height = block_height;19377level_info.m_num_blocks_x = num_blocks_x;19378level_info.m_num_blocks_y = num_blocks_y;19379level_info.m_total_blocks = num_blocks_x * num_blocks_y;19380level_info.m_alpha_flag = m_has_alpha;19381level_info.m_iframe_flag = false;1938219383if (m_etc1s_image_descs.size())19384{19385const uint32_t etc1s_image_index =19386(level_index * basisu::maximum<uint32_t>(m_header.m_layer_count, 1) * m_header.m_face_count) +19387layer_index * m_header.m_face_count +19388face_index;1938919390level_info.m_iframe_flag = (m_etc1s_image_descs[etc1s_image_index].m_image_flags & KTX2_IMAGE_IS_P_FRAME) == 0;19391}1939219393return true;19394}1939519396bool ktx2_transcoder::transcode_image_level(19397uint32_t level_index, uint32_t layer_index, uint32_t face_index,19398void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,19399basist::transcoder_texture_format fmt,19400uint32_t decode_flags, uint32_t output_row_pitch_in_blocks_or_pixels, uint32_t output_rows_in_pixels, int channel0, int channel1,19401ktx2_transcoder_state* pState)19402{19403if (!m_pData)19404{19405BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: Must call init() first\n");19406return false;19407}1940819409if (!pState)19410pState = &m_def_transcoder_state;1941119412if (level_index >= m_levels.size())19413{19414BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: level_index >= m_levels.size()\n");19415return false;19416}1941719418if (m_header.m_face_count > 1)19419{19420if (face_index >= 6)19421{19422BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: face_index >= 6\n");19423return false;19424}19425}19426else if (face_index != 0)19427{19428BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: face_index != 0\n");19429return false;19430}1943119432if (layer_index >= basisu::maximum<uint32_t>(m_header.m_layer_count, 1))19433{19434BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: layer_index >= maximum<uint32_t>(m_header.m_layer_count, 1)\n");19435return false;19436}1943719438const uint8_t* pComp_level_data = m_pData + m_levels[level_index].m_byte_offset.get_uint64();19439uint64_t comp_level_data_size = m_levels[level_index].m_byte_length.get_uint64();1944019441const uint8_t* pUncomp_level_data = pComp_level_data;19442uint64_t uncomp_level_data_size = comp_level_data_size;1944319444if (uncomp_level_data_size > UINT32_MAX)19445{19446BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: uncomp_level_data_size > UINT32_MAX\n");19447return false;19448}1944919450if (m_header.m_supercompression_scheme == KTX2_SS_ZSTANDARD)19451{19452// Check if we've already decompressed this level's supercompressed data.19453if ((int)level_index != pState->m_uncomp_data_level_index)19454{19455// Uncompress the entire level's supercompressed data.19456if (!decompress_level_data(level_index, pState->m_level_uncomp_data))19457{19458BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: decompress_level_data() failed\n");19459return false;19460}19461pState->m_uncomp_data_level_index = level_index;19462}1946319464pUncomp_level_data = pState->m_level_uncomp_data.data();19465uncomp_level_data_size = pState->m_level_uncomp_data.size();19466}1946719468const uint32_t level_width = basisu::maximum<uint32_t>(m_header.m_pixel_width >> level_index, 1);19469const uint32_t level_height = basisu::maximum<uint32_t>(m_header.m_pixel_height >> level_index, 1);19470const uint32_t num_blocks4_x = (level_width + 3) >> 2;19471const uint32_t num_blocks4_y = (level_height + 3) >> 2;1947219473if (m_format == basist::basis_tex_format::cETC1S)19474{19475// Ensure start_transcoding() was called.19476if (m_etc1s_transcoder.get_endpoints().empty())19477{19478BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: must call start_transcoding() first\n");19479return false;19480}1948119482const uint32_t etc1s_image_index =19483(level_index * basisu::maximum<uint32_t>(m_header.m_layer_count, 1) * m_header.m_face_count) +19484layer_index * m_header.m_face_count +19485face_index;1948619487// Sanity check19488if (etc1s_image_index >= m_etc1s_image_descs.size())19489{19490BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: etc1s_image_index >= m_etc1s_image_descs.size()\n");19491assert(0);19492return false;19493}1949419495const ktx2_etc1s_image_desc& image_desc = m_etc1s_image_descs[etc1s_image_index];1949619497if (!m_etc1s_transcoder.transcode_image(fmt,19498pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, m_pData, m_data_size,19499num_blocks4_x, num_blocks4_y, level_width, level_height,19500level_index,19501m_levels[level_index].m_byte_offset.get_uint64() + image_desc.m_rgb_slice_byte_offset, image_desc.m_rgb_slice_byte_length,19502image_desc.m_alpha_slice_byte_length ? (m_levels[level_index].m_byte_offset.get_uint64() + image_desc.m_alpha_slice_byte_offset) : 0, image_desc.m_alpha_slice_byte_length,19503decode_flags, m_has_alpha,19504m_is_video, output_row_pitch_in_blocks_or_pixels, &pState->m_transcoder_state, output_rows_in_pixels))19505{19506BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: ETC1S transcode_image() failed, this is either a bug or the file is corrupted/invalid\n");19507return false;19508}19509}19510else if (m_format == basist::basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE)19511{19512if (!m_astc_6x6_intermediate_image_descs.size())19513{19514BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: must call start_transcoding() first\n");19515return false;19516}1951719518const uint32_t num_blocks6_x = (level_width + 5) / 6;19519const uint32_t num_blocks6_y = (level_height + 5) / 6;1952019521const uint32_t image_index =19522(level_index * basisu::maximum<uint32_t>(m_header.m_layer_count, 1) * m_header.m_face_count) +19523layer_index * m_header.m_face_count +19524face_index;1952519526// Sanity check19527if (image_index >= m_astc_6x6_intermediate_image_descs.size())19528{19529BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: Invalid image_index\n");19530assert(0);19531return false;19532}1953319534const ktx2_astc_hdr_6x6_intermediate_image_desc& image_desc = m_astc_6x6_intermediate_image_descs[image_index];1953519536if (!m_astc_hdr_6x6_intermediate_transcoder.transcode_image(fmt,19537pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels,19538m_pData, m_data_size, num_blocks6_x, num_blocks6_y, level_width, level_height, level_index,19539m_levels[level_index].m_byte_offset.get_uint64() + image_desc.m_rgb_slice_byte_offset, image_desc.m_rgb_slice_byte_length,19540decode_flags, m_has_alpha, m_is_video, output_row_pitch_in_blocks_or_pixels, nullptr, output_rows_in_pixels, channel0, channel1))19541{19542BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: ASTC 6x6 HDR transcode_image() failed, this is either a bug or the file is corrupted/invalid\n");19543return false;19544}19545}19546else if (m_format == basist::basis_tex_format::cASTC_HDR_6x6)19547{19548const uint32_t num_blocks6_x = (level_width + 5) / 6;19549const uint32_t num_blocks6_y = (level_height + 5) / 6;1955019551// Compute length and offset to uncompressed 2D UASTC texture data, given the face/layer indices.19552assert(uncomp_level_data_size == m_levels[level_index].m_uncompressed_byte_length.get_uint64());19553const uint32_t total_2D_image_size = num_blocks6_x * num_blocks6_y * sizeof(astc_helpers::astc_block);1955419555const uint32_t uncomp_ofs = (layer_index * m_header.m_face_count + face_index) * total_2D_image_size;1955619557// Sanity checks19558if (uncomp_ofs >= uncomp_level_data_size)19559{19560BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: uncomp_ofs >= total_2D_image_size\n");19561return false;19562}1956319564if ((uncomp_level_data_size - uncomp_ofs) < total_2D_image_size)19565{19566BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: (uncomp_level_data_size - uncomp_ofs) < total_2D_image_size\n");19567return false;19568}1956919570if (!m_astc_hdr_6x6_transcoder.transcode_image(fmt,19571pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels,19572(const uint8_t*)pUncomp_level_data + uncomp_ofs, (uint32_t)total_2D_image_size, num_blocks6_x, num_blocks6_y, level_width, level_height, level_index,195730, (uint32_t)total_2D_image_size,19574decode_flags, m_has_alpha, m_is_video, output_row_pitch_in_blocks_or_pixels, nullptr, output_rows_in_pixels, channel0, channel1))19575{19576BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: ASTC 6x6 HDR transcode_image() failed, this is either a bug or the file is corrupted/invalid\n");19577return false;19578}19579}19580else if ((m_format == basist::basis_tex_format::cUASTC4x4) ||19581(m_format == basist::basis_tex_format::cUASTC_HDR_4x4))19582{19583// Compute length and offset to uncompressed 2D UASTC texture data, given the face/layer indices.19584assert(uncomp_level_data_size == m_levels[level_index].m_uncompressed_byte_length.get_uint64());19585const uint32_t total_2D_image_size = num_blocks4_x * num_blocks4_y * KTX2_UASTC_BLOCK_SIZE;1958619587const uint32_t uncomp_ofs = (layer_index * m_header.m_face_count + face_index) * total_2D_image_size;1958819589// Sanity checks19590if (uncomp_ofs >= uncomp_level_data_size)19591{19592BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: uncomp_ofs >= total_2D_image_size\n");19593return false;19594}1959519596if ((uncomp_level_data_size - uncomp_ofs) < total_2D_image_size)19597{19598BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: (uncomp_level_data_size - uncomp_ofs) < total_2D_image_size\n");19599return false;19600}1960119602if (m_format == basist::basis_tex_format::cUASTC_HDR_4x4)19603{19604if (!m_uastc_hdr_transcoder.transcode_image(fmt,19605pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels,19606(const uint8_t*)pUncomp_level_data + uncomp_ofs, (uint32_t)total_2D_image_size, num_blocks4_x, num_blocks4_y, level_width, level_height, level_index,196070, (uint32_t)total_2D_image_size,19608decode_flags, m_has_alpha, m_is_video, output_row_pitch_in_blocks_or_pixels, nullptr, output_rows_in_pixels, channel0, channel1))19609{19610BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: UASTC HDR transcode_image() failed, this is either a bug or the file is corrupted/invalid\n");19611return false;19612}19613}19614else19615{19616if (!m_uastc_transcoder.transcode_image(fmt,19617pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels,19618(const uint8_t*)pUncomp_level_data + uncomp_ofs, (uint32_t)total_2D_image_size, num_blocks4_x, num_blocks4_y, level_width, level_height, level_index,196190, (uint32_t)total_2D_image_size,19620decode_flags, m_has_alpha, m_is_video, output_row_pitch_in_blocks_or_pixels, nullptr, output_rows_in_pixels, channel0, channel1))19621{19622BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: UASTC transcode_image() failed, this is either a bug or the file is corrupted/invalid\n");19623return false;19624}19625}19626}19627else19628{19629// Shouldn't get here.19630BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: Internal error\n");19631assert(0);19632return false;19633}1963419635return true;19636}1963719638bool ktx2_transcoder::decompress_level_data(uint32_t level_index, basisu::uint8_vec& uncomp_data)19639{19640const uint8_t* pComp_data = m_levels[level_index].m_byte_offset.get_uint64() + m_pData;19641const uint64_t comp_size = m_levels[level_index].m_byte_length.get_uint64();1964219643const uint64_t uncomp_size = m_levels[level_index].m_uncompressed_byte_length.get_uint64();1964419645if (((size_t)comp_size) != comp_size)19646{19647BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Compressed data too large\n");19648return false;19649}19650if (((size_t)uncomp_size) != uncomp_size)19651{19652BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Uncompressed data too large\n");19653return false;19654}1965519656if (!uncomp_data.try_resize((size_t)uncomp_size))19657{19658BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Out of memory\n");19659return false;19660}1966119662if (m_header.m_supercompression_scheme == KTX2_SS_ZSTANDARD)19663{19664#if BASISD_SUPPORT_KTX2_ZSTD19665size_t actualUncompSize = ZSTD_decompress(uncomp_data.data(), (size_t)uncomp_size, pComp_data, (size_t)comp_size);19666if (ZSTD_isError(actualUncompSize))19667{19668BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Zstd decompression failed, file is invalid or corrupted\n");19669return false;19670}19671if (actualUncompSize != uncomp_size)19672{19673BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Zstd decompression returned too few bytes, file is invalid or corrupted\n");19674return false;19675}19676#else19677BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: File uses Zstd supercompression, but Zstd support was not enabled at compile time (BASISD_SUPPORT_KTX2_ZSTD is 0)\n");19678return false;19679#endif19680}1968119682return true;19683}1968419685bool ktx2_transcoder::read_astc_6x6_hdr_intermediate_global_data()19686{19687const uint32_t image_count = basisu::maximum<uint32_t>(m_header.m_layer_count, 1) * m_header.m_face_count * m_header.m_level_count;19688assert(image_count);1968919690const uint8_t* pSrc = m_pData + m_header.m_sgd_byte_offset.get_uint64();1969119692if (m_header.m_sgd_byte_length.get_uint64() != image_count * sizeof(ktx2_astc_hdr_6x6_intermediate_image_desc))19693{19694BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_astc_6x6_hdr_intermediate_global_data: Invalid global data length\n");19695return false;19696}1969719698m_astc_6x6_intermediate_image_descs.resize(image_count);1969919700memcpy((void *)m_astc_6x6_intermediate_image_descs.data(), pSrc, sizeof(ktx2_astc_hdr_6x6_intermediate_image_desc) * image_count);1970119702// Sanity check the image descs19703for (uint32_t i = 0; i < image_count; i++)19704{19705// transcode_image() will validate the slice offsets/lengths before transcoding.1970619707if (!m_astc_6x6_intermediate_image_descs[i].m_rgb_slice_byte_length)19708{19709BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_astc_6x6_hdr_intermediate_global_data: image descs sanity check failed (1)\n");19710return false;19711}19712}1971319714return true;19715}1971619717bool ktx2_transcoder::decompress_etc1s_global_data()19718{19719// Note: we don't actually support 3D textures in here yet19720//uint32_t layer_pixel_depth = basisu::maximum<uint32_t>(m_header.m_pixel_depth, 1);19721//for (uint32_t i = 1; i < m_header.m_level_count; i++)19722// layer_pixel_depth += basisu::maximum<uint32_t>(m_header.m_pixel_depth >> i, 1);1972319724const uint32_t image_count = basisu::maximum<uint32_t>(m_header.m_layer_count, 1) * m_header.m_face_count * m_header.m_level_count;19725assert(image_count);1972619727const uint8_t* pSrc = m_pData + m_header.m_sgd_byte_offset.get_uint64();1972819729memcpy((void *)&m_etc1s_header, pSrc, sizeof(ktx2_etc1s_global_data_header));19730pSrc += sizeof(ktx2_etc1s_global_data_header);1973119732if ((!m_etc1s_header.m_endpoints_byte_length) || (!m_etc1s_header.m_selectors_byte_length) || (!m_etc1s_header.m_tables_byte_length))19733{19734BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: Invalid ETC1S global data\n");19735return false;19736}1973719738if ((!m_etc1s_header.m_endpoint_count) || (!m_etc1s_header.m_selector_count))19739{19740BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: endpoint and/or selector count is 0, file is invalid or corrupted\n");19741return false;19742}1974319744// Sanity check the ETC1S header.19745if ((sizeof(ktx2_etc1s_global_data_header) +19746sizeof(ktx2_etc1s_image_desc) * image_count +19747m_etc1s_header.m_endpoints_byte_length +19748m_etc1s_header.m_selectors_byte_length +19749m_etc1s_header.m_tables_byte_length +19750m_etc1s_header.m_extended_byte_length) > m_header.m_sgd_byte_length.get_uint64())19751{19752BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: SGD byte length is too small, file is invalid or corrupted\n");19753return false;19754}1975519756if (!m_etc1s_image_descs.try_resize(image_count))19757{19758BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: Out of memory\n");19759return false;19760}1976119762memcpy((void *)m_etc1s_image_descs.data(), pSrc, sizeof(ktx2_etc1s_image_desc) * image_count);19763pSrc += sizeof(ktx2_etc1s_image_desc) * image_count;1976419765// Sanity check the ETC1S image descs19766for (uint32_t i = 0; i < image_count; i++)19767{19768// m_etc1s_transcoder.transcode_image() will validate the slice offsets/lengths before transcoding.1976919770if (!m_etc1s_image_descs[i].m_rgb_slice_byte_length)19771{19772BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: ETC1S image descs sanity check failed (1)\n");19773return false;19774}1977519776if (m_has_alpha)19777{19778if (!m_etc1s_image_descs[i].m_alpha_slice_byte_length)19779{19780BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: ETC1S image descs sanity check failed (2)\n");19781return false;19782}19783}19784}1978519786const uint8_t* pEndpoint_data = pSrc;19787const uint8_t* pSelector_data = pSrc + m_etc1s_header.m_endpoints_byte_length;19788const uint8_t* pTables_data = pSrc + m_etc1s_header.m_endpoints_byte_length + m_etc1s_header.m_selectors_byte_length;1978919790if (!m_etc1s_transcoder.decode_tables(pTables_data, m_etc1s_header.m_tables_byte_length))19791{19792BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: decode_tables() failed, file is invalid or corrupted\n");19793return false;19794}1979519796if (!m_etc1s_transcoder.decode_palettes(19797m_etc1s_header.m_endpoint_count, pEndpoint_data, m_etc1s_header.m_endpoints_byte_length,19798m_etc1s_header.m_selector_count, pSelector_data, m_etc1s_header.m_selectors_byte_length))19799{19800BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: decode_palettes() failed, file is likely corrupted\n");19801return false;19802}1980319804return true;19805}1980619807bool ktx2_transcoder::read_key_values()19808{19809if (!m_header.m_kvd_byte_length)19810{19811if (m_header.m_kvd_byte_offset)19812{19813BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Invalid KVD byte offset (it should be zero when the length is zero)\n");19814return false;19815}1981619817return true;19818}1981919820if (m_header.m_kvd_byte_offset < sizeof(ktx2_header))19821{19822BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Invalid KVD byte offset\n");19823return false;19824}1982519826if ((m_header.m_kvd_byte_offset + m_header.m_kvd_byte_length) > m_data_size)19827{19828BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Invalid KVD byte offset and/or length\n");19829return false;19830}1983119832const uint8_t* pSrc = m_pData + m_header.m_kvd_byte_offset;19833uint32_t src_left = m_header.m_kvd_byte_length;1983419835if (!m_key_values.try_reserve(8))19836{19837BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n");19838return false;19839}1984019841while (src_left > sizeof(uint32_t))19842{19843uint32_t l = basisu::read_le_dword(pSrc);1984419845pSrc += sizeof(uint32_t);19846src_left -= sizeof(uint32_t);1984719848if (l < 2)19849{19850BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Failed reading key value fields (0)\n");19851return false;19852}1985319854if (src_left < l)19855{19856BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Failed reading key value fields (1)\n");19857return false;19858}1985919860if (!m_key_values.try_resize(m_key_values.size() + 1))19861{19862BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n");19863return false;19864}1986519866basisu::uint8_vec& key_data = m_key_values.back().m_key;19867basisu::uint8_vec& value_data = m_key_values.back().m_value;1986819869do19870{19871if (!l)19872{19873BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Failed reading key value fields (2)\n");19874return false;19875}1987619877if (!key_data.try_push_back(*pSrc++))19878{19879BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n");19880return false;19881}1988219883src_left--;19884l--;1988519886} while (key_data.back());1988719888// Ensure key and value are definitely 0 terminated19889if (!key_data.try_push_back('\0'))19890{19891BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n");19892return false;19893}1989419895if (!value_data.try_resize(l))19896{19897BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n");19898return false;19899}1990019901if (l)19902{19903memcpy(value_data.data(), pSrc, l);19904pSrc += l;19905src_left -= l;19906}1990719908// Ensure key and value are definitely 0 terminated19909if (!value_data.try_push_back('\0'))19910{19911BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n");19912return false;19913}1991419915uint32_t ofs = (uint32_t)(pSrc - m_pData) & 3;19916uint32_t alignment_bytes = (4 - ofs) & 3;1991719918if (src_left < alignment_bytes)19919{19920BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Failed reading key value fields (3)\n");19921return false;19922}1992319924pSrc += alignment_bytes;19925src_left -= alignment_bytes;19926}1992719928return true;19929}1993019931#endif // BASISD_SUPPORT_KTX21993219933bool basisu_transcoder_supports_ktx2()19934{19935#if BASISD_SUPPORT_KTX219936return true;19937#else19938return false;19939#endif19940}1994119942bool basisu_transcoder_supports_ktx2_zstd()19943{19944#if BASISD_SUPPORT_KTX2_ZSTD19945return true;19946#else19947return false;19948#endif19949}1995019951//-------------------------------1995219953#if BASISD_SUPPORT_UASTC_HDR19954// This float->half conversion matches how "F32TO16" works on Intel GPU's.19955basist::half_float float_to_half(float val)19956{19957union { float f; int32_t i; uint32_t u; } fi = { val };19958const int flt_m = fi.i & 0x7FFFFF, flt_e = (fi.i >> 23) & 0xFF, flt_s = (fi.i >> 31) & 0x1;19959int s = flt_s, e = 0, m = 0;1996019961// inf/NaN19962if (flt_e == 0xff)19963{19964e = 31;19965if (flt_m != 0) // NaN19966m = 1;19967}19968// not zero or denormal19969else if (flt_e != 0)19970{19971int new_exp = flt_e - 127;19972if (new_exp > 15)19973e = 31;19974else if (new_exp < -14)19975m = lrintf((1 << 24) * fabsf(fi.f));19976else19977{19978e = new_exp + 15;19979m = lrintf(flt_m * (1.0f / ((float)(1 << 13))));19980}19981}1998219983assert((0 <= m) && (m <= 1024));19984if (m == 1024)19985{19986e++;19987m = 0;19988}1998919990assert((s >= 0) && (s <= 1));19991assert((e >= 0) && (e <= 31));19992assert((m >= 0) && (m <= 1023));1999319994basist::half_float result = (basist::half_float)((s << 15) | (e << 10) | m);19995return result;19996}1999719998//------------------------------------------------------------------------------------------------19999// HDR support20000//20001// Originally from bc6h_enc.cpp20002// BC6H decoder fuzzed vs. DirectXTex's for unsigned/signed2000320004const uint8_t g_bc6h_mode_sig_bits[NUM_BC6H_MODES][4] = // base bits, r, g, b20005{20006// 2 subsets20007{ 10, 5, 5, 5, }, // 0, mode 1 in MS/D3D docs20008{ 7, 6, 6, 6, }, // 120009{ 11, 5, 4, 4, }, // 220010{ 11, 4, 5, 4, }, // 320011{ 11, 4, 4, 5, }, // 420012{ 9, 5, 5, 5, }, // 520013{ 8, 6, 5, 5, }, // 620014{ 8, 5, 6, 5, }, // 720015{ 8, 5, 5, 6, }, // 820016{ 6, 6, 6, 6, }, // 9, endpoints not delta encoded, mode 10 in MS/D3D docs20017// 1 subset20018{ 10, 10, 10, 10, }, // 10, endpoints not delta encoded, mode 11 in MS/D3D docs20019{ 11, 9, 9, 9, }, // 1120020{ 12, 8, 8, 8, }, // 1220021{ 16, 4, 4, 4, } // 13, also useful for solid blocks20022};2002320024const int8_t g_bc6h_mode_lookup[32] = { 0, 1, 2, 10, 0, 1, 3, 11, 0, 1, 4, 12, 0, 1, 5, 13, 0, 1, 6, -1, 0, 1, 7, -1, 0, 1, 8, -1, 0, 1, 9, -1 };2002520026const bc6h_bit_layout g_bc6h_bit_layouts[NUM_BC6H_MODES][MAX_BC6H_LAYOUT_INDEX] =20027{20028// comp_index, subset*2+lh_index, last_bit, first_bit20029//------------------------ mode 0: 2 subsets, Weight bits: 46 bits, Endpoint bits: 75 bits (10.555, 10.555, 10.555), delta20030{ { 1, 2, 4, -1 }, { 2, 2, 4, -1 }, { 2, 3, 4, -1 }, { 0, 0, 9, 0 }, { 1, 0, 9, 0 }, { 2, 0, 9, 0 }, { 0, 1, 4, 0 },20031{ 1, 3, 4, -1 }, { 1, 2, 3, 0 }, { 1, 1, 4, 0 }, { 2, 3, 0, -1 }, { 1, 3, 3, 0 }, { 2, 1, 4, 0 }, { 2, 3, 1, -1 },20032{ 2, 2, 3, 0 }, { 0, 2, 4, 0 }, { 2, 3, 2, -1 }, { 0, 3, 4, 0 }, { 2, 3, 3, -1 }, { 3, -1, 4, 0 }, {-1, 0, 0, 0} },20033//------------------------ mode 1: 2 subsets, Weight bits: 46 bits, Endpoint bits: 75 bits (7.666, 7.666, 7.666), delta20034{ { 1, 2, 5, -1 },{ 1, 3, 4, -1 },{ 1, 3, 5, -1 },{ 0, 0, 6, 0 },{ 2, 3, 0, -1 },{ 2, 3, 1, -1 },{ 2, 2, 4, -1 },20035{ 1, 0, 6, 0 },{ 2, 2, 5, -1 },{ 2, 3, 2, -1 },{ 1, 2, 4, -1 },{ 2, 0, 6, 0 },{ 2, 3, 3, -1 },{ 2, 3, 5, -1 },20036{ 2, 3, 4, -1 },{ 0, 1, 5, 0 },{ 1, 2, 3, 0 },{ 1, 1, 5, 0 },{ 1, 3, 3, 0 },{ 2, 1, 5, 0 },{ 2, 2, 3, 0 },{ 0, 2, 5, 0 },20037{ 0, 3, 5, 0 },{ 3, -1, 4, 0 }, {-1, 0, 0, 0} },20038//------------------------ mode 2: 2 subsets, Weight bits: 46 bits, Endpoint bits: 72 bits (11.555, 11.444, 11.444), delta20039{ { 0, 0, 9, 0 },{ 1, 0, 9, 0 },{ 2, 0, 9, 0 },{ 0, 1, 4, 0 },{ 0, 0, 10, -1 },{ 1, 2, 3, 0 },{ 1, 1, 3, 0 },{ 1, 0, 10, -1 },20040{ 2, 3, 0, -1 },{ 1, 3, 3, 0 },{ 2, 1, 3, 0 },{ 2, 0, 10, -1 },{ 2, 3, 1, -1 },{ 2, 2, 3, 0 },{ 0, 2, 4, 0 },{ 2, 3, 2, -1 },20041{ 0, 3, 4, 0 },{ 2, 3, 3, -1 },{ 3, -1, 4, 0 }, {-1, 0, 0, 0} },20042//------------------------ mode 3: 2 subsets, Weight bits: 46 bits, Endpoint bits: 72 bits (11.444, 11.555, 11.444), delta20043{ { 0, 0, 9, 0 },{ 1, 0, 9, 0 },{ 2, 0, 9, 0 },{ 0, 1, 3, 0 },{ 0, 0, 10, -1 },{ 1, 3, 4, -1 },{ 1, 2, 3, 0 },{ 1, 1, 4, 0 },20044{ 1, 0, 10, -1 },{ 1, 3, 3, 0 },{ 2, 1, 3, 0 },{ 2, 0, 10, -1 },{ 2, 3, 1, -1 },{ 2, 2, 3, 0 },{ 0, 2, 3, 0 },{ 2, 3, 0, -1 },20045{ 2, 3, 2, -1 },{ 0, 3, 3, 0 },{ 1, 2, 4, -1 },{ 2, 3, 3, -1 },{ 3, -1, 4, 0 }, {-1, 0, 0, 0} },20046//------------------------ mode 4: 2 subsets, Weight bits: 46 bits, Endpoint bits: 72 bits (11.444, 11.444, 11.555), delta20047{ { 0, 0, 9, 0 },{ 1, 0, 9, 0 },{ 2, 0, 9, 0 },{ 0, 1, 3, 0 },{ 0, 0, 10, -1 },{ 2, 2, 4, -1 },{ 1, 2, 3, 0 },{ 1, 1, 3, 0 },20048{ 1, 0, 10, -1 },{ 2, 3, 0, -1 },{ 1, 3, 3, 0 },{ 2, 1, 4, 0 },{ 2, 0, 10, -1 },{ 2, 2, 3, 0 },{ 0, 2, 3, 0 },{ 2, 3, 1, -1 },20049{ 2, 3, 2, -1 },{ 0, 3, 3, 0 },{ 2, 3, 4, -1 },{ 2, 3, 3, -1 },{ 3, -1, 4, 0 }, {-1, 0, 0, 0} },20050//------------------------ mode 5: 2 subsets, Weight bits: 46 bits, Endpoint bits: 72 bits (9.555, 9.555, 9.555), delta20051{ { 0, 0, 8, 0 },{ 2, 2, 4, -1 },{ 1, 0, 8, 0 },{ 1, 2, 4, -1 },{ 2, 0, 8, 0 },{ 2, 3, 4, -1 },{ 0, 1, 4, 0 },{ 1, 3, 4, -1 },20052{ 1, 2, 3, 0 },{ 1, 1, 4, 0 },{ 2, 3, 0, -1 },{ 1, 3, 3, 0 },{ 2, 1, 4, 0 },{ 2, 3, 1, -1 },{ 2, 2, 3, 0 },{ 0, 2, 4, 0 },20053{ 2, 3, 2, -1 },{ 0, 3, 4, 0 },{ 2, 3, 3, -1 },{ 3, -1, 4, 0 }, {-1, 0, 0, 0} },20054//------------------------ mode 6: 2 subsets, Weight bits: 46 bits, Endpoint bits: 72 bits (8.666, 8.555, 8.555), delta20055{ { 0, 0, 7, 0 },{ 1, 3, 4, -1 },{ 2, 2, 4, -1 },{ 1, 0, 7, 0 },{ 2, 3, 2, -1 },{ 1, 2, 4, -1 },{ 2, 0, 7, 0 },{ 2, 3, 3, -1 },20056{ 2, 3, 4, -1 },{ 0, 1, 5, 0 },{ 1, 2, 3, 0 },{ 1, 1, 4, 0 },{ 2, 3, 0, -1 },{ 1, 3, 3, 0 },{ 2, 1, 4, 0 },{ 2, 3, 1, -1 },20057{ 2, 2, 3, 0 },{ 0, 2, 5, 0 },{ 0, 3, 5, 0 },{ 3, -1, 4, 0 }, {-1, 0, 0, 0} },20058//------------------------ mode 7: 2 subsets, Weight bits: 46 bits, Endpoints bits: 72 bits (8.555, 8.666, 8.555), delta20059{ { 0, 0, 7, 0 },{ 2, 3, 0, -1 },{ 2, 2, 4, -1 },{ 1, 0, 7, 0 },{ 1, 2, 5, -1 },{ 1, 2, 4, -1 },{ 2, 0, 7, 0 },{ 1, 3, 5, -1 },20060{ 2, 3, 4, -1 },{ 0, 1, 4, 0 },{ 1, 3, 4, -1 },{ 1, 2, 3, 0 },{ 1, 1, 5, 0 },{ 1, 3, 3, 0 },{ 2, 1, 4, 0 },{ 2, 3, 1, -1 },20061{ 2, 2, 3, 0 },{ 0, 2, 4, 0 },{ 2, 3, 2, -1 },{ 0, 3, 4, 0 },{ 2, 3, 3, -1 },{ 3, -1, 4, 0 }, {-1, 0, 0, 0} },20062//------------------------ mode 8: 2 subsets, Weight bits: 46 bits, Endpoint bits: 72 bits (8.555, 8.555, 8.666), delta20063{ { 0, 0, 7, 0 },{ 2, 3, 1, -1 },{ 2, 2, 4, -1 },{ 1, 0, 7, 0 },{ 2, 2, 5, -1 },{ 1, 2, 4, -1 },{ 2, 0, 7, 0 },{ 2, 3, 5, -1 },20064{ 2, 3, 4, -1 },{ 0, 1, 4, 0 },{ 1, 3, 4, -1 },{ 1, 2, 3, 0 },{ 1, 1, 4, 0 },{ 2, 3, 0, -1 },{ 1, 3, 3, 0 },{ 2, 1, 5, 0 },20065{ 2, 2, 3, 0 },{ 0, 2, 4, 0 },{ 2, 3, 2, -1 },{ 0, 3, 4, 0 },{ 2, 3, 3, -1 },{ 3, -1, 4, 0 }, {-1, 0, 0, 0} },20066//------------------------ mode 9: 2 subsets, Weight bits: 46 bits, Endpoint bits: 72 bits (6.6.6.6, 6.6.6.6, 6.6.6.6), NO delta20067{ { 0, 0, 5, 0 },{ 1, 3, 4, -1 },{ 2, 3, 0, -1 },{ 2, 3, 1, -1 },{ 2, 2, 4, -1 },{ 1, 0, 5, 0 },{ 1, 2, 5, -1 },{ 2, 2, 5, -1 },20068{ 2, 3, 2, -1 },{ 1, 2, 4, -1 },{ 2, 0, 5, 0 },{ 1, 3, 5, -1 },{ 2, 3, 3, -1 },{ 2, 3, 5, -1 },{ 2, 3, 4, -1 },{ 0, 1, 5, 0 },20069{ 1, 2, 3, 0 },{ 1, 1, 5, 0 },{ 1, 3, 3, 0 },{ 2, 1, 5, 0 },{ 2, 2, 3, 0 },{ 0, 2, 5, 0 },{ 0, 3, 5, 0 },{ 3, -1, 4, 0 }, {-1, 0, 0, 0} },20070//------------------------ mode 10: 1 subset, Weight bits: 63 bits, Endpoint bits: 60 bits (10.10, 10.10, 10.10), NO delta20071{ { 0, 0, 9, 0 },{ 1, 0, 9, 0 },{ 2, 0, 9, 0 },{ 0, 1, 9, 0 },{ 1, 1, 9, 0 },{ 2, 1, 9, 0 }, {-1, 0, 0, 0} },20072//------------------------ mode 11: 1 subset, Weight bits: 63 bits, Endpoint bits: 60 bits (11.9, 11.9, 11.9), delta20073{ { 0, 0, 9, 0 },{ 1, 0, 9, 0 },{ 2, 0, 9, 0 },{ 0, 1, 8, 0 },{ 0, 0, 10, -1 },{ 1, 1, 8, 0 },{ 1, 0, 10, -1 },{ 2, 1, 8, 0 },{ 2, 0, 10, -1 }, {-1, 0, 0, 0} },20074//------------------------ mode 12: 1 subset, Weight bits: 63 bits, Endpoint bits: 60 bits (12.8, 12.8, 12.8), delta20075{ { 0, 0, 9, 0 },{ 1, 0, 9, 0 },{ 2, 0, 9, 0 },{ 0, 1, 7, 0 },{ 0, 0, 10, 11 },{ 1, 1, 7, 0 },{ 1, 0, 10, 11 },{ 2, 1, 7, 0 },{ 2, 0, 10, 11 }, {-1, 0, 0, 0} },20076//------------------------ mode 13: 1 subset, Weight bits: 63 bits, Endpoint bits: 60 bits (16.4, 16.4, 16.4), delta20077{ { 0, 0, 9, 0 },{ 1, 0, 9, 0 },{ 2, 0, 9, 0 },{ 0, 1, 3, 0 },{ 0, 0, 10, 15 },{ 1, 1, 3, 0 },{ 1, 0, 10, 15 },{ 2, 1, 3, 0 },{ 2, 0, 10, 15 }, {-1, 0, 0, 0} }20078};2007920080// The same as the first 32 2-subset patterns in BC7.20081// Bit 7 is a flag indicating that the weight uses 1 less bit than usual.20082const uint8_t g_bc6h_2subset_patterns[TOTAL_BC6H_PARTITION_PATTERNS][4][4] = // [pat][y][x]20083{20084{ {0x80, 0, 1, 1}, { 0, 0, 1, 1 }, { 0, 0, 1, 1 }, { 0, 0, 1, 0x81 }}, { {0x80, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 0x81} },20085{ {0x80, 1, 1, 1}, {0, 1, 1, 1}, {0, 1, 1, 1}, {0, 1, 1, 0x81} }, { {0x80, 0, 0, 1}, {0, 0, 1, 1}, {0, 0, 1, 1}, {0, 1, 1, 0x81} },20086{ {0x80, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 1, 0x81} }, { {0x80, 0, 1, 1}, {0, 1, 1, 1}, {0, 1, 1, 1}, {1, 1, 1, 0x81} },20087{ {0x80, 0, 0, 1}, {0, 0, 1, 1}, {0, 1, 1, 1}, {1, 1, 1, 0x81} }, { {0x80, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 1}, {0, 1, 1, 0x81} },20088{ {0x80, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0x81} }, { {0x80, 0, 1, 1}, {0, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 0x81} },20089{ {0x80, 0, 0, 0}, {0, 0, 0, 1}, {0, 1, 1, 1}, {1, 1, 1, 0x81} }, { {0x80, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 1}, {0, 1, 1, 0x81} },20090{ {0x80, 0, 0, 1}, {0, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 0x81} }, { {0x80, 0, 0, 0}, {0, 0, 0, 0}, {1, 1, 1, 1}, {1, 1, 1, 0x81} },20091{ {0x80, 0, 0, 0}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 0x81} }, { {0x80, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {1, 1, 1, 0x81} },20092{ {0x80, 0, 0, 0}, {1, 0, 0, 0}, {1, 1, 1, 0}, {1, 1, 1, 0x81} }, { {0x80, 1, 0x81, 1}, {0, 0, 0, 1}, {0, 0, 0, 0}, {0, 0, 0, 0} },20093{ {0x80, 0, 0, 0}, {0, 0, 0, 0}, {0x81, 0, 0, 0}, {1, 1, 1, 0} }, { {0x80, 1, 0x81, 1}, {0, 0, 1, 1}, {0, 0, 0, 1}, {0, 0, 0, 0} },20094{ {0x80, 0, 0x81, 1}, {0, 0, 0, 1}, {0, 0, 0, 0}, {0, 0, 0, 0} }, { {0x80, 0, 0, 0}, {1, 0, 0, 0}, {0x81, 1, 0, 0}, {1, 1, 1, 0} },20095{ {0x80, 0, 0, 0}, {0, 0, 0, 0}, {0x81, 0, 0, 0}, {1, 1, 0, 0} }, { {0x80, 1, 1, 1}, {0, 0, 1, 1}, { 0, 0, 1, 1}, {0, 0, 0, 0x81} },20096{ {0x80, 0, 0x81, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 0} }, { {0x80, 0, 0, 0}, {1, 0, 0, 0}, {0x81, 0, 0, 0}, {1, 1, 0, 0} },20097{ {0x80, 1, 0x81, 0}, {0, 1, 1, 0}, {0, 1, 1, 0}, {0, 1, 1, 0} }, { {0x80, 0, 0x81, 1}, {0, 1, 1, 0}, {0, 1, 1, 0}, {1, 1, 0, 0} },20098{ {0x80, 0, 0, 1}, {0, 1, 1, 1}, {0x81, 1, 1, 0}, {1, 0, 0, 0} }, { {0x80, 0, 0, 0}, {1, 1, 1, 1}, {0x81, 1, 1, 1}, {0, 0, 0, 0} },20099{ {0x80, 1, 0x81, 1}, {0, 0, 0, 1}, {1, 0, 0, 0}, {1, 1, 1, 0} }, { {0x80, 0, 0x81, 1}, {1, 0, 0, 1}, {1, 0, 0, 1}, {1, 1, 0, 0} }20100};2010120102const uint8_t g_bc6h_weight3[8] = { 0, 9, 18, 27, 37, 46, 55, 64 };20103const uint8_t g_bc6h_weight4[16] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };2010420105static inline void write_bits(uint64_t val, uint32_t num_bits, uint32_t& bit_pos, uint64_t& l, uint64_t& h)20106{20107assert((num_bits) && (num_bits < 64) && (bit_pos < 128));20108assert(val < (1ULL << num_bits));2010920110if (bit_pos < 64)20111{20112l |= (val << bit_pos);2011320114if ((bit_pos + num_bits) > 64)20115h |= (val >> (64 - bit_pos));20116}20117else20118{20119h |= (val << (bit_pos - 64));20120}2012120122bit_pos += num_bits;20123assert(bit_pos <= 128);20124}2012520126static inline void write_rev_bits(uint64_t val, uint32_t num_bits, uint32_t& bit_pos, uint64_t& l, uint64_t& h)20127{20128assert((num_bits) && (num_bits < 64) && (bit_pos < 128));20129assert(val < (1ULL << num_bits));2013020131for (uint32_t i = 0; i < num_bits; i++)20132write_bits((val >> (num_bits - 1u - i)) & 1, 1, bit_pos, l, h);20133}2013420135void pack_bc6h_block(bc6h_block& dst_blk, bc6h_logical_block& log_blk)20136{20137const uint8_t s_mode_bits[NUM_BC6H_MODES] = { 0b00, 0b01, 0b00010, 0b00110, 0b01010, 0b01110, 0b10010, 0b10110, 0b11010, 0b11110, 0b00011, 0b00111, 0b01011, 0b01111 };2013820139const uint32_t mode = log_blk.m_mode;20140assert(mode < NUM_BC6H_MODES);2014120142uint64_t l = s_mode_bits[mode], h = 0;20143uint32_t bit_pos = (mode >= 2) ? 5 : 2;2014420145const uint32_t num_subsets = (mode >= BC6H_FIRST_1SUBSET_MODE_INDEX) ? 1 : 2;2014620147assert(((num_subsets == 2) && (log_blk.m_partition_pattern < TOTAL_BC6H_PARTITION_PATTERNS)) ||20148((num_subsets == 1) && (!log_blk.m_partition_pattern)));2014920150// Sanity checks20151for (uint32_t c = 0; c < 3; c++)20152{20153assert(log_blk.m_endpoints[c][0] < (1u << g_bc6h_mode_sig_bits[mode][0])); // 1st subset l, base bits20154assert(log_blk.m_endpoints[c][1] < (1u << g_bc6h_mode_sig_bits[mode][c + 1])); // 1st subset h, these are deltas except for modes 9,1020155assert(log_blk.m_endpoints[c][2] < (1u << g_bc6h_mode_sig_bits[mode][c + 1])); // 2nd subset l20156assert(log_blk.m_endpoints[c][3] < (1u << g_bc6h_mode_sig_bits[mode][c + 1])); // 2nd subset h20157}2015820159const bc6h_bit_layout* pLayout = &g_bc6h_bit_layouts[mode][0];2016020161while (pLayout->m_comp != -1)20162{20163uint32_t v = (pLayout->m_comp == 3) ? log_blk.m_partition_pattern : log_blk.m_endpoints[pLayout->m_comp][pLayout->m_index];2016420165if (pLayout->m_first_bit == -1)20166{20167write_bits((v >> pLayout->m_last_bit) & 1, 1, bit_pos, l, h);20168}20169else20170{20171const uint32_t total_bits = basisu::iabs(pLayout->m_last_bit - pLayout->m_first_bit) + 1;2017220173v >>= basisu::minimum(pLayout->m_first_bit, pLayout->m_last_bit);20174v &= ((1 << total_bits) - 1);2017520176if (pLayout->m_first_bit > pLayout->m_last_bit)20177write_rev_bits(v, total_bits, bit_pos, l, h);20178else20179write_bits(v, total_bits, bit_pos, l, h);20180}2018120182pLayout++;20183}2018420185const uint32_t num_mode_sel_bits = (num_subsets == 1) ? 4 : 3;20186const uint8_t* pPat = &g_bc6h_2subset_patterns[log_blk.m_partition_pattern][0][0];2018720188for (uint32_t i = 0; i < 16; i++)20189{20190const uint32_t sel = log_blk.m_weights[i];2019120192uint32_t num_bits = num_mode_sel_bits;20193if (num_subsets == 2)20194{20195const uint32_t subset_index = pPat[i];20196num_bits -= (subset_index >> 7);20197}20198else if (!i)20199{20200num_bits--;20201}2020220203assert(sel < (1u << num_bits));2020420205write_bits(sel, num_bits, bit_pos, l, h);20206}2020720208assert(bit_pos == 128);2020920210basisu::write_le_dword(&dst_blk.m_bytes[0], (uint32_t)l);20211basisu::write_le_dword(&dst_blk.m_bytes[4], (uint32_t)(l >> 32u));20212basisu::write_le_dword(&dst_blk.m_bytes[8], (uint32_t)h);20213basisu::write_le_dword(&dst_blk.m_bytes[12], (uint32_t)(h >> 32u));20214}2021520216#if 020217static inline uint32_t bc6h_blog_dequantize_to_blog16(uint32_t comp, uint32_t bits_per_comp)20218{20219int unq;2022020221if (bits_per_comp >= 15)20222unq = comp;20223else if (comp == 0)20224unq = 0;20225else if (comp == ((1u << bits_per_comp) - 1u))20226unq = 0xFFFFu;20227else20228unq = ((comp << 16u) + 0x8000u) >> bits_per_comp;2022920230return unq;20231}20232#endif2023320234// 6,7,8,9,10,11,1220235const uint32_t BC6H_BLOG_TAB_MIN = 6;20236const uint32_t BC6H_BLOG_TAB_MAX = 12;20237//const uint32_t BC6H_BLOG_TAB_NUM = BC6H_BLOG_TAB_MAX - BC6H_BLOG_TAB_MIN + 1;2023820239// Handles 16, or 6-12 bits. Others assert.20240static inline uint32_t half_to_blog_tab(half_float h, uint32_t num_bits)20241{20242assert(h <= MAX_BC6H_HALF_FLOAT_AS_UINT);20243assert((num_bits == 16) || ((num_bits >= BC6H_BLOG_TAB_MIN) && (num_bits <= BC6H_BLOG_TAB_MAX)));2024420245return bc6h_half_to_blog(h, num_bits);20246#if 020247BASISU_NOTE_UNUSED(BC6H_BLOG_TAB_MIN);20248BASISU_NOTE_UNUSED(BC6H_BLOG_TAB_MAX);2024920250if (num_bits == 16)20251{20252return bc6h_half_to_blog(h, 16);20253}20254else20255{20256assert((num_bits >= BC6H_BLOG_TAB_MIN) && (num_bits <= BC6H_BLOG_TAB_MAX));2025720258// Note: This used to be done using a table lookup, but it required ~224KB of tables. This isn't quite as accurate, but the error is very slight (+-1 half values as ints).20259return bc6h_half_to_blog(h, num_bits);20260}20261#endif20262}2026320264bool g_bc6h_enc_initialized;2026520266void bc6h_enc_init()20267{20268if (g_bc6h_enc_initialized)20269return;2027020271g_bc6h_enc_initialized = true;20272}2027320274// mode 10, 4-bit weights20275void bc6h_enc_block_mode10(bc6h_block* pPacked_block, const half_float pEndpoints[3][2], const uint8_t* pWeights)20276{20277assert(g_bc6h_enc_initialized);2027820279for (uint32_t i = 0; i < 16; i++)20280{20281assert(pWeights[i] <= 15);20282}2028320284bc6h_logical_block log_blk;20285log_blk.clear();2028620287// Convert half endpoints to blog10 (mode 10 doesn't use delta encoding)20288for (uint32_t c = 0; c < 3; c++)20289{20290log_blk.m_endpoints[c][0] = half_to_blog_tab(pEndpoints[c][0], 10);20291log_blk.m_endpoints[c][1] = half_to_blog_tab(pEndpoints[c][1], 10);20292}2029320294memcpy(log_blk.m_weights, pWeights, 16);2029520296if (log_blk.m_weights[0] & 8)20297{20298for (uint32_t i = 0; i < 16; i++)20299log_blk.m_weights[i] = 15 - log_blk.m_weights[i];2030020301for (uint32_t c = 0; c < 3; c++)20302{20303std::swap(log_blk.m_endpoints[c][0], log_blk.m_endpoints[c][1]);20304}20305}2030620307log_blk.m_mode = BC6H_FIRST_1SUBSET_MODE_INDEX;20308pack_bc6h_block(*pPacked_block, log_blk);20309}2031020311// Tries modes 11-13 (delta endpoint) encoding, falling back to mode 10 only when necessary, 4-bit weights20312void bc6h_enc_block_1subset_4bit_weights(bc6h_block* pPacked_block, const half_float pEndpoints[3][2], const uint8_t* pWeights)20313{20314assert(g_bc6h_enc_initialized);2031520316for (uint32_t i = 0; i < 16; i++)20317{20318assert(pWeights[i] <= 15);20319}2032020321bc6h_logical_block log_blk;20322log_blk.clear();2032320324for (uint32_t mode = BC6H_LAST_MODE_INDEX; mode > BC6H_FIRST_1SUBSET_MODE_INDEX; mode--)20325{20326const uint32_t num_base_bits = g_bc6h_mode_sig_bits[mode][0], num_delta_bits = g_bc6h_mode_sig_bits[mode][1];20327const int base_bitmask = (1 << num_base_bits) - 1;20328const int delta_bitmask = (1 << num_delta_bits) - 1;20329BASISU_NOTE_UNUSED(base_bitmask);2033020331assert(num_delta_bits < num_base_bits);20332assert((num_delta_bits == g_bc6h_mode_sig_bits[mode][2]) && (num_delta_bits == g_bc6h_mode_sig_bits[mode][3]));2033320334uint32_t blog_endpoints[3][2];2033520336// Convert half endpoints to blog 16, 12, or 1120337for (uint32_t c = 0; c < 3; c++)20338{20339blog_endpoints[c][0] = half_to_blog_tab(pEndpoints[c][0], num_base_bits);20340assert((int)blog_endpoints[c][0] <= base_bitmask);2034120342blog_endpoints[c][1] = half_to_blog_tab(pEndpoints[c][1], num_base_bits);20343assert((int)blog_endpoints[c][1] <= base_bitmask);20344}2034520346// Copy weights20347memcpy(log_blk.m_weights, pWeights, 16);2034820349// Ensure first weight MSB is 020350if (log_blk.m_weights[0] & 8)20351{20352// Invert weights20353for (uint32_t i = 0; i < 16; i++)20354log_blk.m_weights[i] = 15 - log_blk.m_weights[i];2035520356// Swap blog quantized endpoints20357for (uint32_t c = 0; c < 3; c++)20358{20359std::swap(blog_endpoints[c][0], blog_endpoints[c][1]);20360}20361}2036220363const int max_delta = (1 << (num_delta_bits - 1)) - 1;20364const int min_delta = -(max_delta + 1);20365assert((max_delta - min_delta) == delta_bitmask);2036620367bool failed_flag = false;20368for (uint32_t c = 0; c < 3; c++)20369{20370log_blk.m_endpoints[c][0] = blog_endpoints[c][0];2037120372int delta = (int)blog_endpoints[c][1] - (int)blog_endpoints[c][0];20373if ((delta < min_delta) || (delta > max_delta))20374{20375failed_flag = true;20376break;20377}2037820379log_blk.m_endpoints[c][1] = delta & delta_bitmask;20380}2038120382if (failed_flag)20383continue;2038420385log_blk.m_mode = mode;20386pack_bc6h_block(*pPacked_block, log_blk);2038720388return;20389}2039020391// Worst case fall back to mode 10, which can handle any endpoints20392bc6h_enc_block_mode10(pPacked_block, pEndpoints, pWeights);20393}2039420395// Mode 9 (direct endpoint encoding), 3-bit weights, but only 1 subset20396void bc6h_enc_block_1subset_mode9_3bit_weights(bc6h_block* pPacked_block, const half_float pEndpoints[3][2], const uint8_t* pWeights)20397{20398assert(g_bc6h_enc_initialized);2039920400for (uint32_t i = 0; i < 16; i++)20401{20402assert(pWeights[i] <= 7);20403}2040420405bc6h_logical_block log_blk;20406log_blk.clear();2040720408// Convert half endpoints to blog6 (mode 9 doesn't use delta encoding)20409for (uint32_t c = 0; c < 3; c++)20410{20411log_blk.m_endpoints[c][0] = half_to_blog_tab(pEndpoints[c][0], 6);20412log_blk.m_endpoints[c][2] = log_blk.m_endpoints[c][0];2041320414log_blk.m_endpoints[c][1] = half_to_blog_tab(pEndpoints[c][1], 6);20415log_blk.m_endpoints[c][3] = log_blk.m_endpoints[c][1];20416}2041720418memcpy(log_blk.m_weights, pWeights, 16);2041920420const uint32_t pat_index = 0;20421const uint8_t* pPat = &g_bc6h_2subset_patterns[pat_index][0][0];2042220423if (log_blk.m_weights[0] & 4)20424{20425for (uint32_t c = 0; c < 3; c++)20426std::swap(log_blk.m_endpoints[c][0], log_blk.m_endpoints[c][1]);2042720428for (uint32_t i = 0; i < 16; i++)20429if ((pPat[i] & 0x7F) == 0)20430log_blk.m_weights[i] = 7 - log_blk.m_weights[i];20431}2043220433if (log_blk.m_weights[15] & 4)20434{20435for (uint32_t c = 0; c < 3; c++)20436std::swap(log_blk.m_endpoints[c][2], log_blk.m_endpoints[c][3]);2043720438for (uint32_t i = 0; i < 16; i++)20439if ((pPat[i] & 0x7F) == 1)20440log_blk.m_weights[i] = 7 - log_blk.m_weights[i];20441}2044220443log_blk.m_mode = 9;20444log_blk.m_partition_pattern = pat_index;20445pack_bc6h_block(*pPacked_block, log_blk);20446}2044720448// Tries modes 0-8, falls back to mode 920449void bc6h_enc_block_1subset_3bit_weights(bc6h_block* pPacked_block, const half_float pEndpoints[3][2], const uint8_t* pWeights)20450{20451assert(g_bc6h_enc_initialized);2045220453for (uint32_t i = 0; i < 16; i++)20454{20455assert(pWeights[i] <= 7);20456}2045720458bc6h_logical_block log_blk;20459log_blk.clear();2046020461for (uint32_t mode_iter = 0; mode_iter <= 8; mode_iter++)20462{20463static const int s_mode_order[9] = { 2, 3, 4, 0, 5, 6, 7, 8, 1 }; // ordered from largest base bits to least20464const uint32_t mode = s_mode_order[mode_iter];2046520466const uint32_t num_base_bits = g_bc6h_mode_sig_bits[mode][0];20467const int base_bitmask = (1 << num_base_bits) - 1;20468BASISU_NOTE_UNUSED(base_bitmask);2046920470const uint32_t num_delta_bits[3] = { g_bc6h_mode_sig_bits[mode][1], g_bc6h_mode_sig_bits[mode][2], g_bc6h_mode_sig_bits[mode][3] };20471const int delta_bitmasks[3] = { (1 << num_delta_bits[0]) - 1, (1 << num_delta_bits[1]) - 1, (1 << num_delta_bits[2]) - 1 };2047220473uint32_t blog_endpoints[3][4];2047420475// Convert half endpoints to blog 7-1120476for (uint32_t c = 0; c < 3; c++)20477{20478blog_endpoints[c][0] = half_to_blog_tab(pEndpoints[c][0], num_base_bits);20479blog_endpoints[c][2] = blog_endpoints[c][0];20480assert((int)blog_endpoints[c][0] <= base_bitmask);2048120482blog_endpoints[c][1] = half_to_blog_tab(pEndpoints[c][1], num_base_bits);20483blog_endpoints[c][3] = blog_endpoints[c][1];20484assert((int)blog_endpoints[c][1] <= base_bitmask);20485}2048620487const uint32_t pat_index = 0;20488const uint8_t* pPat = &g_bc6h_2subset_patterns[pat_index][0][0];2048920490memcpy(log_blk.m_weights, pWeights, 16);2049120492if (log_blk.m_weights[0] & 4)20493{20494// Swap part 0's endpoints/weights20495for (uint32_t c = 0; c < 3; c++)20496std::swap(blog_endpoints[c][0], blog_endpoints[c][1]);2049720498for (uint32_t i = 0; i < 16; i++)20499if ((pPat[i] & 0x7F) == 0)20500log_blk.m_weights[i] = 7 - log_blk.m_weights[i];20501}2050220503if (log_blk.m_weights[15] & 4)20504{20505// Swap part 1's endpoints/weights20506for (uint32_t c = 0; c < 3; c++)20507std::swap(blog_endpoints[c][2], blog_endpoints[c][3]);2050820509for (uint32_t i = 0; i < 16; i++)20510if ((pPat[i] & 0x7F) == 1)20511log_blk.m_weights[i] = 7 - log_blk.m_weights[i];20512}2051320514bool failed_flag = false;2051520516for (uint32_t c = 0; c < 3; c++)20517{20518const int max_delta = (1 << (num_delta_bits[c] - 1)) - 1;2051920520const int min_delta = -(max_delta + 1);20521assert((max_delta - min_delta) == delta_bitmasks[c]);2052220523log_blk.m_endpoints[c][0] = blog_endpoints[c][0];2052420525int delta0 = (int)blog_endpoints[c][1] - (int)blog_endpoints[c][0];20526int delta1 = (int)blog_endpoints[c][2] - (int)blog_endpoints[c][0];20527int delta2 = (int)blog_endpoints[c][3] - (int)blog_endpoints[c][0];2052820529if ((delta0 < min_delta) || (delta0 > max_delta) ||20530(delta1 < min_delta) || (delta1 > max_delta) ||20531(delta2 < min_delta) || (delta2 > max_delta))20532{20533failed_flag = true;20534break;20535}2053620537log_blk.m_endpoints[c][1] = delta0 & delta_bitmasks[c];20538log_blk.m_endpoints[c][2] = delta1 & delta_bitmasks[c];20539log_blk.m_endpoints[c][3] = delta2 & delta_bitmasks[c];20540}2054120542if (failed_flag)20543continue;2054420545log_blk.m_mode = mode;20546log_blk.m_partition_pattern = pat_index;20547pack_bc6h_block(*pPacked_block, log_blk);2054820549return;2055020551} // mode_iter2055220553bc6h_enc_block_1subset_mode9_3bit_weights(pPacked_block, pEndpoints, pWeights);20554}2055520556// pEndpoints[subset][comp][lh_index]20557void bc6h_enc_block_2subset_mode9_3bit_weights(bc6h_block* pPacked_block, uint32_t common_part_index, const half_float pEndpoints[2][3][2], const uint8_t* pWeights)20558{20559assert(g_bc6h_enc_initialized);20560assert(common_part_index < basist::TOTAL_ASTC_BC7_COMMON_PARTITIONS2);2056120562for (uint32_t i = 0; i < 16; i++)20563{20564assert(pWeights[i] <= 7);20565}2056620567bc6h_logical_block log_blk;20568log_blk.clear();2056920570// Convert half endpoints to blog6 (mode 9 doesn't use delta encoding)20571for (uint32_t s = 0; s < 2; s++)20572{20573for (uint32_t c = 0; c < 3; c++)20574{20575log_blk.m_endpoints[c][0 + s * 2] = half_to_blog_tab(pEndpoints[s][c][0], 6);20576log_blk.m_endpoints[c][1 + s * 2] = half_to_blog_tab(pEndpoints[s][c][1], 6);20577}20578}2057920580memcpy(log_blk.m_weights, pWeights, 16);2058120582//const uint32_t astc_pattern = basist::g_astc_bc7_common_partitions2[common_part_index].m_astc;20583const uint32_t bc7_pattern = basist::g_astc_bc7_common_partitions2[common_part_index].m_bc7;2058420585const bool invert_flag = basist::g_astc_bc7_common_partitions2[common_part_index].m_invert;20586if (invert_flag)20587{20588for (uint32_t c = 0; c < 3; c++)20589{20590std::swap(log_blk.m_endpoints[c][0], log_blk.m_endpoints[c][2]);20591std::swap(log_blk.m_endpoints[c][1], log_blk.m_endpoints[c][3]);20592}20593}2059420595const uint32_t pat_index = bc7_pattern;20596assert(pat_index < 32);20597const uint8_t* pPat = &g_bc6h_2subset_patterns[pat_index][0][0];2059820599bool swap_flags[2] = { false, false };20600for (uint32_t i = 0; i < 16; i++)20601{20602if ((pPat[i] & 0x80) == 0)20603continue;2060420605if (log_blk.m_weights[i] & 4)20606{20607const uint32_t p = pPat[i] & 1;20608swap_flags[p] = true;20609}20610}2061120612if (swap_flags[0])20613{20614for (uint32_t c = 0; c < 3; c++)20615std::swap(log_blk.m_endpoints[c][0], log_blk.m_endpoints[c][1]);2061620617for (uint32_t i = 0; i < 16; i++)20618if ((pPat[i] & 0x7F) == 0)20619log_blk.m_weights[i] = 7 - log_blk.m_weights[i];20620}2062120622if (swap_flags[1])20623{20624for (uint32_t c = 0; c < 3; c++)20625std::swap(log_blk.m_endpoints[c][2], log_blk.m_endpoints[c][3]);2062620627for (uint32_t i = 0; i < 16; i++)20628if ((pPat[i] & 0x7F) == 1)20629log_blk.m_weights[i] = 7 - log_blk.m_weights[i];20630}2063120632log_blk.m_mode = 9;20633log_blk.m_partition_pattern = pat_index;20634pack_bc6h_block(*pPacked_block, log_blk);20635}2063620637void bc6h_enc_block_2subset_3bit_weights(bc6h_block* pPacked_block, uint32_t common_part_index, const half_float pEndpoints[2][3][2], const uint8_t* pWeights)20638{20639assert(g_bc6h_enc_initialized);2064020641for (uint32_t i = 0; i < 16; i++)20642{20643assert(pWeights[i] <= 7);20644}2064520646bc6h_logical_block log_blk;20647log_blk.clear();2064820649for (uint32_t mode_iter = 0; mode_iter <= 8; mode_iter++)20650{20651static const int s_mode_order[9] = { 2, 3, 4, 0, 5, 6, 7, 8, 1 }; // ordered from largest base bits to least20652const uint32_t mode = s_mode_order[mode_iter];2065320654const uint32_t num_base_bits = g_bc6h_mode_sig_bits[mode][0];20655const int base_bitmask = (1 << num_base_bits) - 1;20656BASISU_NOTE_UNUSED(base_bitmask);2065720658const uint32_t num_delta_bits[3] = { g_bc6h_mode_sig_bits[mode][1], g_bc6h_mode_sig_bits[mode][2], g_bc6h_mode_sig_bits[mode][3] };20659const int delta_bitmasks[3] = { (1 << num_delta_bits[0]) - 1, (1 << num_delta_bits[1]) - 1, (1 << num_delta_bits[2]) - 1 };2066020661uint32_t blog_endpoints[3][4];2066220663// Convert half endpoints to blog 7-1120664for (uint32_t s = 0; s < 2; s++)20665{20666for (uint32_t c = 0; c < 3; c++)20667{20668blog_endpoints[c][0 + s * 2] = half_to_blog_tab(pEndpoints[s][c][0], num_base_bits);20669blog_endpoints[c][1 + s * 2] = half_to_blog_tab(pEndpoints[s][c][1], num_base_bits);20670}20671}2067220673memcpy(log_blk.m_weights, pWeights, 16);2067420675//const uint32_t astc_pattern = basist::g_astc_bc7_common_partitions2[common_part_index].m_astc;20676const uint32_t bc7_pattern = basist::g_astc_bc7_common_partitions2[common_part_index].m_bc7;2067720678const bool invert_flag = basist::g_astc_bc7_common_partitions2[common_part_index].m_invert;20679if (invert_flag)20680{20681for (uint32_t c = 0; c < 3; c++)20682{20683std::swap(blog_endpoints[c][0], blog_endpoints[c][2]);20684std::swap(blog_endpoints[c][1], blog_endpoints[c][3]);20685}20686}2068720688const uint32_t pat_index = bc7_pattern;20689assert(pat_index < 32);20690const uint8_t* pPat = &g_bc6h_2subset_patterns[pat_index][0][0];2069120692bool swap_flags[2] = { false, false };20693for (uint32_t i = 0; i < 16; i++)20694{20695if ((pPat[i] & 0x80) == 0)20696continue;2069720698if (log_blk.m_weights[i] & 4)20699{20700const uint32_t p = pPat[i] & 1;20701swap_flags[p] = true;20702}20703}2070420705if (swap_flags[0])20706{20707for (uint32_t c = 0; c < 3; c++)20708std::swap(blog_endpoints[c][0], blog_endpoints[c][1]);2070920710for (uint32_t i = 0; i < 16; i++)20711if ((pPat[i] & 0x7F) == 0)20712log_blk.m_weights[i] = 7 - log_blk.m_weights[i];20713}2071420715if (swap_flags[1])20716{20717for (uint32_t c = 0; c < 3; c++)20718std::swap(blog_endpoints[c][2], blog_endpoints[c][3]);2071920720for (uint32_t i = 0; i < 16; i++)20721if ((pPat[i] & 0x7F) == 1)20722log_blk.m_weights[i] = 7 - log_blk.m_weights[i];20723}2072420725// Try packing the endpoints20726bool failed_flag = false;2072720728for (uint32_t c = 0; c < 3; c++)20729{20730const int max_delta = (1 << (num_delta_bits[c] - 1)) - 1;2073120732const int min_delta = -(max_delta + 1);20733assert((max_delta - min_delta) == delta_bitmasks[c]);2073420735log_blk.m_endpoints[c][0] = blog_endpoints[c][0];2073620737int delta0 = (int)blog_endpoints[c][1] - (int)blog_endpoints[c][0];20738int delta1 = (int)blog_endpoints[c][2] - (int)blog_endpoints[c][0];20739int delta2 = (int)blog_endpoints[c][3] - (int)blog_endpoints[c][0];2074020741if ((delta0 < min_delta) || (delta0 > max_delta) ||20742(delta1 < min_delta) || (delta1 > max_delta) ||20743(delta2 < min_delta) || (delta2 > max_delta))20744{20745failed_flag = true;20746break;20747}2074820749log_blk.m_endpoints[c][1] = delta0 & delta_bitmasks[c];20750log_blk.m_endpoints[c][2] = delta1 & delta_bitmasks[c];20751log_blk.m_endpoints[c][3] = delta2 & delta_bitmasks[c];20752}2075320754if (failed_flag)20755continue;2075620757log_blk.m_mode = mode;20758log_blk.m_partition_pattern = pat_index;20759pack_bc6h_block(*pPacked_block, log_blk);2076020761//half_float blk[16 * 3];20762//unpack_bc6h(pPacked_block, blk, false);2076320764return;20765}2076620767bc6h_enc_block_2subset_mode9_3bit_weights(pPacked_block, common_part_index, pEndpoints, pWeights);20768}2076920770bool bc6h_enc_block_solid_color(bc6h_block* pPacked_block, const half_float pColor[3])20771{20772assert(g_bc6h_enc_initialized);2077320774if ((pColor[0] | pColor[1] | pColor[2]) & 0x8000)20775return false;2077620777// ASTC block unpacker won't allow Inf/NaN's to come through.20778//if (is_half_inf_or_nan(pColor[0]) || is_half_inf_or_nan(pColor[1]) || is_half_inf_or_nan(pColor[2]))20779// return false;2078020781uint8_t weights[16];20782memset(weights, 0, sizeof(weights));2078320784half_float endpoints[3][2];20785endpoints[0][0] = pColor[0];20786endpoints[0][1] = pColor[0];2078720788endpoints[1][0] = pColor[1];20789endpoints[1][1] = pColor[1];2079020791endpoints[2][0] = pColor[2];20792endpoints[2][1] = pColor[2];2079320794bc6h_enc_block_1subset_4bit_weights(pPacked_block, endpoints, weights);2079520796return true;20797}2079820799//--------------------------------------------------------------------------------------------------------------------------20800// basisu_astc_hdr_core.cpp2080120802static bool g_astc_hdr_core_initialized;20803static int8_t g_astc_partition_id_to_common_bc7_pat_index[1024];2080420805//--------------------------------------------------------------------------------------------------------------------------2080620807void astc_hdr_core_init()20808{20809if (g_astc_hdr_core_initialized)20810return;2081120812memset(g_astc_partition_id_to_common_bc7_pat_index, 0xFF, sizeof(g_astc_partition_id_to_common_bc7_pat_index));2081320814for (uint32_t part_index = 0; part_index < basist::TOTAL_ASTC_BC6H_COMMON_PARTITIONS2; ++part_index)20815{20816const uint32_t astc_pattern = basist::g_astc_bc7_common_partitions2[part_index].m_astc;20817//const uint32_t bc7_pattern = basist::g_astc_bc7_common_partitions2[part_index].m_bc7;2081820819assert(astc_pattern < 1024);20820g_astc_partition_id_to_common_bc7_pat_index[astc_pattern] = (int8_t)part_index;20821}2082220823g_astc_hdr_core_initialized = true;20824}2082520826//--------------------------------------------------------------------------------------------------------------------------2082720828static inline int astc_hdr_sign_extend(int src, int num_src_bits)20829{20830assert(basisu::in_range(num_src_bits, 2, 31));2083120832const bool negative = (src & (1 << (num_src_bits - 1))) != 0;20833if (negative)20834return src | ~((1 << num_src_bits) - 1);20835else20836return src & ((1 << num_src_bits) - 1);20837}2083820839static inline void astc_hdr_pack_bit(20840int& dst, int dst_bit,20841int src_val, int src_bit = 0)20842{20843assert(dst_bit >= 0 && dst_bit <= 31);20844int bit = basisu::get_bit(src_val, src_bit);20845dst |= (bit << dst_bit);20846}2084720848//--------------------------------------------------------------------------------------------------------------------------2084920850void decode_mode7_to_qlog12_ise20(20851const uint8_t* pEndpoints,20852int e[2][3],20853int* pScale)20854{20855assert(g_astc_hdr_core_initialized);2085620857for (uint32_t i = 0; i < NUM_MODE7_ENDPOINTS; i++)20858{20859assert(pEndpoints[i] <= 255);20860}2086120862const int v0 = pEndpoints[0], v1 = pEndpoints[1], v2 = pEndpoints[2], v3 = pEndpoints[3];2086320864// Extract mode bits and unpack to major component and mode.20865const int modeval = ((v0 & 0xC0) >> 6) | ((v1 & 0x80) >> 5) | ((v2 & 0x80) >> 4);2086620867int majcomp, mode;20868if ((modeval & 0xC) != 0xC)20869{20870majcomp = modeval >> 2;20871mode = modeval & 3;20872}20873else if (modeval != 0xF)20874{20875majcomp = modeval & 3;20876mode = 4;20877}20878else20879{20880majcomp = 0;20881mode = 5;20882}2088320884// Extract low-order bits of r, g, b, and s.20885int red = v0 & 0x3f;20886int green = v1 & 0x1f;20887int blue = v2 & 0x1f;20888int scale = v3 & 0x1f;2088920890// Extract high-order bits, which may be assigned depending on mode20891int x0 = (v1 >> 6) & 1;20892int x1 = (v1 >> 5) & 1;20893int x2 = (v2 >> 6) & 1;20894int x3 = (v2 >> 5) & 1;20895int x4 = (v3 >> 7) & 1;20896int x5 = (v3 >> 6) & 1;20897int x6 = (v3 >> 5) & 1;2089820899// Now move the high-order xs into the right place.20900const int ohm = 1 << mode;20901if (ohm & 0x30) green |= x0 << 6;20902if (ohm & 0x3A) green |= x1 << 5;20903if (ohm & 0x30) blue |= x2 << 6;20904if (ohm & 0x3A) blue |= x3 << 5;20905if (ohm & 0x3D) scale |= x6 << 5;20906if (ohm & 0x2D) scale |= x5 << 6;20907if (ohm & 0x04) scale |= x4 << 7;20908if (ohm & 0x3B) red |= x4 << 6;20909if (ohm & 0x04) red |= x3 << 6;20910if (ohm & 0x10) red |= x5 << 7;20911if (ohm & 0x0F) red |= x2 << 7;20912if (ohm & 0x05) red |= x1 << 8;20913if (ohm & 0x0A) red |= x0 << 8;20914if (ohm & 0x05) red |= x0 << 9;20915if (ohm & 0x02) red |= x6 << 9;20916if (ohm & 0x01) red |= x3 << 10;20917if (ohm & 0x02) red |= x5 << 10;2091820919// Shift the bits to the top of the 12-bit result.20920static const int s_shamts[6] = { 1,1,2,3,4,5 };2092120922const int shamt = s_shamts[mode];20923red <<= shamt;20924green <<= shamt;20925blue <<= shamt;20926scale <<= shamt;2092720928// Minor components are stored as differences20929if (mode != 5)20930{20931green = red - green;20932blue = red - blue;20933}2093420935// Swizzle major component into place20936if (majcomp == 1)20937std::swap(red, green);2093820939if (majcomp == 2)20940std::swap(red, blue);2094120942// Clamp output values, set alpha to 1.020943e[1][0] = basisu::clamp(red, 0, 0xFFF);20944e[1][1] = basisu::clamp(green, 0, 0xFFF);20945e[1][2] = basisu::clamp(blue, 0, 0xFFF);2094620947e[0][0] = basisu::clamp(red - scale, 0, 0xFFF);20948e[0][1] = basisu::clamp(green - scale, 0, 0xFFF);20949e[0][2] = basisu::clamp(blue - scale, 0, 0xFFF);2095020951if (pScale)20952*pScale = scale;20953}2095420955//--------------------------------------------------------------------------------------------------------------------------2095620957bool decode_mode7_to_qlog12(20958const uint8_t* pEndpoints,20959int e[2][3],20960int* pScale,20961uint32_t ise_endpoint_range)20962{20963assert(g_astc_hdr_core_initialized);2096420965if (ise_endpoint_range == astc_helpers::BISE_256_LEVELS)20966{20967decode_mode7_to_qlog12_ise20(pEndpoints, e, pScale);20968}20969else20970{20971uint8_t dequantized_endpoints[NUM_MODE7_ENDPOINTS];2097220973for (uint32_t i = 0; i < NUM_MODE7_ENDPOINTS; i++)20974dequantized_endpoints[i] = astc_helpers::g_dequant_tables.get_endpoint_tab(ise_endpoint_range).m_ISE_to_val[pEndpoints[i]];2097520976decode_mode7_to_qlog12_ise20(dequantized_endpoints, e, pScale);20977}2097820979for (uint32_t i = 0; i < 2; i++)20980{20981if (e[i][0] > (int)MAX_QLOG12)20982return false;2098320984if (e[i][1] > (int)MAX_QLOG12)20985return false;2098620987if (e[i][2] > (int)MAX_QLOG12)20988return false;20989}2099020991return true;20992}2099320994//--------------------------------------------------------------------------------------------------------------------------2099520996void decode_mode11_to_qlog12_ise20(20997const uint8_t* pEndpoints,20998int e[2][3])20999{21000#ifdef _DEBUG21001for (uint32_t i = 0; i < NUM_MODE11_ENDPOINTS; i++)21002{21003assert(pEndpoints[i] <= 255);21004}21005#endif2100621007const uint32_t maj_comp = basisu::get_bit(pEndpoints[4], 7) | (basisu::get_bit(pEndpoints[5], 7) << 1);2100821009if (maj_comp == 3)21010{21011// Direct, qlog8 and qlog721012e[0][0] = pEndpoints[0] << 4;21013e[1][0] = pEndpoints[1] << 4;2101421015e[0][1] = pEndpoints[2] << 4;21016e[1][1] = pEndpoints[3] << 4;2101721018e[0][2] = (pEndpoints[4] & 127) << 5;21019e[1][2] = (pEndpoints[5] & 127) << 5;21020}21021else21022{21023int v0 = pEndpoints[0];21024int v1 = pEndpoints[1];21025int v2 = pEndpoints[2];21026int v3 = pEndpoints[3];21027int v4 = pEndpoints[4];21028int v5 = pEndpoints[5];2102921030int mode = 0;21031astc_hdr_pack_bit(mode, 0, v1, 7);21032astc_hdr_pack_bit(mode, 1, v2, 7);21033astc_hdr_pack_bit(mode, 2, v3, 7);2103421035int va = v0;21036astc_hdr_pack_bit(va, 8, v1, 6);2103721038int vb0 = v2 & 63;21039int vb1 = v3 & 63;21040int vc = v1 & 63;2104121042int vd0 = v4 & 0x7F; // this takes more bits than is sometimes needed21043int vd1 = v5 & 0x7F; // this takes more bits than is sometimes needed21044static const int8_t dbitstab[8] = { 7,6,7,6,5,6,5,6 };21045vd0 = astc_hdr_sign_extend(vd0, dbitstab[mode]);21046vd1 = astc_hdr_sign_extend(vd1, dbitstab[mode]);2104721048int x0 = basisu::get_bit(v2, 6);21049int x1 = basisu::get_bit(v3, 6);21050int x2 = basisu::get_bit(v4, 6);21051int x3 = basisu::get_bit(v5, 6);21052int x4 = basisu::get_bit(v4, 5);21053int x5 = basisu::get_bit(v5, 5);2105421055const uint32_t ohm = 1U << mode;21056if (ohm & 0xA4) va |= (x0 << 9);21057if (ohm & 0x08) va |= (x2 << 9);21058if (ohm & 0x50) va |= (x4 << 9);21059if (ohm & 0x50) va |= (x5 << 10);21060if (ohm & 0xA0) va |= (x1 << 10);21061if (ohm & 0xC0) va |= (x2 << 11);21062if (ohm & 0x04) vc |= (x1 << 6);21063if (ohm & 0xE8) vc |= (x3 << 6);21064if (ohm & 0x20) vc |= (x2 << 7);21065if (ohm & 0x5B) vb0 |= (x0 << 6);21066if (ohm & 0x5B) vb1 |= (x1 << 6);21067if (ohm & 0x12) vb0 |= (x2 << 7);21068if (ohm & 0x12) vb1 |= (x3 << 7);2106921070const int shamt = (mode >> 1) ^ 3;2107121072va = (uint32_t)va << shamt;21073vb0 = (uint32_t)vb0 << shamt;21074vb1 = (uint32_t)vb1 << shamt;21075vc = (uint32_t)vc << shamt;21076vd0 = (uint32_t)vd0 << shamt;21077vd1 = (uint32_t)vd1 << shamt;2107821079// qlog1221080e[1][0] = basisu::clamp<int>(va, 0, 0xFFF);21081e[1][1] = basisu::clamp<int>(va - vb0, 0, 0xFFF);21082e[1][2] = basisu::clamp<int>(va - vb1, 0, 0xFFF);2108321084e[0][0] = basisu::clamp<int>(va - vc, 0, 0xFFF);21085e[0][1] = basisu::clamp<int>(va - vb0 - vc - vd0, 0, 0xFFF);21086e[0][2] = basisu::clamp<int>(va - vb1 - vc - vd1, 0, 0xFFF);2108721088if (maj_comp)21089{21090std::swap(e[0][0], e[0][maj_comp]);21091std::swap(e[1][0], e[1][maj_comp]);21092}21093}21094}2109521096//--------------------------------------------------------------------------------------------------------------------------2109721098bool decode_mode11_to_qlog12(21099const uint8_t* pEndpoints,21100int e[2][3],21101uint32_t ise_endpoint_range)21102{21103assert(g_astc_hdr_core_initialized);21104assert((ise_endpoint_range >= astc_helpers::FIRST_VALID_ENDPOINT_ISE_RANGE) && (ise_endpoint_range <= astc_helpers::LAST_VALID_ENDPOINT_ISE_RANGE));2110521106if (ise_endpoint_range == astc_helpers::BISE_256_LEVELS)21107{21108decode_mode11_to_qlog12_ise20(pEndpoints, e);21109}21110else21111{21112uint8_t dequantized_endpoints[NUM_MODE11_ENDPOINTS];2111321114for (uint32_t i = 0; i < NUM_MODE11_ENDPOINTS; i++)21115dequantized_endpoints[i] = astc_helpers::g_dequant_tables.get_endpoint_tab(ise_endpoint_range).m_ISE_to_val[pEndpoints[i]];2111621117decode_mode11_to_qlog12_ise20(dequantized_endpoints, e);21118}2111921120for (uint32_t i = 0; i < 2; i++)21121{21122if (e[i][0] > (int)MAX_QLOG12)21123return false;2112421125if (e[i][1] > (int)MAX_QLOG12)21126return false;2112721128if (e[i][2] > (int)MAX_QLOG12)21129return false;21130}2113121132return true;21133}2113421135//--------------------------------------------------------------------------------------------------------------------------2113621137bool transcode_bc6h_1subset(half_float h_e[3][2], const astc_helpers::log_astc_block& best_blk, bc6h_block& transcoded_bc6h_blk)21138{21139assert(g_astc_hdr_core_initialized);21140assert((best_blk.m_weight_ise_range >= 1) && (best_blk.m_weight_ise_range <= 8));2114121142if (best_blk.m_weight_ise_range == 5)21143{21144// Use 3-bit BC6H weights which are a perfect match for 3-bit ASTC weights, but encode 1-subset as 2 equal subsets21145bc6h_enc_block_1subset_3bit_weights(&transcoded_bc6h_blk, h_e, best_blk.m_weights);21146}21147else21148{21149uint8_t bc6h_weights[16];2115021151if (best_blk.m_weight_ise_range == 1)21152{21153// weight ISE 1: 3 levels21154static const uint8_t s_astc1_to_bc6h_3[3] = { 0, 8, 15 };2115521156for (uint32_t i = 0; i < 16; i++)21157bc6h_weights[i] = s_astc1_to_bc6h_3[best_blk.m_weights[i]];21158}21159else if (best_blk.m_weight_ise_range == 2)21160{21161// weight ISE 2: 4 levels21162static const uint8_t s_astc2_to_bc6h_4[4] = { 0, 5, 10, 15 };2116321164for (uint32_t i = 0; i < 16; i++)21165bc6h_weights[i] = s_astc2_to_bc6h_4[best_blk.m_weights[i]];21166}21167else if (best_blk.m_weight_ise_range == 3)21168{21169// weight ISE 3: 5 levels21170static const uint8_t s_astc3_to_bc6h_4[5] = { 0, 4, 7, 11, 15 };2117121172for (uint32_t i = 0; i < 16; i++)21173bc6h_weights[i] = s_astc3_to_bc6h_4[best_blk.m_weights[i]];21174}21175else if (best_blk.m_weight_ise_range == 4)21176{21177// weight ISE 4: 6 levels21178static const uint8_t s_astc4_to_bc6h_4[6] = { 0, 15, 3, 12, 6, 9 };2117921180for (uint32_t i = 0; i < 16; i++)21181bc6h_weights[i] = s_astc4_to_bc6h_4[best_blk.m_weights[i]];21182}21183else if (best_blk.m_weight_ise_range == 6)21184{21185// weight ISE 6: 10 levels21186static const uint8_t s_astc6_to_bc6h_4[10] = { 0, 15, 2, 13, 3, 12, 5, 10, 6, 9 };2118721188for (uint32_t i = 0; i < 16; i++)21189bc6h_weights[i] = s_astc6_to_bc6h_4[best_blk.m_weights[i]];21190}21191else if (best_blk.m_weight_ise_range == 7)21192{21193// weight ISE 7: 12 levels21194static const uint8_t s_astc7_to_bc6h_4[12] = { 0, 15, 4, 11, 1, 14, 5, 10, 2, 13, 6, 9 };2119521196for (uint32_t i = 0; i < 16; i++)21197bc6h_weights[i] = s_astc7_to_bc6h_4[best_blk.m_weights[i]];21198}21199else if (best_blk.m_weight_ise_range == 8)21200{21201// 16 levels21202memcpy(bc6h_weights, best_blk.m_weights, 16);21203}21204else21205{21206assert(0);21207return false;21208}2120921210bc6h_enc_block_1subset_4bit_weights(&transcoded_bc6h_blk, h_e, bc6h_weights);21211}2121221213return true;21214}2121521216//--------------------------------------------------------------------------------------------------------------------------2121721218bool transcode_bc6h_2subsets(uint32_t common_part_index, const astc_helpers::log_astc_block& best_blk, bc6h_block& transcoded_bc6h_blk)21219{21220assert(g_astc_hdr_core_initialized);21221assert(best_blk.m_num_partitions == 2);21222assert(common_part_index < basist::TOTAL_ASTC_BC6H_COMMON_PARTITIONS2);2122321224half_float bc6h_endpoints[2][3][2]; // [subset][comp][lh_index]2122521226// UASTC HDR checks21227// Both CEM's must be equal in 2-subset UASTC HDR.21228if (best_blk.m_color_endpoint_modes[0] != best_blk.m_color_endpoint_modes[1])21229return false;21230if ((best_blk.m_color_endpoint_modes[0] != 7) && (best_blk.m_color_endpoint_modes[0] != 11))21231return false;2123221233if (best_blk.m_color_endpoint_modes[0] == 7)21234{21235if (!(((best_blk.m_weight_ise_range == 1) && (best_blk.m_endpoint_ise_range == 20)) ||21236((best_blk.m_weight_ise_range == 2) && (best_blk.m_endpoint_ise_range == 20)) ||21237((best_blk.m_weight_ise_range == 3) && (best_blk.m_endpoint_ise_range == 19)) ||21238((best_blk.m_weight_ise_range == 4) && (best_blk.m_endpoint_ise_range == 17)) ||21239((best_blk.m_weight_ise_range == 5) && (best_blk.m_endpoint_ise_range == 15))))21240{21241return false;21242}21243}21244else21245{21246if (!(((best_blk.m_weight_ise_range == 1) && (best_blk.m_endpoint_ise_range == 14)) ||21247((best_blk.m_weight_ise_range == 2) && (best_blk.m_endpoint_ise_range == 12))))21248{21249return false;21250}21251}2125221253for (uint32_t s = 0; s < 2; s++)21254{21255int e[2][3];21256if (best_blk.m_color_endpoint_modes[0] == 7)21257{21258bool success = decode_mode7_to_qlog12(best_blk.m_endpoints + s * NUM_MODE7_ENDPOINTS, e, nullptr, best_blk.m_endpoint_ise_range);21259if (!success)21260return false;21261}21262else21263{21264bool success = decode_mode11_to_qlog12(best_blk.m_endpoints + s * NUM_MODE11_ENDPOINTS, e, best_blk.m_endpoint_ise_range);21265if (!success)21266return false;21267}2126821269for (uint32_t c = 0; c < 3; c++)21270{21271bc6h_endpoints[s][c][0] = qlog_to_half(e[0][c], 12);21272if (is_half_inf_or_nan(bc6h_endpoints[s][c][0]))21273return false;2127421275bc6h_endpoints[s][c][1] = qlog_to_half(e[1][c], 12);21276if (is_half_inf_or_nan(bc6h_endpoints[s][c][1]))21277return false;21278}21279}2128021281uint8_t bc6h_weights[16];21282if (best_blk.m_weight_ise_range == 1)21283{21284static const uint8_t s_astc1_to_bc6h_3[3] = { 0, 4, 7 };2128521286for (uint32_t i = 0; i < 16; i++)21287bc6h_weights[i] = s_astc1_to_bc6h_3[best_blk.m_weights[i]];21288}21289else if (best_blk.m_weight_ise_range == 2)21290{21291static const uint8_t s_astc2_to_bc6h_3[4] = { 0, 2, 5, 7 };2129221293for (uint32_t i = 0; i < 16; i++)21294bc6h_weights[i] = s_astc2_to_bc6h_3[best_blk.m_weights[i]];21295}21296else if (best_blk.m_weight_ise_range == 3)21297{21298static const uint8_t s_astc3_to_bc6h_3[5] = { 0, 2, 4, 5, 7 };2129921300for (uint32_t i = 0; i < 16; i++)21301bc6h_weights[i] = s_astc3_to_bc6h_3[best_blk.m_weights[i]];21302}21303else if (best_blk.m_weight_ise_range == 4)21304{21305static const uint8_t s_astc4_to_bc6h_3[6] = { 0, 7, 1, 6, 3, 4 };2130621307for (uint32_t i = 0; i < 16; i++)21308bc6h_weights[i] = s_astc4_to_bc6h_3[best_blk.m_weights[i]];21309}21310else if (best_blk.m_weight_ise_range == 5)21311{21312memcpy(bc6h_weights, best_blk.m_weights, 16);21313}21314else21315{21316assert(0);21317return false;21318}2131921320bc6h_enc_block_2subset_3bit_weights(&transcoded_bc6h_blk, common_part_index, bc6h_endpoints, bc6h_weights);2132121322return true;21323}2132421325//--------------------------------------------------------------------------------------------------------------------------21326// Transcodes an UASTC HDR block to BC6H. Must have been encoded to UASTC HDR, or this fails.21327bool astc_hdr_transcode_to_bc6h(const astc_blk& src_blk, bc6h_block& dst_blk)21328{21329assert(g_astc_hdr_core_initialized);21330if (!g_astc_hdr_core_initialized)21331{21332assert(0);21333return false;21334}2133521336astc_helpers::log_astc_block log_blk;2133721338if (!astc_helpers::unpack_block(&src_blk, log_blk, 4, 4))21339{21340// Failed unpacking ASTC data21341return false;21342}2134321344return astc_hdr_transcode_to_bc6h(log_blk, dst_blk);21345}2134621347//--------------------------------------------------------------------------------------------------------------------------21348// Transcodes an UASTC HDR block to BC6H. Must have been encoded to UASTC HDR, or this fails.21349bool astc_hdr_transcode_to_bc6h(const astc_helpers::log_astc_block& log_blk, bc6h_block& dst_blk)21350{21351assert(g_astc_hdr_core_initialized);21352if (!g_astc_hdr_core_initialized)21353{21354assert(0);21355return false;21356}2135721358if (log_blk.m_solid_color_flag_ldr)21359{21360// Don't support LDR solid colors.21361return false;21362}2136321364if (log_blk.m_solid_color_flag_hdr)21365{21366// Solid color HDR block21367return bc6h_enc_block_solid_color(&dst_blk, log_blk.m_solid_color);21368}2136921370// Only support 4x4 grid sizes21371if ((log_blk.m_grid_width != 4) || (log_blk.m_grid_height != 4))21372return false;2137321374// Don't support dual plane encoding21375if (log_blk.m_dual_plane)21376return false;2137721378if (log_blk.m_num_partitions == 1)21379{21380// Handle 1 partition (or subset)2138121382// UASTC HDR checks21383if ((log_blk.m_weight_ise_range < 1) || (log_blk.m_weight_ise_range > 8))21384return false;2138521386int e[2][3];21387bool success;2138821389if (log_blk.m_color_endpoint_modes[0] == 7)21390{21391if (log_blk.m_endpoint_ise_range != 20)21392return false;2139321394success = decode_mode7_to_qlog12(log_blk.m_endpoints, e, nullptr, log_blk.m_endpoint_ise_range);21395}21396else if (log_blk.m_color_endpoint_modes[0] == 11)21397{21398// UASTC HDR checks21399if (log_blk.m_weight_ise_range <= 7)21400{21401if (log_blk.m_endpoint_ise_range != 20)21402return false;21403}21404else if (log_blk.m_endpoint_ise_range != 19)21405{21406return false;21407}2140821409success = decode_mode11_to_qlog12(log_blk.m_endpoints, e, log_blk.m_endpoint_ise_range);21410}21411else21412{21413return false;21414}2141521416if (!success)21417return false;2141821419// Transform endpoints to half float21420half_float h_e[3][2] =21421{21422{ qlog_to_half(e[0][0], 12), qlog_to_half(e[1][0], 12) },21423{ qlog_to_half(e[0][1], 12), qlog_to_half(e[1][1], 12) },21424{ qlog_to_half(e[0][2], 12), qlog_to_half(e[1][2], 12) }21425};2142621427// Sanity check for NaN/Inf21428for (uint32_t i = 0; i < 2; i++)21429if (is_half_inf_or_nan(h_e[0][i]) || is_half_inf_or_nan(h_e[1][i]) || is_half_inf_or_nan(h_e[2][i]))21430return false;2143121432// Transcode to bc6h21433if (!transcode_bc6h_1subset(h_e, log_blk, dst_blk))21434return false;21435}21436else if (log_blk.m_num_partitions == 2)21437{21438// Handle 2 partition (or subset)21439int common_bc7_pat_index = g_astc_partition_id_to_common_bc7_pat_index[log_blk.m_partition_id];21440if (common_bc7_pat_index < 0)21441return false;2144221443assert(common_bc7_pat_index < (int)basist::TOTAL_ASTC_BC6H_COMMON_PARTITIONS2);2144421445if (!transcode_bc6h_2subsets(common_bc7_pat_index, log_blk, dst_blk))21446return false;21447}21448else21449{21450// Only supports 1 or 2 partitions (or subsets)21451return false;21452}2145321454return true;21455}2145621457// ASTC 6x6 support21458namespace astc_6x6_hdr21459{21460const block_mode_desc g_block_mode_descs[TOTAL_BLOCK_MODE_DECS] =21461{21462// ------ mode 1121463{ false, 11, 1, 6, 6, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },21464{ false, 11, 1, 6, 6, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },2146521466{ false, 11, 1, 6, 5, astc_helpers::BISE_96_LEVELS, astc_helpers::BISE_5_LEVELS, astc_helpers::BISE_96_LEVELS, astc_helpers::BISE_5_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },21467{ false, 11, 1, 5, 6, astc_helpers::BISE_96_LEVELS, astc_helpers::BISE_5_LEVELS, astc_helpers::BISE_96_LEVELS, astc_helpers::BISE_5_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },2146821469{ false, 11, 1, 6, 4, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_8_LEVELS, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_8_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },21470{ false, 11, 1, 4, 6, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_8_LEVELS, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_8_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },2147121472{ false, 11, 1, 6, 3, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_16_LEVELS, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },21473{ false, 11, 1, 3, 6, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_16_LEVELS, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },2147421475{ false, 11, 1, 5, 5, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_8_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_8_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },21476{ false, 11, 1, 4, 4, astc_helpers::BISE_192_LEVELS, astc_helpers::BISE_16_LEVELS, astc_helpers::BISE_192_LEVELS, astc_helpers::BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },2147721478{ false, 11, 1, 3, 3, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_16_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },2147921480// ------ mode 721481{ false, 7, 1, 6, 6, astc_helpers::BISE_96_LEVELS, astc_helpers::BISE_5_LEVELS, astc_helpers::BISE_96_LEVELS, astc_helpers::BISE_5_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },2148221483{ false, 7, 1, 6, 6, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },21484{ false, 7, 1, 6, 6, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },2148521486{ false, 7, 1, 5, 6, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_6_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_6_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },21487{ false, 7, 1, 6, 5, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_6_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_6_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },2148821489{ false, 7, 1, 3, 6, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_20_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_20_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },21490{ false, 7, 1, 6, 3, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_20_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_20_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },2149121492// ------ mode 11, 2 subset21493{ false, 11, 2, 6, 6, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_2_LEVELS, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },2149421495// 6x3/3x621496{ false, 11, 2, 6, 3, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },21497{ false, 11, 2, 3, 6, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },2149821499// 3x6/6x321500{ false, 11, 2, 3, 6, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },21501{ false, 11, 2, 6, 3, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },2150221503// 3x6/6x321504{ false, 11, 2, 4, 6, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_3_LEVELS, 0, 0 },21505{ false, 11, 2, 6, 4, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_3_LEVELS, 0, 0 },2150621507// ------ mode 7, 2 subset2150821509// 6x5/5x621510{ false, 7, 2, 5, 6, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },21511{ false, 7, 2, 6, 5, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },2151221513// 6x4/4x6 mode 721514{ false, 7, 2, 4, 6, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },21515{ false, 7, 2, 6, 4, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },2151621517// 6x621518{ false, 7, 2, 6, 6, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },2151921520// 6x621521{ false, 7, 2, 6, 6, astc_helpers::BISE_192_LEVELS, astc_helpers::BISE_2_LEVELS, astc_helpers::BISE_192_LEVELS, astc_helpers::BISE_2_LEVELS, 0, 0 },2152221523// 5x521524{ false, 7, 2, 5, 5, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, 0, 0 },2152521526// 6x3/3x6 mode 721527{ false, 7, 2, 3, 6, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_8_LEVELS, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_8_LEVELS, 0, 0 },21528{ false, 7, 2, 6, 3, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_8_LEVELS, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_8_LEVELS, 0, 0 },2152921530// 6x3/3x6 mode 721531{ false, 7, 2, 3, 6, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_6_LEVELS, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_6_LEVELS, 0, 0 },21532{ false, 7, 2, 6, 3, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_6_LEVELS, astc_helpers::BISE_80_LEVELS, astc_helpers::BISE_6_LEVELS, 0, 0 },2153321534// ------ dual plane2153521536// 3x621537{ true, 11, 1, 3, 6, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },21538{ true, 11, 1, 3, 6, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 1 },21539{ true, 11, 1, 3, 6, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 2 },2154021541// 6x321542{ true, 11, 1, 6, 3, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },21543{ true, 11, 1, 6, 3, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 1 },21544{ true, 11, 1, 6, 3, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 2 },2154521546// 3x321547{ true, 11, 1, 3, 3, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_16_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },21548{ true, 11, 1, 3, 3, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_16_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 1 },21549{ true, 11, 1, 3, 3, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_16_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 2 },2155021551// 4x421552{ true, 11, 1, 4, 4, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_5_LEVELS, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_5_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },21553{ true, 11, 1, 4, 4, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_5_LEVELS, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_5_LEVELS, BASIST_HDR_6X6_LEVEL2, 1 },21554{ true, 11, 1, 4, 4, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_5_LEVELS, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_5_LEVELS, BASIST_HDR_6X6_LEVEL2, 2 },2155521556// 5x521557{ true, 11, 1, 5, 5, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_2_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },21558{ true, 11, 1, 5, 5, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_2_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 1 },21559{ true, 11, 1, 5, 5, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_2_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 2 },2156021561// ------ 2x2 modes for RDO21562// note 2x2 modes will be upsampled to 4x4 during transcoding (the min # of weight bits is 7 in ASTC)21563{ true, 11, 1, 2, 2, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_8_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },21564{ true, 11, 1, 2, 2, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_8_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 1 },21565{ true, 11, 1, 2, 2, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_8_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 2 },21566{ false, 11, 1, 2, 2, astc_helpers::BISE_128_LEVELS, astc_helpers::BISE_2_LEVELS, astc_helpers::BISE_256_LEVELS, astc_helpers::BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },2156721568// ------ 3 subsets2156921570// 6x621571{ false, 7, 3, 6, 6, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_2_LEVELS, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },2157221573// 5x521574{ false, 7, 3, 5, 5, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_2_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },2157521576// 4x421577{ false, 7, 3, 4, 4, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },21578{ false, 7, 3, 4, 4, astc_helpers::BISE_40_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_40_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },21579{ false, 7, 3, 4, 4, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_5_LEVELS, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_5_LEVELS, 0, 0 },2158021581// 3x321582{ false, 7, 3, 3, 3, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_8_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_8_LEVELS, 0, 0 },2158321584// 6x421585{ false, 7, 3, 6, 4, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_2_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },21586{ false, 7, 3, 4, 6, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_2_LEVELS, astc_helpers::BISE_64_LEVELS, astc_helpers::BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },2158721588// 6x421589{ false, 7, 3, 6, 4, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_3_LEVELS, 0, 0 },21590{ false, 7, 3, 4, 6, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_3_LEVELS, 0, 0 },2159121592// 6x521593{ false, 7, 3, 6, 5, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_2_LEVELS, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },21594{ false, 7, 3, 5, 6, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_2_LEVELS, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },2159521596// 6x321597{ false, 7, 3, 6, 3, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_3_LEVELS, 0, 0 },21598{ false, 7, 3, 3, 6, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_48_LEVELS, astc_helpers::BISE_3_LEVELS, 0, 0 },2159921600// 6x321601{ false, 7, 3, 6, 3, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },21602{ false, 7, 3, 3, 6, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_4_LEVELS, astc_helpers::BISE_32_LEVELS, astc_helpers::BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },2160321604// 6x321605{ false, 7, 3, 6, 3, astc_helpers::BISE_24_LEVELS, astc_helpers::BISE_5_LEVELS, astc_helpers::BISE_24_LEVELS, astc_helpers::BISE_5_LEVELS, 0, 0 },21606{ false, 7, 3, 3, 6, astc_helpers::BISE_24_LEVELS, astc_helpers::BISE_5_LEVELS, astc_helpers::BISE_24_LEVELS, astc_helpers::BISE_5_LEVELS, 0, 0 },2160721608// 5x421609{ false, 7, 3, 5, 4, astc_helpers::BISE_40_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_40_LEVELS, astc_helpers::BISE_3_LEVELS, 0, 0 },21610{ false, 7, 3, 4, 5, astc_helpers::BISE_40_LEVELS, astc_helpers::BISE_3_LEVELS, astc_helpers::BISE_40_LEVELS, astc_helpers::BISE_3_LEVELS, 0, 0 },21611};2161221613const reuse_xy_delta g_reuse_xy_deltas[NUM_REUSE_XY_DELTAS] =21614{21615{ -1, 0 }, { -2, 0 }, { -3, 0 }, { -4, 0 },21616{ 3, -1 }, { 2, -1 }, { 1, -1 }, { 0, -1 }, { -1, -1 }, { -2, -1 }, { -3, -1 }, { -4, -1 },21617{ 3, -2 }, { 2, -2 }, { 1, -2 }, { 0, -2 }, { -1, -2 }, { -2, -2 }, { -3, -2 }, { -4, -2 },21618{ 3, -3 }, { 2, -3 }, { 1, -3 }, { 0, -3 }, { -1, -3 }, { -2, -3 }, { -3, -3 }, { -4, -3 },21619{ 3, -4 }, { 2, -4 }, { 1, -4 }, { 0, -4 }21620};2162121622//--------------------------------------------------------------------------------------------------------------------------2162321624void 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)21625{21626if (from_ise_range == to_ise_range)21627{21628if (pDst_ise_vals != pSrc_ise_vals)21629memcpy(pDst_ise_vals, pSrc_ise_vals, n);21630return;21631}2163221633const auto& dequant_tab = astc_helpers::g_dequant_tables.get_weight_tab(from_ise_range).m_ISE_to_val;21634const auto& quant_tab = astc_helpers::g_dequant_tables.get_weight_tab(to_ise_range).m_val_to_ise;2163521636for (uint32_t i = 0; i < n; i++)21637pDst_ise_vals[i] = quant_tab[dequant_tab[pSrc_ise_vals[i]]];21638}2163921640//--------------------------------------------------------------------------------------------------------------------------2164121642inline int get_bit(21643int src_val, int src_bit)21644{21645assert(src_bit >= 0 && src_bit <= 31);21646int bit = (src_val >> src_bit) & 1;21647return bit;21648}2164921650inline void pack_bit(21651int& dst, int dst_bit,21652int src_val, int src_bit = 0)21653{21654assert(dst_bit >= 0 && dst_bit <= 31);21655int bit = get_bit(src_val, src_bit);21656dst |= (bit << dst_bit);21657}2165821659// Valid for weight ISE ranges 12-192 levels. Preserves upper 2 or 3 bits post-quantization.21660static uint8_t g_quantize_tables_preserve2[astc_helpers::TOTAL_ISE_RANGES - 1][256];21661static uint8_t g_quantize_tables_preserve3[astc_helpers::TOTAL_ISE_RANGES - 1][256];2166221663const uint32_t g_part2_unique_index_to_seed[NUM_UNIQUE_PARTITIONS2] =21664{2166586, 959, 936, 476, 1007, 672, 447, 423, 488, 422, 273, 65, 267, 786, 585, 195, 108, 731, 878, 812, 264, 125, 868, 581, 258, 390, 549, 872, 661, 352, 645, 543, 988,21666906, 903, 616, 482, 529, 3, 286, 272, 303, 151, 504, 498, 260, 79, 66, 608, 769, 305, 610, 1014, 967, 835, 789, 7, 951, 691, 15, 763, 976, 438, 314, 601, 673, 177,21667252, 615, 436, 220, 899, 623, 433, 674, 278, 797, 107, 847, 114, 470, 760, 821, 490, 329, 945, 387, 471, 225, 172, 83, 418, 966, 439, 316, 247, 43, 343, 625, 798,216681, 61, 73, 307, 136, 474, 42, 664, 1013, 249, 389, 227, 374, 121, 48, 538, 226, 309, 554, 802, 834, 335, 495, 10, 955, 461, 293, 508, 153, 101, 63, 139, 31, 687,21669132, 174, 324, 545, 289, 39, 178, 594, 963, 854, 222, 323, 998, 964, 598, 475, 720, 1019, 983, 91, 703, 614, 394, 612, 281, 207, 930, 758, 586, 128, 517, 426, 306,21670168, 713, 36, 458, 876, 368, 780, 5, 9, 214, 109, 553, 726, 175, 103, 753, 684, 44, 665, 53, 500, 367, 611, 119, 732, 639, 326, 203, 156, 686, 910, 255, 62, 392, 591,21671112, 88, 213, 19, 1022, 478, 90, 486, 799, 702, 730, 414, 99, 1008, 142, 886, 373, 216, 69, 393, 299, 648, 415, 822, 912, 110, 567, 550, 693, 2, 138, 59, 271, 562, 295,21672714, 719, 199, 893, 831, 1006, 662, 235, 262, 78, 51, 902, 298, 190, 169, 583, 347, 890, 958, 909, 49, 987, 696, 633, 480, 50, 764, 826, 1023, 1016, 437, 891, 774, 257,21673724, 791, 526, 593, 690, 638, 858, 895, 794, 995, 130, 87, 877, 819, 318, 649, 376, 211, 284, 937, 370, 688, 229, 994, 115, 842, 60, 521, 95, 694, 804, 146, 754, 487, 55,2167417, 770, 450, 223, 4, 137, 911, 236, 683, 523, 47, 181, 24, 270, 602, 736, 11, 355, 148, 351, 762, 1009, 16, 210, 619, 805, 874, 807, 887, 403, 999, 810, 27, 402, 551, 135,21675778, 33, 409, 993, 71, 363, 159, 183, 77, 596, 670, 380, 968, 811, 404, 348, 539, 158, 578, 196, 621, 68, 530, 193, 100, 167, 919, 353, 366, 327, 643, 948, 518, 756, 801, 558,2167628, 705, 116, 94, 898, 453, 622, 647, 231, 445, 652, 230, 191, 277, 292, 254, 198, 766, 386, 232, 29, 70, 942, 740, 291, 607, 411, 496, 839, 8, 675, 319, 742, 21, 547, 627, 716,21677663, 23, 914, 631, 595, 499, 685, 950, 510, 54, 587, 432, 45, 646, 25, 122, 947, 171, 862, 441, 808, 722, 14, 74, 658, 129, 266, 1001, 534, 395, 527, 250, 206, 237, 67, 897, 634,21678572, 569, 533, 37, 341, 89, 463, 419, 75, 134, 283, 943, 519, 362, 144, 681, 407, 954, 131, 455, 934, 46, 513, 339, 194, 361, 606, 852, 546, 655, 1015, 147, 506, 240, 56, 836, 76,2167998, 600, 430, 388, 980, 695, 817, 279, 58, 215, 149, 170, 531, 870, 18, 727, 154, 26, 938, 929, 302, 697, 452, 218, 700, 524, 828, 751, 869, 217, 440, 35421680};2168121682const uint32_t g_part3_unique_index_to_seed[NUM_UNIQUE_PARTITIONS3] =21683{216840, 8, 11, 14, 15, 17, 18, 19, 26, 31, 34, 35, 36, 38, 44, 47, 48, 49, 51, 56,2168559, 61, 70, 74, 76, 82, 88, 90, 96, 100, 103, 104, 108, 110, 111, 117, 122, 123,21686126, 127, 132, 133, 135, 139, 147, 150, 151, 152, 156, 157, 163, 166, 168, 171,21687175, 176, 179, 181, 182, 183, 186, 189, 192, 199, 203, 205, 207, 210, 214, 216,21688222, 247, 249, 250, 252, 254, 260, 261, 262, 263, 266, 272, 273, 275, 276, 288,21689291, 292, 293, 294, 297, 302, 309, 310, 313, 314, 318, 327, 328, 331, 335, 337,21690346, 356, 357, 358, 363, 365, 368, 378, 381, 384, 386, 390, 391, 392, 396, 397,21691398, 399, 401, 410, 411, 419, 427, 430, 431, 437, 439, 440, 451, 455, 457, 458,21692459, 460, 462, 468, 470, 471, 472, 474, 475, 477, 479, 482, 483, 488, 493, 495,21693496, 502, 503, 504, 507, 510, 511, 512, 515, 516, 518, 519, 522, 523, 525, 526,21694527, 538, 543, 544, 546, 547, 549, 550, 552, 553, 554, 562, 570, 578, 579, 581,21695582, 588, 589, 590, 593, 595, 600, 606, 611, 613, 618, 623, 625, 632, 637, 638,21696645, 646, 650, 651, 658, 659, 662, 666, 667, 669, 670, 678, 679, 685, 686, 687,21697688, 691, 694, 696, 698, 699, 700, 701, 703, 704, 707, 713, 714, 715, 717, 719,21698722, 724, 727, 730, 731, 734, 738, 739, 743, 747, 748, 750, 751, 753, 758, 760,21699764, 766, 769, 775, 776, 783, 784, 785, 787, 791, 793, 798, 799, 802, 804, 805,21700806, 807, 808, 809, 810, 813, 822, 823, 825, 831, 835, 837, 838, 839, 840, 842,21701845, 846, 848, 853, 854, 858, 859, 860, 866, 874, 882, 884, 887, 888, 892, 894,21702898, 902, 907, 914, 915, 918, 919, 922, 923, 925, 927, 931, 932, 937, 938, 940,21703943, 944, 945, 953, 955, 958, 959, 963, 966, 971, 974, 979, 990, 991, 998, 999,217041007, 1010, 1011, 1012, 1015, 1020, 102321705};2170621707static void init_quantize_tables()21708{21709for (uint32_t ise_range = astc_helpers::BISE_192_LEVELS; ise_range >= astc_helpers::BISE_12_LEVELS; ise_range--)21710{21711const uint32_t num_levels = astc_helpers::get_ise_levels(ise_range);21712const auto& ise_to_val_tab = astc_helpers::g_dequant_tables.get_endpoint_tab(ise_range).m_ISE_to_val;2171321714for (uint32_t desired_val = 0; desired_val < 256; desired_val++)21715{21716{21717uint32_t best_err = UINT32_MAX;21718int best_ise_val = -1;2171921720for (uint32_t ise_val = 0; ise_val < num_levels; ise_val++)21721{21722const uint32_t quant_val = ise_to_val_tab[ise_val];2172321724if ((quant_val & 0b11000000) != (desired_val & 0b11000000))21725continue;2172621727uint32_t err = basisu::squarei((int)quant_val - (int)desired_val);21728if (err < best_err)21729{21730best_err = err;21731best_ise_val = ise_val;21732}2173321734} // ise_val2173521736assert(best_ise_val != -1);2173721738g_quantize_tables_preserve2[ise_range][desired_val] = (uint8_t)best_ise_val;21739}2174021741{21742uint32_t best_err = UINT32_MAX;21743int best_ise_val = -1;2174421745for (uint32_t ise_val = 0; ise_val < num_levels; ise_val++)21746{21747const uint32_t quant_val = ise_to_val_tab[ise_val];2174821749if ((quant_val & 0b11100000) != (desired_val & 0b11100000))21750continue;2175121752uint32_t err = basisu::squarei((int)quant_val - (int)desired_val);21753if (err < best_err)21754{21755best_err = err;21756best_ise_val = ise_val;21757}2175821759} // ise_val2176021761assert(best_ise_val != -1);2176221763g_quantize_tables_preserve3[ise_range][desired_val] = (uint8_t)best_ise_val;21764}2176521766} // desired_val2176721768#if 021769for (uint32_t i = 0; i < 256; i++)21770{21771if (g_quantize_tables_preserve2[ise_range][i] != astc_helpers::g_dequant_tables.get_endpoint_tab(ise_range).m_val_to_ise[i])21772{21773fmt_printf("P2, Range: {}, {} vs. {}\n", ise_range, g_quantize_tables_preserve2[ise_range][i], astc_helpers::g_dequant_tables.get_endpoint_tab(ise_range).m_val_to_ise[i]);21774}2177521776if (g_quantize_tables_preserve3[ise_range][i] != astc_helpers::g_dequant_tables.get_endpoint_tab(ise_range).m_val_to_ise[i])21777{21778fmt_printf("P3, Range: {}, {} vs. {}\n", ise_range, g_quantize_tables_preserve3[ise_range][i], astc_helpers::g_dequant_tables.get_endpoint_tab(ise_range).m_val_to_ise[i]);21779}21780}21781#endif2178221783} // ise_range21784}2178521786void 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)21787{21788assert(pSrc_endpoints != pDst_endpoints);21789assert((src_ise_endpoint_range >= astc_helpers::FIRST_VALID_ENDPOINT_ISE_RANGE) && (src_ise_endpoint_range <= astc_helpers::LAST_VALID_ENDPOINT_ISE_RANGE));21790assert((dst_ise_endpoint_range >= astc_helpers::FIRST_VALID_ENDPOINT_ISE_RANGE) && (dst_ise_endpoint_range <= astc_helpers::LAST_VALID_ENDPOINT_ISE_RANGE));2179121792// must be >=12 ISE levels for g_quantize_tables_preserve2 etc.21793assert(dst_ise_endpoint_range >= astc_helpers::BISE_12_LEVELS);2179421795const uint32_t n = (cem == 11) ? basist::NUM_MODE11_ENDPOINTS : basist::NUM_MODE7_ENDPOINTS;2179621797if (src_ise_endpoint_range == dst_ise_endpoint_range)21798{21799memcpy(pDst_endpoints, pSrc_endpoints, n);21800return;21801}2180221803uint8_t temp_endpoints[basist::NUM_MODE11_ENDPOINTS];21804if (src_ise_endpoint_range != astc_helpers::BISE_256_LEVELS)21805{21806assert(n <= basist::NUM_MODE11_ENDPOINTS);2180721808const auto& endpoint_dequant_tab = astc_helpers::g_dequant_tables.get_endpoint_tab(src_ise_endpoint_range).m_ISE_to_val;2180921810for (uint32_t i = 0; i < n; i++)21811temp_endpoints[i] = endpoint_dequant_tab[pSrc_endpoints[i]];2181221813pSrc_endpoints = temp_endpoints;21814}2181521816if (dst_ise_endpoint_range == astc_helpers::BISE_256_LEVELS)21817{21818memcpy(pDst_endpoints, pSrc_endpoints, n);21819return;21820}2182121822const auto& quant_tab = astc_helpers::g_dequant_tables.get_endpoint_tab(dst_ise_endpoint_range).m_val_to_ise;2182321824const auto& dequant_tab = astc_helpers::g_dequant_tables.get_endpoint_tab(dst_ise_endpoint_range).m_ISE_to_val;21825BASISU_NOTE_UNUSED(dequant_tab);2182621827#if 121828// A smarter value quantization that preserves the key upper bits. (If these bits get corrupted, the entire meaning of the encoding can get lost.)21829if (cem == 11)21830{21831assert(n == 6);2183221833int maj_comp = 0;21834pack_bit(maj_comp, 0, pSrc_endpoints[4], 7);21835pack_bit(maj_comp, 1, pSrc_endpoints[5], 7);2183621837if (maj_comp == 3)21838{21839// Direct21840pDst_endpoints[0] = quant_tab[pSrc_endpoints[0]];21841pDst_endpoints[1] = quant_tab[pSrc_endpoints[1]];21842pDst_endpoints[2] = quant_tab[pSrc_endpoints[2]];21843pDst_endpoints[3] = quant_tab[pSrc_endpoints[3]];21844// No need for preserve1 tables, we can use the regular quantization tables because they preserve the MSB.21845pDst_endpoints[4] = quant_tab[pSrc_endpoints[4]];21846pDst_endpoints[5] = quant_tab[pSrc_endpoints[5]];2184721848assert((dequant_tab[pDst_endpoints[4]] & 128) == (pSrc_endpoints[4] & 128));21849assert((dequant_tab[pDst_endpoints[5]] & 128) == (pSrc_endpoints[5] & 128));21850}21851else21852{21853pDst_endpoints[0] = quant_tab[pSrc_endpoints[0]];21854pDst_endpoints[1] = g_quantize_tables_preserve2[dst_ise_endpoint_range][pSrc_endpoints[1]];21855pDst_endpoints[2] = g_quantize_tables_preserve2[dst_ise_endpoint_range][pSrc_endpoints[2]];21856pDst_endpoints[3] = g_quantize_tables_preserve2[dst_ise_endpoint_range][pSrc_endpoints[3]];21857pDst_endpoints[4] = g_quantize_tables_preserve3[dst_ise_endpoint_range][pSrc_endpoints[4]];21858pDst_endpoints[5] = g_quantize_tables_preserve3[dst_ise_endpoint_range][pSrc_endpoints[5]];2185921860assert((dequant_tab[pDst_endpoints[1]] & 0b11000000) == (pSrc_endpoints[1] & 0b11000000));21861assert((dequant_tab[pDst_endpoints[2]] & 0b11000000) == (pSrc_endpoints[2] & 0b11000000));21862assert((dequant_tab[pDst_endpoints[3]] & 0b11000000) == (pSrc_endpoints[3] & 0b11000000));21863assert((dequant_tab[pDst_endpoints[4]] & 0b11100000) == (pSrc_endpoints[4] & 0b11100000));21864assert((dequant_tab[pDst_endpoints[5]] & 0b11100000) == (pSrc_endpoints[5] & 0b11100000));21865}21866}21867else if (cem == 7)21868{21869assert(n == 4);2187021871pDst_endpoints[0] = g_quantize_tables_preserve2[dst_ise_endpoint_range][pSrc_endpoints[0]];21872pDst_endpoints[1] = g_quantize_tables_preserve3[dst_ise_endpoint_range][pSrc_endpoints[1]];21873pDst_endpoints[2] = g_quantize_tables_preserve3[dst_ise_endpoint_range][pSrc_endpoints[2]];21874pDst_endpoints[3] = g_quantize_tables_preserve3[dst_ise_endpoint_range][pSrc_endpoints[3]];2187521876assert((dequant_tab[pDst_endpoints[0]] & 0b11000000) == (pSrc_endpoints[0] & 0b11000000));21877assert((dequant_tab[pDst_endpoints[1]] & 0b11100000) == (pSrc_endpoints[1] & 0b11100000));21878assert((dequant_tab[pDst_endpoints[2]] & 0b11100000) == (pSrc_endpoints[2] & 0b11100000));21879assert((dequant_tab[pDst_endpoints[3]] & 0b11100000) == (pSrc_endpoints[3] & 0b11100000));21880}21881else21882{21883assert(0);21884}21885#else21886for (uint32_t i = 0; i < n; i++)21887{21888uint32_t v = pSrc_endpoints[i];21889assert(v <= 255);2189021891pDst_endpoints[i] = quant_tab[v];21892}21893#endif21894}2189521896void 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)21897{21898assert(decomp_blk.m_weight_ise_range >= astc_helpers::BISE_2_LEVELS);21899assert(decomp_blk.m_weight_ise_range <= astc_helpers::BISE_32_LEVELS);2190021901// Special case for 2x2 which isn't typically valid ASTC (too few weight bits without dual plane). Upsample to 4x4.21902if ((!dual_plane) && (grid_x == 2) && (grid_y == 2))21903{21904decomp_blk.m_grid_width = 4;21905decomp_blk.m_grid_height = 4;2190621907//const uint32_t total_weight_levels = astc_helpers::bise_levels(decomp_blk.m_weight_ise_range);21908const auto& dequant_weight = astc_helpers::g_dequant_tables.get_weight_tab(decomp_blk.m_weight_ise_range).m_ISE_to_val;21909const auto& quant_weight = astc_helpers::g_dequant_tables.get_weight_tab(decomp_blk.m_weight_ise_range).m_val_to_ise;2191021911astc_helpers::weighted_sample weights[16];2191221913compute_upsample_weights(4, 4, 2, 2, weights);2191421915for (uint32_t y = 0; y < 4; y++)21916{21917for (uint32_t x = 0; x < 4; x++)21918{21919const astc_helpers::weighted_sample& sample = weights[x + y * 4];2192021921uint32_t total_weight = 8;2192221923for (uint32_t yo = 0; yo < 2; yo++)21924{21925for (uint32_t xo = 0; xo < 2; xo++)21926{21927if (!sample.m_weights[yo][xo])21928continue;2192921930total_weight += dequant_weight[transcode_weights[basisu::in_bounds((x + xo) + (y + yo) * grid_x, 0, grid_x * grid_y)]] * sample.m_weights[yo][xo];21931} // x21932} // y2193321934total_weight >>= 4;2193521936assert(total_weight <= 64);2193721938decomp_blk.m_weights[x + y * 4] = quant_weight[total_weight];21939}21940}21941}21942else21943{21944const uint32_t num_planes = dual_plane ? 2 : 1;2194521946decomp_blk.m_grid_width = (uint8_t)grid_x;21947decomp_blk.m_grid_height = (uint8_t)grid_y;21948memcpy(decomp_blk.m_weights, transcode_weights, grid_x * grid_y * num_planes);21949}21950}2195121952// cur_y is the current destination row21953// prev_y is the row we want to access21954static inline int calc_row_index(int cur_y, int prev_y, int cur_row_index)21955{21956assert((cur_y >= 0) && (prev_y >= 0));21957assert((cur_row_index >= 0) && (cur_row_index < REUSE_MAX_BUFFER_ROWS));2195821959int delta_y = prev_y - cur_y;21960assert((delta_y > -REUSE_MAX_BUFFER_ROWS) && (delta_y <= 0));2196121962cur_row_index += delta_y;21963if (cur_row_index < 0)21964cur_row_index += REUSE_MAX_BUFFER_ROWS;2196521966assert((cur_row_index >= 0) && (cur_row_index < REUSE_MAX_BUFFER_ROWS));2196721968return cur_row_index;21969}2197021971bool decode_values(basist::bitwise_decoder& decoder, uint32_t total_values, uint32_t ise_range, uint8_t* pValues)21972{21973assert(ise_range <= astc_helpers::BISE_256_LEVELS);2197421975const uint32_t ep_bits = astc_helpers::g_ise_range_table[ise_range][0];21976const uint32_t ep_trits = astc_helpers::g_ise_range_table[ise_range][1];21977const uint32_t ep_quints = astc_helpers::g_ise_range_table[ise_range][2];2197821979uint32_t total_tqs = 0;21980uint32_t bundle_size = 0, mul = 0;21981if (ep_trits)21982{21983total_tqs = (total_values + 4) / 5;21984bundle_size = 5;21985mul = 3;21986}21987else if (ep_quints)21988{21989total_tqs = (total_values + 2) / 3;21990bundle_size = 3;21991mul = 5;21992}2199321994const uint32_t MAX_TQ_VALUES = 32;21995assert(total_tqs <= MAX_TQ_VALUES);21996uint32_t tq_values[MAX_TQ_VALUES];2199721998for (uint32_t i = 0; i < total_tqs; i++)21999{22000uint32_t num_bits = ep_trits ? 8 : 7;2200122002if (i == (total_tqs - 1))22003{22004uint32_t num_remaining = total_values - (total_tqs - 1) * bundle_size;22005if (ep_trits)22006{22007switch (num_remaining)22008{22009case 1: num_bits = 2; break;22010case 2: num_bits = 4; break;22011case 3: num_bits = 5; break;22012case 4: num_bits = 7; break;22013default: break;22014}22015}22016else if (ep_quints)22017{22018switch (num_remaining)22019{22020case 1: num_bits = 3; break;22021case 2: num_bits = 5; break;22022default: break;22023}22024}22025}2202622027tq_values[i] = (uint32_t)decoder.get_bits(num_bits);22028} // i2202922030uint32_t accum = 0;22031uint32_t accum_remaining = 0;22032uint32_t next_tq_index = 0;2203322034for (uint32_t i = 0; i < total_values; i++)22035{22036uint32_t value = (uint32_t)decoder.get_bits(ep_bits);2203722038if (total_tqs)22039{22040if (!accum_remaining)22041{22042assert(next_tq_index < total_tqs);22043accum = tq_values[next_tq_index++];22044accum_remaining = bundle_size;22045}2204622047uint32_t v = accum % mul;22048accum /= mul;22049accum_remaining--;2205022051value |= (v << ep_bits);22052}2205322054pValues[i] = (uint8_t)value;22055}2205622057return true;22058}2205922060static inline uint32_t get_num_endpoint_vals(uint32_t cem)22061{22062assert((cem == 7) || (cem == 11));22063return (cem == 11) ? basist::NUM_MODE11_ENDPOINTS : basist::NUM_MODE7_ENDPOINTS;22064}2206522066const uint32_t g_bc6h_weights4[16] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };2206722068#if 022069static BASISU_FORCE_INLINE int pos_lrintf(float x)22070{22071assert(x >= 0.0f);22072return (int)(x + .5f);22073}2207422075static BASISU_FORCE_INLINE basist::half_float fast_float_to_half_non_neg_no_nan_inf(float val)22076{22077union { float f; int32_t i; uint32_t u; } fi = { val };22078const int flt_m = fi.i & 0x7FFFFF, flt_e = (fi.i >> 23) & 0xFF;22079int e = 0, m = 0;2208022081assert(((fi.i >> 31) == 0) && (flt_e != 0xFF));2208222083// not zero or denormal22084if (flt_e != 0)22085{22086int new_exp = flt_e - 127;22087if (new_exp > 15)22088e = 31;22089else if (new_exp < -14)22090m = pos_lrintf((1 << 24) * fabsf(fi.f));22091else22092{22093e = new_exp + 15;22094m = pos_lrintf(flt_m * (1.0f / ((float)(1 << 13))));22095}22096}2209722098assert((0 <= m) && (m <= 1024));22099if (m == 1024)22100{22101e++;22102m = 0;22103}2210422105assert((e >= 0) && (e <= 31));22106assert((m >= 0) && (m <= 1023));2210722108basist::half_float result = (basist::half_float)((e << 10) | m);22109return result;22110}22111#endif2211222113union fu3222114{22115uint32_t u;22116float f;22117};2211822119static BASISU_FORCE_INLINE basist::half_float fast_float_to_half_no_clamp_neg_nan_or_inf(float f)22120{22121assert(!isnan(f) && !isinf(f));22122assert((f >= 0.0f) && (f <= basist::MAX_HALF_FLOAT));2212322124// Sutract 112 from the exponent, to change the bias from 127 to 15.22125static const fu32 g_f_to_h{ 0x7800000 };2212622127fu32 fu;2212822129fu.f = f * g_f_to_h.f;2213022131uint32_t h = (basist::half_float)((fu.u >> (23 - 10)) & 0x7FFF);2213222133// round to even22134uint32_t mant = fu.u & 8191; // examine lowest 13 bits22135h += (mant > 4096);2213622137if (h > basist::MAX_HALF_FLOAT_AS_INT_BITS)22138h = basist::MAX_HALF_FLOAT_AS_INT_BITS;2213922140return (basist::half_float)h;22141}2214222143static BASISU_FORCE_INLINE float ftoh(float f)22144{22145//float res = (float)fast_float_to_half_non_neg_no_nan_inf(fabsf(f)) * ((f < 0.0f) ? -1.0f : 1.0f);22146float res = (float)fast_float_to_half_no_clamp_neg_nan_or_inf(fabsf(f)) * ((f < 0.0f) ? -1.0f : 1.0f);22147return res;22148}2214922150// Supports positive and denormals only. No NaN or Inf.22151static BASISU_FORCE_INLINE float fast_half_to_float_pos_not_inf_or_nan(basist::half_float h)22152{22153assert(!basist::half_is_signed(h) && !basist::is_half_inf_or_nan(h));2215422155// add 112 to the exponent (112+half float's exp bias of 15=float32's bias of 127)22156static const fu32 K = { 0x77800000 };2215722158fu32 o;22159o.u = h << 13;22160o.f *= K.f;2216122162return o.f;22163}2216422165static BASISU_FORCE_INLINE float inv_sqrt(float v)22166{22167union22168{22169float flt;22170uint32_t ui;22171} un;2217222173un.flt = v;22174un.ui = 0x5F1FFFF9UL - (un.ui >> 1);2217522176return 0.703952253f * un.flt * (2.38924456f - v * (un.flt * un.flt));22177}2217822179static const int FAST_BC6H_STD_DEV_THRESH = 256;22180static const int FAST_BC6H_COMPLEX_STD_DEV_THRESH = 512;22181static const int FAST_BC6H_VERY_COMPLEX_STD_DEV_THRESH = 2048;2218222183static void assign_weights_simple_4(22184const basist::half_float* pPixels,22185uint8_t* pWeights,22186int min_r, int min_g, int min_b,22187int max_r, int max_g, int max_b, int64_t block_max_var)22188{22189BASISU_NOTE_UNUSED(block_max_var);2219022191float fmin_r = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)min_r);22192float fmin_g = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)min_g);22193float fmin_b = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)min_b);2219422195float fmax_r = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)max_r);22196float fmax_g = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)max_g);22197float fmax_b = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)max_b);2219822199float fdir_r = fmax_r - fmin_r;22200float fdir_g = fmax_g - fmin_g;22201float fdir_b = fmax_b - fmin_b;2220222203float l = inv_sqrt(fdir_r * fdir_r + fdir_g * fdir_g + fdir_b * fdir_b);22204if (l != 0.0f)22205{22206fdir_r *= l;22207fdir_g *= l;22208fdir_b *= l;22209}2221022211float lr = ftoh(fmin_r * fdir_r + fmin_g * fdir_g + fmin_b * fdir_b);22212float hr = ftoh(fmax_r * fdir_r + fmax_g * fdir_g + fmax_b * fdir_b);2221322214float frr = (hr == lr) ? 0.0f : (14.93333f / (float)(hr - lr));2221522216lr = (-lr * frr) + 0.53333f;22217for (uint32_t i = 0; i < 16; i++)22218{22219const float r = fast_half_to_float_pos_not_inf_or_nan(pPixels[i * 3 + 0]);22220const float g = fast_half_to_float_pos_not_inf_or_nan(pPixels[i * 3 + 1]);22221const float b = fast_half_to_float_pos_not_inf_or_nan(pPixels[i * 3 + 2]);22222const float w = ftoh(r * fdir_r + g * fdir_g + b * fdir_b);2222322224pWeights[i] = (uint8_t)basisu::clamp((int)(w * frr + lr), 0, 15);22225}22226}2222722228static double assign_weights_4(22229const vec3F* pFloat_pixels, const float* pPixel_scales,22230uint8_t* pWeights,22231int min_r, int min_g, int min_b,22232int max_r, int max_g, int max_b, int64_t block_max_var, bool try_2subsets_flag,22233const fast_bc6h_params& params)22234{22235float cr[16], cg[16], cb[16];2223622237for (uint32_t i = 0; i < 16; i++)22238{22239const uint32_t w = g_bc6h_weights4[i];2224022241cr[i] = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)((min_r * (64 - w) + max_r * w + 32) >> 6));22242cg[i] = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)((min_g * (64 - w) + max_g * w + 32) >> 6));22243cb[i] = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)((min_b * (64 - w) + max_b * w + 32) >> 6));22244}2224522246double total_err = 0.0f;2224722248if (params.m_brute_force_weight4_assignment)22249{22250for (uint32_t i = 0; i < 16; i++)22251{22252const float qr = pFloat_pixels[i].c[0], qg = pFloat_pixels[i].c[1], qb = pFloat_pixels[i].c[2];2225322254float best_err = basisu::squaref(cr[0] - qr) + basisu::squaref(cg[0] - qg) + basisu::squaref(cb[0] - qb);22255uint32_t best_idx = 0;2225622257for (uint32_t j = 1; j < 16; j++)22258{22259float rd = cr[j] - qr, gd = cg[j] - qg, bd = cb[j] - qb;22260float e = rd * rd + gd * gd + bd * bd;2226122262if (e < best_err)22263{22264best_err = e;22265best_idx = j;22266}22267}2226822269pWeights[i] = (uint8_t)best_idx;2227022271total_err += best_err * pPixel_scales[i];22272}22273}22274else22275{22276const float dir_r = cr[15] - cr[0], dir_g = cg[15] - cg[0], dir_b = cb[15] - cb[0];2227722278float dots[16];22279for (uint32_t i = 0; i < 16; i++)22280dots[i] = cr[i] * dir_r + cg[i] * dir_g + cb[i] * dir_b;2228122282float mid_dots[15];22283bool monotonically_increasing = true;22284for (uint32_t i = 0; i < 15; i++)22285{22286mid_dots[i] = (dots[i] + dots[i + 1]) * .5f;2228722288if (dots[i] > dots[i + 1])22289monotonically_increasing = false;22290}2229122292const bool check_more_colors = block_max_var > (FAST_BC6H_VERY_COMPLEX_STD_DEV_THRESH * FAST_BC6H_VERY_COMPLEX_STD_DEV_THRESH * 16); // watch prec2229322294if (!monotonically_increasing)22295{22296// Seems very rare, not worth optimizing the other cases22297for (uint32_t i = 0; i < 16; i++)22298{22299const float qr = pFloat_pixels[i].c[0], qg = pFloat_pixels[i].c[1], qb = pFloat_pixels[i].c[2];2230022301float d = qr * dir_r + qg * dir_g + qb * dir_b;2230222303float best_e = fabsf(d - dots[0]);22304int best_idx = 0;2230522306for (int j = 1; j < 16; j++)22307{22308float e = fabsf(d - dots[j]);22309if (e < best_e)22310{22311best_e = e;22312best_idx = j;22313}22314}2231522316assert((best_idx >= 0) && (best_idx <= 15));2231722318pWeights[i] = (uint8_t)best_idx;2231922320float err = basisu::squaref(qr - cr[best_idx]) + basisu::squaref(qg - cg[best_idx]) + basisu::squaref(qb - cb[best_idx]);22321total_err += err * pPixel_scales[i];22322}22323}22324else if ((!try_2subsets_flag) || (!check_more_colors))22325{22326for (uint32_t i = 0; i < 16; i++)22327{22328const float qr = pFloat_pixels[i].c[0], qg = pFloat_pixels[i].c[1], qb = pFloat_pixels[i].c[2];2232922330uint32_t best_idx = 0;2233122332float d = qr * dir_r + qg * dir_g + qb * dir_b;2233322334int low = 0;2233522336int mid = low + 7;22337if (d >= mid_dots[mid]) low = mid + 1;22338mid = low + 3;22339if (d >= mid_dots[mid]) low = mid + 1;22340mid = low + 1;22341if (d >= mid_dots[mid]) low = mid + 1;22342mid = low;22343if (d >= mid_dots[mid]) low = mid + 1;2234422345best_idx = low;22346assert((best_idx >= 0) && (best_idx <= 15));2234722348pWeights[i] = (uint8_t)best_idx;2234922350// Giesen's MRSSE (Mean Relative Sum of Squared Errors).22351// Our ASTC HDR encoder uses slightly slower approx. MSLE, and it's too late/risky to eval the difference vs. MRSSE on the larger ASTC HDR blocks.22352float err = basisu::squaref(qr - cr[best_idx]) + basisu::squaref(qg - cg[best_idx]) + basisu::squaref(qb - cb[best_idx]);22353total_err += err * pPixel_scales[i];22354}22355}22356else22357{22358for (uint32_t i = 0; i < 16; i++)22359{22360const float qr = pFloat_pixels[i].c[0], qg = pFloat_pixels[i].c[1], qb = pFloat_pixels[i].c[2];2236122362uint32_t best_idx = 0;2236322364float d = qr * dir_r + qg * dir_g + qb * dir_b;2236522366int low = 0;2236722368int mid = low + 7;22369if (d >= mid_dots[mid]) low = mid + 1;22370mid = low + 3;22371if (d >= mid_dots[mid]) low = mid + 1;22372mid = low + 1;22373if (d >= mid_dots[mid]) low = mid + 1;22374mid = low;22375if (d >= mid_dots[mid]) low = mid + 1;2237622377best_idx = low;22378assert((best_idx >= 0) && (best_idx <= 15));2237922380float err = basisu::squaref(qr - cr[best_idx]) + basisu::squaref(qg - cg[best_idx]) + basisu::squaref(qb - cb[best_idx]);2238122382{22383int alt_idx = best_idx + 1;22384if (alt_idx > 15)22385alt_idx = 13;2238622387float alt_err = basisu::squaref(qr - cr[alt_idx]) + basisu::squaref(qg - cg[alt_idx]) + basisu::squaref(qb - cb[alt_idx]);22388if (alt_err < err)22389{22390err = alt_err;22391best_idx = alt_idx;22392}22393}2239422395{22396int alt_idx2 = best_idx - 1;22397if (alt_idx2 < 0)22398alt_idx2 = 2;22399float alt_err2 = basisu::squaref(qr - cr[alt_idx2]) + basisu::squaref(qg - cg[alt_idx2]) + basisu::squaref(qb - cb[alt_idx2]);22400if (alt_err2 < err)22401{22402err = alt_err2;22403best_idx = alt_idx2;22404}22405}2240622407pWeights[i] = (uint8_t)best_idx;2240822409total_err += err * pPixel_scales[i];22410}22411}22412}2241322414return total_err;22415}2241622417static void assign_weights3(uint8_t trial_weights[16],22418uint32_t best_pat_bits,22419uint32_t subset_min_r[2], uint32_t subset_min_g[2], uint32_t subset_min_b[2],22420uint32_t subset_max_r[2], uint32_t subset_max_g[2], uint32_t subset_max_b[2],22421const vec3F* pFloat_pixels)22422{22423float subset_cr[2][8], subset_cg[2][8], subset_cb[2][8];2242422425for (uint32_t subset = 0; subset < 2; subset++)22426{22427const uint32_t min_r = subset_min_r[subset], min_g = subset_min_g[subset], min_b = subset_min_b[subset];22428const uint32_t max_r = subset_max_r[subset], max_g = subset_max_g[subset], max_b = subset_max_b[subset];2242922430for (uint32_t j = 0; j < 8; j++)22431{22432const uint32_t w = g_bc7_weights3[j];2243322434subset_cr[subset][j] = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)((min_r * (64 - w) + max_r * w + 32) >> 6));22435subset_cg[subset][j] = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)((min_g * (64 - w) + max_g * w + 32) >> 6));22436subset_cb[subset][j] = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)((min_b * (64 - w) + max_b * w + 32) >> 6));22437} // j2243822439} // subset2244022441// TODO: Plane optimization?2244222443for (uint32_t i = 0; i < 16; i++)22444{22445const uint32_t subset = (best_pat_bits >> i) & 1;22446const float qr = pFloat_pixels[i].c[0], qg = pFloat_pixels[i].c[1], qb = pFloat_pixels[i].c[2];2244722448float best_error = basisu::squaref(subset_cr[subset][0] - qr) + basisu::squaref(subset_cg[subset][0] - qg) + basisu::squaref(subset_cb[subset][0] - qb);22449uint32_t best_idx = 0;2245022451for (uint32_t j = 1; j < 8; j++)22452{22453float e = basisu::squaref(subset_cr[subset][j] - qr) + basisu::squaref(subset_cg[subset][j] - qg) + basisu::squaref(subset_cb[subset][j] - qb);22454if (e < best_error)22455{22456best_error = e;22457best_idx = j;22458}22459}2246022461trial_weights[i] = (uint8_t)best_idx;2246222463} // i22464}2246522466static double assign_weights_error_3(uint8_t trial_weights[16],22467uint32_t best_pat_bits,22468uint32_t subset_min_r[2], uint32_t subset_min_g[2], uint32_t subset_min_b[2],22469uint32_t subset_max_r[2], uint32_t subset_max_g[2], uint32_t subset_max_b[2],22470const vec3F* pFloat_pixels, const float* pPixel_scales)22471{22472float subset_cr[2][8], subset_cg[2][8], subset_cb[2][8];2247322474for (uint32_t subset = 0; subset < 2; subset++)22475{22476const uint32_t min_r = subset_min_r[subset], min_g = subset_min_g[subset], min_b = subset_min_b[subset];22477const uint32_t max_r = subset_max_r[subset], max_g = subset_max_g[subset], max_b = subset_max_b[subset];2247822479for (uint32_t j = 0; j < 8; j++)22480{22481const uint32_t w = g_bc7_weights3[j];2248222483subset_cr[subset][j] = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)((min_r * (64 - w) + max_r * w + 32) >> 6));22484subset_cg[subset][j] = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)((min_g * (64 - w) + max_g * w + 32) >> 6));22485subset_cb[subset][j] = fast_half_to_float_pos_not_inf_or_nan((basist::half_float)((min_b * (64 - w) + max_b * w + 32) >> 6));22486} // j2248722488} // subset2248922490double trial_error = 0.0f;2249122492// TODO: Plane optimization?2249322494for (uint32_t i = 0; i < 16; i++)22495{22496const uint32_t subset = (best_pat_bits >> i) & 1;22497const float qr = pFloat_pixels[i].c[0], qg = pFloat_pixels[i].c[1], qb = pFloat_pixels[i].c[2];2249822499float best_error = basisu::squaref(subset_cr[subset][0] - qr) + basisu::squaref(subset_cg[subset][0] - qg) + basisu::squaref(subset_cb[subset][0] - qb);22500uint32_t best_idx = 0;2250122502for (uint32_t j = 1; j < 8; j++)22503{22504float e = basisu::squaref(subset_cr[subset][j] - qr) + basisu::squaref(subset_cg[subset][j] - qg) + basisu::squaref(subset_cb[subset][j] - qb);22505if (e < best_error)22506{22507best_error = e;22508best_idx = j;22509}22510}2251122512trial_weights[i] = (uint8_t)best_idx;2251322514trial_error += best_error * pPixel_scales[i];2251522516} // i2251722518return trial_error;22519}2252022521static basist::vec4F g_bc6h_ls_weights_3[8];22522static basist::vec4F g_bc6h_ls_weights_4[16];2252322524const uint32_t BC6H_NUM_PATS = 32;22525static uint32_t g_bc6h_pats2[BC6H_NUM_PATS];2252622527static void fast_encode_bc6h_init()22528{22529for (uint32_t i = 0; i < 8; i++)22530{22531const float w = (float)g_bc7_weights3[i] * (1.0f / 64.0f);22532g_bc6h_ls_weights_3[i].set(w * w, (1.0f - w) * w, (1.0f - w) * (1.0f - w), w);22533}2253422535for (uint32_t i = 0; i < 16; i++)22536{22537const float w = (float)g_bc6h_weights4[i] * (1.0f / 64.0f);22538g_bc6h_ls_weights_4[i].set(w * w, (1.0f - w) * w, (1.0f - w) * (1.0f - w), w);22539}2254022541for (uint32_t pat_index = 0; pat_index < BC6H_NUM_PATS; pat_index++)22542{22543uint32_t pat_bits = 0;2254422545for (uint32_t j = 0; j < 16; j++)22546pat_bits |= (g_bc7_partition2[pat_index * 16 + j] << j);2254722548g_bc6h_pats2[pat_index] = pat_bits;22549}22550}2255122552static int bc6h_dequantize(int val, int bits)22553{22554assert(val < (1 << bits));2255522556int result;22557if (bits >= 15)22558result = val;22559else if (!val)22560result = 0;22561else if (val == ((1 << bits) - 1))22562result = 0xFFFF;22563else22564result = ((val << 16) + 0x8000) >> bits;22565return result;22566}2256722568static inline basist::half_float bc6h_convert_to_half(int val)22569{22570assert(val < 65536);2257122572// scale by 31/6422573return (basist::half_float)((val * 31) >> 6);22574}2257522576static void bc6h_quant_dequant_endpoints(uint32_t& min_r, uint32_t& min_g, uint32_t& min_b, uint32_t& max_r, uint32_t& max_g, uint32_t& max_b, int bits) // bits=1022577{22578min_r = bc6h_convert_to_half(bc6h_dequantize(basist::bc6h_half_to_blog((basist::half_float)min_r, bits), bits));22579min_g = bc6h_convert_to_half(bc6h_dequantize(basist::bc6h_half_to_blog((basist::half_float)min_g, bits), bits));22580min_b = bc6h_convert_to_half(bc6h_dequantize(basist::bc6h_half_to_blog((basist::half_float)min_b, bits), bits));2258122582max_r = bc6h_convert_to_half(bc6h_dequantize(basist::bc6h_half_to_blog((basist::half_float)max_r, bits), bits));22583max_g = bc6h_convert_to_half(bc6h_dequantize(basist::bc6h_half_to_blog((basist::half_float)max_g, bits), bits));22584max_b = bc6h_convert_to_half(bc6h_dequantize(basist::bc6h_half_to_blog((basist::half_float)max_b, bits), bits));22585}2258622587static void bc6h_quant_endpoints(22588uint32_t min_hr, uint32_t min_hg, uint32_t min_hb, uint32_t max_hr, uint32_t max_hg, uint32_t max_hb,22589uint32_t& min_r, uint32_t& min_g, uint32_t& min_b, uint32_t& max_r, uint32_t& max_g, uint32_t& max_b,22590int bits)22591{22592min_r = basist::bc6h_half_to_blog((basist::half_float)min_hr, bits);22593min_g = basist::bc6h_half_to_blog((basist::half_float)min_hg, bits);22594min_b = basist::bc6h_half_to_blog((basist::half_float)min_hb, bits);2259522596max_r = basist::bc6h_half_to_blog((basist::half_float)max_hr, bits);22597max_g = basist::bc6h_half_to_blog((basist::half_float)max_hg, bits);22598max_b = basist::bc6h_half_to_blog((basist::half_float)max_hb, bits);22599}2260022601static void bc6h_dequant_endpoints(22602uint32_t min_br, uint32_t min_bg, uint32_t min_bb, uint32_t max_br, uint32_t max_bg, uint32_t max_bb,22603uint32_t& min_hr, uint32_t& min_hg, uint32_t& min_hb, uint32_t& max_hr, uint32_t& max_hg, uint32_t& max_hb,22604int bits)22605{22606min_hr = bc6h_convert_to_half(bc6h_dequantize(min_br, bits));22607min_hg = bc6h_convert_to_half(bc6h_dequantize(min_bg, bits));22608min_hb = bc6h_convert_to_half(bc6h_dequantize(min_bb, bits));2260922610max_hr = bc6h_convert_to_half(bc6h_dequantize(max_br, bits));22611max_hg = bc6h_convert_to_half(bc6h_dequantize(max_bg, bits));22612max_hb = bc6h_convert_to_half(bc6h_dequantize(max_bb, bits));22613}2261422615static BASISU_FORCE_INLINE int popcount32(uint32_t x)22616{22617#if defined(__EMSCRIPTEN__) || defined(__clang__) || defined(__GNUC__)22618return __builtin_popcount(x);22619#elif defined(_MSC_VER)22620return __popcnt(x);22621#else22622int count = 0;22623while (x)22624{22625x &= (x - 1);22626++count;22627}22628return count;22629#endif22630}2263122632static BASISU_FORCE_INLINE int fast_roundf_int(float x)22633{22634return (x >= 0.0f) ? (int)(x + 0.5f) : (int)(x - 0.5f);22635}2263622637static void fast_encode_bc6h_2subsets_pattern(22638uint32_t best_pat_index, uint32_t best_pat_bits,22639const basist::half_float* pPixels, const vec3F* pFloat_pixels, const float* pPixel_scales,22640double& cur_error, basist::bc6h_logical_block& log_blk,22641int64_t block_max_var,22642int mean_r, int mean_g, int mean_b,22643const fast_bc6h_params& params)22644{22645BASISU_NOTE_UNUSED(block_max_var);2264622647uint32_t subset_means[2][3] = { { 0 } };22648for (uint32_t i = 0; i < 16; i++)22649{22650const uint32_t subset_index = (best_pat_bits >> i) & 1;22651const uint32_t r = pPixels[i * 3 + 0], g = pPixels[i * 3 + 1], b = pPixels[i * 3 + 2];2265222653subset_means[subset_index][0] += r;22654subset_means[subset_index][1] += g;22655subset_means[subset_index][2] += b;22656}2265722658for (uint32_t s = 0; s < 2; s++)22659for (uint32_t c = 0; c < 3; c++)22660subset_means[s][c] = (subset_means[s][c] + 8) / 16;2266122662int64_t subset_icov[2][6] = { { 0 } };2266322664for (uint32_t i = 0; i < 16; i++)22665{22666const uint32_t subset_index = (best_pat_bits >> i) & 1;22667const int r = (int)pPixels[i * 3 + 0] - mean_r, g = (int)pPixels[i * 3 + 1] - mean_g, b = (int)pPixels[i * 3 + 2] - mean_b;2266822669subset_icov[subset_index][0] += r * r;22670subset_icov[subset_index][1] += r * g;22671subset_icov[subset_index][2] += r * b;22672subset_icov[subset_index][3] += g * g;22673subset_icov[subset_index][4] += g * b;22674subset_icov[subset_index][5] += b * b;22675}2267622677vec3F subset_axis[2];2267822679for (uint32_t subset_index = 0; subset_index < 2; subset_index++)22680{22681float cov[6];22682for (uint32_t i = 0; i < 6; i++)22683cov[i] = (float)subset_icov[subset_index][i];2268422685const float sc = 1.0f / (basisu::maximum(cov[0], cov[3], cov[5]) + basisu::REALLY_SMALL_FLOAT_VAL);22686const float wx = sc * cov[0], wy = sc * cov[3], wz = sc * cov[5];2268722688const float alt_xr = cov[0] * wx + cov[1] * wy + cov[2] * wz;22689const float alt_xg = cov[1] * wx + cov[3] * wy + cov[4] * wz;22690const float alt_xb = cov[2] * wx + cov[4] * wy + cov[5] * wz;2269122692float l = basisu::squaref(alt_xr) + basisu::squaref(alt_xg) + basisu::squaref(alt_xb);2269322694float axis_r = 0.57735027f, axis_g = 0.57735027f, axis_b = 0.57735027f;22695if (fabs(l) >= basisu::SMALL_FLOAT_VAL)22696{22697const float inv_l = inv_sqrt(l);22698axis_r = alt_xr * inv_l;22699axis_g = alt_xg * inv_l;22700axis_b = alt_xb * inv_l;22701}2270222703subset_axis[subset_index].set(axis_r, axis_g, axis_b);22704} // s2270522706float subset_min_dot[2] = { basisu::BIG_FLOAT_VAL, basisu::BIG_FLOAT_VAL };22707float subset_max_dot[2] = { -basisu::BIG_FLOAT_VAL, -basisu::BIG_FLOAT_VAL };22708int subset_min_idx[2] = { 0 }, subset_max_idx[2] = { 0 };2270922710for (uint32_t i = 0; i < 16; i++)22711{22712const uint32_t subset_index = (best_pat_bits >> i) & 1;22713const float r = (float)pPixels[i * 3 + 0], g = (float)pPixels[i * 3 + 1], b = (float)pPixels[i * 3 + 2];22714const float dot = r * subset_axis[subset_index].c[0] + g * subset_axis[subset_index].c[1] + b * subset_axis[subset_index].c[2];2271522716if (dot < subset_min_dot[subset_index])22717{22718subset_min_dot[subset_index] = dot;22719subset_min_idx[subset_index] = i;22720}2272122722if (dot > subset_max_dot[subset_index])22723{22724subset_max_dot[subset_index] = dot;22725subset_max_idx[subset_index] = i;22726}22727} // i2272822729uint32_t subset_min_r[2], subset_min_g[2], subset_min_b[2];22730uint32_t subset_max_r[2], subset_max_g[2], subset_max_b[2];2273122732for (uint32_t subset_index = 0; subset_index < 2; subset_index++)22733{22734const uint32_t min_index = subset_min_idx[subset_index] * 3, max_index = subset_max_idx[subset_index] * 3;2273522736subset_min_r[subset_index] = pPixels[min_index + 0];22737subset_min_g[subset_index] = pPixels[min_index + 1];22738subset_min_b[subset_index] = pPixels[min_index + 2];2273922740subset_max_r[subset_index] = pPixels[max_index + 0];22741subset_max_g[subset_index] = pPixels[max_index + 1];22742subset_max_b[subset_index] = pPixels[max_index + 2];2274322744} // subset_index2274522746// least squares with unquantized endpoints22747const bool use_ls = true;22748if (use_ls)22749{22750uint8_t trial_weights[16];22751assign_weights3(trial_weights, best_pat_bits, subset_min_r, subset_min_g, subset_min_b, subset_max_r, subset_max_g, subset_max_b, pFloat_pixels);2275222753float z00[2] = { 0.0f }, z01[2] = { 0.0f }, z10[2] = { 0.0f }, z11[2] = { 0.0f };22754float q00_r[2] = { 0.0f }, q10_r[2] = { 0.0f }, t_r[2] = { 0.0f };22755float q00_g[2] = { 0.0f }, q10_g[2] = { 0.0f }, t_g[2] = { 0.0f };22756float q00_b[2] = { 0.0f }, q10_b[2] = { 0.0f }, t_b[2] = { 0.0f };2275722758for (uint32_t i = 0; i < 16; i++)22759{22760const uint32_t subset = (best_pat_bits >> i) & 1;2276122762float r = (float)pPixels[i * 3 + 0];22763float g = (float)pPixels[i * 3 + 1];22764float b = (float)pPixels[i * 3 + 2];2276522766const uint32_t sel = trial_weights[i];2276722768z00[subset] += g_bc6h_ls_weights_3[sel][0];22769z10[subset] += g_bc6h_ls_weights_3[sel][1];22770z11[subset] += g_bc6h_ls_weights_3[sel][2];2277122772float w = g_bc6h_ls_weights_3[sel][3];2277322774q00_r[subset] += w * r;22775t_r[subset] += r;2277622777q00_g[subset] += w * g;22778t_g[subset] += g;2277922780q00_b[subset] += w * b;22781t_b[subset] += b;22782}2278322784for (uint32_t subset = 0; subset < 2; subset++)22785{22786q10_r[subset] = t_r[subset] - q00_r[subset];22787q10_g[subset] = t_g[subset] - q00_g[subset];22788q10_b[subset] = t_b[subset] - q00_b[subset];2278922790z01[subset] = z10[subset];2279122792float det = z00[subset] * z11[subset] - z01[subset] * z10[subset];22793if (fabs(det) >= basisu::SMALL_FLOAT_VAL)22794{22795det = 1.0f / det;2279622797float iz00 = z11[subset] * det;22798float iz01 = -z01[subset] * det;22799float iz10 = -z10[subset] * det;22800float iz11 = z00[subset] * det;2280122802subset_max_r[subset] = basisu::clamp<int>(fast_roundf_int(iz00 * q00_r[subset] + iz01 * q10_r[subset]), 0, (int)basist::MAX_BC6H_HALF_FLOAT_AS_UINT);22803subset_min_r[subset] = basisu::clamp<int>(fast_roundf_int(iz10 * q00_r[subset] + iz11 * q10_r[subset]), 0, (int)basist::MAX_BC6H_HALF_FLOAT_AS_UINT);2280422805subset_max_g[subset] = basisu::clamp<int>(fast_roundf_int(iz00 * q00_g[subset] + iz01 * q10_g[subset]), 0, (int)basist::MAX_BC6H_HALF_FLOAT_AS_UINT);22806subset_min_g[subset] = basisu::clamp<int>(fast_roundf_int(iz10 * q00_g[subset] + iz11 * q10_g[subset]), 0, (int)basist::MAX_BC6H_HALF_FLOAT_AS_UINT);2280722808subset_max_b[subset] = basisu::clamp<int>(fast_roundf_int(iz00 * q00_b[subset] + iz01 * q10_b[subset]), 0, (int)basist::MAX_BC6H_HALF_FLOAT_AS_UINT);22809subset_min_b[subset] = basisu::clamp<int>(fast_roundf_int(iz10 * q00_b[subset] + iz11 * q10_b[subset]), 0, (int)basist::MAX_BC6H_HALF_FLOAT_AS_UINT);22810}22811} // subset22812}2281322814const int BC6H_2SUBSET_ABS_ENDPOINT_MODE = 9;2281522816int bc6h_mode_index = BC6H_2SUBSET_ABS_ENDPOINT_MODE, num_endpoint_bits = 6;22817uint32_t abs_blog_endpoints[3][4];2281822819if (params.m_num_diff_endpoint_modes_to_try)22820{22821// ordered from largest base bits to least22822static const int s_bc6h_mode_order2[2] = { 5, 1 };22823static const int s_bc6h_mode_order4[4] = { 0, 5, 7, 1 };22824static const int s_bc6h_mode_order9[9] = { 2, 3, 4, 0, 5, 6, 7, 8, 1 };2282522826uint32_t num_endpoint_modes = 2;22827const int* pBC6H_mode_order = s_bc6h_mode_order2;2282822829if (params.m_num_diff_endpoint_modes_to_try >= 9)22830{22831num_endpoint_modes = 9;22832pBC6H_mode_order = s_bc6h_mode_order9;22833}22834else if (params.m_num_diff_endpoint_modes_to_try >= 4)22835{22836num_endpoint_modes = 4;22837pBC6H_mode_order = s_bc6h_mode_order4;22838}2283922840// Find the BC6H mode that will conservatively encode our trial endpoints. The mode chosen will handle any endpoint swaps.22841for (uint32_t bc6h_mode_iter = 0; bc6h_mode_iter < num_endpoint_modes; bc6h_mode_iter++)22842{22843const uint32_t mode = pBC6H_mode_order[bc6h_mode_iter];2284422845const uint32_t num_base_bits = g_bc6h_mode_sig_bits[mode][0];22846const int base_bitmask = (1 << num_base_bits) - 1;22847BASISU_NOTE_UNUSED(base_bitmask);2284822849const uint32_t num_delta_bits[3] = { g_bc6h_mode_sig_bits[mode][1], g_bc6h_mode_sig_bits[mode][2], g_bc6h_mode_sig_bits[mode][3] };22850const int delta_bitmasks[3] = { (1 << num_delta_bits[0]) - 1, (1 << num_delta_bits[1]) - 1, (1 << num_delta_bits[2]) - 1 };2285122852for (uint32_t subset_index = 0; subset_index < 2; subset_index++)22853{22854bc6h_quant_endpoints(22855subset_min_r[subset_index], subset_min_g[subset_index], subset_min_b[subset_index], subset_max_r[subset_index], subset_max_g[subset_index], subset_max_b[subset_index],22856abs_blog_endpoints[0][subset_index * 2 + 0], abs_blog_endpoints[1][subset_index * 2 + 0], abs_blog_endpoints[2][subset_index * 2 + 0],22857abs_blog_endpoints[0][subset_index * 2 + 1], abs_blog_endpoints[1][subset_index * 2 + 1], abs_blog_endpoints[2][subset_index * 2 + 1],22858num_base_bits);22859}2286022861uint32_t c;22862for (c = 0; c < 3; c++)22863{22864// a very conservative check because we don't have the weight indices yet, so we don't know how to swap end point values22865// purposely enforcing a symmetric limit here so we can invert any endpoints later if needed22866const int max_delta = (1 << (num_delta_bits[c] - 1)) - 1;22867const int min_delta = -max_delta;2286822869int delta0 = (int)abs_blog_endpoints[c][1] - (int)abs_blog_endpoints[c][0];22870if ((delta0 < min_delta) || (delta0 > max_delta))22871break;2287222873int delta1 = (int)abs_blog_endpoints[c][2] - (int)abs_blog_endpoints[c][0];22874if ((delta1 < min_delta) || (delta1 > max_delta))22875break;2287622877int delta2 = (int)abs_blog_endpoints[c][3] - (int)abs_blog_endpoints[c][0];22878if ((delta2 < min_delta) || (delta2 > max_delta))22879break;2288022881// in case the endpoints are swapped22882int delta3 = (int)abs_blog_endpoints[c][2] - (int)abs_blog_endpoints[c][1];22883if ((delta3 < min_delta) || (delta3 > max_delta))22884break;2288522886int delta4 = (int)abs_blog_endpoints[c][3] - (int)abs_blog_endpoints[c][1];22887if ((delta4 < min_delta) || (delta4 > max_delta))22888break;22889}2289022891if (c == 3)22892{22893bc6h_mode_index = mode;22894num_endpoint_bits = num_base_bits;22895break;22896}22897}22898}2289922900if (bc6h_mode_index == BC6H_2SUBSET_ABS_ENDPOINT_MODE)22901{22902for (uint32_t subset_index = 0; subset_index < 2; subset_index++)22903{22904bc6h_quant_endpoints(22905subset_min_r[subset_index], subset_min_g[subset_index], subset_min_b[subset_index], subset_max_r[subset_index], subset_max_g[subset_index], subset_max_b[subset_index],22906abs_blog_endpoints[0][subset_index * 2 + 0], abs_blog_endpoints[1][subset_index * 2 + 0], abs_blog_endpoints[2][subset_index * 2 + 0],22907abs_blog_endpoints[0][subset_index * 2 + 1], abs_blog_endpoints[1][subset_index * 2 + 1], abs_blog_endpoints[2][subset_index * 2 + 1],22908num_endpoint_bits);22909}22910}2291122912for (uint32_t subset_index = 0; subset_index < 2; subset_index++)22913{22914bc6h_dequant_endpoints(22915abs_blog_endpoints[0][subset_index * 2 + 0], abs_blog_endpoints[1][subset_index * 2 + 0], abs_blog_endpoints[2][subset_index * 2 + 0],22916abs_blog_endpoints[0][subset_index * 2 + 1], abs_blog_endpoints[1][subset_index * 2 + 1], abs_blog_endpoints[2][subset_index * 2 + 1],22917subset_min_r[subset_index], subset_min_g[subset_index], subset_min_b[subset_index],22918subset_max_r[subset_index], subset_max_g[subset_index], subset_max_b[subset_index], num_endpoint_bits);22919}2292022921uint8_t trial_weights[16];22922double trial_error = assign_weights_error_3(trial_weights, best_pat_bits, subset_min_r, subset_min_g, subset_min_b, subset_max_r, subset_max_g, subset_max_b, pFloat_pixels, pPixel_scales);2292322924if (trial_error < cur_error)22925{22926basist::bc6h_logical_block trial_log_blk;2292722928trial_log_blk.m_mode = bc6h_mode_index;22929trial_log_blk.m_partition_pattern = best_pat_index;2293022931memcpy(trial_log_blk.m_endpoints, abs_blog_endpoints, sizeof(trial_log_blk.m_endpoints));22932memcpy(trial_log_blk.m_weights, trial_weights, 16);2293322934if (trial_log_blk.m_weights[0] & 4)22935{22936for (uint32_t c = 0; c < 3; c++)22937std::swap(trial_log_blk.m_endpoints[c][0], trial_log_blk.m_endpoints[c][1]);2293822939for (uint32_t i = 0; i < 16; i++)22940{22941const uint32_t subset_index = (best_pat_bits >> i) & 1;22942if (subset_index == 0)22943trial_log_blk.m_weights[i] = 7 - trial_log_blk.m_weights[i];22944}22945}2294622947const uint32_t subset2_anchor_index = g_bc7_table_anchor_index_second_subset[best_pat_index];22948if (trial_log_blk.m_weights[subset2_anchor_index] & 4)22949{22950for (uint32_t c = 0; c < 3; c++)22951std::swap(trial_log_blk.m_endpoints[c][2], trial_log_blk.m_endpoints[c][3]);2295222953for (uint32_t i = 0; i < 16; i++)22954{22955const uint32_t subset_index = (best_pat_bits >> i) & 1;22956if (subset_index == 1)22957trial_log_blk.m_weights[i] = 7 - trial_log_blk.m_weights[i];22958}22959}2296022961if (bc6h_mode_index != BC6H_2SUBSET_ABS_ENDPOINT_MODE)22962{22963const uint32_t num_delta_bits[3] = { g_bc6h_mode_sig_bits[bc6h_mode_index][1], g_bc6h_mode_sig_bits[bc6h_mode_index][2], g_bc6h_mode_sig_bits[bc6h_mode_index][3] };22964const int delta_bitmasks[3] = { (1 << num_delta_bits[0]) - 1, (1 << num_delta_bits[1]) - 1, (1 << num_delta_bits[2]) - 1 };2296522966for (uint32_t c = 0; c < 3; c++)22967{22968const int delta0 = (int)trial_log_blk.m_endpoints[c][1] - (int)trial_log_blk.m_endpoints[c][0];22969const int delta1 = (int)trial_log_blk.m_endpoints[c][2] - (int)trial_log_blk.m_endpoints[c][0];22970const int delta2 = (int)trial_log_blk.m_endpoints[c][3] - (int)trial_log_blk.m_endpoints[c][0];2297122972#ifdef _DEBUG22973// sanity check the final endpoints22974const int max_delta = (1 << (num_delta_bits[c] - 1)) - 1;22975const int min_delta = -(max_delta + 1);22976assert((max_delta - min_delta) == delta_bitmasks[c]);2297722978if ((delta0 < min_delta) || (delta0 > max_delta) || (delta1 < min_delta) || (delta1 > max_delta) || (delta2 < min_delta) || (delta2 > max_delta))22979{22980assert(0);22981break;22982}22983#endif2298422985trial_log_blk.m_endpoints[c][1] = delta0 & delta_bitmasks[c];22986trial_log_blk.m_endpoints[c][2] = delta1 & delta_bitmasks[c];22987trial_log_blk.m_endpoints[c][3] = delta2 & delta_bitmasks[c];2298822989} // c22990}2299122992cur_error = trial_error;22993log_blk = trial_log_blk;22994}22995}2299622997static void fast_encode_bc6h_2subsets(22998const basist::half_float* pPixels, const vec3F* pFloat_pixels, const float* pPixel_scales,22999double& cur_error, basist::bc6h_logical_block& log_blk,23000int64_t block_max_var,23001int mean_r, int mean_g, int mean_b, float block_axis_r, float block_axis_g, float block_axis_b,23002const fast_bc6h_params& params)23003{23004assert((params.m_max_2subset_pats_to_try > 0) && (params.m_max_2subset_pats_to_try <= BC6H_NUM_PATS));2300523006if (params.m_max_2subset_pats_to_try == BC6H_NUM_PATS)23007{23008for (uint32_t i = 0; i < BC6H_NUM_PATS; i++)23009{23010const uint32_t best_pat_index = i;23011const uint32_t best_pat_bits = g_bc6h_pats2[best_pat_index];2301223013fast_encode_bc6h_2subsets_pattern(23014best_pat_index, best_pat_bits,23015pPixels, pFloat_pixels, pPixel_scales,23016cur_error, log_blk,23017block_max_var,23018mean_r, mean_g, mean_b, params);23019}23020return;23021}2302223023uint32_t desired_pat_bits = 0;23024for (uint32_t i = 0; i < 16; i++)23025{23026float f = (float)(pPixels[i * 3 + 0] - mean_r) * block_axis_r +23027(float)(pPixels[i * 3 + 1] - mean_g) * block_axis_g +23028(float)(pPixels[i * 3 + 2] - mean_b) * block_axis_b;2302923030desired_pat_bits |= (((f >= 0.0f) ? 1 : 0) << i);23031} // i2303223033if (params.m_max_2subset_pats_to_try == 1)23034{23035uint32_t best_diff = UINT32_MAX;23036for (uint32_t p = 0; p < BC6H_NUM_PATS; p++)23037{23038const uint32_t bc6h_pat_bits = g_bc6h_pats2[p];2303923040int diff = popcount32(bc6h_pat_bits ^ desired_pat_bits);23041int diff_inv = 16 - diff;2304223043uint32_t min_diff = (basisu::minimum<int>(diff, diff_inv) << 8) | p;23044if (min_diff < best_diff)23045best_diff = min_diff;23046} // p2304723048const uint32_t best_pat_index = best_diff & 0xFF;23049const uint32_t best_pat_bits = g_bc6h_pats2[best_pat_index];2305023051fast_encode_bc6h_2subsets_pattern(23052best_pat_index, best_pat_bits,23053pPixels, pFloat_pixels, pPixel_scales,23054cur_error, log_blk,23055block_max_var,23056mean_r, mean_g, mean_b, params);23057}23058else23059{23060assert(params.m_max_2subset_pats_to_try <= BC6H_NUM_PATS);23061uint32_t pat_diffs[BC6H_NUM_PATS];2306223063for (uint32_t p = 0; p < BC6H_NUM_PATS; p++)23064{23065const uint32_t bc6h_pat_bits = g_bc6h_pats2[p];2306623067int diff = popcount32(bc6h_pat_bits ^ desired_pat_bits);23068int diff_inv = 16 - diff;2306923070pat_diffs[p] = (basisu::minimum<int>(diff, diff_inv) << 8) | p;23071} // p2307223073std::sort(pat_diffs, pat_diffs + BC6H_NUM_PATS);2307423075for (uint32_t pat_iter = 0; pat_iter < params.m_max_2subset_pats_to_try; pat_iter++)23076{23077const uint32_t best_pat_index = pat_diffs[pat_iter] & 0xFF;23078const uint32_t best_pat_bits = g_bc6h_pats2[best_pat_index];2307923080fast_encode_bc6h_2subsets_pattern(23081best_pat_index, best_pat_bits,23082pPixels, pFloat_pixels, pPixel_scales,23083cur_error, log_blk,23084block_max_var,23085mean_r, mean_g, mean_b, params);23086}23087}23088}2308923090void fast_encode_bc6h(const basist::half_float* pPixels, basist::bc6h_block* pBlock, const fast_bc6h_params ¶ms)23091{23092basist::bc6h_logical_block log_blk;23093log_blk.clear();2309423095log_blk.m_mode = basist::BC6H_FIRST_1SUBSET_MODE_INDEX;2309623097uint32_t omin_r = UINT32_MAX, omin_g = UINT32_MAX, omin_b = UINT32_MAX;23098uint32_t omax_r = 0, omax_g = 0, omax_b = 0;23099uint32_t total_r = 0, total_g = 0, total_b = 0;2310023101for (uint32_t i = 0; i < 16; i++)23102{23103uint32_t r = pPixels[i * 3 + 0];23104uint32_t g = pPixels[i * 3 + 1];23105uint32_t b = pPixels[i * 3 + 2];2310623107total_r += r;23108total_g += g;23109total_b += b;2311023111omin_r = basisu::minimum(omin_r, r);23112omin_g = basisu::minimum(omin_g, g);23113omin_b = basisu::minimum(omin_b, b);2311423115omax_r = basisu::maximum(omax_r, r);23116omax_g = basisu::maximum(omax_g, g);23117omax_b = basisu::maximum(omax_b, b);23118}2311923120if ((omin_r == omax_r) && (omin_g == omax_g) && (omin_b == omax_b))23121{23122// Solid block23123log_blk.m_endpoints[0][0] = basist::bc6h_half_to_blog16((basist::half_float)omin_r);23124log_blk.m_endpoints[0][1] = 0;2312523126log_blk.m_endpoints[1][0] = basist::bc6h_half_to_blog16((basist::half_float)omin_g);23127log_blk.m_endpoints[1][1] = 0;2312823129log_blk.m_endpoints[2][0] = basist::bc6h_half_to_blog16((basist::half_float)omin_b);23130log_blk.m_endpoints[2][1] = 0;2313123132log_blk.m_mode = 13;23133pack_bc6h_block(*pBlock, log_blk);2313423135return;23136}2313723138uint32_t min_r, min_g, min_b, max_r, max_g, max_b;2313923140int mean_r = (total_r + 8) / 16;23141int mean_g = (total_g + 8) / 16;23142int mean_b = (total_b + 8) / 16;2314323144int64_t icov[6] = { 0, 0, 0, 0, 0, 0 };2314523146for (uint32_t i = 0; i < 16; i++)23147{23148int r = (int)pPixels[i * 3 + 0] - mean_r;23149int g = (int)pPixels[i * 3 + 1] - mean_g;23150int b = (int)pPixels[i * 3 + 2] - mean_b;2315123152icov[0] += r * r;23153icov[1] += r * g;23154icov[2] += r * b;23155icov[3] += g * g;23156icov[4] += g * b;23157icov[5] += b * b;23158}2315923160int64_t block_max_var = basisu::maximum(icov[0], icov[3], icov[5]); // not divided by 16, i.e. scaled by 162316123162if (block_max_var < (FAST_BC6H_STD_DEV_THRESH * FAST_BC6H_STD_DEV_THRESH * 16))23163{23164// Simple block23165min_r = (omax_r - omin_r) / 32 + omin_r;23166min_g = (omax_g - omin_g) / 32 + omin_g;23167min_b = (omax_b - omin_b) / 32 + omin_b;2316823169max_r = ((omax_r - omin_r) * 31) / 32 + omin_r;23170max_g = ((omax_g - omin_g) * 31) / 32 + omin_g;23171max_b = ((omax_b - omin_b) * 31) / 32 + omin_b;2317223173assert((max_r < MAX_HALF_FLOAT_AS_INT_BITS) && (max_g < MAX_HALF_FLOAT_AS_INT_BITS) && (max_b < MAX_HALF_FLOAT_AS_INT_BITS));2317423175bc6h_quant_dequant_endpoints(min_r, min_g, min_b, max_r, max_g, max_b, 10);2317623177assign_weights_simple_4(pPixels, log_blk.m_weights, min_r, min_g, min_b, max_r, max_g, max_b, block_max_var);2317823179log_blk.m_endpoints[0][0] = basist::bc6h_half_to_blog((basist::half_float)min_r, 10);23180log_blk.m_endpoints[0][1] = basist::bc6h_half_to_blog((basist::half_float)max_r, 10);2318123182log_blk.m_endpoints[1][0] = basist::bc6h_half_to_blog((basist::half_float)min_g, 10);23183log_blk.m_endpoints[1][1] = basist::bc6h_half_to_blog((basist::half_float)max_g, 10);2318423185log_blk.m_endpoints[2][0] = basist::bc6h_half_to_blog((basist::half_float)min_b, 10);23186log_blk.m_endpoints[2][1] = basist::bc6h_half_to_blog((basist::half_float)max_b, 10);2318723188if (log_blk.m_weights[0] & 8)23189{23190for (uint32_t i = 0; i < 16; i++)23191log_blk.m_weights[i] = 15 - log_blk.m_weights[i];2319223193for (uint32_t c = 0; c < 3; c++)23194{23195std::swap(log_blk.m_endpoints[c][0], log_blk.m_endpoints[c][1]);23196}23197}2319823199pack_bc6h_block(*pBlock, log_blk);2320023201return;23202}2320323204// block_max_var cannot be 0 here, also trace cannot be 02320523206// Complex block (edges/strong gradients)23207bool try_2subsets = false;23208double cur_err = 0.0f;23209vec3F float_pixels[16];23210float pixel_scales[16];2321123212// covar rows are:23213// 0, 1, 223214// 1, 3, 423215// 2, 4, 523216float cov[6];23217for (uint32_t i = 0; i < 6; i++)23218cov[i] = (float)icov[i];2321923220const float sc = 1.0f / (float)block_max_var;23221const float wx = sc * cov[0], wy = sc * cov[3], wz = sc * cov[5];2322223223const float alt_xr = cov[0] * wx + cov[1] * wy + cov[2] * wz;23224const float alt_xg = cov[1] * wx + cov[3] * wy + cov[4] * wz;23225const float alt_xb = cov[2] * wx + cov[4] * wy + cov[5] * wz;2322623227float l = basisu::squaref(alt_xr) + basisu::squaref(alt_xg) + basisu::squaref(alt_xb);2322823229float axis_r = 0.57735027f, axis_g = 0.57735027f, axis_b = 0.57735027f;23230if (fabs(l) >= basisu::SMALL_FLOAT_VAL)23231{23232const float inv_l = inv_sqrt(l);23233axis_r = alt_xr * inv_l;23234axis_g = alt_xg * inv_l;23235axis_b = alt_xb * inv_l;23236}2323723238const float tr = axis_r * cov[0] + axis_g * cov[1] + axis_b * cov[2];23239const float tg = axis_r * cov[1] + axis_g * cov[3] + axis_b * cov[4];23240const float tb = axis_r * cov[2] + axis_g * cov[4] + axis_b * cov[5];23241const float principle_axis_var = tr * axis_r + tg * axis_g + tb * axis_b;2324223243const float inv_principle_axis_var = 1.0f / (principle_axis_var + basisu::REALLY_SMALL_FLOAT_VAL);23244axis_r = tr * inv_principle_axis_var;23245axis_g = tg * inv_principle_axis_var;23246axis_b = tb * inv_principle_axis_var;2324723248float total_var = cov[0] + cov[3] + cov[5];2324923250// If the principle axis variance vs. the block's total variance accounts for less than this threshold, it's a "very complex" block that may benefit from 2 subsets.23251const float COMPLEX_BLOCK_PRINCIPLE_AXIS_FRACT_THRESH = .995f;23252try_2subsets = principle_axis_var < (total_var * COMPLEX_BLOCK_PRINCIPLE_AXIS_FRACT_THRESH);2325323254uint32_t min_idx = 0, max_idx = 0;23255float min_dot = basisu::BIG_FLOAT_VAL, max_dot = -basisu::BIG_FLOAT_VAL;2325623257for (uint32_t i = 0; i < 16; i++)23258{23259float r = (float)pPixels[i * 3 + 0];23260float g = (float)pPixels[i * 3 + 1];23261float b = (float)pPixels[i * 3 + 2];2326223263float_pixels[i].c[0] = fast_half_to_float_pos_not_inf_or_nan((half_float)r);23264float_pixels[i].c[1] = fast_half_to_float_pos_not_inf_or_nan((half_float)g);23265float_pixels[i].c[2] = fast_half_to_float_pos_not_inf_or_nan((half_float)b);2326623267pixel_scales[i] = 1.0f / (basisu::squaref(float_pixels[i].c[0]) + basisu::squaref(float_pixels[i].c[1]) + basisu::squaref(float_pixels[i].c[2]) + (float)MIN_HALF_FLOAT);2326823269float dot = r * axis_r + g * axis_g + b * axis_b;2327023271if (dot < min_dot)23272{23273min_dot = dot;23274min_idx = i;23275}2327623277if (dot > max_dot)23278{23279max_dot = dot;23280max_idx = i;23281}23282}2328323284min_r = pPixels[min_idx * 3 + 0];23285min_g = pPixels[min_idx * 3 + 1];23286min_b = pPixels[min_idx * 3 + 2];2328723288max_r = pPixels[max_idx * 3 + 0];23289max_g = pPixels[max_idx * 3 + 1];23290max_b = pPixels[max_idx * 3 + 2];2329123292assert((max_r < MAX_HALF_FLOAT_AS_INT_BITS) && (max_g < MAX_HALF_FLOAT_AS_INT_BITS) && (max_b < MAX_HALF_FLOAT_AS_INT_BITS));2329323294bc6h_quant_dequant_endpoints(min_r, min_g, min_b, max_r, max_g, max_b, 10);2329523296cur_err = assign_weights_4(float_pixels, pixel_scales, log_blk.m_weights, min_r, min_g, min_b, max_r, max_g, max_b, block_max_var, try_2subsets, params);2329723298const uint32_t MAX_LS_PASSES = params.m_hq_ls ? 2 : 1;23299for (uint32_t pass = 0; pass < MAX_LS_PASSES; pass++)23300{23301float z00 = 0.0f, z01 = 0.0f, z10 = 0.0f, z11 = 0.0f;23302float q00_r = 0.0f, q10_r = 0.0f, t_r = 0.0f;23303float q00_g = 0.0f, q10_g = 0.0f, t_g = 0.0f;23304float q00_b = 0.0f, q10_b = 0.0f, t_b = 0.0f;2330523306for (uint32_t i = 0; i < 16; i++)23307{23308float r = (float)pPixels[i * 3 + 0];23309float g = (float)pPixels[i * 3 + 1];23310float b = (float)pPixels[i * 3 + 2];2331123312const uint32_t sel = log_blk.m_weights[i];2331323314z00 += g_bc6h_ls_weights_4[sel][0];23315z10 += g_bc6h_ls_weights_4[sel][1];23316z11 += g_bc6h_ls_weights_4[sel][2];2331723318float w = g_bc6h_ls_weights_4[sel][3];2331923320q00_r += w * r;23321t_r += r;2332223323q00_g += w * g;23324t_g += g;2332523326q00_b += w * b;23327t_b += b;23328}2332923330q10_r = t_r - q00_r;23331q10_g = t_g - q00_g;23332q10_b = t_b - q00_b;2333323334z01 = z10;2333523336float det = z00 * z11 - z01 * z10;23337if (fabs(det) < basisu::SMALL_FLOAT_VAL)23338break;2333923340det = 1.0f / det;2334123342float iz00 = z11 * det;23343float iz01 = -z01 * det;23344float iz10 = -z10 * det;23345float iz11 = z00 * det;2334623347uint32_t trial_max_r = (int)basisu::clamp<float>(std::round(iz00 * q00_r + iz01 * q10_r), 0, (float)basist::MAX_BC6H_HALF_FLOAT_AS_UINT);23348uint32_t trial_min_r = (int)basisu::clamp<float>(std::round(iz10 * q00_r + iz11 * q10_r), 0, (float)basist::MAX_BC6H_HALF_FLOAT_AS_UINT);2334923350uint32_t trial_max_g = (int)basisu::clamp<float>(std::round(iz00 * q00_g + iz01 * q10_g), 0, (float)basist::MAX_BC6H_HALF_FLOAT_AS_UINT);23351uint32_t trial_min_g = (int)basisu::clamp<float>(std::round(iz10 * q00_g + iz11 * q10_g), 0, (float)basist::MAX_BC6H_HALF_FLOAT_AS_UINT);2335223353uint32_t trial_max_b = (int)basisu::clamp<float>(std::round(iz00 * q00_b + iz01 * q10_b), 0, (float)basist::MAX_BC6H_HALF_FLOAT_AS_UINT);23354uint32_t trial_min_b = (int)basisu::clamp<float>(std::round(iz10 * q00_b + iz11 * q10_b), 0, (float)basist::MAX_BC6H_HALF_FLOAT_AS_UINT);2335523356bc6h_quant_dequant_endpoints(trial_min_r, trial_min_g, trial_min_b, trial_max_r, trial_max_g, trial_max_b, 10);2335723358uint8_t trial_weights[16];23359double trial_err = assign_weights_4(float_pixels, pixel_scales, trial_weights, trial_min_r, trial_min_g, trial_min_b, trial_max_r, trial_max_g, trial_max_b, block_max_var, try_2subsets, params);2336023361if (trial_err < cur_err)23362{23363cur_err = trial_err;2336423365min_r = trial_min_r;23366max_r = trial_max_r;2336723368min_g = trial_min_g;23369max_g = trial_max_g;2337023371min_b = trial_min_b;23372max_b = trial_max_b;2337323374memcpy(log_blk.m_weights, trial_weights, 16);23375}23376else23377{23378break;23379}2338023381} // pass2338223383#if 023384//if (full_flag)23385if ((try_2subsets) && (block_max_var > (FAST_BC6H_COMPLEX_STD_DEV_THRESH * FAST_BC6H_COMPLEX_STD_DEV_THRESH * 16)))23386{23387min_r = 0;23388max_r = 0;23389min_g = 0;23390max_g = 0;23391min_b = 0;23392max_b = 0;23393}23394#endif2339523396log_blk.m_endpoints[0][0] = basist::bc6h_half_to_blog((basist::half_float)min_r, 10);23397log_blk.m_endpoints[0][1] = basist::bc6h_half_to_blog((basist::half_float)max_r, 10);2339823399log_blk.m_endpoints[1][0] = basist::bc6h_half_to_blog((basist::half_float)min_g, 10);23400log_blk.m_endpoints[1][1] = basist::bc6h_half_to_blog((basist::half_float)max_g, 10);2340123402log_blk.m_endpoints[2][0] = basist::bc6h_half_to_blog((basist::half_float)min_b, 10);23403log_blk.m_endpoints[2][1] = basist::bc6h_half_to_blog((basist::half_float)max_b, 10);2340423405if (log_blk.m_weights[0] & 8)23406{23407for (uint32_t i = 0; i < 16; i++)23408log_blk.m_weights[i] = 15 - log_blk.m_weights[i];2340923410for (uint32_t c = 0; c < 3; c++)23411{23412std::swap(log_blk.m_endpoints[c][0], log_blk.m_endpoints[c][1]);23413}23414}2341523416if ((params.m_max_2subset_pats_to_try > 0) && ((try_2subsets) && (block_max_var > (FAST_BC6H_COMPLEX_STD_DEV_THRESH * FAST_BC6H_COMPLEX_STD_DEV_THRESH * 16))))23417{23418fast_encode_bc6h_2subsets(pPixels, float_pixels, pixel_scales, cur_err, log_blk, block_max_var, mean_r, mean_g, mean_b, axis_r, axis_g, axis_b, params);23419}2342023421pack_bc6h_block(*pBlock, log_blk);23422}2342323424bool 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)23425{23426const uint32_t BLOCK_W = 6, BLOCK_H = 6;2342723428//interval_timer tm;23429//tm.start();2343023431width = 0;23432height = 0;2343323434if (comp_data_size <= (2 * 3 + 1))23435return false;2343623437basist::bitwise_decoder decoder;23438if (!decoder.init(pComp_data, comp_data_size))23439return false;2344023441if (decoder.get_bits(16) != 0xABCD)23442return false;2344323444width = decoder.get_bits(16);23445height = decoder.get_bits(16);2344623447if (!width || !height || (width > MAX_ASTC_HDR_6X6_DIM) || (height > MAX_ASTC_HDR_6X6_DIM))23448return false;2344923450const uint32_t num_blocks_x = (width + BLOCK_W - 1) / BLOCK_W;23451const uint32_t num_blocks_y = (height + BLOCK_H - 1) / BLOCK_H;2345223453const uint32_t total_blocks = num_blocks_x * num_blocks_y;2345423455decoded_blocks.resize(num_blocks_x, num_blocks_y);23456//memset(decoded_blocks.get_ptr(), 0, decoded_blocks.size_in_bytes());2345723458// These are the decoded log blocks, NOT the output log blocks.23459basisu::vector2D<astc_helpers::log_astc_block> decoded_log_blocks(num_blocks_x, REUSE_MAX_BUFFER_ROWS);23460memset(decoded_log_blocks.get_ptr(), 0, decoded_log_blocks.size_in_bytes());2346123462uint32_t cur_bx = 0, cur_by = 0;23463int cur_row_index = 0;2346423465uint32_t step_counter = 0;23466BASISU_NOTE_UNUSED(step_counter);2346723468while (cur_by < num_blocks_y)23469{23470step_counter++;2347123472//if ((cur_bx == 9) && (cur_by == 13))23473// printf("!");2347423475#if SYNC_MARKERS23476uint32_t mk = decoder.get_bits(16);23477if (mk != 0xDEAD)23478{23479printf("!");23480assert(0);23481return false;23482}23483#endif23484if (decoder.get_bits_remaining() < 1)23485return false;2348623487encoding_type et = encoding_type::cBlock;2348823489uint32_t b0 = decoder.get_bits(1);23490if (!b0)23491{23492uint32_t b1 = decoder.get_bits(1);23493if (b1)23494et = encoding_type::cReuse;23495else23496{23497uint32_t b2 = decoder.get_bits(1);23498if (b2)23499et = encoding_type::cSolid;23500else23501et = encoding_type::cRun;23502}23503}2350423505switch (et)23506{23507case encoding_type::cRun:23508{23509if (!cur_bx && !cur_by)23510return false;2351123512const uint32_t run_len = decoder.decode_vlc(5) + 1;2351323514uint32_t num_blocks_remaining = total_blocks - (cur_bx + cur_by * num_blocks_x);23515if (run_len > num_blocks_remaining)23516return false;2351723518uint32_t prev_bx = cur_bx, prev_by = cur_by;2351923520if (cur_bx)23521prev_bx--;23522else23523{23524prev_bx = num_blocks_x - 1;23525prev_by--;23526}2352723528const astc_helpers::log_astc_block& prev_log_blk = decoded_log_blocks(prev_bx, calc_row_index(cur_by, prev_by, cur_row_index));23529const astc_helpers::astc_block& prev_phys_blk = decoded_blocks(prev_bx, prev_by);2353023531assert((prev_log_blk.m_user_mode == 255) || (prev_log_blk.m_user_mode < TOTAL_BLOCK_MODE_DECS));2353223533for (uint32_t i = 0; i < run_len; i++)23534{23535decoded_log_blocks(cur_bx, calc_row_index(cur_by, cur_by, cur_row_index)) = prev_log_blk;23536decoded_blocks(cur_bx, cur_by) = prev_phys_blk;2353723538cur_bx++;23539if (cur_bx == num_blocks_x)23540{23541cur_bx = 0;23542cur_by++;23543cur_row_index = (cur_row_index + 1) % REUSE_MAX_BUFFER_ROWS;23544}23545}2354623547break;23548}23549case encoding_type::cSolid:23550{23551const basist::half_float rh = (basist::half_float)decoder.get_bits(15);23552const basist::half_float gh = (basist::half_float)decoder.get_bits(15);23553const basist::half_float bh = (basist::half_float)decoder.get_bits(15);2355423555astc_helpers::log_astc_block& log_blk = decoded_log_blocks(cur_bx, calc_row_index(cur_by, cur_by, cur_row_index));2355623557log_blk.clear();23558log_blk.m_user_mode = 255;23559log_blk.m_solid_color_flag_hdr = true;23560log_blk.m_solid_color[0] = rh;23561log_blk.m_solid_color[1] = gh;23562log_blk.m_solid_color[2] = bh;23563log_blk.m_solid_color[3] = basist::float_to_half(1.0f);2356423565bool status = astc_helpers::pack_astc_block(decoded_blocks(cur_bx, cur_by), log_blk);23566if (!status)23567return false;2356823569cur_bx++;23570if (cur_bx == num_blocks_x)23571{23572cur_bx = 0;23573cur_by++;23574cur_row_index = (cur_row_index + 1) % REUSE_MAX_BUFFER_ROWS;23575}2357623577break;23578}23579case encoding_type::cReuse:23580{23581if (!cur_bx && !cur_by)23582return false;2358323584const uint32_t reuse_delta_index = decoder.get_bits(REUSE_XY_DELTA_BITS);2358523586const int reuse_delta_x = g_reuse_xy_deltas[reuse_delta_index].m_x;23587const int reuse_delta_y = g_reuse_xy_deltas[reuse_delta_index].m_y;2358823589const int prev_bx = cur_bx + reuse_delta_x, prev_by = cur_by + reuse_delta_y;23590if ((prev_bx < 0) || (prev_bx >= (int)num_blocks_x))23591return false;23592if (prev_by < 0)23593return false;2359423595const astc_helpers::log_astc_block& prev_log_blk = decoded_log_blocks(prev_bx, calc_row_index(cur_by, prev_by, cur_row_index));2359623597if (prev_log_blk.m_solid_color_flag_hdr)23598return false;23599assert(prev_log_blk.m_user_mode < TOTAL_BLOCK_MODE_DECS);2360023601astc_helpers::log_astc_block& log_blk = decoded_log_blocks(cur_bx, calc_row_index(cur_by, cur_by, cur_row_index));23602astc_helpers::astc_block& phys_blk = decoded_blocks(cur_bx, cur_by);2360323604log_blk = prev_log_blk;2360523606const uint32_t total_grid_weights = log_blk.m_grid_width * log_blk.m_grid_height * (log_blk.m_dual_plane ? 2 : 1);2360723608bool status = decode_values(decoder, total_grid_weights, log_blk.m_weight_ise_range, log_blk.m_weights);23609if (!status)23610return false;2361123612#if 023613const astc_helpers::astc_block& prev_phys_blk = decoded_blocks(prev_bx, prev_by);2361423615astc_helpers::log_astc_block decomp_blk;23616status = astc_helpers::unpack_block(&prev_phys_blk, decomp_blk, BLOCK_W, BLOCK_H);23617if (!status)23618return false;2361923620uint8_t transcode_weights[MAX_BLOCK_W * MAX_BLOCK_H * 2];23621requantize_astc_weights(total_grid_weights, log_blk.m_weights, log_blk.m_weight_ise_range, transcode_weights, decomp_blk.m_weight_ise_range);2362223623copy_weight_grid(log_blk.m_dual_plane, log_blk.m_grid_width, log_blk.m_grid_height, transcode_weights, decomp_blk);23624#else23625assert(log_blk.m_user_mode < TOTAL_BLOCK_MODE_DECS);23626const block_mode_desc& bmd = g_block_mode_descs[(uint32_t)log_blk.m_user_mode];23627const uint32_t num_endpoint_values = get_num_endpoint_vals(bmd.m_cem);2362823629assert(bmd.m_grid_x == log_blk.m_grid_width && bmd.m_grid_y == log_blk.m_grid_height);23630assert(bmd.m_dp == log_blk.m_dual_plane);23631assert(bmd.m_cem == log_blk.m_color_endpoint_modes[0]);23632assert(bmd.m_num_partitions == log_blk.m_num_partitions);23633assert(bmd.m_dp_channel == log_blk.m_color_component_selector);2363423635// important: bmd.m_weight_ise_range/m_endpoint_ise_range may not match the logical block's due to deltas.2363623637astc_helpers::log_astc_block decomp_blk;23638decomp_blk.clear();23639decomp_blk.m_dual_plane = bmd.m_dp;23640decomp_blk.m_color_component_selector = (uint8_t)bmd.m_dp_channel;23641decomp_blk.m_partition_id = log_blk.m_partition_id;2364223643decomp_blk.m_num_partitions = (uint8_t)bmd.m_num_partitions;2364423645for (uint32_t p = 0; p < bmd.m_num_partitions; p++)23646decomp_blk.m_color_endpoint_modes[p] = (uint8_t)bmd.m_cem;2364723648decomp_blk.m_endpoint_ise_range = (uint8_t)bmd.m_transcode_endpoint_ise_range;23649decomp_blk.m_weight_ise_range = (uint8_t)bmd.m_transcode_weight_ise_range;2365023651for (uint32_t p = 0; p < bmd.m_num_partitions; p++)23652requantize_ise_endpoints(bmd.m_cem, log_blk.m_endpoint_ise_range, log_blk.m_endpoints + num_endpoint_values * p, bmd.m_transcode_endpoint_ise_range, decomp_blk.m_endpoints + num_endpoint_values * p);2365323654uint8_t transcode_weights[BLOCK_W * BLOCK_H * 2];23655requantize_astc_weights(total_grid_weights, log_blk.m_weights, log_blk.m_weight_ise_range, transcode_weights, bmd.m_transcode_weight_ise_range);2365623657copy_weight_grid(bmd.m_dp, bmd.m_grid_x, bmd.m_grid_y, transcode_weights, decomp_blk);23658#endif23659status = astc_helpers::pack_astc_block(phys_blk, decomp_blk);23660if (!status)23661return false;2366223663cur_bx++;23664if (cur_bx == num_blocks_x)23665{23666cur_bx = 0;23667cur_by++;23668cur_row_index = (cur_row_index + 1) % REUSE_MAX_BUFFER_ROWS;23669}2367023671break;23672}23673case encoding_type::cBlock:23674{23675const block_mode bm = (block_mode)decoder.decode_truncated_binary((uint32_t)block_mode::cBMTotalModes);23676const endpoint_mode em = (endpoint_mode)decoder.decode_truncated_binary((uint32_t)endpoint_mode::cTotal);2367723678switch (em)23679{23680case endpoint_mode::cUseLeft:23681case endpoint_mode::cUseUpper:23682{23683int neighbor_bx = cur_bx, neighbor_by = cur_by;2368423685if (em == endpoint_mode::cUseLeft)23686neighbor_bx--;23687else23688neighbor_by--;2368923690if ((neighbor_bx < 0) || (neighbor_by < 0))23691return false;2369223693const astc_helpers::log_astc_block& neighbor_blk = decoded_log_blocks(neighbor_bx, calc_row_index(cur_by, neighbor_by, cur_row_index));23694if (!neighbor_blk.m_color_endpoint_modes[0])23695return false;2369623697const block_mode_desc& bmd = g_block_mode_descs[(uint32_t)bm];23698const uint32_t num_endpoint_values = get_num_endpoint_vals(bmd.m_cem);2369923700if (bmd.m_cem != neighbor_blk.m_color_endpoint_modes[0])23701return false;2370223703astc_helpers::log_astc_block& log_blk = decoded_log_blocks(cur_bx, calc_row_index(cur_by, cur_by, cur_row_index));23704astc_helpers::astc_block& phys_blk = decoded_blocks(cur_bx, cur_by);2370523706log_blk.clear();23707assert((uint32_t)bm <= UINT8_MAX);23708log_blk.m_user_mode = (uint8_t)bm;23709log_blk.m_num_partitions = 1;23710log_blk.m_color_endpoint_modes[0] = (uint8_t)bmd.m_cem;23711// Important: Notice how we're copying the neighbor's endpoint ISE range. Not using the mode's endpoint ISE range here.23712// This is to avoid introducing more quantization error.23713log_blk.m_endpoint_ise_range = neighbor_blk.m_endpoint_ise_range;23714log_blk.m_weight_ise_range = (uint8_t)bmd.m_weight_ise_range;23715log_blk.m_grid_width = (uint8_t)bmd.m_grid_x;23716log_blk.m_grid_height = (uint8_t)bmd.m_grid_y;23717log_blk.m_dual_plane = (uint8_t)bmd.m_dp;23718log_blk.m_color_component_selector = (uint8_t)bmd.m_dp_channel;2371923720memcpy(log_blk.m_endpoints, neighbor_blk.m_endpoints, num_endpoint_values);2372123722const uint32_t total_grid_weights = bmd.m_grid_x * bmd.m_grid_y * (bmd.m_dp ? 2 : 1);2372323724bool status = decode_values(decoder, total_grid_weights, bmd.m_weight_ise_range, log_blk.m_weights);23725if (!status)23726return false;2372723728astc_helpers::log_astc_block decomp_blk;23729decomp_blk.clear();2373023731decomp_blk.m_num_partitions = 1;23732decomp_blk.m_color_endpoint_modes[0] = (uint8_t)bmd.m_cem;23733decomp_blk.m_endpoint_ise_range = (uint8_t)bmd.m_transcode_endpoint_ise_range;23734decomp_blk.m_weight_ise_range = (uint8_t)bmd.m_transcode_weight_ise_range;23735decomp_blk.m_dual_plane = (uint8_t)bmd.m_dp;23736decomp_blk.m_color_component_selector = (uint8_t)bmd.m_dp_channel;2373723738requantize_ise_endpoints(bmd.m_cem, log_blk.m_endpoint_ise_range, log_blk.m_endpoints, bmd.m_transcode_endpoint_ise_range, decomp_blk.m_endpoints);2373923740uint8_t transcode_weights[BLOCK_W * BLOCK_H * 2];23741requantize_astc_weights(total_grid_weights, log_blk.m_weights, bmd.m_weight_ise_range, transcode_weights, bmd.m_transcode_weight_ise_range);2374223743copy_weight_grid(bmd.m_dp, bmd.m_grid_x, bmd.m_grid_y, transcode_weights, decomp_blk);2374423745status = astc_helpers::pack_astc_block(phys_blk, decomp_blk);23746if (!status)23747return false;2374823749cur_bx++;23750if (cur_bx == num_blocks_x)23751{23752cur_bx = 0;23753cur_by++;23754cur_row_index = (cur_row_index + 1) % REUSE_MAX_BUFFER_ROWS;23755}2375623757break;23758}23759case endpoint_mode::cUseLeftDelta:23760case endpoint_mode::cUseUpperDelta:23761{23762int neighbor_bx = cur_bx, neighbor_by = cur_by;2376323764if (em == endpoint_mode::cUseLeftDelta)23765neighbor_bx--;23766else23767neighbor_by--;2376823769if ((neighbor_bx < 0) || (neighbor_by < 0))23770return false;2377123772const astc_helpers::log_astc_block& neighbor_blk = decoded_log_blocks(neighbor_bx, calc_row_index(cur_by, neighbor_by, cur_row_index));23773if (!neighbor_blk.m_color_endpoint_modes[0])23774return false;2377523776const block_mode_desc& bmd = g_block_mode_descs[(uint32_t)bm];23777const uint32_t num_endpoint_values = get_num_endpoint_vals(bmd.m_cem);2377823779if (bmd.m_cem != neighbor_blk.m_color_endpoint_modes[0])23780return false;2378123782astc_helpers::log_astc_block& log_blk = decoded_log_blocks(cur_bx, calc_row_index(cur_by, cur_by, cur_row_index));23783astc_helpers::astc_block& phys_blk = decoded_blocks(cur_bx, cur_by);2378423785log_blk.clear();23786assert((uint32_t)bm <= UINT8_MAX);23787log_blk.m_user_mode = (uint8_t)bm;23788log_blk.m_num_partitions = 1;23789log_blk.m_color_endpoint_modes[0] = (uint8_t)bmd.m_cem;23790log_blk.m_dual_plane = bmd.m_dp;23791log_blk.m_color_component_selector = (uint8_t)bmd.m_dp_channel;2379223793log_blk.m_endpoint_ise_range = (uint8_t)bmd.m_endpoint_ise_range;23794requantize_ise_endpoints(bmd.m_cem, neighbor_blk.m_endpoint_ise_range, neighbor_blk.m_endpoints, bmd.m_endpoint_ise_range, log_blk.m_endpoints);2379523796const int total_endpoint_delta_vals = 1 << NUM_ENDPOINT_DELTA_BITS;23797const int low_delta_limit = -(total_endpoint_delta_vals / 2); // high_delta_limit = (total_endpoint_delta_vals / 2) - 1;2379823799const auto& ise_to_rank = astc_helpers::g_dequant_tables.get_endpoint_tab(log_blk.m_endpoint_ise_range).m_ISE_to_rank;23800const auto& rank_to_ise = astc_helpers::g_dequant_tables.get_endpoint_tab(log_blk.m_endpoint_ise_range).m_rank_to_ISE;23801const int total_endpoint_levels = astc_helpers::get_ise_levels(log_blk.m_endpoint_ise_range);2380223803for (uint32_t i = 0; i < num_endpoint_values; i++)23804{23805int cur_val = ise_to_rank[log_blk.m_endpoints[i]];2380623807int delta = (int)decoder.get_bits(NUM_ENDPOINT_DELTA_BITS) + low_delta_limit;2380823809cur_val += delta;23810if ((cur_val < 0) || (cur_val >= total_endpoint_levels))23811return false;2381223813log_blk.m_endpoints[i] = rank_to_ise[cur_val];23814}2381523816log_blk.m_weight_ise_range = (uint8_t)bmd.m_weight_ise_range;23817log_blk.m_grid_width = (uint8_t)bmd.m_grid_x;23818log_blk.m_grid_height = (uint8_t)bmd.m_grid_y;2381923820const uint32_t total_grid_weights = bmd.m_grid_x * bmd.m_grid_y * (bmd.m_dp ? 2 : 1);2382123822bool status = decode_values(decoder, total_grid_weights, bmd.m_weight_ise_range, log_blk.m_weights);23823if (!status)23824return false;2382523826astc_helpers::log_astc_block decomp_blk;23827decomp_blk.clear();2382823829decomp_blk.m_num_partitions = 1;23830decomp_blk.m_color_endpoint_modes[0] = (uint8_t)bmd.m_cem;23831decomp_blk.m_endpoint_ise_range = (uint8_t)bmd.m_transcode_endpoint_ise_range;23832decomp_blk.m_weight_ise_range = (uint8_t)bmd.m_transcode_weight_ise_range;23833decomp_blk.m_dual_plane = (uint8_t)bmd.m_dp;23834decomp_blk.m_color_component_selector = (uint8_t)bmd.m_dp_channel;2383523836requantize_ise_endpoints(bmd.m_cem, log_blk.m_endpoint_ise_range, log_blk.m_endpoints, bmd.m_transcode_endpoint_ise_range, decomp_blk.m_endpoints);2383723838uint8_t transcode_weights[BLOCK_W * BLOCK_H * 2];23839requantize_astc_weights(total_grid_weights, log_blk.m_weights, bmd.m_weight_ise_range, transcode_weights, bmd.m_transcode_weight_ise_range);2384023841copy_weight_grid(bmd.m_dp, bmd.m_grid_x, bmd.m_grid_y, transcode_weights, decomp_blk);2384223843status = astc_helpers::pack_astc_block(phys_blk, decomp_blk);23844if (!status)23845return false;2384623847cur_bx++;23848if (cur_bx == num_blocks_x)23849{23850cur_bx = 0;23851cur_by++;23852cur_row_index = (cur_row_index + 1) % REUSE_MAX_BUFFER_ROWS;23853}2385423855break;23856}23857case endpoint_mode::cRaw:23858{23859const block_mode_desc& bmd = g_block_mode_descs[(uint32_t)bm];2386023861const uint32_t num_endpoint_values = get_num_endpoint_vals(bmd.m_cem);2386223863astc_helpers::log_astc_block& log_blk = decoded_log_blocks(cur_bx, calc_row_index(cur_by, cur_by, cur_row_index));23864astc_helpers::astc_block& phys_blk = decoded_blocks(cur_bx, cur_by);2386523866log_blk.clear();2386723868assert((uint32_t)bm <= UINT8_MAX);23869log_blk.m_user_mode = (uint8_t)bm;2387023871log_blk.m_num_partitions = (uint8_t)bmd.m_num_partitions;2387223873for (uint32_t p = 0; p < bmd.m_num_partitions; p++)23874log_blk.m_color_endpoint_modes[p] = (uint8_t)bmd.m_cem;2387523876log_blk.m_endpoint_ise_range = (uint8_t)bmd.m_endpoint_ise_range;23877log_blk.m_weight_ise_range = (uint8_t)bmd.m_weight_ise_range;2387823879log_blk.m_grid_width = (uint8_t)bmd.m_grid_x;23880log_blk.m_grid_height = (uint8_t)bmd.m_grid_y;23881log_blk.m_dual_plane = (uint8_t)bmd.m_dp;23882log_blk.m_color_component_selector = (uint8_t)bmd.m_dp_channel;2388323884if (bmd.m_num_partitions == 2)23885{23886const uint32_t unique_partition_index = decoder.decode_truncated_binary(NUM_UNIQUE_PARTITIONS2);23887log_blk.m_partition_id = (uint16_t)g_part2_unique_index_to_seed[unique_partition_index];23888}23889else if (bmd.m_num_partitions == 3)23890{23891const uint32_t unique_partition_index = decoder.decode_truncated_binary(NUM_UNIQUE_PARTITIONS3);23892log_blk.m_partition_id = (uint16_t)g_part3_unique_index_to_seed[unique_partition_index];23893}2389423895bool status = decode_values(decoder, num_endpoint_values * bmd.m_num_partitions, bmd.m_endpoint_ise_range, log_blk.m_endpoints);23896if (!status)23897return false;2389823899const uint32_t total_grid_weights = bmd.m_grid_x * bmd.m_grid_y * (bmd.m_dp ? 2 : 1);2390023901status = decode_values(decoder, total_grid_weights, bmd.m_weight_ise_range, log_blk.m_weights);23902if (!status)23903return false;2390423905astc_helpers::log_astc_block decomp_blk;23906decomp_blk.clear();23907decomp_blk.m_dual_plane = bmd.m_dp;23908decomp_blk.m_color_component_selector = (uint8_t)bmd.m_dp_channel;23909decomp_blk.m_partition_id = log_blk.m_partition_id;2391023911decomp_blk.m_num_partitions = (uint8_t)bmd.m_num_partitions;2391223913for (uint32_t p = 0; p < bmd.m_num_partitions; p++)23914decomp_blk.m_color_endpoint_modes[p] = (uint8_t)bmd.m_cem;2391523916decomp_blk.m_endpoint_ise_range = (uint8_t)bmd.m_transcode_endpoint_ise_range;23917decomp_blk.m_weight_ise_range = (uint8_t)bmd.m_transcode_weight_ise_range;2391823919for (uint32_t p = 0; p < bmd.m_num_partitions; p++)23920requantize_ise_endpoints(bmd.m_cem, bmd.m_endpoint_ise_range, log_blk.m_endpoints + num_endpoint_values * p, bmd.m_transcode_endpoint_ise_range, decomp_blk.m_endpoints + num_endpoint_values * p);2392123922uint8_t transcode_weights[BLOCK_W * BLOCK_H * 2];23923requantize_astc_weights(total_grid_weights, log_blk.m_weights, bmd.m_weight_ise_range, transcode_weights, bmd.m_transcode_weight_ise_range);2392423925copy_weight_grid(bmd.m_dp, bmd.m_grid_x, bmd.m_grid_y, transcode_weights, decomp_blk);2392623927status = astc_helpers::pack_astc_block(phys_blk, decomp_blk);23928if (!status)23929return false;2393023931cur_bx++;23932if (cur_bx == num_blocks_x)23933{23934cur_bx = 0;23935cur_by++;23936cur_row_index = (cur_row_index + 1) % REUSE_MAX_BUFFER_ROWS;23937}2393823939break;23940}23941default:23942{23943assert(0);23944return false;23945}23946}2394723948break;23949}23950default:23951{23952assert(0);23953return false;23954}23955}23956}2395723958if (decoder.get_bits(16) != 0xA742)23959{23960//fmt_error_printf("End marker not found!\n");23961return false;23962}2396323964//fmt_printf("Total decode_file() time: {} secs\n", tm.get_elapsed_secs());2396523966return true;23967}2396823969} // namespace astc_6x6_hdr2397023971#endif // BASISD_SUPPORT_UASTC_HDR2397223973} // namespace basist239742397523976