diff --git a/.reuse/dep5 b/.reuse/dep5 index 8f0cb36..4b48c6c 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -4,10 +4,10 @@ Source: https://github.com/BinomialLLC/basis_universal # We have asked Binomial about REUSE compliance for their repo, see https://github.com/BinomialLLC/basis_universal/issues/165 Files: * -Copyright: 2019-2020 Binomial LLC +Copyright: 2019-2021 Binomial LLC License: Apache-2.0 -Files: encoder/apg_bmp.* CMakeLists.txt webgl/transcoder/CMakeLists.txt +Files: encoder/apg_bmp.* Copyright: 2019 Anton Gerdelan License: Apache-2.0 @@ -15,6 +15,14 @@ Files: encoder/basisu_astc_decomp.* Copyright: 2016 The Android Open Source Project License: Apache-2.0 +Files: encoder/cppspmd_sse.h +Copyright: Copyright 2016 Nicolas Guillemot +License: Apache-2.0 + Files: encoder/lodepng.* Copyright: 2005-2019 Lode Vandevenne License: Zlib + +Files: zstd/* +Copyright: 2016-present, Facebook, Inc. +License: BSD diff --git a/CMakeLists.txt b/CMakeLists.txt index b7ba452..78dd110 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,12 @@ cmake_minimum_required(VERSION 3.0) option(BUILD_X64 "build 64-bit" TRUE) option(STATIC "static linking" FALSE) option(SSE "SSE 4.1 support" FALSE) +option(ZSTD "ZSTD support for KTX2 transcoding/encoding" TRUE) message("Initial BUILD_X64=${BUILD_X64}") message("Initial CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") +message("Initial SSE=${SSE}") +message("Initial ZSTD=${ZSTD}") if( NOT CMAKE_BUILD_TYPE ) set( CMAKE_BUILD_TYPE Release ) @@ -26,6 +29,12 @@ else() message("SSE disabled") endif() +if (ZSTD) + message("Zstandard enabled") +else() + message("Zstandard disabled") +endif() + if (NOT MSVC) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g") @@ -108,6 +117,10 @@ set(BASISU_SRC_LIST ${COMMON_SRC_LIST} transcoder/basisu_transcoder.cpp ) +if (ZSTD) + set(BASISU_SRC_LIST ${BASISU_SRC_LIST} zstd/zstd.c) +endif() + if (APPLE) set(BIN_DIRECTORY "bin_osx") else() @@ -118,6 +131,12 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${BIN_DIRECTORY}) add_executable(basisu ${BASISU_SRC_LIST}) +if (ZSTD) + target_compile_definitions(basisu PRIVATE BASISD_SUPPORT_KTX2_ZSTD=1) +else() + target_compile_definitions(basisu PRIVATE BASISD_SUPPORT_KTX2_ZSTD=0) +endif() + if (NOT MSVC) target_link_libraries(basisu m pthread) endif() diff --git a/basisu.vcxproj b/basisu.vcxproj index 7ec572f..cd889b1 100644 --- a/basisu.vcxproj +++ b/basisu.vcxproj @@ -104,7 +104,7 @@ ispc_texcomp true true - _MBCS;%(PreprocessorDefinitions);BASISU_SUPPORT_SSE=1 + _MBCS;%(PreprocessorDefinitions);BASISU_SUPPORT_SSE=1; Console @@ -140,7 +140,7 @@ true true ispc_texcomp - NDEBUG;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions);BASISU_SUPPORT_SSE=1 + NDEBUG;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions);BASISU_SUPPORT_SSE=1; false true AnySuitable @@ -175,6 +175,7 @@ + diff --git a/basisu.vcxproj.filters b/basisu.vcxproj.filters index 6259a84..498c859 100644 --- a/basisu.vcxproj.filters +++ b/basisu.vcxproj.filters @@ -62,6 +62,9 @@ encoder + + encoder + diff --git a/basisu_tool.cpp b/basisu_tool.cpp index b8654a9..b201ff4 100644 --- a/basisu_tool.cpp +++ b/basisu_tool.cpp @@ -29,15 +29,18 @@ #include "encoder/basisu_ssim.h" #define MINIZ_HEADER_FILE_ONLY +#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES #include "encoder/basisu_miniz.h" // Set BASISU_CATCH_EXCEPTIONS if you want exceptions to crash the app, otherwise main() catches them. -#define BASISU_CATCH_EXCEPTIONS 0 +#ifndef BASISU_CATCH_EXCEPTIONS + #define BASISU_CATCH_EXCEPTIONS 0 +#endif using namespace basisu; using namespace buminiz; -#define BASISU_TOOL_VERSION "1.14" +#define BASISU_TOOL_VERSION "1.15" enum tool_mode { @@ -71,6 +74,12 @@ static void print_usage() "Filenames prefixed with a @ symbol are read as filename listing files. Listing text files specify which actual filenames to process (one filename per line).\n" "\n" "Options:\n" + " -ktx2: Write .KTX2 ETC1S/UASTC files instead of .basis files. By default, UASTC files will be compressed using Zstandard unless -ktx2_no_zstandard is specified.\n" + " -ktx2_no_zstandard: Don't compress UASTC texture data using Zstandard, store it uncompressed instead.\n" + " -ktx2_zstandard_level X: Set ZStandard compression level to X (see Zstandard documentation, default level is 6)\n" + " -ktx2_animdata_duration X: Set KTX2animData duration field to integer value X (only valid/useful for -tex_type video, default is 1)\n" + " -ktx2_animdata_timescale X: Set KTX2animData timescale field to integer value X (only valid/useful for -tex_type video, default is 15)\n" + " -ktx2_animdata_loopcount X: Set KTX2animData loopcount field to integer value X (only valid/useful for -tex_type video, default is 0)\n" " -file filename.png/bmp/tga/jpg: Input image filename, multiple images are OK, use -file X for each input filename (prefixing input filenames with -file is optional)\n" " -alpha_file filename.png/bmp/tga/jpg: Input alpha image filename, multiple images are OK, use -file X for each input filename (must be paired with -file), images converted to REC709 grayscale and used as input alpha\n" " -multifile_printf: printf() format strint to use to compose multiple filenames\n" @@ -86,7 +95,7 @@ static void print_usage() " -tex_type <2d, 2darray, 3d, video, cubemap>: Set Basis file header's texture type field. Cubemap arrays require multiples of 6 images, in X+, X-, Y+, Y-, Z+, Z- order, each image must be the same resolutions.\n" " 2d=arbitrary 2D images, 2darray=2D array, 3D=volume texture slices, video=video frames, cubemap=array of faces. For 2darray/3d/cubemaps/video, each source image's dimensions and # of mipmap levels must be the same.\n" " 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" + " -framerate X: Set framerate in .basis 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 ETC1S encoding speed vs. quality tradeoff. Range is 0-6, default is 1. Higher values=MUCH slower, but slightly higher quality. Higher levels intended for videos. Use -q first!\n" " -fuzz_testing: Use with -validate: Disables CRC16 validation of file contents before transcoding\n" @@ -250,6 +259,12 @@ class command_line_params public: command_line_params() : m_mode(cDefault), + m_ktx2_mode(false), + m_ktx2_zstandard(true), + m_ktx2_zstandard_level(6), + m_ktx2_animdata_duration(1), + m_ktx2_animdata_timescale(15), + m_ktx2_animdata_loopcount(0), m_multifile_first(0), m_multifile_num(0), m_individual(false), @@ -273,7 +288,39 @@ public: #define REMAINING_ARGS_CHECK(n) if (num_remaining_args < (n)) { error_printf("Error: Expected %u values to follow %s!\n", n, pArg); return false; } - if (strcasecmp(pArg, "-compress") == 0) + if (strcasecmp(pArg, "-ktx2") == 0) + { + m_ktx2_mode = true; + } + else if (strcasecmp(pArg, "-ktx2_no_zstandard") == 0) + { + m_ktx2_zstandard = false; + } + else if (strcasecmp(pArg, "-ktx2_zstandard_level") == 0) + { + REMAINING_ARGS_CHECK(1); + m_ktx2_zstandard_level = atoi(arg_v[arg_index + 1]); + arg_count++; + } + else if (strcasecmp(pArg, "-ktx2_animdata_duration") == 0) + { + REMAINING_ARGS_CHECK(1); + m_ktx2_animdata_duration = atoi(arg_v[arg_index + 1]); + arg_count++; + } + else if (strcasecmp(pArg, "-ktx2_animdata_timescale") == 0) + { + REMAINING_ARGS_CHECK(1); + m_ktx2_animdata_timescale = atoi(arg_v[arg_index + 1]); + arg_count++; + } + else if (strcasecmp(pArg, "-ktx2_animdata_loopcount") == 0) + { + REMAINING_ARGS_CHECK(1); + m_ktx2_animdata_loopcount = atoi(arg_v[arg_index + 1]); + arg_count++; + } + else if (strcasecmp(pArg, "-compress") == 0) m_mode = cCompress; else if (strcasecmp(pArg, "-compare") == 0) m_mode = cCompare; @@ -713,6 +760,13 @@ public: basis_compressor_params m_comp_params; tool_mode m_mode; + + bool m_ktx2_mode; + bool m_ktx2_zstandard; + int m_ktx2_zstandard_level; + uint32_t m_ktx2_animdata_duration; + uint32_t m_ktx2_animdata_timescale; + uint32_t m_ktx2_animdata_loopcount; basisu::vector m_input_filenames; basisu::vector m_input_alpha_filenames; @@ -747,6 +801,7 @@ static bool expand_multifile(command_line_params &opts) } std::string fmt(opts.m_multifile_printf); + // Workaround for MSVC debugger issues. Questionable to leave in here. size_t x = fmt.find_first_of('!'); if (x != std::string::npos) fmt[x] = '%'; @@ -851,7 +906,7 @@ static bool compress_mode(command_line_params &opts) 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()) { @@ -898,6 +953,41 @@ static bool compress_mode(command_line_params &opts) basis_compressor_params ¶ms = opts.m_comp_params; + if (opts.m_ktx2_mode) + { + params.m_create_ktx2_file = true; + if (opts.m_ktx2_zstandard) + params.m_ktx2_uastc_supercompression = basist::KTX2_SS_ZSTANDARD; + else + params.m_ktx2_uastc_supercompression = basist::KTX2_SS_NONE; + + params.m_ktx2_srgb_transfer_func = opts.m_comp_params.m_perceptual; + + if (params.m_tex_type == basist::basis_texture_type::cBASISTexTypeVideoFrames) + { + // Create KTXanimData key value entry + // TODO: Move this to basisu_comp.h + basist::ktx2_transcoder::key_value kv; + + const char* pAD = "KTXanimData"; + kv.m_key.resize(strlen(pAD) + 1); + strcpy((char*)kv.m_key.data(), pAD); + + basist::ktx2_animdata ad; + ad.m_duration = opts.m_ktx2_animdata_duration; + ad.m_timescale = opts.m_ktx2_animdata_timescale; + ad.m_loopcount = opts.m_ktx2_animdata_loopcount; + + kv.m_value.resize(sizeof(ad)); + memcpy(kv.m_value.data(), &ad, sizeof(ad)); + + params.m_ktx2_key_values.push_back(kv); + } + + // TODO- expose this to command line. + params.m_ktx2_zstd_supercompression_level = opts.m_ktx2_zstandard_level; + } + params.m_read_source_images = true; params.m_write_output_basis_files = true; params.m_pSel_codebook = &sel_codebook; @@ -957,7 +1047,11 @@ static bool compress_mode(command_line_params &opts) string_get_filename(opts.m_input_filenames[file_index].c_str(), filename); string_remove_extension(filename); - filename += ".basis"; + + if (opts.m_ktx2_mode) + filename += ".ktx2"; + else + filename += ".basis"; if (opts.m_output_path.size()) string_combine_path(filename, opts.m_output_path.c_str(), filename.c_str()); @@ -990,7 +1084,9 @@ static bool compress_mode(command_line_params &opts) if (ec == basis_compressor::cECSuccess) { - printf("Compression succeeded to file \"%s\" size %i bytes in %3.3f secs\n", params.m_out_filename.c_str(), (int)c.get_output_basis_file().size(), tm.get_elapsed_secs()); + printf("Compression succeeded to file \"%s\" size %u bytes in %3.3f secs\n", params.m_out_filename.c_str(), + opts.m_ktx2_mode ? c.get_output_ktx2_file().size() : c.get_output_basis_file().size(), + tm.get_elapsed_secs()); } else { @@ -1031,6 +1127,9 @@ static bool compress_mode(command_line_params &opts) case basis_compressor::cECFailedUASTCRDOPostProcess: error_printf("Compressor failed during the UASTC post process step!\n"); break; + case basis_compressor::cECFailedCreateKTX2File: + error_printf("Compressor failed creating KTX2 file data!\n"); + break; default: error_printf("basis_compress::process() failed!\n"); break; @@ -1129,6 +1228,1037 @@ static bool compress_mode(command_line_params &opts) return true; } +static bool unpack_and_validate_ktx2_file( + uint32_t file_index, + const std::string& base_filename, + uint8_vec& ktx2_file_data, + command_line_params& opts, + FILE* pCSV_file, + basis_data* pGlobal_codebook_data, + basist::etc1_global_selector_codebook& sel_codebook, + uint32_t& total_unpack_warnings, + uint32_t& total_pvrtc_nonpow2_warnings) +{ + // TODO + (void)pCSV_file; + (void)file_index; + + const bool validate_flag = (opts.m_mode == cValidate); + + basist::ktx2_transcoder dec(&sel_codebook); + + if (!dec.init(ktx2_file_data.data(), ktx2_file_data.size())) + { + error_printf("ktx2_transcoder::init() failed! File either uses an unsupported feature, is invalid, was corrupted, or this is a bug.\n"); + return false; + } + + if (!dec.start_transcoding()) + { + error_printf("ktx2_transcoder::start_transcoding() failed! File either uses an unsupported feature, is invalid, was corrupted, or this is a bug.\n"); + return false; + } + + printf("Resolution: %ux%u\n", dec.get_width(), dec.get_height()); + printf("Mipmap Levels: %u\n", dec.get_levels()); + printf("Texture Array Size (layers): %u\n", dec.get_layers()); + printf("Total Faces: %u (%s)\n", dec.get_faces(), (dec.get_faces() == 6) ? "CUBEMAP" : "2D"); + printf("Is Texture Video: %u\n", dec.is_video()); + + const bool is_etc1s = dec.get_format() == basist::basis_tex_format::cETC1S; + printf("Supercompression Format: %s\n", is_etc1s ? "ETC1S" : "UASTC"); + + printf("Supercompression Scheme: "); + switch (dec.get_header().m_supercompression_scheme) + { + case basist::KTX2_SS_NONE: printf("NONE\n"); break; + case basist::KTX2_SS_BASISLZ: printf("BASISLZ\n"); break; + case basist::KTX2_SS_ZSTANDARD: printf("ZSTANDARD\n"); break; + default: + error_printf("Invalid/unknown/unsupported\n"); + return false; + } + + printf("Has Alpha: %u\n", (uint32_t)dec.get_has_alpha()); + + printf("\nData Format Descriptor (DFD):\n"); + printf("DFD length in bytes: %u\n", dec.get_dfd().size()); + printf("DFD color model: %u\n", dec.get_dfd_color_model()); + printf("DFD color primaries: %u (%s)\n", dec.get_dfd_color_primaries(), basist::ktx2_get_df_color_primaries_str(dec.get_dfd_color_primaries())); + printf("DFD transfer func: %u (%s)\n", dec.get_dfd_transfer_func(), + (dec.get_dfd_transfer_func() == basist::KTX2_KHR_DF_TRANSFER_LINEAR) ? "LINEAR" : ((dec.get_dfd_transfer_func() == basist::KTX2_KHR_DF_TRANSFER_SRGB) ? "SRGB" : "?")); + printf("DFD flags: %u\n", dec.get_dfd_flags()); + printf("DFD samples: %u\n", dec.get_dfd_total_samples()); + if (is_etc1s) + { + printf("DFD chan0: %s\n", basist::ktx2_get_etc1s_df_channel_id_str(dec.get_dfd_channel_id0())); + if (dec.get_dfd_total_samples() == 2) + printf("DFD chan1: %s\n", basist::ktx2_get_etc1s_df_channel_id_str(dec.get_dfd_channel_id1())); + } + else + printf("DFD chan0: %s\n", basist::ktx2_get_uastc_df_channel_id_str(dec.get_dfd_channel_id0())); + + printf("DFD hex values:\n"); + for (uint32_t i = 0; i < dec.get_dfd().size(); i++) + { + if (i) + printf(","); + printf("0x%X", dec.get_dfd()[i]); + } + printf("\n\n"); + + + printf("Total key values: %u\n", dec.get_key_values().size()); + for (uint32_t i = 0; i < dec.get_key_values().size(); i++) + { + printf("%u. Key: \"%s\", Value length in bytes: %u", i, (const char*)dec.get_key_values()[i].m_key.data(), dec.get_key_values()[i].m_value.size()); + + if (dec.get_key_values()[i].m_value.size() > 256) + continue; + + bool is_ascii = true; + for (uint32_t j = 0; j < dec.get_key_values()[i].m_value.size(); j++) + { + uint8_t c = dec.get_key_values()[i].m_value[j]; + if (!( + ((c >= ' ') && (c < 0x80)) || + ((j == dec.get_key_values()[i].m_value.size() - 1) && (!c)) + )) + { + is_ascii = false; + break; + } + } + + if (is_ascii) + { + uint8_vec s(dec.get_key_values()[i].m_value); + s.push_back(0); + printf(" Value String: \"%s\"", (const char *)s.data()); + } + else + { + printf(" Value Bytes: "); + for (uint32_t j = 0; j < dec.get_key_values()[i].m_value.size(); j++) + { + if (j) + printf(","); + printf("0x%X", dec.get_key_values()[i].m_value[j]); + } + } + printf("\n"); + } + + if (is_etc1s) + { + printf("ETC1S header:\n"); + + printf("Endpoint Count: %u, Selector Count: %u, Endpoint Length: %u, Selector Length: %u, Tables Length: %u, Extended Length: %u\n", + (uint32_t)dec.get_etc1s_header().m_endpoint_count, (uint32_t)dec.get_etc1s_header().m_selector_count, + (uint32_t)dec.get_etc1s_header().m_endpoints_byte_length, (uint32_t)dec.get_etc1s_header().m_selectors_byte_length, + (uint32_t)dec.get_etc1s_header().m_tables_byte_length, (uint32_t)dec.get_etc1s_header().m_extended_byte_length); + + printf("Total ETC1S image descs: %u\n", dec.get_etc1s_image_descs().size()); + for (uint32_t i = 0; i < dec.get_etc1s_image_descs().size(); i++) + { + printf("%u. Flags: 0x%X, RGB Ofs: %u Len: %u, Alpha Ofs: %u, Len: %u\n", i, + (uint32_t)dec.get_etc1s_image_descs()[i].m_image_flags, + (uint32_t)dec.get_etc1s_image_descs()[i].m_rgb_slice_byte_offset, (uint32_t)dec.get_etc1s_image_descs()[i].m_rgb_slice_byte_length, + (uint32_t)dec.get_etc1s_image_descs()[i].m_alpha_slice_byte_offset, (uint32_t)dec.get_etc1s_image_descs()[i].m_alpha_slice_byte_length); + } + } + + printf("Levels:\n"); + for (uint32_t i = 0; i < dec.get_levels(); i++) + { + printf("%u. Offset: %llu, Length: %llu, Uncompressed Length: %llu\n", + i, (long long unsigned int)dec.get_level_index()[i].m_byte_offset, + (long long unsigned int)dec.get_level_index()[i].m_byte_length, + (long long unsigned int)dec.get_level_index()[i].m_uncompressed_byte_length); + } + + if (opts.m_mode == cInfo) + { + return true; + } + + // gpu_images[format][face][layer][level] + + basisu::vector< gpu_image_vec > gpu_images[(int)basist::transcoder_texture_format::cTFTotalTextureFormats][6]; + + int first_format = 0; + int last_format = (int)basist::transcoder_texture_format::cTFTotalTextureFormats; + + if (opts.m_etc1_only) + { + first_format = (int)basist::transcoder_texture_format::cTFETC1_RGB; + last_format = first_format + 1; + } + + const uint32_t total_layers = maximum(1, dec.get_layers()); + + for (int format_iter = first_format; format_iter < last_format; format_iter++) + { + basist::transcoder_texture_format tex_fmt = static_cast(format_iter); + + if (basist::basis_transcoder_format_is_uncompressed(tex_fmt)) + continue; + + if (!basis_is_format_supported(tex_fmt, dec.get_format())) + continue; + + if (tex_fmt == basist::transcoder_texture_format::cTFBC7_ALT) + continue; + + for (uint32_t face_index = 0; face_index < dec.get_faces(); face_index++) + { + gpu_images[(int)tex_fmt][face_index].resize(total_layers); + + for (uint32_t layer_index = 0; layer_index < total_layers; layer_index++) + gpu_images[(int)tex_fmt][face_index][layer_index].resize(dec.get_levels()); + } + } + + // Now transcode the file to all supported texture formats and save mipmapped KTX files + for (int format_iter = first_format; format_iter < last_format; format_iter++) + { + const basist::transcoder_texture_format transcoder_tex_fmt = static_cast(format_iter); + + if (basist::basis_transcoder_format_is_uncompressed(transcoder_tex_fmt)) + continue; + if (!basis_is_format_supported(transcoder_tex_fmt, dec.get_format())) + continue; + if (transcoder_tex_fmt == basist::transcoder_texture_format::cTFBC7_ALT) + continue; + + for (uint32_t level_index = 0; level_index < dec.get_levels(); level_index++) + { + for (uint32_t layer_index = 0; layer_index < total_layers; layer_index++) + { + for (uint32_t face_index = 0; face_index < dec.get_faces(); face_index++) + { + basist::ktx2_image_level_info level_info; + + if (!dec.get_image_level_info(level_info, level_index, layer_index, face_index)) + { + error_printf("Failed retrieving image level information (%u %u %u)!\n", layer_index, level_index, face_index); + return false; + } + + if ((transcoder_tex_fmt == basist::transcoder_texture_format::cTFPVRTC1_4_RGB) || (transcoder_tex_fmt == basist::transcoder_texture_format::cTFPVRTC1_4_RGBA)) + { + if (!is_pow2(level_info.m_width) || !is_pow2(level_info.m_height)) + { + total_pvrtc_nonpow2_warnings++; + + printf("Warning: Will not transcode image %u level %u res %ux%u to PVRTC1 (one or more dimension is not a power of 2)\n", layer_index, level_index, level_info.m_width, level_info.m_height); + + // Can't transcode this image level to PVRTC because it's not a pow2 (we're going to support transcoding non-pow2 to the next larger pow2 soon) + continue; + } + } + + basisu::texture_format tex_fmt = basis_get_basisu_texture_format(transcoder_tex_fmt); + + gpu_image& gi = gpu_images[(int)transcoder_tex_fmt][face_index][layer_index][level_index]; + gi.init(tex_fmt, level_info.m_orig_width, level_info.m_orig_height); + + // Fill the buffer with psuedo-random bytes, to help more visibly detect cases where the transcoder fails to write to part of the output. + fill_buffer_with_random_bytes(gi.get_ptr(), gi.get_size_in_bytes()); + + uint32_t decode_flags = 0; + + if (!dec.transcode_image_level(level_index, layer_index, face_index, gi.get_ptr(), gi.get_total_blocks(), transcoder_tex_fmt, decode_flags)) + { + error_printf("Failed transcoding image level (%u %u %u %u)!\n", layer_index, level_index, face_index, format_iter); + return false; + } + + printf("Transcode of layer %u level %u face %u res %ux%u format %s succeeded\n", layer_index, level_index, face_index, level_info.m_orig_width, level_info.m_orig_height, basist::basis_get_format_name(transcoder_tex_fmt)); + } + + } // format_iter + + } // level_index + + } // image_info + + if (!validate_flag) + { + // Now write KTX files and unpack them to individual PNG's + const bool is_cubemap_array = (dec.get_faces() > 1) && (total_layers > 1); + + for (int format_iter = first_format; format_iter < last_format; format_iter++) + { + const basist::transcoder_texture_format transcoder_tex_fmt = static_cast(format_iter); + + if (basist::basis_transcoder_format_is_uncompressed(transcoder_tex_fmt)) + continue; + if (!basis_is_format_supported(transcoder_tex_fmt, dec.get_format())) + continue; + if (transcoder_tex_fmt == basist::transcoder_texture_format::cTFBC7_ALT) + continue; + + if ((!opts.m_no_ktx) && (is_cubemap_array)) + { + // No KTX tool that we know of supports cubemap arrays, so write individual cubemap files. + for (uint32_t layer_index = 0; layer_index < total_layers; layer_index++) + { + basisu::vector cubemap; + for (uint32_t face_index = 0; face_index < 6; face_index++) + cubemap.push_back(gpu_images[format_iter][face_index][layer_index]); + + std::string ktx_filename(base_filename + string_format("_transcoded_cubemap_%s_%u.ktx", basist::basis_get_format_name(transcoder_tex_fmt), layer_index)); + + if (!write_compressed_texture_file(ktx_filename.c_str(), cubemap, true)) + { + error_printf("Failed writing KTX file \"%s\"!\n", ktx_filename.c_str()); + return false; + } + printf("Wrote KTX file \"%s\"\n", ktx_filename.c_str()); + } + } + + for (uint32_t layer_index = 0; layer_index < total_layers; layer_index++) + { + for (uint32_t face_index = 0; face_index < dec.get_faces(); face_index++) + { + gpu_image_vec& gi = gpu_images[format_iter][face_index][layer_index]; + + if (!gi.size()) + continue; + + uint32_t level; + for (level = 0; level < gi.size(); level++) + if (!gi[level].get_total_blocks()) + break; + + if (level < gi.size()) + continue; + + if ((!opts.m_no_ktx) && (!is_cubemap_array)) + { + std::string ktx_filename(base_filename + string_format("_transcoded_%s_%04u.ktx", basist::basis_get_format_name(transcoder_tex_fmt), layer_index)); + if (!write_compressed_texture_file(ktx_filename.c_str(), gi)) + { + error_printf("Failed writing KTX file \"%s\"!\n", ktx_filename.c_str()); + return false; + } + printf("Wrote KTX file \"%s\"\n", ktx_filename.c_str()); + } + + for (uint32_t level_index = 0; level_index < gi.size(); level_index++) + { + basist::ktx2_image_level_info level_info; + + if (!dec.get_image_level_info(level_info, level_index, layer_index, face_index)) + { + error_printf("Failed retrieving image level information (%u %u %u)!\n", layer_index, level_index, face_index); + return false; + } + + image u; + if (!gi[level_index].unpack(u)) + { + printf("Warning: Failed unpacking GPU texture data (%u %u %u %u). Unpacking as much as possible.\n", format_iter, layer_index, level_index, face_index); + total_unpack_warnings++; + } + //u.crop(level_info.m_orig_width, level_info.m_orig_height); + + std::string rgb_filename; + if (gi.size() > 1) + rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, face_index, layer_index); + else + rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), face_index, layer_index); + 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()); + + if (transcoder_tex_fmt == basist::transcoder_texture_format::cTFFXT1_RGB) + { + std::string out_filename; + if (gi.size() > 1) + out_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%u_%04u.out", basist::basis_get_format_name(transcoder_tex_fmt), level_index, face_index, layer_index); + else + out_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%04u.out", basist::basis_get_format_name(transcoder_tex_fmt), face_index, layer_index); + if (!write_3dfx_out_file(out_filename.c_str(), gi[level_index])) + { + error_printf("Failed writing to OUT file \"%s\"\n", out_filename.c_str()); + return false; + } + printf("Wrote .OUT file \"%s\"\n", out_filename.c_str()); + } + + if (basis_transcoder_format_has_alpha(transcoder_tex_fmt)) + { + std::string a_filename; + if (gi.size() > 1) + a_filename = base_filename + string_format("_unpacked_a_%s_%u_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, face_index, layer_index); + else + a_filename = base_filename + string_format("_unpacked_a_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), face_index, layer_index); + if (!save_png(a_filename, u, cImageSaveGrayscale, 3)) + { + error_printf("Failed writing to PNG file \"%s\"\n", a_filename.c_str()); + 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, face_index, layer_index); + else + rgba_filename = base_filename + string_format("_unpacked_rgba_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), face_index, layer_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 + + } // face_index + + } // layer_index + + } // format_iter + + } // if (!validate_flag) + + return true; +} + +static bool unpack_and_validate_basis_file( + uint32_t file_index, + const std::string &base_filename, + uint8_vec &basis_file_data, + command_line_params& opts, + FILE *pCSV_file, + basis_data* pGlobal_codebook_data, + basist::etc1_global_selector_codebook &sel_codebook, + uint32_t &total_unpack_warnings, + uint32_t &total_pvrtc_nonpow2_warnings) +{ + const bool validate_flag = (opts.m_mode == cValidate); + + 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. + + // Validate the file - note this isn't necessary for transcoding + if (!dec.validate_file_checksums(&basis_file_data[0], (uint32_t)basis_file_data.size(), true)) + { + error_printf("File version is unsupported, or file failed one or more CRC checks!\n"); + + return false; + } + } + + printf("File version and CRC checks succeeded\n"); + + basist::basisu_file_info fileinfo; + if (!dec.get_file_info(&basis_file_data[0], (uint32_t)basis_file_data.size(), fileinfo)) + { + 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_file_data[0], (uint32_t)basis_file_data.size())); + + printf("File info:\n"); + printf(" Version: %X\n", fileinfo.m_version); + printf(" Total header size: %u\n", fileinfo.m_total_header_size); + printf(" Total selectors: %u\n", fileinfo.m_total_selectors); + printf(" Selector codebook size: %u\n", fileinfo.m_selector_codebook_size); + printf(" Total endpoints: %u\n", fileinfo.m_total_endpoints); + 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()); + printf(" Total images: %i\n", fileinfo.m_total_images); + printf(" Y Flipped: %u, Has alpha slices: %u\n", fileinfo.m_y_flipped, fileinfo.m_has_alpha_slices); + printf(" userdata0: 0x%X userdata1: 0x%X\n", fileinfo.m_userdata0, fileinfo.m_userdata1); + printf(" Per-image mipmap levels: "); + for (uint32_t i = 0; i < fileinfo.m_total_images; i++) + printf("%u ", fileinfo.m_image_mipmap_levels[i]); + printf("\n"); + + uint32_t total_texels = 0; + + printf("\nImage info:\n"); + for (uint32_t i = 0; i < fileinfo.m_total_images; i++) + { + basist::basisu_image_info ii; + if (!dec.get_image_info(&basis_file_data[0], (uint32_t)basis_file_data.size(), ii, i)) + { + error_printf("get_image_info() failed!\n"); + return false; + } + + printf("Image %u: MipLevels: %u OrigDim: %ux%u, BlockDim: %ux%u, FirstSlice: %u, HasAlpha: %u\n", i, ii.m_total_levels, ii.m_orig_width, ii.m_orig_height, + ii.m_num_blocks_x, ii.m_num_blocks_y, ii.m_first_slice_index, (uint32_t)ii.m_alpha_flag); + + total_texels += ii.m_width * ii.m_height; + } + + printf("\nSlice info:\n"); + + for (uint32_t i = 0; i < fileinfo.m_slice_info.size(); i++) + { + const basist::basisu_slice_info& sliceinfo = fileinfo.m_slice_info[i]; + printf("%u: OrigWidthHeight: %ux%u, BlockDim: %ux%u, TotalBlocks: %u, Compressed size: %u, Image: %u, Level: %u, UnpackedCRC16: 0x%X, alpha: %u, iframe: %i\n", + i, + sliceinfo.m_orig_width, sliceinfo.m_orig_height, + sliceinfo.m_num_blocks_x, sliceinfo.m_num_blocks_y, + sliceinfo.m_total_blocks, + sliceinfo.m_compressed_size, + sliceinfo.m_image_index, sliceinfo.m_level_index, + sliceinfo.m_unpacked_slice_crc16, + (uint32_t)sliceinfo.m_alpha_flag, + (uint32_t)sliceinfo.m_iframe_flag); + } + printf("\n"); + + size_t comp_size = 0; + void* pComp_data = tdefl_compress_mem_to_heap(&basis_file_data[0], basis_file_data.size(), &comp_size, TDEFL_MAX_PROBES_MASK);// TDEFL_DEFAULT_MAX_PROBES); + mz_free(pComp_data); + + const float basis_bits_per_texel = basis_file_data.size() * 8.0f / total_texels; + const float comp_bits_per_texel = comp_size * 8.0f / total_texels; + + printf("Original size: %u, bits per texel: %3.3f\nCompressed size (Deflate): %u, bits per texel: %3.3f\n", (uint32_t)basis_file_data.size(), basis_bits_per_texel, (uint32_t)comp_size, comp_bits_per_texel); + + if (opts.m_mode == cInfo) + { + return true; + } + + if ((fileinfo.m_etc1s) && (fileinfo.m_selector_codebook_size == 0) && (fileinfo.m_endpoint_codebook_size == 0)) + { + // File is ETC1S and uses global codebooks - make sure we loaded one + if (!pGlobal_codebook_data) + { + error_printf("ETC1S file uses global codebooks, but none were loaded (see the -use_global_codebooks option)\n"); + return false; + } + + if ((pGlobal_codebook_data->m_transcoder.get_lowlevel_etc1s_decoder().get_endpoints().size() != fileinfo.m_total_endpoints) || + (pGlobal_codebook_data->m_transcoder.get_lowlevel_etc1s_decoder().get_selectors().size() != fileinfo.m_total_selectors)) + { + error_printf("Supplied global codebook is not compatible with this file\n"); + return false; + } + } + + interval_timer tm; + tm.start(); + + if (!dec.start_transcoding(&basis_file_data[0], (uint32_t)basis_file_data.size())) + { + error_printf("start_transcoding() failed!\n"); + return false; + } + + const double start_transcoding_time_ms = tm.get_elapsed_ms(); + + printf("start_transcoding time: %3.3f ms\n", start_transcoding_time_ms); + + basisu::vector< gpu_image_vec > gpu_images[(int)basist::transcoder_texture_format::cTFTotalTextureFormats]; + + double total_format_transcoding_time_ms[(int)basist::transcoder_texture_format::cTFTotalTextureFormats]; + clear_obj(total_format_transcoding_time_ms); + + int first_format = 0; + int last_format = (int)basist::transcoder_texture_format::cTFTotalTextureFormats; + + if (opts.m_etc1_only) + { + first_format = (int)basist::transcoder_texture_format::cTFETC1_RGB; + last_format = first_format + 1; + } + + if ((pCSV_file) && (file_index == 0)) + { + std::string desc; + desc = "filename,basis_bitrate,comp_bitrate,images,levels,slices,start_transcoding_time,"; + for (int format_iter = first_format; format_iter < last_format; format_iter++) + { + const basist::transcoder_texture_format transcoder_tex_fmt = static_cast(format_iter); + + if (!basis_is_format_supported(transcoder_tex_fmt, fileinfo.m_tex_format)) + continue; + if (transcoder_tex_fmt == basist::transcoder_texture_format::cTFBC7_ALT) + continue; + + desc += std::string(basis_get_format_name(transcoder_tex_fmt)); + if (format_iter != last_format - 1) + desc += ","; + } + fprintf(pCSV_file, "%s\n", desc.c_str()); + } + + for (int format_iter = first_format; format_iter < last_format; format_iter++) + { + basist::transcoder_texture_format tex_fmt = static_cast(format_iter); + + 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); + + for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++) + gpu_images[(int)tex_fmt][image_index].resize(fileinfo.m_image_mipmap_levels[image_index]); + } + + // Now transcode the file to all supported texture formats and save mipmapped KTX files + for (int format_iter = first_format; format_iter < last_format; format_iter++) + { + const basist::transcoder_texture_format transcoder_tex_fmt = static_cast(format_iter); + + 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++) + { + for (uint32_t level_index = 0; level_index < fileinfo.m_image_mipmap_levels[image_index]; level_index++) + { + basist::basisu_image_level_info level_info; + + if (!dec.get_image_level_info(&basis_file_data[0], (uint32_t)basis_file_data.size(), level_info, image_index, level_index)) + { + error_printf("Failed retrieving image level information (%u %u)!\n", image_index, level_index); + return false; + } + + if ((transcoder_tex_fmt == basist::transcoder_texture_format::cTFPVRTC1_4_RGB) || (transcoder_tex_fmt == basist::transcoder_texture_format::cTFPVRTC1_4_RGBA)) + { + if (!is_pow2(level_info.m_width) || !is_pow2(level_info.m_height)) + { + total_pvrtc_nonpow2_warnings++; + + printf("Warning: Will not transcode image %u level %u res %ux%u to PVRTC1 (one or more dimension is not a power of 2)\n", image_index, level_index, level_info.m_width, level_info.m_height); + + // Can't transcode this image level to PVRTC because it's not a pow2 (we're going to support transcoding non-pow2 to the next larger pow2 soon) + continue; + } + } + + basisu::texture_format tex_fmt = basis_get_basisu_texture_format(transcoder_tex_fmt); + + gpu_image& gi = gpu_images[(int)transcoder_tex_fmt][image_index][level_index]; + gi.init(tex_fmt, level_info.m_orig_width, level_info.m_orig_height); + + // Fill the buffer with psuedo-random bytes, to help more visibly detect cases where the transcoder fails to write to part of the output. + fill_buffer_with_random_bytes(gi.get_ptr(), gi.get_size_in_bytes()); + + uint32_t decode_flags = 0; + + tm.start(); + + if (!dec.transcode_image_level(&basis_file_data[0], (uint32_t)basis_file_data.size(), image_index, level_index, gi.get_ptr(), gi.get_total_blocks(), transcoder_tex_fmt, decode_flags)) + { + error_printf("Failed transcoding image level (%u %u %u)!\n", image_index, level_index, format_iter); + return false; + } + + double total_transcode_time = tm.get_elapsed_ms(); + + total_format_transcoding_time_ms[format_iter] += total_transcode_time; + + 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); + + } // format_iter + + } // level_index + + } // image_info + + if (!validate_flag) + { + // Now write KTX files and unpack them to individual PNG's + + for (int format_iter = first_format; format_iter < last_format; format_iter++) + { + const basist::transcoder_texture_format transcoder_tex_fmt = static_cast(format_iter); + + 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)) + { + // No KTX tool that we know of supports cubemap arrays, so write individual cubemap files. + for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index += 6) + { + basisu::vector cubemap; + for (uint32_t i = 0; i < 6; i++) + cubemap.push_back(gpu_images[format_iter][image_index + i]); + + std::string ktx_filename(base_filename + string_format("_transcoded_cubemap_%s_%u.ktx", basist::basis_get_format_name(transcoder_tex_fmt), image_index / 6)); + if (!write_compressed_texture_file(ktx_filename.c_str(), cubemap, true)) + { + error_printf("Failed writing KTX file \"%s\"!\n", ktx_filename.c_str()); + return false; + } + printf("Wrote KTX file \"%s\"\n", ktx_filename.c_str()); + } + } + + for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++) + { + gpu_image_vec& gi = gpu_images[format_iter][image_index]; + + if (!gi.size()) + continue; + + uint32_t level; + for (level = 0; level < gi.size(); level++) + if (!gi[level].get_total_blocks()) + break; + + if (level < gi.size()) + continue; + + if ((!opts.m_no_ktx) && (fileinfo.m_tex_type != basist::cBASISTexTypeCubemapArray)) + { + std::string ktx_filename(base_filename + string_format("_transcoded_%s_%04u.ktx", basist::basis_get_format_name(transcoder_tex_fmt), image_index)); + if (!write_compressed_texture_file(ktx_filename.c_str(), gi)) + { + error_printf("Failed writing KTX file \"%s\"!\n", ktx_filename.c_str()); + return false; + } + printf("Wrote KTX file \"%s\"\n", ktx_filename.c_str()); + } + + for (uint32_t level_index = 0; level_index < gi.size(); level_index++) + { + basist::basisu_image_level_info level_info; + + if (!dec.get_image_level_info(&basis_file_data[0], (uint32_t)basis_file_data.size(), level_info, image_index, level_index)) + { + error_printf("Failed retrieving image level information (%u %u)!\n", image_index, level_index); + return false; + } + + image u; + if (!gi[level_index].unpack(u)) + { + printf("Warning: Failed unpacking GPU texture data (%u %u %u). Unpacking as much as possible.\n", format_iter, image_index, level_index); + total_unpack_warnings++; + } + //u.crop(level_info.m_orig_width, level_info.m_orig_height); + + std::string rgb_filename; + if (gi.size() > 1) + rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index); + else + rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index); + 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()); + + if (transcoder_tex_fmt == basist::transcoder_texture_format::cTFFXT1_RGB) + { + std::string out_filename; + if (gi.size() > 1) + out_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%04u.out", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index); + else + out_filename = base_filename + string_format("_unpacked_rgb_%s_%04u.out", basist::basis_get_format_name(transcoder_tex_fmt), image_index); + if (!write_3dfx_out_file(out_filename.c_str(), gi[level_index])) + { + error_printf("Failed writing to OUT file \"%s\"\n", out_filename.c_str()); + return false; + } + printf("Wrote .OUT file \"%s\"\n", out_filename.c_str()); + } + + if (basis_transcoder_format_has_alpha(transcoder_tex_fmt)) + { + std::string a_filename; + if (gi.size() > 1) + a_filename = base_filename + string_format("_unpacked_a_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index); + else + a_filename = base_filename + string_format("_unpacked_a_%s_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index); + if (!save_png(a_filename, u, cImageSaveGrayscale, 3)) + { + error_printf("Failed writing to PNG file \"%s\"\n", a_filename.c_str()); + 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 + + } // image_index + + } // format_iter + + } // if (!validate_flag) + + // Now unpack to RGBA using the transcoder itself to do the unpacking to raster images + for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++) + { + for (uint32_t level_index = 0; level_index < fileinfo.m_image_mipmap_levels[image_index]; level_index++) + { + const basist::transcoder_texture_format transcoder_tex_fmt = basist::transcoder_texture_format::cTFRGBA32; + + basist::basisu_image_level_info level_info; + + if (!dec.get_image_level_info(&basis_file_data[0], (uint32_t)basis_file_data.size(), level_info, image_index, level_index)) + { + error_printf("Failed retrieving image level information (%u %u)!\n", image_index, level_index); + return false; + } + + image img(level_info.m_orig_width, level_info.m_orig_height); + + fill_buffer_with_random_bytes(&img(0, 0), img.get_total_pixels() * sizeof(uint32_t)); + + tm.start(); + + if (!dec.transcode_image_level(&basis_file_data[0], (uint32_t)basis_file_data.size(), image_index, level_index, &img(0, 0).r, img.get_total_pixels(), transcoder_tex_fmt, 0, img.get_pitch(), nullptr, img.get_height())) + { + error_printf("Failed transcoding image level (%u %u %u)!\n", image_index, level_index, transcoder_tex_fmt); + return false; + } + + double total_transcode_time = tm.get_elapsed_ms(); + + total_format_transcoding_time_ms[(int)transcoder_tex_fmt] += total_transcode_time; + + 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)) + { + error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str()); + return false; + } + printf("Wrote PNG file \"%s\"\n", rgb_filename.c_str()); + + std::string a_filename(base_filename + string_format("_unpacked_a_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index)); + if (!save_png(a_filename, img, cImageSaveGrayscale, 3)) + { + error_printf("Failed writing to PNG file \"%s\"\n", a_filename.c_str()); + return false; + } + printf("Wrote PNG file \"%s\"\n", a_filename.c_str()); + } + + } // level_index + } // image_index + + // Now unpack to RGB565 using the transcoder itself to do the unpacking to raster images + for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++) + { + for (uint32_t level_index = 0; level_index < fileinfo.m_image_mipmap_levels[image_index]; level_index++) + { + const basist::transcoder_texture_format transcoder_tex_fmt = basist::transcoder_texture_format::cTFRGB565; + + basist::basisu_image_level_info level_info; + + if (!dec.get_image_level_info(&basis_file_data[0], (uint32_t)basis_file_data.size(), level_info, image_index, level_index)) + { + error_printf("Failed retrieving image level information (%u %u)!\n", image_index, level_index); + return false; + } + + basisu::vector packed_img(level_info.m_orig_width * level_info.m_orig_height); + + fill_buffer_with_random_bytes(&packed_img[0], packed_img.size() * sizeof(uint16_t)); + + tm.start(); + + if (!dec.transcode_image_level(&basis_file_data[0], (uint32_t)basis_file_data.size(), image_index, level_index, &packed_img[0], (uint32_t)packed_img.size(), transcoder_tex_fmt, 0, level_info.m_orig_width, nullptr, level_info.m_orig_height)) + { + error_printf("Failed transcoding image level (%u %u %u)!\n", image_index, level_index, transcoder_tex_fmt); + return false; + } + + double total_transcode_time = tm.get_elapsed_ms(); + + total_format_transcoding_time_ms[(int)transcoder_tex_fmt] += total_transcode_time; + + image img(level_info.m_orig_width, level_info.m_orig_height); + for (uint32_t y = 0; y < level_info.m_orig_height; y++) + { + for (uint32_t x = 0; x < level_info.m_orig_width; x++) + { + const uint16_t p = packed_img[x + y * level_info.m_orig_width]; + uint32_t r = p >> 11, g = (p >> 5) & 63, b = p & 31; + r = (r << 3) | (r >> 2); + g = (g << 2) | (g >> 4); + b = (b << 3) | (b >> 2); + img(x, y).set(r, g, b, 255); + } + } + + 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)) + { + error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str()); + return false; + } + printf("Wrote PNG file \"%s\"\n", rgb_filename.c_str()); + } + + } // level_index + } // image_index + + // Now unpack to RGBA4444 using the transcoder itself to do the unpacking to raster images + uint32_t max_mipmap_levels = 0; + for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++) + { + for (uint32_t level_index = 0; level_index < fileinfo.m_image_mipmap_levels[image_index]; level_index++) + { + max_mipmap_levels = basisu::maximum(max_mipmap_levels, fileinfo.m_image_mipmap_levels[image_index]); + + const basist::transcoder_texture_format transcoder_tex_fmt = basist::transcoder_texture_format::cTFRGBA4444; + + basist::basisu_image_level_info level_info; + + if (!dec.get_image_level_info(&basis_file_data[0], (uint32_t)basis_file_data.size(), level_info, image_index, level_index)) + { + error_printf("Failed retrieving image level information (%u %u)!\n", image_index, level_index); + return false; + } + + basisu::vector packed_img(level_info.m_orig_width * level_info.m_orig_height); + + fill_buffer_with_random_bytes(&packed_img[0], packed_img.size() * sizeof(uint16_t)); + + tm.start(); + + if (!dec.transcode_image_level(&basis_file_data[0], (uint32_t)basis_file_data.size(), image_index, level_index, &packed_img[0], (uint32_t)packed_img.size(), transcoder_tex_fmt, 0, level_info.m_orig_width, nullptr, level_info.m_orig_height)) + { + error_printf("Failed transcoding image level (%u %u %u)!\n", image_index, level_index, transcoder_tex_fmt); + return false; + } + + double total_transcode_time = tm.get_elapsed_ms(); + + total_format_transcoding_time_ms[(int)transcoder_tex_fmt] += total_transcode_time; + + image img(level_info.m_orig_width, level_info.m_orig_height); + for (uint32_t y = 0; y < level_info.m_orig_height; y++) + { + for (uint32_t x = 0; x < level_info.m_orig_width; x++) + { + const uint16_t p = packed_img[x + y * level_info.m_orig_width]; + uint32_t r = p >> 12, g = (p >> 8) & 15, b = (p >> 4) & 15, a = p & 15; + r = (r << 4) | r; + g = (g << 4) | g; + b = (b << 4) | b; + a = (a << 4) | a; + img(x, y).set(r, g, b, a); + } + } + + 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)) + { + error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str()); + return false; + } + printf("Wrote PNG file \"%s\"\n", rgb_filename.c_str()); + + std::string a_filename(base_filename + string_format("_unpacked_a_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index)); + if (!save_png(a_filename, img, cImageSaveGrayscale, 3)) + { + error_printf("Failed writing to PNG file \"%s\"\n", a_filename.c_str()); + return false; + } + printf("Wrote PNG file \"%s\"\n", a_filename.c_str()); + } + + } // level_index + } // image_index + + if (pCSV_file) + { + fprintf(pCSV_file, "%s, %3.3f, %3.3f, %u, %u, %u, %3.3f, ", + base_filename.c_str(), + basis_bits_per_texel, + comp_bits_per_texel, + fileinfo.m_total_images, + max_mipmap_levels, + (uint32_t)fileinfo.m_slice_info.size(), + start_transcoding_time_ms); + + for (int format_iter = first_format; format_iter < last_format; format_iter++) + { + const basist::transcoder_texture_format transcoder_tex_fmt = static_cast(format_iter); + + if (!basis_is_format_supported(transcoder_tex_fmt, fileinfo.m_tex_format)) + continue; + if (transcoder_tex_fmt == basist::transcoder_texture_format::cTFBC7_ALT) + continue; + + fprintf(pCSV_file, "%3.3f", total_format_transcoding_time_ms[format_iter]); + if (format_iter != (last_format - 1)) + fprintf(pCSV_file, ","); + } + fprintf(pCSV_file, "\n"); + } + + return true; +} + static bool unpack_and_validate_mode(command_line_params &opts) { const bool validate_flag = (opts.m_mode == cValidate); @@ -1176,8 +2306,8 @@ static bool unpack_and_validate_mode(command_line_params &opts) std::string base_filename; string_split_path(pInput_filename, nullptr, nullptr, &base_filename, nullptr); - uint8_vec basis_data; - if (!basisu::read_file_to_vec(pInput_filename, basis_data)) + uint8_vec file_data; + if (!basisu::read_file_to_vec(pInput_filename, file_data)) { error_printf("Failed reading file \"%s\"\n", pInput_filename); if (pCSV_file) fclose(pCSV_file); @@ -1185,9 +2315,7 @@ static bool unpack_and_validate_mode(command_line_params &opts) return false; } - printf("Input file \"%s\"\n", pInput_filename); - - if (!basis_data.size()) + if (!file_data.size()) { error_printf("File is empty!\n"); if (pCSV_file) fclose(pCSV_file); @@ -1195,673 +2323,60 @@ static bool unpack_and_validate_mode(command_line_params &opts) return false; } - if (basis_data.size() > UINT32_MAX) + if (file_data.size() > UINT32_MAX) { 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) + + bool is_ktx2 = false; + if (file_data.size() >= sizeof(basist::g_ktx2_file_identifier)) { - 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. - - // Validate the file - note this isn't necessary for transcoding - if (!dec.validate_file_checksums(&basis_data[0], (uint32_t)basis_data.size(), true)) - { - 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; - } + is_ktx2 = (memcmp(file_data.data(), basist::g_ktx2_file_identifier, sizeof(basist::g_ktx2_file_identifier)) == 0); } - printf("File version and CRC checks succeeded\n"); + printf("Input file \"%s\", KTX2: %u\n", pInput_filename, is_ktx2); - basist::basisu_file_info fileinfo; - if (!dec.get_file_info(&basis_data[0], (uint32_t)basis_data.size(), fileinfo)) + bool status; + if (is_ktx2) { - error_printf("Failed retrieving Basis file information!\n"); - if (pCSV_file) fclose(pCSV_file); - delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr; + status = unpack_and_validate_ktx2_file( + file_index, + base_filename, + file_data, + opts, + pCSV_file, + pGlobal_codebook_data, + sel_codebook, + total_unpack_warnings, + total_pvrtc_nonpow2_warnings); + } + else + { + status = unpack_and_validate_basis_file( + file_index, + base_filename, + file_data, + opts, + pCSV_file, + pGlobal_codebook_data, + sel_codebook, + total_unpack_warnings, + total_pvrtc_nonpow2_warnings); + } + + if (!status) + { + if (pCSV_file) + fclose(pCSV_file); + + delete pGlobal_codebook_data; + pGlobal_codebook_data = nullptr; + 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())); - - printf("File info:\n"); - printf(" Version: %X\n", fileinfo.m_version); - printf(" Total header size: %u\n", fileinfo.m_total_header_size); - printf(" Total selectors: %u\n", fileinfo.m_total_selectors); - printf(" Selector codebook size: %u\n", fileinfo.m_selector_codebook_size); - printf(" Total endpoints: %u\n", fileinfo.m_total_endpoints); - 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()); - printf(" Total images: %i\n", fileinfo.m_total_images); - printf(" Y Flipped: %u, Has alpha slices: %u\n", fileinfo.m_y_flipped, fileinfo.m_has_alpha_slices); - printf(" userdata0: 0x%X userdata1: 0x%X\n", fileinfo.m_userdata0, fileinfo.m_userdata1); - printf(" Per-image mipmap levels: "); - for (uint32_t i = 0; i < fileinfo.m_total_images; i++) - printf("%u ", fileinfo.m_image_mipmap_levels[i]); - printf("\n"); - - uint32_t total_texels = 0; - - printf("\nImage info:\n"); - for (uint32_t i = 0; i < fileinfo.m_total_images; i++) - { - basist::basisu_image_info ii; - if (!dec.get_image_info(&basis_data[0], (uint32_t)basis_data.size(), ii, i)) - { - error_printf("get_image_info() failed!\n"); - if (pCSV_file) fclose(pCSV_file); - delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr; - return false; - } - - printf("Image %u: MipLevels: %u OrigDim: %ux%u, BlockDim: %ux%u, FirstSlice: %u, HasAlpha: %u\n", i, ii.m_total_levels, ii.m_orig_width, ii.m_orig_height, - ii.m_num_blocks_x, ii.m_num_blocks_y, ii.m_first_slice_index, (uint32_t)ii.m_alpha_flag); - - total_texels += ii.m_width * ii.m_height; - } - - printf("\nSlice info:\n"); - - for (uint32_t i = 0; i < fileinfo.m_slice_info.size(); i++) - { - const basist::basisu_slice_info& sliceinfo = fileinfo.m_slice_info[i]; - printf("%u: OrigWidthHeight: %ux%u, BlockDim: %ux%u, TotalBlocks: %u, Compressed size: %u, Image: %u, Level: %u, UnpackedCRC16: 0x%X, alpha: %u, iframe: %i\n", - i, - sliceinfo.m_orig_width, sliceinfo.m_orig_height, - sliceinfo.m_num_blocks_x, sliceinfo.m_num_blocks_y, - sliceinfo.m_total_blocks, - sliceinfo.m_compressed_size, - sliceinfo.m_image_index, sliceinfo.m_level_index, - sliceinfo.m_unpacked_slice_crc16, - (uint32_t)sliceinfo.m_alpha_flag, - (uint32_t)sliceinfo.m_iframe_flag); - } - printf("\n"); - - size_t comp_size = 0; - void* pComp_data = tdefl_compress_mem_to_heap(&basis_data[0], basis_data.size(), &comp_size, TDEFL_MAX_PROBES_MASK);// TDEFL_DEFAULT_MAX_PROBES); - mz_free(pComp_data); - - const float basis_bits_per_texel = basis_data.size() * 8.0f / total_texels; - const float comp_bits_per_texel = comp_size * 8.0f / total_texels; - - printf("Original size: %u, bits per texel: %3.3f\nCompressed size (Deflate): %u, bits per texel: %3.3f\n", (uint32_t)basis_data.size(), basis_bits_per_texel, (uint32_t)comp_size, comp_bits_per_texel); - - if (opts.m_mode == cInfo) - { - if (pCSV_file) fclose(pCSV_file); - delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr; - return true; - } - - if ((fileinfo.m_etc1s) && (fileinfo.m_selector_codebook_size == 0) && (fileinfo.m_endpoint_codebook_size == 0)) - { - // File is ETC1S and uses global codebooks - make sure we loaded one - if (!pGlobal_codebook_data) - { - error_printf("ETC1S file uses global codebooks, but none were loaded (see the -use_global_codebooks option)\n"); - if (pCSV_file) fclose(pCSV_file); - delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr; - return false; - } - - if ((pGlobal_codebook_data->m_transcoder.get_lowlevel_etc1s_decoder().get_endpoints().size() != fileinfo.m_total_endpoints) || - (pGlobal_codebook_data->m_transcoder.get_lowlevel_etc1s_decoder().get_selectors().size() != fileinfo.m_total_selectors)) - { - error_printf("Supplied global codebook is not compatible with this file\n"); - if (pCSV_file) fclose(pCSV_file); - delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr; - return false; - } - } - - interval_timer tm; - tm.start(); - - if (!dec.start_transcoding(&basis_data[0], (uint32_t)basis_data.size())) - { - error_printf("start_transcoding() failed!\n"); - if (pCSV_file) fclose(pCSV_file); - delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr; - return false; - } - - const double start_transcoding_time_ms = tm.get_elapsed_ms(); - - printf("start_transcoding time: %3.3f ms\n", start_transcoding_time_ms); - - basisu::vector< gpu_image_vec > gpu_images[(int)basist::transcoder_texture_format::cTFTotalTextureFormats]; - - double total_format_transcoding_time_ms[(int)basist::transcoder_texture_format::cTFTotalTextureFormats]; - clear_obj(total_format_transcoding_time_ms); - - int first_format = 0; - int last_format = (int)basist::transcoder_texture_format::cTFTotalTextureFormats; - - if (opts.m_etc1_only) - { - first_format = (int)basist::transcoder_texture_format::cTFETC1_RGB; - last_format = first_format + 1; - } - - if ((pCSV_file) && (file_index == 0)) - { - std::string desc; - desc = "filename,basis_bitrate,comp_bitrate,images,levels,slices,start_transcoding_time,"; - for (int format_iter = first_format; format_iter < last_format; format_iter++) - { - const basist::transcoder_texture_format transcoder_tex_fmt = static_cast(format_iter); - - if (!basis_is_format_supported(transcoder_tex_fmt, fileinfo.m_tex_format)) - continue; - if (transcoder_tex_fmt == basist::transcoder_texture_format::cTFBC7_ALT) - continue; - - desc += std::string(basis_get_format_name(transcoder_tex_fmt)); - if (format_iter != last_format - 1) - desc += ","; - } - fprintf(pCSV_file, "%s\n", desc.c_str()); - } - - for (int format_iter = first_format; format_iter < last_format; format_iter++) - { - basist::transcoder_texture_format tex_fmt = static_cast(format_iter); - - 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); - - for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++) - gpu_images[(int)tex_fmt][image_index].resize(fileinfo.m_image_mipmap_levels[image_index]); - } - - // Now transcode the file to all supported texture formats and save mipmapped KTX files - for (int format_iter = first_format; format_iter < last_format; format_iter++) - { - const basist::transcoder_texture_format transcoder_tex_fmt = static_cast(format_iter); - - 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++) - { - for (uint32_t level_index = 0; level_index < fileinfo.m_image_mipmap_levels[image_index]; level_index++) - { - basist::basisu_image_level_info level_info; - - if (!dec.get_image_level_info(&basis_data[0], (uint32_t)basis_data.size(), level_info, image_index, level_index)) - { - 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; - } - - if ((transcoder_tex_fmt == basist::transcoder_texture_format::cTFPVRTC1_4_RGB) || (transcoder_tex_fmt == basist::transcoder_texture_format::cTFPVRTC1_4_RGBA)) - { - if (!is_pow2(level_info.m_width) || !is_pow2(level_info.m_height)) - { - total_pvrtc_nonpow2_warnings++; - - printf("Warning: Will not transcode image %u level %u res %ux%u to PVRTC1 (one or more dimension is not a power of 2)\n", image_index, level_index, level_info.m_width, level_info.m_height); - - // Can't transcode this image level to PVRTC because it's not a pow2 (we're going to support transcoding non-pow2 to the next larger pow2 soon) - continue; - } - } - - basisu::texture_format tex_fmt = basis_get_basisu_texture_format(transcoder_tex_fmt); - - gpu_image& gi = gpu_images[(int)transcoder_tex_fmt][image_index][level_index]; - gi.init(tex_fmt, level_info.m_orig_width, level_info.m_orig_height); - - // Fill the buffer with psuedo-random bytes, to help more visibly detect cases where the transcoder fails to write to part of the output. - fill_buffer_with_random_bytes(gi.get_ptr(), gi.get_size_in_bytes()); - - uint32_t decode_flags = 0; - - tm.start(); - - if (!dec.transcode_image_level(&basis_data[0], (uint32_t)basis_data.size(), image_index, level_index, gi.get_ptr(), gi.get_total_blocks(), transcoder_tex_fmt, decode_flags)) - { - 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; - } - - double total_transcode_time = tm.get_elapsed_ms(); - - total_format_transcoding_time_ms[format_iter] += total_transcode_time; - - 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); - - } // format_iter - - } // level_index - - } // image_info - - if (!validate_flag) - { - // Now write KTX files and unpack them to individual PNG's - - for (int format_iter = first_format; format_iter < last_format; format_iter++) - { - const basist::transcoder_texture_format transcoder_tex_fmt = static_cast(format_iter); - - 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)) - { - // No KTX tool that we know of supports cubemap arrays, so write individual cubemap files. - for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index += 6) - { - basisu::vector cubemap; - for (uint32_t i = 0; i < 6; i++) - cubemap.push_back(gpu_images[format_iter][image_index + i]); - - std::string ktx_filename(base_filename + string_format("_transcoded_cubemap_%s_%u.ktx", basist::basis_get_format_name(transcoder_tex_fmt), image_index / 6)); - if (!write_compressed_texture_file(ktx_filename.c_str(), cubemap, true)) - { - 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()); - } - } - - for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++) - { - gpu_image_vec& gi = gpu_images[format_iter][image_index]; - - if (!gi.size()) - continue; - - uint32_t level; - for (level = 0; level < gi.size(); level++) - if (!gi[level].get_total_blocks()) - break; - - if (level < gi.size()) - continue; - - if ((!opts.m_no_ktx) && (fileinfo.m_tex_type != basist::cBASISTexTypeCubemapArray)) - { - std::string ktx_filename(base_filename + string_format("_transcoded_%s_%04u.ktx", basist::basis_get_format_name(transcoder_tex_fmt), image_index)); - if (!write_compressed_texture_file(ktx_filename.c_str(), gi)) - { - 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()); - } - - for (uint32_t level_index = 0; level_index < gi.size(); level_index++) - { - basist::basisu_image_level_info level_info; - - if (!dec.get_image_level_info(&basis_data[0], (uint32_t)basis_data.size(), level_info, image_index, level_index)) - { - 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; - } - - image u; - if (!gi[level_index].unpack(u)) - { - printf("Warning: Failed unpacking GPU texture data (%u %u %u). Unpacking as much as possible.\n", format_iter, image_index, level_index); - total_unpack_warnings++; - } - //u.crop(level_info.m_orig_width, level_info.m_orig_height); - - std::string rgb_filename; - if (gi.size() > 1) - rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index); - else - rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index); - 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()); - - if (transcoder_tex_fmt == basist::transcoder_texture_format::cTFFXT1_RGB) - { - std::string out_filename; - if (gi.size() > 1) - out_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%04u.out", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index); - else - out_filename = base_filename + string_format("_unpacked_rgb_%s_%04u.out", basist::basis_get_format_name(transcoder_tex_fmt), image_index); - if (!write_3dfx_out_file(out_filename.c_str(), gi[level_index])) - { - 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()); - } - - if (basis_transcoder_format_has_alpha(transcoder_tex_fmt)) - { - std::string a_filename; - if (gi.size() > 1) - a_filename = base_filename + string_format("_unpacked_a_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index); - else - a_filename = base_filename + string_format("_unpacked_a_%s_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index); - if (!save_png(a_filename, u, cImageSaveGrayscale, 3)) - { - 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()); - - 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()); - 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()); - } - - } // level_index - - } // image_index - - } // format_iter - - } // if (!validate_flag) - - // Now unpack to RGBA using the transcoder itself to do the unpacking to raster images - for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++) - { - for (uint32_t level_index = 0; level_index < fileinfo.m_image_mipmap_levels[image_index]; level_index++) - { - const basist::transcoder_texture_format transcoder_tex_fmt = basist::transcoder_texture_format::cTFRGBA32; - - basist::basisu_image_level_info level_info; - - if (!dec.get_image_level_info(&basis_data[0], (uint32_t)basis_data.size(), level_info, image_index, level_index)) - { - 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; - } - - image img(level_info.m_orig_width, level_info.m_orig_height); - - fill_buffer_with_random_bytes(&img(0, 0), img.get_total_pixels() * sizeof(uint32_t)); - - tm.start(); - - if (!dec.transcode_image_level(&basis_data[0], (uint32_t)basis_data.size(), image_index, level_index, &img(0, 0).r, img.get_total_pixels(), transcoder_tex_fmt, 0, img.get_pitch(), nullptr, img.get_height())) - { - 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; - } - - double total_transcode_time = tm.get_elapsed_ms(); - - total_format_transcoding_time_ms[(int)transcoder_tex_fmt] += total_transcode_time; - - 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)) - { - 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()); - - std::string a_filename(base_filename + string_format("_unpacked_a_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index)); - if (!save_png(a_filename, img, cImageSaveGrayscale, 3)) - { - 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()); - } - - } // level_index - } // image_index - - // Now unpack to RGB565 using the transcoder itself to do the unpacking to raster images - for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++) - { - for (uint32_t level_index = 0; level_index < fileinfo.m_image_mipmap_levels[image_index]; level_index++) - { - const basist::transcoder_texture_format transcoder_tex_fmt = basist::transcoder_texture_format::cTFRGB565; - - basist::basisu_image_level_info level_info; - - if (!dec.get_image_level_info(&basis_data[0], (uint32_t)basis_data.size(), level_info, image_index, level_index)) - { - 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; - } - - basisu::vector packed_img(level_info.m_orig_width * level_info.m_orig_height); - - fill_buffer_with_random_bytes(&packed_img[0], packed_img.size() * sizeof(uint16_t)); - - tm.start(); - - if (!dec.transcode_image_level(&basis_data[0], (uint32_t)basis_data.size(), image_index, level_index, &packed_img[0], (uint32_t)packed_img.size(), transcoder_tex_fmt, 0, level_info.m_orig_width, nullptr, level_info.m_orig_height)) - { - 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; - } - - double total_transcode_time = tm.get_elapsed_ms(); - - total_format_transcoding_time_ms[(int)transcoder_tex_fmt] += total_transcode_time; - - image img(level_info.m_orig_width, level_info.m_orig_height); - for (uint32_t y = 0; y < level_info.m_orig_height; y++) - { - for (uint32_t x = 0; x < level_info.m_orig_width; x++) - { - const uint16_t p = packed_img[x + y * level_info.m_orig_width]; - uint32_t r = p >> 11, g = (p >> 5) & 63, b = p & 31; - r = (r << 3) | (r >> 2); - g = (g << 2) | (g >> 4); - b = (b << 3) | (b >> 2); - img(x, y).set(r, g, b, 255); - } - } - - 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)) - { - 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()); - } - - } // level_index - } // image_index - - // Now unpack to RGBA4444 using the transcoder itself to do the unpacking to raster images - uint32_t max_mipmap_levels = 0; - for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++) - { - for (uint32_t level_index = 0; level_index < fileinfo.m_image_mipmap_levels[image_index]; level_index++) - { - max_mipmap_levels = basisu::maximum(max_mipmap_levels, fileinfo.m_image_mipmap_levels[image_index]); - - const basist::transcoder_texture_format transcoder_tex_fmt = basist::transcoder_texture_format::cTFRGBA4444; - - basist::basisu_image_level_info level_info; - - if (!dec.get_image_level_info(&basis_data[0], (uint32_t)basis_data.size(), level_info, image_index, level_index)) - { - 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; - } - - basisu::vector packed_img(level_info.m_orig_width * level_info.m_orig_height); - - fill_buffer_with_random_bytes(&packed_img[0], packed_img.size() * sizeof(uint16_t)); - - tm.start(); - - if (!dec.transcode_image_level(&basis_data[0], (uint32_t)basis_data.size(), image_index, level_index, &packed_img[0], (uint32_t)packed_img.size(), transcoder_tex_fmt, 0, level_info.m_orig_width, nullptr, level_info.m_orig_height)) - { - 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; - } - - double total_transcode_time = tm.get_elapsed_ms(); - - total_format_transcoding_time_ms[(int)transcoder_tex_fmt] += total_transcode_time; - - image img(level_info.m_orig_width, level_info.m_orig_height); - for (uint32_t y = 0; y < level_info.m_orig_height; y++) - { - for (uint32_t x = 0; x < level_info.m_orig_width; x++) - { - const uint16_t p = packed_img[x + y * level_info.m_orig_width]; - uint32_t r = p >> 12, g = (p >> 8) & 15, b = (p >> 4) & 15, a = p & 15; - r = (r << 4) | r; - g = (g << 4) | g; - b = (b << 4) | b; - a = (a << 4) | a; - img(x, y).set(r, g, b, a); - } - } - - 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)) - { - 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()); - - std::string a_filename(base_filename + string_format("_unpacked_a_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index)); - if (!save_png(a_filename, img, cImageSaveGrayscale, 3)) - { - 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()); - } - - } // level_index - } // image_index - - if (pCSV_file) - { - fprintf(pCSV_file, "%s, %3.3f, %3.3f, %u, %u, %u, %3.3f, ", - base_filename.c_str(), - basis_bits_per_texel, - comp_bits_per_texel, - fileinfo.m_total_images, - max_mipmap_levels, - (uint32_t)fileinfo.m_slice_info.size(), - start_transcoding_time_ms); - - for (int format_iter = first_format; format_iter < last_format; format_iter++) - { - const basist::transcoder_texture_format transcoder_tex_fmt = static_cast(format_iter); - - if (!basis_is_format_supported(transcoder_tex_fmt, fileinfo.m_tex_format)) - continue; - if (transcoder_tex_fmt == basist::transcoder_texture_format::cTFBC7_ALT) - continue; - - fprintf(pCSV_file, "%3.3f", total_format_transcoding_time_ms[format_iter]); - if (format_iter != (last_format - 1)) - fprintf(pCSV_file, ","); - } - fprintf(pCSV_file, "\n"); - } } // file_index @@ -3180,7 +3695,7 @@ static bool compsize_mode(command_line_params& opts) static int main_internal(int argc, const char **argv) { - printf("Basis Universal GPU Texture Compressor Reference Encoder v" BASISU_TOOL_VERSION "\nCopyright (C) 2019-2021 Binomial LLC, All rights reserved\n"); + printf("Basis Universal GPU Texture Compressor v" BASISU_TOOL_VERSION "\nCopyright (C) 2019-2021 Binomial LLC, All rights reserved\n"); //interval_timer tm; //tm.start(); @@ -3207,9 +3722,9 @@ static int main_internal(int argc, const char **argv) } #if BASISU_SUPPORT_SSE - printf("Using SSE 4.1: %u, Multithreading: %u\n", g_cpu_supports_sse41, (uint32_t)opts.m_comp_params.m_multithreading); + printf("Using SSE 4.1: %u, Multithreading: %u, Zstandard support: %u\n", g_cpu_supports_sse41, (uint32_t)opts.m_comp_params.m_multithreading, basist::basisu_transcoder_supports_ktx2_zstd()); #else - printf("Multithreading: %u\n", (uint32_t)opts.m_comp_params.m_multithreading); + printf("Multithreading: %u, Zstandard support: %u\n", (uint32_t)opts.m_comp_params.m_multithreading, basist::basisu_transcoder_supports_ktx2_zstd()); #endif if (!opts.process_listing_files()) @@ -3220,7 +3735,7 @@ static int main_internal(int argc, const char **argv) for (size_t i = 0; i < opts.m_input_filenames.size(); i++) { std::string ext(string_get_extension(opts.m_input_filenames[i])); - if (strcasecmp(ext.c_str(), "basis") == 0) + if ((strcasecmp(ext.c_str(), "basis") == 0) || (strcasecmp(ext.c_str(), "ktx") == 0) || (strcasecmp(ext.c_str(), "ktx2") == 0)) { // If they haven't specified any modes, and they give us a .basis file, then assume they want to unpack it. opts.m_mode = cUnpack; diff --git a/encoder/basisu_backend.cpp b/encoder/basisu_backend.cpp index 1efda50..5bdd9b6 100644 --- a/encoder/basisu_backend.cpp +++ b/encoder/basisu_backend.cpp @@ -1687,6 +1687,7 @@ namespace basisu 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; + m_output.m_srgb = m_pFront_end->get_params().m_perceptual; create_endpoint_palette(); create_selector_palette(); diff --git a/encoder/basisu_backend.h b/encoder/basisu_backend.h index dc4652c..d9fba83 100644 --- a/encoder/basisu_backend.h +++ b/encoder/basisu_backend.h @@ -85,6 +85,7 @@ namespace basisu bool m_use_hybrid_sel_codebooks; bool m_used_global_codebooks; + basisu_backend_params() { clear(); @@ -113,10 +114,12 @@ namespace basisu { clear(); } + void clear() { clear_obj(*this); } + uint32_t m_first_block_index; uint32_t m_orig_width; @@ -145,6 +148,7 @@ namespace basisu bool m_etc1s; bool m_uses_global_codebooks; + bool m_srgb; uint32_t m_num_endpoints; uint32_t m_num_selectors; @@ -168,6 +172,7 @@ namespace basisu m_tex_format = basist::basis_tex_format::cETC1S; m_etc1s = false; m_uses_global_codebooks = false; + m_srgb = true; m_num_endpoints = 0; m_num_selectors = 0; diff --git a/encoder/basisu_basis_file.cpp b/encoder/basisu_basis_file.cpp index d74c01f..f4c77be 100644 --- a/encoder/basisu_basis_file.cpp +++ b/encoder/basisu_basis_file.cpp @@ -49,6 +49,8 @@ namespace basisu 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; + if (encoder_output.m_srgb) + m_header.m_flags = m_header.m_flags | basist::cBASISHeaderFlagSRGB; for (uint32_t i = 0; i < encoder_output.m_slice_desc.size(); i++) { diff --git a/encoder/basisu_comp.cpp b/encoder/basisu_comp.cpp index c85d09e..2241351 100644 --- a/encoder/basisu_comp.cpp +++ b/encoder/basisu_comp.cpp @@ -16,9 +16,19 @@ #include "basisu_enc.h" #include #include -#define MINIZ_NO_ZLIB_APIS + +// basisu_transcoder.cpp is where basisu_miniz lives now, we just need the declarations here. +#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES #include "basisu_miniz.h" +#if !BASISD_SUPPORT_KTX2 +#error BASISD_SUPPORT_KTX2 must be enabled (set to 1). +#endif + +#if BASISD_SUPPORT_KTX2_ZSTD +#include "../zstd/zstd.h" +#endif + using namespace buminiz; #define BASISU_USE_STB_IMAGE_RESIZE_FOR_MIPMAP_GEN 0 @@ -122,10 +132,22 @@ namespace basisu 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); + debug_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()); + debug_printf("Global codebook endpoints: %u selectors: %u\n", m_params.m_pGlobal_codebooks->get_endpoints().size(), m_params.m_pGlobal_codebooks->get_selectors().size()); + } + + PRINT_BOOL_VALUE(m_create_ktx2_file); + + debug_printf("KTX2 UASTC supercompression: %u\n", m_params.m_ktx2_uastc_supercompression); + debug_printf("KTX2 Zstd supercompression level: %i\n", (int)m_params.m_ktx2_zstd_supercompression_level); + debug_printf("KTX2 sRGB transfer func: %u\n", (int)m_params.m_ktx2_srgb_transfer_func); + debug_printf("Total KTX2 key values: %u\n", m_params.m_ktx2_key_values.size()); + for (uint32_t i = 0; i < m_params.m_ktx2_key_values.size(); i++) + { + debug_printf("Key: \"%s\"\n", m_params.m_ktx2_key_values[i].m_key.data()); + debug_printf("Value size: %u\n", m_params.m_ktx2_key_values[i].m_value.size()); } #undef PRINT_BOOL_VALUE @@ -153,6 +175,12 @@ namespace basisu if (!validate_texture_type_constraints()) return cECFailedValidating; + if (m_params.m_create_ktx2_file) + { + if (!validate_ktx2_constraints()) + return cECFailedValidating; + } + if (!extract_source_blocks()) return cECFailedFrontEnd; @@ -176,6 +204,12 @@ namespace basisu if (!create_basis_file_and_transcode()) return cECFailedCreateBasisFile; + + if (m_params.m_create_ktx2_file) + { + if (!create_ktx2_file()) + return cECFailedCreateKTX2File; + } if (!write_output_files_and_compute_stats()) return cECFailedWritingOutput; @@ -1294,20 +1328,18 @@ 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(); + const uint8_vec& comp_data = m_params.m_create_ktx2_file ? m_output_ktx2_file : m_basis_file.get_compressed_data(); if (m_params.m_write_output_basis_files) { - const std::string& basis_filename = m_params.m_out_filename; + const std::string& output_filename = m_params.m_out_filename; - if (!write_vec_to_file(basis_filename.c_str(), comp_data)) + if (!write_vec_to_file(output_filename.c_str(), comp_data)) { - error_printf("Failed writing output data to file \"%s\"\n", basis_filename.c_str()); + error_printf("Failed writing output data to file \"%s\"\n", output_filename.c_str()); return false; } - printf("Wrote output .basis file \"%s\"\n", basis_filename.c_str()); + printf("Wrote output .basis/.ktx2 file \"%s\"\n", output_filename.c_str()); } size_t comp_size = 0; @@ -1511,5 +1543,475 @@ namespace basisu return true; } + + // Make sure all the mip 0's have the same dimensions and number of mipmap levels, or we can't encode the KTX2 file. + bool basis_compressor::validate_ktx2_constraints() + { + uint32_t base_width = 0, base_height = 0; + uint32_t total_layers = 0; + for (uint32_t i = 0; i < m_slice_descs.size(); i++) + { + if (m_slice_descs[i].m_mip_index == 0) + { + if (!base_width) + { + base_width = m_slice_descs[i].m_orig_width; + base_height = m_slice_descs[i].m_orig_height; + } + else + { + if ((m_slice_descs[i].m_orig_width != base_width) || (m_slice_descs[i].m_orig_height != base_height)) + { + return false; + } + } + + total_layers = maximum(total_layers, m_slice_descs[i].m_source_file_index + 1); + } + } + + basisu::vector total_mips(total_layers); + for (uint32_t i = 0; i < m_slice_descs.size(); i++) + total_mips[m_slice_descs[i].m_source_file_index] = maximum(total_mips[m_slice_descs[i].m_source_file_index], m_slice_descs[i].m_mip_index + 1); + + for (uint32_t i = 1; i < total_layers; i++) + { + if (total_mips[0] != total_mips[i]) + { + return false; + } + } + + return true; + } + + static uint8_t g_ktx2_etc1s_nonalpha_dfd[44] = { 0x2C,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x28,0x0,0xA3,0x1,0x2,0x0,0x3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3F,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xFF,0xFF,0xFF,0xFF }; + static uint8_t g_ktx2_etc1s_alpha_dfd[60] = { 0x3C,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x38,0x0,0xA3,0x1,0x2,0x0,0x3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3F,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x40,0x0,0x3F,0xF,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xFF,0xFF,0xFF,0xFF }; + static uint8_t g_ktx2_uastc_nonalpha_dfd[44] = { 0x2C,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x28,0x0,0xA6,0x1,0x2,0x0,0x3,0x3,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7F,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xFF,0xFF,0xFF,0xFF }; + static uint8_t g_ktx2_uastc_alpha_dfd[44] = { 0x2C,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x28,0x0,0xA6,0x1,0x2,0x0,0x3,0x3,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7F,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xFF,0xFF,0xFF,0xFF }; + + void basis_compressor::get_dfd(uint8_vec &dfd, const basist::ktx2_header &header) + { + const uint8_t* pDFD; + uint32_t dfd_len; + + if (m_params.m_uastc) + { + if (m_any_source_image_has_alpha) + { + pDFD = g_ktx2_uastc_alpha_dfd; + dfd_len = sizeof(g_ktx2_uastc_alpha_dfd); + } + else + { + pDFD = g_ktx2_uastc_nonalpha_dfd; + dfd_len = sizeof(g_ktx2_uastc_nonalpha_dfd); + } + } + else + { + if (m_any_source_image_has_alpha) + { + pDFD = g_ktx2_etc1s_alpha_dfd; + dfd_len = sizeof(g_ktx2_etc1s_alpha_dfd); + } + else + { + pDFD = g_ktx2_etc1s_nonalpha_dfd; + dfd_len = sizeof(g_ktx2_etc1s_nonalpha_dfd); + } + } + + assert(dfd_len >= 44); + + dfd.resize(dfd_len); + memcpy(dfd.data(), pDFD, dfd_len); + + uint32_t dfd_bits = basisu::read_le_dword(dfd.data() + 3 * sizeof(uint32_t)); + + dfd_bits &= ~(0xFF << 16); + + if (m_params.m_ktx2_srgb_transfer_func) + dfd_bits |= (basist::KTX2_KHR_DF_TRANSFER_SRGB << 16); + else + dfd_bits |= (basist::KTX2_KHR_DF_TRANSFER_LINEAR << 16); + + basisu::write_le_dword(dfd.data() + 3 * sizeof(uint32_t), dfd_bits); + + if (header.m_supercompression_scheme != basist::KTX2_SS_NONE) + { + uint32_t plane_bits = basisu::read_le_dword(dfd.data() + 5 * sizeof(uint32_t)); + + plane_bits &= ~0xFF; + + basisu::write_le_dword(dfd.data() + 5 * sizeof(uint32_t), plane_bits); + } + + // Fix up the DFD channel(s) + uint32_t dfd_chan0 = basisu::read_le_dword(dfd.data() + 7 * sizeof(uint32_t)); + + if (m_params.m_uastc) + { + dfd_chan0 &= ~(0xF << 24); + + // TODO: Allow the caller to override this + if (m_any_source_image_has_alpha) + dfd_chan0 |= (basist::KTX2_DF_CHANNEL_UASTC_RGBA << 24); + else + dfd_chan0 |= (basist::KTX2_DF_CHANNEL_UASTC_RGB << 24); + } + + basisu::write_le_dword(dfd.data() + 7 * sizeof(uint32_t), dfd_chan0); + } + + bool basis_compressor::create_ktx2_file() + { + if (m_params.m_uastc) + { + if ((m_params.m_ktx2_uastc_supercompression != basist::KTX2_SS_NONE) && (m_params.m_ktx2_uastc_supercompression != basist::KTX2_SS_ZSTANDARD)) + return false; + } + + const basisu_backend_output& backend_output = m_backend.get_output(); + + // Determine the width/height, number of array layers, mipmap levels, and the number of faces (1 for 2D, 6 for cubemap). + // This does not support 1D or 3D. + uint32_t base_width = 0, base_height = 0, total_layers = 0, total_levels = 0, total_faces = 1; + + for (uint32_t i = 0; i < m_slice_descs.size(); i++) + { + if ((m_slice_descs[i].m_mip_index == 0) && (!base_width)) + { + base_width = m_slice_descs[i].m_orig_width; + base_height = m_slice_descs[i].m_orig_height; + } + + total_layers = maximum(total_layers, m_slice_descs[i].m_source_file_index + 1); + + if (!m_slice_descs[i].m_source_file_index) + total_levels = maximum(total_levels, m_slice_descs[i].m_mip_index + 1); + } + + if (m_params.m_tex_type == basist::cBASISTexTypeCubemapArray) + { + assert((total_layers % 6) == 0); + + total_layers /= 6; + assert(total_layers >= 1); + + total_faces = 6; + } + + basist::ktx2_header header; + memset(&header, 0, sizeof(header)); + + memcpy(header.m_identifier, basist::g_ktx2_file_identifier, sizeof(basist::g_ktx2_file_identifier)); + header.m_pixel_width = base_width; + header.m_pixel_height = base_height; + header.m_face_count = total_faces; + header.m_vk_format = basist::KTX2_VK_FORMAT_UNDEFINED; + header.m_type_size = 1; + header.m_level_count = total_levels; + header.m_layer_count = (total_layers > 1) ? total_layers : 0; + + if (m_params.m_uastc) + { + switch (m_params.m_ktx2_uastc_supercompression) + { + case basist::KTX2_SS_NONE: + { + header.m_supercompression_scheme = basist::KTX2_SS_NONE; + break; + } + case basist::KTX2_SS_ZSTANDARD: + { +#if BASISD_SUPPORT_KTX2_ZSTD + header.m_supercompression_scheme = basist::KTX2_SS_ZSTANDARD; +#else + header.m_supercompression_scheme = basist::KTX2_SS_NONE; +#endif + break; + } + default: assert(0); return false; + } + } + + basisu::vector level_data_bytes(total_levels); + basisu::vector compressed_level_data_bytes(total_levels); + uint_vec slice_level_offsets(m_slice_descs.size()); + + // This will append the texture data in the correct order (for each level: layer, then face). + for (uint32_t slice_index = 0; slice_index < m_slice_descs.size(); slice_index++) + { + const basisu_backend_slice_desc& slice_desc = m_slice_descs[slice_index]; + + slice_level_offsets[slice_index] = level_data_bytes[slice_desc.m_mip_index].size(); + + if (m_params.m_uastc) + append_vector(level_data_bytes[slice_desc.m_mip_index], m_uastc_backend_output.m_slice_image_data[slice_index]); + else + append_vector(level_data_bytes[slice_desc.m_mip_index], backend_output.m_slice_image_data[slice_index]); + } + + // UASTC supercompression + if ((m_params.m_uastc) && (header.m_supercompression_scheme == basist::KTX2_SS_ZSTANDARD)) + { +#if BASISD_SUPPORT_KTX2_ZSTD + for (uint32_t level_index = 0; level_index < total_levels; level_index++) + { + compressed_level_data_bytes[level_index].resize(ZSTD_compressBound(level_data_bytes[level_index].size())); + + size_t result = ZSTD_compress(compressed_level_data_bytes[level_index].data(), compressed_level_data_bytes[level_index].size(), + level_data_bytes[level_index].data(), level_data_bytes[level_index].size(), + m_params.m_ktx2_zstd_supercompression_level); + + if (ZSTD_isError(result)) + return false; + + compressed_level_data_bytes[level_index].resize(result); + } +#else + // Can't get here + assert(0); + return false; +#endif + } + else + { + // No supercompression + compressed_level_data_bytes = level_data_bytes; + } + + uint8_vec etc1s_global_data; + + // Create ETC1S global supercompressed data + if (!m_params.m_uastc) + { + basist::ktx2_etc1s_global_data_header etc1s_global_data_header; + clear_obj(etc1s_global_data_header); + + etc1s_global_data_header.m_endpoint_count = backend_output.m_num_endpoints; + etc1s_global_data_header.m_selector_count = backend_output.m_num_selectors; + etc1s_global_data_header.m_endpoints_byte_length = backend_output.m_endpoint_palette.size(); + etc1s_global_data_header.m_selectors_byte_length = backend_output.m_selector_palette.size(); + etc1s_global_data_header.m_tables_byte_length = backend_output.m_slice_image_tables.size(); + + basisu::vector etc1s_image_descs(total_levels * total_layers * total_faces); + memset(etc1s_image_descs.data(), 0, etc1s_image_descs.size_in_bytes()); + + for (uint32_t slice_index = 0; slice_index < m_slice_descs.size(); slice_index++) + { + const basisu_backend_slice_desc& slice_desc = m_slice_descs[slice_index]; + + const uint32_t level_index = slice_desc.m_mip_index; + uint32_t layer_index = slice_desc.m_source_file_index; + uint32_t face_index = 0; + + if (m_params.m_tex_type == basist::cBASISTexTypeCubemapArray) + { + face_index = layer_index % 6; + layer_index /= 6; + } + + const uint32_t etc1s_image_index = level_index * (total_layers * total_faces) + layer_index * total_faces + face_index; + + if (slice_desc.m_alpha) + { + etc1s_image_descs[etc1s_image_index].m_alpha_slice_byte_length = backend_output.m_slice_image_data[slice_index].size(); + etc1s_image_descs[etc1s_image_index].m_alpha_slice_byte_offset = slice_level_offsets[slice_index]; + } + else + { + if (m_params.m_tex_type == basist::cBASISTexTypeVideoFrames) + etc1s_image_descs[etc1s_image_index].m_image_flags = !slice_desc.m_iframe ? basist::KTX2_IMAGE_IS_P_FRAME : 0; + + etc1s_image_descs[etc1s_image_index].m_rgb_slice_byte_length = backend_output.m_slice_image_data[slice_index].size(); + etc1s_image_descs[etc1s_image_index].m_rgb_slice_byte_offset = slice_level_offsets[slice_index]; + } + } // slice_index + + append_vector(etc1s_global_data, (const uint8_t*)&etc1s_global_data_header, sizeof(etc1s_global_data_header)); + append_vector(etc1s_global_data, (const uint8_t*)etc1s_image_descs.data(), etc1s_image_descs.size_in_bytes()); + append_vector(etc1s_global_data, backend_output.m_endpoint_palette); + append_vector(etc1s_global_data, backend_output.m_selector_palette); + append_vector(etc1s_global_data, backend_output.m_slice_image_tables); + + header.m_supercompression_scheme = basist::KTX2_SS_BASISLZ; + } + + // Key values + basist::ktx2_transcoder::key_value_vec key_values(m_params.m_ktx2_key_values); + key_values.enlarge(1); + + const char* pKTXwriter = "KTXwriter"; + key_values.back().m_key.resize(strlen(pKTXwriter) + 1); + memcpy(key_values.back().m_key.data(), pKTXwriter, strlen(pKTXwriter) + 1); + + char writer_id[128]; +#ifdef _MSC_VER + sprintf_s(writer_id, sizeof(writer_id), "Basis Universal %s", BASISU_LIB_VERSION_STRING); +#else + snprintf(writer_id, sizeof(writer_id), "Basis Universal %s", BASISU_LIB_VERSION_STRING); +#endif + key_values.back().m_value.resize(strlen(writer_id) + 1); + memcpy(key_values.back().m_value.data(), writer_id, strlen(writer_id) + 1); + + key_values.sort(); + + uint8_vec key_value_data; + + // DFD + uint8_vec dfd; + get_dfd(dfd, header); + + const uint32_t kvd_file_offset = sizeof(header) + sizeof(basist::ktx2_level_index) * total_levels + dfd.size(); + + for (uint32_t pass = 0; pass < 2; pass++) + { + for (uint32_t i = 0; i < key_values.size(); i++) + { + if (key_values[i].m_key.size() < 2) + return false; + + if (key_values[i].m_key.back() != 0) + return false; + + const uint64_t total_len = (uint64_t)key_values[i].m_key.size() + (uint64_t)key_values[i].m_value.size(); + if (total_len >= UINT32_MAX) + return false; + + packed_uint<4> le_len((uint32_t)total_len); + append_vector(key_value_data, (const uint8_t*)&le_len, sizeof(le_len)); + + append_vector(key_value_data, key_values[i].m_key); + append_vector(key_value_data, key_values[i].m_value); + + const uint32_t ofs = key_value_data.size() & 3; + const uint32_t padding = (4 - ofs) & 3; + for (uint32_t p = 0; p < padding; p++) + key_value_data.push_back(0); + } + + if (header.m_supercompression_scheme != basist::KTX2_SS_NONE) + break; + + // Hack to ensure the KVD block ends on a 16 byte boundary, because we have no other official way of aligning the data. + uint32_t kvd_end_file_offset = kvd_file_offset + key_value_data.size(); + uint32_t bytes_needed_to_pad = (16 - (kvd_end_file_offset & 15)) & 15; + if (!bytes_needed_to_pad) + { + // We're good. No need to add a dummy key. + break; + } + + assert(!pass); + if (pass) + return false; + + if (bytes_needed_to_pad < 6) + bytes_needed_to_pad += 16; + + printf("WARNING: Due to a KTX2 validator bug related to mipPadding, we must insert a dummy key into the KTX2 file of %u bytes\n", bytes_needed_to_pad); + + // We're not good - need to add a dummy key large enough to force file alignment so the mip level array gets aligned. + // We can't just add some bytes before the mip level array because ktx2check will see that as extra data in the file that shouldn't be there in ktxValidator::validateDataSize(). + key_values.enlarge(1); + for (uint32_t i = 0; i < (bytes_needed_to_pad - 4 - 1 - 1); i++) + key_values.back().m_key.push_back(127); + + key_values.back().m_key.push_back(0); + + key_values.back().m_value.push_back(0); + + key_values.sort(); + + key_value_data.resize(0); + + // Try again + } + + basisu::vector level_index_array(total_levels); + memset(level_index_array.data(), 0, level_index_array.size_in_bytes()); + + m_output_ktx2_file.clear(); + m_output_ktx2_file.reserve(m_output_basis_file.size()); + + // Dummy header + m_output_ktx2_file.resize(sizeof(header)); + + // Level index array + append_vector(m_output_ktx2_file, (const uint8_t*)level_index_array.data(), level_index_array.size_in_bytes()); + + // DFD + const uint8_t* pDFD = dfd.data(); + uint32_t dfd_len = dfd.size(); + + header.m_dfd_byte_offset = m_output_ktx2_file.size(); + header.m_dfd_byte_length = dfd_len; + append_vector(m_output_ktx2_file, pDFD, dfd_len); + + // Key value data + if (key_value_data.size()) + { + assert(kvd_file_offset == m_output_ktx2_file.size()); + + header.m_kvd_byte_offset = m_output_ktx2_file.size(); + header.m_kvd_byte_length = key_value_data.size(); + append_vector(m_output_ktx2_file, key_value_data); + } + + // Global Supercompressed Data + if (etc1s_global_data.size()) + { + uint32_t ofs = m_output_ktx2_file.size() & 7; + uint32_t padding = (8 - ofs) & 7; + for (uint32_t i = 0; i < padding; i++) + m_output_ktx2_file.push_back(0); + + header.m_sgd_byte_length = etc1s_global_data.size(); + header.m_sgd_byte_offset = m_output_ktx2_file.size(); + + append_vector(m_output_ktx2_file, etc1s_global_data); + } + + // mipPadding + if (header.m_supercompression_scheme == basist::KTX2_SS_NONE) + { + // We currently can't do this or the validator will incorrectly give an error. + uint32_t ofs = m_output_ktx2_file.size() & 15; + uint32_t padding = (16 - ofs) & 15; + + // Make sure we're always aligned here (due to a validator bug). + assert(!padding); + if (padding) + { + printf("WARNING: KTX2 mip level data is not 16-byte aligned! This will trigger a ktx2check validation bug.\n"); + } + + for (uint32_t i = 0; i < padding; i++) + m_output_ktx2_file.push_back(0); + } + + // Level data - write the smallest mipmap first. + for (int level = total_levels - 1; level >= 0; level--) + { + level_index_array[level].m_byte_length = compressed_level_data_bytes[level].size(); + if (m_params.m_uastc) + level_index_array[level].m_uncompressed_byte_length = level_data_bytes[level].size(); + + level_index_array[level].m_byte_offset = m_output_ktx2_file.size(); + append_vector(m_output_ktx2_file, compressed_level_data_bytes[level]); + } + + // Write final header + memcpy(m_output_ktx2_file.data(), &header, sizeof(header)); + + // Write final level index array + memcpy(m_output_ktx2_file.data() + sizeof(header), level_index_array.data(), level_index_array.size_in_bytes()); + + debug_printf("Total .ktx2 output file size: %u\n", m_output_ktx2_file.size()); + + return true; + } } // namespace basisu diff --git a/encoder/basisu_comp.h b/encoder/basisu_comp.h index 9f196a7..b701ec3 100644 --- a/encoder/basisu_comp.h +++ b/encoder/basisu_comp.h @@ -20,6 +20,20 @@ #include "../transcoder/basisu_transcoder.h" #include "basisu_uastc_enc.h" +#define BASISU_LIB_VERSION 115 +#define BASISU_LIB_VERSION_STRING "1.15" + +#ifndef BASISD_SUPPORT_KTX2 + #error BASISD_SUPPORT_KTX2 is undefined +#endif +#ifndef BASISD_SUPPORT_KTX2_ZSTD + #error BASISD_SUPPORT_KTX2_ZSTD is undefined +#endif + +#if !BASISD_SUPPORT_KTX2 + #error BASISD_SUPPORT_KTX2 must be enabled when building the encoder. To reduce code size if KTX2 support isn't needed, set BASISD_SUPPORT_KTX2_ZSTD to 0. +#endif + namespace basisu { const uint32_t BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION = 16384; @@ -185,7 +199,7 @@ namespace basisu T m_max; bool m_changed; }; - + struct basis_compressor_params { basis_compressor_params() : @@ -209,6 +223,8 @@ namespace basisu 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_resample_factor(0.0f, .00125f, 100.0f), + m_ktx2_uastc_supercompression(basist::KTX2_SS_NONE), + m_ktx2_zstd_supercompression_level(6, INT_MIN, INT_MAX), m_pJob_pool(nullptr) { clear(); @@ -291,6 +307,13 @@ namespace basisu m_resample_factor.clear(); m_pGlobal_codebooks = nullptr; + + m_create_ktx2_file.clear(); + m_ktx2_uastc_supercompression = basist::KTX2_SS_NONE; + m_ktx2_key_values.clear(); + m_ktx2_zstd_supercompression_level.clear(); + m_ktx2_srgb_transfer_func.clear(); + m_pJob_pool = nullptr; } @@ -309,7 +332,7 @@ namespace basisu // TODO: Allow caller to supply their own mipmaps // Filename of the output basis file - std::string m_out_filename; + std::string m_out_filename; // The params are done this way so we can detect when the user has explictly changed them. @@ -335,7 +358,7 @@ namespace basisu // Frontend/backend codec parameters bool_param m_no_hybrid_sel_cb; - // Use perceptual sRGB colorspace metrics (for normal maps, etc.) + // Use perceptual sRGB colorspace metrics instead of linear bool_param m_perceptual; // Disable selector RDO, for faster compression but larger files @@ -350,7 +373,7 @@ namespace basisu // Write the output basis file to disk using m_out_filename bool_param m_write_output_basis_files; - + // Compute and display image metrics bool_param m_compute_stats; @@ -412,6 +435,14 @@ namespace basisu param m_resample_factor; const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks; + // KTX2 specific parameters. + // Internally, the compressor always creates a .basis file then it converts that lossless to KTX2. + bool_param m_create_ktx2_file; + basist::ktx2_supercompression m_ktx2_uastc_supercompression; + basist::ktx2_transcoder::key_value_vec m_ktx2_key_values; + param m_ktx2_zstd_supercompression_level; + bool_param m_ktx2_srgb_transfer_func; + job_pool *m_pJob_pool; }; @@ -435,20 +466,25 @@ namespace basisu cECFailedBackend, cECFailedCreateBasisFile, cECFailedWritingOutput, - cECFailedUASTCRDOPostProcess + cECFailedUASTCRDOPostProcess, + cECFailedCreateKTX2File }; error_code process(); + // The output .basis file will always be valid of process() succeeded. const uint8_vec &get_output_basis_file() const { return m_output_basis_file; } + // The output .ktx2 file will only be valid if m_create_ktx2_file was true and process() succeeded. + const uint8_vec& get_output_ktx2_file() const { return m_output_ktx2_file; } + const basisu::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; @@ -482,6 +518,7 @@ namespace basisu basisu::vector m_decoded_output_textures_unpacked_bc7; uint8_vec m_output_basis_file; + uint8_vec m_output_ktx2_file; basisu::vector m_uastc_slice_textures; basisu_backend_output m_uastc_backend_output; @@ -498,6 +535,9 @@ namespace basisu error_code encode_slices_to_uastc(); bool generate_mipmaps(const image &img, basisu::vector &mips, bool has_alpha); bool validate_texture_type_constraints(); + bool validate_ktx2_constraints(); + void get_dfd(uint8_vec& dfd, const basist::ktx2_header& hdr); + bool create_ktx2_file(); }; } // namespace basisu diff --git a/encoder/basisu_enc.cpp b/encoder/basisu_enc.cpp index 491a13a..f02fb62 100644 --- a/encoder/basisu_enc.cpp +++ b/encoder/basisu_enc.cpp @@ -56,6 +56,108 @@ namespace basisu 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 }; + + // This is a Public Domain 8x8 font from here: + // https://github.com/dhepper/font8x8/blob/master/font8x8_basic.h + const uint8_t g_debug_font8x8_basic[127 - 32 + 1][8] = + { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 ( ) + { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!) + { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (") + { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#) + { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($) + { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%) + { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&) + { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (') + { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (() + { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ()) + { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*) + { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,) + { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.) + { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/) + { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) + { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) + { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) + { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) + { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) + { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) + { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) + { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) + { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) + { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (;) + { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<) + { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=) + { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>) + { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?) + { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@) + { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A) + { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B) + { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C) + { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F) + { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G) + { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H) + { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I) + { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J) + { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K) + { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L) + { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M) + { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N) + { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O) + { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P) + { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q) + { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R) + { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S) + { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V) + { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W) + { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X) + { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y) + { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z) + { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([) + { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\) + { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (]) + { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_) + { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`) + { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a) + { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b) + { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c) + { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d) + { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e) + { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g) + { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h) + { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i) + { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j) + { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k) + { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l) + { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m) + { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n) + { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o) + { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q) + { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r) + { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s) + { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v) + { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w) + { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y) + { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z) + { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({) + { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|) + { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (}) + { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F + }; // Encoder library initialization (just call once at startup) void basisu_encoder_init() @@ -1981,5 +2083,57 @@ namespace basisu return read_tga(&filedata[0], (uint32_t)filedata.size(), width, height, n_chans); } + + void image::debug_text(uint32_t x_ofs, uint32_t y_ofs, uint32_t scale_x, uint32_t scale_y, const color_rgba& fg, const color_rgba* pBG, bool alpha_only, const char* pFmt, ...) + { + char buf[2048]; + + va_list args; + va_start(args, pFmt); +#ifdef _WIN32 + vsprintf_s(buf, sizeof(buf), pFmt, args); +#else + vsnprintf(buf, sizeof(buf), pFmt, args); +#endif + va_end(args); + + const char* p = buf; + + const uint32_t orig_x_ofs = x_ofs; + + while (*p) + { + uint8_t c = *p++; + if ((c < 32) || (c > 127)) + c = '.'; + + const uint8_t* pGlpyh = &g_debug_font8x8_basic[c - 32][0]; + + for (uint32_t y = 0; y < 8; y++) + { + uint32_t row_bits = pGlpyh[y]; + for (uint32_t x = 0; x < 8; x++) + { + const uint32_t q = row_bits & (1 << x); + + const color_rgba* pColor = q ? &fg : pBG; + if (!pColor) + continue; + + if (alpha_only) + fill_box_alpha(x_ofs + x * scale_x, y_ofs + y * scale_y, scale_x, scale_y, *pColor); + else + fill_box(x_ofs + x * scale_x, y_ofs + y * scale_y, scale_x, scale_y, *pColor); + } + } + + x_ofs += 8 * scale_x; + if ((x_ofs + 8 * scale_x) > m_width) + { + x_ofs = orig_x_ofs; + y_ofs += 8 * scale_y; + } + } + } } // namespace basisu diff --git a/encoder/basisu_enc.h b/encoder/basisu_enc.h index 09f84b6..05c95cb 100644 --- a/encoder/basisu_enc.h +++ b/encoder/basisu_enc.h @@ -36,6 +36,7 @@ namespace basisu { extern uint8_t g_hamming_dist[256]; + extern const uint8_t g_debug_font8x8_basic[127 - 32 + 1][8]; // Encoder library initialization. // This function MUST be called before encoding anything! @@ -748,6 +749,7 @@ namespace basisu typedef basisu::vector color_rgba_vec; const color_rgba g_black_color(0, 0, 0, 255); + const color_rgba g_black_trans_color(0, 0, 0, 0); const color_rgba g_white_color(255, 255, 255, 255); inline int color_distance(int r0, int g0, int b0, int r1, int g1, int b1) @@ -2448,6 +2450,14 @@ namespace basisu return *this; } + image& fill_box_alpha(uint32_t x, uint32_t y, uint32_t w, uint32_t h, const color_rgba& c) + { + for (uint32_t iy = 0; iy < h; iy++) + for (uint32_t ix = 0; ix < w; ix++) + set_clipped_alpha(x + ix, y + iy, c); + return *this; + } + image &crop_dup_borders(uint32_t w, uint32_t h) { const uint32_t orig_w = m_width, orig_h = m_height; @@ -2537,6 +2547,13 @@ namespace basisu return *this; } + inline image& set_clipped_alpha(int x, int y, const color_rgba& c) + { + if ((static_cast(x) < m_width) && (static_cast(y) < m_height)) + (*this)(x, y).m_comps[3] = c.m_comps[3]; + return *this; + } + // Very straightforward blit with full clipping. Not fast, but it works. image &blit(const image &src, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y) { @@ -2661,6 +2678,8 @@ namespace basisu } return *this; } + + void debug_text(uint32_t x_ofs, uint32_t y_ofs, uint32_t x_scale, uint32_t y_scale, const color_rgba &fg, const color_rgba *pBG, bool alpha_only, const char* p, ...); private: uint32_t m_width, m_height, m_pitch; // all in pixels diff --git a/encoder/basisu_miniz.h b/encoder/basisu_miniz.h index 919e08c..8627abe 100644 --- a/encoder/basisu_miniz.h +++ b/encoder/basisu_miniz.h @@ -672,7 +672,7 @@ void mz_free(void *p) static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } -static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } +//static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } const char *mz_version(void) { @@ -788,7 +788,14 @@ mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { (void)pStream; // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) - return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); + mz_uint64 a = 128ULL + (source_len * 110ULL) / 100ULL; + mz_uint64 b = 128ULL + (mz_uint64)source_len + ((source_len / (31 * 1024)) + 1ULL) * 5ULL; + + mz_uint64 t = MZ_MAX(a, b); + if (((mz_ulong)t) != t) + t = (mz_ulong)(-1); + + return (mz_ulong)t; } int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) diff --git a/transcoder/basisu.h b/transcoder/basisu.h index b7423cf..f33baf6 100644 --- a/transcoder/basisu.h +++ b/transcoder/basisu.h @@ -256,28 +256,92 @@ namespace basisu if ((ha <= lb) || (la >= hb)) return false; return true; } + + static inline uint32_t read_le_dword(const uint8_t *pBytes) + { + return (pBytes[3] << 24U) | (pBytes[2] << 16U) | (pBytes[1] << 8U) | (pBytes[0]); + } + + static inline void write_le_dword(uint8_t* pBytes, uint32_t val) + { + pBytes[0] = (uint8_t)val; + pBytes[1] = (uint8_t)(val >> 8U); + pBytes[2] = (uint8_t)(val >> 16U); + pBytes[3] = (uint8_t)(val >> 24U); + } - // Always little endian 2-4 byte unsigned int + // Always little endian 1-8 byte unsigned int template struct packed_uint { uint8_t m_bytes[NumBytes]; - inline packed_uint() { static_assert(NumBytes <= 4, "NumBytes <= 4"); } - inline packed_uint(uint32_t v) { *this = v; } + inline packed_uint() { static_assert(NumBytes <= sizeof(uint64_t), "Invalid NumBytes"); } + inline packed_uint(uint64_t v) { *this = v; } inline packed_uint(const packed_uint& other) { *this = other; } + + inline packed_uint& operator= (uint64_t v) + { + for (uint32_t i = 0; i < NumBytes; i++) + m_bytes[i] = static_cast(v >> (i * 8)); + return *this; + } - 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 packed_uint& operator= (const packed_uint& rhs) + { + memcpy(m_bytes, rhs.m_bytes, sizeof(m_bytes)); + return *this; + } inline operator uint32_t() const { switch (NumBytes) { - case 1: return m_bytes[0]; - case 2: return (m_bytes[1] << 8U) | m_bytes[0]; - case 3: return (m_bytes[2] << 16U) | (m_bytes[1] << 8U) | (m_bytes[0]); - default: return (m_bytes[3] << 24U) | (m_bytes[2] << 16U) | (m_bytes[1] << 8U) | (m_bytes[0]); + case 1: + { + return m_bytes[0]; + } + case 2: + { + return (m_bytes[1] << 8U) | m_bytes[0]; + } + case 3: + { + return (m_bytes[2] << 16U) | (m_bytes[1] << 8U) | m_bytes[0]; + } + case 4: + { + return read_le_dword(m_bytes); + } + case 5: + { + uint32_t l = read_le_dword(m_bytes); + uint32_t h = m_bytes[4]; + return static_cast(l) | (static_cast(h) << 32U); + } + case 6: + { + uint32_t l = read_le_dword(m_bytes); + uint32_t h = (m_bytes[5] << 8U) | m_bytes[4]; + return static_cast(l) | (static_cast(h) << 32U); + } + case 7: + { + uint32_t l = read_le_dword(m_bytes); + uint32_t h = (m_bytes[6] << 16U) | (m_bytes[5] << 8U) | m_bytes[4]; + return static_cast(l) | (static_cast(h) << 32U); + } + case 8: + { + uint32_t l = read_le_dword(m_bytes); + uint32_t h = read_le_dword(m_bytes + 4); + return static_cast(l) | (static_cast(h) << 32U); + } + default: + { + assert(0); + return 0; + } } } }; diff --git a/transcoder/basisu_containers.h b/transcoder/basisu_containers.h index 5801180..1ca4bab 100644 --- a/transcoder/basisu_containers.h +++ b/transcoder/basisu_containers.h @@ -416,9 +416,32 @@ namespace basisu } } - inline bool try_reserve(size_t new_capacity) + inline bool try_reserve(size_t new_capacity_size_t) { - return increase_capacity(new_capacity, true, true); + if (new_capacity_size_t > UINT32_MAX) + { + assert(0); + return false; + } + + uint32_t new_capacity = (uint32_t)new_capacity_size_t; + + if (new_capacity > m_capacity) + { + if (!increase_capacity(new_capacity, false)) + return false; + } + else if (new_capacity < m_capacity) + { + // Must work around the lack of a "decrease_capacity()" method. + // This case is rare enough in practice that it's probably not worth implementing an optimized in-place resize. + vector tmp; + tmp.increase_capacity(helpers::maximum(m_size, new_capacity), false); + tmp = *this; + swap(tmp); + } + + return true; } // resize(0) sets the container to empty, but does not free the allocated block. diff --git a/transcoder/basisu_file_headers.h b/transcoder/basisu_file_headers.h index a80c33e..4316d73 100644 --- a/transcoder/basisu_file_headers.h +++ b/transcoder/basisu_file_headers.h @@ -21,7 +21,10 @@ namespace basist enum basis_slice_desc_flags { cSliceDescFlagsHasAlpha = 1, - cSliceDescFlagsFrameIsIFrame = 2 // Video only: Frame doesn't refer to previous frame (no usage of conditional replenishment pred symbols) + + // Video only: Frame doesn't refer to previous frame (no usage of conditional replenishment pred symbols) + // Currently the first frame is always an I-Frame, all subsequent frames are P-Frames. This will eventually be changed to periodic I-Frames. + cSliceDescFlagsFrameIsIFrame = 2 }; #pragma pack(push) @@ -47,10 +50,21 @@ namespace basist // File header files enum basis_header_flags { - 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) - cBASISHeaderFlagUsesGlobalCodebook = 8 // For ETC1S files, this will be true if the file utilizes a codebook from another .basis file. + // Always set for ETC1S files. Not set for UASTC files. + cBASISHeaderFlagETC1S = 1, + + // Set if the texture had to be Y flipped before encoding. The actual interpretation of this (is Y up or down?) is up to the user. + cBASISHeaderFlagYFlipped = 2, + + // Set if any slices contain alpha (for ETC1S, if the odd slices contain alpha data) + cBASISHeaderFlagHasAlphaSlices = 4, + + // For ETC1S files, this will be true if the file utilizes a codebook from another .basis file. + cBASISHeaderFlagUsesGlobalCodebook = 8, + + // Set if the texture data is sRGB, otherwise it's linear. + // In reality, we have no idea if the texture data is actually linear or sRGB. This is the m_perceptual parameter passed to the compressor. + cBASISHeaderFlagSRGB = 16, }; // The image type field attempts to describe how to interpret the image data in a Basis file. @@ -89,12 +103,12 @@ namespace basist basisu::packed_uint<2> m_sig; // 2 byte file signature basisu::packed_uint<2> m_ver; // Baseline file version basisu::packed_uint<2> m_header_size; // Header size in bytes, sizeof(basis_file_header) - basisu::packed_uint<2> m_header_crc16; // crc16 of the remaining header data + basisu::packed_uint<2> m_header_crc16; // CRC16 of the remaining header data basisu::packed_uint<4> m_data_size; // The total size of all data after the header basisu::packed_uint<2> m_data_crc16; // The CRC16 of all data after the header - basisu::packed_uint<3> m_total_slices; // The total # of compressed slices (1 slice per image, or 2 for alpha basis files) + basisu::packed_uint<3> m_total_slices; // The total # of compressed slices (1 slice per image, or 2 for alpha .basis files) basisu::packed_uint<3> m_total_images; // The total # of images diff --git a/transcoder/basisu_global_selector_palette.h b/transcoder/basisu_global_selector_palette.h index 686393b..8bedf94 100644 --- a/transcoder/basisu_global_selector_palette.h +++ b/transcoder/basisu_global_selector_palette.h @@ -1,5 +1,7 @@ // basisu_global_selector_palette.h // Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved. +// +// TODO: NONE of this is used in .basis/.ktx2 files. It will be deleted soon. // // 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 269bbb8..29eb3c0 100644 --- a/transcoder/basisu_transcoder.cpp +++ b/transcoder/basisu_transcoder.cpp @@ -39,56 +39,64 @@ #define BASISD_SUPPORTED_BASIS_VERSION (0x13) +#ifndef BASISD_SUPPORT_KTX2 + #error Must have defined BASISD_SUPPORT_KTX2 +#endif + +#ifndef BASISD_SUPPORT_KTX2_ZSTD +#error Must have defined BASISD_SUPPORT_KTX2_ZSTD +#endif + // Set to 1 for fuzz testing. This will disable all CRC16 checks on headers and compressed data. #ifndef BASISU_NO_HEADER_OR_DATA_CRC16_CHECKS -#define BASISU_NO_HEADER_OR_DATA_CRC16_CHECKS 0 + #define BASISU_NO_HEADER_OR_DATA_CRC16_CHECKS 0 #endif #ifndef BASISD_SUPPORT_DXT1 -#define BASISD_SUPPORT_DXT1 1 + #define BASISD_SUPPORT_DXT1 1 #endif #ifndef BASISD_SUPPORT_DXT5A -#define BASISD_SUPPORT_DXT5A 1 + #define BASISD_SUPPORT_DXT5A 1 #endif // Disable all BC7 transcoders if necessary (useful when cross compiling to Javascript) #if defined(BASISD_SUPPORT_BC7) && !BASISD_SUPPORT_BC7 #ifndef BASISD_SUPPORT_BC7_MODE5 - #define BASISD_SUPPORT_BC7_MODE5 0 + #define BASISD_SUPPORT_BC7_MODE5 0 #endif #endif // !BASISD_SUPPORT_BC7 // 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 + #define BASISD_SUPPORT_BC7_MODE5 1 #endif #ifndef BASISD_SUPPORT_PVRTC1 -#define BASISD_SUPPORT_PVRTC1 1 + #define BASISD_SUPPORT_PVRTC1 1 #endif #ifndef BASISD_SUPPORT_ETC2_EAC_A8 -#define BASISD_SUPPORT_ETC2_EAC_A8 1 + #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 + #define BASISD_SUPPORT_UASTC 1 #endif #ifndef BASISD_SUPPORT_ASTC -#define BASISD_SUPPORT_ASTC 1 + #define BASISD_SUPPORT_ASTC 1 #endif // Note that if BASISD_SUPPORT_ATC is enabled, BASISD_SUPPORT_DXT5A should also be enabled for alpha support. #ifndef BASISD_SUPPORT_ATC -#define BASISD_SUPPORT_ATC 1 + #define BASISD_SUPPORT_ATC 1 #endif // Support for ETC2 EAC R11 and ETC2 EAC RG11 #ifndef BASISD_SUPPORT_ETC2_EAC_RG11 -#define BASISD_SUPPORT_ETC2_EAC_RG11 1 + #define BASISD_SUPPORT_ETC2_EAC_RG11 1 #endif // If BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY is 1, opaque blocks will be transcoded to ASTC at slightly higher quality (higher than BC1), but the transcoder tables will be 2x as large. @@ -104,23 +112,23 @@ #endif #ifndef BASISD_SUPPORT_FXT1 -#define BASISD_SUPPORT_FXT1 1 + #define BASISD_SUPPORT_FXT1 1 #endif #ifndef BASISD_SUPPORT_PVRTC2 -#define BASISD_SUPPORT_PVRTC2 1 + #define BASISD_SUPPORT_PVRTC2 1 #endif #if BASISD_SUPPORT_PVRTC2 -#if !BASISD_SUPPORT_ATC -#error BASISD_SUPPORT_ATC must be 1 if BASISD_SUPPORT_PVRTC2 is 1 -#endif + #if !BASISD_SUPPORT_ATC + #error BASISD_SUPPORT_ATC must be 1 if BASISD_SUPPORT_PVRTC2 is 1 + #endif #endif #if BASISD_SUPPORT_ATC -#if !BASISD_SUPPORT_DXT5A -#error BASISD_SUPPORT_DXT5A must be 1 if BASISD_SUPPORT_ATC is 1 -#endif + #if !BASISD_SUPPORT_DXT5A + #error BASISD_SUPPORT_DXT5A must be 1 if BASISD_SUPPORT_ATC is 1 + #endif #endif #define BASISD_WRITE_NEW_BC7_MODE5_TABLES 0 @@ -131,7 +139,16 @@ #define BASISD_WRITE_NEW_ETC2_EAC_R11_TABLES 0 #ifndef BASISD_ENABLE_DEBUG_FLAGS -#define BASISD_ENABLE_DEBUG_FLAGS 0 + #define BASISD_ENABLE_DEBUG_FLAGS 0 +#endif + +// If KTX2 support is enabled, we may need Zstd for decompression of supercompressed UASTC files. Include this header. +#if BASISD_SUPPORT_KTX2 + // If BASISD_SUPPORT_KTX2_ZSTD is 0, UASTC files compressed with Zstd cannot be loaded. + #if BASISD_SUPPORT_KTX2_ZSTD + // We only use two Zstd API's: ZSTD_decompress() and ZSTD_isError() + #include "../zstd/zstd.h" + #endif #endif namespace basisu @@ -1900,13 +1917,14 @@ namespace basist #if BASISD_SUPPORT_UASTC void uastc_init(); #endif + + static bool g_transcoder_initialized; // 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. void basisu_transcoder_init() { - static bool s_initialized; - if (s_initialized) + if (g_transcoder_initialized) { BASISU_DEVEL_ERROR("basisu_transcoder::basisu_transcoder_init: Called more than once\n"); return; @@ -2026,7 +2044,7 @@ namespace basist transcoder_init_pvrtc2(); #endif - s_initialized = true; + g_transcoder_initialized = true; } #if BASISD_SUPPORT_DXT1 @@ -7898,6 +7916,13 @@ namespace basist BASISU_NOTE_UNUSED(transcode_alpha); BASISU_NOTE_UNUSED(pAlpha_blocks); + assert(g_transcoder_initialized); + if (!g_transcoder_initialized) + { + BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: Transcoder not globally initialized.\n"); + return false; + } + if (!pState) pState = &m_def_state; @@ -9356,6 +9381,13 @@ namespace basist BASISU_NOTE_UNUSED(pState); BASISU_NOTE_UNUSED(bc1_allow_threecolor_blocks); + assert(g_transcoder_initialized); + if (!g_transcoder_initialized) + { + BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_slice: Transcoder not globally initialized.\n"); + return false; + } + #if BASISD_SUPPORT_UASTC const uint32_t total_blocks = num_blocks_x * num_blocks_y; @@ -16685,9 +16717,888 @@ namespace basist #endif // #if BASISD_SUPPORT_UASTC +// ------------------------------------------------------------------------------------------------------ +// KTX2 +// ------------------------------------------------------------------------------------------------------ + +#if BASISD_SUPPORT_KTX2 + const uint8_t g_ktx2_file_identifier[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; + + ktx2_transcoder::ktx2_transcoder(basist::etc1_global_selector_codebook* pGlobal_sel_codebook) : + m_etc1s_transcoder(pGlobal_sel_codebook) + { + clear(); + } + + void ktx2_transcoder::clear() + { + m_pData = nullptr; + m_data_size = 0; + + memset(&m_header, 0, sizeof(m_header)); + m_levels.clear(); + m_dfd.clear(); + m_key_values.clear(); + memset(&m_etc1s_header, 0, sizeof(m_etc1s_header)); + m_etc1s_image_descs.clear(); + + m_format = basist::basis_tex_format::cETC1S; + + m_dfd_color_model = 0; + m_dfd_color_prims = KTX2_DF_PRIMARIES_UNSPECIFIED; + m_dfd_transfer_func = 0; + m_dfd_flags = 0; + m_dfd_samples = 0; + m_dfd_chan0 = KTX2_DF_CHANNEL_UASTC_RGB; + m_dfd_chan1 = KTX2_DF_CHANNEL_UASTC_RGB; + + m_etc1s_transcoder.clear(); + + m_def_transcoder_state.clear(); + + m_has_alpha = false; + m_is_video = false; + } + + bool ktx2_transcoder::init(const void* pData, uint32_t data_size) + { + clear(); + + if (!pData) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: pData is nullptr\n"); + assert(0); + return false; + } + + if (data_size <= sizeof(ktx2_header)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: File is impossibly too small to be a valid KTX2 file\n"); + return false; + } + + if (memcmp(pData, g_ktx2_file_identifier, sizeof(g_ktx2_file_identifier)) != 0) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: KTX2 file identifier is not present\n"); + return false; + } + + m_pData = static_cast(pData); + m_data_size = data_size; + + memcpy(&m_header, pData, sizeof(m_header)); + + // We only support UASTC and ETC1S + if (m_header.m_vk_format != KTX2_VK_FORMAT_UNDEFINED) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: KTX2 file must be in ETC1S or UASTC format\n"); + return false; + } + + // 3.3: "When format is VK_FORMAT_UNDEFINED, typeSize must equal 1." + if (m_header.m_type_size != 1) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid type_size\n"); + return false; + } + + // We only currently support 2D textures (plain, cubemapped, or texture array), which is by far the most common use case. + // The BasisU library does not support 1D or 3D textures at all. + if ((m_header.m_pixel_width < 1) || (m_header.m_pixel_height < 1) || (m_header.m_pixel_depth > 0)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Only 2D or cubemap textures are supported\n"); + return false; + } + + // Face count must be 1 or 6 + if ((m_header.m_face_count != 1) && (m_header.m_face_count != 6)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid face count, file is corrupted or invalid\n"); + return false; + } + + if (m_header.m_face_count > 1) + { + // 3.4: Make sure cubemaps are square. + if (m_header.m_pixel_width != m_header.m_pixel_height) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Cubemap is not square\n"); + return false; + } + } + + // 3.7 levelCount: "levelCount=0 is allowed, except for block-compressed formats" + if (m_header.m_level_count < 1) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level count\n"); + return false; + } + + // Sanity check the level count. + if (m_header.m_level_count > KTX2_MAX_SUPPORTED_LEVEL_COUNT) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Too many levels or file is corrupted or invalid\n"); + return false; + } + + if (m_header.m_supercompression_scheme > KTX2_SS_ZSTANDARD) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid/unsupported supercompression or file is corrupted or invalid\n"); + return false; + } + + if (m_header.m_supercompression_scheme == KTX2_SS_BASISLZ) + { + if (m_header.m_sgd_byte_length <= sizeof(ktx2_etc1s_global_data_header)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Supercompression global data is too small\n"); + return false; + } + + if (m_header.m_sgd_byte_offset < sizeof(ktx2_header)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Supercompression global data offset is too low\n"); + return false; + } + + if (m_header.m_sgd_byte_offset + m_header.m_sgd_byte_length > m_data_size) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Supercompression global data offset and/or length is too high\n"); + return false; + } + } + + if (!m_levels.try_resize(m_header.m_level_count)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Out of memory\n"); + return false; + } + + const uint32_t level_index_size_in_bytes = basisu::maximum(1U, (uint32_t)m_header.m_level_count) * sizeof(ktx2_level_index); + + if ((sizeof(ktx2_header) + level_index_size_in_bytes) > m_data_size) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: File is too small (can't read level index array)\n"); + return false; + } + + memcpy(&m_levels[0], m_pData + sizeof(ktx2_header), level_index_size_in_bytes); + + // Sanity check the level offsets and byte sizes + for (uint32_t i = 0; i < m_levels.size(); i++) + { + if (m_levels[i].m_byte_offset < sizeof(ktx2_header)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level offset (too low)\n"); + return false; + } + + if (!m_levels[i].m_byte_length) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level byte length\n"); + } + + if ((m_levels[i].m_byte_offset + m_levels[i].m_byte_length) > m_data_size) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level offset and/or length\n"); + return false; + } + + const uint64_t MAX_SANE_LEVEL_UNCOMP_SIZE = 2048ULL * 1024ULL * 1024ULL; + + if (m_levels[i].m_uncompressed_byte_length >= MAX_SANE_LEVEL_UNCOMP_SIZE) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level offset (too large)\n"); + return false; + } + + if (m_header.m_supercompression_scheme == KTX2_SS_BASISLZ) + { + if (m_levels[i].m_uncompressed_byte_length) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid uncompressed length (0)\n"); + return false; + } + } + else if (m_header.m_supercompression_scheme >= KTX2_SS_ZSTANDARD) + { + if (!m_levels[i].m_uncompressed_byte_length) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid uncompressed length (1)\n"); + return false; + } + } + } + + const uint32_t DFD_MINIMUM_SIZE = 44, DFD_MAXIMUM_SIZE = 60; + if ((m_header.m_dfd_byte_length != DFD_MINIMUM_SIZE) && (m_header.m_dfd_byte_length != DFD_MAXIMUM_SIZE)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Unsupported DFD size\n"); + return false; + } + + if (((m_header.m_dfd_byte_offset + m_header.m_dfd_byte_length) > m_data_size) || (m_header.m_dfd_byte_offset < sizeof(ktx2_header))) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid DFD offset and/or length\n"); + return false; + } + + const uint8_t* pDFD = m_pData + m_header.m_dfd_byte_offset; + + if (!m_dfd.try_resize(m_header.m_dfd_byte_length)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Out of memory\n"); + return false; + } + + memcpy(m_dfd.data(), pDFD, m_header.m_dfd_byte_length); + + // This is all hard coded for only ETC1S and UASTC. + uint32_t dfd_total_size = basisu::read_le_dword(pDFD); + + // 3.10.3: Sanity check + if (dfd_total_size != m_header.m_dfd_byte_length) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: DFD size validation failed (1)\n"); + return false; + } + + // 3.10.3: More sanity checking + if (m_header.m_kvd_byte_length) + { + if (dfd_total_size != m_header.m_kvd_byte_offset - m_header.m_dfd_byte_offset) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: DFD size validation failed (2)\n"); + return false; + } + } + + const uint32_t dfd_bits = basisu::read_le_dword(pDFD + 3 * sizeof(uint32_t)); + const uint32_t sample_channel0 = basisu::read_le_dword(pDFD + 7 * sizeof(uint32_t)); + + m_dfd_color_model = dfd_bits & 255; + m_dfd_color_prims = (ktx2_df_color_primaries)((dfd_bits >> 8) & 255); + m_dfd_transfer_func = (dfd_bits >> 16) & 255; + m_dfd_flags = (dfd_bits >> 24) & 255; + + // See 3.10.1.Restrictions + if ((m_dfd_transfer_func != KTX2_KHR_DF_TRANSFER_LINEAR) && (m_dfd_transfer_func != KTX2_KHR_DF_TRANSFER_SRGB)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid DFD transfer function\n"); + return false; + } + + if (m_dfd_color_model == KTX2_KDF_DF_MODEL_ETC1S) + { + m_format = basist::basis_tex_format::cETC1S; + + // 3.10.2: "Whether the image has 1 or 2 slices can be determined from the DFD’s sample count." + // If m_has_alpha is true it may be 2-channel RRRG or 4-channel RGBA, but we let the caller deal with that. + m_has_alpha = (m_header.m_dfd_byte_length == 60); + + m_dfd_samples = m_has_alpha ? 2 : 1; + m_dfd_chan0 = (ktx2_df_channel_id)((sample_channel0 >> 24) & 15); + + if (m_has_alpha) + { + const uint32_t sample_channel1 = basisu::read_le_dword(pDFD + 11 * sizeof(uint32_t)); + m_dfd_chan1 = (ktx2_df_channel_id)((sample_channel1 >> 24) & 15); + } + } + else if (m_dfd_color_model == KTX2_KDF_DF_MODEL_UASTC) + { + m_format = basist::basis_tex_format::cUASTC4x4; + + m_dfd_samples = 1; + m_dfd_chan0 = (ktx2_df_channel_id)((sample_channel0 >> 24) & 15); + + // We're assuming "DATA" means RGBA so it has alpha. + m_has_alpha = (m_dfd_chan0 == KTX2_DF_CHANNEL_UASTC_RGBA) || (m_dfd_chan0 == KTX2_DF_CHANNEL_UASTC_RRRG); + } + else + { + // Unsupported DFD color model. + BASISU_DEVEL_ERROR("ktx2_transcoder::init: Unsupported DFD color model\n"); + return false; + } + + if (!read_key_values()) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::init: read_key_values() failed\n"); + return false; + } + + // Check for a KTXanimData key + for (uint32_t i = 0; i < m_key_values.size(); i++) + { + if (strcmp(reinterpret_cast(m_key_values[i].m_key.data()), "KTXanimData") == 0) + { + m_is_video = true; + break; + } + } + + return true; + } + + uint32_t ktx2_transcoder::get_etc1s_image_descs_image_flags(uint32_t level_index, uint32_t layer_index, uint32_t face_index) const + { + const uint32_t etc1s_image_index = + (level_index * basisu::maximum(m_header.m_layer_count, 1) * m_header.m_face_count) + + layer_index * m_header.m_face_count + + face_index; + + if (etc1s_image_index >= get_etc1s_image_descs().size()) + { + assert(0); + return 0; + } + + return get_etc1s_image_descs()[etc1s_image_index].m_image_flags; + } + + const basisu::uint8_vec* ktx2_transcoder::find_key(const std::string& key_name) const + { + for (uint32_t i = 0; i < m_key_values.size(); i++) + if (strcmp((const char *)m_key_values[i].m_key.data(), key_name.c_str()) == 0) + return &m_key_values[i].m_value; + + return nullptr; + } + + bool ktx2_transcoder::start_transcoding() + { + if (!m_pData) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::start_transcoding: Must call init() first\n"); + return false; + } + + if (m_header.m_supercompression_scheme == KTX2_SS_BASISLZ) + { + // Check if we've already decompressed the ETC1S global data. If so don't unpack it again. + if (!m_etc1s_transcoder.get_endpoints().empty()) + return true; + + if (!decompress_etc1s_global_data()) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::start_transcoding: decompress_etc1s_global_data() failed\n"); + return false; + } + + if (!m_is_video) + { + // See if there are any P-frames. If so it must be a video, even if there wasn't a KTXanimData key. + // Video cannot be a cubemap, and it must be a texture array. + if ((m_header.m_face_count == 1) && (m_header.m_layer_count > 1)) + { + for (uint32_t i = 0; i < m_etc1s_image_descs.size(); i++) + { + if (m_etc1s_image_descs[i].m_image_flags & KTX2_IMAGE_IS_P_FRAME) + { + m_is_video = true; + break; + } + } + } + } + } + else if (m_header.m_supercompression_scheme == KTX2_SS_ZSTANDARD) + { +#if !BASISD_SUPPORT_KTX2_ZSTD + BASISU_DEVEL_ERROR("ktx2_transcoder::start_transcoding: File uses zstd supercompression, but zstd support was not enabled at compilation time (BASISD_SUPPORT_KTX2_ZSTD == 0)\n"); + return false; +#endif + } + + return true; + } + + bool ktx2_transcoder::get_image_level_info(ktx2_image_level_info& level_info, uint32_t level_index, uint32_t layer_index, uint32_t face_index) const + { + if (level_index >= m_levels.size()) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::get_image_level_info: level_index >= m_levels.size()\n"); + return false; + } + + if (m_header.m_face_count > 1) + { + if (face_index >= 6) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::get_image_level_info: face_index >= 6\n"); + return false; + } + } + else if (face_index != 0) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::get_image_level_info: face_index != 0\n"); + return false; + } + + if (layer_index >= basisu::maximum(m_header.m_layer_count, 1)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::get_image_level_info: layer_index >= maximum(m_header.m_layer_count, 1)\n"); + return false; + } + + const uint32_t level_width = basisu::maximum(m_header.m_pixel_width >> level_index, 1); + const uint32_t level_height = basisu::maximum(m_header.m_pixel_height >> level_index, 1); + const uint32_t num_blocks_x = (level_width + 3) >> 2; + const uint32_t num_blocks_y = (level_height + 3) >> 2; + + level_info.m_face_index = face_index; + level_info.m_layer_index = layer_index; + level_info.m_level_index = level_index; + level_info.m_orig_width = level_width; + level_info.m_orig_height = level_height; + level_info.m_width = num_blocks_x * 4; + level_info.m_height = num_blocks_y * 4; + level_info.m_num_blocks_x = num_blocks_x; + level_info.m_num_blocks_y = num_blocks_y; + level_info.m_total_blocks = num_blocks_x * num_blocks_y; + level_info.m_alpha_flag = m_has_alpha; + level_info.m_iframe_flag = false; + if (m_etc1s_image_descs.size()) + { + const uint32_t etc1s_image_index = + (level_index * basisu::maximum(m_header.m_layer_count, 1) * m_header.m_face_count) + + layer_index * m_header.m_face_count + + face_index; + + level_info.m_iframe_flag = (m_etc1s_image_descs[etc1s_image_index].m_image_flags & KTX2_IMAGE_IS_P_FRAME) == 0; + } + + return true; + } + + bool ktx2_transcoder::transcode_image_level( + uint32_t level_index, uint32_t layer_index, uint32_t face_index, + void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels, + basist::transcoder_texture_format fmt, + uint32_t decode_flags, uint32_t output_row_pitch_in_blocks_or_pixels, uint32_t output_rows_in_pixels, int channel0, int channel1, + ktx2_transcoder_state* pState) + { + if (!m_pData) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: Must call init() first\n"); + return false; + } + + if (!pState) + pState = &m_def_transcoder_state; + + if (level_index >= m_levels.size()) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: level_index >= m_levels.size()\n"); + return false; + } + + if (m_header.m_face_count > 1) + { + if (face_index >= 6) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: face_index >= 6\n"); + return false; + } + } + else if (face_index != 0) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: face_index != 0\n"); + return false; + } + + if (layer_index >= basisu::maximum(m_header.m_layer_count, 1)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: layer_index >= maximum(m_header.m_layer_count, 1)\n"); + return false; + } + + const uint8_t* pComp_level_data = m_pData + m_levels[level_index].m_byte_offset; + uint64_t comp_level_data_size = m_levels[level_index].m_byte_length; + + const uint8_t* pUncomp_level_data = pComp_level_data; + uint64_t uncomp_level_data_size = comp_level_data_size; + + if (uncomp_level_data_size > UINT32_MAX) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: uncomp_level_data_size > UINT32_MAX\n"); + return false; + } + + if (m_header.m_supercompression_scheme == KTX2_SS_ZSTANDARD) + { + // Check if we've already decompressed this level's supercompressed data. + if ((int)level_index != pState->m_uncomp_data_level_index) + { + // Uncompress the entire level's supercompressed data. + if (!decompress_level_data(level_index, pState->m_level_uncomp_data)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: decompress_level_data() failed\n"); + return false; + } + pState->m_uncomp_data_level_index = level_index; + } + + pUncomp_level_data = pState->m_level_uncomp_data.data(); + uncomp_level_data_size = pState->m_level_uncomp_data.size(); + } + + const uint32_t level_width = basisu::maximum(m_header.m_pixel_width >> level_index, 1); + const uint32_t level_height = basisu::maximum(m_header.m_pixel_height >> level_index, 1); + const uint32_t num_blocks_x = (level_width + 3) >> 2; + const uint32_t num_blocks_y = (level_height + 3) >> 2; + + if (m_format == basist::basis_tex_format::cETC1S) + { + // Ensure start_transcoding() was called. + if (m_etc1s_transcoder.get_endpoints().empty()) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: must call start_transcoding() first\n"); + return false; + } + + const uint32_t etc1s_image_index = + (level_index * basisu::maximum(m_header.m_layer_count, 1) * m_header.m_face_count) + + layer_index * m_header.m_face_count + + face_index; + + // Sanity check + if (etc1s_image_index >= m_etc1s_image_descs.size()) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: etc1s_image_index >= m_etc1s_image_descs.size()\n"); + assert(0); + return false; + } + + if (static_cast(m_data_size) != m_data_size) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: File is too large\n"); + return false; + } + + const ktx2_etc1s_image_desc& image_desc = m_etc1s_image_descs[etc1s_image_index]; + + if (!m_etc1s_transcoder.transcode_image(fmt, + pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, m_pData, static_cast(m_data_size), + num_blocks_x, num_blocks_y, level_width, level_height, + level_index, + m_levels[level_index].m_byte_offset + image_desc.m_rgb_slice_byte_offset, image_desc.m_rgb_slice_byte_length, + image_desc.m_alpha_slice_byte_length ? (m_levels[level_index].m_byte_offset + image_desc.m_alpha_slice_byte_offset) : 0, image_desc.m_alpha_slice_byte_length, + decode_flags, m_has_alpha, + m_is_video, output_row_pitch_in_blocks_or_pixels, &pState->m_transcoder_state, output_rows_in_pixels)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: ETC1S transcode_image() failed, this is either a bug or the file is corrupted/invalid\n"); + return false; + } + } + else if (m_format == basist::basis_tex_format::cUASTC4x4) + { + // Compute length and offset to uncompressed 2D UASTC texture data, given the face/layer indices. + assert(uncomp_level_data_size == m_levels[level_index].m_uncompressed_byte_length); + const uint32_t total_2D_image_size = num_blocks_x * num_blocks_y * KTX2_UASTC_BLOCK_SIZE; + + const uint32_t uncomp_ofs = (layer_index * m_header.m_face_count + face_index) * total_2D_image_size; + + // Sanity checks + if (uncomp_ofs >= uncomp_level_data_size) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: uncomp_ofs >= total_2D_image_size\n"); + return false; + } + + if ((uncomp_level_data_size - uncomp_ofs) < total_2D_image_size) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: (uncomp_level_data_size - uncomp_ofs) < total_2D_image_size\n"); + return false; + } + + if (!m_uastc_transcoder.transcode_image(fmt, + pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, + (const uint8_t*)pUncomp_level_data + uncomp_ofs, (uint32_t)total_2D_image_size, num_blocks_x, num_blocks_y, level_width, level_height, level_index, + 0, (uint32_t)total_2D_image_size, + decode_flags, m_has_alpha, m_is_video, output_row_pitch_in_blocks_or_pixels, nullptr, output_rows_in_pixels, channel0, channel1)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: UASTC transcode_image() failed, this is either a bug or the file is corrupted/invalid\n"); + return false; + } + } + else + { + // Shouldn't get here. + BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: Internal error\n"); + assert(0); + return false; + } + + return true; + } + + bool ktx2_transcoder::decompress_level_data(uint32_t level_index, basisu::uint8_vec& uncomp_data) + { + const uint8_t* pComp_data = m_levels[level_index].m_byte_offset + m_pData; + const uint64_t comp_size = m_levels[level_index].m_byte_length; + + const uint64_t uncomp_size = m_levels[level_index].m_uncompressed_byte_length; + + if (((size_t)comp_size) != comp_size) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Compressed data too large\n"); + return false; + } + if (((size_t)uncomp_size) != uncomp_size) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Uncompressed data too large\n"); + return false; + } + + if (!uncomp_data.try_resize(uncomp_size)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Out of memory\n"); + return false; + } + + if (m_header.m_supercompression_scheme == KTX2_SS_ZSTANDARD) + { +#if BASISD_SUPPORT_KTX2_ZSTD + size_t actualUncompSize = ZSTD_decompress(uncomp_data.data(), (size_t)uncomp_size, pComp_data, (size_t)comp_size); + if (ZSTD_isError(actualUncompSize)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Zstd decompression failed, file is invalid or corrupted\n"); + return false; + } + if (actualUncompSize != uncomp_size) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Zstd decompression returned too few bytes, file is invalid or corrupted\n"); + return false; + } +#else + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: File uses Zstd supercompression, but Zstd support was not enabled at compile time (BASISD_SUPPORT_KTX2_ZSTD is 0)\n"); + return false; +#endif + } + + return true; + } + + bool ktx2_transcoder::decompress_etc1s_global_data() + { + // Note: we don't actually support 3D textures in here yet + //uint32_t layer_pixel_depth = basisu::maximum(m_header.m_pixel_depth, 1); + //for (uint32_t i = 1; i < m_header.m_level_count; i++) + // layer_pixel_depth += basisu::maximum(m_header.m_pixel_depth >> i, 1); + + const uint32_t image_count = basisu::maximum(m_header.m_layer_count, 1) * m_header.m_face_count * m_header.m_level_count; + assert(image_count); + + const uint8_t* pSrc = m_pData + m_header.m_sgd_byte_offset; + + memcpy(&m_etc1s_header, pSrc, sizeof(ktx2_etc1s_global_data_header)); + pSrc += sizeof(ktx2_etc1s_global_data_header); + + if ((!m_etc1s_header.m_endpoints_byte_length) || (!m_etc1s_header.m_selectors_byte_length) || (!m_etc1s_header.m_tables_byte_length)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: Invalid ETC1S global data\n"); + return false; + } + + if ((!m_etc1s_header.m_endpoint_count) || (!m_etc1s_header.m_selector_count)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: endpoint and/or selector count is 0, file is invalid or corrupted\n"); + return false; + } + + // Sanity check the ETC1S header. + if ((sizeof(ktx2_etc1s_global_data_header) + + sizeof(ktx2_etc1s_image_desc) * image_count + + m_etc1s_header.m_endpoints_byte_length + + m_etc1s_header.m_selectors_byte_length + + m_etc1s_header.m_tables_byte_length + + m_etc1s_header.m_extended_byte_length) > m_header.m_sgd_byte_length) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: SGD byte length is too small, file is invalid or corrupted\n"); + return false; + } + + if (!m_etc1s_image_descs.try_resize(image_count)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: Out of memory\n"); + return false; + } + + memcpy(m_etc1s_image_descs.data(), pSrc, sizeof(ktx2_etc1s_image_desc) * image_count); + pSrc += sizeof(ktx2_etc1s_image_desc) * image_count; + + // Sanity check the ETC1S image descs + for (uint32_t i = 0; i < image_count; i++) + { + // m_etc1s_transcoder.transcode_image() will validate the slice offsets/lengths before transcoding. + + if (!m_etc1s_image_descs[i].m_rgb_slice_byte_length) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: ETC1S image descs sanity check failed (1)\n"); + return false; + } + + if (m_has_alpha) + { + if (!m_etc1s_image_descs[i].m_alpha_slice_byte_length) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: ETC1S image descs sanity check failed (2)\n"); + return false; + } + } + } + + const uint8_t* pEndpoint_data = pSrc; + const uint8_t* pSelector_data = pSrc + m_etc1s_header.m_endpoints_byte_length; + const uint8_t* pTables_data = pSrc + m_etc1s_header.m_endpoints_byte_length + m_etc1s_header.m_selectors_byte_length; + + if (!m_etc1s_transcoder.decode_tables(pTables_data, m_etc1s_header.m_tables_byte_length)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: decode_tables() failed, file is invalid or corrupted\n"); + return false; + } + + if (!m_etc1s_transcoder.decode_palettes( + m_etc1s_header.m_endpoint_count, pEndpoint_data, m_etc1s_header.m_endpoints_byte_length, + m_etc1s_header.m_selector_count, pSelector_data, m_etc1s_header.m_selectors_byte_length)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: decode_palettes() failed, file is likely corrupted\n"); + return false; + } + + return true; + } + + bool ktx2_transcoder::read_key_values() + { + if (!m_header.m_kvd_byte_length) + { + if (m_header.m_kvd_byte_offset) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Invalid KVD byte offset (it should be zero when the length is zero)\n"); + return false; + } + + return true; + } + + if (m_header.m_kvd_byte_offset < sizeof(ktx2_header)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Invalid KVD byte offset\n"); + return false; + } + + if ((m_header.m_kvd_byte_offset + m_header.m_kvd_byte_length) > m_data_size) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Invalid KVD byte offset and/or length\n"); + return false; + } + + const uint8_t* pSrc = m_pData + m_header.m_kvd_byte_offset; + uint32_t src_left = m_header.m_kvd_byte_length; + + if (!m_key_values.try_reserve(8)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n"); + return false; + } + + while (src_left > sizeof(uint32_t)) + { + uint32_t l = basisu::read_le_dword(pSrc); + + pSrc += sizeof(uint32_t); + src_left -= sizeof(uint32_t); + + if (l < 2) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Failed reading key value fields (0)\n"); + return false; + } + + if (src_left < l) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Failed reading key value fields (1)\n"); + return false; + } + + if (!m_key_values.try_resize(m_key_values.size() + 1)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n"); + return false; + } + + basisu::uint8_vec& key_data = m_key_values.back().m_key; + basisu::uint8_vec& value_data = m_key_values.back().m_value; + + do + { + if (!l) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Failed reading key value fields (2)\n"); + return false; + } + + if (!key_data.try_push_back(*pSrc++)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n"); + return false; + } + + src_left--; + l--; + + } while (key_data.back()); + + if (!value_data.try_resize(l)) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n"); + return false; + } + + if (l) + { + memcpy(value_data.data(), pSrc, l); + pSrc += l; + src_left -= l; + } + + uint32_t ofs = (uint32_t)(pSrc - m_pData) & 3; + uint32_t alignment_bytes = (4 - ofs) & 3; + + if (src_left < alignment_bytes) + { + BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Failed reading key value fields (3)\n"); + return false; + } + + pSrc += alignment_bytes; + src_left -= alignment_bytes; + } + + return true; + } + +#endif // BASISD_SUPPORT_KTX2 + + bool basisu_transcoder_supports_ktx2() + { +#if BASISD_SUPPORT_KTX2 + return true; +#else + return false; +#endif + } + + bool basisu_transcoder_supports_ktx2_zstd() + { +#if BASISD_SUPPORT_KTX2_ZSTD + return true; +#else + return false; +#endif + } + } // namespace basist - - - - - diff --git a/transcoder/basisu_transcoder.h b/transcoder/basisu_transcoder.h index dd71fdd..1dc7251 100644 --- a/transcoder/basisu_transcoder.h +++ b/transcoder/basisu_transcoder.h @@ -15,9 +15,21 @@ // limitations under the License. #pragma once +// By default KTX2 support is enabled to simplify compilation. This implies the need for the Zstandard library (which we distribute as a single source file in the "zstd" directory) by default. +// Set BASISD_SUPPORT_KTX2 to 0 to completely disable KTX2 support as well as Zstd/miniz usage which is only required for UASTC supercompression in KTX2 files. +// Also see BASISD_SUPPORT_KTX2_ZSTD in basisu_transcoder.cpp, which individually disables Zstd usage. +#ifndef BASISD_SUPPORT_KTX2 + #define BASISD_SUPPORT_KTX2 1 +#endif + +// Set BASISD_SUPPORT_KTX2_ZSTD to 0 to disable Zstd usage and KTX2 UASTC Zstd supercompression support +#ifndef BASISD_SUPPORT_KTX2_ZSTD + #define BASISD_SUPPORT_KTX2_ZSTD 1 +#endif + // Set BASISU_FORCE_DEVEL_MESSAGES to 1 to enable debug printf()'s whenever an error occurs, for easier debugging during development. #ifndef BASISU_FORCE_DEVEL_MESSAGES -#define BASISU_FORCE_DEVEL_MESSAGES 0 + #define BASISU_FORCE_DEVEL_MESSAGES 0 #endif #include "basisu_transcoder_internal.h" @@ -49,7 +61,7 @@ namespace basist cTFBC4_R = 4, // Red only, alpha slice is transcoded to output if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified cTFBC5_RG = 5, // XY: Two BC4 blocks, X=R and Y=Alpha, .basis file should have alpha data (if not Y will be all 255's) cTFBC7_RGBA = 6, // RGB or RGBA, mode 5 for ETC1S, modes (1,2,3,5,6,7) for UASTC - + // PVRTC1 4bpp (mobile, PowerVR devices) cTFPVRTC1_4_RGB = 8, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified, nearly lowest quality of any texture format. cTFPVRTC1_4_RGBA = 9, // Opaque+alpha, most useful for simple opacity maps. If .basis file doesn't have alpha cTFPVRTC1_4_RGB will be used instead. Lowest quality of any supported texture format. @@ -66,38 +78,38 @@ namespace basist // Punch-through alpha is relatively easy to support, but full alpha is harder. This format is only here for completeness so opaque-only is fine for now. // See the BASISU_USE_ORIGINAL_3DFX_FXT1_ENCODING macro in basisu_transcoder_internal.h. - cTFPVRTC2_4_RGB = 18, // Opaque-only, almost BC1 quality, much faster to transcode and supports arbitrary texture dimensions (unlike PVRTC1 RGB). - cTFPVRTC2_4_RGBA = 19, // Opaque+alpha, slower to encode than cTFPVRTC2_4_RGB. Premultiplied alpha is highly recommended, otherwise the color channel can leak into the alpha channel on transparent blocks. + cTFPVRTC2_4_RGB = 18, // Opaque-only, almost BC1 quality, much faster to transcode and supports arbitrary texture dimensions (unlike PVRTC1 RGB). + cTFPVRTC2_4_RGBA = 19, // Opaque+alpha, slower to encode than cTFPVRTC2_4_RGB. Premultiplied alpha is highly recommended, otherwise the color channel can leak into the alpha channel on transparent blocks. - cTFETC2_EAC_R11 = 20, // R only (ETC2 EAC R11 unsigned) - cTFETC2_EAC_RG11 = 21, // RG only (ETC2 EAC RG11 unsigned), R=opaque.r, G=alpha - for tangent space normal maps - - // Uncompressed (raw pixel) formats - cTFRGBA32 = 13, // 32bpp RGBA image stored in raster (not block) order in memory, R is first byte, A is last byte. - cTFRGB565 = 14, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 11 - cTFBGR565 = 15, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 0 - cTFRGBA4444 = 16, // 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 0 + cTFETC2_EAC_R11 = 20, // R only (ETC2 EAC R11 unsigned) + cTFETC2_EAC_RG11 = 21, // RG only (ETC2 EAC RG11 unsigned), R=opaque.r, G=alpha - for tangent space normal maps - cTFTotalTextureFormats = 22, + // Uncompressed (raw pixel) formats + cTFRGBA32 = 13, // 32bpp RGBA image stored in raster (not block) order in memory, R is first byte, A is last byte. + cTFRGB565 = 14, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 11 + cTFBGR565 = 15, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 0 + cTFRGBA4444 = 16, // 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 0 - // Old enums for compatibility with code compiled against previous versions - cTFETC1 = cTFETC1_RGB, - cTFETC2 = cTFETC2_RGBA, - cTFBC1 = cTFBC1_RGB, - cTFBC3 = cTFBC3_RGBA, - cTFBC4 = cTFBC4_R, - cTFBC5 = cTFBC5_RG, - - // Previously, the caller had some control over which BC7 mode the transcoder output. We've simplified this due to UASTC, which supports numerous modes. - cTFBC7_M6_RGB = cTFBC7_RGBA, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. Highest quality of all the non-ETC1 formats. - cTFBC7_M5_RGBA = cTFBC7_RGBA, // Opaque+alpha, alpha channel will be opaque for opaque .basis files - cTFBC7_M6_OPAQUE_ONLY = cTFBC7_RGBA, - cTFBC7_M5 = cTFBC7_RGBA, - cTFBC7_ALT = 7, + cTFTotalTextureFormats = 22, - cTFASTC_4x4 = cTFASTC_4x4_RGBA, - - cTFATC_RGBA_INTERPOLATED_ALPHA = cTFATC_RGBA, + // Old enums for compatibility with code compiled against previous versions + cTFETC1 = cTFETC1_RGB, + cTFETC2 = cTFETC2_RGBA, + cTFBC1 = cTFBC1_RGB, + cTFBC3 = cTFBC3_RGBA, + cTFBC4 = cTFBC4_R, + cTFBC5 = cTFBC5_RG, + + // Previously, the caller had some control over which BC7 mode the transcoder output. We've simplified this due to UASTC, which supports numerous modes. + cTFBC7_M6_RGB = cTFBC7_RGBA, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. Highest quality of all the non-ETC1 formats. + cTFBC7_M5_RGBA = cTFBC7_RGBA, // Opaque+alpha, alpha channel will be opaque for opaque .basis files + cTFBC7_M6_OPAQUE_ONLY = cTFBC7_RGBA, + cTFBC7_M5 = cTFBC7_RGBA, + cTFBC7_ALT = 7, + + cTFASTC_4x4 = cTFASTC_4x4_RGBA, + + cTFATC_RGBA_INTERPOLATED_ALPHA = cTFATC_RGBA, }; // For compressed texture formats, this returns the # of bytes per block. For uncompressed, it returns the # of bytes per pixel. @@ -107,7 +119,7 @@ namespace basist // Returns format's name in ASCII const char* basis_get_format_name(transcoder_texture_format fmt); - // Returns block format name in ASCII + // Returns block format name in ASCII const char* basis_get_block_format_name(block_format fmt); // Returns true if the format supports an alpha channel. @@ -118,16 +130,16 @@ namespace basist // Returns the texture type's name in ASCII. const char* basis_get_texture_type_name(basis_texture_type tex_type); - + // Returns true if the transcoder texture type is an uncompressed (raw pixel) format. bool basis_transcoder_format_is_uncompressed(transcoder_texture_format tex_type); // Returns the # of bytes per pixel for uncompressed formats, or 0 for block texture formats. uint32_t basis_get_uncompressed_bytes_per_pixel(transcoder_texture_format fmt); - + // Returns the block width for the specified texture format, which is currently either 4 or 8 for FXT1. uint32_t basis_get_block_width(transcoder_texture_format tex_type); - + // Returns the block height for the specified texture format, which is currently always 4. uint32_t basis_get_block_height(transcoder_texture_format tex_type); @@ -142,7 +154,7 @@ namespace basist uint32_t output_row_pitch_in_blocks_or_pixels, uint32_t output_rows_in_pixels, uint32_t total_slice_blocks); - + class basisu_transcoder; // This struct holds all state used during transcoding. For video, it needs to persist between image transcodes (it holds the previous frame). @@ -156,34 +168,46 @@ namespace basist }; basisu::vector m_block_endpoint_preds[2]; - + enum { cMaxPrevFrameLevels = 16 }; basisu::vector m_prev_frame_indices[2][cMaxPrevFrameLevels]; // [alpha_flag][level_index] + + void clear() + { + for (uint32_t i = 0; i < 2; i++) + { + m_block_endpoint_preds[i].clear(); + + for (uint32_t j = 0; j < cMaxPrevFrameLevels; j++) + m_prev_frame_indices[i][j].clear(); + } + } }; - + // Low-level helper class that does the actual transcoding. class basisu_lowlevel_etc1s_transcoder { friend class basisu_transcoder; - + public: - basisu_lowlevel_etc1s_transcoder(const basist::etc1_global_selector_codebook *pGlobal_sel_codebook); + 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; } + 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); + 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); - bool decode_tables(const uint8_t *pTable_data, uint32_t table_data_size); + bool decode_tables(const uint8_t* pTable_data, uint32_t table_data_size); - bool transcode_slice(void *pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t *pImage_data, uint32_t image_data_size, block_format fmt, + bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt, uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const bool is_video, const bool is_alpha_slice, const uint32_t level_index, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0, - basisu_transcoder_state *pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0); + basisu_transcoder_state* pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0); - bool transcode_slice(void *pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t *pImage_data, uint32_t image_data_size, block_format fmt, - uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header &header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0, - basisu_transcoder_state *pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0) + bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt, + uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0, + basisu_transcoder_state* pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0) { return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt, output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, header.m_tex_type == cBASISTexTypeVideoFrames, (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0, slice_desc.m_level_index, @@ -198,7 +222,7 @@ namespace basist transcoder_texture_format target_format, void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels, const uint8_t* pCompressed_data, uint32_t compressed_data_length, - uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index, + uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index, uint32_t rgb_offset, uint32_t rgb_length, uint32_t alpha_offset, uint32_t alpha_length, uint32_t decode_flags = 0, bool basis_file_has_alpha_slices = false, @@ -220,12 +244,12 @@ namespace basist // Low-level methods typedef basisu::vector endpoint_vec; - const endpoint_vec &get_endpoints() const { return m_local_endpoints; } - - typedef basisu::vector selector_vec; - const selector_vec &get_selectors() const { return m_local_selectors; } + const endpoint_vec& get_endpoints() const { return m_local_endpoints; } - const etc1_global_selector_codebook* get_global_sel_codebook() const { return m_pGlobal_sel_codebook; } + typedef basisu::vector selector_vec; + 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; @@ -233,12 +257,12 @@ namespace basist endpoint_vec m_local_endpoints; selector_vec m_local_selectors; - const etc1_global_selector_codebook *m_pGlobal_sel_codebook; + const etc1_global_selector_codebook* m_pGlobal_sel_codebook; huffman_decoding_table m_endpoint_pred_model, m_delta_endpoint_model, m_selector_model, m_selector_history_buf_rle_model; uint32_t m_selector_history_buf_size; - + basisu_transcoder_state m_def_state; }; @@ -269,18 +293,18 @@ namespace basist public: basisu_lowlevel_uastc_transcoder(); - bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt, - uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0, - basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0); + bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt, + uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0, + basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0); bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt, uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0, basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0) - { - return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt, - output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels, - pState, output_rows_in_pixels, channel0, channel1, decode_flags); - } + { + return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt, + output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels, + pState, output_rows_in_pixels, channel0, channel1, decode_flags); + } // Container independent transcoding bool transcode_image( @@ -288,7 +312,7 @@ namespace basist void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels, const uint8_t* pCompressed_data, uint32_t compressed_data_length, uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index, - uint32_t slice_offset, uint32_t slice_length, + uint32_t slice_offset, uint32_t slice_length, uint32_t decode_flags = 0, bool has_alpha = false, bool is_video = false, @@ -297,7 +321,7 @@ namespace basist uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1); }; - + struct basisu_slice_info { uint32_t m_orig_width; @@ -315,9 +339,9 @@ namespace basist uint32_t m_slice_index; // the slice index in the .basis file uint32_t m_image_index; // the source image index originally provided to the encoder uint32_t m_level_index; // the mipmap level within this image - + uint32_t m_unpacked_slice_crc16; - + bool m_alpha_flag; // true if the slice has alpha data bool m_iframe_flag; // true if the slice is an I-Frame }; @@ -327,7 +351,7 @@ namespace basist struct basisu_image_info { uint32_t m_image_index; - uint32_t m_total_levels; + uint32_t m_total_levels; uint32_t m_orig_width; uint32_t m_orig_height; @@ -339,8 +363,8 @@ namespace basist uint32_t m_num_blocks_y; uint32_t m_total_blocks; - uint32_t m_first_slice_index; - + uint32_t m_first_slice_index; + bool m_alpha_flag; // true if the image has alpha data bool m_iframe_flag; // true if the image is an I-Frame }; @@ -360,13 +384,13 @@ namespace basist uint32_t m_num_blocks_y; uint32_t m_total_blocks; - uint32_t m_first_slice_index; + uint32_t m_first_slice_index; uint32_t m_rgb_file_ofs; uint32_t m_rgb_file_len; uint32_t m_alpha_file_ofs; uint32_t m_alpha_file_len; - + bool m_alpha_flag; // true if the image has alpha data bool m_iframe_flag; // true if the image is an I-Frame }; @@ -378,18 +402,18 @@ namespace basist uint32_t m_total_selectors; // will be 0 for UASTC or if the file uses global codebooks - uint32_t m_selector_codebook_ofs; + uint32_t m_selector_codebook_ofs; uint32_t m_selector_codebook_size; uint32_t m_total_endpoints; // will be 0 for UASTC or if the file uses global codebooks - uint32_t m_endpoint_codebook_ofs; + uint32_t m_endpoint_codebook_ofs; uint32_t m_endpoint_codebook_size; uint32_t m_tables_ofs; uint32_t m_tables_size; - uint32_t m_slices_size; + uint32_t m_slices_size; basis_texture_type m_tex_type; uint32_t m_us_per_frame; @@ -404,7 +428,7 @@ namespace basist uint32_t m_userdata1; basis_tex_format m_tex_format; // ETC1S, UASTC, etc. - + bool m_y_flipped; // true if the image was Y flipped bool m_etc1s; // true if the file is ETC1S bool m_has_alpha_slices; // true if the texture has alpha slices (for ETC1S: even slices RGB, odd slices alpha) @@ -418,67 +442,67 @@ namespace basist basisu_transcoder& operator= (const basisu_transcoder&); public: - basisu_transcoder(const etc1_global_selector_codebook *pGlobal_sel_codebook); + basisu_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook); // Validates the .basis file. This computes a crc16 over the entire file, so it's slow. - bool validate_file_checksums(const void *pData, uint32_t data_size, bool full_validation) const; + bool validate_file_checksums(const void* pData, uint32_t data_size, bool full_validation) const; // Quick header validation - no crc16 checks. - bool validate_header(const void *pData, uint32_t data_size) const; + bool validate_header(const void* pData, uint32_t data_size) const; + + basis_texture_type get_texture_type(const void* pData, uint32_t data_size) const; + bool get_userdata(const void* pData, uint32_t data_size, uint32_t& userdata0, uint32_t& userdata1) const; - basis_texture_type get_texture_type(const void *pData, uint32_t data_size) const; - bool get_userdata(const void *pData, uint32_t data_size, uint32_t &userdata0, uint32_t &userdata1) const; - // Returns the total number of images in the basis file (always 1 or more). // Note that the number of mipmap levels for each image may differ, and that images may have different resolutions. - uint32_t get_total_images(const void *pData, uint32_t data_size) const; + uint32_t get_total_images(const void* pData, uint32_t data_size) const; basis_tex_format get_tex_format(const void* pData, uint32_t data_size) const; // Returns the number of mipmap levels in an image. - uint32_t get_total_image_levels(const void *pData, uint32_t data_size, uint32_t image_index) const; - + uint32_t get_total_image_levels(const void* pData, uint32_t data_size, uint32_t image_index) const; + // Returns basic information about an image. Note that orig_width/orig_height may not be a multiple of 4. - bool get_image_level_desc(const void *pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, uint32_t &orig_width, uint32_t &orig_height, uint32_t &total_blocks) const; + bool get_image_level_desc(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, uint32_t& orig_width, uint32_t& orig_height, uint32_t& total_blocks) const; // Returns information about the specified image. - bool get_image_info(const void *pData, uint32_t data_size, basisu_image_info &image_info, uint32_t image_index) const; + bool get_image_info(const void* pData, uint32_t data_size, basisu_image_info& image_info, uint32_t image_index) const; // Returns information about the specified image's mipmap level. - bool get_image_level_info(const void *pData, uint32_t data_size, basisu_image_level_info &level_info, uint32_t image_index, uint32_t level_index) const; - + bool get_image_level_info(const void* pData, uint32_t data_size, basisu_image_level_info& level_info, uint32_t image_index, uint32_t level_index) const; + // Get a description of the basis file and low-level information about each slice. - bool get_file_info(const void *pData, uint32_t data_size, basisu_file_info &file_info) const; - + bool get_file_info(const void* pData, uint32_t data_size, basisu_file_info& file_info) const; + // start_transcoding() must be called before calling transcode_slice() or transcode_image_level(). // For ETC1S files, this call decompresses the selector/endpoint codebooks, so ideally you would only call this once per .basis file (not each image/mipmap level). - bool start_transcoding(const void *pData, uint32_t data_size); - + bool start_transcoding(const void* pData, uint32_t data_size); + bool stop_transcoding(); - + // Returns true if start_transcoding() has been called. bool get_ready_to_transcode() const { return m_ready_to_transcode; } - + // transcode_image_level() decodes a single mipmap level from the .basis file to any of the supported output texture formats. // It'll first find the slice(s) to transcode, then call transcode_slice() one or two times to decode both the color and alpha texture data (or RG texture data from two slices for BC5). // If the .basis file doesn't have alpha slices, the output alpha blocks will be set to fully opaque (all 255's). // Currently, to decode to PVRTC1 the basis texture's dimensions in pixels must be a power of 2, due to PVRTC1 format requirements. // output_blocks_buf_size_in_blocks_or_pixels should be at least the image level's total_blocks (num_blocks_x * num_blocks_y), or the total number of output pixels if fmt==cTFRGBA32. // output_row_pitch_in_blocks_or_pixels: Number of blocks or pixels per row. If 0, the transcoder uses the slice's num_blocks_x or orig_width (NOT num_blocks_x * 4). Ignored for PVRTC1 (due to texture swizzling). - // output_rows_in_pixels: Ignored unless fmt is cRGBA32. The total number of output rows in the output buffer. If 0, the transcoder assumes the slice's orig_height (NOT num_blocks_y * 4). + // output_rows_in_pixels: Ignored unless fmt is uncompressed (cRGBA32, etc.). The total number of output rows in the output buffer. If 0, the transcoder assumes the slice's orig_height (NOT num_blocks_y * 4). // Notes: // - basisu_transcoder_init() must have been called first to initialize the transcoder lookup tables before calling this function. // - This method assumes the output texture buffer is readable. In some cases to handle alpha, the transcoder will write temporary data to the output texture in // a first pass, which will be read in a second pass. bool transcode_image_level( - const void *pData, uint32_t data_size, - uint32_t image_index, uint32_t level_index, - void *pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels, + const void* pData, uint32_t data_size, + uint32_t image_index, uint32_t level_index, + void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels, transcoder_texture_format fmt, - uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, basisu_transcoder_state *pState = nullptr, uint32_t output_rows_in_pixels = 0) const; + uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0) const; // Finds the basis slice corresponding to the specified image/level/alpha params, or -1 if the slice can't be found. - int find_slice(const void *pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, bool alpha_data) const; + int find_slice(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, bool alpha_data) const; // transcode_slice() decodes a single slice from the .basis file. It's a low-level API - most likely you want to use transcode_image_level(). // This is a low-level API, and will be needed to be called multiple times to decode some texture formats (like BC3, BC5, or ETC2). @@ -489,9 +513,9 @@ namespace basist // output_rows_in_pixels: Ignored unless fmt is cRGBA32. The total number of output rows in the output buffer. If 0, the transcoder assumes the slice's orig_height (NOT num_blocks_y * 4). // Notes: // - basisu_transcoder_init() must have been called first to initialize the transcoder lookup tables before calling this function. - bool 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_stride_in_bytes, uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, basisu_transcoder_state * pState = nullptr, void* pAlpha_blocks = nullptr, + bool 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_stride_in_bytes, uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, basisu_transcoder_state* pState = nullptr, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1) const; static void write_opaque_alpha_blocks( @@ -504,9 +528,10 @@ namespace basist const basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() const { return m_lowlevel_etc1s_decoder; } basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() { return m_lowlevel_etc1s_decoder; } - + const basisu_lowlevel_uastc_transcoder& get_lowlevel_uastc_decoder() const { return m_lowlevel_uastc_decoder; } basisu_lowlevel_uastc_transcoder& get_lowlevel_uastc_decoder() { return m_lowlevel_uastc_decoder; } + private: mutable basisu_lowlevel_etc1s_transcoder m_lowlevel_etc1s_decoder; mutable basisu_lowlevel_uastc_transcoder m_lowlevel_uastc_decoder; @@ -514,13 +539,13 @@ namespace basist bool m_ready_to_transcode; int find_first_slice_index(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index) const; - + bool validate_header_quick(const void* pData, uint32_t data_size) const; }; - // basisu_transcoder_init() must be called before a .basis file can be transcoded. + // basisu_transcoder_init() MUST be called before a .basis file can be transcoded. void basisu_transcoder_init(); - + enum debug_flags_t { cDebugFlagVisCRs = 1, @@ -530,4 +555,387 @@ namespace basist uint32_t get_debug_flags(); void set_debug_flags(uint32_t f); + // ------------------------------------------------------------------------------------------------------ + // Optional .KTX2 file format support + // KTX2 reading optionally requires miniz or Zstd decompressors for supercompressed UASTC files. + // ------------------------------------------------------------------------------------------------------ +#if BASISD_SUPPORT_KTX2 +#pragma pack(push) +#pragma pack(1) + struct ktx2_header + { + uint8_t m_identifier[12]; + basisu::packed_uint<4> m_vk_format; + basisu::packed_uint<4> m_type_size; + basisu::packed_uint<4> m_pixel_width; + basisu::packed_uint<4> m_pixel_height; + basisu::packed_uint<4> m_pixel_depth; + basisu::packed_uint<4> m_layer_count; + basisu::packed_uint<4> m_face_count; + basisu::packed_uint<4> m_level_count; + basisu::packed_uint<4> m_supercompression_scheme; + basisu::packed_uint<4> m_dfd_byte_offset; + basisu::packed_uint<4> m_dfd_byte_length; + basisu::packed_uint<4> m_kvd_byte_offset; + basisu::packed_uint<4> m_kvd_byte_length; + basisu::packed_uint<8> m_sgd_byte_offset; + basisu::packed_uint<8> m_sgd_byte_length; + }; + + struct ktx2_level_index + { + basisu::packed_uint<8> m_byte_offset; + basisu::packed_uint<8> m_byte_length; + basisu::packed_uint<8> m_uncompressed_byte_length; + }; + + struct ktx2_etc1s_global_data_header + { + basisu::packed_uint<2> m_endpoint_count; + basisu::packed_uint<2> m_selector_count; + basisu::packed_uint<4> m_endpoints_byte_length; + basisu::packed_uint<4> m_selectors_byte_length; + basisu::packed_uint<4> m_tables_byte_length; + basisu::packed_uint<4> m_extended_byte_length; + }; + + struct ktx2_etc1s_image_desc + { + basisu::packed_uint<4> m_image_flags; + basisu::packed_uint<4> m_rgb_slice_byte_offset; + basisu::packed_uint<4> m_rgb_slice_byte_length; + basisu::packed_uint<4> m_alpha_slice_byte_offset; + basisu::packed_uint<4> m_alpha_slice_byte_length; + }; + + struct ktx2_animdata + { + basisu::packed_uint<4> m_duration; + basisu::packed_uint<4> m_timescale; + basisu::packed_uint<4> m_loopcount; + }; +#pragma pack(pop) + + const uint32_t KTX2_VK_FORMAT_UNDEFINED = 0; + const uint32_t KTX2_KDF_DF_MODEL_UASTC = 166; + const uint32_t KTX2_KDF_DF_MODEL_ETC1S = 163; + const uint32_t KTX2_IMAGE_IS_P_FRAME = 2; + const uint32_t KTX2_UASTC_BLOCK_SIZE = 16; + const uint32_t KTX2_MAX_SUPPORTED_LEVEL_COUNT = 16; // this is an implementation specific constraint and can be increased + + // The KTX2 transfer functions supported by KTX2 + const uint32_t KTX2_KHR_DF_TRANSFER_LINEAR = 1; + const uint32_t KTX2_KHR_DF_TRANSFER_SRGB = 2; + + enum ktx2_supercompression + { + KTX2_SS_NONE = 0, + KTX2_SS_BASISLZ = 1, + KTX2_SS_ZSTANDARD = 2 + }; + + extern const uint8_t g_ktx2_file_identifier[12]; + + enum ktx2_df_channel_id + { + KTX2_DF_CHANNEL_ETC1S_RGB = 0U, + KTX2_DF_CHANNEL_ETC1S_RRR = 3U, + KTX2_DF_CHANNEL_ETC1S_GGG = 4U, + KTX2_DF_CHANNEL_ETC1S_AAA = 15U, + + KTX2_DF_CHANNEL_UASTC_DATA = 0U, + KTX2_DF_CHANNEL_UASTC_RGB = 0U, + KTX2_DF_CHANNEL_UASTC_RGBA = 3U, + KTX2_DF_CHANNEL_UASTC_RRR = 4U, + KTX2_DF_CHANNEL_UASTC_RRRG = 5U, + KTX2_DF_CHANNEL_UASTC_RG = 6U, + }; + + inline const char* ktx2_get_etc1s_df_channel_id_str(ktx2_df_channel_id id) + { + switch (id) + { + case KTX2_DF_CHANNEL_ETC1S_RGB: return "RGB"; + case KTX2_DF_CHANNEL_ETC1S_RRR: return "RRR"; + case KTX2_DF_CHANNEL_ETC1S_GGG: return "GGG"; + case KTX2_DF_CHANNEL_ETC1S_AAA: return "AAA"; + default: break; + } + return "?"; + } + + inline const char* ktx2_get_uastc_df_channel_id_str(ktx2_df_channel_id id) + { + switch (id) + { + case KTX2_DF_CHANNEL_UASTC_RGB: return "RGB"; + case KTX2_DF_CHANNEL_UASTC_RGBA: return "RGBA"; + case KTX2_DF_CHANNEL_UASTC_RRR: return "RRR"; + case KTX2_DF_CHANNEL_UASTC_RRRG: return "RRRG"; + case KTX2_DF_CHANNEL_UASTC_RG: return "RG"; + default: break; + } + return "?"; + } + + enum ktx2_df_color_primaries + { + KTX2_DF_PRIMARIES_UNSPECIFIED = 0, + KTX2_DF_PRIMARIES_BT709 = 1, + KTX2_DF_PRIMARIES_SRGB = 1, + KTX2_DF_PRIMARIES_BT601_EBU = 2, + KTX2_DF_PRIMARIES_BT601_SMPTE = 3, + KTX2_DF_PRIMARIES_BT2020 = 4, + KTX2_DF_PRIMARIES_CIEXYZ = 5, + KTX2_DF_PRIMARIES_ACES = 6, + KTX2_DF_PRIMARIES_ACESCC = 7, + KTX2_DF_PRIMARIES_NTSC1953 = 8, + KTX2_DF_PRIMARIES_PAL525 = 9, + KTX2_DF_PRIMARIES_DISPLAYP3 = 10, + KTX2_DF_PRIMARIES_ADOBERGB = 11 + }; + + inline const char* ktx2_get_df_color_primaries_str(ktx2_df_color_primaries p) + { + switch (p) + { + case KTX2_DF_PRIMARIES_UNSPECIFIED: return "UNSPECIFIED"; + case KTX2_DF_PRIMARIES_BT709: return "BT709"; + case KTX2_DF_PRIMARIES_BT601_EBU: return "EBU"; + case KTX2_DF_PRIMARIES_BT601_SMPTE: return "SMPTE"; + case KTX2_DF_PRIMARIES_BT2020: return "BT2020"; + case KTX2_DF_PRIMARIES_CIEXYZ: return "CIEXYZ"; + case KTX2_DF_PRIMARIES_ACES: return "ACES"; + case KTX2_DF_PRIMARIES_ACESCC: return "ACESCC"; + case KTX2_DF_PRIMARIES_NTSC1953: return "NTSC1953"; + case KTX2_DF_PRIMARIES_PAL525: return "PAL525"; + case KTX2_DF_PRIMARIES_DISPLAYP3: return "DISPLAYP3"; + case KTX2_DF_PRIMARIES_ADOBERGB: return "ADOBERGB"; + default: break; + } + return "?"; + } + + // Information about a single 2D texture "image" in a KTX2 file. + struct ktx2_image_level_info + { + // The mipmap level index (0=largest), texture array layer index, and cubemap face index of the image. + uint32_t m_level_index; + uint32_t m_layer_index; + uint32_t m_face_index; + + // The image's actual (or the original source image's) width/height in pixels, which may not be divisible by 4 pixels. + uint32_t m_orig_width; + uint32_t m_orig_height; + + // The image's physical width/height, which will always be divisible by 4 pixels. + uint32_t m_width; + uint32_t m_height; + + // The texture's dimensions in 4x4 texel blocks. + uint32_t m_num_blocks_x; + uint32_t m_num_blocks_y; + + // The total number of blocks + uint32_t m_total_blocks; + + // true if the image has alpha data + bool m_alpha_flag; + + // true if the image is an I-Frame. Currently, for ETC1S textures, the first frame will always be an I-Frame, and subsequent frames will always be P-Frames. + bool m_iframe_flag; + }; + + // Thread-specific ETC1S/supercompressed UASTC transcoder state. (If you're not doing multithreading transcoding you can ignore this.) + struct ktx2_transcoder_state + { + basist::basisu_transcoder_state m_transcoder_state; + basisu::uint8_vec m_level_uncomp_data; + int m_uncomp_data_level_index; + + void clear() + { + m_transcoder_state.clear(); + m_level_uncomp_data.clear(); + m_uncomp_data_level_index = -1; + } + }; + + // This class is quite similar to basisu_transcoder. It treats KTX2 files as a simple container for ETC1S/UASTC texture data. + // It does not support 1D or 3D textures. + // It only supports 2D and cubemap textures, with or without mipmaps, texture arrays of 2D/cubemap textures, and texture video files. + // It only supports raw non-supercompressed UASTC, ETC1S, UASTC+Zstd, or UASTC+zlib compressed files. + // DFD (Data Format Descriptor) parsing is purposely as simple as possible. + // If you need to know how to interpret the texture channels you'll need to parse the DFD yourself after calling get_dfd(). + class ktx2_transcoder + { + public: + ktx2_transcoder(basist::etc1_global_selector_codebook* pGlobal_sel_codebook); + + // Frees all allocations, resets object. + void clear(); + + // init() parses the KTX2 header, level index array, DFD, and key values, but nothing else. + // Importantly, it does not parse or decompress the ETC1S global supercompressed data, so some things (like which frames are I/P-Frames) won't be available until start_transcoding() is called. + // This method holds a pointer to the file data until clear() is called. + bool init(const void* pData, uint32_t data_size); + + // Returns the data/size passed to init(). + const uint8_t* get_data() const { return m_pData; } + uint32_t get_data_size() const { return m_data_size; } + + // Returns the KTX2 header. Valid after init(). + const ktx2_header& get_header() const { return m_header; } + + // Returns the KTX2 level index array. There will be one entry for each mipmap level. Valid after init(). + const basisu::vector& get_level_index() const { return m_levels; } + + // Returns the texture's width in texels. Always non-zero, might not be divisible by 4. Valid after init(). + uint32_t get_width() const { return m_header.m_pixel_width; } + + // Returns the texture's height in texels. Always non-zero, might not be divisible by 4. Valid after init(). + uint32_t get_height() const { return m_header.m_pixel_height; } + + // Returns the texture's number of mipmap levels. Always returns 1 or higher. Valid after init(). + uint32_t get_levels() const { return m_header.m_level_count; } + + // Returns the number of faces. Returns 1 for 2D textures and or 6 for cubemaps. Valid after init(). + uint32_t get_faces() const { return m_header.m_face_count; } + + // Returns 0 or the number of layers in the texture array or texture video. Valid after init(). + uint32_t get_layers() const { return m_header.m_layer_count; } + + // Returns cETC1S or cUASTC4x4. Valid after init(). + basist::basis_tex_format get_format() const { return m_format; } + + bool is_etc1s() const { return get_format() == basist::basis_tex_format::cETC1S; } + + bool is_uastc() const { return get_format() == basist::basis_tex_format::cUASTC4x4; } + + // Returns true if the ETC1S file has two planes (typically RGBA, or RRRG), or true if the UASTC file has alpha data. Valid after init(). + uint32_t get_has_alpha() const { return m_has_alpha; } + + // Returns the entire Data Format Descriptor (DFD) from the KTX2 file. Valid after init(). + // See https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#_the_khronos_data_format_descriptor_overview + const basisu::uint8_vec& get_dfd() const { return m_dfd; } + + // Some basic DFD accessors. Valid after init(). + uint32_t get_dfd_color_model() const { return m_dfd_color_model; } + + // Returns the DFD color primary. + // We do not validate the color primaries, so the returned value may not be in the ktx2_df_color_primaries enum. + ktx2_df_color_primaries get_dfd_color_primaries() const { return m_dfd_color_prims; } + + // Returns KTX2_KHR_DF_TRANSFER_LINEAR or KTX2_KHR_DF_TRANSFER_SRGB. + uint32_t get_dfd_transfer_func() const { return m_dfd_transfer_func; } + + uint32_t get_dfd_flags() const { return m_dfd_flags; } + + // Returns 1 (ETC1S/UASTC) or 2 (ETC1S with an internal alpha channel). + uint32_t get_dfd_total_samples() const { return m_dfd_samples; } + + // Returns the channel mapping for each DFD "sample". UASTC always has 1 sample, ETC1S can have one or two. + // Note the returned value SHOULD be one of the ktx2_df_channel_id enums, but we don't validate that. + // It's up to the caller to decide what to do if the value isn't in the enum. + ktx2_df_channel_id get_dfd_channel_id0() const { return m_dfd_chan0; } + ktx2_df_channel_id get_dfd_channel_id1() const { return m_dfd_chan1; } + + // Key value field data. + struct key_value + { + // The key field is UTF8 and always zero terminated. + basisu::uint8_vec m_key; + + // The value may be empty. It consists of raw bytes which may or may not be zero terminated. + basisu::uint8_vec m_value; + + bool operator< (const key_value& rhs) const { return strcmp((const char*)m_key.data(), (const char *)rhs.m_key.data()) < 0; } + }; + typedef basisu::vector key_value_vec; + + // Returns the array of key-value entries. This may be empty. Valid after init(). + // The order of key values fields in this array exactly matches the order they were stored in the file. The keys are supposed to be sorted by their Unicode code points. + const key_value_vec& get_key_values() const { return m_key_values; } + + const basisu::uint8_vec *find_key(const std::string& key_name) const; + + // Low-level ETC1S specific accessors + + // Returns the ETC1S global supercompression data header, which is only valid after start_transcoding() is called. + const ktx2_etc1s_global_data_header& get_etc1s_header() const { return m_etc1s_header; } + + // Returns the array of ETC1S image descriptors, which is only valid after get_etc1s_image_descs() is called. + const basisu::vector& get_etc1s_image_descs() const { return m_etc1s_image_descs; } + + // Must have called startTranscoding() first + uint32_t get_etc1s_image_descs_image_flags(uint32_t level_index, uint32_t layer_index, uint32_t face_index) const; + + // is_video() is only valid after start_transcoding() is called. + // For ETC1S data, if this returns true you must currently transcode the file from first to last frame, in order, without skipping any frames. + bool is_video() const { return m_is_video; } + + // start_transcoding() MUST be called before calling transcode_image(). + // This method decompresses the ETC1S global endpoint/selector codebooks, which is not free, so try to avoid calling it excessively. + bool start_transcoding(); + + // get_image_level_info() be called after init(), but the m_iframe_flag's won't be valid until start_transcoding() is called. + // You can call this method before calling transcode_image_level() to retrieve basic information about the mipmap level's dimensions, etc. + bool get_image_level_info(ktx2_image_level_info& level_info, uint32_t level_index, uint32_t layer_index, uint32_t face_index) const; + + // transcode_image_level() transcodes a single 2D texture or cubemap face from the KTX2 file. + // Internally it uses the same low-level transcode API's as basisu_transcoder::transcode_image_level(). + // If the file is UASTC and is supercompressed with Zstandard, and the file is a texture array or cubemap, it's highly recommended that each mipmap level is + // completely transcoded before switching to another level. Every time the mipmap level is changed all supercompressed level data must be decompressed using Zstandard as a single unit. + // Currently ETC1S videos must always be transcoded from first to last frame (or KTX2 "layer"), in order, with no skipping of frames. + // By default this method is not thread safe unless you specify a pointer to a user allocated thread-specific transcoder_state struct. + bool transcode_image_level( + uint32_t level_index, uint32_t layer_index, uint32_t face_index, + void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels, + basist::transcoder_texture_format fmt, + uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, + ktx2_transcoder_state *pState = nullptr); + + private: + const uint8_t* m_pData; + uint32_t m_data_size; + + ktx2_header m_header; + basisu::vector m_levels; + basisu::uint8_vec m_dfd; + key_value_vec m_key_values; + + ktx2_etc1s_global_data_header m_etc1s_header; + basisu::vector m_etc1s_image_descs; + + basist::basis_tex_format m_format; + + uint32_t m_dfd_color_model; + ktx2_df_color_primaries m_dfd_color_prims; + uint32_t m_dfd_transfer_func; + uint32_t m_dfd_flags; + uint32_t m_dfd_samples; + ktx2_df_channel_id m_dfd_chan0, m_dfd_chan1; + + basist::basisu_lowlevel_etc1s_transcoder m_etc1s_transcoder; + basist::basisu_lowlevel_uastc_transcoder m_uastc_transcoder; + + ktx2_transcoder_state m_def_transcoder_state; + + bool m_has_alpha; + bool m_is_video; + + bool decompress_level_data(uint32_t level_index, basisu::uint8_vec& uncomp_data); + bool decompress_etc1s_global_data(); + bool read_key_values(); + }; + +#endif // BASISD_SUPPORT_KTX2 + + // Returns true if the transcoder was compiled with KTX2 support. + bool basisu_transcoder_supports_ktx2(); + + // Returns true if the transcoder was compiled with Zstandard support. + bool basisu_transcoder_supports_ktx2_zstd(); + } // namespace basisu + diff --git a/transcoder/basisu_transcoder_internal.h b/transcoder/basisu_transcoder_internal.h index ec126c0..2422d78 100644 --- a/transcoder/basisu_transcoder_internal.h +++ b/transcoder/basisu_transcoder_internal.h @@ -20,8 +20,8 @@ #pragma warning (disable: 4127) // conditional expression is constant #endif -#define BASISD_LIB_VERSION 114 -#define BASISD_VERSION_STRING "01.14" +#define BASISD_LIB_VERSION 115 +#define BASISD_VERSION_STRING "01.15" #ifdef _DEBUG #define BASISD_BUILD_DEBUG diff --git a/webgl/encoder/CMakeLists.txt b/webgl/encoder/CMakeLists.txt index 0e3c0d0..e365e1b 100644 --- a/webgl/encoder/CMakeLists.txt +++ b/webgl/encoder/CMakeLists.txt @@ -2,10 +2,16 @@ cmake_minimum_required(VERSION 3.0) project(basisu_encoder_js) +# The encoder always supports generating KTX2 files, but Zstandard support is optional. If it's disabled, KTX2 UASTC files will always be uncompressed. +# If you know you'll never be encoding UASTC+Zstd KTX2 files you can set KTX2_ZSTANDARD to 0 to reduce the size of the compiled encoder. +option(KTX2_ZSTANDARD "KTX2_ZSTANDARD" TRUE) + +message("KTX2_ZSTANDARD=${KTX2_ZSTANDARD}") + if (EMSCRIPTEN) set(CMAKE_CXX_STANDARD 11) - - add_executable(basis_encoder.js + + set(SRC_LIST ../transcoder/basis_wrappers.cpp ../../transcoder/basisu_transcoder.cpp ../../encoder/basisu_backend.cpp @@ -29,13 +35,25 @@ if (EMSCRIPTEN) ../../encoder/jpgd.cpp ) - #target_compile_definitions(basis_encoder.js PRIVATE NDEBUG BASISD_SUPPORT_UASTC=1 BASISD_SUPPORT_BC7=1 BASISD_SUPPORT_ATC=0 BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY=0 BASISD_SUPPORT_PVRTC2=0 BASISD_SUPPORT_FXT1=0 BASISD_SUPPORT_ETC2_EAC_RG11=0 BASISU_SUPPORT_ENCODING=1 BASISU_SUPPORT_SSE=0) + if (KTX2_ZSTANDARD) + set(SRC_LIST ${SRC_LIST} + ../../zstd/zstd.c + ) + set(ZSTD_DEFINITION BASISD_SUPPORT_KTX2_ZSTD=1) + else() + set(ZSTD_DEFINITION BASISD_SUPPORT_KTX2_ZSTD=0) + endif() + + add_executable(basis_encoder.js ${SRC_LIST}) + + #target_compile_definitions(basis_encoder.js PRIVATE NDEBUG BASISD_SUPPORT_UASTC=1 BASISD_SUPPORT_BC7=1 BASISD_SUPPORT_ATC=0 BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY=0 BASISD_SUPPORT_PVRTC2=0 BASISD_SUPPORT_FXT1=0 BASISD_SUPPORT_ETC2_EAC_RG11=0 BASISU_SUPPORT_ENCODING=1 BASISU_SUPPORT_SSE=0 ${ZSTD_DEFINITION} ) #target_compile_options(basis_encoder.js PRIVATE -fno-strict-aliasing -O3) - #target_compile_definitions(basis_encoder.js PRIVATE DEBUG BASISD_SUPPORT_UASTC=1 BASISD_SUPPORT_BC7=1 BASISD_SUPPORT_ATC=0 BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY=0 BASISD_SUPPORT_PVRTC2=0 BASISD_SUPPORT_FXT1=0 BASISD_SUPPORT_ETC2_EAC_RG11=0 BASISU_SUPPORT_ENCODING=1 BASISU_SUPPORT_SSE=0) + #target_compile_definitions(basis_encoder.js PRIVATE DEBUG BASISD_SUPPORT_UASTC=1 BASISD_SUPPORT_BC7=1 BASISD_SUPPORT_ATC=0 BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY=0 BASISD_SUPPORT_PVRTC2=0 BASISD_SUPPORT_FXT1=0 BASISD_SUPPORT_ETC2_EAC_RG11=0 BASISU_SUPPORT_ENCODING=1 BASISU_SUPPORT_SSE=0 ${ZSTD_DEFINITION} ) #target_compile_options(basis_encoder.js PRIVATE -fno-strict-aliasing -g -O1 -fsanitize=undefined -fsanitize=address) - target_compile_definitions(basis_encoder.js PRIVATE NDEBUG BASISD_SUPPORT_UASTC=1 BASISD_SUPPORT_BC7=1 BASISD_SUPPORT_ATC=0 BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY=0 BASISD_SUPPORT_PVRTC2=0 BASISD_SUPPORT_FXT1=0 BASISD_SUPPORT_ETC2_EAC_RG11=0 BASISU_SUPPORT_ENCODING=1 BASISU_SUPPORT_SSE=0) + target_compile_definitions(basis_encoder.js PRIVATE NDEBUG BASISD_SUPPORT_UASTC=1 BASISD_SUPPORT_BC7=1 BASISD_SUPPORT_ATC=0 BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY=0 BASISD_SUPPORT_PVRTC2=0 BASISD_SUPPORT_FXT1=0 BASISD_SUPPORT_ETC2_EAC_RG11=0 BASISU_SUPPORT_ENCODING=1 BASISU_SUPPORT_SSE=0 ${ZSTD_DEFINITION} ) + target_compile_options(basis_encoder.js PRIVATE -fno-strict-aliasing -O3) target_include_directories(basis_encoder.js PRIVATE ../../transcoder) @@ -48,5 +66,6 @@ if (EMSCRIPTEN) #LINK_FLAGS "--bind -s INITIAL_MEMORY=299958272 -g -s DEMANGLE_SUPPORT=1 -s ALLOW_MEMORY_GROWTH=1 -O1 -s ASSERTIONS=1 -s MALLOC=emmalloc -s MODULARIZE=1 -s EXPORT_NAME=BASIS -fsanitize=undefined -fsanitize=address") - LINK_FLAGS "--bind -s ALLOW_MEMORY_GROWTH=1 -O3 -s ASSERTIONS=0 -s INITIAL_MEMORY=299958272 -s MALLOC=emmalloc -s MODULARIZE=1 -s EXPORT_NAME=BASIS") + # TODO: 300MB is really large - probably not necessary? + LINK_FLAGS "--bind -s ALLOW_MEMORY_GROWTH=1 -O3 -s ASSERTIONS=0 -s INITIAL_MEMORY=299958272 -s MALLOC=emmalloc -s MODULARIZE=1 -s EXPORT_NAME=BASIS") endif() diff --git a/webgl/encoder/build/basis_encoder.js b/webgl/encoder/build/basis_encoder.js index 4adc300..2820331 100644 --- a/webgl/encoder/build/basis_encoder.js +++ b/webgl/encoder/build/basis_encoder.js @@ -6,7 +6,7 @@ var BASIS = (function() { function(BASIS) { BASIS = BASIS || {}; -var Module=typeof BASIS!=="undefined"?BASIS:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var STACK_ALIGN=16;function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;return Math.ceil(size/factor)*factor}var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var getTempRet0=function(){return tempRet0};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i=55296&&codeUnit<=57343)++i;len+=4}return len}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||299958272;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){FS.ignorePermissions=false;callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="basis_encoder.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return Promise.resolve().then(getBinary)}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["Z"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["_"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}var tempDouble;var tempI64;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var ExceptionInfoAttrs={DESTRUCTOR_OFFSET:0,REFCOUNT_OFFSET:4,TYPE_OFFSET:8,CAUGHT_OFFSET:12,RETHROWN_OFFSET:13,SIZE:16};function ___cxa_allocate_exception(size){return _malloc(size+ExceptionInfoAttrs.SIZE)+ExceptionInfoAttrs.SIZE}function ExceptionInfo(excPtr){this.excPtr=excPtr;this.ptr=excPtr-ExceptionInfoAttrs.SIZE;this.set_type=function(type){HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]=type};this.get_type=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]};this.set_destructor=function(destructor){HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]=destructor};this.get_destructor=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]};this.set_refcount=function(refcount){HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=refcount};this.set_caught=function(caught){caught=caught?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]=caught};this.get_caught=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]!=0};this.set_rethrown=function(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]=rethrown};this.get_rethrown=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]!=0};this.init=function(type,destructor){this.set_type(type);this.set_destructor(destructor);this.set_refcount(0);this.set_caught(false);this.set_rethrown(false)};this.add_ref=function(){var value=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=value+1};this.release_ref=function(){var prev=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=prev-1;return prev===1}}var exceptionLast=0;var uncaughtExceptionCount=0;function ___cxa_throw(ptr,type,destructor){var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw ptr}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};function getRandomDevice(){if(typeof crypto==="object"&&typeof crypto["getRandomValues"]==="function"){var randomBuffer=new Uint8Array(1);return function(){crypto.getRandomValues(randomBuffer);return randomBuffer[0]}}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");return function(){return crypto_module["randomBytes"](1)[0]}}catch(e){}}return function(){abort("randomDevice")}}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:function(from,to){from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function mmapAlloc(size){var alignedSize=alignMemory(size,16384);var ptr=_malloc(alignedSize);while(size=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0);return},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0;return}if(!node.contents||node.contents.subarray){var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize;return}if(!node.contents)node.contents=[];if(node.contents.length>newSize)node.contents.length=newSize;else while(node.contents.length=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),false);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:function(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:function(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:function(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:function(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:function(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:function(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:function(node){FS.hashRemoveNode(node)},isRoot:function(node){return node===node.parent},isMountpoint:function(node){return!!node.mounted},isFile:function(mode){return(mode&61440)===32768},isDir:function(mode){return(mode&61440)===16384},isLink:function(mode){return(mode&61440)===40960},isChrdev:function(mode){return(mode&61440)===8192},isBlkdev:function(mode){return(mode&61440)===24576},isFIFO:function(mode){return(mode&61440)===4096},isSocket:function(mode){return(mode&49152)===49152},flagModes:{"r":0,"r+":2,"w":577,"w+":578,"a":1089,"a+":1090},modeStringToFlags:function(str){var flags=FS.flagModes[str];if(typeof flags==="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:function(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:function(node,perms){if(FS.ignorePermissions){return 0}if(perms.indexOf("r")!==-1&&!(node.mode&292)){return 2}else if(perms.indexOf("w")!==-1&&!(node.mode&146)){return 2}else if(perms.indexOf("x")!==-1&&!(node.mode&73)){return 2}return 0},mayLookup:function(dir){var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:function(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:function(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:function(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:function(fd_start,fd_end){fd_start=fd_start||0;fd_end=fd_end||FS.MAX_OPEN_FDS;for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:function(fd){return FS.streams[fd]},createStream:function(stream,fd_start,fd_end){if(!FS.FSStream){FS.FSStream=function(){};FS.FSStream.prototype={object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}}}}var newStream=new FS.FSStream;for(var p in stream){newStream[p]=stream[p]}stream=newStream;var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:function(fd){FS.streams[fd]=null},chrdev_stream_ops:{open:function(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:function(){throw new FS.ErrnoError(70)}},major:function(dev){return dev>>8},minor:function(dev){return dev&255},makedev:function(ma,mi){return ma<<8|mi},registerDevice:function(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:function(dev){return FS.devices[dev]},getMounts:function(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:function(populate,callback){if(typeof populate==="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(function(mount){if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:function(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:function(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(function(hash){var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.indexOf(current.mount)!==-1){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:function(parent,name){return parent.node_ops.lookup(parent,name)},mknod:function(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:function(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:function(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:function(path,mode){var dirs=path.split("/");var d="";for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=function(from,to){if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);if(typeof Uint8Array!="undefined")xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(function(chunkNum){var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]==="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]==="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!=="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(function(key){var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});stream_ops.read=function stream_ops_read(stream,buffer,offset,length,position){FS.forceLoadFile(node);var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},doMkdir:function(path,mode){path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0},doMknod:function(path,mode,dev){switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0},doReadlink:function(path,buf,bufsize){if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len},doAccess:function(path,amode){if(amode&~7){return-28}var node;var lookup=FS.lookupPath(path,{follow:true});node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0},doDup:function(path,flags,suggestFD){var suggest=FS.getStream(suggestFD);if(suggest)FS.close(suggest);return FS.open(path,flags,0,suggestFD,suggestFD).fd},doReadv:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},get64:function(low,high){return low}};function ___sys_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.open(stream.path,stream.flags,0,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 12:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:case 21505:{if(!stream.tty)return-59;return 0}case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:{if(!stream.tty)return-59;return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.get();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_open(path,flags,varargs){SYSCALLS.varargs=varargs;try{var pathname=SYSCALLS.getStr(path);var mode=SYSCALLS.get();var stream=FS.open(pathname,flags,mode);return stream.fd}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}var structRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+' "use strict";'+" return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationGroup=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function attachFinalizer(handle){if("undefined"===typeof FinalizationGroup){attachFinalizer=function(handle){return handle};return handle}finalizationGroup=new FinalizationGroup(function(iter){for(var result=iter.next();!result.done;result=iter.next()){var $$=result.value;if(!$$.ptr){console.warn("object already deleted: "+$$.ptr)}else{releaseClassHandle($$)}}});attachFinalizer=function(handle){finalizationGroup.register(handle,handle.$$,handle.$$);return handle};detachFinalizer=function(handle){finalizationGroup.unregister(handle.$$)};return attachFinalizer(handle)}function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}}function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}}function ClassHandle_isDeleted(){return!this.$$.ptr}var delayFunction=undefined;var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}var registeredPointers={};function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError("Expected null or instance of "+desiredClass.name+", got an instance of "+ptrClass.name)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr}function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,__emval_register(function(){clonedHandle["delete"]()}));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+handle.$$.ptrType.name+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)}function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function makeClassHandle(prototype,record){if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record}}))}function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["fromWireType"]=RegisteredPointer_fromWireType}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function dynCallLegacy(sig,ptr,args){if(args&&args.length){return Module["dynCall_"+sig].apply(null,[ptr].concat(args))}return Module["dynCall_"+sig].call(null,ptr)}function dynCall(sig,ptr,args){if(sig.indexOf("j")!=-1){return dynCallLegacy(sig,ptr,args)}return wasmTable.get(ptr).apply(null,args)}function getDynCaller(sig,ptr){assert(sig.indexOf("j")>=0,"getDynCaller should only be called with i64 sigs");var argCache=[];return function(){argCache.length=arguments.length;for(var i=0;i>2)+i])}return array}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);var args=[rawConstructor];var destructors=[];whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName="constructor "+classType.name;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError("Cannot register multiple constructors with identical number of parameters ("+(argCount-1)+") for class '"+classType.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!")}classType.registeredClass.constructor_body[argCount-1]=function unboundTypeHandler(){throwUnboundTypeError("Cannot construct "+classType.name+" due to unbound types",rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){classType.registeredClass.constructor_body[argCount-1]=function constructor_body(){if(arguments.length!==argCount-1){throwBindingError(humanName+" called with "+arguments.length+" arguments, expected "+(argCount-1))}destructors.length=0;args.length=argCount;for(var i=1;i0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __embind_register_enum_value(rawEnumType,name,enumValue){var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(enumType.name+"_"+name,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(valuemaxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_object(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){structRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}}function __embind_register_value_object_field(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){structRegistrations[structType].fields.push({fieldName:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}var emval_methodCallers=[];function __emval_call_void_method(caller,handle,methodName,args){caller=emval_methodCallers[caller];handle=requireHandle(handle);methodName=getStringOrSymbol(methodName);caller(handle,methodName,null,args)}function emval_get_global(){if(typeof globalThis==="object"){return globalThis}return function(){return Function}()("return this")()}function __emval_get_global(name){if(name===0){return __emval_register(emval_get_global())}else{name=getStringOrSymbol(name);return __emval_register(emval_get_global()[name])}}function __emval_addMethodCaller(caller){var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id}function __emval_lookupTypes(argCount,argTypes){var a=new Array(argCount);for(var i=0;i>2)+i],"parameter "+i)}return a}function __emval_get_method_caller(argCount,argTypes){var types=__emval_lookupTypes(argCount,argTypes);var retType=types[0];var signatureName=retType.name+"_$"+types.slice(1).map(function(t){return t.name}).join("_")+"$";var params=["retType"];var args=[retType];var argsList="";for(var i=0;i4){emval_handle_array[handle].refcount+=1}}function craftEmvalAllocator(argCount){var argsList="";for(var i=0;i>> 2) + "+i+'], "parameter '+i+'");\n'+"var arg"+i+" = argType"+i+".readValueFromPointer(args);\n"+"args += argType"+i+"['argPackAdvance'];\n"}functionBody+="var obj = new constructor("+argsList+");\n"+"return __emval_register(obj);\n"+"}\n";return new Function("requireRegisteredType","Module","__emval_register",functionBody)(requireRegisteredType,Module,__emval_register)}var emval_newers={};function __emval_new(handle,argCount,argTypes,args){handle=requireHandle(handle);var newer=emval_newers[argCount];if(!newer){newer=craftEmvalAllocator(argCount);emval_newers[argCount]=newer}return newer(handle,argTypes,args)}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function _abort(){abort()}function _longjmp(env,value){_setThrew(env,value||1);throw"longjmp"}function _emscripten_longjmp(a0,a1){return _longjmp(a0,a1)}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doReadv(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var stream=SYSCALLS.getStreamFromFD(fd);var HIGH_OFFSET=4294967296;var offset=offset_high*HIGH_OFFSET+(offset_low>>>0);var DOUBLE_LIMIT=9007199254740992;if(offset<=-DOUBLE_LIMIT||offset>=DOUBLE_LIMIT){return-61}FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doWritev(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _getTempRet0(){return getTempRet0()|0}function _gettimeofday(ptr){var now=Date.now();HEAP32[ptr>>2]=now/1e3|0;HEAP32[ptr+4>>2]=now%1e3*1e3|0;return 0}function _pthread_join(){return 28}function _setTempRet0($i){setTempRet0($i|0)}function _sysconf(name){switch(name){case 30:return 16384;case 85:var maxHeapSize=2147483648;return maxHeapSize/16384;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:case 79:return 200809;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}setErrNo(28);return-1}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_RegisteredPointer();init_embind();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}__ATINIT__.push({func:function(){___wasm_call_ctors()}});var asmLibraryArg={"J":___cxa_allocate_exception,"I":___cxa_throw,"F":___sys_fcntl64,"R":___sys_ioctl,"S":___sys_open,"B":__embind_finalize_value_object,"U":__embind_register_bool,"A":__embind_register_class,"z":__embind_register_class_constructor,"a":__embind_register_class_function,"d":__embind_register_constant,"T":__embind_register_emval,"w":__embind_register_enum,"c":__embind_register_enum_value,"G":__embind_register_float,"n":__embind_register_function,"l":__embind_register_integer,"i":__embind_register_memory_view,"H":__embind_register_std_string,"x":__embind_register_std_wstring,"C":__embind_register_value_object,"e":__embind_register_value_object_field,"V":__embind_register_void,"p":__emval_as,"s":__emval_call_void_method,"b":__emval_decref,"K":__emval_get_global,"t":__emval_get_method_caller,"v":__emval_get_module_property,"g":__emval_get_property,"m":__emval_incref,"u":__emval_new,"h":__emval_new_cstring,"o":__emval_run_destructors,"k":_abort,"r":_emscripten_longjmp,"M":_emscripten_memcpy_big,"N":_emscripten_resize_heap,"E":_fd_close,"Q":_fd_read,"L":_fd_seek,"D":_fd_write,"j":_getTempRet0,"y":_gettimeofday,"X":invoke_ii,"q":invoke_vi,"W":invoke_vii,"Y":invoke_viii,"P":_pthread_join,"f":_setTempRet0,"O":_sysconf};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["$"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["aa"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["ba"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["ca"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["da"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["ea"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["fa"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["ga"]).apply(null,arguments)};var _setThrew=Module["_setThrew"]=function(){return(_setThrew=Module["_setThrew"]=Module["asm"]["ha"]).apply(null,arguments)};var dynCall_jiiii=Module["dynCall_jiiii"]=function(){return(dynCall_jiiii=Module["dynCall_jiiii"]=Module["asm"]["ia"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["ja"]).apply(null,arguments)};function invoke_vi(index,a1){var sp=stackSave();try{wasmTable.get(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{wasmTable.get(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return wasmTable.get(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{wasmTable.get(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run(); +var Module=typeof BASIS!=="undefined"?BASIS:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var STACK_ALIGN=16;function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;return Math.ceil(size/factor)*factor}var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var getTempRet0=function(){return tempRet0};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i=55296&&codeUnit<=57343)++i;len+=4}return len}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||299958272;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){FS.ignorePermissions=false;callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="basis_encoder.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return Promise.resolve().then(getBinary)}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["Z"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["_"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}var tempDouble;var tempI64;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var ExceptionInfoAttrs={DESTRUCTOR_OFFSET:0,REFCOUNT_OFFSET:4,TYPE_OFFSET:8,CAUGHT_OFFSET:12,RETHROWN_OFFSET:13,SIZE:16};function ___cxa_allocate_exception(size){return _malloc(size+ExceptionInfoAttrs.SIZE)+ExceptionInfoAttrs.SIZE}function ExceptionInfo(excPtr){this.excPtr=excPtr;this.ptr=excPtr-ExceptionInfoAttrs.SIZE;this.set_type=function(type){HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]=type};this.get_type=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]};this.set_destructor=function(destructor){HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]=destructor};this.get_destructor=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]};this.set_refcount=function(refcount){HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=refcount};this.set_caught=function(caught){caught=caught?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]=caught};this.get_caught=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]!=0};this.set_rethrown=function(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]=rethrown};this.get_rethrown=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]!=0};this.init=function(type,destructor){this.set_type(type);this.set_destructor(destructor);this.set_refcount(0);this.set_caught(false);this.set_rethrown(false)};this.add_ref=function(){var value=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=value+1};this.release_ref=function(){var prev=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=prev-1;return prev===1}}var exceptionLast=0;var uncaughtExceptionCount=0;function ___cxa_throw(ptr,type,destructor){var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw ptr}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};function getRandomDevice(){if(typeof crypto==="object"&&typeof crypto["getRandomValues"]==="function"){var randomBuffer=new Uint8Array(1);return function(){crypto.getRandomValues(randomBuffer);return randomBuffer[0]}}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");return function(){return crypto_module["randomBytes"](1)[0]}}catch(e){}}return function(){abort("randomDevice")}}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:function(from,to){from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function mmapAlloc(size){var alignedSize=alignMemory(size,16384);var ptr=_malloc(alignedSize);while(size=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0);return},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0;return}if(!node.contents||node.contents.subarray){var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize;return}if(!node.contents)node.contents=[];if(node.contents.length>newSize)node.contents.length=newSize;else while(node.contents.length=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),false);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:function(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:function(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:function(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:function(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:function(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:function(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:function(node){FS.hashRemoveNode(node)},isRoot:function(node){return node===node.parent},isMountpoint:function(node){return!!node.mounted},isFile:function(mode){return(mode&61440)===32768},isDir:function(mode){return(mode&61440)===16384},isLink:function(mode){return(mode&61440)===40960},isChrdev:function(mode){return(mode&61440)===8192},isBlkdev:function(mode){return(mode&61440)===24576},isFIFO:function(mode){return(mode&61440)===4096},isSocket:function(mode){return(mode&49152)===49152},flagModes:{"r":0,"r+":2,"w":577,"w+":578,"a":1089,"a+":1090},modeStringToFlags:function(str){var flags=FS.flagModes[str];if(typeof flags==="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:function(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:function(node,perms){if(FS.ignorePermissions){return 0}if(perms.indexOf("r")!==-1&&!(node.mode&292)){return 2}else if(perms.indexOf("w")!==-1&&!(node.mode&146)){return 2}else if(perms.indexOf("x")!==-1&&!(node.mode&73)){return 2}return 0},mayLookup:function(dir){var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:function(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:function(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:function(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:function(fd_start,fd_end){fd_start=fd_start||0;fd_end=fd_end||FS.MAX_OPEN_FDS;for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:function(fd){return FS.streams[fd]},createStream:function(stream,fd_start,fd_end){if(!FS.FSStream){FS.FSStream=function(){};FS.FSStream.prototype={object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}}}}var newStream=new FS.FSStream;for(var p in stream){newStream[p]=stream[p]}stream=newStream;var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:function(fd){FS.streams[fd]=null},chrdev_stream_ops:{open:function(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:function(){throw new FS.ErrnoError(70)}},major:function(dev){return dev>>8},minor:function(dev){return dev&255},makedev:function(ma,mi){return ma<<8|mi},registerDevice:function(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:function(dev){return FS.devices[dev]},getMounts:function(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:function(populate,callback){if(typeof populate==="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(function(mount){if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:function(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:function(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(function(hash){var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.indexOf(current.mount)!==-1){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:function(parent,name){return parent.node_ops.lookup(parent,name)},mknod:function(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:function(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:function(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:function(path,mode){var dirs=path.split("/");var d="";for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=function(from,to){if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);if(typeof Uint8Array!="undefined")xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(function(chunkNum){var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]==="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]==="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!=="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(function(key){var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});stream_ops.read=function stream_ops_read(stream,buffer,offset,length,position){FS.forceLoadFile(node);var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},doMkdir:function(path,mode){path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0},doMknod:function(path,mode,dev){switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0},doReadlink:function(path,buf,bufsize){if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len},doAccess:function(path,amode){if(amode&~7){return-28}var node;var lookup=FS.lookupPath(path,{follow:true});node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0},doDup:function(path,flags,suggestFD){var suggest=FS.getStream(suggestFD);if(suggest)FS.close(suggest);return FS.open(path,flags,0,suggestFD,suggestFD).fd},doReadv:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},get64:function(low,high){return low}};function ___sys_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.open(stream.path,stream.flags,0,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 12:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:case 21505:{if(!stream.tty)return-59;return 0}case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:{if(!stream.tty)return-59;return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.get();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_open(path,flags,varargs){SYSCALLS.varargs=varargs;try{var pathname=SYSCALLS.getStr(path);var mode=SYSCALLS.get();var stream=FS.open(pathname,flags,mode);return stream.fd}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}var structRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+' "use strict";'+" return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationGroup=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function attachFinalizer(handle){if("undefined"===typeof FinalizationGroup){attachFinalizer=function(handle){return handle};return handle}finalizationGroup=new FinalizationGroup(function(iter){for(var result=iter.next();!result.done;result=iter.next()){var $$=result.value;if(!$$.ptr){console.warn("object already deleted: "+$$.ptr)}else{releaseClassHandle($$)}}});attachFinalizer=function(handle){finalizationGroup.register(handle,handle.$$,handle.$$);return handle};detachFinalizer=function(handle){finalizationGroup.unregister(handle.$$)};return attachFinalizer(handle)}function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}}function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}}function ClassHandle_isDeleted(){return!this.$$.ptr}var delayFunction=undefined;var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}var registeredPointers={};function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError("Expected null or instance of "+desiredClass.name+", got an instance of "+ptrClass.name)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr}function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,__emval_register(function(){clonedHandle["delete"]()}));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+handle.$$.ptrType.name+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)}function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function makeClassHandle(prototype,record){if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record}}))}function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["fromWireType"]=RegisteredPointer_fromWireType}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function dynCallLegacy(sig,ptr,args){if(args&&args.length){return Module["dynCall_"+sig].apply(null,[ptr].concat(args))}return Module["dynCall_"+sig].call(null,ptr)}function dynCall(sig,ptr,args){if(sig.indexOf("j")!=-1){return dynCallLegacy(sig,ptr,args)}return wasmTable.get(ptr).apply(null,args)}function getDynCaller(sig,ptr){assert(sig.indexOf("j")>=0,"getDynCaller should only be called with i64 sigs");var argCache=[];return function(){argCache.length=arguments.length;for(var i=0;i>2)+i])}return array}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);var args=[rawConstructor];var destructors=[];whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName="constructor "+classType.name;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError("Cannot register multiple constructors with identical number of parameters ("+(argCount-1)+") for class '"+classType.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!")}classType.registeredClass.constructor_body[argCount-1]=function unboundTypeHandler(){throwUnboundTypeError("Cannot construct "+classType.name+" due to unbound types",rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){classType.registeredClass.constructor_body[argCount-1]=function constructor_body(){if(arguments.length!==argCount-1){throwBindingError(humanName+" called with "+arguments.length+" arguments, expected "+(argCount-1))}destructors.length=0;args.length=argCount;for(var i=1;i0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __embind_register_enum_value(rawEnumType,name,enumValue){var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(enumType.name+"_"+name,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(valuemaxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_object(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){structRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}}function __embind_register_value_object_field(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){structRegistrations[structType].fields.push({fieldName:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}var emval_methodCallers=[];function __emval_call_void_method(caller,handle,methodName,args){caller=emval_methodCallers[caller];handle=requireHandle(handle);methodName=getStringOrSymbol(methodName);caller(handle,methodName,null,args)}function emval_get_global(){if(typeof globalThis==="object"){return globalThis}return function(){return Function}()("return this")()}function __emval_get_global(name){if(name===0){return __emval_register(emval_get_global())}else{name=getStringOrSymbol(name);return __emval_register(emval_get_global()[name])}}function __emval_addMethodCaller(caller){var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id}function __emval_lookupTypes(argCount,argTypes){var a=new Array(argCount);for(var i=0;i>2)+i],"parameter "+i)}return a}function __emval_get_method_caller(argCount,argTypes){var types=__emval_lookupTypes(argCount,argTypes);var retType=types[0];var signatureName=retType.name+"_$"+types.slice(1).map(function(t){return t.name}).join("_")+"$";var params=["retType"];var args=[retType];var argsList="";for(var i=0;i4){emval_handle_array[handle].refcount+=1}}function craftEmvalAllocator(argCount){var argsList="";for(var i=0;i>> 2) + "+i+'], "parameter '+i+'");\n'+"var arg"+i+" = argType"+i+".readValueFromPointer(args);\n"+"args += argType"+i+"['argPackAdvance'];\n"}functionBody+="var obj = new constructor("+argsList+");\n"+"return __emval_register(obj);\n"+"}\n";return new Function("requireRegisteredType","Module","__emval_register",functionBody)(requireRegisteredType,Module,__emval_register)}var emval_newers={};function __emval_new(handle,argCount,argTypes,args){handle=requireHandle(handle);var newer=emval_newers[argCount];if(!newer){newer=craftEmvalAllocator(argCount);emval_newers[argCount]=newer}return newer(handle,argTypes,args)}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function _abort(){abort()}function _longjmp(env,value){_setThrew(env,value||1);throw"longjmp"}function _emscripten_longjmp(a0,a1){return _longjmp(a0,a1)}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doReadv(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var stream=SYSCALLS.getStreamFromFD(fd);var HIGH_OFFSET=4294967296;var offset=offset_high*HIGH_OFFSET+(offset_low>>>0);var DOUBLE_LIMIT=9007199254740992;if(offset<=-DOUBLE_LIMIT||offset>=DOUBLE_LIMIT){return-61}FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doWritev(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _getTempRet0(){return getTempRet0()|0}function _gettimeofday(ptr){var now=Date.now();HEAP32[ptr>>2]=now/1e3|0;HEAP32[ptr+4>>2]=now%1e3*1e3|0;return 0}function _pthread_join(){return 28}function _setTempRet0($i){setTempRet0($i|0)}function _sysconf(name){switch(name){case 30:return 16384;case 85:var maxHeapSize=2147483648;return maxHeapSize/16384;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:case 79:return 200809;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}setErrNo(28);return-1}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_RegisteredPointer();init_embind();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}__ATINIT__.push({func:function(){___wasm_call_ctors()}});var asmLibraryArg={"K":___cxa_allocate_exception,"J":___cxa_throw,"G":___sys_fcntl64,"R":___sys_ioctl,"S":___sys_open,"x":__embind_finalize_value_object,"U":__embind_register_bool,"A":__embind_register_class,"z":__embind_register_class_constructor,"a":__embind_register_class_function,"e":__embind_register_constant,"T":__embind_register_emval,"q":__embind_register_enum,"c":__embind_register_enum_value,"H":__embind_register_float,"m":__embind_register_function,"n":__embind_register_integer,"k":__embind_register_memory_view,"I":__embind_register_std_string,"B":__embind_register_std_wstring,"y":__embind_register_value_object,"d":__embind_register_value_object_field,"V":__embind_register_void,"p":__emval_as,"r":__emval_call_void_method,"b":__emval_decref,"D":__emval_get_global,"s":__emval_get_method_caller,"u":__emval_get_module_property,"f":__emval_get_property,"j":__emval_incref,"t":__emval_new,"g":__emval_new_cstring,"o":__emval_run_destructors,"h":_abort,"w":_emscripten_longjmp,"M":_emscripten_memcpy_big,"N":_emscripten_resize_heap,"F":_fd_close,"Q":_fd_read,"L":_fd_seek,"E":_fd_write,"l":_getTempRet0,"C":_gettimeofday,"X":invoke_ii,"v":invoke_vi,"W":invoke_vii,"Y":invoke_viii,"P":_pthread_join,"i":_setTempRet0,"O":_sysconf};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["$"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["aa"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["ba"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["ca"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["da"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["ea"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["fa"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["ga"]).apply(null,arguments)};var _setThrew=Module["_setThrew"]=function(){return(_setThrew=Module["_setThrew"]=Module["asm"]["ha"]).apply(null,arguments)};var dynCall_jiiii=Module["dynCall_jiiii"]=function(){return(dynCall_jiiii=Module["dynCall_jiiii"]=Module["asm"]["ia"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["ja"]).apply(null,arguments)};function invoke_vi(index,a1){var sp=stackSave();try{wasmTable.get(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{wasmTable.get(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return wasmTable.get(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{wasmTable.get(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run(); return BASIS.ready diff --git a/webgl/encoder/build/basis_encoder.wasm b/webgl/encoder/build/basis_encoder.wasm index 3c39bfe..839eab1 100644 Binary files a/webgl/encoder/build/basis_encoder.wasm and b/webgl/encoder/build/basis_encoder.wasm differ diff --git a/webgl/index.html b/webgl/index.html index cfb482e..4166503 100644 --- a/webgl/index.html +++ b/webgl/index.html @@ -7,10 +7,11 @@

Basis WebGL demos

Example webpages using the transcoder (compiled to WASM) to render - Basis textures in WebGL. Both demos require WebAssembly and WebGL support. + Basis/KTX2 textures in WebGL. Both demos require WebAssembly and WebGL support.

diff --git a/webgl/transcoder/CMakeLists.txt b/webgl/transcoder/CMakeLists.txt index 70f963b..372653d 100644 --- a/webgl/transcoder/CMakeLists.txt +++ b/webgl/transcoder/CMakeLists.txt @@ -2,15 +2,46 @@ cmake_minimum_required(VERSION 3.0) project(basisu_transcoder_js) +# KTX2 support (and Zstandard) in the transcoder is purely optional. Use "cmake -DKTX2=FALSE" to completely disable KTX2 support (which also disables Zstandard). +# If this is FALSE you'll only be able to transcode .basis files. +option(KTX2 "KTX2" TRUE) + +# If KTX2 is enabled, Zstandard is needed to support decompressing supercompressed UASTC files. +# If you know you'll never be transcoding KTX2 files using this feature you can set KTX2_ZSTANDARD to 0. +option(KTX2_ZSTANDARD "KTX2_ZSTANDARD" TRUE) + +message("KTX2=${KTX2}") +message("KTX2_ZSTANDARD=${KTX2_ZSTANDARD}") + if (EMSCRIPTEN) set(CMAKE_CXX_STANDARD 11) - - add_executable(basis_transcoder.js - ../../transcoder/basisu_transcoder.cpp - basis_wrappers.cpp + + set(SRC_LIST + ../../transcoder/basisu_transcoder.cpp + basis_wrappers.cpp ) + + if (KTX2) + set(KTX2_DEFINITION BASISD_SUPPORT_KTX2=1) + else() + set(KTX2_DEFINITION BASISD_SUPPORT_KTX2=0) + endif() + + if (KTX2_ZSTANDARD) + set(SRC_LIST ${SRC_LIST} + ../../zstd/zstddeclib.c + ) + set(ZSTD_DEFINITION BASISD_SUPPORT_KTX2_ZSTD=1) + else() + set(ZSTD_DEFINITION BASISD_SUPPORT_KTX2_ZSTD=0) + endif() + +# message("KTX2_DEFINITION=${KTX2_DEFINITION}) +# message("ZSTD_DEFINITION=${ZSTD_DEFINITION}) - target_compile_definitions(basis_transcoder.js PRIVATE NDEBUG BASISD_SUPPORT_UASTC=1 BASISD_SUPPORT_BC7=1 BASISD_SUPPORT_ATC=0 BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY=0 BASISD_SUPPORT_PVRTC2=0 BASISD_SUPPORT_FXT1=0 BASISD_SUPPORT_ETC2_EAC_RG11=0 BASISU_SUPPORT_ENCODING=0) + add_executable(basis_transcoder.js ${SRC_LIST}) + + target_compile_definitions(basis_transcoder.js PRIVATE NDEBUG BASISD_SUPPORT_UASTC=1 BASISD_SUPPORT_BC7=1 BASISD_SUPPORT_ATC=0 BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY=0 BASISD_SUPPORT_PVRTC2=0 BASISD_SUPPORT_FXT1=0 BASISD_SUPPORT_ETC2_EAC_RG11=0 BASISU_SUPPORT_ENCODING=0 ${KTX2_DEFINITION} ${ZSTD_DEFINITION} ) target_compile_options(basis_transcoder.js PRIVATE -O3 -fno-strict-aliasing) target_include_directories(basis_transcoder.js PRIVATE ../../transcoder) diff --git a/webgl/transcoder/basis_wrappers.cpp b/webgl/transcoder/basis_wrappers.cpp index 5f77a26..a1c6428 100644 --- a/webgl/transcoder/basis_wrappers.cpp +++ b/webgl/transcoder/basis_wrappers.cpp @@ -1,7 +1,8 @@ // basis_wrappers.cpp - Wrappers to the C++ compressor and transcoder for WebAssembly/WebGL use. // -// Important: +// **Important**: // Compile with -fno-strict-aliasing +// This code HAS NOT been tested with strict aliasing enabled. // The "initializeBasis()" function MUST be called at least once before using either the compressor or transcoder. // // There are four main categories of wrappers in this module: @@ -104,7 +105,8 @@ static bool copy_to_jsbuffer(const emscripten::val& dstBuffer, const basisu::vec return true; } -#define MAGIC 0xDEADBEE1 +#define BASIS_MAGIC 0xDEADBEE1 +#define KTX2_MAGIC 0xDEADBEE2 struct basis_file_desc { @@ -189,296 +191,808 @@ struct basis_file emscripten::val memoryView = jsBuffer["constructor"].new_(memory, reinterpret_cast(m_file.data()), length); memoryView.call("set", jsBuffer); - if (!m_transcoder.validate_header(m_file.data(), m_file.size())) { + if (!m_transcoder.validate_header(m_file.data(), m_file.size())) + { +#if BASISU_DEBUG_PRINTF + printf("basis_file::basis_file: m_transcoder.validate_header() failed!\n"); +#endif m_file.clear(); } // Initialized after validation - m_magic = MAGIC; + m_magic = BASIS_MAGIC; } - void close() { - assert(m_magic == MAGIC); - m_file.clear(); - } + void close() + { + assert(m_magic == BASIS_MAGIC); + if (m_magic != BASIS_MAGIC) + return; - uint32_t getHasAlpha() { - assert(m_magic == MAGIC); - if (m_magic != MAGIC) - return 0; + m_file.clear(); + } - basisu_image_level_info li; - if (!m_transcoder.get_image_level_info(m_file.data(), m_file.size(), li, 0, 0)) - return 0; + uint32_t getHasAlpha() + { + assert(m_magic == BASIS_MAGIC); + if (m_magic != BASIS_MAGIC) + return 0; - return li.m_alpha_flag; - } + basisu_image_level_info li; + if (!m_transcoder.get_image_level_info(m_file.data(), m_file.size(), li, 0, 0)) + return 0; - uint32_t getNumImages() { - assert(m_magic == MAGIC); - if (m_magic != MAGIC) - return 0; + return li.m_alpha_flag; + } - return m_transcoder.get_total_images(m_file.data(), m_file.size()); - } + uint32_t getNumImages() + { + assert(m_magic == BASIS_MAGIC); + if (m_magic != BASIS_MAGIC) + return 0; - uint32_t getNumLevels(uint32_t image_index) { - assert(m_magic == MAGIC); - if (m_magic != MAGIC) - return 0; + return m_transcoder.get_total_images(m_file.data(), m_file.size()); + } - basisu_image_info ii; - if (!m_transcoder.get_image_info(m_file.data(), m_file.size(), ii, image_index)) - return 0; + uint32_t getNumLevels(uint32_t image_index) + { + assert(m_magic == BASIS_MAGIC); + if (m_magic != BASIS_MAGIC) + return 0; - return ii.m_total_levels; - } + basisu_image_info ii; + if (!m_transcoder.get_image_info(m_file.data(), m_file.size(), ii, image_index)) + return 0; - uint32_t getImageWidth(uint32_t image_index, uint32_t level_index) { - assert(m_magic == MAGIC); - if (m_magic != MAGIC) - return 0; + return ii.m_total_levels; + } - uint32_t orig_width, orig_height, total_blocks; - if (!m_transcoder.get_image_level_desc(m_file.data(), m_file.size(), image_index, level_index, orig_width, orig_height, total_blocks)) - return 0; + uint32_t getImageWidth(uint32_t image_index, uint32_t level_index) + { + assert(m_magic == BASIS_MAGIC); + if (m_magic != BASIS_MAGIC) + return 0; - return orig_width; - } + uint32_t orig_width, orig_height, total_blocks; + if (!m_transcoder.get_image_level_desc(m_file.data(), m_file.size(), image_index, level_index, orig_width, orig_height, total_blocks)) + return 0; - uint32_t getImageHeight(uint32_t image_index, uint32_t level_index) { - assert(m_magic == MAGIC); - if (m_magic != MAGIC) - return 0; + return orig_width; + } - uint32_t orig_width, orig_height, total_blocks; - if (!m_transcoder.get_image_level_desc(m_file.data(), m_file.size(), image_index, level_index, orig_width, orig_height, total_blocks)) - return 0; + uint32_t getImageHeight(uint32_t image_index, uint32_t level_index) + { + assert(m_magic == BASIS_MAGIC); + if (m_magic != BASIS_MAGIC) + return 0; - return orig_height; - } + uint32_t orig_width, orig_height, total_blocks; + if (!m_transcoder.get_image_level_desc(m_file.data(), m_file.size(), image_index, level_index, orig_width, orig_height, total_blocks)) + return 0; + + return orig_height; + } - basis_file_desc getFileDesc() { - basis_file_desc result; - memset(&result, 0, sizeof(result)); + basis_file_desc getFileDesc() + { + basis_file_desc result; + memset(&result, 0, sizeof(result)); - assert(m_magic == MAGIC); - if (m_magic != MAGIC) - return result; + assert(m_magic == BASIS_MAGIC); + if (m_magic != BASIS_MAGIC) + return result; - basisu_file_info file_info; + basisu_file_info file_info; - if (!m_transcoder.get_file_info(m_file.data(), m_file.size(), file_info)) - { - assert(0); - return result; - } + if (!m_transcoder.get_file_info(m_file.data(), m_file.size(), file_info)) + { + assert(0); + return result; + } - result.m_version = file_info.m_version; - result.m_us_per_frame = file_info.m_us_per_frame; - result.m_total_images = file_info.m_total_images; - result.m_userdata0 = file_info.m_userdata0; - result.m_userdata1 = file_info.m_userdata1; - result.m_tex_format = static_cast(file_info.m_tex_format); - result.m_y_flipped = file_info.m_y_flipped; - result.m_has_alpha_slices = file_info.m_has_alpha_slices; + result.m_version = file_info.m_version; + result.m_us_per_frame = file_info.m_us_per_frame; + result.m_total_images = file_info.m_total_images; + result.m_userdata0 = file_info.m_userdata0; + result.m_userdata1 = file_info.m_userdata1; + result.m_tex_format = static_cast(file_info.m_tex_format); + result.m_y_flipped = file_info.m_y_flipped; + result.m_has_alpha_slices = file_info.m_has_alpha_slices; - result.m_num_endpoints = file_info.m_total_endpoints; - result.m_endpoint_palette_ofs = file_info.m_endpoint_codebook_ofs; - result.m_endpoint_palette_len = file_info.m_endpoint_codebook_size; + result.m_num_endpoints = file_info.m_total_endpoints; + result.m_endpoint_palette_ofs = file_info.m_endpoint_codebook_ofs; + result.m_endpoint_palette_len = file_info.m_endpoint_codebook_size; - result.m_num_selectors = file_info.m_total_selectors; - result.m_selector_palette_ofs = file_info.m_selector_codebook_ofs; - result.m_selector_palette_len = file_info.m_selector_codebook_size; + result.m_num_selectors = file_info.m_total_selectors; + result.m_selector_palette_ofs = file_info.m_selector_codebook_ofs; + result.m_selector_palette_len = file_info.m_selector_codebook_size; - result.m_tables_ofs = file_info.m_tables_ofs; - result.m_tables_len = file_info.m_tables_size; + result.m_tables_ofs = file_info.m_tables_ofs; + result.m_tables_len = file_info.m_tables_size; - return result; - } + return result; + } - basis_image_desc getImageDesc(uint32_t image_index) { - basis_image_desc result; - memset(&result, 0, sizeof(result)); + basis_image_desc getImageDesc(uint32_t image_index) + { + basis_image_desc result; + memset(&result, 0, sizeof(result)); - assert(m_magic == MAGIC); - if (m_magic != MAGIC) - return result; + assert(m_magic == BASIS_MAGIC); + if (m_magic != BASIS_MAGIC) + return result; - basisu_image_info image_info; + basisu_image_info image_info; - // bool get_image_info(const void *pData, uint32_t data_size, basisu_image_info &image_info, uint32_t image_index) const; - if (!m_transcoder.get_image_info(m_file.data(), m_file.size(), image_info, image_index)) - { - assert(0); - return result; - } + // bool get_image_info(const void *pData, uint32_t data_size, basisu_image_info &image_info, uint32_t image_index) const; + if (!m_transcoder.get_image_info(m_file.data(), m_file.size(), image_info, image_index)) + { + assert(0); + return result; + } - result.m_orig_width = image_info.m_orig_width; - result.m_orig_height = image_info.m_orig_height; - result.m_num_blocks_x = image_info.m_num_blocks_x; - result.m_num_blocks_y = image_info.m_num_blocks_y; - result.m_num_levels = image_info.m_total_levels; - result.m_alpha_flag = image_info.m_alpha_flag; - result.m_iframe_flag = image_info.m_iframe_flag; + result.m_orig_width = image_info.m_orig_width; + result.m_orig_height = image_info.m_orig_height; + result.m_num_blocks_x = image_info.m_num_blocks_x; + result.m_num_blocks_y = image_info.m_num_blocks_y; + result.m_num_levels = image_info.m_total_levels; + result.m_alpha_flag = image_info.m_alpha_flag; + result.m_iframe_flag = image_info.m_iframe_flag; - return result; - } + return result; + } - basis_image_level_desc getImageLevelDesc(uint32_t image_index, uint32_t level_index) { - basis_image_level_desc result; - memset(&result, 0, sizeof(result)); + basis_image_level_desc getImageLevelDesc(uint32_t image_index, uint32_t level_index) + { + basis_image_level_desc result; + memset(&result, 0, sizeof(result)); - assert(m_magic == MAGIC); - if (m_magic != MAGIC) - return result; + assert(m_magic == BASIS_MAGIC); + if (m_magic != BASIS_MAGIC) + return result; - basisu_image_level_info image_info; + basisu_image_level_info image_info; - if (!m_transcoder.get_image_level_info(m_file.data(), m_file.size(), image_info, image_index, level_index)) - { - assert(0); - return result; - } + if (!m_transcoder.get_image_level_info(m_file.data(), m_file.size(), image_info, image_index, level_index)) + { + assert(0); + return result; + } - result.m_rgb_file_ofs = image_info.m_rgb_file_ofs; - result.m_rgb_file_len = image_info.m_rgb_file_len; - result.m_alpha_file_ofs = image_info.m_alpha_file_ofs; - result.m_alpha_file_len = image_info.m_alpha_file_len; + result.m_rgb_file_ofs = image_info.m_rgb_file_ofs; + result.m_rgb_file_len = image_info.m_rgb_file_len; + result.m_alpha_file_ofs = image_info.m_alpha_file_ofs; + result.m_alpha_file_len = image_info.m_alpha_file_len; - return result; + return result; + } + + uint32_t getImageTranscodedSizeInBytes(uint32_t image_index, uint32_t level_index, uint32_t format) + { + assert(m_magic == BASIS_MAGIC); + if (m_magic != BASIS_MAGIC) + return 0; + + if (format >= (int)transcoder_texture_format::cTFTotalTextureFormats) + return 0; + + uint32_t orig_width, orig_height, total_blocks; + if (!m_transcoder.get_image_level_desc(m_file.data(), m_file.size(), image_index, level_index, orig_width, orig_height, total_blocks)) + return 0; + + const transcoder_texture_format transcoder_format = static_cast(format); + + if (basis_transcoder_format_is_uncompressed(transcoder_format)) + { + // Uncompressed formats are just plain raster images. + const uint32_t bytes_per_pixel = basis_get_uncompressed_bytes_per_pixel(transcoder_format); + const uint32_t bytes_per_line = orig_width * bytes_per_pixel; + const uint32_t bytes_per_slice = bytes_per_line * orig_height; + return bytes_per_slice; + } + else + { + // Compressed formats are 2D arrays of blocks. + const uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(transcoder_format); + + if (transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGB || transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGBA) + { + // For PVRTC1, Basis only writes (or requires) total_blocks * bytes_per_block. But GL requires extra padding for very small textures: + // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt + const uint32_t width = (orig_width + 3) & ~3; + const uint32_t height = (orig_height + 3) & ~3; + const uint32_t size_in_bytes = (std::max(8U, width) * std::max(8U, height) * 4 + 7) / 8; + return size_in_bytes; } - uint32_t getImageTranscodedSizeInBytes(uint32_t image_index, uint32_t level_index, uint32_t format) { - assert(m_magic == MAGIC); - if (m_magic != MAGIC) - return 0; + return total_blocks * bytes_per_block; + } + } - if (format >= (int)transcoder_texture_format::cTFTotalTextureFormats) - return 0; + bool isUASTC() + { + assert(m_magic == BASIS_MAGIC); + if (m_magic != BASIS_MAGIC) + return false; - uint32_t orig_width, orig_height, total_blocks; - if (!m_transcoder.get_image_level_desc(m_file.data(), m_file.size(), image_index, level_index, orig_width, orig_height, total_blocks)) - return 0; + return m_transcoder.get_tex_format(m_file.data(), m_file.size()) == basis_tex_format::cUASTC4x4; + } - const transcoder_texture_format transcoder_format = static_cast(format); + uint32_t startTranscoding() + { + assert(m_magic == BASIS_MAGIC); + if (m_magic != BASIS_MAGIC) + return 0; - if (basis_transcoder_format_is_uncompressed(transcoder_format)) - { - // Uncompressed formats are just plain raster images. - const uint32_t bytes_per_pixel = basis_get_uncompressed_bytes_per_pixel(transcoder_format); - const uint32_t bytes_per_line = orig_width * bytes_per_pixel; - const uint32_t bytes_per_slice = bytes_per_line * orig_height; - return bytes_per_slice; - } - else - { - // Compressed formats are 2D arrays of blocks. - const uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(transcoder_format); + return m_transcoder.start_transcoding(m_file.data(), m_file.size()); + } - if (transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGB || transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGBA) - { - // For PVRTC1, Basis only writes (or requires) total_blocks * bytes_per_block. But GL requires extra padding for very small textures: - // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt - const uint32_t width = (orig_width + 3) & ~3; - const uint32_t height = (orig_height + 3) & ~3; - const uint32_t size_in_bytes = (std::max(8U, width) * std::max(8U, height) * 4 + 7) / 8; - return size_in_bytes; - } + uint32_t transcodeImage(const emscripten::val& dst, uint32_t image_index, uint32_t level_index, uint32_t format, uint32_t unused, uint32_t get_alpha_for_opaque_formats) + { + (void)unused; - return total_blocks * bytes_per_block; - } + assert(m_magic == BASIS_MAGIC); + if (m_magic != BASIS_MAGIC) + return 0; + + if (format >= (int)transcoder_texture_format::cTFTotalTextureFormats) + return 0; + + const transcoder_texture_format transcoder_format = static_cast(format); + + uint32_t orig_width, orig_height, total_blocks; + if (!m_transcoder.get_image_level_desc(m_file.data(), m_file.size(), image_index, level_index, orig_width, orig_height, total_blocks)) + return 0; + + basisu::vector dst_data; + + uint32_t flags = get_alpha_for_opaque_formats ? cDecodeFlagsTranscodeAlphaDataToOpaqueFormats : 0; + + uint32_t status; + + if (basis_transcoder_format_is_uncompressed(transcoder_format)) + { + const uint32_t bytes_per_pixel = basis_get_uncompressed_bytes_per_pixel(transcoder_format); + const uint32_t bytes_per_line = orig_width * bytes_per_pixel; + const uint32_t bytes_per_slice = bytes_per_line * orig_height; + + dst_data.resize(bytes_per_slice); + + status = m_transcoder.transcode_image_level( + m_file.data(), m_file.size(), image_index, level_index, + dst_data.data(), orig_width * orig_height, + transcoder_format, + flags, + orig_width, + nullptr, + orig_height); + } + else + { + uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(transcoder_format); + + uint32_t required_size = total_blocks * bytes_per_block; + + if (transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGB || transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGBA) + { + // For PVRTC1, Basis only writes (or requires) total_blocks * bytes_per_block. But GL requires extra padding for very small textures: + // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt + // The transcoder will clear the extra bytes followed the used blocks to 0. + const uint32_t width = (orig_width + 3) & ~3; + const uint32_t height = (orig_height + 3) & ~3; + required_size = (std::max(8U, width) * std::max(8U, height) * 4 + 7) / 8; + assert(required_size >= total_blocks * bytes_per_block); } - bool isUASTC() { - assert(m_magic == MAGIC); - if (m_magic != MAGIC) - return false; + dst_data.resize(required_size); - return m_transcoder.get_tex_format(m_file.data(), m_file.size()) == basis_tex_format::cUASTC4x4; - } + status = m_transcoder.transcode_image_level( + m_file.data(), m_file.size(), image_index, level_index, + dst_data.data(), dst_data.size() / bytes_per_block, + static_cast(format), + flags); + } - uint32_t startTranscoding() { - assert(m_magic == MAGIC); - if (m_magic != MAGIC) - return 0; + emscripten::val memory = emscripten::val::module_property("HEAP8")["buffer"]; + emscripten::val memoryView = emscripten::val::global("Uint8Array").new_(memory, reinterpret_cast(dst_data.data()), dst_data.size()); - return m_transcoder.start_transcoding(m_file.data(), m_file.size()); - } - - uint32_t transcodeImage(const emscripten::val& dst, uint32_t image_index, uint32_t level_index, uint32_t format, uint32_t unused, uint32_t get_alpha_for_opaque_formats) { - (void)unused; - - assert(m_magic == MAGIC); - if (m_magic != MAGIC) - return 0; - - if (format >= (int)transcoder_texture_format::cTFTotalTextureFormats) - return 0; - - const transcoder_texture_format transcoder_format = static_cast(format); - - uint32_t orig_width, orig_height, total_blocks; - if (!m_transcoder.get_image_level_desc(m_file.data(), m_file.size(), image_index, level_index, orig_width, orig_height, total_blocks)) - return 0; - - basisu::vector dst_data; - - uint32_t flags = get_alpha_for_opaque_formats ? cDecodeFlagsTranscodeAlphaDataToOpaqueFormats : 0; - - uint32_t status; - - if (basis_transcoder_format_is_uncompressed(transcoder_format)) - { - const uint32_t bytes_per_pixel = basis_get_uncompressed_bytes_per_pixel(transcoder_format); - const uint32_t bytes_per_line = orig_width * bytes_per_pixel; - const uint32_t bytes_per_slice = bytes_per_line * orig_height; - - dst_data.resize(bytes_per_slice); - - status = m_transcoder.transcode_image_level( - m_file.data(), m_file.size(), image_index, level_index, - dst_data.data(), orig_width * orig_height, - transcoder_format, - flags, - orig_width, - nullptr, - orig_height); - } - else - { - uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(transcoder_format); - - uint32_t required_size = total_blocks * bytes_per_block; - - if (transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGB || transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGBA) - { - // For PVRTC1, Basis only writes (or requires) total_blocks * bytes_per_block. But GL requires extra padding for very small textures: - // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt - // The transcoder will clear the extra bytes followed the used blocks to 0. - const uint32_t width = (orig_width + 3) & ~3; - const uint32_t height = (orig_height + 3) & ~3; - required_size = (std::max(8U, width) * std::max(8U, height) * 4 + 7) / 8; - assert(required_size >= total_blocks * bytes_per_block); - } - - dst_data.resize(required_size); - - status = m_transcoder.transcode_image_level( - m_file.data(), m_file.size(), image_index, level_index, - dst_data.data(), dst_data.size() / bytes_per_block, - static_cast(format), - flags); - } - - emscripten::val memory = emscripten::val::module_property("HEAP8")["buffer"]; - emscripten::val memoryView = emscripten::val::global("Uint8Array").new_(memory, reinterpret_cast(dst_data.data()), dst_data.size()); - - dst.call("set", memoryView); - return status; - } + dst.call("set", memoryView); + return status; + } }; +#if BASISD_SUPPORT_KTX2 +struct ktx2_header_js +{ + uint32_t m_vk_format; + uint32_t m_type_size; + uint32_t m_pixel_width; + uint32_t m_pixel_height; + uint32_t m_pixel_depth; + uint32_t m_layer_count; + uint32_t m_face_count; + uint32_t m_level_count; + uint32_t m_supercompression_scheme; + uint32_t m_dfd_byte_offset; + uint32_t m_dfd_byte_length; + uint32_t m_kvd_byte_offset; + uint32_t m_kvd_byte_length; + uint32_t m_sgd_byte_offset; + uint32_t m_sgd_byte_length; +}; + +struct ktx2_file +{ + int m_magic = 0; + basist::ktx2_transcoder m_transcoder; + basisu::vector m_file; + bool m_is_valid = false; + + ktx2_file(const emscripten::val& jsBuffer) + : m_file([&]() { + size_t byteLength = jsBuffer["byteLength"].as(); + return basisu::vector(byteLength); + }()), + m_transcoder(g_pGlobal_codebook) + { + if (!g_pGlobal_codebook) + { +#if BASISU_DEBUG_PRINTF + printf("basis_file::basis_file: Must call basis_init() first!\n"); +#endif + assert(0); + return; + } + + unsigned int length = jsBuffer["length"].as(); + emscripten::val memory = emscripten::val::module_property("HEAP8")["buffer"]; + emscripten::val memoryView = jsBuffer["constructor"].new_(memory, reinterpret_cast(m_file.data()), length); + memoryView.call("set", jsBuffer); + + if (!m_transcoder.init(m_file.data(), m_file.size())) + { +#if BASISU_DEBUG_PRINTF + printf("m_transcoder.init() failed!\n"); +#endif + assert(0); + + m_file.clear(); + } + + m_is_valid = true; + + // Initialized after validation + m_magic = KTX2_MAGIC; + } + + bool isValid() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return false; + + return m_is_valid; + } + + void close() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return; + + m_file.clear(); + m_transcoder.clear(); + } + + uint32_t getDFDSize() + { + return m_transcoder.get_dfd().size(); + } + + uint32_t getDFD(const emscripten::val& dst) + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + + const uint8_vec &dst_data = m_transcoder.get_dfd(); + + if (dst_data.size()) + return copy_to_jsbuffer(dst, dst_data); + + return 1; + } + + ktx2_header_js getHeader() + { + ktx2_header_js hdr; + memset(&hdr, 0, sizeof(hdr)); + + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return hdr; + + const basist::ktx2_header& h = m_transcoder.get_header(); + + hdr.m_vk_format = h.m_vk_format; + hdr.m_type_size = h.m_type_size; + hdr.m_pixel_width = h.m_pixel_width; + hdr.m_pixel_height = h.m_pixel_height; + hdr.m_pixel_depth = h.m_pixel_depth; + hdr.m_layer_count = h.m_layer_count; + hdr.m_face_count = h.m_face_count; + hdr.m_level_count = h.m_level_count; + hdr.m_supercompression_scheme = h.m_supercompression_scheme; + hdr.m_dfd_byte_offset = h.m_dfd_byte_offset; + hdr.m_dfd_byte_length = h.m_dfd_byte_length; + hdr.m_kvd_byte_offset = h.m_kvd_byte_offset; + hdr.m_kvd_byte_length = h.m_kvd_byte_length; + + // emscripten doesn't support binding uint64_t for some reason + assert(h.m_sgd_byte_offset <= UINT32_MAX); + assert(h.m_sgd_byte_length <= UINT32_MAX); + hdr.m_sgd_byte_offset = (uint32_t)h.m_sgd_byte_offset; + hdr.m_sgd_byte_length = (uint32_t)h.m_sgd_byte_length; + + return hdr; + } + + bool hasKey(std::string key_name) + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return false; + + return m_transcoder.find_key(key_name) != nullptr; + } + + uint32_t getTotalKeys() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + + return m_transcoder.get_key_values().size(); + } + + std::string getKey(uint32_t index) + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return std::string(""); + + return std::string((const char*)m_transcoder.get_key_values()[index].m_key.data()); + } + + uint32_t getKeyValueSize(std::string key_name) + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + + const uint8_vec* p = m_transcoder.find_key(key_name); + return p ? p->size() : 0; + } + + uint32_t getKeyValue(std::string key_name, const emscripten::val& dst) + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + + const uint8_vec* p = m_transcoder.find_key(key_name); + if (!p) + return 0; + + if (p->size()) + return copy_to_jsbuffer(dst, *p); + + return 1; + } + + uint32_t getWidth() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return m_transcoder.get_width(); + } + + uint32_t getHeight() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return m_transcoder.get_height(); + } + + uint32_t getFaces() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return m_transcoder.get_faces(); + } + + uint32_t getLayers() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return m_transcoder.get_layers(); + } + + uint32_t getLevels() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return m_transcoder.get_levels(); + } + + uint32_t getFormat() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return (uint32_t)m_transcoder.get_format(); + } + + bool isUASTC() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return false; + return m_transcoder.is_uastc(); + } + + bool isETC1S() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return false; + return m_transcoder.is_etc1s(); + } + + bool getHasAlpha() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return false; + return m_transcoder.get_has_alpha(); + } + + uint32_t getDFDColorModel() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return m_transcoder.get_dfd_color_model(); + } + + uint32_t getDFDColorPrimaries() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return m_transcoder.get_dfd_color_primaries(); + } + + uint32_t getDFDTransferFunc() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return m_transcoder.get_dfd_transfer_func(); + } + + uint32_t getDFDFlags() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return m_transcoder.get_dfd_flags(); + } + + uint32_t getDFDTotalSamples() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return m_transcoder.get_dfd_total_samples(); + } + + uint32_t getDFDChannelID0() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return m_transcoder.get_dfd_channel_id0(); + } + + uint32_t getDFDChannelID1() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return m_transcoder.get_dfd_channel_id1(); + } + + // isVideo() will return true if there was a KTXanimData key, or if (after calling start_transcoding()) there were any P-frames on ETC1S files. + bool isVideo() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + return m_transcoder.is_video(); + } + + // startTranscoding() must be called before calling getETC1SImageDescImageFlags(). + uint32_t getETC1SImageDescImageFlags(uint32_t level_index, uint32_t layer_index, uint32_t face_index) + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + + return m_transcoder.get_etc1s_image_descs_image_flags(level_index, layer_index, face_index); + } + + ktx2_image_level_info getImageLevelInfo(uint32_t level_index, uint32_t layer_index, uint32_t face_index) + { + ktx2_image_level_info info; + memset(&info, 0, sizeof(info)); + + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return info; + + if (!m_transcoder.get_image_level_info(info, level_index, layer_index, face_index)) + { + assert(0); + return info; + } + + return info; + } + + uint32_t getImageTranscodedSizeInBytes(uint32_t level_index, uint32_t layer_index, uint32_t face_index, uint32_t format) + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + + if (format >= (int)transcoder_texture_format::cTFTotalTextureFormats) + return 0; + + ktx2_image_level_info info; + if (!m_transcoder.get_image_level_info(info, level_index, layer_index, face_index)) + return 0; + + uint32_t orig_width = info.m_orig_width, orig_height = info.m_orig_height, total_blocks = info.m_total_blocks; + + const transcoder_texture_format transcoder_format = static_cast(format); + + if (basis_transcoder_format_is_uncompressed(transcoder_format)) + { + // Uncompressed formats are just plain raster images. + const uint32_t bytes_per_pixel = basis_get_uncompressed_bytes_per_pixel(transcoder_format); + const uint32_t bytes_per_line = orig_width * bytes_per_pixel; + const uint32_t bytes_per_slice = bytes_per_line * orig_height; + return bytes_per_slice; + } + else + { + // Compressed formats are 2D arrays of blocks. + const uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(transcoder_format); + + if (transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGB || transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGBA) + { + // For PVRTC1, Basis only writes (or requires) total_blocks * bytes_per_block. But GL requires extra padding for very small textures: + // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt + const uint32_t width = (orig_width + 3) & ~3; + const uint32_t height = (orig_height + 3) & ~3; + const uint32_t size_in_bytes = (std::max(8U, width) * std::max(8U, height) * 4 + 7) / 8; + return size_in_bytes; + } + + return total_blocks * bytes_per_block; + } + } + + // Must be called before transcodeImage() can be called. + // On ETC1S files this method decompresses the ETC1S global data, along with fetching the ETC1S image desc array, so it's not free to call. + uint32_t startTranscoding() + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + + return m_transcoder.start_transcoding(); + } + + // get_alpha_for_opaque_formats defaults to false + // channel0/channel1 default to -1 + uint32_t transcodeImage(const emscripten::val& dst, uint32_t level_index, uint32_t layer_index, uint32_t face_index, uint32_t format, uint32_t get_alpha_for_opaque_formats, int channel0, int channel1) + { + assert(m_magic == KTX2_MAGIC); + if (m_magic != KTX2_MAGIC) + return 0; + + if (format >= (int)transcoder_texture_format::cTFTotalTextureFormats) + return 0; + + const transcoder_texture_format transcoder_format = static_cast(format); + + ktx2_image_level_info info; + if (!m_transcoder.get_image_level_info(info, level_index, layer_index, face_index)) + return 0; + + uint32_t orig_width = info.m_orig_width, orig_height = info.m_orig_height, total_blocks = info.m_total_blocks; + + basisu::vector dst_data; + + uint32_t flags = get_alpha_for_opaque_formats ? cDecodeFlagsTranscodeAlphaDataToOpaqueFormats : 0; + + uint32_t status; + + if (basis_transcoder_format_is_uncompressed(transcoder_format)) + { + const uint32_t bytes_per_pixel = basis_get_uncompressed_bytes_per_pixel(transcoder_format); + const uint32_t bytes_per_line = orig_width * bytes_per_pixel; + const uint32_t bytes_per_slice = bytes_per_line * orig_height; + + dst_data.resize(bytes_per_slice); + + status = m_transcoder.transcode_image_level( + level_index, layer_index, face_index, + dst_data.data(), orig_width * orig_height, + transcoder_format, + flags, + orig_width, + orig_height, + channel0, channel1, + nullptr); + } + else + { + uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(transcoder_format); + + uint32_t required_size = total_blocks * bytes_per_block; + + if (transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGB || transcoder_format == transcoder_texture_format::cTFPVRTC1_4_RGBA) + { + // For PVRTC1, Basis only writes (or requires) total_blocks * bytes_per_block. But GL requires extra padding for very small textures: + // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt + // The transcoder will clear the extra bytes followed the used blocks to 0. + const uint32_t width = (orig_width + 3) & ~3; + const uint32_t height = (orig_height + 3) & ~3; + required_size = (std::max(8U, width) * std::max(8U, height) * 4 + 7) / 8; + assert(required_size >= total_blocks * bytes_per_block); + } + + dst_data.resize(required_size); + + status = m_transcoder.transcode_image_level( + level_index, layer_index, face_index, + dst_data.data(), dst_data.size() / bytes_per_block, + static_cast(format), + flags, + 0, + 0, + channel0, channel1, + nullptr); + } + + emscripten::val memory = emscripten::val::module_property("HEAP8")["buffer"]; + emscripten::val memoryView = emscripten::val::global("Uint8Array").new_(memory, reinterpret_cast(dst_data.data()), dst_data.size()); + + dst.call("set", memoryView); + return status; + } + +}; +#endif // BASISD_SUPPORT_KTX2 + #if BASISU_SUPPORT_ENCODING class basis_encoder { @@ -593,12 +1107,24 @@ public: return 0; } - // Compression succeeded, so copy the .basis file bytes to the caller's buffer. - if (!copy_to_jsbuffer(dst_basis_file_js_val, comp.get_output_basis_file())) - return 0; + if (params.m_create_ktx2_file) + { + // Compression succeeded, so copy the .ktx2 file bytes to the caller's buffer. + if (!copy_to_jsbuffer(dst_basis_file_js_val, comp.get_output_ktx2_file())) + return 0; - // Return the file size of the .basis file in bytes. - return (uint32_t)comp.get_output_basis_file().size(); + // Return the file size of the .basis file in bytes. + return (uint32_t)comp.get_output_ktx2_file().size(); + } + else + { + // Compression succeeded, so copy the .basis file bytes to the caller's buffer. + if (!copy_to_jsbuffer(dst_basis_file_js_val, comp.get_output_basis_file())) + return 0; + + // Return the file size of the .basis file in bytes. + return (uint32_t)comp.get_output_basis_file().size(); + } } }; #endif @@ -898,7 +1424,7 @@ EMSCRIPTEN_BINDINGS(basis_codec) { // enum class transcoder_texture_format enum_("transcoder_texture_format") - .value("cTFETC1_RGB", transcoder_texture_format::cTFETC1_RGB) + .value("cTFETC1_RGB", transcoder_texture_format::cTFETC1_RGB) .value("cTFETC2_RGBA", transcoder_texture_format::cTFETC2_RGBA) .value("cTFBC1_RGB", transcoder_texture_format::cTFBC1_RGB) .value("cTFBC3_RGBA", transcoder_texture_format::cTFBC3_RGBA) @@ -932,7 +1458,7 @@ EMSCRIPTEN_BINDINGS(basis_codec) { // Expose enum basis_texture_type enum_("basis_texture_type") - .value("cBASISTexType2D", cBASISTexType2D) + .value("cBASISTexType2D", cBASISTexType2D) .value("cBASISTexType2DArray", cBASISTexType2DArray) .value("cBASISTexTypeCubemapArray", cBASISTexTypeCubemapArray) .value("cBASISTexTypeVideoFrames", cBASISTexTypeVideoFrames) @@ -941,11 +1467,11 @@ EMSCRIPTEN_BINDINGS(basis_codec) { // Expose enum basis_tex_format enum_("basis_tex_format") - .value("cETC1S", basis_tex_format::cETC1S) + .value("cETC1S", basis_tex_format::cETC1S) .value("cUASTC4x4", basis_tex_format::cUASTC4x4) ; - - // Transcoder object. If all you want to do is transcode already encoded .basis files, this is all you really need. + + // .basis file transcoder object. If all you want to do is transcode already encoded .basis files, this is all you really need. class_("BasisFile") .constructor() .function("close", optional_override([](basis_file& self) { @@ -1016,13 +1542,137 @@ EMSCRIPTEN_BINDINGS(basis_codec) { // The low-level UASTC transcoder is a single function. function("transcodeUASTCImage", &transcode_uastc_image); - // Optional encoding/compression support + function("transcoderSupportsKTX2", &basisu_transcoder_supports_ktx2); + function("transcoderSupportsKTX2Zstd", &basisu_transcoder_supports_ktx2_zstd); + +#if BASISD_SUPPORT_KTX2 + // KTX2 enums/constants + enum_("ktx2_supercompression") + .value("KTX2_SS_NONE", KTX2_SS_NONE) + .value("KTX2_SS_BASISLZ", KTX2_SS_BASISLZ) + .value("KTX2_SS_ZSTANDARD", KTX2_SS_ZSTANDARD) + ; + + constant("KTX2_VK_FORMAT_UNDEFINED", KTX2_VK_FORMAT_UNDEFINED); + constant("KTX2_KDF_DF_MODEL_UASTC", KTX2_KDF_DF_MODEL_UASTC); + constant("KTX2_KDF_DF_MODEL_ETC1S", KTX2_KDF_DF_MODEL_ETC1S); + constant("KTX2_IMAGE_IS_P_FRAME", KTX2_IMAGE_IS_P_FRAME); + constant("KTX2_UASTC_BLOCK_SIZE", KTX2_UASTC_BLOCK_SIZE); + constant("KTX2_MAX_SUPPORTED_LEVEL_COUNT", KTX2_MAX_SUPPORTED_LEVEL_COUNT); + + constant("KTX2_KHR_DF_TRANSFER_LINEAR", KTX2_KHR_DF_TRANSFER_LINEAR); + constant("KTX2_KHR_DF_TRANSFER_SRGB", KTX2_KHR_DF_TRANSFER_SRGB); + + enum_("ktx2_df_channel_id") + .value("KTX2_DF_CHANNEL_ETC1S_RGB", KTX2_DF_CHANNEL_ETC1S_RGB) + .value("KTX2_DF_CHANNEL_ETC1S_RRR", KTX2_DF_CHANNEL_ETC1S_RRR) + .value("KTX2_DF_CHANNEL_ETC1S_GGG", KTX2_DF_CHANNEL_ETC1S_GGG) + .value("KTX2_DF_CHANNEL_ETC1S_AAA", KTX2_DF_CHANNEL_ETC1S_AAA) + .value("KTX2_DF_CHANNEL_UASTC_DATA", KTX2_DF_CHANNEL_UASTC_DATA) + .value("KTX2_DF_CHANNEL_UASTC_RGB", KTX2_DF_CHANNEL_UASTC_RGB) + .value("KTX2_DF_CHANNEL_UASTC_RGBA", KTX2_DF_CHANNEL_UASTC_RGBA) + .value("KTX2_DF_CHANNEL_UASTC_RRR", KTX2_DF_CHANNEL_UASTC_RRR) + .value("KTX2_DF_CHANNEL_UASTC_RRRG", KTX2_DF_CHANNEL_UASTC_RRRG) + .value("KTX2_DF_CHANNEL_UASTC_RG", KTX2_DF_CHANNEL_UASTC_RG) + ; + + enum_("ktx2_df_color_primaries") + .value("KTX2_DF_PRIMARIES_UNSPECIFIED", KTX2_DF_PRIMARIES_UNSPECIFIED) + .value("KTX2_DF_PRIMARIES_BT709", KTX2_DF_PRIMARIES_BT709) + .value("KTX2_DF_PRIMARIES_SRGB", KTX2_DF_PRIMARIES_SRGB) + .value("KTX2_DF_PRIMARIES_BT601_EBU", KTX2_DF_PRIMARIES_BT601_EBU) + .value("KTX2_DF_PRIMARIES_BT601_SMPTE", KTX2_DF_PRIMARIES_BT601_SMPTE) + .value("KTX2_DF_PRIMARIES_BT2020", KTX2_DF_PRIMARIES_BT2020) + .value("KTX2_DF_PRIMARIES_CIEXYZ", KTX2_DF_PRIMARIES_CIEXYZ) + .value("KTX2_DF_PRIMARIES_ACES", KTX2_DF_PRIMARIES_ACES) + .value("KTX2_DF_PRIMARIES_ACESCC", KTX2_DF_PRIMARIES_ACESCC) + .value("KTX2_DF_PRIMARIES_NTSC1953", KTX2_DF_PRIMARIES_NTSC1953) + .value("KTX2_DF_PRIMARIES_PAL525", KTX2_DF_PRIMARIES_PAL525) + .value("KTX2_DF_PRIMARIES_DISPLAYP3", KTX2_DF_PRIMARIES_DISPLAYP3) + .value("KTX2_DF_PRIMARIES_ADOBERGB", KTX2_DF_PRIMARIES_ADOBERGB) + ; + + // Expose ktx2_image_level_info structure + value_object("KTX2ImageLevelInfo") + .field("levelIndex", &ktx2_image_level_info::m_level_index) + .field("layerIndex", &ktx2_image_level_info::m_layer_index) + .field("faceIndex", &ktx2_image_level_info::m_face_index) + .field("origWidth", &ktx2_image_level_info::m_orig_width) + .field("origHeight", &ktx2_image_level_info::m_orig_height) + .field("width", &ktx2_image_level_info::m_width) + .field("height", &ktx2_image_level_info::m_height) + .field("numBlocksX", &ktx2_image_level_info::m_num_blocks_x) + .field("numBlocksY", &ktx2_image_level_info::m_num_blocks_y) + .field("totalBlocks", &ktx2_image_level_info::m_total_blocks) + .field("alphaFlag", &ktx2_image_level_info::m_alpha_flag) + .field("iframeFlag", &ktx2_image_level_info::m_iframe_flag) + ; + + // Expose the ktx2_header_js structure + value_object("KTX2Header") + .field("vkFormat", &ktx2_header_js::m_vk_format) + .field("typeSize", &ktx2_header_js::m_type_size) + .field("pixelWidth", &ktx2_header_js::m_pixel_width) + .field("pixelHeight", &ktx2_header_js::m_pixel_height) + .field("pixelDepth", &ktx2_header_js::m_pixel_depth) + .field("layerCount", &ktx2_header_js::m_layer_count) + .field("faceCount", &ktx2_header_js::m_face_count) + .field("levelCount", &ktx2_header_js::m_level_count) + .field("supercompressionScheme", &ktx2_header_js::m_supercompression_scheme) + .field("dfdByteOffset", &ktx2_header_js::m_dfd_byte_offset) + .field("dfdByteLength", &ktx2_header_js::m_dfd_byte_length) + .field("kvdByteOffset", &ktx2_header_js::m_kvd_byte_offset) + .field("kvdByteLength", &ktx2_header_js::m_kvd_byte_length) + .field("sgdByteOffset", &ktx2_header_js::m_sgd_byte_offset) + .field("sgdByteLength", &ktx2_header_js::m_sgd_byte_length) + ; + + // KTX2 transcoder class + class_("KTX2File") + .constructor() + .function("isValid", &ktx2_file::isValid) + .function("close", &ktx2_file::close) + .function("getDFDSize", &ktx2_file::getDFDSize) + .function("getDFD", &ktx2_file::getDFD) + .function("getHeader", &ktx2_file::getHeader) + .function("hasKey", &ktx2_file::hasKey) + .function("getTotalKeys", &ktx2_file::getTotalKeys) + .function("getKey", &ktx2_file::getKey) + .function("getKeyValueSize", &ktx2_file::getKeyValueSize) + .function("getKeyValue", &ktx2_file::getKeyValue) + .function("getWidth", &ktx2_file::getWidth) + .function("getHeight", &ktx2_file::getHeight) + .function("getFaces", &ktx2_file::getFaces) + .function("getLayers", &ktx2_file::getLayers) + .function("getLevels", &ktx2_file::getLevels) + .function("getFormat", &ktx2_file::getFormat) + .function("isUASTC", &ktx2_file::isUASTC) + .function("isETC1S", &ktx2_file::isETC1S) + .function("getHasAlpha", &ktx2_file::getHasAlpha) + .function("getDFDColorModel", &ktx2_file::getDFDColorModel) + .function("getDFDColorPrimaries", &ktx2_file::getDFDColorPrimaries) + .function("getDFDTransferFunc", &ktx2_file::getDFDTransferFunc) + .function("getDFDFlags", &ktx2_file::getDFDFlags) + .function("getDFDTotalSamples", &ktx2_file::getDFDTotalSamples) + .function("getDFDChannelID0", &ktx2_file::getDFDChannelID0) + .function("getDFDChannelID1", &ktx2_file::getDFDChannelID1) + .function("isVideo", &ktx2_file::isVideo) + .function("getETC1SImageDescImageFlags", &ktx2_file::getETC1SImageDescImageFlags) + .function("getImageLevelInfo", &ktx2_file::getImageLevelInfo) + .function("getImageTranscodedSizeInBytes", &ktx2_file::getImageTranscodedSizeInBytes) + .function("startTranscoding", &ktx2_file::startTranscoding) + .function("transcodeImage", &ktx2_file::transcodeImage) + ; + +#endif // BASISD_SUPPORT_KTX2 + + // Optional encoding/compression support of .basis and .KTX2 files (the same class encodes/compresses to either format). #if BASISU_SUPPORT_ENCODING // Compressor Constants - constant("BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION", BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); + constant("BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION", BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); constant("BASISU_DEFAULT_ENDPOINT_RDO_THRESH", BASISU_DEFAULT_ENDPOINT_RDO_THRESH); constant("BASISU_DEFAULT_SELECTOR_RDO_THRESH", BASISU_DEFAULT_SELECTOR_RDO_THRESH); constant("BASISU_DEFAULT_QUALITY", BASISU_DEFAULT_QUALITY); @@ -1054,10 +1704,11 @@ EMSCRIPTEN_BINDINGS(basis_codec) { constant("UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO", UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO); constant("UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH", UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH); - + // Compression/encoding object. - // You create this object, call the set() methods to fill in the parameters/source images/options, call encode(), and you get back a .basis file. + // You create this object, call the set() methods to fill in the parameters/source images/options, call encode(), and you get back a .basis or .KTX2 file. // You can call .encode() multiple times, changing the parameters/options in between calls. + // By default this class encodes to .basis, but call setCreateKTX2File() with true to get .KTX2 files. class_("BasisEncoder") .constructor<>() @@ -1088,6 +1739,7 @@ EMSCRIPTEN_BINDINGS(basis_codec) { // Enables debug output to stdout .function("setDebug", optional_override([](basis_encoder& self, bool debug_flag) { self.m_params.m_debug = debug_flag; + g_debug_printf = debug_flag; })) // If true, the input is assumed to be in sRGB space. Be sure to set this correctly! (Examples: True on photos, albedo/spec maps, and false on normal maps.) @@ -1169,6 +1821,27 @@ EMSCRIPTEN_BINDINGS(basis_codec) { .function("setEndpointRDOThresh", optional_override([](basis_encoder& self, float endpoint_rdo_thresh) { self.m_params.m_endpoint_rdo_thresh = endpoint_rdo_thresh; })) + +#if BASISD_SUPPORT_KTX2 + // --- KTX2 related options + // + // Create .KTX2 files instead of .basis files. By default this is FALSE. + .function("setCreateKTX2File", optional_override([](basis_encoder& self, bool create_ktx2_file) { + self.m_params.m_create_ktx2_file = create_ktx2_file; + })) + + // KTX2: Use UASTC Zstandard supercompression. Defaults to disabled or KTX2_SS_NONE. + .function("setKTX2UASTCSupercompression", optional_override([](basis_encoder& self, bool use_zstandard) { + self.m_params.m_ktx2_uastc_supercompression = use_zstandard ? basist::KTX2_SS_ZSTANDARD : basist::KTX2_SS_NONE; + })) + + // KTX2: Use sRGB transfer func in the file's DFD. Default is FALSE. This should very probably match the "perceptual" setting. + .function("setKTX2SRGBTransferFunc", optional_override([](basis_encoder& self, bool srgb_transfer_func) { + self.m_params.m_ktx2_srgb_transfer_func = srgb_transfer_func; + })) + + // TODO: Expose KTX2 key value array, other options to JavaScript. See encoder/basisu_comp.h. +#endif // --- Mip-map options diff --git a/webgl/transcoder/build/basis_transcoder.js b/webgl/transcoder/build/basis_transcoder.js index 95de34e..9e285dd 100644 --- a/webgl/transcoder/build/basis_transcoder.js +++ b/webgl/transcoder/build/basis_transcoder.js @@ -6,7 +6,7 @@ var BASIS = (function() { function(BASIS) { BASIS = BASIS || {}; -var Module=typeof BASIS!=="undefined"?BASIS:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i=55296&&codeUnit<=57343)++i;len+=4}return len}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="basis_transcoder.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return Promise.resolve().then(getBinary)}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["H"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["I"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var ExceptionInfoAttrs={DESTRUCTOR_OFFSET:0,REFCOUNT_OFFSET:4,TYPE_OFFSET:8,CAUGHT_OFFSET:12,RETHROWN_OFFSET:13,SIZE:16};function ___cxa_allocate_exception(size){return _malloc(size+ExceptionInfoAttrs.SIZE)+ExceptionInfoAttrs.SIZE}function ExceptionInfo(excPtr){this.excPtr=excPtr;this.ptr=excPtr-ExceptionInfoAttrs.SIZE;this.set_type=function(type){HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]=type};this.get_type=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]};this.set_destructor=function(destructor){HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]=destructor};this.get_destructor=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]};this.set_refcount=function(refcount){HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=refcount};this.set_caught=function(caught){caught=caught?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]=caught};this.get_caught=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]!=0};this.set_rethrown=function(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]=rethrown};this.get_rethrown=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]!=0};this.init=function(type,destructor){this.set_type(type);this.set_destructor(destructor);this.set_refcount(0);this.set_caught(false);this.set_rethrown(false)};this.add_ref=function(){var value=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=value+1};this.release_ref=function(){var prev=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=prev-1;return prev===1}}var exceptionLast=0;var uncaughtExceptionCount=0;function ___cxa_throw(ptr,type,destructor){var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw ptr}var structRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+' "use strict";'+" return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationGroup=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function attachFinalizer(handle){if("undefined"===typeof FinalizationGroup){attachFinalizer=function(handle){return handle};return handle}finalizationGroup=new FinalizationGroup(function(iter){for(var result=iter.next();!result.done;result=iter.next()){var $$=result.value;if(!$$.ptr){console.warn("object already deleted: "+$$.ptr)}else{releaseClassHandle($$)}}});attachFinalizer=function(handle){finalizationGroup.register(handle,handle.$$,handle.$$);return handle};detachFinalizer=function(handle){finalizationGroup.unregister(handle.$$)};return attachFinalizer(handle)}function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}}function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}}function ClassHandle_isDeleted(){return!this.$$.ptr}var delayFunction=undefined;var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}var registeredPointers={};function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError("Expected null or instance of "+desiredClass.name+", got an instance of "+ptrClass.name)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr}function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,__emval_register(function(){clonedHandle["delete"]()}));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+handle.$$.ptrType.name+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)}function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function makeClassHandle(prototype,record){if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record}}))}function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["fromWireType"]=RegisteredPointer_fromWireType}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function dynCallLegacy(sig,ptr,args){if(args&&args.length){return Module["dynCall_"+sig].apply(null,[ptr].concat(args))}return Module["dynCall_"+sig].call(null,ptr)}function dynCall(sig,ptr,args){if(sig.indexOf("j")!=-1){return dynCallLegacy(sig,ptr,args)}return wasmTable.get(ptr).apply(null,args)}function getDynCaller(sig,ptr){assert(sig.indexOf("j")>=0,"getDynCaller should only be called with i64 sigs");var argCache=[];return function(){argCache.length=arguments.length;for(var i=0;i>2)+i])}return array}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);var args=[rawConstructor];var destructors=[];whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName="constructor "+classType.name;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError("Cannot register multiple constructors with identical number of parameters ("+(argCount-1)+") for class '"+classType.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!")}classType.registeredClass.constructor_body[argCount-1]=function unboundTypeHandler(){throwUnboundTypeError("Cannot construct "+classType.name+" due to unbound types",rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){classType.registeredClass.constructor_body[argCount-1]=function constructor_body(){if(arguments.length!==argCount-1){throwBindingError(humanName+" called with "+arguments.length+" arguments, expected "+(argCount-1))}destructors.length=0;args.length=argCount;for(var i=1;i0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __embind_register_enum_value(rawEnumType,name,enumValue){var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(enumType.name+"_"+name,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(valuemaxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_object(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){structRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}}function __embind_register_value_object_field(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){structRegistrations[structType].fields.push({fieldName:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}var emval_methodCallers=[];function __emval_call_void_method(caller,handle,methodName,args){caller=emval_methodCallers[caller];handle=requireHandle(handle);methodName=getStringOrSymbol(methodName);caller(handle,methodName,null,args)}function emval_get_global(){if(typeof globalThis==="object"){return globalThis}return function(){return Function}()("return this")()}function __emval_get_global(name){if(name===0){return __emval_register(emval_get_global())}else{name=getStringOrSymbol(name);return __emval_register(emval_get_global()[name])}}function __emval_addMethodCaller(caller){var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id}function __emval_lookupTypes(argCount,argTypes){var a=new Array(argCount);for(var i=0;i>2)+i],"parameter "+i)}return a}function __emval_get_method_caller(argCount,argTypes){var types=__emval_lookupTypes(argCount,argTypes);var retType=types[0];var signatureName=retType.name+"_$"+types.slice(1).map(function(t){return t.name}).join("_")+"$";var params=["retType"];var args=[retType];var argsList="";for(var i=0;i4){emval_handle_array[handle].refcount+=1}}function craftEmvalAllocator(argCount){var argsList="";for(var i=0;i>> 2) + "+i+'], "parameter '+i+'");\n'+"var arg"+i+" = argType"+i+".readValueFromPointer(args);\n"+"args += argType"+i+"['argPackAdvance'];\n"}functionBody+="var obj = new constructor("+argsList+");\n"+"return __emval_register(obj);\n"+"}\n";return new Function("requireRegisteredType","Module","__emval_register",functionBody)(requireRegisteredType,Module,__emval_register)}var emval_newers={};function __emval_new(handle,argCount,argTypes,args){handle=requireHandle(handle);var newer=emval_newers[argCount];if(!newer){newer=craftEmvalAllocator(argCount);emval_newers[argCount]=newer}return newer(handle,argTypes,args)}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function _abort(){abort()}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_RegisteredPointer();init_embind();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();__ATINIT__.push({func:function(){___wasm_call_ctors()}});var asmLibraryArg={"G":___cxa_allocate_exception,"F":___cxa_throw,"s":__embind_finalize_value_object,"D":__embind_register_bool,"x":__embind_register_class,"w":__embind_register_class_constructor,"d":__embind_register_class_function,"C":__embind_register_emval,"o":__embind_register_enum,"b":__embind_register_enum_value,"u":__embind_register_float,"j":__embind_register_function,"h":__embind_register_integer,"g":__embind_register_memory_view,"v":__embind_register_std_string,"r":__embind_register_std_wstring,"t":__embind_register_value_object,"c":__embind_register_value_object_field,"E":__embind_register_void,"l":__emval_as,"p":__emval_call_void_method,"a":__emval_decref,"y":__emval_get_global,"q":__emval_get_method_caller,"n":__emval_get_module_property,"e":__emval_get_property,"i":__emval_incref,"m":__emval_new,"f":__emval_new_cstring,"k":__emval_run_destructors,"B":_abort,"z":_emscripten_memcpy_big,"A":_emscripten_resize_heap};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["J"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["K"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["L"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["M"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["N"]).apply(null,arguments)};var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run(); +var Module=typeof BASIS!=="undefined"?BASIS:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i=55296&&codeUnit<=57343)++i;len+=4}return len}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="basis_transcoder.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return Promise.resolve().then(getBinary)}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["K"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["L"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var structRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+' "use strict";'+" return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationGroup=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function attachFinalizer(handle){if("undefined"===typeof FinalizationGroup){attachFinalizer=function(handle){return handle};return handle}finalizationGroup=new FinalizationGroup(function(iter){for(var result=iter.next();!result.done;result=iter.next()){var $$=result.value;if(!$$.ptr){console.warn("object already deleted: "+$$.ptr)}else{releaseClassHandle($$)}}});attachFinalizer=function(handle){finalizationGroup.register(handle,handle.$$,handle.$$);return handle};detachFinalizer=function(handle){finalizationGroup.unregister(handle.$$)};return attachFinalizer(handle)}function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}}function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}}function ClassHandle_isDeleted(){return!this.$$.ptr}var delayFunction=undefined;var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}var registeredPointers={};function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError("Expected null or instance of "+desiredClass.name+", got an instance of "+ptrClass.name)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr}function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,__emval_register(function(){clonedHandle["delete"]()}));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+handle.$$.ptrType.name+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)}function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function makeClassHandle(prototype,record){if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record}}))}function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["fromWireType"]=RegisteredPointer_fromWireType}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function dynCallLegacy(sig,ptr,args){if(args&&args.length){return Module["dynCall_"+sig].apply(null,[ptr].concat(args))}return Module["dynCall_"+sig].call(null,ptr)}function dynCall(sig,ptr,args){if(sig.indexOf("j")!=-1){return dynCallLegacy(sig,ptr,args)}return wasmTable.get(ptr).apply(null,args)}function getDynCaller(sig,ptr){assert(sig.indexOf("j")>=0,"getDynCaller should only be called with i64 sigs");var argCache=[];return function(){argCache.length=arguments.length;for(var i=0;i>2)+i])}return array}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);var args=[rawConstructor];var destructors=[];whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName="constructor "+classType.name;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError("Cannot register multiple constructors with identical number of parameters ("+(argCount-1)+") for class '"+classType.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!")}classType.registeredClass.constructor_body[argCount-1]=function unboundTypeHandler(){throwUnboundTypeError("Cannot construct "+classType.name+" due to unbound types",rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){classType.registeredClass.constructor_body[argCount-1]=function constructor_body(){if(arguments.length!==argCount-1){throwBindingError(humanName+" called with "+arguments.length+" arguments, expected "+(argCount-1))}destructors.length=0;args.length=argCount;for(var i=1;i0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __embind_register_enum_value(rawEnumType,name,enumValue){var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(enumType.name+"_"+name,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(valuemaxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_object(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){structRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}}function __embind_register_value_object_field(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){structRegistrations[structType].fields.push({fieldName:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}var emval_methodCallers=[];function __emval_call_void_method(caller,handle,methodName,args){caller=emval_methodCallers[caller];handle=requireHandle(handle);methodName=getStringOrSymbol(methodName);caller(handle,methodName,null,args)}function emval_get_global(){if(typeof globalThis==="object"){return globalThis}return function(){return Function}()("return this")()}function __emval_get_global(name){if(name===0){return __emval_register(emval_get_global())}else{name=getStringOrSymbol(name);return __emval_register(emval_get_global()[name])}}function __emval_addMethodCaller(caller){var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id}function __emval_lookupTypes(argCount,argTypes){var a=new Array(argCount);for(var i=0;i>2)+i],"parameter "+i)}return a}function __emval_get_method_caller(argCount,argTypes){var types=__emval_lookupTypes(argCount,argTypes);var retType=types[0];var signatureName=retType.name+"_$"+types.slice(1).map(function(t){return t.name}).join("_")+"$";var params=["retType"];var args=[retType];var argsList="";for(var i=0;i4){emval_handle_array[handle].refcount+=1}}function craftEmvalAllocator(argCount){var argsList="";for(var i=0;i>> 2) + "+i+'], "parameter '+i+'");\n'+"var arg"+i+" = argType"+i+".readValueFromPointer(args);\n"+"args += argType"+i+"['argPackAdvance'];\n"}functionBody+="var obj = new constructor("+argsList+");\n"+"return __emval_register(obj);\n"+"}\n";return new Function("requireRegisteredType","Module","__emval_register",functionBody)(requireRegisteredType,Module,__emval_register)}var emval_newers={};function __emval_new(handle,argCount,argTypes,args){handle=requireHandle(handle);var newer=emval_newers[argCount];if(!newer){newer=craftEmvalAllocator(argCount);emval_newers[argCount]=newer}return newer(handle,argTypes,args)}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function _abort(){abort()}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var SYSCALLS={mappings:{},buffers:[null,[],[]],printChar:function(stream,curr){var buffer=SYSCALLS.buffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},get64:function(low,high){return low}};function _fd_close(fd){return 0}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){}function _fd_write(fd,iov,iovcnt,pnum){var num=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j>2]=num;return 0}function _setTempRet0($i){setTempRet0($i|0)}InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_RegisteredPointer();init_embind();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();__ATINIT__.push({func:function(){___wasm_call_ctors()}});var asmLibraryArg={"t":__embind_finalize_value_object,"I":__embind_register_bool,"x":__embind_register_class,"w":__embind_register_class_constructor,"d":__embind_register_class_function,"k":__embind_register_constant,"H":__embind_register_emval,"n":__embind_register_enum,"a":__embind_register_enum_value,"A":__embind_register_float,"i":__embind_register_function,"j":__embind_register_integer,"h":__embind_register_memory_view,"B":__embind_register_std_string,"v":__embind_register_std_wstring,"u":__embind_register_value_object,"c":__embind_register_value_object_field,"J":__embind_register_void,"m":__emval_as,"s":__emval_call_void_method,"b":__emval_decref,"y":__emval_get_global,"p":__emval_get_method_caller,"r":__emval_get_module_property,"e":__emval_get_property,"g":__emval_incref,"q":__emval_new,"f":__emval_new_cstring,"l":__emval_run_destructors,"o":_abort,"E":_emscripten_memcpy_big,"F":_emscripten_resize_heap,"G":_fd_close,"C":_fd_seek,"z":_fd_write,"D":_setTempRet0};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["M"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["N"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["O"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["P"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["Q"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["R"]).apply(null,arguments)};var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run(); return BASIS.ready diff --git a/webgl/transcoder/build/basis_transcoder.wasm b/webgl/transcoder/build/basis_transcoder.wasm index bfb0162..4b9c343 100644 Binary files a/webgl/transcoder/build/basis_transcoder.wasm and b/webgl/transcoder/build/basis_transcoder.wasm differ diff --git a/webgl_videotest/basis.wasm b/webgl_videotest/basis.wasm index d16a23a..91a3f21 100644 Binary files a/webgl_videotest/basis.wasm and b/webgl_videotest/basis.wasm differ diff --git a/webgl_videotest/build.bat b/webgl_videotest/build.bat index fb828e6..b41b1db 100644 --- a/webgl_videotest/build.bat +++ b/webgl_videotest/build.bat @@ -1,4 +1,4 @@ @REM -O0 -s ASSERTIONS=1 -s DEMANGLE_SUPPORT=1 @REM -O2 -s ASSERTIONS=0 -emcc -s EXPORTED_FUNCTIONS="['allocate', '_malloc', '_free', '_basis_init','_basis_open','_basis_close','_basis_get_has_alpha','_basis_get_num_images','_basis_get_num_levels','_basis_get_image_width','_basis_get_image_height','_basis_get_image_transcoded_size_in_bytes','_basis_transcode_image','_basis_start_transcoding','_basis_stop_transcoding','_basis_get_debug_flags','_basis_set_debug_flags']" -o basis.js -DBASISD_ENABLE_DEBUG_FLAGS=1 ../transcoder/basisu_transcoder.cpp basis_wrappers.cpp -s TOTAL_MEMORY=749993984 -std=c++11 -O2 -s ASSERTIONS=0 -I ../transcoder -rem emcc -s EXPORTED_FUNCTIONS="['allocate', '_malloc', '_free', '_basis_init','_basis_open','_basis_close','_basis_get_has_alpha','_basis_get_num_images','_basis_get_num_levels','_basis_get_image_width','_basis_get_image_height','_basis_get_image_transcoded_size_in_bytes','_basis_transcode_image','_basis_start_transcoding','_basis_stop_transcoding','_basis_get_debug_flags','_basis_set_debug_flags']" -o basis.js -DBASISD_ENABLE_DEBUG_FLAGS=1 ../transcoder/basisu_transcoder.cpp basis_wrappers.cpp -s TOTAL_MEMORY=499974144 -std=c++11 -O0 -s ASSERTIONS=0 -I ../transcoder +emcc -s EXPORTED_FUNCTIONS="['allocate', '_malloc', '_free', '_basis_init','_basis_open','_basis_close','_basis_get_has_alpha','_basis_get_num_images','_basis_get_num_levels','_basis_get_image_width','_basis_get_image_height','_basis_get_image_transcoded_size_in_bytes','_basis_transcode_image','_basis_start_transcoding','_basis_stop_transcoding','_basis_get_debug_flags','_basis_set_debug_flags']" -o basis.js -DBASISD_ENABLE_DEBUG_FLAGS=1 ../transcoder/basisu_transcoder.cpp basis_wrappers.cpp -s TOTAL_MEMORY=749993984 -std=c++11 -O2 -s ASSERTIONS=0 -I ../transcoder -D BASISD_SUPPORT_KTX2=0 +rem emcc -s EXPORTED_FUNCTIONS="['allocate', '_malloc', '_free', '_basis_init','_basis_open','_basis_close','_basis_get_has_alpha','_basis_get_num_images','_basis_get_num_levels','_basis_get_image_width','_basis_get_image_height','_basis_get_image_transcoded_size_in_bytes','_basis_transcode_image','_basis_start_transcoding','_basis_stop_transcoding','_basis_get_debug_flags','_basis_set_debug_flags']" -o basis.js -DBASISD_ENABLE_DEBUG_FLAGS=1 ../transcoder/basisu_transcoder.cpp basis_wrappers.cpp -s TOTAL_MEMORY=499974144 -std=c++11 -O0 -s ASSERTIONS=0 -I ../transcoder -D BASISD_SUPPORT_KTX2=0 diff --git a/webgl_videotest/build.sh b/webgl_videotest/build.sh index 2e78137..c614d6c 100644 --- a/webgl_videotest/build.sh +++ b/webgl_videotest/build.sh @@ -1,3 +1,3 @@ # rg - I haven't tested this shell script yet (I use build.bat on Windows) -emcc -s EXPORTED_FUNCTIONS="['allocate', '_malloc', '_free', '_basis_init','_basis_open','_basis_close','_basis_get_has_alpha','_basis_get_num_images','_basis_get_num_levels','_basis_get_image_width','_basis_get_image_height','_basis_get_image_transcoded_size_in_bytes','_basis_transcode_image','_basis_start_transcoding']" -s TOTAL_MEMORY=749993984 -O2 -s ASSERTIONS=0 -I ../transcoder -o basis.js ../transcoder/basisu_transcoder.cpp basis_wrappers.cpp +emcc -s EXPORTED_FUNCTIONS="['allocate', '_malloc', '_free', '_basis_init','_basis_open','_basis_close','_basis_get_has_alpha','_basis_get_num_images','_basis_get_num_levels','_basis_get_image_width','_basis_get_image_height','_basis_get_image_transcoded_size_in_bytes','_basis_transcode_image','_basis_start_transcoding']" -s TOTAL_MEMORY=749993984 -O2 -s ASSERTIONS=0 -I ../transcoder -o basis.js ../transcoder/basisu_transcoder.cpp basis_wrappers.cpp -D BASISD_SUPPORT_KTX2=0 # chmod -R a+rX . diff --git a/webgl_videotest/index.html b/webgl_videotest/index.html index 205ca83..a302a4a 100644 --- a/webgl_videotest/index.html +++ b/webgl_videotest/index.html @@ -508,7 +508,7 @@ function rewind()

.basis file: - +