Commit Graph
1436 Commits
Author SHA1 Message Date
Richard GeldreichandClaude Opus 5.5 82fc8ca5de Wuffs LDR loader: call each format's decoder functions directly, not via the wuffs_base__image_decoder interface
Wuffs' base-interface dispatch calls the format functions through function
pointers of a different type (void* self vs. the decoder's own type), which
is undefined behavior: clang -fsanitize=function (part of
-fsanitize=undefined, so BASISU_SAN=ON with clang) aborts on every load,
and CFI builds would trap. No behavior change; Wuffs is unmodified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 18:00:08 -04:00
Richard GeldreichandClaude Opus 5.5 375cf38829 Wuffs LDR loader: exact work-buffer cap limits in the comment; note that BASISU_USE_WUFFS_LDR_LOADER must match everywhere
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:27:39 -04:00
Richard GeldreichandClaude Opus 5.5 5e20216f01 README: .QOI is now read by Wuffs; qoi.h only writes .QOI
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:23:11 -04:00
Richard GeldreichandClaude Opus 5.5 a02a54e0d3 basisu -info (PNG): with the Wuffs loader, parse the header with Wuffs only (no pv_png)
Owner directive. With BASISU_USE_WUFFS_LDR_LOADER=1, print_png_info() uses only
get_ldr_image_info(): it prints the true dimensions (even beyond the decode cap,
plus a "can't be loaded" line when they exceed BASISU_WUFFS_LDR_MAX_DIM) or
"Failed parsing PNG file" if Wuffs rejects the header. pv_png::get_png_info and
print_png_loader_info() are no longer used for -info in that configuration;
channels/bit depth/color type/tRNS/gAMA lines are not printed (the adapter stays
thin). With =0 the function body is public master's, unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:20:53 -04:00
Richard GeldreichandClaude Opus 5.5 f4e2f6daad Wuffs LDR loader: cap the decoder work buffer at 1 GiB (was 3 GiB)
Bounds the memory a small corrupt/truncated file claiming a huge image can make the loader allocate
before Wuffs fails. Images within the 16384 dimension cap that need a larger work buffer (the largest
progressive JPEGs, ~>9.4K^2 CMYK 4:4:4 / ~>10.9K^2 YCbCr 4:4:4, and 8-bit RGBA PNGs right at 16384^2)
are now rejected with the work-buffer error. Still overridable with -DBASISU_WUFFS_LDR_MAX_WORKBUF_BYTES.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:57:04 -04:00
Richard GeldreichandClaude Opus 5.5 2c13154fa3 basisu -info (PNG): also report the image loader's (Wuffs) view of the header
With BASISU_USE_WUFFS_LDR_LOADER=1, -info on a PNG keeps the pvpngreader
header details and adds one line from get_ldr_image_info(): the true
dimensions, whether they exceed the loader's decode cap, or that Wuffs
rejects the header (so -info no longer reports a PNG as fine that the
loader then refuses). When pvpngreader's own header parser fails (its 32768
max dimension, ancillary chunk CRCs) the loader's view is still printed, so
e.g. a 65535x65535 PNG header reports its true dimensions ("exceeds the
maximum loadable dimension") instead of only "Failed parsing PNG file".
=0 is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:57:03 -04:00
Richard GeldreichandClaude Opus 5.5 f76df2518b LDR image loader: decode .PNG/.TGA/.JPG/.QOI with Wuffs (BASISU_USE_WUFFS_LDR_LOADER, default 1)
load_png(mem), load_tga(file), load_jpg(file/mem) and load_qoi(mem/file) now
go through a thin Wuffs C-API adapter (wuffs_load_ldr) when
BASISU_USE_WUFFS_LDR_LOADER is 1 (the default, basisu_enc.h). Everything
built on them (load_image, load_image_hdr's LDR paths, basis_compressor,
the basisu tool, KTX2 Studio's WASM encoder) switches automatically. The
old pvpngreader / read_tga() / jpgd / qoi.h decode bodies stay in #else
branches (=0 gives today's code; qoi.h is still used to write .QOI).

The adapter only converts to basisu's RGBA image, enforces the caps and
reports errors. Wuffs' accept/reject and pixels are final: no fallback,
no fixups. decode_image_config reads the header, then a 0 width/height
("empty image"), width/height > BASISU_WUFFS_LDR_MAX_DIM (default
basist::BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION = 16384) or a work buffer >
BASISU_WUFFS_LDR_MAX_WORKBUF_BYTES (default 3 GiB here, the measured worst
case at 16384^2; tradeoff documented in basisu_enc.h) is refused BEFORE any
pixel or work buffer is allocated. All memory is basisu's own
(image::try_alloc - added here - and try_resize, initialized); img is only
replaced on success; nothing asserts on file data.

get_ldr_image_info() is a header-only query (no cap) that returns the
true dimensions, even of files beyond the decode cap.

Wuffs is declared here (wuffs-v0.4.c included without
WUFFS_IMPLEMENTATION, same config header) and compiled in
encoder/3rdparty/basisu_wuffs.cpp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:56:38 -04:00
Richard GeldreichandClaude Opus 5.5 9459482681 Build: compile Wuffs in its own translation unit (encoder/3rdparty/basisu_wuffs.cpp)
basisu_wuffs.cpp defines WUFFS_IMPLEMENTATION and #includes the unmodified
encoder/3rdparty/wuffs-v0.4.c with all warnings silenced in that TU only
(MSVC #pragma warning(push, 0)/pop; gcc/clang diagnostic push/ignored/pop).
basisu_wuffs_config.h holds the Wuffs configuration (modules BASE core/
interfaces/pixconv, ADLER32, CRC32, DEFLATE, ZLIB, PNG, JPEG, TARGA, QOI;
RGBA_NONPREMUL destination only; WUFFS_CONFIG__AVOID_CPU_ARCH = no SIMD), shared
with basisu_enc.cpp's declarations-only include (next commit). With
BASISU_USE_WUFFS_LDR_LOADER=0 the TU compiles to nothing.

Added to every build that compiles the encoder: root CMakeLists.txt
(native, -m32, WASI, Python module) with a BASISU_USE_WUFFS_LDR_LOADER option
(default ON), webgl/encoder/CMakeLists.txt (all Emscripten variants, same
option), and encoder_lib.vcxproj/.filters (basisu.sln: basisu, examples).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:55:17 -04:00
Richard GeldreichandClaude Opus 5.5 1ce7ce9df4 Vendor Wuffs v0.4 (release/c/wuffs-v0.4.c, unmodified) + license notices
encoder/3rdparty/wuffs-v0.4.c is the verbatim release/c/wuffs-v0.4.c from
github.com/google/wuffs at commit 4be865da258c (WUFFS_VERSION_STRING
"0.4.0-alpha.12+4048.20261020"), sha256
458066457347bd6d9bc3c906aa8292bea4488577727569ddd9436fdd89d5f04d.
Wuffs is dual-licensed Apache-2.0 OR MIT; basis_universal elects
Apache-2.0 (same as the rest of the repo, LICENSES/Apache-2.0.txt):
added to .reuse/dep5 and the README's 3rd-party sentence. Wuffs ships no
NOTICE file. Not compiled yet (the next commits add the build files and
the loader adapter).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:54:12 -04:00
Richard GeldreichandClaude Opus 5.5 918d658f95 webgl encoder: accept mainScriptUrlOrBlob in threaded builds (Emscripten 6.0.x)
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>
2026-10-23 14:51:35 -04:00
Rich Geldreich 254c5b8ed1 Merge pull request #474 from ben-polinsky/csp-no-dynamic-execution
Add opt-in WebGL builds that work under a strict CSP (no `unsafe-eval`)
2026-10-23 14:48:47 -04:00
Ben Polinsky b15211474d Add opt-in WebGL builds that do not use eval()/new Function()
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
2026-10-19 09:51:05 -04:00
Ben Polinsky 25e094e81d Fix missing cPackUASTC* constants in the WebGL encoder
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.
2026-10-19 09:51:05 -04:00
Richard Geldreich 1706f948a2 Adding HDR source parm nullptr check
Fixing typo that could lead to a null pointer deref
2026-10-16 03:31:44 -04:00
Rich Geldreich 99f52d63aa Update README.md 2026-09-01 00:08:12 -04:00
Rich Geldreich c9d3f9a643 Update README.md 2026-09-01 00:05:16 -04:00
Rich Geldreich a9acd00363 Remove outdated codec specification links from README
Removed links to XUASTC and XUBC7 LDR specifications.
2026-09-01 00:04:06 -04:00
Rich Geldreich 31fddf4894 Update README.md 2026-09-01 00:03:41 -04:00
Rich Geldreich 480511eb98 Update README with HDR formats transcoding details
Added information about HDR formats transcoding to BC6H.
2026-09-01 00:02:05 -04:00
Rich Geldreich 41a69c394c Update README.md 2026-09-01 00:01:10 -04:00
Rich Geldreich 6f5256c287 Fix formatting issues in README.md 2026-08-31 23:58:54 -04:00
Rich Geldreich 8c93c71860 Update README.md 2026-08-31 23:58:40 -04:00
Rich Geldreich 64f686dd37 Update README.md 2026-08-31 23:56:17 -04:00
Rich Geldreich 3fe4dfa3bc Clarify animated texture reference in README
Updated text for clarity and consistency in the README.
2026-08-31 23:55:47 -04:00
Rich Geldreich 10814a0efa Update README.md 2026-08-31 23:55:11 -04:00
Rich Geldreich 75c4d13abe Update README.md 2026-08-31 23:51:09 -04:00
Rich Geldreich c7f90c991f Update UASTC HDR 6x6 Intermediate link in README 2026-08-31 23:50:51 -04:00
Rich Geldreich 1e9b43689d Update README.md 2026-08-31 23:50:17 -04:00
Rich Geldreich f227f5f7e9 Update UASTC LDR 4x4 description with link 2026-08-31 23:49:58 -04:00
Rich Geldreich 54034c2e64 Update ETC1S description with hyperlink 2026-08-31 23:49:25 -04:00
Rich Geldreich 6136d38196 Update README.md 2026-08-31 23:48:59 -04:00
Rich Geldreich 930325fca3 Revise XUASTC LDR 4x4-12x12 description in README
Updated the description of the XUASTC LDR 4x4-12x12 codec for clarity and conciseness.
2026-08-31 23:48:42 -04:00
Rich Geldreich ef799ff1ca Update README.md 2026-08-31 23:47:57 -04:00
Rich Geldreich 69441680ef Update README.md 2026-08-31 23:47:17 -04:00
Richard Geldreich 4ac36eab9b setting g_xbc7_dc_quant_per_weight_bits to const (very minor fix) 2026-08-31 03:48:15 -04:00
Rich Geldreich f9ebbd80ae Update README to remove v2.50 documentation note
Removed note about ongoing documentation for v2.50.
2026-08-27 23:58:13 -04:00
Rich Geldreich 9bebe16726 Merge pull request #458 from vkedwardli/fix/netbsd
Fix float overload deduction on NetBSD
v2_50
2026-07-22 14:36:49 -04:00
Rich Geldreich 5f6a0a0ca6 Merge pull request #463 from BinomialLLC/fix-ktx2-level-uncomp-msg
KTX2: fix wrong error message for oversized level uncompressed length
2026-07-18 22:28:39 -04:00
Richard GeldreichandClaude Opus 4.8 28ab277db8 KTX2: fix wrong error message for oversized level uncompressed length
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>
2026-07-18 22:24:57 -04:00
Rich Geldreich d77f9308e2 Merge pull request #462 from BinomialLLC/webgl-build-clean-slow
webgl/build.sh: run make clean on --clean, add --slow serial option
2026-07-18 19:45:32 -04:00
Richard GeldreichandClaude Opus 4.8 018cc67cd0 webgl: add --slow option to build.sh for a serial build
--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>
2026-07-18 19:37:52 -04:00
Richard GeldreichandClaude Opus 4.8 b512234dff webgl: build.sh --clean runs 'make clean' before wiping the CMake cache
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>
2026-07-18 19:33:48 -04:00
Rich Geldreich edfa1fc3a4 Merge pull request #461 from BinomialLLC/ktx2-init-range-checks
webgl: add build.sh to build the transcoder/encoder
2026-07-18 19:15:31 -04:00
Richard GeldreichandClaude Opus 4.8 fcab97d371 webgl: add build.sh to build the transcoder/encoder with Emscripten
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>
2026-07-18 19:05:54 -04:00
Rich Geldreich 368e2a5122 Merge pull request #460 from BinomialLLC/ktx2-init-range-checks
previewers: free rgbBuf with free() instead of delete
2026-07-18 18:55:48 -04:00
Richard GeldreichandClaude Opus 4.8 3b0017e677 previewers: free rgbBuf with free() instead of delete
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>
2026-07-18 18:38:36 -04:00
Rich Geldreich 399927dc36 Merge pull request #459 from BinomialLLC/ktx2-init-range-checks
Ktx2 init range checks
2026-07-18 18:18:14 -04:00
Richard Geldreich 38168767f4 rebuilt artifacts 2026-07-18 18:11:39 -04:00
Richard GeldreichandClaude Opus 4.8 57a0a0b4c6 previewers: regenerate single-file transcoder with KTX2/Zstd enabled
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>
2026-07-18 17:59:57 -04:00
Richard GeldreichandClaude Opus 4.8 f2497f14d6 KTX2 transcoder: build with BASISD_SUPPORT_KTX2 disabled
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>
2026-07-18 17:20:38 -04:00