diff --git a/CMakeLists.txt b/CMakeLists.txt
index c5f8a00..4fa46d3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,33 +19,35 @@ else()
message("Building 32-bit")
endif(BUILD_X64)
-set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
-set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
+if (NOT MSVC)
+ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
+ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
-set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
-set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
-set(CMAKE_CXX_FLAGS -std=c++11)
-set(GCC_COMPILE_FLAGS "-fvisibility=hidden -fvisibility-inlines-hidden -fPIC -fno-strict-aliasing -D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -Wextra -Wno-unused-local-typedefs -Wno-unused-value -Wno-unused-parameter -Wno-unused-variable -Wno-reorder")
+ set(CMAKE_CXX_FLAGS -std=c++11)
+ set(GCC_COMPILE_FLAGS "-fvisibility=hidden -fPIC -fno-strict-aliasing -D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -Wextra -Wno-unused-local-typedefs -Wno-unused-value -Wno-unused-parameter -Wno-unused-variable")
-if (NOT BUILD_X64)
- set(GCC_COMPILE_FLAGS "${GCC_COMPILE_FLAGS} -m32")
+ if (NOT BUILD_X64)
+ set(GCC_COMPILE_FLAGS "${GCC_COMPILE_FLAGS} -m32")
+ endif()
+
+ if (STATIC)
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_LINK_FLAGS} -static-libgcc -static-libstdc++ -static")
+ else()
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_LINK_FLAGS} -Wl,-rpath .")
+ endif(STATIC)
+
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCC_COMPILE_FLAGS}")
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GCC_COMPILE_FLAGS}")
+ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${GCC_COMPILE_FLAGS} -D_DEBUG")
+
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMPILE_FLAGS}")
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${GCC_COMPILE_FLAGS}")
+ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${GCC_COMPILE_FLAGS} -D_DEBUG")
endif()
-if (STATIC)
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_LINK_FLAGS} -static-libgcc -static-libstdc++ -static")
-else()
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_LINK_FLAGS} -Wl,-rpath .")
-endif(STATIC)
-
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCC_COMPILE_FLAGS}")
-set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GCC_COMPILE_FLAGS}")
-set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${GCC_COMPILE_FLAGS} -D_DEBUG")
-
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMPILE_FLAGS}")
-set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${GCC_COMPILE_FLAGS}")
-set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${GCC_COMPILE_FLAGS} -D_DEBUG")
-
set(BASISU_SRC_LIST ${COMMON_SRC_LIST}
basisu_backend.cpp
basisu_basis_file.cpp
@@ -61,28 +63,36 @@ set(BASISU_SRC_LIST ${COMMON_SRC_LIST}
basisu_ssim.cpp
basisu_tool.cpp
basisu_astc_decomp.cpp
+ basisu_uastc_enc.cpp
+ basisu_bc7enc.cpp
lodepng.cpp
+ apg_bmp.c
transcoder/basisu_transcoder.cpp
)
if (APPLE)
- set(BIN_DIRECTORY "bin_osx")
+ set(BIN_DIRECTORY "bin_osx")
else()
- set(BIN_DIRECTORY "bin")
+ set(BIN_DIRECTORY "bin")
endif()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${BIN_DIRECTORY})
add_executable(basisu ${BASISU_SRC_LIST})
-target_link_libraries(basisu m pthread)
+
+if (NOT MSVC)
+ target_link_libraries(basisu m pthread)
+endif()
install(TARGETS basisu DESTINATION bin)
-if (CMAKE_BUILD_TYPE STREQUAL Release)
- if (APPLE)
- add_custom_command(TARGET basisu POST_BUILD COMMAND strip -X -x ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/basisu)
- else()
- add_custom_command(TARGET basisu POST_BUILD COMMAND strip -g -X -x ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/basisu)
- endif()
+if (NOT MSVC)
+ if (CMAKE_BUILD_TYPE STREQUAL Release)
+ if (APPLE)
+ add_custom_command(TARGET basisu POST_BUILD COMMAND strip -X -x ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/basisu)
+ else()
+ add_custom_command(TARGET basisu POST_BUILD COMMAND strip -g -X -x ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/basisu)
+ endif()
+ endif()
endif()
diff --git a/basisu.vcxproj b/basisu.vcxproj
index fafad29..c61c4a2 100644
--- a/basisu.vcxproj
+++ b/basisu.vcxproj
@@ -79,6 +79,7 @@
$(SolutionDir)\bin
+ $(ProjectName)D
@@ -98,9 +99,9 @@
Level4
Disabled
true
-
-
+ ispc_texcomp
true
+ true
Console
@@ -133,12 +134,13 @@
true
true
true
-
-
+ ispc_texcomp
NDEBUG;_MBCS;%(PreprocessorDefinitions)
false
true
AnySuitable
+ true
+ false
true
@@ -147,9 +149,14 @@
+
+
+
+
+
@@ -165,6 +172,7 @@
+
@@ -172,6 +180,7 @@
+
@@ -193,7 +202,6 @@
-
diff --git a/basisu.vcxproj.filters b/basisu.vcxproj.filters
index fbff442..218c3f1 100644
--- a/basisu.vcxproj.filters
+++ b/basisu.vcxproj.filters
@@ -19,6 +19,9 @@
+
+
+
@@ -49,6 +52,12 @@
+
+
+
+ transcoder
+
+
@@ -57,9 +66,6 @@
transcoder
-
- transcoder
-
transcoder
diff --git a/basisu_astc_decomp.cpp b/basisu_astc_decomp.cpp
index cc0a6ce..81a1800 100644
--- a/basisu_astc_decomp.cpp
+++ b/basisu_astc_decomp.cpp
@@ -1256,7 +1256,7 @@ void interpolateWeights (TexelWeightPair* dst, const deUint32 (&unquantizedWeigh
const int numWeightsPerTexel = blockMode.isDualPlane ? 2 : 1;
const deUint32 scaleX = (1024 + blockWidth/2) / (blockWidth-1);
const deUint32 scaleY = (1024 + blockHeight/2) / (blockHeight-1);
- DE_ASSERT(blockMode.weightGridWidth*blockMode.weightGridHeight*numWeightsPerTexel <= DE_LENGTH_OF_ARRAY(unquantizedWeights));
+ DE_ASSERT(blockMode.weightGridWidth*blockMode.weightGridHeight*numWeightsPerTexel <= (int)DE_LENGTH_OF_ARRAY(unquantizedWeights));
for (int texelY = 0; texelY < blockHeight; texelY++)
{
for (int texelX = 0; texelX < blockWidth; texelX++)
diff --git a/basisu_backend.cpp b/basisu_backend.cpp
index 3a689e5..4c130e9 100644
--- a/basisu_backend.cpp
+++ b/basisu_backend.cpp
@@ -1,5 +1,5 @@
// basisu_backend.cpp
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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.
@@ -176,7 +176,7 @@ namespace basisu
void basisu_backend::reoptimize_and_sort_endpoints_codebook(uint32_t total_block_endpoints_remapped, uint_vec& all_endpoint_indices)
{
basisu_frontend& r = *m_pFront_end;
- const bool is_video = r.get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
+ //const bool is_video = r.get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
if ((total_block_endpoints_remapped) && (m_params.m_compression_level > 0))
{
@@ -201,10 +201,10 @@ namespace basisu
for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
{
- const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
+ //const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
- const uint32_t width = m_slices[slice_index].m_width;
- const uint32_t height = m_slices[slice_index].m_height;
+ //const uint32_t width = m_slices[slice_index].m_width;
+ //const uint32_t height = m_slices[slice_index].m_height;
const uint32_t num_blocks_x = m_slices[slice_index].m_num_blocks_x;
const uint32_t num_blocks_y = m_slices[slice_index].m_num_blocks_y;
@@ -212,7 +212,7 @@ namespace basisu
{
for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
{
- const uint32_t block_index = first_block_index + block_x + block_y * num_blocks_x;
+ //const uint32_t block_index = first_block_index + block_x + block_y * num_blocks_x;
encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
@@ -336,10 +336,10 @@ namespace basisu
for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
{
const bool is_iframe = m_slices[slice_index].m_iframe;
- const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
+ //const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
- const uint32_t width = m_slices[slice_index].m_width;
- const uint32_t height = m_slices[slice_index].m_height;
+ //const uint32_t width = m_slices[slice_index].m_width;
+ //const uint32_t height = m_slices[slice_index].m_height;
const uint32_t num_blocks_x = m_slices[slice_index].m_num_blocks_x;
const uint32_t num_blocks_y = m_slices[slice_index].m_num_blocks_y;
const int prev_frame_slice_index = find_video_frame(slice_index, -1);
@@ -411,8 +411,8 @@ namespace basisu
const bool is_iframe = m_slices[slice_index].m_iframe;
const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
- const uint32_t width = m_slices[slice_index].m_width;
- const uint32_t height = m_slices[slice_index].m_height;
+ //const uint32_t width = m_slices[slice_index].m_width;
+ //const uint32_t height = m_slices[slice_index].m_height;
const uint32_t num_blocks_x = m_slices[slice_index].m_num_blocks_x;
const uint32_t num_blocks_y = m_slices[slice_index].m_num_blocks_y;
@@ -590,7 +590,7 @@ namespace basisu
{
for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
{
- const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
+ //const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
const uint32_t width = m_slices[slice_index].m_width;
const uint32_t height = m_slices[slice_index].m_height;
const uint32_t num_blocks_x = m_slices[slice_index].m_num_blocks_x;
@@ -603,7 +603,7 @@ namespace basisu
{
for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
{
- const uint32_t block_index = first_block_index + block_x + block_y * num_blocks_x;
+ //const uint32_t block_index = first_block_index + block_x + block_y * num_blocks_x;
encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
@@ -680,11 +680,11 @@ namespace basisu
for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
{
- const int prev_frame_slice_index = is_video ? find_video_frame(slice_index, -1) : -1;
- const int next_frame_slice_index = is_video ? find_video_frame(slice_index, 1) : -1;
+ //const int prev_frame_slice_index = is_video ? find_video_frame(slice_index, -1) : -1;
+ //const int next_frame_slice_index = is_video ? find_video_frame(slice_index, 1) : -1;
const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
- const uint32_t width = m_slices[slice_index].m_width;
- const uint32_t height = m_slices[slice_index].m_height;
+ //const uint32_t width = m_slices[slice_index].m_width;
+ //const uint32_t height = m_slices[slice_index].m_height;
const uint32_t num_blocks_x = m_slices[slice_index].m_num_blocks_x;
const uint32_t num_blocks_y = m_slices[slice_index].m_num_blocks_y;
@@ -702,7 +702,7 @@ namespace basisu
{
for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
{
- const uint32_t block_index = first_block_index + block_x + block_y * num_blocks_x;
+ //const uint32_t block_index = first_block_index + block_x + block_y * num_blocks_x;
encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
@@ -1168,8 +1168,8 @@ namespace basisu
for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
{
- const uint32_t width = m_slices[slice_index].m_width;
- const uint32_t height = m_slices[slice_index].m_height;
+ //const uint32_t width = m_slices[slice_index].m_width;
+ //const uint32_t height = m_slices[slice_index].m_height;
const uint32_t num_blocks_x = m_slices[slice_index].m_num_blocks_x;
const uint32_t num_blocks_y = m_slices[slice_index].m_num_blocks_y;
@@ -1644,7 +1644,7 @@ namespace basisu
uint32_t basisu_backend::encode()
{
- const bool is_video = m_pFront_end->get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
+ //const bool is_video = m_pFront_end->get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
m_output.m_slice_desc = m_slices;
m_output.m_etc1s = m_params.m_etc1s;
diff --git a/basisu_backend.h b/basisu_backend.h
index 1c72fa8..171ca83 100644
--- a/basisu_backend.h
+++ b/basisu_backend.h
@@ -1,5 +1,5 @@
// basisu_backend.h
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -139,6 +139,8 @@ namespace basisu
struct basisu_backend_output
{
+ basist::basis_tex_format m_tex_format;
+
bool m_etc1s;
uint32_t m_num_endpoints;
@@ -160,6 +162,7 @@ namespace basisu
void clear()
{
+ m_tex_format = basist::basis_tex_format::cETC1S;
m_etc1s = false;
m_num_endpoints = 0;
diff --git a/basisu_basis_file.cpp b/basisu_basis_file.cpp
index 3e6b190..ce6acd6 100644
--- a/basisu_basis_file.cpp
+++ b/basisu_basis_file.cpp
@@ -1,5 +1,5 @@
// basisu_basis_file.cpp
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -31,12 +31,19 @@ namespace basisu
m_header.m_total_images = 0;
for (uint32_t i = 0; i < encoder_output.m_slice_desc.size(); i++)
m_header.m_total_images = maximum(m_header.m_total_images, encoder_output.m_slice_desc[i].m_source_file_index + 1);
-
- m_header.m_format = 0;// basist::block_format::cETC1;
+
+ m_header.m_tex_format = (int)encoder_output.m_tex_format;
m_header.m_flags = 0;
if (encoder_output.m_etc1s)
+ {
+ assert(encoder_output.m_tex_format == basist::basis_tex_format::cETC1S);
m_header.m_flags = m_header.m_flags | basist::cBASISHeaderFlagETC1S;
+ }
+ else
+ {
+ assert(encoder_output.m_tex_format != basist::basis_tex_format::cETC1S);
+ }
if (y_flipped)
m_header.m_flags = m_header.m_flags | basist::cBASISHeaderFlagYFlipped;
@@ -85,7 +92,7 @@ namespace basisu
m_images_descs[i].m_level_index = slice_descs[i].m_mip_index;
if (slice_descs[i].m_alpha)
- m_images_descs[i].m_flags = m_images_descs[i].m_flags | basist::cSliceDescFlagsIsAlphaData;
+ m_images_descs[i].m_flags = m_images_descs[i].m_flags | basist::cSliceDescFlagsHasAlpha;
if (slice_descs[i].m_iframe)
m_images_descs[i].m_flags = m_images_descs[i].m_flags | basist::cSliceDescFlagsFrameIsIFrame;
@@ -127,14 +134,23 @@ namespace basisu
assert(m_comp_data.size() == m_slice_descs_file_ofs);
append_vector(m_comp_data, reinterpret_cast(&m_images_descs[0]), m_images_descs.size() * sizeof(m_images_descs[0]));
- assert(m_comp_data.size() == m_endpoint_cb_file_ofs);
- append_vector(m_comp_data, reinterpret_cast(&encoder_output.m_endpoint_palette[0]), encoder_output.m_endpoint_palette.size());
+ if (encoder_output.m_endpoint_palette.size())
+ {
+ assert(m_comp_data.size() == m_endpoint_cb_file_ofs);
+ append_vector(m_comp_data, reinterpret_cast(&encoder_output.m_endpoint_palette[0]), encoder_output.m_endpoint_palette.size());
+ }
- assert(m_comp_data.size() == m_selector_cb_file_ofs);
- append_vector(m_comp_data, reinterpret_cast(&encoder_output.m_selector_palette[0]), encoder_output.m_selector_palette.size());
+ if (encoder_output.m_selector_palette.size())
+ {
+ assert(m_comp_data.size() == m_selector_cb_file_ofs);
+ append_vector(m_comp_data, reinterpret_cast(&encoder_output.m_selector_palette[0]), encoder_output.m_selector_palette.size());
+ }
- assert(m_comp_data.size() == m_tables_file_ofs);
- append_vector(m_comp_data, reinterpret_cast(&encoder_output.m_slice_image_tables[0]), encoder_output.m_slice_image_tables.size());
+ if (encoder_output.m_slice_image_tables.size())
+ {
+ assert(m_comp_data.size() == m_tables_file_ofs);
+ append_vector(m_comp_data, reinterpret_cast(&encoder_output.m_slice_image_tables[0]), encoder_output.m_slice_image_tables.size());
+ }
assert(m_comp_data.size() == m_first_image_file_ofs);
for (uint32_t i = 0; i < slice_descs.size(); i++)
@@ -173,10 +189,20 @@ namespace basisu
m_header_file_ofs = 0;
m_slice_descs_file_ofs = sizeof(basist::basis_file_header);
- m_endpoint_cb_file_ofs = m_slice_descs_file_ofs + sizeof(basist::basis_slice_desc) * (uint32_t)slice_descs.size();
- m_selector_cb_file_ofs = m_endpoint_cb_file_ofs + (uint32_t)encoder_output.m_endpoint_palette.size();
- m_tables_file_ofs = m_selector_cb_file_ofs + (uint32_t)encoder_output.m_selector_palette.size();
- m_first_image_file_ofs = m_tables_file_ofs + (uint32_t)encoder_output.m_slice_image_tables.size();
+ if (encoder_output.m_tex_format == basist::basis_tex_format::cETC1S)
+ {
+ m_endpoint_cb_file_ofs = m_slice_descs_file_ofs + sizeof(basist::basis_slice_desc) * (uint32_t)slice_descs.size();
+ m_selector_cb_file_ofs = m_endpoint_cb_file_ofs + (uint32_t)encoder_output.m_endpoint_palette.size();
+ m_tables_file_ofs = m_selector_cb_file_ofs + (uint32_t)encoder_output.m_selector_palette.size();
+ m_first_image_file_ofs = m_tables_file_ofs + (uint32_t)encoder_output.m_slice_image_tables.size();
+ }
+ else
+ {
+ m_endpoint_cb_file_ofs = 0;
+ m_selector_cb_file_ofs = 0;
+ m_tables_file_ofs = 0;
+ m_first_image_file_ofs = m_slice_descs_file_ofs + sizeof(basist::basis_slice_desc) * (uint32_t)slice_descs.size();
+ }
uint64_t total_file_size = m_first_image_file_ofs;
for (uint32_t i = 0; i < encoder_output.m_slice_image_data.size(); i++)
diff --git a/basisu_comp.cpp b/basisu_comp.cpp
index 1e46793..ba66e30 100644
--- a/basisu_comp.cpp
+++ b/basisu_comp.cpp
@@ -1,5 +1,5 @@
// basisu_comp.cpp
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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,6 +15,9 @@
#include "basisu_comp.h"
#include "basisu_enc.h"
#include
+#include
+#define MINIZ_NO_ZLIB_APIS
+#include "basisu_miniz.h"
#define BASISU_USE_STB_IMAGE_RESIZE_FOR_MIPMAP_GEN 0
#define DEBUG_CROP_TEXTURE_TO_64x64 (0)
@@ -24,10 +27,10 @@
namespace basisu
{
basis_compressor::basis_compressor() :
- m_total_blocks(0),
- m_auto_global_sel_pal(false),
m_basis_file_size(0),
m_basis_bits_per_texel(0),
+ m_total_blocks(0),
+ m_auto_global_sel_pal(false),
m_any_source_image_has_alpha(false)
{
debug_printf("basis_compressor::basis_compressor\n");
@@ -53,6 +56,7 @@ namespace basisu
debug_printf("Source images: %u, source filenames: %u, source alpha filenames: %i\n",
(uint32_t)m_params.m_source_images.size(), (uint32_t)m_params.m_source_filenames.size(), (uint32_t)m_params.m_source_alpha_filenames.size());
+ PRINT_BOOL_VALUE(m_uastc);
PRINT_BOOL_VALUE(m_y_flip);
PRINT_BOOL_VALUE(m_debug);
PRINT_BOOL_VALUE(m_debug_images);
@@ -96,6 +100,13 @@ namespace basisu
debug_printf("m_tex_type: %u\n", m_params.m_tex_type);
debug_printf("m_userdata0: 0x%X, m_userdata1: 0x%X\n", m_params.m_userdata0, m_params.m_userdata1);
debug_printf("m_us_per_frame: %i (%f fps)\n", m_params.m_us_per_frame, m_params.m_us_per_frame ? 1.0f / (m_params.m_us_per_frame / 1000000.0f) : 0);
+ debug_printf("m_pack_uastc_flags: 0x%X\n", m_params.m_pack_uastc_flags);
+
+ PRINT_BOOL_VALUE(m_rdo_uastc);
+ PRINT_FLOAT_VALUE(m_rdo_uastc_quality_scalar);
+ PRINT_INT_VALUE(m_rdo_uastc_dict_size);
+ PRINT_FLOAT_VALUE(m_rdo_uastc_max_allowed_rms_increase_ratio);
+ PRINT_FLOAT_VALUE(m_rdo_uastc_skip_block_rms_thresh);
#undef PRINT_BOOL_VALUE
#undef PRINT_INT_VALUE
@@ -122,14 +133,26 @@ namespace basisu
if (!validate_texture_type_constraints())
return cECFailedValidating;
- if (!process_frontend())
+ if (!extract_source_blocks())
return cECFailedFrontEnd;
- if (!extract_frontend_texture_data())
- return cECFailedFontendExtract;
+ if (m_params.m_uastc)
+ {
+ error_code ec = encode_slices_to_uastc();
+ if (ec != cECSuccess)
+ return ec;
+ }
+ else
+ {
+ if (!process_frontend())
+ return cECFailedFrontEnd;
- if (!process_backend())
- return cECFailedBackend;
+ if (!extract_frontend_texture_data())
+ return cECFailedFontendExtract;
+
+ if (!process_backend())
+ return cECFailedBackend;
+ }
if (!create_basis_file_and_transcode())
return cECFailedCreateBasisFile;
@@ -140,6 +163,99 @@ namespace basisu
return cECSuccess;
}
+ basis_compressor::error_code basis_compressor::encode_slices_to_uastc()
+ {
+ debug_printf("basis_compressor::encode_slices_to_uastc\n");
+
+ m_uastc_slice_textures.resize(m_slice_descs.size());
+ for (uint32_t slice_index = 0; slice_index < m_slice_descs.size(); slice_index++)
+ m_uastc_slice_textures[slice_index].init(texture_format::cUASTC4x4, m_slice_descs[slice_index].m_orig_width, m_slice_descs[slice_index].m_orig_height);
+
+ m_uastc_backend_output.m_tex_format = basist::basis_tex_format::cUASTC4x4;
+ m_uastc_backend_output.m_etc1s = false;
+ m_uastc_backend_output.m_slice_desc = m_slice_descs;
+ m_uastc_backend_output.m_slice_image_data.resize(m_slice_descs.size());
+ m_uastc_backend_output.m_slice_image_crcs.resize(m_slice_descs.size());
+
+ for (uint32_t slice_index = 0; slice_index < m_slice_descs.size(); slice_index++)
+ {
+ gpu_image& tex = m_uastc_slice_textures[slice_index];
+ basisu_backend_slice_desc& slice_desc = m_slice_descs[slice_index];
+ (void)slice_desc;
+
+ const uint32_t num_blocks_x = tex.get_blocks_x();
+ const uint32_t num_blocks_y = tex.get_blocks_y();
+ const uint32_t total_blocks = tex.get_total_blocks();
+ const image& source_image = m_slice_images[slice_index];
+
+ std::atomic total_blocks_processed;
+ total_blocks_processed = 0;
+
+ const uint32_t N = 256;
+ for (uint32_t block_index_iter = 0; block_index_iter < total_blocks; block_index_iter += N)
+ {
+ const uint32_t first_index = block_index_iter;
+ const uint32_t last_index = minimum(total_blocks, block_index_iter + N);
+
+ m_params.m_pJob_pool->add_job([this, first_index, last_index, num_blocks_x, num_blocks_y, total_blocks, &source_image, &tex, &total_blocks_processed]
+ {
+ BASISU_NOTE_UNUSED(num_blocks_y);
+
+ for (uint32_t block_index = first_index; block_index < last_index; block_index++)
+ {
+ const uint32_t block_x = block_index % num_blocks_x;
+ const uint32_t block_y = block_index / num_blocks_x;
+
+ color_rgba block_pixels[4][4];
+
+ source_image.extract_block_clamped((color_rgba*)block_pixels, block_x * 4, block_y * 4, 4, 4);
+
+ basist::uastc_block& dest_block = *(basist::uastc_block*)tex.get_block_ptr(block_x, block_y);
+
+ encode_uastc(&block_pixels[0][0].r, dest_block, m_params.m_pack_uastc_flags);
+
+ total_blocks_processed++;
+
+ uint32_t val = total_blocks_processed;
+ if ((val & 16383) == 16383)
+ {
+ debug_printf("basis_compressor::encode_slices_to_uastc: %3.1f%% done\n", static_cast(val) * 100.0f / total_blocks);
+ }
+
+ }
+ });
+
+ } // block_index_iter
+
+ m_params.m_pJob_pool->wait_for_all();
+
+ if (m_params.m_rdo_uastc)
+ {
+ uastc_rdo_params rdo_params;
+ rdo_params.m_quality_scaler = m_params.m_rdo_uastc_quality_scalar;
+ rdo_params.m_max_allowed_rms_increase_ratio = m_params.m_rdo_uastc_max_allowed_rms_increase_ratio;
+ rdo_params.m_skip_block_rms_thresh = m_params.m_rdo_uastc_skip_block_rms_thresh;
+ rdo_params.m_lz_dict_size = m_params.m_rdo_uastc_dict_size;
+
+ bool status = uastc_rdo(tex.get_total_blocks(), (basist::uastc_block*)tex.get_ptr(),
+ (const color_rgba *)m_source_blocks[slice_desc.m_first_block_index].m_pixels, rdo_params, m_params.m_pack_uastc_flags, m_params.m_pJob_pool,
+ m_params.m_pJob_pool ? std::min(4, (uint32_t)m_params.m_pJob_pool->get_total_threads()) : 0);
+ if (!status)
+ {
+ return cECFailedUASTCRDOPostProcess;
+ }
+ }
+
+ m_uastc_backend_output.m_slice_image_data[slice_index].resize(tex.get_size_in_bytes());
+ memcpy(&m_uastc_backend_output.m_slice_image_data[slice_index][0], tex.get_ptr(), tex.get_size_in_bytes());
+
+ m_uastc_backend_output.m_slice_image_crcs[slice_index] = basist::crc16(tex.get_ptr(), tex.get_size_in_bytes(), 0);
+
+ } // slice_index
+
+ return cECSuccess;
+ }
+
bool basis_compressor::generate_mipmaps(const image &img, std::vector &mips, bool has_alpha)
{
debug_printf("basis_compressor::generate_mipmaps\n");
@@ -247,7 +363,7 @@ namespace basisu
pSource_filename = m_params.m_source_filenames[source_file_index].c_str();
// Load the source image
- if (!load_png(pSource_filename, file_image))
+ if (!load_image(pSource_filename, file_image))
{
error_printf("Failed reading source image: %s\n", pSource_filename);
return false;
@@ -262,7 +378,7 @@ namespace basisu
image alpha_data;
- if (!load_png(pSource_alpha_image, alpha_data))
+ if (!load_image(pSource_alpha_image, alpha_data))
{
error_printf("Failed reading source image: %s\n", pSource_alpha_image);
return false;
@@ -349,6 +465,7 @@ namespace basisu
image &file_image = source_images[source_file_index];
const std::string &source_filename = source_filenames[source_file_index];
+ // Now, for each source image, create the slices corresponding to that image.
std::vector slices;
slices.reserve(32);
@@ -364,9 +481,9 @@ namespace basisu
for (uint32_t i = 0; i < slices.size(); i++)
mip_indices[i] = i;
- if (m_any_source_image_has_alpha)
+ if ((m_any_source_image_has_alpha) && (!m_params.m_uastc))
{
- // If source has alpha, then even mips will have RGB, and odd mips will have alpha in RGB.
+ // For ETC1S, if source has alpha, then even mips will have RGB, and odd mips will have alpha in RGB.
std::vector alpha_slices;
uint_vec new_mip_indices;
@@ -403,18 +520,29 @@ namespace basisu
for (uint32_t slice_index = 0; slice_index < slices.size(); slice_index++)
{
- const bool is_alpha_slice = m_any_source_image_has_alpha && ((slice_index & 1) != 0);
-
- image &slice_image = slices[slice_index];
+ image& slice_image = slices[slice_index];
const uint32_t orig_width = slice_image.get_width();
const uint32_t orig_height = slice_image.get_height();
+ bool is_alpha_slice = false;
+ if (m_any_source_image_has_alpha)
+ {
+ if (m_params.m_uastc)
+ {
+ is_alpha_slice = slice_image.has_alpha();
+ }
+ else
+ {
+ is_alpha_slice = (slice_index & 1) != 0;
+ }
+ }
+
// Enlarge the source image to 4x4 block boundaries, duplicating edge pixels if necessary to avoid introducing extra colors into blocks.
slice_image.crop_dup_borders(slice_image.get_block_width(4) * 4, slice_image.get_block_height(4) * 4);
if (m_params.m_debug_images)
{
- save_png(string_format("basis_debug_source_image_%u_%u.png", source_file_index, slice_index).c_str(), slice_image);
+ save_png(string_format("basis_debug_source_image_%u_slice_%u.png", source_file_index, slice_index).c_str(), slice_image);
}
enlarge_vector(m_stats, 1);
@@ -505,28 +633,31 @@ namespace basisu
if (m_any_source_image_has_alpha)
{
- // Alpha slices must be at odd slice indices
- if (slice_desc.m_alpha)
+ if (!m_params.m_uastc)
{
- if ((i & 1) == 0)
- return false;
-
- const basisu_backend_slice_desc &prev_slice_desc = m_slice_descs[i - 1];
+ // For ETC1S, alpha slices must be at odd slice indices.
+ if (slice_desc.m_alpha)
+ {
+ if ((i & 1) == 0)
+ return false;
- // Make sure previous slice has this image's color data
- if (prev_slice_desc.m_source_file_index != slice_desc.m_source_file_index)
- return false;
- if (prev_slice_desc.m_alpha)
- return false;
- if (prev_slice_desc.m_mip_index != slice_desc.m_mip_index)
- return false;
- if (prev_slice_desc.m_num_blocks_x != slice_desc.m_num_blocks_x)
- return false;
- if (prev_slice_desc.m_num_blocks_y != slice_desc.m_num_blocks_y)
+ const basisu_backend_slice_desc& prev_slice_desc = m_slice_descs[i - 1];
+
+ // Make sure previous slice has this image's color data
+ if (prev_slice_desc.m_source_file_index != slice_desc.m_source_file_index)
+ return false;
+ if (prev_slice_desc.m_alpha)
+ return false;
+ if (prev_slice_desc.m_mip_index != slice_desc.m_mip_index)
+ return false;
+ if (prev_slice_desc.m_num_blocks_x != slice_desc.m_num_blocks_x)
+ return false;
+ if (prev_slice_desc.m_num_blocks_y != slice_desc.m_num_blocks_y)
+ return false;
+ }
+ else if (i & 1)
return false;
}
- else if (i & 1)
- return false;
}
else if (slice_desc.m_alpha)
{
@@ -610,26 +741,33 @@ namespace basisu
return true;
}
- bool basis_compressor::process_frontend()
+ bool basis_compressor::extract_source_blocks()
{
- debug_printf("basis_compressor::process_frontend\n");
-
+ debug_printf("basis_compressor::extract_source_blocks\n");
+
m_source_blocks.resize(m_total_blocks);
-
+
for (uint32_t slice_index = 0; slice_index < m_slice_images.size(); slice_index++)
{
- const basisu_backend_slice_desc &slice_desc = m_slice_descs[slice_index];
+ const basisu_backend_slice_desc& slice_desc = m_slice_descs[slice_index];
const uint32_t num_blocks_x = slice_desc.m_num_blocks_x;
const uint32_t num_blocks_y = slice_desc.m_num_blocks_y;
- const image &source_image = m_slice_images[slice_index];
+ const image& source_image = m_slice_images[slice_index];
for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
source_image.extract_block_clamped(m_source_blocks[slice_desc.m_first_block_index + block_x + block_y * num_blocks_x].get_ptr(), block_x * 4, block_y * 4, 4, 4);
}
-
+
+ return true;
+ }
+
+ bool basis_compressor::process_frontend()
+ {
+ debug_printf("basis_compressor::process_frontend\n");
+
#if 0
// TODO
basis_etc1_pack_params pack_params;
@@ -925,19 +1063,26 @@ namespace basisu
{
debug_printf("basis_compressor::create_basis_file_and_transcode\n");
- const basisu_backend_output &encoded_output = m_backend.get_output();
+ const basisu_backend_output& encoded_output = m_params.m_uastc ? m_uastc_backend_output : m_backend.get_output();
if (!m_basis_file.init(encoded_output, m_params.m_tex_type, m_params.m_userdata0, m_params.m_userdata1, m_params.m_y_flip, m_params.m_us_per_frame))
{
- error_printf("basis_compressor::write_output_files_and_compute_stats: basisu_backend:init() failed!\n");
+ error_printf("basis_compressor::create_basis_file_and_transcode: basisu_backend:init() failed!\n");
return false;
}
-
+
const uint8_vec &comp_data = m_basis_file.get_compressed_data();
m_output_basis_file = comp_data;
- // Verify the compressed data by transcoding it to ETC1/BC1 and validating the CRC's.
+ interval_timer tm;
+ tm.start();
+
+ basist::basisu_transcoder_init();
+
+ debug_printf("basist::basisu_transcoder_init: Took %f ms\n", tm.get_elapsed_ms());
+
+ // Verify the compressed data by transcoding it to ASTC (or ETC1)/BC7 and validating the CRC's.
basist::basisu_transcoder decoder(m_params.m_pSel_codebook);
if (!decoder.validate_file_checksums(&comp_data[0], (uint32_t)comp_data.size(), true))
{
@@ -948,10 +1093,9 @@ namespace basisu
m_decoded_output_textures.resize(m_slice_descs.size());
m_decoded_output_textures_unpacked.resize(m_slice_descs.size());
- m_decoded_output_textures_bc1.resize(m_slice_descs.size());
- m_decoded_output_textures_unpacked_bc1.resize(m_slice_descs.size());
-
- interval_timer tm;
+ m_decoded_output_textures_bc7.resize(m_slice_descs.size());
+ m_decoded_output_textures_unpacked_bc7.resize(m_slice_descs.size());
+
tm.start();
if (!decoder.start_transcoding(&comp_data[0], (uint32_t)comp_data.size()))
@@ -970,74 +1114,72 @@ namespace basisu
for (uint32_t i = 0; i < m_slice_descs.size(); i++)
{
gpu_image decoded_texture;
- decoded_texture.init(texture_format::cETC1, m_slice_descs[i].m_width, m_slice_descs[i].m_height);
+ decoded_texture.init(m_params.m_uastc ? texture_format::cASTC4x4 : texture_format::cETC1, m_slice_descs[i].m_width, m_slice_descs[i].m_height);
tm.start();
+
+ basist::block_format format = m_params.m_uastc ? basist::block_format::cASTC_4x4 : basist::block_format::cETC1;
+ uint32_t bytes_per_block = m_params.m_uastc ? 16 : 8;
if (!decoder.transcode_slice(&comp_data[0], (uint32_t)comp_data.size(), i,
- reinterpret_cast(decoded_texture.get_ptr()), m_slice_descs[i].m_num_blocks_x * m_slice_descs[i].m_num_blocks_y, basist::block_format::cETC1, 8))
+ reinterpret_cast(decoded_texture.get_ptr()), m_slice_descs[i].m_num_blocks_x * m_slice_descs[i].m_num_blocks_y, format, bytes_per_block))
{
- error_printf("Transcoding failed to ETC1 on slice %u!\n", i);
+ error_printf("Transcoding failed on slice %u!\n", i);
return false;
}
total_time_etc1 += tm.get_elapsed_secs();
- uint32_t image_crc16 = basist::crc16(decoded_texture.get_ptr(), decoded_texture.get_size_in_bytes(), 0);
- if (image_crc16 != m_backend.get_output().m_slice_image_crcs[i])
+ if (encoded_output.m_tex_format == basist::basis_tex_format::cETC1S)
{
- error_printf("Decoded image data CRC check failed on slice %u!\n", i);
- return false;
+ uint32_t image_crc16 = basist::crc16(decoded_texture.get_ptr(), decoded_texture.get_size_in_bytes(), 0);
+ if (image_crc16 != encoded_output.m_slice_image_crcs[i])
+ {
+ error_printf("Decoded image data CRC check failed on slice %u!\n", i);
+ return false;
+ }
+ debug_printf("Decoded image data CRC check succeeded on slice %i\n", i);
}
- debug_printf("Decoded image data CRC check succeeded on slice %i\n", i);
m_decoded_output_textures[i] = decoded_texture;
total_orig_pixels += m_slice_descs[i].m_orig_width * m_slice_descs[i].m_orig_height;
total_texels += m_slice_descs[i].m_width * m_slice_descs[i].m_height;
}
-
- tm.start();
-
- basist::basisu_transcoder_init();
-
- debug_printf("basist::basisu_transcoder_init: Took %f ms\n", tm.get_elapsed_ms());
-
- double total_time_bc1 = 0;
+
+ double total_time_bc7 = 0;
for (uint32_t i = 0; i < m_slice_descs.size(); i++)
{
gpu_image decoded_texture;
- decoded_texture.init(texture_format::cBC1, m_slice_descs[i].m_width, m_slice_descs[i].m_height);
+ decoded_texture.init(texture_format::cBC7, m_slice_descs[i].m_width, m_slice_descs[i].m_height);
tm.start();
if (!decoder.transcode_slice(&comp_data[0], (uint32_t)comp_data.size(), i,
- reinterpret_cast(decoded_texture.get_ptr()), m_slice_descs[i].m_num_blocks_x * m_slice_descs[i].m_num_blocks_y, basist::block_format::cBC1, 8))
+ reinterpret_cast(decoded_texture.get_ptr()), m_slice_descs[i].m_num_blocks_x * m_slice_descs[i].m_num_blocks_y, basist::block_format::cBC7, 16))
{
- error_printf("Transcoding failed to BC1 on slice %u!\n", i);
+ error_printf("Transcoding failed to BC7 on slice %u!\n", i);
return false;
}
- total_time_bc1 += tm.get_elapsed_secs();
+ total_time_bc7 += tm.get_elapsed_secs();
- m_decoded_output_textures_bc1[i] = decoded_texture;
+ m_decoded_output_textures_bc7[i] = decoded_texture;
}
for (uint32_t i = 0; i < m_slice_descs.size(); i++)
{
m_decoded_output_textures[i].unpack(m_decoded_output_textures_unpacked[i]);
- m_decoded_output_textures_bc1[i].unpack(m_decoded_output_textures_unpacked_bc1[i]);
+ m_decoded_output_textures_bc7[i].unpack(m_decoded_output_textures_unpacked_bc7[i]);
}
- debug_printf("Transcoded to ETC1 in %3.3fms, %f texels/sec\n", total_time_etc1 * 1000.0f, total_orig_pixels / total_time_etc1);
+ debug_printf("Transcoded to %s in %3.3fms, %f texels/sec\n", m_params.m_uastc ? "ASTC" : "ETC1", total_time_etc1 * 1000.0f, total_orig_pixels / total_time_etc1);
- debug_printf("Transcoded to BC1 in %3.3fms, %f texels/sec\n", total_time_bc1 * 1000.0f, total_orig_pixels / total_time_bc1);
+ debug_printf("Transcoded to BC7 in %3.3fms, %f texels/sec\n", total_time_bc7 * 1000.0f, total_orig_pixels / total_time_bc7);
debug_printf("Total .basis output file size: %u, %3.3f bits/texel\n", comp_data.size(), comp_data.size() * 8.0f / total_orig_pixels);
-
- m_output_blocks.resize(0);
-
+
uint32_t total_orig_texels = 0;
for (uint32_t slice_index = 0; slice_index < m_slice_descs.size(); slice_index++)
{
@@ -1046,10 +1188,9 @@ namespace basisu
total_orig_texels += slice_desc.m_orig_width * slice_desc.m_orig_height;
const uint32_t total_blocks = slice_desc.m_num_blocks_x * slice_desc.m_num_blocks_y;
+ BASISU_NOTE_UNUSED(total_blocks);
assert(m_decoded_output_textures[slice_index].get_total_blocks() == total_blocks);
-
- memcpy(enlarge_vector(m_output_blocks, total_blocks), m_decoded_output_textures[slice_index].get_ptr(), sizeof(etc_block) * total_blocks);
}
m_basis_file_size = (uint32_t)comp_data.size();
@@ -1062,9 +1203,11 @@ namespace basisu
{
debug_printf("basis_compressor::write_output_files_and_compute_stats\n");
+ //const basisu_backend_output& encoded_output = m_params.m_uastc ? m_uastc_backend_output : m_backend.get_output();
+
+ const uint8_vec& comp_data = m_basis_file.get_compressed_data();
if (m_params.m_write_output_basis_files)
{
- const uint8_vec &comp_data = m_basis_file.get_compressed_data();
const std::string& basis_filename = m_params.m_out_filename;
@@ -1076,6 +1219,27 @@ namespace basisu
printf("Wrote output .basis file \"%s\"\n", basis_filename.c_str());
}
+ size_t comp_size = 0;
+ if ((m_params.m_compute_stats) && (m_params.m_uastc) && (comp_data.size()))
+ {
+ void* pComp_data = tdefl_compress_mem_to_heap(&comp_data[0], comp_data.size(), &comp_size, TDEFL_MAX_PROBES_MASK);// TDEFL_DEFAULT_MAX_PROBES);
+ size_t decomp_size = 0;
+ void* pDecomp_data = tinfl_decompress_mem_to_heap(pComp_data, comp_size, &decomp_size, 0);
+ if ((decomp_size != comp_data.size()) || (memcmp(pDecomp_data, &comp_data[0], decomp_size) != 0))
+ {
+ printf("basis_compressor::create_basis_file_and_transcode:: miniz compression or decompression failed!\n");
+ return false;
+ }
+ mz_free(pComp_data);
+ mz_free(pDecomp_data);
+ uint32_t total_texels = 0;
+ for (uint32_t i = 0; i < m_slice_descs.size(); i++)
+ total_texels += (m_slice_descs[i].m_num_blocks_x * m_slice_descs[i].m_num_blocks_y) * 16;
+ debug_printf(".basis file size: %u, LZ compressed file size: %u, %3.2f bits/texel\n",
+ (uint32_t)comp_data.size(),
+ (uint32_t)comp_size,
+ comp_size * 8.0f / total_texels);
+ }
m_stats.resize(m_slice_descs.size());
@@ -1097,75 +1261,119 @@ namespace basisu
image_metrics em;
- // ---- .basis ETC1S stats
- em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 0);
- em.print(".basis ETC1S 709 Luma: ");
-
- s.m_basis_etc1s_luma_709_psnr = static_cast(em.m_psnr);
- s.m_basis_etc1s_luma_709_ssim = static_cast(em.m_ssim);
-
- em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 0, true, true);
- em.print(".basis ETC1S 601 Luma: ");
-
- s.m_basis_etc1s_luma_601_psnr = static_cast(em.m_psnr);
-
+ // ---- .basis stats
em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 3);
- em.print(".basis ETC1S RGB Avg: ");
+ em.print(".basis RGB Avg: ");
+ s.m_basis_rgb_avg_psnr = em.m_psnr;
- s.m_basis_etc1s_rgb_avg_psnr = em.m_psnr;
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 4);
+ em.print(".basis RGBA Avg: ");
+ s.m_basis_rgba_avg_psnr = em.m_psnr;
- if (m_slice_descs.size() == 1)
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 1);
+ em.print(".basis R Avg: ");
+
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 1, 1);
+ em.print(".basis G Avg: ");
+
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 2, 1);
+ em.print(".basis B Avg: ");
+
+ if (m_params.m_uastc)
{
- debug_printf(".basis Luma 709 PSNR per bit/texel*10000: %3.3f\n", 10000.0f * s.m_basis_etc1s_luma_709_psnr / ((m_backend.get_output().get_output_size_estimate() * 8.0f) / (slice_desc.m_orig_width * slice_desc.m_orig_height)));
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 3, 1);
+ em.print(".basis A Avg: ");
+
+ s.m_basis_a_avg_psnr = em.m_psnr;
}
- // ---- .basis BC1 stats
- em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc1[slice_index], 0, 0);
- em.print(".basis BC1 709 Luma: ");
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 0);
+ em.print(".basis 709 Luma: ");
+ s.m_basis_luma_709_psnr = static_cast(em.m_psnr);
+ s.m_basis_luma_709_ssim = static_cast(em.m_ssim);
+
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 0, true, true);
+ em.print(".basis 601 Luma: ");
+ s.m_basis_luma_601_psnr = static_cast(em.m_psnr);
- s.m_basis_bc1_luma_709_psnr = static_cast(em.m_psnr);
- s.m_basis_bc1_luma_709_ssim = static_cast(em.m_ssim);
+ if (m_slice_descs.size() == 1)
+ {
+ const uint32_t output_size = comp_size ? (uint32_t)comp_size : (uint32_t)comp_data.size();
+ debug_printf(".basis RGB PSNR per bit/texel*10000: %3.3f\n", 10000.0f * s.m_basis_rgb_avg_psnr / ((output_size * 8.0f) / (slice_desc.m_orig_width * slice_desc.m_orig_height)));
+ debug_printf(".basis Luma 709 PSNR per bit/texel*10000: %3.3f\n", 10000.0f * s.m_basis_luma_709_psnr / ((output_size * 8.0f) / (slice_desc.m_orig_width * slice_desc.m_orig_height)));
+ }
- em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc1[slice_index], 0, 0, true, true);
- em.print(".basis BC1 601 Luma: ");
+ {
+ // ---- BC7 stats
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 3);
+ em.print("BC7 RGB Avg: ");
+ s.m_bc7_rgb_avg_psnr = em.m_psnr;
- s.m_basis_bc1_luma_601_psnr = static_cast(em.m_psnr);
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 4);
+ em.print("BC7 RGBA Avg: ");
+ s.m_bc7_rgba_avg_psnr = em.m_psnr;
- em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc1[slice_index], 0, 3);
- em.print(".basis BC1 RGB Avg: ");
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 1);
+ em.print("BC7 R Avg: ");
- s.m_basis_bc1_rgb_avg_psnr = static_cast(em.m_psnr);
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 1, 1);
+ em.print("BC7 G Avg: ");
- // ---- Nearly best possible ETC1S stats
- em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 0);
- em.print("Unquantized ETC1S 709 Luma: ");
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 2, 1);
+ em.print("BC7 B Avg: ");
- s.m_best_luma_709_psnr = static_cast(em.m_psnr);
- s.m_best_luma_709_ssim = static_cast(em.m_ssim);
+ if (m_params.m_uastc)
+ {
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 3, 1);
+ em.print("BC7 A Avg: ");
- em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 0, true, true);
- em.print("Unquantized ETC1S 601 Luma: ");
+ s.m_bc7_a_avg_psnr = em.m_psnr;
+ }
- s.m_best_luma_601_psnr = static_cast(em.m_psnr);
-
- em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 3);
- em.print("Unquantized ETC1S RGB Avg: ");
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 0);
+ em.print("BC7 709 Luma: ");
+ s.m_bc7_luma_709_psnr = static_cast(em.m_psnr);
+ s.m_bc7_luma_709_ssim = static_cast(em.m_ssim);
- s.m_best_rgb_avg_psnr = static_cast(em.m_psnr);
+ em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 0, true, true);
+ em.print("BC7 601 Luma: ");
+ s.m_bc7_luma_601_psnr = static_cast(em.m_psnr);
+ }
+
+ if (!m_params.m_uastc)
+ {
+ // ---- Nearly best possible ETC1S stats
+ em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 0);
+ em.print("Unquantized ETC1S 709 Luma: ");
+
+ s.m_best_etc1s_luma_709_psnr = static_cast(em.m_psnr);
+ s.m_best_etc1s_luma_709_ssim = static_cast(em.m_ssim);
+
+ em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 0, true, true);
+ em.print("Unquantized ETC1S 601 Luma: ");
+
+ s.m_best_etc1s_luma_601_psnr = static_cast(em.m_psnr);
+
+ em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 3);
+ em.print("Unquantized ETC1S RGB Avg: ");
+
+ s.m_best_etc1s_rgb_avg_psnr = static_cast(em.m_psnr);
+ }
}
- if (m_frontend.get_params().m_debug_images)
- {
- std::string out_basename;
- if (m_params.m_out_filename.size())
- string_get_filename(m_params.m_out_filename.c_str(), out_basename);
- else if (m_params.m_source_filenames.size())
- string_get_filename(m_params.m_source_filenames[slice_desc.m_source_file_index].c_str(), out_basename);
-
- string_remove_extension(out_basename);
- out_basename = "basis_debug_" + out_basename + string_format("_slice_%u", slice_index);
+ std::string out_basename;
+ if (m_params.m_out_filename.size())
+ string_get_filename(m_params.m_out_filename.c_str(), out_basename);
+ else if (m_params.m_source_filenames.size())
+ string_get_filename(m_params.m_source_filenames[slice_desc.m_source_file_index].c_str(), out_basename);
+ string_remove_extension(out_basename);
+ out_basename = "basis_debug_" + out_basename + string_format("_slice_%u", slice_index);
+
+ if ((!m_params.m_uastc) && (m_frontend.get_params().m_debug_images))
+ {
// Write "best" ETC1S debug images
+ if (!m_params.m_uastc)
{
gpu_image best_etc1s_gpu_image(m_best_etc1s_images[slice_index]);
best_etc1s_gpu_image.override_dimensions(slice_desc.m_orig_width, slice_desc.m_orig_height);
@@ -1175,27 +1383,30 @@ namespace basisu
best_etc1s_gpu_image.unpack(best_etc1s_unpacked);
save_png(out_basename + "_best_etc1s.png", best_etc1s_unpacked);
}
+ }
- // Write decoded ETC1S debug images
+ if (m_params.m_debug_images)
+ {
+ // Write decoded ETC1S/ASTC debug images
{
- gpu_image decoded_etc1s(m_decoded_output_textures[slice_index]);
- decoded_etc1s.override_dimensions(slice_desc.m_orig_width, slice_desc.m_orig_height);
- write_compressed_texture_file((out_basename + "_decoded_etc1s.ktx").c_str(), decoded_etc1s);
+ gpu_image decoded_etc1s_or_astc(m_decoded_output_textures[slice_index]);
+ decoded_etc1s_or_astc.override_dimensions(slice_desc.m_orig_width, slice_desc.m_orig_height);
+ write_compressed_texture_file((out_basename + "_transcoded_etc1s_astc.ktx").c_str(), decoded_etc1s_or_astc);
image temp(m_decoded_output_textures_unpacked[slice_index]);
temp.crop(slice_desc.m_orig_width, slice_desc.m_orig_height);
- save_png(out_basename + "_decoded_etc1s.png", temp);
+ save_png(out_basename + "_transcoded_etc1s_astc.png", temp);
}
-
- // Write decoded BC1 debug images
- {
- gpu_image decoded_bc1(m_decoded_output_textures_bc1[slice_index]);
- decoded_bc1.override_dimensions(slice_desc.m_orig_width, slice_desc.m_orig_height);
- write_compressed_texture_file((out_basename + "_decoded_bc1.ktx").c_str(), decoded_bc1);
- image temp(m_decoded_output_textures_unpacked_bc1[slice_index]);
+ // Write decoded BC7 debug images
+ {
+ gpu_image decoded_bc7(m_decoded_output_textures_bc7[slice_index]);
+ decoded_bc7.override_dimensions(slice_desc.m_orig_width, slice_desc.m_orig_height);
+ write_compressed_texture_file((out_basename + "_transcoded_bc7.ktx").c_str(), decoded_bc7);
+
+ image temp(m_decoded_output_textures_unpacked_bc7[slice_index]);
temp.crop(slice_desc.m_orig_width, slice_desc.m_orig_height);
- save_png(out_basename + "_decoded_bc1.png", temp);
+ save_png(out_basename + "_transcoded_bc7.png", temp);
}
}
}
diff --git a/basisu_comp.h b/basisu_comp.h
index 1c201dd..af32655 100644
--- a/basisu_comp.h
+++ b/basisu_comp.h
@@ -1,5 +1,5 @@
// basisu_comp.h
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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,6 +18,7 @@
#include "basisu_basis_file.h"
#include "transcoder/basisu_global_selector_palette.h"
#include "transcoder/basisu_transcoder.h"
+#include "basisu_uastc_enc.h"
namespace basisu
{
@@ -52,43 +53,52 @@ namespace basisu
m_filename.clear();
m_width = 0;
m_height = 0;
+
+ m_basis_rgb_avg_psnr = 0.0f;
+ m_basis_rgba_avg_psnr = 0.0f;
+ m_basis_a_avg_psnr = 0.0f;
+ m_basis_luma_709_psnr = 0.0f;
+ m_basis_luma_601_psnr = 0.0f;
+ m_basis_luma_709_ssim = 0.0f;
- m_basis_etc1s_rgb_avg_psnr = 0.0f;
- m_basis_etc1s_luma_709_psnr = 0.0f;
- m_basis_etc1s_luma_601_psnr = 0.0f;
- m_basis_etc1s_luma_709_ssim = 0.0f;
-
- m_basis_bc1_rgb_avg_psnr = 0.0f;
- m_basis_bc1_luma_709_psnr = 0.0f;
- m_basis_bc1_luma_601_psnr = 0.0f;
- m_basis_bc1_luma_709_ssim = 0.0f;
-
- m_best_rgb_avg_psnr = 0.0f;
- m_best_luma_709_psnr = 0.0f;
- m_best_luma_601_psnr = 0.0f;
- m_best_luma_709_ssim = 0.0f;
+ m_bc7_rgb_avg_psnr = 0.0f;
+ m_bc7_rgba_avg_psnr = 0.0f;
+ m_bc7_a_avg_psnr = 0.0f;
+ m_bc7_luma_709_psnr = 0.0f;
+ m_bc7_luma_601_psnr = 0.0f;
+ m_bc7_luma_709_ssim = 0.0f;
+
+ m_best_etc1s_rgb_avg_psnr = 0.0f;
+ m_best_etc1s_luma_709_psnr = 0.0f;
+ m_best_etc1s_luma_601_psnr = 0.0f;
+ m_best_etc1s_luma_709_ssim = 0.0f;
}
std::string m_filename;
uint32_t m_width;
uint32_t m_height;
- // .basis compressed
- float m_basis_etc1s_rgb_avg_psnr;
- float m_basis_etc1s_luma_709_psnr;
- float m_basis_etc1s_luma_601_psnr;
- float m_basis_etc1s_luma_709_ssim;
-
- float m_basis_bc1_rgb_avg_psnr;
- float m_basis_bc1_luma_709_psnr;
- float m_basis_bc1_luma_601_psnr;
- float m_basis_bc1_luma_709_ssim;
+ // .basis compressed (ETC1S or UASTC statistics)
+ float m_basis_rgb_avg_psnr;
+ float m_basis_rgba_avg_psnr;
+ float m_basis_a_avg_psnr;
+ float m_basis_luma_709_psnr;
+ float m_basis_luma_601_psnr;
+ float m_basis_luma_709_ssim;
- // Normal (highest quality) compressed ETC1S
- float m_best_rgb_avg_psnr;
- float m_best_luma_709_psnr;
- float m_best_luma_601_psnr;
- float m_best_luma_709_ssim;
+ // BC7 statistics
+ float m_bc7_rgb_avg_psnr;
+ float m_bc7_rgba_avg_psnr;
+ float m_bc7_a_avg_psnr;
+ float m_bc7_luma_709_psnr;
+ float m_bc7_luma_601_psnr;
+ float m_bc7_luma_709_ssim;
+
+ // 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;
+ float m_best_etc1s_luma_709_ssim;
};
template
@@ -175,18 +185,23 @@ namespace basisu
struct basis_compressor_params
{
basis_compressor_params() :
+ m_pSel_codebook(NULL),
+ m_compression_level((int)BASISU_DEFAULT_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_COMPRESSION_LEVEL),
+ m_selector_rdo_thresh(BASISU_DEFAULT_SELECTOR_RDO_THRESH, 0.0f, 1e+10f),
+ m_endpoint_rdo_thresh(BASISU_DEFAULT_ENDPOINT_RDO_THRESH, 0.0f, 1e+10f),
m_hybrid_sel_cb_quality_thresh(BASISU_DEFAULT_HYBRID_SEL_CB_QUALITY_THRESH, 0.0f, 1e+10f),
m_global_pal_bits(8, 0, ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_PAL_BITS),
m_global_mod_bits(8, 0, basist::etc1_global_palette_entry_modifier::cTotalBits),
- m_endpoint_rdo_thresh(BASISU_DEFAULT_ENDPOINT_RDO_THRESH, 0.0f, 1e+10f),
- m_selector_rdo_thresh(BASISU_DEFAULT_SELECTOR_RDO_THRESH, 0.0f, 1e+10f),
- m_pSel_codebook(NULL),
+ m_mip_scale(1.0f, .000125f, 4.0f),
+ m_mip_smallest_dimension(1, 1, 16384),
m_max_endpoint_clusters(512),
m_max_selector_clusters(512),
m_quality_level(-1),
- m_mip_scale(1.0f, .000125f, 4.0f),
- m_mip_smallest_dimension(1, 1, 16384),
- m_compression_level((int)BASISU_DEFAULT_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_COMPRESSION_LEVEL),
+ m_pack_uastc_flags(cPackUASTCLevelDefault),
+ m_rdo_uastc_quality_scalar(1.0f, 0.001f, 10.0f),
+ m_rdo_uastc_dict_size(32768, 256, 65536),
+ m_rdo_uastc_max_allowed_rms_increase_ratio(UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO, .01f, 100.0f),
+ m_rdo_uastc_skip_block_rms_thresh(UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH, .01f, 100.0f),
m_pJob_pool(nullptr)
{
clear();
@@ -196,6 +211,8 @@ namespace basisu
{
m_pSel_codebook = NULL;
+ m_uastc.clear();
+
m_source_filenames.clear();
m_source_alpha_filenames.clear();
@@ -247,12 +264,21 @@ namespace basisu
m_userdata1 = 0;
m_us_per_frame = 0;
+ m_pack_uastc_flags = cPackUASTCLevelDefault;
+ m_rdo_uastc.clear();
+ m_rdo_uastc_quality_scalar.clear();
+ m_rdo_uastc_max_allowed_rms_increase_ratio.clear();
+ m_rdo_uastc_skip_block_rms_thresh.clear();
+
m_pJob_pool = nullptr;
}
-
+
// Pointer to the global selector codebook, or nullptr to not use a global selector codebook
const basist::etc1_global_selector_codebook *m_pSel_codebook;
+ // True to generate UASTC .basis file data, otherwise ETC1S.
+ bool_param m_uastc;
+
// 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.
std::vector m_source_filenames;
@@ -343,6 +369,14 @@ namespace basisu
uint32_t m_userdata1;
uint32_t m_us_per_frame;
+ // cPackUASTCLevelDefault, etc.
+ uint32_t m_pack_uastc_flags;
+ bool_param m_rdo_uastc;
+ param m_rdo_uastc_quality_scalar;
+ param m_rdo_uastc_dict_size;
+ param m_rdo_uastc_max_allowed_rms_increase_ratio;
+ param m_rdo_uastc_skip_block_rms_thresh;
+
job_pool *m_pJob_pool;
};
@@ -360,25 +394,26 @@ namespace basisu
cECSuccess = 0,
cECFailedReadingSourceImages,
cECFailedValidating,
+ cECFailedEncodeUASTC,
cECFailedFrontEnd,
cECFailedFontendExtract,
cECFailedBackend,
cECFailedCreateBasisFile,
- cECFailedWritingOutput
+ cECFailedWritingOutput,
+ cECFailedUASTCRDOPostProcess
};
error_code process();
const uint8_vec &get_output_basis_file() const { return m_output_basis_file; }
- const etc_block_vec &get_output_blocks() const { return m_output_blocks; }
-
+
const std::vector &get_stats() const { return m_stats; }
uint32_t get_basis_file_size() const { return m_basis_file_size; }
double get_basis_bits_per_texel() const { return m_basis_bits_per_texel; }
bool get_any_source_image_has_alpha() const { return m_any_source_image_has_alpha; }
-
+
private:
basis_compressor_params m_params;
@@ -408,20 +443,24 @@ namespace basisu
std::vector m_decoded_output_textures;
std::vector m_decoded_output_textures_unpacked;
- std::vector m_decoded_output_textures_bc1;
- std::vector m_decoded_output_textures_unpacked_bc1;
+ std::vector m_decoded_output_textures_bc7;
+ std::vector m_decoded_output_textures_unpacked_bc7;
uint8_vec m_output_basis_file;
- etc_block_vec m_output_blocks;
+
+ std::vector m_uastc_slice_textures;
+ basisu_backend_output m_uastc_backend_output;
bool m_any_source_image_has_alpha;
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_output_files_and_compute_stats();
+ error_code encode_slices_to_uastc();
bool generate_mipmaps(const image &img, std::vector &mips, bool has_alpha);
bool validate_texture_type_constraints();
};
diff --git a/basisu_enc.cpp b/basisu_enc.cpp
index 7057c65..f4a9b20 100644
--- a/basisu_enc.cpp
+++ b/basisu_enc.cpp
@@ -1,5 +1,5 @@
// basisu_enc.cpp
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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,6 +18,8 @@
#include "basisu_resampler_filters.h"
#include "basisu_etc.h"
#include "transcoder/basisu_transcoder.h"
+#include "basisu_bc7enc.h"
+#include "apg_bmp.h"
#if defined(_WIN32)
// For QueryPerformanceCounter/QueryPerformanceFrequency
@@ -54,6 +56,9 @@ namespace basisu
void basisu_encoder_init()
{
basist::basisu_transcoder_init();
+ pack_etc1_solid_color_init();
+ //uastc_init();
+ bc7enc_compress_block_init(); // must be after uastc_init()
}
void error_printf(const char *pFmt, ...)
@@ -108,7 +113,7 @@ namespace basisu
#else
#error TODO
#endif
-
+
interval_timer::interval_timer() : m_start_time(0), m_stop_time(0), m_started(false), m_stopped(false)
{
if (!g_timer_freq)
@@ -169,6 +174,61 @@ namespace basisu
return ticks * g_timer_freq;
}
+ const uint32_t MAX_32BIT_ALLOC_SIZE = 250000000;
+
+ bool load_bmp(const char* pFilename, image& img)
+ {
+ int w = 0, h = 0;
+ unsigned int n_chans = 0;
+ unsigned char* pImage_data = apg_bmp_read(pFilename, &w, &h, &n_chans);
+
+ if ((!pImage_data) || (!w) || (!h) || ((n_chans != 3) && (n_chans != 4)))
+ {
+ error_printf("Failed loading .BMP image \"%s\"!\n", pFilename);
+
+ if (pImage_data)
+ apg_bmp_free(pImage_data);
+
+ return false;
+ }
+
+ if (sizeof(void *) == sizeof(uint32_t))
+ {
+ if ((w * h * n_chans) > MAX_32BIT_ALLOC_SIZE)
+ {
+ error_printf("Image \"%s\" is too large (%ux%u) to process in a 32-bit build!\n", pFilename, w, h);
+
+ if (pImage_data)
+ apg_bmp_free(pImage_data);
+
+ return false;
+ }
+ }
+
+ img.resize(w, h);
+
+ const uint8_t *pSrc = pImage_data;
+ for (int y = 0; y < h; y++)
+ {
+ color_rgba *pDst = &img(0, y);
+
+ for (int x = 0; x < w; x++)
+ {
+ pDst->r = pSrc[0];
+ pDst->g = pSrc[1];
+ pDst->b = pSrc[2];
+ pDst->a = (n_chans == 3) ? 255 : pSrc[3];
+
+ pSrc += n_chans;
+ ++pDst;
+ }
+ }
+
+ apg_bmp_free(pImage_data);
+
+ return true;
+ }
+
bool load_png(const char* pFilename, image& img)
{
std::vector buffer;
@@ -187,10 +247,9 @@ namespace basisu
return false;
const uint32_t exepected_alloc_size = w * h * sizeof(uint32_t);
-
+
// If the file is too large on 32-bit builds then just bail now, to prevent causing a memory exception.
- const uint32_t MAX_ALLOC_SIZE = 250000000;
- if (exepected_alloc_size >= MAX_ALLOC_SIZE)
+ if (exepected_alloc_size >= MAX_32BIT_ALLOC_SIZE)
{
error_printf("Image \"%s\" is too large (%ux%u) to process in a 32-bit build!\n", pFilename, w, h);
return false;
@@ -213,12 +272,33 @@ namespace basisu
return true;
}
+
+ bool load_image(const char* pFilename, image& img)
+ {
+ std::string ext(string_get_extension(std::string(pFilename)));
+
+ if (ext.length() == 0)
+ return false;
+
+ const char *pExt = ext.c_str();
+
+ if (strcasecmp(pExt, "png") == 0)
+ return load_png(pFilename, img);
+ if (strcasecmp(pExt, "bmp") == 0)
+ return load_bmp(pFilename, img);
+
+ return false;
+ }
- bool save_png(const char* pFilename, const image & img, uint32_t image_save_flags, uint32_t grayscale_comp)
+ bool save_png(const char* pFilename, const image &img, uint32_t image_save_flags, uint32_t grayscale_comp)
{
if (!img.get_total_pixels())
return false;
+ const uint32_t MAX_PNG_IMAGE_DIM = 32768;
+ if ((img.get_width() > MAX_PNG_IMAGE_DIM) || (img.get_height() > MAX_PNG_IMAGE_DIM))
+ return false;
+
std::vector out;
unsigned err = 0;
@@ -231,16 +311,17 @@ namespace basisu
for (uint32_t x = 0; x < img.get_width(); x++)
*pDst++ = img(x, y)[grayscale_comp];
- err = lodepng::encode(out, (const uint8_t*)& g_pixels[0], img.get_width(), img.get_height(), LCT_GREY, 8);
+ err = lodepng::encode(out, (const uint8_t*)&g_pixels[0], img.get_width(), img.get_height(), LCT_GREY, 8);
}
else
{
bool has_alpha = img.has_alpha();
if ((!has_alpha) || ((image_save_flags & cImageSaveIgnoreAlpha) != 0))
{
- uint8_vec rgb_pixels(img.get_width() * 3 * img.get_height());
+ const uint64_t total_bytes = (uint64_t)img.get_width() * 3U * (uint64_t)img.get_height();
+ uint8_vec rgb_pixels(total_bytes);
uint8_t *pDst = &rgb_pixels[0];
-
+
for (uint32_t y = 0; y < img.get_height(); y++)
{
for (uint32_t x = 0; x < img.get_width(); x++)
@@ -1171,9 +1252,9 @@ namespace basisu
total_values *= (double)clamp(total_chans, 1, 4);
m_mean = (float)clamp(sum / total_values, 0.0f, 255.0);
- m_mean_squared = (float)clamp(sum2 / total_values, 0.0f, 255.0 * 255.0);
+ m_mean_squared = (float)clamp(sum2 / total_values, 0.0f, 255.0f * 255.0f);
m_rms = (float)sqrt(m_mean_squared);
- m_psnr = m_rms ? (float)clamp(log10(255.0 / m_rms) * 20.0, 0.0f, 300.0f) : 1e+10f;
+ m_psnr = m_rms ? (float)clamp(log10(255.0 / m_rms) * 20.0f, 0.0f, 100.0f) : 100.0f;
}
void fill_buffer_with_random_bytes(void *pBuf, size_t size, uint32_t seed)
@@ -1253,8 +1334,8 @@ namespace basisu
}
job_pool::job_pool(uint32_t num_threads) :
- m_kill_flag(false),
- m_num_active_jobs(0)
+ m_num_active_jobs(0),
+ m_kill_flag(false)
{
assert(num_threads >= 1U);
diff --git a/basisu_enc.h b/basisu_enc.h
index c2b9133..146111b 100644
--- a/basisu_enc.h
+++ b/basisu_enc.h
@@ -1,5 +1,5 @@
// basisu_enc.h
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -31,7 +31,8 @@ namespace basisu
{
extern uint8_t g_hamming_dist[256];
- // Encoder library initialization
+ // Encoder library initialization.
+ // This function MUST be called before encoding anything!
void basisu_encoder_init();
void error_printf(const char *pFmt, ...);
@@ -42,7 +43,67 @@ namespace basisu
{
return (uint8_t)((i & 0xFFFFFF00U) ? (~(i >> 31)) : i);
}
-
+
+ inline int32_t clampi(int32_t value, int32_t low, int32_t high)
+ {
+ if (value < low)
+ value = low;
+ else if (value > high)
+ value = high;
+ return value;
+ }
+
+ inline uint8_t mul_8(uint32_t v, uint32_t a)
+ {
+ v = v * a + 128;
+ return (uint8_t)((v + (v >> 8)) >> 8);
+ }
+ inline uint64_t read_bits(const uint8_t* pBuf, uint32_t& bit_offset, uint32_t codesize)
+ {
+ assert(codesize <= 64);
+ uint64_t bits = 0;
+ uint32_t total_bits = 0;
+
+ while (total_bits < codesize)
+ {
+ uint32_t byte_bit_offset = bit_offset & 7;
+ uint32_t bits_to_read = minimum(codesize - total_bits, 8 - byte_bit_offset);
+
+ uint32_t byte_bits = pBuf[bit_offset >> 3] >> byte_bit_offset;
+ byte_bits &= ((1 << bits_to_read) - 1);
+
+ bits |= ((uint64_t)(byte_bits) << total_bits);
+
+ total_bits += bits_to_read;
+ bit_offset += bits_to_read;
+ }
+
+ return bits;
+ }
+
+ inline uint32_t read_bits32(const uint8_t* pBuf, uint32_t& bit_offset, uint32_t codesize)
+ {
+ assert(codesize <= 32);
+ uint32_t bits = 0;
+ uint32_t total_bits = 0;
+
+ while (total_bits < codesize)
+ {
+ uint32_t byte_bit_offset = bit_offset & 7;
+ uint32_t bits_to_read = minimum(codesize - total_bits, 8 - byte_bit_offset);
+
+ uint32_t byte_bits = pBuf[bit_offset >> 3] >> byte_bit_offset;
+ byte_bits &= ((1 << bits_to_read) - 1);
+
+ bits |= (byte_bits << total_bits);
+
+ total_bits += bits_to_read;
+ bit_offset += bits_to_read;
+ }
+
+ return bits;
+ }
+
// Hashing
inline uint32_t bitmix32c(uint32_t v)
@@ -78,6 +139,71 @@ namespace basisu
return hash_hsieh(reinterpret_cast(&k), sizeof(k));
}
};
+ class running_stat
+ {
+ public:
+ running_stat() :
+ m_n(0),
+ m_old_m(0), m_new_m(0), m_old_s(0), m_new_s(0)
+ {
+ }
+ void clear()
+ {
+ m_n = 0;
+ }
+ void push(double x)
+ {
+ m_n++;
+ if (m_n == 1)
+ {
+ m_old_m = m_new_m = x;
+ m_old_s = 0.0;
+ m_min = x;
+ m_max = x;
+ }
+ else
+ {
+ m_new_m = m_old_m + (x - m_old_m) / m_n;
+ m_new_s = m_old_s + (x - m_old_m) * (x - m_new_m);
+ m_old_m = m_new_m;
+ m_old_s = m_new_s;
+ m_min = std::min(x, m_min);
+ m_max = std::max(x, m_max);
+ }
+ }
+ uint32_t get_num() const
+ {
+ return m_n;
+ }
+ double get_mean() const
+ {
+ return (m_n > 0) ? m_new_m : 0.0;
+ }
+
+ double get_variance() const
+ {
+ return ((m_n > 1) ? m_new_s / (m_n - 1) : 0.0);
+ }
+
+ double get_std_dev() const
+ {
+ return sqrt(get_variance());
+ }
+
+ double get_min() const
+ {
+ return m_min;
+ }
+
+ double get_max() const
+ {
+ return m_max;
+ }
+
+ private:
+ uint32_t m_n;
+ double m_old_m, m_new_m, m_old_s, m_new_s, m_min, m_max;
+ };
// Linear algebra
@@ -117,7 +243,7 @@ namespace basisu
inline vec &set(const vec &other)
{
uint32_t i;
- if (static_cast(&other) == static_cast(this))
+ if ((const void *)(&other) == (const void *)(this))
return *this;
const uint32_t m = minimum(OtherN, N);
for (i = 0; i < m; i++)
@@ -357,6 +483,7 @@ namespace basisu
BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(job_pool);
public:
+ // num_threads is the TOTAL number of job pool threads, including the calling thread! So 2=1 new thread, 3=2 new threads, etc.
job_pool(uint32_t num_threads);
~job_pool();
@@ -419,7 +546,7 @@ namespace basisu
return *this;
}
};
-
+
class color_rgba
{
public:
@@ -439,6 +566,25 @@ namespace basisu
inline color_rgba()
{
static_assert(sizeof(*this) == 4, "sizeof(*this) != 4");
+ static_assert(sizeof(*this) == sizeof(basist::color32), "sizeof(*this) != sizeof(basist::color32)");
+ }
+
+ // Not too hot about this idea.
+ inline color_rgba(const basist::color32& other) :
+ r(other.r),
+ g(other.g),
+ b(other.b),
+ a(other.a)
+ {
+ }
+
+ color_rgba& operator= (const basist::color32& rhs)
+ {
+ r = rhs.r;
+ g = rhs.g;
+ b = rhs.b;
+ a = rhs.a;
+ return *this;
}
inline color_rgba(int y)
@@ -562,6 +708,14 @@ namespace basisu
inline int get_601_luma() const { return (19595U * m_comps[0] + 38470U * m_comps[1] + 7471U * m_comps[2] + 32768U) >> 16U; }
inline int get_709_luma() const { return (13938U * m_comps[0] + 46869U * m_comps[1] + 4729U * m_comps[2] + 32768U) >> 16U; }
inline int get_luma(bool luma_601) const { return luma_601 ? get_601_luma() : get_709_luma(); }
+
+ inline basist::color32 get_color32() const
+ {
+ return basist::color32(r, g, b, a);
+ }
+
+ static color_rgba comp_min(const color_rgba& a, const color_rgba& b) { return color_rgba(std::min(a[0], b[0]), std::min(a[1], b[1]), std::min(a[2], b[2]), std::min(a[3], b[3])); }
+ static color_rgba comp_max(const color_rgba& a, const color_rgba& b) { return color_rgba(std::max(a[0], b[0]), std::max(a[1], b[1]), std::max(a[2], b[2]), std::max(a[3], b[3])); }
};
typedef std::vector color_rgba_vec;
@@ -621,6 +775,13 @@ namespace basisu
return color_distance(e1, e2, alpha);
}
+ static inline uint32_t color_distance_la(const color_rgba& a, const color_rgba& b)
+ {
+ const int dl = a.r - b.r;
+ const int da = a.a - b.a;
+ return dl * dl + da * da;
+ }
+
// String helpers
inline int string_find_right(const std::string& filename, char c)
@@ -1238,7 +1399,7 @@ namespace basisu
bool prep_split(const tsvq_node &node, TrainingVectorType &l_child_result, TrainingVectorType &r_child_result) const
{
- const uint32_t N = TrainingVectorType::num_elements;
+ //const uint32_t N = TrainingVectorType::num_elements;
if (2 == node.m_training_vecs.size())
{
@@ -2634,9 +2795,13 @@ namespace basisu
};
// Image saving/loading/resampling
-
+
bool load_png(const char* pFilename, image& img);
inline bool load_png(const std::string &filename, image &img) { return load_png(filename.c_str(), img); }
+
+ // Currently loads .BMP or .PNG.
+ 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); }
enum
{
diff --git a/basisu_etc.cpp b/basisu_etc.cpp
index 2a23afb..f57ae09 100644
--- a/basisu_etc.cpp
+++ b/basisu_etc.cpp
@@ -1,5 +1,5 @@
// basis_etc.cpp
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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.
@@ -19,6 +19,228 @@
namespace basisu
{
+ const int8_t g_etc2_eac_tables[16][8] =
+ {
+ { -3, -6, -9, -15, 2, 5, 8, 14 }, { -3, -7, -10, -13, 2, 6, 9, 12 }, { -2, -5, -8, -13, 1, 4, 7, 12 }, { -2, -4, -6, -13, 1, 3, 5, 12 },
+ { -3, -6, -8, -12, 2, 5, 7, 11 }, { -3, -7, -9, -11, 2, 6, 8, 10 }, { -4, -7, -8, -11, 3, 6, 7, 10 }, { -3, -5, -8, -11, 2, 4, 7, 10 },
+ { -2, -6, -8, -10, 1, 5, 7, 9 }, { -2, -5, -8, -10, 1, 4, 7, 9 }, { -2, -4, -8, -10, 1, 3, 7, 9 }, { -2, -5, -7, -10, 1, 4, 6, 9 },
+ { -3, -4, -7, -10, 2, 3, 6, 9 }, { -1, -2, -3, -10, 0, 1, 2, 9 }, { -4, -6, -8, -9, 3, 5, 7, 8 }, { -3, -5, -7, -9, 2, 4, 6, 8 }
+ };
+
+ const int8_t g_etc2_eac_tables8[16][8] =
+ {
+ { -24, -48, -72, -120, 16, 40, 64, 112 }, { -24,-56,-80,-104,16,48,72,96 }, { -16,-40,-64,-104,8,32,56,96 }, { -16,-32,-48,-104,8,24,40,96 },
+ { -24,-48,-64,-96,16,40,56,88 }, { -24,-56,-72,-88,16,48,64,80 }, { -32,-56,-64,-88,24,48,56,80 }, { -24,-40,-64,-88,16,32,56,80 },
+ { -16,-48,-64,-80,8,40,56,72 }, { -16,-40,-64,-80,8,32,56,72 }, { -16,-32,-64,-80,8,24,56,72 }, { -16,-40,-56,-80,8,32,48,72 },
+ { -24,-32,-56,-80,16,24,48,72 }, { -8,-16,-24,-80,0,8,16,72 }, { -32,-48,-64,-72,24,40,56,64 }, { -24,-40,-56,-72,16,32,48,64 }
+ };
+
+ // Given an ETC1 diff/inten_table/selector, and an 8-bit desired color, this table encodes the best packed_color in the low byte, and the abs error in the high byte.
+ static uint16_t g_etc1_inverse_lookup[2 * 8 * 4][256]; // [ diff/inten_table/selector][desired_color ]
+
+ // g_color8_to_etc_block_config[color][table_index] = Supplies for each 8-bit color value a list of packed ETC1 diff/intensity table/selectors/packed_colors that map to that color.
+ // To pack: diff | (inten << 1) | (selector << 4) | (packed_c << 8)
+ static const uint16_t g_etc1_color8_to_etc_block_config_0_255[2][33] =
+ {
+ { 0x0000, 0x0010, 0x0002, 0x0012, 0x0004, 0x0014, 0x0006, 0x0016, 0x0008, 0x0018, 0x000A, 0x001A, 0x000C, 0x001C, 0x000E, 0x001E, 0x0001, 0x0011, 0x0003, 0x0013, 0x0005, 0x0015, 0x0007, 0x0017, 0x0009, 0x0019, 0x000B, 0x001B, 0x000D, 0x001D, 0x000F, 0x001F, 0xFFFF },
+ { 0x0F20, 0x0F30, 0x0E32, 0x0F22, 0x0E34, 0x0F24, 0x0D36, 0x0F26, 0x0C38, 0x0E28, 0x0B3A, 0x0E2A, 0x093C, 0x0E2C, 0x053E, 0x0D2E, 0x1E31, 0x1F21, 0x1D33, 0x1F23, 0x1C35, 0x1E25, 0x1A37, 0x1E27, 0x1839, 0x1D29, 0x163B, 0x1C2B, 0x133D, 0x1B2D, 0x093F, 0x1A2F, 0xFFFF },
+ };
+
+ // Really only [254][11].
+ static const uint16_t g_etc1_color8_to_etc_block_config_1_to_254[254][12] =
+ {
+ { 0x021C, 0x0D0D, 0xFFFF }, { 0x0020, 0x0021, 0x0A0B, 0x061F, 0xFFFF }, { 0x0113, 0x0217, 0xFFFF }, { 0x0116, 0x031E, 0x0B0E, 0x0405, 0xFFFF }, { 0x0022, 0x0204, 0x050A, 0x0023, 0xFFFF }, { 0x0111, 0x0319, 0x0809, 0x170F, 0xFFFF }, {
+ 0x0303, 0x0215, 0x0607, 0xFFFF }, { 0x0030, 0x0114, 0x0408, 0x0031, 0x0201, 0x051D, 0xFFFF }, { 0x0100, 0x0024, 0x0306, 0x0025, 0x041B, 0x0E0D, 0xFFFF }, { 0x021A, 0x0121, 0x0B0B, 0x071F, 0xFFFF }, { 0x0213, 0x0317, 0xFFFF }, { 0x0112,
+ 0x0505, 0xFFFF }, { 0x0026, 0x070C, 0x0123, 0x0027, 0xFFFF }, { 0x0211, 0x0909, 0xFFFF }, { 0x0110, 0x0315, 0x0707, 0x0419, 0x180F, 0xFFFF }, { 0x0218, 0x0131, 0x0301, 0x0403, 0x061D, 0xFFFF }, { 0x0032, 0x0202, 0x0033, 0x0125, 0x051B,
+ 0x0F0D, 0xFFFF }, { 0x0028, 0x031C, 0x0221, 0x0029, 0xFFFF }, { 0x0120, 0x0313, 0x0C0B, 0x081F, 0xFFFF }, { 0x0605, 0x0417, 0xFFFF }, { 0x0216, 0x041E, 0x0C0E, 0x0223, 0x0127, 0xFFFF }, { 0x0122, 0x0304, 0x060A, 0x0311, 0x0A09, 0xFFFF
+ }, { 0x0519, 0x190F, 0xFFFF }, { 0x002A, 0x0231, 0x0503, 0x0415, 0x0807, 0x002B, 0x071D, 0xFFFF }, { 0x0130, 0x0214, 0x0508, 0x0401, 0x0133, 0x0225, 0x061B, 0xFFFF }, { 0x0200, 0x0124, 0x0406, 0x0321, 0x0129, 0x100D, 0xFFFF }, { 0x031A,
+ 0x0D0B, 0x091F, 0xFFFF }, { 0x0413, 0x0705, 0x0517, 0xFFFF }, { 0x0212, 0x0034, 0x0323, 0x0035, 0x0227, 0xFFFF }, { 0x0126, 0x080C, 0x0B09, 0xFFFF }, { 0x0411, 0x0619, 0x1A0F, 0xFFFF }, { 0x0210, 0x0331, 0x0603, 0x0515, 0x0907, 0x012B,
+ 0xFFFF }, { 0x0318, 0x002C, 0x0501, 0x0233, 0x0325, 0x071B, 0x002D, 0x081D, 0xFFFF }, { 0x0132, 0x0302, 0x0229, 0x110D, 0xFFFF }, { 0x0128, 0x041C, 0x0421, 0x0E0B, 0x0A1F, 0xFFFF }, { 0x0220, 0x0513, 0x0617, 0xFFFF }, { 0x0135, 0x0805,
+ 0x0327, 0xFFFF }, { 0x0316, 0x051E, 0x0D0E, 0x0423, 0xFFFF }, { 0x0222, 0x0404, 0x070A, 0x0511, 0x0719, 0x0C09, 0x1B0F, 0xFFFF }, { 0x0703, 0x0615, 0x0A07, 0x022B, 0xFFFF }, { 0x012A, 0x0431, 0x0601, 0x0333, 0x012D, 0x091D, 0xFFFF }, {
+ 0x0230, 0x0314, 0x0036, 0x0608, 0x0425, 0x0037, 0x0329, 0x081B, 0x120D, 0xFFFF }, { 0x0300, 0x0224, 0x0506, 0x0521, 0x0F0B, 0x0B1F, 0xFFFF }, { 0x041A, 0x0613, 0x0717, 0xFFFF }, { 0x0235, 0x0905, 0xFFFF }, { 0x0312, 0x0134, 0x0523,
+ 0x0427, 0xFFFF }, { 0x0226, 0x090C, 0x002E, 0x0611, 0x0D09, 0x002F, 0xFFFF }, { 0x0715, 0x0B07, 0x0819, 0x032B, 0x1C0F, 0xFFFF }, { 0x0310, 0x0531, 0x0701, 0x0803, 0x022D, 0x0A1D, 0xFFFF }, { 0x0418, 0x012C, 0x0433, 0x0525, 0x0137, 0x091B,
+ 0x130D, 0xFFFF }, { 0x0232, 0x0402, 0x0621, 0x0429, 0xFFFF }, { 0x0228, 0x051C, 0x0713, 0x100B, 0x0C1F, 0xFFFF }, { 0x0320, 0x0335, 0x0A05, 0x0817, 0xFFFF }, { 0x0623, 0x0527, 0xFFFF }, { 0x0416, 0x061E, 0x0E0E, 0x0711, 0x0E09, 0x012F,
+ 0xFFFF }, { 0x0322, 0x0504, 0x080A, 0x0919, 0x1D0F, 0xFFFF }, { 0x0631, 0x0903, 0x0815, 0x0C07, 0x042B, 0x032D, 0x0B1D, 0xFFFF }, { 0x022A, 0x0801, 0x0533, 0x0625, 0x0237, 0x0A1B, 0xFFFF }, { 0x0330, 0x0414, 0x0136, 0x0708, 0x0721, 0x0529,
+ 0x140D, 0xFFFF }, { 0x0400, 0x0324, 0x0606, 0x0038, 0x0039, 0x110B, 0x0D1F, 0xFFFF }, { 0x051A, 0x0813, 0x0B05, 0x0917, 0xFFFF }, { 0x0723, 0x0435, 0x0627, 0xFFFF }, { 0x0412, 0x0234, 0x0F09, 0x022F, 0xFFFF }, { 0x0326, 0x0A0C, 0x012E,
+ 0x0811, 0x0A19, 0x1E0F, 0xFFFF }, { 0x0731, 0x0A03, 0x0915, 0x0D07, 0x052B, 0xFFFF }, { 0x0410, 0x0901, 0x0633, 0x0725, 0x0337, 0x0B1B, 0x042D, 0x0C1D, 0xFFFF }, { 0x0518, 0x022C, 0x0629, 0x150D, 0xFFFF }, { 0x0332, 0x0502, 0x0821, 0x0139,
+ 0x120B, 0x0E1F, 0xFFFF }, { 0x0328, 0x061C, 0x0913, 0x0A17, 0xFFFF }, { 0x0420, 0x0535, 0x0C05, 0x0727, 0xFFFF }, { 0x0823, 0x032F, 0xFFFF }, { 0x0516, 0x071E, 0x0F0E, 0x0911, 0x0B19, 0x1009, 0x1F0F, 0xFFFF }, { 0x0422, 0x0604, 0x090A,
+ 0x0B03, 0x0A15, 0x0E07, 0x062B, 0xFFFF }, { 0x0831, 0x0A01, 0x0733, 0x052D, 0x0D1D, 0xFFFF }, { 0x032A, 0x0825, 0x0437, 0x0729, 0x0C1B, 0x160D, 0xFFFF }, { 0x0430, 0x0514, 0x0236, 0x0808, 0x0921, 0x0239, 0x130B, 0x0F1F, 0xFFFF }, { 0x0500,
+ 0x0424, 0x0706, 0x0138, 0x0A13, 0x0B17, 0xFFFF }, { 0x061A, 0x0635, 0x0D05, 0xFFFF }, { 0x0923, 0x0827, 0xFFFF }, { 0x0512, 0x0334, 0x003A, 0x0A11, 0x1109, 0x003B, 0x042F, 0xFFFF }, { 0x0426, 0x0B0C, 0x022E, 0x0B15, 0x0F07, 0x0C19,
+ 0x072B, 0xFFFF }, { 0x0931, 0x0B01, 0x0C03, 0x062D, 0x0E1D, 0xFFFF }, { 0x0510, 0x0833, 0x0925, 0x0537, 0x0D1B, 0x170D, 0xFFFF }, { 0x0618, 0x032C, 0x0A21, 0x0339, 0x0829, 0xFFFF }, { 0x0432, 0x0602, 0x0B13, 0x140B, 0x101F, 0xFFFF }, {
+ 0x0428, 0x071C, 0x0735, 0x0E05, 0x0C17, 0xFFFF }, { 0x0520, 0x0A23, 0x0927, 0xFFFF }, { 0x0B11, 0x1209, 0x013B, 0x052F, 0xFFFF }, { 0x0616, 0x081E, 0x0D19, 0xFFFF }, { 0x0522, 0x0704, 0x0A0A, 0x0A31, 0x0D03, 0x0C15, 0x1007, 0x082B, 0x072D,
+ 0x0F1D, 0xFFFF }, { 0x0C01, 0x0933, 0x0A25, 0x0637, 0x0E1B, 0xFFFF }, { 0x042A, 0x0B21, 0x0929, 0x180D, 0xFFFF }, { 0x0530, 0x0614, 0x0336, 0x0908, 0x0439, 0x150B, 0x111F, 0xFFFF }, { 0x0600, 0x0524, 0x0806, 0x0238, 0x0C13, 0x0F05,
+ 0x0D17, 0xFFFF }, { 0x071A, 0x0B23, 0x0835, 0x0A27, 0xFFFF }, { 0x1309, 0x023B, 0x062F, 0xFFFF }, { 0x0612, 0x0434, 0x013A, 0x0C11, 0x0E19, 0xFFFF }, { 0x0526, 0x0C0C, 0x032E, 0x0B31, 0x0E03, 0x0D15, 0x1107, 0x092B, 0xFFFF }, { 0x0D01,
+ 0x0A33, 0x0B25, 0x0737, 0x0F1B, 0x082D, 0x101D, 0xFFFF }, { 0x0610, 0x0A29, 0x190D, 0xFFFF }, { 0x0718, 0x042C, 0x0C21, 0x0539, 0x160B, 0x121F, 0xFFFF }, { 0x0532, 0x0702, 0x0D13, 0x0E17, 0xFFFF }, { 0x0528, 0x081C, 0x0935, 0x1005, 0x0B27,
+ 0xFFFF }, { 0x0620, 0x0C23, 0x033B, 0x072F, 0xFFFF }, { 0x0D11, 0x0F19, 0x1409, 0xFFFF }, { 0x0716, 0x003C, 0x091E, 0x0F03, 0x0E15, 0x1207, 0x0A2B, 0x003D, 0xFFFF }, { 0x0622, 0x0804, 0x0B0A, 0x0C31, 0x0E01, 0x0B33, 0x092D, 0x111D,
+ 0xFFFF }, { 0x0C25, 0x0837, 0x0B29, 0x101B, 0x1A0D, 0xFFFF }, { 0x052A, 0x0D21, 0x0639, 0x170B, 0x131F, 0xFFFF }, { 0x0630, 0x0714, 0x0436, 0x0A08, 0x0E13, 0x0F17, 0xFFFF }, { 0x0700, 0x0624, 0x0906, 0x0338, 0x0A35, 0x1105, 0xFFFF }, {
+ 0x081A, 0x0D23, 0x0C27, 0xFFFF }, { 0x0E11, 0x1509, 0x043B, 0x082F, 0xFFFF }, { 0x0712, 0x0534, 0x023A, 0x0F15, 0x1307, 0x1019, 0x0B2B, 0x013D, 0xFFFF }, { 0x0626, 0x0D0C, 0x042E, 0x0D31, 0x0F01, 0x1003, 0x0A2D, 0x121D, 0xFFFF }, { 0x0C33,
+ 0x0D25, 0x0937, 0x111B, 0x1B0D, 0xFFFF }, { 0x0710, 0x0E21, 0x0739, 0x0C29, 0xFFFF }, { 0x0818, 0x052C, 0x0F13, 0x180B, 0x141F, 0xFFFF }, { 0x0632, 0x0802, 0x0B35, 0x1205, 0x1017, 0xFFFF }, { 0x0628, 0x091C, 0x0E23, 0x0D27, 0xFFFF }, {
+ 0x0720, 0x0F11, 0x1609, 0x053B, 0x092F, 0xFFFF }, { 0x1119, 0x023D, 0xFFFF }, { 0x0816, 0x013C, 0x0A1E, 0x0E31, 0x1103, 0x1015, 0x1407, 0x0C2B, 0x0B2D, 0x131D, 0xFFFF }, { 0x0722, 0x0904, 0x0C0A, 0x1001, 0x0D33, 0x0E25, 0x0A37, 0x121B,
+ 0xFFFF }, { 0x0F21, 0x0D29, 0x1C0D, 0xFFFF }, { 0x062A, 0x0839, 0x190B, 0x151F, 0xFFFF }, { 0x0730, 0x0814, 0x0536, 0x0B08, 0x1013, 0x1305, 0x1117, 0xFFFF }, { 0x0800, 0x0724, 0x0A06, 0x0438, 0x0F23, 0x0C35, 0x0E27, 0xFFFF }, { 0x091A,
+ 0x1709, 0x063B, 0x0A2F, 0xFFFF }, { 0x1011, 0x1219, 0x033D, 0xFFFF }, { 0x0812, 0x0634, 0x033A, 0x0F31, 0x1203, 0x1115, 0x1507, 0x0D2B, 0xFFFF }, { 0x0726, 0x0E0C, 0x052E, 0x1101, 0x0E33, 0x0F25, 0x0B37, 0x131B, 0x0C2D, 0x141D, 0xFFFF }, {
+ 0x0E29, 0x1D0D, 0xFFFF }, { 0x0810, 0x1021, 0x0939, 0x1A0B, 0x161F, 0xFFFF }, { 0x0918, 0x062C, 0x1113, 0x1217, 0xFFFF }, { 0x0732, 0x0902, 0x0D35, 0x1405, 0x0F27, 0xFFFF }, { 0x0728, 0x0A1C, 0x1023, 0x073B, 0x0B2F, 0xFFFF }, { 0x0820,
+ 0x1111, 0x1319, 0x1809, 0xFFFF }, { 0x1303, 0x1215, 0x1607, 0x0E2B, 0x043D, 0xFFFF }, { 0x0916, 0x023C, 0x0B1E, 0x1031, 0x1201, 0x0F33, 0x0D2D, 0x151D, 0xFFFF }, { 0x0822, 0x0A04, 0x0D0A, 0x1025, 0x0C37, 0x0F29, 0x141B, 0x1E0D, 0xFFFF }, {
+ 0x1121, 0x0A39, 0x1B0B, 0x171F, 0xFFFF }, { 0x072A, 0x1213, 0x1317, 0xFFFF }, { 0x0830, 0x0914, 0x0636, 0x0C08, 0x0E35, 0x1505, 0xFFFF }, { 0x0900, 0x0824, 0x0B06, 0x0538, 0x1123, 0x1027, 0xFFFF }, { 0x0A1A, 0x1211, 0x1909, 0x083B, 0x0C2F,
+ 0xFFFF }, { 0x1315, 0x1707, 0x1419, 0x0F2B, 0x053D, 0xFFFF }, { 0x0912, 0x0734, 0x043A, 0x1131, 0x1301, 0x1403, 0x0E2D, 0x161D, 0xFFFF }, { 0x0826, 0x0F0C, 0x062E, 0x1033, 0x1125, 0x0D37, 0x151B, 0x1F0D, 0xFFFF }, { 0x1221, 0x0B39, 0x1029,
+ 0xFFFF }, { 0x0910, 0x1313, 0x1C0B, 0x181F, 0xFFFF }, { 0x0A18, 0x072C, 0x0F35, 0x1605, 0x1417, 0xFFFF }, { 0x0832, 0x0A02, 0x1223, 0x1127, 0xFFFF }, { 0x0828, 0x0B1C, 0x1311, 0x1A09, 0x093B, 0x0D2F, 0xFFFF }, { 0x0920, 0x1519, 0x063D,
+ 0xFFFF }, { 0x1231, 0x1503, 0x1415, 0x1807, 0x102B, 0x0F2D, 0x171D, 0xFFFF }, { 0x0A16, 0x033C, 0x0C1E, 0x1401, 0x1133, 0x1225, 0x0E37, 0x161B, 0xFFFF }, { 0x0922, 0x0B04, 0x0E0A, 0x1321, 0x1129, 0xFFFF }, { 0x0C39, 0x1D0B, 0x191F, 0xFFFF
+ }, { 0x082A, 0x1413, 0x1705, 0x1517, 0xFFFF }, { 0x0930, 0x0A14, 0x0736, 0x0D08, 0x1323, 0x1035, 0x1227, 0xFFFF }, { 0x0A00, 0x0924, 0x0C06, 0x0638, 0x1B09, 0x0A3B, 0x0E2F, 0xFFFF }, { 0x0B1A, 0x1411, 0x1619, 0x073D, 0xFFFF }, { 0x1331,
+ 0x1603, 0x1515, 0x1907, 0x112B, 0xFFFF }, { 0x0A12, 0x0834, 0x053A, 0x1501, 0x1233, 0x1325, 0x0F37, 0x171B, 0x102D, 0x181D, 0xFFFF }, { 0x0926, 0x072E, 0x1229, 0xFFFF }, { 0x1421, 0x0D39, 0x1E0B, 0x1A1F, 0xFFFF }, { 0x0A10, 0x1513,
+ 0x1617, 0xFFFF }, { 0x0B18, 0x082C, 0x1135, 0x1805, 0x1327, 0xFFFF }, { 0x0932, 0x0B02, 0x1423, 0x0B3B, 0x0F2F, 0xFFFF }, { 0x0928, 0x0C1C, 0x1511, 0x1719, 0x1C09, 0xFFFF }, { 0x0A20, 0x1703, 0x1615, 0x1A07, 0x122B, 0x083D, 0xFFFF }, {
+ 0x1431, 0x1601, 0x1333, 0x112D, 0x191D, 0xFFFF }, { 0x0B16, 0x043C, 0x0D1E, 0x1425, 0x1037, 0x1329, 0x181B, 0xFFFF }, { 0x0A22, 0x0C04, 0x0F0A, 0x1521, 0x0E39, 0x1F0B, 0x1B1F, 0xFFFF }, { 0x1613, 0x1717, 0xFFFF }, { 0x092A, 0x1235, 0x1905,
+ 0xFFFF }, { 0x0A30, 0x0B14, 0x0836, 0x0E08, 0x1523, 0x1427, 0xFFFF }, { 0x0B00, 0x0A24, 0x0D06, 0x0738, 0x1611, 0x1D09, 0x0C3B, 0x102F, 0xFFFF }, { 0x0C1A, 0x1715, 0x1B07, 0x1819, 0x132B, 0x093D, 0xFFFF }, { 0x1531, 0x1701, 0x1803, 0x122D,
+ 0x1A1D, 0xFFFF }, { 0x0B12, 0x0934, 0x063A, 0x1433, 0x1525, 0x1137, 0x191B, 0xFFFF }, { 0x0A26, 0x003E, 0x082E, 0x1621, 0x0F39, 0x1429, 0x003F, 0xFFFF }, { 0x1713, 0x1C1F, 0xFFFF }, { 0x0B10, 0x1335, 0x1A05, 0x1817, 0xFFFF }, { 0x0C18,
+ 0x092C, 0x1623, 0x1527, 0xFFFF }, { 0x0A32, 0x0C02, 0x1711, 0x1E09, 0x0D3B, 0x112F, 0xFFFF }, { 0x0A28, 0x0D1C, 0x1919, 0x0A3D, 0xFFFF }, { 0x0B20, 0x1631, 0x1903, 0x1815, 0x1C07, 0x142B, 0x132D, 0x1B1D, 0xFFFF }, { 0x1801, 0x1533, 0x1625,
+ 0x1237, 0x1A1B, 0xFFFF }, { 0x0C16, 0x053C, 0x0E1E, 0x1721, 0x1529, 0x013F, 0xFFFF }, { 0x0B22, 0x0D04, 0x1039, 0x1D1F, 0xFFFF }, { 0x1813, 0x1B05, 0x1917, 0xFFFF }, { 0x0A2A, 0x1723, 0x1435, 0x1627, 0xFFFF }, { 0x0B30, 0x0C14, 0x0936,
+ 0x0F08, 0x1F09, 0x0E3B, 0x122F, 0xFFFF }, { 0x0C00, 0x0B24, 0x0E06, 0x0838, 0x1811, 0x1A19, 0x0B3D, 0xFFFF }, { 0x0D1A, 0x1731, 0x1A03, 0x1915, 0x1D07, 0x152B, 0xFFFF }, { 0x1901, 0x1633, 0x1725, 0x1337, 0x1B1B, 0x142D, 0x1C1D, 0xFFFF }, {
+ 0x0C12, 0x0A34, 0x073A, 0x1629, 0x023F, 0xFFFF }, { 0x0B26, 0x013E, 0x092E, 0x1821, 0x1139, 0x1E1F, 0xFFFF }, { 0x1913, 0x1A17, 0xFFFF }, { 0x0C10, 0x1535, 0x1C05, 0x1727, 0xFFFF }, { 0x0D18, 0x0A2C, 0x1823, 0x0F3B, 0x132F, 0xFFFF }, {
+ 0x0B32, 0x0D02, 0x1911, 0x1B19, 0xFFFF }, { 0x0B28, 0x0E1C, 0x1B03, 0x1A15, 0x1E07, 0x162B, 0x0C3D, 0xFFFF }, { 0x0C20, 0x1831, 0x1A01, 0x1733, 0x152D, 0x1D1D, 0xFFFF }, { 0x1825, 0x1437, 0x1729, 0x1C1B, 0x033F, 0xFFFF }, { 0x0D16, 0x063C,
+ 0x0F1E, 0x1921, 0x1239, 0x1F1F, 0xFFFF }, { 0x0C22, 0x0E04, 0x1A13, 0x1B17, 0xFFFF }, { 0x1635, 0x1D05, 0xFFFF }, { 0x0B2A, 0x1923, 0x1827, 0xFFFF }, { 0x0C30, 0x0D14, 0x0A36, 0x1A11, 0x103B, 0x142F, 0xFFFF }, { 0x0D00, 0x0C24, 0x0F06,
+ 0x0938, 0x1B15, 0x1F07, 0x1C19, 0x172B, 0x0D3D, 0xFFFF }, { 0x0E1A, 0x1931, 0x1B01, 0x1C03, 0x162D, 0x1E1D, 0xFFFF }, { 0x1833, 0x1925, 0x1537, 0x1D1B, 0xFFFF }, { 0x0D12, 0x0B34, 0x083A, 0x1A21, 0x1339, 0x1829, 0x043F, 0xFFFF }, { 0x0C26,
+ 0x023E, 0x0A2E, 0x1B13, 0xFFFF }, { 0x1735, 0x1E05, 0x1C17, 0xFFFF }, { 0x0D10, 0x1A23, 0x1927, 0xFFFF }, { 0x0E18, 0x0B2C, 0x1B11, 0x113B, 0x152F, 0xFFFF }, { 0x0C32, 0x0E02, 0x1D19, 0x0E3D, 0xFFFF }, { 0x0C28, 0x0F1C, 0x1A31, 0x1D03,
+ 0x1C15, 0x182B, 0x172D, 0x1F1D, 0xFFFF }, { 0x0D20, 0x1C01, 0x1933, 0x1A25, 0x1637, 0x1E1B, 0xFFFF }, { 0x1B21, 0x1929, 0x053F, 0xFFFF }, { 0x0E16, 0x073C, 0x1439, 0xFFFF }, { 0x0D22, 0x0F04, 0x1C13, 0x1F05, 0x1D17, 0xFFFF }, { 0x1B23,
+ 0x1835, 0x1A27, 0xFFFF }, { 0x0C2A, 0x123B, 0x162F, 0xFFFF }, { 0x0D30, 0x0E14, 0x0B36, 0x1C11, 0x1E19, 0x0F3D, 0xFFFF }, { 0x0E00, 0x0D24, 0x0A38, 0x1B31, 0x1E03, 0x1D15, 0x192B, 0xFFFF }, { 0x0F1A, 0x1D01, 0x1A33, 0x1B25, 0x1737, 0x1F1B,
+ 0x182D, 0xFFFF }, { 0x1A29, 0x063F, 0xFFFF }, { 0x0E12, 0x0C34, 0x093A, 0x1C21, 0x1539, 0xFFFF }, { 0x0D26, 0x033E, 0x0B2E, 0x1D13, 0x1E17, 0xFFFF }, { 0x1935, 0x1B27, 0xFFFF }, { 0x0E10, 0x1C23, 0x133B, 0x172F, 0xFFFF }, { 0x0F18,
+ 0x0C2C, 0x1D11, 0x1F19, 0xFFFF }, { 0x0D32, 0x0F02, 0x1F03, 0x1E15, 0x1A2B, 0x103D, 0xFFFF }, { 0x0D28, 0x1C31, 0x1E01, 0x1B33, 0x192D, 0xFFFF }, { 0x0E20, 0x1C25, 0x1837, 0x1B29, 0x073F, 0xFFFF }, { 0x1D21, 0x1639, 0xFFFF }, { 0x0F16,
+ 0x083C, 0x1E13, 0x1F17, 0xFFFF }, { 0x0E22, 0x1A35, 0xFFFF }, { 0x1D23, 0x1C27, 0xFFFF }, { 0x0D2A, 0x1E11, 0x143B, 0x182F, 0xFFFF }, { 0x0E30, 0x0F14, 0x0C36, 0x1F15, 0x1B2B, 0x113D, 0xFFFF }, { 0x0F00, 0x0E24, 0x0B38, 0x1D31, 0x1F01,
+ 0x1A2D, 0xFFFF }, { 0x1C33, 0x1D25, 0x1937, 0xFFFF }, { 0x1E21, 0x1739, 0x1C29, 0x083F, 0xFFFF }, { 0x0F12, 0x0D34, 0x0A3A, 0x1F13, 0xFFFF }, { 0x0E26, 0x043E, 0x0C2E, 0x1B35, 0xFFFF }, { 0x1E23, 0x1D27, 0xFFFF }, { 0x0F10, 0x1F11, 0x153B, 0x192F, 0xFFFF }, { 0x0D2C, 0x123D, 0xFFFF },
+ };
+
+ static uint32_t etc1_decode_value(uint32_t diff, uint32_t inten, uint32_t selector, uint32_t packed_c)
+ {
+ const uint32_t limit = diff ? 32 : 16;
+ BASISU_NOTE_UNUSED(limit);
+ assert((diff < 2) && (inten < 8) && (selector < 4) && (packed_c < limit));
+ int c;
+ if (diff)
+ c = (packed_c >> 2) | (packed_c << 3);
+ else
+ c = packed_c | (packed_c << 4);
+ c += g_etc1_inten_tables[inten][selector];
+ c = clamp(c, 0, 255);
+ return c;
+ }
+
+ void pack_etc1_solid_color_init()
+ {
+ for (uint32_t diff = 0; diff < 2; diff++)
+ {
+ const uint32_t limit = diff ? 32 : 16;
+
+ for (uint32_t inten = 0; inten < 8; inten++)
+ {
+ for (uint32_t selector = 0; selector < 4; selector++)
+ {
+ const uint32_t inverse_table_index = diff + (inten << 1) + (selector << 4);
+ for (uint32_t color = 0; color < 256; color++)
+ {
+ uint32_t best_error = UINT32_MAX, best_packed_c = 0;
+ for (uint32_t packed_c = 0; packed_c < limit; packed_c++)
+ {
+ int v = etc1_decode_value(diff, inten, selector, packed_c);
+ uint32_t err = labs(v - static_cast(color));
+ if (err < best_error)
+ {
+ best_error = err;
+ best_packed_c = packed_c;
+ if (!best_error)
+ break;
+ }
+ }
+ assert(best_error <= 255);
+ g_etc1_inverse_lookup[inverse_table_index][color] = static_cast(best_packed_c | (best_error << 8));
+ }
+ }
+ }
+ }
+ }
+
+ // Packs solid color blocks efficiently using a set of small precomputed tables.
+ // For random 888 inputs, MSE results are better than Erricson's ETC1 packer in "slow" mode ~9.5% of the time, is slightly worse only ~.01% of the time, and is equal the rest of the time.
+ uint64_t pack_etc1_block_solid_color(etc_block& block, const uint8_t* pColor)
+ {
+ assert(g_etc1_inverse_lookup[0][255]);
+
+ static uint32_t s_next_comp[4] = { 1, 2, 0, 1 };
+
+ uint32_t best_error = UINT32_MAX, best_i = 0;
+ int best_x = 0, best_packed_c1 = 0, best_packed_c2 = 0;
+
+ // For each possible 8-bit value, there is a precomputed list of diff/inten/selector configurations that allow that 8-bit value to be encoded with no error.
+ for (uint32_t i = 0; i < 3; i++)
+ {
+ const uint32_t c1 = pColor[s_next_comp[i]], c2 = pColor[s_next_comp[i + 1]];
+
+ const int delta_range = 1;
+ for (int delta = -delta_range; delta <= delta_range; delta++)
+ {
+ const int c_plus_delta = clamp(pColor[i] + delta, 0, 255);
+
+ const uint16_t* pTable;
+ if (!c_plus_delta)
+ pTable = g_etc1_color8_to_etc_block_config_0_255[0];
+ else if (c_plus_delta == 255)
+ pTable = g_etc1_color8_to_etc_block_config_0_255[1];
+ else
+ pTable = g_etc1_color8_to_etc_block_config_1_to_254[c_plus_delta - 1];
+
+ do
+ {
+ const uint32_t x = *pTable++;
+
+#ifdef _DEBUG
+ const uint32_t diff = x & 1;
+ const uint32_t inten = (x >> 1) & 7;
+ const uint32_t selector = (x >> 4) & 3;
+ const uint32_t p0 = (x >> 8) & 255;
+ assert(etc1_decode_value(diff, inten, selector, p0) == (uint32_t)c_plus_delta);
+#endif
+
+ const uint16_t* pInverse_table = g_etc1_inverse_lookup[x & 0xFF];
+ uint16_t p1 = pInverse_table[c1];
+ uint16_t p2 = pInverse_table[c2];
+ const uint32_t trial_error = square(c_plus_delta - pColor[i]) + square(p1 >> 8) + square(p2 >> 8);
+ if (trial_error < best_error)
+ {
+ best_error = trial_error;
+ best_x = x;
+ best_packed_c1 = p1 & 0xFF;
+ best_packed_c2 = p2 & 0xFF;
+ best_i = i;
+ if (!best_error)
+ goto found_perfect_match;
+ }
+ } while (*pTable != 0xFFFF);
+ }
+ }
+ found_perfect_match:
+
+ const uint32_t diff = best_x & 1;
+ const uint32_t inten = (best_x >> 1) & 7;
+
+ block.m_bytes[3] = static_cast(((inten | (inten << 3)) << 2) | (diff << 1));
+
+ const uint32_t etc1_selector = g_selector_index_to_etc1[(best_x >> 4) & 3];
+ *reinterpret_cast(&block.m_bytes[4]) = (etc1_selector & 2) ? 0xFFFF : 0;
+ *reinterpret_cast(&block.m_bytes[6]) = (etc1_selector & 1) ? 0xFFFF : 0;
+
+ const uint32_t best_packed_c0 = (best_x >> 8) & 255;
+ if (diff)
+ {
+ block.m_bytes[best_i] = static_cast(best_packed_c0 << 3);
+ block.m_bytes[s_next_comp[best_i]] = static_cast(best_packed_c1 << 3);
+ block.m_bytes[s_next_comp[best_i + 1]] = static_cast(best_packed_c2 << 3);
+ }
+ else
+ {
+ block.m_bytes[best_i] = static_cast(best_packed_c0 | (best_packed_c0 << 4));
+ block.m_bytes[s_next_comp[best_i]] = static_cast(best_packed_c1 | (best_packed_c1 << 4));
+ block.m_bytes[s_next_comp[best_i + 1]] = static_cast(best_packed_c2 | (best_packed_c2 << 4));
+ }
+
+ return best_error;
+ }
+
const uint32_t BASISU_ETC1_CLUSTER_FIT_ORDER_TABLE_SIZE = 165;
static const struct { uint8_t m_v[4]; } g_cluster_fit_order_tab[BASISU_ETC1_CLUSTER_FIT_ORDER_TABLE_SIZE] =
@@ -657,7 +879,7 @@ namespace basisu
if (m_best_solution.m_error == 0)
return;
- const uint32_t n = m_pParams->m_num_src_pixels;
+ //const uint32_t n = m_pParams->m_num_src_pixels;
const int scan_delta_size = m_pParams->m_scan_delta_size;
// Scan through a subset of the 3D lattice centered around the avg block color trying each 3D (555 or 444) lattice point as a potential block color.
@@ -982,7 +1204,7 @@ namespace basisu
block_inten[s] = block_color.r + block_color.g + block_color.b;
}
- // evaluate_solution_fast() enforces/assumesd a total ordering of the input colors along the intensity (1,1,1) axis to more quickly classify the inputs to selectors.
+ // evaluate_solution_fast() enforces/assumes a total ordering of the input colors along the intensity (1,1,1) axis to more quickly classify the inputs to selectors.
// The inputs colors have been presorted along the projection onto this axis, and ETC1 block colors are always ordered along the intensity axis, so this classification is fast.
// 0 1 2 3
// 01 12 23
@@ -1071,4 +1293,110 @@ namespace basisu
return success;
}
+ uint64_t pack_eac_a8(pack_eac_a8_results& results, const uint8_t* pPixels, uint32_t num_pixels, uint32_t base_search_rad, uint32_t mul_search_rad, uint32_t table_mask)
+ {
+ results.m_selectors.resize(num_pixels);
+ results.m_selectors_temp.resize(num_pixels);
+
+ uint32_t min_alpha = 255, max_alpha = 0;
+ for (uint32_t i = 0; i < num_pixels; i++)
+ {
+ const uint32_t a = pPixels[i];
+ if (a < min_alpha) min_alpha = a;
+ if (a > max_alpha) max_alpha = a;
+ }
+
+ if (min_alpha == max_alpha)
+ {
+ results.m_base = min_alpha;
+ results.m_table = 13;
+ results.m_multiplier = 1;
+ for (uint32_t i = 0; i < num_pixels; i++)
+ results.m_selectors[i] = 4;
+ return 0;
+ }
+
+ const uint32_t alpha_range = max_alpha - min_alpha;
+
+ uint64_t best_err = UINT64_MAX;
+
+ for (uint32_t table = 0; table < 16; table++)
+ {
+ if ((table_mask & (1U << table)) == 0)
+ continue;
+
+ const float range = (float)(g_etc2_eac_tables[table][ETC2_EAC_MAX_VALUE_SELECTOR] - g_etc2_eac_tables[table][ETC2_EAC_MIN_VALUE_SELECTOR]);
+ const int center = (int)roundf(lerp((float)min_alpha, (float)max_alpha, (float)(0 - g_etc2_eac_tables[table][ETC2_EAC_MIN_VALUE_SELECTOR]) / range));
+
+ const int base_min = clamp255(center - base_search_rad);
+ const int base_max = clamp255(center + base_search_rad);
+
+ const int mul = (int)roundf(alpha_range / range);
+ const int mul_low = clamp(mul - mul_search_rad, 1, 15);
+ const int mul_high = clamp(mul + mul_search_rad, 1, 15);
+
+ for (int base = base_min; base <= base_max; base++)
+ {
+ for (int multiplier = mul_low; multiplier <= mul_high; multiplier++)
+ {
+ uint64_t total_err = 0;
+
+ for (uint32_t i = 0; i < num_pixels; i++)
+ {
+ const int a = pPixels[i];
+
+ uint32_t best_s_err = UINT32_MAX;
+ uint32_t best_s = 0;
+ for (uint32_t s = 0; s < 8; s++)
+ {
+ const int v = clamp255((int)multiplier * g_etc2_eac_tables[table][s] + (int)base);
+
+ uint32_t err = iabs(a - v);
+ if (err < best_s_err)
+ {
+ best_s_err = err;
+ best_s = s;
+ }
+ }
+
+ results.m_selectors_temp[i] = static_cast(best_s);
+
+ total_err += best_s_err * best_s_err;
+ if (total_err >= best_err)
+ break;
+ }
+
+ if (total_err < best_err)
+ {
+ best_err = total_err;
+ results.m_base = base;
+ results.m_multiplier = multiplier;
+ results.m_table = table;
+ results.m_selectors.swap(results.m_selectors_temp);
+ if (!best_err)
+ return best_err;
+ }
+
+ } // table
+
+ } // multiplier
+
+ } // base
+
+ return best_err;
+ }
+
+ void pack_eac_a8(eac_a8_block* pBlock, const uint8_t* pPixels, uint32_t base_search_rad, uint32_t mul_search_rad, uint32_t table_mask)
+ {
+ pack_eac_a8_results results;
+ pack_eac_a8(results, pPixels, 16, base_search_rad, mul_search_rad, table_mask);
+
+ pBlock->m_base = results.m_base;
+ pBlock->m_multiplier = results.m_multiplier;
+ pBlock->m_table = results.m_table;
+ for (uint32_t y = 0; y < 4; y++)
+ for (uint32_t x = 0; x < 4; x++)
+ pBlock->set_selector(x, y, results.m_selectors[x + y * 4]);
+ }
+
} // namespace basisu
diff --git a/basisu_etc.h b/basisu_etc.h
index a202d01..c1f3101 100644
--- a/basisu_etc.h
+++ b/basisu_etc.h
@@ -1,5 +1,5 @@
// basis_etc.h
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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.
@@ -266,6 +266,27 @@ namespace basisu
p[-2] |= (msb << byte_bit_ofs);
}
+ // Selector "etc1_val" ranges from 0-3 and is a direct (raw) ETC1 selector.
+ inline void set_raw_selector(uint32_t x, uint32_t y, uint32_t etc1_val)
+ {
+ assert((x | y | etc1_val) < 4);
+ const uint32_t bit_index = x * 4 + y;
+
+ uint8_t* p = &m_bytes[7 - (bit_index >> 3)];
+
+ const uint32_t byte_bit_ofs = bit_index & 7;
+ const uint32_t mask = 1 << byte_bit_ofs;
+
+ const uint32_t lsb = etc1_val & 1;
+ const uint32_t msb = etc1_val >> 1;
+
+ p[0] &= ~mask;
+ p[0] |= (lsb << byte_bit_ofs);
+
+ p[-2] &= ~mask;
+ p[-2] |= (msb << byte_bit_ofs);
+ }
+
inline uint32_t get_raw_selector_bits() const
{
return m_bytes[4] | (m_bytes[5] << 8) | (m_bytes[6] << 16) | (m_bytes[7] << 24);
@@ -622,6 +643,23 @@ namespace basisu
return true;
}
+ bool set_block_color5_clamp(const color_rgba &c0_unscaled, const color_rgba &c1_unscaled)
+ {
+ set_diff_bit(true);
+ set_base5_color(pack_color5(c0_unscaled, false));
+
+ int dr = c1_unscaled.r - c0_unscaled.r;
+ int dg = c1_unscaled.g - c0_unscaled.g;
+ int db = c1_unscaled.b - c0_unscaled.b;
+
+ dr = clamp(dr, cETC1ColorDeltaMin, cETC1ColorDeltaMax);
+ dg = clamp(dg, cETC1ColorDeltaMin, cETC1ColorDeltaMax);
+ db = clamp(db, cETC1ColorDeltaMin, cETC1ColorDeltaMax);
+
+ set_delta3_color(pack_delta3(dr, dg, db));
+
+ return true;
+ }
color_rgba get_selector_color(uint32_t x, uint32_t y, uint32_t s) const
{
color_rgba block_colors[4];
@@ -1042,5 +1080,77 @@ namespace basisu
{
etc1_optimizer m_optimizer;
};
+
+ void pack_etc1_solid_color_init();
+ uint64_t pack_etc1_block_solid_color(etc_block& block, const uint8_t* pColor);
+ // ETC EAC
+ extern const int8_t g_etc2_eac_tables[16][8];
+ extern const int8_t g_etc2_eac_tables8[16][8];
+
+ const uint32_t ETC2_EAC_MIN_VALUE_SELECTOR = 3, ETC2_EAC_MAX_VALUE_SELECTOR = 7;
+
+ struct eac_a8_block
+ {
+ uint16_t m_base : 8;
+ uint16_t m_table : 4;
+ uint16_t m_multiplier : 4;
+
+ uint8_t m_selectors[6];
+
+ inline uint32_t get_selector(uint32_t x, uint32_t y, uint64_t selector_bits) const
+ {
+ assert((x < 4) && (y < 4));
+ return static_cast((selector_bits >> (45 - (y + x * 4) * 3)) & 7);
+ }
+
+ inline uint64_t get_selector_bits() const
+ {
+ uint64_t pixels = ((uint64_t)m_selectors[0] << 40) | ((uint64_t)m_selectors[1] << 32) | ((uint64_t)m_selectors[2] << 24) | ((uint64_t)m_selectors[3] << 16) | ((uint64_t)m_selectors[4] << 8) | m_selectors[5];
+ return pixels;
+ }
+
+ inline void set_selector_bits(uint64_t pixels)
+ {
+ m_selectors[0] = (uint8_t)(pixels >> 40);
+ m_selectors[1] = (uint8_t)(pixels >> 32);
+ m_selectors[2] = (uint8_t)(pixels >> 24);
+ m_selectors[3] = (uint8_t)(pixels >> 16);
+ m_selectors[4] = (uint8_t)(pixels >> 8);
+ m_selectors[5] = (uint8_t)(pixels);
+ }
+
+ void set_selector(uint32_t x, uint32_t y, uint32_t s)
+ {
+ assert((x < 4) && (y < 4) && (s < 8));
+
+ const uint32_t ofs = 45 - (y + x * 4) * 3;
+
+ uint64_t pixels = get_selector_bits();
+
+ pixels &= ~(7ULL << ofs);
+ pixels |= (static_cast(s) << ofs);
+
+ set_selector_bits(pixels);
+ }
+ };
+
+ struct etc2_rgba_block
+ {
+ eac_a8_block m_alpha;
+ etc_block m_rgb;
+ };
+
+ struct pack_eac_a8_results
+ {
+ uint32_t m_base;
+ uint32_t m_table;
+ uint32_t m_multiplier;
+ uint8_vec m_selectors;
+ uint8_vec m_selectors_temp;
+ };
+
+ uint64_t pack_eac_a8(pack_eac_a8_results& results, const uint8_t* pPixels, uint32_t num_pixels, uint32_t base_search_rad, uint32_t mul_search_rad, uint32_t table_mask = UINT32_MAX);
+ void pack_eac_a8(eac_a8_block* pBlock, const uint8_t* pPixels, uint32_t base_search_rad, uint32_t mul_search_rad, uint32_t table_mask = UINT32_MAX);
+
} // namespace basisu
diff --git a/basisu_frontend.cpp b/basisu_frontend.cpp
index 6f7a9bf..b09aa61 100644
--- a/basisu_frontend.cpp
+++ b/basisu_frontend.cpp
@@ -1,5 +1,5 @@
// basisu_frontend.cpp
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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.
@@ -29,7 +29,7 @@ namespace basisu
const uint32_t cMaxCodebookCreationThreads = 8;
const uint32_t BASISU_MAX_ENDPOINT_REFINEMENT_STEPS = 3;
- const uint32_t BASISU_MAX_SELECTOR_REFINEMENT_STEPS = 3;
+ //const uint32_t BASISU_MAX_SELECTOR_REFINEMENT_STEPS = 3;
const uint32_t BASISU_ENDPOINT_PARENT_CODEBOOK_SIZE = 16;
const uint32_t BASISU_SELECTOR_PARENT_CODEBOOK_SIZE = 16;
@@ -837,7 +837,7 @@ namespace basisu
continue;
#endif
- const uint32_t new_endpoint_cluster_index = (uint32_t)m_endpoint_clusters.size();
+ //const uint32_t new_endpoint_cluster_index = (uint32_t)m_endpoint_clusters.size();
enlarge_vector(m_endpoint_clusters, 1)->push_back(training_vector_index);
enlarge_vector(m_endpoint_cluster_etc_params, 1);
@@ -1085,7 +1085,7 @@ namespace basisu
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
- const bool is_flipped = true;
+ //const bool is_flipped = true;
const uint32_t cluster_index = block_clusters[block_index][0];
BASISU_FRONTEND_VERIFY(cluster_index == block_clusters[block_index][1]);
@@ -1938,7 +1938,7 @@ namespace basisu
const uint32_t block_index = training_vector_index >> 1;
const uint32_t subblock_index = training_vector_index & 1;
- const bool is_flipped = true;
+ //const bool is_flipped = true;
etc_block &blk = m_encoded_blocks[block_index];
diff --git a/basisu_frontend.h b/basisu_frontend.h
index c3f5d23..e4ccf14 100644
--- a/basisu_frontend.h
+++ b/basisu_frontend.h
@@ -1,5 +1,5 @@
// basisu_frontend.h
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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.
@@ -72,16 +72,18 @@ namespace basisu
m_perceptual(true),
m_debug_stats(false),
m_debug_images(false),
+
m_dump_endpoint_clusterization(true),
+ m_validate(false),
+ m_multithreaded(false),
+ m_disable_hierarchical_endpoint_codebooks(false),
m_pGlobal_sel_codebook(NULL),
m_num_global_sel_codebook_pal_bits(0),
m_num_global_sel_codebook_mod_bits(0),
m_use_hybrid_selector_codebooks(false),
m_hybrid_codebook_quality_thresh(0.0f),
- m_validate(false),
m_tex_type(basist::cBASISTexType2D),
- m_multithreaded(false),
- m_disable_hierarchical_endpoint_codebooks(false),
+
m_pJob_pool(nullptr)
{
}
diff --git a/basisu_gpu_texture.cpp b/basisu_gpu_texture.cpp
index 117668c..8c92516 100644
--- a/basisu_gpu_texture.cpp
+++ b/basisu_gpu_texture.cpp
@@ -1,5 +1,5 @@
// basisu_gpu_texture.cpp
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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.
@@ -16,38 +16,10 @@
#include "basisu_enc.h"
#include "basisu_pvrtc1_4.h"
#include "basisu_astc_decomp.h"
+#include "basisu_bc7enc.h"
namespace basisu
{
- const int8_t g_etc2_eac_tables[16][8] =
- {
- { -3, -6, -9, -15, 2, 5, 8, 14 }, { -3, -7, -10, -13, 2, 6, 9, 12 }, { -2, -5, -8, -13, 1, 4, 7, 12 }, { -2, -4, -6, -13, 1, 3, 5, 12 },
- { -3, -6, -8, -12, 2, 5, 7, 11 }, { -3, -7, -9, -11, 2, 6, 8, 10 }, { -4, -7, -8, -11, 3, 6, 7, 10 }, { -3, -5, -8, -11, 2, 4, 7, 10 },
- { -2, -6, -8, -10, 1, 5, 7, 9 }, { -2, -5, -8, -10, 1, 4, 7, 9 }, { -2, -4, -8, -10, 1, 3, 7, 9 }, { -2, -5, -7, -10, 1, 4, 6, 9 },
- { -3, -4, -7, -10, 2, 3, 6, 9 }, { -1, -2, -3, -10, 0, 1, 2, 9 }, { -4, -6, -8, -9, 3, 5, 7, 8 }, { -3, -5, -7, -9, 2, 4, 6, 8 }
- };
-
- struct eac_a8_block
- {
- uint16_t m_base : 8;
- uint16_t m_table : 4;
- uint16_t m_multiplier : 4;
-
- uint8_t m_selectors[6];
-
- inline uint32_t get_selector(uint32_t x, uint32_t y, uint64_t selector_bits) const
- {
- assert((x < 4) && (y < 4));
- return static_cast((selector_bits >> (45 - (y + x * 4) * 3)) & 7);
- }
-
- inline uint64_t get_selector_bits() const
- {
- uint64_t pixels = ((uint64_t)m_selectors[0] << 40) | ((uint64_t)m_selectors[1] << 32) | ((uint64_t)m_selectors[2] << 24) | ((uint64_t)m_selectors[3] << 16) | ((uint64_t)m_selectors[4] << 8) | m_selectors[5];
- return pixels;
- }
- };
-
void unpack_etc2_eac(const void *pBlock_bits, color_rgba *pPixels)
{
static_assert(sizeof(eac_a8_block) == 8, "sizeof(eac_a8_block) == 8");
@@ -317,6 +289,191 @@ 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.
+
+ static inline uint32_t bc7_dequant(uint32_t val, uint32_t pbit, uint32_t val_bits) { assert(val < (1U << val_bits)); assert(pbit < 2); assert(val_bits >= 4 && val_bits <= 8); const uint32_t total_bits = val_bits + 1; val = (val << 1) | pbit; val <<= (8 - total_bits); val |= (val >> total_bits); assert(val <= 255); return val; }
+ static inline uint32_t bc7_dequant(uint32_t val, uint32_t val_bits) { assert(val < (1U << val_bits)); assert(val_bits >= 4 && val_bits <= 8); val <<= (8 - val_bits); val |= (val >> val_bits); assert(val <= 255); return val; }
+
+ static inline uint32_t bc7_interp2(uint32_t l, uint32_t h, uint32_t w) { assert(w < 4); return (l * (64 - basist::g_bc7_weights2[w]) + h * basist::g_bc7_weights2[w] + 32) >> 6; }
+ static inline uint32_t bc7_interp3(uint32_t l, uint32_t h, uint32_t w) { assert(w < 8); return (l * (64 - basist::g_bc7_weights3[w]) + h * basist::g_bc7_weights3[w] + 32) >> 6; }
+ static inline uint32_t bc7_interp4(uint32_t l, uint32_t h, uint32_t w) { assert(w < 16); return (l * (64 - basist::g_bc7_weights4[w]) + h * basist::g_bc7_weights4[w] + 32) >> 6; }
+ static inline uint32_t bc7_interp(uint32_t l, uint32_t h, uint32_t w, uint32_t bits)
+ {
+ assert(l <= 255 && h <= 255);
+ switch (bits)
+ {
+ case 2: return bc7_interp2(l, h, w);
+ case 3: return bc7_interp3(l, h, w);
+ case 4: return bc7_interp4(l, h, w);
+ default:
+ break;
+ }
+ return 0;
+ }
+
+ bool unpack_bc7_mode0_2(uint32_t mode, const void* pBlock_bits, color_rgba* pPixels)
+ {
+ //const uint32_t SUBSETS = 3;
+ const uint32_t ENDPOINTS = 6;
+ const uint32_t COMPS = 3;
+ const uint32_t WEIGHT_BITS = (mode == 0) ? 3 : 2;
+ const uint32_t ENDPOINT_BITS = (mode == 0) ? 4 : 5;
+ const uint32_t PBITS = (mode == 0) ? 6 : 0;
+ const uint32_t WEIGHT_VALS = 1 << WEIGHT_BITS;
+
+ uint32_t bit_offset = 0;
+ const uint8_t* pBuf = static_cast(pBlock_bits);
+
+ if (read_bits32(pBuf, bit_offset, mode + 1) != (1U << mode)) return false;
+
+ const uint32_t part = read_bits32(pBuf, bit_offset, (mode == 0) ? 4 : 6);
+
+ color_rgba endpoints[ENDPOINTS];
+ for (uint32_t c = 0; c < COMPS; c++)
+ for (uint32_t e = 0; e < ENDPOINTS; e++)
+ endpoints[e][c] = (uint8_t)read_bits32(pBuf, bit_offset, ENDPOINT_BITS);
+
+ uint32_t pbits[6];
+ for (uint32_t p = 0; p < PBITS; p++)
+ pbits[p] = read_bits32(pBuf, bit_offset, 1);
+
+ uint32_t weights[16];
+ for (uint32_t i = 0; i < 16; i++)
+ weights[i] = read_bits32(pBuf, bit_offset, ((!i) || (i == basist::g_bc7_table_anchor_index_third_subset_1[part]) || (i == basist::g_bc7_table_anchor_index_third_subset_2[part])) ? (WEIGHT_BITS - 1) : WEIGHT_BITS);
+
+ assert(bit_offset == 128);
+
+ for (uint32_t e = 0; e < ENDPOINTS; e++)
+ for (uint32_t c = 0; c < 4; c++)
+ endpoints[e][c] = (uint8_t)((c == 3) ? 255 : (PBITS ? bc7_dequant(endpoints[e][c], pbits[e], ENDPOINT_BITS) : bc7_dequant(endpoints[e][c], ENDPOINT_BITS)));
+
+ color_rgba block_colors[3][8];
+ for (uint32_t s = 0; s < 3; s++)
+ for (uint32_t i = 0; i < WEIGHT_VALS; i++)
+ {
+ for (uint32_t c = 0; c < 3; c++)
+ block_colors[s][i][c] = (uint8_t)bc7_interp(endpoints[s * 2 + 0][c], endpoints[s * 2 + 1][c], i, WEIGHT_BITS);
+ block_colors[s][i][3] = 255;
+ }
+
+ for (uint32_t i = 0; i < 16; i++)
+ pPixels[i] = block_colors[basist::g_bc7_partition3[part * 16 + i]][weights[i]];
+
+ return true;
+ }
+
+ bool unpack_bc7_mode1_3_7(uint32_t mode, const void* pBlock_bits, color_rgba* pPixels)
+ {
+ //const uint32_t SUBSETS = 2;
+ const uint32_t ENDPOINTS = 4;
+ const uint32_t COMPS = (mode == 7) ? 4 : 3;
+ const uint32_t WEIGHT_BITS = (mode == 1) ? 3 : 2;
+ const uint32_t ENDPOINT_BITS = (mode == 7) ? 5 : ((mode == 1) ? 6 : 7);
+ const uint32_t PBITS = (mode == 1) ? 2 : 4;
+ const uint32_t SHARED_PBITS = (mode == 1) ? true : false;
+ const uint32_t WEIGHT_VALS = 1 << WEIGHT_BITS;
+
+ uint32_t bit_offset = 0;
+ const uint8_t* pBuf = static_cast(pBlock_bits);
+
+ if (read_bits32(pBuf, bit_offset, mode + 1) != (1U << mode)) return false;
+
+ const uint32_t part = read_bits32(pBuf, bit_offset, 6);
+
+ color_rgba endpoints[ENDPOINTS];
+ for (uint32_t c = 0; c < COMPS; c++)
+ for (uint32_t e = 0; e < ENDPOINTS; e++)
+ endpoints[e][c] = (uint8_t)read_bits32(pBuf, bit_offset, ENDPOINT_BITS);
+
+ uint32_t pbits[4];
+ for (uint32_t p = 0; p < PBITS; p++)
+ pbits[p] = read_bits32(pBuf, bit_offset, 1);
+
+ uint32_t weights[16];
+ for (uint32_t i = 0; i < 16; i++)
+ weights[i] = read_bits32(pBuf, bit_offset, ((!i) || (i == basist::g_bc7_table_anchor_index_second_subset[part])) ? (WEIGHT_BITS - 1) : WEIGHT_BITS);
+
+ assert(bit_offset == 128);
+
+ for (uint32_t e = 0; e < ENDPOINTS; e++)
+ for (uint32_t c = 0; c < 4; c++)
+ endpoints[e][c] = (uint8_t)((c == ((mode == 7U) ? 4U : 3U)) ? 255 : bc7_dequant(endpoints[e][c], pbits[SHARED_PBITS ? (e >> 1) : e], ENDPOINT_BITS));
+
+ color_rgba block_colors[2][8];
+ for (uint32_t s = 0; s < 2; s++)
+ for (uint32_t i = 0; i < WEIGHT_VALS; i++)
+ {
+ for (uint32_t c = 0; c < COMPS; c++)
+ block_colors[s][i][c] = (uint8_t)bc7_interp(endpoints[s * 2 + 0][c], endpoints[s * 2 + 1][c], i, WEIGHT_BITS);
+ block_colors[s][i][3] = (COMPS == 3) ? 255 : block_colors[s][i][3];
+ }
+
+ for (uint32_t i = 0; i < 16; i++)
+ pPixels[i] = block_colors[basist::g_bc7_partition2[part * 16 + i]][weights[i]];
+
+ return true;
+ }
+
+ bool unpack_bc7_mode4_5(uint32_t mode, const void* pBlock_bits, color_rgba* pPixels)
+ {
+ const uint32_t ENDPOINTS = 2;
+ const uint32_t COMPS = 4;
+ const uint32_t WEIGHT_BITS = 2;
+ const uint32_t A_WEIGHT_BITS = (mode == 4) ? 3 : 2;
+ const uint32_t ENDPOINT_BITS = (mode == 4) ? 5 : 7;
+ const uint32_t A_ENDPOINT_BITS = (mode == 4) ? 6 : 8;
+ //const uint32_t WEIGHT_VALS = 1 << WEIGHT_BITS;
+ //const uint32_t A_WEIGHT_VALS = 1 << A_WEIGHT_BITS;
+
+ uint32_t bit_offset = 0;
+ const uint8_t* pBuf = static_cast(pBlock_bits);
+
+ if (read_bits32(pBuf, bit_offset, mode + 1) != (1U << mode)) return false;
+
+ const uint32_t comp_rot = read_bits32(pBuf, bit_offset, 2);
+ const uint32_t index_mode = (mode == 4) ? read_bits32(pBuf, bit_offset, 1) : 0;
+
+ color_rgba endpoints[ENDPOINTS];
+ for (uint32_t c = 0; c < COMPS; c++)
+ for (uint32_t e = 0; e < ENDPOINTS; e++)
+ endpoints[e][c] = (uint8_t)read_bits32(pBuf, bit_offset, (c == 3) ? A_ENDPOINT_BITS : ENDPOINT_BITS);
+
+ const uint32_t weight_bits[2] = { index_mode ? A_WEIGHT_BITS : WEIGHT_BITS, index_mode ? WEIGHT_BITS : A_WEIGHT_BITS };
+
+ uint32_t weights[16], a_weights[16];
+
+ for (uint32_t i = 0; i < 16; i++)
+ (index_mode ? a_weights : weights)[i] = read_bits32(pBuf, bit_offset, weight_bits[index_mode] - ((!i) ? 1 : 0));
+
+ for (uint32_t i = 0; i < 16; i++)
+ (index_mode ? weights : a_weights)[i] = read_bits32(pBuf, bit_offset, weight_bits[1 - index_mode] - ((!i) ? 1 : 0));
+
+ assert(bit_offset == 128);
+
+ for (uint32_t e = 0; e < ENDPOINTS; e++)
+ for (uint32_t c = 0; c < 4; c++)
+ endpoints[e][c] = (uint8_t)bc7_dequant(endpoints[e][c], (c == 3) ? A_ENDPOINT_BITS : ENDPOINT_BITS);
+
+ color_rgba block_colors[8];
+ for (uint32_t i = 0; i < (1U << weight_bits[0]); i++)
+ for (uint32_t c = 0; c < 3; c++)
+ block_colors[i][c] = (uint8_t)bc7_interp(endpoints[0][c], endpoints[1][c], i, weight_bits[0]);
+
+ for (uint32_t i = 0; i < (1U << weight_bits[1]); i++)
+ block_colors[i][3] = (uint8_t)bc7_interp(endpoints[0][3], endpoints[1][3], i, weight_bits[1]);
+
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ pPixels[i] = block_colors[weights[i]];
+ pPixels[i].a = block_colors[a_weights[i]].a;
+ if (comp_rot >= 1)
+ std::swap(pPixels[i].a, pPixels[i].m_comps[comp_rot - 1]);
+ }
+
+ return true;
+ }
+
struct bc7_mode_6
{
struct
@@ -364,9 +521,6 @@ namespace basisu
};
};
- static const uint32_t g_bc7_weights4[16] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
-
- // The transcoder only outputs mode 6 at the moment, so this is easy.
bool unpack_bc7_mode6(const void *pBlock_bits, color_rgba *pPixels)
{
static_assert(sizeof(bc7_mode_6) == 16, "sizeof(bc7_mode_6) == 16");
@@ -388,7 +542,7 @@ namespace basisu
color_rgba vals[16];
for (uint32_t i = 0; i < 16; i++)
{
- const uint32_t w = g_bc7_weights4[i];
+ const uint32_t w = basist::g_bc7_weights4[i];
const uint32_t iw = 64 - w;
vals[i].set_noclamp_rgba(
(r0 * iw + r1 * w + 32) >> 6,
@@ -420,183 +574,37 @@ namespace basisu
return true;
}
- static inline uint32_t get_block_bits(const uint8_t* pBytes, uint32_t bit_ofs, uint32_t bits_wanted)
+ bool unpack_bc7(const void *pBlock, color_rgba *pPixels)
{
- assert(bits_wanted < 32);
+ const uint32_t first_byte = static_cast(pBlock)[0];
- uint32_t v = 0;
- uint32_t total_bits = 0;
-
- while (total_bits < bits_wanted)
+ for (uint32_t mode = 0; mode <= 7; mode++)
{
- uint32_t k = pBytes[bit_ofs >> 3];
- k >>= (bit_ofs & 7);
- uint32_t num_bits_in_byte = 8 - (bit_ofs & 7);
-
- v |= (k << total_bits);
- total_bits += num_bits_in_byte;
- bit_ofs += num_bits_in_byte;
- }
-
- return v & ((1 << bits_wanted) - 1);
- }
-
- struct bc7_mode_5
- {
- union
- {
- struct
+ if (first_byte & (1U << mode))
{
- uint64_t m_mode : 6;
- uint64_t m_rot : 2;
-
- uint64_t m_r0 : 7;
- uint64_t m_r1 : 7;
- uint64_t m_g0 : 7;
- uint64_t m_g1 : 7;
- uint64_t m_b0 : 7;
- uint64_t m_b1 : 7;
- uint64_t m_a0 : 8;
- uint64_t m_a1_0 : 6;
-
- } m_lo;
-
- uint64_t m_lo_bits;
- };
-
- union
- {
- struct
- {
- uint64_t m_a1_1 : 2;
-
- // bit 2
- uint64_t m_c00 : 1;
- uint64_t m_c10 : 2;
- uint64_t m_c20 : 2;
- uint64_t m_c30 : 2;
-
- uint64_t m_c01 : 2;
- uint64_t m_c11 : 2;
- uint64_t m_c21 : 2;
- uint64_t m_c31 : 2;
-
- uint64_t m_c02 : 2;
- uint64_t m_c12 : 2;
- uint64_t m_c22 : 2;
- uint64_t m_c32 : 2;
-
- uint64_t m_c03 : 2;
- uint64_t m_c13 : 2;
- uint64_t m_c23 : 2;
- uint64_t m_c33 : 2;
-
- // bit 33
- uint64_t m_a00 : 1;
- uint64_t m_a10 : 2;
- uint64_t m_a20 : 2;
- uint64_t m_a30 : 2;
-
- uint64_t m_a01 : 2;
- uint64_t m_a11 : 2;
- uint64_t m_a21 : 2;
- uint64_t m_a31 : 2;
-
- uint64_t m_a02 : 2;
- uint64_t m_a12 : 2;
- uint64_t m_a22 : 2;
- uint64_t m_a32 : 2;
-
- uint64_t m_a03 : 2;
- uint64_t m_a13 : 2;
- uint64_t m_a23 : 2;
- uint64_t m_a33 : 2;
-
- } m_hi;
-
- uint64_t m_hi_bits;
- };
-
- color_rgba get_low_color() const
- {
- return color_rgba(cNoClamp,
- (int)((m_lo.m_r0 << 1) | (m_lo.m_r0 >> 6)),
- (int)((m_lo.m_g0 << 1) | (m_lo.m_g0 >> 6)),
- (int)((m_lo.m_b0 << 1) | (m_lo.m_b0 >> 6)),
- m_lo.m_a0);
- }
-
- color_rgba get_high_color() const
- {
- return color_rgba(cNoClamp,
- (int)((m_lo.m_r1 << 1) | (m_lo.m_r1 >> 6)),
- (int)((m_lo.m_g1 << 1) | (m_lo.m_g1 >> 6)),
- (int)((m_lo.m_b1 << 1) | (m_lo.m_b1 >> 6)),
- (int)m_lo.m_a1_0 | ((int)m_hi.m_a1_1 << 6));
- }
-
- void get_block_colors(color_rgba* pColors) const
- {
- const color_rgba low_color(get_low_color());
- const color_rgba high_color(get_high_color());
-
- for (uint32_t i = 0; i < 4; i++)
- {
- static const uint32_t s_bc7_weights2[4] = { 0, 21, 43, 64 };
-
- pColors[i].set_noclamp_rgba(
- (low_color.r * (64 - s_bc7_weights2[i]) + high_color.r * s_bc7_weights2[i] + 32) >> 6,
- (low_color.g * (64 - s_bc7_weights2[i]) + high_color.g * s_bc7_weights2[i] + 32) >> 6,
- (low_color.b * (64 - s_bc7_weights2[i]) + high_color.b * s_bc7_weights2[i] + 32) >> 6,
- (low_color.a * (64 - s_bc7_weights2[i]) + high_color.a * s_bc7_weights2[i] + 32) >> 6);
+ switch (mode)
+ {
+ case 0:
+ case 2:
+ return unpack_bc7_mode0_2(mode, pBlock, pPixels);
+ case 1:
+ case 3:
+ case 7:
+ return unpack_bc7_mode1_3_7(mode, pBlock, pPixels);
+ case 4:
+ case 5:
+ return unpack_bc7_mode4_5(mode, pBlock, pPixels);
+ case 6:
+ return unpack_bc7_mode6(pBlock, pPixels);
+ default:
+ break;
+ }
}
- }
-
- uint32_t get_selector(uint32_t idx, bool alpha) const
- {
- const uint32_t size = (idx == 0) ? 1 : 2;
-
- uint32_t ofs = alpha ? 97 : 66;
-
- if (idx)
- ofs += 1 + 2 * (idx - 1);
-
- return get_block_bits(reinterpret_cast(this), ofs, size);
- }
- };
-
- bool unpack_bc7_mode5(const void* pBlock_bits, color_rgba* pPixels)
- {
- static_assert(sizeof(bc7_mode_5) == 16, "sizeof(bc7_mode_5) == 16");
-
- const bc7_mode_5& block = *static_cast(pBlock_bits);
-
- if (block.m_lo.m_mode != (1 << 5))
- return false;
-
- color_rgba block_colors[4];
- block.get_block_colors(block_colors);
-
- const uint32_t rot = block.m_lo.m_rot;
-
- for (uint32_t i = 0; i < 16; i++)
- {
- const uint32_t cs = block.get_selector(i, false);
-
- color_rgba c(block_colors[cs]);
-
- const uint32_t as = block.get_selector(i, true);
- c.a = block_colors[as].a;
-
- if (rot > 0)
- std::swap(c[3], c[rot - 1]);
-
- pPixels[i] = c;
}
- return true;
+ return false;
}
-
+
struct fxt1_block
{
union
@@ -903,13 +911,14 @@ namespace basisu
etc2_eac_r11 m_c[2];
};
- static void unpack_etc2_eac_r(const etc2_eac_r11* p, color_rgba* pPixels, uint32_t c)
+ void unpack_etc2_eac_r(const void *p, color_rgba* pPixels, uint32_t c)
{
- const uint64_t sels = p->get_sels();
+ const etc2_eac_r11* pBlock = static_cast(p);
+ const uint64_t sels = pBlock->get_sels();
- const int base = (int)p->m_base * 8 + 4;
- const int mul = p->m_mul ? ((int)p->m_mul * 8) : 1;
- const int table = (int)p->m_table;
+ const int base = (int)pBlock->m_base * 8 + 4;
+ const int mul = pBlock->m_mul ? ((int)pBlock->m_mul * 8) : 1;
+ const int table = (int)pBlock->m_table;
for (uint32_t y = 0; y < 4; y++)
{
@@ -923,7 +932,8 @@ namespace basisu
val = clamp(val, 0, 2047);
// Convert to 8-bits with rounding
- pPixels[x + y * 4].m_comps[c] = static_cast((val * 255 + 1024) / 2047);
+ //pPixels[x + y * 4].m_comps[c] = static_cast((val * 255 + 1024) / 2047);
+ pPixels[x + y * 4].m_comps[c] = static_cast((val * 255 + 1023) / 2047);
} // x
} // y
@@ -939,6 +949,11 @@ namespace basisu
}
}
+ void unpack_uastc(const void* p, color_rgba* pPixels)
+ {
+ basist::unpack_uastc(*static_cast(p), (basist::color32 *)pPixels, false);
+ }
+
// Unpacks to RGBA, R, RG, or A
bool unpack_block(texture_format fmt, const void* pBlock, color_rgba* pPixels)
{
@@ -966,14 +981,7 @@ namespace basisu
}
case texture_format::cBC7:
{
- // We only support modes 5 and 6.
- if (!unpack_bc7_mode5(pBlock, pPixels))
- {
- if (!unpack_bc7_mode6(pBlock, pPixels))
- return false;
- }
-
- break;
+ return unpack_bc7(pBlock, pPixels);
}
// Full ETC2 color blocks (planar/T/H modes) is currently unsupported in basisu, but we do support ETC2 with alpha (using ETC1 for color)
case texture_format::cETC2_RGB:
@@ -1032,6 +1040,11 @@ namespace basisu
unpack_etc2_eac_rg(pBlock, pPixels);
break;
}
+ case texture_format::cUASTC4x4:
+ {
+ unpack_uastc(pBlock, pPixels);
+ break;
+ }
default:
{
assert(0);
@@ -1113,6 +1126,7 @@ namespace basisu
KTX_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02,
KTX_COMPRESSED_RGBA_ASTC_4x4_KHR = 0x93B0,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR = 0x93D0,
+ KTX_COMPRESSED_RGBA_UASTC_4x4_KHR = 0x94CC, // TODO - Use proper value!
KTX_ATC_RGB_AMD = 0x8C92,
KTX_ATC_RGBA_INTERPOLATED_ALPHA_AMD = 0x87EE,
KTX_COMPRESSED_RGB_FXT1_3DFX = 0x86B0,
@@ -1305,6 +1319,12 @@ namespace basisu
base_internal_fmt = KTX_RG;
break;
}
+ case texture_format::cUASTC4x4:
+ {
+ internal_fmt = KTX_COMPRESSED_RGBA_UASTC_4x4_KHR;
+ base_internal_fmt = KTX_RGBA;
+ break;
+ }
case texture_format::cFXT1_RGB:
{
internal_fmt = KTX_COMPRESSED_RGB_FXT1_3DFX;
@@ -1415,7 +1435,7 @@ namespace basisu
return write_compressed_texture_file(pFilename, v, false);
}
- const uint32_t OUT_FILE_MAGIC = 'TEXC';
+ //const uint32_t OUT_FILE_MAGIC = 'TEXC';
struct out_file_header
{
packed_uint<4> m_magic;
@@ -1428,7 +1448,11 @@ namespace basisu
bool write_3dfx_out_file(const char* pFilename, const gpu_image& gi)
{
out_file_header hdr;
- hdr.m_magic = OUT_FILE_MAGIC;
+ //hdr.m_magic = OUT_FILE_MAGIC;
+ hdr.m_magic.m_bytes[0] = 67;
+ hdr.m_magic.m_bytes[1] = 88;
+ hdr.m_magic.m_bytes[2] = 69;
+ hdr.m_magic.m_bytes[3] = 84;
hdr.m_pad = 0;
hdr.m_width = gi.get_blocks_x() * 8;
hdr.m_height = gi.get_blocks_y() * 4;
diff --git a/basisu_gpu_texture.h b/basisu_gpu_texture.h
index 8a49757..7cf2b41 100644
--- a/basisu_gpu_texture.h
+++ b/basisu_gpu_texture.h
@@ -1,5 +1,5 @@
// basisu_gpu_texture.h
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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,8 +18,7 @@
namespace basisu
{
- // GPU texture image
-
+ // GPU texture "image"
class gpu_image
{
public:
@@ -133,22 +132,23 @@ namespace basisu
bool write_compressed_texture_file(const char *pFilename, const gpu_image &g);
bool write_3dfx_out_file(const char* pFilename, const gpu_image& gi);
- // GPU texture block unpacking
+ // GPU texture block unpacking
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_mode5(const void* pBlock_bits, color_rgba* pPixels);
+ bool unpack_bc7(const void* pBlock_bits, color_rgba* pPixels);
void unpack_atc(const void* pBlock_bits, color_rgba* pPixels);
bool unpack_fxt1(const void* p, color_rgba* pPixels);
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 only capable of unpacking texture data created by the transcoder.
- // For some texture formats (like BC7, or ETC2) it's not a complete implementation.
+ // unpack_block() is primarily intended to unpack texture data created by the transcoder.
+ // For some texture formats (like ETC2 RGB, PVRTC2, FXT1) it's not a complete implementation.
bool unpack_block(texture_format fmt, const void *pBlock, color_rgba *pPixels);
} // namespace basisu
diff --git a/basisu_pvrtc1_4.cpp b/basisu_pvrtc1_4.cpp
index f0122fc..330cea8 100644
--- a/basisu_pvrtc1_4.cpp
+++ b/basisu_pvrtc1_4.cpp
@@ -1,5 +1,5 @@
// basisu_pvrtc1_4.cpp
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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.
@@ -16,6 +16,118 @@
namespace basisu
{
+#if 0
+ static const uint8_t g_pvrtc_5[32] = { 0,8,16,24,33,41,49,57,66,74,82,90,99,107,115,123,132,140,148,156,165,173,181,189,198,206,214,222,231,239,247,255 };
+ static const uint8_t g_pvrtc_4[16] = { 0,16,33,49,66,82,99,115,140,156,173,189,206,222,239,255 };
+ static const uint8_t g_pvrtc_3[8] = { 0,33,74,107,148,181,222,255 };
+ static const uint8_t g_pvrtc_alpha[9] = { 0,34,68,102,136,170,204,238,255 };
+#endif
+
+ static const uint8_t g_pvrtc_5_nearest[256] = { 0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,31,31,31,31 };
+ static const uint8_t g_pvrtc_4_nearest[256] = { 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15 };
+#if 0
+ static const uint8_t g_pvrtc_3_nearest[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 };
+ static const uint8_t g_pvrtc_alpha_nearest[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8 };
+#endif
+
+#if 0
+ static const uint8_t g_pvrtc_5_floor[256] =
+ {
+ 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,
+ 3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,
+ 7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,
+ 11,11,11,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,15,15,15,15,15,
+ 15,15,15,15,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,19,19,19,19,
+ 19,19,19,19,19,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,23,23,23,
+ 23,23,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,27,27,
+ 27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,31
+ };
+
+ static const uint8_t g_pvrtc_5_ceil[256] =
+ {
+ 0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,
+ 4,4,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,8,8,8,8,8,8,
+ 8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,12,12,12,12,12,
+ 12,12,12,12,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,16,16,16,16,
+ 16,16,16,16,16,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,20,20,20,
+ 20,20,20,20,20,20,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,24,24,
+ 24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,28,
+ 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31
+ };
+
+ static const uint8_t g_pvrtc_4_floor[256] =
+ {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,
+ 9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,
+ 11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,
+ 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15
+ };
+
+ static const uint8_t g_pvrtc_4_ceil[256] =
+ {
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,
+ 10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,
+ 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,
+ 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15
+ };
+
+ static const uint8_t g_pvrtc_3_floor[256] =
+ {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7
+ };
+
+ static const uint8_t g_pvrtc_3_ceil[256] =
+ {
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+ };
+
+ static const uint8_t g_pvrtc_alpha_floor[256] =
+ {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8
+ };
+
+ static const uint8_t g_pvrtc_alpha_ceil[256] =
+ {
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+ };
+#endif
+
uint32_t pvrtc4_swizzle_uv(uint32_t width, uint32_t height, uint32_t x, uint32_t y)
{
assert((x < width) && (y < height) && basisu::is_pow2(height) && basisu::is_pow2(width));
@@ -266,4 +378,187 @@ namespace basisu
}
}
+ uint64_t pvrtc4_image::local_endpoint_optimization_opaque(uint32_t bx, uint32_t by, const image& orig_img, bool perceptual)
+ {
+ uint64_t initial_error = evaluate_1x1_endpoint_error(bx, by, orig_img, perceptual, false);
+ if (!initial_error)
+ return initial_error;
+
+ vec3F c_avg_orig(0);
+
+ for (int y = 0; y < 7; y++)
+ {
+ const uint32_t py = wrap_y(by * 4 + y - 1);
+ for (uint32_t x = 0; x < 7; x++)
+ {
+ const uint32_t px = wrap_x(bx * 4 + x - 1);
+
+ const color_rgba& c = orig_img(px, py);
+
+ c_avg_orig[0] += c[0];
+ c_avg_orig[1] += c[1];
+ c_avg_orig[2] += c[2];
+ }
+ }
+
+ c_avg_orig *= 1.0f / 49.0f;
+
+ vec3F quant_colors[2];
+ quant_colors[0].set(c_avg_orig);
+ quant_colors[0] -= vec3F(.0125f);
+
+ quant_colors[1].set(c_avg_orig);
+ quant_colors[1] += vec3F(.0125f);
+
+ float total_weight[2];
+
+ bool success = true;
+
+ for (uint32_t pass = 0; pass < 4; pass++)
+ {
+ vec3F new_colors[2] = { vec3F(0), vec3F(0) };
+ memset(total_weight, 0, sizeof(total_weight));
+
+ static const float s_weights[7][7] =
+ {
+ { 1.000000f, 1.637089f, 2.080362f, 2.242640f, 2.080362f, 1.637089f, 1.000000f },
+ { 1.637089f, 2.414213f, 3.006572f, 3.242640f, 3.006572f, 2.414213f, 1.637089f },
+ { 2.080362f, 3.006572f, 3.828426f, 4.242640f, 3.828426f, 3.006572f, 2.080362f },
+ { 2.242640f, 3.242640f, 4.242640f, 5.000000f, 4.242640f, 3.242640f, 2.242640f },
+ { 2.080362f, 3.006572f, 3.828426f, 4.242640f, 3.828426f, 3.006572f, 2.080362f },
+ { 1.637089f, 2.414213f, 3.006572f, 3.242640f, 3.006572f, 2.414213f, 1.637089f },
+ { 1.000000f, 1.637089f, 2.080362f, 2.242640f, 2.080362f, 1.637089f, 1.000000f }
+ };
+
+ for (int y = 0; y < 7; y++)
+ {
+ const uint32_t py = wrap_y(by * 4 + y - 1);
+ for (uint32_t x = 0; x < 7; x++)
+ {
+ const uint32_t px = wrap_x(bx * 4 + x - 1);
+
+ const color_rgba& orig_c = orig_img(px, py);
+
+ vec3F color(orig_c[0], orig_c[1], orig_c[2]);
+
+ uint32_t c = quant_colors[0].squared_distance(color) > quant_colors[1].squared_distance(color);
+
+ const float weight = s_weights[y][x];
+ new_colors[c] += color * weight;
+
+ total_weight[c] += weight;
+ }
+ }
+
+ if (!total_weight[0] || !total_weight[1])
+ success = false;
+
+ quant_colors[0] = new_colors[0] / (float)total_weight[0];
+ quant_colors[1] = new_colors[1] / (float)total_weight[1];
+ }
+
+ if (!success)
+ {
+ quant_colors[0] = c_avg_orig;
+ quant_colors[1] = c_avg_orig;
+ }
+
+ vec4F colors[2] = { quant_colors[0], quant_colors[1] };
+
+ colors[0] += vec3F(.5f);
+ colors[1] += vec3F(.5f);
+ color_rgba color_0((int)colors[0][0], (int)colors[0][1], (int)colors[0][2], 0);
+ color_rgba color_1((int)colors[1][0], (int)colors[1][1], (int)colors[1][2], 0);
+
+ pvrtc4_block cur_blocks[3][3];
+
+ for (int y = -1; y <= 1; y++)
+ {
+ for (int x = -1; x <= 1; x++)
+ {
+ const uint32_t block_x = wrap_block_x(bx + x);
+ const uint32_t block_y = wrap_block_y(by + y);
+ cur_blocks[x + 1][y + 1] = m_blocks(block_x, block_y);
+ }
+ }
+
+ color_rgba l1(0), h1(0);
+
+ l1[0] = g_pvrtc_5_nearest[color_0[0]];
+ h1[0] = g_pvrtc_5_nearest[color_1[0]];
+
+ l1[1] = g_pvrtc_5_nearest[color_0[1]];
+ h1[1] = g_pvrtc_5_nearest[color_1[1]];
+
+ l1[2] = g_pvrtc_4_nearest[color_0[2]];
+ h1[2] = g_pvrtc_5_nearest[color_0[2]];
+
+ l1[3] = 0;
+ h1[3] = 0;
+
+ m_blocks(bx, by).set_endpoint_raw(0, l1, true);
+ m_blocks(bx, by).set_endpoint_raw(1, h1, true);
+
+ uint64_t e03_err_0 = remap_pixels_influenced_by_endpoint(bx, by, orig_img, perceptual, false);
+
+ pvrtc4_block blocks0[3][3];
+ for (int y = -1; y <= 1; y++)
+ {
+ for (int x = -1; x <= 1; x++)
+ {
+ const uint32_t block_x = wrap_block_x(bx + x);
+ const uint32_t block_y = wrap_block_y(by + y);
+ blocks0[x + 1][y + 1] = m_blocks(block_x, block_y);
+ }
+ }
+
+ l1[0] = g_pvrtc_5_nearest[color_1[0]];
+ h1[0] = g_pvrtc_5_nearest[color_0[0]];
+
+ l1[1] = g_pvrtc_5_nearest[color_1[1]];
+ h1[1] = g_pvrtc_5_nearest[color_0[1]];
+
+ l1[2] = g_pvrtc_4_nearest[color_1[2]];
+ h1[2] = g_pvrtc_5_nearest[color_0[2]];
+
+ l1[3] = 0;
+ h1[3] = 0;
+
+ m_blocks(bx, by).set_endpoint_raw(0, l1, true);
+ m_blocks(bx, by).set_endpoint_raw(1, h1, true);
+
+ uint64_t e03_err_1 = remap_pixels_influenced_by_endpoint(bx, by, orig_img, perceptual, false);
+
+ if (initial_error < std::min(e03_err_0, e03_err_1))
+ {
+ for (int y = -1; y <= 1; y++)
+ {
+ for (int x = -1; x <= 1; x++)
+ {
+ const uint32_t block_x = wrap_block_x(bx + x);
+ const uint32_t block_y = wrap_block_y(by + y);
+ m_blocks(block_x, block_y) = cur_blocks[x + 1][y + 1];
+ }
+ }
+ return initial_error;
+ }
+ else if (e03_err_0 < e03_err_1)
+ {
+ for (int y = -1; y <= 1; y++)
+ {
+ for (int x = -1; x <= 1; x++)
+ {
+ const uint32_t block_x = wrap_block_x(bx + x);
+ const uint32_t block_y = wrap_block_y(by + y);
+ m_blocks(block_x, block_y) = blocks0[x + 1][y + 1];
+ }
+ }
+ assert(e03_err_0 == evaluate_1x1_endpoint_error(bx, by, orig_img, perceptual, false));
+ return e03_err_0;
+ }
+
+ assert(e03_err_1 == evaluate_1x1_endpoint_error(bx, by, orig_img, perceptual, false));
+ return e03_err_1;
+ }
+
} // basisu
diff --git a/basisu_pvrtc1_4.h b/basisu_pvrtc1_4.h
index 80b4413..1f17661 100644
--- a/basisu_pvrtc1_4.h
+++ b/basisu_pvrtc1_4.h
@@ -1,5 +1,5 @@
// basisu_pvrtc1_4.cpp
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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.
@@ -87,6 +87,14 @@ namespace basisu
return (m_modulation >> ((y * 4 + x) * 2)) & 3;
}
+ inline void set_modulation(uint32_t x, uint32_t y, uint32_t s)
+ {
+ assert((x < 4) && (y < 4) && (s < 4));
+ uint32_t n = (y * 4 + x) * 2;
+ m_modulation = (m_modulation & (~(3 << n))) | (s << n);
+ assert(get_modulation(x, y) == s);
+ }
+
// Scaled by 8
inline const uint32_t* get_scaled_modulation_values(bool block_uses_transparent_modulation) const
{
@@ -107,7 +115,7 @@ namespace basisu
}
// opaque endpoints: 554, 555
- // transparent endpoints: 3443 or 3444
+ // transparent endpoints: 3443, 3444
inline void set_endpoint_raw(uint32_t endpoint_index, const color_rgba& c, bool opaque_endpoint)
{
assert(endpoint_index < 2);
@@ -352,7 +360,93 @@ namespace basisu
return result;
}
-
+
+ inline void set_modulation(uint32_t x, uint32_t y, uint32_t s)
+ {
+ assert((x < m_width) && (y < m_height));
+ return m_blocks(x >> 2, y >> 2).set_modulation(x & 3, y & 3, s);
+ }
+
+ inline uint64_t map_pixel(uint32_t x, uint32_t y, const color_rgba& c, bool perceptual, bool alpha_is_significant, bool record = true)
+ {
+ color_rgba v[4];
+ get_interpolated_colors(x, y, v);
+
+ uint64_t best_dist = color_distance(perceptual, c, v[0], alpha_is_significant);
+ uint32_t best_v = 0;
+ for (uint32_t i = 1; i < 4; i++)
+ {
+ uint64_t dist = color_distance(perceptual, c, v[i], alpha_is_significant);
+ if (dist < best_dist)
+ {
+ best_dist = dist;
+ best_v = i;
+ }
+ }
+
+ if (record)
+ set_modulation(x, y, best_v);
+
+ return best_dist;
+ }
+
+ inline uint64_t remap_pixels_influenced_by_endpoint(uint32_t bx, uint32_t by, const image& orig_img, bool perceptual, bool alpha_is_significant)
+ {
+ uint64_t total_error = 0;
+
+ for (int yd = -3; yd <= 3; yd++)
+ {
+ const int y = wrap_y((int)by * 4 + 2 + yd);
+
+ for (int xd = -3; xd <= 3; xd++)
+ {
+ const int x = wrap_x((int)bx * 4 + 2 + xd);
+
+ total_error += map_pixel(x, y, orig_img(x, y), perceptual, alpha_is_significant);
+ }
+ }
+
+ return total_error;
+ }
+
+ inline uint64_t evaluate_1x1_endpoint_error(uint32_t bx, uint32_t by, const image& orig_img, bool perceptual, bool alpha_is_significant, uint64_t threshold_error = 0) const
+ {
+ uint64_t total_error = 0;
+
+ for (int yd = -3; yd <= 3; yd++)
+ {
+ const int y = wrap_y((int)by * 4 + 2 + yd);
+
+ for (int xd = -3; xd <= 3; xd++)
+ {
+ const int x = wrap_x((int)bx * 4 + 2 + xd);
+
+ total_error += color_distance(perceptual, get_pixel(x, y), orig_img(x, y), alpha_is_significant);
+
+ if ((threshold_error) && (total_error >= threshold_error))
+ return total_error;
+ }
+ }
+
+ return total_error;
+ }
+
+ uint64_t local_endpoint_optimization_opaque(uint32_t bx, uint32_t by, const image& orig_img, bool perceptual);
+
+ inline uint64_t map_all_pixels(const image& img, bool perceptual, bool alpha_is_significant)
+ {
+ assert(m_width == img.get_width());
+ assert(m_height == img.get_height());
+
+ uint64_t total_error = 0;
+ for (uint32_t y = 0; y < img.get_height(); y++)
+ for (uint32_t x = 0; x < img.get_width(); x++)
+ total_error += map_pixel(x, y, img(x, y), perceptual, alpha_is_significant);
+
+ return total_error;
+ }
+
+ public:
uint32_t m_width, m_height;
pvrtc4_block_vector2D m_blocks;
uint32_t m_block_width, m_block_height;
diff --git a/basisu_resample_filters.cpp b/basisu_resample_filters.cpp
index d0b2fd7..39df8bd 100644
--- a/basisu_resample_filters.cpp
+++ b/basisu_resample_filters.cpp
@@ -1,5 +1,5 @@
// basisu_resampler_filters.cpp
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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.
@@ -283,7 +283,7 @@ namespace basisu
return sum;
}
- static const float KAISER_ALPHA = 4.0;
+ //static const float KAISER_ALPHA = 4.0;
static double kaiser(double alpha, double half_width, double x)
{
const double ratio = (x / half_width);
diff --git a/basisu_tool.cpp b/basisu_tool.cpp
index 8172a8c..3fbc56a 100644
--- a/basisu_tool.cpp
+++ b/basisu_tool.cpp
@@ -1,5 +1,5 @@
// basisu_tool.cpp
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -11,6 +11,11 @@
// 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.
+#if _MSC_VER
+// For sprintf(), strcpy()
+#define _CRT_SECURE_NO_WARNINGS (1)
+#endif
+
#include "transcoder/basisu.h"
#include "transcoder/basisu_transcoder_internal.h"
#include "basisu_enc.h"
@@ -23,20 +28,23 @@
#include "transcoder/basisu_transcoder.h"
#include "basisu_ssim.h"
-#define BASISU_CATCH_EXCEPTIONS 1
+// Set BASISU_CATCH_EXCEPTIONS if you want exceptions to crash the app, otherwise main() catches them.
+#define BASISU_CATCH_EXCEPTIONS 0
using namespace basisu;
-#define BASISU_TOOL_VERSION "1.10.00"
+#define BASISU_TOOL_VERSION "1.12.00"
enum tool_mode
{
cDefault,
cCompress,
cValidate,
+ cInfo,
cUnpack,
cCompare,
cVersion,
+ cBench
};
static void print_usage()
@@ -47,6 +55,7 @@ static void print_usage()
"The default mode is compression of one or more PNG files to a .basis file. Alternate modes:\n"
" -unpack: Use transcoder to unpack .basis file to one or more .ktx/.png files\n"
" -validate: Validate and display information about a .basis file\n"
+ " -info: Display high-level information about a .basis file\n"
" -compare: Compare two PNG images specified with -file, output PSNR and SSIM statistics and RGB/A delta images\n"
" -version: Print basisu version and exit\n"
"Unless an explicit mode is specified, if one or more files have the .basis extension this tool defaults to unpack mode.\n"
@@ -62,7 +71,7 @@ static void print_usage()
" -multifile_printf: printf() format strint to use to compose multiple filenames\n"
" -multifile_first: The index of the first file to process, default is 0 (must specify -multifile_printf and -multifile_num)\n"
" -multifile_num: The total number of files to process.\n"
- " -q X: Set quality level, 1-255, default is 128, lower=better compression/lower quality/faster, higher=less compression/higher quality/slower, default is 128. For even higher quality, use -max_endpoints/-max_selectors.\n"
+ " -q X: Set ETC1S quality level, 1-255, default is 128, lower=better compression/lower quality/faster, higher=less compression/higher quality/slower, default is 128. For even higher quality, use -max_endpoints/-max_selectors.\n"
" -linear: Use linear colorspace metrics (instead of the default sRGB), and by default linear (not sRGB) mipmap filtering.\n"
" -output_file filename: Output .basis/.ktx filename\n"
" -output_path: Output .basis/.ktx files to specified directory.\n"
@@ -74,8 +83,13 @@ static void print_usage()
" For video, the .basis file will be written with the first frame being an I-Frame, and subsequent frames being P-Frames (using conditional replenishment). Playback must always occur in order from first to last image.\n"
" -framerate X: Set framerate in header to X/frames sec.\n"
" -individual: Process input images individually and output multiple .basis files (not as a texture array)\n"
- " -comp_level X: Set encoding speed vs. quality tradeoff. Range is 0-5, default is 1. Higher values=MUCH slower, but slightly higher quality. Mostly intended for videos. Use -q first!\n"
+ " -comp_level X: Set ETC1S encoding speed vs. quality tradeoff. Range is 0-5, default is 1. Higher values=MUCH slower, but slightly higher quality. Mostly intended for videos. Use -q first!\n"
" -fuzz_testing: Use with -validate: Disables CRC16 validation of file contents before transcoding\n"
+ "\nUASTC options:\n"
+ " -uastc: Enable UASTC texture mode, instead of the default ETC1S mode. Significantly higher texture quality, but larger files. (Note that .basis files must be losslessly compressed by the user.)\n"
+ " -uastc_level: Set UASTC encoding level. Range is [0,4], default is 2, higher=slower but higher quality. 0=fastest/lowest quality, 3=slowest practical option, 4=impractically slow/highest achievable quality\n"
+ " -uastc_rdo_q X: Enable UASTC RDO post-processing and set UASTC RDO quality scalar to X. Lower values=higher quality/larger LZ compressed files, higher values=lower quality/smaller LZ compressed files. Good range to try is [.2-4].\n"
+ " -uastc_rdo_d X: Set UASTC RDO dictionary size in bytes. Default is 32768. Lower values=faster, but less compression.\n"
"\n"
"More options:\n"
" -max_endpoints X: Manually set the max number of color endpoint clusters from 1-16128, use instead of -q\n"
@@ -90,6 +104,7 @@ static void print_usage()
" -etc1_only: Only unpack to ETC1, skipping the other texture formats during -unpack\n"
" -disable_hierarchical_endpoint_codebooks: Disable hierarchical endpoint codebook usage, slower but higher quality on some compression levels\n"
" -compare_ssim: Compute and display SSIM of image comparison (slow)\n"
+ " -bench: UASTC benchmark mode, for development only\n"
"\n"
"Mipmap generation options:\n"
" -mipmap: Generate mipmaps for each source image\n"
@@ -229,7 +244,8 @@ public:
m_no_ktx(false),
m_etc1_only(false),
m_fuzz_testing(false),
- m_compare_ssim(false)
+ m_compare_ssim(false),
+ m_bench(false)
{
}
@@ -252,10 +268,14 @@ public:
m_mode = cUnpack;
else if (strcasecmp(pArg, "-validate") == 0)
m_mode = cValidate;
+ else if (strcasecmp(pArg, "-info") == 0)
+ m_mode = cInfo;
else if (strcasecmp(pArg, "-version") == 0)
m_mode = cVersion;
else if (strcasecmp(pArg, "-compare_ssim") == 0)
m_compare_ssim = true;
+ else if (strcasecmp(pArg, "-bench") == 0)
+ m_mode = cBench;
else if (strcasecmp(pArg, "-file") == 0)
{
REMAINING_ARGS_CHECK(1);
@@ -286,6 +306,37 @@ public:
m_multifile_num = atoi(arg_v[arg_index + 1]);
arg_count++;
}
+ else if (strcasecmp(pArg, "-uastc") == 0)
+ m_comp_params.m_uastc = true;
+ else if (strcasecmp(pArg, "-uastc_level") == 0)
+ {
+ REMAINING_ARGS_CHECK(1);
+
+ int uastc_level = atoi(arg_v[arg_index + 1]);
+
+ uastc_level = clamp(uastc_level, 0, TOTAL_PACK_UASTC_LEVELS - 1);
+
+ static_assert(TOTAL_PACK_UASTC_LEVELS == 5, "TOTAL_PACK_UASTC_LEVELS==5");
+ static const uint32_t s_level_flags[TOTAL_PACK_UASTC_LEVELS] = { cPackUASTCLevelFastest, cPackUASTCLevelFaster, cPackUASTCLevelDefault, cPackUASTCLevelSlower, cPackUASTCLevelVerySlow };
+
+ m_comp_params.m_pack_uastc_flags &= ~cPackUASTCLevelMask;
+ m_comp_params.m_pack_uastc_flags |= s_level_flags[uastc_level];
+
+ arg_count++;
+ }
+ else if (strcasecmp(pArg, "-uastc_rdo_q") == 0)
+ {
+ REMAINING_ARGS_CHECK(1);
+ m_comp_params.m_rdo_uastc_quality_scalar = (float)atof(arg_v[arg_index + 1]);
+ m_comp_params.m_rdo_uastc = true;
+ arg_count++;
+ }
+ else if (strcasecmp(pArg, "-uastc_rdo_d") == 0)
+ {
+ REMAINING_ARGS_CHECK(1);
+ m_comp_params.m_rdo_uastc_dict_size = atoi(arg_v[arg_index + 1]);
+ arg_count++;
+ }
else if (strcasecmp(pArg, "-linear") == 0)
m_comp_params.m_perceptual = false;
else if (strcasecmp(pArg, "-srgb") == 0)
@@ -585,6 +636,7 @@ public:
bool m_etc1_only;
bool m_fuzz_testing;
bool m_compare_ssim;
+ bool m_bench;
};
static bool expand_multifile(command_line_params &opts)
@@ -670,7 +722,7 @@ static bool compress_mode(command_line_params &opts)
}
}
- printf("Processing %u total files\n", (uint32_t)opts.m_input_filenames.size());
+ printf("Processing %u total file(s)\n", (uint32_t)opts.m_input_filenames.size());
for (size_t file_index = 0; file_index < (opts.m_individual ? opts.m_input_filenames.size() : 1U); file_index++)
{
@@ -759,6 +811,9 @@ static bool compress_mode(command_line_params &opts)
case basis_compressor::cECFailedValidating:
error_printf("Compressor failed 2darray/cubemap/video validation checks!\n");
break;
+ case basis_compressor::cECFailedEncodeUASTC:
+ error_printf("Compressor UASTC encode failed!\n");
+ break;
case basis_compressor::cECFailedFrontEnd:
error_printf("Compressor frontend stage failed!\n");
break;
@@ -774,6 +829,9 @@ static bool compress_mode(command_line_params &opts)
case basis_compressor::cECFailedWritingOutput:
error_printf("Compressor failed writing to output Basis file!\n");
break;
+ case basis_compressor::cECFailedUASTCRDOPostProcess:
+ error_printf("Compressor failed during the UASTC post process step!\n");
+ break;
default:
error_printf("basis_compress::process() failed!\n");
break;
@@ -795,14 +853,15 @@ static bool compress_mode(command_line_params &opts)
{
for (size_t slice_index = 0; slice_index < c.get_stats().size(); slice_index++)
{
- fprintf(pCSV_file, "\"%s\", %u, %u, %u, %u, %u, %f, %f, %f, %f, %u, %u, %f\n",
+ fprintf(pCSV_file, "\"%s\", %u, %u, %u, %u, %u, %f, %f, %f, %f, %f, %u, %u, %f\n",
params.m_out_filename.c_str(),
(uint32_t)slice_index, (uint32_t)c.get_stats().size(),
c.get_stats()[slice_index].m_width, c.get_stats()[slice_index].m_height, (uint32_t)c.get_any_source_image_has_alpha(),
c.get_basis_bits_per_texel(),
- c.get_stats()[slice_index].m_best_luma_709_psnr,
- c.get_stats()[slice_index].m_basis_etc1s_luma_709_psnr,
- c.get_stats()[slice_index].m_basis_bc1_luma_709_psnr,
+ c.get_stats()[slice_index].m_basis_rgb_avg_psnr,
+ c.get_stats()[slice_index].m_basis_rgba_avg_psnr,
+ c.get_stats()[slice_index].m_basis_luma_709_psnr,
+ c.get_stats()[slice_index].m_best_etc1s_luma_709_psnr,
params.m_quality_level, (int)params.m_compression_level, tm.get_elapsed_secs());
fflush(pCSV_file);
}
@@ -822,8 +881,9 @@ static bool compress_mode(command_line_params &opts)
return true;
}
-static bool unpack_and_validate_mode(command_line_params &opts, bool validate_flag)
+static bool unpack_and_validate_mode(command_line_params &opts)
{
+ const bool validate_flag = (opts.m_mode == cValidate);
basist::etc1_global_selector_codebook sel_codebook(basist::g_global_selector_cb_size, basist::g_global_selector_cb);
if (!opts.m_input_filenames.size())
@@ -885,7 +945,7 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
error_printf("Failed retrieving Basis file information!\n");
return false;
}
-
+
assert(fileinfo.m_total_images == fileinfo.m_image_mipmap_levels.size());
assert(fileinfo.m_total_images == dec.get_total_images(&basis_data[0], (uint32_t)basis_data.size()));
@@ -898,6 +958,7 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
printf(" Endpoint codebook size: %u\n", fileinfo.m_endpoint_codebook_size);
printf(" Tables size: %u\n", fileinfo.m_tables_size);
printf(" Slices size: %u\n", fileinfo.m_slices_size);
+ printf(" Texture format: %s\n", (fileinfo.m_tex_format == basist::basis_tex_format::cUASTC4x4) ? "UASTC" : "ETC1S");
printf(" Texture type: %s\n", basist::basis_get_texture_type_name(fileinfo.m_tex_type));
printf(" us per frame: %u (%f fps)\n", fileinfo.m_us_per_frame, fileinfo.m_us_per_frame ? (1.0f / ((float)fileinfo.m_us_per_frame / 1000000.0f)) : 0.0f);
printf(" Total slices: %u\n", (uint32_t)fileinfo.m_slice_info.size());
@@ -940,6 +1001,8 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
}
printf("\n");
+ if (opts.m_mode == cInfo)
+ return true;
interval_timer tm;
tm.start();
@@ -968,6 +1031,12 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
if (basist::basis_transcoder_format_is_uncompressed(tex_fmt))
continue;
+
+ if (!basis_is_format_supported(tex_fmt, fileinfo.m_tex_format))
+ continue;
+
+ if (tex_fmt == basist::transcoder_texture_format::cTFBC7_ALT)
+ continue;
gpu_images[(int)tex_fmt].resize(fileinfo.m_total_images);
@@ -982,6 +1051,10 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
if (basist::basis_transcoder_format_is_uncompressed(transcoder_tex_fmt))
continue;
+ if (!basis_is_format_supported(transcoder_tex_fmt, fileinfo.m_tex_format))
+ continue;
+ if (transcoder_tex_fmt == basist::transcoder_texture_format::cTFBC7_ALT)
+ continue;
for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++)
{
@@ -1046,6 +1119,10 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
if (basist::basis_transcoder_format_is_uncompressed(transcoder_tex_fmt))
continue;
+ if (!basis_is_format_supported(transcoder_tex_fmt, fileinfo.m_tex_format))
+ continue;
+ if (transcoder_tex_fmt == basist::transcoder_texture_format::cTFBC7_ALT)
+ continue;
if ((!opts.m_no_ktx) && (fileinfo.m_tex_type == basist::cBASISTexTypeCubemapArray))
{
@@ -1150,6 +1227,18 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
return false;
}
printf("Wrote PNG file \"%s\"\n", a_filename.c_str());
+
+ std::string rgba_filename;
+ if (gi.size() > 1)
+ rgba_filename = base_filename + string_format("_unpacked_rgba_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index);
+ else
+ rgba_filename = base_filename + string_format("_unpacked_rgba_%s_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index);
+ if (!save_png(rgba_filename, u))
+ {
+ error_printf("Failed writing to PNG file \"%s\"\n", rgba_filename.c_str());
+ return false;
+ }
+ printf("Wrote PNG file \"%s\"\n", rgba_filename.c_str());
}
} // level_index
@@ -1191,6 +1280,8 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
printf("Transcode of image %u level %u res %ux%u format %s succeeded in %3.3f ms\n", image_index, level_index, level_info.m_orig_width, level_info.m_orig_height, basist::basis_get_format_name(transcoder_tex_fmt), total_transcode_time);
+ if (!validate_flag)
+ {
std::string rgb_filename(base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index));
if (!save_png(rgb_filename, img, cImageSaveIgnoreAlpha))
{
@@ -1206,6 +1297,7 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
return false;
}
printf("Wrote PNG file \"%s\"\n", a_filename.c_str());
+ }
} // level_index
} // image_index
@@ -1255,6 +1347,8 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
printf("Transcode of image %u level %u res %ux%u format %s succeeded in %3.3f ms\n", image_index, level_index, level_info.m_orig_width, level_info.m_orig_height, basist::basis_get_format_name(transcoder_tex_fmt), total_transcode_time);
+ if (!validate_flag)
+ {
std::string rgb_filename(base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index));
if (!save_png(rgb_filename, img, cImageSaveIgnoreAlpha))
{
@@ -1262,6 +1356,7 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
return false;
}
printf("Wrote PNG file \"%s\"\n", rgb_filename.c_str());
+ }
} // level_index
} // image_index
@@ -1312,6 +1407,8 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
printf("Transcode of image %u level %u res %ux%u format %s succeeded in %3.3f ms\n", image_index, level_index, level_info.m_orig_width, level_info.m_orig_height, basist::basis_get_format_name(transcoder_tex_fmt), total_transcode_time);
+ if (!validate_flag)
+ {
std::string rgb_filename(base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index));
if (!save_png(rgb_filename, img, cImageSaveIgnoreAlpha))
{
@@ -1327,6 +1424,7 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
return false;
}
printf("Wrote PNG file \"%s\"\n", a_filename.c_str());
+ }
} // level_index
} // image_index
@@ -1353,7 +1451,7 @@ static bool compare_mode(command_line_params &opts)
}
image a, b;
- if (!load_png(opts.m_input_filenames[0].c_str(), a))
+ if (!load_image(opts.m_input_filenames[0].c_str(), a))
{
error_printf("Failed loading image from file \"%s\"!\n", opts.m_input_filenames[0].c_str());
return false;
@@ -1361,7 +1459,7 @@ static bool compare_mode(command_line_params &opts)
printf("Loaded \"%s\", %ux%u, has alpha: %u\n", opts.m_input_filenames[0].c_str(), a.get_width(), a.get_height(), a.has_alpha());
- if (!load_png(opts.m_input_filenames[1].c_str(), b))
+ if (!load_image(opts.m_input_filenames[1].c_str(), b))
{
error_printf("Failed loading image from file \"%s\"!\n", opts.m_input_filenames[1].c_str());
return false;
@@ -1386,6 +1484,9 @@ static bool compare_mode(command_line_params &opts)
im.calc(a, b, 0, 3);
im.print("RGB ");
+ im.calc(a, b, 0, 4);
+ im.print("RGBA ");
+
im.calc(a, b, 0, 1);
im.print("R ");
@@ -1395,6 +1496,9 @@ static bool compare_mode(command_line_params &opts)
im.calc(a, b, 2, 1);
im.print("B ");
+ im.calc(a, b, 3, 1);
+ im.print("A ");
+
im.calc(a, b, 0, 0);
im.print("Y 709 " );
@@ -1450,9 +1554,1156 @@ static bool compare_mode(command_line_params &opts)
return true;
}
+#include "basisu_astc_decomp.h"
+#include "basisu_pvrtc1_4.h"
+
+#define MINIZ_HEADER_FILE_ONLY
+#include "basisu_miniz.h"
+static bool bench_mode(command_line_params& opts)
+{
+#if 0
+ ispc::bc7e_compress_block_init();
+
+ ispc::bc7e_compress_block_params pack_params;
+ memset(&pack_params, 0, sizeof(pack_params));
+ ispc::bc7e_compress_block_params_init_slow(&pack_params, false);
+#endif
+
+ const uint32_t JOB_POOL_SIZE = 7;
+ job_pool jpool(JOB_POOL_SIZE);
+
+ float total_uastc_psnr = 0, total_uastc_a_psnr = 0, total_uastc_rgba_psnr = 0;
+ float total_rdo_uastc_psnr = 0, total_rdo_uastc_a_psnr = 0, total_rdo_uastc_rgba_psnr = 0;
+ float total_uastc2_psnr = 0, total_uastc2_a_psnr = 0, total_uastc2_rgba_psnr = 0;
+ float total_bc7_psnr = 0, total_bc7_a_psnr = 0, total_bc7_rgba_psnr = 0;
+ float total_rdo_bc7_psnr = 0, total_rdo_bc7_a_psnr = 0, total_rdo_bc7_rgba_psnr = 0;
+ float total_obc1_psnr = 0;
+ float total_obc1_2_psnr = 0;
+ float total_obc1_psnr_sq = 0;
+ float total_obc1_2_psnr_sq = 0;
+ float total_bc1_psnr = 0;
+ float total_bc1_psnr_sq = 0;
+ //float total_obc7_psnr = 0, total_obc7_rgba_psnr = 0;
+ //float total_obc7_a_psnr = 0;
+ //float total_oastc_psnr = 0, total_oastc_rgba_psnr = 0;
+ float total_bc7enc_psnr = 0, total_bc7enc_rgba_psnr = 0, total_bc7enc_a_psnr = 0;
+ //float total_oastc_a_psnr = 0;
+ float total_etc1_psnr = 0;
+ float total_etc1_y_psnr = 0;
+ float total_etc1_g_psnr = 0;
+ float total_etc2_psnr = 0, total_etc2_rgba_psnr = 0;
+ float total_etc2_a_psnr = 0;
+ float total_bc3_psnr = 0, total_bc3_rgba_psnr = 0;
+ float total_bc3_a_psnr = 0;
+ float total_eac_r11_psnr = 0;
+ float total_eac_rg11_psnr = 0;
+ float total_pvrtc1_rgb_psnr = 0, total_pvrtc1_rgba_psnr = 0;
+ float total_pvrtc1_a_psnr = 0;
+ uint32_t total_images = 0;
+ uint32_t total_a_images = 0;
+ uint32_t total_pvrtc1_images = 0;
+
+ uint64_t overall_mode_hist[basist::TOTAL_UASTC_MODES];
+ memset(overall_mode_hist, 0, sizeof(overall_mode_hist));
+
+ std::mutex mode_hist_mutex;
+
+ uint32_t etc1_hint_hist[32];
+ memset(etc1_hint_hist, 0, sizeof(etc1_hint_hist));
+
+ srand(1023);
+ uint32_t first_image = 96;
+ uint32_t last_image = 96; //34
+
+ if (opts.m_input_filenames.size() >= 1)
+ {
+ first_image = 1;
+ last_image = 1;
+ }
+
+ const bool perceptual = false;
+ const bool force_la = false;
+
+ interval_timer otm;
+ otm.start();
+
+ //const uint32_t flags = cPackUASTCLevelFastest;// | cPackUASTCETC1DisableFlipAndIndividual;// Slower;
+ //const uint32_t flags = cPackUASTCLevelFaster;
+ //const uint32_t flags = cPackUASTCLevelVerySlow;
+ const uint32_t flags = cPackUASTCLevelDefault;
+
+ uint32_t etc1_inten_hist[8] = { 0,0,0,0,0,0,0,0 };
+ uint32_t etc1_flip_hist[2] = { 0, 0 };
+ uint32_t etc1_diff_hist[2] = { 0, 0 };
+
+ double overall_total_enc_time = 0;
+ double overall_total_bench_time = 0;
+ double overall_total_bench2_time = 0;
+ uint64_t overall_blocks = 0;
+
+ //bc7enc_compress_block_params bc7enc_p;
+ //bc7enc_compress_block_params_init(&bc7enc_p);
+ //bc7enc_compress_block_params_init_linear_weights(&bc7enc_p);
+ //bc7enc_p.m_uber_level = 3;
+
+ uint64_t total_comp_size = 0;
+ uint64_t total_raw_size = 0;
+ uint64_t total_rdo_comp_size = 0;
+ uint64_t total_rdo_raw_size = 0;
+ uint64_t total_comp_blocks = 0;
+
+ for (uint32_t image_index = first_image; image_index <= last_image; image_index++)
+ {
+ uint64_t mode_hist[basist::TOTAL_UASTC_MODES];
+ memset(mode_hist, 0, sizeof(mode_hist));
+
+ char buf[1024];
+ if (opts.m_input_filenames.size() >= 1)
+ strcpy(buf, opts.m_input_filenames[0].c_str());
+ else
+ sprintf(buf, "c:/dev/test_images/photo_png/kodim%02u.png", image_index);
+
+ printf("Image: %s\n", buf);
+
+ image img;
+ if (!load_image(buf, img))
+ return 0;
+
+ if (opts.m_input_filenames.size() == 2)
+ {
+ image alpha_img;
+ if (!load_image(opts.m_input_filenames[1].c_str(), alpha_img))
+ return 0;
+
+ printf("Alpha image: %s, %ux%u\n", opts.m_input_filenames[1].c_str(), alpha_img.get_width(), alpha_img.get_height());
+
+ for (uint32_t x = 0; x < alpha_img.get_width(); x++)
+ for (uint32_t y = 0; y < alpha_img.get_height(); y++)
+ {
+ if (x < img.get_width() && y < img.get_height())
+ img(x, y)[3] = (uint8_t)alpha_img(x, y).get_709_luma();
+ }
+ }
+
+ if (force_la)
+ {
+ for (uint32_t x = 0; x < img.get_width(); x++)
+ {
+ for (uint32_t y = 0; y < img.get_height(); y++)
+ {
+ const color_rgba& c = img(x, y);
+ img(x, y).set(c.r, c.r, c.r, c.g);
+ }
+ }
+ }
+
+ // HACK HACK
+ //if (!img.has_alpha())
+ // continue;
+
+ // HACK HACK
+ //img.crop(1024, 1024);
+
+ const uint32_t num_blocks_x = img.get_block_width(4);
+ const uint32_t num_blocks_y = img.get_block_height(4);
+ const uint32_t total_blocks = num_blocks_x * num_blocks_y;
+ const bool img_has_alpha = img.has_alpha();
+
+ img.crop_dup_borders(num_blocks_x * 4, num_blocks_y * 4);
+
+ printf("%ux%u, has alpha: %u\n", img.get_width(), img.get_height(), img_has_alpha);
+
+ image uastc_img(num_blocks_x * 4, num_blocks_y * 4);
+ image rdo_uastc_img(num_blocks_x * 4, num_blocks_y * 4);
+ image uastc2_img(num_blocks_x * 4, num_blocks_y * 4);
+ image opt_bc1_img(num_blocks_x * 4, num_blocks_y * 4);
+ image opt_bc1_2_img(num_blocks_x * 4, num_blocks_y * 4);
+ image bc1_img(num_blocks_x * 4, num_blocks_y * 4);
+ image bc3_img(num_blocks_x * 4, num_blocks_y * 4);
+ image eac_r11_img(num_blocks_x * 4, num_blocks_y * 4);
+ image eac_rg11_img(num_blocks_x * 4, num_blocks_y * 4);
+ image bc7_img(num_blocks_x * 4, num_blocks_y * 4);
+ image rdo_bc7_img(num_blocks_x * 4, num_blocks_y * 4);
+ image opt_bc7_img(num_blocks_x * 4, num_blocks_y * 4);
+ image etc1_img(num_blocks_x * 4, num_blocks_y * 4);
+ image etc1_g_img(num_blocks_x * 4, num_blocks_y * 4);
+ image etc2_img(num_blocks_x * 4, num_blocks_y * 4);
+ image part_img(num_blocks_x * 4, num_blocks_y * 4);
+ image opt_astc_img(num_blocks_x * 4, num_blocks_y * 4);
+ image bc7enc_img(num_blocks_x * 4, num_blocks_y * 4);
+
+ uint32_t total_bc1_hint0s = 0;
+ uint32_t total_bc1_hint1s = 0;
+ uint32_t total_bc1_hint01s = 0;
+
+ double total_enc_time = 0;
+ double total_bench_time = 0;
+ double total_bench2_time = 0;
+
+ std::vector ublocks(total_blocks);
+
+#if 0
+ astc_enc_settings astc_settings;
+ //if (img_has_alpha)
+ GetProfile_astc_alpha_slow(&astc_settings, 4, 4);
+ //else
+ // GetProfile_astc_fast(&astc_settings, 4, 4);
+#endif
+
+#if 0
+//#pragma omp parallel for
+ for (int by = 0; by < (int)num_blocks_y; by++)
+ {
+ // Process 64 blocks at a time, for efficient SIMD processing.
+ // Ideally, N >= 8 (or more) and (N % 8) == 0.
+ const int N = 64;
+
+ for (uint32_t bx = 0; bx < num_blocks_x; bx += N)
+ {
+ const uint32_t num_blocks_to_process = std::min(num_blocks_x - bx, N);
+
+ color_rgba pixels[16 * N];
+
+#if 0
+ // BC7E
+ // Extract num_blocks_to_process 4x4 pixel blocks from the source image and put them into the pixels[] array.
+ for (uint32_t b = 0; b < num_blocks_to_process; b++)
+ img.extract_block_clamped(pixels + b * 16, (bx + b) * 4, by * 4, 4, 4);
+
+ // Compress the blocks to BC7.
+ // Note: If you've used Intel's ispc_texcomp, the input pixels are different. BC7E requires a pointer to an array of 16 pixels for each block.
+ basist::bc7_block packed_blocks[N];
+ ispc::bc7e_compress_blocks(num_blocks_to_process, (uint64_t*)packed_blocks, reinterpret_cast(pixels), &pack_params);
+
+ for (uint32_t i = 0; i < num_blocks_to_process; i++)
+ {
+ color_rgba decoded_block[4][4];
+
+ //detexDecompressBlockBPTC((uint8_t *)&packed_blocks[i], 0xFF, 0, (uint8_t *)&decoded_block[0][0]);
+ unpack_block(texture_format::cBC7, &packed_blocks[i], &decoded_block[0][0]);
+
+ opt_bc7_img.set_block_clipped(&decoded_block[0][0], (bx + i) * 4, by * 4, 4, 4);
+ }
+#endif
+
+#if 0
+ // ispc_texcomp
+ color_rgba raster_pixels[(N * 4) * 4];
+
+ const uint32_t raster_width = num_blocks_to_process * 4;
+ const uint32_t raster_height = 4;
+
+ rgba_surface surf;
+ surf.ptr = &raster_pixels[0].r;
+ surf.width = raster_width;
+ surf.height = 4;
+ surf.stride = raster_width * 4;
+
+ for (uint32_t b = 0; b < num_blocks_to_process; b++)
+ for (uint32_t y = 0; y < 4; y++)
+ for (uint32_t x = 0; x < 4; x++)
+ raster_pixels[y * raster_width + b * 4 + x] = pixels[b * 16 + y * 4 + x];
+
+ uint8_t astc_blocks[16 * N];
+ CompressBlocksASTC(&surf, astc_blocks, &astc_settings);
+
+ for (uint32_t i = 0; i < num_blocks_to_process; i++)
+ {
+ color_rgba decoded_astc_block[4][4];
+ basisu_astc::astc::decompress((uint8_t*)decoded_astc_block, (uint8_t*)&astc_blocks[i * 16], false, 4, 4);
+
+ opt_astc_img.set_block_clipped(&decoded_astc_block[0][0], (bx + i) * 4, by * 4, 4, 4);
+ }
+#endif
+ }
+ }
+#endif
+
+ const uint32_t N = 128;
+ for (uint32_t block_index_iter = 0; block_index_iter < total_blocks; block_index_iter += N)
+ {
+ const uint32_t first_index = block_index_iter;
+ const uint32_t last_index = minimum(total_blocks, block_index_iter + N);
+
+ jpool.add_job([first_index, last_index, &img, num_blocks_x, num_blocks_y,
+ &opt_bc1_img, &opt_bc1_2_img, &mode_hist, &overall_mode_hist, &uastc_img, &uastc2_img, &bc7_img, &part_img, &mode_hist_mutex, &bc1_img, &etc1_img, &etc1_g_img, &etc2_img, &etc1_hint_hist, &perceptual,
+ &total_bc1_hint0s, &total_bc1_hint1s, &total_bc1_hint01s, &bc3_img, &total_enc_time, &eac_r11_img, &eac_rg11_img, &ublocks, &flags, &etc1_inten_hist, &etc1_flip_hist, &etc1_diff_hist, &total_bench_time, &total_bench2_time,
+ //&bc7enc_p, &bc7enc_img] {
+ &bc7enc_img] {
+
+ BASISU_NOTE_UNUSED(num_blocks_y);
+ BASISU_NOTE_UNUSED(perceptual);
+ BASISU_NOTE_UNUSED(flags);
+
+ for (uint32_t block_index = first_index; block_index < last_index; block_index++)
+ {
+ const uint32_t block_x = block_index % num_blocks_x;
+ const uint32_t block_y = block_index / num_blocks_x;
+
+ //uint32_t block_x = 170;
+ //uint32_t block_y = 167;
+
+ // HACK HACK
+ //if ((block_x == 77) && (block_y == 54))
+ // printf("!");
+
+ color_rgba block[4][4];
+ img.extract_block_clamped(&block[0][0], block_x * 4, block_y * 4, 4, 4);
+
+ uint8_t bc7_block[16];
+ //bc7enc_compress_block(bc7_block, block, &bc7enc_p);
+ color_rgba decoded_bc7enc_blk[4][4];
+ unpack_block(texture_format::cBC7, &bc7_block, &decoded_bc7enc_blk[0][0]);
+ bc7enc_img.set_block_clipped(&decoded_bc7enc_blk[0][0], block_x * 4, block_y * 4, 4, 4);
+
+ // Pack near-optimal BC1
+ // stb_dxt BC1 encoder
+ uint8_t bc1_block[8];
+
+ interval_timer btm;
+ btm.start();
+
+ //stb_compress_dxt_block(bc1_block, (uint8_t*)&block[0][0], 0, STB_DXT_HIGHQUAL);
+ basist::encode_bc1(bc1_block, (uint8_t*)&block[0][0], 0);// basist::cEncodeBC1HighQuality);
+ double total_b_time = btm.get_elapsed_secs();
+ {
+ std::lock_guard lck(mode_hist_mutex);
+ total_bench_time += total_b_time;
+ }
+
+ color_rgba block_bc1[4][4];
+ unpack_block(texture_format::cBC1, bc1_block, &block_bc1[0][0]);
+ opt_bc1_img.set_block_clipped(&block_bc1[0][0], block_x * 4, block_y * 4, 4, 4);
+
+ //uint64_t e1 = 0;
+ //for (uint32_t i = 0; i < 16; i++)
+ // e1 += color_distance(((color_rgba*)block_bc1)[i], ((color_rgba*)block)[i], false);
+
+ // My BC1 encoder
+ uint8_t bc1_block_2[8];
+ color_rgba block_bc1_2[4][4];
+
+ btm.start();
+ basist::encode_bc1_alt(bc1_block_2, (uint8_t*)&block[0][0], basist::cEncodeBC1HighQuality);
+ double total_b2_time = btm.get_elapsed_secs();
+ {
+ std::lock_guard lck(mode_hist_mutex);
+ total_bench2_time += total_b2_time;
+ }
+
+ unpack_block(texture_format::cBC1, bc1_block_2, &block_bc1_2[0][0]);
+ //uint64_t e2 = 0;
+ //for (uint32_t i = 0; i < 16; i++)
+ // e2 += color_distance(((color_rgba *)block_bc1_2)[i], ((color_rgba*)block)[i], false);
+
+ opt_bc1_2_img.set_block_clipped(&block_bc1_2[0][0], block_x * 4, block_y * 4, 4, 4);
+
+ // Encode to UASTC
+ basist::uastc_block encoded_uastc_blk;
+
+ interval_timer tm;
+ tm.start();
+ encode_uastc(&block[0][0].r, encoded_uastc_blk, flags);
+ double total_time = tm.get_elapsed_secs();
+ {
+ std::lock_guard lck(mode_hist_mutex);
+ total_enc_time += total_time;
+ }
+
+ ublocks[block_x + block_y * num_blocks_x] = encoded_uastc_blk;
+
+#if 0
+ for (uint32_t i = 0; i < 16; i++)
+ printf("0x%X,", encoded_uastc_blk.m_bytes[i]);
+ printf("\n");
+#endif
+
+ // Unpack UASTC
+ basist::unpacked_uastc_block unpacked_uastc_blk;
+ unpack_uastc(encoded_uastc_blk, unpacked_uastc_blk, false);
+
+ color_rgba unpacked_uastc_block_pixels[4][4];
+ bool success = basist::unpack_uastc(unpacked_uastc_blk, (basist::color32*) & unpacked_uastc_block_pixels[0][0], false);
+ (void)success;
+ assert(success);
+
+ uastc_img.set_block_clipped(&unpacked_uastc_block_pixels[0][0], block_x * 4, block_y * 4, 4, 4);
+
+ const uint32_t best_mode = unpacked_uastc_blk.m_mode;
+
+ {
+ std::lock_guard lck(mode_hist_mutex);
+ assert(best_mode < basist::TOTAL_UASTC_MODES);
+ if (best_mode < basist::TOTAL_UASTC_MODES)
+ {
+ mode_hist[best_mode]++;
+ overall_mode_hist[best_mode]++;
+ }
+
+ if (basist::g_uastc_mode_has_etc1_bias[best_mode])
+ etc1_hint_hist[unpacked_uastc_blk.m_etc1_bias]++;
+
+ total_bc1_hint0s += unpacked_uastc_blk.m_bc1_hint0;
+ total_bc1_hint1s += unpacked_uastc_blk.m_bc1_hint1;
+ total_bc1_hint01s += (unpacked_uastc_blk.m_bc1_hint0 || unpacked_uastc_blk.m_bc1_hint1);
+
+ etc1_inten_hist[unpacked_uastc_blk.m_etc1_inten0]++;
+ etc1_inten_hist[unpacked_uastc_blk.m_etc1_inten1]++;
+
+ etc1_flip_hist[unpacked_uastc_blk.m_etc1_flip]++;
+ etc1_diff_hist[unpacked_uastc_blk.m_etc1_diff]++;
+ }
+
+ // Transcode to BC1
+ color_rgba tblock_bc1[4][4];
+
+ uint8_t tbc1_block[8];
+ transcode_uastc_to_bc1(encoded_uastc_blk, tbc1_block, false);
+ unpack_block(texture_format::cBC1, tbc1_block, &tblock_bc1[0][0]);
+ bc1_img.set_block_clipped(&tblock_bc1[0][0], block_x * 4, block_y * 4, 4, 4);
+
+ // Transcode to BC7
+ basist::bc7_optimization_results best_bc7_results;
+ transcode_uastc_to_bc7(unpacked_uastc_blk, best_bc7_results);
+
+ {
+ basist::bc7_block bc7_data;
+ encode_bc7_block(&bc7_data, &best_bc7_results);
+
+ color_rgba decoded_bc7_blk[4][4];
+ unpack_block(texture_format::cBC7, &bc7_data, &decoded_bc7_blk[0][0]);
+
+ bc7_img.set_block_clipped(&decoded_bc7_blk[0][0], block_x * 4, block_y * 4, 4, 4);
+
+ // Compute partition visualization image
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ uint32_t part = 0;
+ switch (best_bc7_results.m_mode)
+ {
+ case 1:
+ case 3:
+ case 7:
+ part = basist::g_bc7_partition2[best_bc7_results.m_partition * 16 + x + y * 4];
+ break;
+ case 0:
+ case 2:
+ part = basist::g_bc7_partition3[best_bc7_results.m_partition * 16 + x + y * 4];
+ break;
+ }
+
+ color_rgba c(0, 255, 0, 255);
+ if (part == 1)
+ c.set(255, 0, 0, 255);
+ else if (part == 2)
+ c.set(0, 0, 255, 255);
+
+ part_img.set_clipped(block_x * 4 + x, block_y * 4 + y, c);
+ }
+ }
+ }
+
+ bool high_quality = false;
+
+ // Transcode UASTC->BC3
+ uint8_t ublock_bc3[16];
+ transcode_uastc_to_bc3(encoded_uastc_blk, ublock_bc3, high_quality);
+ color_rgba ublock_bc3_unpacked[4][4];
+ unpack_block(texture_format::cBC3, &ublock_bc3, &ublock_bc3_unpacked[0][0]);
+ bc3_img.set_block_clipped(&ublock_bc3_unpacked[0][0], block_x * 4, block_y * 4, 4, 4);
+
+ // Transcode UASTC->R11
+ uint8_t ublock_eac_r11[8];
+ transcode_uastc_to_etc2_eac_r11(encoded_uastc_blk, ublock_eac_r11, high_quality, 0);
+ color_rgba ublock_eac_r11_unpacked[4][4];
+ for (uint32_t y = 0; y < 4; y++)
+ for (uint32_t x = 0; x < 4; x++)
+ ublock_eac_r11_unpacked[y][x].set(0, 0, 0, 255);
+ unpack_block(texture_format::cETC2_R11_EAC, &ublock_eac_r11, &ublock_eac_r11_unpacked[0][0]);
+ eac_r11_img.set_block_clipped(&ublock_eac_r11_unpacked[0][0], block_x * 4, block_y * 4, 4, 4);
+
+ // Transcode UASTC->RG11
+ uint8_t ublock_eac_rg11[16];
+ transcode_uastc_to_etc2_eac_rg11(encoded_uastc_blk, ublock_eac_rg11, high_quality, 0, 1);
+ color_rgba ublock_eac_rg11_unpacked[4][4];
+ for (uint32_t y = 0; y < 4; y++)
+ for (uint32_t x = 0; x < 4; x++)
+ ublock_eac_rg11_unpacked[y][x].set(0, 0, 0, 255);
+ unpack_block(texture_format::cETC2_RG11_EAC, &ublock_eac_rg11, &ublock_eac_rg11_unpacked[0][0]);
+ eac_rg11_img.set_block_clipped(&ublock_eac_rg11_unpacked[0][0], block_x * 4, block_y * 4, 4, 4);
+
+ // ETC1
+ etc_block unpacked_etc1;
+ transcode_uastc_to_etc1(encoded_uastc_blk, &unpacked_etc1);
+ color_rgba unpacked_etc1_block[16];
+ unpack_etc1(unpacked_etc1, unpacked_etc1_block);
+ etc1_img.set_block_clipped(unpacked_etc1_block, block_x * 4, block_y * 4, 4, 4);
+
+ // ETC1 Y
+ etc_block unpacked_etc1_g;
+
+ transcode_uastc_to_etc1(encoded_uastc_blk, &unpacked_etc1_g, 1);
+
+ color_rgba unpacked_etc1_g_block[16];
+ unpack_etc1(unpacked_etc1_g, unpacked_etc1_g_block);
+ etc1_g_img.set_block_clipped(unpacked_etc1_g_block, block_x * 4, block_y * 4, 4, 4);
+
+ // ETC2
+ etc2_rgba_block unpacked_etc2;
+ transcode_uastc_to_etc2_rgba(encoded_uastc_blk, &unpacked_etc2);
+
+ color_rgba unpacked_etc2_block[16];
+ unpack_block(texture_format::cETC2_RGBA, &unpacked_etc2, unpacked_etc2_block);
+ etc2_img.set_block_clipped(unpacked_etc2_block, block_x * 4, block_y * 4, 4, 4);
+
+ // UASTC->ASTC
+ uint32_t tastc_data[4];
+
+ transcode_uastc_to_astc(encoded_uastc_blk, tastc_data);
+
+ color_rgba decoded_tastc_block[4][4];
+ basisu_astc::astc::decompress((uint8_t*)decoded_tastc_block, (uint8_t*)&tastc_data, false, 4, 4);
+
+ uastc2_img.set_block_clipped(&decoded_tastc_block[0][0], block_x * 4, block_y * 4, 4, 4);
+
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ if (decoded_tastc_block[y][x] != unpacked_uastc_block_pixels[y][x])
+ {
+ printf("UASTC!=ASTC!\n");
+ }
+ }
+ }
+
+ } // block_index
+
+ });
+
+ } // block_index_iter
+
+ jpool.wait_for_all();
+
+ {
+ size_t comp_size = 0;
+ void* pComp_data = tdefl_compress_mem_to_heap(&ublocks[0], ublocks.size() * 16, &comp_size, TDEFL_MAX_PROBES_MASK);// TDEFL_DEFAULT_MAX_PROBES);
+
+ size_t decomp_size;
+ void* pDecomp_data = tinfl_decompress_mem_to_heap(pComp_data, comp_size, &decomp_size, 0);
+
+ if ((decomp_size != ublocks.size() * 16) || (memcmp(pDecomp_data, &ublocks[0], decomp_size) != 0))
+ {
+ printf("Compression or decompression failed!\n");
+ exit(1);
+ }
+
+ mz_free(pComp_data);
+ mz_free(pDecomp_data);
+
+ printf("Pre-RDO UASTC size: %u, compressed size: %u, %3.2f bits/texel\n",
+ (uint32_t)ublocks.size() * 16,
+ (uint32_t)comp_size,
+ comp_size * 8.0f / img.get_total_pixels());
+
+ total_comp_size += comp_size;
+ total_raw_size += ublocks.size() * 16;
+ }
+
+ std::vector orig_block_pixels(ublocks.size() * 16);
+ for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
+ for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
+ img.extract_block_clamped(&orig_block_pixels[(block_x + block_y * num_blocks_x) * 16], block_x * 4, block_y * 4, 4, 4);
+
+ // HACK HACK
+ const uint32_t max_rdo_jobs = 4;
+
+ char rdo_fname[256];
+ FILE* pFile = nullptr;
+ for (uint32_t try_index = 0; try_index < 100; try_index++)
+ {
+ sprintf(rdo_fname, "rdo_%02u_%u.csv", image_index, try_index);
+ pFile = fopen(rdo_fname, "rb");
+ if (pFile)
+ {
+ fclose(pFile);
+ continue;
+ }
+
+ pFile = fopen(rdo_fname, "w");
+ if (!pFile)
+ printf("Cannot open CSV file %s\n", rdo_fname);
+ else
+ {
+ printf("Opened CSV file %s\n", rdo_fname);
+ break;
+ }
+ }
+
+ for (float q = .2f; q <= 3.0f; q += (q >= 1.0f ? .5f : .1f))
+ {
+ printf("Q: %f\n", q);
+
+ uastc_rdo_params p;
+ p.m_quality_scaler = q;
+ p.m_max_allowed_rms_increase_ratio = 10.0f;
+ p.m_skip_block_rms_thresh = 8.0f;
+
+ bool rdo_status = uastc_rdo((uint32_t)ublocks.size(), &ublocks[0], &orig_block_pixels[0], p, flags, &jpool, max_rdo_jobs);
+ if (!rdo_status)
+ {
+ printf("uastc_rdo() failed!\n");
+ return false;
+ }
+ for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
+ {
+ for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
+ {
+ const basist::uastc_block& blk = ublocks[block_x + block_y * num_blocks_x];
+
+ color_rgba unpacked_block[4][4];
+ if (!basist::unpack_uastc(blk, (basist::color32*)unpacked_block, false))
+ {
+ printf("Block unpack failed!\n");
+ exit(1);
+ }
+
+ rdo_uastc_img.set_block_clipped(&unpacked_block[0][0], block_x * 4, block_y * 4, 4, 4);
+
+ basist::bc7_optimization_results best_bc7_results;
+ transcode_uastc_to_bc7(blk, best_bc7_results);
+
+ basist::bc7_block bc7_data;
+ encode_bc7_block(&bc7_data, &best_bc7_results);
+
+ color_rgba decoded_bc7_blk[4][4];
+ unpack_block(texture_format::cBC7, &bc7_data, &decoded_bc7_blk[0][0]);
+
+ rdo_bc7_img.set_block_clipped(&decoded_bc7_blk[0][0], block_x * 4, block_y * 4, 4, 4);
+ }
+ }
+
+ image_metrics em;
+ em.calc(img, rdo_uastc_img, 0, 3);
+ em.print("RDOUASTC RGB ");
+
+ size_t comp_size = 0;
+ void* pComp_data = tdefl_compress_mem_to_heap(&ublocks[0], ublocks.size() * 16, &comp_size, TDEFL_MAX_PROBES_MASK);// TDEFL_DEFAULT_MAX_PROBES);
+
+ size_t decomp_size;
+ void* pDecomp_data = tinfl_decompress_mem_to_heap(pComp_data, comp_size, &decomp_size, 0);
+
+ if ((decomp_size != ublocks.size() * 16) || (memcmp(pDecomp_data, &ublocks[0], decomp_size) != 0))
+ {
+ printf("Compression or decompression failed!\n");
+ exit(1);
+ }
+
+ mz_free(pComp_data);
+ mz_free(pDecomp_data);
+
+ printf("RDO UASTC size: %u, compressed size: %u, %3.2f bits/texel\n",
+ (uint32_t)ublocks.size() * 16,
+ (uint32_t)comp_size,
+ comp_size * 8.0f / img.get_total_pixels());
+
+ if (pFile)
+ fprintf(pFile, "%f, %f, %f\n", q, comp_size * 8.0f / img.get_total_pixels(), em.m_psnr);
+ }
+ if (pFile)
+ fclose(pFile);
+
+ {
+ size_t comp_size = 0;
+ void* pComp_data = tdefl_compress_mem_to_heap(&ublocks[0], ublocks.size() * 16, &comp_size, TDEFL_MAX_PROBES_MASK);// TDEFL_DEFAULT_MAX_PROBES);
+
+ size_t decomp_size;
+ void* pDecomp_data = tinfl_decompress_mem_to_heap(pComp_data, comp_size, &decomp_size, 0);
+
+ if ((decomp_size != ublocks.size() * 16) || (memcmp(pDecomp_data, &ublocks[0], decomp_size) != 0))
+ {
+ printf("Compression or decompression failed!\n");
+ exit(1);
+ }
+
+ mz_free(pComp_data);
+ mz_free(pDecomp_data);
+
+ printf("RDO UASTC size: %u, compressed size: %u, %3.2f bits/texel\n",
+ (uint32_t)ublocks.size() * 16,
+ (uint32_t)comp_size,
+ comp_size * 8.0f / img.get_total_pixels());
+
+ total_rdo_comp_size += comp_size;
+ total_rdo_raw_size += ublocks.size() * 16;
+ total_comp_blocks += ublocks.size();
+ }
+
+ printf("Total blocks: %u\n", total_blocks);
+ printf("Total BC1 hint 0's: %u %3.1f%%\n", total_bc1_hint0s, total_bc1_hint0s * 100.0f / total_blocks);
+ printf("Total BC1 hint 1's: %u %3.1f%%\n", total_bc1_hint1s, total_bc1_hint1s * 100.0f / total_blocks);
+ printf("Total BC1 hint 01's: %u %3.1f%%\n", total_bc1_hint01s, total_bc1_hint01s * 100.0f / total_blocks);
+ printf("Total enc time per block: %f us\n", total_enc_time / total_blocks * 1000000.0f);
+ printf("Total bench time per block: %f us\n", total_bench_time / total_blocks * 1000000.0f);
+ printf("Total bench2 time per block: %f us\n", total_bench2_time / total_blocks * 1000000.0f);
+
+ overall_total_enc_time += total_enc_time;
+ overall_total_bench_time += total_bench_time;
+ overall_total_bench2_time += total_bench2_time;
+ overall_blocks += total_blocks;
+
+ printf("ETC1 inten hist: %u %u %u %u %u %u %u %u\n", etc1_inten_hist[0], etc1_inten_hist[1], etc1_inten_hist[2], etc1_inten_hist[3],
+ etc1_inten_hist[4], etc1_inten_hist[5], etc1_inten_hist[6], etc1_inten_hist[7]);
+ printf("ETC1 flip hist: %u %u\n", etc1_flip_hist[0], etc1_flip_hist[1]);
+ printf("ETC1 diff hist: %u %u\n", etc1_diff_hist[0], etc1_diff_hist[1]);
+
+ printf("UASTC mode histogram:\n");
+ uint64_t total_hist = 0;
+ for (uint32_t i = 0; i < basist::TOTAL_UASTC_MODES; i++)
+ total_hist += mode_hist[i];
+ for (uint32_t i = 0; i < basist::TOTAL_UASTC_MODES; i++)
+ printf("%u: %u %3.2f%%\n", i, (uint32_t)mode_hist[i], mode_hist[i] * 100.0f / total_hist);
+
+ char fn[256];
+
+#if 0
+ for (uint32_t y = 0; y < img.get_height(); y++)
+ for (uint32_t x = 0; x < img.get_width(); x++)
+ {
+ //static inline uint8_t to_5(uint32_t v) { ; }
+ color_rgba &c = img(x, y);
+
+ for (uint32_t i = 0; i < 3; i++)
+ {
+ const uint32_t limit = (i == 1) ? 63 : 31;
+
+ uint32_t v = c[i];
+ v = v * limit + 128; v = (uint8_t)((v + (v >> 8)) >> 8);
+ v = (v * 255 + (limit / 2)) / limit;
+
+ c[i] = (uint8_t)v;
+ }
+
+ }
+#endif
+
+ sprintf(fn, "orig_%02u.png", image_index);
+ save_png(fn, img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "orig_a_%02u.png", image_index);
+ save_png(fn, img, cImageSaveGrayscale, 3);
+
+ sprintf(fn, "unpacked_uastc_%02u.png", image_index);
+ save_png(fn, uastc_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_uastc_a_%02u.png", image_index);
+ save_png(fn, uastc_img, cImageSaveGrayscale, 3);
+
+ sprintf(fn, "unpacked_rdo_uastc_%02u.png", image_index);
+ save_png(fn, rdo_uastc_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_rdo_uastc_a_%02u.png", image_index);
+ save_png(fn, rdo_uastc_img, cImageSaveGrayscale, 3);
+
+ sprintf(fn, "unpacked_uastc2_%02u.png", image_index);
+ save_png(fn, uastc2_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_bc7_%02u.png", image_index);
+ save_png(fn, bc7_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_bc7_a_%02u.png", image_index);
+ save_png(fn, bc7_img, cImageSaveGrayscale, 3);
+
+ sprintf(fn, "unpacked_rdo_bc7_%02u.png", image_index);
+ save_png(fn, rdo_bc7_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_rdo_bc7_a_%02u.png", image_index);
+ save_png(fn, rdo_bc7_img, cImageSaveGrayscale, 3);
+
+ sprintf(fn, "unpacked_opt_bc7_%02u.png", image_index);
+ save_png(fn, opt_bc7_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_opt_bc7_a_%02u.png", image_index);
+ save_png(fn, opt_bc7_img, cImageSaveGrayscale, 3);
+
+ sprintf(fn, "unpacked_opt_astc_%02u.png", image_index);
+ save_png(fn, opt_astc_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_opt_astc_a_%02u.png", image_index);
+ save_png(fn, opt_astc_img, cImageSaveGrayscale, 3);
+
+ sprintf(fn, "unpacked_bc7enc_%02u.png", image_index);
+ save_png(fn, bc7enc_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_bc7enc_a_%02u.png", image_index);
+ save_png(fn, bc7enc_img, cImageSaveGrayscale, 3);
+
+ sprintf(fn, "unpacked_opt_bc1_%02u.png", image_index);
+ save_png(fn, opt_bc1_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_opt_bc1_2_%02u.png", image_index);
+ save_png(fn, opt_bc1_2_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_tbc1_%02u.png", image_index);
+ save_png(fn, bc1_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_bc3_%02u.png", image_index);
+ save_png(fn, bc3_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_eac_r11_%02u.png", image_index);
+ save_png(fn, eac_r11_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_eac_rg11_%02u.png", image_index);
+ save_png(fn, eac_rg11_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_bc3_a_%02u.png", image_index);
+ save_png(fn, bc3_img, cImageSaveGrayscale, 3);
+
+ sprintf(fn, "part_vis_%02u.png", image_index);
+ save_png(fn, part_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_etc1_%02u.png", image_index);
+ save_png(fn, etc1_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_etc1_g_%02u.png", image_index);
+ save_png(fn, etc1_g_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_etc2_%02u.png", image_index);
+ save_png(fn, etc2_img, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_etc2_a_%02u.png", image_index);
+ save_png(fn, etc2_img, cImageSaveGrayscale, 3);
+
+ image_metrics em;
+
+ // UASTC
+ em.calc(img, uastc_img, 0, 3);
+ em.print("UASTC RGB ");
+ total_uastc_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, uastc_img, 3, 1);
+ em.print("UASTC A ");
+ if (img_has_alpha)
+ total_uastc_a_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, uastc_img, 0, 4);
+ em.print("UASTC RGBA ");
+ total_uastc_rgba_psnr += std::min(99.0f, em.m_psnr);
+
+ // RDO UASTC
+ em.calc(img, rdo_uastc_img, 0, 3);
+ em.print("RDOUASTC RGB ");
+ total_rdo_uastc_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, rdo_uastc_img, 3, 1);
+ em.print("RDOUASTC A ");
+ if (img_has_alpha)
+ total_rdo_uastc_a_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, rdo_uastc_img, 0, 4);
+ em.print("RDOUASTC RGBA ");
+ total_rdo_uastc_rgba_psnr += std::min(99.0f, em.m_psnr);
+
+ // UASTC2
+ em.calc(img, uastc2_img, 0, 3);
+ em.print("UASTC2 RGB ");
+ total_uastc2_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, uastc2_img, 3, 1);
+ em.print("UASTC2 A ");
+ if (img_has_alpha)
+ total_uastc2_a_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, uastc2_img, 0, 4);
+ em.print("UASTC2 RGBA ");
+ total_uastc2_rgba_psnr += std::min(99.0f, em.m_psnr);
+
+ // BC7
+ em.calc(img, bc7_img, 0, 3);
+ em.print("BC7 RGB ");
+ total_bc7_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, bc7_img, 3, 1);
+ em.print("BC7 A ");
+ if (img_has_alpha)
+ total_bc7_a_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, bc7_img, 0, 4);
+ em.print("BC7 RGBA ");
+ total_bc7_rgba_psnr += std::min(99.0f, em.m_psnr);
+
+ // RDO BC7
+ em.calc(img, rdo_bc7_img, 0, 3);
+ em.print("RDOBC7 RGB ");
+ total_rdo_bc7_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, rdo_bc7_img, 3, 1);
+ em.print("RDOBC7 A ");
+ if (img_has_alpha)
+ total_rdo_bc7_a_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, rdo_bc7_img, 0, 4);
+ em.print("RDOBC7 RGBA ");
+ total_rdo_bc7_rgba_psnr += std::min(99.0f, em.m_psnr);
+
+#if 0
+ // OBC7
+ em.calc(img, opt_bc7_img, 0, 3);
+ em.print("OBC7 RGB ");
+ total_obc7_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, opt_bc7_img, 3, 1);
+ em.print("OBC7 A ");
+ if (img_has_alpha)
+ total_obc7_a_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, opt_bc7_img, 0, 4);
+ em.print("OBC7 RGBA ");
+ total_obc7_rgba_psnr += std::min(99.0f, em.m_psnr);
+
+ // OASTC
+ em.calc(img, opt_astc_img, 0, 3);
+ em.print("OASTC RGB ");
+ total_oastc_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, opt_astc_img, 3, 1);
+ em.print("OASTC A ");
+ if (img_has_alpha)
+ total_oastc_a_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, opt_astc_img, 0, 4);
+ em.print("OASTC RGBA ");
+ total_oastc_rgba_psnr += std::min(99.0f, em.m_psnr);
+#endif
+
+ // bc7enc
+ em.calc(img, bc7enc_img, 0, 3);
+ em.print("BC7ENC RGB ");
+ total_bc7enc_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, bc7enc_img, 3, 1);
+ em.print("BC7ENC A ");
+ if (img_has_alpha)
+ total_bc7enc_a_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, bc7enc_img, 0, 4);
+ em.print("BC7ENC RGBA ");
+ total_bc7enc_rgba_psnr += std::min(99.0f, em.m_psnr);
+
+#if 1
+ // OBC1
+ em.calc(img, opt_bc1_img, 0, 3);
+ em.print("OBC1 RGB ");
+ total_obc1_psnr += std::min(99.0f, em.m_psnr);
+ total_obc1_psnr_sq += std::min(99.0f, em.m_psnr) * std::min(99.0f, em.m_psnr);
+#endif
+
+ em.calc(img, opt_bc1_2_img, 0, 3);
+ em.print("OBC1 2 RGB ");
+ total_obc1_2_psnr += std::min(99.0f, em.m_psnr);
+ total_obc1_2_psnr_sq += std::min(99.0f, em.m_psnr) * std::min(99.0f, em.m_psnr);
+
+ em.calc(img, bc1_img, 0, 3);
+ em.print("BC1 RGB ");
+ total_bc1_psnr += std::min(99.0f, em.m_psnr);
+ total_bc1_psnr_sq += std::min(99.0f, em.m_psnr) * std::min(99.0f, em.m_psnr);
+
+ // ETC1
+ em.calc(img, etc1_img, 0, 3);
+ em.print("ETC1 RGB ");
+ total_etc1_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, etc1_img, 0, 0);
+ em.print("ETC1 Y ");
+ total_etc1_y_psnr += std::min(99.0f, em.m_psnr);
+
+ // ETC1
+ em.calc(img, etc1_g_img, 1, 1);
+ em.print("ETC1 G ");
+ total_etc1_g_psnr += std::min(99.0f, em.m_psnr);
+
+ // ETC2
+ em.calc(img, etc2_img, 0, 3);
+ em.print("ETC2 RGB ");
+ total_etc2_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, etc2_img, 3, 1);
+ em.print("ETC2 A ");
+ if (img_has_alpha)
+ total_etc2_a_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, etc2_img, 0, 4);
+ em.print("ETC2 RGBA ");
+ total_etc2_rgba_psnr += std::min(99.0f, em.m_psnr);
+
+ // BC3
+ em.calc(img, bc3_img, 0, 3);
+ em.print("BC3 RGB ");
+ total_bc3_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, bc3_img, 3, 1);
+ em.print("BC3 A ");
+ if (img_has_alpha)
+ total_bc3_a_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, bc3_img, 0, 4);
+ em.print("BC3 RGBA ");
+ total_bc3_rgba_psnr += std::min(99.0f, em.m_psnr);
+
+ // EAC R11
+ em.calc(img, eac_r11_img, 0, 1);
+ em.print("EAC R11 ");
+ total_eac_r11_psnr += std::min(99.0f, em.m_psnr);
+
+ // EAC RG11
+ em.calc(img, eac_rg11_img, 0, 2);
+ em.print("EAC RG11 ");
+ total_eac_rg11_psnr += std::min(99.0f, em.m_psnr);
+
+ const uint32_t width = num_blocks_x * 4;
+ const uint32_t height = num_blocks_y * 4;
+ if (is_pow2(width) && is_pow2(height))
+ {
+ pvrtc4_image pi(width, height);
+
+ transcode_uastc_to_pvrtc1_4_rgba(&ublocks[0], pi.get_blocks().get_ptr(), num_blocks_x, num_blocks_y, false);
+
+ pi.deswizzle();
+
+ //pi.map_all_pixels(img, perceptual, false);
+
+ image pi_unpacked;
+ pi.unpack_all_pixels(pi_unpacked);
+
+#if 0
+ sprintf(fn, "unpacked_pvrtc1_rgb_before_%02u.png", image_index);
+ save_png(fn, pi_unpacked, cImageSaveIgnoreAlpha);
+
+ em.calc(img, pi_unpacked, 0, 3);
+ em.print("PVRTC1 RGB Before ");
+
+ for (uint32_t pass = 0; pass < 1; pass++)
+ {
+ for (uint32_t by = 0; by < num_blocks_y; by++)
+ {
+ for (uint32_t bx = 0; bx < num_blocks_x; bx++)
+ {
+ pi.local_endpoint_optimization_opaque(bx, by, img, perceptual, false);
+ }
+ }
+ }
+
+ //pi.map_all_pixels(img, perceptual, false);
+
+ pi.unpack_all_pixels(pi_unpacked);
+#endif
+
+ sprintf(fn, "unpacked_pvrtc1_%02u.png", image_index);
+ save_png(fn, pi_unpacked, cImageSaveIgnoreAlpha);
+
+ sprintf(fn, "unpacked_pvrtc1_a_%02u.png", image_index);
+ save_png(fn, pi_unpacked, cImageSaveGrayscale, 3);
+
+ em.calc(img, pi_unpacked, 0, 3);
+ em.print("PVRTC1 After RGB ");
+ total_pvrtc1_rgb_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, pi_unpacked, 3, 1);
+ em.print("PVRTC1 After A ");
+ total_pvrtc1_a_psnr += std::min(99.0f, em.m_psnr);
+
+ em.calc(img, pi_unpacked, 0, 4);
+ em.print("PVRTC1 After RGBA ");
+ total_pvrtc1_rgba_psnr += std::min(99.0f, em.m_psnr);
+
+ total_pvrtc1_images++;
+ }
+
+ printf("ETC1 hint histogram:\n");
+ for (uint32_t i = 0; i < 32; i++)
+ printf("%u ", etc1_hint_hist[i]);
+ printf("\n");
+
+ total_images++;
+ if (img_has_alpha)
+ total_a_images++;
+
+ } // image_index
+
+ printf("Total time: %f secs\n", otm.get_elapsed_secs());
+
+ printf("Total Non-RDO UASTC size: %llu, compressed size: %llu, %3.2f bits/texel\n",
+ (unsigned long long)total_raw_size,
+ (unsigned long long)total_comp_size,
+ total_comp_size * 8.0f / (total_comp_blocks * 16));
+
+ printf("Total RDO UASTC size: %llu, compressed size: %llu, %3.2f bits/texel\n",
+ (unsigned long long)total_rdo_raw_size,
+ (unsigned long long)total_rdo_comp_size,
+ total_rdo_comp_size * 8.0f / (total_comp_blocks * 16));
+
+ printf("Overall enc time per block: %f us\n", overall_total_enc_time / overall_blocks * 1000000.0f);
+ printf("Overall bench time per block: %f us\n", overall_total_bench_time / overall_blocks * 1000000.0f);
+ printf("Overall bench2 time per block: %f us\n", overall_total_bench2_time / overall_blocks * 1000000.0f);
+
+ printf("Overall ASTC mode histogram:\n");
+ uint64_t total_hist = 0;
+ for (uint32_t i = 0; i < basist::TOTAL_UASTC_MODES; i++)
+ total_hist += overall_mode_hist[i];
+
+ for (uint32_t i = 0; i < basist::TOTAL_UASTC_MODES; i++)
+ printf("%u: %u %3.2f%%\n", i, (uint32_t)overall_mode_hist[i], overall_mode_hist[i] * 100.0f / total_hist);
+
+ printf("Total images: %u, total images with alpha: %u, total PVRTC1 images: %u\n", total_images, total_a_images, total_pvrtc1_images);
+
+ if (!total_a_images)
+ total_a_images = 1;
+
+ printf("Avg UASTC RGB PSNR: %f, A PSNR: %f, RGBA PSNR: %f\n", total_uastc_psnr / total_images, total_uastc_a_psnr / total_a_images, total_uastc_rgba_psnr / total_images);
+ printf("Avg UASTC2 RGB PSNR: %f, A PSNR: %f, RGBA PSNR: %f\n", total_uastc2_psnr / total_images, total_uastc2_a_psnr / total_a_images, total_uastc2_rgba_psnr / total_images);
+ printf("Avg RDO UASTC RGB PSNR: %f, A PSNR: %f, RGBA PSNR: %f\n", total_rdo_uastc_psnr / total_images, total_rdo_uastc_a_psnr / total_a_images, total_rdo_uastc_rgba_psnr / total_images);
+ printf("Avg BC7 RGB PSNR: %f, A PSNR: %f, RGBA PSNR: %f\n", total_bc7_psnr / total_images, total_bc7_a_psnr / total_a_images, total_bc7_rgba_psnr / total_images);
+ printf("Avg RDO BC7 RGB PSNR: %f, A PSNR: %f, RGBA PSNR: %f\n", total_rdo_bc7_psnr / total_images, total_rdo_bc7_a_psnr / total_a_images, total_rdo_bc7_rgba_psnr / total_images);
+ //printf("Avg Opt BC7 RGB PSNR: %f, A PSNR: %f, RGBA PSNR: %f\n", total_obc7_psnr / total_images, total_obc7_a_psnr / total_a_images, total_obc7_rgba_psnr / total_images);
+ //printf("Avg Opt ASTC RGB PSNR: %f, A PSNR: %f, RGBA PSNR: %f\n", total_oastc_psnr / total_images, total_oastc_a_psnr / total_a_images, total_oastc_rgba_psnr / total_images);
+ printf("Avg BC7ENC RGB PSNR: %f, A PSNR: %f, RGBA PSNR: %f\n", total_bc7enc_psnr / total_images, total_bc7enc_a_psnr / total_a_images, total_bc7enc_rgba_psnr / total_images);
+
+ printf("Avg Opt BC1 PSNR: %f, std dev: %f\n", total_obc1_psnr / total_images, sqrtf(std::max(0.0f, (total_obc1_psnr_sq / total_images) - (total_obc1_psnr / total_images) * (total_obc1_psnr / total_images))));
+
+ printf("Avg Opt BC1 2 PSNR: %f, std dev: %f\n", total_obc1_2_psnr / total_images, sqrtf(std::max(0.0f, (total_obc1_2_psnr_sq / total_images) - (total_obc1_2_psnr / total_images) * (total_obc1_2_psnr / total_images))));
+
+ printf("Avg BC1 PSNR: %f, std dev: %f\n", total_bc1_psnr / total_images, sqrtf(std::max(0.0f, (total_bc1_psnr_sq / total_images) - (total_bc1_psnr / total_images) * (total_bc1_psnr / total_images))));
+
+ printf("Avg ETC1 RGB PSNR: %f\n", total_etc1_psnr / total_images);
+ printf("Avg ETC1 Y PSNR: %f\n", total_etc1_y_psnr / total_images);
+ printf("Avg ETC1 G PSNR: %f\n", total_etc1_g_psnr / total_images);
+
+ printf("Avg ETC2 RGB PSNR: %f\n", total_etc2_psnr / total_images);
+ printf("Avg ETC2 A PSNR: %f\n", total_etc2_a_psnr / total_a_images);
+ printf("Avg ETC2 RGBA PSNR: %f\n", total_etc2_rgba_psnr / total_images);
+
+ printf("Avg BC3 RGB PSNR: %f\n", total_bc3_psnr / total_images);
+ printf("Avg BC3 A PSNR: %f\n", total_bc3_a_psnr / total_a_images);
+ printf("Avg BC3 RGBA PSNR: %f\n", total_bc3_rgba_psnr / total_images);
+
+ printf("Avg EAC R11 PSNR: %f\n", total_eac_r11_psnr / total_images);
+ printf("Avg EAC RG11 PSNR: %f\n", total_eac_rg11_psnr / total_images);
+
+ if (total_pvrtc1_images)
+ {
+ printf("Avg PVRTC1 RGB PSNR: %f\n", total_pvrtc1_rgb_psnr / total_pvrtc1_images);
+ printf("Avg PVRTC1 A PSNR: %f\n", total_pvrtc1_a_psnr / total_pvrtc1_images);
+ printf("Avg PVRTC1 RGBA PSNR: %f\n", total_pvrtc1_rgba_psnr / total_pvrtc1_images);
+ }
+
+ return true;
+}
+
static int main_internal(int argc, const char **argv)
{
- printf("Basis Universal GPU Texture Compressor Reference Encoder v" BASISU_TOOL_VERSION ", Copyright (C) 2019 Binomial LLC, All rights reserved\n");
+ printf("Basis Universal GPU Texture Compressor Reference Encoder v" BASISU_TOOL_VERSION "\nCopyright (C) 2019-2020 Binomial LLC, All rights reserved\n");
//interval_timer tm;
//tm.start();
@@ -1474,7 +2725,7 @@ static int main_internal(int argc, const char **argv)
command_line_params opts;
if (!opts.parse(argc, argv))
{
- print_usage();
+ //print_usage();
return EXIT_FAILURE;
}
@@ -1504,10 +2755,9 @@ static int main_internal(int argc, const char **argv)
status = compress_mode(opts);
break;
case cValidate:
- status = unpack_and_validate_mode(opts, true);
- break;
+ case cInfo:
case cUnpack:
- status = unpack_and_validate_mode(opts, false);
+ status = unpack_and_validate_mode(opts);
break;
case cCompare:
status = compare_mode(opts);
@@ -1515,6 +2765,9 @@ static int main_internal(int argc, const char **argv)
case cVersion:
status = true; // We printed the version at the beginning of main_internal
break;
+ case cBench:
+ status = bench_mode(opts);
+ break;
default:
assert(0);
break;
@@ -1523,8 +2776,12 @@ static int main_internal(int argc, const char **argv)
return status ? EXIT_SUCCESS : EXIT_FAILURE;
}
-int main(int argc, const char **argv)
+int main(int argc, const char** argv)
{
+#ifdef _DEBUG
+ printf("DEBUG\n");
+#endif
+
int status = EXIT_FAILURE;
#if BASISU_CATCH_EXCEPTIONS
diff --git a/lodepng.cpp b/lodepng.cpp
index cf964d0..60ad5ff 100644
--- a/lodepng.cpp
+++ b/lodepng.cpp
@@ -29,6 +29,7 @@ Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for
*/
#ifdef _MSC_VER
+#define _CRT_SECURE_NO_DEPRECATE
#pragma warning (disable : 4201)
#ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
diff --git a/transcoder/basisu.h b/transcoder/basisu.h
index 25600a6..a62854e 100644
--- a/transcoder/basisu.h
+++ b/transcoder/basisu.h
@@ -1,5 +1,5 @@
// basisu.h
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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");
@@ -79,9 +79,9 @@
#define strcasecmp _stricmp
#endif
-// Set to one to enable debug printf()'s when any errors occur, for development/debugging.
-#ifndef BASISU_DEVEL_MESSAGES
-#define BASISU_DEVEL_MESSAGES 0
+// Set to one to enable debug printf()'s when any errors occur, for development/debugging. Especially useful for WebGL development.
+#ifndef BASISU_FORCE_DEVEL_MESSAGES
+#define BASISU_FORCE_DEVEL_MESSAGES 0
#endif
#define BASISU_NOTE_UNUSED(x) (void)(x)
@@ -92,7 +92,7 @@
#define BASISU_STRINGIZE(x) #x
#define BASISU_STRINGIZE2(x) BASISU_STRINGIZE(x)
-#if BASISU_DEVEL_MESSAGES
+#if BASISU_FORCE_DEVEL_MESSAGES
#define BASISU_DEVEL_ERROR(...) do { basisu::debug_printf(__VA_ARGS__); } while(0)
#else
#define BASISU_DEVEL_ERROR(...)
@@ -125,9 +125,25 @@ namespace basisu
template inline S maximum(S a, S b) { return (a > b) ? a : b; }
template inline S maximum(S a, S b, S c) { return maximum(maximum(a, b), c); }
+ template inline S maximum(S a, S b, S c, S d) { return maximum(maximum(maximum(a, b), c), d); }
template inline S minimum(S a, S b) { return (a < b) ? a : b; }
template inline S minimum(S a, S b, S c) { return minimum(minimum(a, b), c); }
+ template inline S minimum(S a, S b, S c, S d) { return minimum(minimum(minimum(a, b), c), d); }
+
+ inline float clampf(float value, float low, float high) { if (value < low) value = low; else if (value > high) value = high; return value; }
+ inline float saturate(float value) { return clampf(value, 0, 1.0f); }
+ inline uint8_t minimumub(uint8_t a, uint8_t b) { return (a < b) ? a : b; }
+ inline uint32_t minimumu(uint32_t a, uint32_t b) { return (a < b) ? a : b; }
+ inline int32_t minimumi(int32_t a, int32_t b) { return (a < b) ? a : b; }
+ inline float minimumf(float a, float b) { return (a < b) ? a : b; }
+ inline uint8_t maximumub(uint8_t a, uint8_t b) { return (a > b) ? a : b; }
+ inline uint32_t maximumu(uint32_t a, uint32_t b) { return (a > b) ? a : b; }
+ inline int32_t maximumi(int32_t a, int32_t b) { return (a > b) ? a : b; }
+ inline float maximumf(float a, float b) { return (a > b) ? a : b; }
+ inline int squarei(int i) { return i * i; }
+ inline float squaref(float i) { return i * i; }
+ template inline T square(T a) { return a * a; }
template inline S clamp(S value, S low, S high) { return (value < low) ? low : ((value > high) ? high : value); }
@@ -137,8 +153,6 @@ namespace basisu
template inline void clear_vector(T &vec) { vec.erase(vec.begin(), vec.end()); }
template inline typename T::value_type *enlarge_vector(T &vec, size_t n) { size_t cs = vec.size(); vec.resize(cs + n); return &vec[cs]; }
- template inline S square(S val) { return val * val; }
-
inline bool is_pow2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }
inline bool is_pow2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }
@@ -256,6 +270,7 @@ namespace basisu
inline packed_uint(const packed_uint& other) { *this = other; }
inline packed_uint& operator= (uint32_t v) { for (uint32_t i = 0; i < NumBytes; i++) m_bytes[i] = static_cast(v >> (i * 8)); return *this; }
+ inline packed_uint& operator= (const packed_uint& rhs) { memcpy(m_bytes, rhs.m_bytes, sizeof(m_bytes)); return *this; }
inline operator uint32_t() const
{
@@ -308,15 +323,15 @@ namespace basisu
// Block-based formats
cETC1, // ETC1
cETC1S, // ETC1 (subset: diff colors only, no subblocks)
- cETC2_RGB, // ETC2 color block
- cETC2_RGBA, // ETC2 alpha block followed by ETC2 color block
+ cETC2_RGB, // ETC2 color block (basisu doesn't support ETC2 planar/T/H modes - just basic ETC1)
+ cETC2_RGBA, // ETC2 EAC alpha block followed by ETC2 color block
cETC2_ALPHA, // ETC2 EAC alpha block
cBC1, // DXT1
- cBC3, // DXT5 (DXT5A block followed by a DXT1 block)
+ cBC3, // DXT5 (BC4/DXT5A block followed by a BC1/DXT1 block)
cBC4, // DXT5A
- cBC5, // 3DC/DXN (two DXT5A blocks)
+ cBC5, // 3DC/DXN (two BC4/DXT5A blocks)
cBC7,
- cASTC4x4,
+ cASTC4x4, // LDR only
cPVRTC1_4_RGB,
cPVRTC1_4_RGBA,
cATC_RGB,
@@ -325,6 +340,7 @@ namespace basisu
cPVRTC2_4_RGBA,
cETC2_R11_EAC,
cETC2_RG11_EAC,
+ cUASTC4x4,
// Uncompressed/raw pixels
cRGBA32,
diff --git a/transcoder/basisu_file_headers.h b/transcoder/basisu_file_headers.h
index c90b3f3..e682996 100644
--- a/transcoder/basisu_file_headers.h
+++ b/transcoder/basisu_file_headers.h
@@ -1,5 +1,5 @@
// basis_file_headers.h
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ namespace basist
// Slice desc header flags
enum basis_slice_desc_flags
{
- cSliceDescFlagsIsAlphaData = 1,
+ cSliceDescFlagsHasAlpha = 1,
cSliceDescFlagsFrameIsIFrame = 2 // Video only: Frame doesn't refer to previous frame (no usage of conditional replenishment pred symbols)
};
@@ -47,9 +47,9 @@ namespace basist
// File header files
enum basis_header_flags
{
- cBASISHeaderFlagETC1S = 1, // Always set for basis universal files
+ cBASISHeaderFlagETC1S = 1, // Always set for ETC1S files. Not set for UASTC files.
cBASISHeaderFlagYFlipped = 2, // Set if the texture had to be Y flipped before encoding
- cBASISHeaderFlagHasAlphaSlices = 4 // True if the odd slices contain alpha data
+ cBASISHeaderFlagHasAlphaSlices = 4 // True if any slices contain alpha (for ETC1S, if the odd slices contain alpha data)
};
// The image type field attempts to describe how to interpret the image data in a Basis file.
@@ -71,6 +71,12 @@ namespace basist
cBASISMaxUSPerFrame = 0xFFFFFF
};
+ enum class basis_tex_format
+ {
+ cETC1S = 0,
+ cUASTC4x4 = 1
+ };
+
struct basis_file_header
{
enum
@@ -91,7 +97,7 @@ namespace basist
basisu::packed_uint<3> m_total_images; // The total # of images
- basisu::packed_uint<1> m_format; // enum basist::block_format
+ basisu::packed_uint<1> m_tex_format; // enum basis_tex_format
basisu::packed_uint<2> m_flags; // enum basist::header_flags
basisu::packed_uint<1> m_tex_type; // enum basist::basis_texture_type
basisu::packed_uint<3> m_us_per_frame; // Framerate of video, in microseconds per frame
diff --git a/transcoder/basisu_global_selector_cb.h b/transcoder/basisu_global_selector_cb.h
index 695b0b3..8ab5098 100644
--- a/transcoder/basisu_global_selector_cb.h
+++ b/transcoder/basisu_global_selector_cb.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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.
diff --git a/transcoder/basisu_global_selector_palette.h b/transcoder/basisu_global_selector_palette.h
index b026054..4c94b01 100644
--- a/transcoder/basisu_global_selector_palette.h
+++ b/transcoder/basisu_global_selector_palette.h
@@ -1,5 +1,5 @@
// basisu_global_selector_palette.h
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 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.
diff --git a/transcoder/basisu_transcoder.cpp b/transcoder/basisu_transcoder.cpp
index d15b601..386f523 100644
--- a/transcoder/basisu_transcoder.cpp
+++ b/transcoder/basisu_transcoder.cpp
@@ -1,5 +1,5 @@
// basisu_transcoder.cpp
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,7 +17,19 @@
#include
#include
-// The supported .basis file header version. Keep in sync with BASIS_FILE_VERSION.
+#ifndef IS_BIG_ENDIAN
+// TODO: This doesn't work on OSX. How can this be so difficult?
+//#if defined(__BIG_ENDIAN__) || defined(_BIG_ENDIAN) || defined(BIG_ENDIAN)
+// #define IS_BIG_ENDIAN (1)
+//#else
+ #define IS_BIG_ENDIAN (0)
+//#endif
+#endif
+
+#ifndef USE_UNALIGNED_WORD_READS
+#define USE_UNALIGNED_WORD_READS (1)
+#endif
+
#define BASISD_SUPPORTED_BASIS_VERSION (0x13)
// Set to 1 for fuzz testing. This will disable all CRC16 checks on headers and compressed data.
@@ -35,21 +47,12 @@
// Disable all BC7 transcoders if necessary (useful when cross compiling to Javascript)
#if defined(BASISD_SUPPORT_BC7) && !BASISD_SUPPORT_BC7
- #ifndef BASISD_SUPPORT_BC7_MODE6_OPAQUE_ONLY
- #define BASISD_SUPPORT_BC7_MODE6_OPAQUE_ONLY 0
- #endif
#ifndef BASISD_SUPPORT_BC7_MODE5
#define BASISD_SUPPORT_BC7_MODE5 0
#endif
#endif // !BASISD_SUPPORT_BC7
-// BC7 mode 6 opaque only is the highest quality (compared to ETC1), but the tables are massive.
-// For web/mobile use you probably should disable this.
-#ifndef BASISD_SUPPORT_BC7_MODE6_OPAQUE_ONLY
-#define BASISD_SUPPORT_BC7_MODE6_OPAQUE_ONLY 1
-#endif
-
-// BC7 mode 5 supports both opaque and opaque+alpha textures, and uses substantially less memory than BC7 mode 6 and even BC1.
+// BC7 mode 5 supports both opaque and opaque+alpha textures, and uses less memory BC1.
#ifndef BASISD_SUPPORT_BC7_MODE5
#define BASISD_SUPPORT_BC7_MODE5 1
#endif
@@ -62,6 +65,11 @@
#define BASISD_SUPPORT_ETC2_EAC_A8 1
#endif
+// Set BASISD_SUPPORT_UASTC to 0 to completely disable support for transcoding UASTC files.
+#ifndef BASISD_SUPPORT_UASTC
+#define BASISD_SUPPORT_UASTC 1
+#endif
+
#ifndef BASISD_SUPPORT_ASTC
#define BASISD_SUPPORT_ASTC 1
#endif
@@ -108,7 +116,6 @@
#endif
#endif
-#define BASISD_WRITE_NEW_BC7_TABLES 0
#define BASISD_WRITE_NEW_BC7_MODE5_TABLES 0
#define BASISD_WRITE_NEW_DXT1_TABLES 0
#define BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES 0
@@ -131,7 +138,7 @@ namespace basisu
void debug_printf(const char* pFmt, ...)
{
-#if BASISU_DEVEL_MESSAGES
+#if BASISU_FORCE_DEVEL_MESSAGES
g_debug_printf = true;
#endif
if (g_debug_printf)
@@ -146,9 +153,6 @@ namespace basisu
namespace basist
{
-#if BASISD_SUPPORT_BC7_MODE6_OPAQUE_ONLY
-#include "basisu_transcoder_tables_bc7_m6.inc"
-#endif
#if BASISD_ENABLE_DEBUG_FLAGS
static uint32_t g_debug_flags = 0;
@@ -165,11 +169,23 @@ namespace basist
void set_debug_flags(uint32_t f)
{
- (void)f;
+ BASISU_NOTE_UNUSED(f);
#if BASISD_ENABLE_DEBUG_FLAGS
g_debug_flags = f;
#endif
}
+
+ inline uint16_t byteswap_uint16(uint16_t v)
+ {
+ return static_cast((v >> 8) | (v << 8));
+ }
+
+ static inline int32_t clampi(int32_t value, int32_t low, int32_t high) { if (value < low) value = low; else if (value > high) value = high; return value; }
+ static inline float clampf(float value, float low, float high) { if (value < low) value = low; else if (value > high) value = high; return value; }
+ static inline float saturate(float value) { return clampf(value, 0, 1.0f); }
+
+ static inline uint8_t mul_8(uint32_t v, uint32_t q) { v = v * q + 128; return (uint8_t)((v + (v >> 8)) >> 8); }
+
uint16_t crc16(const void* r, size_t size, uint16_t crc)
{
crc = ~crc;
@@ -279,6 +295,9 @@ namespace basist
DECLARE_ETC1_INTEN_TABLE(g_etc1_inten_tables, 1);
DECLARE_ETC1_INTEN_TABLE(g_etc1_inten_tables16, 16);
DECLARE_ETC1_INTEN_TABLE(g_etc1_inten_tables48, 3 * 16);
+
+ //const uint8_t g_etc1_to_selector_index[cETC1SelectorValues] = { 2, 3, 1, 0 };
+ const uint8_t g_selector_index_to_etc1[cETC1SelectorValues] = { 3, 2, 0, 1 };
static const uint8_t g_etc_5_to_8[32] = { 0, 8, 16, 24, 33, 41, 49, 57, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140, 148, 156, 165, 173, 181, 189, 198, 206, 214, 222, 231, 239, 247, 255 };
@@ -522,11 +541,39 @@ namespace basist
return static_cast(b | (g << 5U) | (r << 10U));
}
+ inline uint16_t get_base4_color(uint32_t idx) const
+ {
+ uint32_t r, g, b;
+ if (idx)
+ {
+ r = get_byte_bits(cETC1AbsColor4R2BitOffset, 4);
+ g = get_byte_bits(cETC1AbsColor4G2BitOffset, 4);
+ b = get_byte_bits(cETC1AbsColor4B2BitOffset, 4);
+ }
+ else
+ {
+ r = get_byte_bits(cETC1AbsColor4R1BitOffset, 4);
+ g = get_byte_bits(cETC1AbsColor4G1BitOffset, 4);
+ b = get_byte_bits(cETC1AbsColor4B1BitOffset, 4);
+ }
+ return static_cast(b | (g << 4U) | (r << 8U));
+ }
+
inline color32 get_base5_color_unscaled() const
{
return color32(m_differential.m_red1, m_differential.m_green1, m_differential.m_blue1, 255);
}
+ inline bool get_flip_bit() const
+ {
+ return (m_bytes[3] & 1) != 0;
+ }
+
+ inline bool get_diff_bit() const
+ {
+ return (m_bytes[3] & 2) != 0;
+ }
+
inline uint32_t get_inten_table(uint32_t subblock_id) const
{
assert(subblock_id < 2);
@@ -534,6 +581,38 @@ namespace basist
return (m_bytes[3] >> ofs) & 7;
}
+ inline uint16_t get_delta3_color() const
+ {
+ const uint32_t r = get_byte_bits(cETC1DeltaColor3RBitOffset, 3);
+ const uint32_t g = get_byte_bits(cETC1DeltaColor3GBitOffset, 3);
+ const uint32_t b = get_byte_bits(cETC1DeltaColor3BBitOffset, 3);
+ return static_cast(b | (g << 3U) | (r << 6U));
+ }
+
+ void get_block_colors(color32* pBlock_colors, uint32_t subblock_index) const
+ {
+ color32 b;
+
+ if (get_diff_bit())
+ {
+ if (subblock_index)
+ unpack_color5(b, get_base5_color(), get_delta3_color(), true, 255);
+ else
+ unpack_color5(b, get_base5_color(), true);
+ }
+ else
+ {
+ b = unpack_color4(get_base4_color(subblock_index), true, 255);
+ }
+
+ const int* pInten_table = g_etc1_inten_tables[get_inten_table(subblock_index)];
+
+ pBlock_colors[0].set_noclamp_rgba(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);
+ pBlock_colors[1].set_noclamp_rgba(clamp255(b.r + pInten_table[1]), clamp255(b.g + pInten_table[1]), clamp255(b.b + pInten_table[1]), 255);
+ pBlock_colors[2].set_noclamp_rgba(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);
+ pBlock_colors[3].set_noclamp_rgba(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);
+ }
+
static uint16_t pack_color4(const color32& color, bool scaled, uint32_t bias = 127U)
{
return pack_color4(color.r, color.g, color.b, scaled, bias);
@@ -592,7 +671,17 @@ namespace basist
return static_cast(b | (g << 3) | (r << 6));
}
- static color32 unpack_color5(uint16_t packed_color5, bool scaled, uint32_t alpha = 255)
+ static void unpack_delta3(int& r, int& g, int& b, uint16_t packed_delta3)
+ {
+ r = (packed_delta3 >> 6) & 7;
+ g = (packed_delta3 >> 3) & 7;
+ b = packed_delta3 & 7;
+ if (r >= 4) r -= 8;
+ if (g >= 4) g -= 8;
+ if (b >= 4) b -= 8;
+ }
+
+ static color32 unpack_color5(uint16_t packed_color5, bool scaled, uint32_t alpha)
{
uint32_t b = packed_color5 & 31U;
uint32_t g = (packed_color5 >> 5U) & 31U;
@@ -605,7 +694,9 @@ namespace basist
r = (r << 3U) | (r >> 2U);
}
- return color32(r, g, b, alpha);
+ assert(alpha <= 255);
+
+ return color32(cNoClamp, r, g, b, alpha);
}
static void unpack_color5(uint32_t& r, uint32_t& g, uint32_t& b, uint16_t packed_color5, bool scaled)
@@ -615,6 +706,64 @@ namespace basist
g = c.g;
b = c.b;
}
+
+ static void unpack_color5(color32& result, uint16_t packed_color5, bool scaled)
+ {
+ result = unpack_color5(packed_color5, scaled, 255);
+ }
+
+ static bool unpack_color5(color32& result, uint16_t packed_color5, uint16_t packed_delta3, bool scaled, uint32_t alpha)
+ {
+ int dr, dg, db;
+ unpack_delta3(dr, dg, db, packed_delta3);
+
+ int r = ((packed_color5 >> 10U) & 31U) + dr;
+ int g = ((packed_color5 >> 5U) & 31U) + dg;
+ int b = (packed_color5 & 31U) + db;
+
+ bool success = true;
+ if (static_cast(r | g | b) > 31U)
+ {
+ success = false;
+ r = basisu::clamp(r, 0, 31);
+ g = basisu::clamp(g, 0, 31);
+ b = basisu::clamp(b, 0, 31);
+ }
+
+ if (scaled)
+ {
+ b = (b << 3U) | (b >> 2U);
+ g = (g << 3U) | (g >> 2U);
+ r = (r << 3U) | (r >> 2U);
+ }
+
+ result.set_noclamp_rgba(r, g, b, basisu::minimum(alpha, 255U));
+ return success;
+ }
+
+ static color32 unpack_color4(uint16_t packed_color4, bool scaled, uint32_t alpha)
+ {
+ uint32_t b = packed_color4 & 15U;
+ uint32_t g = (packed_color4 >> 4U) & 15U;
+ uint32_t r = (packed_color4 >> 8U) & 15U;
+
+ if (scaled)
+ {
+ b = (b << 4U) | b;
+ g = (g << 4U) | g;
+ r = (r << 4U) | r;
+ }
+
+ return color32(cNoClamp, r, g, b, basisu::minimum(alpha, 255U));
+ }
+
+ static void unpack_color4(uint32_t& r, uint32_t& g, uint32_t& b, uint16_t packed_color4, bool scaled)
+ {
+ color32 c(unpack_color4(packed_color4, scaled, 0));
+ r = c.r;
+ g = c.g;
+ b = c.b;
+ }
static void get_diff_subblock_colors(color32* pDst, uint16_t packed_color5, uint32_t table_idx)
{
@@ -823,197 +972,6 @@ namespace basist
uint32_t m_high;
};
-#if BASISD_SUPPORT_BC7_MODE6_OPAQUE_ONLY
- static dxt_selector_range g_etc1_to_bc7_selector_ranges[] =
- {
- { 0, 0 },
- { 1, 1 },
- { 2, 2 },
- { 3, 3 },
-
- { 0, 3 },
-
- { 1, 3 },
- { 0, 2 },
-
- { 1, 2 },
-
- { 2, 3 },
- { 0, 1 },
- };
- const uint32_t NUM_ETC1_TO_BC7_M6_SELECTOR_RANGES = sizeof(g_etc1_to_bc7_selector_ranges) / sizeof(g_etc1_to_bc7_selector_ranges[0]);
-
- static uint32_t g_etc1_to_bc7_m6_selector_range_index[4][4];
-
- static const uint8_t g_etc1_to_bc7_selector_mappings[][4] =
- {
-#if 1
- { 5 * 0, 5 * 0, 5 * 0, 5 * 0 },
- { 5 * 0, 5 * 0, 5 * 0, 5 * 1 },
- { 5 * 0, 5 * 0, 5 * 0, 5 * 2 },
- { 5 * 0, 5 * 0, 5 * 0, 5 * 3 },
- { 5 * 0, 5 * 0, 5 * 1, 5 * 1 },
- { 5 * 0, 5 * 0, 5 * 1, 5 * 2 },
- { 5 * 0, 5 * 0, 5 * 1, 5 * 3 },
- { 5 * 0, 5 * 0, 5 * 2, 5 * 2 },
- { 5 * 0, 5 * 0, 5 * 2, 5 * 3 },
- { 5 * 0, 5 * 0, 5 * 3, 5 * 3 },
- { 5 * 0, 5 * 1, 5 * 1, 5 * 1 },
- { 5 * 0, 5 * 1, 5 * 1, 5 * 2 },
- { 5 * 0, 5 * 1, 5 * 1, 5 * 3 },
- { 5 * 0, 5 * 1, 5 * 2, 5 * 2 },
- { 5 * 0, 5 * 1, 5 * 2, 5 * 3 },
- { 5 * 0, 5 * 1, 5 * 3, 5 * 3 },
- { 5 * 0, 5 * 2, 5 * 2, 5 * 2 },
- { 5 * 0, 5 * 2, 5 * 2, 5 * 3 },
- { 5 * 0, 5 * 2, 5 * 3, 5 * 3 },
- { 5 * 0, 5 * 3, 5 * 3, 5 * 3 },
- { 5 * 1, 5 * 1, 5 * 1, 5 * 1 },
- { 5 * 1, 5 * 1, 5 * 1, 5 * 2 },
- { 5 * 1, 5 * 1, 5 * 1, 5 * 3 },
- { 5 * 1, 5 * 1, 5 * 2, 5 * 2 },
- { 5 * 1, 5 * 1, 5 * 2, 5 * 3 },
- { 5 * 1, 5 * 1, 5 * 3, 5 * 3 },
- { 5 * 1, 5 * 2, 5 * 2, 5 * 2 },
- { 5 * 1, 5 * 2, 5 * 2, 5 * 3 },
- { 5 * 1, 5 * 2, 5 * 3, 5 * 3 },
- { 5 * 1, 5 * 3, 5 * 3, 5 * 3 },
- { 5 * 2, 5 * 2, 5 * 2, 5 * 2 },
- { 5 * 2, 5 * 2, 5 * 2, 5 * 3 },
- { 5 * 2, 5 * 2, 5 * 3, 5 * 3 },
- { 5 * 2, 5 * 3, 5 * 3, 5 * 3 },
- { 5 * 3, 5 * 3, 5 * 3, 5 * 3 },
-
- { 0, 1, 2, 3 },
- { 0, 0, 1, 1 },
- { 0, 0, 0, 1 },
- { 0, 2, 4, 6 },
- { 0, 3, 6, 9 },
- { 0, 4, 8, 12 },
-
- { 0, 4, 9, 15 },
- { 0, 6, 11, 15 },
-
- { 1, 2, 3, 4 },
- { 1, 3, 5, 7 },
-
- { 1, 8, 8, 14 },
-#else
- { 5 * 0, 5 * 0, 5 * 1, 5 * 1 },
- { 5 * 0, 5 * 0, 5 * 1, 5 * 2 },
- { 5 * 0, 5 * 0, 5 * 1, 5 * 3 },
- { 5 * 0, 5 * 0, 5 * 2, 5 * 3 },
- { 5 * 0, 5 * 1, 5 * 1, 5 * 1 },
- { 5 * 0, 5 * 1, 5 * 2, 5 * 2 },
- { 5 * 0, 5 * 1, 5 * 2, 5 * 3 },
- { 5 * 0, 5 * 2, 5 * 3, 5 * 3 },
- { 5 * 1, 5 * 2, 5 * 2, 5 * 2 },
-#endif
- { 5 * 1, 5 * 2, 5 * 3, 5 * 3 },
- { 8, 8, 8, 8 },
- };
- const uint32_t NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS = sizeof(g_etc1_to_bc7_selector_mappings) / sizeof(g_etc1_to_bc7_selector_mappings[0]);
-
- static uint8_t g_etc1_to_bc7_selector_mappings_inv[NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS][4];
-
- // encoding from LSB to MSB: low8, high8, error16, size is [32*8][NUM_ETC1_TO_BC7_M6_SELECTOR_RANGES][NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS]
- extern const uint32_t* g_etc1_to_bc7_m6_table[];
-
- const uint16_t s_bptc_table_aWeight4[16] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
-
-#if BASISD_WRITE_NEW_BC7_TABLES
- static void create_etc1_to_bc7_m6_conversion_table()
- {
- FILE* pFile = NULL;
-
- pFile = fopen("basisu_decoder_tables_bc7_m6.inc", "w");
-
- for (int inten = 0; inten < 8; inten++)
- {
- for (uint32_t g = 0; g < 32; g++)
- {
- color32 block_colors[4];
- decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);
-
- fprintf(pFile, "static const uint32_t g_etc1_to_bc7_m6_table%u[] = {\n", g + inten * 32);
- uint32_t n = 0;
-
- for (uint32_t sr = 0; sr < NUM_ETC1_TO_BC7_M6_SELECTOR_RANGES; sr++)
- {
- const uint32_t low_selector = g_etc1_to_bc7_selector_ranges[sr].m_low;
- const uint32_t high_selector = g_etc1_to_bc7_selector_ranges[sr].m_high;
-
- for (uint32_t m = 0; m < NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS; m++)
- {
- uint32_t best_lo = 0;
- uint32_t best_hi = 0;
- uint64_t best_err = UINT64_MAX;
-
- for (uint32_t hi = 0; hi <= 127; hi++)
- {
- for (uint32_t lo = 0; lo <= 127; lo++)
- {
- uint32_t bc7_block_colors[16];
-
- bc7_block_colors[0] = lo << 1;
- bc7_block_colors[15] = (hi << 1) | 1;
-
- for (uint32_t i = 1; i < 15; i++)
- bc7_block_colors[i] = (bc7_block_colors[0] * (64 - s_bptc_table_aWeight4[i]) + bc7_block_colors[15] * s_bptc_table_aWeight4[i] + 32) >> 6;
-
- uint64_t total_err = 0;
-
- for (uint32_t s = low_selector; s <= high_selector; s++)
- {
- int err = (int)block_colors[s].g - (int)bc7_block_colors[g_etc1_to_bc7_selector_mappings[m][s]];
-
- total_err += err * err;
- }
-
- if (total_err < best_err)
- {
- best_err = total_err;
- best_lo = lo;
- best_hi = hi;
- }
- } // lo
-
- } // hi
-
- best_err = basisu::minimum(best_err, 0xFFFF);
-
- const uint32_t index = (g + inten * 32) * (NUM_ETC1_TO_BC7_M6_SELECTOR_RANGES * NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS) + (sr * NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS) + m;
-
- uint32_t v = best_err | (best_lo << 18) | (best_hi << 25);
-
- fprintf(pFile, "0x%X,", v);
- n++;
- if ((n & 31) == 31)
- fprintf(pFile, "\n");
-
- } // m
- } // sr
-
- fprintf(pFile, "};\n");
-
- } // g
- } // inten
-
- fprintf(pFile, "const uint32_t *g_etc1_to_bc7_m6_table[] = {\n");
-
- for (uint32_t i = 0; i < 32 * 8; i++)
- {
- fprintf(pFile, "g_etc1_to_bc7_m6_table%u, ", i);
- if ((i & 15) == 15)
- fprintf(pFile, "\n");
- }
-
- fprintf(pFile, "};\n");
- fclose(pFile);
- }
-#endif
-#endif
-
struct etc1_to_dxt1_56_solution
{
uint8_t m_lo;
@@ -1064,7 +1022,9 @@ namespace basist
static const etc1_to_dxt1_56_solution g_etc1_to_dxt_5[32 * 8 * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS * NUM_ETC1_TO_DXT1_SELECTOR_RANGES] = {
#include "basisu_transcoder_tables_dxt1_5.inc"
};
+#endif // BASISD_SUPPORT_DXT1
+#if BASISD_SUPPORT_DXT1 || BASISD_SUPPORT_UASTC
// First saw the idea for optimal BC1 single-color block encoding using lookup tables in ryg_dxt.
struct bc1_match_entry
{
@@ -1089,14 +1049,15 @@ namespace basist
if (sel == 1)
{
// Selector 1
- e = abs(((hi_e * 2 + lo_e) / 3) - i) + ((abs(hi_e - lo_e) >> 5));
+ e = basisu::iabs(((hi_e * 2 + lo_e) / 3) - i);
+ e += (basisu::iabs(hi_e - lo_e) * 3) / 100;
}
else
{
assert(sel == 0);
// Selector 0
- e = abs(hi_e - i);
+ e = basisu::iabs(hi_e - i);
}
if (e < lowest_e)
@@ -1111,7 +1072,7 @@ namespace basist
} // lo
}
}
-#endif // BASISD_SUPPORT_DXT1
+#endif
#if BASISD_WRITE_NEW_DXT1_TABLES
static void create_etc1_to_dxt1_5_conversion_table()
@@ -1268,7 +1229,8 @@ namespace basist
}
#endif
-#if BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_ETC2_EAC_RG11
+
+#if BASISD_SUPPORT_UASTC || BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_ETC2_EAC_RG11
static const int8_t g_eac_modifier_table[16][8] =
{
{ -3, -6, -9, -15, 2, 5, 8, 14 },
@@ -1344,6 +1306,9 @@ namespace basist
}
};
+#endif // #if BASISD_SUPPORT_UASTC BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_ETC2_EAC_RG11
+
+#if BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_ETC2_EAC_RG11
static const dxt_selector_range s_etc2_eac_selector_ranges[] =
{
{ 0, 3 },
@@ -1924,6 +1889,10 @@ namespace basist
#if BASISD_SUPPORT_PVRTC2
static void transcoder_init_pvrtc2();
#endif
+
+#if BASISD_SUPPORT_UASTC
+ void uastc_init();
+#endif
// Library global initialization. Requires ~9 milliseconds when compiled and executed natively on a Core i7 2.2 GHz.
// If this is too slow, these computed tables can easilky be moved to be compiled in.
@@ -1931,7 +1900,16 @@ namespace basist
{
static bool s_initialized;
if (s_initialized)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::basisu_transcoder_init: Called more than once\n");
return;
+ }
+
+ BASISU_DEVEL_ERROR("basisu_transcoder::basisu_transcoder_init: Initializing (this is not an error)\n");
+
+#if BASISD_SUPPORT_UASTC
+ uastc_init();
+#endif
#if BASISD_SUPPORT_ASTC
transcoder_init_astc();
@@ -1943,11 +1921,6 @@ namespace basist
exit(0);
#endif
-#if BASISD_WRITE_NEW_BC7_TABLES
- create_etc1_to_bc7_m6_conversion_table();
- exit(0);
-#endif
-
#if BASISD_WRITE_NEW_BC7_MODE5_TABLES
create_etc1_to_bc7_m5_color_conversion_table();
create_etc1_to_bc7_m5_alpha_conversion_table();
@@ -1975,7 +1948,7 @@ namespace basist
exit(0);
#endif
-#if BASISD_SUPPORT_DXT1
+#if BASISD_SUPPORT_DXT1 || BASISD_SUPPORT_UASTC
uint8_t bc1_expand5[32];
for (int i = 0; i < 32; i++)
bc1_expand5[i] = static_cast((i << 3) | (i >> 2));
@@ -1988,6 +1961,17 @@ namespace basist
prepare_bc1_single_color_table(g_bc1_match6_equals_1, bc1_expand6, 64, 64, 1);
prepare_bc1_single_color_table(g_bc1_match6_equals_0, bc1_expand6, 1, 64, 0);
+#if 0
+ for (uint32_t i = 0; i < 256; i++)
+ {
+ printf("%u %u %u\n", i, (i * 63 + 127) / 255, g_bc1_match6_equals_0[i].m_hi);
+ }
+ exit(0);
+#endif
+
+#endif
+
+#if BASISD_SUPPORT_DXT1
for (uint32_t i = 0; i < NUM_ETC1_TO_DXT1_SELECTOR_RANGES; i++)
{
uint32_t l = g_etc1_to_dxt1_selector_ranges[i].m_low;
@@ -2023,19 +2007,6 @@ namespace basist
}
#endif
-#if BASISD_SUPPORT_BC7_MODE6_OPAQUE_ONLY
- for (uint32_t i = 0; i < NUM_ETC1_TO_BC7_M6_SELECTOR_RANGES; i++)
- {
- uint32_t l = g_etc1_to_bc7_selector_ranges[i].m_low;
- uint32_t h = g_etc1_to_bc7_selector_ranges[i].m_high;
- g_etc1_to_bc7_m6_selector_range_index[l][h] = i;
- }
-
- for (uint32_t sm = 0; sm < NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS; sm++)
- for (uint32_t j = 0; j < 4; j++)
- g_etc1_to_bc7_selector_mappings_inv[sm][j] = 15 - g_etc1_to_bc7_selector_mappings[sm][j];
-#endif
-
#if BASISD_SUPPORT_BC7_MODE5
transcoder_init_bc7_mode5();
#endif
@@ -2780,7 +2751,7 @@ namespace basist
// PVRTC
-#if BASISD_SUPPORT_PVRTC1
+#if BASISD_SUPPORT_PVRTC1 || BASISD_SUPPORT_UASTC
static const uint16_t g_pvrtc_swizzle_table[256] =
{
0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015, 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055, 0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115, 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155,
@@ -3304,6 +3275,7 @@ namespace basist
}
};
+#if 0
static const uint8_t g_pvrtc_bilinear_weights[16][4] =
{
{ 4, 4, 4, 4 }, { 2, 6, 2, 6 }, { 8, 0, 8, 0 }, { 6, 2, 6, 2 },
@@ -3311,6 +3283,7 @@ namespace basist
{ 8, 8, 0, 0 }, { 4, 12, 0, 0 }, { 16, 0, 0, 0 }, { 12, 4, 0, 0 },
{ 6, 6, 2, 2 }, { 3, 9, 1, 3 }, { 12, 0, 4, 0 }, { 9, 3, 3, 1 },
};
+#endif
struct pvrtc1_temp_block
{
@@ -3402,7 +3375,9 @@ namespace basist
color32 c(get_endpoint_8888(endpoints, endpoint_index));
return c.r + c.g + c.b + c.a;
}
+#endif
+#if BASISD_SUPPORT_PVRTC1
// TODO: Support decoding a non-pow2 ETC1S texture into the next larger pow2 PVRTC texture.
static void fixup_pvrtc1_4_modulation_rgb(const decoder_etc_block* pETC_Blocks, const uint32_t* pPVRTC_endpoints, void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y)
{
@@ -3411,7 +3386,7 @@ namespace basist
const uint32_t x_bits = basisu::total_bits(x_mask);
const uint32_t y_bits = basisu::total_bits(y_mask);
const uint32_t min_bits = basisu::minimum(x_bits, y_bits);
- const uint32_t max_bits = basisu::maximum(x_bits, y_bits);
+ //const uint32_t max_bits = basisu::maximum(x_bits, y_bits);
const uint32_t swizzle_mask = (1 << (min_bits * 2)) - 1;
uint32_t block_index = 0;
@@ -3592,7 +3567,7 @@ namespace basist
const uint32_t x_bits = basisu::total_bits(x_mask);
const uint32_t y_bits = basisu::total_bits(y_mask);
const uint32_t min_bits = basisu::minimum(x_bits, y_bits);
- const uint32_t max_bits = basisu::maximum(x_bits, y_bits);
+ //const uint32_t max_bits = basisu::maximum(x_bits, y_bits);
const uint32_t swizzle_mask = (1 << (min_bits * 2)) - 1;
uint32_t block_index = 0;
@@ -3763,257 +3738,6 @@ namespace basist
}
#endif // BASISD_SUPPORT_PVRTC1
-#if BASISD_SUPPORT_BC7_MODE6_OPAQUE_ONLY
- struct bc7_mode_6
- {
- struct
- {
- uint64_t m_mode : 7;
- uint64_t m_r0 : 7;
- uint64_t m_r1 : 7;
- uint64_t m_g0 : 7;
- uint64_t m_g1 : 7;
- uint64_t m_b0 : 7;
- uint64_t m_b1 : 7;
- uint64_t m_a0 : 7;
- uint64_t m_a1 : 7;
- uint64_t m_p0 : 1;
- } m_lo;
-
- union
- {
- struct
- {
- uint64_t m_p1 : 1;
- uint64_t m_s00 : 3;
- uint64_t m_s10 : 4;
- uint64_t m_s20 : 4;
- uint64_t m_s30 : 4;
-
- uint64_t m_s01 : 4;
- uint64_t m_s11 : 4;
- uint64_t m_s21 : 4;
- uint64_t m_s31 : 4;
-
- uint64_t m_s02 : 4;
- uint64_t m_s12 : 4;
- uint64_t m_s22 : 4;
- uint64_t m_s32 : 4;
-
- uint64_t m_s03 : 4;
- uint64_t m_s13 : 4;
- uint64_t m_s23 : 4;
- uint64_t m_s33 : 4;
-
- } m_hi;
-
- uint64_t m_hi_bits;
- };
- };
-
- static void convert_etc1s_to_bc7_m6(bc7_mode_6* pDst_block, const endpoint *pEndpoint, const selector* pSelector)
- {
-#if !BASISD_WRITE_NEW_BC7_TABLES
- const uint32_t low_selector = pSelector->m_lo_selector;
- const uint32_t high_selector = pSelector->m_hi_selector;
-
- const uint32_t base_color_r = pEndpoint->m_color5.r;
- const uint32_t base_color_g = pEndpoint->m_color5.g;
- const uint32_t base_color_b = pEndpoint->m_color5.b;
- const uint32_t inten_table = pEndpoint->m_inten5;
-
- if (pSelector->m_num_unique_selectors <= 2)
- {
- // Only two unique selectors so just switch to block truncation coding (BTC) to avoid quality issues on extreme blocks.
- pDst_block->m_lo.m_mode = 64;
-
- pDst_block->m_lo.m_a0 = 127;
- pDst_block->m_lo.m_a1 = 127;
-
- color32 block_colors[4];
-
- decoder_etc_block::get_block_colors5(block_colors, color32(base_color_r, base_color_g, base_color_b, 255), inten_table);
-
- const uint32_t r0 = block_colors[low_selector].r;
- const uint32_t g0 = block_colors[low_selector].g;
- const uint32_t b0 = block_colors[low_selector].b;
- const uint32_t low_bits0 = (r0 & 1) + (g0 & 1) + (b0 & 1);
- uint32_t p0 = low_bits0 >= 2;
-
- const uint32_t r1 = block_colors[high_selector].r;
- const uint32_t g1 = block_colors[high_selector].g;
- const uint32_t b1 = block_colors[high_selector].b;
- const uint32_t low_bits1 = (r1 & 1) + (g1 & 1) + (b1 & 1);
- uint32_t p1 = low_bits1 >= 2;
-
- pDst_block->m_lo.m_r0 = r0 >> 1;
- pDst_block->m_lo.m_g0 = g0 >> 1;
- pDst_block->m_lo.m_b0 = b0 >> 1;
- pDst_block->m_lo.m_p0 = p0;
-
- pDst_block->m_lo.m_r1 = r1 >> 1;
- pDst_block->m_lo.m_g1 = g1 >> 1;
- pDst_block->m_lo.m_b1 = b1 >> 1;
-
- uint32_t output_low_selector = 0;
- uint32_t output_bit_offset = 1;
- uint64_t output_hi_bits = p1;
-
- for (uint32_t y = 0; y < 4; y++)
- {
- for (uint32_t x = 0; x < 4; x++)
- {
- uint32_t s = pSelector->get_selector(x, y);
- uint32_t os = (s == low_selector) ? output_low_selector : (15 ^ output_low_selector);
-
- uint32_t num_bits = 4;
-
- if ((x | y) == 0)
- {
- if (os & 8)
- {
- pDst_block->m_lo.m_r0 = r1 >> 1;
- pDst_block->m_lo.m_g0 = g1 >> 1;
- pDst_block->m_lo.m_b0 = b1 >> 1;
- pDst_block->m_lo.m_p0 = p1;
-
- pDst_block->m_lo.m_r1 = r0 >> 1;
- pDst_block->m_lo.m_g1 = g0 >> 1;
- pDst_block->m_lo.m_b1 = b0 >> 1;
-
- output_hi_bits &= ~1ULL;
- output_hi_bits |= p0;
- std::swap(p0, p1);
-
- output_low_selector = 15;
- os = 0;
- }
-
- num_bits = 3;
- }
-
- output_hi_bits |= (static_cast(os) << output_bit_offset);
- output_bit_offset += num_bits;
- }
- }
-
- pDst_block->m_hi_bits = output_hi_bits;
-
- assert(pDst_block->m_hi.m_p1 == p1);
-
- return;
- }
-
- uint32_t selector_range_table = g_etc1_to_bc7_m6_selector_range_index[low_selector][high_selector];
-
- const uint32_t* pTable_r = g_etc1_to_bc7_m6_table[base_color_r + inten_table * 32] + (selector_range_table * NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS);
- const uint32_t* pTable_g = g_etc1_to_bc7_m6_table[base_color_g + inten_table * 32] + (selector_range_table * NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS);
- const uint32_t* pTable_b = g_etc1_to_bc7_m6_table[base_color_b + inten_table * 32] + (selector_range_table * NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS);
-
-#if 1
- assert(NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS == 48);
-
- uint32_t best_err0 = UINT_MAX, best_err1 = UINT_MAX;
-
-#define DO_ITER2(idx) \
- { \
- uint32_t v0 = ((pTable_r[(idx)+0] + pTable_g[(idx)+0] + pTable_b[(idx)+0]) << 14) | ((idx) + 0); if (v0 < best_err0) best_err0 = v0; \
- uint32_t v1 = ((pTable_r[(idx)+1] + pTable_g[(idx)+1] + pTable_b[(idx)+1]) << 14) | ((idx) + 1); if (v1 < best_err1) best_err1 = v1; \
- }
-#define DO_ITER4(idx) DO_ITER2(idx); DO_ITER2((idx) + 2);
-#define DO_ITER8(idx) DO_ITER4(idx); DO_ITER4((idx) + 4);
-#define DO_ITER16(idx) DO_ITER8(idx); DO_ITER8((idx) + 8);
-
- DO_ITER16(0);
- DO_ITER16(16);
- DO_ITER16(32);
-#undef DO_ITER2
-#undef DO_ITER4
-#undef DO_ITER8
-#undef DO_ITER16
-
- uint32_t best_err = basisu::minimum(best_err0, best_err1);
- uint32_t best_mapping = best_err & 0xFF;
- //best_err >>= 14;
-#else
- uint32_t best_err = UINT_MAX;
- uint32_t best_mapping = 0;
- assert((NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS % 2) == 0);
- for (uint32_t m = 0; m < NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS; m += 2)
- {
-#define DO_ITER(idx) { uint32_t total_err = (pTable_r[idx] + pTable_g[idx] + pTable_b[idx]) & 0x3FFFF; if (total_err < best_err) { best_err = total_err; best_mapping = idx; } }
- DO_ITER(m);
- DO_ITER(m + 1);
-#undef DO_ITER
- }
-#endif
-
- pDst_block->m_lo.m_mode = 64;
-
- pDst_block->m_lo.m_a0 = 127;
- pDst_block->m_lo.m_a1 = 127;
-
- uint64_t v = 0;
- const uint8_t* pSelectors_xlat;
-
- if (g_etc1_to_bc7_selector_mappings[best_mapping][pSelector->get_selector(0, 0)] & 8)
- {
- pDst_block->m_lo.m_r1 = (pTable_r[best_mapping] >> 18) & 0x7F;
- pDst_block->m_lo.m_g1 = (pTable_g[best_mapping] >> 18) & 0x7F;
- pDst_block->m_lo.m_b1 = (pTable_b[best_mapping] >> 18) & 0x7F;
-
- pDst_block->m_lo.m_r0 = (pTable_r[best_mapping] >> 25) & 0x7F;
- pDst_block->m_lo.m_g0 = (pTable_g[best_mapping] >> 25) & 0x7F;
- pDst_block->m_lo.m_b0 = (pTable_b[best_mapping] >> 25) & 0x7F;
-
- pDst_block->m_lo.m_p0 = 1;
- pDst_block->m_hi.m_p1 = 0;
-
- v = 0;
- pSelectors_xlat = &g_etc1_to_bc7_selector_mappings_inv[best_mapping][0];
- }
- else
- {
- pDst_block->m_lo.m_r0 = (pTable_r[best_mapping] >> 18) & 0x7F;
- pDst_block->m_lo.m_g0 = (pTable_g[best_mapping] >> 18) & 0x7F;
- pDst_block->m_lo.m_b0 = (pTable_b[best_mapping] >> 18) & 0x7F;
-
- pDst_block->m_lo.m_r1 = (pTable_r[best_mapping] >> 25) & 0x7F;
- pDst_block->m_lo.m_g1 = (pTable_g[best_mapping] >> 25) & 0x7F;
- pDst_block->m_lo.m_b1 = (pTable_b[best_mapping] >> 25) & 0x7F;
-
- pDst_block->m_lo.m_p0 = 0;
- pDst_block->m_hi.m_p1 = 1;
-
- v = 1;
- pSelectors_xlat = &g_etc1_to_bc7_selector_mappings[best_mapping][0];
- }
-
- uint64_t v1 = 0, v2 = 0, v3 = 0;
-
-#define DO_X(x, s0, s1, s2, s3) { \
- v |= ((uint64_t)pSelectors_xlat[(pSelector->m_selectors[0] >> ((x) * 2)) & 3] << (s0)); \
- v1 |= ((uint64_t)pSelectors_xlat[(pSelector->m_selectors[1] >> ((x) * 2)) & 3] << (s1)); \
- v2 |= ((uint64_t)pSelectors_xlat[(pSelector->m_selectors[2] >> ((x) * 2)) & 3] << (s2)); \
- v3 |= ((uint64_t)pSelectors_xlat[(pSelector->m_selectors[3] >> ((x) * 2)) & 3] << (s3)); }
-
- // 1 4 8 12
- // 16 20 24 28
- // 32 36 40 44
- // 48 52 56 60
-
- DO_X(0, 1, 16, 32, 48);
- DO_X(1, 4, 20, 36, 52);
- DO_X(2, 8, 24, 40, 56);
- DO_X(3, 12, 28, 44, 60);
-#undef DO_X
-
- pDst_block->m_hi_bits = v | v1 | v2 | v3;
-#endif
-
- }
-#endif // BASISD_SUPPORT_BC7_MODE6_OPAQUE_ONLY
-
#if BASISD_SUPPORT_BC7_MODE5
static dxt_selector_range g_etc1_to_bc7_m5_selector_ranges[] =
{
@@ -4425,9 +4149,11 @@ namespace basist
{
bc7_mode_5* pDst_block = static_cast(pDst);
+ // First ensure the block is cleared to all 0's
static_cast(pDst)[0] = 0;
static_cast(pDst)[1] = 0;
+ // Set alpha to 255
pDst_block->m_lo.m_mode = 1 << 5;
pDst_block->m_lo.m_a0 = 255;
pDst_block->m_lo.m_a1_0 = 63;
@@ -4690,7 +4416,11 @@ namespace basist
set_block_bits((uint8_t*)pDst, output_bits, 31, 97);
}
#endif // BASISD_SUPPORT_BC7_MODE5
-
+
+#if BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_UASTC
+ static const uint8_t g_etc2_eac_a8_sel4[6] = { 0x92, 0x49, 0x24, 0x92, 0x49, 0x24 };
+#endif
+
#if BASISD_SUPPORT_ETC2_EAC_A8
static void convert_etc1s_to_etc2_eac_a8(eac_block* pDst_block, const endpoint* pEndpoints, const selector* pSelector)
{
@@ -4712,8 +4442,7 @@ namespace basist
pDst_block->m_multiplier = 1;
// selectors are all 4's
- static const uint8_t s_etc2_eac_a8_sel4[6] = { 0x92, 0x49, 0x24, 0x92, 0x49, 0x24 };
- memcpy(pDst_block->m_selectors, s_etc2_eac_a8_sel4, sizeof(s_etc2_eac_a8_sel4));
+ memcpy(pDst_block->m_selectors, g_etc2_eac_a8_sel4, sizeof(g_etc2_eac_a8_sel4));
return;
}
@@ -5325,31 +5054,30 @@ namespace basist
#endif
-#if BASISD_SUPPORT_ASTC
- struct astc_block_params
- {
- // 2 groups of 5, but only a max of 8 are used (RRGGBBAA00)
- uint8_t m_endpoints[10];
- uint8_t m_weights[32];
- };
-
+#if BASISD_SUPPORT_UASTC || BASISD_SUPPORT_ASTC
// Table encodes 5 trits to 8 output bits. 3^5 entries.
// Inverse of the trit bit manipulation process in https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#astc-integer-sequence-encoding
- static const uint8_t g_astc_trit_encode[243] = { 0, 1, 2, 4, 5, 6, 8, 9, 10, 16, 17, 18, 20, 21, 22, 24, 25, 26, 3, 7, 11, 19, 23, 27, 12, 13, 14, 32, 33, 34, 36, 37, 38, 40, 41, 42, 48, 49, 50, 52, 53, 54, 56, 57, 58, 35, 39,
- 43, 51, 55, 59, 44, 45, 46, 64, 65, 66, 68, 69, 70, 72, 73, 74, 80, 81, 82, 84, 85, 86, 88, 89, 90, 67, 71, 75, 83, 87, 91, 76, 77, 78, 128, 129, 130, 132, 133, 134, 136, 137, 138, 144, 145, 146, 148, 149, 150, 152, 153, 154,
- 131, 135, 139, 147, 151, 155, 140, 141, 142, 160, 161, 162, 164, 165, 166, 168, 169, 170, 176, 177, 178, 180, 181, 182, 184, 185, 186, 163, 167, 171, 179, 183, 187, 172, 173, 174, 192, 193, 194, 196, 197, 198, 200, 201, 202,
- 208, 209, 210, 212, 213, 214, 216, 217, 218, 195, 199, 203, 211, 215, 219, 204, 205, 206, 96, 97, 98, 100, 101, 102, 104, 105, 106, 112, 113, 114, 116, 117, 118, 120, 121, 122, 99, 103, 107, 115, 119, 123, 108, 109, 110, 224,
- 225, 226, 228, 229, 230, 232, 233, 234, 240, 241, 242, 244, 245, 246, 248, 249, 250, 227, 231, 235, 243, 247, 251, 236, 237, 238, 28, 29, 30, 60, 61, 62, 92, 93, 94, 156, 157, 158, 188, 189, 190, 220, 221, 222, 31, 63, 95, 159,
+ static const uint8_t g_astc_trit_encode[243] = { 0, 1, 2, 4, 5, 6, 8, 9, 10, 16, 17, 18, 20, 21, 22, 24, 25, 26, 3, 7, 11, 19, 23, 27, 12, 13, 14, 32, 33, 34, 36, 37, 38, 40, 41, 42, 48, 49, 50, 52, 53, 54, 56, 57, 58, 35, 39,
+ 43, 51, 55, 59, 44, 45, 46, 64, 65, 66, 68, 69, 70, 72, 73, 74, 80, 81, 82, 84, 85, 86, 88, 89, 90, 67, 71, 75, 83, 87, 91, 76, 77, 78, 128, 129, 130, 132, 133, 134, 136, 137, 138, 144, 145, 146, 148, 149, 150, 152, 153, 154,
+ 131, 135, 139, 147, 151, 155, 140, 141, 142, 160, 161, 162, 164, 165, 166, 168, 169, 170, 176, 177, 178, 180, 181, 182, 184, 185, 186, 163, 167, 171, 179, 183, 187, 172, 173, 174, 192, 193, 194, 196, 197, 198, 200, 201, 202,
+ 208, 209, 210, 212, 213, 214, 216, 217, 218, 195, 199, 203, 211, 215, 219, 204, 205, 206, 96, 97, 98, 100, 101, 102, 104, 105, 106, 112, 113, 114, 116, 117, 118, 120, 121, 122, 99, 103, 107, 115, 119, 123, 108, 109, 110, 224,
+ 225, 226, 228, 229, 230, 232, 233, 234, 240, 241, 242, 244, 245, 246, 248, 249, 250, 227, 231, 235, 243, 247, 251, 236, 237, 238, 28, 29, 30, 60, 61, 62, 92, 93, 94, 156, 157, 158, 188, 189, 190, 220, 221, 222, 31, 63, 95, 159,
191, 223, 124, 125, 126 };
+ // Extracts bits [low,high]
+ static inline uint32_t astc_extract_bits(uint32_t bits, int low, int high)
+ {
+ return (bits >> low) & ((1 << (high - low + 1)) - 1);
+ }
+
// Writes bits to output in an endian safe way
- static inline void astc_set_bits(uint32_t *pOutput, int &bit_pos, uint32_t value, int total_bits)
+ static inline void astc_set_bits(uint32_t* pOutput, int& bit_pos, uint32_t value, uint32_t total_bits)
{
uint8_t* pBytes = reinterpret_cast(pOutput);
-
+
while (total_bits)
{
- const uint32_t bits_to_write = std::min(total_bits, 8 - (bit_pos & 7));
+ const uint32_t bits_to_write = std::min(total_bits, 8 - (bit_pos & 7));
pBytes[bit_pos >> 3] |= static_cast(value << (bit_pos & 7));
@@ -5359,14 +5087,8 @@ namespace basist
}
}
- // Extracts bits [low,high]
- static inline uint32_t astc_extract_bits(uint32_t bits, int low, int high)
- {
- return (bits >> low) & ((1 << (high - low + 1)) - 1);
- }
-
// Encodes 5 values to output, usable for any range that uses trits and bits
- static void astc_encode_trits(uint32_t *pOutput, const uint8_t *pValues, int& bit_pos, int n)
+ static void astc_encode_trits(uint32_t* pOutput, const uint8_t* pValues, int& bit_pos, int n)
{
// First extract the trits and the bits from the 5 input values
int trits = 0, bits[5];
@@ -5374,9 +5096,9 @@ namespace basist
for (int i = 0; i < 5; i++)
{
static const int s_muls[5] = { 1, 3, 9, 27, 81 };
-
+
const int t = pValues[i] >> n;
-
+
trits += t * s_muls[i];
bits[i] = pValues[i] & bit_mask;
}
@@ -5386,14 +5108,23 @@ namespace basist
assert(trits < 243);
const int T = g_astc_trit_encode[trits];
-
+
// Now interleave the 8 encoded trit bits with the bits to form the encoded output. See table 94.
astc_set_bits(pOutput, bit_pos, bits[0] | (astc_extract_bits(T, 0, 1) << n) | (bits[1] << (2 + n)), n * 2 + 2);
- astc_set_bits(pOutput, bit_pos, astc_extract_bits(T, 2, 3) | (bits[2] << 2) | (astc_extract_bits(T, 4, 4) << (2 + n)) | (bits[3] << (3 + n)) | (astc_extract_bits(T, 5, 6) << (3 + n * 2)) |
+ astc_set_bits(pOutput, bit_pos, astc_extract_bits(T, 2, 3) | (bits[2] << 2) | (astc_extract_bits(T, 4, 4) << (2 + n)) | (bits[3] << (3 + n)) | (astc_extract_bits(T, 5, 6) << (3 + n * 2)) |
(bits[4] << (5 + n * 2)) | (astc_extract_bits(T, 7, 7) << (5 + n * 3)), n * 3 + 6);
}
+#endif // #if BASISD_SUPPORT_UASTC || BASISD_SUPPORT_ASTC
+#if BASISD_SUPPORT_ASTC
+ struct astc_block_params
+ {
+ // 2 groups of 5, but only a max of 8 are used (RRGGBBAA00)
+ uint8_t m_endpoints[10];
+ uint8_t m_weights[32];
+ };
+
// Packs a single format ASTC block using Color Endpoint Mode 12 (LDR RGBA direct), endpoint BISE range 13, 2-bit weights (range 2).
// We're always going to output blocks containing alpha, even if the input doesn't have alpha, for simplicity.
// Each block always has 4x4 weights, uses range 13 BISE encoding on the endpoints (0-47), and each weight ranges from 0-3. This encoding should be roughly equal in quality vs. BC1 for color.
@@ -6255,10 +5986,13 @@ namespace basist
static const etc1s_to_atc_solution g_etc1s_to_pvrtc2_45[32 * 8 * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS * NUM_ETC1S_TO_ATC_SELECTOR_RANGES] = {
#include "basisu_transcoder_tables_pvrtc2_45.inc"
};
-
+
+#if 0
static const etc1s_to_atc_solution g_etc1s_to_pvrtc2_alpha_33[32 * 8 * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS * NUM_ETC1S_TO_ATC_SELECTOR_RANGES] = {
#include "basisu_transcoder_tables_pvrtc2_alpha_33.inc"
};
+#endif
+
#endif
static const etc1s_to_atc_solution g_etc1s_to_atc_55[32 * 8 * NUM_ETC1S_TO_ATC_SELECTOR_MAPPINGS * NUM_ETC1S_TO_ATC_SELECTOR_RANGES] = {
@@ -7167,10 +6901,7 @@ namespace basist
}
typedef struct { float c[4]; } vec4F;
-
- static inline int32_t clampi(int32_t value, int32_t low, int32_t high) { if (value < low) value = low; else if (value > high) value = high; return value; }
- static inline float clampf(float value, float low, float high) { if (value < low) value = low; else if (value > high) value = high; return value; }
- static inline float saturate(float value) { return clampf(value, 0, 1.0f); }
+
static inline vec4F* vec4F_set_scalar(vec4F* pV, float x) { pV->c[0] = x; pV->c[1] = x; pV->c[2] = x; pV->c[3] = x; return pV; }
static inline vec4F* vec4F_set(vec4F* pV, float x, float y, float z, float w) { pV->c[0] = x; pV->c[1] = y; pV->c[2] = z; pV->c[3] = w; return pV; }
static inline vec4F* vec4F_saturate_in_place(vec4F* pV) { pV->c[0] = saturate(pV->c[0]); pV->c[1] = saturate(pV->c[1]); pV->c[2] = saturate(pV->c[2]); pV->c[3] = saturate(pV->c[3]); return pV; }
@@ -7188,7 +6919,7 @@ namespace basist
}
static inline int sq(int x) { return x * x; }
-
+
// PVRTC2 is a slightly borked format for alpha: In Non-Interpolated mode, the way AlphaB8 is exanded from 4 to 8 bits means it can never be 0.
// This is actually very bad, because on 100% transparent blocks which have non-trivial color pixels, part of the color channel will leak into alpha!
// And there's nothing straightforward we can do because using the other modes is too expensive/complex. I can see why Apple didn't adopt it.
@@ -7461,7 +7192,7 @@ namespace basist
}
vec4F_normalize_in_place(&axis);
-
+
if (vec4F_dot(&axis, &axis) < .5f)
vec4F_set_scalar(&axis, .5f);
@@ -7488,7 +7219,15 @@ namespace basist
minColor = vec4F_saturate(&c0);
maxColor = vec4F_saturate(&c1);
if (minColor.c[3] > maxColor.c[3])
- std::swap(minColor, maxColor);
+ {
+ // VS 2019 release Code Generator issue
+ //std::swap(minColor, maxColor);
+
+ float a = minColor.c[0], b = minColor.c[1], c = minColor.c[2], d = minColor.c[3];
+ minColor.c[0] = maxColor.c[0]; minColor.c[1] = maxColor.c[1]; minColor.c[2] = maxColor.c[2]; minColor.c[3] = maxColor.c[3];
+ minColor.c[0] = maxColor.c[0]; minColor.c[1] = maxColor.c[1]; minColor.c[2] = maxColor.c[2]; minColor.c[3] = maxColor.c[3];
+ maxColor.c[0] = a; maxColor.c[1] = b; maxColor.c[2] = c; maxColor.c[3] = d;
+ }
}
else
{
@@ -7768,13 +7507,13 @@ namespace basist
}
#endif // BASISD_SUPPORT_PVRTC2
- basisu_lowlevel_transcoder::basisu_lowlevel_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook) :
+ basisu_lowlevel_etc1s_transcoder::basisu_lowlevel_etc1s_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook) :
m_pGlobal_sel_codebook(pGlobal_sel_codebook),
m_selector_history_buf_size(0)
{
}
- bool basisu_lowlevel_transcoder::decode_palettes(
+ bool basisu_lowlevel_etc1s_transcoder::decode_palettes(
uint32_t num_endpoints, const uint8_t* pEndpoints_data, uint32_t endpoints_data_size,
uint32_t num_selectors, const uint8_t* pSelectors_data, uint32_t selectors_data_size)
{
@@ -7784,37 +7523,37 @@ namespace basist
if (!sym_codec.init(pEndpoints_data, endpoints_data_size))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 0\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 0\n");
return false;
}
if (!sym_codec.read_huffman_table(color5_delta_model0))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 1\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 1\n");
return false;
}
if (!sym_codec.read_huffman_table(color5_delta_model1))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 1a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 1a\n");
return false;
}
if (!sym_codec.read_huffman_table(color5_delta_model2))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 2a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 2a\n");
return false;
}
if (!sym_codec.read_huffman_table(inten_delta_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 2b\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 2b\n");
return false;
}
if (!color5_delta_model0.is_valid() || !color5_delta_model1.is_valid() || !color5_delta_model2.is_valid() || !inten_delta_model.is_valid())
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 2b\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 2b\n");
return false;
}
@@ -7861,7 +7600,7 @@ namespace basist
if (!sym_codec.init(pSelectors_data, selectors_data_size))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 5\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 5\n");
return false;
}
@@ -7880,12 +7619,12 @@ namespace basist
{
if (!sym_codec.read_huffman_table(mod_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 6\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 6\n");
return false;
}
if (!mod_model.is_valid())
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 6a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 6a\n");
return false;
}
}
@@ -7902,7 +7641,7 @@ namespace basist
if (pal_index >= m_pGlobal_sel_codebook->size())
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 7z\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 7z\n");
return false;
}
@@ -7928,12 +7667,12 @@ namespace basist
basist::huffman_decoding_table uses_global_cb_bitflags_model;
if (!sym_codec.read_huffman_table(uses_global_cb_bitflags_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 7\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 7\n");
return false;
}
if (!uses_global_cb_bitflags_model.is_valid())
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 7a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 7a\n");
return false;
}
@@ -7942,12 +7681,12 @@ namespace basist
{
if (!sym_codec.read_huffman_table(global_mod_indices_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 8\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 8\n");
return false;
}
if (!global_mod_indices_model.is_valid())
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 8a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 8a\n");
return false;
}
}
@@ -7975,7 +7714,7 @@ namespace basist
if (pal_index >= m_pGlobal_sel_codebook->size())
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 8b\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 8b\n");
return false;
}
@@ -8022,13 +7761,13 @@ namespace basist
{
if (!sym_codec.read_huffman_table(delta_selector_pal_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 10\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10\n");
return false;
}
if ((num_selectors > 1) && (!delta_selector_pal_model.is_valid()))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 10a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10a\n");
return false;
}
@@ -8071,60 +7810,60 @@ namespace basist
return true;
}
- bool basisu_lowlevel_transcoder::decode_tables(const uint8_t* pTable_data, uint32_t table_data_size)
+ bool basisu_lowlevel_etc1s_transcoder::decode_tables(const uint8_t* pTable_data, uint32_t table_data_size)
{
basist::bitwise_decoder sym_codec;
if (!sym_codec.init(pTable_data, table_data_size))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 0\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 0\n");
return false;
}
if (!sym_codec.read_huffman_table(m_endpoint_pred_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 1\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 1\n");
return false;
}
if (m_endpoint_pred_model.get_code_sizes().size() == 0)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 1a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 1a\n");
return false;
}
if (!sym_codec.read_huffman_table(m_delta_endpoint_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 2\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 2\n");
return false;
}
if (m_delta_endpoint_model.get_code_sizes().size() == 0)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 2a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 2a\n");
return false;
}
if (!sym_codec.read_huffman_table(m_selector_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 3\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 3\n");
return false;
}
if (m_selector_model.get_code_sizes().size() == 0)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 3a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 3a\n");
return false;
}
if (!sym_codec.read_huffman_table(m_selector_history_buf_rle_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 4\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 4\n");
return false;
}
if (m_selector_history_buf_rle_model.get_code_sizes().size() == 0)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 4a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 4a\n");
return false;
}
@@ -8135,12 +7874,12 @@ namespace basist
return true;
}
- bool basisu_lowlevel_transcoder::transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
+ bool basisu_lowlevel_etc1s_transcoder::transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
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,
basisu_transcoder_state* pState, bool transcode_alpha, void *pAlpha_blocks, uint32_t output_rows_in_pixels)
{
- (void)transcode_alpha;
- (void)pAlpha_blocks;
+ BASISU_NOTE_UNUSED(transcode_alpha);
+ BASISU_NOTE_UNUSED(pAlpha_blocks);
if (!pState)
pState = &m_def_state;
@@ -8171,12 +7910,12 @@ namespace basist
if (is_video)
{
// TODO: Add check to make sure the caller hasn't tried skipping past p-frames
- const bool alpha_flag = (slice_desc.m_flags & cSliceDescFlagsIsAlphaData) != 0;
+ const bool alpha_flag = (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0;
const uint32_t level_index = slice_desc.m_level_index;
if (level_index >= basisu_transcoder_state::cMaxPrevFrameLevels)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: unsupported level_index\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: unsupported level_index\n");
return false;
}
@@ -8189,7 +7928,7 @@ namespace basist
if (!sym_codec.init(pImage_data, image_data_size))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: sym_codec.init failed\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: sym_codec.init failed\n");
return false;
}
@@ -8212,7 +7951,7 @@ namespace basist
pPVRTC_work_mem = malloc(num_blocks_x * num_blocks_y * (sizeof(decoder_etc_block) + sizeof(uint32_t)));
if (!pPVRTC_work_mem)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: malloc failed\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: malloc failed\n");
return false;
}
pPVRTC_endpoints = (uint32_t*) & ((decoder_etc_block*)pPVRTC_work_mem)[num_blocks_x * num_blocks_y];
@@ -8279,7 +8018,7 @@ namespace basist
// Left
if (!block_x)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (0)\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (0)\n");
if (pPVRTC_work_mem)
free(pPVRTC_work_mem);
return false;
@@ -8292,7 +8031,7 @@ namespace basist
// Upper
if (!block_y)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (1)\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (1)\n");
if (pPVRTC_work_mem)
free(pPVRTC_work_mem);
return false;
@@ -8314,7 +8053,7 @@ namespace basist
// Upper left
if ((!block_x) || (!block_y))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (2)\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (2)\n");
if (pPVRTC_work_mem)
free(pPVRTC_work_mem);
return false;
@@ -8363,7 +8102,7 @@ namespace basist
if (cur_selector_rle_count > total_blocks)
{
// The file is corrupted or we've got a bug.
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (3)\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (3)\n");
if (pPVRTC_work_mem)
free(pPVRTC_work_mem);
return false;
@@ -8384,7 +8123,7 @@ namespace basist
if (history_buf_index >= (int)selector_history_buf.size())
{
// The file is corrupted or we've got a bug.
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (4)\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (4)\n");
if (pPVRTC_work_mem)
free(pPVRTC_work_mem);
return false;
@@ -8407,7 +8146,7 @@ namespace basist
if ((endpoint_index >= m_endpoints.size()) || (selector_index >= m_selectors.size()))
{
// The file is corrupted or we've got a bug.
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (5)\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (5)\n");
if (pPVRTC_work_mem)
free(pPVRTC_work_mem);
return false;
@@ -8559,16 +8298,7 @@ namespace basist
break;
}
- case block_format::cBC7_M6_OPAQUE_ONLY:
- {
-#if BASISD_SUPPORT_BC7_MODE6_OPAQUE_ONLY
- void* pDst_block = static_cast(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;
- convert_etc1s_to_bc7_m6(static_cast(pDst_block), pEndpoints, pSelector);
-#else
- assert(0);
-#endif
- break;
- }
+ case block_format::cBC7: // for more consistency with UASTC
case block_format::cBC7_M5_COLOR:
{
#if BASISD_SUPPORT_BC7_MODE5
@@ -8648,7 +8378,7 @@ namespace basist
assert(transcode_alpha);
void* pDst_block = static_cast(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;
-
+
convert_etc1s_to_pvrtc2_rgba(pDst_block, pEndpoints, pSelector, &m_endpoints[0], &m_selectors[0]);
#endif
break;
@@ -8665,8 +8395,8 @@ namespace basist
assert(sizeof(uint32_t) == output_block_or_pixel_stride_in_bytes);
uint8_t* pDst_pixels = static_cast(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint32_t);
- const uint32_t max_x = basisu::minimum(4, output_row_pitch_in_blocks_or_pixels - block_x * 4);
- const uint32_t max_y = basisu::minimum(4, output_rows_in_pixels - block_y * 4);
+ const uint32_t max_x = basisu::minimum(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);
+ const uint32_t max_y = basisu::minimum(4, (int)output_rows_in_pixels - (int)block_y * 4);
int colors[4];
decoder_etc_block::get_block_colors5_g(colors, pEndpoints->m_color5, pEndpoints->m_inten5);
@@ -8705,8 +8435,8 @@ namespace basist
assert(sizeof(uint32_t) == output_block_or_pixel_stride_in_bytes);
uint8_t* pDst_pixels = static_cast(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint32_t);
- const uint32_t max_x = basisu::minimum(4, output_row_pitch_in_blocks_or_pixels - block_x * 4);
- const uint32_t max_y = basisu::minimum(4, output_rows_in_pixels - block_y * 4);
+ const uint32_t max_x = basisu::minimum(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);
+ const uint32_t max_y = basisu::minimum(4, (int)output_rows_in_pixels - (int)block_y * 4);
color32 colors[4];
decoder_etc_block::get_block_colors5(colors, pEndpoints->m_color5, pEndpoints->m_inten5);
@@ -8734,8 +8464,8 @@ namespace basist
assert(sizeof(uint32_t) == output_block_or_pixel_stride_in_bytes);
uint8_t* pDst_pixels = static_cast(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint32_t);
- const uint32_t max_x = basisu::minimum(4, output_row_pitch_in_blocks_or_pixels - block_x * 4);
- const uint32_t max_y = basisu::minimum(4, output_rows_in_pixels - block_y * 4);
+ const uint32_t max_x = basisu::minimum(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);
+ const uint32_t max_y = basisu::minimum(4, (int)output_rows_in_pixels - (int)block_y * 4);
color32 colors[4];
decoder_etc_block::get_block_colors5(colors, pEndpoints->m_color5, pEndpoints->m_inten5);
@@ -8765,8 +8495,8 @@ namespace basist
assert(sizeof(uint16_t) == output_block_or_pixel_stride_in_bytes);
uint8_t* pDst_pixels = static_cast(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint16_t);
- const uint32_t max_x = basisu::minimum(4, output_row_pitch_in_blocks_or_pixels - block_x * 4);
- const uint32_t max_y = basisu::minimum(4, output_rows_in_pixels - block_y * 4);
+ const uint32_t max_x = basisu::minimum(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);
+ const uint32_t max_y = basisu::minimum(4, (int)output_rows_in_pixels - (int)block_y * 4);
color32 colors[4];
decoder_etc_block::get_block_colors5(colors, pEndpoints->m_color5, pEndpoints->m_inten5);
@@ -8775,12 +8505,20 @@ namespace basist
if (fmt == block_format::cRGB565)
{
for (uint32_t i = 0; i < 4; i++)
- packed_colors[i] = static_cast(((colors[i].r >> 3) << 11) | ((colors[i].g >> 2) << 5) | (colors[i].b >> 3));
+ {
+ packed_colors[i] = static_cast((mul_8(colors[i].r, 31) << 11) | (mul_8(colors[i].g, 63) << 5) | mul_8(colors[i].b, 31));
+ if (IS_BIG_ENDIAN)
+ packed_colors[i] = byteswap_uint16(packed_colors[i]);
+ }
}
else
{
for (uint32_t i = 0; i < 4; i++)
- packed_colors[i] = static_cast(((colors[i].b >> 3) << 11) | ((colors[i].g >> 2) << 5) | (colors[i].r >> 3));
+ {
+ packed_colors[i] = static_cast((mul_8(colors[i].b, 31) << 11) | (mul_8(colors[i].g, 63) << 5) | mul_8(colors[i].r, 31));
+ if (IS_BIG_ENDIAN)
+ packed_colors[i] = byteswap_uint16(packed_colors[i]);
+ }
}
for (uint32_t y = 0; y < max_y; y++)
@@ -8800,15 +8538,17 @@ namespace basist
assert(sizeof(uint16_t) == output_block_or_pixel_stride_in_bytes);
uint8_t* pDst_pixels = static_cast(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint16_t);
- const uint32_t max_x = basisu::minimum(4, output_row_pitch_in_blocks_or_pixels - block_x * 4);
- const uint32_t max_y = basisu::minimum(4, output_rows_in_pixels - block_y * 4);
+ const uint32_t max_x = basisu::minimum(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);
+ const uint32_t max_y = basisu::minimum(4, (int)output_rows_in_pixels - (int)block_y * 4);
color32 colors[4];
decoder_etc_block::get_block_colors5(colors, pEndpoints->m_color5, pEndpoints->m_inten5);
uint16_t packed_colors[4];
for (uint32_t i = 0; i < 4; i++)
- packed_colors[i] = static_cast(((colors[i].r >> 4) << 12) | ((colors[i].g >> 4) << 8) | ((colors[i].b >> 4) << 4));
+ {
+ packed_colors[i] = static_cast((mul_8(colors[i].r, 15) << 12) | (mul_8(colors[i].g, 15) << 8) | (mul_8(colors[i].b, 15) << 4));
+ }
for (uint32_t y = 0; y < max_y; y++)
{
@@ -8817,7 +8557,14 @@ namespace basist
for (uint32_t x = 0; x < max_x; x++)
{
uint16_t cur = reinterpret_cast(pDst_pixels)[x];
+ if (IS_BIG_ENDIAN)
+ cur = byteswap_uint16(cur);
+
cur = (cur & 0xF) | packed_colors[(s >> (x * 2)) & 3];
+
+ if (IS_BIG_ENDIAN)
+ cur = byteswap_uint16(cur);
+
reinterpret_cast(pDst_pixels)[x] = cur;
}
@@ -8831,15 +8578,19 @@ namespace basist
assert(sizeof(uint16_t) == output_block_or_pixel_stride_in_bytes);
uint8_t* pDst_pixels = static_cast(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint16_t);
- const uint32_t max_x = basisu::minimum(4, output_row_pitch_in_blocks_or_pixels - block_x * 4);
- const uint32_t max_y = basisu::minimum(4, output_rows_in_pixels - block_y * 4);
+ const uint32_t max_x = basisu::minimum(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);
+ const uint32_t max_y = basisu::minimum(4, (int)output_rows_in_pixels - (int)block_y * 4);
color32 colors[4];
decoder_etc_block::get_block_colors5(colors, pEndpoints->m_color5, pEndpoints->m_inten5);
uint16_t packed_colors[4];
for (uint32_t i = 0; i < 4; i++)
- packed_colors[i] = static_cast(((colors[i].r >> 4) << 12) | ((colors[i].g >> 4) << 8) | ((colors[i].b >> 4) << 4) | 0xF);
+ {
+ packed_colors[i] = static_cast((mul_8(colors[i].r, 15) << 12) | (mul_8(colors[i].g, 15) << 8) | (mul_8(colors[i].b, 15) << 4) | 0xF);
+ if (IS_BIG_ENDIAN)
+ packed_colors[i] = byteswap_uint16(packed_colors[i]);
+ }
for (uint32_t y = 0; y < max_y; y++)
{
@@ -8858,22 +8609,28 @@ namespace basist
assert(sizeof(uint16_t) == output_block_or_pixel_stride_in_bytes);
uint8_t* pDst_pixels = static_cast(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint16_t);
- const uint32_t max_x = basisu::minimum(4, output_row_pitch_in_blocks_or_pixels - block_x * 4);
- const uint32_t max_y = basisu::minimum(4, output_rows_in_pixels - block_y * 4);
+ const uint32_t max_x = basisu::minimum(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);
+ const uint32_t max_y = basisu::minimum(4, (int)output_rows_in_pixels - (int)block_y * 4);
color32 colors[4];
decoder_etc_block::get_block_colors5(colors, pEndpoints->m_color5, pEndpoints->m_inten5);
uint16_t packed_colors[4];
for (uint32_t i = 0; i < 4; i++)
- packed_colors[i] = colors[i].g >> 4;
+ {
+ packed_colors[i] = mul_8(colors[i].g, 15);
+ if (IS_BIG_ENDIAN)
+ packed_colors[i] = byteswap_uint16(packed_colors[i]);
+ }
for (uint32_t y = 0; y < max_y; y++)
{
const uint32_t s = pSelector->m_selectors[y];
for (uint32_t x = 0; x < max_x; x++)
+ {
reinterpret_cast(pDst_pixels)[x] = packed_colors[(s >> (x * 2)) & 3];
+ }
pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint16_t);
}
@@ -8903,7 +8660,7 @@ namespace basist
if (endpoint_pred_repeat_count != 0)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: endpoint_pred_repeat_count != 0. The file is corrupted or this is a bug\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: endpoint_pred_repeat_count != 0. The file is corrupted or this is a bug\n");
return false;
}
@@ -8922,9 +8679,264 @@ namespace basist
return true;
}
+
+ basisu_lowlevel_uastc_transcoder::basisu_lowlevel_uastc_transcoder()
+ {
+ }
+ bool basisu_lowlevel_uastc_transcoder::transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
+ 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,
+ basisu_transcoder_state* pState, uint32_t output_rows_in_pixels, int channel0, int channel1, uint32_t decode_flags)
+ {
+ BASISU_NOTE_UNUSED(pState);
+ BASISU_NOTE_UNUSED(bc1_allow_threecolor_blocks);
+
+#if BASISD_SUPPORT_UASTC
+ const uint32_t total_blocks = num_blocks_x * num_blocks_y;
+
+ if (!output_row_pitch_in_blocks_or_pixels)
+ {
+ if (basis_block_format_is_uncompressed(fmt))
+ output_row_pitch_in_blocks_or_pixels = slice_desc.m_orig_width;
+ else
+ {
+ if (fmt == block_format::cFXT1_RGB)
+ output_row_pitch_in_blocks_or_pixels = (slice_desc.m_orig_width + 7) / 8;
+ else
+ output_row_pitch_in_blocks_or_pixels = num_blocks_x;
+ }
+ }
+
+ if (basis_block_format_is_uncompressed(fmt))
+ {
+ if (!output_rows_in_pixels)
+ output_rows_in_pixels = slice_desc.m_orig_height;
+ }
+
+ uint32_t total_expected_block_bytes = sizeof(uastc_block) * total_blocks;
+ if (image_data_size < total_expected_block_bytes)
+ {
+ BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_slice: image_data_size < total_expected_block_bytes The file is corrupted or this is a bug.\n");
+ return false;
+ }
+
+ const uastc_block* pSource_block = reinterpret_cast(pImage_data);
+
+ const bool high_quality = (decode_flags & cDecodeFlagsHighQuality) != 0;
+ const bool from_alpha = ((header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0) && (decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;
+
+ bool status = false;
+ if ((fmt == block_format::cPVRTC1_4_RGB) || (fmt == block_format::cPVRTC1_4_RGBA))
+ {
+ if (fmt == block_format::cPVRTC1_4_RGBA)
+ transcode_uastc_to_pvrtc1_4_rgba((const uastc_block*)pImage_data, pDst_blocks, num_blocks_x, num_blocks_y, high_quality);
+ else
+ transcode_uastc_to_pvrtc1_4_rgb((const uastc_block *)pImage_data, pDst_blocks, num_blocks_x, num_blocks_y, high_quality, from_alpha);
+ }
+ else
+ {
+ for (uint32_t block_y = 0; block_y < num_blocks_y; ++block_y)
+ {
+ void* pDst_block = (uint8_t*)pDst_blocks + block_y * output_row_pitch_in_blocks_or_pixels * output_block_or_pixel_stride_in_bytes;
+
+ for (uint32_t block_x = 0; block_x < num_blocks_x; ++block_x, ++pSource_block, pDst_block = (uint8_t *)pDst_block + output_block_or_pixel_stride_in_bytes)
+ {
+ switch (fmt)
+ {
+ case block_format::cETC1:
+ {
+ if (from_alpha)
+ status = transcode_uastc_to_etc1(*pSource_block, pDst_block, 3);
+ else
+ status = transcode_uastc_to_etc1(*pSource_block, pDst_block);
+ break;
+ }
+ case block_format::cETC2_RGBA:
+ {
+ status = transcode_uastc_to_etc2_rgba(*pSource_block, pDst_block);
+ break;
+ }
+ case block_format::cBC1:
+ {
+ status = transcode_uastc_to_bc1(*pSource_block, pDst_block, high_quality);
+ break;
+ }
+ case block_format::cBC3:
+ {
+ status = transcode_uastc_to_bc3(*pSource_block, pDst_block, high_quality);
+ break;
+ }
+ case block_format::cBC4:
+ {
+ if (channel0 < 0)
+ channel0 = 0;
+ status = transcode_uastc_to_bc4(*pSource_block, pDst_block, high_quality, channel0);
+ break;
+ }
+ case block_format::cBC5:
+ {
+ if (channel0 < 0)
+ channel0 = 0;
+ if (channel1 < 0)
+ channel1 = 3;
+ status = transcode_uastc_to_bc5(*pSource_block, pDst_block, high_quality, channel0, channel1);
+ break;
+ }
+ case block_format::cBC7:
+ case block_format::cBC7_M5_COLOR: // for consistently with ETC1S
+ {
+ status = transcode_uastc_to_bc7(*pSource_block, pDst_block);
+ break;
+ }
+ case block_format::cASTC_4x4:
+ {
+ status = transcode_uastc_to_astc(*pSource_block, pDst_block);
+ break;
+ }
+ case block_format::cETC2_EAC_R11:
+ {
+ if (channel0 < 0)
+ channel0 = 0;
+ status = transcode_uastc_to_etc2_eac_r11(*pSource_block, pDst_block, high_quality, channel0);
+ break;
+ }
+ case block_format::cETC2_EAC_RG11:
+ {
+ if (channel0 < 0)
+ channel0 = 0;
+ if (channel1 < 3)
+ channel1 = 3;
+ status = transcode_uastc_to_etc2_eac_rg11(*pSource_block, pDst_block, high_quality, channel0, channel1);
+ break;
+ }
+ case block_format::cRGBA32:
+ {
+ color32 block_pixels[4][4];
+ status = unpack_uastc(*pSource_block, (color32 *)block_pixels, false);
+
+ assert(sizeof(uint32_t) == output_block_or_pixel_stride_in_bytes);
+ uint8_t* pDst_pixels = static_cast(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint32_t);
+
+ const uint32_t max_x = basisu::minimum(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);
+ const uint32_t max_y = basisu::minimum(4, (int)output_rows_in_pixels - (int)block_y * 4);
+
+ for (uint32_t y = 0; y < max_y; y++)
+ {
+ for (uint32_t x = 0; x < max_x; x++)
+ {
+ const color32& c = block_pixels[y][x];
+
+ pDst_pixels[0 + 4 * x] = c.r;
+ pDst_pixels[1 + 4 * x] = c.g;
+ pDst_pixels[2 + 4 * x] = c.b;
+ pDst_pixels[3 + 4 * x] = c.a;
+ }
+
+ pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint32_t);
+ }
+
+ break;
+ }
+ case block_format::cRGB565:
+ case block_format::cBGR565:
+ {
+ color32 block_pixels[4][4];
+ status = unpack_uastc(*pSource_block, (color32*)block_pixels, false);
+
+ assert(sizeof(uint16_t) == output_block_or_pixel_stride_in_bytes);
+ uint8_t* pDst_pixels = static_cast(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint16_t);
+
+ const uint32_t max_x = basisu::minimum(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);
+ const uint32_t max_y = basisu::minimum(4, (int)output_rows_in_pixels - (int)block_y * 4);
+
+ for (uint32_t y = 0; y < max_y; y++)
+ {
+ for (uint32_t x = 0; x < max_x; x++)
+ {
+ const color32& c = block_pixels[y][x];
+
+ const uint16_t packed = (fmt == block_format::cRGB565) ? static_cast((mul_8(c.r, 31) << 11) | (mul_8(c.g, 63) << 5) | mul_8(c.b, 31)) :
+ static_cast((mul_8(c.b, 31) << 11) | (mul_8(c.g, 63) << 5) | mul_8(c.r, 31));
+
+ pDst_pixels[x * 2 + 0] = (uint8_t)(packed & 0xFF);
+ pDst_pixels[x * 2 + 1] = (uint8_t)((packed >> 8) & 0xFF);
+ }
+
+ pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint16_t);
+ }
+
+ break;
+ }
+ case block_format::cRGBA4444:
+ {
+ color32 block_pixels[4][4];
+ status = unpack_uastc(*pSource_block, (color32*)block_pixels, false);
+
+ assert(sizeof(uint16_t) == output_block_or_pixel_stride_in_bytes);
+ uint8_t* pDst_pixels = static_cast(pDst_blocks) + (block_x * 4 + block_y * 4 * output_row_pitch_in_blocks_or_pixels) * sizeof(uint16_t);
+
+ const uint32_t max_x = basisu::minimum(4, (int)output_row_pitch_in_blocks_or_pixels - (int)block_x * 4);
+ const uint32_t max_y = basisu::minimum(4, (int)output_rows_in_pixels - (int)block_y * 4);
+
+ for (uint32_t y = 0; y < max_y; y++)
+ {
+ for (uint32_t x = 0; x < max_x; x++)
+ {
+ const color32& c = block_pixels[y][x];
+
+ const uint16_t packed = static_cast((mul_8(c.r, 15) << 12) | (mul_8(c.g, 15) << 8) | (mul_8(c.b, 15) << 4) | mul_8(c.a, 15));
+
+ pDst_pixels[x * 2 + 0] = (uint8_t)(packed & 0xFF);
+ pDst_pixels[x * 2 + 1] = (uint8_t)((packed >> 8) & 0xFF);
+ }
+
+ pDst_pixels += output_row_pitch_in_blocks_or_pixels * sizeof(uint16_t);
+ }
+ break;
+ }
+ default:
+ assert(0);
+ break;
+
+ }
+
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_slice: Transcoder failed to unpack a UASTC block - this is a bug, or the data was corrupted\n");
+ return false;
+ }
+
+ } // block_x
+
+ } // block_y
+ }
+
+ return true;
+#else
+ BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_slice: UASTC is unsupported\n");
+
+ BASISU_NOTE_UNUSED(decode_flags);
+ BASISU_NOTE_UNUSED(channel0);
+ BASISU_NOTE_UNUSED(channel1);
+ BASISU_NOTE_UNUSED(output_rows_in_pixels);
+ BASISU_NOTE_UNUSED(output_row_pitch_in_blocks_or_pixels);
+ BASISU_NOTE_UNUSED(slice_desc);
+ BASISU_NOTE_UNUSED(header);
+ BASISU_NOTE_UNUSED(output_block_or_pixel_stride_in_bytes);
+ BASISU_NOTE_UNUSED(fmt);
+ BASISU_NOTE_UNUSED(image_data_size);
+ BASISU_NOTE_UNUSED(pImage_data);
+ BASISU_NOTE_UNUSED(num_blocks_x);
+ BASISU_NOTE_UNUSED(num_blocks_y);
+ BASISU_NOTE_UNUSED(pDst_blocks);
+
+ return false;
+#endif
+ }
+
basisu_transcoder::basisu_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook) :
- m_lowlevel_decoder(pGlobal_sel_codebook)
+ m_lowlevel_etc1s_decoder(pGlobal_sel_codebook),
+ m_ready_to_transcode(false)
{
}
@@ -9027,22 +9039,33 @@ namespace basist
return false;
}
- if (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices)
+ if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)
{
- if (pHeader->m_total_slices & 1)
+ if (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid alpha basis file\n");
+ if (pHeader->m_total_slices & 1)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid alpha .basis file\n");
+ return false;
+ }
+ }
+
+ // This flag dates back to pre-Basis Universal, when .basis supported full ETC1 too.
+ if ((pHeader->m_flags & cBASISHeaderFlagETC1S) == 0)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: Invalid .basis file (ETC1S check)\n");
return false;
}
}
-
- if ((pHeader->m_flags & cBASISHeaderFlagETC1S) == 0)
+ else
{
- // We only support ETC1S in basis universal
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid basis file (ETC1S flag check)\n");
- return false;
+ if ((pHeader->m_flags & cBASISHeaderFlagETC1S) != 0)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: Invalid .basis file (ETC1S check)\n");
+ return false;
+ }
}
-
+
if ((pHeader->m_slice_desc_file_ofs >= data_size) ||
((data_size - pHeader->m_slice_desc_file_ofs) < (sizeof(basis_slice_desc) * pHeader->m_total_slices))
)
@@ -9103,6 +9126,19 @@ namespace basist
return pHeader->m_total_images;
}
+ basis_tex_format basisu_transcoder::get_tex_format(const void* pData, uint32_t data_size) const
+ {
+ if (!validate_header_quick(pData, data_size))
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: header validation failed\n");
+ return basis_tex_format::cETC1S;
+ }
+
+ const basis_file_header* pHeader = static_cast(pData);
+
+ return (basis_tex_format)(uint32_t)pHeader->m_tex_format;
+ }
+
bool basisu_transcoder::get_image_info(const void* pData, uint32_t data_size, basisu_image_info& image_info, uint32_t image_index) const
{
if (!validate_header_quick(pData, data_size))
@@ -9145,8 +9181,17 @@ namespace basist
image_info.m_image_index = image_index;
image_info.m_total_levels = total_levels;
- image_info.m_alpha_flag = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0;
+
+ image_info.m_alpha_flag = false;
+
+ // For ETC1S, if anything has alpha all images have alpha. For UASTC, we only report alpha when the image actually has alpha.
+ if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)
+ image_info.m_alpha_flag = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0;
+ else
+ image_info.m_alpha_flag = (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0;
+
image_info.m_iframe_flag = (slice_desc.m_flags & cSliceDescFlagsFrameIsIFrame) != 0;
+
image_info.m_width = slice_desc.m_num_blocks_x * 4;
image_info.m_height = slice_desc.m_num_blocks_y * 4;
image_info.m_orig_width = slice_desc.m_orig_width;
@@ -9264,7 +9309,13 @@ namespace basist
image_info.m_image_index = image_index;
image_info.m_level_index = level_index;
- image_info.m_alpha_flag = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0;
+
+ // For ETC1S, if anything has alpha all images have alpha. For UASTC, we only report alpha when the image actually has alpha.
+ if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)
+ image_info.m_alpha_flag = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0;
+ else
+ image_info.m_alpha_flag = (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0;
+
image_info.m_iframe_flag = (slice_desc.m_flags & cSliceDescFlagsFrameIsIFrame) != 0;
image_info.m_width = slice_desc.m_num_blocks_x * 4;
image_info.m_height = slice_desc.m_num_blocks_y * 4;
@@ -9301,7 +9352,10 @@ namespace basist
file_info.m_tables_size = pHeader->m_tables_file_size;
- file_info.m_etc1s = (pHeader->m_flags & cBASISHeaderFlagETC1S) != 0;
+ file_info.m_tex_format = static_cast(static_cast(pHeader->m_tex_format));
+
+ file_info.m_etc1s = (pHeader->m_tex_format == (int)basis_tex_format::cETC1S);
+
file_info.m_y_flipped = (pHeader->m_flags & cBASISHeaderFlagYFlipped) != 0;
file_info.m_has_alpha_slices = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0;
@@ -9346,7 +9400,7 @@ namespace basist
slice_info.m_image_index = pSlice_descs[i].m_image_index;
slice_info.m_level_index = pSlice_descs[i].m_level_index;
slice_info.m_unpacked_slice_crc16 = pSlice_descs[i].m_slice_data_crc16;
- slice_info.m_alpha_flag = (pSlice_descs[i].m_flags & cSliceDescFlagsIsAlphaData) != 0;
+ slice_info.m_alpha_flag = (pSlice_descs[i].m_flags & cSliceDescFlagsHasAlpha) != 0;
slice_info.m_iframe_flag = (pSlice_descs[i].m_flags & cSliceDescFlagsFrameIsIFrame) != 0;
if (pSlice_descs[i].m_image_index >= pHeader->m_total_images)
@@ -9366,15 +9420,9 @@ namespace basist
return true;
}
-
- bool basisu_transcoder::start_transcoding(const void* pData, uint32_t data_size) const
+
+ bool basisu_transcoder::start_transcoding(const void* pData, uint32_t data_size)
{
- if (m_lowlevel_decoder.m_endpoints.size())
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: already called start_transcoding\n");
- return true;
- }
-
if (!validate_header_quick(pData, data_size))
{
BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: header validation failed\n");
@@ -9382,59 +9430,85 @@ namespace basist
}
const basis_file_header* pHeader = reinterpret_cast(pData);
-
const uint8_t* pDataU8 = static_cast(pData);
- if (!pHeader->m_endpoint_cb_file_size || !pHeader->m_selector_cb_file_size || !pHeader->m_tables_file_size)
+ if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted (0)\n");
- }
+ if (m_lowlevel_etc1s_decoder.m_endpoints.size())
+ {
+ m_lowlevel_etc1s_decoder.clear();
+ }
- if ((pHeader->m_endpoint_cb_file_ofs > data_size) || (pHeader->m_selector_cb_file_ofs > data_size) || (pHeader->m_tables_file_ofs > data_size))
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (1)\n");
- return false;
- }
+ if (!pHeader->m_endpoint_cb_file_size || !pHeader->m_selector_cb_file_size || !pHeader->m_tables_file_size)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted (0)\n");
+ }
- if (pHeader->m_endpoint_cb_file_size > (data_size - pHeader->m_endpoint_cb_file_ofs))
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (2)\n");
- return false;
- }
+ if ((pHeader->m_endpoint_cb_file_ofs > data_size) || (pHeader->m_selector_cb_file_ofs > data_size) || (pHeader->m_tables_file_ofs > data_size))
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (1)\n");
+ return false;
+ }
- if (pHeader->m_selector_cb_file_size > (data_size - pHeader->m_selector_cb_file_ofs))
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (3)\n");
- return false;
- }
+ if (pHeader->m_endpoint_cb_file_size > (data_size - pHeader->m_endpoint_cb_file_ofs))
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (2)\n");
+ return false;
+ }
- if (pHeader->m_tables_file_size > (data_size - pHeader->m_tables_file_ofs))
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (3)\n");
- return false;
- }
+ if (pHeader->m_selector_cb_file_size > (data_size - pHeader->m_selector_cb_file_ofs))
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (3)\n");
+ return false;
+ }
- if (!m_lowlevel_decoder.decode_palettes(
- pHeader->m_total_endpoints, pDataU8 + pHeader->m_endpoint_cb_file_ofs, pHeader->m_endpoint_cb_file_size,
- pHeader->m_total_selectors, pDataU8 + pHeader->m_selector_cb_file_ofs, pHeader->m_selector_cb_file_size))
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: decode_palettes failed\n");
- return false;
- }
+ if (pHeader->m_tables_file_size > (data_size - pHeader->m_tables_file_ofs))
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (3)\n");
+ return false;
+ }
- if (!m_lowlevel_decoder.decode_tables(pDataU8 + pHeader->m_tables_file_ofs, pHeader->m_tables_file_size))
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: decode_tables failed\n");
- return false;
+ if (!m_lowlevel_etc1s_decoder.decode_palettes(
+ pHeader->m_total_endpoints, pDataU8 + pHeader->m_endpoint_cb_file_ofs, pHeader->m_endpoint_cb_file_size,
+ pHeader->m_total_selectors, pDataU8 + pHeader->m_selector_cb_file_ofs, pHeader->m_selector_cb_file_size))
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: decode_palettes failed\n");
+ return false;
+ }
+
+ if (!m_lowlevel_etc1s_decoder.decode_tables(pDataU8 + pHeader->m_tables_file_ofs, pHeader->m_tables_file_size))
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: decode_tables failed\n");
+ return false;
+ }
}
+ else
+ {
+ // Nothing special to do for UASTC.
+ if (m_lowlevel_etc1s_decoder.m_endpoints.size())
+ {
+ m_lowlevel_etc1s_decoder.clear();
+ }
+ }
+
+ m_ready_to_transcode = true;
return true;
}
- bool basisu_transcoder::transcode_slice(const void* pData, uint32_t data_size, uint32_t slice_index, void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels, block_format fmt,
- uint32_t output_block_or_pixel_stride_in_bytes, uint32_t decode_flags, uint32_t output_row_pitch_in_blocks_or_pixels, basisu_transcoder_state* pState, void *pAlpha_blocks, uint32_t output_rows_in_pixels) const
+ bool basisu_transcoder::stop_transcoding()
{
- if (!m_lowlevel_decoder.m_endpoints.size())
+ m_lowlevel_etc1s_decoder.clear();
+
+ m_ready_to_transcode = false;
+
+ return true;
+ }
+
+ bool basisu_transcoder::transcode_slice(const void* pData, uint32_t data_size, uint32_t slice_index, void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels, block_format fmt,
+ uint32_t output_block_or_pixel_stride_in_bytes, uint32_t decode_flags, uint32_t output_row_pitch_in_blocks_or_pixels, basisu_transcoder_state* pState, void *pAlpha_blocks, uint32_t output_rows_in_pixels, int channel0, int channel1) const
+ {
+ if (!m_ready_to_transcode)
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: must call start_transcoding first\n");
return false;
@@ -9529,16 +9603,26 @@ namespace basist
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: invalid slice_desc.m_file_size, or passed in buffer too small\n");
return false;
}
-
- return m_lowlevel_decoder.transcode_slice(pOutput_blocks, slice_desc.m_num_blocks_x, slice_desc.m_num_blocks_y,
- pDataU8 + slice_desc.m_file_ofs, slice_desc.m_file_size,
- fmt, output_block_or_pixel_stride_in_bytes, (decode_flags & cDecodeFlagsBC1ForbidThreeColorBlocks) == 0, *pHeader, slice_desc, output_row_pitch_in_blocks_or_pixels, pState,
- (decode_flags & cDecodeFlagsOutputHasAlphaIndices) != 0, pAlpha_blocks, output_rows_in_pixels);
+
+ if (pHeader->m_tex_format == (int)basis_tex_format::cUASTC4x4)
+ {
+ return m_lowlevel_uastc_decoder.transcode_slice(pOutput_blocks, slice_desc.m_num_blocks_x, slice_desc.m_num_blocks_y,
+ pDataU8 + slice_desc.m_file_ofs, slice_desc.m_file_size,
+ fmt, output_block_or_pixel_stride_in_bytes, (decode_flags & cDecodeFlagsBC1ForbidThreeColorBlocks) == 0, *pHeader, slice_desc, output_row_pitch_in_blocks_or_pixels, pState,
+ output_rows_in_pixels, channel0, channel1, decode_flags);
+ }
+ else
+ {
+ return m_lowlevel_etc1s_decoder.transcode_slice(pOutput_blocks, slice_desc.m_num_blocks_x, slice_desc.m_num_blocks_y,
+ pDataU8 + slice_desc.m_file_ofs, slice_desc.m_file_size,
+ fmt, output_block_or_pixel_stride_in_bytes, (decode_flags & cDecodeFlagsBC1ForbidThreeColorBlocks) == 0, *pHeader, slice_desc, output_row_pitch_in_blocks_or_pixels, pState,
+ (decode_flags & cDecodeFlagsOutputHasAlphaIndices) != 0, pAlpha_blocks, output_rows_in_pixels);
+ }
}
int basisu_transcoder::find_first_slice_index(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index) const
{
- (void)data_size;
+ BASISU_NOTE_UNUSED(data_size);
const basis_file_header* pHeader = reinterpret_cast(pData);
const uint8_t* pDataU8 = static_cast(pData);
@@ -9576,9 +9660,16 @@ namespace basist
const basis_slice_desc& slice_desc = pSlice_descs[slice_iter];
if ((slice_desc.m_image_index == image_index) && (slice_desc.m_level_index == level_index))
{
- const bool slice_alpha = (slice_desc.m_flags & cSliceDescFlagsIsAlphaData) != 0;
- if (slice_alpha == alpha_data)
+ if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)
+ {
+ const bool slice_alpha = (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0;
+ if (slice_alpha == alpha_data)
+ return slice_iter;
+ }
+ else
+ {
return slice_iter;
+ }
}
}
@@ -9606,8 +9697,7 @@ namespace basist
blk.m_table = 13;
// Selectors are all 4's
- static const uint8_t s_etc2_eac_a8_sel4[6] = { 0x92, 0x49, 0x24, 0x92, 0x49, 0x24 };
- memcpy(&blk.m_selectors, s_etc2_eac_a8_sel4, sizeof(s_etc2_eac_a8_sel4));
+ memcpy(&blk.m_selectors, g_etc2_eac_a8_sel4, sizeof(g_etc2_eac_a8_sel4));
for (uint32_t y = 0; y < num_blocks_y; y++)
{
@@ -9648,9 +9738,9 @@ namespace basist
transcoder_texture_format fmt,
uint32_t decode_flags, uint32_t output_row_pitch_in_blocks_or_pixels, basisu_transcoder_state *pState, uint32_t output_rows_in_pixels) const
{
- const uint32_t bytes_per_block = basis_get_bytes_per_block(fmt);
+ const uint32_t bytes_per_block_or_pixel = basis_get_bytes_per_block_or_pixel(fmt);
- if (!m_lowlevel_decoder.m_endpoints.size())
+ if (!m_ready_to_transcode)
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: must call start_transcoding() first\n");
return false;
@@ -9693,37 +9783,40 @@ namespace basist
fmt = transcoder_texture_format::cTFPVRTC1_4_RGB;
}
- if (pSlice_descs[slice_index].m_flags & cSliceDescFlagsIsAlphaData)
+ if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file has out of order alpha slice\n");
-
- // The first slice shouldn't have alpha data in a properly formed basis file
- return false;
- }
-
- if (basis_file_has_alpha_slices)
- {
- // The alpha data should immediately follow the color data, and have the same resolution.
- if ((slice_index + 1U) >= pHeader->m_total_slices)
+ if (pSlice_descs[slice_index].m_flags & cSliceDescFlagsHasAlpha)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file has missing alpha slice\n");
- // basis file is missing the alpha slice
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file has out of order alpha slice\n");
+
+ // The first slice shouldn't have alpha data in a properly formed basis file
return false;
}
- // Basic sanity checks
- if ((pSlice_descs[slice_index + 1].m_flags & cSliceDescFlagsIsAlphaData) == 0)
+ if (basis_file_has_alpha_slices)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file has missing alpha slice (flag check)\n");
- // This slice should have alpha data
- return false;
- }
+ // The alpha data should immediately follow the color data, and have the same resolution.
+ if ((slice_index + 1U) >= pHeader->m_total_slices)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file has missing alpha slice\n");
+ // basis file is missing the alpha slice
+ return false;
+ }
- if ((pSlice_descs[slice_index].m_num_blocks_x != pSlice_descs[slice_index + 1].m_num_blocks_x) || (pSlice_descs[slice_index].m_num_blocks_y != pSlice_descs[slice_index + 1].m_num_blocks_y))
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file slice dimensions bad\n");
- // Alpha slice should have been the same res as the color slice
- return false;
+ // Basic sanity checks
+ if ((pSlice_descs[slice_index + 1].m_flags & cSliceDescFlagsHasAlpha) == 0)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file has missing alpha slice (flag check)\n");
+ // This slice should have alpha data
+ return false;
+ }
+
+ if ((pSlice_descs[slice_index].m_num_blocks_x != pSlice_descs[slice_index + 1].m_num_blocks_x) || (pSlice_descs[slice_index].m_num_blocks_y != pSlice_descs[slice_index + 1].m_num_blocks_y))
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file slice dimensions bad\n");
+ // Alpha slice should have been the same res as the color slice
+ return false;
+ }
}
}
@@ -9735,518 +9828,729 @@ namespace basist
{
// The transcoder doesn't write beyond total_slice_blocks, so we need to clear the rest ourselves.
// For GL usage, PVRTC1 4bpp image size is (max(width, 8)* max(height, 8) * 4 + 7) / 8.
- // However, for KTX and internally in Basis this formula isn't used, it's just ((width+3)/4) * ((height+3)/4) * bytes_per_block. This is all the transcoder actually writes to memory.
- memset(static_cast(pOutput_blocks) + total_slice_blocks * bytes_per_block, 0, (output_blocks_buf_size_in_blocks_or_pixels - total_slice_blocks) * bytes_per_block);
+ // However, for KTX and internally in Basis this formula isn't used, it's just ((width+3)/4) * ((height+3)/4) * bytes_per_block_or_pixel. This is all the transcoder actually writes to memory.
+ memset(static_cast(pOutput_blocks) + total_slice_blocks * bytes_per_block_or_pixel, 0, (output_blocks_buf_size_in_blocks_or_pixels - total_slice_blocks) * bytes_per_block_or_pixel);
}
-
- switch (fmt)
+
+ if (pHeader->m_tex_format == (int)basis_tex_format::cUASTC4x4)
{
- case transcoder_texture_format::cTFETC1_RGB:
- {
- uint32_t slice_index_to_decode = slice_index;
- // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
- if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
- slice_index_to_decode++;
-
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, bytes_per_block, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- if (!status)
+ // UASTC4x4
+ switch (fmt)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC1 failed\n");
+ case transcoder_texture_format::cTFETC1_RGB:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC1 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFETC2_RGBA:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_RGBA, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFBC1_RGB:
+ {
+ // TODO: ETC1S allows BC1 from alpha channel. That doesn't seem actually useful, though.
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC1 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFBC3_RGBA:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC3, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC3 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFBC4_R:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState,
+ nullptr, 0,
+ ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats)) ? 3 : 0);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC4 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFBC5_RG:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC5, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState,
+ nullptr, 0,
+ 0, 3);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC5 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFBC7_RGBA:
+ case transcoder_texture_format::cTFBC7_ALT:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC7 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFPVRTC1_4_RGB:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 RGB 4bpp failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFPVRTC1_4_RGBA:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGBA, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 RGBA 4bpp failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFASTC_4x4_RGBA:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ASTC 4x4 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFATC_RGB:
+ case transcoder_texture_format::cTFATC_RGBA:
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: UASTC->ATC currently unsupported\n");
+ return false;
+ }
+ case transcoder_texture_format::cTFFXT1_RGB:
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: UASTC->FXT1 currently unsupported\n");
+ return false;
+ }
+ case transcoder_texture_format::cTFPVRTC2_4_RGB:
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: UASTC->PVRTC2 currently unsupported\n");
+ return false;
+ }
+ case transcoder_texture_format::cTFPVRTC2_4_RGBA:
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: UASTC->PVRTC2 currently unsupported\n");
+ return false;
+ }
+ case transcoder_texture_format::cTFETC2_EAC_R11:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState,
+ nullptr, 0,
+ ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats)) ? 3 : 0);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to EAC R11 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFETC2_EAC_RG11:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_RG11, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState,
+ nullptr, 0,
+ 0, 3);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to EAC RG11 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFRGBA32:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGBA32, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA32 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFRGB565:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGB565, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGB565 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFBGR565:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBGR565, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGB565 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFRGBA4444:
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGBA4444, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA4444 failed\n");
+ }
+ break;
+ }
+ default:
+ {
+ assert(0);
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: Invalid fmt\n");
+ break;
+ }
}
- break;
}
- case transcoder_texture_format::cTFBC1_RGB:
+ else
{
+ // ETC1S
+
+ switch (fmt)
+ {
+ case transcoder_texture_format::cTFETC1_RGB:
+ {
+ uint32_t slice_index_to_decode = slice_index;
+ // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
+ if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
+ slice_index_to_decode++;
+
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC1 failed\n");
+ }
+ break;
+ }
+ case transcoder_texture_format::cTFBC1_RGB:
+ {
#if !BASISD_SUPPORT_DXT1
- return false;
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: DXT1 unsupported\n");
+ return false;
#endif
- uint32_t slice_index_to_decode = slice_index;
- // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
- if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
- slice_index_to_decode++;
+ uint32_t slice_index_to_decode = slice_index;
+ // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
+ if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
+ slice_index_to_decode++;
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, bytes_per_block, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- if (!status)
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC1 failed\n");
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC1 failed\n");
+ }
+ break;
}
- break;
- }
- case transcoder_texture_format::cTFBC4_R:
- {
+ case transcoder_texture_format::cTFBC4_R:
+ {
#if !BASISD_SUPPORT_DXT5A
- return false;
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: DXT5A unsupported\n");
+ return false;
#endif
- uint32_t slice_index_to_decode = slice_index;
- // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
- if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
- slice_index_to_decode++;
+ uint32_t slice_index_to_decode = slice_index;
+ // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
+ if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
+ slice_index_to_decode++;
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, bytes_per_block, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- if (!status)
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC4 failed\n");
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC4 failed\n");
+ }
+ break;
}
- break;
- }
- case transcoder_texture_format::cTFPVRTC1_4_RGB:
- {
+ case transcoder_texture_format::cTFPVRTC1_4_RGB:
+ {
#if !BASISD_SUPPORT_PVRTC1
- return false;
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: PVRTC1 unsupported\n");
+ return false;
#endif
- uint32_t slice_index_to_decode = slice_index;
- // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
- if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
- slice_index_to_decode++;
+ uint32_t slice_index_to_decode = slice_index;
+ // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
+ if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
+ slice_index_to_decode++;
- // output_row_pitch_in_blocks_or_pixels is actually ignored because we're transcoding to PVRTC1. (Print a dev warning if it's != 0?)
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGB, bytes_per_block, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- if (!status)
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 4 RGB failed\n");
- }
- break;
- }
- case transcoder_texture_format::cTFPVRTC1_4_RGBA:
- {
-#if !BASISD_SUPPORT_PVRTC1
- return false;
-#endif
- assert(basis_file_has_alpha_slices);
-
- // Temp buffer to hold alpha block endpoint/selector indices
- std::vector temp_block_indices(total_slice_blocks);
-
- // First transcode alpha data to temp buffer
- status = transcode_slice(pData, data_size, slice_index + 1, &temp_block_indices[0], total_slice_blocks, block_format::cIndices, sizeof(uint32_t), decode_flags, pSlice_descs[slice_index].m_num_blocks_x, pState);
- if (!status)
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 4 RGBA failed (0)\n");
- }
- else
- {
// output_row_pitch_in_blocks_or_pixels is actually ignored because we're transcoding to PVRTC1. (Print a dev warning if it's != 0?)
- status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGBA, bytes_per_block, decode_flags, output_row_pitch_in_blocks_or_pixels, pState, &temp_block_indices[0]);
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
if (!status)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 4 RGBA failed (1)\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 4 RGB failed\n");
}
+ break;
}
-
- break;
- }
- case transcoder_texture_format::cTFBC7_M6_RGB:
- {
-#if !BASISD_SUPPORT_BC7_MODE6_OPAQUE_ONLY
- return false;
+ case transcoder_texture_format::cTFPVRTC1_4_RGBA:
+ {
+#if !BASISD_SUPPORT_PVRTC1
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: PVRTC1 unsupported\n");
+ return false;
#endif
- uint32_t slice_index_to_decode = slice_index;
- // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
- if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
- slice_index_to_decode++;
+ assert(basis_file_has_alpha_slices);
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7_M6_OPAQUE_ONLY, bytes_per_block, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- if (!status)
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC7 m6 opaque only failed\n");
- }
- break;
- }
- case transcoder_texture_format::cTFBC7_M5_RGBA:
- {
-#if !BASISD_SUPPORT_BC7_MODE5
- return false;
-#else
- assert(bytes_per_block == 16);
+ // Temp buffer to hold alpha block endpoint/selector indices
+ std::vector temp_block_indices(total_slice_blocks);
- // First transcode the color slice. The cBC7_M5_COLOR transcoder will output opaque mode 5 blocks.
- status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7_M5_COLOR, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-
- if ((status) && (basis_file_has_alpha_slices))
- {
- // Now transcode the alpha slice. The cBC7_M5_ALPHA transcoder will now change the opaque mode 5 blocks to blocks with alpha.
- status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7_M5_ALPHA, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- }
-
- break;
-#endif
- }
- case transcoder_texture_format::cTFETC2_RGBA:
- {
-#if !BASISD_SUPPORT_ETC2_EAC_A8
- return false;
-#endif
- assert(bytes_per_block == 16);
-
- if (basis_file_has_alpha_slices)
- {
- // First decode the alpha data
- status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_A8, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- }
- else
- {
- write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_A8, 16, output_row_pitch_in_blocks_or_pixels);
- status = true;
- }
-
- if (status)
- {
- // Now decode the color data
- status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ // First transcode alpha data to temp buffer
+ status = transcode_slice(pData, data_size, slice_index + 1, &temp_block_indices[0], total_slice_blocks, block_format::cIndices, sizeof(uint32_t), decode_flags, pSlice_descs[slice_index].m_num_blocks_x, pState);
if (!status)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2 RGB failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 4 RGBA failed (0)\n");
}
- }
- else
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2 A failed\n");
- }
- break;
- }
- case transcoder_texture_format::cTFBC3_RGBA:
- {
-#if !BASISD_SUPPORT_DXT1
- return false;
-#endif
-#if !BASISD_SUPPORT_DXT5A
- return false;
-#endif
- assert(bytes_per_block == 16);
-
- // First decode the alpha data
- if (basis_file_has_alpha_slices)
- {
- status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- }
- else
- {
- write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);
- status = true;
- }
-
- if (status)
- {
- // Now decode the color data. Forbid 3 color blocks, which aren't allowed in BC3.
- status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, 16, decode_flags | cDecodeFlagsBC1ForbidThreeColorBlocks, output_row_pitch_in_blocks_or_pixels, pState);
- if (!status)
+ else
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC3 RGB failed\n");
- }
- }
- else
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC3 A failed\n");
- }
-
- break;
- }
- case transcoder_texture_format::cTFBC5_RG:
- {
-#if !BASISD_SUPPORT_DXT5A
- return false;
-#endif
- assert(bytes_per_block == 16);
-
- // Decode the R data (actually the green channel of the color data slice in the basis file)
- status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- if (status)
- {
- if (basis_file_has_alpha_slices)
- {
- // Decode the G data (actually the green channel of the alpha data slice in the basis file)
- status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ // output_row_pitch_in_blocks_or_pixels is actually ignored because we're transcoding to PVRTC1. (Print a dev warning if it's != 0?)
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGBA, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState, &temp_block_indices[0]);
if (!status)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC5 1 failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 4 RGBA failed (1)\n");
+ }
+ }
+
+ break;
+ }
+ case transcoder_texture_format::cTFBC7_RGBA:
+ case transcoder_texture_format::cTFBC7_ALT:
+ {
+#if !BASISD_SUPPORT_BC7_MODE5
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: BC7 unsupported\n");
+ return false;
+#else
+ assert(bytes_per_block_or_pixel == 16);
+ // We used to support transcoding just alpha to BC7 - but is that useful at all?
+
+ // First transcode the color slice. The cBC7_M5_COLOR transcoder will output opaque mode 5 blocks.
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7_M5_COLOR, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+
+ if ((status) && (basis_file_has_alpha_slices))
+ {
+ // Now transcode the alpha slice. The cBC7_M5_ALPHA transcoder will now change the opaque mode 5 blocks to blocks with alpha.
+ status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7_M5_ALPHA, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ }
+
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC7 failed (0)\n");
+ }
+
+ break;
+#endif
+ }
+ case transcoder_texture_format::cTFETC2_RGBA:
+ {
+#if !BASISD_SUPPORT_ETC2_EAC_A8
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: ETC2 EAC A8 unsupported\n");
+ return false;
+#endif
+ assert(bytes_per_block_or_pixel == 16);
+
+ if (basis_file_has_alpha_slices)
+ {
+ // First decode the alpha data
+ status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_A8, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ }
+ else
+ {
+ write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_A8, 16, output_row_pitch_in_blocks_or_pixels);
+ status = true;
+ }
+
+ if (status)
+ {
+ // Now decode the color data
+ status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2 RGB failed\n");
}
}
else
{
- write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);
- status = true;
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2 A failed\n");
}
+ break;
}
- else
+ case transcoder_texture_format::cTFBC3_RGBA:
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC5 channel 0 failed\n");
- }
- break;
- }
- case transcoder_texture_format::cTFASTC_4x4_RGBA:
- {
-#if !BASISD_SUPPORT_ASTC
- return false;
-#endif
- assert(bytes_per_block == 16);
-
- if (basis_file_has_alpha_slices)
- {
- // First decode the alpha data to the output (we're using the output texture as a temp buffer here).
- status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cIndices, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- if (!status)
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to failed\n");
- }
- else
- {
- // Now decode the color data and transcode to ASTC. The transcoder function will read the alpha selector data from the output texture as it converts and
- // transcode both the alpha and color data at the same time to ASTC.
- status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, 16, decode_flags | cDecodeFlagsOutputHasAlphaIndices, output_row_pitch_in_blocks_or_pixels, pState);
- }
- }
- else
- status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-
- break;
- }
- case transcoder_texture_format::cTFATC_RGB:
- {
-#if !BASISD_SUPPORT_ATC
- return false;
-#endif
- uint32_t slice_index_to_decode = slice_index;
- // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
- if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
- slice_index_to_decode++;
-
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cATC_RGB, bytes_per_block, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- if (!status)
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ATC_RGB failed\n");
- }
- break;
- }
- case transcoder_texture_format::cTFATC_RGBA:
- {
-#if !BASISD_SUPPORT_ATC
- return false;
+#if !BASISD_SUPPORT_DXT1
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: DXT1 unsupported\n");
+ return false;
#endif
#if !BASISD_SUPPORT_DXT5A
- return false;
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: DXT5A unsupported\n");
+ return false;
#endif
- assert(bytes_per_block == 16);
+ assert(bytes_per_block_or_pixel == 16);
- // First decode the alpha data
- if (basis_file_has_alpha_slices)
- {
- status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- }
- else
- {
- write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);
- status = true;
- }
-
- if (status)
- {
- status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cATC_RGB, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- if (!status)
+ // First decode the alpha data
+ if (basis_file_has_alpha_slices)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ATC RGB failed\n");
- }
- }
- else
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ATC A failed\n");
- }
- break;
- }
- case transcoder_texture_format::cTFPVRTC2_4_RGB:
- {
-#if !BASISD_SUPPORT_PVRTC2
- return false;
-#endif
- uint32_t slice_index_to_decode = slice_index;
- // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
- if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
- slice_index_to_decode++;
-
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGB, bytes_per_block, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- if (!status)
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to cPVRTC2_4_RGB failed\n");
- }
- break;
- }
- case transcoder_texture_format::cTFPVRTC2_4_RGBA:
- {
-#if !BASISD_SUPPORT_PVRTC2
- return false;
-#endif
- if (basis_file_has_alpha_slices)
- {
- // First decode the alpha data to the output (we're using the output texture as a temp buffer here).
- status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cIndices, bytes_per_block, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- if (!status)
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to failed\n");
+ status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
}
else
{
- // Now decode the color data and transcode to PVRTC2 RGBA.
- status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGBA, bytes_per_block, decode_flags | cDecodeFlagsOutputHasAlphaIndices, output_row_pitch_in_blocks_or_pixels, pState);
+ write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);
+ status = true;
}
+
+ if (status)
+ {
+ // Now decode the color data. Forbid 3 color blocks, which aren't allowed in BC3.
+ status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, 16, decode_flags | cDecodeFlagsBC1ForbidThreeColorBlocks, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC3 RGB failed\n");
+ }
+ }
+ else
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC3 A failed\n");
+ }
+
+ break;
}
- else
- status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGB, bytes_per_block, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-
- if (!status)
+ case transcoder_texture_format::cTFBC5_RG:
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to cPVRTC2_4_RGBA failed\n");
+#if !BASISD_SUPPORT_DXT5A
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: DXT5A unsupported\n");
+ return false;
+#endif
+ assert(bytes_per_block_or_pixel == 16);
+
+ // Decode the R data (actually the green channel of the color data slice in the basis file)
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (status)
+ {
+ if (basis_file_has_alpha_slices)
+ {
+ // Decode the G data (actually the green channel of the alpha data slice in the basis file)
+ status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC5 1 failed\n");
+ }
+ }
+ else
+ {
+ write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);
+ status = true;
+ }
+ }
+ else
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC5 channel 0 failed\n");
+ }
+ break;
}
-
- break;
- }
- case transcoder_texture_format::cTFRGBA32:
- {
- // Raw 32bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.
-
- // First decode the alpha data
- if (basis_file_has_alpha_slices)
- status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cA32, sizeof(uint32_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
- else
- status = true;
-
- if (status)
+ case transcoder_texture_format::cTFASTC_4x4_RGBA:
{
- status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, basis_file_has_alpha_slices ? block_format::cRGB32 : block_format::cRGBA32, sizeof(uint32_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
+#if !BASISD_SUPPORT_ASTC
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: ASTC unsupported\n");
+ return false;
+#endif
+ assert(bytes_per_block_or_pixel == 16);
+
+ if (basis_file_has_alpha_slices)
+ {
+ // First decode the alpha data to the output (we're using the output texture as a temp buffer here).
+ status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cIndices, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (status)
+ {
+ // Now decode the color data and transcode to ASTC. The transcoder function will read the alpha selector data from the output texture as it converts and
+ // transcode both the alpha and color data at the same time to ASTC.
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, 16, decode_flags | cDecodeFlagsOutputHasAlphaIndices, output_row_pitch_in_blocks_or_pixels, pState);
+ }
+ }
+ else
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ASTC failed (0)\n");
+ }
+
+ break;
+ }
+ case transcoder_texture_format::cTFATC_RGB:
+ {
+#if !BASISD_SUPPORT_ATC
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: ATC unsupported\n");
+ return false;
+#endif
+ uint32_t slice_index_to_decode = slice_index;
+ // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
+ if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
+ slice_index_to_decode++;
+
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cATC_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
if (!status)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA32 RGB failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ATC_RGB failed\n");
}
+ break;
}
- else
+ case transcoder_texture_format::cTFATC_RGBA:
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA32 A failed\n");
+#if !BASISD_SUPPORT_ATC
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: ATC unsupported\n");
+ return false;
+#endif
+#if !BASISD_SUPPORT_DXT5A
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: DXT5A unsupported\n");
+ return false;
+#endif
+ assert(bytes_per_block_or_pixel == 16);
+
+ // First decode the alpha data
+ if (basis_file_has_alpha_slices)
+ {
+ status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ }
+ else
+ {
+ write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);
+ status = true;
+ }
+
+ if (status)
+ {
+ status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cATC_RGB, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ATC RGB failed\n");
+ }
+ }
+ else
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ATC A failed\n");
+ }
+ break;
}
-
- break;
- }
- case transcoder_texture_format::cTFRGB565:
- case transcoder_texture_format::cTFBGR565:
- {
- // Raw 16bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.
-
- uint32_t slice_index_to_decode = slice_index;
- // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
- if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
- slice_index_to_decode++;
-
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, (fmt == transcoder_texture_format::cTFRGB565) ? block_format::cRGB565 : block_format::cBGR565, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
- if (!status)
+ case transcoder_texture_format::cTFPVRTC2_4_RGB:
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGB565 RGB failed\n");
- }
+#if !BASISD_SUPPORT_PVRTC2
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: PVRTC2 unsupported\n");
+ return false;
+#endif
+ uint32_t slice_index_to_decode = slice_index;
+ // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
+ if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
+ slice_index_to_decode++;
- break;
- }
- case transcoder_texture_format::cTFRGBA4444:
- {
- // Raw 16bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.
-
- // First decode the alpha data
- if (basis_file_has_alpha_slices)
- status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGBA4444_ALPHA, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
- else
- status = true;
-
- if (status)
- {
- status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, basis_file_has_alpha_slices ? block_format::cRGBA4444_COLOR : block_format::cRGBA4444_COLOR_OPAQUE, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
if (!status)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA4444 RGB failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to cPVRTC2_4_RGB failed\n");
}
+ break;
}
- else
+ case transcoder_texture_format::cTFPVRTC2_4_RGBA:
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA4444 A failed\n");
- }
+#if !BASISD_SUPPORT_PVRTC2
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: PVRTC2 unsupported\n");
+ return false;
+#endif
+ if (basis_file_has_alpha_slices)
+ {
+ // First decode the alpha data to the output (we're using the output texture as a temp buffer here).
+ status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cIndices, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to failed\n");
+ }
+ else
+ {
+ // Now decode the color data and transcode to PVRTC2 RGBA.
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGBA, bytes_per_block_or_pixel, decode_flags | cDecodeFlagsOutputHasAlphaIndices, output_row_pitch_in_blocks_or_pixels, pState);
+ }
+ }
+ else
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- break;
- }
- case transcoder_texture_format::cTFFXT1_RGB:
- {
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to cPVRTC2_4_RGBA failed\n");
+ }
+
+ break;
+ }
+ case transcoder_texture_format::cTFRGBA32:
+ {
+ // Raw 32bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.
+
+ // First decode the alpha data
+ if (basis_file_has_alpha_slices)
+ status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cA32, sizeof(uint32_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
+ else
+ status = true;
+
+ if (status)
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, basis_file_has_alpha_slices ? block_format::cRGB32 : block_format::cRGBA32, sizeof(uint32_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA32 RGB failed\n");
+ }
+ }
+ else
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA32 A failed\n");
+ }
+
+ break;
+ }
+ case transcoder_texture_format::cTFRGB565:
+ case transcoder_texture_format::cTFBGR565:
+ {
+ // Raw 16bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.
+
+ uint32_t slice_index_to_decode = slice_index;
+ // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
+ if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
+ slice_index_to_decode++;
+
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, (fmt == transcoder_texture_format::cTFRGB565) ? block_format::cRGB565 : block_format::cBGR565, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGB565 RGB failed\n");
+ }
+
+ break;
+ }
+ case transcoder_texture_format::cTFRGBA4444:
+ {
+ // Raw 16bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.
+
+ // First decode the alpha data
+ if (basis_file_has_alpha_slices)
+ status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGBA4444_ALPHA, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
+ else
+ status = true;
+
+ if (status)
+ {
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, basis_file_has_alpha_slices ? block_format::cRGBA4444_COLOR : block_format::cRGBA4444_COLOR_OPAQUE, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA4444 RGB failed\n");
+ }
+ }
+ else
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA4444 A failed\n");
+ }
+
+ break;
+ }
+ case transcoder_texture_format::cTFFXT1_RGB:
+ {
#if !BASISD_SUPPORT_FXT1
- return false;
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: FXT1 unsupported\n");
+ return false;
#endif
- uint32_t slice_index_to_decode = slice_index;
- // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
- if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
- slice_index_to_decode++;
+ uint32_t slice_index_to_decode = slice_index;
+ // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
+ if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
+ slice_index_to_decode++;
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cFXT1_RGB, bytes_per_block, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- if (!status)
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to FXT1_RGB failed\n");
- }
- break;
- }
- case transcoder_texture_format::cTFETC2_EAC_R11:
- {
-#if !BASISD_SUPPORT_ETC2_EAC_RG11
- return false;
-#endif
- uint32_t slice_index_to_decode = slice_index;
- // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
- if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
- slice_index_to_decode++;
-
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, bytes_per_block, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- if (!status)
- {
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2_EAC_R11 failed\n");
- }
-
- break;
- }
- case transcoder_texture_format::cTFETC2_EAC_RG11:
- {
-#if !BASISD_SUPPORT_ETC2_EAC_RG11
- return false;
-#endif
- assert(bytes_per_block == 16);
-
- if (basis_file_has_alpha_slices)
- {
- // First decode the alpha data to G
- status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t *)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
- }
- else
- {
- write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, (uint8_t *)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, 16, output_row_pitch_in_blocks_or_pixels);
- status = true;
- }
-
- if (status)
- {
- // Now decode the color data to R
- status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cFXT1_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
if (!status)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2_EAC_R11 R failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to FXT1_RGB failed\n");
}
+ break;
}
- else
+ case transcoder_texture_format::cTFETC2_EAC_R11:
{
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2_EAC_R11 G failed\n");
- }
+#if !BASISD_SUPPORT_ETC2_EAC_RG11
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: EAC_RG11 unsupported\n");
+ return false;
+#endif
+ uint32_t slice_index_to_decode = slice_index;
+ // If the caller wants us to transcode the mip level's alpha data, then use the next slice.
+ if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
+ slice_index_to_decode++;
- break;
- }
- default:
- {
- assert(0);
- BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: Invalid fmt\n");
- break;
- }
- }
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2_EAC_R11 failed\n");
+ }
+
+ break;
+ }
+ case transcoder_texture_format::cTFETC2_EAC_RG11:
+ {
+#if !BASISD_SUPPORT_ETC2_EAC_RG11
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: EAC_RG11 unsupported\n");
+ return false;
+#endif
+ assert(bytes_per_block_or_pixel == 16);
+
+ if (basis_file_has_alpha_slices)
+ {
+ // First decode the alpha data to G
+ status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ }
+ else
+ {
+ write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, 16, output_row_pitch_in_blocks_or_pixels);
+ status = true;
+ }
+
+ if (status)
+ {
+ // Now decode the color data to R
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2_EAC_R11 R failed\n");
+ }
+ }
+ else
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2_EAC_R11 G failed\n");
+ }
+
+ break;
+ }
+ default:
+ {
+ assert(0);
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: Invalid fmt\n");
+ break;
+ }
+ }
+ } // if (pHeader->m_tex_format == (int)basis_tex_format::cUASTC4x4)
+
+ if (!status)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: Returning false\n");
+ }
+ else
+ {
+ //BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: Returning true\n");
+ }
return status;
}
- uint32_t basis_get_bytes_per_block(transcoder_texture_format fmt)
+ uint32_t basis_get_bytes_per_block_or_pixel(transcoder_texture_format fmt)
{
switch (fmt)
{
@@ -10260,8 +10564,8 @@ namespace basist
case transcoder_texture_format::cTFPVRTC2_4_RGBA:
case transcoder_texture_format::cTFETC2_EAC_R11:
return 8;
- case transcoder_texture_format::cTFBC7_M6_RGB:
- case transcoder_texture_format::cTFBC7_M5_RGBA:
+ case transcoder_texture_format::cTFBC7_RGBA:
+ case transcoder_texture_format::cTFBC7_ALT:
case transcoder_texture_format::cTFETC2_RGBA:
case transcoder_texture_format::cTFBC3_RGBA:
case transcoder_texture_format::cTFBC5_RG:
@@ -10271,11 +10575,11 @@ namespace basist
case transcoder_texture_format::cTFETC2_EAC_RG11:
return 16;
case transcoder_texture_format::cTFRGBA32:
- return sizeof(uint32_t) * 16;
+ return sizeof(uint32_t);
case transcoder_texture_format::cTFRGB565:
case transcoder_texture_format::cTFBGR565:
case transcoder_texture_format::cTFRGBA4444:
- return sizeof(uint16_t) * 16;
+ return sizeof(uint16_t);
default:
assert(0);
BASISU_DEVEL_ERROR("basis_get_basisu_texture_format: Invalid fmt\n");
@@ -10293,8 +10597,8 @@ namespace basist
case transcoder_texture_format::cTFBC4_R: return "BC4_R";
case transcoder_texture_format::cTFPVRTC1_4_RGB: return "PVRTC1_4_RGB";
case transcoder_texture_format::cTFPVRTC1_4_RGBA: return "PVRTC1_4_RGBA";
- case transcoder_texture_format::cTFBC7_M6_RGB: return "BC7_M6_RGB";
- case transcoder_texture_format::cTFBC7_M5_RGBA: return "BC7_M5_RGBA";
+ case transcoder_texture_format::cTFBC7_RGBA: return "BC7_RGBA";
+ case transcoder_texture_format::cTFBC7_ALT: return "BC7_RGBA";
case transcoder_texture_format::cTFETC2_RGBA: return "ETC2_RGBA";
case transcoder_texture_format::cTFBC3_RGBA: return "BC3_RGBA";
case transcoder_texture_format::cTFBC5_RG: return "BC5_RG";
@@ -10342,7 +10646,8 @@ namespace basist
case transcoder_texture_format::cTFETC2_RGBA:
case transcoder_texture_format::cTFBC3_RGBA:
case transcoder_texture_format::cTFASTC_4x4_RGBA:
- case transcoder_texture_format::cTFBC7_M5_RGBA:
+ case transcoder_texture_format::cTFBC7_RGBA:
+ case transcoder_texture_format::cTFBC7_ALT:
case transcoder_texture_format::cTFPVRTC1_4_RGBA:
case transcoder_texture_format::cTFPVRTC2_4_RGBA:
case transcoder_texture_format::cTFATC_RGBA:
@@ -10364,8 +10669,8 @@ namespace basist
case transcoder_texture_format::cTFBC4_R: return basisu::texture_format::cBC4;
case transcoder_texture_format::cTFPVRTC1_4_RGB: return basisu::texture_format::cPVRTC1_4_RGB;
case transcoder_texture_format::cTFPVRTC1_4_RGBA: return basisu::texture_format::cPVRTC1_4_RGBA;
- case transcoder_texture_format::cTFBC7_M6_RGB: return basisu::texture_format::cBC7;
- case transcoder_texture_format::cTFBC7_M5_RGBA: return basisu::texture_format::cBC7;
+ case transcoder_texture_format::cTFBC7_RGBA: return basisu::texture_format::cBC7;
+ case transcoder_texture_format::cTFBC7_ALT: return basisu::texture_format::cBC7;
case transcoder_texture_format::cTFETC2_RGBA: return basisu::texture_format::cETC2_RGBA;
case transcoder_texture_format::cTFBC3_RGBA: return basisu::texture_format::cBC3;
case transcoder_texture_format::cTFBC5_RG: return basisu::texture_format::cBC5;
@@ -10404,15 +10709,16 @@ namespace basist
return false;
}
- bool basis_block_format_is_uncompressed(block_format tex_type)
+ bool basis_block_format_is_uncompressed(block_format blk_fmt)
{
- switch (tex_type)
+ switch (blk_fmt)
{
case block_format::cRGB32:
case block_format::cRGBA32:
case block_format::cA32:
case block_format::cRGB565:
case block_format::cBGR565:
+ case block_format::cRGBA4444:
case block_format::cRGBA4444_COLOR:
case block_format::cRGBA4444_ALPHA:
case block_format::cRGBA4444_COLOR_OPAQUE:
@@ -10453,80 +10759,5551 @@ namespace basist
uint32_t basis_get_block_height(transcoder_texture_format tex_type)
{
- (void)tex_type;
+ BASISU_NOTE_UNUSED(tex_type);
return 4;
}
- bool basis_is_format_supported(transcoder_texture_format tex_type)
+ bool basis_is_format_supported(transcoder_texture_format tex_type, basis_tex_format fmt)
{
- switch (tex_type)
+ if (fmt == basis_tex_format::cUASTC4x4)
{
- // ETC1 and uncompressed are always supported.
- case transcoder_texture_format::cTFETC1_RGB:
- case transcoder_texture_format::cTFRGBA32:
- case transcoder_texture_format::cTFRGB565:
- case transcoder_texture_format::cTFBGR565:
- case transcoder_texture_format::cTFRGBA4444:
- return true;
+#if BASISD_SUPPORT_UASTC
+ switch (tex_type)
+ {
+ // These niche formats aren't currently supported for UASTC - everything else is.
+ case transcoder_texture_format::cTFPVRTC2_4_RGB:
+ case transcoder_texture_format::cTFPVRTC2_4_RGBA:
+ case transcoder_texture_format::cTFATC_RGB:
+ case transcoder_texture_format::cTFATC_RGBA:
+ case transcoder_texture_format::cTFFXT1_RGB:
+ return false;
+ default:
+ return true;
+ }
+#endif
+ }
+ else
+ {
+ switch (tex_type)
+ {
+ // ETC1 and uncompressed are always supported.
+ case transcoder_texture_format::cTFETC1_RGB:
+ case transcoder_texture_format::cTFRGBA32:
+ case transcoder_texture_format::cTFRGB565:
+ case transcoder_texture_format::cTFBGR565:
+ case transcoder_texture_format::cTFRGBA4444:
+ return true;
#if BASISD_SUPPORT_DXT1
- case transcoder_texture_format::cTFBC1_RGB:
- return true;
+ case transcoder_texture_format::cTFBC1_RGB:
+ return true;
#endif
#if BASISD_SUPPORT_DXT5A
- case transcoder_texture_format::cTFBC4_R:
- case transcoder_texture_format::cTFBC5_RG:
- return true;
+ case transcoder_texture_format::cTFBC4_R:
+ case transcoder_texture_format::cTFBC5_RG:
+ return true;
#endif
#if BASISD_SUPPORT_DXT1 && BASISD_SUPPORT_DXT5A
- case transcoder_texture_format::cTFBC3_RGBA:
- return true;
+ case transcoder_texture_format::cTFBC3_RGBA:
+ return true;
#endif
#if BASISD_SUPPORT_PVRTC1
- case transcoder_texture_format::cTFPVRTC1_4_RGB:
- case transcoder_texture_format::cTFPVRTC1_4_RGBA:
- return true;
-#endif
-#if BASISD_SUPPORT_BC7_MODE6_OPAQUE_ONLY
- case transcoder_texture_format::cTFBC7_M6_RGB:
- return true;
+ case transcoder_texture_format::cTFPVRTC1_4_RGB:
+ case transcoder_texture_format::cTFPVRTC1_4_RGBA:
+ return true;
#endif
#if BASISD_SUPPORT_BC7_MODE5
- case transcoder_texture_format::cTFBC7_M5_RGBA:
- return true;
+ case transcoder_texture_format::cTFBC7_RGBA:
+ case transcoder_texture_format::cTFBC7_ALT:
+ return true;
#endif
#if BASISD_SUPPORT_ETC2_EAC_A8
- case transcoder_texture_format::cTFETC2_RGBA:
- return true;
+ case transcoder_texture_format::cTFETC2_RGBA:
+ return true;
#endif
#if BASISD_SUPPORT_ASTC
- case transcoder_texture_format::cTFASTC_4x4_RGBA:
- return true;
+ case transcoder_texture_format::cTFASTC_4x4_RGBA:
+ return true;
#endif
#if BASISD_SUPPORT_ATC
- case transcoder_texture_format::cTFATC_RGB:
- case transcoder_texture_format::cTFATC_RGBA:
- return true;
+ case transcoder_texture_format::cTFATC_RGB:
+ case transcoder_texture_format::cTFATC_RGBA:
+ return true;
#endif
#if BASISD_SUPPORT_FXT1
- case transcoder_texture_format::cTFFXT1_RGB:
- return true;
+ case transcoder_texture_format::cTFFXT1_RGB:
+ return true;
#endif
#if BASISD_SUPPORT_PVRTC2
- case transcoder_texture_format::cTFPVRTC2_4_RGB:
- case transcoder_texture_format::cTFPVRTC2_4_RGBA:
- return true;
+ case transcoder_texture_format::cTFPVRTC2_4_RGB:
+ case transcoder_texture_format::cTFPVRTC2_4_RGBA:
+ return true;
#endif
#if BASISD_SUPPORT_ETC2_EAC_RG11
- case transcoder_texture_format::cTFETC2_EAC_R11:
- case transcoder_texture_format::cTFETC2_EAC_RG11:
- return true;
+ case transcoder_texture_format::cTFETC2_EAC_R11:
+ case transcoder_texture_format::cTFETC2_EAC_RG11:
+ return true;
#endif
- default:
- break;
+ default:
+ break;
+ }
}
return false;
}
+ // ------------------------------------------------------------------------------------------------------
+ // UASTC
+ // ------------------------------------------------------------------------------------------------------
+
+#if BASISD_SUPPORT_UASTC
+ const astc_bc7_common_partition2_desc g_astc_bc7_common_partitions2[TOTAL_ASTC_BC7_COMMON_PARTITIONS2] =
+ {
+ { 0, 28, false }, { 1, 20, false }, { 2, 16, true }, { 3, 29, false },
+ { 4, 91, true }, { 5, 9, false }, { 6, 107, true }, { 7, 72, true },
+ { 8, 149, false }, { 9, 204, true }, { 10, 50, false }, { 11, 114, true },
+ { 12, 496, true }, { 13, 17, true }, { 14, 78, false }, { 15, 39, true },
+ { 17, 252, true }, { 18, 828, true }, { 19, 43, false }, { 20, 156, false },
+ { 21, 116, false }, { 22, 210, true }, { 23, 476, true }, { 24, 273, false },
+ { 25, 684, true }, { 26, 359, false }, { 29, 246, true }, { 32, 195, true },
+ { 33, 694, true }, { 52, 524, true }
+ };
+
+ const bc73_astc2_common_partition_desc g_bc7_3_astc2_common_partitions[TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS] =
+ {
+ { 10, 36, 4 }, { 11, 48, 4 }, { 0, 61, 3 }, { 2, 137, 4 },
+ { 8, 161, 5 }, { 13, 183, 4 }, { 1, 226, 2 }, { 33, 281, 2 },
+ { 40, 302, 3 }, { 20, 307, 4 }, { 21, 479, 0 }, { 58, 495, 3 },
+ { 3, 593, 0 }, { 32, 594, 2 }, { 59, 605, 1 }, { 34, 799, 3 },
+ { 20, 812, 1 }, { 14, 988, 4 }, { 31, 993, 3 }
+ };
+
+ const astc_bc7_common_partition3_desc g_astc_bc7_common_partitions3[TOTAL_ASTC_BC7_COMMON_PARTITIONS3] =
+ {
+ { 4, 260, 0 }, { 8, 74, 5 }, { 9, 32, 5 }, { 10, 156, 2 },
+ { 11, 183, 2 }, { 12, 15, 0 }, { 13, 745, 4 }, { 20, 0, 1 },
+ { 35, 335, 1 }, { 36, 902, 5 }, { 57, 254, 0 }
+ };
+
+ const uint8_t g_astc_to_bc7_partition_index_perm_tables[6][3] = { { 0, 1, 2 }, { 1, 2, 0 }, { 2, 0, 1 }, { 2, 1, 0 }, { 0, 2, 1 }, { 1, 0, 2 } };
+
+ const uint8_t g_bc7_to_astc_partition_index_perm_tables[6][3] = { { 0, 1, 2 }, { 2, 0, 1 }, { 1, 2, 0 }, { 2, 1, 0 }, { 0, 2, 1 }, { 1, 0, 2 } };
+
+ uint32_t bc7_convert_partition_index_3_to_2(uint32_t p, uint32_t k)
+ {
+ assert(k < 6);
+ switch (k >> 1)
+ {
+ case 0:
+ if (p <= 1)
+ p = 0;
+ else
+ p = 1;
+ break;
+ case 1:
+ if (p == 0)
+ p = 0;
+ else
+ p = 1;
+ break;
+ case 2:
+ if ((p == 0) || (p == 2))
+ p = 0;
+ else
+ p = 1;
+ break;
+ }
+ if (k & 1)
+ p = 1 - p;
+ return p;
+ }
+
+ static const uint8_t g_zero_pattern[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ const uint8_t g_astc_bc7_patterns2[TOTAL_ASTC_BC7_COMMON_PARTITIONS2][16] =
+ {
+ { 0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1 }, { 0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1 }, { 1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0 }, { 0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1 },
+ { 1,1,1,1,1,1,1,0,1,1,1,0,1,1,0,0 }, { 0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1 }, { 1,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0 }, { 1,1,1,1,1,1,1,0,1,1,0,0,1,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1 }, { 1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1 }, { 1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0 },
+ { 1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0 }, { 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1 }, { 1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0 },
+ { 1,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1 }, { 1,1,1,1,1,1,1,1,0,1,1,1,0,0,0,1 }, { 0,1,1,1,0,0,1,1,0,0,0,1,0,0,0,0 }, { 0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0 }, { 1,1,1,1,1,1,1,1,0,1,1,1,0,0,1,1 }, { 1,0,0,0,1,1,0,0,1,1,0,0,1,1,1,0 }, { 0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0 },
+ { 1,1,1,1,0,1,1,1,0,1,1,1,0,0,1,1 }, { 0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0 }, { 1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1 }, { 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0 },
+ { 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0 }, { 1,0,0,1,0,0,1,1,0,1,1,0,1,1,0,0 }
+ };
+
+ const uint8_t g_astc_bc7_patterns3[TOTAL_ASTC_BC7_COMMON_PARTITIONS3][16] =
+ {
+ { 0,0,0,0,0,0,0,0,1,1,2,2,1,1,2,2 }, { 1,1,1,1,1,1,1,1,0,0,0,0,2,2,2,2 }, { 1,1,1,1,0,0,0,0,0,0,0,0,2,2,2,2 }, { 1,1,1,1,2,2,2,2,0,0,0,0,0,0,0,0 },
+ { 1,1,2,0,1,1,2,0,1,1,2,0,1,1,2,0 }, { 0,1,1,2,0,1,1,2,0,1,1,2,0,1,1,2 }, { 0,2,1,1,0,2,1,1,0,2,1,1,0,2,1,1 }, { 2,0,0,0,2,0,0,0,2,1,1,1,2,1,1,1 },
+ { 2,0,1,2,2,0,1,2,2,0,1,2,2,0,1,2 }, { 1,1,1,1,0,0,0,0,2,2,2,2,1,1,1,1 }, { 0,0,2,2,0,0,1,1,0,0,1,1,0,0,2,2 }
+ };
+
+ const uint8_t g_bc7_3_astc2_patterns2[TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS][16] =
+ {
+ { 0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0 }, { 0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0 }, { 1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1 },
+ { 1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1 }, { 0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0 }, { 0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1 }, { 0,1,1,1,0,0,1,1,0,0,1,1,0,0,1,1 },
+ { 1,1,0,0,0,0,0,0,0,0,1,1,1,1,0,0 }, { 0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,0 }, { 1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0 },
+ { 0,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1 }, { 1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0 }, { 1,1,0,0,1,1,0,0,1,1,0,0,1,0,0,0 },
+ { 1,1,1,1,1,1,1,1,1,0,0,0,1,0,0,0 }, { 0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,0 }, { 1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0 }
+ };
+
+ const uint8_t g_astc_bc7_pattern2_anchors[TOTAL_ASTC_BC7_COMMON_PARTITIONS2][3] =
+ {
+ { 0, 2 }, { 0, 3 }, { 1, 0 }, { 0, 3 }, { 7, 0 }, { 0, 2 }, { 3, 0 }, { 7, 0 },
+ { 0, 11 }, { 2, 0 }, { 0, 7 }, { 11, 0 }, { 3, 0 }, { 8, 0 }, { 0, 4 }, { 12, 0 },
+ { 1, 0 }, { 8, 0 }, { 0, 1 }, { 0, 2 }, { 0, 4 }, { 8, 0 }, { 1, 0 }, { 0, 2 },
+ { 4, 0 }, { 0, 1 }, { 4, 0 }, { 1, 0 }, { 4, 0 }, { 1, 0 }
+ };
+
+ const uint8_t g_astc_bc7_pattern3_anchors[TOTAL_ASTC_BC7_COMMON_PARTITIONS3][3] =
+ {
+ { 0, 8, 10 }, { 8, 0, 12 }, { 4, 0, 12 }, { 8, 0, 4 }, { 3, 0, 2 }, { 0, 1, 3 }, { 0, 2, 1 }, { 1, 9, 0 }, { 1, 2, 0 }, { 4, 0, 8 }, { 0, 6, 2 }
+ };
+
+ const uint8_t g_bc7_3_astc2_patterns2_anchors[TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS][3] =
+ {
+ { 0, 4 }, { 0, 2 }, { 2, 0 }, { 0, 7 }, { 8, 0 }, { 0, 1 }, { 0, 3 }, { 0, 1 }, { 2, 0 }, { 0, 1 }, { 0, 8 }, { 2, 0 }, { 0, 1 }, { 0, 7 }, { 12, 0 }, { 2, 0 }, { 9, 0 }, { 0, 2 }, { 4, 0 }
+ };
+
+ const uint32_t g_uastc_mode_huff_codes[TOTAL_UASTC_MODES + 1][2] =
+ {
+ { 0x1, 4 },
+ { 0x35, 6 },
+ { 0x1D, 5 },
+ { 0x3, 5 },
+
+ { 0x13, 5 },
+ { 0xB, 5 },
+ { 0x1B, 5 },
+ { 0x7, 5 },
+
+ { 0x17, 5 },
+ { 0xF, 5 },
+ { 0x2, 3 },
+ { 0x0, 2 },
+
+ { 0x6, 3 },
+ { 0x1F, 5 },
+ { 0xD, 5 },
+ { 0x5, 7 },
+
+ { 0x15, 6 },
+ { 0x25, 6 },
+ { 0x9, 4 },
+ { 0x45, 7 } // future expansion
+ };
+
+ // If g_uastc_mode_huff_codes[] changes this table must be updated!
+ static const uint8_t g_uastc_huff_modes[128] =
+ {
+ 11,0,10,3,11,15,12,7,11,18,10,5,11,14,12,9,11,0,10,4,11,16,12,8,11,18,10,6,11,2,12,13,11,0,10,3,11,17,12,7,11,18,10,5,11,14,12,9,11,0,10,4,11,1,12,8,11,18,10,6,11,2,12,13,11,0,10,3,11,
+ 19,12,7,11,18,10,5,11,14,12,9,11,0,10,4,11,16,12,8,11,18,10,6,11,2,12,13,11,0,10,3,11,17,12,7,11,18,10,5,11,14,12,9,11,0,10,4,11,1,12,8,11,18,10,6,11,2,12,13
+ };
+
+ const uint8_t g_uastc_mode_weight_bits[TOTAL_UASTC_MODES] = { 4, 2, 3, 2, 2, 3, 2, 2, 0, 2, 4, 2, 3, 1, 2, 4, 2, 2, 5 };
+ const uint8_t g_uastc_mode_weight_ranges[TOTAL_UASTC_MODES] = { 8, 2, 5, 2, 2, 5, 2, 2, 0, 2, 8, 2, 5, 0, 2, 8, 2, 2, 11 };
+ const uint8_t g_uastc_mode_endpoint_ranges[TOTAL_UASTC_MODES] = { 19, 20, 8, 7, 12, 20, 18, 12, 0, 8, 13, 13, 19, 20, 20, 20, 20, 20, 11 };
+ const uint8_t g_uastc_mode_subsets[TOTAL_UASTC_MODES] = { 1, 1, 2, 3, 2, 1, 1, 2, 0, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1 };
+ const uint8_t g_uastc_mode_planes[TOTAL_UASTC_MODES] = { 1, 1, 1, 1, 1, 1, 2, 1, 0, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1 };
+ const uint8_t g_uastc_mode_comps[TOTAL_UASTC_MODES] = { 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 3 };
+ const uint8_t g_uastc_mode_has_etc1_bias[TOTAL_UASTC_MODES] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 };
+ const uint8_t g_uastc_mode_has_bc1_hint0[TOTAL_UASTC_MODES] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
+ const uint8_t g_uastc_mode_has_bc1_hint1[TOTAL_UASTC_MODES] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 };
+ const uint8_t g_uastc_mode_cem[TOTAL_UASTC_MODES] = { 8, 8, 8, 8, 8, 8, 8, 8, 0, 12, 12, 12, 12, 12, 12, 4, 4, 4, 8 };
+ const uint8_t g_uastc_mode_has_alpha[TOTAL_UASTC_MODES] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 };
+ const uint8_t g_uastc_mode_is_la[TOTAL_UASTC_MODES] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0 };
+ const uint8_t g_uastc_mode_total_hint_bits[TOTAL_UASTC_MODES] = { 15, 15, 15, 15, 15, 15, 15, 15, 0, 23, 17, 17, 17, 23, 23, 23, 23, 23, 15 };
+
+ // bits, trits, quints
+ const int g_astc_bise_range_table[TOTAL_ASTC_RANGES][3] =
+ {
+ { 1, 0, 0 }, // 0-1 0
+ { 0, 1, 0 }, // 0-2 1
+ { 2, 0, 0 }, // 0-3 2
+ { 0, 0, 1 }, // 0-4 3
+
+ { 1, 1, 0 }, // 0-5 4
+ { 3, 0, 0 }, // 0-7 5
+ { 1, 0, 1 }, // 0-9 6
+ { 2, 1, 0 }, // 0-11 7
+
+ { 4, 0, 0 }, // 0-15 8
+ { 2, 0, 1 }, // 0-19 9
+ { 3, 1, 0 }, // 0-23 10
+ { 5, 0, 0 }, // 0-31 11
+
+ { 3, 0, 1 }, // 0-39 12
+ { 4, 1, 0 }, // 0-47 13
+ { 6, 0, 0 }, // 0-63 14
+ { 4, 0, 1 }, // 0-79 15
+
+ { 5, 1, 0 }, // 0-95 16
+ { 7, 0, 0 }, // 0-127 17
+ { 5, 0, 1 }, // 0-159 18
+ { 6, 1, 0 }, // 0-191 19
+
+ { 8, 0, 0 }, // 0-255 20
+ };
+
+ int astc_get_levels(int range)
+ {
+ assert(range < (int)BC7ENC_TOTAL_ASTC_RANGES);
+ return (1 + 2 * g_astc_bise_range_table[range][1] + 4 * g_astc_bise_range_table[range][2]) << g_astc_bise_range_table[range][0];
+ }
+
+ // g_astc_unquant[] is the inverse of g_astc_sorted_order_unquant[]
+ astc_quant_bin g_astc_unquant[BC7ENC_TOTAL_ASTC_RANGES][256]; // [ASTC encoded endpoint index]
+
+ // Taken right from the ASTC spec.
+ static struct
+ {
+ const char* m_pB_str;
+ uint32_t m_c;
+ } g_astc_endpoint_unquant_params[BC7ENC_TOTAL_ASTC_RANGES] =
+ {
+ { "", 0 },
+ { "", 0 },
+ { "", 0 },
+ { "", 0 },
+ { "000000000", 204, }, // 0-5
+ { "", 0 },
+ { "000000000", 113, }, // 0-9
+ { "b000b0bb0", 93 }, // 0-11
+ { "", 0 },
+ { "b0000bb00", 54 }, // 0-19
+ { "cb000cbcb", 44 }, // 0-23
+ { "", 0 },
+ { "cb0000cbc", 26 }, // 0-39
+ { "dcb000dcb", 22 }, // 0-47
+ { "", 0 },
+ { "dcb0000dc", 13 }, // 0-79
+ { "edcb000ed", 11 }, // 0-95
+ { "", 0 },
+ { "edcb0000e", 6 }, // 0-159
+ { "fedcb000f", 5 }, // 0-191
+ { "", 0 },
+ };
+
+ bool astc_is_valid_endpoint_range(uint32_t range)
+ {
+ if ((g_astc_bise_range_table[range][1] == 0) && (g_astc_bise_range_table[range][2] == 0))
+ return true;
+
+ return g_astc_endpoint_unquant_params[range].m_c != 0;
+ }
+
+ uint32_t unquant_astc_endpoint(uint32_t packed_bits, uint32_t packed_trits, uint32_t packed_quints, uint32_t range)
+ {
+ assert(range < BC7ENC_TOTAL_ASTC_RANGES);
+
+ const uint32_t bits = g_astc_bise_range_table[range][0];
+ const uint32_t trits = g_astc_bise_range_table[range][1];
+ const uint32_t quints = g_astc_bise_range_table[range][2];
+
+ uint32_t val = 0;
+ if ((!trits) && (!quints))
+ {
+ assert(!packed_trits && !packed_quints);
+
+ int bits_left = 8;
+ while (bits_left > 0)
+ {
+ uint32_t v = packed_bits;
+
+ int n = basisu::minimumi(bits_left, bits);
+ if (n < (int)bits)
+ v >>= (bits - n);
+
+ assert(v < (1U << n));
+
+ val |= (v << (bits_left - n));
+ bits_left -= n;
+ }
+ }
+ else
+ {
+ const uint32_t A = (packed_bits & 1) ? 511 : 0;
+ const uint32_t C = g_astc_endpoint_unquant_params[range].m_c;
+ const uint32_t D = trits ? packed_trits : packed_quints;
+
+ assert(C);
+
+ uint32_t B = 0;
+ for (uint32_t i = 0; i < 9; i++)
+ {
+ B <<= 1;
+
+ char c = g_astc_endpoint_unquant_params[range].m_pB_str[i];
+ if (c != '0')
+ {
+ c -= 'a';
+ B |= ((packed_bits >> c) & 1);
+ }
+ }
+
+ val = D * C + B;
+ val = val ^ A;
+ val = (A & 0x80) | (val >> 2);
+ }
+
+ return val;
+ }
+
+ uint32_t unquant_astc_endpoint_val(uint32_t packed_val, uint32_t range)
+ {
+ assert(range < BC7ENC_TOTAL_ASTC_RANGES);
+ assert(packed_val < (uint32_t)astc_get_levels(range));
+
+ const uint32_t bits = g_astc_bise_range_table[range][0];
+ const uint32_t trits = g_astc_bise_range_table[range][1];
+ const uint32_t quints = g_astc_bise_range_table[range][2];
+
+ if ((!trits) && (!quints))
+ return unquant_astc_endpoint(packed_val, 0, 0, range);
+ else if (trits)
+ return unquant_astc_endpoint(packed_val & ((1 << bits) - 1), packed_val >> bits, 0, range);
+ else
+ return unquant_astc_endpoint(packed_val & ((1 << bits) - 1), 0, packed_val >> bits, range);
+ }
+
+ // BC7 - Various BC7 tables/helpers
+ const uint32_t g_bc7_weights1[2] = { 0, 64 };
+ const uint32_t g_bc7_weights2[4] = { 0, 21, 43, 64 };
+ const uint32_t g_bc7_weights3[8] = { 0, 9, 18, 27, 37, 46, 55, 64 };
+ const uint32_t g_bc7_weights4[16] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
+ const uint32_t g_astc_weights4[16] = { 0, 4, 8, 12, 17, 21, 25, 29, 35, 39, 43, 47, 52, 56, 60, 64 };
+ const uint32_t g_astc_weights5[32] = { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64 };
+ const uint32_t g_astc_weights_3levels[3] = { 0, 32, 64 };
+
+ const uint8_t g_bc7_partition1[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+
+ const uint8_t g_bc7_partition2[64 * 16] =
+ {
+ 0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1, 0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1, 0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1, 0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1, 0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1, 0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,
+ 0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1, 0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1, 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,
+ 0,0,0,0,1,0,0,0,1,1,1,0,1,1,1,1, 0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,0, 0,1,1,1,0,0,1,1,0,0,0,1,0,0,0,0, 0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0, 0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0, 0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,
+ 0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0, 0,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0, 0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0, 0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0, 0,0,0,1,0,1,1,1,1,1,1,0,1,0,0,0, 0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0, 0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,0, 0,0,1,1,1,0,0,1,1,0,0,1,1,1,0,0,
+ 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1, 0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1, 0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0, 0,0,1,1,0,0,1,1,1,1,0,0,1,1,0,0, 0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0, 0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0, 0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1, 0,1,0,1,1,0,1,0,1,0,1,0,0,1,0,1,
+ 0,1,1,1,0,0,1,1,1,1,0,0,1,1,1,0, 0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0, 0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0, 0,0,1,1,1,0,1,1,1,1,0,1,1,1,0,0, 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, 0,0,1,1,1,1,0,0,1,1,0,0,0,0,1,1, 0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1, 0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,
+ 0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0, 0,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0, 0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0, 0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,0, 0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1, 0,1,1,0,0,0,1,1,1,0,0,1,1,1,0,0, 0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,0,
+ 0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,1, 0,1,1,0,0,0,1,1,0,0,1,1,1,0,0,1, 0,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1, 0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,1, 0,0,0,0,1,1,1,1,0,0,1,1,0,0,1,1, 0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0, 0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0, 0,1,0,0,0,1,0,0,0,1,1,1,0,1,1,1
+ };
+
+ const uint8_t g_bc7_partition3[64 * 16] =
+ {
+ 0,0,1,1,0,0,1,1,0,2,2,1,2,2,2,2, 0,0,0,1,0,0,1,1,2,2,1,1,2,2,2,1, 0,0,0,0,2,0,0,1,2,2,1,1,2,2,1,1, 0,2,2,2,0,0,2,2,0,0,1,1,0,1,1,1, 0,0,0,0,0,0,0,0,1,1,2,2,1,1,2,2, 0,0,1,1,0,0,1,1,0,0,2,2,0,0,2,2, 0,0,2,2,0,0,2,2,1,1,1,1,1,1,1,1, 0,0,1,1,0,0,1,1,2,2,1,1,2,2,1,1,
+ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2, 0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2, 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2, 0,0,1,2,0,0,1,2,0,0,1,2,0,0,1,2, 0,1,1,2,0,1,1,2,0,1,1,2,0,1,1,2, 0,1,2,2,0,1,2,2,0,1,2,2,0,1,2,2, 0,0,1,1,0,1,1,2,1,1,2,2,1,2,2,2, 0,0,1,1,2,0,0,1,2,2,0,0,2,2,2,0,
+ 0,0,0,1,0,0,1,1,0,1,1,2,1,1,2,2, 0,1,1,1,0,0,1,1,2,0,0,1,2,2,0,0, 0,0,0,0,1,1,2,2,1,1,2,2,1,1,2,2, 0,0,2,2,0,0,2,2,0,0,2,2,1,1,1,1, 0,1,1,1,0,1,1,1,0,2,2,2,0,2,2,2, 0,0,0,1,0,0,0,1,2,2,2,1,2,2,2,1, 0,0,0,0,0,0,1,1,0,1,2,2,0,1,2,2, 0,0,0,0,1,1,0,0,2,2,1,0,2,2,1,0,
+ 0,1,2,2,0,1,2,2,0,0,1,1,0,0,0,0, 0,0,1,2,0,0,1,2,1,1,2,2,2,2,2,2, 0,1,1,0,1,2,2,1,1,2,2,1,0,1,1,0, 0,0,0,0,0,1,1,0,1,2,2,1,1,2,2,1, 0,0,2,2,1,1,0,2,1,1,0,2,0,0,2,2, 0,1,1,0,0,1,1,0,2,0,0,2,2,2,2,2, 0,0,1,1,0,1,2,2,0,1,2,2,0,0,1,1, 0,0,0,0,2,0,0,0,2,2,1,1,2,2,2,1,
+ 0,0,0,0,0,0,0,2,1,1,2,2,1,2,2,2, 0,2,2,2,0,0,2,2,0,0,1,2,0,0,1,1, 0,0,1,1,0,0,1,2,0,0,2,2,0,2,2,2, 0,1,2,0,0,1,2,0,0,1,2,0,0,1,2,0, 0,0,0,0,1,1,1,1,2,2,2,2,0,0,0,0, 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0, 0,1,2,0,2,0,1,2,1,2,0,1,0,1,2,0, 0,0,1,1,2,2,0,0,1,1,2,2,0,0,1,1,
+ 0,0,1,1,1,1,2,2,2,2,0,0,0,0,1,1, 0,1,0,1,0,1,0,1,2,2,2,2,2,2,2,2, 0,0,0,0,0,0,0,0,2,1,2,1,2,1,2,1, 0,0,2,2,1,1,2,2,0,0,2,2,1,1,2,2, 0,0,2,2,0,0,1,1,0,0,2,2,0,0,1,1, 0,2,2,0,1,2,2,1,0,2,2,0,1,2,2,1, 0,1,0,1,2,2,2,2,2,2,2,2,0,1,0,1, 0,0,0,0,2,1,2,1,2,1,2,1,2,1,2,1,
+ 0,1,0,1,0,1,0,1,0,1,0,1,2,2,2,2, 0,2,2,2,0,1,1,1,0,2,2,2,0,1,1,1, 0,0,0,2,1,1,1,2,0,0,0,2,1,1,1,2, 0,0,0,0,2,1,1,2,2,1,1,2,2,1,1,2, 0,2,2,2,0,1,1,1,0,1,1,1,0,2,2,2, 0,0,0,2,1,1,1,2,1,1,1,2,0,0,0,2, 0,1,1,0,0,1,1,0,0,1,1,0,2,2,2,2, 0,0,0,0,0,0,0,0,2,1,1,2,2,1,1,2,
+ 0,1,1,0,0,1,1,0,2,2,2,2,2,2,2,2, 0,0,2,2,0,0,1,1,0,0,1,1,0,0,2,2, 0,0,2,2,1,1,2,2,1,1,2,2,0,0,2,2, 0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,2, 0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,1, 0,2,2,2,1,2,2,2,0,2,2,2,1,2,2,2, 0,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2, 0,1,1,1,2,0,1,1,2,2,0,1,2,2,2,0,
+ };
+
+ const uint8_t g_bc7_table_anchor_index_second_subset[64] = { 15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15, 15, 2, 8, 2, 2, 8, 8,15, 2, 8, 2, 2, 8, 8, 2, 2, 15,15, 6, 8, 2, 8,15,15, 2, 8, 2, 2, 2,15,15, 6, 6, 2, 6, 8,15,15, 2, 2, 15,15,15,15,15, 2, 2,15 };
+
+ const uint8_t g_bc7_table_anchor_index_third_subset_1[64] =
+ {
+ 3, 3,15,15, 8, 3,15,15, 8, 8, 6, 6, 6, 5, 3, 3, 3, 3, 8,15, 3, 3, 6,10, 5, 8, 8, 6, 8, 5,15,15, 8,15, 3, 5, 6,10, 8,15, 15, 3,15, 5,15,15,15,15, 3,15, 5, 5, 5, 8, 5,10, 5,10, 8,13,15,12, 3, 3
+ };
+
+ const uint8_t g_bc7_table_anchor_index_third_subset_2[64] =
+ {
+ 15, 8, 8, 3,15,15, 3, 8, 15,15,15,15,15,15,15, 8, 15, 8,15, 3,15, 8,15, 8, 3,15, 6,10,15,15,10, 8, 15, 3,15,10,10, 8, 9,10, 6,15, 8,15, 3, 6, 6, 8, 15, 3,15,15,15,15,15,15, 15,15,15,15, 3,15,15, 8
+ };
+
+ const uint8_t g_bc7_num_subsets[8] = { 3, 2, 3, 2, 1, 1, 1, 2 };
+ const uint8_t g_bc7_partition_bits[8] = { 4, 6, 6, 6, 0, 0, 0, 6 };
+ const uint8_t g_bc7_color_index_bitcount[8] = { 3, 3, 2, 2, 2, 2, 4, 2 };
+
+ const uint8_t g_bc7_mode_has_p_bits[8] = { 1, 1, 0, 1, 0, 0, 1, 1 };
+ const uint8_t g_bc7_mode_has_shared_p_bits[8] = { 0, 1, 0, 0, 0, 0, 0, 0 };
+ const uint8_t g_bc7_color_precision_table[8] = { 4, 6, 5, 7, 5, 7, 7, 5 };
+ const int8_t g_bc7_alpha_precision_table[8] = { 0, 0, 0, 0, 6, 8, 7, 5 };
+
+ const uint8_t g_bc7_alpha_index_bitcount[8] = { 0, 0, 0, 0, 3, 2, 4, 2 };
+
+ endpoint_err g_bc7_mode_6_optimal_endpoints[256][2]; // [c][pbit]
+ endpoint_err g_bc7_mode_5_optimal_endpoints[256]; // [c]
+
+ static inline void bc7_set_block_bits(uint8_t* pBytes, uint32_t val, uint32_t num_bits, uint32_t* pCur_ofs)
+ {
+ assert((num_bits <= 32) && (val < (1ULL << num_bits)));
+ while (num_bits)
+ {
+ const uint32_t n = basisu::minimumu(8 - (*pCur_ofs & 7), num_bits);
+ pBytes[*pCur_ofs >> 3] |= (uint8_t)(val << (*pCur_ofs & 7));
+ val >>= n;
+ num_bits -= n;
+ *pCur_ofs += n;
+ }
+ assert(*pCur_ofs <= 128);
+ }
+
+ // TODO: Optimize this.
+ void encode_bc7_block(void* pBlock, const bc7_optimization_results* pResults)
+ {
+ const uint32_t best_mode = pResults->m_mode;
+
+ const uint32_t total_subsets = g_bc7_num_subsets[best_mode];
+ const uint32_t total_partitions = 1 << g_bc7_partition_bits[best_mode];
+ //const uint32_t num_rotations = 1 << g_bc7_rotation_bits[best_mode];
+ //const uint32_t num_index_selectors = (best_mode == 4) ? 2 : 1;
+
+ const uint8_t* pPartition;
+ if (total_subsets == 1)
+ pPartition = &g_bc7_partition1[0];
+ else if (total_subsets == 2)
+ pPartition = &g_bc7_partition2[pResults->m_partition * 16];
+ else
+ pPartition = &g_bc7_partition3[pResults->m_partition * 16];
+
+ uint8_t color_selectors[16];
+ memcpy(color_selectors, pResults->m_selectors, 16);
+
+ uint8_t alpha_selectors[16];
+ memcpy(alpha_selectors, pResults->m_alpha_selectors, 16);
+
+ color_quad_u8 low[3], high[3];
+ memcpy(low, pResults->m_low, sizeof(low));
+ memcpy(high, pResults->m_high, sizeof(high));
+
+ uint32_t pbits[3][2];
+ memcpy(pbits, pResults->m_pbits, sizeof(pbits));
+
+ int anchor[3] = { -1, -1, -1 };
+
+ for (uint32_t k = 0; k < total_subsets; k++)
+ {
+ uint32_t anchor_index = 0;
+ if (k)
+ {
+ if ((total_subsets == 3) && (k == 1))
+ anchor_index = g_bc7_table_anchor_index_third_subset_1[pResults->m_partition];
+ else if ((total_subsets == 3) && (k == 2))
+ anchor_index = g_bc7_table_anchor_index_third_subset_2[pResults->m_partition];
+ else
+ anchor_index = g_bc7_table_anchor_index_second_subset[pResults->m_partition];
+ }
+
+ anchor[k] = anchor_index;
+
+ const uint32_t color_index_bits = get_bc7_color_index_size(best_mode, pResults->m_index_selector);
+ const uint32_t num_color_indices = 1 << color_index_bits;
+
+ if (color_selectors[anchor_index] & (num_color_indices >> 1))
+ {
+ for (uint32_t i = 0; i < 16; i++)
+ if (pPartition[i] == k)
+ color_selectors[i] = (uint8_t)((num_color_indices - 1) - color_selectors[i]);
+
+ if (get_bc7_mode_has_seperate_alpha_selectors(best_mode))
+ {
+ for (uint32_t q = 0; q < 3; q++)
+ {
+ uint8_t t = low[k].m_c[q];
+ low[k].m_c[q] = high[k].m_c[q];
+ high[k].m_c[q] = t;
+ }
+ }
+ else
+ {
+ color_quad_u8 tmp = low[k];
+ low[k] = high[k];
+ high[k] = tmp;
+ }
+
+ if (!g_bc7_mode_has_shared_p_bits[best_mode])
+ {
+ uint32_t t = pbits[k][0];
+ pbits[k][0] = pbits[k][1];
+ pbits[k][1] = t;
+ }
+ }
+
+ if (get_bc7_mode_has_seperate_alpha_selectors(best_mode))
+ {
+ const uint32_t alpha_index_bits = get_bc7_alpha_index_size(best_mode, pResults->m_index_selector);
+ const uint32_t num_alpha_indices = 1 << alpha_index_bits;
+
+ if (alpha_selectors[anchor_index] & (num_alpha_indices >> 1))
+ {
+ for (uint32_t i = 0; i < 16; i++)
+ if (pPartition[i] == k)
+ alpha_selectors[i] = (uint8_t)((num_alpha_indices - 1) - alpha_selectors[i]);
+
+ uint8_t t = low[k].m_c[3];
+ low[k].m_c[3] = high[k].m_c[3];
+ high[k].m_c[3] = t;
+ }
+ }
+ }
+
+ uint8_t* pBlock_bytes = (uint8_t*)(pBlock);
+ memset(pBlock_bytes, 0, BC7ENC_BLOCK_SIZE);
+
+ uint32_t cur_bit_ofs = 0;
+ bc7_set_block_bits(pBlock_bytes, 1 << best_mode, best_mode + 1, &cur_bit_ofs);
+
+ if ((best_mode == 4) || (best_mode == 5))
+ bc7_set_block_bits(pBlock_bytes, pResults->m_rotation, 2, &cur_bit_ofs);
+
+ if (best_mode == 4)
+ bc7_set_block_bits(pBlock_bytes, pResults->m_index_selector, 1, &cur_bit_ofs);
+
+ if (total_partitions > 1)
+ bc7_set_block_bits(pBlock_bytes, pResults->m_partition, (total_partitions == 64) ? 6 : 4, &cur_bit_ofs);
+
+ const uint32_t total_comps = (best_mode >= 4) ? 4 : 3;
+ for (uint32_t comp = 0; comp < total_comps; comp++)
+ {
+ for (uint32_t subset = 0; subset < total_subsets; subset++)
+ {
+ bc7_set_block_bits(pBlock_bytes, low[subset].m_c[comp], (comp == 3) ? g_bc7_alpha_precision_table[best_mode] : g_bc7_color_precision_table[best_mode], &cur_bit_ofs);
+ bc7_set_block_bits(pBlock_bytes, high[subset].m_c[comp], (comp == 3) ? g_bc7_alpha_precision_table[best_mode] : g_bc7_color_precision_table[best_mode], &cur_bit_ofs);
+ }
+ }
+
+ if (g_bc7_mode_has_p_bits[best_mode])
+ {
+ for (uint32_t subset = 0; subset < total_subsets; subset++)
+ {
+ bc7_set_block_bits(pBlock_bytes, pbits[subset][0], 1, &cur_bit_ofs);
+ if (!g_bc7_mode_has_shared_p_bits[best_mode])
+ bc7_set_block_bits(pBlock_bytes, pbits[subset][1], 1, &cur_bit_ofs);
+ }
+ }
+
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ int idx = x + y * 4;
+
+ uint32_t n = pResults->m_index_selector ? get_bc7_alpha_index_size(best_mode, pResults->m_index_selector) : get_bc7_color_index_size(best_mode, pResults->m_index_selector);
+
+ if ((idx == anchor[0]) || (idx == anchor[1]) || (idx == anchor[2]))
+ n--;
+
+ bc7_set_block_bits(pBlock_bytes, pResults->m_index_selector ? alpha_selectors[idx] : color_selectors[idx], n, &cur_bit_ofs);
+ }
+ }
+
+ if (get_bc7_mode_has_seperate_alpha_selectors(best_mode))
+ {
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ int idx = x + y * 4;
+
+ uint32_t n = pResults->m_index_selector ? get_bc7_color_index_size(best_mode, pResults->m_index_selector) : get_bc7_alpha_index_size(best_mode, pResults->m_index_selector);
+
+ if ((idx == anchor[0]) || (idx == anchor[1]) || (idx == anchor[2]))
+ n--;
+
+ bc7_set_block_bits(pBlock_bytes, pResults->m_index_selector ? color_selectors[idx] : alpha_selectors[idx], n, &cur_bit_ofs);
+ }
+ }
+ }
+
+ assert(cur_bit_ofs == 128);
+ }
+
+ // ASTC
+ static inline void astc_set_bits_1_to_9(uint32_t* pDst, int& bit_offset, uint32_t code, uint32_t codesize)
+ {
+ uint8_t* pBuf = reinterpret_cast(pDst);
+
+ assert(codesize <= 9);
+ if (codesize)
+ {
+ uint32_t byte_bit_offset = bit_offset & 7;
+ uint32_t val = code << byte_bit_offset;
+
+ uint32_t index = bit_offset >> 3;
+ pBuf[index] |= (uint8_t)val;
+
+ if (codesize > (8 - byte_bit_offset))
+ pBuf[index + 1] |= (uint8_t)(val >> 8);
+
+ bit_offset += codesize;
+ }
+ }
+
+ void pack_astc_solid_block(void* pDst_block, const color32& color)
+ {
+ uint32_t r = color[0], g = color[1], b = color[2];
+ uint32_t a = color[3];
+
+ uint32_t* pOutput = static_cast(pDst_block);
+ uint8_t* pBytes = reinterpret_cast(pDst_block);
+
+ pBytes[0] = 0xfc; pBytes[1] = 0xfd; pBytes[2] = 0xff; pBytes[3] = 0xff;
+
+ pOutput[1] = 0xffffffff;
+ pOutput[2] = 0;
+ pOutput[3] = 0;
+
+ int bit_pos = 64;
+ astc_set_bits(reinterpret_cast(pDst_block), bit_pos, r | (r << 8), 16);
+ astc_set_bits(reinterpret_cast(pDst_block), bit_pos, g | (g << 8), 16);
+ astc_set_bits(reinterpret_cast(pDst_block), bit_pos, b | (b << 8), 16);
+ astc_set_bits(reinterpret_cast(pDst_block), bit_pos, a | (a << 8), 16);
+ }
+
+ // See 23.21 https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#_partition_pattern_generation
+#ifdef _DEBUG
+ static inline uint32_t astc_hash52(uint32_t v)
+ {
+ uint32_t p = v;
+ p ^= p >> 15; p -= p << 17; p += p << 7; p += p << 4;
+ p ^= p >> 5; p += p << 16; p ^= p >> 7; p ^= p >> 3;
+ p ^= p << 6; p ^= p >> 17;
+ return p;
+ }
+
+ int astc_compute_texel_partition(int seed, int x, int y, int z, int partitioncount, bool small_block)
+ {
+ if (small_block)
+ {
+ x <<= 1; y <<= 1; z <<= 1;
+ }
+ seed += (partitioncount - 1) * 1024;
+ uint32_t rnum = astc_hash52(seed);
+ uint8_t seed1 = rnum & 0xF;
+ uint8_t seed2 = (rnum >> 4) & 0xF;
+ uint8_t seed3 = (rnum >> 8) & 0xF;
+ uint8_t seed4 = (rnum >> 12) & 0xF;
+ uint8_t seed5 = (rnum >> 16) & 0xF;
+ uint8_t seed6 = (rnum >> 20) & 0xF;
+ uint8_t seed7 = (rnum >> 24) & 0xF;
+ uint8_t seed8 = (rnum >> 28) & 0xF;
+ uint8_t seed9 = (rnum >> 18) & 0xF;
+ uint8_t seed10 = (rnum >> 22) & 0xF;
+ uint8_t seed11 = (rnum >> 26) & 0xF;
+ uint8_t seed12 = ((rnum >> 30) | (rnum << 2)) & 0xF;
+
+ seed1 *= seed1; seed2 *= seed2;
+ seed3 *= seed3; seed4 *= seed4;
+ seed5 *= seed5; seed6 *= seed6;
+ seed7 *= seed7; seed8 *= seed8;
+ seed9 *= seed9; seed10 *= seed10;
+ seed11 *= seed11; seed12 *= seed12;
+
+ int sh1, sh2, sh3;
+ if (seed & 1)
+ {
+ sh1 = (seed & 2 ? 4 : 5); sh2 = (partitioncount == 3 ? 6 : 5);
+ }
+ else
+ {
+ sh1 = (partitioncount == 3 ? 6 : 5); sh2 = (seed & 2 ? 4 : 5);
+ }
+ sh3 = (seed & 0x10) ? sh1 : sh2;
+
+ seed1 >>= sh1; seed2 >>= sh2; seed3 >>= sh1; seed4 >>= sh2;
+ seed5 >>= sh1; seed6 >>= sh2; seed7 >>= sh1; seed8 >>= sh2;
+ seed9 >>= sh3; seed10 >>= sh3; seed11 >>= sh3; seed12 >>= sh3;
+
+ int a = seed1 * x + seed2 * y + seed11 * z + (rnum >> 14);
+ int b = seed3 * x + seed4 * y + seed12 * z + (rnum >> 10);
+ int c = seed5 * x + seed6 * y + seed9 * z + (rnum >> 6);
+ int d = seed7 * x + seed8 * y + seed10 * z + (rnum >> 2);
+
+ a &= 0x3F; b &= 0x3F; c &= 0x3F; d &= 0x3F;
+
+ if (partitioncount < 4) d = 0;
+ if (partitioncount < 3) c = 0;
+
+ if (a >= b && a >= c && a >= d)
+ return 0;
+ else if (b >= c && b >= d)
+ return 1;
+ else if (c >= d)
+ return 2;
+ else
+ return 3;
+ }
+#endif
+
+ static const uint8_t g_astc_quint_encode[125] =
+ {
+ 0, 1, 2, 3, 4, 8, 9, 10, 11, 12, 16, 17, 18, 19, 20, 24, 25, 26, 27, 28, 5, 13, 21, 29, 6, 32, 33, 34, 35, 36, 40, 41, 42, 43, 44, 48, 49, 50, 51, 52, 56, 57,
+ 58, 59, 60, 37, 45, 53, 61, 14, 64, 65, 66, 67, 68, 72, 73, 74, 75, 76, 80, 81, 82, 83, 84, 88, 89, 90, 91, 92, 69, 77, 85, 93, 22, 96, 97, 98, 99, 100, 104,
+ 105, 106, 107, 108, 112, 113, 114, 115, 116, 120, 121, 122, 123, 124, 101, 109, 117, 125, 30, 102, 103, 70, 71, 38, 110, 111, 78, 79, 46, 118, 119, 86, 87, 54,
+ 126, 127, 94, 95, 62, 39, 47, 55, 63, 31
+ };
+
+ // Encodes 3 values to output, usable for any range that uses quints and bits
+ static inline void astc_encode_quints(uint32_t* pOutput, const uint8_t* pValues, int& bit_pos, int n)
+ {
+ // First extract the trits and the bits from the 5 input values
+ int quints = 0, bits[3];
+ const uint32_t bit_mask = (1 << n) - 1;
+ for (int i = 0; i < 3; i++)
+ {
+ static const int s_muls[3] = { 1, 5, 25 };
+
+ const int t = pValues[i] >> n;
+
+ quints += t * s_muls[i];
+ bits[i] = pValues[i] & bit_mask;
+ }
+
+ // Encode the quints, by inverting the bit manipulations done by the decoder, converting 3 quints into 7-bits.
+ // See https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#astc-integer-sequence-encoding
+
+ assert(quints < 125);
+ const int T = g_astc_quint_encode[quints];
+
+ // Now interleave the 7 encoded quint bits with the bits to form the encoded output. See table 95-96.
+ astc_set_bits(pOutput, bit_pos, bits[0] | (astc_extract_bits(T, 0, 2) << n) | (bits[1] << (3 + n)) | (astc_extract_bits(T, 3, 4) << (3 + n * 2)) |
+ (bits[2] << (5 + n * 2)) | (astc_extract_bits(T, 5, 6) << (5 + n * 3)), 7 + n * 3);
+ }
+
+ // Packs values using ASTC's BISE to output buffer.
+ static void astc_pack_bise(uint32_t* pDst, const uint8_t* pSrc_vals, int bit_pos, int num_vals, int range)
+ {
+ uint32_t temp[5] = { 0, 0, 0, 0, 0 };
+
+ const int num_bits = g_astc_bise_range_table[range][0];
+
+ int group_size = 0;
+ if (g_astc_bise_range_table[range][1])
+ group_size = 5;
+ else if (g_astc_bise_range_table[range][2])
+ group_size = 3;
+
+ if (group_size)
+ {
+ // Range has trits or quints - pack each group of 5 or 3 values
+ const int total_groups = (group_size == 5) ? ((num_vals + 4) / 5) : ((num_vals + 2) / 3);
+
+ for (int group_index = 0; group_index < total_groups; group_index++)
+ {
+ uint8_t vals[5] = { 0, 0, 0, 0, 0 };
+
+ const int limit = std::min(group_size, num_vals - group_index * group_size);
+ for (int i = 0; i < limit; i++)
+ vals[i] = pSrc_vals[group_index * group_size + i];
+
+ if (group_size == 5)
+ astc_encode_trits(temp, vals, bit_pos, num_bits);
+ else
+ astc_encode_quints(temp, vals, bit_pos, num_bits);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < num_vals; i++)
+ astc_set_bits_1_to_9(temp, bit_pos, pSrc_vals[i], num_bits);
+ }
+
+ pDst[0] |= temp[0]; pDst[1] |= temp[1];
+ pDst[2] |= temp[2]; pDst[3] |= temp[3];
+ }
+
+ const uint32_t ASTC_BLOCK_MODE_BITS = 11;
+ const uint32_t ASTC_PART_BITS = 2;
+ const uint32_t ASTC_CEM_BITS = 4;
+ const uint32_t ASTC_PARTITION_INDEX_BITS = 10;
+ const uint32_t ASTC_CCS_BITS = 2;
+
+ const uint32_t g_uastc_mode_astc_block_mode[TOTAL_UASTC_MODES] = { 0x242, 0x42, 0x53, 0x42, 0x42, 0x53, 0x442, 0x42, 0, 0x42, 0x242, 0x442, 0x53, 0x441, 0x42, 0x242, 0x42, 0x442, 0x253 };
+
+ bool pack_astc_block(uint32_t* pDst, const astc_block_desc* pBlock, uint32_t uastc_mode)
+ {
+ assert(uastc_mode < TOTAL_UASTC_MODES);
+ uint8_t* pDst_bytes = reinterpret_cast(pDst);
+
+ const int total_weights = pBlock->m_dual_plane ? 32 : 16;
+
+ // Set mode bits - see Table 146-147
+ uint32_t mode = g_uastc_mode_astc_block_mode[uastc_mode];
+ pDst_bytes[0] = (uint8_t)mode;
+ pDst_bytes[1] = (uint8_t)(mode >> 8);
+
+ memset(pDst_bytes + 2, 0, 16 - 2);
+
+ int bit_pos = ASTC_BLOCK_MODE_BITS;
+
+ // We only support 1-5 bit weight indices
+ assert(!g_astc_bise_range_table[pBlock->m_weight_range][1] && !g_astc_bise_range_table[pBlock->m_weight_range][2]);
+ const int bits_per_weight = g_astc_bise_range_table[pBlock->m_weight_range][0];
+
+ // See table 143 - PART
+ astc_set_bits_1_to_9(pDst, bit_pos, pBlock->m_subsets - 1, ASTC_PART_BITS);
+
+ if (pBlock->m_subsets == 1)
+ astc_set_bits_1_to_9(pDst, bit_pos, pBlock->m_cem, ASTC_CEM_BITS);
+ else
+ {
+ // See table 145
+ astc_set_bits(pDst, bit_pos, pBlock->m_partition_seed, ASTC_PARTITION_INDEX_BITS);
+
+ // Table 150 - we assume all CEM's are equal, so write 2 0's along with the CEM
+ astc_set_bits_1_to_9(pDst, bit_pos, (pBlock->m_cem << 2) & 63, ASTC_CEM_BITS + 2);
+ }
+
+ if (pBlock->m_dual_plane)
+ {
+ const int total_weight_bits = total_weights * bits_per_weight;
+
+ // See Illegal Encodings 23.24
+ // https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#_illegal_encodings
+ assert((total_weight_bits >= 24) && (total_weight_bits <= 96));
+
+ int ccs_bit_pos = 128 - total_weight_bits - ASTC_CCS_BITS;
+ astc_set_bits_1_to_9(pDst, ccs_bit_pos, pBlock->m_ccs, ASTC_CCS_BITS);
+ }
+
+ const int num_cem_pairs = (1 + (pBlock->m_cem >> 2)) * pBlock->m_subsets;
+ assert(num_cem_pairs <= 9);
+
+ astc_pack_bise(pDst, pBlock->m_endpoints, bit_pos, num_cem_pairs * 2, g_uastc_mode_endpoint_ranges[uastc_mode]);
+
+ // Write the weight bits in reverse bit order.
+ switch (bits_per_weight)
+ {
+ case 1:
+ {
+ const uint32_t N = 1;
+ for (int i = 0; i < total_weights; i++)
+ {
+ const uint32_t ofs = 128 - N - i;
+ assert((ofs >> 3) < 16);
+ pDst_bytes[ofs >> 3] |= (pBlock->m_weights[i] << (ofs & 7));
+ }
+ break;
+ }
+ case 2:
+ {
+ const uint32_t N = 2;
+ for (int i = 0; i < total_weights; i++)
+ {
+ static const uint8_t s_reverse_bits2[4] = { 0, 2, 1, 3 };
+ const uint32_t ofs = 128 - N - (i * N);
+ assert((ofs >> 3) < 16);
+ pDst_bytes[ofs >> 3] |= (s_reverse_bits2[pBlock->m_weights[i]] << (ofs & 7));
+ }
+ break;
+ }
+ case 3:
+ {
+ const uint32_t N = 3;
+ for (int i = 0; i < total_weights; i++)
+ {
+ static const uint8_t s_reverse_bits3[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+
+ const uint32_t ofs = 128 - N - (i * N);
+ const uint32_t rev = s_reverse_bits3[pBlock->m_weights[i]] << (ofs & 7);
+
+ uint32_t index = ofs >> 3;
+ assert(index < 16);
+ pDst_bytes[index++] |= rev & 0xFF;
+ if (index < 16)
+ pDst_bytes[index++] |= (rev >> 8);
+ }
+ break;
+ }
+ case 4:
+ {
+ const uint32_t N = 4;
+ for (int i = 0; i < total_weights; i++)
+ {
+ static const uint8_t s_reverse_bits4[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
+ const int ofs = 128 - N - (i * N);
+ assert(ofs >= 0 && (ofs >> 3) < 16);
+ pDst_bytes[ofs >> 3] |= (s_reverse_bits4[pBlock->m_weights[i]] << (ofs & 7));
+ }
+ break;
+ }
+ case 5:
+ {
+ const uint32_t N = 5;
+ for (int i = 0; i < total_weights; i++)
+ {
+ static const uint8_t s_reverse_bits5[32] = { 0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31 };
+
+ const uint32_t ofs = 128 - N - (i * N);
+ const uint32_t rev = s_reverse_bits5[pBlock->m_weights[i]] << (ofs & 7);
+
+ uint32_t index = ofs >> 3;
+ assert(index < 16);
+ pDst_bytes[index++] |= rev & 0xFF;
+ if (index < 16)
+ pDst_bytes[index++] |= (rev >> 8);
+ }
+
+ break;
+ }
+ default:
+ assert(0);
+ break;
+ }
+
+ return true;
+ }
+
+ const uint8_t* get_anchor_indices(uint32_t subsets, uint32_t mode, uint32_t common_pattern, const uint8_t*& pPartition_pattern)
+ {
+ const uint8_t* pSubset_anchor_indices = g_zero_pattern;
+ pPartition_pattern = g_zero_pattern;
+
+ if (subsets >= 2)
+ {
+ if (subsets == 3)
+ {
+ pPartition_pattern = &g_astc_bc7_patterns3[common_pattern][0];
+ pSubset_anchor_indices = &g_astc_bc7_pattern3_anchors[common_pattern][0];
+ }
+ else if (mode == 7)
+ {
+ pPartition_pattern = &g_bc7_3_astc2_patterns2[common_pattern][0];
+ pSubset_anchor_indices = &g_bc7_3_astc2_patterns2_anchors[common_pattern][0];
+ }
+ else
+ {
+ pPartition_pattern = &g_astc_bc7_patterns2[common_pattern][0];
+ pSubset_anchor_indices = &g_astc_bc7_pattern2_anchors[common_pattern][0];
+ }
+ }
+
+ return pSubset_anchor_indices;
+ }
+
+ static inline uint32_t read_bit(const uint8_t* pBuf, uint32_t& bit_offset)
+ {
+ uint32_t byte_bits = pBuf[bit_offset >> 3] >> (bit_offset & 7);
+ bit_offset += 1;
+ return byte_bits & 1;
+ }
+
+ static inline uint32_t read_bits1_to_9(const uint8_t* pBuf, uint32_t& bit_offset, uint32_t codesize)
+ {
+ assert(codesize <= 9);
+ if (!codesize)
+ return 0;
+
+ if ((IS_BIG_ENDIAN) || (!USE_UNALIGNED_WORD_READS) || (bit_offset >= 112))
+ {
+ const uint8_t* pBytes = &pBuf[bit_offset >> 3U];
+
+ uint32_t byte_bit_offset = bit_offset & 7U;
+
+ uint32_t bits = pBytes[0] >> byte_bit_offset;
+ uint32_t bits_read = basisu::minimum(codesize, 8 - byte_bit_offset);
+
+ uint32_t bits_remaining = codesize - bits_read;
+ if (bits_remaining)
+ bits |= ((uint32_t)pBytes[1]) << bits_read;
+
+ bit_offset += codesize;
+
+ return bits & ((1U << codesize) - 1U);
+ }
+
+ uint32_t byte_bit_offset = bit_offset & 7U;
+ const uint16_t w = *(const uint16_t *)(&pBuf[bit_offset >> 3U]);
+ bit_offset += codesize;
+ return (w >> byte_bit_offset) & ((1U << codesize) - 1U);
+ }
+
+ inline uint64_t read_bits64(const uint8_t* pBuf, uint32_t& bit_offset, uint32_t codesize)
+ {
+ assert(codesize <= 64U);
+ uint64_t bits = 0;
+ uint32_t total_bits = 0;
+
+ while (total_bits < codesize)
+ {
+ uint32_t byte_bit_offset = bit_offset & 7U;
+ uint32_t bits_to_read = basisu::minimum(codesize - total_bits, 8U - byte_bit_offset);
+
+ uint32_t byte_bits = pBuf[bit_offset >> 3U] >> byte_bit_offset;
+ byte_bits &= ((1U << bits_to_read) - 1U);
+
+ bits |= ((uint64_t)(byte_bits) << total_bits);
+
+ total_bits += bits_to_read;
+ bit_offset += bits_to_read;
+ }
+
+ return bits;
+ }
+
+ static inline uint32_t read_bits1_to_9_fst(const uint8_t* pBuf, uint32_t& bit_offset, uint32_t codesize)
+ {
+ assert(codesize <= 9);
+ if (!codesize)
+ return 0;
+ assert(bit_offset < 112);
+
+ if ((IS_BIG_ENDIAN) || (!USE_UNALIGNED_WORD_READS))
+ {
+ const uint8_t* pBytes = &pBuf[bit_offset >> 3U];
+
+ uint32_t byte_bit_offset = bit_offset & 7U;
+
+ uint32_t bits = pBytes[0] >> byte_bit_offset;
+ uint32_t bits_read = basisu::minimum(codesize, 8 - byte_bit_offset);
+
+ uint32_t bits_remaining = codesize - bits_read;
+ if (bits_remaining)
+ bits |= ((uint32_t)pBytes[1]) << bits_read;
+
+ bit_offset += codesize;
+
+ return bits & ((1U << codesize) - 1U);
+ }
+
+ uint32_t byte_bit_offset = bit_offset & 7U;
+ const uint16_t w = *(const uint16_t*)(&pBuf[bit_offset >> 3U]);
+ bit_offset += codesize;
+ return (w >> byte_bit_offset)& ((1U << codesize) - 1U);
+ }
+
+ bool unpack_uastc(const uastc_block& blk, unpacked_uastc_block& unpacked, bool blue_contract_check, bool read_hints)
+ {
+ //memset(&unpacked, 0, sizeof(unpacked));
+
+#if 0
+ uint8_t table[128];
+ memset(table, 0xFF, sizeof(table));
+
+ {
+ for (uint32_t mode = 0; mode <= TOTAL_UASTC_MODES; mode++)
+ {
+ const uint32_t code = g_uastc_mode_huff_codes[mode][0];
+ const uint32_t codesize = g_uastc_mode_huff_codes[mode][1];
+
+ table[code] = mode;
+
+ uint32_t bits_left = 7 - codesize;
+ for (uint32_t i = 0; i < (1 << bits_left); i++)
+ table[code | (i << codesize)] = mode;
+ }
+
+ for (uint32_t i = 0; i < 128; i++)
+ printf("%u,", table[i]);
+ exit(0);
+ }
+#endif
+
+ const int mode = g_uastc_huff_modes[blk.m_bytes[0] & 127];
+ if (mode >= (int)TOTAL_UASTC_MODES)
+ return false;
+
+ unpacked.m_mode = mode;
+
+ uint32_t bit_ofs = g_uastc_mode_huff_codes[mode][1];
+
+ if (mode == UASTC_MODE_INDEX_SOLID_COLOR)
+ {
+ unpacked.m_solid_color.r = (uint8_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 8);
+ unpacked.m_solid_color.g = (uint8_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 8);
+ unpacked.m_solid_color.b = (uint8_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 8);
+ unpacked.m_solid_color.a = (uint8_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 8);
+
+ if (read_hints)
+ {
+ unpacked.m_etc1_flip = false;
+ unpacked.m_etc1_diff = read_bit(blk.m_bytes, bit_ofs) != 0;
+ unpacked.m_etc1_inten0 = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 3);
+ unpacked.m_etc1_inten1 = 0;
+ unpacked.m_etc1_selector = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 2);
+ unpacked.m_etc1_r = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 5);
+ unpacked.m_etc1_g = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 5);
+ unpacked.m_etc1_b = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 5);
+ unpacked.m_etc1_bias = 0;
+ unpacked.m_etc2_hints = 0;
+ }
+
+ return true;
+ }
+
+ if (read_hints)
+ {
+ if (g_uastc_mode_has_bc1_hint0[mode])
+ unpacked.m_bc1_hint0 = read_bit(blk.m_bytes, bit_ofs) != 0;
+ else
+ unpacked.m_bc1_hint0 = false;
+
+ if (g_uastc_mode_has_bc1_hint1[mode])
+ unpacked.m_bc1_hint1 = read_bit(blk.m_bytes, bit_ofs) != 0;
+ else
+ unpacked.m_bc1_hint1 = false;
+
+ unpacked.m_etc1_flip = read_bit(blk.m_bytes, bit_ofs) != 0;
+ unpacked.m_etc1_diff = read_bit(blk.m_bytes, bit_ofs) != 0;
+ unpacked.m_etc1_inten0 = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 3);
+ unpacked.m_etc1_inten1 = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 3);
+
+ if (g_uastc_mode_has_etc1_bias[mode])
+ unpacked.m_etc1_bias = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 5);
+ else
+ unpacked.m_etc1_bias = 0;
+
+ if (g_uastc_mode_has_alpha[mode])
+ {
+ unpacked.m_etc2_hints = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 8);
+ //assert(unpacked.m_etc2_hints > 0);
+ }
+ else
+ unpacked.m_etc2_hints = 0;
+ }
+ else
+ bit_ofs += g_uastc_mode_total_hint_bits[mode];
+
+ uint32_t subsets = 1;
+ switch (mode)
+ {
+ case 2:
+ case 4:
+ case 7:
+ case 9:
+ case 16:
+ unpacked.m_common_pattern = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 5);
+ subsets = 2;
+ break;
+ case 3:
+ unpacked.m_common_pattern = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 4);
+ subsets = 3;
+ break;
+ default:
+ break;
+ }
+
+ uint32_t part_seed = 0;
+ switch (mode)
+ {
+ case 2:
+ case 4:
+ case 9:
+ case 16:
+ if (unpacked.m_common_pattern >= TOTAL_ASTC_BC7_COMMON_PARTITIONS2)
+ return false;
+
+ part_seed = g_astc_bc7_common_partitions2[unpacked.m_common_pattern].m_astc;
+ break;
+ case 3:
+ if (unpacked.m_common_pattern >= TOTAL_ASTC_BC7_COMMON_PARTITIONS3)
+ return false;
+
+ part_seed = g_astc_bc7_common_partitions3[unpacked.m_common_pattern].m_astc;
+ break;
+ case 7:
+ if (unpacked.m_common_pattern >= TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS)
+ return false;
+
+ part_seed = g_bc7_3_astc2_common_partitions[unpacked.m_common_pattern].m_astc2;
+ break;
+ default:
+ break;
+ }
+
+ uint32_t total_planes = 1;
+ switch (mode)
+ {
+ case 6:
+ case 11:
+ case 13:
+ unpacked.m_astc.m_ccs = (int)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, 2);
+ total_planes = 2;
+ break;
+ case 17:
+ unpacked.m_astc.m_ccs = 3;
+ total_planes = 2;
+ break;
+ default:
+ break;
+ }
+
+ unpacked.m_astc.m_dual_plane = (total_planes == 2);
+
+ unpacked.m_astc.m_subsets = subsets;
+ unpacked.m_astc.m_partition_seed = part_seed;
+
+ const uint32_t total_comps = g_uastc_mode_comps[mode];
+
+ const uint32_t weight_bits = g_uastc_mode_weight_bits[mode];
+
+ unpacked.m_astc.m_weight_range = g_uastc_mode_weight_ranges[mode];
+
+ const uint32_t total_values = total_comps * 2 * subsets;
+ const uint32_t endpoint_range = g_uastc_mode_endpoint_ranges[mode];
+
+ const uint32_t cem = g_uastc_mode_cem[mode];
+ unpacked.m_astc.m_cem = cem;
+
+ const uint32_t ep_bits = g_astc_bise_range_table[endpoint_range][0];
+ const uint32_t ep_trits = g_astc_bise_range_table[endpoint_range][1];
+ const uint32_t ep_quints = g_astc_bise_range_table[endpoint_range][2];
+
+ uint32_t total_tqs = 0;
+ uint32_t bundle_size = 0, mul = 0;
+ if (ep_trits)
+ {
+ total_tqs = (total_values + 4) / 5;
+ bundle_size = 5;
+ mul = 3;
+ }
+ else if (ep_quints)
+ {
+ total_tqs = (total_values + 2) / 3;
+ bundle_size = 3;
+ mul = 5;
+ }
+
+ uint32_t tq_values[8];
+ for (uint32_t i = 0; i < total_tqs; i++)
+ {
+ uint32_t num_bits = ep_trits ? 8 : 7;
+ if (i == (total_tqs - 1))
+ {
+ uint32_t num_remaining = total_values - (total_tqs - 1) * bundle_size;
+ if (ep_trits)
+ {
+ switch (num_remaining)
+ {
+ case 1: num_bits = 2; break;
+ case 2: num_bits = 4; break;
+ case 3: num_bits = 5; break;
+ case 4: num_bits = 7; break;
+ default: break;
+ }
+ }
+ else if (ep_quints)
+ {
+ switch (num_remaining)
+ {
+ case 1: num_bits = 3; break;
+ case 2: num_bits = 5; break;
+ default: break;
+ }
+ }
+ }
+
+ tq_values[i] = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, num_bits);
+ } // i
+
+ uint32_t accum = 0;
+ uint32_t accum_remaining = 0;
+ uint32_t next_tq_index = 0;
+
+ for (uint32_t i = 0; i < total_values; i++)
+ {
+ uint32_t value = (uint32_t)read_bits1_to_9_fst(blk.m_bytes, bit_ofs, ep_bits);
+
+ if (total_tqs)
+ {
+ if (!accum_remaining)
+ {
+ assert(next_tq_index < total_tqs);
+ accum = tq_values[next_tq_index++];
+ accum_remaining = bundle_size;
+ }
+
+ // TODO: Optimize with tables
+ uint32_t v = accum % mul;
+ accum /= mul;
+ accum_remaining--;
+
+ value |= (v << ep_bits);
+ }
+
+ unpacked.m_astc.m_endpoints[i] = (uint8_t)value;
+ }
+
+ const uint8_t* pPartition_pattern;
+ const uint8_t* pSubset_anchor_indices = get_anchor_indices(subsets, mode, unpacked.m_common_pattern, pPartition_pattern);
+
+#ifdef _DEBUG
+ for (uint32_t i = 0; i < 16; i++)
+ assert(pPartition_pattern[i] == astc_compute_texel_partition(part_seed, i & 3, i >> 2, 0, subsets, true));
+
+ for (uint32_t subset_index = 0; subset_index < subsets; subset_index++)
+ {
+ uint32_t anchor_index = 0;
+
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ if (pPartition_pattern[i] == subset_index)
+ {
+ anchor_index = i;
+ break;
+ }
+ }
+
+ assert(pSubset_anchor_indices[subset_index] == anchor_index);
+ }
+#endif
+
+#if 0
+ const uint32_t total_planes_shift = total_planes - 1;
+ for (uint32_t i = 0; i < 16 * total_planes; i++)
+ {
+ uint32_t num_bits = weight_bits;
+ for (uint32_t s = 0; s < subsets; s++)
+ {
+ if (pSubset_anchor_indices[s] == (i >> total_planes_shift))
+ {
+ num_bits--;
+ break;
+ }
+ }
+
+ unpacked.m_astc.m_weights[i] = (uint8_t)read_bits1_to_9(blk.m_bytes, bit_ofs, num_bits);
+ }
+#endif
+
+ if (mode == 18)
+ {
+ // Mode 18 is the only mode with more than 64 weight bits.
+ for (uint32_t i = 0; i < 16; i++)
+ unpacked.m_astc.m_weights[i] = (uint8_t)read_bits1_to_9(blk.m_bytes, bit_ofs, i ? weight_bits : (weight_bits - 1));
+ }
+ else
+ {
+ // All other modes have <= 64 weight bits.
+ uint64_t bits;
+
+ // Read the weight bits
+ if ((IS_BIG_ENDIAN) || (!USE_UNALIGNED_WORD_READS))
+ bits = read_bits64(blk.m_bytes, bit_ofs, std::min(64, 128 - (int)bit_ofs));
+ else
+ {
+ bits = blk.m_qwords[1];
+
+ if (bit_ofs >= 64U)
+ bits >>= (bit_ofs - 64U);
+ else
+ {
+ assert(bit_ofs >= 56U);
+
+ uint32_t bits_needed = 64U - bit_ofs;
+ bits <<= bits_needed;
+ bits |= (blk.m_bytes[7] >> (8U - bits_needed));
+ }
+ }
+
+ bit_ofs = 0;
+
+ const uint32_t mask = (1U << weight_bits) - 1U;
+ const uint32_t anchor_mask = (1U << (weight_bits - 1U)) - 1U;
+
+ if (total_planes == 2)
+ {
+ // Dual plane modes always have a single subset, and the first 2 weights are anchors.
+
+ unpacked.m_astc.m_weights[0] = (uint8_t)((uint32_t)(bits >> bit_ofs) & anchor_mask);
+ bit_ofs += (weight_bits - 1);
+
+ unpacked.m_astc.m_weights[1] = (uint8_t)((uint32_t)(bits >> bit_ofs) & anchor_mask);
+ bit_ofs += (weight_bits - 1);
+
+ for (uint32_t i = 2; i < 32; i++)
+ {
+ unpacked.m_astc.m_weights[i] = (uint8_t)((uint32_t)(bits >> bit_ofs) & mask);
+ bit_ofs += weight_bits;
+ }
+ }
+ else
+ {
+ if (subsets == 1)
+ {
+ // Specialize the single subset case.
+ if (weight_bits == 4)
+ {
+ assert(bit_ofs == 0);
+
+ // Specialize the most common case: 4-bit weights.
+ unpacked.m_astc.m_weights[0] = (uint8_t)((uint32_t)(bits) & 7);
+ unpacked.m_astc.m_weights[1] = (uint8_t)((uint32_t)(bits >> 3) & 15);
+ unpacked.m_astc.m_weights[2] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 1)) & 15);
+ unpacked.m_astc.m_weights[3] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 2)) & 15);
+
+ unpacked.m_astc.m_weights[4] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 3)) & 15);
+ unpacked.m_astc.m_weights[5] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 4)) & 15);
+ unpacked.m_astc.m_weights[6] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 5)) & 15);
+ unpacked.m_astc.m_weights[7] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 6)) & 15);
+
+ unpacked.m_astc.m_weights[8] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 7)) & 15);
+ unpacked.m_astc.m_weights[9] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 8)) & 15);
+ unpacked.m_astc.m_weights[10] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 9)) & 15);
+ unpacked.m_astc.m_weights[11] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 10)) & 15);
+
+ unpacked.m_astc.m_weights[12] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 11)) & 15);
+ unpacked.m_astc.m_weights[13] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 12)) & 15);
+ unpacked.m_astc.m_weights[14] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 13)) & 15);
+ unpacked.m_astc.m_weights[15] = (uint8_t)((uint32_t)(bits >> (3 + 4 * 14)) & 15);
+ }
+ else
+ {
+ // First weight is always an anchor.
+ unpacked.m_astc.m_weights[0] = (uint8_t)((uint32_t)(bits >> bit_ofs) & anchor_mask);
+ bit_ofs += (weight_bits - 1);
+
+ for (uint32_t i = 1; i < 16; i++)
+ {
+ unpacked.m_astc.m_weights[i] = (uint8_t)((uint32_t)(bits >> bit_ofs) & mask);
+ bit_ofs += weight_bits;
+ }
+ }
+ }
+ else
+ {
+ const uint32_t a0 = pSubset_anchor_indices[0], a1 = pSubset_anchor_indices[1], a2 = pSubset_anchor_indices[2];
+
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ if ((i == a0) || (i == a1) || (i == a2))
+ {
+ unpacked.m_astc.m_weights[i] = (uint8_t)((uint32_t)(bits >> bit_ofs) & anchor_mask);
+ bit_ofs += (weight_bits - 1);
+ }
+ else
+ {
+ unpacked.m_astc.m_weights[i] = (uint8_t)((uint32_t)(bits >> bit_ofs) & mask);
+ bit_ofs += weight_bits;
+ }
+ }
+ }
+ }
+ }
+
+ if ((blue_contract_check) && (total_comps >= 3))
+ {
+ // We only need to disable ASTC Blue Contraction when we'll be packing to ASTC. The other transcoders don't care.
+ bool invert_subset[3] = { false, false, false };
+ bool any_flag = false;
+
+ for (uint32_t subset_index = 0; subset_index < subsets; subset_index++)
+ {
+ const int s0 = g_astc_unquant[endpoint_range][unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + 0]].m_unquant +
+ g_astc_unquant[endpoint_range][unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + 2]].m_unquant +
+ g_astc_unquant[endpoint_range][unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + 4]].m_unquant;
+
+ const int s1 = g_astc_unquant[endpoint_range][unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + 1]].m_unquant +
+ g_astc_unquant[endpoint_range][unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + 3]].m_unquant +
+ g_astc_unquant[endpoint_range][unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + 5]].m_unquant;
+
+ if (s1 < s0)
+ {
+ for (uint32_t c = 0; c < total_comps; c++)
+ std::swap(unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + c * 2 + 0], unpacked.m_astc.m_endpoints[subset_index * total_comps * 2 + c * 2 + 1]);
+
+ invert_subset[subset_index] = true;
+ any_flag = true;
+ }
+ }
+
+ if (any_flag)
+ {
+ const uint32_t weight_mask = (1 << weight_bits) - 1;
+
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ uint32_t subset = pPartition_pattern[i];
+
+ if (invert_subset[subset])
+ {
+ unpacked.m_astc.m_weights[i * total_planes] = (uint8_t)(weight_mask - unpacked.m_astc.m_weights[i * total_planes]);
+
+ if (total_planes == 2)
+ unpacked.m_astc.m_weights[i * total_planes + 1] = (uint8_t)(weight_mask - unpacked.m_astc.m_weights[i * total_planes + 1]);
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ static const uint32_t* g_astc_weight_tables[6] = { nullptr, g_bc7_weights1, g_bc7_weights2, g_bc7_weights3, g_astc_weights4, g_astc_weights5 };
+
+ bool unpack_uastc(uint32_t mode, uint32_t common_pattern, const color32& solid_color, const astc_block_desc& astc, color32* pPixels, bool srgb)
+ {
+ if (mode == UASTC_MODE_INDEX_SOLID_COLOR)
+ {
+ for (uint32_t i = 0; i < 16; i++)
+ pPixels[i] = solid_color;
+ return true;
+ }
+
+ color32 endpoints[3][2];
+
+ const uint32_t total_subsets = g_uastc_mode_subsets[mode];
+ const uint32_t total_comps = std::min(4U, g_uastc_mode_comps[mode]);
+ const uint32_t endpoint_range = g_uastc_mode_endpoint_ranges[mode];
+ const uint32_t total_planes = g_uastc_mode_planes[mode];
+ const uint32_t weight_bits = g_uastc_mode_weight_bits[mode];
+ const uint32_t weight_levels = 1 << weight_bits;
+
+ for (uint32_t subset_index = 0; subset_index < total_subsets; subset_index++)
+ {
+ if (total_comps == 2)
+ {
+ const uint32_t ll = g_astc_unquant[endpoint_range][astc.m_endpoints[subset_index * total_comps * 2 + 0 * 2 + 0]].m_unquant;
+ const uint32_t lh = g_astc_unquant[endpoint_range][astc.m_endpoints[subset_index * total_comps * 2 + 0 * 2 + 1]].m_unquant;
+
+ const uint32_t al = g_astc_unquant[endpoint_range][astc.m_endpoints[subset_index * total_comps * 2 + 1 * 2 + 0]].m_unquant;
+ const uint32_t ah = g_astc_unquant[endpoint_range][astc.m_endpoints[subset_index * total_comps * 2 + 1 * 2 + 1]].m_unquant;
+
+ endpoints[subset_index][0].set_noclamp_rgba(ll, ll, ll, al);
+ endpoints[subset_index][1].set_noclamp_rgba(lh, lh, lh, ah);
+ }
+ else
+ {
+ for (uint32_t comp_index = 0; comp_index < total_comps; comp_index++)
+ {
+ endpoints[subset_index][0][comp_index] = g_astc_unquant[endpoint_range][astc.m_endpoints[subset_index * total_comps * 2 + comp_index * 2 + 0]].m_unquant;
+ endpoints[subset_index][1][comp_index] = g_astc_unquant[endpoint_range][astc.m_endpoints[subset_index * total_comps * 2 + comp_index * 2 + 1]].m_unquant;
+ }
+ for (uint32_t comp_index = total_comps; comp_index < 4; comp_index++)
+ {
+ endpoints[subset_index][0][comp_index] = 255;
+ endpoints[subset_index][1][comp_index] = 255;
+ }
+ }
+ }
+
+ color32 block_colors[3][32];
+
+ const uint32_t* pWeights = g_astc_weight_tables[weight_bits];
+
+ for (uint32_t subset_index = 0; subset_index < total_subsets; subset_index++)
+ {
+ for (uint32_t l = 0; l < weight_levels; l++)
+ {
+ if (total_comps == 2)
+ {
+ const uint8_t lc = (uint8_t)astc_interpolate(endpoints[subset_index][0][0], endpoints[subset_index][1][0], pWeights[l], srgb);
+ const uint8_t ac = (uint8_t)astc_interpolate(endpoints[subset_index][0][3], endpoints[subset_index][1][3], pWeights[l], srgb);
+
+ block_colors[subset_index][l].set(lc, lc, lc, ac);
+ }
+ else
+ {
+ uint32_t comp_index;
+ for (comp_index = 0; comp_index < total_comps; comp_index++)
+ block_colors[subset_index][l][comp_index] = (uint8_t)astc_interpolate(endpoints[subset_index][0][comp_index], endpoints[subset_index][1][comp_index], pWeights[l], srgb);
+
+ for (; comp_index < 4; comp_index++)
+ block_colors[subset_index][l][comp_index] = 255;
+ }
+ }
+ }
+
+ const uint8_t* pPartition_pattern = g_zero_pattern;
+
+ if (total_subsets >= 2)
+ {
+ if (total_subsets == 3)
+ pPartition_pattern = &g_astc_bc7_patterns3[common_pattern][0];
+ else if (mode == 7)
+ pPartition_pattern = &g_bc7_3_astc2_patterns2[common_pattern][0];
+ else
+ pPartition_pattern = &g_astc_bc7_patterns2[common_pattern][0];
+
+#ifdef _DEBUG
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ assert(pPartition_pattern[i] == (uint8_t)astc_compute_texel_partition(astc.m_partition_seed, i & 3, i >> 2, 0, total_subsets, true));
+ }
+#endif
+ }
+
+ if (total_planes == 1)
+ {
+ if (total_subsets == 1)
+ {
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ assert(astc.m_weights[i] < weight_levels);
+ pPixels[i] = block_colors[0][astc.m_weights[i]];
+ }
+ }
+ else
+ {
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ assert(astc.m_weights[i] < weight_levels);
+ pPixels[i] = block_colors[pPartition_pattern[i]][astc.m_weights[i]];
+ }
+ }
+ }
+ else
+ {
+ assert(total_subsets == 1);
+
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ const uint32_t subset_index = 0; // pPartition_pattern[i];
+
+ const uint32_t weight_index0 = astc.m_weights[i * 2];
+ const uint32_t weight_index1 = astc.m_weights[i * 2 + 1];
+
+ assert(weight_index0 < weight_levels && weight_index1 < weight_levels);
+
+ color32& c = pPixels[i];
+ for (uint32_t comp = 0; comp < 4; comp++)
+ {
+ if ((int)comp == astc.m_ccs)
+ c[comp] = block_colors[subset_index][weight_index1][comp];
+ else
+ c[comp] = block_colors[subset_index][weight_index0][comp];
+ }
+ }
+ }
+
+ return true;
+ }
+
+ bool unpack_uastc(const unpacked_uastc_block& unpacked_blk, color32* pPixels, bool srgb)
+ {
+ return unpack_uastc(unpacked_blk.m_mode, unpacked_blk.m_common_pattern, unpacked_blk.m_solid_color, unpacked_blk.m_astc, pPixels, srgb);
+ }
+
+ bool unpack_uastc(const uastc_block& blk, color32* pPixels, bool srgb)
+ {
+ unpacked_uastc_block unpacked_blk;
+
+ if (!unpack_uastc(blk, unpacked_blk, false, false))
+ return false;
+
+ return unpack_uastc(unpacked_blk, pPixels, srgb);
+ }
+
+ // Determines the best shared pbits to use to encode xl/xh
+ static void determine_shared_pbits(
+ uint32_t total_comps, uint32_t comp_bits, float xl[4], float xh[4],
+ color_quad_u8& bestMinColor, color_quad_u8& bestMaxColor, uint32_t best_pbits[2])
+ {
+ const uint32_t total_bits = comp_bits + 1;
+ assert(total_bits >= 4 && total_bits <= 8);
+
+ const int iscalep = (1 << total_bits) - 1;
+ const float scalep = (float)iscalep;
+
+ float best_err = 1e+9f;
+
+ for (int p = 0; p < 2; p++)
+ {
+ color_quad_u8 xMinColor, xMaxColor;
+ for (uint32_t c = 0; c < 4; c++)
+ {
+ xMinColor.m_c[c] = (uint8_t)(clampi(((int)((xl[c] * scalep - p) / 2.0f + .5f)) * 2 + p, p, iscalep - 1 + p));
+ xMaxColor.m_c[c] = (uint8_t)(clampi(((int)((xh[c] * scalep - p) / 2.0f + .5f)) * 2 + p, p, iscalep - 1 + p));
+ }
+
+ color_quad_u8 scaledLow, scaledHigh;
+
+ for (uint32_t i = 0; i < 4; i++)
+ {
+ scaledLow.m_c[i] = (xMinColor.m_c[i] << (8 - total_bits));
+ scaledLow.m_c[i] |= (scaledLow.m_c[i] >> total_bits);
+ assert(scaledLow.m_c[i] <= 255);
+
+ scaledHigh.m_c[i] = (xMaxColor.m_c[i] << (8 - total_bits));
+ scaledHigh.m_c[i] |= (scaledHigh.m_c[i] >> total_bits);
+ assert(scaledHigh.m_c[i] <= 255);
+ }
+
+ float err = 0;
+ for (uint32_t i = 0; i < total_comps; i++)
+ err += basisu::squaref((scaledLow.m_c[i] / 255.0f) - xl[i]) + basisu::squaref((scaledHigh.m_c[i] / 255.0f) - xh[i]);
+
+ if (err < best_err)
+ {
+ best_err = err;
+ best_pbits[0] = p;
+ best_pbits[1] = p;
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ bestMinColor.m_c[j] = xMinColor.m_c[j] >> 1;
+ bestMaxColor.m_c[j] = xMaxColor.m_c[j] >> 1;
+ }
+ }
+ }
+ }
+
+ // Determines the best unique pbits to use to encode xl/xh
+ static void determine_unique_pbits(
+ uint32_t total_comps, uint32_t comp_bits, float xl[4], float xh[4],
+ color_quad_u8& bestMinColor, color_quad_u8& bestMaxColor, uint32_t best_pbits[2])
+ {
+ const uint32_t total_bits = comp_bits + 1;
+ const int iscalep = (1 << total_bits) - 1;
+ const float scalep = (float)iscalep;
+
+ float best_err0 = 1e+9f;
+ float best_err1 = 1e+9f;
+
+ for (int p = 0; p < 2; p++)
+ {
+ color_quad_u8 xMinColor, xMaxColor;
+
+ for (uint32_t c = 0; c < 4; c++)
+ {
+ xMinColor.m_c[c] = (uint8_t)(clampi(((int)((xl[c] * scalep - p) / 2.0f + .5f)) * 2 + p, p, iscalep - 1 + p));
+ xMaxColor.m_c[c] = (uint8_t)(clampi(((int)((xh[c] * scalep - p) / 2.0f + .5f)) * 2 + p, p, iscalep - 1 + p));
+ }
+
+ color_quad_u8 scaledLow, scaledHigh;
+ for (uint32_t i = 0; i < 4; i++)
+ {
+ scaledLow.m_c[i] = (xMinColor.m_c[i] << (8 - total_bits));
+ scaledLow.m_c[i] |= (scaledLow.m_c[i] >> total_bits);
+ assert(scaledLow.m_c[i] <= 255);
+
+ scaledHigh.m_c[i] = (xMaxColor.m_c[i] << (8 - total_bits));
+ scaledHigh.m_c[i] |= (scaledHigh.m_c[i] >> total_bits);
+ assert(scaledHigh.m_c[i] <= 255);
+ }
+
+ float err0 = 0, err1 = 0;
+ for (uint32_t i = 0; i < total_comps; i++)
+ {
+ err0 += basisu::squaref(scaledLow.m_c[i] - xl[i] * 255.0f);
+ err1 += basisu::squaref(scaledHigh.m_c[i] - xh[i] * 255.0f);
+ }
+
+ if (err0 < best_err0)
+ {
+ best_err0 = err0;
+ best_pbits[0] = p;
+
+ bestMinColor.m_c[0] = xMinColor.m_c[0] >> 1;
+ bestMinColor.m_c[1] = xMinColor.m_c[1] >> 1;
+ bestMinColor.m_c[2] = xMinColor.m_c[2] >> 1;
+ bestMinColor.m_c[3] = xMinColor.m_c[3] >> 1;
+ }
+
+ if (err1 < best_err1)
+ {
+ best_err1 = err1;
+ best_pbits[1] = p;
+
+ bestMaxColor.m_c[0] = xMaxColor.m_c[0] >> 1;
+ bestMaxColor.m_c[1] = xMaxColor.m_c[1] >> 1;
+ bestMaxColor.m_c[2] = xMaxColor.m_c[2] >> 1;
+ bestMaxColor.m_c[3] = xMaxColor.m_c[3] >> 1;
+ }
+ }
+ }
+
+ bool transcode_uastc_to_astc(const uastc_block& src_blk, void* pDst)
+ {
+ unpacked_uastc_block unpacked_src_blk;
+ if (!unpack_uastc(src_blk, unpacked_src_blk, true, false))
+ return false;
+
+ bool success = false;
+ if (unpacked_src_blk.m_mode == UASTC_MODE_INDEX_SOLID_COLOR)
+ {
+ pack_astc_solid_block(pDst, unpacked_src_blk.m_solid_color);
+ success = true;
+ }
+ else
+ {
+ success = pack_astc_block(static_cast(pDst), &unpacked_src_blk.m_astc, unpacked_src_blk.m_mode);
+ }
+
+ return success;
+ }
+
+ bool transcode_uastc_to_bc7(const unpacked_uastc_block& unpacked_src_blk, bc7_optimization_results& dst_blk)
+ {
+ memset(&dst_blk, 0, sizeof(dst_blk));
+
+ const uint32_t mode = unpacked_src_blk.m_mode;
+
+ const uint32_t endpoint_range = g_uastc_mode_endpoint_ranges[mode];
+ const uint32_t total_comps = g_uastc_mode_comps[mode];
+
+ switch (mode)
+ {
+ case 0:
+ case 5:
+ case 10:
+ case 12:
+ case 14:
+ case 15:
+ case 18:
+ {
+ // MODE 0: DualPlane: 0, WeightRange: 8 (16), Subsets: 1, EndpointRange: 19 (192) - BC7 MODE6 RGB
+ // MODE 5: DualPlane: 0, WeightRange : 5 (8), Subsets : 1, EndpointRange : 20 (256) - BC7 MODE6 RGB
+ // MODE 10 DualPlane: 0, WeightRange: 8 (16), Subsets: 1, EndpointRange: 13 (48) - BC7 MODE6
+ // MODE 12: DualPlane: 0, WeightRange : 5 (8), Subsets : 1, EndpointRange : 19 (192) - BC7 MODE6
+ // MODE 14: DualPlane: 0, WeightRange : 2 (4), Subsets : 1, EndpointRange : 20 (256) - BC7 MODE6
+ // MODE 18: DualPlane: 0, WeightRange : 11 (32), Subsets : 1, CEM : 8, EndpointRange : 11 (32) - BC7 MODE6
+ // MODE 15: DualPlane: 0, WeightRange : 8 (16), Subsets : 1, CEM : 4 (LA Direct), EndpointRange : 20 (256) - BC7 MODE6
+ dst_blk.m_mode = 6;
+
+ float xl[4], xh[4];
+ if (total_comps == 2)
+ {
+ xl[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[0]].m_unquant / 255.0f;
+ xh[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[1]].m_unquant / 255.0f;
+
+ xl[1] = xl[0];
+ xh[1] = xh[0];
+
+ xl[2] = xl[0];
+ xh[2] = xh[0];
+
+ xl[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[2]].m_unquant / 255.0f;
+ xh[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[3]].m_unquant / 255.0f;
+ }
+ else
+ {
+ xl[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[0]].m_unquant / 255.0f;
+ xl[1] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[2]].m_unquant / 255.0f;
+ xl[2] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[4]].m_unquant / 255.0f;
+
+ xh[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[1]].m_unquant / 255.0f;
+ xh[1] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[3]].m_unquant / 255.0f;
+ xh[2] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[5]].m_unquant / 255.0f;
+
+ if (total_comps == 4)
+ {
+ xl[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[6]].m_unquant / 255.0f;
+ xh[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[7]].m_unquant / 255.0f;
+ }
+ else
+ {
+ xl[3] = 1.0f;
+ xh[3] = 1.0f;
+ }
+ }
+
+ uint32_t best_pbits[2];
+ color_quad_u8 bestMinColor, bestMaxColor;
+ determine_unique_pbits((total_comps == 2) ? 4 : total_comps, 7, xl, xh, bestMinColor, bestMaxColor, best_pbits);
+
+ dst_blk.m_low[0] = bestMinColor;
+ dst_blk.m_high[0] = bestMaxColor;
+
+ if (total_comps == 3)
+ {
+ dst_blk.m_low[0].m_c[3] = 127;
+ dst_blk.m_high[0].m_c[3] = 127;
+ }
+
+ dst_blk.m_pbits[0][0] = best_pbits[0];
+ dst_blk.m_pbits[0][1] = best_pbits[1];
+
+ if (mode == 18)
+ {
+ const uint8_t s_bc7_5_to_4[32] = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15 };
+ for (uint32_t i = 0; i < 16; i++)
+ dst_blk.m_selectors[i] = s_bc7_5_to_4[unpacked_src_blk.m_astc.m_weights[i]];
+ }
+ else if (mode == 14)
+ {
+ const uint8_t s_bc7_2_to_4[4] = { 0, 5, 10, 15 };
+ for (uint32_t i = 0; i < 16; i++)
+ dst_blk.m_selectors[i] = s_bc7_2_to_4[unpacked_src_blk.m_astc.m_weights[i]];
+ }
+ else if ((mode == 5) || (mode == 12))
+ {
+ const uint8_t s_bc7_3_to_4[8] = { 0, 2, 4, 6, 9, 11, 13, 15 };
+ for (uint32_t i = 0; i < 16; i++)
+ dst_blk.m_selectors[i] = s_bc7_3_to_4[unpacked_src_blk.m_astc.m_weights[i]];
+ }
+ else
+ {
+ for (uint32_t i = 0; i < 16; i++)
+ dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];
+ }
+
+ break;
+ }
+ case 1:
+ {
+ // DualPlane: 0, WeightRange : 2 (4), Subsets : 1, EndpointRange : 20 (256) - BC7 MODE3
+ // Mode 1 uses endpoint range 20 - no need to use ASTC dequant tables.
+ dst_blk.m_mode = 3;
+
+ float xl[4], xh[4];
+ xl[0] = unpacked_src_blk.m_astc.m_endpoints[0] / 255.0f;
+ xl[1] = unpacked_src_blk.m_astc.m_endpoints[2] / 255.0f;
+ xl[2] = unpacked_src_blk.m_astc.m_endpoints[4] / 255.0f;
+ xl[3] = 1.0f;
+
+ xh[0] = unpacked_src_blk.m_astc.m_endpoints[1] / 255.0f;
+ xh[1] = unpacked_src_blk.m_astc.m_endpoints[3] / 255.0f;
+ xh[2] = unpacked_src_blk.m_astc.m_endpoints[5] / 255.0f;
+ xh[3] = 1.0f;
+
+ uint32_t best_pbits[2];
+ color_quad_u8 bestMinColor, bestMaxColor;
+ memset(&bestMinColor, 0, sizeof(bestMinColor));
+ memset(&bestMaxColor, 0, sizeof(bestMaxColor));
+ determine_unique_pbits(3, 7, xl, xh, bestMinColor, bestMaxColor, best_pbits);
+
+ for (uint32_t i = 0; i < 3; i++)
+ {
+ dst_blk.m_low[0].m_c[i] = bestMinColor.m_c[i];
+ dst_blk.m_high[0].m_c[i] = bestMaxColor.m_c[i];
+ dst_blk.m_low[1].m_c[i] = bestMinColor.m_c[i];
+ dst_blk.m_high[1].m_c[i] = bestMaxColor.m_c[i];
+ }
+ dst_blk.m_pbits[0][0] = best_pbits[0];
+ dst_blk.m_pbits[0][1] = best_pbits[1];
+ dst_blk.m_pbits[1][0] = best_pbits[0];
+ dst_blk.m_pbits[1][1] = best_pbits[1];
+
+ for (uint32_t i = 0; i < 16; i++)
+ dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];
+
+ break;
+ }
+ case 2:
+ {
+ // 2. DualPlane: 0, WeightRange : 5 (8), Subsets : 2, EndpointRange : 8 (16) - BC7 MODE1
+ dst_blk.m_mode = 1;
+ dst_blk.m_partition = g_astc_bc7_common_partitions2[unpacked_src_blk.m_common_pattern].m_bc7;
+
+ const bool invert_partition = g_astc_bc7_common_partitions2[unpacked_src_blk.m_common_pattern].m_invert;
+
+ float xl[4], xh[4];
+ xl[3] = 1.0f;
+ xh[3] = 1.0f;
+
+ for (uint32_t subset = 0; subset < 2; subset++)
+ {
+ for (uint32_t i = 0; i < 3; i++)
+ {
+ uint32_t v = unpacked_src_blk.m_astc.m_endpoints[i * 2 + subset * 6];
+ v = (v << 4) | v;
+ xl[i] = v / 255.0f;
+
+ v = unpacked_src_blk.m_astc.m_endpoints[i * 2 + subset * 6 + 1];
+ v = (v << 4) | v;
+ xh[i] = v / 255.0f;
+ }
+
+ uint32_t best_pbits[2] = { 0, 0 };
+ color_quad_u8 bestMinColor, bestMaxColor;
+ memset(&bestMinColor, 0, sizeof(bestMinColor));
+ memset(&bestMaxColor, 0, sizeof(bestMaxColor));
+ determine_shared_pbits(3, 6, xl, xh, bestMinColor, bestMaxColor, best_pbits);
+
+ const uint32_t bc7_subset_index = invert_partition ? (1 - subset) : subset;
+
+ for (uint32_t i = 0; i < 3; i++)
+ {
+ dst_blk.m_low[bc7_subset_index].m_c[i] = bestMinColor.m_c[i];
+ dst_blk.m_high[bc7_subset_index].m_c[i] = bestMaxColor.m_c[i];
+ }
+
+ dst_blk.m_pbits[bc7_subset_index][0] = best_pbits[0];
+ } // subset
+
+ for (uint32_t i = 0; i < 16; i++)
+ dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];
+
+ break;
+ }
+ case 3:
+ {
+ // DualPlane: 0, WeightRange : 2 (4), Subsets : 3, EndpointRange : 7 (12) - BC7 MODE2
+ dst_blk.m_mode = 2;
+ dst_blk.m_partition = g_astc_bc7_common_partitions3[unpacked_src_blk.m_common_pattern].m_bc7;
+
+ const uint32_t perm = g_astc_bc7_common_partitions3[unpacked_src_blk.m_common_pattern].m_astc_to_bc7_perm;
+
+ for (uint32_t subset = 0; subset < 3; subset++)
+ {
+ for (uint32_t comp = 0; comp < 3; comp++)
+ {
+ uint32_t lo = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[comp * 2 + 0 + subset * 6]].m_unquant;
+ uint32_t hi = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[comp * 2 + 1 + subset * 6]].m_unquant;
+
+ // TODO: I think this can be improved by using tables like Basis Universal does with ETC1S conversion.
+ lo = (lo * 31 + 127) / 255;
+ hi = (hi * 31 + 127) / 255;
+
+ const uint32_t bc7_subset_index = g_astc_to_bc7_partition_index_perm_tables[perm][subset];
+
+ dst_blk.m_low[bc7_subset_index].m_c[comp] = (uint8_t)lo;
+ dst_blk.m_high[bc7_subset_index].m_c[comp] = (uint8_t)hi;
+ }
+ }
+
+ for (uint32_t i = 0; i < 16; i++)
+ dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];
+
+ break;
+ }
+ case 4:
+ {
+ // 4. DualPlane: 0, WeightRange: 2 (4), Subsets: 2, EndpointRange: 12 (40) - BC7 MODE3
+ dst_blk.m_mode = 3;
+ dst_blk.m_partition = g_astc_bc7_common_partitions2[unpacked_src_blk.m_common_pattern].m_bc7;
+
+ const bool invert_partition = g_astc_bc7_common_partitions2[unpacked_src_blk.m_common_pattern].m_invert;
+
+ float xl[4], xh[4];
+ xl[3] = 1.0f;
+ xh[3] = 1.0f;
+
+ for (uint32_t subset = 0; subset < 2; subset++)
+ {
+ for (uint32_t i = 0; i < 3; i++)
+ {
+ xl[i] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[i * 2 + subset * 6]].m_unquant / 255.0f;
+ xh[i] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[i * 2 + subset * 6 + 1]].m_unquant / 255.0f;
+ }
+
+ uint32_t best_pbits[2] = { 0, 0 };
+ color_quad_u8 bestMinColor, bestMaxColor;
+ memset(&bestMinColor, 0, sizeof(bestMinColor));
+ memset(&bestMaxColor, 0, sizeof(bestMaxColor));
+ determine_unique_pbits(3, 7, xl, xh, bestMinColor, bestMaxColor, best_pbits);
+
+ const uint32_t bc7_subset_index = invert_partition ? (1 - subset) : subset;
+
+ for (uint32_t i = 0; i < 3; i++)
+ {
+ dst_blk.m_low[bc7_subset_index].m_c[i] = bestMinColor.m_c[i];
+ dst_blk.m_high[bc7_subset_index].m_c[i] = bestMaxColor.m_c[i];
+ }
+ dst_blk.m_low[bc7_subset_index].m_c[3] = 127;
+ dst_blk.m_high[bc7_subset_index].m_c[3] = 127;
+
+ dst_blk.m_pbits[bc7_subset_index][0] = best_pbits[0];
+ dst_blk.m_pbits[bc7_subset_index][1] = best_pbits[1];
+
+ } // subset
+
+ for (uint32_t i = 0; i < 16; i++)
+ dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];
+
+ break;
+ }
+ case 6:
+ case 11:
+ case 13:
+ case 17:
+ {
+ // MODE 6: DualPlane: 1, WeightRange : 2 (4), Subsets : 1, EndpointRange : 18 (160) - BC7 MODE5 RGB
+ // MODE 11: DualPlane: 1, WeightRange: 2 (4), Subsets: 1, EndpointRange: 13 (48) - BC7 MODE5
+ // MODE 13: DualPlane: 1, WeightRange: 0 (2), Subsets : 1, EndpointRange : 20 (256) - BC7 MODE5
+ // MODE 17: DualPlane: 1, WeightRange: 2 (4), Subsets: 1, CEM: 4 (LA Direct), EndpointRange: 20 (256) - BC7 MODE5
+ dst_blk.m_mode = 5;
+ dst_blk.m_rotation = (unpacked_src_blk.m_astc.m_ccs + 1) & 3;
+
+ if (total_comps == 2)
+ {
+ assert(unpacked_src_blk.m_astc.m_ccs == 3);
+
+ dst_blk.m_low->m_c[0] = (uint8_t)((g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[0]].m_unquant * 127 + 127) / 255);
+ dst_blk.m_high->m_c[0] = (uint8_t)((g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[1]].m_unquant * 127 + 127) / 255);
+
+ dst_blk.m_low->m_c[1] = dst_blk.m_low->m_c[0];
+ dst_blk.m_high->m_c[1] = dst_blk.m_high->m_c[0];
+
+ dst_blk.m_low->m_c[2] = dst_blk.m_low->m_c[0];
+ dst_blk.m_high->m_c[2] = dst_blk.m_high->m_c[0];
+
+ dst_blk.m_low->m_c[3] = (uint8_t)(g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[2]].m_unquant);
+ dst_blk.m_high->m_c[3] = (uint8_t)(g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[3]].m_unquant);
+ }
+ else
+ {
+ for (uint32_t astc_comp = 0; astc_comp < 4; astc_comp++)
+ {
+ uint32_t bc7_comp = astc_comp;
+ // ASTC and BC7 handle dual plane component rotations differently:
+ // ASTC: 2nd plane separately interpolates the CCS channel.
+ // BC7: 2nd plane channel is swapped with alpha, 2nd plane controls alpha interpolation, then we swap alpha with the desired channel.
+ if (astc_comp == (uint32_t)unpacked_src_blk.m_astc.m_ccs)
+ bc7_comp = 3;
+ else if (astc_comp == 3)
+ bc7_comp = unpacked_src_blk.m_astc.m_ccs;
+
+ uint32_t l = 255, h = 255;
+ if (astc_comp < total_comps)
+ {
+ l = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[astc_comp * 2 + 0]].m_unquant;
+ h = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[astc_comp * 2 + 1]].m_unquant;
+ }
+
+ if (bc7_comp < 3)
+ {
+ l = (l * 127 + 127) / 255;
+ h = (h * 127 + 127) / 255;
+ }
+
+ dst_blk.m_low->m_c[bc7_comp] = (uint8_t)l;
+ dst_blk.m_high->m_c[bc7_comp] = (uint8_t)h;
+ }
+ }
+
+ if (mode == 13)
+ {
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i * 2] ? 3 : 0;
+ dst_blk.m_alpha_selectors[i] = unpacked_src_blk.m_astc.m_weights[i * 2 + 1] ? 3 : 0;
+ }
+ }
+ else
+ {
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i * 2];
+ dst_blk.m_alpha_selectors[i] = unpacked_src_blk.m_astc.m_weights[i * 2 + 1];
+ }
+ }
+
+ break;
+ }
+ case 7:
+ {
+ // DualPlane: 0, WeightRange : 2 (4), Subsets : 2, EndpointRange : 12 (40) - BC7 MODE2
+ dst_blk.m_mode = 2;
+ dst_blk.m_partition = g_bc7_3_astc2_common_partitions[unpacked_src_blk.m_common_pattern].m_bc73;
+
+ const uint32_t common_pattern_k = g_bc7_3_astc2_common_partitions[unpacked_src_blk.m_common_pattern].k;
+
+ for (uint32_t bc7_part = 0; bc7_part < 3; bc7_part++)
+ {
+ const uint32_t astc_part = bc7_convert_partition_index_3_to_2(bc7_part, common_pattern_k);
+
+ for (uint32_t c = 0; c < 3; c++)
+ {
+ dst_blk.m_low[bc7_part].m_c[c] = (g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[c * 2 + 0 + astc_part * 6]].m_unquant * 31 + 127) / 255;
+ dst_blk.m_high[bc7_part].m_c[c] = (g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[c * 2 + 1 + astc_part * 6]].m_unquant * 31 + 127) / 255;
+ }
+ }
+
+ for (uint32_t i = 0; i < 16; i++)
+ dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];
+
+ break;
+ }
+ case UASTC_MODE_INDEX_SOLID_COLOR:
+ {
+ // Void-Extent: Solid Color RGBA (BC7 MODE5 or MODE6)
+ const color32& solid_color = unpacked_src_blk.m_solid_color;
+
+ uint32_t best_err0 = g_bc7_mode_6_optimal_endpoints[solid_color.r][0].m_error + g_bc7_mode_6_optimal_endpoints[solid_color.g][0].m_error +
+ g_bc7_mode_6_optimal_endpoints[solid_color.b][0].m_error + g_bc7_mode_6_optimal_endpoints[solid_color.a][0].m_error;
+
+ uint32_t best_err1 = g_bc7_mode_6_optimal_endpoints[solid_color.r][1].m_error + g_bc7_mode_6_optimal_endpoints[solid_color.g][1].m_error +
+ g_bc7_mode_6_optimal_endpoints[solid_color.b][1].m_error + g_bc7_mode_6_optimal_endpoints[solid_color.a][1].m_error;
+
+ if (best_err0 > 0 && best_err1 > 0)
+ {
+ dst_blk.m_mode = 5;
+
+ for (uint32_t c = 0; c < 3; c++)
+ {
+ dst_blk.m_low[0].m_c[c] = g_bc7_mode_5_optimal_endpoints[solid_color.c[c]].m_lo;
+ dst_blk.m_high[0].m_c[c] = g_bc7_mode_5_optimal_endpoints[solid_color.c[c]].m_hi;
+ }
+
+ memset(dst_blk.m_selectors, BC7ENC_MODE_5_OPTIMAL_INDEX, 16);
+
+ dst_blk.m_low[0].m_c[3] = solid_color.c[3];
+ dst_blk.m_high[0].m_c[3] = solid_color.c[3];
+
+ //memset(dst_blk.m_alpha_selectors, 0, 16);
+ }
+ else
+ {
+ dst_blk.m_mode = 6;
+
+ uint32_t best_p = 0;
+ if (best_err1 < best_err0)
+ best_p = 1;
+
+ for (uint32_t c = 0; c < 4; c++)
+ {
+ dst_blk.m_low[0].m_c[c] = g_bc7_mode_6_optimal_endpoints[solid_color.c[c]][best_p].m_lo;
+ dst_blk.m_high[0].m_c[c] = g_bc7_mode_6_optimal_endpoints[solid_color.c[c]][best_p].m_hi;
+ }
+
+ dst_blk.m_pbits[0][0] = best_p;
+ dst_blk.m_pbits[0][1] = best_p;
+ memset(dst_blk.m_selectors, BC7ENC_MODE_6_OPTIMAL_INDEX, 16);
+ }
+
+ break;
+ }
+ case 9:
+ case 16:
+ {
+ // 9. DualPlane: 0, WeightRange : 2 (4), Subsets : 2, EndpointRange : 8 (16) - BC7 MODE7
+ // 16. DualPlane: 0, WeightRange: 2 (4), Subsets: 2, CEM: 4 (LA Direct), EndpointRange: 20 (256) - BC7 MODE7
+
+ dst_blk.m_mode = 7;
+ dst_blk.m_partition = g_astc_bc7_common_partitions2[unpacked_src_blk.m_common_pattern].m_bc7;
+
+ const bool invert_partition = g_astc_bc7_common_partitions2[unpacked_src_blk.m_common_pattern].m_invert;
+
+ for (uint32_t astc_subset = 0; astc_subset < 2; astc_subset++)
+ {
+ float xl[4], xh[4];
+
+ if (total_comps == 2)
+ {
+ xl[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[0 + astc_subset * 4]].m_unquant / 255.0f;
+ xh[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[1 + astc_subset * 4]].m_unquant / 255.0f;
+
+ xl[1] = xl[0];
+ xh[1] = xh[0];
+
+ xl[2] = xl[0];
+ xh[2] = xh[0];
+
+ xl[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[2 + astc_subset * 4]].m_unquant / 255.0f;
+ xh[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[3 + astc_subset * 4]].m_unquant / 255.0f;
+ }
+ else
+ {
+ xl[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[0 + astc_subset * 8]].m_unquant / 255.0f;
+ xl[1] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[2 + astc_subset * 8]].m_unquant / 255.0f;
+ xl[2] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[4 + astc_subset * 8]].m_unquant / 255.0f;
+ xl[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[6 + astc_subset * 8]].m_unquant / 255.0f;
+
+ xh[0] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[1 + astc_subset * 8]].m_unquant / 255.0f;
+ xh[1] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[3 + astc_subset * 8]].m_unquant / 255.0f;
+ xh[2] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[5 + astc_subset * 8]].m_unquant / 255.0f;
+ xh[3] = g_astc_unquant[endpoint_range][unpacked_src_blk.m_astc.m_endpoints[7 + astc_subset * 8]].m_unquant / 255.0f;
+ }
+
+ uint32_t best_pbits[2] = { 0, 0 };
+ color_quad_u8 bestMinColor, bestMaxColor;
+ memset(&bestMinColor, 0, sizeof(bestMinColor));
+ memset(&bestMaxColor, 0, sizeof(bestMaxColor));
+ determine_unique_pbits(4, 5, xl, xh, bestMinColor, bestMaxColor, best_pbits);
+
+ const uint32_t bc7_subset_index = invert_partition ? (1 - astc_subset) : astc_subset;
+
+ dst_blk.m_low[bc7_subset_index] = bestMinColor;
+ dst_blk.m_high[bc7_subset_index] = bestMaxColor;
+
+ dst_blk.m_pbits[bc7_subset_index][0] = best_pbits[0];
+ dst_blk.m_pbits[bc7_subset_index][1] = best_pbits[1];
+ } // astc_subset
+
+ for (uint32_t i = 0; i < 16; i++)
+ dst_blk.m_selectors[i] = unpacked_src_blk.m_astc.m_weights[i];
+
+ break;
+ }
+ default:
+ return false;
+ }
+
+ return true;
+ }
+
+ bool transcode_uastc_to_bc7(const uastc_block& src_blk, bc7_optimization_results& dst_blk)
+ {
+ unpacked_uastc_block unpacked_src_blk;
+ if (!unpack_uastc(src_blk, unpacked_src_blk, false, false))
+ return false;
+
+ return transcode_uastc_to_bc7(unpacked_src_blk, dst_blk);
+ }
+
+ bool transcode_uastc_to_bc7(const uastc_block& src_blk, void* pDst)
+ {
+ bc7_optimization_results temp;
+ if (!transcode_uastc_to_bc7(src_blk, temp))
+ return false;
+
+ encode_bc7_block(pDst, &temp);
+ return true;
+ }
+
+ color32 apply_etc1_bias(const color32 &block_color, uint32_t bias, uint32_t limit, uint32_t subblock)
+ {
+ color32 result;
+
+ for (uint32_t c = 0; c < 3; c++)
+ {
+ static const int s_divs[3] = { 1, 3, 9 };
+
+ int delta = 0;
+
+ switch (bias)
+ {
+ case 2: delta = subblock ? 0 : ((c == 0) ? -1 : 0); break;
+ case 5: delta = subblock ? 0 : ((c == 1) ? -1 : 0); break;
+ case 6: delta = subblock ? 0 : ((c == 2) ? -1 : 0); break;
+
+ case 7: delta = subblock ? 0 : ((c == 0) ? 1 : 0); break;
+ case 11: delta = subblock ? 0 : ((c == 1) ? 1 : 0); break;
+ case 15: delta = subblock ? 0 : ((c == 2) ? 1 : 0); break;
+
+ case 18: delta = subblock ? ((c == 0) ? -1 : 0) : 0; break;
+ case 19: delta = subblock ? ((c == 1) ? -1 : 0) : 0; break;
+ case 20: delta = subblock ? ((c == 2) ? -1 : 0) : 0; break;
+
+ case 21: delta = subblock ? ((c == 0) ? 1 : 0) : 0; break;
+ case 24: delta = subblock ? ((c == 1) ? 1 : 0) : 0; break;
+ case 8: delta = subblock ? ((c == 2) ? 1 : 0) : 0; break;
+
+ case 10: delta = -2; break;
+
+ case 27: delta = subblock ? 0 : -1; break;
+ case 28: delta = subblock ? -1 : 1; break;
+ case 29: delta = subblock ? 1 : 0; break;
+ case 30: delta = subblock ? -1 : 0; break;
+ case 31: delta = subblock ? 0 : 1; break;
+
+ default:
+ delta = ((bias / s_divs[c]) % 3) - 1;
+ break;
+ }
+
+ int v = block_color[c];
+ if (v == 0)
+ {
+ if (delta == -2)
+ v += 3;
+ else
+ v += delta + 1;
+ }
+ else if (v == (int)limit)
+ {
+ v += (delta - 1);
+ }
+ else
+ {
+ v += delta;
+ if ((v < 0) || (v > (int)limit))
+ v = (v - delta) - delta;
+ }
+
+ assert(v >= 0);
+ assert(v <= (int)limit);
+
+ result[c] = (uint8_t)v;
+ }
+
+ return result;
+ }
+
+ static void etc1_determine_selectors(decoder_etc_block& dst_blk, const color32* pSource_pixels, uint32_t first_subblock, uint32_t last_subblock)
+ {
+ static const uint8_t s_tran[4] = { 1, 0, 2, 3 };
+
+ uint16_t l_bitmask = 0;
+ uint16_t h_bitmask = 0;
+
+ for (uint32_t subblock = first_subblock; subblock < last_subblock; subblock++)
+ {
+ color32 block_colors[4];
+ dst_blk.get_block_colors(block_colors, subblock);
+
+ uint32_t block_y[4];
+ for (uint32_t i = 0; i < 4; i++)
+ block_y[i] = block_colors[i][0] * 54 + block_colors[i][1] * 183 + block_colors[i][2] * 19;
+
+ const uint32_t block_y01 = block_y[0] + block_y[1];
+ const uint32_t block_y12 = block_y[1] + block_y[2];
+ const uint32_t block_y23 = block_y[2] + block_y[3];
+
+ // X0 X0 X0 X0 X1 X1 X1 X1 X2 X2 X2 X2 X3 X3 X3 X3
+ // Y0 Y1 Y2 Y3 Y0 Y1 Y2 Y3 Y0 Y1 Y2 Y3 Y0 Y1 Y2 Y3
+
+ if (dst_blk.get_flip_bit())
+ {
+ uint32_t ofs = subblock * 2;
+
+ for (uint32_t y = 0; y < 2; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ const color32& c = pSource_pixels[x + (subblock * 2 + y) * 4];
+ const uint32_t l = c[0] * 108 + c[1] * 366 + c[2] * 38;
+
+ uint32_t t = s_tran[(l < block_y01) + (l < block_y12) + (l < block_y23)];
+
+ assert(ofs < 16);
+ l_bitmask |= ((t & 1) << ofs);
+ h_bitmask |= ((t >> 1) << ofs);
+ ofs += 4;
+ }
+
+ ofs = (int)ofs + 1 - 4 * 4;
+ }
+ }
+ else
+ {
+ uint32_t ofs = (subblock * 2) * 4;
+ for (uint32_t x = 0; x < 2; x++)
+ {
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ const color32& c = pSource_pixels[subblock * 2 + x + y * 4];
+ const uint32_t l = c[0] * 108 + c[1] * 366 + c[2] * 38;
+
+ uint32_t t = s_tran[(l < block_y01) + (l < block_y12) + (l < block_y23)];
+
+ assert(ofs < 16);
+ l_bitmask |= ((t & 1) << ofs);
+ h_bitmask |= ((t >> 1) << ofs);
+ ++ofs;
+ }
+ }
+ }
+ }
+
+ dst_blk.m_bytes[7] = (uint8_t)(l_bitmask);
+ dst_blk.m_bytes[6] = (uint8_t)(l_bitmask >> 8);
+ dst_blk.m_bytes[5] = (uint8_t)(h_bitmask);
+ dst_blk.m_bytes[4] = (uint8_t)(h_bitmask >> 8);
+ }
+
+ static const uint8_t s_etc1_solid_selectors[4][4] = { { 255, 255, 255, 255 }, { 255, 255, 0, 0 }, { 0, 0, 0, 0 }, {0, 0, 255, 255 } };
+
+ struct etc_coord2
+ {
+ uint8_t m_x, m_y;
+ };
+
+ // [flip][subblock][pixel_index]
+ const etc_coord2 g_etc1_pixel_coords[2][2][8] =
+ {
+ {
+ {
+ { 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 },
+ { 1, 0 }, { 1, 1 }, { 1, 2 }, { 1, 3 }
+ },
+ {
+ { 2, 0 }, { 2, 1 }, { 2, 2 }, { 2, 3 },
+ { 3, 0 }, { 3, 1 }, { 3, 2 }, { 3, 3 }
+ }
+ },
+ {
+ {
+ { 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 },
+ { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1 }
+ },
+ {
+ { 0, 2 }, { 1, 2 }, { 2, 2 }, { 3, 2 },
+ { 0, 3 }, { 1, 3 }, { 2, 3 }, { 3, 3 }
+ },
+ }
+ };
+
+ void transcode_uastc_to_etc1(unpacked_uastc_block& unpacked_src_blk, color32 block_pixels[4][4], void* pDst)
+ {
+ decoder_etc_block& dst_blk = *static_cast(pDst);
+
+ if (unpacked_src_blk.m_mode == UASTC_MODE_INDEX_SOLID_COLOR)
+ {
+ dst_blk.m_bytes[3] = (uint8_t)((unpacked_src_blk.m_etc1_diff << 1) | (unpacked_src_blk.m_etc1_inten0 << 5) | (unpacked_src_blk.m_etc1_inten0 << 2));
+
+ if (unpacked_src_blk.m_etc1_diff)
+ {
+ dst_blk.m_bytes[0] = (uint8_t)(unpacked_src_blk.m_etc1_r << 3);
+ dst_blk.m_bytes[1] = (uint8_t)(unpacked_src_blk.m_etc1_g << 3);
+ dst_blk.m_bytes[2] = (uint8_t)(unpacked_src_blk.m_etc1_b << 3);
+ }
+ else
+ {
+ dst_blk.m_bytes[0] = (uint8_t)(unpacked_src_blk.m_etc1_r | (unpacked_src_blk.m_etc1_r << 4));
+ dst_blk.m_bytes[1] = (uint8_t)(unpacked_src_blk.m_etc1_g | (unpacked_src_blk.m_etc1_g << 4));
+ dst_blk.m_bytes[2] = (uint8_t)(unpacked_src_blk.m_etc1_b | (unpacked_src_blk.m_etc1_b << 4));
+ }
+
+ memcpy(dst_blk.m_bytes + 4, &s_etc1_solid_selectors[unpacked_src_blk.m_etc1_selector][0], 4);
+
+ return;
+ }
+
+ const bool flip = unpacked_src_blk.m_etc1_flip != 0;
+ const bool diff = unpacked_src_blk.m_etc1_diff != 0;
+
+ dst_blk.m_bytes[3] = (uint8_t)((int)flip | (diff << 1) | (unpacked_src_blk.m_etc1_inten0 << 5) | (unpacked_src_blk.m_etc1_inten1 << 2));
+
+ const uint32_t limit = diff ? 31 : 15;
+
+ color32 block_colors[2];
+
+ for (uint32_t subset = 0; subset < 2; subset++)
+ {
+ uint32_t avg_color[3];
+ memset(avg_color, 0, sizeof(avg_color));
+
+ for (uint32_t j = 0; j < 8; j++)
+ {
+ const etc_coord2& c = g_etc1_pixel_coords[flip][subset][j];
+
+ avg_color[0] += block_pixels[c.m_y][c.m_x].r;
+ avg_color[1] += block_pixels[c.m_y][c.m_x].g;
+ avg_color[2] += block_pixels[c.m_y][c.m_x].b;
+ } // j
+
+ block_colors[subset][0] = (uint8_t)((avg_color[0] * limit + 1020) / (8 * 255));
+ block_colors[subset][1] = (uint8_t)((avg_color[1] * limit + 1020) / (8 * 255));
+ block_colors[subset][2] = (uint8_t)((avg_color[2] * limit + 1020) / (8 * 255));
+ block_colors[subset][3] = 0;
+
+ if (g_uastc_mode_has_etc1_bias[unpacked_src_blk.m_mode])
+ {
+ block_colors[subset] = apply_etc1_bias(block_colors[subset], unpacked_src_blk.m_etc1_bias, limit, subset);
+ }
+
+ } // subset
+
+ if (diff)
+ {
+ int dr = block_colors[1].r - block_colors[0].r;
+ int dg = block_colors[1].g - block_colors[0].g;
+ int db = block_colors[1].b - block_colors[0].b;
+
+ dr = basisu::clamp(dr, cETC1ColorDeltaMin, cETC1ColorDeltaMax);
+ dg = basisu::clamp(dg, cETC1ColorDeltaMin, cETC1ColorDeltaMax);
+ db = basisu::clamp(db, cETC1ColorDeltaMin, cETC1ColorDeltaMax);
+
+ if (dr < 0) dr += 8;
+ if (dg < 0) dg += 8;
+ if (db < 0) db += 8;
+
+ dst_blk.m_bytes[0] = (uint8_t)((block_colors[0].r << 3) | dr);
+ dst_blk.m_bytes[1] = (uint8_t)((block_colors[0].g << 3) | dg);
+ dst_blk.m_bytes[2] = (uint8_t)((block_colors[0].b << 3) | db);
+ }
+ else
+ {
+ dst_blk.m_bytes[0] = (uint8_t)(block_colors[1].r | (block_colors[0].r << 4));
+ dst_blk.m_bytes[1] = (uint8_t)(block_colors[1].g | (block_colors[0].g << 4));
+ dst_blk.m_bytes[2] = (uint8_t)(block_colors[1].b | (block_colors[0].b << 4));
+ }
+
+ etc1_determine_selectors(dst_blk, &block_pixels[0][0], 0, 2);
+ }
+
+ bool transcode_uastc_to_etc1(const uastc_block& src_blk, void* pDst)
+ {
+ unpacked_uastc_block unpacked_src_blk;
+ if (!unpack_uastc(src_blk, unpacked_src_blk, false))
+ return false;
+
+ color32 block_pixels[4][4];
+ if (unpacked_src_blk.m_mode != UASTC_MODE_INDEX_SOLID_COLOR)
+ {
+ const bool unpack_srgb = false;
+ if (!unpack_uastc(unpacked_src_blk, &block_pixels[0][0], unpack_srgb))
+ return false;
+ }
+
+ transcode_uastc_to_etc1(unpacked_src_blk, block_pixels, pDst);
+
+ return true;
+ }
+
+ static inline int gray_distance2(const uint8_t c, int y)
+ {
+ int gray_dist = (int)c - y;
+ return gray_dist * gray_dist;
+ }
+
+ static bool pack_etc1_y_estimate_flipped(const uint8_t* pSrc_pixels,
+ int& upper_avg, int& lower_avg, int& left_avg, int& right_avg)
+ {
+ int sums[2][2];
+
+#define GET_XY(x, y) pSrc_pixels[(x) + ((y) * 4)]
+
+ sums[0][0] = GET_XY(0, 0) + GET_XY(0, 1) + GET_XY(1, 0) + GET_XY(1, 1);
+ sums[1][0] = GET_XY(2, 0) + GET_XY(2, 1) + GET_XY(3, 0) + GET_XY(3, 1);
+ sums[0][1] = GET_XY(0, 2) + GET_XY(0, 3) + GET_XY(1, 2) + GET_XY(1, 3);
+ sums[1][1] = GET_XY(2, 2) + GET_XY(2, 3) + GET_XY(3, 2) + GET_XY(3, 3);
+
+ upper_avg = (sums[0][0] + sums[1][0] + 4) / 8;
+ lower_avg = (sums[0][1] + sums[1][1] + 4) / 8;
+ left_avg = (sums[0][0] + sums[0][1] + 4) / 8;
+ right_avg = (sums[1][0] + sums[1][1] + 4) / 8;
+
+#undef GET_XY
+#define GET_XY(x, y, a) gray_distance2(pSrc_pixels[(x) + ((y) * 4)], a)
+
+ int upper_gray_dist = 0, lower_gray_dist = 0, left_gray_dist = 0, right_gray_dist = 0;
+ for (uint32_t i = 0; i < 4; i++)
+ {
+ for (uint32_t j = 0; j < 2; j++)
+ {
+ upper_gray_dist += GET_XY(i, j, upper_avg);
+ lower_gray_dist += GET_XY(i, 2 + j, lower_avg);
+ left_gray_dist += GET_XY(j, i, left_avg);
+ right_gray_dist += GET_XY(2 + j, i, right_avg);
+ }
+ }
+
+#undef GET_XY
+
+ int upper_lower_sum = upper_gray_dist + lower_gray_dist;
+ int left_right_sum = left_gray_dist + right_gray_dist;
+
+ return upper_lower_sum < left_right_sum;
+ }
+
+ // Base Sel Table
+ // XXXXX XX XXX
+ static const uint16_t g_etc1_y_solid_block_configs[256] =
+ {
+ 0,781,64,161,260,192,33,131,96,320,65,162,261,193,34,291,97,224,66,163,262,194,35,549,98,4,67,653,164,195,523,36,99,5,578,68,165,353,196,37,135,100,324,69,166,354,197,38,295,101,228,70,167,
+ 355,198,39,553,102,8,71,608,168,199,527,40,103,9,582,72,169,357,200,41,139,104,328,73,170,358,201,42,299,105,232,74,171,359,202,43,557,106,12,75,612,172,203,531,44,107,13,586,76,173,361,
+ 204,45,143,108,332,77,174,362,205,46,303,109,236,78,175,363,206,47,561,110,16,79,616,176,207,535,48,111,17,590,80,177,365,208,49,147,112,336,81,178,366,209,50,307,113,240,82,179,367,210,
+ 51,565,114,20,83,620,180,211,539,52,115,21,594,84,181,369,212,53,151,116,340,85,182,370,213,54,311,117,244,86,183,371,214,55,569,118,24,87,624,184,215,543,56,119,25,598,88,185,373,216,57,
+ 155,120,344,89,186,374,217,58,315,121,248,90,187,375,218,59,573,122,28,91,628,188,219,754,60,123,29,602,92,189,377,220,61,159,124,348,93,190,378,221,62,319,125,252,94,191,379,222,63,882,126
+ };
+
+ // individual
+ // table base sel0 sel1 sel2 sel3
+ static const uint16_t g_etc1_y_solid_block_4i_configs[256] =
+ {
+ 0xA000,0xA800,0x540B,0xAA01,0xAA01,0xFE00,0xFF00,0xFF00,0x8,0x5515,0x5509,0x5509,0xAA03,0x5508,0x5508,0x9508,0xA508,0xA908,0xAA08,0x5513,0xAA09,0xAA09,0xAA05,0xFF08,0xFF08,0x10,0x551D,0x5511,0x5511,
+ 0xAA0B,0x5510,0x5510,0x9510,0xA510,0xA910,0xAA10,0x551B,0xAA11,0xAA11,0xAA0D,0xFF10,0xFF10,0x18,0x5525,0x5519,0x5519,0xAA13,0x5518,0x5518,0x9518,0xA518,0xA918,0xAA18,0x5523,0xAA19,0xAA19,0xAA15,
+ 0xFF18,0xFF18,0x20,0x552D,0x5521,0x5521,0xAA1B,0x5520,0x5520,0x9520,0xA520,0xA920,0xAA20,0x552B,0xAA21,0xAA21,0xAA1D,0xFF20,0xFF20,0x28,0x5535,0x5529,0x5529,0xAA23,0x5528,0x5528,0x9528,0xA528,0xA928,
+ 0xAA28,0x5533,0xAA29,0xAA29,0xAA25,0xFF28,0xFF28,0x30,0x553D,0x5531,0x5531,0xAA2B,0x5530,0x5530,0x9530,0xA530,0xA930,0xAA30,0x553B,0xAA31,0xAA31,0xAA2D,0xFF30,0xFF30,0x38,0x5545,0x5539,0x5539,0xAA33,
+ 0x5538,0x5538,0x9538,0xA538,0xA938,0xAA38,0x5543,0xAA39,0xAA39,0xAA35,0xFF38,0xFF38,0x40,0x554D,0x5541,0x5541,0xAA3B,0x5540,0x5540,0x9540,0xA540,0xA940,0xAA40,0x554B,0xAA41,0xAA41,0xAA3D,0xFF40,0xFF40,
+ 0x48,0x5555,0x5549,0x5549,0xAA43,0x5548,0x5548,0x9548,0xA548,0xA948,0xAA48,0x5553,0xAA49,0xAA49,0xAA45,0xFF48,0xFF48,0x50,0x555D,0x5551,0x5551,0xAA4B,0x5550,0x5550,0x9550,0xA550,0xA950,0xAA50,0x555B,
+ 0xAA51,0xAA51,0xAA4D,0xFF50,0xFF50,0x58,0x5565,0x5559,0x5559,0xAA53,0x5558,0x5558,0x9558,0xA558,0xA958,0xAA58,0x5563,0xAA59,0xAA59,0xAA55,0xFF58,0xFF58,0x60,0x556D,0x5561,0x5561,0xAA5B,0x5560,0x5560,
+ 0x9560,0xA560,0xA960,0xAA60,0x556B,0xAA61,0xAA61,0xAA5D,0xFF60,0xFF60,0x68,0x5575,0x5569,0x5569,0xAA63,0x5568,0x5568,0x9568,0xA568,0xA968,0xAA68,0x5573,0xAA69,0xAA69,0xAA65,0xFF68,0xFF68,0x70,0x557D,
+ 0x5571,0x5571,0xAA6B,0x5570,0x5570,0x9570,0xA570,0xA970,0xAA70,0x557B,0xAA71,0xAA71,0xAA6D,0xFF70,0xFF70,0x78,0x78,0x5579,0x5579,0xAA73,0x5578,0x9578,0x2578,0xE6E,0x278
+ };
+
+ static const uint16_t g_etc1_y_solid_block_2i_configs[256] =
+ {
+ 0x416,0x800,0xA00,0x50B,0xA01,0xA01,0xF00,0xF00,0xF00,0x8,0x515,0x509,0x509,0xA03,0x508,0x508,0xF01,0xF01,0xA08,0xA08,0x513,0xA09,0xA09,0xA05,0xF08,0xF08,0x10,0x51D,0x511,0x511,0xA0B,0x510,0x510,0xF09,
+ 0xF09,0xA10,0xA10,0x51B,0xA11,0xA11,0xA0D,0xF10,0xF10,0x18,0x525,0x519,0x519,0xA13,0x518,0x518,0xF11,0xF11,0xA18,0xA18,0x523,0xA19,0xA19,0xA15,0xF18,0xF18,0x20,0x52D,0x521,0x521,0xA1B,0x520,0x520,0xF19,
+ 0xF19,0xA20,0xA20,0x52B,0xA21,0xA21,0xA1D,0xF20,0xF20,0x28,0x535,0x529,0x529,0xA23,0x528,0x528,0xF21,0xF21,0xA28,0xA28,0x533,0xA29,0xA29,0xA25,0xF28,0xF28,0x30,0x53D,0x531,0x531,0xA2B,0x530,0x530,0xF29,
+ 0xF29,0xA30,0xA30,0x53B,0xA31,0xA31,0xA2D,0xF30,0xF30,0x38,0x545,0x539,0x539,0xA33,0x538,0x538,0xF31,0xF31,0xA38,0xA38,0x543,0xA39,0xA39,0xA35,0xF38,0xF38,0x40,0x54D,0x541,0x541,0xA3B,0x540,0x540,0xF39,
+ 0xF39,0xA40,0xA40,0x54B,0xA41,0xA41,0xA3D,0xF40,0xF40,0x48,0x555,0x549,0x549,0xA43,0x548,0x548,0xF41,0xF41,0xA48,0xA48,0x553,0xA49,0xA49,0xA45,0xF48,0xF48,0x50,0x55D,0x551,0x551,0xA4B,0x550,0x550,0xF49,
+ 0xF49,0xA50,0xA50,0x55B,0xA51,0xA51,0xA4D,0xF50,0xF50,0x58,0x565,0x559,0x559,0xA53,0x558,0x558,0xF51,0xF51,0xA58,0xA58,0x563,0xA59,0xA59,0xA55,0xF58,0xF58,0x60,0x56D,0x561,0x561,0xA5B,0x560,0x560,0xF59,
+ 0xF59,0xA60,0xA60,0x56B,0xA61,0xA61,0xA5D,0xF60,0xF60,0x68,0x575,0x569,0x569,0xA63,0x568,0x568,0xF61,0xF61,0xA68,0xA68,0x573,0xA69,0xA69,0xA65,0xF68,0xF68,0x70,0x57D,0x571,0x571,0xA6B,0x570,0x570,0xF69,
+ 0xF69,0xA70,0xA70,0x57B,0xA71,0xA71,0xA6D,0xF70,0xF70,0x78,0x78,0x579,0x579,0xA73,0x578,0x578,0xE6E,0x278
+ };
+
+ static const uint16_t g_etc1_y_solid_block_1i_configs[256] =
+ {
+ 0x0,0x116,0x200,0x200,0x10B,0x201,0x201,0x300,0x300,0x8,0x115,0x109,0x109,0x203,0x108,0x108,0x114,0x301,0x204,0x208,0x208,0x113,0x209,0x209,0x205,0x308,0x10,0x11D,0x111,0x111,0x20B,0x110,0x110,0x11C,0x309,
+ 0x20C,0x210,0x210,0x11B,0x211,0x211,0x20D,0x310,0x18,0x125,0x119,0x119,0x213,0x118,0x118,0x124,0x311,0x214,0x218,0x218,0x123,0x219,0x219,0x215,0x318,0x20,0x12D,0x121,0x121,0x21B,0x120,0x120,0x12C,0x319,0x21C,
+ 0x220,0x220,0x12B,0x221,0x221,0x21D,0x320,0x28,0x135,0x129,0x129,0x223,0x128,0x128,0x134,0x321,0x224,0x228,0x228,0x133,0x229,0x229,0x225,0x328,0x30,0x13D,0x131,0x131,0x22B,0x130,0x130,0x13C,0x329,0x22C,0x230,
+ 0x230,0x13B,0x231,0x231,0x22D,0x330,0x38,0x145,0x139,0x139,0x233,0x138,0x138,0x144,0x331,0x234,0x238,0x238,0x143,0x239,0x239,0x235,0x338,0x40,0x14D,0x141,0x141,0x23B,0x140,0x140,0x14C,0x339,0x23C,0x240,0x240,
+ 0x14B,0x241,0x241,0x23D,0x340,0x48,0x155,0x149,0x149,0x243,0x148,0x148,0x154,0x341,0x244,0x248,0x248,0x153,0x249,0x249,0x245,0x348,0x50,0x15D,0x151,0x151,0x24B,0x150,0x150,0x15C,0x349,0x24C,0x250,0x250,0x15B,
+ 0x251,0x251,0x24D,0x350,0x58,0x165,0x159,0x159,0x253,0x158,0x158,0x164,0x351,0x254,0x258,0x258,0x163,0x259,0x259,0x255,0x358,0x60,0x16D,0x161,0x161,0x25B,0x160,0x160,0x16C,0x359,0x25C,0x260,0x260,0x16B,0x261,
+ 0x261,0x25D,0x360,0x68,0x175,0x169,0x169,0x263,0x168,0x168,0x174,0x361,0x264,0x268,0x268,0x173,0x269,0x269,0x265,0x368,0x70,0x17D,0x171,0x171,0x26B,0x170,0x170,0x17C,0x369,0x26C,0x270,0x270,0x17B,0x271,0x271,
+ 0x26D,0x370,0x78,0x78,0x179,0x179,0x273,0x178,0x178,0x26E,0x278
+ };
+
+ // We don't have any useful hints to accelerate single channel ETC1, so we need to real-time encode from scratch.
+ bool transcode_uastc_to_etc1(const uastc_block& src_blk, void* pDst, uint32_t channel)
+ {
+ unpacked_uastc_block unpacked_src_blk;
+ if (!unpack_uastc(src_blk, unpacked_src_blk, false))
+ return false;
+
+#if 0
+ for (uint32_t individ = 0; individ < 2; individ++)
+ {
+ uint32_t overall_error = 0;
+
+ for (uint32_t c = 0; c < 256; c++)
+ {
+ uint32_t best_err = UINT32_MAX;
+ uint32_t best_individ = 0;
+ uint32_t best_base = 0;
+ uint32_t best_sels[4] = { 0,0,0,0 };
+ uint32_t best_table = 0;
+
+ const uint32_t limit = individ ? 16 : 32;
+
+ for (uint32_t table = 0; table < 8; table++)
+ {
+ for (uint32_t base = 0; base < limit; base++)
+ {
+ uint32_t total_e = 0;
+ uint32_t sels[4] = { 0,0,0,0 };
+
+ const uint32_t N = 4;
+ for (uint32_t i = 0; i < std::min(N, (256 - c)); i++)
+ {
+ uint32_t best_sel_e = UINT32_MAX;
+ uint32_t best_sel = 0;
+
+ for (uint32_t sel = 0; sel < 4; sel++)
+ {
+ int val = individ ? ((base << 4) | base) : ((base << 3) | (base >> 2));
+ val = clamp255(val + g_etc1_inten_tables[table][sel]);
+
+ int e = iabs(val - clamp255(c + i));
+ if (e < best_sel_e)
+ {
+ best_sel_e = e;
+ best_sel = sel;
+ }
+
+ } // sel
+
+ sels[i] = best_sel;
+ total_e += best_sel_e * best_sel_e;
+
+ } // i
+
+ if (total_e < best_err)
+ {
+ best_err = total_e;
+ best_individ = individ;
+ best_base = base;
+ memcpy(best_sels, sels, sizeof(best_sels));
+ best_table = table;
+ }
+
+ } // base
+ } // table
+
+ //printf("%u: %u,%u,%u,%u,%u,%u,%u,%u\n", c, best_err, best_individ, best_table, best_base, best_sels[0], best_sels[1], best_sels[2], best_sels[3]);
+
+ uint32_t encoded = best_table | (best_base << 3) |
+ (best_sels[0] << 8) |
+ (best_sels[1] << 10) |
+ (best_sels[2] << 12) |
+ (best_sels[3] << 14);
+
+ printf("0x%X,", encoded);
+
+ overall_error += best_err;
+ } // c
+
+ printf("\n");
+ printf("Overall error: %u\n", overall_error);
+
+ } // individ
+
+ exit(0);
+#endif
+
+#if 0
+ for (uint32_t individ = 0; individ < 2; individ++)
+ {
+ uint32_t overall_error = 0;
+
+ for (uint32_t c = 0; c < 256; c++)
+ {
+ uint32_t best_err = UINT32_MAX;
+ uint32_t best_individ = 0;
+ uint32_t best_base = 0;
+ uint32_t best_sels[4] = { 0,0,0,0 };
+ uint32_t best_table = 0;
+
+ const uint32_t limit = individ ? 16 : 32;
+
+ for (uint32_t table = 0; table < 8; table++)
+ {
+ for (uint32_t base = 0; base < limit; base++)
+ {
+ uint32_t total_e = 0;
+ uint32_t sels[4] = { 0,0,0,0 };
+
+ const uint32_t N = 1;
+ for (uint32_t i = 0; i < std::min(N, (256 - c)); i++)
+ {
+ uint32_t best_sel_e = UINT32_MAX;
+ uint32_t best_sel = 0;
+
+ for (uint32_t sel = 0; sel < 4; sel++)
+ {
+ int val = individ ? ((base << 4) | base) : ((base << 3) | (base >> 2));
+ val = clamp255(val + g_etc1_inten_tables[table][sel]);
+
+ int e = iabs(val - clamp255(c + i));
+ if (e < best_sel_e)
+ {
+ best_sel_e = e;
+ best_sel = sel;
+ }
+
+ } // sel
+
+ sels[i] = best_sel;
+ total_e += best_sel_e * best_sel_e;
+
+ } // i
+
+ if (total_e < best_err)
+ {
+ best_err = total_e;
+ best_individ = individ;
+ best_base = base;
+ memcpy(best_sels, sels, sizeof(best_sels));
+ best_table = table;
+ }
+
+ } // base
+ } // table
+
+ //printf("%u: %u,%u,%u,%u,%u,%u,%u,%u\n", c, best_err, best_individ, best_table, best_base, best_sels[0], best_sels[1], best_sels[2], best_sels[3]);
+
+ uint32_t encoded = best_table | (best_base << 3) |
+ (best_sels[0] << 8) |
+ (best_sels[1] << 10) |
+ (best_sels[2] << 12) |
+ (best_sels[3] << 14);
+
+ printf("0x%X,", encoded);
+
+ overall_error += best_err;
+ } // c
+
+ printf("\n");
+ printf("Overall error: %u\n", overall_error);
+
+ } // individ
+
+ exit(0);
+#endif
+
+ decoder_etc_block& dst_blk = *static_cast(pDst);
+
+ if (unpacked_src_blk.m_mode == UASTC_MODE_INDEX_SOLID_COLOR)
+ {
+ const uint32_t y = unpacked_src_blk.m_solid_color[channel];
+ const uint32_t encoded_config = g_etc1_y_solid_block_configs[y];
+
+ const uint32_t base = encoded_config & 31;
+ const uint32_t sel = (encoded_config >> 5) & 3;
+ const uint32_t table = encoded_config >> 7;
+
+ dst_blk.m_bytes[3] = (uint8_t)(2 | (table << 5) | (table << 2));
+
+ dst_blk.m_bytes[0] = (uint8_t)(base << 3);
+ dst_blk.m_bytes[1] = (uint8_t)(base << 3);
+ dst_blk.m_bytes[2] = (uint8_t)(base << 3);
+
+ memcpy(dst_blk.m_bytes + 4, &s_etc1_solid_selectors[sel][0], 4);
+ return true;
+ }
+
+ color32 block_pixels[4][4];
+ const bool unpack_srgb = false;
+ if (!unpack_uastc(unpacked_src_blk, &block_pixels[0][0], unpack_srgb))
+ return false;
+
+ uint8_t block_y[4][4];
+ for (uint32_t i = 0; i < 16; i++)
+ ((uint8_t*)block_y)[i] = ((color32*)block_pixels)[i][channel];
+
+ int upper_avg, lower_avg, left_avg, right_avg;
+ bool flip = pack_etc1_y_estimate_flipped(&block_y[0][0], upper_avg, lower_avg, left_avg, right_avg);
+
+ // non-flipped: | |
+ // vs.
+ // flipped: --
+ // --
+
+ uint32_t low[2] = { 255, 255 }, high[2] = { 0, 0 };
+
+ if (flip)
+ {
+ for (uint32_t y = 0; y < 2; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ const uint32_t v = block_y[y][x];
+ low[0] = std::min(low[0], v);
+ high[0] = std::max(high[0], v);
+ }
+ }
+ for (uint32_t y = 2; y < 4; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ const uint32_t v = block_y[y][x];
+ low[1] = std::min(low[1], v);
+ high[1] = std::max(high[1], v);
+ }
+ }
+ }
+ else
+ {
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ for (uint32_t x = 0; x < 2; x++)
+ {
+ const uint32_t v = block_y[y][x];
+ low[0] = std::min(low[0], v);
+ high[0] = std::max(high[0], v);
+ }
+ }
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ for (uint32_t x = 2; x < 4; x++)
+ {
+ const uint32_t v = block_y[y][x];
+ low[1] = std::min(low[1], v);
+ high[1] = std::max(high[1], v);
+ }
+ }
+ }
+
+ const uint32_t range[2] = { high[0] - low[0], high[1] - low[1] };
+
+ dst_blk.m_bytes[3] = (uint8_t)((int)flip);
+
+ if ((range[0] <= 3) && (range[1] <= 3))
+ {
+ // This is primarily for better gradients.
+ dst_blk.m_bytes[0] = 0;
+ dst_blk.m_bytes[1] = 0;
+ dst_blk.m_bytes[2] = 0;
+
+ uint16_t l_bitmask = 0, h_bitmask = 0;
+
+ for (uint32_t subblock = 0; subblock < 2; subblock++)
+ {
+ const uint32_t encoded = (range[subblock] == 0) ? g_etc1_y_solid_block_1i_configs[low[subblock]] : ((range[subblock] < 2) ? g_etc1_y_solid_block_2i_configs[low[subblock]] : g_etc1_y_solid_block_4i_configs[low[subblock]]);
+
+ const uint32_t table = encoded & 7;
+ const uint32_t base = (encoded >> 3) & 31;
+ assert(base <= 15);
+ const uint32_t sels[4] = { (encoded >> 8) & 3, (encoded >> 10) & 3, (encoded >> 12) & 3, (encoded >> 14) & 3 };
+
+ dst_blk.m_bytes[3] |= (uint8_t)(table << (subblock ? 2 : 5));
+
+ const uint32_t sv = base << (subblock ? 0 : 4);
+ dst_blk.m_bytes[0] |= (uint8_t)(sv);
+ dst_blk.m_bytes[1] |= (uint8_t)(sv);
+ dst_blk.m_bytes[2] |= (uint8_t)(sv);
+
+ if (flip)
+ {
+ uint32_t ofs = subblock * 2;
+ for (uint32_t y = 0; y < 2; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ uint32_t t = block_y[y + subblock * 2][x];
+ assert(t >= low[subblock] && t <= high[subblock]);
+ t -= low[subblock];
+ assert(t <= 3);
+
+ t = g_selector_index_to_etc1[sels[t]];
+
+ assert(ofs < 16);
+ l_bitmask |= ((t & 1) << ofs);
+ h_bitmask |= ((t >> 1) << ofs);
+ ofs += 4;
+ }
+
+ ofs = (int)ofs + 1 - 4 * 4;
+ }
+ }
+ else
+ {
+ uint32_t ofs = (subblock * 2) * 4;
+ for (uint32_t x = 0; x < 2; x++)
+ {
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ uint32_t t = block_y[y][x + subblock * 2];
+ assert(t >= low[subblock] && t <= high[subblock]);
+ t -= low[subblock];
+ assert(t <= 3);
+
+ t = g_selector_index_to_etc1[sels[t]];
+
+ assert(ofs < 16);
+ l_bitmask |= ((t & 1) << ofs);
+ h_bitmask |= ((t >> 1) << ofs);
+ ++ofs;
+ }
+ }
+ }
+ } // subblock
+
+ dst_blk.m_bytes[7] = (uint8_t)(l_bitmask);
+ dst_blk.m_bytes[6] = (uint8_t)(l_bitmask >> 8);
+ dst_blk.m_bytes[5] = (uint8_t)(h_bitmask);
+ dst_blk.m_bytes[4] = (uint8_t)(h_bitmask >> 8);
+
+ return true;
+ }
+
+ uint32_t y0 = ((flip ? upper_avg : left_avg) * 31 + 127) / 255;
+ uint32_t y1 = ((flip ? lower_avg : right_avg) * 31 + 127) / 255;
+
+ bool diff = true;
+
+ int dy = y1 - y0;
+
+ if ((dy < cETC1ColorDeltaMin) || (dy > cETC1ColorDeltaMax))
+ {
+ diff = false;
+
+ y0 = ((flip ? upper_avg : left_avg) * 15 + 127) / 255;
+ y1 = ((flip ? lower_avg : right_avg) * 15 + 127) / 255;
+
+ dst_blk.m_bytes[0] = (uint8_t)(y1 | (y0 << 4));
+ dst_blk.m_bytes[1] = (uint8_t)(y1 | (y0 << 4));
+ dst_blk.m_bytes[2] = (uint8_t)(y1 | (y0 << 4));
+ }
+ else
+ {
+ dy = basisu::clamp(dy, cETC1ColorDeltaMin, cETC1ColorDeltaMax);
+
+ y1 = y0 + dy;
+
+ if (dy < 0) dy += 8;
+
+ dst_blk.m_bytes[0] = (uint8_t)((y0 << 3) | dy);
+ dst_blk.m_bytes[1] = (uint8_t)((y0 << 3) | dy);
+ dst_blk.m_bytes[2] = (uint8_t)((y0 << 3) | dy);
+
+ dst_blk.m_bytes[3] |= 2;
+ }
+
+ const uint32_t base_y[2] = { diff ? ((y0 << 3) | (y0 >> 2)) : ((y0 << 4) | y0), diff ? ((y1 << 3) | (y1 >> 2)) : ((y1 << 4) | y1) };
+
+ uint32_t enc_range[2];
+ for (uint32_t subset = 0; subset < 2; subset++)
+ {
+ const int pos = basisu::iabs((int)high[subset] - (int)base_y[subset]);
+ const int neg = basisu::iabs((int)base_y[subset] - (int)low[subset]);
+
+ enc_range[subset] = basisu::maximum(pos, neg);
+ }
+
+ uint16_t l_bitmask = 0, h_bitmask = 0;
+ for (uint32_t subblock = 0; subblock < 2; subblock++)
+ {
+ if ((!diff) && (range[subblock] <= 3))
+ {
+ const uint32_t encoded = (range[subblock] == 0) ? g_etc1_y_solid_block_1i_configs[low[subblock]] : ((range[subblock] < 2) ? g_etc1_y_solid_block_2i_configs[low[subblock]] : g_etc1_y_solid_block_4i_configs[low[subblock]]);
+
+ const uint32_t table = encoded & 7;
+ const uint32_t base = (encoded >> 3) & 31;
+ assert(base <= 15);
+ const uint32_t sels[4] = { (encoded >> 8) & 3, (encoded >> 10) & 3, (encoded >> 12) & 3, (encoded >> 14) & 3 };
+
+ dst_blk.m_bytes[3] |= (uint8_t)(table << (subblock ? 2 : 5));
+
+ const uint32_t mask = ~(0xF << (subblock ? 0 : 4));
+
+ dst_blk.m_bytes[0] &= mask;
+ dst_blk.m_bytes[1] &= mask;
+ dst_blk.m_bytes[2] &= mask;
+
+ const uint32_t sv = base << (subblock ? 0 : 4);
+ dst_blk.m_bytes[0] |= (uint8_t)(sv);
+ dst_blk.m_bytes[1] |= (uint8_t)(sv);
+ dst_blk.m_bytes[2] |= (uint8_t)(sv);
+
+ if (flip)
+ {
+ uint32_t ofs = subblock * 2;
+ for (uint32_t y = 0; y < 2; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ uint32_t t = block_y[y + subblock * 2][x];
+ assert(t >= low[subblock] && t <= high[subblock]);
+ t -= low[subblock];
+ assert(t <= 3);
+
+ t = g_selector_index_to_etc1[sels[t]];
+
+ assert(ofs < 16);
+ l_bitmask |= ((t & 1) << ofs);
+ h_bitmask |= ((t >> 1) << ofs);
+ ofs += 4;
+ }
+
+ ofs = (int)ofs + 1 - 4 * 4;
+ }
+ }
+ else
+ {
+ uint32_t ofs = (subblock * 2) * 4;
+ for (uint32_t x = 0; x < 2; x++)
+ {
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ uint32_t t = block_y[y][x + subblock * 2];
+ assert(t >= low[subblock] && t <= high[subblock]);
+ t -= low[subblock];
+ assert(t <= 3);
+
+ t = g_selector_index_to_etc1[sels[t]];
+
+ assert(ofs < 16);
+ l_bitmask |= ((t & 1) << ofs);
+ h_bitmask |= ((t >> 1) << ofs);
+ ++ofs;
+ }
+ }
+ }
+
+ continue;
+ } // if
+
+ uint32_t best_err = UINT32_MAX;
+ uint8_t best_sels[8];
+ uint32_t best_inten = 0;
+
+ const int base = base_y[subblock];
+
+ const int low_limit = -base;
+ const int high_limit = 255 - base;
+
+ assert(low_limit <= 0 && high_limit >= 0);
+
+ uint32_t inten_table_mask = 0xFF;
+ const uint32_t er = enc_range[subblock];
+ // Each one of these tables is expensive to evaluate, so let's only examine the ones we know may be useful.
+ if (er <= 51)
+ {
+ inten_table_mask = 0xF;
+
+ if (er > 22)
+ inten_table_mask &= ~(1 << 0);
+
+ if ((er < 4) || (er > 39))
+ inten_table_mask &= ~(1 << 1);
+
+ if (er < 9)
+ inten_table_mask &= ~(1 << 2);
+
+ if (er < 12)
+ inten_table_mask &= ~(1 << 3);
+ }
+ else
+ {
+ inten_table_mask &= ~((1 << 0) | (1 << 1));
+
+ if (er > 60)
+ inten_table_mask &= ~(1 << 2);
+
+ if (er > 89)
+ inten_table_mask &= ~(1 << 3);
+
+ if (er > 120)
+ inten_table_mask &= ~(1 << 4);
+
+ if (er > 136)
+ inten_table_mask &= ~(1 << 5);
+
+ if (er > 174)
+ inten_table_mask &= ~(1 << 6);
+ }
+
+ for (uint32_t inten = 0; inten < 8; inten++)
+ {
+ if ((inten_table_mask & (1 << inten)) == 0)
+ continue;
+
+ const int t0 = std::max(low_limit, g_etc1_inten_tables[inten][0]);
+ const int t1 = std::max(low_limit, g_etc1_inten_tables[inten][1]);
+ const int t2 = std::min(high_limit, g_etc1_inten_tables[inten][2]);
+ const int t3 = std::min(high_limit, g_etc1_inten_tables[inten][3]);
+ assert((t0 <= t1) && (t1 <= t2) && (t2 <= t3));
+
+ const int tv[4] = { t2, t3, t1, t0 };
+
+ const int thresh01 = t0 + t1;
+ const int thresh12 = t1 + t2;
+ const int thresh23 = t2 + t3;
+
+ assert(thresh01 <= thresh12 && thresh12 <= thresh23);
+
+ static const uint8_t s_table[4] = { 1, 0, 2, 3 };
+
+ uint32_t total_err = 0;
+ uint8_t sels[8];
+
+ if (flip)
+ {
+ if (((int)high[subblock] - base) * 2 < thresh01)
+ {
+ memset(sels, 3, 8);
+
+ for (uint32_t y = 0; y < 2; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ const int delta = (int)block_y[y + subblock * 2][x] - base;
+
+ const uint32_t c = 3;
+
+ uint32_t e = basisu::iabs(tv[c] - delta);
+ total_err += e * e;
+ }
+ if (total_err >= best_err)
+ break;
+ }
+ }
+ else if (((int)low[subblock] - base) * 2 >= thresh23)
+ {
+ memset(sels, 1, 8);
+
+ for (uint32_t y = 0; y < 2; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ const int delta = (int)block_y[y + subblock * 2][x] - base;
+
+ const uint32_t c = 1;
+
+ uint32_t e = basisu::iabs(tv[c] - delta);
+ total_err += e * e;
+ }
+ if (total_err >= best_err)
+ break;
+ }
+ }
+ else
+ {
+ for (uint32_t y = 0; y < 2; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ const int delta = (int)block_y[y + subblock * 2][x] - base;
+ const int delta2 = delta * 2;
+
+ uint32_t c = s_table[(delta2 < thresh01) + (delta2 < thresh12) + (delta2 < thresh23)];
+ sels[y * 4 + x] = (uint8_t)c;
+
+ uint32_t e = basisu::iabs(tv[c] - delta);
+ total_err += e * e;
+ }
+ if (total_err >= best_err)
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (((int)high[subblock] - base) * 2 < thresh01)
+ {
+ memset(sels, 3, 8);
+
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ for (uint32_t x = 0; x < 2; x++)
+ {
+ const int delta = (int)block_y[y][x + subblock * 2] - base;
+
+ const uint32_t c = 3;
+
+ uint32_t e = basisu::iabs(tv[c] - delta);
+ total_err += e * e;
+ }
+ if (total_err >= best_err)
+ break;
+ }
+ }
+ else if (((int)low[subblock] - base) * 2 >= thresh23)
+ {
+ memset(sels, 1, 8);
+
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ for (uint32_t x = 0; x < 2; x++)
+ {
+ const int delta = (int)block_y[y][x + subblock * 2] - base;
+
+ const uint32_t c = 1;
+
+ uint32_t e = basisu::iabs(tv[c] - delta);
+ total_err += e * e;
+ }
+ if (total_err >= best_err)
+ break;
+ }
+ }
+ else
+ {
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ for (uint32_t x = 0; x < 2; x++)
+ {
+ const int delta = (int)block_y[y][x + subblock * 2] - base;
+ const int delta2 = delta * 2;
+
+ uint32_t c = s_table[(delta2 < thresh01) + (delta2 < thresh12) + (delta2 < thresh23)];
+ sels[y * 2 + x] = (uint8_t)c;
+
+ uint32_t e = basisu::iabs(tv[c] - delta);
+ total_err += e * e;
+ }
+ if (total_err >= best_err)
+ break;
+ }
+ }
+ }
+
+ if (total_err < best_err)
+ {
+ best_err = total_err;
+ best_inten = inten;
+ memcpy(best_sels, sels, 8);
+ }
+
+ } // inten
+
+ //g_inten_hist[best_inten][enc_range[subblock]]++;
+
+ dst_blk.m_bytes[3] |= (uint8_t)(best_inten << (subblock ? 2 : 5));
+
+ if (flip)
+ {
+ uint32_t ofs = subblock * 2;
+ for (uint32_t y = 0; y < 2; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ uint32_t t = best_sels[y * 4 + x];
+
+ assert(ofs < 16);
+ l_bitmask |= ((t & 1) << ofs);
+ h_bitmask |= ((t >> 1) << ofs);
+ ofs += 4;
+ }
+
+ ofs = (int)ofs + 1 - 4 * 4;
+ }
+ }
+ else
+ {
+ uint32_t ofs = (subblock * 2) * 4;
+ for (uint32_t x = 0; x < 2; x++)
+ {
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ uint32_t t = best_sels[y * 2 + x];
+
+ assert(ofs < 16);
+ l_bitmask |= ((t & 1) << ofs);
+ h_bitmask |= ((t >> 1) << ofs);
+ ++ofs;
+ }
+ }
+ }
+
+ } // subblock
+
+ dst_blk.m_bytes[7] = (uint8_t)(l_bitmask);
+ dst_blk.m_bytes[6] = (uint8_t)(l_bitmask >> 8);
+ dst_blk.m_bytes[5] = (uint8_t)(h_bitmask);
+ dst_blk.m_bytes[4] = (uint8_t)(h_bitmask >> 8);
+
+ return true;
+ }
+
+ const uint32_t ETC2_EAC_MIN_VALUE_SELECTOR = 3, ETC2_EAC_MAX_VALUE_SELECTOR = 7;
+
+ void transcode_uastc_to_etc2_eac_a8(unpacked_uastc_block& unpacked_src_blk, color32 block_pixels[4][4], void* pDst)
+ {
+ eac_block& dst = *static_cast(pDst);
+ const color32* pSrc_pixels = &block_pixels[0][0];
+
+ if ((!g_uastc_mode_has_alpha[unpacked_src_blk.m_mode]) || (unpacked_src_blk.m_mode == UASTC_MODE_INDEX_SOLID_COLOR))
+ {
+ const uint32_t a = (unpacked_src_blk.m_mode == UASTC_MODE_INDEX_SOLID_COLOR) ? unpacked_src_blk.m_solid_color[3] : 255;
+
+ dst.m_base = a;
+ dst.m_table = 13;
+ dst.m_multiplier = 1;
+
+ memcpy(dst.m_selectors, g_etc2_eac_a8_sel4, sizeof(g_etc2_eac_a8_sel4));
+
+ return;
+ }
+
+ uint32_t min_a = 255, max_a = 0;
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ min_a = std::min(min_a, pSrc_pixels[i].a);
+ max_a = std::max(max_a, pSrc_pixels[i].a);
+ }
+
+ if (min_a == max_a)
+ {
+ dst.m_base = min_a;
+ dst.m_table = 13;
+ dst.m_multiplier = 1;
+
+ memcpy(dst.m_selectors, g_etc2_eac_a8_sel4, sizeof(g_etc2_eac_a8_sel4));
+ return;
+ }
+
+ const uint32_t table = unpacked_src_blk.m_etc2_hints & 0xF;
+ const int multiplier = unpacked_src_blk.m_etc2_hints >> 4;
+
+ assert(multiplier >= 1);
+
+ dst.m_multiplier = multiplier;
+ dst.m_table = table;
+
+ const float range = (float)(g_eac_modifier_table[dst.m_table][ETC2_EAC_MAX_VALUE_SELECTOR] - g_eac_modifier_table[dst.m_table][ETC2_EAC_MIN_VALUE_SELECTOR]);
+ const int center = (int)roundf(basisu::lerp((float)min_a, (float)max_a, (float)(0 - g_eac_modifier_table[dst.m_table][ETC2_EAC_MIN_VALUE_SELECTOR]) / range));
+
+ dst.m_base = center;
+
+ const int8_t* pTable = &g_eac_modifier_table[dst.m_table][0];
+
+ uint32_t vals[8];
+ for (uint32_t j = 0; j < 8; j++)
+ vals[j] = clamp255(center + (pTable[j] * multiplier));
+
+ uint64_t sels = 0;
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ const uint32_t a = block_pixels[i & 3][i >> 2].a;
+
+ const uint32_t err0 = (basisu::iabs(vals[0] - a) << 3) | 0;
+ const uint32_t err1 = (basisu::iabs(vals[1] - a) << 3) | 1;
+ const uint32_t err2 = (basisu::iabs(vals[2] - a) << 3) | 2;
+ const uint32_t err3 = (basisu::iabs(vals[3] - a) << 3) | 3;
+ const uint32_t err4 = (basisu::iabs(vals[4] - a) << 3) | 4;
+ const uint32_t err5 = (basisu::iabs(vals[5] - a) << 3) | 5;
+ const uint32_t err6 = (basisu::iabs(vals[6] - a) << 3) | 6;
+ const uint32_t err7 = (basisu::iabs(vals[7] - a) << 3) | 7;
+
+ const uint32_t min_err = basisu::minimum(basisu::minimum(basisu::minimum(basisu::minimum(basisu::minimum(basisu::minimum(err0, err1, err2), err3), err4), err5), err6), err7);
+
+ const uint64_t best_index = min_err & 7;
+ sels |= (best_index << (45 - i * 3));
+ }
+
+ dst.set_selector_bits(sels);
+ }
+
+ bool transcode_uastc_to_etc2_rgba(const uastc_block& src_blk, void* pDst)
+ {
+ eac_block& dst_etc2_eac_a8_blk = *static_cast(pDst);
+ decoder_etc_block& dst_etc1_blk = static_cast(pDst)[1];
+
+ unpacked_uastc_block unpacked_src_blk;
+ if (!unpack_uastc(src_blk, unpacked_src_blk, false))
+ return false;
+
+ color32 block_pixels[4][4];
+ if (unpacked_src_blk.m_mode != UASTC_MODE_INDEX_SOLID_COLOR)
+ {
+ const bool unpack_srgb = false;
+ if (!unpack_uastc(unpacked_src_blk, &block_pixels[0][0], unpack_srgb))
+ return false;
+ }
+
+ transcode_uastc_to_etc2_eac_a8(unpacked_src_blk, block_pixels, &dst_etc2_eac_a8_blk);
+
+ transcode_uastc_to_etc1(unpacked_src_blk, block_pixels, &dst_etc1_blk);
+
+ return true;
+ }
+
+ static const uint8_t s_uastc5_to_bc1[32] = { 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1 };
+ static const uint8_t s_uastc4_to_bc1[16] = { 0, 0, 0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 1, 1, 1 };
+ static const uint8_t s_uastc3_to_bc1[8] = { 0, 0, 2, 2, 3, 3, 1, 1 };
+ static const uint8_t s_uastc2_to_bc1[4] = { 0, 2, 3, 1 };
+ static const uint8_t s_uastc1_to_bc1[2] = { 0, 1 };
+ const uint8_t* s_uastc_to_bc1_weights[6] = { nullptr, s_uastc1_to_bc1, s_uastc2_to_bc1, s_uastc3_to_bc1, s_uastc4_to_bc1, s_uastc5_to_bc1 };
+
+ void encode_bc4(void* pDst, const uint8_t* pPixels, uint32_t stride)
+ {
+ uint32_t min0_v, max0_v, min1_v, max1_v,min2_v, max2_v, min3_v, max3_v;
+
+ {
+ min0_v = max0_v = pPixels[0 * stride];
+ min1_v = max1_v = pPixels[1 * stride];
+ min2_v = max2_v = pPixels[2 * stride];
+ min3_v = max3_v = pPixels[3 * stride];
+ }
+
+ {
+ uint32_t v0 = pPixels[4 * stride]; min0_v = std::min(min0_v, v0); max0_v = std::max(max0_v, v0);
+ uint32_t v1 = pPixels[5 * stride]; min1_v = std::min(min1_v, v1); max1_v = std::max(max1_v, v1);
+ uint32_t v2 = pPixels[6 * stride]; min2_v = std::min(min2_v, v2); max2_v = std::max(max2_v, v2);
+ uint32_t v3 = pPixels[7 * stride]; min3_v = std::min(min3_v, v3); max3_v = std::max(max3_v, v3);
+ }
+
+ {
+ uint32_t v0 = pPixels[8 * stride]; min0_v = std::min(min0_v, v0); max0_v = std::max(max0_v, v0);
+ uint32_t v1 = pPixels[9 * stride]; min1_v = std::min(min1_v, v1); max1_v = std::max(max1_v, v1);
+ uint32_t v2 = pPixels[10 * stride]; min2_v = std::min(min2_v, v2); max2_v = std::max(max2_v, v2);
+ uint32_t v3 = pPixels[11 * stride]; min3_v = std::min(min3_v, v3); max3_v = std::max(max3_v, v3);
+ }
+
+ {
+ uint32_t v0 = pPixels[12 * stride]; min0_v = std::min(min0_v, v0); max0_v = std::max(max0_v, v0);
+ uint32_t v1 = pPixels[13 * stride]; min1_v = std::min(min1_v, v1); max1_v = std::max(max1_v, v1);
+ uint32_t v2 = pPixels[14 * stride]; min2_v = std::min(min2_v, v2); max2_v = std::max(max2_v, v2);
+ uint32_t v3 = pPixels[15 * stride]; min3_v = std::min(min3_v, v3); max3_v = std::max(max3_v, v3);
+ }
+
+ const uint32_t min_v = basisu::minimum(min0_v, min1_v, min2_v, min3_v);
+ const uint32_t max_v = basisu::maximum(max0_v, max1_v, max2_v, max3_v);
+
+ uint8_t* pDst_bytes = static_cast(pDst);
+ pDst_bytes[0] = (uint8_t)max_v;
+ pDst_bytes[1] = (uint8_t)min_v;
+
+ if (max_v == min_v)
+ {
+ memset(pDst_bytes + 2, 0, 6);
+ return;
+ }
+
+ const uint32_t delta = max_v - min_v;
+
+ // min_v is now 0. Compute thresholds between values by scaling max_v. It's x14 because we're adding two x7 scale factors.
+ const int t0 = delta * 13;
+ const int t1 = delta * 11;
+ const int t2 = delta * 9;
+ const int t3 = delta * 7;
+ const int t4 = delta * 5;
+ const int t5 = delta * 3;
+ const int t6 = delta * 1;
+
+ // BC4 floors in its divisions, which we compensate for with the 4 bias.
+ // This function is optimal for all possible inputs (i.e. it outputs the same results as checking all 8 values and choosing the closest one).
+ const int bias = 4 - min_v * 14;
+
+ static const uint32_t s_tran0[8] = { 1U , 7U , 6U , 5U , 4U , 3U , 2U , 0U };
+ static const uint32_t s_tran1[8] = { 1U << 3U, 7U << 3U, 6U << 3U, 5U << 3U, 4U << 3U, 3U << 3U, 2U << 3U, 0U << 3U };
+ static const uint32_t s_tran2[8] = { 1U << 6U, 7U << 6U, 6U << 6U, 5U << 6U, 4U << 6U, 3U << 6U, 2U << 6U, 0U << 6U };
+ static const uint32_t s_tran3[8] = { 1U << 9U, 7U << 9U, 6U << 9U, 5U << 9U, 4U << 9U, 3U << 9U, 2U << 9U, 0U << 9U };
+
+ uint64_t a0, a1, a2, a3;
+ {
+ const int v0 = pPixels[0 * stride] * 14 + bias;
+ const int v1 = pPixels[1 * stride] * 14 + bias;
+ const int v2 = pPixels[2 * stride] * 14 + bias;
+ const int v3 = pPixels[3 * stride] * 14 + bias;
+ a0 = s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)];
+ a1 = s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)];
+ a2 = s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)];
+ a3 = s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)];
+ }
+
+ {
+ const int v0 = pPixels[4 * stride] * 14 + bias;
+ const int v1 = pPixels[5 * stride] * 14 + bias;
+ const int v2 = pPixels[6 * stride] * 14 + bias;
+ const int v3 = pPixels[7 * stride] * 14 + bias;
+ a0 |= (s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)] << 12U);
+ a1 |= (s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)] << 12U);
+ a2 |= (s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)] << 12U);
+ a3 |= (s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)] << 12U);
+ }
+
+ {
+ const int v0 = pPixels[8 * stride] * 14 + bias;
+ const int v1 = pPixels[9 * stride] * 14 + bias;
+ const int v2 = pPixels[10 * stride] * 14 + bias;
+ const int v3 = pPixels[11 * stride] * 14 + bias;
+ a0 |= (((uint64_t)s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)]) << 24U);
+ a1 |= (((uint64_t)s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)]) << 24U);
+ a2 |= (((uint64_t)s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)]) << 24U);
+ a3 |= (((uint64_t)s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)]) << 24U);
+ }
+
+ {
+ const int v0 = pPixels[12 * stride] * 14 + bias;
+ const int v1 = pPixels[13 * stride] * 14 + bias;
+ const int v2 = pPixels[14 * stride] * 14 + bias;
+ const int v3 = pPixels[15 * stride] * 14 + bias;
+ a0 |= (((uint64_t)s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)]) << 36U);
+ a1 |= (((uint64_t)s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)]) << 36U);
+ a2 |= (((uint64_t)s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)]) << 36U);
+ a3 |= (((uint64_t)s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)]) << 36U);
+ }
+
+ const uint64_t f = a0 | a1 | a2 | a3;
+
+ pDst_bytes[2] = (uint8_t)f;
+ pDst_bytes[3] = (uint8_t)(f >> 8U);
+ pDst_bytes[4] = (uint8_t)(f >> 16U);
+ pDst_bytes[5] = (uint8_t)(f >> 24U);
+ pDst_bytes[6] = (uint8_t)(f >> 32U);
+ pDst_bytes[7] = (uint8_t)(f >> 40U);
+ }
+
+ static void bc1_find_sels(const color32 *pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16])
+ {
+ uint32_t block_r[4], block_g[4], block_b[4];
+
+ block_r[0] = (lr << 3) | (lr >> 2); block_g[0] = (lg << 2) | (lg >> 4); block_b[0] = (lb << 3) | (lb >> 2);
+ block_r[3] = (hr << 3) | (hr >> 2); block_g[3] = (hg << 2) | (hg >> 4); block_b[3] = (hb << 3) | (hb >> 2);
+ block_r[1] = (block_r[0] * 2 + block_r[3]) / 3; block_g[1] = (block_g[0] * 2 + block_g[3]) / 3; block_b[1] = (block_b[0] * 2 + block_b[3]) / 3;
+ block_r[2] = (block_r[3] * 2 + block_r[0]) / 3; block_g[2] = (block_g[3] * 2 + block_g[0]) / 3; block_b[2] = (block_b[3] * 2 + block_b[0]) / 3;
+
+ int ar = block_r[3] - block_r[0], ag = block_g[3] - block_g[0], ab = block_b[3] - block_b[0];
+
+ int dots[4];
+ for (uint32_t i = 0; i < 4; i++)
+ dots[i] = (int)block_r[i] * ar + (int)block_g[i] * ag + (int)block_b[i] * ab;
+
+ int t0 = dots[0] + dots[1], t1 = dots[1] + dots[2], t2 = dots[2] + dots[3];
+
+ ar *= 2; ag *= 2; ab *= 2;
+
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ const int d = pSrc_pixels[i].r * ar + pSrc_pixels[i].g * ag + pSrc_pixels[i].b * ab;
+ static const uint8_t s_sels[4] = { 3, 2, 1, 0 };
+
+ // Rounding matters here!
+ // d <= t0: <=, not <, to the later LS step "sees" a wider range of selectors. It matters for quality.
+ sels[i] = s_sels[(d <= t0) + (d < t1) + (d < t2)];
+ }
+ }
+
+ static inline void bc1_find_sels_2(const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16])
+ {
+ uint32_t block_r[4], block_g[4], block_b[4];
+
+ block_r[0] = (lr << 3) | (lr >> 2); block_g[0] = (lg << 2) | (lg >> 4); block_b[0] = (lb << 3) | (lb >> 2);
+ block_r[3] = (hr << 3) | (hr >> 2); block_g[3] = (hg << 2) | (hg >> 4); block_b[3] = (hb << 3) | (hb >> 2);
+ block_r[1] = (block_r[0] * 2 + block_r[3]) / 3; block_g[1] = (block_g[0] * 2 + block_g[3]) / 3; block_b[1] = (block_b[0] * 2 + block_b[3]) / 3;
+ block_r[2] = (block_r[3] * 2 + block_r[0]) / 3; block_g[2] = (block_g[3] * 2 + block_g[0]) / 3; block_b[2] = (block_b[3] * 2 + block_b[0]) / 3;
+
+ int ar = block_r[3] - block_r[0], ag = block_g[3] - block_g[0], ab = block_b[3] - block_b[0];
+
+ int dots[4];
+ for (uint32_t i = 0; i < 4; i++)
+ dots[i] = (int)block_r[i] * ar + (int)block_g[i] * ag + (int)block_b[i] * ab;
+
+ int t0 = dots[0] + dots[1], t1 = dots[1] + dots[2], t2 = dots[2] + dots[3];
+
+ ar *= 2; ag *= 2; ab *= 2;
+
+ static const uint8_t s_sels[4] = { 3, 2, 1, 0 };
+
+ for (uint32_t i = 0; i < 16; i += 4)
+ {
+ const int d0 = pSrc_pixels[i+0].r * ar + pSrc_pixels[i+0].g * ag + pSrc_pixels[i+0].b * ab;
+ const int d1 = pSrc_pixels[i+1].r * ar + pSrc_pixels[i+1].g * ag + pSrc_pixels[i+1].b * ab;
+ const int d2 = pSrc_pixels[i+2].r * ar + pSrc_pixels[i+2].g * ag + pSrc_pixels[i+2].b * ab;
+ const int d3 = pSrc_pixels[i+3].r * ar + pSrc_pixels[i+3].g * ag + pSrc_pixels[i+3].b * ab;
+
+ sels[i+0] = s_sels[(d0 <= t0) + (d0 < t1) + (d0 < t2)];
+ sels[i+1] = s_sels[(d1 <= t0) + (d1 < t1) + (d1 < t2)];
+ sels[i+2] = s_sels[(d2 <= t0) + (d2 < t1) + (d2 < t2)];
+ sels[i+3] = s_sels[(d3 <= t0) + (d3 < t1) + (d3 < t2)];
+ }
+ }
+
+ struct vec3F { float c[3]; };
+
+ static bool compute_least_squares_endpoints_rgb(const color32* pColors, const uint8_t* pSelectors, vec3F* pXl, vec3F* pXh)
+ {
+ // Derived from bc7enc16's LS function.
+ // Least squares using normal equations: 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.
+ uint32_t uq00_r = 0, uq10_r = 0, ut_r = 0, uq00_g = 0, uq10_g = 0, ut_g = 0, uq00_b = 0, uq10_b = 0, ut_b = 0;
+
+ // This table is: 9 * (w * w), 9 * ((1.0f - w) * w), 9 * ((1.0f - w) * (1.0f - w))
+ // where w is [0,1/3,2/3,1]. 9 is the perfect multiplier.
+ static const uint32_t s_weight_vals[4] = { 0x000009, 0x010204, 0x040201, 0x090000 };
+
+ uint32_t weight_accum = 0;
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ const uint32_t r = pColors[i].c[0], g = pColors[i].c[1], b = pColors[i].c[2];
+ const uint32_t sel = pSelectors[i];
+ ut_r += r;
+ ut_g += g;
+ ut_b += b;
+ weight_accum += s_weight_vals[sel];
+ uq00_r += sel * r;
+ uq00_g += sel * g;
+ uq00_b += sel * b;
+ }
+
+ float q00_r = (float)uq00_r, q10_r = (float)uq10_r, t_r = (float)ut_r;
+ float q00_g = (float)uq00_g, q10_g = (float)uq10_g, t_g = (float)ut_g;
+ float q00_b = (float)uq00_b, q10_b = (float)uq10_b, t_b = (float)ut_b;
+
+ q10_r = t_r * 3.0f - q00_r;
+ q10_g = t_g * 3.0f - q00_g;
+ q10_b = t_b * 3.0f - q00_b;
+
+ float z00 = (float)((weight_accum >> 16) & 0xFF);
+ float z10 = (float)((weight_accum >> 8) & 0xFF);
+ float z11 = (float)(weight_accum & 0xFF);
+ float z01 = z10;
+
+ float det = z00 * z11 - z01 * z10;
+ if (fabs(det) < 1e-8f)
+ return false;
+
+ det = 3.0f / det;
+
+ float iz00, iz01, iz10, iz11;
+ iz00 = z11 * det;
+ iz01 = -z01 * det;
+ iz10 = -z10 * det;
+ iz11 = z00 * det;
+
+ pXl->c[0] = iz00 * q00_r + iz01 * q10_r; pXh->c[0] = iz10 * q00_r + iz11 * q10_r;
+ pXl->c[1] = iz00 * q00_g + iz01 * q10_g; pXh->c[1] = iz10 * q00_g + iz11 * q10_g;
+ pXl->c[2] = iz00 * q00_b + iz01 * q10_b; pXh->c[2] = iz10 * q00_b + iz11 * q10_b;
+
+ // Check and fix channel singularities - might not be needed, but is in UASTC's encoder.
+ for (uint32_t c = 0; c < 3; c++)
+ {
+ if ((pXl->c[c] < 0.0f) || (pXh->c[c] > 255.0f))
+ {
+ uint32_t lo_v = UINT32_MAX, hi_v = 0;
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ lo_v = basisu::minimumu(lo_v, pColors[i].c[c]);
+ hi_v = basisu::maximumu(hi_v, pColors[i].c[c]);
+ }
+
+ if (lo_v == hi_v)
+ {
+ pXl->c[c] = (float)lo_v;
+ pXh->c[c] = (float)hi_v;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ void encode_bc1_solid_block(void* pDst, uint32_t fr, uint32_t fg, uint32_t fb)
+ {
+ dxt1_block* pDst_block = static_cast(pDst);
+
+ uint32_t mask = 0xAA;
+ uint32_t max16 = (g_bc1_match5_equals_1[fr].m_hi << 11) | (g_bc1_match6_equals_1[fg].m_hi << 5) | g_bc1_match5_equals_1[fb].m_hi;
+ uint32_t min16 = (g_bc1_match5_equals_1[fr].m_lo << 11) | (g_bc1_match6_equals_1[fg].m_lo << 5) | g_bc1_match5_equals_1[fb].m_lo;
+
+ if (min16 == max16)
+ {
+ // Always forbid 3 color blocks
+ // This is to guarantee that BC3 blocks never use punchthrough alpha (3 color) mode, which isn't supported on some (all?) GPU's.
+ mask = 0;
+
+ // Make l > h
+ if (min16 > 0)
+ min16--;
+ else
+ {
+ // l = h = 0
+ assert(min16 == max16 && max16 == 0);
+
+ max16 = 1;
+ min16 = 0;
+ mask = 0x55;
+ }
+
+ assert(max16 > min16);
+ }
+
+ if (max16 < min16)
+ {
+ std::swap(max16, min16);
+ mask ^= 0x55;
+ }
+
+ pDst_block->set_low_color(static_cast