Merge branch 'branch'
diff --git a/basisu.vcxproj b/basisu.vcxproj
index 71cd4f3..7ec572f 100644
--- a/basisu.vcxproj
+++ b/basisu.vcxproj
@@ -205,6 +205,7 @@
<ClInclude Include="encoder\lodepng.h" />
<ClInclude Include="transcoder\basisu.h" />
<ClInclude Include="transcoder\basisu_containers.h" />
+ <ClInclude Include="transcoder\basisu_file_headers.h" />
<ClInclude Include="transcoder\basisu_transcoder.h" />
<ClInclude Include="transcoder\basisu_transcoder_internal.h" />
<ClInclude Include="transcoder\basisu_global_selector_palette.h" />
diff --git a/basisu.vcxproj.filters b/basisu.vcxproj.filters
index 8916e7c..6259a84 100644
--- a/basisu.vcxproj.filters
+++ b/basisu.vcxproj.filters
@@ -163,6 +163,9 @@
<ClInclude Include="transcoder\basisu_containers_impl.h">
<Filter>transcoder</Filter>
</ClInclude>
+ <ClInclude Include="transcoder\basisu_file_headers.h">
+ <Filter>transcoder</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="transcoder\basisu_transcoder_tables_dxt1_6.inc">
diff --git a/basisu_tool.cpp b/basisu_tool.cpp
index 2623a5d..feeaa88 100644
--- a/basisu_tool.cpp
+++ b/basisu_tool.cpp
@@ -120,6 +120,7 @@
" -bench: UASTC benchmark mode, for development only\n"
" -resample_factor X: Resample all input textures by scale factor X using a box filter\n"
" -no_sse: Forbid all SSE instruction set usage\n"
+ " -validate_etc1s: Validate internal ETC1S compressor's data structures.\n"
"\n"
"Mipmap generation options:\n"
" -mipmap: Generate mipmaps for each source image\n"
@@ -406,10 +407,23 @@
m_comp_params.m_debug = true;
enable_debug_printf(true);
}
+ else if (strcasecmp(pArg, "-validate_etc1s") == 0)
+ {
+ m_comp_params.m_validate = true;
+ }
else if (strcasecmp(pArg, "-debug_images") == 0)
m_comp_params.m_debug_images = true;
else if (strcasecmp(pArg, "-stats") == 0)
m_comp_params.m_compute_stats = true;
+ else if (strcasecmp(pArg, "-gen_global_codebooks") == 0)
+ {
+ }
+ else if (strcasecmp(pArg, "-use_global_codebooks") == 0)
+ {
+ REMAINING_ARGS_CHECK(1);
+ m_etc1s_use_global_codebooks_file = arg_v[arg_index + 1];
+ arg_count++;
+ }
else if (strcasecmp(pArg, "-comp_level") == 0)
{
REMAINING_ARGS_CHECK(1);
@@ -711,6 +725,7 @@
std::string m_csv_file;
+ std::string m_etc1s_use_global_codebooks_file;
bool m_individual;
bool m_no_ktx;
bool m_etc1_only;
@@ -757,6 +772,55 @@
return true;
}
+struct basis_data
+{
+ basis_data(basist::etc1_global_selector_codebook& sel_codebook) :
+ m_transcoder(&sel_codebook)
+ {
+ }
+ uint8_vec m_file_data;
+ basist::basisu_transcoder m_transcoder;
+};
+static basis_data *load_basis_file(const char *pInput_filename, basist::etc1_global_selector_codebook &sel_codebook, bool force_etc1s)
+{
+ basis_data* p = new basis_data(sel_codebook);
+ uint8_vec &basis_data = p->m_file_data;
+ if (!basisu::read_file_to_vec(pInput_filename, basis_data))
+ {
+ error_printf("Failed reading file \"%s\"\n", pInput_filename);
+ delete p;
+ return nullptr;
+ }
+ printf("Input file \"%s\"\n", pInput_filename);
+ if (!basis_data.size())
+ {
+ error_printf("File is empty!\n");
+ delete p;
+ return nullptr;
+ }
+ if (basis_data.size() > UINT32_MAX)
+ {
+ error_printf("File is too large!\n");
+ delete p;
+ return nullptr;
+ }
+ if (force_etc1s)
+ {
+ if (p->m_transcoder.get_tex_format((const void*)&p->m_file_data[0], (uint32_t)p->m_file_data.size()) != basist::basis_tex_format::cETC1S)
+ {
+ error_printf("Global codebook file must be in ETC1S format!\n");
+ delete p;
+ return nullptr;
+ }
+ }
+ if (!p->m_transcoder.start_transcoding(&basis_data[0], (uint32_t)basis_data.size()))
+ {
+ error_printf("start_transcoding() failed!\n");
+ delete p;
+ return nullptr;
+ }
+ return p;
+}
static bool compress_mode(command_line_params &opts)
{
basist::etc1_global_selector_codebook sel_codebook(basist::g_global_selector_cb_size, basist::g_global_selector_cb);
@@ -784,13 +848,52 @@
error_printf("No input files to process!\n");
return false;
}
+ basis_data* pGlobal_codebook_data = nullptr;
+ if (opts.m_etc1s_use_global_codebooks_file.size())
+ {
+ pGlobal_codebook_data = load_basis_file(opts.m_etc1s_use_global_codebooks_file.c_str(), sel_codebook, true);
+ if (!pGlobal_codebook_data)
+ return false;
+#if 0
+ basis_data* pGlobal_codebook_data2 = load_basis_file("xmen_1024.basis", sel_codebook, true);
+ const basist::basisu_lowlevel_etc1s_transcoder &ta = pGlobal_codebook_data->m_transcoder.get_lowlevel_etc1s_decoder();
+ const basist::basisu_lowlevel_etc1s_transcoder &tb = pGlobal_codebook_data2->m_transcoder.get_lowlevel_etc1s_decoder();
+ if (ta.get_endpoints().size() != tb.get_endpoints().size())
+ {
+ printf("Endpoint CB's don't match\n");
+ }
+ else if (ta.get_selectors().size() != tb.get_selectors().size())
+ {
+ printf("Selector CB's don't match\n");
+ }
+ else
+ {
+ for (uint32_t i = 0; i < ta.get_endpoints().size(); i++)
+ {
+ if (ta.get_endpoints()[i] != tb.get_endpoints()[i])
+ {
+ printf("Endoint CB mismatch entry %u\n", i);
+ }
+ }
+ for (uint32_t i = 0; i < ta.get_selectors().size(); i++)
+ {
+ if (ta.get_selectors()[i] != tb.get_selectors()[i])
+ {
+ printf("Selector CB mismatch entry %u\n", i);
+ }
+ }
+ }
+ delete pGlobal_codebook_data2;
+ pGlobal_codebook_data2 = nullptr;
+#endif
+ }
basis_compressor_params ¶ms = opts.m_comp_params;
params.m_read_source_images = true;
params.m_write_output_basis_files = true;
params.m_pSel_codebook = &sel_codebook;
-
+ params.m_pGlobal_codebooks = pGlobal_codebook_data ? &pGlobal_codebook_data->m_transcoder.get_lowlevel_etc1s_decoder() : nullptr;
FILE *pCSV_file = nullptr;
if (opts.m_csv_file.size())
{
@@ -799,6 +902,7 @@
if (!pCSV_file)
{
error_printf("Failed opening CVS file \"%s\"\n", opts.m_csv_file.c_str());
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
fprintf(pCSV_file, "Filename, Size, Slices, Width, Height, HasAlpha, BitsPerTexel, Slice0RGBAvgPSNR, Slice0RGBAAvgPSNR, Slice0Luma709PSNR, Slice0BestETC1SLuma709PSNR, Q, CL, Time, RGBAvgPSNRMin, RGBAvgPSNRAvg, AAvgPSNRMin, AAvgPSNRAvg, Luma709PSNRMin, Luma709PSNRAvg\n");
@@ -865,6 +969,7 @@
pCSV_file = nullptr;
}
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -931,6 +1036,7 @@
pCSV_file = nullptr;
}
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
}
@@ -1009,6 +1115,8 @@
fclose(pCSV_file);
pCSV_file = nullptr;
}
+ delete pGlobal_codebook_data;
+ pGlobal_codebook_data = nullptr;
return true;
}
@@ -1018,9 +1126,17 @@
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);
+ basis_data* pGlobal_codebook_data = nullptr;
+ if (opts.m_etc1s_use_global_codebooks_file.size())
+ {
+ pGlobal_codebook_data = load_basis_file(opts.m_etc1s_use_global_codebooks_file.c_str(), sel_codebook, true);
+ if (!pGlobal_codebook_data)
+ return false;
+ }
if (!opts.m_input_filenames.size())
{
error_printf("No input files to process!\n");
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1031,6 +1147,7 @@
if (!pCSV_file)
{
error_printf("Failed opening CVS file \"%s\"\n", opts.m_csv_file.c_str());
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
//fprintf(pCSV_file, "Filename, Size, Slices, Width, Height, HasAlpha, BitsPerTexel, Slice0RGBAvgPSNR, Slice0RGBAAvgPSNR, Slice0Luma709PSNR, Slice0BestETC1SLuma709PSNR, Q, CL, Time, RGBAvgPSNRMin, RGBAvgPSNRAvg, AAvgPSNRMin, AAvgPSNRAvg, Luma709PSNRMin, Luma709PSNRAvg\n");
@@ -1051,6 +1168,7 @@
{
error_printf("Failed reading file \"%s\"\n", pInput_filename);
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1060,6 +1178,7 @@
{
error_printf("File is empty!\n");
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1067,11 +1186,16 @@
{
error_printf("File is too large!\n");
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
basist::basisu_transcoder dec(&sel_codebook);
+ if (pGlobal_codebook_data)
+ {
+ dec.set_global_codebooks(&pGlobal_codebook_data->m_transcoder.get_lowlevel_etc1s_decoder());
+ }
if (!opts.m_fuzz_testing)
{
// Skip the full validation, which CRC16's the entire file.
@@ -1081,6 +1205,7 @@
{
error_printf("File version is unsupported, or file fail CRC checks!\n");
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
}
@@ -1092,6 +1217,7 @@
{
error_printf("Failed retrieving Basis file information!\n");
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1129,6 +1255,7 @@
{
error_printf("get_image_info() failed!\n");
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1178,6 +1305,7 @@
{
error_printf("start_transcoding() failed!\n");
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1260,6 +1388,7 @@
{
error_printf("Failed retrieving image level information (%u %u)!\n", image_index, level_index);
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1292,6 +1421,7 @@
{
error_printf("Failed transcoding image level (%u %u %u)!\n", image_index, level_index, format_iter);
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1336,6 +1466,7 @@
{
error_printf("Failed writing KTX file \"%s\"!\n", ktx_filename.c_str());
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
printf("Wrote KTX file \"%s\"\n", ktx_filename.c_str());
@@ -1364,6 +1495,7 @@
{
error_printf("Failed writing KTX file \"%s\"!\n", ktx_filename.c_str());
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
printf("Wrote KTX file \"%s\"\n", ktx_filename.c_str());
@@ -1377,6 +1509,7 @@
{
error_printf("Failed retrieving image level information (%u %u)!\n", image_index, level_index);
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1396,6 +1529,7 @@
if (!save_png(rgb_filename, u, cImageSaveIgnoreAlpha))
{
error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str());
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
printf("Wrote PNG file \"%s\"\n", rgb_filename.c_str());
@@ -1411,6 +1545,7 @@
{
error_printf("Failed writing to OUT file \"%s\"\n", out_filename.c_str());
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
printf("Wrote .OUT file \"%s\"\n", out_filename.c_str());
@@ -1427,6 +1562,7 @@
{
error_printf("Failed writing to PNG file \"%s\"\n", a_filename.c_str());
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
printf("Wrote PNG file \"%s\"\n", a_filename.c_str());
@@ -1440,6 +1576,7 @@
{
error_printf("Failed writing to PNG file \"%s\"\n", rgba_filename.c_str());
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
printf("Wrote PNG file \"%s\"\n", rgba_filename.c_str());
@@ -1466,6 +1603,7 @@
{
error_printf("Failed retrieving image level information (%u %u)!\n", image_index, level_index);
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1479,6 +1617,7 @@
{
error_printf("Failed transcoding image level (%u %u %u)!\n", image_index, level_index, transcoder_tex_fmt);
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1495,6 +1634,7 @@
{
error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str());
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
printf("Wrote PNG file \"%s\"\n", rgb_filename.c_str());
@@ -1504,6 +1644,7 @@
{
error_printf("Failed writing to PNG file \"%s\"\n", a_filename.c_str());
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
printf("Wrote PNG file \"%s\"\n", a_filename.c_str());
@@ -1525,6 +1666,7 @@
{
error_printf("Failed retrieving image level information (%u %u)!\n", image_index, level_index);
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1538,6 +1680,7 @@
{
error_printf("Failed transcoding image level (%u %u %u)!\n", image_index, level_index, transcoder_tex_fmt);
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1568,6 +1711,7 @@
{
error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str());
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
printf("Wrote PNG file \"%s\"\n", rgb_filename.c_str());
@@ -1592,6 +1736,7 @@
{
error_printf("Failed retrieving image level information (%u %u)!\n", image_index, level_index);
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1605,6 +1750,7 @@
{
error_printf("Failed transcoding image level (%u %u %u)!\n", image_index, level_index, transcoder_tex_fmt);
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
@@ -1636,6 +1782,7 @@
{
error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str());
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
printf("Wrote PNG file \"%s\"\n", rgb_filename.c_str());
@@ -1645,6 +1792,7 @@
{
error_printf("Failed writing to PNG file \"%s\"\n", a_filename.c_str());
if (pCSV_file) fclose(pCSV_file);
+ delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
return false;
}
printf("Wrote PNG file \"%s\"\n", a_filename.c_str());
@@ -1695,6 +1843,8 @@
fclose(pCSV_file);
pCSV_file = nullptr;
}
+ delete pGlobal_codebook_data;
+ pGlobal_codebook_data = nullptr;
return true;
}
diff --git a/encoder/basisu_backend.cpp b/encoder/basisu_backend.cpp
index 5db04f0..bc9d025 100644
--- a/encoder/basisu_backend.cpp
+++ b/encoder/basisu_backend.cpp
@@ -183,8 +183,16 @@
basisu_frontend& r = *m_pFront_end;
//const bool is_video = r.get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
- //if ((total_block_endpoints_remapped) && (m_params.m_compression_level > 0))
- if ((total_block_endpoints_remapped) && (m_params.m_compression_level > 1))
+ if (m_params.m_used_global_codebooks)
+ {
+ m_endpoint_remap_table_old_to_new.resize(r.get_total_endpoint_clusters());
+ for (uint32_t i = 0; i < r.get_total_endpoint_clusters(); i++)
+ m_endpoint_remap_table_old_to_new[i] = i;
+ }
+ else
+ {
+ //if ((total_block_endpoints_remapped) && (m_params.m_compression_level > 0))
+ if ((total_block_endpoints_remapped) && (m_params.m_compression_level > 1))
{
// We've changed the block endpoint indices, so we need to go and adjust the endpoint codebook (remove unused entries, optimize existing entries that have changed)
uint_vec new_block_endpoints(get_total_blocks());
@@ -236,6 +244,7 @@
palette_index_reorderer reorderer;
reorderer.init((uint32_t)all_endpoint_indices.size(), &all_endpoint_indices[0], r.get_total_endpoint_clusters(), nullptr, nullptr, 0);
m_endpoint_remap_table_old_to_new = reorderer.get_remap_table();
+ }
m_endpoint_remap_table_new_to_old.resize(r.get_total_endpoint_clusters());
for (uint32_t i = 0; i < m_endpoint_remap_table_old_to_new.size(); i++)
@@ -248,7 +257,7 @@
m_selector_remap_table_new_to_old.resize(r.get_total_selector_clusters());
- if (m_params.m_compression_level == 0)
+ if ((m_params.m_compression_level == 0) || (m_params.m_used_global_codebooks))
{
for (uint32_t i = 0; i < r.get_total_selector_clusters(); i++)
m_selector_remap_table_new_to_old[i] = i;
@@ -1115,7 +1124,7 @@
total_used_selector_history_buf, total_used_selector_history_buf * 100.0f / get_total_blocks());
//if ((total_endpoint_indices_remapped) && (m_params.m_compression_level > 0))
- if ((total_endpoint_indices_remapped) && (m_params.m_compression_level > 1))
+ if ((total_endpoint_indices_remapped) && (m_params.m_compression_level > 1) && (!m_params.m_used_global_codebooks))
{
int_vec unused;
r.reoptimize_remapped_endpoints(block_endpoint_indices, unused, false, &block_selector_indices);
@@ -1676,6 +1685,7 @@
//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;
+ m_output.m_uses_global_codebooks = m_params.m_used_global_codebooks;
create_endpoint_palette();
create_selector_palette();
diff --git a/encoder/basisu_backend.h b/encoder/basisu_backend.h
index 0f9ca37..dc4652c 100644
--- a/encoder/basisu_backend.h
+++ b/encoder/basisu_backend.h
@@ -84,6 +84,7 @@
uint32_t m_global_sel_codebook_mod_bits;
bool m_use_hybrid_sel_codebooks;
+ bool m_used_global_codebooks;
basisu_backend_params()
{
clear();
@@ -102,6 +103,7 @@
m_global_sel_codebook_pal_bits = ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_PAL_BITS;
m_global_sel_codebook_mod_bits = basist::etc1_global_palette_entry_modifier::cTotalBits;
m_use_hybrid_sel_codebooks = false;
+ m_used_global_codebooks = false;
}
};
@@ -142,6 +144,7 @@
basist::basis_tex_format m_tex_format;
bool m_etc1s;
+ bool m_uses_global_codebooks;
uint32_t m_num_endpoints;
uint32_t m_num_selectors;
@@ -164,6 +167,7 @@
{
m_tex_format = basist::basis_tex_format::cETC1S;
m_etc1s = false;
+ m_uses_global_codebooks = false;
m_num_endpoints = 0;
m_num_selectors = 0;
@@ -201,6 +205,7 @@
uint32_t encode();
const basisu_backend_output &get_output() const { return m_output; }
+ const basisu_backend_params& get_params() const { return m_params; }
private:
basisu_frontend *m_pFront_end;
diff --git a/encoder/basisu_basis_file.cpp b/encoder/basisu_basis_file.cpp
index 705ed7e..c7d12d8 100644
--- a/encoder/basisu_basis_file.cpp
+++ b/encoder/basisu_basis_file.cpp
@@ -47,6 +47,8 @@
if (y_flipped)
m_header.m_flags = m_header.m_flags | basist::cBASISHeaderFlagYFlipped;
+ if (encoder_output.m_uses_global_codebooks)
+ m_header.m_flags = m_header.m_flags | basist::cBASISHeaderFlagUsesGlobalCodebook;
for (uint32_t i = 0; i < encoder_output.m_slice_desc.size(); i++)
{
@@ -64,12 +66,26 @@
m_header.m_userdata1 = userdata1;
m_header.m_total_endpoints = encoder_output.m_num_endpoints;
+ if (!encoder_output.m_uses_global_codebooks)
+ {
m_header.m_endpoint_cb_file_ofs = m_endpoint_cb_file_ofs;
m_header.m_endpoint_cb_file_size = (uint32_t)encoder_output.m_endpoint_palette.size();
+ }
+ else
+ {
+ assert(!m_endpoint_cb_file_ofs);
+ }
m_header.m_total_selectors = encoder_output.m_num_selectors;
+ if (!encoder_output.m_uses_global_codebooks)
+ {
m_header.m_selector_cb_file_ofs = m_selector_cb_file_ofs;
m_header.m_selector_cb_file_size = (uint32_t)encoder_output.m_selector_palette.size();
+ }
+ else
+ {
+ assert(!m_selector_cb_file_ofs);
+ }
m_header.m_tables_file_ofs = m_tables_file_ofs;
m_header.m_tables_file_size = (uint32_t)encoder_output.m_slice_image_tables.size();
@@ -134,6 +150,8 @@
assert(m_comp_data.size() == m_slice_descs_file_ofs);
append_vector(m_comp_data, reinterpret_cast<const uint8_t*>(&m_images_descs[0]), m_images_descs.size() * sizeof(m_images_descs[0]));
+ if (!encoder_output.m_uses_global_codebooks)
+ {
if (encoder_output.m_endpoint_palette.size())
{
assert(m_comp_data.size() == m_endpoint_cb_file_ofs);
@@ -144,6 +162,7 @@
{
assert(m_comp_data.size() == m_selector_cb_file_ofs);
append_vector(m_comp_data, reinterpret_cast<const uint8_t*>(&encoder_output.m_selector_palette[0]), encoder_output.m_selector_palette.size());
+ }
}
if (encoder_output.m_slice_image_tables.size())
@@ -179,8 +198,17 @@
const basisu_backend_slice_desc_vec &slice_descs = encoder_output.m_slice_desc;
// The Basis file uses 32-bit fields for lots of stuff, so make sure it's not too large.
- uint64_t check_size = (uint64_t)sizeof(basist::basis_file_header) + (uint64_t)sizeof(basist::basis_slice_desc) * slice_descs.size() +
+ uint64_t check_size = 0;
+ if (!encoder_output.m_uses_global_codebooks)
+ {
+ check_size = (uint64_t)sizeof(basist::basis_file_header) + (uint64_t)sizeof(basist::basis_slice_desc) * slice_descs.size() +
(uint64_t)encoder_output.m_endpoint_palette.size() + (uint64_t)encoder_output.m_selector_palette.size() + (uint64_t)encoder_output.m_slice_image_tables.size();
+ }
+ else
+ {
+ check_size = (uint64_t)sizeof(basist::basis_file_header) + (uint64_t)sizeof(basist::basis_slice_desc) * slice_descs.size() +
+ (uint64_t)encoder_output.m_slice_image_tables.size();
+ }
if (check_size >= 0xFFFF0000ULL)
{
error_printf("basisu_file::init: File is too large!\n");
@@ -191,9 +219,18 @@
m_slice_descs_file_ofs = sizeof(basist::basis_file_header);
if (encoder_output.m_tex_format == basist::basis_tex_format::cETC1S)
{
+ if (encoder_output.m_uses_global_codebooks)
+ {
+ m_endpoint_cb_file_ofs = 0;
+ m_selector_cb_file_ofs = 0;
+ m_tables_file_ofs = m_slice_descs_file_ofs + sizeof(basist::basis_slice_desc) * (uint32_t)slice_descs.size();
+ }
+ else
+ {
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
diff --git a/encoder/basisu_comp.cpp b/encoder/basisu_comp.cpp
index 9ca1ef5..c85d09e 100644
--- a/encoder/basisu_comp.cpp
+++ b/encoder/basisu_comp.cpp
@@ -61,6 +61,7 @@
PRINT_BOOL_VALUE(m_uastc);
PRINT_BOOL_VALUE(m_y_flip);
PRINT_BOOL_VALUE(m_debug);
+ PRINT_BOOL_VALUE(m_validate);
PRINT_BOOL_VALUE(m_debug_images);
PRINT_BOOL_VALUE(m_global_sel_pal);
PRINT_BOOL_VALUE(m_auto_global_sel_pal);
@@ -121,6 +122,11 @@
PRINT_BOOL_VALUE(m_rdo_uastc_multithreading);
PRINT_FLOAT_VALUE(m_resample_factor);
+ printf("Has global codebooks: %u\n", m_params.m_pGlobal_codebooks ? 1 : 0);
+ if (m_params.m_pGlobal_codebooks)
+ {
+ printf("Global codebook endpoints: %u selectors: %u\n", m_params.m_pGlobal_codebooks->get_endpoints().size(), m_params.m_pGlobal_codebooks->get_selectors().size());
+ }
#undef PRINT_BOOL_VALUE
#undef PRINT_INT_VALUE
@@ -1006,7 +1012,9 @@
p.m_tex_type = m_params.m_tex_type;
p.m_multithreaded = m_params.m_multithreading;
p.m_disable_hierarchical_endpoint_codebooks = m_params.m_disable_hierarchical_endpoint_codebooks;
+ p.m_validate = m_params.m_validate;
p.m_pJob_pool = m_params.m_pJob_pool;
+ p.m_pGlobal_codebooks = m_params.m_pGlobal_codebooks;
if ((m_params.m_global_sel_pal) || (m_auto_global_sel_pal))
{
@@ -1113,6 +1121,7 @@
backend_params.m_global_sel_codebook_pal_bits = m_frontend.get_params().m_num_global_sel_codebook_pal_bits;
backend_params.m_global_sel_codebook_mod_bits = m_frontend.get_params().m_num_global_sel_codebook_mod_bits;
backend_params.m_use_hybrid_sel_codebooks = m_frontend.get_params().m_use_hybrid_selector_codebooks;
+ backend_params.m_used_global_codebooks = m_frontend.get_params().m_pGlobal_codebooks != nullptr;
m_backend.init(&m_frontend, backend_params, m_slice_descs, m_params.m_pSel_codebook);
uint32_t total_packed_bytes = m_backend.encode();
@@ -1166,6 +1175,10 @@
m_decoded_output_textures_unpacked_bc7.resize(m_slice_descs.size());
tm.start();
+ if (m_params.m_pGlobal_codebooks)
+ {
+ decoder.set_global_codebooks(m_params.m_pGlobal_codebooks);
+ }
if (!decoder.start_transcoding(&comp_data[0], (uint32_t)comp_data.size()))
{
diff --git a/encoder/basisu_comp.h b/encoder/basisu_comp.h
index c1a5090..9f196a7 100644
--- a/encoder/basisu_comp.h
+++ b/encoder/basisu_comp.h
@@ -230,6 +230,7 @@
m_y_flip.clear();
m_debug.clear();
+ m_validate.clear();
m_debug_images.clear();
m_global_sel_pal.clear();
m_auto_global_sel_pal.clear();
@@ -289,6 +290,7 @@
m_resample_factor.clear();
+ m_pGlobal_codebooks = nullptr;
m_pJob_pool = nullptr;
}
@@ -319,6 +321,7 @@
// Output debug information during compression
bool_param<false> m_debug;
+ bool_param<false> m_validate;
// m_debug_images is pretty slow
bool_param<false> m_debug_images;
@@ -407,6 +410,7 @@
bool_param<true> m_rdo_uastc_multithreading;
param<float> m_resample_factor;
+ const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks;
job_pool *m_pJob_pool;
};
diff --git a/encoder/basisu_frontend.cpp b/encoder/basisu_frontend.cpp
index f7d47e9..526dc41 100644
--- a/encoder/basisu_frontend.cpp
+++ b/encoder/basisu_frontend.cpp
@@ -196,107 +196,114 @@
init_etc1_images();
- init_endpoint_training_vectors();
-
- generate_endpoint_clusters();
-
- for (uint32_t refine_endpoint_step = 0; refine_endpoint_step < m_num_endpoint_codebook_iterations; refine_endpoint_step++)
+ if (m_params.m_pGlobal_codebooks)
{
- BASISU_FRONTEND_VERIFY(check_etc1s_constraints());
+ init_global_codebooks();
+ }
+ else
+ {
+ init_endpoint_training_vectors();
- if (refine_endpoint_step)
+ generate_endpoint_clusters();
+
+ for (uint32_t refine_endpoint_step = 0; refine_endpoint_step < m_num_endpoint_codebook_iterations; refine_endpoint_step++)
{
- introduce_new_endpoint_clusters();
- }
+ BASISU_FRONTEND_VERIFY(check_etc1s_constraints());
- generate_endpoint_codebook(refine_endpoint_step);
-
- if ((m_params.m_debug_images) && (m_params.m_dump_endpoint_clusterization))
- {
- char buf[256];
- snprintf(buf, sizeof(buf), "endpoint_cluster_vis_pre_%u.png", refine_endpoint_step);
- dump_endpoint_clusterization_visualization(buf, false);
- }
-
- bool early_out = false;
-
- if (m_endpoint_refinement)
- {
- //dump_endpoint_clusterization_visualization("endpoint_clusters_before_refinement.png");
-
- if (!refine_endpoint_clusterization())
- early_out = true;
-
- if ((m_params.m_tex_type == basist::cBASISTexTypeVideoFrames) && (!refine_endpoint_step) && (m_num_endpoint_codebook_iterations == 1))
+ if (refine_endpoint_step)
{
- eliminate_redundant_or_empty_endpoint_clusters();
- generate_endpoint_codebook(refine_endpoint_step);
+ introduce_new_endpoint_clusters();
}
+ generate_endpoint_codebook(refine_endpoint_step);
+
if ((m_params.m_debug_images) && (m_params.m_dump_endpoint_clusterization))
{
char buf[256];
- snprintf(buf, sizeof(buf), "endpoint_cluster_vis_post_%u.png", refine_endpoint_step);
-
+ snprintf(buf, sizeof(buf), "endpoint_cluster_vis_pre_%u.png", refine_endpoint_step);
dump_endpoint_clusterization_visualization(buf, false);
- snprintf(buf, sizeof(buf), "endpoint_cluster_colors_vis_post_%u.png", refine_endpoint_step);
-
- dump_endpoint_clusterization_visualization(buf, true);
}
- }
+
+ bool early_out = false;
+
+ if (m_endpoint_refinement)
+ {
+ //dump_endpoint_clusterization_visualization("endpoint_clusters_before_refinement.png");
+
+ if (!refine_endpoint_clusterization())
+ early_out = true;
+
+ if ((m_params.m_tex_type == basist::cBASISTexTypeVideoFrames) && (!refine_endpoint_step) && (m_num_endpoint_codebook_iterations == 1))
+ {
+ eliminate_redundant_or_empty_endpoint_clusters();
+ generate_endpoint_codebook(refine_endpoint_step);
+ }
+
+ if ((m_params.m_debug_images) && (m_params.m_dump_endpoint_clusterization))
+ {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "endpoint_cluster_vis_post_%u.png", refine_endpoint_step);
+
+ dump_endpoint_clusterization_visualization(buf, false);
+ snprintf(buf, sizeof(buf), "endpoint_cluster_colors_vis_post_%u.png", refine_endpoint_step);
+
+ dump_endpoint_clusterization_visualization(buf, true);
+ }
+ }
- eliminate_redundant_or_empty_endpoint_clusters();
+ eliminate_redundant_or_empty_endpoint_clusters();
- if (m_params.m_debug_stats)
- debug_printf("Total endpoint clusters: %u\n", (uint32_t)m_endpoint_clusters.size());
+ if (m_params.m_debug_stats)
+ debug_printf("Total endpoint clusters: %u\n", (uint32_t)m_endpoint_clusters.size());
- if (early_out)
- break;
- }
+ if (early_out)
+ break;
+ }
- BASISU_FRONTEND_VERIFY(check_etc1s_constraints());
+ BASISU_FRONTEND_VERIFY(check_etc1s_constraints());
- generate_block_endpoint_clusters();
+ generate_block_endpoint_clusters();
- create_initial_packed_texture();
+ create_initial_packed_texture();
- generate_selector_clusters();
+ generate_selector_clusters();
- if (m_use_hierarchical_selector_codebooks)
- compute_selector_clusters_within_each_parent_cluster();
+ if (m_use_hierarchical_selector_codebooks)
+ compute_selector_clusters_within_each_parent_cluster();
- if (m_params.m_compression_level == 0)
- {
- create_optimized_selector_codebook(0);
-
- find_optimal_selector_clusters_for_each_block();
-
- introduce_special_selector_clusters();
- }
- else
- {
- const uint32_t num_refine_selector_steps = m_params.m_pGlobal_sel_codebook ? 1 : m_num_selector_codebook_iterations;
- for (uint32_t refine_selector_steps = 0; refine_selector_steps < num_refine_selector_steps; refine_selector_steps++)
+ if (m_params.m_compression_level == 0)
{
- create_optimized_selector_codebook(refine_selector_steps);
+ create_optimized_selector_codebook(0);
find_optimal_selector_clusters_for_each_block();
-
+
introduce_special_selector_clusters();
-
- if ((m_params.m_compression_level >= 4) || (m_params.m_tex_type == basist::cBASISTexTypeVideoFrames))
+ }
+ else
+ {
+ const uint32_t num_refine_selector_steps = m_params.m_pGlobal_sel_codebook ? 1 : m_num_selector_codebook_iterations;
+ for (uint32_t refine_selector_steps = 0; refine_selector_steps < num_refine_selector_steps; refine_selector_steps++)
{
- if (!refine_block_endpoints_given_selectors())
- break;
+ create_optimized_selector_codebook(refine_selector_steps);
+
+ find_optimal_selector_clusters_for_each_block();
+
+ introduce_special_selector_clusters();
+
+ if ((m_params.m_compression_level >= 4) || (m_params.m_tex_type == basist::cBASISTexTypeVideoFrames))
+ {
+ if (!refine_block_endpoints_given_selectors())
+ break;
+ }
}
}
+
+ optimize_selector_codebook();
+
+ if (m_params.m_debug_stats)
+ debug_printf("Total selector clusters: %u\n", (uint32_t)m_selector_cluster_block_indices.size());
}
- optimize_selector_codebook();
-
- if (m_params.m_debug_stats)
- debug_printf("Total selector clusters: %u\n", (uint32_t)m_selector_cluster_block_indices.size());
-
finalize();
if (m_params.m_validate)
@@ -310,6 +317,258 @@
return true;
}
+ bool basisu_frontend::init_global_codebooks()
+ {
+ const basist::basisu_lowlevel_etc1s_transcoder* pTranscoder = m_params.m_pGlobal_codebooks;
+
+ const basist::basisu_lowlevel_etc1s_transcoder::endpoint_vec& endpoints = pTranscoder->get_endpoints();
+ const basist::basisu_lowlevel_etc1s_transcoder::selector_vec& selectors = pTranscoder->get_selectors();
+
+ m_endpoint_cluster_etc_params.resize(endpoints.size());
+ for (uint32_t i = 0; i < endpoints.size(); i++)
+ {
+ m_endpoint_cluster_etc_params[i].m_inten_table[0] = endpoints[i].m_inten5;
+ m_endpoint_cluster_etc_params[i].m_inten_table[1] = endpoints[i].m_inten5;
+
+ m_endpoint_cluster_etc_params[i].m_color_unscaled[0].set(endpoints[i].m_color5.r, endpoints[i].m_color5.g, endpoints[i].m_color5.b, 255);
+ m_endpoint_cluster_etc_params[i].m_color_used[0] = true;
+ m_endpoint_cluster_etc_params[i].m_valid = true;
+ }
+
+ m_optimized_cluster_selectors.resize(selectors.size());
+ for (uint32_t i = 0; i < m_optimized_cluster_selectors.size(); i++)
+ {
+ for (uint32_t y = 0; y < 4; y++)
+ for (uint32_t x = 0; x < 4; x++)
+ m_optimized_cluster_selectors[i].set_selector(x, y, selectors[i].get_selector(x, y));
+ }
+
+ m_block_endpoint_clusters_indices.resize(m_total_blocks);
+
+ m_orig_encoded_blocks.resize(m_total_blocks);
+
+ m_block_selector_cluster_index.resize(m_total_blocks);
+
+#if 0
+ for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
+ {
+ const uint32_t first_index = block_index_iter;
+ const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
+
+#ifndef __EMSCRIPTEN__
+ m_params.m_pJob_pool->add_job([this, first_index, last_index] {
+#endif
+
+ for (uint32_t block_index = first_index; block_index < last_index; block_index++)
+ {
+ const etc_block& blk = m_etc1_blocks_etc1s[block_index];
+
+ const uint32_t block_endpoint_index = m_block_endpoint_clusters_indices[block_index][0];
+
+ etc_block trial_blk;
+ trial_blk.set_block_color5_etc1s(blk.m_color_unscaled[0]);
+ trial_blk.set_flip_bit(true);
+
+ uint64_t best_err = UINT64_MAX;
+ uint32_t best_index = 0;
+
+ for (uint32_t i = 0; i < m_optimized_cluster_selectors.size(); i++)
+ {
+ trial_blk.set_raw_selector_bits(m_optimized_cluster_selectors[i].get_raw_selector_bits());
+
+ const uint64_t cur_err = trial_blk.evaluate_etc1_error(get_source_pixel_block(block_index).get_ptr(), m_params.m_perceptual);
+ if (cur_err < best_err)
+ {
+ best_err = cur_err;
+ best_index = i;
+ if (!cur_err)
+ break;
+ }
+
+ } // block_index
+
+ m_block_selector_cluster_index[block_index] = best_index;
+ }
+
+#ifndef __EMSCRIPTEN__
+ });
+#endif
+
+ }
+
+#ifndef __EMSCRIPTEN__
+ m_params.m_pJob_pool->wait_for_all();
+#endif
+
+ m_encoded_blocks.resize(m_total_blocks);
+ for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
+ {
+ const uint32_t endpoint_index = m_block_endpoint_clusters_indices[block_index][0];
+ const uint32_t selector_index = m_block_selector_cluster_index[block_index];
+
+ etc_block& blk = m_encoded_blocks[block_index];
+
+ blk.set_block_color5_etc1s(m_endpoint_cluster_etc_params[endpoint_index].m_color_unscaled[0]);
+ blk.set_inten_tables_etc1s(m_endpoint_cluster_etc_params[endpoint_index].m_inten_table[0]);
+ blk.set_flip_bit(true);
+ blk.set_raw_selector_bits(m_optimized_cluster_selectors[selector_index].get_raw_selector_bits());
+ }
+#endif
+
+ const uint32_t NUM_PASSES = 3;
+ for (uint32_t pass = 0; pass < NUM_PASSES; pass++)
+ {
+ debug_printf("init_global_codebooks: pass %u\n", pass);
+
+ const uint32_t N = 128;
+ for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
+ {
+ const uint32_t first_index = block_index_iter;
+ const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
+
+#ifndef __EMSCRIPTEN__
+ m_params.m_pJob_pool->add_job([this, first_index, last_index, pass] {
+#endif
+
+ for (uint32_t block_index = first_index; block_index < last_index; block_index++)
+ {
+ const etc_block& blk = pass ? m_encoded_blocks[block_index] : m_etc1_blocks_etc1s[block_index];
+ const uint32_t blk_raw_selector_bits = blk.get_raw_selector_bits();
+
+ etc_block trial_blk(blk);
+ trial_blk.set_raw_selector_bits(blk_raw_selector_bits);
+ trial_blk.set_flip_bit(true);
+
+ uint64_t best_err = UINT64_MAX;
+ uint32_t best_index = 0;
+ etc_block best_block(trial_blk);
+
+ for (uint32_t i = 0; i < m_endpoint_cluster_etc_params.size(); i++)
+ {
+ if (m_endpoint_cluster_etc_params[i].m_inten_table[0] > blk.get_inten_table(0))
+ continue;
+
+ trial_blk.set_block_color5_etc1s(m_endpoint_cluster_etc_params[i].m_color_unscaled[0]);
+ trial_blk.set_inten_tables_etc1s(m_endpoint_cluster_etc_params[i].m_inten_table[0]);
+
+ const color_rgba* pSource_pixels = get_source_pixel_block(block_index).get_ptr();
+ uint64_t cur_err;
+ if (!pass)
+ cur_err = trial_blk.determine_selectors(pSource_pixels, m_params.m_perceptual);
+ else
+ cur_err = trial_blk.evaluate_etc1_error(pSource_pixels, m_params.m_perceptual);
+
+ if (cur_err < best_err)
+ {
+ best_err = cur_err;
+ best_index = i;
+ best_block = trial_blk;
+
+ if (!cur_err)
+ break;
+ }
+ }
+
+ m_block_endpoint_clusters_indices[block_index][0] = best_index;
+ m_block_endpoint_clusters_indices[block_index][1] = best_index;
+
+ m_orig_encoded_blocks[block_index] = best_block;
+
+ } // block_index
+
+#ifndef __EMSCRIPTEN__
+ });
+#endif
+
+ }
+
+#ifndef __EMSCRIPTEN__
+ m_params.m_pJob_pool->wait_for_all();
+#endif
+
+ m_endpoint_clusters.resize(0);
+ m_endpoint_clusters.resize(endpoints.size());
+ for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
+ {
+ const uint32_t endpoint_cluster_index = m_block_endpoint_clusters_indices[block_index][0];
+ m_endpoint_clusters[endpoint_cluster_index].push_back(block_index * 2);
+ m_endpoint_clusters[endpoint_cluster_index].push_back(block_index * 2 + 1);
+ }
+
+ m_block_selector_cluster_index.resize(m_total_blocks);
+
+ for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
+ {
+ const uint32_t first_index = block_index_iter;
+ const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
+
+#ifndef __EMSCRIPTEN__
+ m_params.m_pJob_pool->add_job([this, first_index, last_index, pass] {
+#endif
+
+ for (uint32_t block_index = first_index; block_index < last_index; block_index++)
+ {
+ const uint32_t block_endpoint_index = m_block_endpoint_clusters_indices[block_index][0];
+
+ etc_block trial_blk;
+ trial_blk.set_block_color5_etc1s(m_endpoint_cluster_etc_params[block_endpoint_index].m_color_unscaled[0]);
+ trial_blk.set_inten_tables_etc1s(m_endpoint_cluster_etc_params[block_endpoint_index].m_inten_table[0]);
+ trial_blk.set_flip_bit(true);
+
+ uint64_t best_err = UINT64_MAX;
+ uint32_t best_index = 0;
+
+ for (uint32_t i = 0; i < m_optimized_cluster_selectors.size(); i++)
+ {
+ trial_blk.set_raw_selector_bits(m_optimized_cluster_selectors[i].get_raw_selector_bits());
+
+ const uint64_t cur_err = trial_blk.evaluate_etc1_error(get_source_pixel_block(block_index).get_ptr(), m_params.m_perceptual);
+ if (cur_err < best_err)
+ {
+ best_err = cur_err;
+ best_index = i;
+ if (!cur_err)
+ break;
+ }
+
+ } // block_index
+
+ m_block_selector_cluster_index[block_index] = best_index;
+ }
+
+#ifndef __EMSCRIPTEN__
+ });
+#endif
+
+ }
+
+#ifndef __EMSCRIPTEN__
+ m_params.m_pJob_pool->wait_for_all();
+#endif
+
+ m_encoded_blocks.resize(m_total_blocks);
+ for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
+ {
+ const uint32_t endpoint_index = m_block_endpoint_clusters_indices[block_index][0];
+ const uint32_t selector_index = m_block_selector_cluster_index[block_index];
+
+ etc_block& blk = m_encoded_blocks[block_index];
+
+ blk.set_block_color5_etc1s(m_endpoint_cluster_etc_params[endpoint_index].m_color_unscaled[0]);
+ blk.set_inten_tables_etc1s(m_endpoint_cluster_etc_params[endpoint_index].m_inten_table[0]);
+ blk.set_flip_bit(true);
+ blk.set_raw_selector_bits(m_optimized_cluster_selectors[selector_index].get_raw_selector_bits());
+ }
+
+ } // pass
+
+ m_selector_cluster_block_indices.resize(selectors.size());
+ for (uint32_t block_index = 0; block_index < m_etc1_blocks_etc1s.size(); block_index++)
+ m_selector_cluster_block_indices[m_block_selector_cluster_index[block_index]].push_back(block_index);
+
+ return true;
+ }
+
void basisu_frontend::introduce_special_selector_clusters()
{
debug_printf("introduce_special_selector_clusters\n");
diff --git a/encoder/basisu_frontend.h b/encoder/basisu_frontend.h
index 1831d08..4ff6d40 100644
--- a/encoder/basisu_frontend.h
+++ b/encoder/basisu_frontend.h
@@ -18,6 +18,7 @@
#include "basisu_gpu_texture.h"
#include "basisu_global_selector_palette_helpers.h"
#include "../transcoder/basisu_file_headers.h"
+#include "../transcoder/basisu_transcoder.h"
namespace basisu
{
@@ -83,6 +84,7 @@
m_use_hybrid_selector_codebooks(false),
m_hybrid_codebook_quality_thresh(0.0f),
m_tex_type(basist::cBASISTexType2D),
+ m_pGlobal_codebooks(nullptr),
m_pJob_pool(nullptr)
{
@@ -110,6 +112,7 @@
bool m_use_hybrid_selector_codebooks;
float m_hybrid_codebook_quality_thresh;
basist::basis_texture_type m_tex_type;
+ const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks;
job_pool *m_pJob_pool;
};
@@ -330,6 +333,7 @@
//-----------------------------------------------------------------------------
void init_etc1_images();
+ bool init_global_codebooks();
void init_endpoint_training_vectors();
void dump_endpoint_clusterization_visualization(const char *pFilename, bool vis_endpoint_colors);
void generate_endpoint_clusters();
diff --git a/transcoder/basisu_file_headers.h b/transcoder/basisu_file_headers.h
index a9c67f9..a80c33e 100644
--- a/transcoder/basisu_file_headers.h
+++ b/transcoder/basisu_file_headers.h
@@ -49,7 +49,8 @@
{
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 any slices contain alpha (for ETC1S, if the odd slices contain alpha data)
+ cBASISHeaderFlagHasAlphaSlices = 4, // True if any slices contain alpha (for ETC1S, if the odd slices contain alpha data)
+ cBASISHeaderFlagUsesGlobalCodebook = 8 // For ETC1S files, this will be true if the file utilizes a codebook from another .basis file.
};
// The image type field attempts to describe how to interpret the image data in a Basis file.
diff --git a/transcoder/basisu_transcoder.cpp b/transcoder/basisu_transcoder.cpp
index e304907..2afdb5d 100644
--- a/transcoder/basisu_transcoder.cpp
+++ b/transcoder/basisu_transcoder.cpp
@@ -7515,6 +7515,7 @@
#endif // BASISD_SUPPORT_PVRTC2
basisu_lowlevel_etc1s_transcoder::basisu_lowlevel_etc1s_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook) :
+ m_pGlobal_codebook(nullptr),
m_pGlobal_sel_codebook(pGlobal_sel_codebook),
m_selector_history_buf_size(0)
{
@@ -7524,6 +7525,11 @@
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)
{
+ if (m_pGlobal_codebook)
+ {
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 11\n");
+ return false;
+ }
bitwise_decoder sym_codec;
huffman_decoding_table color5_delta_model0, color5_delta_model1, color5_delta_model2, inten_delta_model;
@@ -7566,7 +7572,7 @@
const bool endpoints_are_grayscale = sym_codec.get_bits(1) != 0;
- m_endpoints.resize(num_endpoints);
+ m_local_endpoints.resize(num_endpoints);
color32 prev_color5(16, 16, 16, 0);
uint32_t prev_inten = 0;
@@ -7574,8 +7580,8 @@
for (uint32_t i = 0; i < num_endpoints; i++)
{
uint32_t inten_delta = sym_codec.decode_huffman(inten_delta_model);
- m_endpoints[i].m_inten5 = static_cast<uint8_t>((inten_delta + prev_inten) & 7);
- prev_inten = m_endpoints[i].m_inten5;
+ m_local_endpoints[i].m_inten5 = static_cast<uint8_t>((inten_delta + prev_inten) & 7);
+ prev_inten = m_local_endpoints[i].m_inten5;
for (uint32_t c = 0; c < (endpoints_are_grayscale ? 1U : 3U); c++)
{
@@ -7589,21 +7595,21 @@
int v = (prev_color5[c] + delta) & 31;
- m_endpoints[i].m_color5[c] = static_cast<uint8_t>(v);
+ m_local_endpoints[i].m_color5[c] = static_cast<uint8_t>(v);
prev_color5[c] = static_cast<uint8_t>(v);
}
if (endpoints_are_grayscale)
{
- m_endpoints[i].m_color5[1] = m_endpoints[i].m_color5[0];
- m_endpoints[i].m_color5[2] = m_endpoints[i].m_color5[0];
+ m_local_endpoints[i].m_color5[1] = m_local_endpoints[i].m_color5[0];
+ m_local_endpoints[i].m_color5[2] = m_local_endpoints[i].m_color5[0];
}
}
sym_codec.stop();
- m_selectors.resize(num_selectors);
+ m_local_selectors.resize(num_selectors);
if (!sym_codec.init(pSelectors_data, selectors_data_size))
{
@@ -7657,9 +7663,9 @@
// TODO: Optimize this
for (uint32_t y = 0; y < 4; y++)
for (uint32_t x = 0; x < 4; x++)
- m_selectors[i].set_selector(x, y, e[x + y * 4]);
+ m_local_selectors[i].set_selector(x, y, e[x + y * 4]);
- m_selectors[i].init_flags();
+ m_local_selectors[i].init_flags();
}
}
else
@@ -7729,7 +7735,7 @@
for (uint32_t y = 0; y < 4; y++)
for (uint32_t x = 0; x < 4; x++)
- m_selectors[q].set_selector(x, y, e[x + y * 4]);
+ m_local_selectors[q].set_selector(x, y, e[x + y * 4]);
}
else
{
@@ -7738,11 +7744,11 @@
uint32_t cur_byte = sym_codec.get_bits(8);
for (uint32_t k = 0; k < 4; k++)
- m_selectors[q].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
+ m_local_selectors[q].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
}
}
- m_selectors[q].init_flags();
+ m_local_selectors[q].init_flags();
}
}
else
@@ -7758,10 +7764,10 @@
uint32_t cur_byte = sym_codec.get_bits(8);
for (uint32_t k = 0; k < 4; k++)
- m_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
+ m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
}
- m_selectors[i].init_flags();
+ m_local_selectors[i].init_flags();
}
}
else
@@ -7790,9 +7796,9 @@
prev_bytes[j] = static_cast<uint8_t>(cur_byte);
for (uint32_t k = 0; k < 4; k++)
- m_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
+ m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
}
- m_selectors[i].init_flags();
+ m_local_selectors[i].init_flags();
continue;
}
@@ -7804,9 +7810,9 @@
prev_bytes[j] = static_cast<uint8_t>(cur_byte);
for (uint32_t k = 0; k < 4; k++)
- m_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
+ m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
}
- m_selectors[i].init_flags();
+ m_local_selectors[i].init_flags();
}
}
}
@@ -7944,7 +7950,7 @@
approx_move_to_front selector_history_buf(m_selector_history_buf_size);
- const uint32_t SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX = (uint32_t)m_selectors.size();
+ const uint32_t SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX = (uint32_t)m_local_selectors.size();
const uint32_t SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX = m_selector_history_buf_size + SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX;
uint32_t cur_selector_rle_count = 0;
@@ -7977,6 +7983,13 @@
int prev_endpoint_pred_sym = 0;
int endpoint_pred_repeat_count = 0;
uint32_t prev_endpoint_index = 0;
+ const endpoint_vec& endpoints = m_pGlobal_codebook ? m_pGlobal_codebook->m_local_endpoints : m_local_endpoints;
+ const selector_vec& selectors = m_pGlobal_codebook ? m_pGlobal_codebook->m_local_selectors : m_local_selectors;
+ if (!endpoints.size() || !selectors.size())
+ {
+ BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: global codebooks must be unpacked first\n");
+ return false;
+ }
for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
{
@@ -8078,8 +8091,8 @@
const uint32_t delta_sym = sym_codec.decode_huffman(m_delta_endpoint_model);
endpoint_index = delta_sym + prev_endpoint_index;
- if (endpoint_index >= m_endpoints.size())
- endpoint_index -= (int)m_endpoints.size();
+ if (endpoint_index >= endpoints.size())
+ endpoint_index -= (int)endpoints.size();
}
pState->m_block_endpoint_preds[cur_block_endpoint_pred_array][block_x].m_endpoint_index = (uint16_t)endpoint_index;
@@ -8094,7 +8107,7 @@
{
cur_selector_rle_count--;
- selector_sym = (int)m_selectors.size();
+ selector_sym = (int)selectors.size();
}
else
{
@@ -8118,17 +8131,17 @@
return false;
}
- selector_sym = (int)m_selectors.size();
+ selector_sym = (int)selectors.size();
cur_selector_rle_count--;
}
}
- if (selector_sym >= (int)m_selectors.size())
+ if (selector_sym >= (int)selectors.size())
{
assert(m_selector_history_buf_size > 0);
- int history_buf_index = selector_sym - (int)m_selectors.size();
+ int history_buf_index = selector_sym - (int)selectors.size();
if (history_buf_index >= (int)selector_history_buf.size())
{
@@ -8153,7 +8166,7 @@
}
}
- if ((endpoint_index >= m_endpoints.size()) || (selector_index >= m_selectors.size()))
+ if ((endpoint_index >= endpoints.size()) || (selector_index >= selectors.size()))
{
// The file is corrupted or we've got a bug.
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (5)\n");
@@ -8177,8 +8190,8 @@
}
#endif
- const endpoint* pEndpoints = &m_endpoints[endpoint_index];
- const selector* pSelector = &m_selectors[selector_index];
+ const endpoint* pEndpoints = &endpoints[endpoint_index];
+ const selector* pSelector = &selectors[selector_index];
switch (fmt)
{
@@ -8282,8 +8295,8 @@
const uint16_t* pAlpha_block = reinterpret_cast<uint16_t*>(static_cast<uint8_t*>(pAlpha_blocks) + (block_x + block_y * num_blocks_x) * sizeof(uint32_t));
- const endpoint* pAlpha_endpoints = &m_endpoints[pAlpha_block[0]];
- const selector* pAlpha_selector = &m_selectors[pAlpha_block[1]];
+ const endpoint* pAlpha_endpoints = &endpoints[pAlpha_block[0]];
+ const selector* pAlpha_selector = &selectors[pAlpha_block[1]];
const color32& alpha_base_color = pAlpha_endpoints->m_color5;
const uint32_t alpha_inten_table = pAlpha_endpoints->m_inten5;
@@ -8342,7 +8355,7 @@
{
#if BASISD_SUPPORT_ASTC
void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;
- convert_etc1s_to_astc_4x4(pDst_block, pEndpoints, pSelector, transcode_alpha, &m_endpoints[0], &m_selectors[0]);
+ convert_etc1s_to_astc_4x4(pDst_block, pEndpoints, pSelector, transcode_alpha, &endpoints[0], &selectors[0]);
#else
assert(0);
#endif
@@ -8388,7 +8401,7 @@
void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;
- convert_etc1s_to_pvrtc2_rgba(pDst_block, pEndpoints, pSelector, &m_endpoints[0], &m_selectors[0]);
+ convert_etc1s_to_pvrtc2_rgba(pDst_block, pEndpoints, pSelector, &endpoints[0], &selectors[0]);
#endif
break;
}
@@ -8680,7 +8693,7 @@
if (fmt == block_format::cPVRTC1_4_RGB)
fixup_pvrtc1_4_modulation_rgb((decoder_etc_block*)pPVRTC_work_mem, pPVRTC_endpoints, pDst_blocks, num_blocks_x, num_blocks_y);
else if (fmt == block_format::cPVRTC1_4_RGBA)
- fixup_pvrtc1_4_modulation_rgba((decoder_etc_block*)pPVRTC_work_mem, pPVRTC_endpoints, pDst_blocks, num_blocks_x, num_blocks_y, pAlpha_blocks, &m_endpoints[0], &m_selectors[0]);
+ fixup_pvrtc1_4_modulation_rgba((decoder_etc_block*)pPVRTC_work_mem, pPVRTC_endpoints, pDst_blocks, num_blocks_x, num_blocks_y, pAlpha_blocks, &endpoints[0], &selectors[0]);
#endif // BASISD_SUPPORT_PVRTC1
if (pPVRTC_work_mem)
@@ -10378,46 +10391,84 @@
if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)
{
- if (m_lowlevel_etc1s_decoder.m_endpoints.size())
+ if (m_lowlevel_etc1s_decoder.m_local_endpoints.size())
{
m_lowlevel_etc1s_decoder.clear();
}
- if (!pHeader->m_endpoint_cb_file_size || !pHeader->m_selector_cb_file_size || !pHeader->m_tables_file_size)
+ if (pHeader->m_flags & cBASISHeaderFlagUsesGlobalCodebook)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted (0)\n");
+ if (!m_lowlevel_etc1s_decoder.get_global_codebooks())
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: File uses global codebooks, but set_global_codebooks() has not been called\n");
+ return false;
+ }
+ if (!m_lowlevel_etc1s_decoder.get_global_codebooks()->get_endpoints().size())
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: Global codebooks must be unpacked first by calling start_transcoding()\n");
+ return false;
+ }
+ if ((m_lowlevel_etc1s_decoder.get_global_codebooks()->get_endpoints().size() != pHeader->m_total_endpoints) ||
+ (m_lowlevel_etc1s_decoder.get_global_codebooks()->get_selectors().size() != pHeader->m_total_selectors))
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: Global codebook size mismatch (wrong codebooks for file).\n");
+ return false;
+ }
+ if (!pHeader->m_tables_file_size)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted (2)\n");
+ return false;
+ }
+ if (pHeader->m_tables_file_ofs > data_size)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (4)\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 (5)\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))
+ else
{
- 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");
+ 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_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_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 (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_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))
@@ -10429,7 +10480,7 @@
else
{
// Nothing special to do for UASTC.
- if (m_lowlevel_etc1s_decoder.m_endpoints.size())
+ if (m_lowlevel_etc1s_decoder.m_local_endpoints.size())
{
m_lowlevel_etc1s_decoder.clear();
}
diff --git a/transcoder/basisu_transcoder.h b/transcoder/basisu_transcoder.h
index 1134a34..4c5c815 100644
--- a/transcoder/basisu_transcoder.h
+++ b/transcoder/basisu_transcoder.h
@@ -169,6 +169,8 @@
public:
basisu_lowlevel_etc1s_transcoder(const basist::etc1_global_selector_codebook *pGlobal_sel_codebook);
+ void set_global_codebooks(const basisu_lowlevel_etc1s_transcoder* pGlobal_codebook) { m_pGlobal_codebook = pGlobal_codebook; }
+ const basisu_lowlevel_etc1s_transcoder *get_global_codebooks() const { return m_pGlobal_codebook; }
bool 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);
@@ -207,8 +209,8 @@
void clear()
{
- m_endpoints.clear();
- m_selectors.clear();
+ m_local_endpoints.clear();
+ m_local_selectors.clear();
m_endpoint_pred_model.clear();
m_delta_endpoint_model.clear();
m_selector_model.clear();
@@ -216,12 +218,20 @@
m_selector_history_buf_size = 0;
}
- private:
+ // Low-level methods
typedef basisu::vector<endpoint> endpoint_vec;
- endpoint_vec m_endpoints;
-
+ const endpoint_vec &get_endpoints() const { return m_local_endpoints; }
+
typedef basisu::vector<selector> selector_vec;
- selector_vec m_selectors;
+ const selector_vec &get_selectors() const { return m_local_selectors; }
+
+ const etc1_global_selector_codebook* get_global_sel_codebook() const { return m_pGlobal_sel_codebook; }
+
+ private:
+ const basisu_lowlevel_etc1s_transcoder* m_pGlobal_codebook;
+
+ endpoint_vec m_local_endpoints;
+ selector_vec m_local_selectors;
const etc1_global_selector_codebook *m_pGlobal_sel_codebook;
@@ -487,6 +497,14 @@
void* pOutput_blocks, block_format fmt,
uint32_t block_stride_in_bytes, uint32_t output_row_pitch_in_blocks_or_pixels);
+ void set_global_codebooks(const basisu_lowlevel_etc1s_transcoder* pGlobal_codebook) { m_lowlevel_etc1s_decoder.set_global_codebooks(pGlobal_codebook); }
+ const basisu_lowlevel_etc1s_transcoder* get_global_codebooks() const { return m_lowlevel_etc1s_decoder.get_global_codebooks(); }
+
+ const basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() const { return m_lowlevel_etc1s_decoder; }
+ basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() { return m_lowlevel_etc1s_decoder; }
+
+ const basisu_lowlevel_uastc_transcoder& get_lowlevel_uastc_decoder() const { return m_lowlevel_uastc_decoder; }
+ basisu_lowlevel_uastc_transcoder& get_lowlevel_uastc_decoder() { return m_lowlevel_uastc_decoder; }
private:
mutable basisu_lowlevel_etc1s_transcoder m_lowlevel_etc1s_decoder;
mutable basisu_lowlevel_uastc_transcoder m_lowlevel_uastc_decoder;
diff --git a/transcoder/basisu_transcoder_internal.h b/transcoder/basisu_transcoder_internal.h
index 28e7eeb..1bf5a16 100644
--- a/transcoder/basisu_transcoder_internal.h
+++ b/transcoder/basisu_transcoder_internal.h
@@ -694,6 +694,11 @@
{
color32 m_color5;
uint8_t m_inten5;
+ bool operator== (const endpoint& rhs) const
+ {
+ return (m_color5.r == rhs.m_color5.r) && (m_color5.g == rhs.m_color5.g) && (m_color5.b == rhs.m_color5.b) && (m_inten5 == rhs.m_inten5);
+ }
+ bool operator!= (const endpoint& rhs) const { return !(*this == rhs); }
};
struct selector
@@ -706,6 +711,17 @@
uint8_t m_lo_selector, m_hi_selector;
uint8_t m_num_unique_selectors;
+ bool operator== (const selector& rhs) const
+ {
+ return (m_selectors[0] == rhs.m_selectors[0]) &&
+ (m_selectors[1] == rhs.m_selectors[1]) &&
+ (m_selectors[2] == rhs.m_selectors[2]) &&
+ (m_selectors[3] == rhs.m_selectors[3]);
+ }
+ bool operator!= (const selector& rhs) const
+ {
+ return !(*this == rhs);
+ }
void init_flags()
{