Newer Emscripten (6.0.x) no longer includes mainScriptUrlOrBlob in its default INCOMING_MODULE_JS_API,
so the threaded encoder loaded via importScripts() in ktx2_studio/encode-worker.js ignored it and the
pthread pool never came up (the worker hangs while loading). Add it explicitly (Emscripten's default list
plus mainScriptUrlOrBlob), as in the development tree.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the default DYNAMIC_EXECUTION=1, embind generates its invokers at
runtime with new Function(). A Content-Security-Policy without
'unsafe-eval' blocks this, so the transcoder and encoder fail to start
with an EvalError.
Add two CMake options to webgl/transcoder and webgl/encoder:
- NO_DYNAMIC_EXECUTION (default OFF) adds -s DYNAMIC_EXECUTION=0.
- EMBIND_AOT (default ON, only used with NO_DYNAMIC_EXECUTION) adds
-s EMBIND_AOT=1. This generates the invokers at build time, so
embind calls stay as fast as with the default build.
The default build does not change. The .wasm output is byte-identical
in all configurations; only the JS glue changes.
Fixes#464
The cPackUASTC* values are members of an anonymous enum. embind's
constant() registers a constant against the type of its value, and no
binding exists for an anonymous enum type. embind therefore waits for
that type forever, and the constants never appear on the module.
Module.cPackUASTCLevelDefault and the others are undefined in the
shipped basis_encoder.js.
Cast the values to uint32_t so that they bind as plain numbers.
EMBIND_AOT=1 makes this a link error ("Missing binding for type"), so
this change is also necessary to build the encoder with that flag.
The sanity check on a level's m_uncompressed_byte_length (>= 2 GiB)
logged "Invalid level offset (too large)", which names the wrong field.
Correct it to "Invalid level uncompressed byte length (too large)".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
--slow uses `make -j1` (one file at a time, the classic manual `make`)
instead of the default `make -j$(nproc)`. Useful for readable output or
constrained machines; parallel remains the default.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
For a more thorough clean, --clean now runs `make clean` (removing the
build outputs and object files via the existing build system) before
deleting CMakeCache.txt/CMakeFiles and reconfiguring.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Convenience script (place: webgl/build.sh) that runs emcmake/cmake/make
in webgl/transcoder/build and webgl/encoder/build. Stops on the first
error (set -euo pipefail), builds both by default or a named target
(transcoder/encoder), supports --clean and -D... cmake pass-through, is
runnable from any directory, and auto-sources emsdk if emcmake is not on
PATH.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BasisThumbProvider::GetThumbnail() allocates rgbBuf with malloc() but
freed it with delete, which is a mismatched-allocator (and delete on a
void*) undefined behavior. Use free() to match the allocation, as is
already done for the sibling 'data' buffer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Regenerate contrib/previewers/lib/basisu_transcoder.cpp from the current
transcoder sources with the single_file_transcoder combine.py script,
replacing a long-stale copy. KTX2 and Zstd are enabled.
Only the Basis Universal sources are amalgamated; Zstd is kept as an
ordinary #include "../zstd/zstd.h" (via combine.py's -k option) rather
than being inlined, and zstd/zstd.c is compiled and linked separately,
so Zstd is neither touched nor duplicated.
Update the previewers project to build the current (C++17) transcoder:
- set LanguageStandard to stdcpp17 (the transcoder uses inline variables
and other C++17 features);
- define NOMINMAX (the Windows shell headers, included before the
transcoder, otherwise define min/max macros that break the
std::numeric_limits<>::max() calls);
- add the zstd directory to the include paths and compile
..\..\..\zstd\zstd.c as a separate compilation unit.
The DLL builds for Debug/Release, x64 and Win32. The previewer still
only registers .basis files; enabling KTX2/Zstd just makes the
transcoder compile cleanly out of the box.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two things prevented the transcoder from compiling with
BASISD_SUPPORT_KTX2 set to 0:
- ktx2_image_level_info was defined inside the #if BASISD_SUPPORT_KTX2
block, but dds_transcoder::get_image_level_info() takes it by
reference and is compiled unconditionally. Move the struct ahead of
that block so it is always defined; it has no KTX2-specific members.
- The "#include ../zstd/zstd.h" was nested inside #if BASISD_SUPPORT_KTX2,
but the Zstd decompressor (zstd_decompress()) is compiled whenever
BASISD_SUPPORT_KTX2_ZSTD is set, independent of KTX2 (Zstd is also used
by other codecs). With KTX2 off but Zstd on, the ZSTD_* symbols were
referenced without the header being included. Gate the include on
BASISD_SUPPORT_KTX2_ZSTD alone, matching where the symbols are used.
BASISD_SUPPORT_KTX2_ZSTD still defaults to 1 (unchanged). The default
build (KTX2 and Zstd both enabled) is unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The KTX2 level and supercompression-global-data (SGD) offset/length
fields are full 64-bit values read from the file. init() validated them
with "offset + length > m_data_size", where the addition can wrap around
and incorrectly pass for out-of-range values. Rewrite both checks as
"offset > m_data_size || length > (uint64_t)m_data_size - offset", which
cannot overflow (offset is confirmed <= m_data_size before the
subtraction, so it can't underflow either). The result is identical to
the original for every in-range input.
Also in init():
- Reject a level whose byte_length is 0. This case already logged an
error but fell through without returning false.
- Add KTX2_MAX_SUPPORTED_LAYER_COUNT (65535) and reject files whose
layer_count exceeds it, mirroring KTX2_MAX_SUPPORTED_LEVEL_COUNT, so
layer_count * face_count * level_count stays bounded.
read_slice_offset_len_global_data(): compute image_count in 64-bit,
verify m_sgd_byte_length == image_count * descriptor_size before sizing
the descriptor array, and use try_resize() (returns false on failure)
instead of resize() (which aborts the process) so an out-of-range count
fails cleanly instead of attempting an oversized allocation. Also
compute image_count in 64-bit in decompress_etc1s_global_data().
Valid files are handled exactly as before.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>