Merge pull request #97582 from BlueCube3310/basisu-hdr

BasisU: Update to 1.50.0 and add HDR support
This commit is contained in:
Thaddeus Crews 2024-10-14 14:09:57 -05:00
commit 56ed76a372
No known key found for this signature in database
GPG Key ID: 62181B86FE9E5D84
63 changed files with 17114 additions and 792 deletions

View File

@ -339,11 +339,6 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
return err;
}
if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) {
//basis universal does not support float formats, fall back
compress_mode = COMPRESS_VRAM_COMPRESSED;
}
if (compress_mode == COMPRESS_VRAM_COMPRESSED) {
//if using video ram, optimize
if (channel_pack == 0) {

View File

@ -593,11 +593,6 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
}
}
if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) {
// Basis universal does not support float formats, fallback.
compress_mode = COMPRESS_VRAM_COMPRESSED;
}
bool detect_3d = int(p_options["detect_3d/compress_to"]) > 0;
bool detect_roughness = roughness == 0;
bool detect_normal = normal == 0;

View File

@ -14,6 +14,8 @@ thirdparty_obj = []
thirdparty_dir = "#thirdparty/basis_universal/"
# Sync list with upstream CMakeLists.txt
encoder_sources = [
"3rdparty/android_astc_decomp.cpp",
"basisu_astc_hdr_enc.cpp",
"basisu_backend.cpp",
"basisu_basis_file.cpp",
"basisu_bc7enc.cpp",
@ -45,6 +47,8 @@ else:
if env["builtin_zstd"]:
env_basisu.Prepend(CPPPATH=["#thirdparty/zstd"])
env_basisu.Prepend(CPPPATH=["#thirdparty/tinyexr"])
if env.dev_build:
env_basisu.Append(CPPDEFINES=[("BASISU_DEVEL_MESSAGES", 1), ("BASISD_ENABLE_DEBUG_FLAGS", 1)])

View File

@ -30,6 +30,8 @@
#include "image_compress_basisu.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
#include "servers/rendering_server.h"
#include <transcoder/basisu_transcoder.h>
@ -46,9 +48,48 @@ void basis_universal_init() {
}
#ifdef TOOLS_ENABLED
template <typename T>
inline void _basisu_pad_mipmap(const uint8_t *p_image_mip_data, Vector<uint8_t> &r_mip_data_padded, int p_next_width, int p_next_height, int p_width, int p_height, int64_t p_size) {
// Source mip's data interpreted as 32-bit RGBA blocks to help with copying pixel data.
const T *mip_src_data = reinterpret_cast<const T *>(p_image_mip_data);
// Reserve space in the padded buffer.
r_mip_data_padded.resize(p_next_width * p_next_height * sizeof(T));
T *data_padded_ptr = reinterpret_cast<T *>(r_mip_data_padded.ptrw());
// Pad mipmap to the nearest block by smearing.
int x = 0, y = 0;
for (y = 0; y < p_height; y++) {
for (x = 0; x < p_width; x++) {
data_padded_ptr[p_next_width * y + x] = mip_src_data[p_width * y + x];
}
// First, smear in x.
for (; x < p_next_width; x++) {
data_padded_ptr[p_next_width * y + x] = data_padded_ptr[p_next_width * y + x - 1];
}
}
// Then, smear in y.
for (; y < p_next_height; y++) {
for (x = 0; x < p_next_width; x++) {
data_padded_ptr[p_next_width * y + x] = data_padded_ptr[p_next_width * y + x - p_next_width];
}
}
}
Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels) {
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
Ref<Image> image = p_image->duplicate();
image->convert(Image::FORMAT_RGBA8);
bool is_hdr = false;
if (image->get_format() <= Image::FORMAT_RGB565) {
image->convert(Image::FORMAT_RGBA8);
} else if (image->get_format() <= Image::FORMAT_RGBE9995) {
image->convert(Image::FORMAT_RGBAF);
is_hdr = true;
}
basisu::basis_compressor_params params;
@ -74,32 +115,42 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
basisu::job_pool job_pool(OS::get_singleton()->get_processor_count());
params.m_pJob_pool = &job_pool;
BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_RG;
switch (p_channels) {
case Image::USED_CHANNELS_L: {
decompress_format = BASIS_DECOMPRESS_RGB;
} break;
case Image::USED_CHANNELS_LA: {
params.m_force_alpha = true;
decompress_format = BASIS_DECOMPRESS_RGBA;
} break;
case Image::USED_CHANNELS_R: {
decompress_format = BASIS_DECOMPRESS_R;
} break;
case Image::USED_CHANNELS_RG: {
params.m_force_alpha = true;
image->convert_rg_to_ra_rgba8();
decompress_format = BASIS_DECOMPRESS_RG;
} break;
case Image::USED_CHANNELS_RGB: {
decompress_format = BASIS_DECOMPRESS_RGB;
} break;
case Image::USED_CHANNELS_RGBA: {
params.m_force_alpha = true;
decompress_format = BASIS_DECOMPRESS_RGBA;
} break;
BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_MAX;
if (is_hdr) {
decompress_format = BASIS_DECOMPRESS_HDR_RGB;
params.m_hdr = true;
params.m_uastc_hdr_options.set_quality_level(0);
} else {
switch (p_channels) {
case Image::USED_CHANNELS_L: {
decompress_format = BASIS_DECOMPRESS_RGB;
} break;
case Image::USED_CHANNELS_LA: {
params.m_force_alpha = true;
decompress_format = BASIS_DECOMPRESS_RGBA;
} break;
case Image::USED_CHANNELS_R: {
decompress_format = BASIS_DECOMPRESS_R;
} break;
case Image::USED_CHANNELS_RG: {
params.m_force_alpha = true;
image->convert_rg_to_ra_rgba8();
decompress_format = BASIS_DECOMPRESS_RG;
} break;
case Image::USED_CHANNELS_RGB: {
decompress_format = BASIS_DECOMPRESS_RGB;
} break;
case Image::USED_CHANNELS_RGBA: {
params.m_force_alpha = true;
decompress_format = BASIS_DECOMPRESS_RGBA;
} break;
}
}
ERR_FAIL_COND_V(decompress_format == BASIS_DECOMPRESS_MAX, Vector<uint8_t>());
// Copy the source image data with mipmaps into BasisU.
{
const int orig_width = image->get_width();
@ -113,9 +164,10 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
Vector<uint8_t> image_data = image->get_data();
basisu::vector<basisu::image> basisu_mipmaps;
basisu::vector<basisu::imagef> basisu_mipmaps_hdr;
// Buffer for storing padded mipmap data.
Vector<uint32_t> mip_data_padded;
Vector<uint8_t> mip_data_padded;
for (int32_t i = 0; i <= image->get_mipmap_count(); i++) {
int64_t ofs, size;
@ -126,31 +178,10 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
// Pad the mipmap's data if its resolution isn't divisible by 4.
if (image->has_mipmaps() && !is_res_div_4 && (width > 2 && height > 2) && (width != next_width || height != next_height)) {
// Source mip's data interpreted as 32-bit RGBA blocks to help with copying pixel data.
const uint32_t *mip_src_data = reinterpret_cast<const uint32_t *>(image_mip_data);
// Reserve space in the padded buffer.
mip_data_padded.resize(next_width * next_height);
uint32_t *data_padded_ptr = mip_data_padded.ptrw();
// Pad mipmap to the nearest block by smearing.
int x = 0, y = 0;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
data_padded_ptr[next_width * y + x] = mip_src_data[width * y + x];
}
// First, smear in x.
for (; x < next_width; x++) {
data_padded_ptr[next_width * y + x] = data_padded_ptr[next_width * y + x - 1];
}
}
// Then, smear in y.
for (; y < next_height; y++) {
for (x = 0; x < next_width; x++) {
data_padded_ptr[next_width * y + x] = data_padded_ptr[next_width * y + x - next_width];
}
if (is_hdr) {
_basisu_pad_mipmap<BasisRGBAF>(image_mip_data, mip_data_padded, next_width, next_height, width, height, size);
} else {
_basisu_pad_mipmap<uint32_t>(image_mip_data, mip_data_padded, next_width, next_height, width, height, size);
}
// Override the image_mip_data pointer with our temporary Vector.
@ -159,7 +190,7 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
// Override the mipmap's properties.
width = next_width;
height = next_height;
size = mip_data_padded.size() * 4;
size = mip_data_padded.size();
}
// Get the next mipmap's resolution.
@ -167,44 +198,61 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
next_height /= 2;
// Copy the source mipmap's data to a BasisU image.
basisu::image basisu_image(width, height);
memcpy(basisu_image.get_ptr(), image_mip_data, size);
if (is_hdr) {
basisu::imagef basisu_image(width, height);
memcpy(reinterpret_cast<uint8_t *>(basisu_image.get_ptr()), image_mip_data, size);
if (i == 0) {
params.m_source_images_hdr.push_back(basisu_image);
} else {
basisu_mipmaps_hdr.push_back(basisu_image);
}
if (i == 0) {
params.m_source_images.push_back(basisu_image);
} else {
basisu_mipmaps.push_back(basisu_image);
basisu::image basisu_image(width, height);
memcpy(basisu_image.get_ptr(), image_mip_data, size);
if (i == 0) {
params.m_source_images.push_back(basisu_image);
} else {
basisu_mipmaps.push_back(basisu_image);
}
}
}
params.m_source_mipmap_images.push_back(basisu_mipmaps);
if (is_hdr) {
params.m_source_mipmap_images_hdr.push_back(basisu_mipmaps_hdr);
} else {
params.m_source_mipmap_images.push_back(basisu_mipmaps);
}
}
// Encode the image data.
Vector<uint8_t> basisu_data;
basisu::basis_compressor compressor;
compressor.init(params);
int basisu_err = compressor.process();
ERR_FAIL_COND_V(basisu_err != basisu::basis_compressor::cECSuccess, basisu_data);
ERR_FAIL_COND_V(basisu_err != basisu::basis_compressor::cECSuccess, Vector<uint8_t>());
const basisu::uint8_vec &basisu_out = compressor.get_output_basis_file();
basisu_data.resize(basisu_out.size() + 4);
const basisu::uint8_vec &basisu_encoded = compressor.get_output_basis_file();
// Copy the encoded data to the buffer.
{
uint8_t *wb = basisu_data.ptrw();
*(uint32_t *)wb = decompress_format;
Vector<uint8_t> basisu_data;
basisu_data.resize(basisu_encoded.size() + 4);
uint8_t *basisu_data_ptr = basisu_data.ptrw();
memcpy(wb + 4, basisu_out.get_ptr(), basisu_out.size());
}
// Copy the encoded BasisU data into the output buffer.
*(uint32_t *)basisu_data_ptr = decompress_format;
memcpy(basisu_data_ptr + 4, basisu_encoded.get_ptr(), basisu_encoded.size());
print_verbose(vformat("BasisU: Encoding a %dx%d image with %d mipmaps took %d ms.", p_image->get_width(), p_image->get_height(), p_image->get_mipmap_count(), OS::get_singleton()->get_ticks_msec() - start_time));
return basisu_data;
}
#endif // TOOLS_ENABLED
Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
Ref<Image> image;
ERR_FAIL_NULL_V_MSG(p_data, image, "Cannot unpack invalid BasisUniversal data.");
@ -320,6 +368,23 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
}
} break;
case BASIS_DECOMPRESS_HDR_RGB: {
if (bptc_supported) {
basisu_format = basist::transcoder_texture_format::cTFBC6H;
image_format = Image::FORMAT_BPTC_RGBFU;
} else if (astc_supported) {
basisu_format = basist::transcoder_texture_format::cTFASTC_HDR_4x4_RGBA;
image_format = Image::FORMAT_ASTC_4x4_HDR;
} else {
// No supported VRAM compression formats, decompress.
basisu_format = basist::transcoder_texture_format::cTFRGB_9E5;
image_format = Image::FORMAT_RGBE9995;
}
} break;
default: {
ERR_FAIL_V(image);
} break;
}
src_ptr += 4;
@ -371,6 +436,9 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
}
}
print_verbose(vformat("BasisU: Transcoding a %dx%d image with %d mipmaps into %s took %d ms.",
image->get_width(), image->get_height(), image->get_mipmap_count(), Image::get_format_name(image_format), OS::get_singleton()->get_ticks_msec() - start_time));
return image;
}

View File

@ -39,11 +39,20 @@ enum BasisDecompressFormat {
BASIS_DECOMPRESS_RGBA,
BASIS_DECOMPRESS_RG_AS_RA,
BASIS_DECOMPRESS_R,
BASIS_DECOMPRESS_HDR_RGB,
BASIS_DECOMPRESS_MAX
};
void basis_universal_init();
#ifdef TOOLS_ENABLED
struct BasisRGBAF {
uint32_t r;
uint32_t g;
uint32_t b;
uint32_t a;
};
Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels);
#endif

View File

@ -59,12 +59,13 @@ Files extracted from upstream source:
## basis_universal
- Upstream: https://github.com/BinomialLLC/basis_universal
- Version: 1.16.4 (900e40fb5d2502927360fe2f31762bdbb624455f, 2023)
- Version: 1.50.0 (051ad6d8a64bb95a79e8601c317055fd1782ad3e, 2024)
- License: Apache 2.0
Files extracted from upstream source:
- `encoder/` and `transcoder/` folders, minus `jpgd.{cpp,h}`
- `encoder/` and `transcoder/` folders, with the following files removed from `encoder`:
`jpgd.{cpp,h}`, `3rdparty/{qoi.h,tinydds.h,tinyexr.cpp,tinyexr.h}`
- `LICENSE`
Applied upstream PR https://github.com/BinomialLLC/basis_universal/pull/344 to

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
// File: android_astc_decomp.h
#ifndef _TCUASTCUTIL_HPP
#define _TCUASTCUTIL_HPP
/*-------------------------------------------------------------------------
* drawElements Quality Program Tester Core
* ----------------------------------------
*
* Copyright 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief ASTC Utilities.
*//*--------------------------------------------------------------------*/
#include <vector>
#include <stdint.h>
namespace basisu_astc
{
namespace astc
{
// Unpacks a single ASTC block to pDst
// If isSRGB is true, the spec requires the decoder to scale the LDR 8-bit endpoints to 16-bit before interpolation slightly differently,
// which will lead to different outputs. So be sure to set it correctly (ideally it should match whatever the encoder did).
bool decompress_ldr(uint8_t* pDst, const uint8_t* data, bool isSRGB, int blockWidth, int blockHeight);
bool decompress_hdr(float* pDstRGBA, const uint8_t* data, int blockWidth, int blockHeight);
bool is_hdr(const uint8_t* data, int blockWidth, int blockHeight, bool& is_hdr);
} // astc
} // basisu
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,224 @@
// basisu_astc_hdr_enc.h
#pragma once
#include "basisu_enc.h"
#include "basisu_gpu_texture.h"
#include "../transcoder/basisu_astc_helpers.h"
#include "../transcoder/basisu_astc_hdr_core.h"
namespace basisu
{
// This MUST be called before encoding any blocks.
void astc_hdr_enc_init();
const uint32_t MODE11_FIRST_ISE_RANGE = astc_helpers::BISE_3_LEVELS, MODE11_LAST_ISE_RANGE = astc_helpers::BISE_16_LEVELS;
const uint32_t MODE7_PART1_FIRST_ISE_RANGE = astc_helpers::BISE_3_LEVELS, MODE7_PART1_LAST_ISE_RANGE = astc_helpers::BISE_16_LEVELS;
const uint32_t MODE7_PART2_FIRST_ISE_RANGE = astc_helpers::BISE_3_LEVELS, MODE7_PART2_LAST_ISE_RANGE = astc_helpers::BISE_8_LEVELS;
const uint32_t MODE11_PART2_FIRST_ISE_RANGE = astc_helpers::BISE_3_LEVELS, MODE11_PART2_LAST_ISE_RANGE = astc_helpers::BISE_4_LEVELS;
const uint32_t MODE11_TOTAL_SUBMODES = 8; // plus an extra hidden submode, directly encoded, for direct, so really 9 (see tables 99/100 of the ASTC spec)
const uint32_t MODE7_TOTAL_SUBMODES = 6;
struct astc_hdr_codec_options
{
float m_bc6h_err_weight;
bool m_use_solid;
bool m_use_mode11;
bool m_mode11_uber_mode;
uint32_t m_first_mode11_weight_ise_range;
uint32_t m_last_mode11_weight_ise_range;
bool m_mode11_direct_only;
int32_t m_first_mode11_submode;
int32_t m_last_mode11_submode;
bool m_use_mode7_part1;
uint32_t m_first_mode7_part1_weight_ise_range;
uint32_t m_last_mode7_part1_weight_ise_range;
bool m_use_mode7_part2;
uint32_t m_mode7_part2_part_masks;
uint32_t m_first_mode7_part2_weight_ise_range;
uint32_t m_last_mode7_part2_weight_ise_range;
bool m_use_mode11_part2;
uint32_t m_mode11_part2_part_masks;
uint32_t m_first_mode11_part2_weight_ise_range;
uint32_t m_last_mode11_part2_weight_ise_range;
float m_r_err_scale, m_g_err_scale;
bool m_refine_weights;
uint32_t m_level;
bool m_use_estimated_partitions;
uint32_t m_max_estimated_partitions;
// If true, the ASTC HDR compressor is allowed to more aggressively vary weight indices for slightly higher compression in non-fastest mode. This will hurt BC6H quality, however.
bool m_allow_uber_mode;
astc_hdr_codec_options();
void init();
// TODO: set_quality_level() is preferred to configure the codec for transcoding purposes.
static const int cMinLevel = 0;
static const int cMaxLevel = 4;
static const int cDefaultLevel = 1;
void set_quality_level(int level);
private:
void set_quality_best();
void set_quality_normal();
void set_quality_fastest();
};
struct astc_hdr_pack_results
{
double m_best_block_error;
double m_bc6h_block_error; // note this is not used/set by the encoder, here for convienance
// Encoder results (logical ASTC block)
astc_helpers::log_astc_block m_best_blk;
// For statistical use
uint32_t m_best_submodes[2];
uint32_t m_best_pat_index;
bool m_constrained_weights;
bool m_improved_via_refinement_flag;
// Only valid if the block is solid
basist::astc_blk m_solid_blk;
// The BC6H transcoded block
basist::bc6h_block m_bc6h_block;
// Solid color/void extent flag
bool m_is_solid;
void clear()
{
m_best_block_error = 1e+30f;
m_bc6h_block_error = 1e+30f;
m_best_blk.clear();
m_best_blk.m_grid_width = 4;
m_best_blk.m_grid_height = 4;
m_best_blk.m_endpoint_ise_range = 20; // 0-255
clear_obj(m_best_submodes);
m_best_pat_index = 0;
m_constrained_weights = false;
clear_obj(m_bc6h_block);
m_is_solid = false;
m_improved_via_refinement_flag = false;
}
};
void interpolate_qlog12_colors(
const int e[2][3],
basist::half_float* pDecoded_half,
vec3F* pDecoded_float,
uint32_t n, uint32_t ise_weight_range);
bool get_astc_hdr_mode_11_block_colors(
const uint8_t* pEndpoints,
basist::half_float* pDecoded_half,
vec3F* pDecoded_float,
uint32_t n, uint32_t ise_weight_range, uint32_t ise_endpoint_range);
bool get_astc_hdr_mode_7_block_colors(
const uint8_t* pEndpoints,
basist::half_float* pDecoded_half,
vec3F* pDecoded_float,
uint32_t n, uint32_t ise_weight_range, uint32_t ise_endpoint_range);
double eval_selectors(
uint32_t num_pixels,
uint8_t* pWeights,
const basist::half_float* pBlock_pixels_half,
uint32_t num_weight_levels,
const basist::half_float* pDecoded_half,
const astc_hdr_codec_options& coptions,
uint32_t usable_selector_bitmask = UINT32_MAX);
double compute_block_error(const basist::half_float* pOrig_block, const basist::half_float* pPacked_block, const astc_hdr_codec_options& coptions);
// Encodes a 4x4 ASTC HDR block given a 4x4 array of source block pixels/texels.
// Supports solid color blocks, mode 11 (all submodes), mode 7/1 partition (all submodes),
// and mode 7/2 partitions (all submodes) - 30 patterns, only the ones also in common with the BC6H format.
// The packed ASTC weight grid dimensions are currently always 4x4 texels, but may be also 3x3 in the future.
// This function is thread safe, i.e. it may be called from multiple encoding threads simultanously with different blocks.
//
// Parameters:
// pRGBPixels - An array of 48 (16 RGB) floats: the 4x4 block to pack
// pPacked_block - A pointer to the packed ASTC HDR block
// coptions - Codec options
// pInternal_results - An optional pointer to details about how the block was packed, for statistics/debugging purposes. May be nullptr.
//
// Requirements:
// astc_hdr_enc_init() MUST have been called first to initialized the codec.
// Input pixels are checked and cannot be NaN's, Inf's, signed, or too large (greater than MAX_HALF_FLOAT, or 65504).
// Normal values and denormals are okay.
bool astc_hdr_enc_block(
const float* pRGBPixels,
const astc_hdr_codec_options& coptions,
basisu::vector<astc_hdr_pack_results> &all_results);
bool astc_hdr_pack_results_to_block(basist::astc_blk& dst_blk, const astc_hdr_pack_results& results);
bool astc_hdr_refine_weights(const basist::half_float* pSource_block, astc_hdr_pack_results& cur_results, const astc_hdr_codec_options& coptions, float bc6h_weight, bool* pImproved_flag);
struct astc_hdr_block_stats
{
std::mutex m_mutex;
uint32_t m_total_blocks;
uint32_t m_total_2part, m_total_solid;
uint32_t m_total_mode7_1part, m_total_mode7_2part;
uint32_t m_total_mode11_1part, m_total_mode11_2part;
uint32_t m_total_mode11_1part_constrained_weights;
uint32_t m_weight_range_hist_7[11];
uint32_t m_weight_range_hist_7_2part[11];
uint32_t m_mode7_submode_hist[6];
uint32_t m_weight_range_hist_11[11];
uint32_t m_weight_range_hist_11_2part[11];
uint32_t m_mode11_submode_hist[9];
uint32_t m_part_hist[32];
uint32_t m_total_refined;
astc_hdr_block_stats() { clear(); }
void clear()
{
std::lock_guard<std::mutex> lck(m_mutex);
m_total_blocks = 0;
m_total_mode7_1part = 0, m_total_mode7_2part = 0, m_total_mode11_1part = 0, m_total_2part = 0, m_total_solid = 0, m_total_mode11_2part = 0;
m_total_mode11_1part_constrained_weights = 0;
m_total_refined = 0;
clear_obj(m_weight_range_hist_11);
clear_obj(m_weight_range_hist_11_2part);
clear_obj(m_weight_range_hist_7);
clear_obj(m_weight_range_hist_7_2part);
clear_obj(m_mode7_submode_hist);
clear_obj(m_mode11_submode_hist);
clear_obj(m_part_hist);
}
void update(const astc_hdr_pack_results& log_blk);
void print();
};
} // namespace basisu

View File

@ -1,5 +1,5 @@
// basisu_backend.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_backend.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_basis_file.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_basis_file.h
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// File: basisu_bc7enc.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -394,6 +394,7 @@ void bc7enc_compress_block_init()
static void compute_least_squares_endpoints_rgba(uint32_t N, const uint8_t *pSelectors, const bc7enc_vec4F* pSelector_weights, bc7enc_vec4F* pXl, bc7enc_vec4F* pXh, const color_quad_u8 *pColors)
{
// Least squares using normal equations: http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf
// https://web.archive.org/web/20150319232457/http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf
// I did this in matrix form first, expanded out all the ops, then optimized it a bit.
double z00 = 0.0f, z01 = 0.0f, z10 = 0.0f, z11 = 0.0f;
double q00_r = 0.0f, q10_r = 0.0f, t_r = 0.0f;
@ -1301,6 +1302,7 @@ void check_best_overall_error(const color_cell_compressor_params *pParams, color
for (uint32_t c = 0; c < 4; c++)
colors[i].m_c[c] = (uint8_t)astc_interpolate_linear(colors[0].m_c[c], colors[n - 1].m_c[c], pParams->m_pSelector_weights[i]);
#ifdef _DEBUG
uint64_t total_err = 0;
for (uint32_t p = 0; p < pParams->m_num_pixels; p++)
{
@ -1313,6 +1315,7 @@ void check_best_overall_error(const color_cell_compressor_params *pParams, color
total_err += compute_color_distance_rgb(&orig, &packed, pParams->m_perceptual, pParams->m_weights);
}
assert(total_err == pResults->m_best_overall_err);
#endif
// HACK HACK
//if (total_err != pResults->m_best_overall_err)

View File

@ -1,5 +1,5 @@
// File: basisu_bc7enc.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
// basisu_comp.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -18,9 +18,10 @@
#include "basisu_basis_file.h"
#include "../transcoder/basisu_transcoder.h"
#include "basisu_uastc_enc.h"
#include "basisu_astc_hdr_enc.h"
#define BASISU_LIB_VERSION 116
#define BASISU_LIB_VERSION_STRING "1.16"
#define BASISU_LIB_VERSION 150
#define BASISU_LIB_VERSION_STRING "1.50"
#ifndef BASISD_SUPPORT_KTX2
#error BASISD_SUPPORT_KTX2 is undefined
@ -81,6 +82,8 @@ namespace basisu
m_basis_luma_601_psnr = 0.0f;
m_basis_luma_709_ssim = 0.0f;
m_basis_rgb_avg_bc6h_psnr = 0.0f;
m_bc7_rgb_avg_psnr = 0.0f;
m_bc7_rgba_avg_psnr = 0.0f;
m_bc7_a_avg_psnr = 0.0f;
@ -100,7 +103,7 @@ namespace basisu
uint32_t m_width;
uint32_t m_height;
// .basis compressed (ETC1S or UASTC statistics)
// .basis/.ktx2 compressed (LDR: ETC1S or UASTC statistics, HDR: transcoded BC6H statistics)
float m_basis_rgb_avg_psnr;
float m_basis_rgba_avg_psnr;
float m_basis_a_avg_psnr;
@ -108,7 +111,10 @@ namespace basisu
float m_basis_luma_601_psnr;
float m_basis_luma_709_ssim;
// BC7 statistics
// UASTC HDR only.
float m_basis_rgb_avg_bc6h_psnr;
// LDR: BC7 statistics
float m_bc7_rgb_avg_psnr;
float m_bc7_rgba_avg_psnr;
float m_bc7_a_avg_psnr;
@ -116,7 +122,7 @@ namespace basisu
float m_bc7_luma_601_psnr;
float m_bc7_luma_709_ssim;
// Highest achievable quality ETC1S statistics
// LDR: Highest achievable quality ETC1S statistics
float m_best_etc1s_rgb_avg_psnr;
float m_best_etc1s_luma_709_psnr;
float m_best_etc1s_luma_601_psnr;
@ -256,7 +262,7 @@ namespace basisu
m_no_selector_rdo.clear();
m_selector_rdo_thresh.clear();
m_read_source_images.clear();
m_write_output_basis_files.clear();
m_write_output_basis_or_ktx2_files.clear();
m_compression_level.clear();
m_compute_stats.clear();
m_print_stats.clear();
@ -317,27 +323,38 @@ namespace basisu
m_validate_output_data.clear();
m_hdr_ldr_srgb_to_linear_conversion.clear();
m_hdr_favor_astc.clear();
m_pJob_pool = nullptr;
}
// True to generate UASTC .basis file data, otherwise ETC1S.
// True to generate UASTC .basis/.KTX2 file data, otherwise ETC1S.
bool_param<false> m_uastc;
// Set m_hdr to true to switch to UASTC HDR mode.
bool_param<false> m_hdr;
bool_param<false> m_use_opencl;
// If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG images to read.
// Otherwise, the compressor processes the images in m_source_images.
// If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG etc. images to read.
// Otherwise, the compressor processes the images in m_source_images or m_source_images_hdr.
basisu::vector<std::string> m_source_filenames;
basisu::vector<std::string> m_source_alpha_filenames;
basisu::vector<image> m_source_images;
basisu::vector<imagef> m_source_images_hdr;
// Stores mipmaps starting from level 1. Level 0 is still stored in m_source_images, as usual.
// If m_source_mipmaps isn't empty, automatic mipmap generation isn't done. m_source_mipmaps.size() MUST equal m_source_images.size() or the compressor returns an error.
// The compressor applies the user-provided swizzling (in m_swizzle) to these images.
basisu::vector< basisu::vector<image> > m_source_mipmap_images;
basisu::vector< basisu::vector<imagef> > m_source_mipmap_images_hdr;
// Filename of the output basis file
// Filename of the output basis/ktx2 file
std::string m_out_filename;
// The params are done this way so we can detect when the user has explictly changed them.
@ -373,8 +390,8 @@ namespace basisu
// Read source images from m_source_filenames/m_source_alpha_filenames
bool_param<false> m_read_source_images;
// Write the output basis file to disk using m_out_filename
bool_param<false> m_write_output_basis_files;
// Write the output basis/ktx2 file to disk using m_out_filename
bool_param<false> m_write_output_basis_or_ktx2_files;
// Compute and display image metrics
bool_param<false> m_compute_stats;
@ -382,15 +399,15 @@ namespace basisu
// Print stats to stdout, if m_compute_stats is true.
bool_param<true> m_print_stats;
// Check to see if any input image has an alpha channel, if so then the output basis file will have alpha channels
// Check to see if any input image has an alpha channel, if so then the output basis/ktx2 file will have alpha channels
bool_param<true> m_check_for_alpha;
// Always put alpha slices in the output basis file, even when the input doesn't have alpha
// Always put alpha slices in the output basis/ktx2 file, even when the input doesn't have alpha
bool_param<false> m_force_alpha;
bool_param<true> m_multithreading;
// Split the R channel to RGB and the G channel to alpha, then write a basis file with alpha channels
char m_swizzle[4];
// Split the R channel to RGB and the G channel to alpha, then write a basis/ktx2 file with alpha channels
uint8_t m_swizzle[4];
bool_param<false> m_renormalize;
@ -448,8 +465,17 @@ namespace basisu
param<int> m_ktx2_zstd_supercompression_level;
bool_param<false> m_ktx2_srgb_transfer_func;
astc_hdr_codec_options m_uastc_hdr_options;
bool_param<false> m_validate_output_data;
// If true, LDR images (such as PNG) will be converted to normalized [0,1] linear light (via a sRGB->Linear conversion) and then processed as HDR.
// Otherwise, LDR images will be processed as HDR as-is.
bool_param<true> m_hdr_ldr_srgb_to_linear_conversion;
// If true, ASTC HDR quality is favored more than BC6H quality. Otherwise it's a rough balance.
bool_param<false> m_hdr_favor_astc;
job_pool *m_pJob_pool;
};
@ -504,6 +530,7 @@ namespace basisu
opencl_context_ptr m_pOpenCL_context;
basisu::vector<image> m_slice_images;
basisu::vector<imagef> m_slice_images_hdr;
basisu::vector<image_stats> m_stats;
@ -515,7 +542,9 @@ namespace basisu
uint32_t m_total_blocks;
basisu_frontend m_frontend;
pixel_block_vec m_source_blocks;
pixel_block_hdr_vec m_source_blocks_hdr;
basisu::vector<gpu_image> m_frontend_output_textures;
@ -526,11 +555,17 @@ namespace basisu
basisu_file m_basis_file;
basisu::vector<gpu_image> m_decoded_output_textures;
basisu::vector<gpu_image> m_decoded_output_textures; // BC6H in HDR mode
basisu::vector<image> m_decoded_output_textures_unpacked;
basisu::vector<gpu_image> m_decoded_output_textures_bc7;
basisu::vector<image> m_decoded_output_textures_unpacked_bc7;
basisu::vector<imagef> m_decoded_output_textures_bc6h_hdr_unpacked; // BC6H in HDR mode
basisu::vector<gpu_image> m_decoded_output_textures_astc_hdr;
basisu::vector<imagef> m_decoded_output_textures_astc_hdr_unpacked;
uint8_vec m_output_basis_file;
uint8_vec m_output_ktx2_file;
@ -541,14 +576,21 @@ namespace basisu
bool m_opencl_failed;
void check_for_hdr_inputs();
bool sanity_check_input_params();
void clean_hdr_image(imagef& src_img);
bool read_dds_source_images();
bool read_source_images();
bool extract_source_blocks();
bool process_frontend();
bool extract_frontend_texture_data();
bool process_backend();
bool create_basis_file_and_transcode();
bool write_hdr_debug_images(const char* pBasename, const imagef& img, uint32_t width, uint32_t height);
bool write_output_files_and_compute_stats();
error_code encode_slices_to_uastc_hdr();
error_code encode_slices_to_uastc();
bool generate_mipmaps(const imagef& img, basisu::vector<imagef>& mips, bool has_alpha);
bool generate_mipmaps(const image &img, basisu::vector<image> &mips, bool has_alpha);
bool validate_texture_type_constraints();
bool validate_ktx2_constraints();
@ -568,7 +610,8 @@ namespace basisu
//
// flags_and_quality: Combination of the above flags logically OR'd with the ETC1S or UASTC level, i.e. "cFlagSRGB | cFlagGenMipsClamp | cFlagThreaded | 128" or "cFlagSRGB | cFlagGenMipsClamp | cFlagUASTC | cFlagThreaded | cPackUASTCLevelDefault".
// In ETC1S mode, the lower 8-bits are the ETC1S quality level which ranges from [1,255] (higher=better quality/larger files)
// In UASTC mode, the lower 8-bits are the UASTC pack level (see cPackUASTCLevelFastest, etc.). Fastest/lowest quality is 0, so be sure to set it correctly.
// In UASTC mode, the lower 8-bits are the UASTC LDR/HDR pack level (see cPackUASTCLevelFastest, etc.). Fastest/lowest quality is 0, so be sure to set it correctly. Valid values are [0,4] for both LDR/HDR.
// In UASTC mode, be sure to set this, otherwise it defaults to 0 (fastest/lowest quality).
//
// uastc_rdo_quality: Float UASTC RDO quality level (0=no change, higher values lower quality but increase compressibility, initially try .5-1.5)
//
@ -594,20 +637,36 @@ namespace basisu
cFlagUASTCRDO = 1 << 18, // use RDO postprocessing when generating UASTC files (must set uastc_rdo_quality to the quality scalar)
cFlagPrintStats = 1 << 19, // print image stats to stdout
cFlagPrintStatus = 1 << 20 // print status to stdout
cFlagPrintStatus = 1 << 20, // print status to stdout
cFlagHDR = 1 << 21, // Force encoder into HDR mode, even if source image is LDR.
cFlagHDRLDRImageSRGBToLinearConversion = 1 << 22, // In HDR mode, convert LDR source images to linear before encoding.
cFlagDebugImages = 1 << 23 // enable status output
};
// This function accepts an array of source images.
// If more than one image is provided, it's assumed the images form a mipmap pyramid and automatic mipmap generation is disabled.
// Returns a pointer to the compressed .basis or .ktx2 file data. *pSize is the size of the compressed data. The returned block must be freed using basis_free_data().
// Returns a pointer to the compressed .basis or .ktx2 file data. *pSize is the size of the compressed data.
// Important: The returned block MUST be manually freed using basis_free_data().
// basisu_encoder_init() MUST be called first!
// LDR version. To compress the LDR source image as HDR: Use the cFlagHDR flag.
void* basis_compress(
const basisu::vector<image> &source_images,
uint32_t flags_and_quality, float uastc_rdo_quality,
size_t* pSize,
image_stats* pStats = nullptr);
// This function only accepts a single source image.
// HDR-only version.
// Important: The returned block MUST be manually freed using basis_free_data().
void* basis_compress(
const basisu::vector<imagef>& source_images_hdr,
uint32_t flags_and_quality,
size_t* pSize,
image_stats* pStats = nullptr);
// This function only accepts a single LDR source image. It's just a wrapper for basis_compress() above.
// Important: The returned block MUST be manually freed using basis_free_data().
void* basis_compress(
const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
uint32_t flags_and_quality, float uastc_rdo_quality,
@ -615,6 +674,7 @@ namespace basisu
image_stats* pStats = nullptr);
// Frees the dynamically allocated file data returned by basis_compress().
// This MUST be called on the pointer returned by basis_compress() when you're done with it.
void basis_free_data(void* p);
// Runs a short benchmark using synthetic image data to time OpenCL encoding vs. CPU encoding, with multithreading enabled.

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
// basisu_enc.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -48,7 +48,8 @@ namespace basisu
// Encoder library initialization.
// This function MUST be called before encoding anything!
void basisu_encoder_init(bool use_opencl = false, bool opencl_force_serialization = false);
// Returns false if library initialization fails.
bool basisu_encoder_init(bool use_opencl = false, bool opencl_force_serialization = false);
void basisu_encoder_deinit();
// basisu_kernels_sse.cpp - will be a no-op and g_cpu_supports_sse41 will always be false unless compiled with BASISU_SUPPORT_SSE=1
@ -70,6 +71,18 @@ namespace basisu
return (uint8_t)((i & 0xFFFFFF00U) ? (~(i >> 31)) : i);
}
inline int left_shift32(int val, int shift)
{
assert((shift >= 0) && (shift < 32));
return static_cast<int>(static_cast<uint32_t>(val) << shift);
}
inline uint32_t left_shift32(uint32_t val, int shift)
{
assert((shift >= 0) && (shift < 32));
return val << shift;
}
inline int32_t clampi(int32_t value, int32_t low, int32_t high)
{
if (value < low)
@ -130,6 +143,31 @@ namespace basisu
return bits;
}
// Open interval
inline int bounds_check(int v, int l, int h) { (void)v; (void)l; (void)h; assert(v >= l && v < h); return v; }
inline uint32_t bounds_check(uint32_t v, uint32_t l, uint32_t h) { (void)v; (void)l; (void)h; assert(v >= l && v < h); return v; }
// Closed interval
inline int bounds_check_incl(int v, int l, int h) { (void)v; (void)l; (void)h; assert(v >= l && v <= h); return v; }
inline uint32_t bounds_check_incl(uint32_t v, uint32_t l, uint32_t h) { (void)v; (void)l; (void)h; assert(v >= l && v <= h); return v; }
inline uint32_t clz(uint32_t x)
{
if (!x)
return 32;
uint32_t n = 0;
while ((x & 0x80000000) == 0)
{
x <<= 1u;
n++;
}
return n;
}
bool string_begins_with(const std::string& str, const char* pPhrase);
// Hashing
@ -268,6 +306,7 @@ namespace basisu
public:
enum { num_elements = N };
typedef T scalar_type;
inline vec() { }
inline vec(eZero) { set_zero(); }
@ -291,6 +330,7 @@ namespace basisu
inline bool operator<(const vec &rhs) const { for (uint32_t i = 0; i < N; i++) { if (m_v[i] < rhs.m_v[i]) return true; else if (m_v[i] != rhs.m_v[i]) return false; } return false; }
inline void set_zero() { for (uint32_t i = 0; i < N; i++) m_v[i] = 0; }
inline void clear() { set_zero(); }
template <uint32_t OtherN, typename OtherT>
inline vec &set(const vec<OtherN, OtherT> &other)
@ -391,7 +431,7 @@ namespace basisu
inline T distance(const vec &other) const { return static_cast<T>(sqrt(squared_distance(other))); }
inline double distance_d(const vec& other) const { return sqrt(squared_distance_d(other)); }
inline vec &normalize_in_place() { T len = length(); if (len != 0.0f) *this *= (1.0f / len); return *this; }
inline vec &normalize_in_place() { T len = length(); if (len != 0.0f) *this *= (1.0f / len); return *this; }
inline vec &clamp(T l, T h)
{
@ -722,7 +762,7 @@ namespace basisu
void job_thread(uint32_t index);
};
// Simple 32-bit color class
// Simple 64-bit color class
class color_rgba_i16
{
@ -1116,7 +1156,9 @@ namespace basisu
{
std::string result(s);
for (size_t i = 0; i < result.size(); i++)
result[i] = (char)tolower((int)result[i]);
{
result[i] = (char)tolower((uint8_t)(result[i]));
}
return result;
}
@ -1408,7 +1450,7 @@ namespace basisu
size_t get_total_training_vecs() const { return m_training_vecs.size(); }
const array_of_weighted_training_vecs &get_training_vecs() const { return m_training_vecs; }
array_of_weighted_training_vecs &get_training_vecs() { return m_training_vecs; }
array_of_weighted_training_vecs &get_training_vecs() { return m_training_vecs; }
void retrieve(basisu::vector< basisu::vector<uint32_t> > &codebook) const
{
@ -1437,36 +1479,36 @@ namespace basisu
}
void retrieve(uint32_t max_clusters, basisu::vector<uint_vec> &codebook) const
{
{
uint_vec node_stack;
node_stack.reserve(512);
node_stack.reserve(512);
codebook.resize(0);
codebook.reserve(max_clusters);
codebook.resize(0);
codebook.reserve(max_clusters);
uint32_t node_index = 0;
uint32_t node_index = 0;
while (true)
{
const tsvq_node& cur = m_nodes[node_index];
while (true)
{
const tsvq_node& cur = m_nodes[node_index];
if (cur.is_leaf() || ((2 + cur.m_codebook_index) > (int)max_clusters))
{
codebook.resize(codebook.size() + 1);
codebook.back() = cur.m_training_vecs;
if (cur.is_leaf() || ((2 + cur.m_codebook_index) > (int)max_clusters))
{
codebook.resize(codebook.size() + 1);
codebook.back() = cur.m_training_vecs;
if (node_stack.empty())
break;
if (node_stack.empty())
break;
node_index = node_stack.back();
node_stack.pop_back();
continue;
}
node_index = node_stack.back();
node_stack.pop_back();
continue;
}
node_stack.push_back(cur.m_right_index);
node_index = cur.m_left_index;
}
}
node_stack.push_back(cur.m_right_index);
node_index = cur.m_left_index;
}
}
bool generate(uint32_t max_size)
{
@ -2319,6 +2361,14 @@ namespace basisu
m_total_bits = 0;
}
inline void restart()
{
m_bytes.resize(0);
m_bit_buffer = 0;
m_bit_buffer_size = 0;
m_total_bits = 0;
}
inline const uint8_vec &get_bytes() const { return m_bytes; }
inline uint64_t get_total_bits() const { return m_total_bits; }
@ -2920,11 +2970,11 @@ namespace basisu
inline const color_rgba *get_ptr() const { return &m_pixels[0]; }
inline color_rgba *get_ptr() { return &m_pixels[0]; }
bool has_alpha() const
bool has_alpha(uint32_t channel = 3) const
{
for (uint32_t y = 0; y < m_height; ++y)
for (uint32_t x = 0; x < m_width; ++x)
if ((*this)(x, y).a < 255)
if ((*this)(x, y)[channel] < 255)
return true;
return false;
@ -3130,6 +3180,31 @@ namespace basisu
return *this;
}
imagef& crop_dup_borders(uint32_t w, uint32_t h)
{
const uint32_t orig_w = m_width, orig_h = m_height;
crop(w, h);
if (orig_w && orig_h)
{
if (m_width > orig_w)
{
for (uint32_t x = orig_w; x < m_width; x++)
for (uint32_t y = 0; y < m_height; y++)
set_clipped(x, y, get_clamped(minimum(x, orig_w - 1U), minimum(y, orig_h - 1U)));
}
if (m_height > orig_h)
{
for (uint32_t y = orig_h; y < m_height; y++)
for (uint32_t x = 0; x < m_width; x++)
set_clipped(x, y, get_clamped(minimum(x, orig_w - 1U), minimum(y, orig_h - 1U)));
}
}
return *this;
}
inline const vec4F &operator() (uint32_t x, uint32_t y) const { assert(x < m_width && y < m_height); return m_pixels[x + y * m_pitch]; }
inline vec4F &operator() (uint32_t x, uint32_t y) { assert(x < m_width && y < m_height); return m_pixels[x + y * m_pitch]; }
@ -3213,19 +3288,128 @@ namespace basisu
inline const vec4F *get_ptr() const { return &m_pixels[0]; }
inline vec4F *get_ptr() { return &m_pixels[0]; }
bool clean_astc_hdr_pixels(float highest_mag)
{
bool status = true;
bool nan_msg = false;
bool inf_msg = false;
bool neg_zero_msg = false;
bool neg_msg = false;
bool clamp_msg = false;
for (uint32_t iy = 0; iy < m_height; iy++)
{
for (uint32_t ix = 0; ix < m_width; ix++)
{
vec4F& c = (*this)(ix, iy);
for (uint32_t s = 0; s < 4; s++)
{
float &p = c[s];
union { float f; uint32_t u; } x; x.f = p;
if ((std::isnan(p)) || (std::isinf(p)) || (x.u == 0x80000000))
{
if (std::isnan(p))
{
if (!nan_msg)
{
fprintf(stderr, "One or more pixels was NaN, setting to 0.\n");
nan_msg = true;
}
}
if (std::isinf(p))
{
if (!inf_msg)
{
fprintf(stderr, "One or more pixels was INF, setting to 0.\n");
inf_msg = true;
}
}
if (x.u == 0x80000000)
{
if (!neg_zero_msg)
{
fprintf(stderr, "One or more pixels was -0, setting them to 0.\n");
neg_zero_msg = true;
}
}
p = 0.0f;
status = false;
}
else
{
//const float o = p;
if (p < 0.0f)
{
p = 0.0f;
if (!neg_msg)
{
fprintf(stderr, "One or more pixels was negative -- setting these pixel components to 0 because ASTC HDR doesn't support signed values.\n");
neg_msg = true;
}
status = false;
}
if (p > highest_mag)
{
p = highest_mag;
if (!clamp_msg)
{
fprintf(stderr, "One or more pixels had to be clamped to %f.\n", highest_mag);
clamp_msg = true;
}
status = false;
}
}
}
}
}
return status;
}
imagef& flip_y()
{
for (uint32_t y = 0; y < m_height / 2; ++y)
for (uint32_t x = 0; x < m_width; ++x)
std::swap((*this)(x, y), (*this)(x, m_height - 1 - y));
return *this;
}
private:
uint32_t m_width, m_height, m_pitch; // all in pixels
vec4F_vec m_pixels;
};
// REC 709 coefficients
const float REC_709_R = 0.212656f, REC_709_G = 0.715158f, REC_709_B = 0.072186f;
inline float get_luminance(const vec4F &c)
{
return c[0] * REC_709_R + c[1] * REC_709_G + c[2] * REC_709_B;
}
float linear_to_srgb(float l);
float srgb_to_linear(float s);
// Image metrics
class image_metrics
{
public:
// TODO: Add ssim
float m_max, m_mean, m_mean_squared, m_rms, m_psnr, m_ssim;
double m_max, m_mean, m_mean_squared, m_rms, m_psnr, m_ssim;
bool m_has_neg, m_hf_mag_overflow, m_any_abnormal;
image_metrics()
{
@ -3240,10 +3424,17 @@ namespace basisu
m_rms = 0;
m_psnr = 0;
m_ssim = 0;
m_has_neg = false;
m_hf_mag_overflow = false;
m_any_abnormal = false;
}
void print(const char *pPrefix = nullptr) { printf("%sMax: %3.0f Mean: %3.3f RMS: %3.3f PSNR: %2.3f dB\n", pPrefix ? pPrefix : "", m_max, m_mean, m_rms, m_psnr); }
void print(const char *pPrefix = nullptr) { printf("%sMax: %3.3f Mean: %3.3f RMS: %3.3f PSNR: %2.3f dB\n", pPrefix ? pPrefix : "", m_max, m_mean, m_rms, m_psnr); }
void print_hp(const char* pPrefix = nullptr) { printf("%sMax: %3.6f Mean: %3.6f RMS: %3.6f PSNR: %2.6f dB, Any Neg: %u, Half float overflow: %u, Any NaN/Inf: %u\n", pPrefix ? pPrefix : "", m_max, m_mean, m_rms, m_psnr, m_has_neg, m_hf_mag_overflow, m_any_abnormal); }
void calc(const imagef& a, const imagef& b, uint32_t first_chan = 0, uint32_t total_chans = 0, bool avg_comp_error = true, bool log = false);
void calc_half(const imagef& a, const imagef& b, uint32_t first_chan, uint32_t total_chans, bool avg_comp_error);
void calc_half2(const imagef& a, const imagef& b, uint32_t first_chan, uint32_t total_chans, bool avg_comp_error);
void calc(const image &a, const image &b, uint32_t first_chan = 0, uint32_t total_chans = 0, bool avg_comp_error = true, bool use_601_luma = false);
};
@ -3256,6 +3447,8 @@ namespace basisu
bool load_tga(const char* pFilename, image& img);
inline bool load_tga(const std::string &filename, image &img) { return load_tga(filename.c_str(), img); }
bool load_qoi(const char* pFilename, image& img);
bool load_jpg(const char *pFilename, image& img);
inline bool load_jpg(const std::string &filename, image &img) { return load_jpg(filename.c_str(), img); }
@ -3263,9 +3456,64 @@ namespace basisu
bool load_image(const char* pFilename, image& img);
inline bool load_image(const std::string &filename, image &img) { return load_image(filename.c_str(), img); }
// Supports .HDR and most (but not all) .EXR's (see TinyEXR).
bool load_image_hdr(const char* pFilename, imagef& img, bool ldr_srgb_to_linear = true);
inline bool load_image_hdr(const std::string& filename, imagef& img, bool ldr_srgb_to_linear = true) { return load_image_hdr(filename.c_str(), img, ldr_srgb_to_linear); }
enum class hdr_image_type
{
cHITRGBAHalfFloat = 0,
cHITRGBAFloat = 1,
cHITPNGImage = 2,
cHITEXRImage = 3,
cHITHDRImage = 4
};
bool load_image_hdr(const void* pMem, size_t mem_size, imagef& img, uint32_t width, uint32_t height, hdr_image_type img_type, bool ldr_srgb_to_linear);
uint8_t *read_tga(const uint8_t *pBuf, uint32_t buf_size, int &width, int &height, int &n_chans);
uint8_t *read_tga(const char *pFilename, int &width, int &height, int &n_chans);
struct rgbe_header_info
{
std::string m_program;
// Note no validation is done, either gamma or exposure may be 0.
double m_gamma;
bool m_has_gamma;
double m_exposure; // watts/steradian/m^2.
bool m_has_exposure;
void clear()
{
m_program.clear();
m_gamma = 1.0f;
m_has_gamma = false;
m_exposure = 1.0f;
m_has_exposure = false;
}
};
bool read_rgbe(const uint8_vec& filedata, imagef& img, rgbe_header_info& hdr_info);
bool read_rgbe(const char* pFilename, imagef& img, rgbe_header_info &hdr_info);
bool write_rgbe(uint8_vec& file_data, imagef& img, rgbe_header_info& hdr_info);
bool write_rgbe(const char* pFilename, imagef& img, rgbe_header_info& hdr_info);
bool read_exr(const char* pFilename, imagef& img, int& n_chans);
bool read_exr(const void* pMem, size_t mem_size, imagef& img);
enum
{
WRITE_EXR_LINEAR_HINT = 1, // hint for lossy comp. methods: exr_perceptual_treatment_t, logarithmic or linear, defaults to logarithmic
WRITE_EXR_STORE_FLOATS = 2, // use 32-bit floats, otherwise it uses half floats
WRITE_EXR_NO_COMPRESSION = 4 // no compression, otherwise it uses ZIP compression (16 scanlines per block)
};
// Supports 1 (Y), 3 (RGB), or 4 (RGBA) channel images.
bool write_exr(const char* pFilename, imagef& img, uint32_t n_chans, uint32_t flags);
enum
{
cImageSaveGrayscale = 1,
@ -3276,19 +3524,22 @@ namespace basisu
inline bool save_png(const std::string &filename, const image &img, uint32_t image_save_flags = 0, uint32_t grayscale_comp = 0) { return save_png(filename.c_str(), img, image_save_flags, grayscale_comp); }
bool read_file_to_vec(const char* pFilename, uint8_vec& data);
bool read_file_to_data(const char* pFilename, void *pData, size_t len);
bool write_data_to_file(const char* pFilename, const void* pData, size_t len);
inline bool write_vec_to_file(const char* pFilename, const uint8_vec& v) { return v.size() ? write_data_to_file(pFilename, &v[0], v.size()) : write_data_to_file(pFilename, "", 0); }
float linear_to_srgb(float l);
float srgb_to_linear(float s);
bool image_resample(const image &src, image &dst, bool srgb = false,
const char *pFilter = "lanczos4", float filter_scale = 1.0f,
bool wrapping = false,
uint32_t first_comp = 0, uint32_t num_comps = 4);
bool image_resample(const imagef& src, imagef& dst,
const char* pFilter = "lanczos4", float filter_scale = 1.0f,
bool wrapping = false,
uint32_t first_comp = 0, uint32_t num_comps = 4);
// Timing
typedef uint64_t timer_ticks;
@ -3319,6 +3570,8 @@ namespace basisu
bool m_started, m_stopped;
};
inline double get_interval_timer() { return interval_timer::ticks_to_secs(interval_timer::get_ticks()); }
// 2D array
template<typename T>
@ -3372,8 +3625,8 @@ namespace basisu
inline const T &operator[] (uint32_t i) const { return m_values[i]; }
inline T &operator[] (uint32_t i) { return m_values[i]; }
inline const T &at_clamped(int x, int y) const { return (*this)(clamp<int>(x, 0, m_width), clamp<int>(y, 0, m_height)); }
inline T &at_clamped(int x, int y) { return (*this)(clamp<int>(x, 0, m_width), clamp<int>(y, 0, m_height)); }
inline const T &at_clamped(int x, int y) const { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); }
inline T &at_clamped(int x, int y) { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); }
void clear()
{
@ -3450,7 +3703,327 @@ namespace basisu
}
};
typedef basisu::vector<pixel_block> pixel_block_vec;
struct pixel_block_hdr
{
vec4F m_pixels[cPixelBlockHeight][cPixelBlockWidth]; // [y][x]
inline const vec4F& operator() (uint32_t x, uint32_t y) const { assert((x < cPixelBlockWidth) && (y < cPixelBlockHeight)); return m_pixels[y][x]; }
inline vec4F& operator() (uint32_t x, uint32_t y) { assert((x < cPixelBlockWidth) && (y < cPixelBlockHeight)); return m_pixels[y][x]; }
inline const vec4F* get_ptr() const { return &m_pixels[0][0]; }
inline vec4F* get_ptr() { return &m_pixels[0][0]; }
inline void clear() { clear_obj(*this); }
inline bool operator== (const pixel_block& rhs) const
{
return memcmp(m_pixels, rhs.m_pixels, sizeof(m_pixels)) == 0;
}
};
typedef basisu::vector<pixel_block_hdr> pixel_block_hdr_vec;
void tonemap_image_reinhard(image& ldr_img, const imagef& hdr_img, float exposure);
bool tonemap_image_compressive(image& dst_img, const imagef& hdr_test_img);
// Intersection
enum eClear { cClear = 0 };
enum eInitExpand { cInitExpand = 0 };
template<typename vector_type>
class ray
{
public:
typedef vector_type vector_t;
typedef typename vector_type::scalar_type scalar_type;
inline ray() { }
inline ray(eClear) { clear(); }
inline ray(const vector_type& origin, const vector_type& direction) : m_origin(origin), m_direction(direction) { }
inline void clear()
{
m_origin.clear();
m_direction.clear();
}
inline const vector_type& get_origin(void) const { return m_origin; }
inline void set_origin(const vector_type& origin) { m_origin = origin; }
inline const vector_type& get_direction(void) const { return m_direction; }
inline void set_direction(const vector_type& direction) { m_direction = direction; }
inline void set_endpoints(const vector_type& start, const vector_type& end)
{
m_origin = start;
m_direction = end - start;
m_direction.normalize_in_place();
}
inline vector_type eval(scalar_type t) const
{
return m_origin + m_direction * t;
}
private:
vector_type m_origin;
vector_type m_direction;
};
typedef ray<vec2F> ray2F;
typedef ray<vec3F> ray3F;
template<typename T>
class vec_interval
{
public:
enum { N = T::num_elements };
typedef typename T::scalar_type scalar_type;
inline vec_interval(const T& v) { m_bounds[0] = v; m_bounds[1] = v; }
inline vec_interval(const T& low, const T& high) { m_bounds[0] = low; m_bounds[1] = high; }
inline vec_interval() { }
inline vec_interval(eClear) { clear(); }
inline vec_interval(eInitExpand) { init_expand(); }
inline void clear() { m_bounds[0].clear(); m_bounds[1].clear(); }
inline void init_expand()
{
m_bounds[0].set(1e+30f, 1e+30f, 1e+30f);
m_bounds[1].set(-1e+30f, -1e+30f, -1e+30f);
}
inline vec_interval expand(const T& p)
{
for (uint32_t c = 0; c < N; c++)
{
if (p[c] < m_bounds[0][c])
m_bounds[0][c] = p[c];
if (p[c] > m_bounds[1][c])
m_bounds[1][c] = p[c];
}
return *this;
}
inline const T& operator[] (uint32_t i) const { assert(i < 2); return m_bounds[i]; }
inline T& operator[] (uint32_t i) { assert(i < 2); return m_bounds[i]; }
const T& get_low() const { return m_bounds[0]; }
T& get_low() { return m_bounds[0]; }
const T& get_high() const { return m_bounds[1]; }
T& get_high() { return m_bounds[1]; }
scalar_type get_dim(uint32_t axis) const { return m_bounds[1][axis] - m_bounds[0][axis]; }
bool contains(const T& p) const
{
const T& low = get_low(), high = get_high();
for (uint32_t i = 0; i < N; i++)
{
if (p[i] < low[i])
return false;
if (p[i] > high[i])
return false;
}
return true;
}
private:
T m_bounds[2];
};
typedef vec_interval<vec1F> vec_interval1F;
typedef vec_interval<vec2F> vec_interval2F;
typedef vec_interval<vec3F> vec_interval3F;
typedef vec_interval<vec4F> vec_interval4F;
typedef vec_interval2F aabb2F;
typedef vec_interval3F aabb3F;
namespace intersection
{
enum result
{
cBackfacing = -1,
cFailure = 0,
cSuccess,
cParallel,
cInside,
};
// Returns cInside, cSuccess, or cFailure.
// Algorithm: Graphics Gems 1
template<typename vector_type, typename scalar_type, typename ray_type, typename aabb_type>
result ray_aabb(vector_type& coord, scalar_type& t, const ray_type& ray, const aabb_type& box)
{
enum
{
cNumDim = vector_type::num_elements,
cRight = 0,
cLeft = 1,
cMiddle = 2
};
bool inside = true;
int quadrant[cNumDim];
scalar_type candidate_plane[cNumDim];
for (int i = 0; i < cNumDim; i++)
{
if (ray.get_origin()[i] < box[0][i])
{
quadrant[i] = cLeft;
candidate_plane[i] = box[0][i];
inside = false;
}
else if (ray.get_origin()[i] > box[1][i])
{
quadrant[i] = cRight;
candidate_plane[i] = box[1][i];
inside = false;
}
else
{
quadrant[i] = cMiddle;
}
}
if (inside)
{
coord = ray.get_origin();
t = 0.0f;
return cInside;
}
scalar_type max_t[cNumDim];
for (int i = 0; i < cNumDim; i++)
{
if ((quadrant[i] != cMiddle) && (ray.get_direction()[i] != 0.0f))
max_t[i] = (candidate_plane[i] - ray.get_origin()[i]) / ray.get_direction()[i];
else
max_t[i] = -1.0f;
}
int which_plane = 0;
for (int i = 1; i < cNumDim; i++)
if (max_t[which_plane] < max_t[i])
which_plane = i;
if (max_t[which_plane] < 0.0f)
return cFailure;
for (int i = 0; i < cNumDim; i++)
{
if (i != which_plane)
{
coord[i] = ray.get_origin()[i] + max_t[which_plane] * ray.get_direction()[i];
if ((coord[i] < box[0][i]) || (coord[i] > box[1][i]))
return cFailure;
}
else
{
coord[i] = candidate_plane[i];
}
assert(coord[i] >= box[0][i] && coord[i] <= box[1][i]);
}
t = max_t[which_plane];
return cSuccess;
}
template<typename vector_type, typename scalar_type, typename ray_type, typename aabb_type>
result ray_aabb(bool& started_within, vector_type& coord, scalar_type& t, const ray_type& ray, const aabb_type& box)
{
if (!box.contains(ray.get_origin()))
{
started_within = false;
return ray_aabb(coord, t, ray, box);
}
started_within = true;
typename vector_type::T diag_dist = box.diagonal_length() * 1.5f;
ray_type outside_ray(ray.eval(diag_dist), -ray.get_direction());
result res(ray_aabb(coord, t, outside_ray, box));
if (res != cSuccess)
return res;
t = basisu::maximum(0.0f, diag_dist - t);
return cSuccess;
}
} // intersect
// This float->half conversion matches how "F32TO16" works on Intel GPU's.
// Input cannot be negative, Inf or Nan.
inline basist::half_float float_to_half_non_neg_no_nan_inf(float val)
{
union { float f; int32_t i; uint32_t u; } fi = { val };
const int flt_m = fi.i & 0x7FFFFF, flt_e = (fi.i >> 23) & 0xFF;
int e = 0, m = 0;
assert(((fi.i >> 31) == 0) && (flt_e != 0xFF));
// not zero or denormal
if (flt_e != 0)
{
int new_exp = flt_e - 127;
if (new_exp > 15)
e = 31;
else if (new_exp < -14)
m = lrintf((1 << 24) * fabsf(fi.f));
else
{
e = new_exp + 15;
m = lrintf(flt_m * (1.0f / ((float)(1 << 13))));
}
}
assert((0 <= m) && (m <= 1024));
if (m == 1024)
{
e++;
m = 0;
}
assert((e >= 0) && (e <= 31));
assert((m >= 0) && (m <= 1023));
basist::half_float result = (basist::half_float)((e << 10) | m);
return result;
}
// Supports positive and denormals only. No NaN or Inf.
inline float fast_half_to_float_pos_not_inf_or_nan(basist::half_float h)
{
assert(!basist::half_is_signed(h) && !basist::is_half_inf_or_nan(h));
union fu32
{
uint32_t u;
float f;
};
static const fu32 K = { 0x77800000 };
fu32 o;
o.u = h << 13;
o.f *= K.f;
return o.f;
}
} // namespace basisu

View File

@ -1,5 +1,5 @@
// basis_etc.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basis_etc.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_frontend.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -2347,6 +2347,7 @@ namespace basisu
continue;
uint64_t overall_best_err = 0;
(void)overall_best_err;
uint64_t total_err[4][4][4];
clear_obj(total_err);

View File

@ -1,5 +1,5 @@
// basisu_frontend.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_gpu_texture.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -15,13 +15,15 @@
#include "basisu_gpu_texture.h"
#include "basisu_enc.h"
#include "basisu_pvrtc1_4.h"
#if BASISU_USE_ASTC_DECOMPRESS
#include "basisu_astc_decomp.h"
#endif
#include "3rdparty/android_astc_decomp.h"
#include "basisu_bc7enc.h"
#include "../transcoder/basisu_astc_hdr_core.h"
namespace basisu
{
//------------------------------------------------------------------------------------------------
// ETC2 EAC
void unpack_etc2_eac(const void *pBlock_bits, color_rgba *pPixels)
{
static_assert(sizeof(eac_a8_block) == 8, "sizeof(eac_a8_block) == 8");
@ -56,6 +58,8 @@ namespace basisu
pPixels[15].a = clamp255(base + pTable[pBlock->get_selector(3, 3, selector_bits)] * mul);
}
//------------------------------------------------------------------------------------------------
// BC1
struct bc1_block
{
enum { cTotalEndpointBytes = 2, cTotalSelectorBytes = 4 };
@ -274,6 +278,9 @@ namespace basisu
return used_punchthrough;
}
//------------------------------------------------------------------------------------------------
// BC3-5
struct bc4_block
{
enum { cBC4SelectorBits = 3, cTotalSelectorBytes = 6, cMaxSelectorValues = 8 };
@ -372,7 +379,8 @@ namespace basisu
unpack_bc4(pBlock_bits, &pPixels[0].r, sizeof(color_rgba));
unpack_bc4((const uint8_t *)pBlock_bits + sizeof(bc4_block), &pPixels[0].g, sizeof(color_rgba));
}
//------------------------------------------------------------------------------------------------
// ATC isn't officially documented, so I'm assuming these references:
// http://www.guildsoftware.com/papers/2012.Converting.DXTC.to.ATC.pdf
// https://github.com/Triang3l/S3TConv/blob/master/s3tconv_atitc.c
@ -426,6 +434,7 @@ namespace basisu
}
}
//------------------------------------------------------------------------------------------------
// BC7 mode 0-7 decompression.
// Instead of one monster routine to unpack all the BC7 modes, we're lumping the 3 subset, 2 subset, 1 subset, and dual plane modes together into simple shared routines.
@ -742,6 +751,255 @@ namespace basisu
return false;
}
static inline int bc6h_sign_extend(int val, int bits)
{
assert((bits >= 1) && (bits < 32));
assert((val >= 0) && (val < (1 << bits)));
return (val << (32 - bits)) >> (32 - bits);
}
static inline int bc6h_apply_delta(int base, int delta, int num_bits, int is_signed)
{
int bitmask = ((1 << num_bits) - 1);
int v = (base + delta) & bitmask;
return is_signed ? bc6h_sign_extend(v, num_bits) : v;
}
static int bc6h_dequantize(int val, int bits, int is_signed)
{
int result;
if (is_signed)
{
if (bits >= 16)
result = val;
else
{
int s_flag = 0;
if (val < 0)
{
s_flag = 1;
val = -val;
}
if (val == 0)
result = 0;
else if (val >= ((1 << (bits - 1)) - 1))
result = 0x7FFF;
else
result = ((val << 15) + 0x4000) >> (bits - 1);
if (s_flag)
result = -result;
}
}
else
{
if (bits >= 15)
result = val;
else if (!val)
result = 0;
else if (val == ((1 << bits) - 1))
result = 0xFFFF;
else
result = ((val << 16) + 0x8000) >> bits;
}
return result;
}
static inline int bc6h_interpolate(int a, int b, const uint8_t* pWeights, int index)
{
return (a * (64 - (int)pWeights[index]) + b * (int)pWeights[index] + 32) >> 6;
}
static inline basist::half_float bc6h_convert_to_half(int val, int is_signed)
{
if (!is_signed)
{
// scale by 31/64
return (basist::half_float)((val * 31) >> 6);
}
// scale by 31/32
val = (val < 0) ? -(((-val) * 31) >> 5) : (val * 31) >> 5;
int s = 0;
if (val < 0)
{
s = 0x8000;
val = -val;
}
return (basist::half_float)(s | val);
}
static inline uint32_t bc6h_get_bits(uint32_t num_bits, uint64_t& l, uint64_t& h, uint32_t& total_bits)
{
assert((num_bits) && (num_bits <= 63));
uint32_t v = (uint32_t)(l & ((1U << num_bits) - 1U));
l >>= num_bits;
l |= (h << (64U - num_bits));
h >>= num_bits;
total_bits += num_bits;
assert(total_bits <= 128);
return v;
}
static inline uint32_t bc6h_reverse_bits(uint32_t v, uint32_t num_bits)
{
uint32_t res = 0;
for (uint32_t i = 0; i < num_bits; i++)
{
uint32_t bit = (v & (1u << i)) != 0u;
res |= (bit << (num_bits - 1u - i));
}
return res;
}
static inline uint64_t bc6h_read_le_qword(const void* p)
{
const uint8_t* pSrc = static_cast<const uint8_t*>(p);
return ((uint64_t)read_le_dword(pSrc)) | (((uint64_t)read_le_dword(pSrc + sizeof(uint32_t))) << 32U);
}
bool unpack_bc6h(const void* pSrc_block, void* pDst_block, bool is_signed, uint32_t dest_pitch_in_halfs)
{
assert(dest_pitch_in_halfs >= 4 * 3);
const uint32_t MAX_SUBSETS = 2, MAX_COMPS = 3;
const uint8_t* pSrc = static_cast<const uint8_t*>(pSrc_block);
basist::half_float* pDst = static_cast<basist::half_float*>(pDst_block);
uint64_t blo = bc6h_read_le_qword(pSrc), bhi = bc6h_read_le_qword(pSrc + sizeof(uint64_t));
// Unpack mode
const int mode = basist::g_bc6h_mode_lookup[blo & 31];
if (mode < 0)
{
for (int y = 0; y < 4; y++)
{
memset(pDst, 0, sizeof(basist::half_float) * 4);
pDst += dest_pitch_in_halfs;
}
return false;
}
// Skip mode bits
uint32_t total_bits_read = 0;
bc6h_get_bits((mode < 2) ? 2 : 5, blo, bhi, total_bits_read);
assert(mode < (int)basist::NUM_BC6H_MODES);
const uint32_t num_subsets = (mode >= 10) ? 1 : 2;
const bool is_mode_9_or_10 = (mode == 9) || (mode == 10);
// Unpack endpoint components
int comps[MAX_SUBSETS][MAX_COMPS][2] = { { { 0 } } }; // [subset][comp][l/h]
int part_index = 0;
uint32_t layout_index = 0;
while (layout_index < basist::MAX_BC6H_LAYOUT_INDEX)
{
const basist::bc6h_bit_layout& layout = basist::g_bc6h_bit_layouts[mode][layout_index];
if (layout.m_comp < 0)
break;
const int subset = layout.m_index >> 1, lh_index = layout.m_index & 1;
assert((layout.m_comp == 3) || ((subset >= 0) && (subset < (int)MAX_SUBSETS)));
const int last_bit = layout.m_last_bit, first_bit = layout.m_first_bit;
assert(last_bit >= 0);
int& res = (layout.m_comp == 3) ? part_index : comps[subset][layout.m_comp][lh_index];
if (first_bit < 0)
{
res |= (bc6h_get_bits(1, blo, bhi, total_bits_read) << last_bit);
}
else
{
const int total_bits = iabs(last_bit - first_bit) + 1;
const int bit_shift = basisu::minimum(first_bit, last_bit);
int b = bc6h_get_bits(total_bits, blo, bhi, total_bits_read);
if (last_bit < first_bit)
b = bc6h_reverse_bits(b, total_bits);
res |= (b << bit_shift);
}
layout_index++;
}
assert(layout_index != basist::MAX_BC6H_LAYOUT_INDEX);
// Sign extend/dequantize endpoints
const int num_sig_bits = basist::g_bc6h_mode_sig_bits[mode][0];
if (is_signed)
{
for (uint32_t comp = 0; comp < 3; comp++)
comps[0][comp][0] = bc6h_sign_extend(comps[0][comp][0], num_sig_bits);
}
if (is_signed || !is_mode_9_or_10)
{
for (uint32_t subset = 0; subset < num_subsets; subset++)
for (uint32_t comp = 0; comp < 3; comp++)
for (uint32_t lh = (subset ? 0 : 1); lh < 2; lh++)
comps[subset][comp][lh] = bc6h_sign_extend(comps[subset][comp][lh], basist::g_bc6h_mode_sig_bits[mode][1 + comp]);
}
if (!is_mode_9_or_10)
{
for (uint32_t subset = 0; subset < num_subsets; subset++)
for (uint32_t comp = 0; comp < 3; comp++)
for (uint32_t lh = (subset ? 0 : 1); lh < 2; lh++)
comps[subset][comp][lh] = bc6h_apply_delta(comps[0][comp][0], comps[subset][comp][lh], num_sig_bits, is_signed);
}
for (uint32_t subset = 0; subset < num_subsets; subset++)
for (uint32_t comp = 0; comp < 3; comp++)
for (uint32_t lh = 0; lh < 2; lh++)
comps[subset][comp][lh] = bc6h_dequantize(comps[subset][comp][lh], num_sig_bits, is_signed);
// Now unpack weights and output texels
const int weight_bits = (mode >= 10) ? 4 : 3;
const uint8_t* pWeights = (mode >= 10) ? basist::g_bc6h_weight4 : basist::g_bc6h_weight3;
dest_pitch_in_halfs -= 4 * 3;
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
int subset = (num_subsets == 1) ? ((x | y) ? 0 : 0x80) : basist::g_bc6h_2subset_patterns[part_index][y][x];
const int num_bits = weight_bits + ((subset & 0x80) ? -1 : 0);
subset &= 1;
const int weight_index = bc6h_get_bits(num_bits, blo, bhi, total_bits_read);
pDst[0] = bc6h_convert_to_half(bc6h_interpolate(comps[subset][0][0], comps[subset][0][1], pWeights, weight_index), is_signed);
pDst[1] = bc6h_convert_to_half(bc6h_interpolate(comps[subset][1][0], comps[subset][1][1], pWeights, weight_index), is_signed);
pDst[2] = bc6h_convert_to_half(bc6h_interpolate(comps[subset][2][0], comps[subset][2][1], pWeights, weight_index), is_signed);
pDst += 3;
}
pDst += dest_pitch_in_halfs;
}
assert(total_bits_read == 128);
return true;
}
//------------------------------------------------------------------------------------------------
// FXT1 (for fun, and because some modern Intel parts support it, and because a subset is like BC1)
struct fxt1_block
{
union
@ -901,6 +1159,9 @@ namespace basisu
return true;
}
//------------------------------------------------------------------------------------------------
// PVRTC2 (non-interpolated, hard_flag=1 modulation=0 subset only!)
struct pvrtc2_block
{
uint8_t m_modulation[4];
@ -1015,6 +1276,9 @@ namespace basisu
return true;
}
//------------------------------------------------------------------------------------------------
// ETC2 EAC R11 or RG11
struct etc2_eac_r11
{
uint64_t m_base : 8;
@ -1085,13 +1349,16 @@ namespace basisu
unpack_etc2_eac_r(pBlock, pPixels, c);
}
}
//------------------------------------------------------------------------------------------------
// UASTC
void unpack_uastc(const void* p, color_rgba* pPixels)
{
basist::unpack_uastc(*static_cast<const basist::uastc_block*>(p), (basist::color32 *)pPixels, false);
}
// Unpacks to RGBA, R, RG, or A
// Unpacks to RGBA, R, RG, or A. LDR GPU texture formats only.
bool unpack_block(texture_format fmt, const void* pBlock, color_rgba* pPixels)
{
switch (fmt)
@ -1150,14 +1417,24 @@ namespace basisu
unpack_etc2_eac(pBlock, pPixels);
break;
}
case texture_format::cASTC4x4:
case texture_format::cBC6HSigned:
case texture_format::cBC6HUnsigned:
case texture_format::cASTC_HDR_4x4:
case texture_format::cUASTC_HDR_4x4:
{
// Can't unpack HDR blocks in unpack_block() because it returns 32bpp pixel data.
assert(0);
return false;
}
case texture_format::cASTC_LDR_4x4:
{
#if BASISU_USE_ASTC_DECOMPRESS
const bool astc_srgb = false;
basisu_astc::astc::decompress(reinterpret_cast<uint8_t*>(pPixels), static_cast<const uint8_t*>(pBlock), astc_srgb, 4, 4);
#else
memset(pPixels, 255, 16 * sizeof(color_rgba));
#endif
bool status = basisu_astc::astc::decompress_ldr(reinterpret_cast<uint8_t*>(pPixels), static_cast<const uint8_t*>(pBlock), astc_srgb, 4, 4);
assert(status);
if (!status)
return false;
break;
}
case texture_format::cATC_RGB:
@ -1206,6 +1483,66 @@ namespace basisu
return true;
}
bool unpack_block_hdr(texture_format fmt, const void* pBlock, vec4F* pPixels)
{
switch (fmt)
{
case texture_format::cASTC_HDR_4x4:
case texture_format::cUASTC_HDR_4x4:
{
#if 1
bool status = basisu_astc::astc::decompress_hdr(&pPixels[0][0], (uint8_t*)pBlock, 4, 4);
assert(status);
if (!status)
return false;
#else
basist::half_float half_block[16][4];
astc_helpers::log_astc_block log_blk;
if (!astc_helpers::unpack_block(pBlock, log_blk, 4, 4))
return false;
if (!astc_helpers::decode_block(log_blk, half_block, 4, 4, astc_helpers::cDecodeModeHDR16))
return false;
for (uint32_t p = 0; p < 16; p++)
{
pPixels[p][0] = basist::half_to_float(half_block[p][0]);
pPixels[p][1] = basist::half_to_float(half_block[p][1]);
pPixels[p][2] = basist::half_to_float(half_block[p][2]);
pPixels[p][3] = basist::half_to_float(half_block[p][3]);
}
//memset(pPixels, 0, sizeof(vec4F) * 16);
#endif
return true;
}
case texture_format::cBC6HSigned:
case texture_format::cBC6HUnsigned:
{
basist::half_float half_block[16][3];
unpack_bc6h(pBlock, half_block, fmt == texture_format::cBC6HSigned);
for (uint32_t p = 0; p < 16; p++)
{
pPixels[p][0] = basist::half_to_float(half_block[p][0]);
pPixels[p][1] = basist::half_to_float(half_block[p][1]);
pPixels[p][2] = basist::half_to_float(half_block[p][2]);
pPixels[p][3] = 1.0f;
}
return true;
}
default:
{
break;
}
}
assert(0);
return false;
}
bool gpu_image::unpack(image& img) const
{
img.resize(get_pixel_width(), get_pixel_height());
@ -1252,7 +1589,48 @@ namespace basisu
return success;
}
bool gpu_image::unpack_hdr(imagef& img) const
{
if ((m_fmt != texture_format::cASTC_HDR_4x4) &&
(m_fmt != texture_format::cUASTC_HDR_4x4) &&
(m_fmt != texture_format::cBC6HUnsigned) &&
(m_fmt != texture_format::cBC6HSigned))
{
// Can't call on LDR images, at least currently. (Could unpack the LDR data and convert to float.)
assert(0);
return false;
}
img.resize(get_pixel_width(), get_pixel_height());
img.set_all(vec4F(0.0f));
if (!img.get_width() || !img.get_height())
return true;
assert((m_block_width <= cMaxBlockSize) && (m_block_height <= cMaxBlockSize));
vec4F pixels[cMaxBlockSize * cMaxBlockSize];
clear_obj(pixels);
bool success = true;
for (uint32_t by = 0; by < m_blocks_y; by++)
{
for (uint32_t bx = 0; bx < m_blocks_x; bx++)
{
const void* pBlock = get_block_ptr(bx, by);
if (!unpack_block_hdr(m_fmt, pBlock, pixels))
success = false;
img.set_block_clipped(pixels, bx * m_block_width, by * m_block_height, m_block_width, m_block_height);
} // bx
} // by
return success;
}
// KTX1 texture file writing
static const uint8_t g_ktx_file_id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
// KTX/GL enums
@ -1273,6 +1651,8 @@ namespace basisu
KTX_COMPRESSED_RGBA8_ETC2_EAC = 0x9278,
KTX_COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C,
KTX_COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D,
KTX_COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E,
KTX_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F,
KTX_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00,
KTX_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02,
KTX_COMPRESSED_RGBA_ASTC_4x4_KHR = 0x93B0,
@ -1319,6 +1699,7 @@ namespace basisu
uint32_t width = 0, height = 0, total_levels = 0;
basisu::texture_format fmt = texture_format::cInvalidTextureFormat;
// Sanity check the input
if (cubemap_flag)
{
if ((gpu_images.size() % 6) != 0)
@ -1327,7 +1708,7 @@ namespace basisu
return false;
}
}
for (uint32_t array_index = 0; array_index < gpu_images.size(); array_index++)
{
const gpu_image_vec &levels = gpu_images[array_index];
@ -1426,6 +1807,18 @@ namespace basisu
base_internal_fmt = KTX_RGBA;
break;
}
case texture_format::cBC6HSigned:
{
internal_fmt = KTX_COMPRESSED_RGB_BPTC_SIGNED_FLOAT;
base_internal_fmt = KTX_RGBA;
break;
}
case texture_format::cBC6HUnsigned:
{
internal_fmt = KTX_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
base_internal_fmt = KTX_RGBA;
break;
}
case texture_format::cBC7:
{
internal_fmt = KTX_COMPRESSED_RGBA_BPTC_UNORM;
@ -1443,7 +1836,10 @@ namespace basisu
base_internal_fmt = KTX_RGBA;
break;
}
case texture_format::cASTC4x4:
// We use different enums for HDR vs. LDR ASTC, but internally they are both just ASTC.
case texture_format::cASTC_LDR_4x4:
case texture_format::cASTC_HDR_4x4:
case texture_format::cUASTC_HDR_4x4: // UASTC_HDR is just HDR-only ASTC
{
internal_fmt = KTX_COMPRESSED_RGBA_ASTC_4x4_KHR;
base_internal_fmt = KTX_RGBA;
@ -1496,17 +1892,17 @@ namespace basisu
return false;
}
}
ktx_header header;
header.clear();
memcpy(&header.m_identifier, g_ktx_file_id, sizeof(g_ktx_file_id));
header.m_endianness = KTX_ENDIAN;
header.m_pixelWidth = width;
header.m_pixelHeight = height;
header.m_glTypeSize = 1;
header.m_glInternalFormat = internal_fmt;
header.m_glBaseInternalFormat = base_internal_fmt;
@ -1517,12 +1913,12 @@ namespace basisu
header.m_numberOfMipmapLevels = total_levels;
header.m_numberOfFaces = cubemap_flag ? 6 : 1;
append_vector(ktx_data, (uint8_t *)&header, sizeof(header));
append_vector(ktx_data, (uint8_t*)&header, sizeof(header));
for (uint32_t level_index = 0; level_index < total_levels; level_index++)
{
uint32_t img_size = gpu_images[0][level_index].get_size_in_bytes();
if ((header.m_numberOfFaces == 1) || (header.m_numberOfArrayElements > 1))
{
img_size = img_size * header.m_numberOfFaces * maximum<uint32_t>(1, header.m_numberOfArrayElements);
@ -1531,9 +1927,10 @@ namespace basisu
assert(img_size && ((img_size & 3) == 0));
packed_uint<4> packed_img_size(img_size);
append_vector(ktx_data, (uint8_t *)&packed_img_size, sizeof(packed_img_size));
append_vector(ktx_data, (uint8_t*)&packed_img_size, sizeof(packed_img_size));
uint32_t bytes_written = 0;
(void)bytes_written;
for (uint32_t array_index = 0; array_index < maximum<uint32_t>(1, header.m_numberOfArrayElements); array_index++)
{
@ -1541,11 +1938,11 @@ namespace basisu
{
const gpu_image& img = gpu_images[cubemap_flag ? (array_index * 6 + face_index) : array_index][level_index];
append_vector(ktx_data, (uint8_t *)img.get_ptr(), img.get_size_in_bytes());
append_vector(ktx_data, (uint8_t*)img.get_ptr(), img.get_size_in_bytes());
bytes_written += img.get_size_in_bytes();
}
} // array_index
} // level_index
@ -1553,7 +1950,58 @@ namespace basisu
return true;
}
bool write_compressed_texture_file(const char* pFilename, const basisu::vector<gpu_image_vec>& g, bool cubemap_flag)
bool does_dds_support_format(texture_format fmt)
{
switch (fmt)
{
case texture_format::cBC1_NV:
case texture_format::cBC1_AMD:
case texture_format::cBC1:
case texture_format::cBC3:
case texture_format::cBC4:
case texture_format::cBC5:
case texture_format::cBC6HSigned:
case texture_format::cBC6HUnsigned:
case texture_format::cBC7:
return true;
default:
break;
}
return false;
}
// Only supports the basic DirectX BC texture formats.
// gpu_images array is: [face/layer][mipmap level]
// For cubemap arrays, # of face/layers must be a multiple of 6.
// Accepts 2D, 2D mipmapped, 2D array, 2D array mipmapped
// and cubemap, cubemap mipmapped, and cubemap array mipmapped.
bool write_dds_file(uint8_vec &dds_data, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format)
{
return false;
}
bool write_dds_file(const char* pFilename, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format)
{
uint8_vec dds_data;
if (!write_dds_file(dds_data, gpu_images, cubemap_flag, use_srgb_format))
return false;
if (!write_vec_to_file(pFilename, dds_data))
{
fprintf(stderr, "write_dds_file: Failed writing DDS file data\n");
return false;
}
return true;
}
bool read_uncompressed_dds_file(const char* pFilename, basisu::vector<image> &ldr_mips, basisu::vector<imagef>& hdr_mips)
{
return false;
}
bool write_compressed_texture_file(const char* pFilename, const basisu::vector<gpu_image_vec>& g, bool cubemap_flag, bool use_srgb_format)
{
std::string extension(string_tolower(string_get_extension(pFilename)));
@ -1570,8 +2018,8 @@ namespace basisu
}
else if (extension == "dds")
{
// TODO
return false;
if (!write_dds_file(filedata, g, cubemap_flag, use_srgb_format))
return false;
}
else
{
@ -1583,11 +2031,18 @@ namespace basisu
return basisu::write_vec_to_file(pFilename, filedata);
}
bool write_compressed_texture_file(const char* pFilename, const gpu_image& g)
bool write_compressed_texture_file(const char* pFilename, const gpu_image_vec& g, bool use_srgb_format)
{
basisu::vector<gpu_image_vec> a;
a.push_back(g);
return write_compressed_texture_file(pFilename, a, false, use_srgb_format);
}
bool write_compressed_texture_file(const char* pFilename, const gpu_image& g, bool use_srgb_format)
{
basisu::vector<gpu_image_vec> v;
enlarge_vector(v, 1)->push_back(g);
return write_compressed_texture_file(pFilename, v, false);
return write_compressed_texture_file(pFilename, v, false, use_srgb_format);
}
//const uint32_t OUT_FILE_MAGIC = 'TEXC';
@ -1626,5 +2081,49 @@ namespace basisu
return fclose(pFile) != EOF;
}
// The .astc texture format is readable using ARM's astcenc, AMD Compressonator, and other engines/tools. It oddly doesn't support mipmaps, limiting
// its usefulness/relevance.
// https://github.com/ARM-software/astc-encoder/blob/main/Docs/FileFormat.md
bool write_astc_file(const char* pFilename, const void* pBlocks, uint32_t block_width, uint32_t block_height, uint32_t dim_x, uint32_t dim_y)
{
assert(pBlocks && (block_width >= 4) && (block_height >= 4) && (dim_x > 0) && (dim_y > 0));
uint8_vec file_data;
file_data.push_back(0x13);
file_data.push_back(0xAB);
file_data.push_back(0xA1);
file_data.push_back(0x5C);
file_data.push_back((uint8_t)block_width);
file_data.push_back((uint8_t)block_height);
file_data.push_back(1);
file_data.push_back((uint8_t)dim_x);
file_data.push_back((uint8_t)(dim_x >> 8));
file_data.push_back((uint8_t)(dim_x >> 16));
file_data.push_back((uint8_t)dim_y);
file_data.push_back((uint8_t)(dim_y >> 8));
file_data.push_back((uint8_t)(dim_y >> 16));
file_data.push_back((uint8_t)1);
file_data.push_back((uint8_t)0);
file_data.push_back((uint8_t)0);
const uint32_t num_blocks_x = (dim_x + block_width - 1) / block_width;
const uint32_t num_blocks_y = (dim_y + block_height - 1) / block_height;
const uint32_t total_bytes = num_blocks_x * num_blocks_y * 16;
const size_t cur_size = file_data.size();
file_data.resize(cur_size + total_bytes);
memcpy(&file_data[cur_size], pBlocks, total_bytes);
return write_vec_to_file(pFilename, file_data);
}
} // basisu

View File

@ -1,5 +1,5 @@
// basisu_gpu_texture.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -48,6 +48,7 @@ namespace basisu
}
inline texture_format get_format() const { return m_fmt; }
inline bool is_hdr() const { return is_hdr_texture_format(m_fmt); }
// Width/height in pixels
inline uint32_t get_pixel_width() const { return m_width; }
@ -100,9 +101,13 @@ namespace basisu
m_blocks.resize(m_blocks_x * m_blocks_y * m_qwords_per_block);
}
// Unpacks LDR textures only.
bool unpack(image& img) const;
// Unpacks HDR textures only.
bool unpack_hdr(imagef& img) const;
void override_dimensions(uint32_t w, uint32_t h)
inline void override_dimensions(uint32_t w, uint32_t h)
{
m_width = w;
m_height = h;
@ -116,39 +121,50 @@ namespace basisu
typedef basisu::vector<gpu_image> gpu_image_vec;
// KTX file writing
// KTX1 file writing
bool create_ktx_texture_file(uint8_vec &ktx_data, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag);
bool write_compressed_texture_file(const char *pFilename, const basisu::vector<gpu_image_vec>& g, bool cubemap_flag);
inline bool write_compressed_texture_file(const char *pFilename, const gpu_image_vec &g)
{
basisu::vector<gpu_image_vec> a;
a.push_back(g);
return write_compressed_texture_file(pFilename, a, false);
}
bool does_dds_support_format(texture_format fmt);
bool write_dds_file(uint8_vec& dds_data, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format);
bool write_dds_file(const char* pFilename, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format);
bool write_compressed_texture_file(const char *pFilename, const gpu_image &g);
// Currently reads 2D 32bpp RGBA, 16-bit HALF RGBA, or 32-bit FLOAT RGBA, with or without mipmaps. No tex arrays or cubemaps, yet.
bool read_uncompressed_dds_file(const char* pFilename, basisu::vector<image>& ldr_mips, basisu::vector<imagef>& hdr_mips);
// Supports DDS and KTX
bool write_compressed_texture_file(const char *pFilename, const basisu::vector<gpu_image_vec>& g, bool cubemap_flag, bool use_srgb_format);
bool write_compressed_texture_file(const char* pFilename, const gpu_image_vec& g, bool use_srgb_format);
bool write_compressed_texture_file(const char *pFilename, const gpu_image &g, bool use_srgb_format);
bool write_3dfx_out_file(const char* pFilename, const gpu_image& gi);
// GPU texture block unpacking
// For ETC1, use in basisu_etc.h: bool unpack_etc1(const etc_block& block, color_rgba *pDst, bool preserve_alpha)
void unpack_etc2_eac(const void *pBlock_bits, color_rgba *pPixels);
bool unpack_bc1(const void *pBlock_bits, color_rgba *pPixels, bool set_alpha);
void unpack_bc4(const void *pBlock_bits, uint8_t *pPixels, uint32_t stride);
bool unpack_bc3(const void *pBlock_bits, color_rgba *pPixels);
void unpack_bc5(const void *pBlock_bits, color_rgba *pPixels);
bool unpack_bc7_mode6(const void *pBlock_bits, color_rgba *pPixels);
bool unpack_bc7(const void* pBlock_bits, color_rgba* pPixels);
bool unpack_bc7(const void* pBlock_bits, color_rgba* pPixels); // full format
bool unpack_bc6h(const void* pSrc_block, void* pDst_block, bool is_signed, uint32_t dest_pitch_in_halfs = 4 * 3); // full format, outputs HALF values, RGB texels only (not RGBA)
void unpack_atc(const void* pBlock_bits, color_rgba* pPixels);
// We only support CC_MIXED non-alpha blocks here because that's the only mode the transcoder uses at the moment.
bool unpack_fxt1(const void* p, color_rgba* pPixels);
// PVRTC2 is currently limited to only what our transcoder outputs (non-interpolated, hard_flag=1 modulation=0). In this mode, PVRTC2 looks much like BC1/ATC.
bool unpack_pvrtc2(const void* p, color_rgba* pPixels);
void unpack_etc2_eac_r(const void *p, color_rgba* pPixels, uint32_t c);
void unpack_etc2_eac_rg(const void* p, color_rgba* pPixels);
// unpack_block() is primarily intended to unpack texture data created by the transcoder.
// For some texture formats (like ETC2 RGB, PVRTC2, FXT1) it's not a complete implementation.
// For some texture formats (like ETC2 RGB, PVRTC2, FXT1) it's not yet a complete implementation.
// Unpacks LDR texture formats only.
bool unpack_block(texture_format fmt, const void *pBlock, color_rgba *pPixels);
// Unpacks HDR texture formats only.
bool unpack_block_hdr(texture_format fmt, const void* pBlock, vec4F* pPixels);
bool write_astc_file(const char* pFilename, const void* pBlocks, uint32_t block_width, uint32_t block_height, uint32_t dim_x, uint32_t dim_y);
} // namespace basisu

View File

@ -1,5 +1,5 @@
// basisu_kernels_declares.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_kernels_imp.h - Do not directly include
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_kernels_sse.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -22,22 +22,6 @@
#include <intrin.h>
#endif
#if !defined(_MSC_VER)
#if __AVX__ || __AVX2__ || __AVX512F__
#error Please check your compiler options
#endif
#if CPPSPMD_SSE2
#if __SSE4_1__ || __SSE3__ || __SSE4_2__ || __SSSE3__
#error SSE4.1/SSE3/SSE4.2/SSSE3 cannot be enabled to use this file
#endif
#else
#if !__SSE4_1__ || !__SSE3__ || !__SSSE3__
#error Please check your compiler options
#endif
#endif
#endif
#include "cppspmd_sse.h"
#include "cppspmd_type_aliases.h"

View File

@ -3,7 +3,7 @@
Forked from the public domain/unlicense version at: https://code.google.com/archive/p/miniz/
Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -1973,7 +1973,7 @@ static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahe
(TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
if (!probe_len)
{
*pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break;
*pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); break;
}
else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len)
{
@ -2101,7 +2101,7 @@ static mz_bool tdefl_compress_fast(tdefl_compressor *d)
total_lz_bytes += cur_match_len;
lookahead_pos += cur_match_len;
dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE);
dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE);
cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;
MZ_ASSERT(lookahead_size >= cur_match_len);
lookahead_size -= cur_match_len;
@ -2129,7 +2129,7 @@ static mz_bool tdefl_compress_fast(tdefl_compressor *d)
d->m_huff_count[0][lit]++;
lookahead_pos++;
dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE);
dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE);
cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
lookahead_size--;
@ -2283,7 +2283,7 @@ static mz_bool tdefl_compress_normal(tdefl_compressor *d)
d->m_lookahead_pos += len_to_move;
MZ_ASSERT(d->m_lookahead_size >= len_to_move);
d->m_lookahead_size -= len_to_move;
d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE);
d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE);
// Check if it's time to flush the current LZ codes to the internal output buffer.
if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ||
( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) )

View File

@ -1,5 +1,5 @@
// basisu_opencl.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_opencl.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Note: Undefine or set BASISU_SUPPORT_OPENCL to 0 to completely OpenCL support.
//

View File

@ -1,5 +1,5 @@
// basisu_pvrtc1_4.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_pvrtc1_4.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -231,7 +231,18 @@ namespace basisu
inline void set_to_black()
{
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#endif
memset(m_blocks.get_ptr(), 0, m_blocks.size_in_bytes());
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
}
inline bool get_block_uses_transparent_modulation(uint32_t bx, uint32_t by) const

View File

@ -1,5 +1,5 @@
// basisu_resampler_filters.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_resampler.cpp
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_resampler.h
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_resampler_filters.h
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_ssim.cpp
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_ssim.h
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
// basisu_uastc_enc.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -13,11 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "basisu_uastc_enc.h"
#if BASISU_USE_ASTC_DECOMPRESS
#include "basisu_astc_decomp.h"
#endif
#include "3rdparty/android_astc_decomp.h"
#include "basisu_gpu_texture.h"
#include "basisu_bc7enc.h"
@ -384,6 +380,7 @@ namespace basisu
}
uint32_t total_endpoint_bits = 0;
(void)total_endpoint_bits;
for (uint32_t i = 0; i < total_tq_values; i++)
{
@ -428,6 +425,8 @@ namespace basisu
#endif
uint32_t total_weight_bits = 0;
(void)total_weight_bits;
const uint32_t plane_shift = (total_planes == 2) ? 1 : 0;
for (uint32_t i = 0; i < 16 * total_planes; i++)
{
@ -3175,6 +3174,7 @@ namespace basisu
const bool favor_bc7_error = !favor_uastc_error && ((flags & cPackUASTCFavorBC7Error) != 0);
//const bool etc1_perceptual = true;
// TODO: This uses 64KB of stack space!
uastc_encode_results results[MAX_ENCODE_RESULTS];
level = clampi(level, cPackUASTCLevelFastest, cPackUASTCLevelVerySlow);
@ -3567,7 +3567,6 @@ namespace basisu
success = basist::unpack_uastc(temp_block, (basist::color32 *)temp_block_unpacked, false);
VALIDATE(success);
#if BASISU_USE_ASTC_DECOMPRESS
// Now round trip to packed ASTC and back, then decode to pixels.
uint32_t astc_data[4];
@ -3580,7 +3579,7 @@ namespace basisu
}
color_rgba decoded_astc_block[4][4];
success = basisu_astc::astc::decompress((uint8_t*)decoded_astc_block, (uint8_t*)&astc_data, false, 4, 4);
success = basisu_astc::astc::decompress_ldr((uint8_t*)decoded_astc_block, (uint8_t*)&astc_data, false, 4, 4);
VALIDATE(success);
for (uint32_t y = 0; y < 4; y++)
@ -3595,7 +3594,6 @@ namespace basisu
VALIDATE(temp_block_unpacked[y][x].c[3] == decoded_uastc_block[y][x].a);
}
}
#endif
}
#endif
@ -3789,8 +3787,9 @@ namespace basisu
{
uint64_t m_sel;
uint32_t m_ofs;
uint32_t m_pad; // avoid implicit padding for selector_bitsequence_hash
selector_bitsequence() { }
selector_bitsequence(uint32_t bit_ofs, uint64_t sel) : m_sel(sel), m_ofs(bit_ofs) { }
selector_bitsequence(uint32_t bit_ofs, uint64_t sel) : m_sel(sel), m_ofs(bit_ofs), m_pad(0) { }
bool operator== (const selector_bitsequence& other) const
{
return (m_ofs == other.m_ofs) && (m_sel == other.m_sel);
@ -3811,7 +3810,7 @@ namespace basisu
{
std::size_t operator()(selector_bitsequence const& s) const noexcept
{
return static_cast<std::size_t>(hash_hsieh((uint8_t *)&s, sizeof(s)) ^ s.m_sel);
return hash_hsieh((const uint8_t*)&s, sizeof(s));
}
};

View File

@ -1,5 +1,5 @@
// basisu_uastc_enc.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,7 +1,7 @@
// Do not include this header directly.
// Control flow functionality in common between all the headers.
//
// Copyright 2020-2021 Binomial LLC
// Copyright 2020-2024 Binomial LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,6 +1,6 @@
// Do not include this header directly.
//
// Copyright 2020-2021 Binomial LLC
// Copyright 2020-2024 Binomial LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -646,7 +646,7 @@ CPPSPMD_FORCE_INLINE vint spmd_kernel::count_set_bits(vint x)
{
vint v = x - (VUINT_SHIFT_RIGHT(x, 1) & 0x55555555);
vint v1 = (v & 0x33333333) + (VUINT_SHIFT_RIGHT(v, 2) & 0x33333333);
return VUINT_SHIFT_RIGHT(((v1 + VUINT_SHIFT_RIGHT(v1, 4) & 0xF0F0F0F) * 0x1010101), 24);
return VUINT_SHIFT_RIGHT(((v1 + (VUINT_SHIFT_RIGHT(v1, 4) & 0xF0F0F0F)) * 0x1010101), 24);
}
CPPSPMD_FORCE_INLINE vint cmple_epu16(const vint &a, const vint &b)

View File

@ -1,7 +1,7 @@
// Do not include this header directly.
// This header defines shared struct spmd_kernel helpers.
//
// Copyright 2020-2021 Binomial LLC
// Copyright 2020-2024 Binomial LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -450,7 +450,7 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE explicit operator vint() const;
private:
vbool& operator=(const vbool&);
//vbool& operator=(const vbool&);
};
friend vbool operator!(const vbool& v);
@ -481,7 +481,7 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE explicit vfloat(int value) : m_value(_mm_set1_ps((float)value)) { }
private:
vfloat& operator=(const vfloat&);
//vfloat& operator=(const vfloat&);
};
CPPSPMD_FORCE_INLINE vfloat& store(vfloat& dst, const vfloat& src)
@ -514,7 +514,7 @@ struct spmd_kernel
float* m_pValue;
private:
float_lref& operator=(const float_lref&);
//float_lref& operator=(const float_lref&);
};
CPPSPMD_FORCE_INLINE const float_lref& store(const float_lref& dst, const vfloat& src)
@ -561,7 +561,7 @@ struct spmd_kernel
float* m_pValue;
private:
float_vref& operator=(const float_vref&);
//float_vref& operator=(const float_vref&);
};
// Varying ref to varying float
@ -571,7 +571,7 @@ struct spmd_kernel
vfloat* m_pValue;
private:
vfloat_vref& operator=(const vfloat_vref&);
//vfloat_vref& operator=(const vfloat_vref&);
};
// Varying ref to varying int
@ -581,7 +581,7 @@ struct spmd_kernel
vint* m_pValue;
private:
vint_vref& operator=(const vint_vref&);
//vint_vref& operator=(const vint_vref&);
};
CPPSPMD_FORCE_INLINE const float_vref& store(const float_vref& dst, const vfloat& src);
@ -624,7 +624,7 @@ struct spmd_kernel
int* m_pValue;
private:
int_lref& operator=(const int_lref&);
//int_lref& operator=(const int_lref&);
};
CPPSPMD_FORCE_INLINE const int_lref& store(const int_lref& dst, const vint& src)
@ -663,7 +663,7 @@ struct spmd_kernel
int16_t* m_pValue;
private:
int16_lref& operator=(const int16_lref&);
//int16_lref& operator=(const int16_lref&);
};
CPPSPMD_FORCE_INLINE const int16_lref& store(const int16_lref& dst, const vint& src)
@ -720,7 +720,7 @@ struct spmd_kernel
const int* m_pValue;
private:
cint_lref& operator=(const cint_lref&);
//cint_lref& operator=(const cint_lref&);
};
CPPSPMD_FORCE_INLINE vint load(const cint_lref& src)
@ -742,7 +742,7 @@ struct spmd_kernel
int* m_pValue;
private:
int_vref& operator=(const int_vref&);
//int_vref& operator=(const int_vref&);
};
// Varying ref to constant ints
@ -752,7 +752,7 @@ struct spmd_kernel
const int* m_pValue;
private:
cint_vref& operator=(const cint_vref&);
//cint_vref& operator=(const cint_vref&);
};
// Varying int
@ -810,7 +810,7 @@ struct spmd_kernel
}
private:
vint& operator=(const vint&);
//vint& operator=(const vint&);
};
// Load/store linear int
@ -1206,7 +1206,7 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE vint load_all(const vint_vref& src)
{
// TODO: There's surely a better way
__m128i k;
__m128i k = _mm_setzero_si128();
k = insert_x(k, ((int*)(&src.m_pValue[extract_x(src.m_vindex)]))[0]);
k = insert_y(k, ((int*)(&src.m_pValue[extract_y(src.m_vindex)]))[1]);
@ -1261,7 +1261,7 @@ struct spmd_kernel
}
private:
lint& operator=(const lint&);
//lint& operator=(const lint&);
};
CPPSPMD_FORCE_INLINE lint& store_all(lint& dst, const lint& src)

View File

@ -1,7 +1,7 @@
// cppspmd_type_aliases.h
// Do not include this file directly
//
// Copyright 2020-2021 Binomial LLC
// Copyright 2020-2024 Binomial LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -163,7 +163,7 @@ public:
{
if ((sizeof(size_t) == sizeof(uint32_t)) && (new_size > 0x7FFFFFFFUL))
return 0;
m_buf.resize(new_size);
m_buf.resize((size_t)new_size);
}
memcpy(&m_buf[(size_t)m_ofs], pBuf, len);
@ -178,11 +178,11 @@ public:
return 0;
uint64_t max_bytes = minimum<uint64_t>(len, m_buf.size() - m_ofs);
memcpy(pBuf, &m_buf[(size_t)m_ofs], max_bytes);
memcpy(pBuf, &m_buf[(size_t)m_ofs], (size_t)max_bytes);
m_ofs += max_bytes;
return max_bytes;
return (size_t)max_bytes;
}
};
@ -249,11 +249,11 @@ public:
return 0;
uint64_t max_bytes = minimum<uint64_t>(len, m_buf_size - m_ofs);
memcpy(pBuf, &m_pBuf[(size_t)m_ofs], max_bytes);
memcpy(pBuf, &m_pBuf[(size_t)m_ofs], (size_t)max_bytes);
m_ofs += max_bytes;
return max_bytes;
return (size_t)max_bytes;
}
};
@ -1626,8 +1626,8 @@ int png_decoder::png_decode_start()
if (m_ihdr.m_ilace_type == 1)
{
int i;
uint32_t total_lines, lines_processed;
//int i;
//uint32_t total_lines, lines_processed;
m_adam7_pass_size_x[0] = adam7_pass_size(m_ihdr.m_width, 0, 8);
m_adam7_pass_size_x[1] = adam7_pass_size(m_ihdr.m_width, 4, 8);
@ -1651,10 +1651,12 @@ int png_decoder::png_decode_start()
m_pass_y_left = 0;
#if 0
total_lines = lines_processed = 0;
for (i = 0; i < 7; i++)
total_lines += m_adam7_pass_size_y[i];
#endif
for (; ; )
{
@ -1675,7 +1677,7 @@ int png_decoder::png_decode_start()
}
}
lines_processed++;
//lines_processed++;
}
m_adam7_decoded_flag = TRUE;

View File

@ -0,0 +1,23 @@
diff --git a/thirdparty/basis_universal/encoder/basisu_enc.cpp b/thirdparty/basis_universal/encoder/basisu_enc.cpp
index 6c0ac0ad370..2bf486a0287 100644
--- a/thirdparty/basis_universal/encoder/basisu_enc.cpp
+++ b/thirdparty/basis_universal/encoder/basisu_enc.cpp
@@ -27,7 +27,7 @@
#ifndef TINYEXR_USE_ZFP
#define TINYEXR_USE_ZFP (1)
#endif
-#include "3rdparty/tinyexr.h"
+#include <tinyexr.h>
#ifndef MINIZ_HEADER_FILE_ONLY
#define MINIZ_HEADER_FILE_ONLY
@@ -3257,7 +3257,8 @@ namespace basisu
float* out_rgba = nullptr;
const char* err = nullptr;
- int status = LoadEXRWithLayer(&out_rgba, &width, &height, pFilename, nullptr, &err, &n_chans);
+ int status = LoadEXRWithLayer(&out_rgba, &width, &height, pFilename, nullptr, &err);
+ n_chans = 4;
if (status != 0)
{
error_printf("Failed loading .EXR image \"%s\"! (TinyEXR error: %s)\n", pFilename, err ? err : "?");

View File

@ -0,0 +1,446 @@
diff --git a/thirdparty/basis_universal/encoder/basisu_enc.cpp b/thirdparty/basis_universal/encoder/basisu_enc.cpp
index 2bf486a0287..fff98e83014 100644
--- a/thirdparty/basis_universal/encoder/basisu_enc.cpp
+++ b/thirdparty/basis_universal/encoder/basisu_enc.cpp
@@ -37,9 +37,6 @@
#endif
#include "basisu_miniz.h"
-#define QOI_IMPLEMENTATION
-#include "3rdparty/qoi.h"
-
#if defined(_WIN32)
// For QueryPerformanceCounter/QueryPerformanceFrequency
#define WIN32_LEAN_AND_MEAN
@@ -408,16 +405,7 @@ namespace basisu
bool load_qoi(const char* pFilename, image& img)
{
- qoi_desc desc;
- clear_obj(desc);
-
- void* p = qoi_read(pFilename, &desc, 4);
- if (!p)
- return false;
-
- img.grant_ownership(static_cast<color_rgba *>(p), desc.width, desc.height);
-
- return true;
+ return false;
}
bool load_png(const uint8_t *pBuf, size_t buf_size, image &img, const char *pFilename)
diff --git a/thirdparty/basis_universal/encoder/basisu_gpu_texture.cpp b/thirdparty/basis_universal/encoder/basisu_gpu_texture.cpp
index 000869a5337..342446b8fd4 100644
--- a/thirdparty/basis_universal/encoder/basisu_gpu_texture.cpp
+++ b/thirdparty/basis_universal/encoder/basisu_gpu_texture.cpp
@@ -19,9 +19,6 @@
#include "basisu_bc7enc.h"
#include "../transcoder/basisu_astc_hdr_core.h"
-#define TINYDDS_IMPLEMENTATION
-#include "3rdparty/tinydds.h"
-
namespace basisu
{
//------------------------------------------------------------------------------------------------
@@ -1979,208 +1976,8 @@ namespace basisu
// Accepts 2D, 2D mipmapped, 2D array, 2D array mipmapped
// and cubemap, cubemap mipmapped, and cubemap array mipmapped.
bool write_dds_file(uint8_vec &dds_data, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format)
- {
- if (!gpu_images.size())
- {
- assert(0);
- return false;
- }
-
- // Sanity check the input
- uint32_t slices = 1;
- if (cubemap_flag)
- {
- if ((gpu_images.size() % 6) != 0)
- {
- assert(0);
- return false;
- }
- slices = gpu_images.size() / 6;
- }
- else
- {
- slices = gpu_images.size();
- }
-
- uint32_t width = 0, height = 0, total_levels = 0;
- basisu::texture_format fmt = texture_format::cInvalidTextureFormat;
-
- // Sanity check the input for consistent # of dimensions and mip levels
- for (uint32_t array_index = 0; array_index < gpu_images.size(); array_index++)
- {
- const gpu_image_vec& levels = gpu_images[array_index];
-
- if (!levels.size())
- {
- // Empty mip chain
- assert(0);
- return false;
- }
-
- if (!array_index)
- {
- width = levels[0].get_pixel_width();
- height = levels[0].get_pixel_height();
- total_levels = (uint32_t)levels.size();
- fmt = levels[0].get_format();
- }
- else
- {
- if ((width != levels[0].get_pixel_width()) ||
- (height != levels[0].get_pixel_height()) ||
- (total_levels != levels.size()))
- {
- // All cubemap/texture array faces must be the same dimension
- assert(0);
- return false;
- }
- }
-
- for (uint32_t level_index = 0; level_index < levels.size(); level_index++)
- {
- if (level_index)
- {
- if ((levels[level_index].get_pixel_width() != maximum<uint32_t>(1, levels[0].get_pixel_width() >> level_index)) ||
- (levels[level_index].get_pixel_height() != maximum<uint32_t>(1, levels[0].get_pixel_height() >> level_index)))
- {
- // Malformed mipmap chain
- assert(0);
- return false;
- }
- }
-
- if (fmt != levels[level_index].get_format())
- {
- // All input textures must use the same GPU format
- assert(0);
- return false;
- }
- }
- }
-
- // No mipmap levels
- if (!total_levels)
- {
- assert(0);
- return false;
- }
-
- // Create the DDS mipmap level data
- uint8_vec mipmaps[32];
-
- // See https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-file-layout-for-cubic-environment-maps
- // DDS cubemap organization is cubemap face 0 followed by all mips, then cubemap face 1 followed by all mips, etc.
- // Unfortunately tinydds.h's writer doesn't handle this case correctly, so we work around it here.
- // This also applies with 2D texture arrays, too. RenderDoc and ddsview (DirectXTex) views each type (cubemap array and 2D texture array) correctly.
- // Also see "Using Texture Arrays in Direct3D 10/11":
- // https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide
- for (uint32_t array_index = 0; array_index < gpu_images.size(); array_index++)
- {
- const gpu_image_vec& levels = gpu_images[array_index];
-
- for (uint32_t level_index = 0; level_index < levels.size(); level_index++)
- {
- append_vector(mipmaps[0], (uint8_t*)levels[level_index].get_ptr(), levels[level_index].get_size_in_bytes());
-
- } // level_index
- } // array_index
-
-#if 0
- // This organization, required by tinydds.h's API, is wrong.
- {
- for (uint32_t array_index = 0; array_index < gpu_images.size(); array_index++)
- {
- const gpu_image_vec& levels = gpu_images[array_index];
-
- for (uint32_t level_index = 0; level_index < levels.size(); level_index++)
- {
- append_vector(mipmaps[level_index], (uint8_t*)levels[level_index].get_ptr(), levels[level_index].get_size_in_bytes());
-
- } // level_index
- } // array_index
- }
-#endif
-
- // Write DDS file using tinydds
- TinyDDS_WriteCallbacks cbs;
- cbs.error = [](void* user, char const* msg) { BASISU_NOTE_UNUSED(user); fprintf(stderr, "tinydds: %s\n", msg); };
- cbs.alloc = [](void* user, size_t size) -> void* { BASISU_NOTE_UNUSED(user); return malloc(size); };
- cbs.free = [](void* user, void* memory) { BASISU_NOTE_UNUSED(user); free(memory); };
- cbs.write = [](void* user, void const* buffer, size_t byteCount) { BASISU_NOTE_UNUSED(user); uint8_vec* pVec = (uint8_vec*)user; append_vector(*pVec, (const uint8_t*)buffer, byteCount); };
-
- uint32_t mipmap_sizes[32];
- const void* mipmap_ptrs[32];
-
- clear_obj(mipmap_sizes);
- clear_obj(mipmap_ptrs);
-
- assert(total_levels < 32);
- for (uint32_t i = 0; i < total_levels; i++)
- {
- mipmap_sizes[i] = mipmaps[i].size_in_bytes();
- mipmap_ptrs[i] = mipmaps[i].get_ptr();
- }
-
- // Select tinydds texture format
- uint32_t tinydds_fmt = 0;
-
- switch (fmt)
- {
- case texture_format::cBC1_NV:
- case texture_format::cBC1_AMD:
- case texture_format::cBC1:
- tinydds_fmt = use_srgb_format ? TDDS_BC1_RGBA_SRGB_BLOCK : TDDS_BC1_RGBA_UNORM_BLOCK;
- break;
- case texture_format::cBC3:
- tinydds_fmt = use_srgb_format ? TDDS_BC3_SRGB_BLOCK : TDDS_BC3_UNORM_BLOCK;
- break;
- case texture_format::cBC4:
- tinydds_fmt = TDDS_BC4_UNORM_BLOCK;
- break;
- case texture_format::cBC5:
- tinydds_fmt = TDDS_BC5_UNORM_BLOCK;
- break;
- case texture_format::cBC6HSigned:
- tinydds_fmt = TDDS_BC6H_SFLOAT_BLOCK;
- break;
- case texture_format::cBC6HUnsigned:
- tinydds_fmt = TDDS_BC6H_UFLOAT_BLOCK;
- break;
- case texture_format::cBC7:
- tinydds_fmt = use_srgb_format ? TDDS_BC7_SRGB_BLOCK : TDDS_BC7_UNORM_BLOCK;
- break;
- default:
- {
- fprintf(stderr, "Warning: Unsupported format in write_dds_file().\n");
- return false;
- }
- }
-
- // DirectXTex's DDSView doesn't handle odd sizes textures correctly. RenderDoc loads them fine, however.
- // Trying to work around this here results in invalid mipmaps.
- //width = (width + 3) & ~3;
- //height = (height + 3) & ~3;
-
- bool status = TinyDDS_WriteImage(&cbs,
- &dds_data,
- width,
- height,
- 1,
- slices,
- total_levels,
- (TinyDDS_Format)tinydds_fmt,
- cubemap_flag,
- true,
- mipmap_sizes,
- mipmap_ptrs);
-
- if (!status)
- {
- fprintf(stderr, "write_dds_file: Failed creating DDS file\n");
- return false;
- }
-
- return true;
+ {
+ return false;
}
bool write_dds_file(const char* pFilename, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format)
@@ -2201,188 +1998,6 @@ namespace basisu
bool read_uncompressed_dds_file(const char* pFilename, basisu::vector<image> &ldr_mips, basisu::vector<imagef>& hdr_mips)
{
- const uint32_t MAX_IMAGE_DIM = 16384;
-
- TinyDDS_Callbacks cbs;
-
- cbs.errorFn = [](void* user, char const* msg) { BASISU_NOTE_UNUSED(user); fprintf(stderr, "tinydds: %s\n", msg); };
- cbs.allocFn = [](void* user, size_t size) -> void* { BASISU_NOTE_UNUSED(user); return malloc(size); };
- cbs.freeFn = [](void* user, void* memory) { BASISU_NOTE_UNUSED(user); free(memory); };
- cbs.readFn = [](void* user, void* buffer, size_t byteCount) -> size_t { return (size_t)fread(buffer, 1, byteCount, (FILE*)user); };
-
-#ifdef _MSC_VER
- cbs.seekFn = [](void* user, int64_t ofs) -> bool { return _fseeki64((FILE*)user, ofs, SEEK_SET) == 0; };
- cbs.tellFn = [](void* user) -> int64_t { return _ftelli64((FILE*)user); };
-#else
- cbs.seekFn = [](void* user, int64_t ofs) -> bool { return fseek((FILE*)user, (long)ofs, SEEK_SET) == 0; };
- cbs.tellFn = [](void* user) -> int64_t { return (int64_t)ftell((FILE*)user); };
-#endif
-
- FILE* pFile = fopen_safe(pFilename, "rb");
- if (!pFile)
- {
- error_printf("Can't open .DDS file \"%s\"\n", pFilename);
- return false;
- }
-
- // These are the formats AMD Compressonator supports in its UI.
- enum dds_fmt
- {
- cRGBA32,
- cRGBA_HALF,
- cRGBA_FLOAT
- };
-
- bool status = false;
- dds_fmt fmt = cRGBA32;
- uint32_t width = 0, height = 0;
- bool hdr_flag = false;
- TinyDDS_Format tfmt = TDDS_UNDEFINED;
-
- TinyDDS_ContextHandle ctx = TinyDDS_CreateContext(&cbs, pFile);
- if (!ctx)
- goto failure;
-
- status = TinyDDS_ReadHeader(ctx);
- if (!status)
- {
- error_printf("Failed parsing DDS header in file \"%s\"\n", pFilename);
- goto failure;
- }
-
- if ((!TinyDDS_Is2D(ctx)) || (TinyDDS_ArraySlices(ctx) > 1) || (TinyDDS_IsCubemap(ctx)))
- {
- error_printf("Unsupported DDS texture type in file \"%s\"\n", pFilename);
- goto failure;
- }
-
- width = TinyDDS_Width(ctx);
- height = TinyDDS_Height(ctx);
-
- if (!width || !height)
- {
- error_printf("DDS texture dimensions invalid in file \"%s\"\n", pFilename);
- goto failure;
- }
-
- if ((width > MAX_IMAGE_DIM) || (height > MAX_IMAGE_DIM))
- {
- error_printf("DDS texture dimensions too large in file \"%s\"\n", pFilename);
- goto failure;
- }
-
- tfmt = TinyDDS_GetFormat(ctx);
- switch (tfmt)
- {
- case TDDS_R8G8B8A8_SRGB:
- case TDDS_R8G8B8A8_UNORM:
- case TDDS_B8G8R8A8_SRGB:
- case TDDS_B8G8R8A8_UNORM:
- fmt = cRGBA32;
- break;
- case TDDS_R16G16B16A16_SFLOAT:
- fmt = cRGBA_HALF;
- hdr_flag = true;
- break;
- case TDDS_R32G32B32A32_SFLOAT:
- fmt = cRGBA_FLOAT;
- hdr_flag = true;
- break;
- default:
- error_printf("File \"%s\" has an unsupported DDS texture format (only supports RGBA/BGRA 32bpp, RGBA HALF float, or RGBA FLOAT)\n", pFilename);
- goto failure;
- }
-
- if (hdr_flag)
- hdr_mips.resize(TinyDDS_NumberOfMipmaps(ctx));
- else
- ldr_mips.resize(TinyDDS_NumberOfMipmaps(ctx));
-
- for (uint32_t level = 0; level < TinyDDS_NumberOfMipmaps(ctx); level++)
- {
- const uint32_t level_width = TinyDDS_MipMapReduce(width, level);
- const uint32_t level_height = TinyDDS_MipMapReduce(height, level);
- const uint32_t total_level_texels = level_width * level_height;
-
- const void* pImage = TinyDDS_ImageRawData(ctx, level);
- const uint32_t image_size = TinyDDS_ImageSize(ctx, level);
-
- if (fmt == cRGBA32)
- {
- ldr_mips[level].resize(level_width, level_height);
-
- if ((ldr_mips[level].get_total_pixels() * sizeof(uint32_t) != image_size))
- {
- assert(0);
- goto failure;
- }
-
- memcpy(ldr_mips[level].get_ptr(), pImage, image_size);
-
- if ((tfmt == TDDS_B8G8R8A8_SRGB) || (tfmt == TDDS_B8G8R8A8_UNORM))
- {
- // Swap R and B components.
- uint32_t *pTexels = (uint32_t *)ldr_mips[level].get_ptr();
- for (uint32_t i = 0; i < total_level_texels; i++)
- {
- const uint32_t v = pTexels[i];
- const uint32_t r = (v >> 16) & 0xFF;
- const uint32_t b = v & 0xFF;
- pTexels[i] = r | (b << 16) | (v & 0xFF00FF00);
- }
- }
- }
- else if (fmt == cRGBA_FLOAT)
- {
- hdr_mips[level].resize(level_width, level_height);
-
- if ((hdr_mips[level].get_total_pixels() * sizeof(float) * 4 != image_size))
- {
- assert(0);
- goto failure;
- }
-
- memcpy(hdr_mips[level].get_ptr(), pImage, image_size);
- }
- else if (fmt == cRGBA_HALF)
- {
- hdr_mips[level].resize(level_width, level_height);
-
- if ((hdr_mips[level].get_total_pixels() * sizeof(basist::half_float) * 4 != image_size))
- {
- assert(0);
- goto failure;
- }
-
- // Unpack half to float.
- const basist::half_float* pSrc_comps = static_cast<const basist::half_float*>(pImage);
- vec4F* pDst_texels = hdr_mips[level].get_ptr();
-
- for (uint32_t i = 0; i < total_level_texels; i++)
- {
- (*pDst_texels)[0] = basist::half_to_float(pSrc_comps[0]);
- (*pDst_texels)[1] = basist::half_to_float(pSrc_comps[1]);
- (*pDst_texels)[2] = basist::half_to_float(pSrc_comps[2]);
- (*pDst_texels)[3] = basist::half_to_float(pSrc_comps[3]);
-
- pSrc_comps += 4;
- pDst_texels++;
- } // y
- }
- } // level
-
- TinyDDS_DestroyContext(ctx);
- fclose(pFile);
-
- return true;
-
- failure:
- if (ctx)
- TinyDDS_DestroyContext(ctx);
-
- if (pFile)
- fclose(pFile);
-
return false;
}

View File

@ -1,5 +1,5 @@
// basisu.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
//
// Licensed under the Apache License, Version 2.0 (the "License");
@ -117,13 +117,26 @@ namespace basisu
typedef basisu::vector<uint64_t> uint64_vec;
typedef basisu::vector<int> int_vec;
typedef basisu::vector<bool> bool_vec;
typedef basisu::vector<float> float_vec;
void enable_debug_printf(bool enabled);
void debug_printf(const char *pFmt, ...);
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#endif
template <typename T> inline void clear_obj(T& obj) { memset(&obj, 0, sizeof(obj)); }
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
template <typename T0, typename T1> inline T0 lerp(T0 a, T0 b, T1 c) { return a + (b - a) * c; }
template <typename S> inline S maximum(S a, S b) { return (a > b) ? a : b; }
@ -162,10 +175,45 @@ namespace basisu
template<typename T> inline T open_range_check(T v, T minv, T maxv) { assert(v >= minv && v < maxv); BASISU_NOTE_UNUSED(minv); BASISU_NOTE_UNUSED(maxv); return v; }
template<typename T> inline T open_range_check(T v, T maxv) { assert(v < maxv); BASISU_NOTE_UNUSED(maxv); return v; }
// Open interval
inline bool in_bounds(int v, int l, int h)
{
return (v >= l) && (v < h);
}
// Closed interval
inline bool in_range(int v, int l, int h)
{
return (v >= l) && (v <= h);
}
inline uint32_t total_bits(uint32_t v) { uint32_t l = 0; for ( ; v > 0U; ++l) v >>= 1; return l; }
template<typename T> inline T saturate(T val) { return clamp(val, 0.0f, 1.0f); }
inline uint32_t get_bit(uint32_t src, int ndx)
{
assert(in_bounds(ndx, 0, 32));
return (src >> ndx) & 1;
}
inline bool is_bit_set(uint32_t src, int ndx)
{
return get_bit(src, ndx) != 0;
}
inline uint32_t get_bits(uint32_t val, int low, int high)
{
const int num_bits = (high - low) + 1;
assert(in_range(num_bits, 1, 32));
val >>= low;
if (num_bits != 32)
val &= ((1u << num_bits) - 1);
return val;
}
template<typename T, typename R> inline void append_vector(T &vec, const R *pObjs, size_t n)
{
if (n)
@ -267,6 +315,11 @@ namespace basisu
return true;
}
static inline uint32_t read_le_word(const uint8_t* pBytes)
{
return (pBytes[1] << 8U) | (pBytes[0]);
}
static inline uint32_t read_le_dword(const uint8_t *pBytes)
{
return (pBytes[3] << 24U) | (pBytes[2] << 16U) | (pBytes[1] << 8U) | (pBytes[0]);
@ -303,6 +356,10 @@ namespace basisu
return *this;
}
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#endif
inline operator uint32_t() const
{
switch (NumBytes)
@ -354,6 +411,9 @@ namespace basisu
}
}
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
};
enum eZero { cZero };
@ -402,8 +462,11 @@ namespace basisu
cBC3, // DXT5 (BC4/DXT5A block followed by a BC1/DXT1 block)
cBC4, // DXT5A
cBC5, // 3DC/DXN (two BC4/DXT5A blocks)
cBC6HSigned, // HDR
cBC6HUnsigned, // HDR
cBC7,
cASTC4x4, // LDR only
cASTC_LDR_4x4, // ASTC 4x4 LDR only
cASTC_HDR_4x4, // ASTC 4x4 HDR only (but may use LDR ASTC blocks internally)
cPVRTC1_4_RGB,
cPVRTC1_4_RGBA,
cATC_RGB,
@ -413,17 +476,22 @@ namespace basisu
cETC2_R11_EAC,
cETC2_RG11_EAC,
cUASTC4x4,
cUASTC_HDR_4x4,
cBC1_NV,
cBC1_AMD,
// Uncompressed/raw pixels
cRGBA32,
cRGB565,
cBGR565,
cRGBA4444,
cABGR4444
cABGR4444,
cRGBA_HALF,
cRGB_HALF,
cRGB_9E5
};
// This is bytes per block for GPU formats, or bytes per texel for uncompressed formats.
inline uint32_t get_bytes_per_block(texture_format fmt)
{
switch (fmt)
@ -443,13 +511,27 @@ namespace basisu
case texture_format::cETC2_R11_EAC:
return 8;
case texture_format::cRGBA32:
return sizeof(uint32_t) * 16;
case texture_format::cRGB_9E5:
return sizeof(uint32_t);
case texture_format::cRGB_HALF:
return sizeof(uint16_t) * 3;
case texture_format::cRGBA_HALF:
return sizeof(uint16_t) * 4;
case texture_format::cRGB565:
case texture_format::cBGR565:
case texture_format::cRGBA4444:
case texture_format::cABGR4444:
return sizeof(uint16_t);
default:
break;
}
// Everything else is 16 bytes/block.
return 16;
}
// This is qwords per block for GPU formats, or not valid for uncompressed formats.
inline uint32_t get_qwords_per_block(texture_format fmt)
{
return get_bytes_per_block(fmt) >> 3;
@ -473,6 +555,17 @@ namespace basisu
BASISU_NOTE_UNUSED(fmt);
return 4;
}
inline bool is_hdr_texture_format(texture_format fmt)
{
if (fmt == texture_format::cASTC_HDR_4x4)
return true;
if (fmt == texture_format::cUASTC_HDR_4x4)
return true;
if ((fmt == texture_format::cBC6HSigned) || (fmt == texture_format::cBC6HUnsigned))
return true;
return false;
}
} // namespace basisu

View File

@ -0,0 +1,102 @@
// File: basisu_astc_hdr_core.h
#pragma once
#include "basisu_astc_helpers.h"
namespace basist
{
struct astc_blk
{
uint8_t m_vals[16];
};
// ASTC_HDR_MAX_VAL is the maximum color component value that can be encoded.
// If the input has values higher than this, they need to be linearly scaled so all values are between [0,ASTC_HDR_MAX_VAL], and the linear scaling inverted in the shader.
const float ASTC_HDR_MAX_VAL = 65216.0f; // actually MAX_QLOG12_VAL
// Maximum usable QLOG encodings, and their floating point equivalent values, that don't result in NaN/Inf's.
const uint32_t MAX_QLOG7 = 123;
//const float MAX_QLOG7_VAL = 55296.0f;
const uint32_t MAX_QLOG8 = 247;
//const float MAX_QLOG8_VAL = 60416.0f;
const uint32_t MAX_QLOG9 = 495;
//const float MAX_QLOG9_VAL = 62976.0f;
const uint32_t MAX_QLOG10 = 991;
//const float MAX_QLOG10_VAL = 64256.0f;
const uint32_t MAX_QLOG11 = 1983;
//const float MAX_QLOG11_VAL = 64896.0f;
const uint32_t MAX_QLOG12 = 3967;
//const float MAX_QLOG12_VAL = 65216.0f;
const uint32_t MAX_QLOG16 = 63487;
const float MAX_QLOG16_VAL = 65504.0f;
const uint32_t NUM_MODE11_ENDPOINTS = 6, NUM_MODE7_ENDPOINTS = 4;
// Notes:
// qlog16_to_half(half_to_qlog16(half_val_as_int)) == half_val_as_int (is lossless)
// However, this is not lossless in the general sense.
inline half_float qlog16_to_half_slow(uint32_t qlog16)
{
assert(qlog16 <= 0xFFFF);
int C = qlog16;
int E = (C & 0xF800) >> 11;
int M = C & 0x7FF;
int Mt;
if (M < 512)
Mt = 3 * M;
else if (M >= 1536)
Mt = 5 * M - 2048;
else
Mt = 4 * M - 512;
int Cf = (E << 10) + (Mt >> 3);
return (half_float)Cf;
}
// This is not lossless
inline half_float qlog_to_half_slow(uint32_t qlog, uint32_t bits)
{
assert((bits >= 7U) && (bits <= 16U));
assert(qlog < (1U << bits));
int C = qlog << (16 - bits);
return qlog16_to_half_slow(C);
}
void astc_hdr_core_init();
void decode_mode7_to_qlog12_ise20(
const uint8_t* pEndpoints,
int e[2][3],
int* pScale);
bool decode_mode7_to_qlog12(
const uint8_t* pEndpoints,
int e[2][3],
int* pScale,
uint32_t ise_endpoint_range);
void decode_mode11_to_qlog12_ise20(
const uint8_t* pEndpoints,
int e[2][3]);
bool decode_mode11_to_qlog12(
const uint8_t* pEndpoints,
int e[2][3],
uint32_t ise_endpoint_range);
bool transcode_bc6h_1subset(half_float h_e[3][2], const astc_helpers::log_astc_block& best_blk, bc6h_block& transcoded_bc6h_blk);
bool transcode_bc6h_2subsets(uint32_t common_part_index, const astc_helpers::log_astc_block& best_blk, bc6h_block& transcoded_bc6h_blk);
bool astc_hdr_transcode_to_bc6h(const astc_blk& src_blk, bc6h_block& dst_blk);
bool astc_hdr_transcode_to_bc6h(const astc_helpers::log_astc_block& log_blk, bc6h_block& dst_blk);
} // namespace basist

File diff suppressed because it is too large Load Diff

View File

@ -188,8 +188,9 @@ namespace basisu
#define BASISU_IS_SCALAR_TYPE(T) (scalar_type<T>::cFlag)
#if defined(__GNUC__) && __GNUC__<5
#define BASISU_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__)
#if !defined(BASISU_HAVE_STD_TRIVIALLY_COPYABLE) && defined(__GNUC__) && __GNUC__<5
//#define BASISU_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__)
#define BASISU_IS_TRIVIALLY_COPYABLE(...) __is_trivially_copyable(__VA_ARGS__)
#else
#define BASISU_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value
#endif
@ -286,8 +287,19 @@ namespace basisu
if (BASISU_IS_BITWISE_COPYABLE(T))
{
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#endif
if ((m_p) && (other.m_p))
memcpy(m_p, other.m_p, m_size * sizeof(T));
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
}
else
{
@ -330,8 +342,19 @@ namespace basisu
if (BASISU_IS_BITWISE_COPYABLE(T))
{
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#endif
if ((m_p) && (other.m_p))
memcpy(m_p, other.m_p, other.m_size * sizeof(T));
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
}
else
{
@ -501,7 +524,7 @@ namespace basisu
if (new_capacity > m_capacity)
{
if (!increase_capacity(new_capacity, false))
if (!increase_capacity(new_capacity, false, true))
return false;
}
else if (new_capacity < m_capacity)
@ -509,7 +532,8 @@ namespace basisu
// Must work around the lack of a "decrease_capacity()" method.
// This case is rare enough in practice that it's probably not worth implementing an optimized in-place resize.
vector tmp;
tmp.increase_capacity(helpers::maximum(m_size, new_capacity), false);
if (!tmp.increase_capacity(helpers::maximum(m_size, new_capacity), false, true))
return false;
tmp = *this;
swap(tmp);
}
@ -750,7 +774,21 @@ namespace basisu
}
// Copy "down" the objects to preserve, filling in the empty slots.
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#endif
memmove(pDst, pSrc, num_to_move * sizeof(T));
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
}
else
{
@ -1003,7 +1041,21 @@ namespace basisu
inline void set_all(const T& o)
{
if ((sizeof(T) == 1) && (scalar_type<T>::cFlag))
{
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#endif
memset(m_p, *reinterpret_cast<const uint8_t*>(&o), m_size);
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
}
else
{
T* pDst = m_p;
@ -1029,7 +1081,7 @@ namespace basisu
// Important: This method is used in Basis Universal. If you change how this container allocates memory, you'll need to change any users of this method.
inline bool grant_ownership(T* p, uint32_t size, uint32_t capacity)
{
// To to prevent the caller from obviously shooting themselves in the foot.
// To prevent the caller from obviously shooting themselves in the foot.
if (((p + capacity) > m_p) && (p < (m_p + m_capacity)))
{
// Can grant ownership of a block inside the container itself!

View File

@ -19,23 +19,30 @@ namespace basisu
if (m_capacity >= min_new_capacity)
return true;
size_t new_capacity = min_new_capacity;
if ((grow_hint) && (!helpers::is_power_of_2((uint64_t)new_capacity)))
uint64_t new_capacity_u64 = min_new_capacity;
if ((grow_hint) && (!helpers::is_power_of_2(new_capacity_u64)))
new_capacity_u64 = helpers::next_pow2(new_capacity_u64);
size_t new_capacity = (size_t)new_capacity_u64;
if (new_capacity != new_capacity_u64)
{
new_capacity = (size_t)helpers::next_pow2((uint64_t)new_capacity);
assert(new_capacity && (new_capacity > m_capacity));
if (new_capacity < min_new_capacity)
{
if (nofail)
return false;
fprintf(stderr, "vector too large\n");
abort();
}
if (nofail)
return false;
fprintf(stderr, "elemental_vector::increase_capacity: vector too large\n");
abort();
}
const size_t desired_size = element_size * new_capacity;
const uint64_t desired_size_u64 = (uint64_t)element_size * new_capacity;
const size_t desired_size = (size_t)desired_size_u64;
if (desired_size_u64 != desired_size)
{
if (nofail)
return false;
fprintf(stderr, "elemental_vector::increase_capacity: vector too large\n");
abort();
}
size_t actual_size = 0;
if (!pMover)
{
@ -46,11 +53,7 @@ namespace basisu
return false;
char buf[256];
#ifdef _MSC_VER
sprintf_s(buf, sizeof(buf), "vector: realloc() failed allocating %u bytes", (uint32_t)desired_size);
#else
sprintf(buf, "vector: realloc() failed allocating %u bytes", (uint32_t)desired_size);
#endif
snprintf(buf, sizeof(buf), "elemental_vector::increase_capacity: realloc() failed allocating %zu bytes", desired_size);
fprintf(stderr, "%s", buf);
abort();
}
@ -75,11 +78,7 @@ namespace basisu
return false;
char buf[256];
#ifdef _MSC_VER
sprintf_s(buf, sizeof(buf), "vector: malloc() failed allocating %u bytes", (uint32_t)desired_size);
#else
sprintf(buf, "vector: malloc() failed allocating %u bytes", (uint32_t)desired_size);
#endif
snprintf(buf, sizeof(buf), "elemental_vector::increase_capacity: malloc() failed allocating %zu bytes", desired_size);
fprintf(stderr, "%s", buf);
abort();
}

View File

@ -1,5 +1,5 @@
// basis_file_headers.h
// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -89,7 +89,8 @@ namespace basist
enum class basis_tex_format
{
cETC1S = 0,
cUASTC4x4 = 1
cUASTC4x4 = 1,
cUASTC_HDR_4x4 = 2
};
struct basis_file_header

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
// basisu_transcoder.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
//
// Licensed under the Apache License, Version 2.0 (the "License");
@ -29,6 +29,7 @@
// Set BASISU_FORCE_DEVEL_MESSAGES to 1 to enable debug printf()'s whenever an error occurs, for easier debugging during development.
#ifndef BASISU_FORCE_DEVEL_MESSAGES
// TODO - disable before checking in
#define BASISU_FORCE_DEVEL_MESSAGES 0
#endif
@ -55,7 +56,7 @@ namespace basist
cTFETC2_RGBA = 1, // Opaque+alpha, ETC2_EAC_A8 block followed by a ETC1 block, alpha channel will be opaque for opaque .basis files
// BC1-5, BC7 (desktop, some mobile devices)
cTFBC1_RGB = 2, // Opaque only, no punchthrough alpha support yet, transcodes alpha slice if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
cTFBC1_RGB = 2, // Opaque only, no punchthrough alpha support yet, transcodes alpha slice if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
cTFBC3_RGBA = 3, // Opaque+alpha, BC4 followed by a BC1 block, alpha channel will be opaque for opaque .basis files
cTFBC4_R = 4, // Red only, alpha slice is transcoded to output if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
cTFBC5_RG = 5, // XY: Two BC4 blocks, X=R and Y=Alpha, .basis file should have alpha data (if not Y will be all 255's)
@ -63,10 +64,11 @@ namespace basist
// PVRTC1 4bpp (mobile, PowerVR devices)
cTFPVRTC1_4_RGB = 8, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified, nearly lowest quality of any texture format.
cTFPVRTC1_4_RGBA = 9, // Opaque+alpha, most useful for simple opacity maps. If .basis file doesn't have alpha cTFPVRTC1_4_RGB will be used instead. Lowest quality of any supported texture format.
cTFPVRTC1_4_RGBA = 9, // Opaque+alpha, most useful for simple opacity maps. If .basis file doesn't have alpha cTFPVRTC1_4_RGB will be used instead. Lowest quality of any supported texture format.
// ASTC (mobile, Intel devices, hopefully all desktop GPU's one day)
cTFASTC_4x4_RGBA = 10, // Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files. Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.
cTFASTC_4x4_RGBA = 10, // LDR. Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files.
// LDR: Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.
// ATC (mobile, Adreno devices, this is a niche format)
cTFATC_RGB = 11, // Opaque, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. ATI ATC (GL_ATC_RGB_AMD)
@ -74,8 +76,8 @@ namespace basist
// FXT1 (desktop, Intel devices, this is a super obscure format)
cTFFXT1_RGB = 17, // Opaque only, uses exclusively CC_MIXED blocks. Notable for having a 8x4 block size. GL_3DFX_texture_compression_FXT1 is supported on Intel integrated GPU's (such as HD 630).
// Punch-through alpha is relatively easy to support, but full alpha is harder. This format is only here for completeness so opaque-only is fine for now.
// See the BASISU_USE_ORIGINAL_3DFX_FXT1_ENCODING macro in basisu_transcoder_internal.h.
// Punch-through alpha is relatively easy to support, but full alpha is harder. This format is only here for completeness so opaque-only is fine for now.
// See the BASISU_USE_ORIGINAL_3DFX_FXT1_ENCODING macro in basisu_transcoder_internal.h.
cTFPVRTC2_4_RGB = 18, // Opaque-only, almost BC1 quality, much faster to transcode and supports arbitrary texture dimensions (unlike PVRTC1 RGB).
cTFPVRTC2_4_RGBA = 19, // Opaque+alpha, slower to encode than cTFPVRTC2_4_RGB. Premultiplied alpha is highly recommended, otherwise the color channel can leak into the alpha channel on transparent blocks.
@ -83,13 +85,22 @@ namespace basist
cTFETC2_EAC_R11 = 20, // R only (ETC2 EAC R11 unsigned)
cTFETC2_EAC_RG11 = 21, // RG only (ETC2 EAC RG11 unsigned), R=opaque.r, G=alpha - for tangent space normal maps
cTFBC6H = 22, // HDR, RGB only, unsigned
cTFASTC_HDR_4x4_RGBA = 23, // HDR, RGBA (currently UASTC HDR is only RGB), unsigned
// Uncompressed (raw pixel) formats
// Note these uncompressed formats (RGBA32, 565, and 4444) can only be transcoded to from LDR input files (ETC1S or UASTC LDR).
cTFRGBA32 = 13, // 32bpp RGBA image stored in raster (not block) order in memory, R is first byte, A is last byte.
cTFRGB565 = 14, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 11
cTFBGR565 = 15, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 0
cTFRGBA4444 = 16, // 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 0
cTFRGBA4444 = 16, // 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 0
// Note these uncompressed formats (HALF and 9E5) can only be transcoded to from HDR input files (UASTC HDR).
cTFRGB_HALF = 24, // 48bpp RGB half (16-bits/component, 3 components)
cTFRGBA_HALF = 25, // 64bpp RGBA half (16-bits/component, 4 components) (A will always currently 1.0, UASTC_HDR doesn't support alpha)
cTFRGB_9E5 = 26, // 32bpp RGB 9E5 (shared exponent, positive only, see GL_EXT_texture_shared_exponent)
cTFTotalTextureFormats = 22,
cTFTotalTextureFormats = 27,
// Old enums for compatibility with code compiled against previous versions
cTFETC1 = cTFETC1_RGB,
@ -124,6 +135,9 @@ namespace basist
// Returns true if the format supports an alpha channel.
bool basis_transcoder_format_has_alpha(transcoder_texture_format fmt);
// Returns true if the format is HDR.
bool basis_transcoder_format_is_hdr(transcoder_texture_format fmt);
// Returns the basisu::texture_format corresponding to the specified transcoder_texture_format.
basisu::texture_format basis_get_basisu_texture_format(transcoder_texture_format fmt);
@ -142,7 +156,7 @@ namespace basist
// Returns the block height for the specified texture format, which is currently always 4.
uint32_t basis_get_block_height(transcoder_texture_format tex_type);
// Returns true if the specified format was enabled at compile time.
// Returns true if the specified format was enabled at compile time, and is supported for the specific basis/ktx2 texture format (ETC1S, UASTC, or UASTC HDR).
bool basis_is_format_supported(transcoder_texture_format tex_type, basis_tex_format fmt = basis_tex_format::cETC1S);
// Validates that the output buffer is large enough to hold the entire transcoded texture.
@ -317,6 +331,42 @@ namespace basist
int channel0 = -1, int channel1 = -1);
};
class basisu_lowlevel_uastc_hdr_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_uastc_hdr_transcoder();
bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
{
return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt,
output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
}
// Container independent transcoding
bool transcode_image(
transcoder_texture_format target_format,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint32_t slice_offset, uint32_t slice_length,
uint32_t decode_flags = 0,
bool has_alpha = false,
bool is_video = false,
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr,
uint32_t output_rows_in_pixels = 0,
int channel0 = -1, int channel1 = -1);
};
struct basisu_slice_info
{
uint32_t m_orig_width;
@ -530,6 +580,7 @@ namespace basist
private:
mutable basisu_lowlevel_etc1s_transcoder m_lowlevel_etc1s_decoder;
mutable basisu_lowlevel_uastc_transcoder m_lowlevel_uastc_decoder;
mutable basisu_lowlevel_uastc_hdr_transcoder m_lowlevel_uastc_hdr_decoder;
bool m_ready_to_transcode;
@ -612,10 +663,12 @@ namespace basist
#pragma pack(pop)
const uint32_t KTX2_VK_FORMAT_UNDEFINED = 0;
const uint32_t KTX2_FORMAT_UASTC_4x4_SFLOAT_BLOCK = 1000066000; // TODO, is this correct?
const uint32_t KTX2_KDF_DF_MODEL_UASTC = 166;
const uint32_t KTX2_KDF_DF_MODEL_UASTC_HDR = 167;
const uint32_t KTX2_KDF_DF_MODEL_ETC1S = 163;
const uint32_t KTX2_IMAGE_IS_P_FRAME = 2;
const uint32_t KTX2_UASTC_BLOCK_SIZE = 16;
const uint32_t KTX2_UASTC_BLOCK_SIZE = 16; // also the block size for UASTC_HDR
const uint32_t KTX2_MAX_SUPPORTED_LEVEL_COUNT = 16; // this is an implementation specific constraint and can be increased
// The KTX2 transfer functions supported by KTX2
@ -800,13 +853,15 @@ namespace basist
// Returns 0 or the number of layers in the texture array or texture video. Valid after init().
uint32_t get_layers() const { return m_header.m_layer_count; }
// Returns cETC1S or cUASTC4x4. Valid after init().
// Returns cETC1S, cUASTC4x4, or cUASTC_HDR_4x4. Valid after init().
basist::basis_tex_format get_format() const { return m_format; }
bool is_etc1s() const { return get_format() == basist::basis_tex_format::cETC1S; }
bool is_uastc() const { return get_format() == basist::basis_tex_format::cUASTC4x4; }
bool is_hdr() const { return get_format() == basist::basis_tex_format::cUASTC_HDR_4x4; }
// Returns true if the ETC1S file has two planes (typically RGBA, or RRRG), or true if the UASTC file has alpha data. Valid after init().
uint32_t get_has_alpha() const { return m_has_alpha; }
@ -913,6 +968,7 @@ namespace basist
basist::basisu_lowlevel_etc1s_transcoder m_etc1s_transcoder;
basist::basisu_lowlevel_uastc_transcoder m_uastc_transcoder;
basist::basisu_lowlevel_uastc_hdr_transcoder m_uastc_hdr_transcoder;
ktx2_transcoder_state m_def_transcoder_state;

View File

@ -1,5 +1,5 @@
// basisu_transcoder_internal.h - Universal texture format transcoder library.
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
//
@ -20,8 +20,9 @@
#pragma warning (disable: 4127) // conditional expression is constant
#endif
#define BASISD_LIB_VERSION 116
#define BASISD_VERSION_STRING "01.16"
// v1.50: Added UASTC HDR support
#define BASISD_LIB_VERSION 150
#define BASISD_VERSION_STRING "01.50"
#ifdef _DEBUG
#define BASISD_BUILD_DEBUG
@ -82,9 +83,15 @@ namespace basist
cRGBA4444_ALPHA,
cRGBA4444_COLOR_OPAQUE,
cRGBA4444,
cRGBA_HALF,
cRGB_HALF,
cRGB_9E5,
cUASTC_4x4,
cUASTC_4x4, // LDR, universal
cUASTC_HDR_4x4, // HDR, transcodes only to 4x4 HDR ASTC, BC6H, or uncompressed
cBC6H,
cASTC_HDR_4x4,
cTotalBlockFormats
};
@ -264,8 +271,8 @@ namespace basist
}
const basisu::uint8_vec &get_code_sizes() const { return m_code_sizes; }
const basisu::int_vec get_lookup() const { return m_lookup; }
const basisu::int16_vec get_tree() const { return m_tree; }
const basisu::int_vec &get_lookup() const { return m_lookup; }
const basisu::int16_vec &get_tree() const { return m_tree; }
bool is_valid() const { return m_code_sizes.size() > 0; }
@ -789,7 +796,198 @@ namespace basist
};
bool basis_block_format_is_uncompressed(block_format tex_type);
//------------------------------------
typedef uint16_t half_float;
const double MIN_DENORM_HALF_FLOAT = 0.000000059604645; // smallest positive subnormal number
const double MIN_HALF_FLOAT = 0.00006103515625; // smallest positive normal number
const double MAX_HALF_FLOAT = 65504.0; // largest normal number
inline uint32_t get_bits(uint32_t val, int low, int high)
{
const int num_bits = (high - low) + 1;
assert((num_bits >= 1) && (num_bits <= 32));
val >>= low;
if (num_bits != 32)
val &= ((1u << num_bits) - 1);
return val;
}
inline bool is_half_inf_or_nan(half_float v)
{
return get_bits(v, 10, 14) == 31;
}
inline bool is_half_denorm(half_float v)
{
int e = (v >> 10) & 31;
return !e;
}
inline int get_half_exp(half_float v)
{
int e = ((v >> 10) & 31);
return e ? (e - 15) : -14;
}
inline int get_half_mantissa(half_float v)
{
if (is_half_denorm(v))
return v & 0x3FF;
return (v & 0x3FF) | 0x400;
}
inline float get_half_mantissaf(half_float v)
{
return ((float)get_half_mantissa(v)) / 1024.0f;
}
inline int get_half_sign(half_float v)
{
return v ? ((v & 0x8000) ? -1 : 1) : 0;
}
inline bool half_is_signed(half_float v)
{
return (v & 0x8000) != 0;
}
#if 0
int hexp = get_half_exp(Cf);
float hman = get_half_mantissaf(Cf);
int hsign = get_half_sign(Cf);
float k = powf(2.0f, hexp) * hman * hsign;
if (is_half_inf_or_nan(Cf))
k = std::numeric_limits<float>::quiet_NaN();
#endif
half_float float_to_half(float val);
inline float half_to_float(half_float hval)
{
union { float f; uint32_t u; } x = { 0 };
uint32_t s = ((uint32_t)hval >> 15) & 1;
uint32_t e = ((uint32_t)hval >> 10) & 0x1F;
uint32_t m = (uint32_t)hval & 0x3FF;
if (!e)
{
if (!m)
{
// +- 0
x.u = s << 31;
return x.f;
}
else
{
// denormalized
while (!(m & 0x00000400))
{
m <<= 1;
--e;
}
++e;
m &= ~0x00000400;
}
}
else if (e == 31)
{
if (m == 0)
{
// +/- INF
x.u = (s << 31) | 0x7f800000;
return x.f;
}
else
{
// +/- NaN
x.u = (s << 31) | 0x7f800000 | (m << 13);
return x.f;
}
}
e = e + (127 - 15);
m = m << 13;
assert(s <= 1);
assert(m <= 0x7FFFFF);
assert(e <= 255);
x.u = m | (e << 23) | (s << 31);
return x.f;
}
// Originally from bc6h_enc.h
void bc6h_enc_init();
const uint32_t MAX_BLOG16_VAL = 0xFFFF;
// BC6H internals
const uint32_t NUM_BC6H_MODES = 14;
const uint32_t BC6H_LAST_MODE_INDEX = 13;
const uint32_t BC6H_FIRST_1SUBSET_MODE_INDEX = 10; // in the MS docs, this is "mode 11" (where the first mode is 1), 60 bits for endpoints (10.10, 10.10, 10.10), 63 bits for weights
const uint32_t TOTAL_BC6H_PARTITION_PATTERNS = 32;
extern const uint8_t g_bc6h_mode_sig_bits[NUM_BC6H_MODES][4]; // base, r, g, b
struct bc6h_bit_layout
{
int8_t m_comp; // R=0,G=1,B=2,D=3 (D=partition index)
int8_t m_index; // 0-3, 0-1 Low/High subset 1, 2-3 Low/High subset 2, -1=partition index (d)
int8_t m_last_bit;
int8_t m_first_bit; // may be -1 if a single bit, may be >m_last_bit if reversed
};
const uint32_t MAX_BC6H_LAYOUT_INDEX = 25;
extern const bc6h_bit_layout g_bc6h_bit_layouts[NUM_BC6H_MODES][MAX_BC6H_LAYOUT_INDEX];
extern const uint8_t g_bc6h_2subset_patterns[TOTAL_BC6H_PARTITION_PATTERNS][4][4]; // [y][x]
extern const uint8_t g_bc6h_weight3[8];
extern const uint8_t g_bc6h_weight4[16];
extern const int8_t g_bc6h_mode_lookup[32];
// Converts b16 to half float
inline half_float bc6h_blog16_to_half(uint32_t comp)
{
assert(comp <= 0xFFFF);
// scale the magnitude by 31/64
comp = (comp * 31u) >> 6u;
return (half_float)comp;
}
const uint32_t MAX_BC6H_HALF_FLOAT_AS_UINT = 0x7BFF;
// Inverts bc6h_blog16_to_half().
// Returns the nearest blog16 given a half value.
inline uint32_t bc6h_half_to_blog16(half_float h)
{
assert(h <= MAX_BC6H_HALF_FLOAT_AS_UINT);
return (h * 64 + 30) / 31;
}
struct bc6h_block
{
uint8_t m_bytes[16];
};
void bc6h_enc_block_mode10(bc6h_block* pPacked_block, const half_float pEndpoints[3][2], const uint8_t* pWeights);
void bc6h_enc_block_1subset_4bit_weights(bc6h_block* pPacked_block, const half_float pEndpoints[3][2], const uint8_t* pWeights);
void bc6h_enc_block_1subset_mode9_3bit_weights(bc6h_block* pPacked_block, const half_float pEndpoints[3][2], const uint8_t* pWeights);
void bc6h_enc_block_1subset_3bit_weights(bc6h_block* pPacked_block, const half_float pEndpoints[3][2], const uint8_t* pWeights);
void 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); // pEndpoints[subset][comp][lh_index]
void 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); // pEndpoints[subset][comp][lh_index]
bool bc6h_enc_block_solid_color(bc6h_block* pPacked_block, const half_float pColor[3]);
} // namespace basist

View File

@ -1,4 +1,4 @@
// Copyright (C) 2017-2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2017-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,4 +1,4 @@
// Copyright (C) 2017-2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2017-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -13,6 +13,7 @@ namespace basist
const uint32_t UASTC_MODE_INDEX_SOLID_COLOR = 8;
const uint32_t TOTAL_ASTC_BC7_COMMON_PARTITIONS2 = 30;
const uint32_t TOTAL_ASTC_BC6H_COMMON_PARTITIONS2 = 27; // BC6H only supports only 5-bit pattern indices, BC7 supports 4-bit or 6-bit
const uint32_t TOTAL_ASTC_BC7_COMMON_PARTITIONS3 = 11;
const uint32_t TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS = 19;