feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -29,7 +29,10 @@ Files extracted from upstream source:
- `shaders` folder from `src/ffx-fsr2-api` with `ffx_*.hlsl` files excluded
- `LICENSE.txt`
Apply `patches` to add the new options required by Godot and general compilation fixes.
Patches:
- `0001-build-fixes.patch` (GH-81197)
- `0002-godot-fsr2-options.patch` (GH-81197)
## angle
@ -59,16 +62,21 @@ Files extracted from upstream source:
## basis_universal
- Upstream: https://github.com/BinomialLLC/basis_universal
- Version: 1.16.4 (900e40fb5d2502927360fe2f31762bdbb624455f, 2023)
- Version: 1.50.0 (051ad6d8a64bb95a79e8601c317055fd1782ad3e, 2024)
- License: Apache 2.0
Files extracted from upstream source:
- `encoder/` and `transcoder/` folders, minus `jpgd.{cpp,h}`
- `encoder/` and `transcoder/` folders, with the following files removed from `encoder`:
`jpgd.{cpp,h}`, `3rdparty/{qoi.h,tinydds.h,tinyexr.cpp,tinyexr.h}`
- `LICENSE`
Applied upstream PR https://github.com/BinomialLLC/basis_universal/pull/344 to
fix build with our own copy of zstd (patch in `patches`).
Patches:
- `0001-external-zstd-pr344.patch` (GH-73441)
- `0002-external-jpgd.patch` (GH-88508)
- `0003-external-tinyexr.patch` (GH-97582)
- `0004-remove-tinydds-qoi.patch` (GH-97582)
## brotli
@ -87,14 +95,19 @@ Files extracted from upstream source:
## certs
- Upstream: Mozilla, via https://github.com/bagder/ca-bundle
- Version: git (c5a419971b1bec220368c619aaafd0b818aa119f, 2024)
- Version: git (4d3fe6683f651d96be1bbef316b201e9b33b274d, 2024),
generated from mozilla-release changeset b8ea2342548b8571e58f9176d9555ccdb5ec199f
- License: MPL 2.0
Files extracted from upstream source:
- `ca-bundle.crt` renamed to `ca-certificates.crt`
## clipper2
- Upstream: https://github.com/AngusJohnson/Clipper2
- Version: 1.3.0 (98db5662e8dd1808a5a7b50c5605a2289bb390e8, 2023)
- Version: 1.4.0 (736ddb0b53d97fd5f65dd3d9bbf8a0993eaf387c, 2024)
- License: BSL 1.0
Files extracted from upstream source:
@ -102,8 +115,10 @@ Files extracted from upstream source:
- `CPP/Clipper2Lib/` folder (in root)
- `LICENSE`
Apply the patches in the `patches/` folder when syncing on newer upstream
commits.
Patches:
- `0001-disable-exceptions.patch` (GH-80796)
- `0002-llvm-disable-int1280-math.patch` (GH-95964)
## cvtt
@ -117,10 +132,9 @@ Files extracted from upstream source:
- All `.cpp` and `.h` files except the folders `MakeTables` and `etc2packer`
- `LICENSE.txt`
Changes related to BC6H packing and unpacking made upstream in
https://github.com/elasota/cvtt/commit/2e4b6b2747aec11f4cc6dd09ef43fa8ce769f6e2
have been removed as they caused massive quality regressions. Apply the patches
in the `patches/` folder when syncing on newer upstream commits.
Patches:
- `0001-revert-bc6h-reorg.patch` (GH-73715)
## d3d12ma
@ -135,9 +149,9 @@ Files extracted from upstream source:
- `include/D3D12MemAlloc.h`
- `LICENSE.txt`, `NOTICES.txt`
Important: Some files have Godot-made changes for use with MinGW.
They are marked with `/* GODOT start */` and `/* GODOT end */`
comments.
Patches:
- `0001-mingw-support.patch` (GH-83452)
## directx_headers
@ -152,9 +166,10 @@ Files extracted from upstream source:
- `include/dxguids/*.h`
- `LICENSE`
Important: Some files have Godot-made changes for use with MinGW.
They are marked with `/* GODOT start */` and `/* GODOT end */`
comments.
Patches:
- `0001-mingw-pragma.patch` (GH-83452)
- `0002-win7-8-dynamic-load.patch` (GH-88496)
## doctest
@ -182,13 +197,17 @@ Files extracted from upstream:
- All config files listed in `modules/raycast/godot_update_embree.py`
- `LICENSE.txt`
The `modules/raycast/godot_update_embree.py` script can be used to pull the
relevant files from the latest Embree release and apply some automatic changes.
Patches:
Some changes have been made in order to remove exceptions and fix minor build errors.
They are marked with `// -- GODOT start --` and `// -- GODOT end --`
comments. Apply the patches in the `patches/` folder when syncing on newer upstream
commits.
- `0001-disable-exceptions.patch` (GH-48050)
- `0002-godot-config.patch` (GH-88783)
- `0003-emscripten-nthreads.patch` (GH-69799)
- `0004-mingw-no-cpuidex.patch` (GH-92488)
- `0005-mingw-llvm-arm64.patch` (GH-93364)
- `0006-include-order-dllexport.patch` (GH-94256)
The `modules/raycast/godot_update_embree.py` script can be used to pull the
relevant files from the latest Embree release and apply patches automatically.
## enet
@ -202,23 +221,21 @@ Files extracted from upstream source:
- All `.c` files in the main directory (except `unix.c` and `win32.c`)
- The `include/enet/` folder as `enet/` (except `unix.h` and `win32.h`)
- `LICENSE` file
- Added 3 files `enet_godot.cpp`, `enet/enet_godot.h`, and `enet/enet_godot_ext.h`,
providing ENet socket implementation using Godot classes, allowing IPv6 and DTLS.
Important: `enet.h`, `host.c`, `protocol.c` have been slightly modified
to be usable by Godot's socket implementation and allow IPv6 and DTLS.
Apply the patches in the `patches/` folder when syncing on newer upstream
commits.
Patches:
Three files (`godot.cpp`, `enet/godot.h`, `enet/godot_ext.h`) have been added to
provide ENet socket implementation using Godot classes.
- `0001-godot-socket.patch` (GH-7985)
It is still possible to build against a system wide ENet but doing so will limit
its functionality to IPv4 only.
Important: Building against a system wide ENet is possible, but will limit its
functionality to IPv4 only and no DTLS. We recommend against it.
## etcpak
- Upstream: https://github.com/wolfpld/etcpak
- Version: git (5380688660a3801aec4b25483366027fe0442d7b, 2024)
- Version: 2.0 (a43d6925bee49277945cf3e311e4a022ae0c2073, 2024)
- License: BSD-3-Clause
Files extracted from upstream source:
@ -228,8 +245,14 @@ Files extracted from upstream source:
Dither.{cpp,hpp} ForceInline.hpp Math.hpp ProcessCommon.hpp ProcessRGB.{cpp,hpp}
ProcessDxtc.{cpp,hpp} Tables.{cpp,hpp} Vector.hpp
```
- The files `DecodeRGB.{cpp.hpp}` are based on the code from the original repository.
- `AUTHORS.txt` and `LICENSE.txt`
Patches:
- `0001-remove-bc7enc.patch` (GH-101362)
## fonts
- `DroidSans*.woff2`:
@ -240,10 +263,6 @@ Files extracted from upstream source:
* Upstream: https://github.com/JetBrains/JetBrainsMono
* Version: 2.304 (cd5227bd1f61dff3bbd6c814ceaf7ffd95e947d9, 2023)
* License: OFL-1.1
- `NotoNaskhArabicUI*.woff2`:
* Upstream: https://github.com/notofonts/arabic
* Version: 2.014 (133ccaebf922ca080a7eef22998611ac3c242df9, 2022)
* License: OFL-1.1
- `NotoSans*.woff2`:
* Upstream: https://github.com/notofonts/latin-greek-cyrillic
* Version: 2.012 (9ea0c8d37bff0c0067b03777f40aa04f2bf78f99, 2023)
@ -292,6 +311,10 @@ Files extracted from upstream source:
* Upstream: https://fonts.google.com/specimen/Open+Sans
* Version: 1.10 (downloaded from Google Fonts in February 2021)
* License: Apache 2.0
- `Vazirmatn*.woff2`:
* Upstream: https://github.com/rastikerdar/vazirmatn
* Version: 33.003 (83629f877e8f084cc07b47030b5d3a0ff06c76ec, 2022)
* License: OFL-1.1
All fonts are converted from the unhinted `.ttf` sources using the
`https://github.com/google/woff2` tool.
@ -334,11 +357,12 @@ Files generated from [upstream web instance](https://gen.glad.sh/):
- `glx.c`
- `glad/glx.h`
See the permalinks in `glad/gl.h` and `glad/glx.h` to regenrate the files with
See the permalinks in `glad/gl.h` and `glad/glx.h` to regenerate the files with
a new version of the web instance.
Some changes have been made in order to allow loading OpenGL and OpenGLES APIs at the same time.
See the patches in the `patches` directory.
Patches:
- `0001-enable-both-gl-and-gles.patch` (GH-72831)
## glslang
@ -360,6 +384,11 @@ Files extracted from upstream source:
- `LICENSE.txt`
- Unnecessary files like `CMakeLists.txt` or `updateGrammar` removed
Patches:
- `0001-apple-disable-absolute-paths.patch` (GH-92010)
- `0002-gcc15-include-fix.patch` (GH-102022)
## graphite
@ -377,7 +406,7 @@ Files extracted from upstream source:
## harfbuzz
- Upstream: https://github.com/harfbuzz/harfbuzz
- Version: 8.5.0 (30485ee8c3d43c553afb9d78b9924cb71c8d2f19, 2024)
- Version: 10.1.0 (9ef44a2d67ac870c1f7f671f6dc98d08a2579865, 2024)
- License: MIT
Files extracted from upstream source:
@ -385,13 +414,13 @@ Files extracted from upstream source:
- `AUTHORS`, `COPYING`, `THANKS`
- From the `src` folder, recursively:
- All the `.cc`, `.h`, `.hh` files
- Except `main.cc`, `harfbuzz*.cc`, `failing-alloc.c`, `test*.cc`, `hb-wasm*.*`
- Except `main.cc`, `harfbuzz*.cc`, `failing-alloc.c`, `test*.cc`, `hb-wasm*.*`, `wasm/*`
## icu4c
- Upstream: https://github.com/unicode-org/icu
- Version: 75.1 (7750081bda4b3bc1768ae03849ec70f67ea10625, 2024)
- Version: 76.1 (8eca245c7484ac6cc179e3e5f7c1ea7680810f39, 2024)
- License: Unicode
Files extracted from upstream source:
@ -403,7 +432,7 @@ Files extracted from upstream source:
Files generated from upstream source:
- The `icudt75l.dat` built with the provided `godot_data.json` config file (see
- The `icudt_godot.dat` built with the provided `godot_data.json` config file (see
https://github.com/unicode-org/icu/blob/master/docs/userguide/icu_data/buildtool.md
for instructions).
@ -413,7 +442,19 @@ Files generated from upstream source:
3. Reconfigure ICU with custom data config:
`ICU_DATA_FILTER_FILE={GODOT_SOURCE}/thirdparty/icu4c/godot_data.json ./runConfigureICU {PLATFORM} --with-data-packaging=common`
4. Delete `data/out` folder and rebuild data: `cd data && rm -rf ./out && make`
5. Copy `source/data/out/icudt75l.dat` to the `{GODOT_SOURCE}/thirdparty/icu4c/icudt75l.dat`
5. Copy `source/data/out/icudt{ICU_VERSION}l.dat` to the `{GODOT_SOURCE}/thirdparty/icu4c/icudt_godot.dat`
## jolt_physics
- Upstream: https://github.com/jrouwe/JoltPhysics
- Version: 5.2.1 (f094082aa2bbfcbebc725dbe8b8f65c7d5152886, 2024)
- License: MIT
Files extracted from upstream source:
- All files in `Jolt/`, except `Jolt/Jolt.cmake` and any files dependent on `ENABLE_OBJECT_STREAM`, as seen in `Jolt/Jolt.cmake`
- `LICENSE`
## jpeg-compressor
@ -427,6 +468,10 @@ Files extracted from upstream source:
- `jpgd*.{c,h}`
- `jpge*.{c,h}`
Patches:
- `0001-clang-fortify-fix.patch` (GH-101927)
## libbacktrace
@ -436,9 +481,15 @@ Files extracted from upstream source:
Files extracted from upstream source:
- `*.{c,h}` files for Windows platform
- `*.{c,h}` files for Windows platform, i.e. remove the following:
* `allocfail.c`, `instrumented_alloc.c`, `*test*.{c,h}`
* `elf.c`, `macho.c`, `mmap.c`, `mmapio.c`, `nounwind.c`, `unknown.c`, `xcoff.c`
- `LICENSE`
Patches:
- `0001-big-files-support.patch` (GH-100281)
## libktx
@ -457,7 +508,10 @@ Files extracted from upstream source:
- `other_include/KHR/`
- `utils/unused.h`
Some Godot-specific changes are applied via patches included in the `patches` folder.
Patches:
- `0001-external-basisu.patch` (GH-76572)
- `0002-disable-astc-block-ext.patch` (GH-76572)
## libogg
@ -476,14 +530,14 @@ Files extracted from upstream source:
## libpng
- Upstream: http://libpng.org/pub/png/libpng.html
- Version: 1.6.43 (ed217e3e601d8e462f7fd1e04bed43ac42212429, 2024)
- Version: 1.6.45 (51f5bd68b9b806d2c92b4318164d28b49357da31, 2024)
- License: libpng/zlib
Files extracted from upstream source:
- All `.c` and `.h` files of the main directory, apart from `example.c` and
`pngtest.c`
- `arm/`, `intel/` and `powerpc/` folders
- `arm/` (minus `filter_neon.S`), `intel/`, `loongarch/`, and `powerpc/` (minus `.editorconfig`) folders
- `scripts/pnglibconf.h.prebuilt` as `pnglibconf.h`
- `LICENSE`
@ -525,37 +579,60 @@ Files extracted from upstream source:
- `src/` and `sharpyuv/` except from `.am`, `.rc` and `.in` files
- `AUTHORS`, `COPYING`, `PATENTS`
Patch `godot-node-debug-fix.patch` workarounds shadowing of Godot's Node class
in the MSVC debugger.
Patches:
- `0001-msvc-node-debug-rename.patch`
- `0002-msvc-arm64-fpstrict.patch`
- `0003-clang-cl-sse2-sse41.patch`
## linuxbsd_headers
See `linuxbsd_headers/README.md`.
## manifold
- Upstream: https://github.com/elalish/manifold
- Version: 3.0.1 (98b8142519d35c13e0e25cfa9fd6e3a271403be6, 2024)
- License: Apache 2.0
File extracted from upstream source:
- `src/` and `include/`, except from `CMakeLists.txt`, `cross_section.cpp` and `meshIO.{cpp,h}`
- `AUTHORS`, `LICENSE`
## mbedtls
- Upstream: https://github.com/Mbed-TLS/mbedtls
- Version: 3.6.0 (2ca6c285a0dd3f33982dd57299012dacab1ff206, 2024)
- Version: 3.6.2 (107ea89daaefb9867ea9121002fbbdf926780e98, 2024)
- License: Apache 2.0
File extracted from upstream release tarball:
- All `.h` from `include/mbedtls/` to `thirdparty/mbedtls/include/mbedtls/`
and all `.h` from `include/psa/` to `thirdparty/mbedtls/include/psa/`
- All `.c` and `.h` from `library/` to `thirdparty/mbedtls/library/` except
for the `psa_*.c` source files
- All `.c` and `.h` from `library/` to `thirdparty/mbedtls/library/`
- From `library/` to `thirdparty/mbedtls/library/`:
- All `.c` and `.h` files
- Except `bignum_mod.c`, `block_cipher.c`, `ecp_curves_new.c`, `lmots.c`,
`lms.c`
- The `LICENSE` file (edited to keep only the Apache 2.0 variant)
- Applied the patch `no-flexible-arrays.diff` to fix Windows build (see
upstream GH-9020)
- Applied the patch `msvc-redeclaration-bug.diff` to fix a compilation error
with some MSVC versions
- Added 2 files `godot_core_mbedtls_platform.c` and `godot_core_mbedtls_config.h`
providing configuration for light bundling with core
- Added the file `godot_module_mbedtls_config.h` to customize the build
configuration when bundling the full library
Patches:
- `0001-msvc-2019-psa-redeclaration.patch` (GH-90535)
## meshoptimizer
- Upstream: https://github.com/zeux/meshoptimizer
- Version: 0.20 (c21d3be6ddf627f8ca852ba4b6db9903b0557858, 2023)
- Version: 0.22 (4affad044571506a5724c9a6f15424f43e86f731, 2024)
- License: MIT
Files extracted from upstream repository:
@ -563,8 +640,9 @@ Files extracted from upstream repository:
- All files in `src/`
- `LICENSE.md`
A patch is included to modify the simplifier to report only distance error
metrics instead of a combination of distance and attribute errors.
Patches:
- `0001-simplifier-distance-only-error.patch` (GH-98529)
## mingw-std-threads
@ -582,8 +660,10 @@ Files extracted from upstream repository:
- `mingw.shared_mutex.h`
- `mingw.thread.h`
Once copied, apply `godot.patch` (needed because Godot is built without exceptions
and to avoid std:: replacements leak in Clang builds).
Patches:
- `0001-disable-exceptions.patch` (GH-85039)
- `0002-clang-std-replacements-leak.patch` (GH-85208)
## minimp3
@ -598,26 +678,27 @@ Files extracted from upstream repository:
- `minimp3_ex.h`
- `LICENSE`
Some changes have been made in order to fix Windows on ARM build errors, and
to solve some MSVC warnings. See the patches in the `patches` directory.
Patches:
- `0001-msvc-arm.patch` (GH-64921)
- `0002-msvc-warnings.patch` (GH-66545)
## miniupnpc
- Upstream: https://github.com/miniupnp/miniupnp
- Version: 2.2.7 (d4d5ec7d48c093b37b2ea5d7171ede21ce9d7ff2, 2024)
- Version: 2.2.8 (b55145ec095652289a59c33603f3abafee898273, 2024)
- License: BSD-3-Clause
Files extracted from upstream source:
- Copy `miniupnpc/src` and `miniupnpc/include` to `thirdparty/miniupnpc`
- `miniupnpc/src/` as `src/`
- `miniupnpc/include/` as `include/miniupnpc/`
- Remove the following test or sample files:
`listdevices.c,minihttptestserver.c,miniupnpcmodule.c,upnpc.c,upnperrors.*,test*`
- `LICENSE`
The only modified file is `src/miniupnpcstrings.h`, which was created for Godot
(it is usually autogenerated by cmake). Bump the version number for miniupnpc in
that file when upgrading.
- `src/miniupnpcstrings.h` was created manually for Godot (it is usually generated
by CMake). Bump the version number for miniupnpc in that file when upgrading.
## minizip
@ -632,19 +713,19 @@ Files extracted from the upstream source:
`{crypt.h,ioapi.{c,h},unzip.{c,h},zip.{c,h}}`
`MiniZip64_info.txt`
Important: Some files have Godot-made changes for use in core/io.
They are marked with `/* GODOT start */` and `/* GODOT end */`
comments and a patch is provided in the `patches` folder.
Patches:
- `0001-godot-seek.patch` (GH-10428)
## misc
Collection of single-file libraries used in Godot components.
- `clipper.{cpp,hpp}`
* Upstream: https://sourceforge.net/projects/polyclipping
* Version: 6.4.2 (2017) + Godot changes (added optional exceptions handling)
* License: BSL-1.0
- `bcdec.h`
* Upstream: https://github.com/iOrange/bcdec
* Version: git (3b29f8f44466c7d59852670f82f53905cf627d48, 2024)
* License: MIT
- `cubemap_coeffs.h`
* Upstream: https://research.activision.com/publications/archives/fast-filtering-of-reflection-probes
File coeffs_const_8.txt (retrieved April 2020)
@ -653,14 +734,27 @@ Collection of single-file libraries used in Godot components.
* Upstream: https://github.com/ariya/FastLZ
* Version: 0.5.0 (4f20f54d46f5a6dd4fae4def134933369b7602d2, 2020)
* License: MIT
- `FastNoiseLite.h`
* Upstream: https://github.com/Auburn/FastNoiseLite
* Version: 1.1.0 (f7af54b56518aa659e1cf9fb103c0b6e36a833d9, 2023)
* License: MIT
* Patches:
- `FastNoiseLite-0001-namespace-warnings.patch` (GH-88526)
- `ifaddrs-android.{cc,h}`
* Upstream: https://chromium.googlesource.com/external/webrtc/stable/talk/+/master/base/ifaddrs-android.h
* Version: git (5976650443d68ccfadf1dea24999ee459dd2819d, 2013)
* License: BSD-3-Clause
* Patches:
- `ifaddrs-android-0001-complete-struct.patch` (GH-34101)
- `mikktspace.{c,h}`
* Upstream: https://archive.blender.org/wiki/index.php/Dev:Shading/Tangent_Space_Normal_Maps/
* Version: 1.0 (2011)
* License: zlib
- `nvapi_minimal.h`
* Upstream: http://download.nvidia.com/XFree86/nvapi-open-source-sdk
* Version: R525
* License: MIT
* Modifications: Created from upstream `nvapi.h` by removing unnecessary code.
- `ok_color.h`
* Upstream: https://github.com/bottosson/bottosson.github.io/blob/master/misc/ok_color.h
* Version: git (d69831edb90ffdcd08b7e64da3c5405acd48ad2c, 2022)
@ -677,13 +771,15 @@ Collection of single-file libraries used in Godot components.
- `polypartition.{cpp,h}`
* Upstream: https://github.com/ivanfratric/polypartition (`src/polypartition.{cpp,h}`)
* Version: git (7bdffb428b2b19ad1c43aa44c714dcc104177e84, 2021)
* Modifications: Change from STL to Godot types (see provided patch).
* License: MIT
- `qoa.h`
* Patches:
- `polypartition-0001-godot-types.patch` (2185c018f)
- `polypartition-0002-shadow-warning.patch` (GH-66808)
- `qoa.{c,h}`
* Upstream: https://github.com/phoboslab/qoa
* Version: git (5c2a86d615661f34636cf179abf4fa278d3257e0, 2024)
* Modifications: Inlined functions, patched uninitialized variables and untyped mallocs.
* Version: git (a2d927f8ce78a85e903676a33e0f956e53b89f7d, 2024)
* License: MIT
* Modifications: Added implementation through `qoa.c`.
- `r128.{c,h}`
* Upstream: https://github.com/fahickman/r128
* Version: git (6fc177671c47640d5bb69af10cf4ee91050015a1, 2023)
@ -692,10 +788,12 @@ Collection of single-file libraries used in Godot components.
* Upstream: https://github.com/antirez/smaz
* Version: git (2f625846a775501fb69456567409a8b12f10ea25, 2012)
* License: BSD-3-Clause
* Modifications: use `const char*` instead of `char*` for input string
* Modifications: License included in header.
* Patches:
- `smaz-0001-write-string-warning.patch` (GH-8572)
- `smolv.{cpp,h}`
* Upstream: https://github.com/aras-p/smol-v
* Version: git (4b52c165c13763051a18e80ffbc2ee436314ceb2, 2020)
* Version: git (9dd54c379ac29fa148cb1b829bb939ba7381d8f4, 2024)
* License: Public Domain or MIT
- `stb_rect_pack.h`
* Upstream: https://github.com/nothings/stb
@ -710,7 +808,7 @@ Collection of single-file libraries used in Godot components.
## msdfgen
- Upstream: https://github.com/Chlumsky/msdfgen
- Version: 1.11 (f12d7ca00091a632a289865b85c3f2e0bfc6542d, 2023)
- Version: 1.12 (85e8b3d47b3d1a42e4a5ebda0a24fb1cc2e669e0, 2024)
- License: MIT
Files extracted from the upstream source:
@ -720,35 +818,10 @@ Files extracted from the upstream source:
- `LICENSE.txt`
## noise
- Upstream: https://github.com/Auburn/FastNoiseLite
- Version: 1.1.0 (f7af54b56518aa659e1cf9fb103c0b6e36a833d9, 2023)
- License: MIT
Files extracted from the upstream source:
- `FastNoiseLite.h`
- `LICENSE`
Some custom changes were made to fix compiler warnings, and can be re-applied
with the provided patch.
## nvapi
- Upstream: http://download.nvidia.com/XFree86/nvapi-open-source-sdk
- Version: R525
- License: MIT
- `nvapi_minimal.h` was created by using `nvapi.h` from upstream and removing
unnecessary code.
## openxr
- Upstream: https://github.com/KhronosGroup/OpenXR-SDK
- Version: 1.0.34 (288d3a7ebc1ad959f62d51da75baa3d27438c499, 2024)
- Version: 1.1.41 (7d1c0961351bac61fd7bb72d402649d5ac3f2935, 2024)
- License: Apache 2.0
Files extracted from upstream source:
@ -772,6 +845,10 @@ Exclude:
`*.{def,expsym,in,json,map,pom,rc,txt}`
- All dotfiles
Patches:
- `0001-glad-egl.patch` (GH-98824)
## pcre2
@ -827,6 +904,22 @@ and solve conflicts and also enrich the feature set originally
proposed by these libraries and better integrate them with Godot.
## spirv-cross
- Upstream: https://github.com/KhronosGroup/SPIRV-Cross
- Version: git (6173e24b31f09a0c3217103a130e74c4ddec14a6, 2024)
- License: Apache 2.0
Files extracted from upstream source:
- All `.cpp`, `.hpp` and `.h` files, minus `main.cpp`, `spirv_cross_c.*`, `spirv_hlsl.*`, `spirv_cpp.*`
- `include/` folder
- `LICENSE` and `LICENSES/` folder, minus `CC-BY-4.0.txt`
Versions of this SDK do not have to match the `vulkan` section, as this SDK is required
to generate Metal source from Vulkan SPIR-V.
## spirv-reflect
- Upstream: https://github.com/KhronosGroup/SPIRV-Reflect
@ -842,59 +935,47 @@ Files extracted from upstream source:
- `include/` folder
- `LICENSE`
Some downstream changes have been made and are identified by
`// -- GODOT begin --` and `// -- GODOT end --` comments.
They can be reapplied using the patches included in the `patches`
folder.
Patches:
- `0001-specialization-constants.patch` (GH-50325)
- `0002-zero-size-for-sc-sized-arrays.patch` (GH-94985)
## squish
## thorvg
- Upstream: https://sourceforge.net/projects/libsquish
- Version: 1.15 (r104, 2017)
- Upstream: https://github.com/thorvg/thorvg
- Version: 0.15.10 (bca94d244c67f573c6eddc27d783d9a6b1ef2f1b, 2025)
- License: MIT
Files extracted from upstream source:
- `LICENSE.txt`
- All `.cpp`, `.h` and `.inl` files
- See `thorvg/update-thorvg.sh` for extraction instructions.
Set the version number and run the script.
Some downstream changes have been made and are identified by
`// -- GODOT begin --` and `// -- GODOT end --` comments.
They can be reapplied using the patches included in the `patches`
folder.
Patches:
- `0001-revert-tvglines-bezier-precision.patch` (GH-96658)
## tinyexr
- Upstream: https://github.com/syoyo/tinyexr
- Version: 1.0.8 (6c8742cc8145c8f629698cd8248900990946d6b1, 2024)
- Version: 1.0.9 (5fcb4dcb6e3abf96214b67e5c54db1ceec6a455c, 2024)
- License: BSD-3-Clause
Files extracted from upstream source:
- `tinyexr.{cc,h}`
The `tinyexr.cc` file was modified to include `zlib.h` which we provide,
instead of `miniz.h` as an external dependency.
Patches:
## thorvg
- Upstream: https://github.com/thorvg/thorvg
- Version: 0.14.2 (f6c4d8a94e0b2194fe911d6e19a550683055dd50, 2024)
- License: MIT
Files extracted from upstream source:
See `thorvg/update-thorvg.sh` for extraction instructions. Set the version
number and run the script.
- `0001-external-zlib.patch` (GH-55115)
## ufbx
- Upstream: https://github.com/ufbx/ufbx
- Version: 0.14.0 (80ff790ab36507b99ec7e4ef55b9cfb076ce821b, 2024)
- Version: 0.15.0 (24eea6f40929fe0f679b7950def378edb003afdb, 2024)
- License: MIT
Files extracted from upstream source:
@ -914,10 +995,14 @@ Files extracted from upstream source:
- From `src/VHACD_Lib/`: `inc`, `public` and `src`
- `LICENSE`
Some downstream changes have been made and are identified by
`// -- GODOT start --` and `// -- GODOT end --` comments.
They can be reapplied using the patches included in the `vhacd`
folder.
Patches:
- `0001-bullet-namespace.patch` (GH-27929)
- `0002-fpermissive-fix.patch` (GH-27929)
- `0003-fix-musl-build.patch` (GH-34250)
- `0004-fix-msvc-arm-build.patch` (GH-34331)
- `0005-fix-scale-calculation.patch` (GH-38506)
- `0006-gcc13-include-fix.patch` (GH-77949)
## volk
@ -961,7 +1046,12 @@ SDK release: https://github.com/KhronosGroup/Vulkan-Utility-Libraries/blob/main/
Version: 3.1.0 (009ecd192c1289c7529bff248a16cfe896254816, 2024)
`vk_mem_alloc.cpp` is a Godot file and should be preserved on updates.
Patches in the `patches` directory should be re-applied after updates.
Patches:
- `0001-VKEnumStringHelper-godot-vulkan.patch` (GH-97510)
- `0002-VMA-godot-vulkan.patch` (GH-97510)
- `0003-VMA-add-vmaCalculateLazilyAllocatedBytes.patch` (GH-99257)
## wayland
@ -992,6 +1082,7 @@ Files extracted from upstream source:
- `staging/fractional-scale/fractional-scale-v1.xml`
- `staging/xdg-activation/README`
- `staging/xdg-activation/xdg-activation-v1.xml`
- `staging/xdg-system-bell/xdg-system-bell-v1.xml`
- `unstable/idle-inhibit/README`
- `unstable/idle-inhibit/idle-inhibit-unstable-v1.xml`
- `unstable/pointer-constraints/README`
@ -1025,10 +1116,12 @@ File extracted from upstream release tarball:
Contents might need tweaking for Godot, review diff
- All `.c` and `.h` files from `lib/`
- All `.h` in `lib/includes/wslay/` as `wslay/`
- `wslay/wslay.h` has a small Godot addition to fix MSVC build
See `patches/msvcfix.diff`
- `COPYING`
Patches:
- `0001-msvc-build-fix.patch` (GH-30263)
## xatlas
@ -1050,7 +1143,7 @@ Files extracted from upstream source:
Files extracted from upstream source:
- All `.c` and `.h` files, minus `infback.c`
- All `.c` and `.h` files, except `gz*.c` and `infback.c`
- `LICENSE`

View file

@ -36,7 +36,6 @@
#pragma clang diagnostic ignored "-Wunused-variable"
#endif
// -- GODOT start --
#ifndef _countof
#define _countof(array) (sizeof(array) / sizeof(array[0]))
#endif
@ -45,7 +44,6 @@
#include <wchar.h>
#define wcscpy_s wcscpy
#endif
// -- GODOT end --
// max queued frames for descriptor management
static const uint32_t FSR2_MAX_QUEUED_FRAMES = 16;
@ -954,9 +952,7 @@ static FfxErrorCode fsr2Dispatch(FfxFsr2Context_Private* context, const FfxFsr2D
context->constants.lumaMipDimensions[0] = uint32_t(context->constants.maxRenderSize[0] / mipDiv);
context->constants.lumaMipDimensions[1] = uint32_t(context->constants.maxRenderSize[1] / mipDiv);
// -- GODOT start --
memcpy(context->constants.reprojectionMatrix, params->reprojectionMatrix, sizeof(context->constants.reprojectionMatrix));
// -- GODOT end --
// reactive mask bias
const int32_t threadGroupWorkRegionDim = 8;

View file

@ -146,10 +146,7 @@ typedef struct FfxFsr2DispatchDescription {
float autoReactiveScale; ///< A value to scale the reactive mask
float autoReactiveMax; ///< A value to clamp the reactive mask
// -- GODOT start --
float reprojectionMatrix[16]; ///< The matrix used for reprojecting pixels with invalid motion vectors by using the depth.
// -- GODOT end --
} FfxFsr2DispatchDescription;
/// A structure encapsulating the parameters for automatic generation of a reactive mask

View file

@ -45,10 +45,8 @@ typedef struct Fsr2Constants {
float dynamicResChangeFactor;
float viewSpaceToMetersFactor;
// -- GODOT start --
float pad;
float reprojectionMatrix[16];
// -- GODOT end --
} Fsr2Constants;
struct FfxFsr2ContextDescription;

View file

@ -22,9 +22,7 @@
#pragma once
#include <stdint.h>
// -- GODOT start --
#include <stdlib.h>
// -- GODOT end --
#if defined (FFX_GCC)
/// FidelityFX exported functions

View file

@ -0,0 +1,136 @@
diff --git a/thirdparty/amd-fsr2/ffx_fsr2.cpp b/thirdparty/amd-fsr2/ffx_fsr2.cpp
index 051018e437..3970aa7f5b 100644
--- a/thirdparty/amd-fsr2/ffx_fsr2.cpp
+++ b/thirdparty/amd-fsr2/ffx_fsr2.cpp
@@ -36,6 +36,15 @@
#pragma clang diagnostic ignored "-Wunused-variable"
#endif
+#ifndef _countof
+#define _countof(array) (sizeof(array) / sizeof(array[0]))
+#endif
+
+#ifndef _MSC_VER
+#include <wchar.h>
+#define wcscpy_s wcscpy
+#endif
+
// max queued frames for descriptor management
static const uint32_t FSR2_MAX_QUEUED_FRAMES = 16;
diff --git a/thirdparty/amd-fsr2/ffx_types.h b/thirdparty/amd-fsr2/ffx_types.h
index 74edd192c4..f71b259cce 100644
--- a/thirdparty/amd-fsr2/ffx_types.h
+++ b/thirdparty/amd-fsr2/ffx_types.h
@@ -22,6 +22,7 @@
#pragma once
#include <stdint.h>
+#include <stdlib.h>
#if defined (FFX_GCC)
/// FidelityFX exported functions
diff --git a/thirdparty/amd-fsr2/shaders/ffx_fsr2_accumulate_pass.glsl b/thirdparty/amd-fsr2/shaders/ffx_fsr2_accumulate_pass.glsl
index ebbe610ffa..31d68292d4 100644
--- a/thirdparty/amd-fsr2/shaders/ffx_fsr2_accumulate_pass.glsl
+++ b/thirdparty/amd-fsr2/shaders/ffx_fsr2_accumulate_pass.glsl
@@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#version 450
+//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require
diff --git a/thirdparty/amd-fsr2/shaders/ffx_fsr2_autogen_reactive_pass.glsl b/thirdparty/amd-fsr2/shaders/ffx_fsr2_autogen_reactive_pass.glsl
index 7ae41cf0c1..3b86c17d4d 100644
--- a/thirdparty/amd-fsr2/shaders/ffx_fsr2_autogen_reactive_pass.glsl
+++ b/thirdparty/amd-fsr2/shaders/ffx_fsr2_autogen_reactive_pass.glsl
@@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#version 450
+//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require
diff --git a/thirdparty/amd-fsr2/shaders/ffx_fsr2_compute_luminance_pyramid_pass.glsl b/thirdparty/amd-fsr2/shaders/ffx_fsr2_compute_luminance_pyramid_pass.glsl
index 15186e3bb6..8439c4e9d4 100644
--- a/thirdparty/amd-fsr2/shaders/ffx_fsr2_compute_luminance_pyramid_pass.glsl
+++ b/thirdparty/amd-fsr2/shaders/ffx_fsr2_compute_luminance_pyramid_pass.glsl
@@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#version 450
+//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require
diff --git a/thirdparty/amd-fsr2/shaders/ffx_fsr2_depth_clip_pass.glsl b/thirdparty/amd-fsr2/shaders/ffx_fsr2_depth_clip_pass.glsl
index fcb2b76528..45ec5bdb86 100644
--- a/thirdparty/amd-fsr2/shaders/ffx_fsr2_depth_clip_pass.glsl
+++ b/thirdparty/amd-fsr2/shaders/ffx_fsr2_depth_clip_pass.glsl
@@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#version 450
+//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require
diff --git a/thirdparty/amd-fsr2/shaders/ffx_fsr2_lock_pass.glsl b/thirdparty/amd-fsr2/shaders/ffx_fsr2_lock_pass.glsl
index f7cad59c20..7c3a4c2740 100644
--- a/thirdparty/amd-fsr2/shaders/ffx_fsr2_lock_pass.glsl
+++ b/thirdparty/amd-fsr2/shaders/ffx_fsr2_lock_pass.glsl
@@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#version 450
+//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require
diff --git a/thirdparty/amd-fsr2/shaders/ffx_fsr2_rcas_pass.glsl b/thirdparty/amd-fsr2/shaders/ffx_fsr2_rcas_pass.glsl
index f0823c2bc8..8b4ebc6afc 100644
--- a/thirdparty/amd-fsr2/shaders/ffx_fsr2_rcas_pass.glsl
+++ b/thirdparty/amd-fsr2/shaders/ffx_fsr2_rcas_pass.glsl
@@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#version 450
+//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require
diff --git a/thirdparty/amd-fsr2/shaders/ffx_fsr2_reconstruct_previous_depth_pass.glsl b/thirdparty/amd-fsr2/shaders/ffx_fsr2_reconstruct_previous_depth_pass.glsl
index 20e17eef8c..be4395aaed 100644
--- a/thirdparty/amd-fsr2/shaders/ffx_fsr2_reconstruct_previous_depth_pass.glsl
+++ b/thirdparty/amd-fsr2/shaders/ffx_fsr2_reconstruct_previous_depth_pass.glsl
@@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#version 450
+//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require
diff --git a/thirdparty/amd-fsr2/shaders/ffx_fsr2_tcr_autogen_pass.glsl b/thirdparty/amd-fsr2/shaders/ffx_fsr2_tcr_autogen_pass.glsl
index bebca91099..7d6a66b8ac 100644
--- a/thirdparty/amd-fsr2/shaders/ffx_fsr2_tcr_autogen_pass.glsl
+++ b/thirdparty/amd-fsr2/shaders/ffx_fsr2_tcr_autogen_pass.glsl
@@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#version 450
+//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require

View file

@ -0,0 +1,121 @@
diff --git a/thirdparty/amd-fsr2/ffx_fsr2.cpp b/thirdparty/amd-fsr2/ffx_fsr2.cpp
index 3970aa7f5b..ec571b9cd2 100644
--- a/thirdparty/amd-fsr2/ffx_fsr2.cpp
+++ b/thirdparty/amd-fsr2/ffx_fsr2.cpp
@@ -952,6 +952,8 @@ static FfxErrorCode fsr2Dispatch(FfxFsr2Context_Private* context, const FfxFsr2D
context->constants.lumaMipDimensions[0] = uint32_t(context->constants.maxRenderSize[0] / mipDiv);
context->constants.lumaMipDimensions[1] = uint32_t(context->constants.maxRenderSize[1] / mipDiv);
+ memcpy(context->constants.reprojectionMatrix, params->reprojectionMatrix, sizeof(context->constants.reprojectionMatrix));
+
// reactive mask bias
const int32_t threadGroupWorkRegionDim = 8;
const int32_t dispatchSrcX = (context->constants.renderSize[0] + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
diff --git a/thirdparty/amd-fsr2/ffx_fsr2.h b/thirdparty/amd-fsr2/ffx_fsr2.h
index 2a1c74abb1..dfcd4caf35 100644
--- a/thirdparty/amd-fsr2/ffx_fsr2.h
+++ b/thirdparty/amd-fsr2/ffx_fsr2.h
@@ -146,6 +146,7 @@ typedef struct FfxFsr2DispatchDescription {
float autoReactiveScale; ///< A value to scale the reactive mask
float autoReactiveMax; ///< A value to clamp the reactive mask
+ float reprojectionMatrix[16]; ///< The matrix used for reprojecting pixels with invalid motion vectors by using the depth.
} FfxFsr2DispatchDescription;
/// A structure encapsulating the parameters for automatic generation of a reactive mask
diff --git a/thirdparty/amd-fsr2/ffx_fsr2_private.h b/thirdparty/amd-fsr2/ffx_fsr2_private.h
index 6b5fbc5117..8a9aec5778 100644
--- a/thirdparty/amd-fsr2/ffx_fsr2_private.h
+++ b/thirdparty/amd-fsr2/ffx_fsr2_private.h
@@ -44,6 +44,9 @@ typedef struct Fsr2Constants {
float deltaTime;
float dynamicResChangeFactor;
float viewSpaceToMetersFactor;
+
+ float pad;
+ float reprojectionMatrix[16];
} Fsr2Constants;
struct FfxFsr2ContextDescription;
diff --git a/thirdparty/amd-fsr2/shaders/ffx_fsr2_accumulate_pass.glsl b/thirdparty/amd-fsr2/shaders/ffx_fsr2_accumulate_pass.glsl
index 31d68292d4..2e98c8a6c5 100644
--- a/thirdparty/amd-fsr2/shaders/ffx_fsr2_accumulate_pass.glsl
+++ b/thirdparty/amd-fsr2/shaders/ffx_fsr2_accumulate_pass.glsl
@@ -35,7 +35,7 @@
#endif
#define FSR2_BIND_SRV_INTERNAL_UPSCALED 3
#define FSR2_BIND_SRV_LOCK_STATUS 4
-#define FSR2_BIND_SRV_INPUT_DEPTH_CLIP 5
+//#define FSR2_BIND_SRV_INPUT_DEPTH_CLIP 5
#define FSR2_BIND_SRV_PREPARED_INPUT_COLOR 6
#define FSR2_BIND_SRV_LUMA_INSTABILITY 7
#define FSR2_BIND_SRV_LANCZOS_LUT 8
@@ -52,6 +52,10 @@
#define FSR2_BIND_CB_FSR2 18
+#if FFX_FSR2_OPTION_GODOT_DERIVE_INVALID_MOTION_VECTORS
+#define FSR2_BIND_SRV_INPUT_DEPTH 5
+#endif
+
#include "ffx_fsr2_callbacks_glsl.h"
#include "ffx_fsr2_common.h"
#include "ffx_fsr2_sample.h"
diff --git a/thirdparty/amd-fsr2/shaders/ffx_fsr2_callbacks_glsl.h b/thirdparty/amd-fsr2/shaders/ffx_fsr2_callbacks_glsl.h
index 10da13fb81..b610037cc6 100644
--- a/thirdparty/amd-fsr2/shaders/ffx_fsr2_callbacks_glsl.h
+++ b/thirdparty/amd-fsr2/shaders/ffx_fsr2_callbacks_glsl.h
@@ -52,6 +52,9 @@
FfxFloat32 fDeltaTime;
FfxFloat32 fDynamicResChangeFactor;
FfxFloat32 fViewSpaceToMetersFactor;
+
+ FfxFloat32 fPad;
+ mat4 mReprojectionMatrix;
} cbFSR2;
#endif
@@ -317,7 +320,11 @@ FfxFloat32 LoadInputDepth(FfxInt32x2 iPxPos)
#if defined(FSR2_BIND_SRV_REACTIVE_MASK)
FfxFloat32 LoadReactiveMask(FfxInt32x2 iPxPos)
{
+#if FFX_FSR2_OPTION_GODOT_REACTIVE_MASK_CLAMP
+ return min(texelFetch(r_reactive_mask, FfxInt32x2(iPxPos), 0).r, 0.9f);
+#else
return texelFetch(r_reactive_mask, FfxInt32x2(iPxPos), 0).r;
+#endif
}
#endif
@@ -354,6 +361,16 @@ FfxFloat32x2 LoadInputMotionVector(FfxInt32x2 iPxDilatedMotionVectorPos)
{
FfxFloat32x2 fSrcMotionVector = texelFetch(r_input_motion_vectors, iPxDilatedMotionVectorPos, 0).xy;
+#if FFX_FSR2_OPTION_GODOT_DERIVE_INVALID_MOTION_VECTORS
+ bool bInvalidMotionVector = all(lessThanEqual(fSrcMotionVector, vec2(-1.0f, -1.0f)));
+ if (bInvalidMotionVector)
+ {
+ FfxFloat32 fSrcDepth = LoadInputDepth(iPxDilatedMotionVectorPos);
+ FfxFloat32x2 fUv = (iPxDilatedMotionVectorPos + FfxFloat32(0.5)) / RenderSize();
+ fSrcMotionVector = FFX_FSR2_OPTION_GODOT_DERIVE_INVALID_MOTION_VECTORS_FUNCTION(fUv, fSrcDepth, cbFSR2.mReprojectionMatrix);
+ }
+#endif
+
FfxFloat32x2 fUvMotionVector = fSrcMotionVector * MotionVectorScale();
#if FFX_FSR2_OPTION_JITTERED_MOTION_VECTORS
diff --git a/thirdparty/amd-fsr2/shaders/ffx_fsr2_tcr_autogen_pass.glsl b/thirdparty/amd-fsr2/shaders/ffx_fsr2_tcr_autogen_pass.glsl
index 7d6a66b8ac..5c042c332a 100644
--- a/thirdparty/amd-fsr2/shaders/ffx_fsr2_tcr_autogen_pass.glsl
+++ b/thirdparty/amd-fsr2/shaders/ffx_fsr2_tcr_autogen_pass.glsl
@@ -40,6 +40,10 @@
#define FSR2_BIND_CB_FSR2 11
#define FSR2_BIND_CB_REACTIVE 12
+#if FFX_FSR2_OPTION_GODOT_DERIVE_INVALID_MOTION_VECTORS
+#define FSR2_BIND_SRV_INPUT_DEPTH 13
+#endif
+
#include "ffx_fsr2_callbacks_glsl.h"
#include "ffx_fsr2_common.h"

View file

@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require
@ -35,6 +35,7 @@
#endif
#define FSR2_BIND_SRV_INTERNAL_UPSCALED 3
#define FSR2_BIND_SRV_LOCK_STATUS 4
//#define FSR2_BIND_SRV_INPUT_DEPTH_CLIP 5
#define FSR2_BIND_SRV_PREPARED_INPUT_COLOR 6
#define FSR2_BIND_SRV_LUMA_INSTABILITY 7
#define FSR2_BIND_SRV_LANCZOS_LUT 8
@ -51,11 +52,9 @@
#define FSR2_BIND_CB_FSR2 18
// -- GODOT start --
#if FFX_FSR2_OPTION_GODOT_DERIVE_INVALID_MOTION_VECTORS
#define FSR2_BIND_SRV_INPUT_DEPTH 5
#endif
// -- GODOT end --
#include "ffx_fsr2_callbacks_glsl.h"
#include "ffx_fsr2_common.h"

View file

@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require

View file

@ -52,11 +52,9 @@
FfxFloat32 fDeltaTime;
FfxFloat32 fDynamicResChangeFactor;
FfxFloat32 fViewSpaceToMetersFactor;
// -- GODOT start --
FfxFloat32 fPad;
mat4 mReprojectionMatrix;
// -- GODOT end --
} cbFSR2;
#endif
@ -322,13 +320,11 @@ FfxFloat32 LoadInputDepth(FfxInt32x2 iPxPos)
#if defined(FSR2_BIND_SRV_REACTIVE_MASK)
FfxFloat32 LoadReactiveMask(FfxInt32x2 iPxPos)
{
// -- GODOT start --
#if FFX_FSR2_OPTION_GODOT_REACTIVE_MASK_CLAMP
return min(texelFetch(r_reactive_mask, FfxInt32x2(iPxPos), 0).r, 0.9f);
#else
return texelFetch(r_reactive_mask, FfxInt32x2(iPxPos), 0).r;
#endif
// -- GODOT end --
}
#endif
@ -365,7 +361,6 @@ FfxFloat32x2 LoadInputMotionVector(FfxInt32x2 iPxDilatedMotionVectorPos)
{
FfxFloat32x2 fSrcMotionVector = texelFetch(r_input_motion_vectors, iPxDilatedMotionVectorPos, 0).xy;
// -- GODOT start --
#if FFX_FSR2_OPTION_GODOT_DERIVE_INVALID_MOTION_VECTORS
bool bInvalidMotionVector = all(lessThanEqual(fSrcMotionVector, vec2(-1.0f, -1.0f)));
if (bInvalidMotionVector)
@ -375,7 +370,6 @@ FfxFloat32x2 LoadInputMotionVector(FfxInt32x2 iPxDilatedMotionVectorPos)
fSrcMotionVector = FFX_FSR2_OPTION_GODOT_DERIVE_INVALID_MOTION_VECTORS_FUNCTION(fUv, fSrcDepth, cbFSR2.mReprojectionMatrix);
}
#endif
// -- GODOT end --
FfxFloat32x2 fUvMotionVector = fSrcMotionVector * MotionVectorScale();

View file

@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require

View file

@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require

View file

@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require

View file

@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require

View file

@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require

View file

@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//#version 450
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require
@ -40,11 +40,9 @@
#define FSR2_BIND_CB_FSR2 11
#define FSR2_BIND_CB_REACTIVE 12
// -- GODOT start --
#if FFX_FSR2_OPTION_GODOT_DERIVE_INVALID_MOTION_VECTORS
#define FSR2_BIND_SRV_INPUT_DEPTH 13
#endif
// -- GODOT end --
#include "ffx_fsr2_callbacks_glsl.h"
#include "ffx_fsr2_common.h"

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,45 @@
// File: android_astc_decomp.h
#ifndef _TCUASTCUTIL_HPP
#define _TCUASTCUTIL_HPP
/*-------------------------------------------------------------------------
* drawElements Quality Program Tester Core
* ----------------------------------------
*
* Copyright 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief ASTC Utilities.
*//*--------------------------------------------------------------------*/
#include <vector>
#include <stdint.h>
namespace basisu_astc
{
namespace astc
{
// Unpacks a single ASTC block to pDst
// If isSRGB is true, the spec requires the decoder to scale the LDR 8-bit endpoints to 16-bit before interpolation slightly differently,
// which will lead to different outputs. So be sure to set it correctly (ideally it should match whatever the encoder did).
bool decompress_ldr(uint8_t* pDst, const uint8_t* data, bool isSRGB, int blockWidth, int blockHeight);
bool decompress_hdr(float* pDstRGBA, const uint8_t* data, int blockWidth, int blockHeight);
bool is_hdr(const uint8_t* data, int blockWidth, int blockHeight, bool& is_hdr);
} // astc
} // basisu
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,224 @@
// basisu_astc_hdr_enc.h
#pragma once
#include "basisu_enc.h"
#include "basisu_gpu_texture.h"
#include "../transcoder/basisu_astc_helpers.h"
#include "../transcoder/basisu_astc_hdr_core.h"
namespace basisu
{
// This MUST be called before encoding any blocks.
void astc_hdr_enc_init();
const uint32_t MODE11_FIRST_ISE_RANGE = astc_helpers::BISE_3_LEVELS, MODE11_LAST_ISE_RANGE = astc_helpers::BISE_16_LEVELS;
const uint32_t MODE7_PART1_FIRST_ISE_RANGE = astc_helpers::BISE_3_LEVELS, MODE7_PART1_LAST_ISE_RANGE = astc_helpers::BISE_16_LEVELS;
const uint32_t MODE7_PART2_FIRST_ISE_RANGE = astc_helpers::BISE_3_LEVELS, MODE7_PART2_LAST_ISE_RANGE = astc_helpers::BISE_8_LEVELS;
const uint32_t MODE11_PART2_FIRST_ISE_RANGE = astc_helpers::BISE_3_LEVELS, MODE11_PART2_LAST_ISE_RANGE = astc_helpers::BISE_4_LEVELS;
const uint32_t MODE11_TOTAL_SUBMODES = 8; // plus an extra hidden submode, directly encoded, for direct, so really 9 (see tables 99/100 of the ASTC spec)
const uint32_t MODE7_TOTAL_SUBMODES = 6;
struct astc_hdr_codec_options
{
float m_bc6h_err_weight;
bool m_use_solid;
bool m_use_mode11;
bool m_mode11_uber_mode;
uint32_t m_first_mode11_weight_ise_range;
uint32_t m_last_mode11_weight_ise_range;
bool m_mode11_direct_only;
int32_t m_first_mode11_submode;
int32_t m_last_mode11_submode;
bool m_use_mode7_part1;
uint32_t m_first_mode7_part1_weight_ise_range;
uint32_t m_last_mode7_part1_weight_ise_range;
bool m_use_mode7_part2;
uint32_t m_mode7_part2_part_masks;
uint32_t m_first_mode7_part2_weight_ise_range;
uint32_t m_last_mode7_part2_weight_ise_range;
bool m_use_mode11_part2;
uint32_t m_mode11_part2_part_masks;
uint32_t m_first_mode11_part2_weight_ise_range;
uint32_t m_last_mode11_part2_weight_ise_range;
float m_r_err_scale, m_g_err_scale;
bool m_refine_weights;
uint32_t m_level;
bool m_use_estimated_partitions;
uint32_t m_max_estimated_partitions;
// If true, the ASTC HDR compressor is allowed to more aggressively vary weight indices for slightly higher compression in non-fastest mode. This will hurt BC6H quality, however.
bool m_allow_uber_mode;
astc_hdr_codec_options();
void init();
// TODO: set_quality_level() is preferred to configure the codec for transcoding purposes.
static const int cMinLevel = 0;
static const int cMaxLevel = 4;
static const int cDefaultLevel = 1;
void set_quality_level(int level);
private:
void set_quality_best();
void set_quality_normal();
void set_quality_fastest();
};
struct astc_hdr_pack_results
{
double m_best_block_error;
double m_bc6h_block_error; // note this is not used/set by the encoder, here for convienance
// Encoder results (logical ASTC block)
astc_helpers::log_astc_block m_best_blk;
// For statistical use
uint32_t m_best_submodes[2];
uint32_t m_best_pat_index;
bool m_constrained_weights;
bool m_improved_via_refinement_flag;
// Only valid if the block is solid
basist::astc_blk m_solid_blk;
// The BC6H transcoded block
basist::bc6h_block m_bc6h_block;
// Solid color/void extent flag
bool m_is_solid;
void clear()
{
m_best_block_error = 1e+30f;
m_bc6h_block_error = 1e+30f;
m_best_blk.clear();
m_best_blk.m_grid_width = 4;
m_best_blk.m_grid_height = 4;
m_best_blk.m_endpoint_ise_range = 20; // 0-255
clear_obj(m_best_submodes);
m_best_pat_index = 0;
m_constrained_weights = false;
clear_obj(m_bc6h_block);
m_is_solid = false;
m_improved_via_refinement_flag = false;
}
};
void interpolate_qlog12_colors(
const int e[2][3],
basist::half_float* pDecoded_half,
vec3F* pDecoded_float,
uint32_t n, uint32_t ise_weight_range);
bool get_astc_hdr_mode_11_block_colors(
const uint8_t* pEndpoints,
basist::half_float* pDecoded_half,
vec3F* pDecoded_float,
uint32_t n, uint32_t ise_weight_range, uint32_t ise_endpoint_range);
bool get_astc_hdr_mode_7_block_colors(
const uint8_t* pEndpoints,
basist::half_float* pDecoded_half,
vec3F* pDecoded_float,
uint32_t n, uint32_t ise_weight_range, uint32_t ise_endpoint_range);
double eval_selectors(
uint32_t num_pixels,
uint8_t* pWeights,
const basist::half_float* pBlock_pixels_half,
uint32_t num_weight_levels,
const basist::half_float* pDecoded_half,
const astc_hdr_codec_options& coptions,
uint32_t usable_selector_bitmask = UINT32_MAX);
double compute_block_error(const basist::half_float* pOrig_block, const basist::half_float* pPacked_block, const astc_hdr_codec_options& coptions);
// Encodes a 4x4 ASTC HDR block given a 4x4 array of source block pixels/texels.
// Supports solid color blocks, mode 11 (all submodes), mode 7/1 partition (all submodes),
// and mode 7/2 partitions (all submodes) - 30 patterns, only the ones also in common with the BC6H format.
// The packed ASTC weight grid dimensions are currently always 4x4 texels, but may be also 3x3 in the future.
// This function is thread safe, i.e. it may be called from multiple encoding threads simultanously with different blocks.
//
// Parameters:
// pRGBPixels - An array of 48 (16 RGB) floats: the 4x4 block to pack
// pPacked_block - A pointer to the packed ASTC HDR block
// coptions - Codec options
// pInternal_results - An optional pointer to details about how the block was packed, for statistics/debugging purposes. May be nullptr.
//
// Requirements:
// astc_hdr_enc_init() MUST have been called first to initialized the codec.
// Input pixels are checked and cannot be NaN's, Inf's, signed, or too large (greater than MAX_HALF_FLOAT, or 65504).
// Normal values and denormals are okay.
bool astc_hdr_enc_block(
const float* pRGBPixels,
const astc_hdr_codec_options& coptions,
basisu::vector<astc_hdr_pack_results> &all_results);
bool astc_hdr_pack_results_to_block(basist::astc_blk& dst_blk, const astc_hdr_pack_results& results);
bool astc_hdr_refine_weights(const basist::half_float* pSource_block, astc_hdr_pack_results& cur_results, const astc_hdr_codec_options& coptions, float bc6h_weight, bool* pImproved_flag);
struct astc_hdr_block_stats
{
std::mutex m_mutex;
uint32_t m_total_blocks;
uint32_t m_total_2part, m_total_solid;
uint32_t m_total_mode7_1part, m_total_mode7_2part;
uint32_t m_total_mode11_1part, m_total_mode11_2part;
uint32_t m_total_mode11_1part_constrained_weights;
uint32_t m_weight_range_hist_7[11];
uint32_t m_weight_range_hist_7_2part[11];
uint32_t m_mode7_submode_hist[6];
uint32_t m_weight_range_hist_11[11];
uint32_t m_weight_range_hist_11_2part[11];
uint32_t m_mode11_submode_hist[9];
uint32_t m_part_hist[32];
uint32_t m_total_refined;
astc_hdr_block_stats() { clear(); }
void clear()
{
std::lock_guard<std::mutex> lck(m_mutex);
m_total_blocks = 0;
m_total_mode7_1part = 0, m_total_mode7_2part = 0, m_total_mode11_1part = 0, m_total_2part = 0, m_total_solid = 0, m_total_mode11_2part = 0;
m_total_mode11_1part_constrained_weights = 0;
m_total_refined = 0;
clear_obj(m_weight_range_hist_11);
clear_obj(m_weight_range_hist_11_2part);
clear_obj(m_weight_range_hist_7);
clear_obj(m_weight_range_hist_7_2part);
clear_obj(m_mode7_submode_hist);
clear_obj(m_mode11_submode_hist);
clear_obj(m_part_hist);
}
void update(const astc_hdr_pack_results& log_blk);
void print();
};
} // namespace basisu

View file

@ -1,5 +1,5 @@
// basisu_backend.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_backend.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_basis_file.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_basis_file.h
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// File: basisu_bc7enc.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -394,6 +394,7 @@ void bc7enc_compress_block_init()
static void compute_least_squares_endpoints_rgba(uint32_t N, const uint8_t *pSelectors, const bc7enc_vec4F* pSelector_weights, bc7enc_vec4F* pXl, bc7enc_vec4F* pXh, const color_quad_u8 *pColors)
{
// Least squares using normal equations: http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf
// https://web.archive.org/web/20150319232457/http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf
// I did this in matrix form first, expanded out all the ops, then optimized it a bit.
double z00 = 0.0f, z01 = 0.0f, z10 = 0.0f, z11 = 0.0f;
double q00_r = 0.0f, q10_r = 0.0f, t_r = 0.0f;
@ -1301,6 +1302,7 @@ void check_best_overall_error(const color_cell_compressor_params *pParams, color
for (uint32_t c = 0; c < 4; c++)
colors[i].m_c[c] = (uint8_t)astc_interpolate_linear(colors[0].m_c[c], colors[n - 1].m_c[c], pParams->m_pSelector_weights[i]);
#ifdef _DEBUG
uint64_t total_err = 0;
for (uint32_t p = 0; p < pParams->m_num_pixels; p++)
{
@ -1313,6 +1315,7 @@ void check_best_overall_error(const color_cell_compressor_params *pParams, color
total_err += compute_color_distance_rgb(&orig, &packed, pParams->m_perceptual, pParams->m_weights);
}
assert(total_err == pResults->m_best_overall_err);
#endif
// HACK HACK
//if (total_err != pResults->m_best_overall_err)

View file

@ -1,5 +1,5 @@
// File: basisu_bc7enc.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
// basisu_comp.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -18,9 +18,10 @@
#include "basisu_basis_file.h"
#include "../transcoder/basisu_transcoder.h"
#include "basisu_uastc_enc.h"
#include "basisu_astc_hdr_enc.h"
#define BASISU_LIB_VERSION 116
#define BASISU_LIB_VERSION_STRING "1.16"
#define BASISU_LIB_VERSION 150
#define BASISU_LIB_VERSION_STRING "1.50"
#ifndef BASISD_SUPPORT_KTX2
#error BASISD_SUPPORT_KTX2 is undefined
@ -81,6 +82,8 @@ namespace basisu
m_basis_luma_601_psnr = 0.0f;
m_basis_luma_709_ssim = 0.0f;
m_basis_rgb_avg_bc6h_psnr = 0.0f;
m_bc7_rgb_avg_psnr = 0.0f;
m_bc7_rgba_avg_psnr = 0.0f;
m_bc7_a_avg_psnr = 0.0f;
@ -100,7 +103,7 @@ namespace basisu
uint32_t m_width;
uint32_t m_height;
// .basis compressed (ETC1S or UASTC statistics)
// .basis/.ktx2 compressed (LDR: ETC1S or UASTC statistics, HDR: transcoded BC6H statistics)
float m_basis_rgb_avg_psnr;
float m_basis_rgba_avg_psnr;
float m_basis_a_avg_psnr;
@ -108,7 +111,10 @@ namespace basisu
float m_basis_luma_601_psnr;
float m_basis_luma_709_ssim;
// BC7 statistics
// UASTC HDR only.
float m_basis_rgb_avg_bc6h_psnr;
// LDR: BC7 statistics
float m_bc7_rgb_avg_psnr;
float m_bc7_rgba_avg_psnr;
float m_bc7_a_avg_psnr;
@ -116,7 +122,7 @@ namespace basisu
float m_bc7_luma_601_psnr;
float m_bc7_luma_709_ssim;
// Highest achievable quality ETC1S statistics
// LDR: Highest achievable quality ETC1S statistics
float m_best_etc1s_rgb_avg_psnr;
float m_best_etc1s_luma_709_psnr;
float m_best_etc1s_luma_601_psnr;
@ -256,7 +262,7 @@ namespace basisu
m_no_selector_rdo.clear();
m_selector_rdo_thresh.clear();
m_read_source_images.clear();
m_write_output_basis_files.clear();
m_write_output_basis_or_ktx2_files.clear();
m_compression_level.clear();
m_compute_stats.clear();
m_print_stats.clear();
@ -317,27 +323,38 @@ namespace basisu
m_validate_output_data.clear();
m_hdr_ldr_srgb_to_linear_conversion.clear();
m_hdr_favor_astc.clear();
m_pJob_pool = nullptr;
}
// True to generate UASTC .basis file data, otherwise ETC1S.
// True to generate UASTC .basis/.KTX2 file data, otherwise ETC1S.
bool_param<false> m_uastc;
// Set m_hdr to true to switch to UASTC HDR mode.
bool_param<false> m_hdr;
bool_param<false> m_use_opencl;
// If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG images to read.
// Otherwise, the compressor processes the images in m_source_images.
// If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG etc. images to read.
// Otherwise, the compressor processes the images in m_source_images or m_source_images_hdr.
basisu::vector<std::string> m_source_filenames;
basisu::vector<std::string> m_source_alpha_filenames;
basisu::vector<image> m_source_images;
basisu::vector<imagef> m_source_images_hdr;
// Stores mipmaps starting from level 1. Level 0 is still stored in m_source_images, as usual.
// If m_source_mipmaps isn't empty, automatic mipmap generation isn't done. m_source_mipmaps.size() MUST equal m_source_images.size() or the compressor returns an error.
// The compressor applies the user-provided swizzling (in m_swizzle) to these images.
basisu::vector< basisu::vector<image> > m_source_mipmap_images;
basisu::vector< basisu::vector<imagef> > m_source_mipmap_images_hdr;
// Filename of the output basis file
// Filename of the output basis/ktx2 file
std::string m_out_filename;
// The params are done this way so we can detect when the user has explictly changed them.
@ -373,8 +390,8 @@ namespace basisu
// Read source images from m_source_filenames/m_source_alpha_filenames
bool_param<false> m_read_source_images;
// Write the output basis file to disk using m_out_filename
bool_param<false> m_write_output_basis_files;
// Write the output basis/ktx2 file to disk using m_out_filename
bool_param<false> m_write_output_basis_or_ktx2_files;
// Compute and display image metrics
bool_param<false> m_compute_stats;
@ -382,15 +399,15 @@ namespace basisu
// Print stats to stdout, if m_compute_stats is true.
bool_param<true> m_print_stats;
// Check to see if any input image has an alpha channel, if so then the output basis file will have alpha channels
// Check to see if any input image has an alpha channel, if so then the output basis/ktx2 file will have alpha channels
bool_param<true> m_check_for_alpha;
// Always put alpha slices in the output basis file, even when the input doesn't have alpha
// Always put alpha slices in the output basis/ktx2 file, even when the input doesn't have alpha
bool_param<false> m_force_alpha;
bool_param<true> m_multithreading;
// Split the R channel to RGB and the G channel to alpha, then write a basis file with alpha channels
char m_swizzle[4];
// Split the R channel to RGB and the G channel to alpha, then write a basis/ktx2 file with alpha channels
uint8_t m_swizzle[4];
bool_param<false> m_renormalize;
@ -448,8 +465,17 @@ namespace basisu
param<int> m_ktx2_zstd_supercompression_level;
bool_param<false> m_ktx2_srgb_transfer_func;
astc_hdr_codec_options m_uastc_hdr_options;
bool_param<false> m_validate_output_data;
// If true, LDR images (such as PNG) will be converted to normalized [0,1] linear light (via a sRGB->Linear conversion) and then processed as HDR.
// Otherwise, LDR images will be processed as HDR as-is.
bool_param<true> m_hdr_ldr_srgb_to_linear_conversion;
// If true, ASTC HDR quality is favored more than BC6H quality. Otherwise it's a rough balance.
bool_param<false> m_hdr_favor_astc;
job_pool *m_pJob_pool;
};
@ -504,6 +530,7 @@ namespace basisu
opencl_context_ptr m_pOpenCL_context;
basisu::vector<image> m_slice_images;
basisu::vector<imagef> m_slice_images_hdr;
basisu::vector<image_stats> m_stats;
@ -515,7 +542,9 @@ namespace basisu
uint32_t m_total_blocks;
basisu_frontend m_frontend;
pixel_block_vec m_source_blocks;
pixel_block_hdr_vec m_source_blocks_hdr;
basisu::vector<gpu_image> m_frontend_output_textures;
@ -526,11 +555,17 @@ namespace basisu
basisu_file m_basis_file;
basisu::vector<gpu_image> m_decoded_output_textures;
basisu::vector<gpu_image> m_decoded_output_textures; // BC6H in HDR mode
basisu::vector<image> m_decoded_output_textures_unpacked;
basisu::vector<gpu_image> m_decoded_output_textures_bc7;
basisu::vector<image> m_decoded_output_textures_unpacked_bc7;
basisu::vector<imagef> m_decoded_output_textures_bc6h_hdr_unpacked; // BC6H in HDR mode
basisu::vector<gpu_image> m_decoded_output_textures_astc_hdr;
basisu::vector<imagef> m_decoded_output_textures_astc_hdr_unpacked;
uint8_vec m_output_basis_file;
uint8_vec m_output_ktx2_file;
@ -541,14 +576,21 @@ namespace basisu
bool m_opencl_failed;
void check_for_hdr_inputs();
bool sanity_check_input_params();
void clean_hdr_image(imagef& src_img);
bool read_dds_source_images();
bool read_source_images();
bool extract_source_blocks();
bool process_frontend();
bool extract_frontend_texture_data();
bool process_backend();
bool create_basis_file_and_transcode();
bool write_hdr_debug_images(const char* pBasename, const imagef& img, uint32_t width, uint32_t height);
bool write_output_files_and_compute_stats();
error_code encode_slices_to_uastc_hdr();
error_code encode_slices_to_uastc();
bool generate_mipmaps(const imagef& img, basisu::vector<imagef>& mips, bool has_alpha);
bool generate_mipmaps(const image &img, basisu::vector<image> &mips, bool has_alpha);
bool validate_texture_type_constraints();
bool validate_ktx2_constraints();
@ -568,7 +610,8 @@ namespace basisu
//
// flags_and_quality: Combination of the above flags logically OR'd with the ETC1S or UASTC level, i.e. "cFlagSRGB | cFlagGenMipsClamp | cFlagThreaded | 128" or "cFlagSRGB | cFlagGenMipsClamp | cFlagUASTC | cFlagThreaded | cPackUASTCLevelDefault".
// In ETC1S mode, the lower 8-bits are the ETC1S quality level which ranges from [1,255] (higher=better quality/larger files)
// In UASTC mode, the lower 8-bits are the UASTC pack level (see cPackUASTCLevelFastest, etc.). Fastest/lowest quality is 0, so be sure to set it correctly.
// In UASTC mode, the lower 8-bits are the UASTC LDR/HDR pack level (see cPackUASTCLevelFastest, etc.). Fastest/lowest quality is 0, so be sure to set it correctly. Valid values are [0,4] for both LDR/HDR.
// In UASTC mode, be sure to set this, otherwise it defaults to 0 (fastest/lowest quality).
//
// uastc_rdo_quality: Float UASTC RDO quality level (0=no change, higher values lower quality but increase compressibility, initially try .5-1.5)
//
@ -594,20 +637,36 @@ namespace basisu
cFlagUASTCRDO = 1 << 18, // use RDO postprocessing when generating UASTC files (must set uastc_rdo_quality to the quality scalar)
cFlagPrintStats = 1 << 19, // print image stats to stdout
cFlagPrintStatus = 1 << 20 // print status to stdout
cFlagPrintStatus = 1 << 20, // print status to stdout
cFlagHDR = 1 << 21, // Force encoder into HDR mode, even if source image is LDR.
cFlagHDRLDRImageSRGBToLinearConversion = 1 << 22, // In HDR mode, convert LDR source images to linear before encoding.
cFlagDebugImages = 1 << 23 // enable status output
};
// This function accepts an array of source images.
// If more than one image is provided, it's assumed the images form a mipmap pyramid and automatic mipmap generation is disabled.
// Returns a pointer to the compressed .basis or .ktx2 file data. *pSize is the size of the compressed data. The returned block must be freed using basis_free_data().
// Returns a pointer to the compressed .basis or .ktx2 file data. *pSize is the size of the compressed data.
// Important: The returned block MUST be manually freed using basis_free_data().
// basisu_encoder_init() MUST be called first!
// LDR version. To compress the LDR source image as HDR: Use the cFlagHDR flag.
void* basis_compress(
const basisu::vector<image> &source_images,
uint32_t flags_and_quality, float uastc_rdo_quality,
size_t* pSize,
image_stats* pStats = nullptr);
// This function only accepts a single source image.
// HDR-only version.
// Important: The returned block MUST be manually freed using basis_free_data().
void* basis_compress(
const basisu::vector<imagef>& source_images_hdr,
uint32_t flags_and_quality,
size_t* pSize,
image_stats* pStats = nullptr);
// This function only accepts a single LDR source image. It's just a wrapper for basis_compress() above.
// Important: The returned block MUST be manually freed using basis_free_data().
void* basis_compress(
const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
uint32_t flags_and_quality, float uastc_rdo_quality,
@ -615,6 +674,7 @@ namespace basisu
image_stats* pStats = nullptr);
// Frees the dynamically allocated file data returned by basis_compress().
// This MUST be called on the pointer returned by basis_compress() when you're done with it.
void basis_free_data(void* p);
// Runs a short benchmark using synthetic image data to time OpenCL encoding vs. CPU encoding, with multithreading enabled.

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
// basisu_enc.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -48,7 +48,8 @@ namespace basisu
// Encoder library initialization.
// This function MUST be called before encoding anything!
void basisu_encoder_init(bool use_opencl = false, bool opencl_force_serialization = false);
// Returns false if library initialization fails.
bool basisu_encoder_init(bool use_opencl = false, bool opencl_force_serialization = false);
void basisu_encoder_deinit();
// basisu_kernels_sse.cpp - will be a no-op and g_cpu_supports_sse41 will always be false unless compiled with BASISU_SUPPORT_SSE=1
@ -70,6 +71,18 @@ namespace basisu
return (uint8_t)((i & 0xFFFFFF00U) ? (~(i >> 31)) : i);
}
inline int left_shift32(int val, int shift)
{
assert((shift >= 0) && (shift < 32));
return static_cast<int>(static_cast<uint32_t>(val) << shift);
}
inline uint32_t left_shift32(uint32_t val, int shift)
{
assert((shift >= 0) && (shift < 32));
return val << shift;
}
inline int32_t clampi(int32_t value, int32_t low, int32_t high)
{
if (value < low)
@ -130,6 +143,31 @@ namespace basisu
return bits;
}
// Open interval
inline int bounds_check(int v, int l, int h) { (void)v; (void)l; (void)h; assert(v >= l && v < h); return v; }
inline uint32_t bounds_check(uint32_t v, uint32_t l, uint32_t h) { (void)v; (void)l; (void)h; assert(v >= l && v < h); return v; }
// Closed interval
inline int bounds_check_incl(int v, int l, int h) { (void)v; (void)l; (void)h; assert(v >= l && v <= h); return v; }
inline uint32_t bounds_check_incl(uint32_t v, uint32_t l, uint32_t h) { (void)v; (void)l; (void)h; assert(v >= l && v <= h); return v; }
inline uint32_t clz(uint32_t x)
{
if (!x)
return 32;
uint32_t n = 0;
while ((x & 0x80000000) == 0)
{
x <<= 1u;
n++;
}
return n;
}
bool string_begins_with(const std::string& str, const char* pPhrase);
// Hashing
@ -268,6 +306,7 @@ namespace basisu
public:
enum { num_elements = N };
typedef T scalar_type;
inline vec() { }
inline vec(eZero) { set_zero(); }
@ -291,6 +330,7 @@ namespace basisu
inline bool operator<(const vec &rhs) const { for (uint32_t i = 0; i < N; i++) { if (m_v[i] < rhs.m_v[i]) return true; else if (m_v[i] != rhs.m_v[i]) return false; } return false; }
inline void set_zero() { for (uint32_t i = 0; i < N; i++) m_v[i] = 0; }
inline void clear() { set_zero(); }
template <uint32_t OtherN, typename OtherT>
inline vec &set(const vec<OtherN, OtherT> &other)
@ -391,7 +431,7 @@ namespace basisu
inline T distance(const vec &other) const { return static_cast<T>(sqrt(squared_distance(other))); }
inline double distance_d(const vec& other) const { return sqrt(squared_distance_d(other)); }
inline vec &normalize_in_place() { T len = length(); if (len != 0.0f) *this *= (1.0f / len); return *this; }
inline vec &normalize_in_place() { T len = length(); if (len != 0.0f) *this *= (1.0f / len); return *this; }
inline vec &clamp(T l, T h)
{
@ -722,7 +762,7 @@ namespace basisu
void job_thread(uint32_t index);
};
// Simple 32-bit color class
// Simple 64-bit color class
class color_rgba_i16
{
@ -1116,7 +1156,9 @@ namespace basisu
{
std::string result(s);
for (size_t i = 0; i < result.size(); i++)
result[i] = (char)tolower((int)result[i]);
{
result[i] = (char)tolower((uint8_t)(result[i]));
}
return result;
}
@ -1408,7 +1450,7 @@ namespace basisu
size_t get_total_training_vecs() const { return m_training_vecs.size(); }
const array_of_weighted_training_vecs &get_training_vecs() const { return m_training_vecs; }
array_of_weighted_training_vecs &get_training_vecs() { return m_training_vecs; }
array_of_weighted_training_vecs &get_training_vecs() { return m_training_vecs; }
void retrieve(basisu::vector< basisu::vector<uint32_t> > &codebook) const
{
@ -1437,36 +1479,36 @@ namespace basisu
}
void retrieve(uint32_t max_clusters, basisu::vector<uint_vec> &codebook) const
{
{
uint_vec node_stack;
node_stack.reserve(512);
node_stack.reserve(512);
codebook.resize(0);
codebook.reserve(max_clusters);
codebook.resize(0);
codebook.reserve(max_clusters);
uint32_t node_index = 0;
uint32_t node_index = 0;
while (true)
{
const tsvq_node& cur = m_nodes[node_index];
while (true)
{
const tsvq_node& cur = m_nodes[node_index];
if (cur.is_leaf() || ((2 + cur.m_codebook_index) > (int)max_clusters))
{
codebook.resize(codebook.size() + 1);
codebook.back() = cur.m_training_vecs;
if (cur.is_leaf() || ((2 + cur.m_codebook_index) > (int)max_clusters))
{
codebook.resize(codebook.size() + 1);
codebook.back() = cur.m_training_vecs;
if (node_stack.empty())
break;
if (node_stack.empty())
break;
node_index = node_stack.back();
node_stack.pop_back();
continue;
}
node_index = node_stack.back();
node_stack.pop_back();
continue;
}
node_stack.push_back(cur.m_right_index);
node_index = cur.m_left_index;
}
}
node_stack.push_back(cur.m_right_index);
node_index = cur.m_left_index;
}
}
bool generate(uint32_t max_size)
{
@ -2319,6 +2361,14 @@ namespace basisu
m_total_bits = 0;
}
inline void restart()
{
m_bytes.resize(0);
m_bit_buffer = 0;
m_bit_buffer_size = 0;
m_total_bits = 0;
}
inline const uint8_vec &get_bytes() const { return m_bytes; }
inline uint64_t get_total_bits() const { return m_total_bits; }
@ -2920,11 +2970,11 @@ namespace basisu
inline const color_rgba *get_ptr() const { return &m_pixels[0]; }
inline color_rgba *get_ptr() { return &m_pixels[0]; }
bool has_alpha() const
bool has_alpha(uint32_t channel = 3) const
{
for (uint32_t y = 0; y < m_height; ++y)
for (uint32_t x = 0; x < m_width; ++x)
if ((*this)(x, y).a < 255)
if ((*this)(x, y)[channel] < 255)
return true;
return false;
@ -3130,6 +3180,31 @@ namespace basisu
return *this;
}
imagef& crop_dup_borders(uint32_t w, uint32_t h)
{
const uint32_t orig_w = m_width, orig_h = m_height;
crop(w, h);
if (orig_w && orig_h)
{
if (m_width > orig_w)
{
for (uint32_t x = orig_w; x < m_width; x++)
for (uint32_t y = 0; y < m_height; y++)
set_clipped(x, y, get_clamped(minimum(x, orig_w - 1U), minimum(y, orig_h - 1U)));
}
if (m_height > orig_h)
{
for (uint32_t y = orig_h; y < m_height; y++)
for (uint32_t x = 0; x < m_width; x++)
set_clipped(x, y, get_clamped(minimum(x, orig_w - 1U), minimum(y, orig_h - 1U)));
}
}
return *this;
}
inline const vec4F &operator() (uint32_t x, uint32_t y) const { assert(x < m_width && y < m_height); return m_pixels[x + y * m_pitch]; }
inline vec4F &operator() (uint32_t x, uint32_t y) { assert(x < m_width && y < m_height); return m_pixels[x + y * m_pitch]; }
@ -3213,19 +3288,128 @@ namespace basisu
inline const vec4F *get_ptr() const { return &m_pixels[0]; }
inline vec4F *get_ptr() { return &m_pixels[0]; }
bool clean_astc_hdr_pixels(float highest_mag)
{
bool status = true;
bool nan_msg = false;
bool inf_msg = false;
bool neg_zero_msg = false;
bool neg_msg = false;
bool clamp_msg = false;
for (uint32_t iy = 0; iy < m_height; iy++)
{
for (uint32_t ix = 0; ix < m_width; ix++)
{
vec4F& c = (*this)(ix, iy);
for (uint32_t s = 0; s < 4; s++)
{
float &p = c[s];
union { float f; uint32_t u; } x; x.f = p;
if ((std::isnan(p)) || (std::isinf(p)) || (x.u == 0x80000000))
{
if (std::isnan(p))
{
if (!nan_msg)
{
fprintf(stderr, "One or more pixels was NaN, setting to 0.\n");
nan_msg = true;
}
}
if (std::isinf(p))
{
if (!inf_msg)
{
fprintf(stderr, "One or more pixels was INF, setting to 0.\n");
inf_msg = true;
}
}
if (x.u == 0x80000000)
{
if (!neg_zero_msg)
{
fprintf(stderr, "One or more pixels was -0, setting them to 0.\n");
neg_zero_msg = true;
}
}
p = 0.0f;
status = false;
}
else
{
//const float o = p;
if (p < 0.0f)
{
p = 0.0f;
if (!neg_msg)
{
fprintf(stderr, "One or more pixels was negative -- setting these pixel components to 0 because ASTC HDR doesn't support signed values.\n");
neg_msg = true;
}
status = false;
}
if (p > highest_mag)
{
p = highest_mag;
if (!clamp_msg)
{
fprintf(stderr, "One or more pixels had to be clamped to %f.\n", highest_mag);
clamp_msg = true;
}
status = false;
}
}
}
}
}
return status;
}
imagef& flip_y()
{
for (uint32_t y = 0; y < m_height / 2; ++y)
for (uint32_t x = 0; x < m_width; ++x)
std::swap((*this)(x, y), (*this)(x, m_height - 1 - y));
return *this;
}
private:
uint32_t m_width, m_height, m_pitch; // all in pixels
vec4F_vec m_pixels;
};
// REC 709 coefficients
const float REC_709_R = 0.212656f, REC_709_G = 0.715158f, REC_709_B = 0.072186f;
inline float get_luminance(const vec4F &c)
{
return c[0] * REC_709_R + c[1] * REC_709_G + c[2] * REC_709_B;
}
float linear_to_srgb(float l);
float srgb_to_linear(float s);
// Image metrics
class image_metrics
{
public:
// TODO: Add ssim
float m_max, m_mean, m_mean_squared, m_rms, m_psnr, m_ssim;
double m_max, m_mean, m_mean_squared, m_rms, m_psnr, m_ssim;
bool m_has_neg, m_hf_mag_overflow, m_any_abnormal;
image_metrics()
{
@ -3240,10 +3424,17 @@ namespace basisu
m_rms = 0;
m_psnr = 0;
m_ssim = 0;
m_has_neg = false;
m_hf_mag_overflow = false;
m_any_abnormal = false;
}
void print(const char *pPrefix = nullptr) { printf("%sMax: %3.0f Mean: %3.3f RMS: %3.3f PSNR: %2.3f dB\n", pPrefix ? pPrefix : "", m_max, m_mean, m_rms, m_psnr); }
void print(const char *pPrefix = nullptr) { printf("%sMax: %3.3f Mean: %3.3f RMS: %3.3f PSNR: %2.3f dB\n", pPrefix ? pPrefix : "", m_max, m_mean, m_rms, m_psnr); }
void print_hp(const char* pPrefix = nullptr) { printf("%sMax: %3.6f Mean: %3.6f RMS: %3.6f PSNR: %2.6f dB, Any Neg: %u, Half float overflow: %u, Any NaN/Inf: %u\n", pPrefix ? pPrefix : "", m_max, m_mean, m_rms, m_psnr, m_has_neg, m_hf_mag_overflow, m_any_abnormal); }
void calc(const imagef& a, const imagef& b, uint32_t first_chan = 0, uint32_t total_chans = 0, bool avg_comp_error = true, bool log = false);
void calc_half(const imagef& a, const imagef& b, uint32_t first_chan, uint32_t total_chans, bool avg_comp_error);
void calc_half2(const imagef& a, const imagef& b, uint32_t first_chan, uint32_t total_chans, bool avg_comp_error);
void calc(const image &a, const image &b, uint32_t first_chan = 0, uint32_t total_chans = 0, bool avg_comp_error = true, bool use_601_luma = false);
};
@ -3256,6 +3447,8 @@ namespace basisu
bool load_tga(const char* pFilename, image& img);
inline bool load_tga(const std::string &filename, image &img) { return load_tga(filename.c_str(), img); }
bool load_qoi(const char* pFilename, image& img);
bool load_jpg(const char *pFilename, image& img);
inline bool load_jpg(const std::string &filename, image &img) { return load_jpg(filename.c_str(), img); }
@ -3263,9 +3456,64 @@ namespace basisu
bool load_image(const char* pFilename, image& img);
inline bool load_image(const std::string &filename, image &img) { return load_image(filename.c_str(), img); }
// Supports .HDR and most (but not all) .EXR's (see TinyEXR).
bool load_image_hdr(const char* pFilename, imagef& img, bool ldr_srgb_to_linear = true);
inline bool load_image_hdr(const std::string& filename, imagef& img, bool ldr_srgb_to_linear = true) { return load_image_hdr(filename.c_str(), img, ldr_srgb_to_linear); }
enum class hdr_image_type
{
cHITRGBAHalfFloat = 0,
cHITRGBAFloat = 1,
cHITPNGImage = 2,
cHITEXRImage = 3,
cHITHDRImage = 4
};
bool load_image_hdr(const void* pMem, size_t mem_size, imagef& img, uint32_t width, uint32_t height, hdr_image_type img_type, bool ldr_srgb_to_linear);
uint8_t *read_tga(const uint8_t *pBuf, uint32_t buf_size, int &width, int &height, int &n_chans);
uint8_t *read_tga(const char *pFilename, int &width, int &height, int &n_chans);
struct rgbe_header_info
{
std::string m_program;
// Note no validation is done, either gamma or exposure may be 0.
double m_gamma;
bool m_has_gamma;
double m_exposure; // watts/steradian/m^2.
bool m_has_exposure;
void clear()
{
m_program.clear();
m_gamma = 1.0f;
m_has_gamma = false;
m_exposure = 1.0f;
m_has_exposure = false;
}
};
bool read_rgbe(const uint8_vec& filedata, imagef& img, rgbe_header_info& hdr_info);
bool read_rgbe(const char* pFilename, imagef& img, rgbe_header_info &hdr_info);
bool write_rgbe(uint8_vec& file_data, imagef& img, rgbe_header_info& hdr_info);
bool write_rgbe(const char* pFilename, imagef& img, rgbe_header_info& hdr_info);
bool read_exr(const char* pFilename, imagef& img, int& n_chans);
bool read_exr(const void* pMem, size_t mem_size, imagef& img);
enum
{
WRITE_EXR_LINEAR_HINT = 1, // hint for lossy comp. methods: exr_perceptual_treatment_t, logarithmic or linear, defaults to logarithmic
WRITE_EXR_STORE_FLOATS = 2, // use 32-bit floats, otherwise it uses half floats
WRITE_EXR_NO_COMPRESSION = 4 // no compression, otherwise it uses ZIP compression (16 scanlines per block)
};
// Supports 1 (Y), 3 (RGB), or 4 (RGBA) channel images.
bool write_exr(const char* pFilename, imagef& img, uint32_t n_chans, uint32_t flags);
enum
{
cImageSaveGrayscale = 1,
@ -3276,19 +3524,22 @@ namespace basisu
inline bool save_png(const std::string &filename, const image &img, uint32_t image_save_flags = 0, uint32_t grayscale_comp = 0) { return save_png(filename.c_str(), img, image_save_flags, grayscale_comp); }
bool read_file_to_vec(const char* pFilename, uint8_vec& data);
bool read_file_to_data(const char* pFilename, void *pData, size_t len);
bool write_data_to_file(const char* pFilename, const void* pData, size_t len);
inline bool write_vec_to_file(const char* pFilename, const uint8_vec& v) { return v.size() ? write_data_to_file(pFilename, &v[0], v.size()) : write_data_to_file(pFilename, "", 0); }
float linear_to_srgb(float l);
float srgb_to_linear(float s);
bool image_resample(const image &src, image &dst, bool srgb = false,
const char *pFilter = "lanczos4", float filter_scale = 1.0f,
bool wrapping = false,
uint32_t first_comp = 0, uint32_t num_comps = 4);
bool image_resample(const imagef& src, imagef& dst,
const char* pFilter = "lanczos4", float filter_scale = 1.0f,
bool wrapping = false,
uint32_t first_comp = 0, uint32_t num_comps = 4);
// Timing
typedef uint64_t timer_ticks;
@ -3319,6 +3570,8 @@ namespace basisu
bool m_started, m_stopped;
};
inline double get_interval_timer() { return interval_timer::ticks_to_secs(interval_timer::get_ticks()); }
// 2D array
template<typename T>
@ -3372,8 +3625,8 @@ namespace basisu
inline const T &operator[] (uint32_t i) const { return m_values[i]; }
inline T &operator[] (uint32_t i) { return m_values[i]; }
inline const T &at_clamped(int x, int y) const { return (*this)(clamp<int>(x, 0, m_width), clamp<int>(y, 0, m_height)); }
inline T &at_clamped(int x, int y) { return (*this)(clamp<int>(x, 0, m_width), clamp<int>(y, 0, m_height)); }
inline const T &at_clamped(int x, int y) const { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); }
inline T &at_clamped(int x, int y) { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); }
void clear()
{
@ -3450,7 +3703,327 @@ namespace basisu
}
};
typedef basisu::vector<pixel_block> pixel_block_vec;
struct pixel_block_hdr
{
vec4F m_pixels[cPixelBlockHeight][cPixelBlockWidth]; // [y][x]
inline const vec4F& operator() (uint32_t x, uint32_t y) const { assert((x < cPixelBlockWidth) && (y < cPixelBlockHeight)); return m_pixels[y][x]; }
inline vec4F& operator() (uint32_t x, uint32_t y) { assert((x < cPixelBlockWidth) && (y < cPixelBlockHeight)); return m_pixels[y][x]; }
inline const vec4F* get_ptr() const { return &m_pixels[0][0]; }
inline vec4F* get_ptr() { return &m_pixels[0][0]; }
inline void clear() { clear_obj(*this); }
inline bool operator== (const pixel_block& rhs) const
{
return memcmp(m_pixels, rhs.m_pixels, sizeof(m_pixels)) == 0;
}
};
typedef basisu::vector<pixel_block_hdr> pixel_block_hdr_vec;
void tonemap_image_reinhard(image& ldr_img, const imagef& hdr_img, float exposure);
bool tonemap_image_compressive(image& dst_img, const imagef& hdr_test_img);
// Intersection
enum eClear { cClear = 0 };
enum eInitExpand { cInitExpand = 0 };
template<typename vector_type>
class ray
{
public:
typedef vector_type vector_t;
typedef typename vector_type::scalar_type scalar_type;
inline ray() { }
inline ray(eClear) { clear(); }
inline ray(const vector_type& origin, const vector_type& direction) : m_origin(origin), m_direction(direction) { }
inline void clear()
{
m_origin.clear();
m_direction.clear();
}
inline const vector_type& get_origin(void) const { return m_origin; }
inline void set_origin(const vector_type& origin) { m_origin = origin; }
inline const vector_type& get_direction(void) const { return m_direction; }
inline void set_direction(const vector_type& direction) { m_direction = direction; }
inline void set_endpoints(const vector_type& start, const vector_type& end)
{
m_origin = start;
m_direction = end - start;
m_direction.normalize_in_place();
}
inline vector_type eval(scalar_type t) const
{
return m_origin + m_direction * t;
}
private:
vector_type m_origin;
vector_type m_direction;
};
typedef ray<vec2F> ray2F;
typedef ray<vec3F> ray3F;
template<typename T>
class vec_interval
{
public:
enum { N = T::num_elements };
typedef typename T::scalar_type scalar_type;
inline vec_interval(const T& v) { m_bounds[0] = v; m_bounds[1] = v; }
inline vec_interval(const T& low, const T& high) { m_bounds[0] = low; m_bounds[1] = high; }
inline vec_interval() { }
inline vec_interval(eClear) { clear(); }
inline vec_interval(eInitExpand) { init_expand(); }
inline void clear() { m_bounds[0].clear(); m_bounds[1].clear(); }
inline void init_expand()
{
m_bounds[0].set(1e+30f, 1e+30f, 1e+30f);
m_bounds[1].set(-1e+30f, -1e+30f, -1e+30f);
}
inline vec_interval expand(const T& p)
{
for (uint32_t c = 0; c < N; c++)
{
if (p[c] < m_bounds[0][c])
m_bounds[0][c] = p[c];
if (p[c] > m_bounds[1][c])
m_bounds[1][c] = p[c];
}
return *this;
}
inline const T& operator[] (uint32_t i) const { assert(i < 2); return m_bounds[i]; }
inline T& operator[] (uint32_t i) { assert(i < 2); return m_bounds[i]; }
const T& get_low() const { return m_bounds[0]; }
T& get_low() { return m_bounds[0]; }
const T& get_high() const { return m_bounds[1]; }
T& get_high() { return m_bounds[1]; }
scalar_type get_dim(uint32_t axis) const { return m_bounds[1][axis] - m_bounds[0][axis]; }
bool contains(const T& p) const
{
const T& low = get_low(), high = get_high();
for (uint32_t i = 0; i < N; i++)
{
if (p[i] < low[i])
return false;
if (p[i] > high[i])
return false;
}
return true;
}
private:
T m_bounds[2];
};
typedef vec_interval<vec1F> vec_interval1F;
typedef vec_interval<vec2F> vec_interval2F;
typedef vec_interval<vec3F> vec_interval3F;
typedef vec_interval<vec4F> vec_interval4F;
typedef vec_interval2F aabb2F;
typedef vec_interval3F aabb3F;
namespace intersection
{
enum result
{
cBackfacing = -1,
cFailure = 0,
cSuccess,
cParallel,
cInside,
};
// Returns cInside, cSuccess, or cFailure.
// Algorithm: Graphics Gems 1
template<typename vector_type, typename scalar_type, typename ray_type, typename aabb_type>
result ray_aabb(vector_type& coord, scalar_type& t, const ray_type& ray, const aabb_type& box)
{
enum
{
cNumDim = vector_type::num_elements,
cRight = 0,
cLeft = 1,
cMiddle = 2
};
bool inside = true;
int quadrant[cNumDim];
scalar_type candidate_plane[cNumDim];
for (int i = 0; i < cNumDim; i++)
{
if (ray.get_origin()[i] < box[0][i])
{
quadrant[i] = cLeft;
candidate_plane[i] = box[0][i];
inside = false;
}
else if (ray.get_origin()[i] > box[1][i])
{
quadrant[i] = cRight;
candidate_plane[i] = box[1][i];
inside = false;
}
else
{
quadrant[i] = cMiddle;
}
}
if (inside)
{
coord = ray.get_origin();
t = 0.0f;
return cInside;
}
scalar_type max_t[cNumDim];
for (int i = 0; i < cNumDim; i++)
{
if ((quadrant[i] != cMiddle) && (ray.get_direction()[i] != 0.0f))
max_t[i] = (candidate_plane[i] - ray.get_origin()[i]) / ray.get_direction()[i];
else
max_t[i] = -1.0f;
}
int which_plane = 0;
for (int i = 1; i < cNumDim; i++)
if (max_t[which_plane] < max_t[i])
which_plane = i;
if (max_t[which_plane] < 0.0f)
return cFailure;
for (int i = 0; i < cNumDim; i++)
{
if (i != which_plane)
{
coord[i] = ray.get_origin()[i] + max_t[which_plane] * ray.get_direction()[i];
if ((coord[i] < box[0][i]) || (coord[i] > box[1][i]))
return cFailure;
}
else
{
coord[i] = candidate_plane[i];
}
assert(coord[i] >= box[0][i] && coord[i] <= box[1][i]);
}
t = max_t[which_plane];
return cSuccess;
}
template<typename vector_type, typename scalar_type, typename ray_type, typename aabb_type>
result ray_aabb(bool& started_within, vector_type& coord, scalar_type& t, const ray_type& ray, const aabb_type& box)
{
if (!box.contains(ray.get_origin()))
{
started_within = false;
return ray_aabb(coord, t, ray, box);
}
started_within = true;
typename vector_type::T diag_dist = box.diagonal_length() * 1.5f;
ray_type outside_ray(ray.eval(diag_dist), -ray.get_direction());
result res(ray_aabb(coord, t, outside_ray, box));
if (res != cSuccess)
return res;
t = basisu::maximum(0.0f, diag_dist - t);
return cSuccess;
}
} // intersect
// This float->half conversion matches how "F32TO16" works on Intel GPU's.
// Input cannot be negative, Inf or Nan.
inline basist::half_float float_to_half_non_neg_no_nan_inf(float val)
{
union { float f; int32_t i; uint32_t u; } fi = { val };
const int flt_m = fi.i & 0x7FFFFF, flt_e = (fi.i >> 23) & 0xFF;
int e = 0, m = 0;
assert(((fi.i >> 31) == 0) && (flt_e != 0xFF));
// not zero or denormal
if (flt_e != 0)
{
int new_exp = flt_e - 127;
if (new_exp > 15)
e = 31;
else if (new_exp < -14)
m = lrintf((1 << 24) * fabsf(fi.f));
else
{
e = new_exp + 15;
m = lrintf(flt_m * (1.0f / ((float)(1 << 13))));
}
}
assert((0 <= m) && (m <= 1024));
if (m == 1024)
{
e++;
m = 0;
}
assert((e >= 0) && (e <= 31));
assert((m >= 0) && (m <= 1023));
basist::half_float result = (basist::half_float)((e << 10) | m);
return result;
}
// Supports positive and denormals only. No NaN or Inf.
inline float fast_half_to_float_pos_not_inf_or_nan(basist::half_float h)
{
assert(!basist::half_is_signed(h) && !basist::is_half_inf_or_nan(h));
union fu32
{
uint32_t u;
float f;
};
static const fu32 K = { 0x77800000 };
fu32 o;
o.u = h << 13;
o.f *= K.f;
return o.f;
}
} // namespace basisu

View file

@ -1,5 +1,5 @@
// basis_etc.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basis_etc.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_frontend.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -2347,6 +2347,7 @@ namespace basisu
continue;
uint64_t overall_best_err = 0;
(void)overall_best_err;
uint64_t total_err[4][4][4];
clear_obj(total_err);

View file

@ -1,5 +1,5 @@
// basisu_frontend.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_gpu_texture.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -15,13 +15,15 @@
#include "basisu_gpu_texture.h"
#include "basisu_enc.h"
#include "basisu_pvrtc1_4.h"
#if BASISU_USE_ASTC_DECOMPRESS
#include "basisu_astc_decomp.h"
#endif
#include "3rdparty/android_astc_decomp.h"
#include "basisu_bc7enc.h"
#include "../transcoder/basisu_astc_hdr_core.h"
namespace basisu
{
//------------------------------------------------------------------------------------------------
// ETC2 EAC
void unpack_etc2_eac(const void *pBlock_bits, color_rgba *pPixels)
{
static_assert(sizeof(eac_a8_block) == 8, "sizeof(eac_a8_block) == 8");
@ -56,6 +58,8 @@ namespace basisu
pPixels[15].a = clamp255(base + pTable[pBlock->get_selector(3, 3, selector_bits)] * mul);
}
//------------------------------------------------------------------------------------------------
// BC1
struct bc1_block
{
enum { cTotalEndpointBytes = 2, cTotalSelectorBytes = 4 };
@ -274,6 +278,9 @@ namespace basisu
return used_punchthrough;
}
//------------------------------------------------------------------------------------------------
// BC3-5
struct bc4_block
{
enum { cBC4SelectorBits = 3, cTotalSelectorBytes = 6, cMaxSelectorValues = 8 };
@ -372,7 +379,8 @@ namespace basisu
unpack_bc4(pBlock_bits, &pPixels[0].r, sizeof(color_rgba));
unpack_bc4((const uint8_t *)pBlock_bits + sizeof(bc4_block), &pPixels[0].g, sizeof(color_rgba));
}
//------------------------------------------------------------------------------------------------
// ATC isn't officially documented, so I'm assuming these references:
// http://www.guildsoftware.com/papers/2012.Converting.DXTC.to.ATC.pdf
// https://github.com/Triang3l/S3TConv/blob/master/s3tconv_atitc.c
@ -426,6 +434,7 @@ namespace basisu
}
}
//------------------------------------------------------------------------------------------------
// BC7 mode 0-7 decompression.
// Instead of one monster routine to unpack all the BC7 modes, we're lumping the 3 subset, 2 subset, 1 subset, and dual plane modes together into simple shared routines.
@ -742,6 +751,255 @@ namespace basisu
return false;
}
static inline int bc6h_sign_extend(int val, int bits)
{
assert((bits >= 1) && (bits < 32));
assert((val >= 0) && (val < (1 << bits)));
return (val << (32 - bits)) >> (32 - bits);
}
static inline int bc6h_apply_delta(int base, int delta, int num_bits, int is_signed)
{
int bitmask = ((1 << num_bits) - 1);
int v = (base + delta) & bitmask;
return is_signed ? bc6h_sign_extend(v, num_bits) : v;
}
static int bc6h_dequantize(int val, int bits, int is_signed)
{
int result;
if (is_signed)
{
if (bits >= 16)
result = val;
else
{
int s_flag = 0;
if (val < 0)
{
s_flag = 1;
val = -val;
}
if (val == 0)
result = 0;
else if (val >= ((1 << (bits - 1)) - 1))
result = 0x7FFF;
else
result = ((val << 15) + 0x4000) >> (bits - 1);
if (s_flag)
result = -result;
}
}
else
{
if (bits >= 15)
result = val;
else if (!val)
result = 0;
else if (val == ((1 << bits) - 1))
result = 0xFFFF;
else
result = ((val << 16) + 0x8000) >> bits;
}
return result;
}
static inline int bc6h_interpolate(int a, int b, const uint8_t* pWeights, int index)
{
return (a * (64 - (int)pWeights[index]) + b * (int)pWeights[index] + 32) >> 6;
}
static inline basist::half_float bc6h_convert_to_half(int val, int is_signed)
{
if (!is_signed)
{
// scale by 31/64
return (basist::half_float)((val * 31) >> 6);
}
// scale by 31/32
val = (val < 0) ? -(((-val) * 31) >> 5) : (val * 31) >> 5;
int s = 0;
if (val < 0)
{
s = 0x8000;
val = -val;
}
return (basist::half_float)(s | val);
}
static inline uint32_t bc6h_get_bits(uint32_t num_bits, uint64_t& l, uint64_t& h, uint32_t& total_bits)
{
assert((num_bits) && (num_bits <= 63));
uint32_t v = (uint32_t)(l & ((1U << num_bits) - 1U));
l >>= num_bits;
l |= (h << (64U - num_bits));
h >>= num_bits;
total_bits += num_bits;
assert(total_bits <= 128);
return v;
}
static inline uint32_t bc6h_reverse_bits(uint32_t v, uint32_t num_bits)
{
uint32_t res = 0;
for (uint32_t i = 0; i < num_bits; i++)
{
uint32_t bit = (v & (1u << i)) != 0u;
res |= (bit << (num_bits - 1u - i));
}
return res;
}
static inline uint64_t bc6h_read_le_qword(const void* p)
{
const uint8_t* pSrc = static_cast<const uint8_t*>(p);
return ((uint64_t)read_le_dword(pSrc)) | (((uint64_t)read_le_dword(pSrc + sizeof(uint32_t))) << 32U);
}
bool unpack_bc6h(const void* pSrc_block, void* pDst_block, bool is_signed, uint32_t dest_pitch_in_halfs)
{
assert(dest_pitch_in_halfs >= 4 * 3);
const uint32_t MAX_SUBSETS = 2, MAX_COMPS = 3;
const uint8_t* pSrc = static_cast<const uint8_t*>(pSrc_block);
basist::half_float* pDst = static_cast<basist::half_float*>(pDst_block);
uint64_t blo = bc6h_read_le_qword(pSrc), bhi = bc6h_read_le_qword(pSrc + sizeof(uint64_t));
// Unpack mode
const int mode = basist::g_bc6h_mode_lookup[blo & 31];
if (mode < 0)
{
for (int y = 0; y < 4; y++)
{
memset(pDst, 0, sizeof(basist::half_float) * 4);
pDst += dest_pitch_in_halfs;
}
return false;
}
// Skip mode bits
uint32_t total_bits_read = 0;
bc6h_get_bits((mode < 2) ? 2 : 5, blo, bhi, total_bits_read);
assert(mode < (int)basist::NUM_BC6H_MODES);
const uint32_t num_subsets = (mode >= 10) ? 1 : 2;
const bool is_mode_9_or_10 = (mode == 9) || (mode == 10);
// Unpack endpoint components
int comps[MAX_SUBSETS][MAX_COMPS][2] = { { { 0 } } }; // [subset][comp][l/h]
int part_index = 0;
uint32_t layout_index = 0;
while (layout_index < basist::MAX_BC6H_LAYOUT_INDEX)
{
const basist::bc6h_bit_layout& layout = basist::g_bc6h_bit_layouts[mode][layout_index];
if (layout.m_comp < 0)
break;
const int subset = layout.m_index >> 1, lh_index = layout.m_index & 1;
assert((layout.m_comp == 3) || ((subset >= 0) && (subset < (int)MAX_SUBSETS)));
const int last_bit = layout.m_last_bit, first_bit = layout.m_first_bit;
assert(last_bit >= 0);
int& res = (layout.m_comp == 3) ? part_index : comps[subset][layout.m_comp][lh_index];
if (first_bit < 0)
{
res |= (bc6h_get_bits(1, blo, bhi, total_bits_read) << last_bit);
}
else
{
const int total_bits = iabs(last_bit - first_bit) + 1;
const int bit_shift = basisu::minimum(first_bit, last_bit);
int b = bc6h_get_bits(total_bits, blo, bhi, total_bits_read);
if (last_bit < first_bit)
b = bc6h_reverse_bits(b, total_bits);
res |= (b << bit_shift);
}
layout_index++;
}
assert(layout_index != basist::MAX_BC6H_LAYOUT_INDEX);
// Sign extend/dequantize endpoints
const int num_sig_bits = basist::g_bc6h_mode_sig_bits[mode][0];
if (is_signed)
{
for (uint32_t comp = 0; comp < 3; comp++)
comps[0][comp][0] = bc6h_sign_extend(comps[0][comp][0], num_sig_bits);
}
if (is_signed || !is_mode_9_or_10)
{
for (uint32_t subset = 0; subset < num_subsets; subset++)
for (uint32_t comp = 0; comp < 3; comp++)
for (uint32_t lh = (subset ? 0 : 1); lh < 2; lh++)
comps[subset][comp][lh] = bc6h_sign_extend(comps[subset][comp][lh], basist::g_bc6h_mode_sig_bits[mode][1 + comp]);
}
if (!is_mode_9_or_10)
{
for (uint32_t subset = 0; subset < num_subsets; subset++)
for (uint32_t comp = 0; comp < 3; comp++)
for (uint32_t lh = (subset ? 0 : 1); lh < 2; lh++)
comps[subset][comp][lh] = bc6h_apply_delta(comps[0][comp][0], comps[subset][comp][lh], num_sig_bits, is_signed);
}
for (uint32_t subset = 0; subset < num_subsets; subset++)
for (uint32_t comp = 0; comp < 3; comp++)
for (uint32_t lh = 0; lh < 2; lh++)
comps[subset][comp][lh] = bc6h_dequantize(comps[subset][comp][lh], num_sig_bits, is_signed);
// Now unpack weights and output texels
const int weight_bits = (mode >= 10) ? 4 : 3;
const uint8_t* pWeights = (mode >= 10) ? basist::g_bc6h_weight4 : basist::g_bc6h_weight3;
dest_pitch_in_halfs -= 4 * 3;
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
int subset = (num_subsets == 1) ? ((x | y) ? 0 : 0x80) : basist::g_bc6h_2subset_patterns[part_index][y][x];
const int num_bits = weight_bits + ((subset & 0x80) ? -1 : 0);
subset &= 1;
const int weight_index = bc6h_get_bits(num_bits, blo, bhi, total_bits_read);
pDst[0] = bc6h_convert_to_half(bc6h_interpolate(comps[subset][0][0], comps[subset][0][1], pWeights, weight_index), is_signed);
pDst[1] = bc6h_convert_to_half(bc6h_interpolate(comps[subset][1][0], comps[subset][1][1], pWeights, weight_index), is_signed);
pDst[2] = bc6h_convert_to_half(bc6h_interpolate(comps[subset][2][0], comps[subset][2][1], pWeights, weight_index), is_signed);
pDst += 3;
}
pDst += dest_pitch_in_halfs;
}
assert(total_bits_read == 128);
return true;
}
//------------------------------------------------------------------------------------------------
// FXT1 (for fun, and because some modern Intel parts support it, and because a subset is like BC1)
struct fxt1_block
{
union
@ -901,6 +1159,9 @@ namespace basisu
return true;
}
//------------------------------------------------------------------------------------------------
// PVRTC2 (non-interpolated, hard_flag=1 modulation=0 subset only!)
struct pvrtc2_block
{
uint8_t m_modulation[4];
@ -1015,6 +1276,9 @@ namespace basisu
return true;
}
//------------------------------------------------------------------------------------------------
// ETC2 EAC R11 or RG11
struct etc2_eac_r11
{
uint64_t m_base : 8;
@ -1085,13 +1349,16 @@ namespace basisu
unpack_etc2_eac_r(pBlock, pPixels, c);
}
}
//------------------------------------------------------------------------------------------------
// UASTC
void unpack_uastc(const void* p, color_rgba* pPixels)
{
basist::unpack_uastc(*static_cast<const basist::uastc_block*>(p), (basist::color32 *)pPixels, false);
}
// Unpacks to RGBA, R, RG, or A
// Unpacks to RGBA, R, RG, or A. LDR GPU texture formats only.
bool unpack_block(texture_format fmt, const void* pBlock, color_rgba* pPixels)
{
switch (fmt)
@ -1150,14 +1417,24 @@ namespace basisu
unpack_etc2_eac(pBlock, pPixels);
break;
}
case texture_format::cASTC4x4:
case texture_format::cBC6HSigned:
case texture_format::cBC6HUnsigned:
case texture_format::cASTC_HDR_4x4:
case texture_format::cUASTC_HDR_4x4:
{
// Can't unpack HDR blocks in unpack_block() because it returns 32bpp pixel data.
assert(0);
return false;
}
case texture_format::cASTC_LDR_4x4:
{
#if BASISU_USE_ASTC_DECOMPRESS
const bool astc_srgb = false;
basisu_astc::astc::decompress(reinterpret_cast<uint8_t*>(pPixels), static_cast<const uint8_t*>(pBlock), astc_srgb, 4, 4);
#else
memset(pPixels, 255, 16 * sizeof(color_rgba));
#endif
bool status = basisu_astc::astc::decompress_ldr(reinterpret_cast<uint8_t*>(pPixels), static_cast<const uint8_t*>(pBlock), astc_srgb, 4, 4);
assert(status);
if (!status)
return false;
break;
}
case texture_format::cATC_RGB:
@ -1206,6 +1483,66 @@ namespace basisu
return true;
}
bool unpack_block_hdr(texture_format fmt, const void* pBlock, vec4F* pPixels)
{
switch (fmt)
{
case texture_format::cASTC_HDR_4x4:
case texture_format::cUASTC_HDR_4x4:
{
#if 1
bool status = basisu_astc::astc::decompress_hdr(&pPixels[0][0], (uint8_t*)pBlock, 4, 4);
assert(status);
if (!status)
return false;
#else
basist::half_float half_block[16][4];
astc_helpers::log_astc_block log_blk;
if (!astc_helpers::unpack_block(pBlock, log_blk, 4, 4))
return false;
if (!astc_helpers::decode_block(log_blk, half_block, 4, 4, astc_helpers::cDecodeModeHDR16))
return false;
for (uint32_t p = 0; p < 16; p++)
{
pPixels[p][0] = basist::half_to_float(half_block[p][0]);
pPixels[p][1] = basist::half_to_float(half_block[p][1]);
pPixels[p][2] = basist::half_to_float(half_block[p][2]);
pPixels[p][3] = basist::half_to_float(half_block[p][3]);
}
//memset(pPixels, 0, sizeof(vec4F) * 16);
#endif
return true;
}
case texture_format::cBC6HSigned:
case texture_format::cBC6HUnsigned:
{
basist::half_float half_block[16][3];
unpack_bc6h(pBlock, half_block, fmt == texture_format::cBC6HSigned);
for (uint32_t p = 0; p < 16; p++)
{
pPixels[p][0] = basist::half_to_float(half_block[p][0]);
pPixels[p][1] = basist::half_to_float(half_block[p][1]);
pPixels[p][2] = basist::half_to_float(half_block[p][2]);
pPixels[p][3] = 1.0f;
}
return true;
}
default:
{
break;
}
}
assert(0);
return false;
}
bool gpu_image::unpack(image& img) const
{
img.resize(get_pixel_width(), get_pixel_height());
@ -1252,7 +1589,48 @@ namespace basisu
return success;
}
bool gpu_image::unpack_hdr(imagef& img) const
{
if ((m_fmt != texture_format::cASTC_HDR_4x4) &&
(m_fmt != texture_format::cUASTC_HDR_4x4) &&
(m_fmt != texture_format::cBC6HUnsigned) &&
(m_fmt != texture_format::cBC6HSigned))
{
// Can't call on LDR images, at least currently. (Could unpack the LDR data and convert to float.)
assert(0);
return false;
}
img.resize(get_pixel_width(), get_pixel_height());
img.set_all(vec4F(0.0f));
if (!img.get_width() || !img.get_height())
return true;
assert((m_block_width <= cMaxBlockSize) && (m_block_height <= cMaxBlockSize));
vec4F pixels[cMaxBlockSize * cMaxBlockSize];
clear_obj(pixels);
bool success = true;
for (uint32_t by = 0; by < m_blocks_y; by++)
{
for (uint32_t bx = 0; bx < m_blocks_x; bx++)
{
const void* pBlock = get_block_ptr(bx, by);
if (!unpack_block_hdr(m_fmt, pBlock, pixels))
success = false;
img.set_block_clipped(pixels, bx * m_block_width, by * m_block_height, m_block_width, m_block_height);
} // bx
} // by
return success;
}
// KTX1 texture file writing
static const uint8_t g_ktx_file_id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
// KTX/GL enums
@ -1273,6 +1651,8 @@ namespace basisu
KTX_COMPRESSED_RGBA8_ETC2_EAC = 0x9278,
KTX_COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C,
KTX_COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D,
KTX_COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E,
KTX_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F,
KTX_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00,
KTX_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02,
KTX_COMPRESSED_RGBA_ASTC_4x4_KHR = 0x93B0,
@ -1319,6 +1699,7 @@ namespace basisu
uint32_t width = 0, height = 0, total_levels = 0;
basisu::texture_format fmt = texture_format::cInvalidTextureFormat;
// Sanity check the input
if (cubemap_flag)
{
if ((gpu_images.size() % 6) != 0)
@ -1327,7 +1708,7 @@ namespace basisu
return false;
}
}
for (uint32_t array_index = 0; array_index < gpu_images.size(); array_index++)
{
const gpu_image_vec &levels = gpu_images[array_index];
@ -1426,6 +1807,18 @@ namespace basisu
base_internal_fmt = KTX_RGBA;
break;
}
case texture_format::cBC6HSigned:
{
internal_fmt = KTX_COMPRESSED_RGB_BPTC_SIGNED_FLOAT;
base_internal_fmt = KTX_RGBA;
break;
}
case texture_format::cBC6HUnsigned:
{
internal_fmt = KTX_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
base_internal_fmt = KTX_RGBA;
break;
}
case texture_format::cBC7:
{
internal_fmt = KTX_COMPRESSED_RGBA_BPTC_UNORM;
@ -1443,7 +1836,10 @@ namespace basisu
base_internal_fmt = KTX_RGBA;
break;
}
case texture_format::cASTC4x4:
// We use different enums for HDR vs. LDR ASTC, but internally they are both just ASTC.
case texture_format::cASTC_LDR_4x4:
case texture_format::cASTC_HDR_4x4:
case texture_format::cUASTC_HDR_4x4: // UASTC_HDR is just HDR-only ASTC
{
internal_fmt = KTX_COMPRESSED_RGBA_ASTC_4x4_KHR;
base_internal_fmt = KTX_RGBA;
@ -1496,17 +1892,17 @@ namespace basisu
return false;
}
}
ktx_header header;
header.clear();
memcpy(&header.m_identifier, g_ktx_file_id, sizeof(g_ktx_file_id));
header.m_endianness = KTX_ENDIAN;
header.m_pixelWidth = width;
header.m_pixelHeight = height;
header.m_glTypeSize = 1;
header.m_glInternalFormat = internal_fmt;
header.m_glBaseInternalFormat = base_internal_fmt;
@ -1517,12 +1913,12 @@ namespace basisu
header.m_numberOfMipmapLevels = total_levels;
header.m_numberOfFaces = cubemap_flag ? 6 : 1;
append_vector(ktx_data, (uint8_t *)&header, sizeof(header));
append_vector(ktx_data, (uint8_t*)&header, sizeof(header));
for (uint32_t level_index = 0; level_index < total_levels; level_index++)
{
uint32_t img_size = gpu_images[0][level_index].get_size_in_bytes();
if ((header.m_numberOfFaces == 1) || (header.m_numberOfArrayElements > 1))
{
img_size = img_size * header.m_numberOfFaces * maximum<uint32_t>(1, header.m_numberOfArrayElements);
@ -1531,9 +1927,10 @@ namespace basisu
assert(img_size && ((img_size & 3) == 0));
packed_uint<4> packed_img_size(img_size);
append_vector(ktx_data, (uint8_t *)&packed_img_size, sizeof(packed_img_size));
append_vector(ktx_data, (uint8_t*)&packed_img_size, sizeof(packed_img_size));
uint32_t bytes_written = 0;
(void)bytes_written;
for (uint32_t array_index = 0; array_index < maximum<uint32_t>(1, header.m_numberOfArrayElements); array_index++)
{
@ -1541,11 +1938,11 @@ namespace basisu
{
const gpu_image& img = gpu_images[cubemap_flag ? (array_index * 6 + face_index) : array_index][level_index];
append_vector(ktx_data, (uint8_t *)img.get_ptr(), img.get_size_in_bytes());
append_vector(ktx_data, (uint8_t*)img.get_ptr(), img.get_size_in_bytes());
bytes_written += img.get_size_in_bytes();
}
} // array_index
} // level_index
@ -1553,7 +1950,58 @@ namespace basisu
return true;
}
bool write_compressed_texture_file(const char* pFilename, const basisu::vector<gpu_image_vec>& g, bool cubemap_flag)
bool does_dds_support_format(texture_format fmt)
{
switch (fmt)
{
case texture_format::cBC1_NV:
case texture_format::cBC1_AMD:
case texture_format::cBC1:
case texture_format::cBC3:
case texture_format::cBC4:
case texture_format::cBC5:
case texture_format::cBC6HSigned:
case texture_format::cBC6HUnsigned:
case texture_format::cBC7:
return true;
default:
break;
}
return false;
}
// Only supports the basic DirectX BC texture formats.
// gpu_images array is: [face/layer][mipmap level]
// For cubemap arrays, # of face/layers must be a multiple of 6.
// Accepts 2D, 2D mipmapped, 2D array, 2D array mipmapped
// and cubemap, cubemap mipmapped, and cubemap array mipmapped.
bool write_dds_file(uint8_vec &dds_data, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format)
{
return false;
}
bool write_dds_file(const char* pFilename, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format)
{
uint8_vec dds_data;
if (!write_dds_file(dds_data, gpu_images, cubemap_flag, use_srgb_format))
return false;
if (!write_vec_to_file(pFilename, dds_data))
{
fprintf(stderr, "write_dds_file: Failed writing DDS file data\n");
return false;
}
return true;
}
bool read_uncompressed_dds_file(const char* pFilename, basisu::vector<image> &ldr_mips, basisu::vector<imagef>& hdr_mips)
{
return false;
}
bool write_compressed_texture_file(const char* pFilename, const basisu::vector<gpu_image_vec>& g, bool cubemap_flag, bool use_srgb_format)
{
std::string extension(string_tolower(string_get_extension(pFilename)));
@ -1570,8 +2018,8 @@ namespace basisu
}
else if (extension == "dds")
{
// TODO
return false;
if (!write_dds_file(filedata, g, cubemap_flag, use_srgb_format))
return false;
}
else
{
@ -1583,11 +2031,18 @@ namespace basisu
return basisu::write_vec_to_file(pFilename, filedata);
}
bool write_compressed_texture_file(const char* pFilename, const gpu_image& g)
bool write_compressed_texture_file(const char* pFilename, const gpu_image_vec& g, bool use_srgb_format)
{
basisu::vector<gpu_image_vec> a;
a.push_back(g);
return write_compressed_texture_file(pFilename, a, false, use_srgb_format);
}
bool write_compressed_texture_file(const char* pFilename, const gpu_image& g, bool use_srgb_format)
{
basisu::vector<gpu_image_vec> v;
enlarge_vector(v, 1)->push_back(g);
return write_compressed_texture_file(pFilename, v, false);
return write_compressed_texture_file(pFilename, v, false, use_srgb_format);
}
//const uint32_t OUT_FILE_MAGIC = 'TEXC';
@ -1626,5 +2081,49 @@ namespace basisu
return fclose(pFile) != EOF;
}
// The .astc texture format is readable using ARM's astcenc, AMD Compressonator, and other engines/tools. It oddly doesn't support mipmaps, limiting
// its usefulness/relevance.
// https://github.com/ARM-software/astc-encoder/blob/main/Docs/FileFormat.md
bool write_astc_file(const char* pFilename, const void* pBlocks, uint32_t block_width, uint32_t block_height, uint32_t dim_x, uint32_t dim_y)
{
assert(pBlocks && (block_width >= 4) && (block_height >= 4) && (dim_x > 0) && (dim_y > 0));
uint8_vec file_data;
file_data.push_back(0x13);
file_data.push_back(0xAB);
file_data.push_back(0xA1);
file_data.push_back(0x5C);
file_data.push_back((uint8_t)block_width);
file_data.push_back((uint8_t)block_height);
file_data.push_back(1);
file_data.push_back((uint8_t)dim_x);
file_data.push_back((uint8_t)(dim_x >> 8));
file_data.push_back((uint8_t)(dim_x >> 16));
file_data.push_back((uint8_t)dim_y);
file_data.push_back((uint8_t)(dim_y >> 8));
file_data.push_back((uint8_t)(dim_y >> 16));
file_data.push_back((uint8_t)1);
file_data.push_back((uint8_t)0);
file_data.push_back((uint8_t)0);
const uint32_t num_blocks_x = (dim_x + block_width - 1) / block_width;
const uint32_t num_blocks_y = (dim_y + block_height - 1) / block_height;
const uint32_t total_bytes = num_blocks_x * num_blocks_y * 16;
const size_t cur_size = file_data.size();
file_data.resize(cur_size + total_bytes);
memcpy(&file_data[cur_size], pBlocks, total_bytes);
return write_vec_to_file(pFilename, file_data);
}
} // basisu

View file

@ -1,5 +1,5 @@
// basisu_gpu_texture.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -48,6 +48,7 @@ namespace basisu
}
inline texture_format get_format() const { return m_fmt; }
inline bool is_hdr() const { return is_hdr_texture_format(m_fmt); }
// Width/height in pixels
inline uint32_t get_pixel_width() const { return m_width; }
@ -100,9 +101,13 @@ namespace basisu
m_blocks.resize(m_blocks_x * m_blocks_y * m_qwords_per_block);
}
// Unpacks LDR textures only.
bool unpack(image& img) const;
// Unpacks HDR textures only.
bool unpack_hdr(imagef& img) const;
void override_dimensions(uint32_t w, uint32_t h)
inline void override_dimensions(uint32_t w, uint32_t h)
{
m_width = w;
m_height = h;
@ -116,39 +121,50 @@ namespace basisu
typedef basisu::vector<gpu_image> gpu_image_vec;
// KTX file writing
// KTX1 file writing
bool create_ktx_texture_file(uint8_vec &ktx_data, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag);
bool write_compressed_texture_file(const char *pFilename, const basisu::vector<gpu_image_vec>& g, bool cubemap_flag);
inline bool write_compressed_texture_file(const char *pFilename, const gpu_image_vec &g)
{
basisu::vector<gpu_image_vec> a;
a.push_back(g);
return write_compressed_texture_file(pFilename, a, false);
}
bool does_dds_support_format(texture_format fmt);
bool write_dds_file(uint8_vec& dds_data, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format);
bool write_dds_file(const char* pFilename, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format);
bool write_compressed_texture_file(const char *pFilename, const gpu_image &g);
// Currently reads 2D 32bpp RGBA, 16-bit HALF RGBA, or 32-bit FLOAT RGBA, with or without mipmaps. No tex arrays or cubemaps, yet.
bool read_uncompressed_dds_file(const char* pFilename, basisu::vector<image>& ldr_mips, basisu::vector<imagef>& hdr_mips);
// Supports DDS and KTX
bool write_compressed_texture_file(const char *pFilename, const basisu::vector<gpu_image_vec>& g, bool cubemap_flag, bool use_srgb_format);
bool write_compressed_texture_file(const char* pFilename, const gpu_image_vec& g, bool use_srgb_format);
bool write_compressed_texture_file(const char *pFilename, const gpu_image &g, bool use_srgb_format);
bool write_3dfx_out_file(const char* pFilename, const gpu_image& gi);
// GPU texture block unpacking
// For ETC1, use in basisu_etc.h: bool unpack_etc1(const etc_block& block, color_rgba *pDst, bool preserve_alpha)
void unpack_etc2_eac(const void *pBlock_bits, color_rgba *pPixels);
bool unpack_bc1(const void *pBlock_bits, color_rgba *pPixels, bool set_alpha);
void unpack_bc4(const void *pBlock_bits, uint8_t *pPixels, uint32_t stride);
bool unpack_bc3(const void *pBlock_bits, color_rgba *pPixels);
void unpack_bc5(const void *pBlock_bits, color_rgba *pPixels);
bool unpack_bc7_mode6(const void *pBlock_bits, color_rgba *pPixels);
bool unpack_bc7(const void* pBlock_bits, color_rgba* pPixels);
bool unpack_bc7(const void* pBlock_bits, color_rgba* pPixels); // full format
bool unpack_bc6h(const void* pSrc_block, void* pDst_block, bool is_signed, uint32_t dest_pitch_in_halfs = 4 * 3); // full format, outputs HALF values, RGB texels only (not RGBA)
void unpack_atc(const void* pBlock_bits, color_rgba* pPixels);
// We only support CC_MIXED non-alpha blocks here because that's the only mode the transcoder uses at the moment.
bool unpack_fxt1(const void* p, color_rgba* pPixels);
// PVRTC2 is currently limited to only what our transcoder outputs (non-interpolated, hard_flag=1 modulation=0). In this mode, PVRTC2 looks much like BC1/ATC.
bool unpack_pvrtc2(const void* p, color_rgba* pPixels);
void unpack_etc2_eac_r(const void *p, color_rgba* pPixels, uint32_t c);
void unpack_etc2_eac_rg(const void* p, color_rgba* pPixels);
// unpack_block() is primarily intended to unpack texture data created by the transcoder.
// For some texture formats (like ETC2 RGB, PVRTC2, FXT1) it's not a complete implementation.
// For some texture formats (like ETC2 RGB, PVRTC2, FXT1) it's not yet a complete implementation.
// Unpacks LDR texture formats only.
bool unpack_block(texture_format fmt, const void *pBlock, color_rgba *pPixels);
// Unpacks HDR texture formats only.
bool unpack_block_hdr(texture_format fmt, const void* pBlock, vec4F* pPixels);
bool write_astc_file(const char* pFilename, const void* pBlocks, uint32_t block_width, uint32_t block_height, uint32_t dim_x, uint32_t dim_y);
} // namespace basisu

View file

@ -1,5 +1,5 @@
// basisu_kernels_declares.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_kernels_imp.h - Do not directly include
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_kernels_sse.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -22,22 +22,6 @@
#include <intrin.h>
#endif
#if !defined(_MSC_VER)
#if __AVX__ || __AVX2__ || __AVX512F__
#error Please check your compiler options
#endif
#if CPPSPMD_SSE2
#if __SSE4_1__ || __SSE3__ || __SSE4_2__ || __SSSE3__
#error SSE4.1/SSE3/SSE4.2/SSSE3 cannot be enabled to use this file
#endif
#else
#if !__SSE4_1__ || !__SSE3__ || !__SSSE3__
#error Please check your compiler options
#endif
#endif
#endif
#include "cppspmd_sse.h"
#include "cppspmd_type_aliases.h"

View file

@ -3,7 +3,7 @@
Forked from the public domain/unlicense version at: https://code.google.com/archive/p/miniz/
Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -1973,7 +1973,7 @@ static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahe
(TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
if (!probe_len)
{
*pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break;
*pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); break;
}
else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len)
{
@ -2101,7 +2101,7 @@ static mz_bool tdefl_compress_fast(tdefl_compressor *d)
total_lz_bytes += cur_match_len;
lookahead_pos += cur_match_len;
dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE);
dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE);
cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;
MZ_ASSERT(lookahead_size >= cur_match_len);
lookahead_size -= cur_match_len;
@ -2129,7 +2129,7 @@ static mz_bool tdefl_compress_fast(tdefl_compressor *d)
d->m_huff_count[0][lit]++;
lookahead_pos++;
dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE);
dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE);
cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
lookahead_size--;
@ -2283,7 +2283,7 @@ static mz_bool tdefl_compress_normal(tdefl_compressor *d)
d->m_lookahead_pos += len_to_move;
MZ_ASSERT(d->m_lookahead_size >= len_to_move);
d->m_lookahead_size -= len_to_move;
d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE);
d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE);
// Check if it's time to flush the current LZ codes to the internal output buffer.
if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ||
( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) )

View file

@ -1,5 +1,5 @@
// basisu_opencl.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_opencl.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Note: Undefine or set BASISU_SUPPORT_OPENCL to 0 to completely OpenCL support.
//

View file

@ -1,5 +1,5 @@
// basisu_pvrtc1_4.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_pvrtc1_4.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -231,7 +231,18 @@ namespace basisu
inline void set_to_black()
{
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#endif
memset(m_blocks.get_ptr(), 0, m_blocks.size_in_bytes());
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
}
inline bool get_block_uses_transparent_modulation(uint32_t bx, uint32_t by) const

View file

@ -1,5 +1,5 @@
// basisu_resampler_filters.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_resampler.cpp
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_resampler.h
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_resampler_filters.h
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_ssim.cpp
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_ssim.h
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,5 +1,5 @@
// basisu_uastc_enc.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -13,11 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "basisu_uastc_enc.h"
#if BASISU_USE_ASTC_DECOMPRESS
#include "basisu_astc_decomp.h"
#endif
#include "3rdparty/android_astc_decomp.h"
#include "basisu_gpu_texture.h"
#include "basisu_bc7enc.h"
@ -384,6 +380,7 @@ namespace basisu
}
uint32_t total_endpoint_bits = 0;
(void)total_endpoint_bits;
for (uint32_t i = 0; i < total_tq_values; i++)
{
@ -428,6 +425,8 @@ namespace basisu
#endif
uint32_t total_weight_bits = 0;
(void)total_weight_bits;
const uint32_t plane_shift = (total_planes == 2) ? 1 : 0;
for (uint32_t i = 0; i < 16 * total_planes; i++)
{
@ -3175,6 +3174,7 @@ namespace basisu
const bool favor_bc7_error = !favor_uastc_error && ((flags & cPackUASTCFavorBC7Error) != 0);
//const bool etc1_perceptual = true;
// TODO: This uses 64KB of stack space!
uastc_encode_results results[MAX_ENCODE_RESULTS];
level = clampi(level, cPackUASTCLevelFastest, cPackUASTCLevelVerySlow);
@ -3567,7 +3567,6 @@ namespace basisu
success = basist::unpack_uastc(temp_block, (basist::color32 *)temp_block_unpacked, false);
VALIDATE(success);
#if BASISU_USE_ASTC_DECOMPRESS
// Now round trip to packed ASTC and back, then decode to pixels.
uint32_t astc_data[4];
@ -3580,7 +3579,7 @@ namespace basisu
}
color_rgba decoded_astc_block[4][4];
success = basisu_astc::astc::decompress((uint8_t*)decoded_astc_block, (uint8_t*)&astc_data, false, 4, 4);
success = basisu_astc::astc::decompress_ldr((uint8_t*)decoded_astc_block, (uint8_t*)&astc_data, false, 4, 4);
VALIDATE(success);
for (uint32_t y = 0; y < 4; y++)
@ -3595,7 +3594,6 @@ namespace basisu
VALIDATE(temp_block_unpacked[y][x].c[3] == decoded_uastc_block[y][x].a);
}
}
#endif
}
#endif
@ -3789,8 +3787,9 @@ namespace basisu
{
uint64_t m_sel;
uint32_t m_ofs;
uint32_t m_pad; // avoid implicit padding for selector_bitsequence_hash
selector_bitsequence() { }
selector_bitsequence(uint32_t bit_ofs, uint64_t sel) : m_sel(sel), m_ofs(bit_ofs) { }
selector_bitsequence(uint32_t bit_ofs, uint64_t sel) : m_sel(sel), m_ofs(bit_ofs), m_pad(0) { }
bool operator== (const selector_bitsequence& other) const
{
return (m_ofs == other.m_ofs) && (m_sel == other.m_sel);
@ -3811,7 +3810,7 @@ namespace basisu
{
std::size_t operator()(selector_bitsequence const& s) const noexcept
{
return static_cast<std::size_t>(hash_hsieh((uint8_t *)&s, sizeof(s)) ^ s.m_sel);
return hash_hsieh((const uint8_t*)&s, sizeof(s));
}
};

View file

@ -1,5 +1,5 @@
// basisu_uastc_enc.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,7 +1,7 @@
// Do not include this header directly.
// Control flow functionality in common between all the headers.
//
// Copyright 2020-2021 Binomial LLC
// Copyright 2020-2024 Binomial LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,6 +1,6 @@
// Do not include this header directly.
//
// Copyright 2020-2021 Binomial LLC
// Copyright 2020-2024 Binomial LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -646,7 +646,7 @@ CPPSPMD_FORCE_INLINE vint spmd_kernel::count_set_bits(vint x)
{
vint v = x - (VUINT_SHIFT_RIGHT(x, 1) & 0x55555555);
vint v1 = (v & 0x33333333) + (VUINT_SHIFT_RIGHT(v, 2) & 0x33333333);
return VUINT_SHIFT_RIGHT(((v1 + VUINT_SHIFT_RIGHT(v1, 4) & 0xF0F0F0F) * 0x1010101), 24);
return VUINT_SHIFT_RIGHT(((v1 + (VUINT_SHIFT_RIGHT(v1, 4) & 0xF0F0F0F)) * 0x1010101), 24);
}
CPPSPMD_FORCE_INLINE vint cmple_epu16(const vint &a, const vint &b)

View file

@ -1,7 +1,7 @@
// Do not include this header directly.
// This header defines shared struct spmd_kernel helpers.
//
// Copyright 2020-2021 Binomial LLC
// Copyright 2020-2024 Binomial LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -450,7 +450,7 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE explicit operator vint() const;
private:
vbool& operator=(const vbool&);
//vbool& operator=(const vbool&);
};
friend vbool operator!(const vbool& v);
@ -481,7 +481,7 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE explicit vfloat(int value) : m_value(_mm_set1_ps((float)value)) { }
private:
vfloat& operator=(const vfloat&);
//vfloat& operator=(const vfloat&);
};
CPPSPMD_FORCE_INLINE vfloat& store(vfloat& dst, const vfloat& src)
@ -514,7 +514,7 @@ struct spmd_kernel
float* m_pValue;
private:
float_lref& operator=(const float_lref&);
//float_lref& operator=(const float_lref&);
};
CPPSPMD_FORCE_INLINE const float_lref& store(const float_lref& dst, const vfloat& src)
@ -561,7 +561,7 @@ struct spmd_kernel
float* m_pValue;
private:
float_vref& operator=(const float_vref&);
//float_vref& operator=(const float_vref&);
};
// Varying ref to varying float
@ -571,7 +571,7 @@ struct spmd_kernel
vfloat* m_pValue;
private:
vfloat_vref& operator=(const vfloat_vref&);
//vfloat_vref& operator=(const vfloat_vref&);
};
// Varying ref to varying int
@ -581,7 +581,7 @@ struct spmd_kernel
vint* m_pValue;
private:
vint_vref& operator=(const vint_vref&);
//vint_vref& operator=(const vint_vref&);
};
CPPSPMD_FORCE_INLINE const float_vref& store(const float_vref& dst, const vfloat& src);
@ -624,7 +624,7 @@ struct spmd_kernel
int* m_pValue;
private:
int_lref& operator=(const int_lref&);
//int_lref& operator=(const int_lref&);
};
CPPSPMD_FORCE_INLINE const int_lref& store(const int_lref& dst, const vint& src)
@ -663,7 +663,7 @@ struct spmd_kernel
int16_t* m_pValue;
private:
int16_lref& operator=(const int16_lref&);
//int16_lref& operator=(const int16_lref&);
};
CPPSPMD_FORCE_INLINE const int16_lref& store(const int16_lref& dst, const vint& src)
@ -720,7 +720,7 @@ struct spmd_kernel
const int* m_pValue;
private:
cint_lref& operator=(const cint_lref&);
//cint_lref& operator=(const cint_lref&);
};
CPPSPMD_FORCE_INLINE vint load(const cint_lref& src)
@ -742,7 +742,7 @@ struct spmd_kernel
int* m_pValue;
private:
int_vref& operator=(const int_vref&);
//int_vref& operator=(const int_vref&);
};
// Varying ref to constant ints
@ -752,7 +752,7 @@ struct spmd_kernel
const int* m_pValue;
private:
cint_vref& operator=(const cint_vref&);
//cint_vref& operator=(const cint_vref&);
};
// Varying int
@ -810,7 +810,7 @@ struct spmd_kernel
}
private:
vint& operator=(const vint&);
//vint& operator=(const vint&);
};
// Load/store linear int
@ -1206,7 +1206,7 @@ struct spmd_kernel
CPPSPMD_FORCE_INLINE vint load_all(const vint_vref& src)
{
// TODO: There's surely a better way
__m128i k;
__m128i k = _mm_setzero_si128();
k = insert_x(k, ((int*)(&src.m_pValue[extract_x(src.m_vindex)]))[0]);
k = insert_y(k, ((int*)(&src.m_pValue[extract_y(src.m_vindex)]))[1]);
@ -1261,7 +1261,7 @@ struct spmd_kernel
}
private:
lint& operator=(const lint&);
//lint& operator=(const lint&);
};
CPPSPMD_FORCE_INLINE lint& store_all(lint& dst, const lint& src)

View file

@ -1,7 +1,7 @@
// cppspmd_type_aliases.h
// Do not include this file directly
//
// Copyright 2020-2021 Binomial LLC
// Copyright 2020-2024 Binomial LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -163,7 +163,7 @@ public:
{
if ((sizeof(size_t) == sizeof(uint32_t)) && (new_size > 0x7FFFFFFFUL))
return 0;
m_buf.resize(new_size);
m_buf.resize((size_t)new_size);
}
memcpy(&m_buf[(size_t)m_ofs], pBuf, len);
@ -178,11 +178,11 @@ public:
return 0;
uint64_t max_bytes = minimum<uint64_t>(len, m_buf.size() - m_ofs);
memcpy(pBuf, &m_buf[(size_t)m_ofs], max_bytes);
memcpy(pBuf, &m_buf[(size_t)m_ofs], (size_t)max_bytes);
m_ofs += max_bytes;
return max_bytes;
return (size_t)max_bytes;
}
};
@ -249,11 +249,11 @@ public:
return 0;
uint64_t max_bytes = minimum<uint64_t>(len, m_buf_size - m_ofs);
memcpy(pBuf, &m_pBuf[(size_t)m_ofs], max_bytes);
memcpy(pBuf, &m_pBuf[(size_t)m_ofs], (size_t)max_bytes);
m_ofs += max_bytes;
return max_bytes;
return (size_t)max_bytes;
}
};
@ -1626,8 +1626,8 @@ int png_decoder::png_decode_start()
if (m_ihdr.m_ilace_type == 1)
{
int i;
uint32_t total_lines, lines_processed;
//int i;
//uint32_t total_lines, lines_processed;
m_adam7_pass_size_x[0] = adam7_pass_size(m_ihdr.m_width, 0, 8);
m_adam7_pass_size_x[1] = adam7_pass_size(m_ihdr.m_width, 4, 8);
@ -1651,10 +1651,12 @@ int png_decoder::png_decode_start()
m_pass_y_left = 0;
#if 0
total_lines = lines_processed = 0;
for (i = 0; i < 7; i++)
total_lines += m_adam7_pass_size_y[i];
#endif
for (; ; )
{
@ -1675,7 +1677,7 @@ int png_decoder::png_decode_start()
}
}
lines_processed++;
//lines_processed++;
}
m_adam7_decoded_flag = TRUE;

View file

@ -0,0 +1,26 @@
diff --git a/thirdparty/basis_universal/encoder/basisu_comp.cpp b/thirdparty/basis_universal/encoder/basisu_comp.cpp
index f16e75bd46..81813257cd 100644
--- a/thirdparty/basis_universal/encoder/basisu_comp.cpp
+++ b/thirdparty/basis_universal/encoder/basisu_comp.cpp
@@ -33,7 +33,7 @@
#endif
#if BASISD_SUPPORT_KTX2_ZSTD
-#include "../zstd/zstd.h"
+#include <zstd.h>
#endif
// Set to 1 to disable the mipPadding alignment workaround (which only seems to be needed when no key-values are written at all)
diff --git a/thirdparty/basis_universal/transcoder/basisu_transcoder.cpp b/thirdparty/basis_universal/transcoder/basisu_transcoder.cpp
index ea994b0c4f..32018cd282 100644
--- a/thirdparty/basis_universal/transcoder/basisu_transcoder.cpp
+++ b/thirdparty/basis_universal/transcoder/basisu_transcoder.cpp
@@ -164,7 +164,7 @@
// 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"
+ #include <zstd.h>
#endif
#endif

View file

@ -0,0 +1,13 @@
diff --git a/thirdparty/basis_universal/encoder/basisu_enc.cpp b/thirdparty/basis_universal/encoder/basisu_enc.cpp
index 47e8981bc3..6c0ac0ad37 100644
--- a/thirdparty/basis_universal/encoder/basisu_enc.cpp
+++ b/thirdparty/basis_universal/encoder/basisu_enc.cpp
@@ -458,7 +458,7 @@ namespace basisu
bool load_jpg(const char *pFilename, image& img)
{
int width = 0, height = 0, actual_comps = 0;
- uint8_t *pImage_data = jpgd::decompress_jpeg_image_from_file(pFilename, &width, &height, &actual_comps, 4, jpgd::jpeg_decoder::cFlagLinearChromaFiltering);
+ uint8_t *pImage_data = jpgd::decompress_jpeg_image_from_file(pFilename, &width, &height, &actual_comps, 4, jpgd::jpeg_decoder::cFlagBoxChromaFiltering);
if (!pImage_data)
return false;

View file

@ -0,0 +1,23 @@
diff --git a/thirdparty/basis_universal/encoder/basisu_enc.cpp b/thirdparty/basis_universal/encoder/basisu_enc.cpp
index 6c0ac0ad37..2bf486a028 100644
--- a/thirdparty/basis_universal/encoder/basisu_enc.cpp
+++ b/thirdparty/basis_universal/encoder/basisu_enc.cpp
@@ -27,7 +27,7 @@
#ifndef TINYEXR_USE_ZFP
#define TINYEXR_USE_ZFP (1)
#endif
-#include "3rdparty/tinyexr.h"
+#include <tinyexr.h>
#ifndef MINIZ_HEADER_FILE_ONLY
#define MINIZ_HEADER_FILE_ONLY
@@ -3257,7 +3257,8 @@ namespace basisu
float* out_rgba = nullptr;
const char* err = nullptr;
- int status = LoadEXRWithLayer(&out_rgba, &width, &height, pFilename, nullptr, &err, &n_chans);
+ int status = LoadEXRWithLayer(&out_rgba, &width, &height, pFilename, nullptr, &err);
+ n_chans = 4;
if (status != 0)
{
error_printf("Failed loading .EXR image \"%s\"! (TinyEXR error: %s)\n", pFilename, err ? err : "?");

View file

@ -0,0 +1,444 @@
diff --git a/thirdparty/basis_universal/encoder/basisu_enc.cpp b/thirdparty/basis_universal/encoder/basisu_enc.cpp
index 2bf486a028..fff98e8301 100644
--- a/thirdparty/basis_universal/encoder/basisu_enc.cpp
+++ b/thirdparty/basis_universal/encoder/basisu_enc.cpp
@@ -37,9 +37,6 @@
#endif
#include "basisu_miniz.h"
-#define QOI_IMPLEMENTATION
-#include "3rdparty/qoi.h"
-
#if defined(_WIN32)
// For QueryPerformanceCounter/QueryPerformanceFrequency
#define WIN32_LEAN_AND_MEAN
@@ -408,16 +405,7 @@ namespace basisu
bool load_qoi(const char* pFilename, image& img)
{
- qoi_desc desc;
- clear_obj(desc);
-
- void* p = qoi_read(pFilename, &desc, 4);
- if (!p)
- return false;
-
- img.grant_ownership(static_cast<color_rgba *>(p), desc.width, desc.height);
-
- return true;
+ return false;
}
bool load_png(const uint8_t *pBuf, size_t buf_size, image &img, const char *pFilename)
diff --git a/thirdparty/basis_universal/encoder/basisu_gpu_texture.cpp b/thirdparty/basis_universal/encoder/basisu_gpu_texture.cpp
index 000869a533..648cfb47ae 100644
--- a/thirdparty/basis_universal/encoder/basisu_gpu_texture.cpp
+++ b/thirdparty/basis_universal/encoder/basisu_gpu_texture.cpp
@@ -19,9 +19,6 @@
#include "basisu_bc7enc.h"
#include "../transcoder/basisu_astc_hdr_core.h"
-#define TINYDDS_IMPLEMENTATION
-#include "3rdparty/tinydds.h"
-
namespace basisu
{
//------------------------------------------------------------------------------------------------
@@ -1980,207 +1977,7 @@ namespace basisu
// and cubemap, cubemap mipmapped, and cubemap array mipmapped.
bool write_dds_file(uint8_vec &dds_data, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format)
{
- if (!gpu_images.size())
- {
- assert(0);
- return false;
- }
-
- // Sanity check the input
- uint32_t slices = 1;
- if (cubemap_flag)
- {
- if ((gpu_images.size() % 6) != 0)
- {
- assert(0);
- return false;
- }
- slices = gpu_images.size() / 6;
- }
- else
- {
- slices = gpu_images.size();
- }
-
- uint32_t width = 0, height = 0, total_levels = 0;
- basisu::texture_format fmt = texture_format::cInvalidTextureFormat;
-
- // Sanity check the input for consistent # of dimensions and mip levels
- for (uint32_t array_index = 0; array_index < gpu_images.size(); array_index++)
- {
- const gpu_image_vec& levels = gpu_images[array_index];
-
- if (!levels.size())
- {
- // Empty mip chain
- assert(0);
- return false;
- }
-
- if (!array_index)
- {
- width = levels[0].get_pixel_width();
- height = levels[0].get_pixel_height();
- total_levels = (uint32_t)levels.size();
- fmt = levels[0].get_format();
- }
- else
- {
- if ((width != levels[0].get_pixel_width()) ||
- (height != levels[0].get_pixel_height()) ||
- (total_levels != levels.size()))
- {
- // All cubemap/texture array faces must be the same dimension
- assert(0);
- return false;
- }
- }
-
- for (uint32_t level_index = 0; level_index < levels.size(); level_index++)
- {
- if (level_index)
- {
- if ((levels[level_index].get_pixel_width() != maximum<uint32_t>(1, levels[0].get_pixel_width() >> level_index)) ||
- (levels[level_index].get_pixel_height() != maximum<uint32_t>(1, levels[0].get_pixel_height() >> level_index)))
- {
- // Malformed mipmap chain
- assert(0);
- return false;
- }
- }
-
- if (fmt != levels[level_index].get_format())
- {
- // All input textures must use the same GPU format
- assert(0);
- return false;
- }
- }
- }
-
- // No mipmap levels
- if (!total_levels)
- {
- assert(0);
- return false;
- }
-
- // Create the DDS mipmap level data
- uint8_vec mipmaps[32];
-
- // See https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-file-layout-for-cubic-environment-maps
- // DDS cubemap organization is cubemap face 0 followed by all mips, then cubemap face 1 followed by all mips, etc.
- // Unfortunately tinydds.h's writer doesn't handle this case correctly, so we work around it here.
- // This also applies with 2D texture arrays, too. RenderDoc and ddsview (DirectXTex) views each type (cubemap array and 2D texture array) correctly.
- // Also see "Using Texture Arrays in Direct3D 10/11":
- // https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide
- for (uint32_t array_index = 0; array_index < gpu_images.size(); array_index++)
- {
- const gpu_image_vec& levels = gpu_images[array_index];
-
- for (uint32_t level_index = 0; level_index < levels.size(); level_index++)
- {
- append_vector(mipmaps[0], (uint8_t*)levels[level_index].get_ptr(), levels[level_index].get_size_in_bytes());
-
- } // level_index
- } // array_index
-
-#if 0
- // This organization, required by tinydds.h's API, is wrong.
- {
- for (uint32_t array_index = 0; array_index < gpu_images.size(); array_index++)
- {
- const gpu_image_vec& levels = gpu_images[array_index];
-
- for (uint32_t level_index = 0; level_index < levels.size(); level_index++)
- {
- append_vector(mipmaps[level_index], (uint8_t*)levels[level_index].get_ptr(), levels[level_index].get_size_in_bytes());
-
- } // level_index
- } // array_index
- }
-#endif
-
- // Write DDS file using tinydds
- TinyDDS_WriteCallbacks cbs;
- cbs.error = [](void* user, char const* msg) { BASISU_NOTE_UNUSED(user); fprintf(stderr, "tinydds: %s\n", msg); };
- cbs.alloc = [](void* user, size_t size) -> void* { BASISU_NOTE_UNUSED(user); return malloc(size); };
- cbs.free = [](void* user, void* memory) { BASISU_NOTE_UNUSED(user); free(memory); };
- cbs.write = [](void* user, void const* buffer, size_t byteCount) { BASISU_NOTE_UNUSED(user); uint8_vec* pVec = (uint8_vec*)user; append_vector(*pVec, (const uint8_t*)buffer, byteCount); };
-
- uint32_t mipmap_sizes[32];
- const void* mipmap_ptrs[32];
-
- clear_obj(mipmap_sizes);
- clear_obj(mipmap_ptrs);
-
- assert(total_levels < 32);
- for (uint32_t i = 0; i < total_levels; i++)
- {
- mipmap_sizes[i] = mipmaps[i].size_in_bytes();
- mipmap_ptrs[i] = mipmaps[i].get_ptr();
- }
-
- // Select tinydds texture format
- uint32_t tinydds_fmt = 0;
-
- switch (fmt)
- {
- case texture_format::cBC1_NV:
- case texture_format::cBC1_AMD:
- case texture_format::cBC1:
- tinydds_fmt = use_srgb_format ? TDDS_BC1_RGBA_SRGB_BLOCK : TDDS_BC1_RGBA_UNORM_BLOCK;
- break;
- case texture_format::cBC3:
- tinydds_fmt = use_srgb_format ? TDDS_BC3_SRGB_BLOCK : TDDS_BC3_UNORM_BLOCK;
- break;
- case texture_format::cBC4:
- tinydds_fmt = TDDS_BC4_UNORM_BLOCK;
- break;
- case texture_format::cBC5:
- tinydds_fmt = TDDS_BC5_UNORM_BLOCK;
- break;
- case texture_format::cBC6HSigned:
- tinydds_fmt = TDDS_BC6H_SFLOAT_BLOCK;
- break;
- case texture_format::cBC6HUnsigned:
- tinydds_fmt = TDDS_BC6H_UFLOAT_BLOCK;
- break;
- case texture_format::cBC7:
- tinydds_fmt = use_srgb_format ? TDDS_BC7_SRGB_BLOCK : TDDS_BC7_UNORM_BLOCK;
- break;
- default:
- {
- fprintf(stderr, "Warning: Unsupported format in write_dds_file().\n");
- return false;
- }
- }
-
- // DirectXTex's DDSView doesn't handle odd sizes textures correctly. RenderDoc loads them fine, however.
- // Trying to work around this here results in invalid mipmaps.
- //width = (width + 3) & ~3;
- //height = (height + 3) & ~3;
-
- bool status = TinyDDS_WriteImage(&cbs,
- &dds_data,
- width,
- height,
- 1,
- slices,
- total_levels,
- (TinyDDS_Format)tinydds_fmt,
- cubemap_flag,
- true,
- mipmap_sizes,
- mipmap_ptrs);
-
- if (!status)
- {
- fprintf(stderr, "write_dds_file: Failed creating DDS file\n");
- return false;
- }
-
- return true;
+ return false;
}
bool write_dds_file(const char* pFilename, const basisu::vector<gpu_image_vec>& gpu_images, bool cubemap_flag, bool use_srgb_format)
@@ -2201,188 +1998,6 @@ namespace basisu
bool read_uncompressed_dds_file(const char* pFilename, basisu::vector<image> &ldr_mips, basisu::vector<imagef>& hdr_mips)
{
- const uint32_t MAX_IMAGE_DIM = 16384;
-
- TinyDDS_Callbacks cbs;
-
- cbs.errorFn = [](void* user, char const* msg) { BASISU_NOTE_UNUSED(user); fprintf(stderr, "tinydds: %s\n", msg); };
- cbs.allocFn = [](void* user, size_t size) -> void* { BASISU_NOTE_UNUSED(user); return malloc(size); };
- cbs.freeFn = [](void* user, void* memory) { BASISU_NOTE_UNUSED(user); free(memory); };
- cbs.readFn = [](void* user, void* buffer, size_t byteCount) -> size_t { return (size_t)fread(buffer, 1, byteCount, (FILE*)user); };
-
-#ifdef _MSC_VER
- cbs.seekFn = [](void* user, int64_t ofs) -> bool { return _fseeki64((FILE*)user, ofs, SEEK_SET) == 0; };
- cbs.tellFn = [](void* user) -> int64_t { return _ftelli64((FILE*)user); };
-#else
- cbs.seekFn = [](void* user, int64_t ofs) -> bool { return fseek((FILE*)user, (long)ofs, SEEK_SET) == 0; };
- cbs.tellFn = [](void* user) -> int64_t { return (int64_t)ftell((FILE*)user); };
-#endif
-
- FILE* pFile = fopen_safe(pFilename, "rb");
- if (!pFile)
- {
- error_printf("Can't open .DDS file \"%s\"\n", pFilename);
- return false;
- }
-
- // These are the formats AMD Compressonator supports in its UI.
- enum dds_fmt
- {
- cRGBA32,
- cRGBA_HALF,
- cRGBA_FLOAT
- };
-
- bool status = false;
- dds_fmt fmt = cRGBA32;
- uint32_t width = 0, height = 0;
- bool hdr_flag = false;
- TinyDDS_Format tfmt = TDDS_UNDEFINED;
-
- TinyDDS_ContextHandle ctx = TinyDDS_CreateContext(&cbs, pFile);
- if (!ctx)
- goto failure;
-
- status = TinyDDS_ReadHeader(ctx);
- if (!status)
- {
- error_printf("Failed parsing DDS header in file \"%s\"\n", pFilename);
- goto failure;
- }
-
- if ((!TinyDDS_Is2D(ctx)) || (TinyDDS_ArraySlices(ctx) > 1) || (TinyDDS_IsCubemap(ctx)))
- {
- error_printf("Unsupported DDS texture type in file \"%s\"\n", pFilename);
- goto failure;
- }
-
- width = TinyDDS_Width(ctx);
- height = TinyDDS_Height(ctx);
-
- if (!width || !height)
- {
- error_printf("DDS texture dimensions invalid in file \"%s\"\n", pFilename);
- goto failure;
- }
-
- if ((width > MAX_IMAGE_DIM) || (height > MAX_IMAGE_DIM))
- {
- error_printf("DDS texture dimensions too large in file \"%s\"\n", pFilename);
- goto failure;
- }
-
- tfmt = TinyDDS_GetFormat(ctx);
- switch (tfmt)
- {
- case TDDS_R8G8B8A8_SRGB:
- case TDDS_R8G8B8A8_UNORM:
- case TDDS_B8G8R8A8_SRGB:
- case TDDS_B8G8R8A8_UNORM:
- fmt = cRGBA32;
- break;
- case TDDS_R16G16B16A16_SFLOAT:
- fmt = cRGBA_HALF;
- hdr_flag = true;
- break;
- case TDDS_R32G32B32A32_SFLOAT:
- fmt = cRGBA_FLOAT;
- hdr_flag = true;
- break;
- default:
- error_printf("File \"%s\" has an unsupported DDS texture format (only supports RGBA/BGRA 32bpp, RGBA HALF float, or RGBA FLOAT)\n", pFilename);
- goto failure;
- }
-
- if (hdr_flag)
- hdr_mips.resize(TinyDDS_NumberOfMipmaps(ctx));
- else
- ldr_mips.resize(TinyDDS_NumberOfMipmaps(ctx));
-
- for (uint32_t level = 0; level < TinyDDS_NumberOfMipmaps(ctx); level++)
- {
- const uint32_t level_width = TinyDDS_MipMapReduce(width, level);
- const uint32_t level_height = TinyDDS_MipMapReduce(height, level);
- const uint32_t total_level_texels = level_width * level_height;
-
- const void* pImage = TinyDDS_ImageRawData(ctx, level);
- const uint32_t image_size = TinyDDS_ImageSize(ctx, level);
-
- if (fmt == cRGBA32)
- {
- ldr_mips[level].resize(level_width, level_height);
-
- if ((ldr_mips[level].get_total_pixels() * sizeof(uint32_t) != image_size))
- {
- assert(0);
- goto failure;
- }
-
- memcpy(ldr_mips[level].get_ptr(), pImage, image_size);
-
- if ((tfmt == TDDS_B8G8R8A8_SRGB) || (tfmt == TDDS_B8G8R8A8_UNORM))
- {
- // Swap R and B components.
- uint32_t *pTexels = (uint32_t *)ldr_mips[level].get_ptr();
- for (uint32_t i = 0; i < total_level_texels; i++)
- {
- const uint32_t v = pTexels[i];
- const uint32_t r = (v >> 16) & 0xFF;
- const uint32_t b = v & 0xFF;
- pTexels[i] = r | (b << 16) | (v & 0xFF00FF00);
- }
- }
- }
- else if (fmt == cRGBA_FLOAT)
- {
- hdr_mips[level].resize(level_width, level_height);
-
- if ((hdr_mips[level].get_total_pixels() * sizeof(float) * 4 != image_size))
- {
- assert(0);
- goto failure;
- }
-
- memcpy(hdr_mips[level].get_ptr(), pImage, image_size);
- }
- else if (fmt == cRGBA_HALF)
- {
- hdr_mips[level].resize(level_width, level_height);
-
- if ((hdr_mips[level].get_total_pixels() * sizeof(basist::half_float) * 4 != image_size))
- {
- assert(0);
- goto failure;
- }
-
- // Unpack half to float.
- const basist::half_float* pSrc_comps = static_cast<const basist::half_float*>(pImage);
- vec4F* pDst_texels = hdr_mips[level].get_ptr();
-
- for (uint32_t i = 0; i < total_level_texels; i++)
- {
- (*pDst_texels)[0] = basist::half_to_float(pSrc_comps[0]);
- (*pDst_texels)[1] = basist::half_to_float(pSrc_comps[1]);
- (*pDst_texels)[2] = basist::half_to_float(pSrc_comps[2]);
- (*pDst_texels)[3] = basist::half_to_float(pSrc_comps[3]);
-
- pSrc_comps += 4;
- pDst_texels++;
- } // y
- }
- } // level
-
- TinyDDS_DestroyContext(ctx);
- fclose(pFile);
-
- return true;
-
- failure:
- if (ctx)
- TinyDDS_DestroyContext(ctx);
-
- if (pFile)
- fclose(pFile);
-
return false;
}

View file

@ -1,43 +0,0 @@
From b4a0fa23c13da413d94b99f307e401c3b83e0108 Mon Sep 17 00:00:00 2001
From: Ondrej Stava <ondrej.stava@gmail.com>
Date: Fri, 23 Apr 2021 18:59:45 -0700
Subject: [PATCH] Made it easier to use the library with external zstdlib
implementations (mostly in non CMake builds).
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
In our internal repository, we have our own version of zstdlib and introducing extra copy is both undesirable and potentially dangerous (due to ODR violations).
Co-authored-by: Rémi Verschelde <rverschelde@gmail.com>
---
encoder/basisu_comp.cpp | 2 +-
transcoder/basisu_transcoder.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/encoder/basisu_comp.cpp b/encoder/basisu_comp.cpp
index 41eae2b7..4e69e9e2 100644
--- a/encoder/basisu_comp.cpp
+++ b/encoder/basisu_comp.cpp
@@ -28,7 +28,7 @@
#endif
#if BASISD_SUPPORT_KTX2_ZSTD
-#include "../zstd/zstd.h"
+#include <zstd.h>
#endif
// Set to 1 to disable the mipPadding alignment workaround (which only seems to be needed when no key-values are written at all)
diff --git a/transcoder/basisu_transcoder.cpp b/transcoder/basisu_transcoder.cpp
index 3aeba0ee..c698861f 100644
--- a/transcoder/basisu_transcoder.cpp
+++ b/transcoder/basisu_transcoder.cpp
@@ -155,7 +155,7 @@
// 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"
+ #include <zstd.h>
#endif
#endif

View file

@ -1,5 +1,5 @@
// basisu.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
//
// Licensed under the Apache License, Version 2.0 (the "License");
@ -117,13 +117,26 @@ namespace basisu
typedef basisu::vector<uint64_t> uint64_vec;
typedef basisu::vector<int> int_vec;
typedef basisu::vector<bool> bool_vec;
typedef basisu::vector<float> float_vec;
void enable_debug_printf(bool enabled);
void debug_printf(const char *pFmt, ...);
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#endif
template <typename T> inline void clear_obj(T& obj) { memset(&obj, 0, sizeof(obj)); }
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
template <typename T0, typename T1> inline T0 lerp(T0 a, T0 b, T1 c) { return a + (b - a) * c; }
template <typename S> inline S maximum(S a, S b) { return (a > b) ? a : b; }
@ -162,10 +175,45 @@ namespace basisu
template<typename T> inline T open_range_check(T v, T minv, T maxv) { assert(v >= minv && v < maxv); BASISU_NOTE_UNUSED(minv); BASISU_NOTE_UNUSED(maxv); return v; }
template<typename T> inline T open_range_check(T v, T maxv) { assert(v < maxv); BASISU_NOTE_UNUSED(maxv); return v; }
// Open interval
inline bool in_bounds(int v, int l, int h)
{
return (v >= l) && (v < h);
}
// Closed interval
inline bool in_range(int v, int l, int h)
{
return (v >= l) && (v <= h);
}
inline uint32_t total_bits(uint32_t v) { uint32_t l = 0; for ( ; v > 0U; ++l) v >>= 1; return l; }
template<typename T> inline T saturate(T val) { return clamp(val, 0.0f, 1.0f); }
inline uint32_t get_bit(uint32_t src, int ndx)
{
assert(in_bounds(ndx, 0, 32));
return (src >> ndx) & 1;
}
inline bool is_bit_set(uint32_t src, int ndx)
{
return get_bit(src, ndx) != 0;
}
inline uint32_t get_bits(uint32_t val, int low, int high)
{
const int num_bits = (high - low) + 1;
assert(in_range(num_bits, 1, 32));
val >>= low;
if (num_bits != 32)
val &= ((1u << num_bits) - 1);
return val;
}
template<typename T, typename R> inline void append_vector(T &vec, const R *pObjs, size_t n)
{
if (n)
@ -267,6 +315,11 @@ namespace basisu
return true;
}
static inline uint32_t read_le_word(const uint8_t* pBytes)
{
return (pBytes[1] << 8U) | (pBytes[0]);
}
static inline uint32_t read_le_dword(const uint8_t *pBytes)
{
return (pBytes[3] << 24U) | (pBytes[2] << 16U) | (pBytes[1] << 8U) | (pBytes[0]);
@ -303,6 +356,10 @@ namespace basisu
return *this;
}
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#endif
inline operator uint32_t() const
{
switch (NumBytes)
@ -354,6 +411,9 @@ namespace basisu
}
}
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
};
enum eZero { cZero };
@ -402,8 +462,11 @@ namespace basisu
cBC3, // DXT5 (BC4/DXT5A block followed by a BC1/DXT1 block)
cBC4, // DXT5A
cBC5, // 3DC/DXN (two BC4/DXT5A blocks)
cBC6HSigned, // HDR
cBC6HUnsigned, // HDR
cBC7,
cASTC4x4, // LDR only
cASTC_LDR_4x4, // ASTC 4x4 LDR only
cASTC_HDR_4x4, // ASTC 4x4 HDR only (but may use LDR ASTC blocks internally)
cPVRTC1_4_RGB,
cPVRTC1_4_RGBA,
cATC_RGB,
@ -413,17 +476,22 @@ namespace basisu
cETC2_R11_EAC,
cETC2_RG11_EAC,
cUASTC4x4,
cUASTC_HDR_4x4,
cBC1_NV,
cBC1_AMD,
// Uncompressed/raw pixels
cRGBA32,
cRGB565,
cBGR565,
cRGBA4444,
cABGR4444
cABGR4444,
cRGBA_HALF,
cRGB_HALF,
cRGB_9E5
};
// This is bytes per block for GPU formats, or bytes per texel for uncompressed formats.
inline uint32_t get_bytes_per_block(texture_format fmt)
{
switch (fmt)
@ -443,13 +511,27 @@ namespace basisu
case texture_format::cETC2_R11_EAC:
return 8;
case texture_format::cRGBA32:
return sizeof(uint32_t) * 16;
case texture_format::cRGB_9E5:
return sizeof(uint32_t);
case texture_format::cRGB_HALF:
return sizeof(uint16_t) * 3;
case texture_format::cRGBA_HALF:
return sizeof(uint16_t) * 4;
case texture_format::cRGB565:
case texture_format::cBGR565:
case texture_format::cRGBA4444:
case texture_format::cABGR4444:
return sizeof(uint16_t);
default:
break;
}
// Everything else is 16 bytes/block.
return 16;
}
// This is qwords per block for GPU formats, or not valid for uncompressed formats.
inline uint32_t get_qwords_per_block(texture_format fmt)
{
return get_bytes_per_block(fmt) >> 3;
@ -473,6 +555,17 @@ namespace basisu
BASISU_NOTE_UNUSED(fmt);
return 4;
}
inline bool is_hdr_texture_format(texture_format fmt)
{
if (fmt == texture_format::cASTC_HDR_4x4)
return true;
if (fmt == texture_format::cUASTC_HDR_4x4)
return true;
if ((fmt == texture_format::cBC6HSigned) || (fmt == texture_format::cBC6HUnsigned))
return true;
return false;
}
} // namespace basisu

View file

@ -0,0 +1,102 @@
// File: basisu_astc_hdr_core.h
#pragma once
#include "basisu_astc_helpers.h"
namespace basist
{
struct astc_blk
{
uint8_t m_vals[16];
};
// ASTC_HDR_MAX_VAL is the maximum color component value that can be encoded.
// If the input has values higher than this, they need to be linearly scaled so all values are between [0,ASTC_HDR_MAX_VAL], and the linear scaling inverted in the shader.
const float ASTC_HDR_MAX_VAL = 65216.0f; // actually MAX_QLOG12_VAL
// Maximum usable QLOG encodings, and their floating point equivalent values, that don't result in NaN/Inf's.
const uint32_t MAX_QLOG7 = 123;
//const float MAX_QLOG7_VAL = 55296.0f;
const uint32_t MAX_QLOG8 = 247;
//const float MAX_QLOG8_VAL = 60416.0f;
const uint32_t MAX_QLOG9 = 495;
//const float MAX_QLOG9_VAL = 62976.0f;
const uint32_t MAX_QLOG10 = 991;
//const float MAX_QLOG10_VAL = 64256.0f;
const uint32_t MAX_QLOG11 = 1983;
//const float MAX_QLOG11_VAL = 64896.0f;
const uint32_t MAX_QLOG12 = 3967;
//const float MAX_QLOG12_VAL = 65216.0f;
const uint32_t MAX_QLOG16 = 63487;
const float MAX_QLOG16_VAL = 65504.0f;
const uint32_t NUM_MODE11_ENDPOINTS = 6, NUM_MODE7_ENDPOINTS = 4;
// Notes:
// qlog16_to_half(half_to_qlog16(half_val_as_int)) == half_val_as_int (is lossless)
// However, this is not lossless in the general sense.
inline half_float qlog16_to_half_slow(uint32_t qlog16)
{
assert(qlog16 <= 0xFFFF);
int C = qlog16;
int E = (C & 0xF800) >> 11;
int M = C & 0x7FF;
int Mt;
if (M < 512)
Mt = 3 * M;
else if (M >= 1536)
Mt = 5 * M - 2048;
else
Mt = 4 * M - 512;
int Cf = (E << 10) + (Mt >> 3);
return (half_float)Cf;
}
// This is not lossless
inline half_float qlog_to_half_slow(uint32_t qlog, uint32_t bits)
{
assert((bits >= 7U) && (bits <= 16U));
assert(qlog < (1U << bits));
int C = qlog << (16 - bits);
return qlog16_to_half_slow(C);
}
void astc_hdr_core_init();
void decode_mode7_to_qlog12_ise20(
const uint8_t* pEndpoints,
int e[2][3],
int* pScale);
bool decode_mode7_to_qlog12(
const uint8_t* pEndpoints,
int e[2][3],
int* pScale,
uint32_t ise_endpoint_range);
void decode_mode11_to_qlog12_ise20(
const uint8_t* pEndpoints,
int e[2][3]);
bool decode_mode11_to_qlog12(
const uint8_t* pEndpoints,
int e[2][3],
uint32_t ise_endpoint_range);
bool transcode_bc6h_1subset(half_float h_e[3][2], const astc_helpers::log_astc_block& best_blk, bc6h_block& transcoded_bc6h_blk);
bool transcode_bc6h_2subsets(uint32_t common_part_index, const astc_helpers::log_astc_block& best_blk, bc6h_block& transcoded_bc6h_blk);
bool astc_hdr_transcode_to_bc6h(const astc_blk& src_blk, bc6h_block& dst_blk);
bool astc_hdr_transcode_to_bc6h(const astc_helpers::log_astc_block& log_blk, bc6h_block& dst_blk);
} // namespace basist

File diff suppressed because it is too large Load diff

View file

@ -188,8 +188,9 @@ namespace basisu
#define BASISU_IS_SCALAR_TYPE(T) (scalar_type<T>::cFlag)
#if defined(__GNUC__) && __GNUC__<5
#define BASISU_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__)
#if !defined(BASISU_HAVE_STD_TRIVIALLY_COPYABLE) && defined(__GNUC__) && __GNUC__<5
//#define BASISU_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__)
#define BASISU_IS_TRIVIALLY_COPYABLE(...) __is_trivially_copyable(__VA_ARGS__)
#else
#define BASISU_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value
#endif
@ -286,8 +287,19 @@ namespace basisu
if (BASISU_IS_BITWISE_COPYABLE(T))
{
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#endif
if ((m_p) && (other.m_p))
memcpy(m_p, other.m_p, m_size * sizeof(T));
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
}
else
{
@ -330,8 +342,19 @@ namespace basisu
if (BASISU_IS_BITWISE_COPYABLE(T))
{
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#endif
if ((m_p) && (other.m_p))
memcpy(m_p, other.m_p, other.m_size * sizeof(T));
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
}
else
{
@ -501,7 +524,7 @@ namespace basisu
if (new_capacity > m_capacity)
{
if (!increase_capacity(new_capacity, false))
if (!increase_capacity(new_capacity, false, true))
return false;
}
else if (new_capacity < m_capacity)
@ -509,7 +532,8 @@ namespace basisu
// 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);
if (!tmp.increase_capacity(helpers::maximum(m_size, new_capacity), false, true))
return false;
tmp = *this;
swap(tmp);
}
@ -750,7 +774,21 @@ namespace basisu
}
// Copy "down" the objects to preserve, filling in the empty slots.
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#endif
memmove(pDst, pSrc, num_to_move * sizeof(T));
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
}
else
{
@ -1003,7 +1041,21 @@ namespace basisu
inline void set_all(const T& o)
{
if ((sizeof(T) == 1) && (scalar_type<T>::cFlag))
{
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#endif
memset(m_p, *reinterpret_cast<const uint8_t*>(&o), m_size);
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
}
else
{
T* pDst = m_p;
@ -1029,7 +1081,7 @@ namespace basisu
// Important: This method is used in Basis Universal. If you change how this container allocates memory, you'll need to change any users of this method.
inline bool grant_ownership(T* p, uint32_t size, uint32_t capacity)
{
// To to prevent the caller from obviously shooting themselves in the foot.
// To prevent the caller from obviously shooting themselves in the foot.
if (((p + capacity) > m_p) && (p < (m_p + m_capacity)))
{
// Can grant ownership of a block inside the container itself!

View file

@ -19,23 +19,30 @@ namespace basisu
if (m_capacity >= min_new_capacity)
return true;
size_t new_capacity = min_new_capacity;
if ((grow_hint) && (!helpers::is_power_of_2((uint64_t)new_capacity)))
uint64_t new_capacity_u64 = min_new_capacity;
if ((grow_hint) && (!helpers::is_power_of_2(new_capacity_u64)))
new_capacity_u64 = helpers::next_pow2(new_capacity_u64);
size_t new_capacity = (size_t)new_capacity_u64;
if (new_capacity != new_capacity_u64)
{
new_capacity = (size_t)helpers::next_pow2((uint64_t)new_capacity);
assert(new_capacity && (new_capacity > m_capacity));
if (new_capacity < min_new_capacity)
{
if (nofail)
return false;
fprintf(stderr, "vector too large\n");
abort();
}
if (nofail)
return false;
fprintf(stderr, "elemental_vector::increase_capacity: vector too large\n");
abort();
}
const size_t desired_size = element_size * new_capacity;
const uint64_t desired_size_u64 = (uint64_t)element_size * new_capacity;
const size_t desired_size = (size_t)desired_size_u64;
if (desired_size_u64 != desired_size)
{
if (nofail)
return false;
fprintf(stderr, "elemental_vector::increase_capacity: vector too large\n");
abort();
}
size_t actual_size = 0;
if (!pMover)
{
@ -46,11 +53,7 @@ namespace basisu
return false;
char buf[256];
#ifdef _MSC_VER
sprintf_s(buf, sizeof(buf), "vector: realloc() failed allocating %u bytes", (uint32_t)desired_size);
#else
sprintf(buf, "vector: realloc() failed allocating %u bytes", (uint32_t)desired_size);
#endif
snprintf(buf, sizeof(buf), "elemental_vector::increase_capacity: realloc() failed allocating %zu bytes", desired_size);
fprintf(stderr, "%s", buf);
abort();
}
@ -75,11 +78,7 @@ namespace basisu
return false;
char buf[256];
#ifdef _MSC_VER
sprintf_s(buf, sizeof(buf), "vector: malloc() failed allocating %u bytes", (uint32_t)desired_size);
#else
sprintf(buf, "vector: malloc() failed allocating %u bytes", (uint32_t)desired_size);
#endif
snprintf(buf, sizeof(buf), "elemental_vector::increase_capacity: malloc() failed allocating %zu bytes", desired_size);
fprintf(stderr, "%s", buf);
abort();
}

View file

@ -1,5 +1,5 @@
// basis_file_headers.h
// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -89,7 +89,8 @@ namespace basist
enum class basis_tex_format
{
cETC1S = 0,
cUASTC4x4 = 1
cUASTC4x4 = 1,
cUASTC_HDR_4x4 = 2
};
struct basis_file_header

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
// basisu_transcoder.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
//
// Licensed under the Apache License, Version 2.0 (the "License");
@ -29,6 +29,7 @@
// 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
// TODO - disable before checking in
#define BASISU_FORCE_DEVEL_MESSAGES 0
#endif
@ -55,7 +56,7 @@ namespace basist
cTFETC2_RGBA = 1, // Opaque+alpha, ETC2_EAC_A8 block followed by a ETC1 block, alpha channel will be opaque for opaque .basis files
// BC1-5, BC7 (desktop, some mobile devices)
cTFBC1_RGB = 2, // Opaque only, no punchthrough alpha support yet, transcodes alpha slice if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
cTFBC1_RGB = 2, // Opaque only, no punchthrough alpha support yet, transcodes alpha slice if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
cTFBC3_RGBA = 3, // Opaque+alpha, BC4 followed by a BC1 block, alpha channel will be opaque for opaque .basis files
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)
@ -63,10 +64,11 @@ namespace basist
// 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.
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.
// ASTC (mobile, Intel devices, hopefully all desktop GPU's one day)
cTFASTC_4x4_RGBA = 10, // Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files. Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.
cTFASTC_4x4_RGBA = 10, // LDR. Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files.
// LDR: Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.
// ATC (mobile, Adreno devices, this is a niche format)
cTFATC_RGB = 11, // Opaque, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. ATI ATC (GL_ATC_RGB_AMD)
@ -74,8 +76,8 @@ namespace basist
// FXT1 (desktop, Intel devices, this is a super obscure format)
cTFFXT1_RGB = 17, // Opaque only, uses exclusively CC_MIXED blocks. Notable for having a 8x4 block size. GL_3DFX_texture_compression_FXT1 is supported on Intel integrated GPU's (such as HD 630).
// 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.
// 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.
@ -83,13 +85,22 @@ namespace basist
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
cTFBC6H = 22, // HDR, RGB only, unsigned
cTFASTC_HDR_4x4_RGBA = 23, // HDR, RGBA (currently UASTC HDR is only RGB), unsigned
// Uncompressed (raw pixel) formats
// Note these uncompressed formats (RGBA32, 565, and 4444) can only be transcoded to from LDR input files (ETC1S or UASTC LDR).
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
cTFRGBA4444 = 16, // 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 0
// Note these uncompressed formats (HALF and 9E5) can only be transcoded to from HDR input files (UASTC HDR).
cTFRGB_HALF = 24, // 48bpp RGB half (16-bits/component, 3 components)
cTFRGBA_HALF = 25, // 64bpp RGBA half (16-bits/component, 4 components) (A will always currently 1.0, UASTC_HDR doesn't support alpha)
cTFRGB_9E5 = 26, // 32bpp RGB 9E5 (shared exponent, positive only, see GL_EXT_texture_shared_exponent)
cTFTotalTextureFormats = 22,
cTFTotalTextureFormats = 27,
// Old enums for compatibility with code compiled against previous versions
cTFETC1 = cTFETC1_RGB,
@ -124,6 +135,9 @@ namespace basist
// Returns true if the format supports an alpha channel.
bool basis_transcoder_format_has_alpha(transcoder_texture_format fmt);
// Returns true if the format is HDR.
bool basis_transcoder_format_is_hdr(transcoder_texture_format fmt);
// Returns the basisu::texture_format corresponding to the specified transcoder_texture_format.
basisu::texture_format basis_get_basisu_texture_format(transcoder_texture_format fmt);
@ -142,7 +156,7 @@ namespace basist
// 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);
// Returns true if the specified format was enabled at compile time.
// Returns true if the specified format was enabled at compile time, and is supported for the specific basis/ktx2 texture format (ETC1S, UASTC, or UASTC HDR).
bool basis_is_format_supported(transcoder_texture_format tex_type, basis_tex_format fmt = basis_tex_format::cETC1S);
// Validates that the output buffer is large enough to hold the entire transcoded texture.
@ -317,6 +331,42 @@ namespace basist
int channel0 = -1, int channel1 = -1);
};
class basisu_lowlevel_uastc_hdr_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_uastc_hdr_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, 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);
}
// Container independent transcoding
bool transcode_image(
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 slice_offset, uint32_t slice_length,
uint32_t decode_flags = 0,
bool has_alpha = false,
bool is_video = false,
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);
};
struct basisu_slice_info
{
uint32_t m_orig_width;
@ -530,6 +580,7 @@ namespace basist
private:
mutable basisu_lowlevel_etc1s_transcoder m_lowlevel_etc1s_decoder;
mutable basisu_lowlevel_uastc_transcoder m_lowlevel_uastc_decoder;
mutable basisu_lowlevel_uastc_hdr_transcoder m_lowlevel_uastc_hdr_decoder;
bool m_ready_to_transcode;
@ -612,10 +663,12 @@ namespace basist
#pragma pack(pop)
const uint32_t KTX2_VK_FORMAT_UNDEFINED = 0;
const uint32_t KTX2_FORMAT_UASTC_4x4_SFLOAT_BLOCK = 1000066000; // TODO, is this correct?
const uint32_t KTX2_KDF_DF_MODEL_UASTC = 166;
const uint32_t KTX2_KDF_DF_MODEL_UASTC_HDR = 167;
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_UASTC_BLOCK_SIZE = 16; // also the block size for UASTC_HDR
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
@ -800,13 +853,15 @@ namespace basist
// 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().
// Returns cETC1S, cUASTC4x4, or cUASTC_HDR_4x4. 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; }
bool is_hdr() const { return get_format() == basist::basis_tex_format::cUASTC_HDR_4x4; }
// 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; }
@ -913,6 +968,7 @@ namespace basist
basist::basisu_lowlevel_etc1s_transcoder m_etc1s_transcoder;
basist::basisu_lowlevel_uastc_transcoder m_uastc_transcoder;
basist::basisu_lowlevel_uastc_hdr_transcoder m_uastc_hdr_transcoder;
ktx2_transcoder_state m_def_transcoder_state;

View file

@ -1,5 +1,5 @@
// basisu_transcoder_internal.h - Universal texture format transcoder library.
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
//
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
//
@ -20,8 +20,9 @@
#pragma warning (disable: 4127) // conditional expression is constant
#endif
#define BASISD_LIB_VERSION 116
#define BASISD_VERSION_STRING "01.16"
// v1.50: Added UASTC HDR support
#define BASISD_LIB_VERSION 150
#define BASISD_VERSION_STRING "01.50"
#ifdef _DEBUG
#define BASISD_BUILD_DEBUG
@ -82,9 +83,15 @@ namespace basist
cRGBA4444_ALPHA,
cRGBA4444_COLOR_OPAQUE,
cRGBA4444,
cRGBA_HALF,
cRGB_HALF,
cRGB_9E5,
cUASTC_4x4,
cUASTC_4x4, // LDR, universal
cUASTC_HDR_4x4, // HDR, transcodes only to 4x4 HDR ASTC, BC6H, or uncompressed
cBC6H,
cASTC_HDR_4x4,
cTotalBlockFormats
};
@ -264,8 +271,8 @@ namespace basist
}
const basisu::uint8_vec &get_code_sizes() const { return m_code_sizes; }
const basisu::int_vec get_lookup() const { return m_lookup; }
const basisu::int16_vec get_tree() const { return m_tree; }
const basisu::int_vec &get_lookup() const { return m_lookup; }
const basisu::int16_vec &get_tree() const { return m_tree; }
bool is_valid() const { return m_code_sizes.size() > 0; }
@ -789,7 +796,198 @@ namespace basist
};
bool basis_block_format_is_uncompressed(block_format tex_type);
//------------------------------------
typedef uint16_t half_float;
const double MIN_DENORM_HALF_FLOAT = 0.000000059604645; // smallest positive subnormal number
const double MIN_HALF_FLOAT = 0.00006103515625; // smallest positive normal number
const double MAX_HALF_FLOAT = 65504.0; // largest normal number
inline uint32_t get_bits(uint32_t val, int low, int high)
{
const int num_bits = (high - low) + 1;
assert((num_bits >= 1) && (num_bits <= 32));
val >>= low;
if (num_bits != 32)
val &= ((1u << num_bits) - 1);
return val;
}
inline bool is_half_inf_or_nan(half_float v)
{
return get_bits(v, 10, 14) == 31;
}
inline bool is_half_denorm(half_float v)
{
int e = (v >> 10) & 31;
return !e;
}
inline int get_half_exp(half_float v)
{
int e = ((v >> 10) & 31);
return e ? (e - 15) : -14;
}
inline int get_half_mantissa(half_float v)
{
if (is_half_denorm(v))
return v & 0x3FF;
return (v & 0x3FF) | 0x400;
}
inline float get_half_mantissaf(half_float v)
{
return ((float)get_half_mantissa(v)) / 1024.0f;
}
inline int get_half_sign(half_float v)
{
return v ? ((v & 0x8000) ? -1 : 1) : 0;
}
inline bool half_is_signed(half_float v)
{
return (v & 0x8000) != 0;
}
#if 0
int hexp = get_half_exp(Cf);
float hman = get_half_mantissaf(Cf);
int hsign = get_half_sign(Cf);
float k = powf(2.0f, hexp) * hman * hsign;
if (is_half_inf_or_nan(Cf))
k = std::numeric_limits<float>::quiet_NaN();
#endif
half_float float_to_half(float val);
inline float half_to_float(half_float hval)
{
union { float f; uint32_t u; } x = { 0 };
uint32_t s = ((uint32_t)hval >> 15) & 1;
uint32_t e = ((uint32_t)hval >> 10) & 0x1F;
uint32_t m = (uint32_t)hval & 0x3FF;
if (!e)
{
if (!m)
{
// +- 0
x.u = s << 31;
return x.f;
}
else
{
// denormalized
while (!(m & 0x00000400))
{
m <<= 1;
--e;
}
++e;
m &= ~0x00000400;
}
}
else if (e == 31)
{
if (m == 0)
{
// +/- INF
x.u = (s << 31) | 0x7f800000;
return x.f;
}
else
{
// +/- NaN
x.u = (s << 31) | 0x7f800000 | (m << 13);
return x.f;
}
}
e = e + (127 - 15);
m = m << 13;
assert(s <= 1);
assert(m <= 0x7FFFFF);
assert(e <= 255);
x.u = m | (e << 23) | (s << 31);
return x.f;
}
// Originally from bc6h_enc.h
void bc6h_enc_init();
const uint32_t MAX_BLOG16_VAL = 0xFFFF;
// BC6H internals
const uint32_t NUM_BC6H_MODES = 14;
const uint32_t BC6H_LAST_MODE_INDEX = 13;
const uint32_t BC6H_FIRST_1SUBSET_MODE_INDEX = 10; // in the MS docs, this is "mode 11" (where the first mode is 1), 60 bits for endpoints (10.10, 10.10, 10.10), 63 bits for weights
const uint32_t TOTAL_BC6H_PARTITION_PATTERNS = 32;
extern const uint8_t g_bc6h_mode_sig_bits[NUM_BC6H_MODES][4]; // base, r, g, b
struct bc6h_bit_layout
{
int8_t m_comp; // R=0,G=1,B=2,D=3 (D=partition index)
int8_t m_index; // 0-3, 0-1 Low/High subset 1, 2-3 Low/High subset 2, -1=partition index (d)
int8_t m_last_bit;
int8_t m_first_bit; // may be -1 if a single bit, may be >m_last_bit if reversed
};
const uint32_t MAX_BC6H_LAYOUT_INDEX = 25;
extern const bc6h_bit_layout g_bc6h_bit_layouts[NUM_BC6H_MODES][MAX_BC6H_LAYOUT_INDEX];
extern const uint8_t g_bc6h_2subset_patterns[TOTAL_BC6H_PARTITION_PATTERNS][4][4]; // [y][x]
extern const uint8_t g_bc6h_weight3[8];
extern const uint8_t g_bc6h_weight4[16];
extern const int8_t g_bc6h_mode_lookup[32];
// Converts b16 to half float
inline half_float bc6h_blog16_to_half(uint32_t comp)
{
assert(comp <= 0xFFFF);
// scale the magnitude by 31/64
comp = (comp * 31u) >> 6u;
return (half_float)comp;
}
const uint32_t MAX_BC6H_HALF_FLOAT_AS_UINT = 0x7BFF;
// Inverts bc6h_blog16_to_half().
// Returns the nearest blog16 given a half value.
inline uint32_t bc6h_half_to_blog16(half_float h)
{
assert(h <= MAX_BC6H_HALF_FLOAT_AS_UINT);
return (h * 64 + 30) / 31;
}
struct bc6h_block
{
uint8_t m_bytes[16];
};
void bc6h_enc_block_mode10(bc6h_block* pPacked_block, const half_float pEndpoints[3][2], const uint8_t* pWeights);
void bc6h_enc_block_1subset_4bit_weights(bc6h_block* pPacked_block, const half_float pEndpoints[3][2], const uint8_t* pWeights);
void bc6h_enc_block_1subset_mode9_3bit_weights(bc6h_block* pPacked_block, const half_float pEndpoints[3][2], const uint8_t* pWeights);
void bc6h_enc_block_1subset_3bit_weights(bc6h_block* pPacked_block, const half_float pEndpoints[3][2], const uint8_t* pWeights);
void bc6h_enc_block_2subset_mode9_3bit_weights(bc6h_block* pPacked_block, uint32_t common_part_index, const half_float pEndpoints[2][3][2], const uint8_t* pWeights); // pEndpoints[subset][comp][lh_index]
void bc6h_enc_block_2subset_3bit_weights(bc6h_block* pPacked_block, uint32_t common_part_index, const half_float pEndpoints[2][3][2], const uint8_t* pWeights); // pEndpoints[subset][comp][lh_index]
bool bc6h_enc_block_solid_color(bc6h_block* pPacked_block, const half_float pColor[3]);
} // namespace basist

View file

@ -1,4 +1,4 @@
// Copyright (C) 2017-2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2017-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,4 +1,4 @@
// Copyright (C) 2017-2019 Binomial LLC. All Rights Reserved.
// Copyright (C) 2017-2024 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -13,6 +13,7 @@ namespace basist
const uint32_t UASTC_MODE_INDEX_SOLID_COLOR = 8;
const uint32_t TOTAL_ASTC_BC7_COMMON_PARTITIONS2 = 30;
const uint32_t TOTAL_ASTC_BC6H_COMMON_PARTITIONS2 = 27; // BC6H only supports only 5-bit pattern indices, BC7 supports 4-bit or 6-bit
const uint32_t TOTAL_ASTC_BC7_COMMON_PARTITIONS3 = 11;
const uint32_t TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS = 19;

View file

@ -1,7 +1,9 @@
##
## Bundle of CA Root Certificates
##
## Certificate data from Mozilla as of: Mon Mar 11 15:15:21 2024 GMT
## Certificate data from Mozilla as of: Sat Oct 19 21:26:09 2024 GMT
##
## Find updated versions here: https://curl.se/docs/caextract.html
##
## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates
@ -14,7 +16,7 @@
## Just configure this file as the SSLCACertificateFile.
##
## Conversion done with mk-ca-bundle.pl version 1.29.
## SHA256: 4d96bd539f4719e9ace493757afbe4a23ee8579de1c97fbebc50bba3c12e8c1e
## SHA256: 36105b01631f9fc03b1eca779b44a30a1a5890b9bf8dc07ccb001a07301e01cf
##
@ -2600,36 +2602,6 @@ vLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+
CAezNIm8BZ/3Hobui3A=
-----END CERTIFICATE-----
GLOBALTRUST 2020
================
-----BEGIN CERTIFICATE-----
MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQVQx
IzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVT
VCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYxMDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAh
BgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAy
MDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWi
D59bRatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9ZYybNpyrO
VPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3QWPKzv9pj2gOlTblzLmM
CcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPwyJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCm
fecqQjuCgGOlYx8ZzHyyZqjC0203b+J+BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKA
A1GqtH6qRNdDYfOiaxaJSaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9OR
JitHHmkHr96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj04KlG
DfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9MedKZssCz3AwyIDMvU
clOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIwq7ejMZdnrY8XD2zHc+0klGvIg5rQ
mjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUw
AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1Ud
IwQYMBaAFNwuH9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA
VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJCXtzoRlgHNQIw
4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd6IwPS3BD0IL/qMy/pJTAvoe9
iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS
8cE54+X1+NZK3TTN+2/BT+MAi1bikvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2
HcqtbepBEX4tdJP7wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxS
vTOBTI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6CMUO+1918
oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn4rnvyOL2NSl6dPrFf4IF
YqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+IaFvowdlxfv1k7/9nR4hYJS8+hge9+6jl
gqispdNpQ80xiEmEU5LAsTkbOYMBMMTyqfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg==
-----END CERTIFICATE-----
ANF Secure Server Root CA
=========================
-----BEGIN CERTIFICATE-----
@ -3579,3 +3551,116 @@ wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+ljX273CXE2whJdV/LItM3z7gLfEdxquVeE
HVlNjM7IDiPCtyaaEBRx/pOyiriA8A4QntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0
o82bNSQ3+pCTE4FCxpgmdTdmQRCsu/WU48IxK63nI1bMNSWSs1A=
-----END CERTIFICATE-----
FIRMAPROFESIONAL CA ROOT-A WEB
==============================
-----BEGIN CERTIFICATE-----
MIICejCCAgCgAwIBAgIQMZch7a+JQn81QYehZ1ZMbTAKBggqhkjOPQQDAzBuMQswCQYDVQQGEwJF
UzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UEYQwPVkFURVMtQTYyNjM0MDY4
MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENBIFJPT1QtQSBXRUIwHhcNMjIwNDA2MDkwMTM2
WhcNNDcwMzMxMDkwMTM2WjBuMQswCQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25h
bCBTQTEYMBYGA1UEYQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFM
IENBIFJPT1QtQSBXRUIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARHU+osEaR3xyrq89Zfe9MEkVz6
iMYiuYMQYneEMy3pA4jU4DP37XcsSmDq5G+tbbT4TIqk5B/K6k84Si6CcyvHZpsKjECcfIr28jlg
st7L7Ljkb+qbXbdTkBgyVcUgt5SjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUk+FD
Y1w8ndYn81LsF7Kpryz3dvgwHQYDVR0OBBYEFJPhQ2NcPJ3WJ/NS7Beyqa8s93b4MA4GA1UdDwEB
/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjAdfKR7w4l1M+E7qUW/Runpod3JIha3RxEL2Jq68cgL
cFBTApFwhVmpHqTm6iMxoAACMQD94vizrxa5HnPEluPBMBnYfubDl94cT7iJLzPrSA8Z94dGXSaQ
pYXFuXqUPoeovQA=
-----END CERTIFICATE-----
TWCA CYBER Root CA
==================
-----BEGIN CERTIFICATE-----
MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQMQswCQYDVQQG
EwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NB
IENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5WhcNNDcxMTIyMTU1OTU5WjBQMQswCQYDVQQG
EwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NB
IENZQkVSIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG+Moe2Qkgfh1s
Ts6P40czRJzHyWmqOlt47nDSkvgEs1JSHWdyKKHfi12VCv7qze33Kc7wb3+szT3vsxxFavcokPFh
V8UMxKNQXd7UtcsZyoC5dc4pztKFIuwCY8xEMCDa6pFbVuYdHNWdZsc/34bKS1PE2Y2yHer43CdT
o0fhYcx9tbD47nORxc5zb87uEB8aBs/pJ2DFTxnk684iJkXXYJndzk834H/nY62wuFm40AZoNWDT
Nq5xQwTxaWV4fPMf88oon1oglWa0zbfuj3ikRRjpJi+NmykosaS3Om251Bw4ckVYsV7r8Cibt4LK
/c/WMw+f+5eesRycnupfXtuq3VTpMCEobY5583WSjCb+3MX2w7DfRFlDo7YDKPYIMKoNM+HvnKkH
IuNZW0CP2oi3aQiotyMuRAlZN1vH4xfyIutuOVLF3lSnmMlLIJXcRolftBL5hSmO68gnFSDAS9TM
fAxsNAwmmyYxpjyn9tnQS6Jk/zuZQXLB4HCX8SS7K8R0IrGsayIyJNN4KsDAoS/xUgXJP+92ZuJF
2A09rZXIx4kmyA+upwMu+8Ff+iDhcK2wZSA3M2Cw1a/XDBzCkHDXShi8fgGwsOsVHkQGzaRP6AzR
wyAQ4VRlnrZR0Bp2a0JaWHY06rc3Ga4udfmW5cFZ95RXKSWNOkyrTZpB0F8mAwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSdhWEUfMFib5do5E83
QOGt4A1WNzAdBgNVHQ4EFgQUnYVhFHzBYm+XaORPN0DhreANVjcwDQYJKoZIhvcNAQEMBQADggIB
AGSPesRiDrWIzLjHhg6hShbNcAu3p4ULs3a2D6f/CIsLJc+o1IN1KriWiLb73y0ttGlTITVX1olN
c79pj3CjYcya2x6a4CD4bLubIp1dhDGaLIrdaqHXKGnK/nZVekZn68xDiBaiA9a5F/gZbG0jAn/x
X9AKKSM70aoK7akXJlQKTcKlTfjF/biBzysseKNnTKkHmvPfXvt89YnNdJdhEGoHK4Fa0o635yDR
IG4kqIQnoVesqlVYL9zZyvpoBJ7tRCT5dEA7IzOrg1oYJkK2bVS1FmAwbLGg+LhBoF1JSdJlBTrq
/p1hvIbZv97Tujqxf36SNI7JAG7cmL3c7IAFrQI932XtCwP39xaEBDG6k5TY8hL4iuO/Qq+n1M0R
FxbIQh0UqEL20kCGoE8jypZFVmAGzbdVAaYBlGX+bgUJurSkquLvWL69J1bY73NxW0Qz8ppy6rBe
Pm6pUlvscG21h483XjyMnM7k8M4MZ0HMzvaAq07MTFb1wWFZk7Q+ptq4NxKfKjLji7gh7MMrZQzv
It6IKTtM1/r+t+FHvpw+PoP7UV31aPcuIYXcv/Fa4nzXxeSDwWrruoBa3lwtcHb4yOWHh8qgnaHl
IhInD0Q9HWzq1MKLL295q39QpsQZp6F6t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X
-----END CERTIFICATE-----
SecureSign Root CA12
====================
-----BEGIN CERTIFICATE-----
MIIDcjCCAlqgAwIBAgIUZvnHwa/swlG07VOX5uaCwysckBYwDQYJKoZIhvcNAQELBQAwUTELMAkG
A1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRT
ZWN1cmVTaWduIFJvb3QgQ0ExMjAeFw0yMDA0MDgwNTM2NDZaFw00MDA0MDgwNTM2NDZaMFExCzAJ
BgNVBAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMU
U2VjdXJlU2lnbiBSb290IENBMTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6OcE3
emhFKxS06+QT61d1I02PJC0W6K6OyX2kVzsqdiUzg2zqMoqUm048luT9Ub+ZyZN+v/mtp7JIKwcc
J/VMvHASd6SFVLX9kHrko+RRWAPNEHl57muTH2SOa2SroxPjcf59q5zdJ1M3s6oYwlkm7Fsf0uZl
fO+TvdhYXAvA42VvPMfKWeP+bl+sg779XSVOKik71gurFzJ4pOE+lEa+Ym6b3kaosRbnhW70CEBF
EaCeVESE99g2zvVQR9wsMJvuwPWW0v4JhscGWa5Pro4RmHvzC1KqYiaqId+OJTN5lxZJjfU+1Uef
NzFJM3IFTQy2VYzxV4+Kh9GtxRESOaCtAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
AQH/BAQDAgEGMB0GA1UdDgQWBBRXNPN0zwRL1SXm8UC2LEzZLemgrTANBgkqhkiG9w0BAQsFAAOC
AQEAPrvbFxbS8hQBICw4g0utvsqFepq2m2um4fylOqyttCg6r9cBg0krY6LdmmQOmFxv3Y67ilQi
LUoT865AQ9tPkbeGGuwAtEGBpE/6aouIs3YIcipJQMPTw4WJmBClnW8Zt7vPemVV2zfrPIpyMpce
mik+rY3moxtt9XUa5rBouVui7mlHJzWhhpmA8zNL4WukJsPvdFlseqJkth5Ew1DgDzk9qTPxpfPS
vWKErI4cqc1avTc7bgoitPQV55FYxTpE05Uo2cBl6XLK0A+9H7MV2anjpEcJnuDLN/v9vZfVvhga
aaI5gdka9at/yOPiZwud9AzqVN/Ssq+xIvEg37xEHA==
-----END CERTIFICATE-----
SecureSign Root CA14
====================
-----BEGIN CERTIFICATE-----
MIIFcjCCA1qgAwIBAgIUZNtaDCBO6Ncpd8hQJ6JaJ90t8sswDQYJKoZIhvcNAQEMBQAwUTELMAkG
A1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRT
ZWN1cmVTaWduIFJvb3QgQ0ExNDAeFw0yMDA0MDgwNzA2MTlaFw00NTA0MDgwNzA2MTlaMFExCzAJ
BgNVBAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMU
U2VjdXJlU2lnbiBSb290IENBMTQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF0nqh
1oq/FjHQmNE6lPxauG4iwWL3pwon71D2LrGeaBLwbCRjOfHw3xDG3rdSINVSW0KZnvOgvlIfX8xn
bacuUKLBl422+JX1sLrcneC+y9/3OPJH9aaakpUqYllQC6KxNedlsmGy6pJxaeQp8E+BgQQ8sqVb
1MWoWWd7VRxJq3qdwudzTe/NCcLEVxLbAQ4jeQkHO6Lo/IrPj8BGJJw4J+CDnRugv3gVEOuGTgpa
/d/aLIJ+7sr2KeH6caH3iGicnPCNvg9JkdjqOvn90Ghx2+m1K06Ckm9mH+Dw3EzsytHqunQG+bOE
kJTRX45zGRBdAuVwpcAQ0BB8b8VYSbSwbprafZX1zNoCr7gsfXmPvkPx+SgojQlD+Ajda8iLLCSx
jVIHvXiby8posqTdDEx5YMaZ0ZPxMBoH064iwurO8YQJzOAUbn8/ftKChazcqRZOhaBgy/ac18iz
ju3Gm5h1DVXoX+WViwKkrkMpKBGk5hIwAUt1ax5mnXkvpXYvHUC0bcl9eQjs0Wq2XSqypWa9a4X0
dFbD9ed1Uigspf9mR6XU/v6eVL9lfgHWMI+lNpyiUBzuOIABSMbHdPTGrMNASRZhdCyvjG817XsY
AFs2PJxQDcqSMxDxJklt33UkN4Ii1+iW/RVLApY+B3KVfqs9TC7XyvDf4Fg/LS8EmjijAQIDAQAB
o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUBpOjCl4oaTeq
YR3r6/wtbyPk86AwDQYJKoZIhvcNAQEMBQADggIBAJaAcgkGfpzMkwQWu6A6jZJOtxEaCnFxEM0E
rX+lRVAQZk5KQaID2RFPeje5S+LGjzJmdSX7684/AykmjbgWHfYfM25I5uj4V7Ibed87hwriZLoA
ymzvftAj63iP/2SbNDefNWWipAA9EiOWWF3KY4fGoweITedpdopTzfFP7ELyk+OZpDc8h7hi2/Ds
Hzc/N19DzFGdtfCXwreFamgLRB7lUe6TzktuhsHSDCRZNhqfLJGP4xjblJUK7ZGqDpncllPjYYPG
FrojutzdfhrGe0K22VoF3Jpf1d+42kd92jjbrDnVHmtsKheMYc2xbXIBw8MgAGJoFjHVdqqGuw6q
nsb58Nn4DSEC5MUoFlkRudlpcyqSeLiSV5sI8jrlL5WwWLdrIBRtFO8KvH7YVdiI2i/6GaX7i+B/
OfVyK4XELKzvGUWSTLNhB9xNH27SgRNcmvMSZ4PPmz+Ln52kuaiWA3rF7iDeM9ovnhp6dB7h7sxa
OgTdsxoEqBRjrLdHEoOabPXm6RUVkRqEGQ6UROcSjiVbgGcZ3GOTEAtlLor6CZpO2oYofaphNdgO
pygau1LgePhsumywbrmHXumZNTfxPWQrqaA0k89jL9WB365jJ6UeTo3cKXhZ+PmhIIynJkBugnLN
eLLIjzwec+fBH7/PzqUqm9tEZDKgu39cJRNItX+S
-----END CERTIFICATE-----
SecureSign Root CA15
====================
-----BEGIN CERTIFICATE-----
MIICIzCCAamgAwIBAgIUFhXHw9hJp75pDIqI7fBw+d23PocwCgYIKoZIzj0EAwMwUTELMAkGA1UE
BhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRTZWN1
cmVTaWduIFJvb3QgQ0ExNTAeFw0yMDA0MDgwODMyNTZaFw00NTA0MDgwODMyNTZaMFExCzAJBgNV
BAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2Vj
dXJlU2lnbiBSb290IENBMTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQLUHSNZDKZmbPSYAi4Io5G
dCx4wCtELW1fHcmuS1Iggz24FG1Th2CeX2yF2wYUleDHKP+dX+Sq8bOLbe1PL0vJSpSRZHX+AezB
2Ot6lHhWGENfa4HL9rzatAy2KZMIaY+jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
AgEGMB0GA1UdDgQWBBTrQciu/NWeUUj1vYv0hyCTQSvT9DAKBggqhkjOPQQDAwNoADBlAjEA2S6J
fl5OpBEHvVnCB96rMjhTKkZEBhd6zlHp4P9mLQlO4E/0BdGF9jVg3PVys0Z9AjBEmEYagoUeYWmJ
SwdLZrWeqrqgHkHZAXQ6bkU6iYAZezKYVWOr62Nuk22rGwlgMU4=
-----END CERTIFICATE-----

View file

@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 24 November 2023 *
* Date : 12 May 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : Core Clipper Library structures and functions *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
@ -19,6 +19,7 @@
#include <algorithm>
#include <climits>
#include <numeric>
#include <optional>
#include "clipper2/clipper.version.h"
#define CLIPPER2_THROW(exception) std::abort()
@ -51,19 +52,19 @@ namespace Clipper2Lib
// error codes (2^n)
const int precision_error_i = 1; // non-fatal
const int scale_error_i = 2; // non-fatal
const int non_pair_error_i = 4; // non-fatal
const int undefined_error_i = 32; // fatal
const int scale_error_i = 2; // non-fatal
const int non_pair_error_i = 4; // non-fatal
const int undefined_error_i = 32; // fatal
const int range_error_i = 64;
#ifndef PI
static const double PI = 3.141592653589793238;
#endif
#ifdef CLIPPER2_MAX_PRECISION
const int MAX_DECIMAL_PRECISION = CLIPPER2_MAX_PRECISION;
#ifdef CLIPPER2_MAX_DECIMAL_PRECISION
const int CLIPPER2_MAX_DEC_PRECISION = CLIPPER2_MAX_DECIMAL_PRECISION;
#else
const int MAX_DECIMAL_PRECISION = 8; // see Discussions #564
const int CLIPPER2_MAX_DEC_PRECISION = 8; // see Discussions #564
#endif
static const int64_t MAX_COORD = INT64_MAX >> 2;
@ -74,7 +75,7 @@ namespace Clipper2Lib
static const double MAX_DBL = (std::numeric_limits<double>::max)();
static void DoError(int error_code)
static void DoError([[maybe_unused]] int error_code)
{
#if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS)
switch (error_code)
@ -95,6 +96,13 @@ namespace Clipper2Lib
#endif
}
// can we call std::round on T? (default false) (#824)
template <typename T, typename = void>
struct is_round_invocable : std::false_type {};
template <typename T>
struct is_round_invocable<T, std::void_t<decltype(std::round(std::declval<T>()))>> : std::true_type {};
//By far the most widely used filling rules for polygons are EvenOdd
//and NonZero, sometimes called Alternate and Winding respectively.
@ -113,8 +121,8 @@ namespace Clipper2Lib
template <typename T2>
inline void Init(const T2 x_ = 0, const T2 y_ = 0, const int64_t z_ = 0)
{
if constexpr (std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T2>::is_integer)
if constexpr (std::is_integral_v<T> &&
is_round_invocable<T2>::value && !std::is_integral_v<T2>)
{
x = static_cast<T>(std::round(x_));
y = static_cast<T>(std::round(y_));
@ -143,6 +151,12 @@ namespace Clipper2Lib
Init(p.x, p.y, p.z);
}
template <typename T2>
explicit Point(const Point<T2>& p, int64_t z_)
{
Init(p.x, p.y, z_);
}
Point operator * (const double scale) const
{
return Point(x * scale, y * scale, z);
@ -161,8 +175,8 @@ namespace Clipper2Lib
template <typename T2>
inline void Init(const T2 x_ = 0, const T2 y_ = 0)
{
if constexpr (std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T2>::is_integer)
if constexpr (std::is_integral_v<T> &&
is_round_invocable<T2>::value && !std::is_integral_v<T2>)
{
x = static_cast<T>(std::round(x_));
y = static_cast<T>(std::round(y_));
@ -244,6 +258,14 @@ namespace Clipper2Lib
(std::numeric_limits<double>::max)(),
(std::numeric_limits<double>::max)());
template<typename T>
static inline Point<T> MidPoint(const Point<T>& p1, const Point<T>& p2)
{
Point<T> result;
result.x = (p1.x + p2.x) / 2;
result.y = (p1.y + p2.y) / 2;
return result;
}
// Rect ------------------------------------------------------------------------
@ -275,10 +297,19 @@ namespace Clipper2Lib
else
{
left = top = (std::numeric_limits<T>::max)();
right = bottom = (std::numeric_limits<T>::lowest)();
right = bottom = std::numeric_limits<T>::lowest();
}
}
static Rect<T> InvalidRect()
{
return {
(std::numeric_limits<T>::max)(),
(std::numeric_limits<T>::max)(),
std::numeric_limits<T>::lowest(),
std::numeric_limits<T>::lowest() };
}
bool IsValid() const { return left != (std::numeric_limits<T>::max)(); }
T Width() const { return right - left; }
@ -329,7 +360,7 @@ namespace Clipper2Lib
};
bool operator==(const Rect<T>& other) const {
return left == other.left && right == other.right &&
return left == other.left && right == other.right &&
top == other.top && bottom == other.bottom;
}
@ -344,8 +375,8 @@ namespace Clipper2Lib
{
Rect<T1> result;
if constexpr (std::numeric_limits<T1>::is_integer &&
!std::numeric_limits<T2>::is_integer)
if constexpr (std::is_integral_v<T1> &&
is_round_invocable<T2>::value && !std::is_integral_v<T2>)
{
result.left = static_cast<T1>(std::round(rect.left * scale));
result.top = static_cast<T1>(std::round(rect.top * scale));
@ -354,32 +385,24 @@ namespace Clipper2Lib
}
else
{
result.left = rect.left * scale;
result.top = rect.top * scale;
result.right = rect.right * scale;
result.bottom = rect.bottom * scale;
result.left = static_cast<T1>(rect.left * scale);
result.top = static_cast<T1>(rect.top * scale);
result.right = static_cast<T1>(rect.right * scale);
result.bottom = static_cast<T1>(rect.bottom * scale);
}
return result;
}
static const Rect64 InvalidRect64 = Rect64(
(std::numeric_limits<int64_t>::max)(),
(std::numeric_limits<int64_t>::max)(),
(std::numeric_limits<int64_t>::lowest)(),
(std::numeric_limits<int64_t>::lowest)());
static const RectD InvalidRectD = RectD(
(std::numeric_limits<double>::max)(),
(std::numeric_limits<double>::max)(),
(std::numeric_limits<double>::lowest)(),
(std::numeric_limits<double>::lowest)());
static const Rect64 InvalidRect64 = Rect64::InvalidRect();
static const RectD InvalidRectD = RectD::InvalidRect();
template <typename T>
Rect<T> GetBounds(const Path<T>& path)
{
auto xmin = (std::numeric_limits<T>::max)();
auto ymin = (std::numeric_limits<T>::max)();
auto xmax = std::numeric_limits<T>::lowest();
auto ymax = std::numeric_limits<T>::lowest();
T xmin = (std::numeric_limits<T>::max)();
T ymin = (std::numeric_limits<T>::max)();
T xmax = std::numeric_limits<T>::lowest();
T ymax = std::numeric_limits<T>::lowest();
for (const auto& p : path)
{
if (p.x < xmin) xmin = p.x;
@ -393,17 +416,52 @@ namespace Clipper2Lib
template <typename T>
Rect<T> GetBounds(const Paths<T>& paths)
{
auto xmin = (std::numeric_limits<T>::max)();
auto ymin = (std::numeric_limits<T>::max)();
auto xmax = std::numeric_limits<T>::lowest();
auto ymax = std::numeric_limits<T>::lowest();
T xmin = (std::numeric_limits<T>::max)();
T ymin = (std::numeric_limits<T>::max)();
T xmax = std::numeric_limits<T>::lowest();
T ymax = std::numeric_limits<T>::lowest();
for (const Path<T>& path : paths)
for (const Point<T>& p : path)
{
if (p.x < xmin) xmin = p.x;
if (p.x > xmax) xmax = p.x;
if (p.y < ymin) ymin = p.y;
if (p.y > ymax) ymax = p.y;
if (p.x < xmin) xmin = p.x;
if (p.x > xmax) xmax = p.x;
if (p.y < ymin) ymin = p.y;
if (p.y > ymax) ymax = p.y;
}
return Rect<T>(xmin, ymin, xmax, ymax);
}
template <typename T, typename T2>
Rect<T> GetBounds(const Path<T2>& path)
{
T xmin = (std::numeric_limits<T>::max)();
T ymin = (std::numeric_limits<T>::max)();
T xmax = std::numeric_limits<T>::lowest();
T ymax = std::numeric_limits<T>::lowest();
for (const auto& p : path)
{
if (p.x < xmin) xmin = static_cast<T>(p.x);
if (p.x > xmax) xmax = static_cast<T>(p.x);
if (p.y < ymin) ymin = static_cast<T>(p.y);
if (p.y > ymax) ymax = static_cast<T>(p.y);
}
return Rect<T>(xmin, ymin, xmax, ymax);
}
template <typename T, typename T2>
Rect<T> GetBounds(const Paths<T2>& paths)
{
T xmin = (std::numeric_limits<T>::max)();
T ymin = (std::numeric_limits<T>::max)();
T xmax = std::numeric_limits<T>::lowest();
T ymax = std::numeric_limits<T>::lowest();
for (const Path<T2>& path : paths)
for (const Point<T2>& p : path)
{
if (p.x < xmin) xmin = static_cast<T>(p.x);
if (p.x > xmax) xmax = static_cast<T>(p.x);
if (p.y < ymin) ymin = static_cast<T>(p.y);
if (p.y > ymax) ymax = static_cast<T>(p.y);
}
return Rect<T>(xmin, ymin, xmax, ymax);
}
@ -431,7 +489,7 @@ namespace Clipper2Lib
template <typename T1, typename T2>
inline Path<T1> ScalePath(const Path<T2>& path,
inline Path<T1> ScalePath(const Path<T2>& path,
double scale_x, double scale_y, int& error_code)
{
Path<T1> result;
@ -447,11 +505,11 @@ namespace Clipper2Lib
result.reserve(path.size());
#ifdef USINGZ
std::transform(path.begin(), path.end(), back_inserter(result),
[scale_x, scale_y](const auto& pt)
[scale_x, scale_y](const auto& pt)
{ return Point<T1>(pt.x * scale_x, pt.y * scale_y, pt.z); });
#else
std::transform(path.begin(), path.end(), back_inserter(result),
[scale_x, scale_y](const auto& pt)
[scale_x, scale_y](const auto& pt)
{ return Point<T1>(pt.x * scale_x, pt.y * scale_y); });
#endif
return result;
@ -465,20 +523,19 @@ namespace Clipper2Lib
}
template <typename T1, typename T2>
inline Paths<T1> ScalePaths(const Paths<T2>& paths,
inline Paths<T1> ScalePaths(const Paths<T2>& paths,
double scale_x, double scale_y, int& error_code)
{
Paths<T1> result;
if constexpr (std::numeric_limits<T1>::is_integer &&
!std::numeric_limits<T2>::is_integer)
if constexpr (std::is_integral_v<T1>)
{
RectD r = GetBounds(paths);
RectD r = GetBounds<double, T2>(paths);
if ((r.left * scale_x) < min_coord ||
(r.right * scale_x) > max_coord ||
(r.top * scale_y) < min_coord ||
(r.bottom * scale_y) > max_coord)
{
{
error_code |= range_error_i;
DoError(range_error_i);
return result; // empty path
@ -493,7 +550,7 @@ namespace Clipper2Lib
}
template <typename T1, typename T2>
inline Paths<T1> ScalePaths(const Paths<T2>& paths,
inline Paths<T1> ScalePaths(const Paths<T2>& paths,
double scale, int& error_code)
{
return ScalePaths<T1, T2>(paths, scale, scale, error_code);
@ -590,20 +647,92 @@ namespace Clipper2Lib
// Miscellaneous ------------------------------------------------------------
inline void CheckPrecision(int& precision, int& error_code)
inline void CheckPrecisionRange(int& precision, int& error_code)
{
if (precision >= -MAX_DECIMAL_PRECISION && precision <= MAX_DECIMAL_PRECISION) return;
if (precision >= -CLIPPER2_MAX_DEC_PRECISION &&
precision <= CLIPPER2_MAX_DEC_PRECISION) return;
error_code |= precision_error_i; // non-fatal error
DoError(precision_error_i); // does nothing unless exceptions enabled
precision = precision > 0 ? MAX_DECIMAL_PRECISION : -MAX_DECIMAL_PRECISION;
DoError(precision_error_i); // does nothing when exceptions are disabled
precision = precision > 0 ? CLIPPER2_MAX_DEC_PRECISION : -CLIPPER2_MAX_DEC_PRECISION;
}
inline void CheckPrecision(int& precision)
inline void CheckPrecisionRange(int& precision)
{
int error_code = 0;
CheckPrecision(precision, error_code);
CheckPrecisionRange(precision, error_code);
}
inline int TriSign(int64_t x) // returns 0, 1 or -1
{
return (x > 0) - (x < 0);
}
struct MultiplyUInt64Result
{
const uint64_t result = 0;
const uint64_t carry = 0;
bool operator==(const MultiplyUInt64Result& other) const
{
return result == other.result && carry == other.carry;
};
};
inline MultiplyUInt64Result Multiply(uint64_t a, uint64_t b) // #834, #835
{
const auto lo = [](uint64_t x) { return x & 0xFFFFFFFF; };
const auto hi = [](uint64_t x) { return x >> 32; };
const uint64_t x1 = lo(a) * lo(b);
const uint64_t x2 = hi(a) * lo(b) + hi(x1);
const uint64_t x3 = lo(a) * hi(b) + lo(x2);
const uint64_t result = lo(x3) << 32 | lo(x1);
const uint64_t carry = hi(a) * hi(b) + hi(x2) + hi(x3);
return { result, carry };
}
// returns true if (and only if) a * b == c * d
inline bool ProductsAreEqual(int64_t a, int64_t b, int64_t c, int64_t d)
{
// Work around LLVM issue: https://github.com/llvm/llvm-project/issues/16778
// Details: https://github.com/godotengine/godot/pull/95964#issuecomment-2306581804
//#if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
// const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
// const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
// return ab == cd;
//#else
// nb: unsigned values needed for calculating overflow carry
const auto abs_a = static_cast<uint64_t>(std::abs(a));
const auto abs_b = static_cast<uint64_t>(std::abs(b));
const auto abs_c = static_cast<uint64_t>(std::abs(c));
const auto abs_d = static_cast<uint64_t>(std::abs(d));
const auto abs_ab = Multiply(abs_a, abs_b);
const auto abs_cd = Multiply(abs_c, abs_d);
// nb: it's important to differentiate 0 values here from other values
const auto sign_ab = TriSign(a) * TriSign(b);
const auto sign_cd = TriSign(c) * TriSign(d);
return abs_ab == abs_cd && sign_ab == sign_cd;
// #endif
}
template <typename T>
inline bool IsCollinear(const Point<T>& pt1,
const Point<T>& sharedPt, const Point<T>& pt2) // #777
{
const auto a = sharedPt.x - pt1.x;
const auto b = pt2.y - sharedPt.y;
const auto c = sharedPt.y - pt1.y;
const auto d = pt2.x - sharedPt.x;
// When checking for collinearity with very large coordinate values
// then ProductsAreEqual is more accurate than using CrossProduct.
return ProductsAreEqual(a, b, c, d);
}
template <typename T>
inline double CrossProduct(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3) {
return (static_cast<double>(pt2.x - pt1.x) * static_cast<double>(pt3.y -
@ -635,15 +764,17 @@ namespace Clipper2Lib
}
template <typename T>
inline double DistanceFromLineSqrd(const Point<T>& pt, const Point<T>& ln1, const Point<T>& ln2)
inline double PerpendicDistFromLineSqrd(const Point<T>& pt,
const Point<T>& line1, const Point<T>& line2)
{
//perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
//see http://en.wikipedia.org/wiki/Perpendicular_distance
double A = static_cast<double>(ln1.y - ln2.y);
double B = static_cast<double>(ln2.x - ln1.x);
double C = A * ln1.x + B * ln1.y;
C = A * pt.x + B * pt.y - C;
return (C * C) / (A * A + B * B);
double a = static_cast<double>(pt.x - line1.x);
double b = static_cast<double>(pt.y - line1.y);
double c = static_cast<double>(line2.x - line1.x);
double d = static_cast<double>(line2.y - line1.y);
if (c == 0 && d == 0) return 0;
return Sqr(a * d - c * b) / (c * c + d * d);
}
template <typename T>
@ -663,7 +794,7 @@ namespace Clipper2Lib
}
if (cnt & 1)
a += static_cast<double>(it2->y + it1->y) * (it2->x - it1->x);
return a * 0.5;
return (a * 0.5);
}
template <typename T>
@ -681,16 +812,73 @@ namespace Clipper2Lib
template <typename T>
inline bool IsPositive(const Path<T>& poly)
{
// A curve has positive orientation [and area] if a region 'R'
// A curve has positive orientation [and area] if a region 'R'
// is on the left when traveling around the outside of 'R'.
//https://mathworld.wolfram.com/CurveOrientation.html
//nb: This statement is premised on using Cartesian coordinates
return Area<T>(poly) >= 0;
}
inline bool GetIntersectPoint(const Point64& ln1a, const Point64& ln1b,
const Point64& ln2a, const Point64& ln2b, Point64& ip)
{
#if CLIPPER2_HI_PRECISION
// caution: this will compromise performance
// https://github.com/AngusJohnson/Clipper2/issues/317#issuecomment-1314023253
// See also CPP/BenchMark/GetIntersectPtBenchmark.cpp
#define CC_MIN(x,y) ((x)>(y)?(y):(x))
#define CC_MAX(x,y) ((x)<(y)?(y):(x))
template<typename T>
inline bool GetSegmentIntersectPt(const Point<T>& ln1a, const Point<T>& ln1b,
const Point<T>& ln2a, const Point<T>& ln2b, Point<T>& ip)
{
double ln1dy = static_cast<double>(ln1b.y - ln1a.y);
double ln1dx = static_cast<double>(ln1a.x - ln1b.x);
double ln2dy = static_cast<double>(ln2b.y - ln2a.y);
double ln2dx = static_cast<double>(ln2a.x - ln2b.x);
double det = (ln2dy * ln1dx) - (ln1dy * ln2dx);
if (det == 0.0) return false;
T bb0minx = CC_MIN(ln1a.x, ln1b.x);
T bb0miny = CC_MIN(ln1a.y, ln1b.y);
T bb0maxx = CC_MAX(ln1a.x, ln1b.x);
T bb0maxy = CC_MAX(ln1a.y, ln1b.y);
T bb1minx = CC_MIN(ln2a.x, ln2b.x);
T bb1miny = CC_MIN(ln2a.y, ln2b.y);
T bb1maxx = CC_MAX(ln2a.x, ln2b.x);
T bb1maxy = CC_MAX(ln2a.y, ln2b.y);
if constexpr (std::is_integral_v<T>)
{
int64_t originx = (CC_MIN(bb0maxx, bb1maxx) + CC_MAX(bb0minx, bb1minx)) >> 1;
int64_t originy = (CC_MIN(bb0maxy, bb1maxy) + CC_MAX(bb0miny, bb1miny)) >> 1;
double ln0c = (ln1dy * static_cast<double>(ln1a.x - originx)) +
(ln1dx * static_cast<double>(ln1a.y - originy));
double ln1c = (ln2dy * static_cast<double>(ln2a.x - originx)) +
(ln2dx * static_cast<double>(ln2a.y - originy));
double hitx = ((ln1dx * ln1c) - (ln2dx * ln0c)) / det;
double hity = ((ln2dy * ln0c) - (ln1dy * ln1c)) / det;
ip.x = originx + (T)nearbyint(hitx);
ip.y = originy + (T)nearbyint(hity);
}
else
{
double originx = (CC_MIN(bb0maxx, bb1maxx) + CC_MAX(bb0minx, bb1minx)) / 2.0;
double originy = (CC_MIN(bb0maxy, bb1maxy) + CC_MAX(bb0miny, bb1miny)) / 2.0;
double ln0c = (ln1dy * static_cast<double>(ln1a.x - originx)) +
(ln1dx * static_cast<double>(ln1a.y - originy));
double ln1c = (ln2dy * static_cast<double>(ln2a.x - originx)) +
(ln2dx * static_cast<double>(ln2a.y - originy));
double hitx = ((ln1dx * ln1c) - (ln2dx * ln0c)) / det;
double hity = ((ln2dy * ln0c) - (ln1dy * ln1c)) / det;
ip.x = originx + static_cast<T>(hitx);
ip.y = originy + static_cast<T>(hity);
}
return true;
}
#else
template<typename T>
inline bool GetSegmentIntersectPt(const Point<T>& ln1a, const Point<T>& ln1b,
const Point<T>& ln2a, const Point<T>& ln2b, Point<T>& ip)
{
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
double dx1 = static_cast<double>(ln1b.x - ln1a.x);
double dy1 = static_cast<double>(ln1b.y - ln1a.y);
@ -700,15 +888,44 @@ namespace Clipper2Lib
double det = dy1 * dx2 - dy2 * dx1;
if (det == 0.0) return false;
double t = ((ln1a.x - ln2a.x) * dy2 - (ln1a.y - ln2a.y) * dx2) / det;
if (t <= 0.0) ip = ln1a; // ?? check further (see also #568)
else if (t >= 1.0) ip = ln1b; // ?? check further
if (t <= 0.0) ip = ln1a;
else if (t >= 1.0) ip = ln1b;
else
{
ip.x = static_cast<int64_t>(ln1a.x + t * dx1);
ip.y = static_cast<int64_t>(ln1a.y + t * dy1);
}
ip.x = static_cast<T>(ln1a.x + t * dx1);
ip.y = static_cast<T>(ln1a.y + t * dy1);
}
return true;
}
#endif
template<typename T>
inline Point<T> TranslatePoint(const Point<T>& pt, double dx, double dy)
{
#ifdef USINGZ
return Point<T>(pt.x + dx, pt.y + dy, pt.z);
#else
return Point<T>(pt.x + dx, pt.y + dy);
#endif
}
template<typename T>
inline Point<T> ReflectPoint(const Point<T>& pt, const Point<T>& pivot)
{
#ifdef USINGZ
return Point<T>(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y), pt.z);
#else
return Point<T>(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y));
#endif
}
template<typename T>
inline int GetSign(const T& val)
{
if (!val) return 0;
return (val > 0) ? 1 : -1;
}
inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b,
const Point64& seg2a, const Point64& seg2b, bool inclusive = false)
@ -724,10 +941,10 @@ namespace Clipper2Lib
return (res1 || res2 || res3 || res4); // ensures not collinear
}
else {
return (CrossProduct(seg1a, seg2a, seg2b) *
CrossProduct(seg1b, seg2a, seg2b) < 0) &&
(CrossProduct(seg2a, seg1a, seg1b) *
CrossProduct(seg2b, seg1a, seg1b) < 0);
return (GetSign(CrossProduct(seg1a, seg2a, seg2b)) *
GetSign(CrossProduct(seg1b, seg2a, seg2b)) < 0) &&
(GetSign(CrossProduct(seg2a, seg1a, seg1b)) *
GetSign(CrossProduct(seg2b, seg1a, seg1b)) < 0);
}
}
@ -743,7 +960,7 @@ namespace Clipper2Lib
static_cast<double>(offPt.y - seg1.y) * dy) /
(Sqr(dx) + Sqr(dy));
if (q < 0) q = 0; else if (q > 1) q = 1;
if constexpr (std::numeric_limits<T>::is_integer)
if constexpr (std::is_integral_v<T>)
return Point<T>(
seg1.x + static_cast<T>(nearbyint(q * dx)),
seg1.y + static_cast<T>(nearbyint(q * dy)));
@ -770,7 +987,7 @@ namespace Clipper2Lib
return PointInPolygonResult::IsOutside;
bool is_above = first->y < pt.y, starting_above = is_above;
curr = first +1;
curr = first +1;
while (true)
{
if (curr == cend)
@ -779,7 +996,7 @@ namespace Clipper2Lib
cend = first;
curr = cbegin;
}
if (is_above)
{
while (curr != cend && curr->y < pt.y) ++curr;
@ -791,14 +1008,14 @@ namespace Clipper2Lib
if (curr == cend) continue;
}
if (curr == cbegin)
if (curr == cbegin)
prev = polygon.cend() - 1; //nb: NOT cend (since might equal first)
else
else
prev = curr - 1;
if (curr->y == pt.y)
{
if (curr->x == pt.x ||
if (curr->x == pt.x ||
(curr->y == prev->y &&
((pt.x < prev->x) != (pt.x < curr->x))))
return PointInPolygonResult::IsOn;
@ -822,7 +1039,7 @@ namespace Clipper2Lib
is_above = !is_above;
++curr;
}
if (is_above != starting_above)
{
cend = polygon.cend();

View file

@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 22 November 2023 *
* Date : 5 July 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This is the main polygon clipping module *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
@ -33,7 +33,7 @@ namespace Clipper2Lib {
//Note: all clipping operations except for Difference are commutative.
enum class ClipType { None, Intersection, Union, Difference, Xor };
enum class PathType { Subject, Clip };
enum class JoinWith { None, Left, Right };
@ -41,7 +41,7 @@ namespace Clipper2Lib {
None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8
};
constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b)
constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b)
{
return (enum VertexFlags)(uint32_t(a) & uint32_t(b));
}
@ -95,7 +95,7 @@ namespace Clipper2Lib {
Path64 path;
bool is_open = false;
~OutRec() {
~OutRec() {
if (splits) delete splits;
// nb: don't delete the split pointers
// as these are owned by ClipperBase's outrec_list_
@ -106,7 +106,7 @@ namespace Clipper2Lib {
//Important: UP and DOWN here are premised on Y-axis positive down
//displays, which is the orientation used in Clipper's development.
///////////////////////////////////////////////////////////////////
struct Active {
Point64 bot;
Point64 top;
@ -230,7 +230,7 @@ namespace Clipper2Lib {
inline bool PopHorz(Active *&e);
inline OutPt* StartOpenPath(Active &e, const Point64& pt);
inline void UpdateEdgeIntoAEL(Active *e);
OutPt* IntersectEdges(Active &e1, Active &e2, const Point64& pt);
void IntersectEdges(Active &e1, Active &e2, const Point64& pt);
inline void DeleteFromAEL(Active &e);
inline void AdjustCurrXAndCopyToSEL(const int64_t top_y);
void DoIntersections(const int64_t top_y);
@ -240,7 +240,7 @@ namespace Clipper2Lib {
void SwapPositionsInAEL(Active& edge1, Active& edge2);
OutRec* NewOutRec();
OutPt* AddOutPt(const Active &e, const Point64& pt);
OutPt* AddLocalMinPoly(Active &e1, Active &e2,
OutPt* AddLocalMinPoly(Active &e1, Active &e2,
const Point64& pt, bool is_new = false);
OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt);
void DoHorizontal(Active &horz);
@ -251,13 +251,13 @@ namespace Clipper2Lib {
void JoinOutrecPaths(Active &e1, Active &e2);
void FixSelfIntersects(OutRec* outrec);
void DoSplitOp(OutRec* outRec, OutPt* splitOp);
inline void AddTrialHorzJoin(OutPt* op);
void ConvertHorzSegsToJoins();
void ProcessHorzJoins();
void Split(Active& e, const Point64& pt);
inline void CheckJoinLeft(Active& e,
inline void CheckJoinLeft(Active& e,
const Point64& pt, bool check_curr_x = false);
inline void CheckJoinRight(Active& e,
const Point64& pt, bool check_curr_x = false);
@ -326,12 +326,12 @@ namespace Clipper2Lib {
const PolyPath* Parent() const { return parent_; }
bool IsHole() const
bool IsHole() const
{
unsigned lvl = Level();
//Even levels except level 0
return lvl && !(lvl & 1);
}
}
};
typedef typename std::vector<std::unique_ptr<PolyPath64>> PolyPath64List;
@ -343,15 +343,16 @@ namespace Clipper2Lib {
Path64 polygon_;
public:
explicit PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {}
explicit PolyPath64(PolyPath64* parent, const Path64& path) : PolyPath(parent) { polygon_ = path; }
~PolyPath64() {
childs_.resize(0);
}
PolyPath64* operator [] (size_t index) const
{
{
return childs_[index].get(); //std::unique_ptr
}
}
PolyPath64* Child(size_t index) const
{
@ -363,10 +364,7 @@ namespace Clipper2Lib {
PolyPath64* AddChild(const Path64& path) override
{
auto p = std::make_unique<PolyPath64>(this);
auto* result = childs_.emplace_back(std::move(p)).get();
result->polygon_ = path;
return result;
return childs_.emplace_back(std::make_unique<PolyPath64>(this, path)).get();
}
void Clear() override
@ -401,12 +399,25 @@ namespace Clipper2Lib {
scale_ = parent ? parent->scale_ : 1.0;
}
explicit PolyPathD(PolyPathD* parent, const Path64& path) : PolyPath(parent)
{
scale_ = parent ? parent->scale_ : 1.0;
int error_code = 0;
polygon_ = ScalePath<double, int64_t>(path, scale_, error_code);
}
explicit PolyPathD(PolyPathD* parent, const PathD& path) : PolyPath(parent)
{
scale_ = parent ? parent->scale_ : 1.0;
polygon_ = path;
}
~PolyPathD() {
childs_.resize(0);
}
PolyPathD* operator [] (size_t index) const
{
{
return childs_[index].get();
}
@ -420,22 +431,15 @@ namespace Clipper2Lib {
void SetScale(double value) { scale_ = value; }
double Scale() const { return scale_; }
PolyPathD* AddChild(const Path64& path) override
{
int error_code = 0;
auto p = std::make_unique<PolyPathD>(this);
PolyPathD* result = childs_.emplace_back(std::move(p)).get();
result->polygon_ = ScalePath<double, int64_t>(path, scale_, error_code);
return result;
return childs_.emplace_back(std::make_unique<PolyPathD>(this, path)).get();
}
PolyPathD* AddChild(const PathD& path)
{
auto p = std::make_unique<PolyPathD>(this);
PolyPathD* result = childs_.emplace_back(std::move(p)).get();
result->polygon_ = path;
return result;
return childs_.emplace_back(std::make_unique<PolyPathD>(this, path)).get();
}
void Clear() override
@ -488,7 +492,7 @@ namespace Clipper2Lib {
return Execute(clip_type, fill_rule, closed_paths, dummy);
}
bool Execute(ClipType clip_type, FillRule fill_rule,
bool Execute(ClipType clip_type, FillRule fill_rule,
Paths64& closed_paths, Paths64& open_paths)
{
closed_paths.clear();
@ -530,7 +534,7 @@ namespace Clipper2Lib {
public:
explicit ClipperD(int precision = 2) : ClipperBase()
{
CheckPrecision(precision, error_code_);
CheckPrecisionRange(precision, error_code_);
// to optimize scaling / descaling precision
// set the scale to a power of double's radix (2) (#25)
scale_ = std::pow(std::numeric_limits<double>::radix,
@ -560,12 +564,12 @@ namespace Clipper2Lib {
void CheckCallback()
{
if(zCallbackD_)
// if the user defined float point callback has been assigned
// if the user defined float point callback has been assigned
// then assign the proxy callback function
ClipperBase::zCallback_ =
ClipperBase::zCallback_ =
std::bind(&ClipperD::ZCB, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5);
std::placeholders::_4, std::placeholders::_5);
else
ClipperBase::zCallback_ = nullptr;
}
@ -632,6 +636,6 @@ namespace Clipper2Lib {
};
} // namespace
} // namespace
#endif // CLIPPER_ENGINE_H

View file

@ -1,14 +1,14 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 26 November 2023 *
* Date : 14 May 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This module exports the Clipper2 Library (ie DLL/so) *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
/*
/*
Boolean clipping:
cliptype: None=0, Intersection=1, Union=2, Difference=3, Xor=4
fillrule: EvenOdd=0, NonZero=1, Positive=2, Negative=3
@ -19,12 +19,12 @@
The path structures used extensively in other parts of this library are all
based on std::vector classes. Since C++ classes can't be accessed by other
languages, these paths must be converted into simple C data structures that
can be understood by just about any programming language. And these C style
path structures are simple arrays of int64_t (CPath64) and double (CPathD).
languages, these paths are converted into very simple array data structures
(of either int64_t for CPath64 or double for CPathD) that can be parsed by
just about any programming language.
CPath64 and CPathD:
These are arrays of consecutive x and y path coordinates preceeded by
These are arrays of consecutive x and y path coordinates preceeded by
a pair of values containing the path's length (N) and a 0 value.
__________________________________
|counter|coord1|coord2|...|coordN|
@ -34,23 +34,24 @@ __________________________________
CPaths64 and CPathsD:
These are also arrays containing any number of consecutive CPath64 or
CPathD structures. But preceeding these consecutive paths, there is pair of
values that contain the total length of the array (A) structure and
the number (C) of CPath64 or CPathD it contains.
values that contain the total length of the array structure (A) and the
number of CPath64 or CPathD it contains (C). The space these structures will
occupy in memory = A * sizeof(int64_t) or A * sizeof(double) respectively.
_______________________________
|counter|path1|path2|...|pathC|
|A , C | |
_______________________________
CPolytree64 and CPolytreeD:
These are also arrays consisting of CPolyPath structures that represent
These are also arrays consisting of CPolyPath structures that represent
individual paths in a tree structure. However, the very first (ie top)
CPolyPath is just the tree container that won't have a path. And because
CPolyPath is just the tree container that doesn't have a path. And because
of that, its structure will be very slightly different from the remaining
CPolyPath. This difference will be discussed below.
CPolyPath64 and CPolyPathD:
These are simple arrays consisting of a series of path coordinates followed
by any number of child (ie nested) CPolyPath. Preceeding these are two values
These are simple arrays consisting of a series of path coordinates followed
by any number of child (ie nested) CPolyPath. Preceeding these are two values
indicating the length of the path (N) and the number of child CPolyPath (C).
____________________________________________________________
|counter|coord1|coord2|...|coordN| child1|child2|...|childC|
@ -58,19 +59,20 @@ ____________________________________________________________
____________________________________________________________
As mentioned above, the very first CPolyPath structure is just a container
that owns (both directly and indirectly) every other CPolyPath in the tree.
that owns (both directly and indirectly) every other CPolyPath in the tree.
Since this first CPolyPath has no path, instead of a path length, its very
first value will contain the total length of the CPolytree array structure.
first value will contain the total length of the CPolytree array (not its
total bytes length).
All theses exported structures (CPaths64, CPathsD, CPolyTree64 & CPolyTreeD)
are arrays of type int64_t or double. And the first value in these arrays
will always contain the length of that array.
Again, all theses exported structures (CPaths64, CPathsD, CPolyTree64 &
CPolyTreeD) are arrays of either type int64_t or double, and the first
value in these arrays will always be the length of that array.
These array structures are allocated in heap memory which will eventually
need to be released. But since applications dynamically linking to these
functions may use different memory managers, the only safe way to free up
this memory is to use the exported DisposeArray64 and DisposeArrayD
functions below.
These array structures are allocated in heap memory which will eventually
need to be released. However, since applications dynamically linking to
these functions may use different memory managers, the only safe way to
free up this memory is to use the exported DisposeArray64 and
DisposeArrayD functions (see below).
*/
@ -128,7 +130,7 @@ inline Rect<T> CRectToRect(const CRect<T>& rect)
#ifdef _WIN32
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
#else
#define EXTERN_DLL_EXPORT extern "C"
#define EXTERN_DLL_EXPORT extern "C"
#endif
@ -173,8 +175,8 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype,
bool preserve_collinear = true, bool reverse_solution = false);
EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths,
double delta, uint8_t jointype, uint8_t endtype,
double miter_limit = 2.0, double arc_tolerance = 0.0,
double delta, uint8_t jointype, uint8_t endtype,
double miter_limit = 2.0, double arc_tolerance = 0.0,
bool reverse_solution = false);
EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
double delta, uint8_t jointype, uint8_t endtype,
@ -219,10 +221,10 @@ static size_t GetPolyPath64ArrayLen(const PolyPath64& pp)
return result;
}
static void GetPolytreeCountAndCStorageSize(const PolyTree64& tree,
static void GetPolytreeCountAndCStorageSize(const PolyTree64& tree,
size_t& cnt, size_t& array_len)
{
cnt = tree.Count(); // nb: top level count only
cnt = tree.Count(); // nb: top level count only
array_len = GetPolyPath64ArrayLen(tree);
}
@ -271,17 +273,34 @@ CPathsD CreateCPathsDFromPaths64(const Paths64& paths, double scale)
return result;
}
template <typename T>
static Path<T> ConvertCPath(T* path)
{
Path<T> result;
if (!path) return result;
T* v = path;
size_t cnt = static_cast<size_t>(*v);
v += 2; // skip 0 value
result.reserve(cnt);
for (size_t j = 0; j < cnt; ++j)
{
T x = *v++, y = *v++;
result.push_back(Point<T>(x, y));
}
return result;
}
template <typename T>
static Paths<T> ConvertCPaths(T* paths)
{
Paths<T> result;
if (!paths) return result;
T* v = paths; ++v;
size_t cnt = *v++;
size_t cnt = static_cast<size_t>(*v++);
result.reserve(cnt);
for (size_t i = 0; i < cnt; ++i)
{
size_t cnt2 = *v;
size_t cnt2 = static_cast<size_t>(*v);
v += 2;
Path<T> path;
path.reserve(cnt2);
@ -300,17 +319,17 @@ static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale)
{
Paths64 result;
if (!paths) return result;
double* v = paths;
double* v = paths;
++v; // skip the first value (0)
int64_t cnt = (int64_t)*v++;
size_t cnt = static_cast<size_t>(*v++);
result.reserve(cnt);
for (int i = 0; i < cnt; ++i)
for (size_t i = 0; i < cnt; ++i)
{
int64_t cnt2 = (int64_t)*v;
size_t cnt2 = static_cast<size_t>(*v);
v += 2;
Path64 path;
path.reserve(cnt2);
for (int j = 0; j < cnt2; ++j)
for (size_t j = 0; j < cnt2; ++j)
{
double x = *v++ * scale;
double y = *v++ * scale;
@ -362,7 +381,7 @@ EXTERN_DLL_EXPORT const char* Version()
return CLIPPER2_VERSION;
}
EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
uint8_t fillrule, const CPaths64 subjects,
const CPaths64 subjects_open, const CPaths64 clips,
CPaths64& solution, CPaths64& solution_open,
@ -370,7 +389,7 @@ EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
{
if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
Paths64 sub, sub_open, clp, sol, sol_open;
sub = ConvertCPaths(subjects);
sub_open = ConvertCPaths(subjects_open);
@ -382,7 +401,7 @@ EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
if (sub.size() > 0) clipper.AddSubject(sub);
if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
if (clp.size() > 0) clipper.AddClip(clp);
if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open))
if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open))
return -1; // clipping bug - should never happen :)
solution = CreateCPaths(sol);
solution_open = CreateCPaths(sol_open);
@ -455,7 +474,7 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype,
if (precision < -8 || precision > 8) return -5;
if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
double scale = std::pow(10, precision);
int err = 0;
@ -485,10 +504,10 @@ EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths,
{
Paths64 pp;
pp = ConvertCPaths(paths);
ClipperOffset clip_offset( miter_limit,
ClipperOffset clip_offset( miter_limit,
arc_tolerance, reverse_solution);
clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype));
Paths64 result;
Paths64 result;
clip_offset.Execute(delta, result);
return CreateCPaths(result);
}
@ -560,6 +579,22 @@ EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect,
return CreateCPathsDFromPaths64(result, 1 / scale);
}
EXTERN_DLL_EXPORT CPaths64 MinkowskiSum64(const CPath64& cpattern, const CPath64& cpath, bool is_closed)
{
Path64 path = ConvertCPath(cpath);
Path64 pattern = ConvertCPath(cpattern);
Paths64 solution = MinkowskiSum(pattern, path, is_closed);
return CreateCPaths(solution);
}
EXTERN_DLL_EXPORT CPaths64 MinkowskiDiff64(const CPath64& cpattern, const CPath64& cpath, bool is_closed)
{
Path64 path = ConvertCPath(cpath);
Path64 pattern = ConvertCPath(cpattern);
Paths64 solution = MinkowskiDiff(pattern, path, is_closed);
return CreateCPaths(solution);
}
} // end Clipper2Lib namespace
#endif // CLIPPER2_EXPORT_H

View file

@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 18 November 2023 *
* Date : 27 April 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This module provides a simple interface to the Clipper Library *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
@ -24,7 +24,7 @@ namespace Clipper2Lib {
inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule,
const Paths64& subjects, const Paths64& clips)
{
{
Paths64 result;
Clipper64 clipper;
clipper.AddSubject(subjects);
@ -47,7 +47,7 @@ namespace Clipper2Lib {
const PathsD& subjects, const PathsD& clips, int precision = 2)
{
int error_code = 0;
CheckPrecision(precision, error_code);
CheckPrecisionRange(precision, error_code);
PathsD result;
if (error_code) return result;
ClipperD clipper(precision);
@ -58,12 +58,12 @@ namespace Clipper2Lib {
}
inline void BooleanOp(ClipType cliptype, FillRule fillrule,
const PathsD& subjects, const PathsD& clips,
const PathsD& subjects, const PathsD& clips,
PolyTreeD& polytree, int precision = 2)
{
polytree.Clear();
int error_code = 0;
CheckPrecision(precision, error_code);
CheckPrecisionRange(precision, error_code);
if (error_code) return;
ClipperD clipper(precision);
clipper.AddSubject(subjects);
@ -75,7 +75,7 @@ namespace Clipper2Lib {
{
return BooleanOp(ClipType::Intersection, fillrule, subjects, clips);
}
inline PathsD Intersect(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
{
return BooleanOp(ClipType::Intersection, fillrule, subjects, clips, decimal_prec);
@ -104,7 +104,7 @@ namespace Clipper2Lib {
{
PathsD result;
int error_code = 0;
CheckPrecision(precision, error_code);
CheckPrecisionRange(precision, error_code);
if (error_code) return result;
ClipperD clipper(precision);
clipper.AddSubject(subjects);
@ -145,11 +145,11 @@ namespace Clipper2Lib {
}
inline PathsD InflatePaths(const PathsD& paths, double delta,
JoinType jt, EndType et, double miter_limit = 2.0,
JoinType jt, EndType et, double miter_limit = 2.0,
int precision = 2, double arc_tolerance = 0.0)
{
int error_code = 0;
CheckPrecision(precision, error_code);
CheckPrecisionRange(precision, error_code);
if (!delta) return paths;
if (error_code) return PathsD();
const double scale = std::pow(10, precision);
@ -219,13 +219,13 @@ namespace Clipper2Lib {
{
if (rect.IsEmpty() || paths.empty()) return PathsD();
int error_code = 0;
CheckPrecision(precision, error_code);
CheckPrecisionRange(precision, error_code);
if (error_code) return PathsD();
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(rect, scale);
RectClip64 rc(r);
Paths64 pp = ScalePaths<int64_t, double>(paths, scale, error_code);
if (error_code) return PathsD(); // ie: error_code result is lost
if (error_code) return PathsD(); // ie: error_code result is lost
return ScalePaths<double, int64_t>(
rc.Execute(pp), 1 / scale, error_code);
}
@ -251,7 +251,7 @@ namespace Clipper2Lib {
{
if (rect.IsEmpty() || lines.empty()) return PathsD();
int error_code = 0;
CheckPrecision(precision, error_code);
CheckPrecisionRange(precision, error_code);
if (error_code) return PathsD();
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(rect, scale);
@ -290,8 +290,8 @@ namespace Clipper2Lib {
{
// return false if this child isn't fully contained by its parent
// checking for a single vertex outside is a bit too crude since
// it doesn't account for rounding errors. It's better to check
// checking for a single vertex outside is a bit too crude since
// it doesn't account for rounding errors. It's better to check
// for consecutive vertices found outside the parent's polygon.
int outsideCnt = 0;
@ -311,7 +311,7 @@ namespace Clipper2Lib {
return true;
}
static void OutlinePolyPath(std::ostream& os,
static void OutlinePolyPath(std::ostream& os,
size_t idx, bool isHole, size_t count, const std::string& preamble)
{
std::string plural = (count == 1) ? "." : "s.";
@ -342,19 +342,19 @@ namespace Clipper2Lib {
}
template<typename T, typename U>
inline constexpr void MakePathGeneric(const T an_array,
inline constexpr void MakePathGeneric(const T an_array,
size_t array_size, std::vector<U>& result)
{
result.reserve(array_size / 2);
for (size_t i = 0; i < array_size; i +=2)
#ifdef USINGZ
result.push_back( U{ an_array[i], an_array[i +1], 0} );
result.push_back( U{ an_array[i], an_array[i + 1], 0} );
#else
result.push_back( U{ an_array[i], an_array[i + 1]} );
#endif
}
} // end details namespace
} // end details namespace
inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp)
{
@ -398,7 +398,7 @@ namespace Clipper2Lib {
inline bool CheckPolytreeFullyContainsChildren(const PolyTree64& polytree)
{
for (const auto& child : polytree)
if (child->Count() > 0 &&
if (child->Count() > 0 &&
!details::PolyPath64ContainsChildren(*child))
return false;
return true;
@ -471,7 +471,7 @@ namespace Clipper2Lib {
std::size_t size = N / 3;
Path64 result(size);
for (size_t i = 0; i < size; ++i)
result[i] = Point64(list[i * 3],
result[i] = Point64(list[i * 3],
list[i * 3 + 1], list[i * 3 + 2]);
return result;
}
@ -489,7 +489,7 @@ namespace Clipper2Lib {
list[i * 3 + 1], list[i * 3 + 2]);
else
for (size_t i = 0; i < size; ++i)
result[i] = PointD(list[i * 3], list[i * 3 + 1],
result[i] = PointD(list[i * 3], list[i * 3 + 1],
static_cast<int64_t>(list[i * 3 + 2]));
return result;
}
@ -510,9 +510,9 @@ namespace Clipper2Lib {
if (!is_open_path)
{
while (srcIt != stop && !CrossProduct(*stop, *srcIt, *(srcIt + 1)))
while (srcIt != stop && IsCollinear(*stop, *srcIt, *(srcIt + 1)))
++srcIt;
while (srcIt != stop && !CrossProduct(*(stop - 1), *stop, *srcIt))
while (srcIt != stop && IsCollinear(*(stop - 1), *stop, *srcIt))
--stop;
if (srcIt == stop) return Path64();
}
@ -521,7 +521,7 @@ namespace Clipper2Lib {
dst.push_back(*prevIt);
for (; srcIt != stop; ++srcIt)
{
if (CrossProduct(*prevIt, *srcIt, *(srcIt + 1)))
if (!IsCollinear(*prevIt, *srcIt, *(srcIt + 1)))
{
prevIt = srcIt;
dst.push_back(*prevIt);
@ -530,12 +530,12 @@ namespace Clipper2Lib {
if (is_open_path)
dst.push_back(*srcIt);
else if (CrossProduct(*prevIt, *stop, dst[0]))
else if (!IsCollinear(*prevIt, *stop, dst[0]))
dst.push_back(*stop);
else
{
while (dst.size() > 2 &&
!CrossProduct(dst[dst.size() - 1], dst[dst.size() - 2], dst[0]))
IsCollinear(dst[dst.size() - 1], dst[dst.size() - 2], dst[0]))
dst.pop_back();
if (dst.size() < 3) return Path64();
}
@ -545,7 +545,7 @@ namespace Clipper2Lib {
inline PathD TrimCollinear(const PathD& path, int precision, bool is_open_path = false)
{
int error_code = 0;
CheckPrecision(precision, error_code);
CheckPrecisionRange(precision, error_code);
if (error_code) return PathD();
const double scale = std::pow(10, precision);
Path64 p = ScalePath<int64_t, double>(path, scale, error_code);
@ -580,23 +580,23 @@ namespace Clipper2Lib {
double cp = std::abs(CrossProduct(pt1, pt2, pt3));
return (cp * cp) / (DistanceSqr(pt1, pt2) * DistanceSqr(pt2, pt3)) < sin_sqrd_min_angle_rads;
}
template <typename T>
inline Path<T> Ellipse(const Rect<T>& rect, int steps = 0)
inline Path<T> Ellipse(const Rect<T>& rect, size_t steps = 0)
{
return Ellipse(rect.MidPoint(),
static_cast<double>(rect.Width()) *0.5,
return Ellipse(rect.MidPoint(),
static_cast<double>(rect.Width()) *0.5,
static_cast<double>(rect.Height()) * 0.5, steps);
}
template <typename T>
inline Path<T> Ellipse(const Point<T>& center,
double radiusX, double radiusY = 0, int steps = 0)
double radiusX, double radiusY = 0, size_t steps = 0)
{
if (radiusX <= 0) return Path<T>();
if (radiusY <= 0) radiusY = radiusX;
if (steps <= 2)
steps = static_cast<int>(PI * sqrt((radiusX + radiusY) / 2));
steps = static_cast<size_t>(PI * sqrt((radiusX + radiusY) / 2));
double si = std::sin(2 * PI / steps);
double co = std::cos(2 * PI / steps);
@ -604,7 +604,7 @@ namespace Clipper2Lib {
Path<T> result;
result.reserve(steps);
result.push_back(Point<T>(center.x + radiusX, static_cast<double>(center.y)));
for (int i = 1; i < steps; ++i)
for (size_t i = 1; i < steps; ++i)
{
result.push_back(Point<T>(center.x + radiusX * dx, center.y + radiusY * dy));
double x = dx * co - dy * si;
@ -614,19 +614,7 @@ namespace Clipper2Lib {
return result;
}
template <typename T>
inline double PerpendicDistFromLineSqrd(const Point<T>& pt,
const Point<T>& line1, const Point<T>& line2)
{
double a = static_cast<double>(pt.x - line1.x);
double b = static_cast<double>(pt.y - line1.y);
double c = static_cast<double>(line2.x - line1.x);
double d = static_cast<double>(line2.y - line1.y);
if (c == 0 && d == 0) return 0;
return Sqr(a * d - c * b) / (c * c + d * d);
}
inline size_t GetNext(size_t current, size_t high,
inline size_t GetNext(size_t current, size_t high,
const std::vector<bool>& flags)
{
++current;
@ -637,7 +625,7 @@ namespace Clipper2Lib {
return current;
}
inline size_t GetPrior(size_t current, size_t high,
inline size_t GetPrior(size_t current, size_t high,
const std::vector<bool>& flags)
{
if (current == 0) current = high;
@ -650,7 +638,7 @@ namespace Clipper2Lib {
}
template <typename T>
inline Path<T> SimplifyPath(const Path<T> &path,
inline Path<T> SimplifyPath(const Path<T> &path,
double epsilon, bool isClosedPath = true)
{
const size_t len = path.size(), high = len -1;
@ -665,7 +653,7 @@ namespace Clipper2Lib {
distSqr[0] = PerpendicDistFromLineSqrd(path[0], path[high], path[1]);
distSqr[high] = PerpendicDistFromLineSqrd(path[high], path[0], path[high - 1]);
}
else
else
{
distSqr[0] = MAX_DBL;
distSqr[high] = MAX_DBL;
@ -684,7 +672,7 @@ namespace Clipper2Lib {
} while (curr != start && distSqr[curr] > epsSqr);
if (curr == start) break;
}
prior = GetPrior(curr, high, flags);
next = GetNext(curr, high, flags);
if (next == prior) break;
@ -699,7 +687,7 @@ namespace Clipper2Lib {
}
else
prior2 = GetPrior(prior, high, flags);
flags[curr] = true;
curr = next;
next = GetNext(next, high, flags);
@ -717,7 +705,7 @@ namespace Clipper2Lib {
}
template <typename T>
inline Paths<T> SimplifyPaths(const Paths<T> &paths,
inline Paths<T> SimplifyPaths(const Paths<T> &paths,
double epsilon, bool isClosedPath = true)
{
Paths<T> result;

View file

@ -15,7 +15,7 @@
#include <string>
#include "clipper2/clipper.core.h"
namespace Clipper2Lib
namespace Clipper2Lib
{
namespace detail

View file

@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 19 November 2023 *
* Date : 24 March 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : Path Offset (Inflate/Shrink) *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
@ -34,9 +34,7 @@ private:
class Group {
public:
Paths64 paths_in;
std::vector<bool> is_hole_list;
std::vector<Rect64> bounds_list;
int lowest_path_idx = -1;
std::optional<size_t> lowest_path_idx{};
bool is_reversed = false;
JoinType join_type;
EndType end_type;
@ -52,7 +50,8 @@ private:
double step_cos_ = 0.0;
PathD norms;
Path64 path_out;
Paths64 solution;
Paths64* solution = nullptr;
PolyTree64* solution_tree = nullptr;
std::vector<Group> groups_;
JoinType join_type_ = JoinType::Bevel;
EndType end_type_ = EndType::Polygon;
@ -64,9 +63,10 @@ private:
#ifdef USINGZ
ZCallback64 zCallback64_ = nullptr;
void ZCB(const Point64& bot1, const Point64& top1,
const Point64& bot2, const Point64& top2, Point64& ip);
#endif
DeltaCallback64 deltaCallback64_ = nullptr;
size_t CalcSolutionCapacity();
bool CheckReverseOrientation();
void DoBevel(const Path64& path, size_t j, size_t k);
@ -83,7 +83,7 @@ private:
public:
explicit ClipperOffset(double miter_limit = 2.0,
double arc_tolerance = 0.0,
bool preserve_collinear = false,
bool preserve_collinear = false,
bool reverse_solution = false) :
miter_limit_(miter_limit), arc_tolerance_(arc_tolerance),
preserve_collinear_(preserve_collinear),
@ -91,7 +91,7 @@ public:
~ClipperOffset() { Clear(); };
int ErrorCode() { return error_code_; };
int ErrorCode() const { return error_code_; };
void AddPath(const Path64& path, JoinType jt_, EndType et_);
void AddPaths(const Paths64& paths, JoinType jt_, EndType et_);
void Clear() { groups_.clear(); norms.clear(); };

View file

@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 1 November 2023 *
* Date : 5 July 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : FAST rectangular clipping *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
@ -18,6 +18,7 @@
namespace Clipper2Lib
{
// Location: the order is important here, see StartLocsIsClockwise()
enum class Location { Left, Top, Right, Bottom, Inside };
class OutPt2;
@ -26,10 +27,10 @@ namespace Clipper2Lib
class OutPt2 {
public:
Point64 pt;
size_t owner_idx;
OutPt2List* edge;
OutPt2* next;
OutPt2* prev;
size_t owner_idx = 0;
OutPt2List* edge = nullptr;
OutPt2* next = nullptr;
OutPt2* prev = nullptr;
};
//------------------------------------------------------------------------------
@ -50,9 +51,9 @@ namespace Clipper2Lib
OutPt2List edges_[8]; // clockwise and counter-clockwise
std::vector<Location> start_locs_;
void CheckEdges();
void TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw);
void TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw);
void GetNextLocation(const Path64& path,
Location& loc, int& i, int highI);
Location& loc, size_t& i, size_t highI);
OutPt2* Add(Point64 pt, bool start_new = false);
void AddCorner(Location prev, Location curr);
void AddCorner(Location& loc, bool isClockwise);

View file

@ -1,6 +1,6 @@
#ifndef CLIPPER_VERSION_H
#define CLIPPER_VERSION_H
constexpr auto CLIPPER2_VERSION = "1.3.0";
constexpr auto CLIPPER2_VERSION = "1.4.0";
#endif // CLIPPER_VERSION_H

View file

@ -1,9 +1,9 @@
diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
index b3dddeeaa2..a77cdad5f4 100644
index 925c04685e..67dd731af6 100644
--- a/thirdparty/clipper2/include/clipper2/clipper.core.h
+++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
@@ -21,6 +21,8 @@
#include <numeric>
@@ -22,6 +22,8 @@
#include <optional>
#include "clipper2/clipper.version.h"
+#define CLIPPER2_THROW(exception) std::abort()
@ -11,7 +11,7 @@ index b3dddeeaa2..a77cdad5f4 100644
namespace Clipper2Lib
{
@@ -78,18 +80,18 @@ namespace Clipper2Lib
@@ -79,18 +81,18 @@ namespace Clipper2Lib
switch (error_code)
{
case precision_error_i:

View file

@ -0,0 +1,32 @@
diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
index 67dd731af6..dd1b873d5d 100644
--- a/thirdparty/clipper2/include/clipper2/clipper.core.h
+++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
@@ -695,11 +695,13 @@ namespace Clipper2Lib
// returns true if (and only if) a * b == c * d
inline bool ProductsAreEqual(int64_t a, int64_t b, int64_t c, int64_t d)
{
-#if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
- const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
- const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
- return ab == cd;
-#else
+// Work around LLVM issue: https://github.com/llvm/llvm-project/issues/16778
+// Details: https://github.com/godotengine/godot/pull/95964#issuecomment-2306581804
+//#if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
+// const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
+// const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
+// return ab == cd;
+//#else
// nb: unsigned values needed for calculating overflow carry
const auto abs_a = static_cast<uint64_t>(std::abs(a));
const auto abs_b = static_cast<uint64_t>(std::abs(b));
@@ -714,7 +716,7 @@ namespace Clipper2Lib
const auto sign_cd = TriSign(c) * TriSign(d);
return abs_ab == abs_cd && sign_ab == sign_cd;
-#endif
+// #endif
}
template <typename T>

View file

@ -1,22 +0,0 @@
diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
index a77cdad5f4..0de7c3720e 100644
--- a/thirdparty/clipper2/include/clipper2/clipper.core.h
+++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
@@ -138,7 +138,7 @@ namespace Clipper2Lib
}
template <typename T2>
- explicit Point<T>(const Point<T2>& p)
+ explicit Point(const Point<T2>& p)
{
Init(p.x, p.y, p.z);
}
@@ -180,7 +180,7 @@ namespace Clipper2Lib
Point(const T2 x_, const T2 y_) { Init(x_, y_); }
template <typename T2>
- explicit Point<T>(const Point<T2>& p) { Init(p.x, p.y); }
+ explicit Point(const Point<T2>& p) { Init(p.x, p.y); }
Point operator * (const double scale) const
{

View file

@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 22 November 2023 *
* Date : 27 April 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This is the main polygon clipping module *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
@ -31,11 +31,11 @@ namespace Clipper2Lib {
static const Rect64 invalid_rect = Rect64(false);
// Every closed path (or polygon) is made up of a series of vertices forming
// edges that alternate between going up (relative to the Y-axis) and going
// down. Edges consecutively going up or consecutively going down are called
// 'bounds' (ie sides if they're simple polygons). 'Local Minima' refer to
// vertices where descending bounds become ascending ones.
// Every closed path (ie polygon) is made up of a series of vertices forming edge
// 'bounds' that alternate between ascending bounds (containing edges going up
// relative to the Y-axis) and descending bounds. 'Local Minima' refers to
// vertices where ascending and descending bounds join at the bottom, and
// 'Local Maxima' are where ascending and descending bounds join at the top.
struct Scanline {
int64_t y = 0;
@ -63,6 +63,7 @@ namespace Clipper2Lib {
}
};
inline bool IsOdd(int val)
{
return (val & 1) ? true : false;
@ -188,7 +189,7 @@ namespace Clipper2Lib {
}
//PrevPrevVertex: useful to get the (inverted Y-axis) top of the
//alternate edge (ie left or right bound) during edge insertion.
//alternate edge (ie left or right bound) during edge insertion.
inline Vertex* PrevPrevVertex(const Active& ae)
{
if (ae.wind_dx > 0)
@ -233,15 +234,15 @@ namespace Clipper2Lib {
Vertex* result = e.vertex_top;
if (e.wind_dx > 0)
while ((result->next->pt.y == result->pt.y) &&
((result->flags & (VertexFlags::OpenEnd |
((result->flags & (VertexFlags::OpenEnd |
VertexFlags::LocalMax)) == VertexFlags::None))
result = result->next;
else
while (result->prev->pt.y == result->pt.y &&
((result->flags & (VertexFlags::OpenEnd |
((result->flags & (VertexFlags::OpenEnd |
VertexFlags::LocalMax)) == VertexFlags::None))
result = result->prev;
if (!IsMaxima(*result)) result = nullptr; // not a maxima
if (!IsMaxima(*result)) result = nullptr; // not a maxima
return result;
}
@ -252,7 +253,7 @@ namespace Clipper2Lib {
while (result->next->pt.y == result->pt.y) result = result->next;
else
while (result->prev->pt.y == result->pt.y) result = result->prev;
if (!IsMaxima(*result)) result = nullptr; // not a maxima
if (!IsMaxima(*result)) result = nullptr; // not a maxima
return result;
}
@ -613,13 +614,13 @@ namespace Clipper2Lib {
list.push_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
}
void AddPaths_(const Paths64& paths, PathType polytype, bool is_open,
void AddPaths_(const Paths64& paths, PathType polytype, bool is_open,
std::vector<Vertex*>& vertexLists, LocalMinimaList& locMinList)
{
const auto total_vertex_count =
std::accumulate(paths.begin(), paths.end(), 0,
std::accumulate(paths.begin(), paths.end(), size_t(0),
[](const auto& a, const Path64& path)
{return a + static_cast<unsigned>(path.size()); });
{return a + path.size(); });
if (total_vertex_count == 0) return;
Vertex* vertices = new Vertex[total_vertex_count], * v = vertices;
@ -810,7 +811,7 @@ namespace Clipper2Lib {
void ClipperBase::SetZ(const Active& e1, const Active& e2, Point64& ip)
{
if (!zCallback_) return;
// prioritize subject over clip vertices by passing
// prioritize subject over clip vertices by passing
// subject vertices before clip vertices in the callback
if (GetPolyType(e1) == PathType::Subject)
{
@ -845,11 +846,11 @@ namespace Clipper2Lib {
if (is_open) has_open_paths_ = true;
minima_list_sorted_ = false;
AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_);
}
}
void ClipperBase::AddReuseableData(const ReuseableDataContainer64& reuseable_data)
void ClipperBase::AddReuseableData(const ReuseableDataContainer64& reuseable_data)
{
// nb: reuseable_data will continue to own the vertices
// nb: reuseable_data will continue to own the vertices
// and remains responsible for their clean up.
succeeded_ = false;
minima_list_sorted_ = false;
@ -1117,7 +1118,6 @@ namespace Clipper2Lib {
}
}
bool IsValidAelOrder(const Active& resident, const Active& newcomer)
{
if (newcomer.curr_x != resident.curr_x)
@ -1149,8 +1149,8 @@ namespace Clipper2Lib {
//resident must also have just been inserted
else if (resident.is_left_bound != newcomerIsLeft)
return newcomerIsLeft;
else if (CrossProduct(PrevPrevVertex(resident)->pt,
resident.bot, resident.top) == 0) return true;
else if (IsCollinear(PrevPrevVertex(resident)->pt,
resident.bot, resident.top)) return true;
else
//compare turning direction of the alternate bound
return (CrossProduct(PrevPrevVertex(resident)->pt,
@ -1385,7 +1385,7 @@ namespace Clipper2Lib {
{
if (IsJoined(e1)) Split(e1, pt);
if (IsJoined(e2)) Split(e2, pt);
if (IsFront(e1) == IsFront(e2))
{
if (IsOpenEnd(e1))
@ -1409,7 +1409,7 @@ namespace Clipper2Lib {
{
Active* e = GetPrevHotEdge(e1);
if (!e)
outrec.owner = nullptr;
outrec.owner = nullptr;
else
SetOwner(&outrec, e->outrec);
// nb: outRec.owner here is likely NOT the real
@ -1476,7 +1476,7 @@ namespace Clipper2Lib {
e2.outrec->pts = e1.outrec->pts;
e1.outrec->pts = nullptr;
}
else
else
SetOwner(e2.outrec, e1.outrec);
//and e1 and e2 are maxima and are about to be dropped from the Actives list.
@ -1526,7 +1526,6 @@ namespace Clipper2Lib {
return new_op;
}
void ClipperBase::CleanCollinear(OutRec* outrec)
{
outrec = GetRealOutRec(outrec);
@ -1541,7 +1540,7 @@ namespace Clipper2Lib {
for (; ; )
{
//NB if preserveCollinear == true, then only remove 180 deg. spikes
if ((CrossProduct(op2->prev->pt, op2->pt, op2->next->pt) == 0) &&
if (IsCollinear(op2->prev->pt, op2->pt, op2->next->pt) &&
(op2->pt == op2->prev->pt ||
op2->pt == op2->next->pt || !preserve_collinear_ ||
DotProduct(op2->prev->pt, op2->pt, op2->next->pt) < 0))
@ -1566,14 +1565,14 @@ namespace Clipper2Lib {
void ClipperBase::DoSplitOp(OutRec* outrec, OutPt* splitOp)
{
// splitOp.prev -> splitOp &&
// splitOp.prev -> splitOp &&
// splitOp.next -> splitOp.next.next are intersecting
OutPt* prevOp = splitOp->prev;
OutPt* nextNextOp = splitOp->next->next;
outrec->pts = prevOp;
Point64 ip;
GetIntersectPoint(prevOp->pt, splitOp->pt,
GetSegmentIntersectPt(prevOp->pt, splitOp->pt,
splitOp->next->pt, nextNextOp->pt, ip);
#ifdef USINGZ
@ -1617,7 +1616,7 @@ namespace Clipper2Lib {
{
OutRec* newOr = NewOutRec();
newOr->owner = outrec->owner;
splitOp->outrec = newOr;
splitOp->next->outrec = newOr;
OutPt* newOp = new OutPt(ip, newOr);
@ -1772,12 +1771,12 @@ namespace Clipper2Lib {
}
OutPt* ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt)
void ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt)
{
//MANAGE OPEN PATH INTERSECTIONS SEPARATELY ...
if (has_open_paths_ && (IsOpen(e1) || IsOpen(e2)))
{
if (IsOpen(e1) && IsOpen(e2)) return nullptr;
if (IsOpen(e1) && IsOpen(e2)) return;
Active* edge_o, * edge_c;
if (IsOpen(e1))
{
@ -1791,29 +1790,40 @@ namespace Clipper2Lib {
}
if (IsJoined(*edge_c)) Split(*edge_c, pt); // needed for safety
if (abs(edge_c->wind_cnt) != 1) return nullptr;
if (abs(edge_c->wind_cnt) != 1) return;
switch (cliptype_)
{
case ClipType::Union:
if (!IsHotEdge(*edge_c)) return nullptr;
if (!IsHotEdge(*edge_c)) return;
break;
default:
if (edge_c->local_min->polytype == PathType::Subject)
return nullptr;
return;
}
switch (fillrule_)
{
case FillRule::Positive: if (edge_c->wind_cnt != 1) return nullptr; break;
case FillRule::Negative: if (edge_c->wind_cnt != -1) return nullptr; break;
default: if (std::abs(edge_c->wind_cnt) != 1) return nullptr; break;
case FillRule::Positive:
if (edge_c->wind_cnt != 1) return;
break;
case FillRule::Negative:
if (edge_c->wind_cnt != -1) return;
break;
default:
if (std::abs(edge_c->wind_cnt) != 1) return;
}
#ifdef USINGZ
OutPt* resultOp;
#endif
//toggle contribution ...
if (IsHotEdge(*edge_o))
{
#ifdef USINGZ
resultOp = AddOutPt(*edge_o, pt);
#else
AddOutPt(*edge_o, pt);
#endif
if (IsFront(*edge_o)) edge_o->outrec->front_edge = nullptr;
else edge_o->outrec->back_edge = nullptr;
edge_o->outrec = nullptr;
@ -1833,18 +1843,26 @@ namespace Clipper2Lib {
SetSides(*e3->outrec, *edge_o, *e3);
else
SetSides(*e3->outrec, *e3, *edge_o);
return e3->outrec->pts;
return;
}
else
#ifdef USINGZ
resultOp = StartOpenPath(*edge_o, pt);
#else
StartOpenPath(*edge_o, pt);
#endif
}
else
#ifdef USINGZ
resultOp = StartOpenPath(*edge_o, pt);
#else
StartOpenPath(*edge_o, pt);
#endif
#ifdef USINGZ
if (zCallback_) SetZ(*edge_o, *edge_c, resultOp->pt);
#endif
return resultOp;
return;
} // end of an open path intersection
//MANAGING CLOSED PATHS FROM HERE ON
@ -1913,22 +1931,25 @@ namespace Clipper2Lib {
const bool e1_windcnt_in_01 = old_e1_windcnt == 0 || old_e1_windcnt == 1;
const bool e2_windcnt_in_01 = old_e2_windcnt == 0 || old_e2_windcnt == 1;
if ((!IsHotEdge(e1) && !e1_windcnt_in_01) || (!IsHotEdge(e2) && !e2_windcnt_in_01))
{
return nullptr;
}
if ((!IsHotEdge(e1) && !e1_windcnt_in_01) ||
(!IsHotEdge(e2) && !e2_windcnt_in_01))
return;
//NOW PROCESS THE INTERSECTION ...
#ifdef USINGZ
OutPt* resultOp = nullptr;
#endif
//if both edges are 'hot' ...
if (IsHotEdge(e1) && IsHotEdge(e2))
{
if ((old_e1_windcnt != 0 && old_e1_windcnt != 1) || (old_e2_windcnt != 0 && old_e2_windcnt != 1) ||
(e1.local_min->polytype != e2.local_min->polytype && cliptype_ != ClipType::Xor))
{
resultOp = AddLocalMaxPoly(e1, e2, pt);
#ifdef USINGZ
resultOp = AddLocalMaxPoly(e1, e2, pt);
if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt);
#else
AddLocalMaxPoly(e1, e2, pt);
#endif
}
else if (IsFront(e1) || (e1.outrec == e2.outrec))
@ -1937,19 +1958,20 @@ namespace Clipper2Lib {
//it's sensible to split polygons that ony touch at
//a common vertex (not at common edges).
resultOp = AddLocalMaxPoly(e1, e2, pt);
#ifdef USINGZ
resultOp = AddLocalMaxPoly(e1, e2, pt);
OutPt* op2 = AddLocalMinPoly(e1, e2, pt);
if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt);
if (zCallback_) SetZ(e1, e2, op2->pt);
#else
AddLocalMaxPoly(e1, e2, pt);
AddLocalMinPoly(e1, e2, pt);
#endif
}
else
{
resultOp = AddOutPt(e1, pt);
#ifdef USINGZ
resultOp = AddOutPt(e1, pt);
OutPt* op2 = AddOutPt(e2, pt);
if (zCallback_)
{
@ -1957,6 +1979,7 @@ namespace Clipper2Lib {
SetZ(e1, e2, op2->pt);
}
#else
AddOutPt(e1, pt);
AddOutPt(e2, pt);
#endif
SwapOutrecs(e1, e2);
@ -1964,17 +1987,21 @@ namespace Clipper2Lib {
}
else if (IsHotEdge(e1))
{
resultOp = AddOutPt(e1, pt);
#ifdef USINGZ
resultOp = AddOutPt(e1, pt);
if (zCallback_) SetZ(e1, e2, resultOp->pt);
#else
AddOutPt(e1, pt);
#endif
SwapOutrecs(e1, e2);
}
else if (IsHotEdge(e2))
{
resultOp = AddOutPt(e2, pt);
#ifdef USINGZ
resultOp = AddOutPt(e2, pt);
if (zCallback_) SetZ(e1, e2, resultOp->pt);
#else
AddOutPt(e2, pt);
#endif
SwapOutrecs(e1, e2);
}
@ -2004,33 +2031,53 @@ namespace Clipper2Lib {
if (!IsSamePolyType(e1, e2))
{
resultOp = AddLocalMinPoly(e1, e2, pt, false);
#ifdef USINGZ
resultOp = AddLocalMinPoly(e1, e2, pt, false);
if (zCallback_) SetZ(e1, e2, resultOp->pt);
#else
AddLocalMinPoly(e1, e2, pt, false);
#endif
}
else if (old_e1_windcnt == 1 && old_e2_windcnt == 1)
{
#ifdef USINGZ
resultOp = nullptr;
#endif
switch (cliptype_)
{
case ClipType::Union:
if (e1Wc2 <= 0 && e2Wc2 <= 0)
#ifdef USINGZ
resultOp = AddLocalMinPoly(e1, e2, pt, false);
#else
AddLocalMinPoly(e1, e2, pt, false);
#endif
break;
case ClipType::Difference:
if (((GetPolyType(e1) == PathType::Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
((GetPolyType(e1) == PathType::Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
{
#ifdef USINGZ
resultOp = AddLocalMinPoly(e1, e2, pt, false);
#else
AddLocalMinPoly(e1, e2, pt, false);
#endif
}
break;
case ClipType::Xor:
#ifdef USINGZ
resultOp = AddLocalMinPoly(e1, e2, pt, false);
#else
AddLocalMinPoly(e1, e2, pt, false);
#endif
break;
default:
if (e1Wc2 > 0 && e2Wc2 > 0)
#ifdef USINGZ
resultOp = AddLocalMinPoly(e1, e2, pt, false);
#else
AddLocalMinPoly(e1, e2, pt, false);
#endif
break;
}
#ifdef USINGZ
@ -2038,7 +2085,6 @@ namespace Clipper2Lib {
#endif
}
}
return resultOp;
}
inline void ClipperBase::DeleteFromAEL(Active& e)
@ -2065,7 +2111,7 @@ namespace Clipper2Lib {
e->next_in_sel = e->next_in_ael;
e->jump = e->next_in_sel;
if (e->join_with == JoinWith::Left)
e->curr_x = e->prev_in_ael->curr_x; // also avoids complications
e->curr_x = e->prev_in_ael->curr_x; // also avoids complications
else
e->curr_x = TopX(*e, top_y);
e = e->next_in_ael;
@ -2138,7 +2184,7 @@ namespace Clipper2Lib {
if (outrecHasEdges)
{
OutPt* opA = outrec->pts, * opZ = opA->next;
while (opP != opZ && opP->prev->pt.y == curr_y)
while (opP != opZ && opP->prev->pt.y == curr_y)
opP = opP->prev;
while (opN != opA && opN->next->pt.y == curr_y)
opN = opN->next;
@ -2150,7 +2196,7 @@ namespace Clipper2Lib {
while (opN->next != opP && opN->next->pt.y == curr_y)
opN = opN->next;
}
bool result =
bool result =
SetHorzSegHeadingForward(hs, opP, opN) &&
!hs.left_op->horz;
@ -2160,13 +2206,14 @@ namespace Clipper2Lib {
hs.right_op = nullptr; // (for sorting)
return result;
}
void ClipperBase::ConvertHorzSegsToJoins()
{
auto j = std::count_if(horz_seg_list_.begin(),
auto j = std::count_if(horz_seg_list_.begin(),
horz_seg_list_.end(),
[](HorzSegment& hs) { return UpdateHorzSegment(hs); });
if (j < 2) return;
std::stable_sort(horz_seg_list_.begin(), horz_seg_list_.end(), HorzSegSorter());
HorzSegmentList::iterator hs1 = horz_seg_list_.begin(), hs2;
@ -2207,8 +2254,8 @@ namespace Clipper2Lib {
DuplicateOp(hs1->left_op, false));
horz_join_list_.push_back(join);
}
}
}
}
}
}
void MoveSplits(OutRec* fromOr, OutRec* toOr)
@ -2301,7 +2348,7 @@ namespace Clipper2Lib {
void ClipperBase::AddNewIntersectNode(Active& e1, Active& e2, int64_t top_y)
{
Point64 ip;
if (!GetIntersectPoint(e1.bot, e1.top, e2.bot, e2.top, ip))
if (!GetSegmentIntersectPt(e1.bot, e1.top, e2.bot, e2.top, ip))
ip = Point64(e1.curr_x, top_y); //parallel edges
//rounding errors can occasionally place the calculated intersection
@ -2321,7 +2368,7 @@ namespace Clipper2Lib {
ip = GetClosestPointOnSegment(ip, e1.bot, e1.top);
else if (abs_dx2 > 100)
ip = GetClosestPointOnSegment(ip, e2.bot, e2.top);
else
else
{
if (ip.y < top_y) ip.y = top_y;
else ip.y = bot_y_;
@ -2453,7 +2500,7 @@ namespace Clipper2Lib {
horz_seg_list_.push_back(HorzSegment(op));
}
bool ClipperBase::ResetHorzDirection(const Active& horz,
bool ClipperBase::ResetHorzDirection(const Active& horz,
const Vertex* max_vertex, int64_t& horz_left, int64_t& horz_right)
{
if (horz.bot.x == horz.top.x)
@ -2536,8 +2583,8 @@ namespace Clipper2Lib {
if (IsHotEdge(horz) && IsJoined(*e))
Split(*e, e->top);
//if (IsHotEdge(horz) != IsHotEdge(*e))
// DoError(undefined_error_i);
//if (IsHotEdge(horz) != IsHotEdge(*e))
// DoError(undefined_error_i);
if (IsHotEdge(horz))
{
@ -2641,7 +2688,7 @@ namespace Clipper2Lib {
ResetHorzDirection(horz, vertex_max, horz_left, horz_right);
}
if (IsHotEdge(horz))
if (IsHotEdge(horz))
{
OutPt* op = AddOutPt(horz, horz.top);
AddTrialHorzJoin(op);
@ -2754,21 +2801,23 @@ namespace Clipper2Lib {
}
}
void ClipperBase::CheckJoinLeft(Active& e,
void ClipperBase::CheckJoinLeft(Active& e,
const Point64& pt, bool check_curr_x)
{
Active* prev = e.prev_in_ael;
if (IsOpen(e) || !IsHotEdge(e) || !prev ||
IsOpen(*prev) || !IsHotEdge(*prev)) return;
if (!prev ||
!IsHotEdge(e) || !IsHotEdge(*prev) ||
IsHorizontal(e) || IsHorizontal(*prev) ||
IsOpen(e) || IsOpen(*prev) ) return;
if ((pt.y < e.top.y + 2 || pt.y < prev->top.y + 2) &&
((e.bot.y > pt.y) || (prev->bot.y > pt.y))) return; // avoid trivial joins
((e.bot.y > pt.y) || (prev->bot.y > pt.y))) return; // avoid trivial joins
if (check_curr_x)
{
if (DistanceFromLineSqrd(pt, prev->bot, prev->top) > 0.25) return;
if (PerpendicDistFromLineSqrd(pt, prev->bot, prev->top) > 0.25) return;
}
else if (e.curr_x != prev->curr_x) return;
if (CrossProduct(e.top, pt, prev->top)) return;
if (!IsCollinear(e.top, pt, prev->top)) return;
if (e.outrec->idx == prev->outrec->idx)
AddLocalMaxPoly(*prev, e, pt);
@ -2780,22 +2829,24 @@ namespace Clipper2Lib {
e.join_with = JoinWith::Left;
}
void ClipperBase::CheckJoinRight(Active& e,
void ClipperBase::CheckJoinRight(Active& e,
const Point64& pt, bool check_curr_x)
{
Active* next = e.next_in_ael;
if (IsOpen(e) || !IsHotEdge(e) ||
!next || IsOpen(*next) || !IsHotEdge(*next)) return;
if (!next ||
!IsHotEdge(e) || !IsHotEdge(*next) ||
IsHorizontal(e) || IsHorizontal(*next) ||
IsOpen(e) || IsOpen(*next)) return;
if ((pt.y < e.top.y +2 || pt.y < next->top.y +2) &&
((e.bot.y > pt.y) || (next->bot.y > pt.y))) return; // avoid trivial joins
((e.bot.y > pt.y) || (next->bot.y > pt.y))) return; // avoid trivial joins
if (check_curr_x)
{
if (DistanceFromLineSqrd(pt, next->bot, next->top) > 0.35) return;
if (PerpendicDistFromLineSqrd(pt, next->bot, next->top) > 0.35) return;
}
else if (e.curr_x != next->curr_x) return;
if (CrossProduct(e.top, pt, next->top)) return;
if (!IsCollinear(e.top, pt, next->top)) return;
if (e.outrec->idx == next->outrec->idx)
AddLocalMaxPoly(e, *next, pt);
else if (e.outrec->idx < next->outrec->idx)
@ -2863,7 +2914,7 @@ namespace Clipper2Lib {
op2 = op2->next;
}
if (path.size() == 3 && IsVerySmallTriangle(*op2)) return false;
if (!isOpen && path.size() == 3 && IsVerySmallTriangle(*op2)) return false;
else return true;
}
@ -2872,8 +2923,8 @@ namespace Clipper2Lib {
if (!outrec->pts) return false;
if (!outrec->bounds.IsEmpty()) return true;
CleanCollinear(outrec);
if (!outrec->pts ||
!BuildPath64(outrec->pts, reverse_solution_, false, outrec->path)){
if (!outrec->pts ||
!BuildPath64(outrec->pts, reverse_solution_, false, outrec->path)){
return false;}
outrec->bounds = GetBounds(outrec->path);
return true;
@ -2887,10 +2938,10 @@ namespace Clipper2Lib {
if(!split || split == outrec || split->recursive_split == outrec) continue;
split->recursive_split = outrec; // prevent infinite loops
if (split->splits && CheckSplitOwner(outrec, split->splits))
if (split->splits && CheckSplitOwner(outrec, split->splits))
return true;
else if (CheckBounds(split) &&
IsValidOwner(outrec, split) &&
else if (CheckBounds(split) &&
IsValidOwner(outrec, split) &&
split->bounds.Contains(outrec->bounds) &&
Path1InsidePath2(outrec->pts, split->pts))
{
@ -2919,7 +2970,7 @@ namespace Clipper2Lib {
if (outrec->owner)
{
if (!outrec->owner->polypath)
if (!outrec->owner->polypath)
RecursiveCheckOwners(outrec->owner, polypath);
outrec->polypath = outrec->owner->polypath->AddChild(outrec->path);
}
@ -2968,7 +3019,7 @@ namespace Clipper2Lib {
open_paths.resize(0);
if (has_open_paths_)
open_paths.reserve(outrec_list_.size());
// outrec_list_.size() is not static here because
// CheckBounds below can indirectly add additional
// OutRec (via FixOutRecPts & CleanCollinear)
@ -2991,7 +3042,7 @@ namespace Clipper2Lib {
bool BuildPathD(OutPt* op, bool reverse, bool isOpen, PathD& path, double inv_scale)
{
if (!op || op->next == op || (!isOpen && op->next == op->prev))
if (!op || op->next == op || (!isOpen && op->next == op->prev))
return false;
path.resize(0);
@ -3024,7 +3075,7 @@ namespace Clipper2Lib {
#else
path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale));
#endif
}
if (reverse)
op2 = op2->prev;

View file

@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 28 November 2023 *
* Date : 17 April 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : Path Offset (Inflate/Shrink) *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
@ -20,60 +20,19 @@ const double floating_point_tolerance = 1e-12;
// Miscellaneous methods
//------------------------------------------------------------------------------
inline bool ToggleBoolIf(bool val, bool condition)
std::optional<size_t> GetLowestClosedPathIdx(const Paths64& paths)
{
return condition ? !val : val;
}
void GetMultiBounds(const Paths64& paths, std::vector<Rect64>& recList)
{
recList.reserve(paths.size());
for (const Path64& path : paths)
{
if (path.size() < 1)
{
recList.push_back(InvalidRect64);
continue;
}
int64_t x = path[0].x, y = path[0].y;
Rect64 r = Rect64(x, y, x, y);
for (const Point64& pt : path)
{
if (pt.y > r.bottom) r.bottom = pt.y;
else if (pt.y < r.top) r.top = pt.y;
if (pt.x > r.right) r.right = pt.x;
else if (pt.x < r.left) r.left = pt.x;
}
recList.push_back(r);
}
}
bool ValidateBounds(std::vector<Rect64>& recList, double delta)
{
int64_t int_delta = static_cast<int64_t>(delta);
int64_t big = MAX_COORD - int_delta;
int64_t small = MIN_COORD + int_delta;
for (const Rect64& r : recList)
{
if (!r.IsValid()) continue; // ignore invalid paths
else if (r.left < small || r.right > big ||
r.top < small || r.bottom > big) return false;
}
return true;
}
int GetLowestClosedPathIdx(std::vector<Rect64>& boundsList)
{
int i = -1, result = -1;
std::optional<size_t> result;
Point64 botPt = Point64(INT64_MAX, INT64_MIN);
for (const Rect64& r : boundsList)
{
++i;
if (!r.IsValid()) continue; // ignore invalid paths
else if (r.bottom > botPt.y || (r.bottom == botPt.y && r.left < botPt.x))
for (size_t i = 0; i < paths.size(); ++i)
{
for (const Point64& pt : paths[i])
{
botPt = Point64(r.left, r.bottom);
result = static_cast<int>(i);
if ((pt.y < botPt.y) ||
((pt.y == botPt.y) && (pt.x >= botPt.x))) continue;
result = i;
botPt.x = pt.x;
botPt.y = pt.y;
}
}
return result;
@ -96,14 +55,14 @@ inline bool AlmostZero(double value, double epsilon = 0.001)
return std::fabs(value) < epsilon;
}
inline double Hypot(double x, double y)
inline double Hypot(double x, double y)
{
//see https://stackoverflow.com/a/32436148/359538
return std::sqrt(x * x + y * y);
}
inline PointD NormalizeVector(const PointD& vec)
{
{
double h = Hypot(vec.x, vec.y);
if (AlmostZero(h)) return PointD(0,0);
double inverseHypot = 1 / h;
@ -164,30 +123,21 @@ ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType
for (Path64& p: paths_in)
StripDuplicates(p, is_joined);
// get bounds of each path --> bounds_list
GetMultiBounds(paths_in, bounds_list);
if (end_type == EndType::Polygon)
{
is_hole_list.reserve(paths_in.size());
for (const Path64& path : paths_in)
is_hole_list.push_back(Area(path) < 0);
lowest_path_idx = GetLowestClosedPathIdx(bounds_list);
lowest_path_idx = GetLowestClosedPathIdx(paths_in);
// the lowermost path must be an outer path, so if its orientation is negative,
// then flag the whole group is 'reversed' (will negate delta etc.)
// as this is much more efficient than reversing every path.
is_reversed = (lowest_path_idx >= 0) && is_hole_list[lowest_path_idx];
if (is_reversed) is_hole_list.flip();
is_reversed = (lowest_path_idx.has_value()) && Area(paths_in[lowest_path_idx.value()]) < 0;
}
else
{
lowest_path_idx = -1;
lowest_path_idx = std::nullopt;
is_reversed = false;
is_hole_list.resize(paths_in.size());
}
}
//------------------------------------------------------------------------------
// ClipperOffset methods
//------------------------------------------------------------------------------
@ -216,66 +166,29 @@ void ClipperOffset::BuildNormals(const Path64& path)
norms.push_back(GetUnitNormal(*path_stop_iter, *(path.cbegin())));
}
inline PointD TranslatePoint(const PointD& pt, double dx, double dy)
{
#ifdef USINGZ
return PointD(pt.x + dx, pt.y + dy, pt.z);
#else
return PointD(pt.x + dx, pt.y + dy);
#endif
}
inline PointD ReflectPoint(const PointD& pt, const PointD& pivot)
{
#ifdef USINGZ
return PointD(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y), pt.z);
#else
return PointD(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y));
#endif
}
PointD IntersectPoint(const PointD& pt1a, const PointD& pt1b,
const PointD& pt2a, const PointD& pt2b)
{
if (pt1a.x == pt1b.x) //vertical
{
if (pt2a.x == pt2b.x) return PointD(0, 0);
double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x);
double b2 = pt2a.y - m2 * pt2a.x;
return PointD(pt1a.x, m2 * pt1a.x + b2);
}
else if (pt2a.x == pt2b.x) //vertical
{
double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x);
double b1 = pt1a.y - m1 * pt1a.x;
return PointD(pt2a.x, m1 * pt2a.x + b1);
}
else
{
double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x);
double b1 = pt1a.y - m1 * pt1a.x;
double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x);
double b2 = pt2a.y - m2 * pt2a.x;
if (m1 == m2) return PointD(0, 0);
double x = (b2 - b1) / (m1 - m2);
return PointD(x, m1 * x + b1);
}
}
void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k)
{
PointD pt1, pt2;
if (j == k)
{
double abs_delta = std::abs(group_delta_);
#ifdef USINGZ
pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y, path[j].z);
pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y, path[j].z);
#else
pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y);
pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y);
}
#endif
}
else
{
#ifdef USINGZ
pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y, path[j].z);
pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y, path[j].z);
#else
pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y);
pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y);
#endif
}
path_out.push_back(Point64(pt1));
path_out.push_back(Point64(pt2));
@ -284,7 +197,7 @@ void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k)
void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k)
{
PointD vec;
if (j == k)
if (j == k)
vec = PointD(norms[j].y, -norms[j].x);
else
vec = GetAvgUnitVector(
@ -304,10 +217,8 @@ void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k)
if (j == k)
{
PointD pt4 = PointD(pt3.x + vec.x * group_delta_, pt3.y + vec.y * group_delta_);
PointD pt = IntersectPoint(pt1, pt2, pt3, pt4);
#ifdef USINGZ
pt.z = ptQ.z;
#endif
PointD pt = ptQ;
GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
//get the second intersect point through reflecion
path_out.push_back(Point64(ReflectPoint(pt, ptQ)));
path_out.push_back(Point64(pt));
@ -315,10 +226,8 @@ void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k)
else
{
PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_);
PointD pt = IntersectPoint(pt1, pt2, pt3, pt4);
#ifdef USINGZ
pt.z = ptQ.z;
#endif
PointD pt = ptQ;
GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
path_out.push_back(Point64(pt));
//get the second intersect point through reflecion
path_out.push_back(Point64(ReflectPoint(pt, ptQ)));
@ -343,7 +252,7 @@ void ClipperOffset::DoMiter(const Path64& path, size_t j, size_t k, double cos_a
void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle)
{
if (deltaCallback64_) {
// when deltaCallback64_ is assigned, group_delta_ won't be constant,
// when deltaCallback64_ is assigned, group_delta_ won't be constant,
// so we'll need to do the following calculations for *every* vertex.
double abs_delta = std::fabs(group_delta_);
double arcTol = (arc_tolerance_ > floating_point_tolerance ?
@ -387,7 +296,7 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size
// sin(A) < 0: right turning
// cos(A) < 0: change in angle is more than 90 degree
if (path[j] == path[k]) { k = j; return; }
if (path[j] == path[k]) return;
double sin_a = CrossProduct(norms[j], norms[k]);
double cos_a = DotProduct(norms[j], norms[k]);
@ -404,18 +313,29 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size
return;
}
if (cos_a > -0.99 && (sin_a * group_delta_ < 0)) // test for concavity first (#593)
if (cos_a > -0.999 && (sin_a * group_delta_ < 0)) // test for concavity first (#593)
{
// is concave
// is concave (so insert 3 points that will create a negative region)
#ifdef USINGZ
path_out.push_back(Point64(GetPerpendic(path[j], norms[k], group_delta_), path[j].z));
#else
path_out.push_back(GetPerpendic(path[j], norms[k], group_delta_));
// this extra point is the only (simple) way to ensure that
// path reversals are fully cleaned with the trailing clipper
path_out.push_back(path[j]); // (#405)
#endif
// this extra point is the only simple way to ensure that path reversals
// (ie over-shrunk paths) are fully cleaned out with the trailing union op.
// However it's probably safe to skip this whenever an angle is almost flat.
if (cos_a < 0.99) path_out.push_back(path[j]); // (#405)
#ifdef USINGZ
path_out.push_back(Point64(GetPerpendic(path[j], norms[j], group_delta_), path[j].z));
#else
path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_));
#endif
}
else if (cos_a > 0.999 && join_type_ != JoinType::Round)
else if (cos_a > 0.999 && join_type_ != JoinType::Round)
{
// almost straight - less than 2.5 degree (#424, #482, #526 & #724)
// almost straight - less than 2.5 degree (#424, #482, #526 & #724)
DoMiter(path, j, k, cos_a);
}
else if (join_type_ == JoinType::Miter)
@ -435,9 +355,9 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size
void ClipperOffset::OffsetPolygon(Group& group, const Path64& path)
{
path_out.clear();
for (Path64::size_type j = 0, k = path.size() -1; j < path.size(); k = j, ++j)
OffsetPoint(group, path, j, k);
solution.push_back(path_out);
for (Path64::size_type j = 0, k = path.size() - 1; j < path.size(); k = j, ++j)
OffsetPoint(group, path, j, k);
solution->push_back(path_out);
}
void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path)
@ -445,8 +365,8 @@ void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path)
OffsetPolygon(group, path);
Path64 reverse_path(path);
std::reverse(reverse_path.begin(), reverse_path.end());
//rebuild normals // BuildNormals(path);
//rebuild normals
std::reverse(norms.begin(), norms.end());
norms.push_back(norms[0]);
norms.erase(norms.begin());
@ -459,7 +379,7 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
{
// do the line start cap
if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, 0, 0);
if (std::fabs(group_delta_) <= floating_point_tolerance)
path_out.push_back(path[0]);
else
@ -477,13 +397,13 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
break;
}
}
size_t highI = path.size() - 1;
// offset the left side going forward
for (Path64::size_type j = 1, k = 0; j < highI; k = j, ++j)
OffsetPoint(group, path, j, k);
// reverse normals
// reverse normals
for (size_t i = highI; i > 0; --i)
norms[i] = PointD(-norms[i - 1].x, -norms[i - 1].y);
norms[0] = norms[highI];
@ -510,41 +430,34 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
}
}
for (size_t j = highI, k = 0; j > 0; k = j, --j)
for (size_t j = highI -1, k = highI; j > 0; k = j, --j)
OffsetPoint(group, path, j, k);
solution.push_back(path_out);
solution->push_back(path_out);
}
void ClipperOffset::DoGroupOffset(Group& group)
{
if (group.end_type == EndType::Polygon)
{
// a straight path (2 points) can now also be 'polygon' offset
// a straight path (2 points) can now also be 'polygon' offset
// where the ends will be treated as (180 deg.) joins
if (group.lowest_path_idx < 0) delta_ = std::abs(delta_);
if (!group.lowest_path_idx.has_value()) delta_ = std::abs(delta_);
group_delta_ = (group.is_reversed) ? -delta_ : delta_;
}
else
group_delta_ = std::abs(delta_);// *0.5;
double abs_delta = std::fabs(group_delta_);
if (!ValidateBounds(group.bounds_list, abs_delta))
{
DoError(range_error_i);
error_code_ |= range_error_i;
return;
}
join_type_ = group.join_type;
end_type_ = group.end_type;
if (group.join_type == JoinType::Round || group.end_type == EndType::Round)
{
// calculate a sensible number of steps (for 360 deg for the given offset)
// arcTol - when arc_tolerance_ is undefined (0), the amount of
// curve imprecision that's allowed is based on the size of the
// offset (delta). Obviously very large offsets will almost always
// require much less precision. See also offset_triginometry2.svg
// calculate the number of steps required to approximate a circle
// (see http://www.angusj.com/clipper2/Docs/Trigonometry.htm)
// arcTol - when arc_tolerance_ is undefined (0) then curve imprecision
// will be relative to the size of the offset (delta). Obviously very
//large offsets will almost always require much less precision.
double arcTol = (arc_tolerance_ > floating_point_tolerance ?
std::min(abs_delta, arc_tolerance_) :
std::log10(2 + abs_delta) * default_arc_tolerance);
@ -556,24 +469,29 @@ void ClipperOffset::DoGroupOffset(Group& group)
steps_per_rad_ = steps_per_360 / (2 * PI);
}
std::vector<Rect64>::const_iterator path_rect_it = group.bounds_list.cbegin();
std::vector<bool>::const_iterator is_hole_it = group.is_hole_list.cbegin();
//double min_area = PI * Sqr(group_delta_);
Paths64::const_iterator path_in_it = group.paths_in.cbegin();
for ( ; path_in_it != group.paths_in.cend(); ++path_in_it, ++path_rect_it, ++is_hole_it)
for ( ; path_in_it != group.paths_in.cend(); ++path_in_it)
{
if (!path_rect_it->IsValid()) continue;
Path64::size_type pathLen = path_in_it->size();
path_out.clear();
if (pathLen == 1) // single point
{
if (deltaCallback64_)
{
group_delta_ = deltaCallback64_(*path_in_it, norms, 0, 0);
if (group.is_reversed) group_delta_ = -group_delta_;
abs_delta = std::fabs(group_delta_);
}
if (group_delta_ < 1) continue;
const Point64& pt = (*path_in_it)[0];
//single vertex so build a circle or square ...
if (group.join_type == JoinType::Round)
{
double radius = abs_delta;
int steps = static_cast<int>(std::ceil(steps_per_rad_ * 2 * PI)); //#617
size_t steps = steps_per_rad_ > 0 ? static_cast<size_t>(std::ceil(steps_per_rad_ * 2 * PI)) : 0; //#617
path_out = Ellipse(pt, radius, radius, steps);
#ifdef USINGZ
for (auto& p : path_out) p.z = pt.z;
@ -588,19 +506,14 @@ void ClipperOffset::DoGroupOffset(Group& group)
for (auto& p : path_out) p.z = pt.z;
#endif
}
solution.push_back(path_out);
continue;
} // end of offsetting a single point
// when shrinking outer paths, make sure they can shrink this far (#593)
// also when shrinking holes, make sure they too can shrink this far (#715)
if ((group_delta_ > 0) == ToggleBoolIf(*is_hole_it, group.is_reversed) &&
(std::min(path_rect_it->Width(), path_rect_it->Height()) <= -group_delta_ * 2) )
continue;
solution->push_back(path_out);
continue;
} // end of offsetting a single point
if ((pathLen == 2) && (group.end_type == EndType::Joined))
end_type_ = (group.join_type == JoinType::Round) ?
EndType::Round :
end_type_ = (group.join_type == JoinType::Round) ?
EndType::Round :
EndType::Square;
BuildNormals(*path_in_it);
@ -610,6 +523,16 @@ void ClipperOffset::DoGroupOffset(Group& group)
}
}
#ifdef USINGZ
void ClipperOffset::ZCB(const Point64& bot1, const Point64& top1,
const Point64& bot2, const Point64& top2, Point64& ip)
{
if (bot1.z && ((bot1.z == bot2.z) || (bot1.z == top2.z))) ip.z = bot1.z;
else if (bot2.z && (bot2.z == top1.z)) ip.z = bot2.z;
else if (top1.z && (top1.z == top2.z)) ip.z = top1.z;
else if (zCallback64_) zCallback64_(bot1, top1, bot2, top2, ip);
}
#endif
size_t ClipperOffset::CalcSolutionCapacity()
{
@ -635,40 +558,35 @@ bool ClipperOffset::CheckReverseOrientation()
void ClipperOffset::ExecuteInternal(double delta)
{
error_code_ = 0;
solution.clear();
if (groups_.size() == 0) return;
solution.reserve(CalcSolutionCapacity());
solution->reserve(CalcSolutionCapacity());
if (std::abs(delta) < 0.5) // ie: offset is insignificant
if (std::abs(delta) < 0.5) // ie: offset is insignificant
{
Paths64::size_type sol_size = 0;
for (const Group& group : groups_) sol_size += group.paths_in.size();
solution.reserve(sol_size);
solution->reserve(sol_size);
for (const Group& group : groups_)
copy(group.paths_in.begin(), group.paths_in.end(), back_inserter(solution));
return;
copy(group.paths_in.begin(), group.paths_in.end(), back_inserter(*solution));
}
temp_lim_ = (miter_limit_ <= 1) ?
2.0 :
2.0 / (miter_limit_ * miter_limit_);
delta_ = delta;
std::vector<Group>::iterator git;
for (git = groups_.begin(); git != groups_.end(); ++git)
else
{
DoGroupOffset(*git);
if (!error_code_) continue; // all OK
solution.clear();
temp_lim_ = (miter_limit_ <= 1) ?
2.0 :
2.0 / (miter_limit_ * miter_limit_);
delta_ = delta;
std::vector<Group>::iterator git;
for (git = groups_.begin(); git != groups_.end(); ++git)
{
DoGroupOffset(*git);
if (!error_code_) continue; // all OK
solution->clear();
}
}
}
void ClipperOffset::Execute(double delta, Paths64& paths)
{
paths.clear();
ExecuteInternal(delta);
if (!solution.size()) return;
if (!solution->size()) return;
bool paths_reversed = CheckReverseOrientation();
//clean up self-intersections ...
@ -677,41 +595,45 @@ void ClipperOffset::Execute(double delta, Paths64& paths)
//the solution should retain the orientation of the input
c.ReverseSolution(reverse_solution_ != paths_reversed);
#ifdef USINGZ
if (zCallback64_) { c.SetZCallback(zCallback64_); }
auto fp = std::bind(&ClipperOffset::ZCB, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5);
c.SetZCallback(fp);
#endif
c.AddSubject(solution);
if (paths_reversed)
c.Execute(ClipType::Union, FillRule::Negative, paths);
c.AddSubject(*solution);
if (solution_tree)
{
if (paths_reversed)
c.Execute(ClipType::Union, FillRule::Negative, *solution_tree);
else
c.Execute(ClipType::Union, FillRule::Positive, *solution_tree);
}
else
c.Execute(ClipType::Union, FillRule::Positive, paths);
{
if (paths_reversed)
c.Execute(ClipType::Union, FillRule::Negative, *solution);
else
c.Execute(ClipType::Union, FillRule::Positive, *solution);
}
}
void ClipperOffset::Execute(double delta, Paths64& paths)
{
paths.clear();
solution = &paths;
solution_tree = nullptr;
ExecuteInternal(delta);
}
void ClipperOffset::Execute(double delta, PolyTree64& polytree)
{
polytree.Clear();
solution_tree = &polytree;
solution = new Paths64();
ExecuteInternal(delta);
if (!solution.size()) return;
bool paths_reversed = CheckReverseOrientation();
//clean up self-intersections ...
Clipper64 c;
c.PreserveCollinear(false);
//the solution should retain the orientation of the input
c.ReverseSolution (reverse_solution_ != paths_reversed);
#ifdef USINGZ
if (zCallback64_) {
c.SetZCallback(zCallback64_);
}
#endif
c.AddSubject(solution);
if (paths_reversed)
c.Execute(ClipType::Union, FillRule::Negative, polytree);
else
c.Execute(ClipType::Union, FillRule::Positive, polytree);
delete solution;
solution = nullptr;
}
void ClipperOffset::Execute(DeltaCallback64 delta_cb, Paths64& paths)

View file

@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 8 September 2023 *
* Date : 5 July 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : FAST rectangular clipping *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
@ -71,7 +71,7 @@ namespace Clipper2Lib {
return pt1.y == pt2.y;
}
inline bool GetSegmentIntersection(const Point64& p1,
bool GetSegmentIntersection(const Point64& p1,
const Point64& p2, const Point64& p3, const Point64& p4, Point64& ip)
{
double res1 = CrossProduct(p1, p3, p4);
@ -113,7 +113,7 @@ namespace Clipper2Lib {
if ((res3 > 0) == (res4 > 0)) return false;
// segments must intersect to get here
return GetIntersectPoint(p1, p2, p3, p4, ip);
return GetSegmentIntersectPt(p1, p2, p3, p4, ip);
}
inline bool GetIntersection(const Path64& rectPath,
@ -125,7 +125,7 @@ namespace Clipper2Lib {
{
case Location::Left:
if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) return true;
else if ((p.y < rectPath[0].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip))
else if ((p.y < rectPath[0].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip))
{
loc = Location::Top;
return true;
@ -180,7 +180,7 @@ namespace Clipper2Lib {
else return false;
default: // loc == rInside
if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip))
if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip))
{
loc = Location::Left;
return true;
@ -320,9 +320,9 @@ namespace Clipper2Lib {
// this method is only called by InternalExecute.
// Later splitting & rejoining won't create additional op's,
// though they will change the (non-storage) results_ count.
int curr_idx = static_cast<int>(results_.size()) - 1;
size_t curr_idx = results_.size();
OutPt2* result;
if (curr_idx < 0 || start_new)
if (curr_idx == 0 || start_new)
{
result = &op_container_.emplace_back(OutPt2());
result->pt = pt;
@ -332,6 +332,7 @@ namespace Clipper2Lib {
}
else
{
--curr_idx;
OutPt2* prevOp = results_[curr_idx];
if (prevOp->pt == pt) return prevOp;
result = &op_container_.emplace_back(OutPt2());
@ -349,27 +350,27 @@ namespace Clipper2Lib {
void RectClip64::AddCorner(Location prev, Location curr)
{
if (HeadingClockwise(prev, curr))
Add(rect_as_path_[static_cast<int>(prev)]);
Add(rect_as_path_[static_cast<size_t>(prev)]);
else
Add(rect_as_path_[static_cast<int>(curr)]);
Add(rect_as_path_[static_cast<size_t>(curr)]);
}
void RectClip64::AddCorner(Location& loc, bool isClockwise)
{
if (isClockwise)
{
Add(rect_as_path_[static_cast<int>(loc)]);
Add(rect_as_path_[static_cast<size_t>(loc)]);
loc = GetAdjacentLocation(loc, true);
}
else
{
loc = GetAdjacentLocation(loc, false);
Add(rect_as_path_[static_cast<int>(loc)]);
Add(rect_as_path_[static_cast<size_t>(loc)]);
}
}
void RectClip64::GetNextLocation(const Path64& path,
Location& loc, int& i, int highI)
Location& loc, size_t& i, size_t highI)
{
switch (loc)
{
@ -420,31 +421,52 @@ namespace Clipper2Lib {
break; //inner loop
}
break;
} //switch
} //switch
}
bool StartLocsAreClockwise(const std::vector<Location>& startlocs)
{
int result = 0;
for (size_t i = 1; i < startlocs.size(); ++i)
{
int d = static_cast<int>(startlocs[i]) - static_cast<int>(startlocs[i - 1]);
switch (d)
{
case -1: result -= 1; break;
case 1: result += 1; break;
case -3: result += 1; break;
case 3: result -= 1; break;
}
}
return result > 0;
}
void RectClip64::ExecuteInternal(const Path64& path)
{
int i = 0, highI = static_cast<int>(path.size()) - 1;
if (path.size() < 1)
return;
size_t highI = path.size() - 1;
Location prev = Location::Inside, loc;
Location crossing_loc = Location::Inside;
Location first_cross_ = Location::Inside;
if (!GetLocation(rect_, path[highI], loc))
{
i = highI - 1;
while (i >= 0 && !GetLocation(rect_, path[i], prev)) --i;
if (i < 0)
size_t i = highI;
while (i > 0 && !GetLocation(rect_, path[i - 1], prev))
--i;
if (i == 0)
{
// all of path must be inside fRect
for (const auto& pt : path) Add(pt);
return;
}
if (prev == Location::Inside) loc = Location::Inside;
i = 0;
}
Location startingLoc = loc;
Location starting_loc = loc;
///////////////////////////////////////////////////
size_t i = 0;
while (i <= highI)
{
prev = loc;
@ -454,12 +476,12 @@ namespace Clipper2Lib {
if (i > highI) break;
Point64 ip, ip2;
Point64 prev_pt = (i) ?
path[static_cast<size_t>(i - 1)] :
Point64 prev_pt = (i) ?
path[static_cast<size_t>(i - 1)] :
path[highI];
crossing_loc = loc;
if (!GetIntersection(rect_as_path_,
if (!GetIntersection(rect_as_path_,
path[i], prev_pt, crossing_loc, ip))
{
// ie remaining outside
@ -470,7 +492,7 @@ namespace Clipper2Lib {
start_locs_.push_back(prev);
prev = GetAdjacentLocation(prev, isClockw);
} while (prev != loc);
crossing_loc = crossing_prev; // still not crossed
crossing_loc = crossing_prev; // still not crossed
}
else if (prev != Location::Inside && prev != loc)
{
@ -504,7 +526,7 @@ namespace Clipper2Lib {
}
else if (prev != Location::Inside)
{
// passing right through rect. 'ip' here will be the second
// passing right through rect. 'ip' here will be the second
// intersect pt but we'll also need the first intersect pt (ip2)
loc = prev;
GetIntersection(rect_as_path_, prev_pt, path[i], loc, ip2);
@ -543,7 +565,7 @@ namespace Clipper2Lib {
if (first_cross_ == Location::Inside)
{
// path never intersects
if (startingLoc != Location::Inside)
if (starting_loc != Location::Inside)
{
// path is outside rect
// but being outside, it still may not contain rect
@ -552,11 +574,13 @@ namespace Clipper2Lib {
{
// yep, the path does fully contain rect
// so add rect to the solution
bool is_clockwise_path = StartLocsAreClockwise(start_locs_);
for (size_t j = 0; j < 4; ++j)
{
Add(rect_as_path_[j]);
size_t k = is_clockwise_path ? j : 3 - j; // reverses result path
Add(rect_as_path_[k]);
// we may well need to do some splitting later, so
AddToEdge(edges_[j * 2], results_[0]);
AddToEdge(edges_[k * 2], results_[0]);
}
}
}
@ -589,8 +613,7 @@ namespace Clipper2Lib {
OutPt2* op2 = op;
do
{
if (!CrossProduct(op2->prev->pt,
op2->pt, op2->next->pt))
if (IsCollinear(op2->prev->pt, op2->pt, op2->next->pt))
{
if (op2 == op)
{
@ -640,7 +663,7 @@ namespace Clipper2Lib {
}
}
void RectClip64::TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw)
void RectClip64::TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw)
{
if (ccw.empty()) return;
bool isHorz = ((idx == 1) || (idx == 3));
@ -648,7 +671,7 @@ namespace Clipper2Lib {
size_t i = 0, j = 0;
OutPt2* p1, * p2, * p1a, * p2a, * op, * op2;
while (i < cw.size())
while (i < cw.size())
{
p1 = cw[i];
if (!p1 || p1->next == p1->prev)
@ -825,8 +848,8 @@ namespace Clipper2Lib {
OutPt2* op2 = op->next;
while (op2 && op2 != op)
{
if (CrossProduct(op2->prev->pt,
op2->pt, op2->next->pt) == 0)
if (IsCollinear(op2->prev->pt,
op2->pt, op2->next->pt))
{
op = op2->prev;
op2 = UnlinkOp(op2);
@ -854,7 +877,7 @@ namespace Clipper2Lib {
if (rect_.IsEmpty()) return result;
for (const Path64& path : paths)
{
{
if (path.size() < 3) continue;
path_bounds_ = GetBounds(path);
if (!rect_.Intersects(path_bounds_))
@ -868,9 +891,9 @@ namespace Clipper2Lib {
ExecuteInternal(path);
CheckEdges();
for (int i = 0; i < 4; ++i)
for (size_t i = 0; i < 4; ++i)
TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]);
for (OutPt2*& op : results_)
{
Path64 tmp = GetPath(op);
@ -925,14 +948,14 @@ namespace Clipper2Lib {
op_container_ = std::deque<OutPt2>();
start_locs_.clear();
int i = 1, highI = static_cast<int>(path.size()) - 1;
size_t i = 1, highI = path.size() - 1;
Location prev = Location::Inside, loc;
Location crossing_loc;
if (!GetLocation(rect_, path[0], loc))
{
while (i <= highI && !GetLocation(rect_, path[i], prev)) ++i;
if (i > highI)
if (i > highI)
{
// all of path must be inside fRect
for (const auto& pt : path) Add(pt);
@ -953,7 +976,7 @@ namespace Clipper2Lib {
Point64 prev_pt = path[static_cast<size_t>(i - 1)];
crossing_loc = loc;
if (!GetIntersection(rect_as_path_,
if (!GetIntersection(rect_as_path_,
path[i], prev_pt, crossing_loc, ip))
{
// ie remaining outside
@ -971,10 +994,10 @@ namespace Clipper2Lib {
}
else if (prev != Location::Inside)
{
// passing right through rect. 'ip' here will be the second
// passing right through rect. 'ip' here will be the second
// intersect pt but we'll also need the first intersect pt (ip2)
crossing_loc = prev;
GetIntersection(rect_as_path_,
GetIntersection(rect_as_path_,
prev_pt, path[i], crossing_loc, ip2);
Add(ip2, true);
Add(ip);
@ -991,14 +1014,14 @@ namespace Clipper2Lib {
{
Path64 result;
if (!op || op == op->next) return result;
op = op->next; // starting at path beginning
op = op->next; // starting at path beginning
result.push_back(op->pt);
OutPt2 *op2 = op->next;
while (op2 != op)
{
result.push_back(op2->pt);
op2 = op2->next;
}
}
return result;
}

View file

@ -33,13 +33,11 @@
#include <shared_mutex>
#endif
/* GODOT start */
#if !defined(_MSC_VER)
#include <guiddef.h>
#include <dxguids.h>
#endif
/* GODOT end */
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
@ -8186,7 +8184,6 @@ HRESULT AllocatorPimpl::UpdateD3D12Budget()
D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC& resourceDesc) const
{
/* GODOT start */
#if defined(_MSC_VER) || !defined(_WIN32)
return m_Device->GetResourceAllocationInfo(0, 1, &resourceDesc);
#else
@ -8194,7 +8191,6 @@ D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(c
m_Device->GetResourceAllocationInfo(&ret, 0, 1, &resourceDesc);
return ret;
#endif
/* GODOT end */
}
#ifdef __ID3D12Device8_INTERFACE_DEFINED__
@ -8202,7 +8198,6 @@ D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(c
{
D3D12MA_ASSERT(m_Device8 != NULL);
D3D12_RESOURCE_ALLOCATION_INFO1 info1Unused;
/* GODOT start */
#if defined(_MSC_VER) || !defined(_WIN32)
return m_Device8->GetResourceAllocationInfo2(0, 1, &resourceDesc, &info1Unused);
#else
@ -8210,7 +8205,6 @@ D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(c
m_Device8->GetResourceAllocationInfo2(&ret, 0, 1, &resourceDesc, &info1Unused);
return ret;
#endif
/* GODOT end */
}
#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__

View file

@ -0,0 +1,45 @@
diff --git a/thirdparty/d3d12ma/D3D12MemAlloc.cpp b/thirdparty/d3d12ma/D3D12MemAlloc.cpp
index 8e2488091a..80d910e694 100644
--- a/thirdparty/d3d12ma/D3D12MemAlloc.cpp
+++ b/thirdparty/d3d12ma/D3D12MemAlloc.cpp
@@ -33,6 +33,12 @@
#include <shared_mutex>
#endif
+#if !defined(_MSC_VER)
+#include <guiddef.h>
+
+#include <dxguids.h>
+#endif
+
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
@@ -8178,7 +8184,13 @@ HRESULT AllocatorPimpl::UpdateD3D12Budget()
D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC& resourceDesc) const
{
+#if defined(_MSC_VER) || !defined(_WIN32)
return m_Device->GetResourceAllocationInfo(0, 1, &resourceDesc);
+#else
+ D3D12_RESOURCE_ALLOCATION_INFO ret;
+ m_Device->GetResourceAllocationInfo(&ret, 0, 1, &resourceDesc);
+ return ret;
+#endif
}
#ifdef __ID3D12Device8_INTERFACE_DEFINED__
@@ -8186,7 +8198,13 @@ D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(c
{
D3D12MA_ASSERT(m_Device8 != NULL);
D3D12_RESOURCE_ALLOCATION_INFO1 info1Unused;
+#if defined(_MSC_VER) || !defined(_WIN32)
return m_Device8->GetResourceAllocationInfo2(0, 1, &resourceDesc, &info1Unused);
+#else
+ D3D12_RESOURCE_ALLOCATION_INFO ret;
+ m_Device8->GetResourceAllocationInfo2(&ret, 0, 1, &resourceDesc, &info1Unused);
+ return ret;
+#endif
}
#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__

View file

@ -1,4 +1,4 @@
//*********************************************************
//*********************************************************
//
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License (MIT).

View file

@ -25,12 +25,10 @@
struct DefaultSampleMask { operator UINT() noexcept { return UINT_MAX; } };
struct DefaultSampleDesc { operator DXGI_SAMPLE_DESC() noexcept { return DXGI_SAMPLE_DESC{1, 0}; } };
/* GODOT start */
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4324)
#endif
/* GODOT start */
template <typename InnerStructType, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type, typename DefaultArg = InnerStructType>
class alignas(void*) CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT
{
@ -46,11 +44,9 @@ public:
InnerStructType* operator&() noexcept { return &pssInner; }
InnerStructType const* operator&() const noexcept { return &pssInner; }
};
/* GODOT start */
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
/* GODOT end */
typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_PIPELINE_STATE_FLAGS, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS> CD3DX12_PIPELINE_STATE_STREAM_FLAGS;
typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK> CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK;
typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< ID3D12RootSignature*, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE> CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE;

View file

@ -996,9 +996,7 @@ struct CD3DX12_GPU_DESCRIPTOR_HANDLE : public D3D12_GPU_DESCRIPTOR_HANDLE
// two code paths for building root signatures, this helper method reconstructs a 1.0 signature when
// 1.1 is not supported.
inline HRESULT D3DX12SerializeVersionedRootSignature(
/* GODOT start */
_In_ HMODULE pLibD3D12,
/* GODOT end */
_In_ const D3D12_VERSIONED_ROOT_SIGNATURE_DESC* pRootSignatureDesc,
D3D_ROOT_SIGNATURE_VERSION MaxVersion,
_Outptr_ ID3DBlob** ppBlob,
@ -1009,22 +1007,18 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
*ppErrorBlob = nullptr;
}
/* GODOT start */
PFN_D3D12_SERIALIZE_ROOT_SIGNATURE d3d_D3D12SerializeRootSignature = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)(void *)GetProcAddress(pLibD3D12, "D3D12SerializeRootSignature");
PFN_D3D12_SERIALIZE_ROOT_SIGNATURE d3d_D3D12SerializeRootSignature = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)(void *)GetProcAddress(pLibD3D12, "D3D12SerializeRootSignature");
if (d3d_D3D12SerializeRootSignature == nullptr) {
return E_INVALIDARG;
}
PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE d3d_D3D12SerializeVersionedRootSignature = (PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE)(void *)GetProcAddress(pLibD3D12, "D3D12SerializeVersionedRootSignature");
/* GODOT end */
switch (MaxVersion)
{
case D3D_ROOT_SIGNATURE_VERSION_1_0:
switch (pRootSignatureDesc->Version)
{
case D3D_ROOT_SIGNATURE_VERSION_1_0:
/* GODOT start */
return d3d_D3D12SerializeRootSignature(&pRootSignatureDesc->Desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob);
/* GODOT end */
case D3D_ROOT_SIGNATURE_VERSION_1_1:
#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609)
@ -1126,9 +1120,7 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
if (SUCCEEDED(hr))
{
const CD3DX12_ROOT_SIGNATURE_DESC desc_1_0(desc_1_1.NumParameters, pParameters_1_0, desc_1_1.NumStaticSamplers, pStaticSamplers == nullptr ? desc_1_1.pStaticSamplers : pStaticSamplers, desc_1_1.Flags);
/* GODOT start */
hr = d3d_D3D12SerializeRootSignature(&desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob);
/* GODOT end */
}
if (pParameters)
@ -1159,9 +1151,7 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
{
case D3D_ROOT_SIGNATURE_VERSION_1_0:
case D3D_ROOT_SIGNATURE_VERSION_1_1:
/* GODOT start */
return d3d_D3D12SerializeVersionedRootSignature(pRootSignatureDesc, ppBlob, ppErrorBlob);
/* GODOT end */
#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609)
case D3D_ROOT_SIGNATURE_VERSION_1_2:
@ -1197,9 +1187,7 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
if (SUCCEEDED(hr))
{
const CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC desc(desc_1_1.NumParameters, desc_1_1.pParameters, desc_1_1.NumStaticSamplers, pStaticSamplers == nullptr ? desc_1_1.pStaticSamplers : pStaticSamplers, desc_1_1.Flags);
/* GODOT start */
hr = d3d_D3D12SerializeVersionedRootSignature(&desc, ppBlob, ppErrorBlob);
/* GODOT end */
}
if (pStaticSamplers)
@ -1215,9 +1203,7 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609)
case D3D_ROOT_SIGNATURE_VERSION_1_2:
#endif
/* GODOT start */
return d3d_D3D12SerializeVersionedRootSignature(pRootSignatureDesc, ppBlob, ppErrorBlob);
/* GODOT end */
}
return E_INVALIDARG;

View file

@ -0,0 +1,25 @@
diff --git a/thirdparty/directx_headers/include/directx/d3dx12_pipeline_state_stream.h b/thirdparty/directx_headers/include/directx/d3dx12_pipeline_state_stream.h
index f061e79596..27c7f20448 100644
--- a/thirdparty/directx_headers/include/directx/d3dx12_pipeline_state_stream.h
+++ b/thirdparty/directx_headers/include/directx/d3dx12_pipeline_state_stream.h
@@ -25,8 +25,10 @@
struct DefaultSampleMask { operator UINT() noexcept { return UINT_MAX; } };
struct DefaultSampleDesc { operator DXGI_SAMPLE_DESC() noexcept { return DXGI_SAMPLE_DESC{1, 0}; } };
+#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4324)
+#endif
template <typename InnerStructType, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type, typename DefaultArg = InnerStructType>
class alignas(void*) CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT
{
@@ -42,7 +44,9 @@ public:
InnerStructType* operator&() noexcept { return &pssInner; }
InnerStructType const* operator&() const noexcept { return &pssInner; }
};
+#if defined(_MSC_VER)
#pragma warning(pop)
+#endif
typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_PIPELINE_STATE_FLAGS, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS> CD3DX12_PIPELINE_STATE_STREAM_FLAGS;
typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK> CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK;
typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< ID3D12RootSignature*, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE> CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE;

View file

@ -1,28 +1,24 @@
diff --git a/thirdparty/directx_headers/include/directx/d3dx12_root_signature.h b/thirdparty/directx_headers/include/directx/d3dx12_root_signature.h
index 572efed852..18efa7a0cb 100644
index 572efed852..2da79d10f1 100644
--- a/thirdparty/directx_headers/include/directx/d3dx12_root_signature.h
+++ b/thirdparty/directx_headers/include/directx/d3dx12_root_signature.h
@@ -996,6 +996,9 @@ struct CD3DX12_GPU_DESCRIPTOR_HANDLE : public D3D12_GPU_DESCRIPTOR_HANDLE
@@ -996,6 +996,7 @@ struct CD3DX12_GPU_DESCRIPTOR_HANDLE : public D3D12_GPU_DESCRIPTOR_HANDLE
// two code paths for building root signatures, this helper method reconstructs a 1.0 signature when
// 1.1 is not supported.
inline HRESULT D3DX12SerializeVersionedRootSignature(
+/* GODOT start */
+ _In_ HMODULE pLibD3D12,
+/* GODOT end */
_In_ const D3D12_VERSIONED_ROOT_SIGNATURE_DESC* pRootSignatureDesc,
D3D_ROOT_SIGNATURE_VERSION MaxVersion,
_Outptr_ ID3DBlob** ppBlob,
@@ -1006,13 +1009,22 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
@@ -1006,13 +1007,18 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
*ppErrorBlob = nullptr;
}
+ /* GODOT start */
+ PFN_D3D12_SERIALIZE_ROOT_SIGNATURE d3d_D3D12SerializeRootSignature = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)(void *)GetProcAddress(pLibD3D12, "D3D12SerializeRootSignature");
+ PFN_D3D12_SERIALIZE_ROOT_SIGNATURE d3d_D3D12SerializeRootSignature = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)(void *)GetProcAddress(pLibD3D12, "D3D12SerializeRootSignature");
+ if (d3d_D3D12SerializeRootSignature == nullptr) {
+ return E_INVALIDARG;
+ }
+ PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE d3d_D3D12SerializeVersionedRootSignature = (PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE)(void *)GetProcAddress(pLibD3D12, "D3D12SerializeVersionedRootSignature");
+ /* GODOT end */
switch (MaxVersion)
{
case D3D_ROOT_SIGNATURE_VERSION_1_0:
@ -30,53 +26,43 @@ index 572efed852..18efa7a0cb 100644
{
case D3D_ROOT_SIGNATURE_VERSION_1_0:
- return D3D12SerializeRootSignature(&pRootSignatureDesc->Desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob);
+/* GODOT start */
+ return d3d_D3D12SerializeRootSignature(&pRootSignatureDesc->Desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob);
+/* GODOT end */
case D3D_ROOT_SIGNATURE_VERSION_1_1:
#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609)
@@ -1114,7 +1126,9 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
@@ -1114,7 +1120,7 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
if (SUCCEEDED(hr))
{
const CD3DX12_ROOT_SIGNATURE_DESC desc_1_0(desc_1_1.NumParameters, pParameters_1_0, desc_1_1.NumStaticSamplers, pStaticSamplers == nullptr ? desc_1_1.pStaticSamplers : pStaticSamplers, desc_1_1.Flags);
- hr = D3D12SerializeRootSignature(&desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob);
+/* GODOT start */
+ hr = d3d_D3D12SerializeRootSignature(&desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob);
+/* GODOT end */
}
if (pParameters)
@@ -1145,7 +1159,9 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
@@ -1145,7 +1151,7 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
{
case D3D_ROOT_SIGNATURE_VERSION_1_0:
case D3D_ROOT_SIGNATURE_VERSION_1_1:
- return D3D12SerializeVersionedRootSignature(pRootSignatureDesc, ppBlob, ppErrorBlob);
+/* GODOT start */
+ return d3d_D3D12SerializeVersionedRootSignature(pRootSignatureDesc, ppBlob, ppErrorBlob);
+/* GODOT end */
#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609)
case D3D_ROOT_SIGNATURE_VERSION_1_2:
@@ -1181,7 +1197,9 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
@@ -1181,7 +1187,7 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
if (SUCCEEDED(hr))
{
const CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC desc(desc_1_1.NumParameters, desc_1_1.pParameters, desc_1_1.NumStaticSamplers, pStaticSamplers == nullptr ? desc_1_1.pStaticSamplers : pStaticSamplers, desc_1_1.Flags);
- hr = D3D12SerializeVersionedRootSignature(&desc, ppBlob, ppErrorBlob);
+/* GODOT start */
+ hr = d3d_D3D12SerializeVersionedRootSignature(&desc, ppBlob, ppErrorBlob);
+/* GODOT end */
}
if (pStaticSamplers)
@@ -1197,7 +1215,9 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
@@ -1197,7 +1203,7 @@ inline HRESULT D3DX12SerializeVersionedRootSignature(
#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609)
case D3D_ROOT_SIGNATURE_VERSION_1_2:
#endif
- return D3D12SerializeVersionedRootSignature(pRootSignatureDesc, ppBlob, ppErrorBlob);
+/* GODOT start */
+ return d3d_D3D12SerializeVersionedRootSignature(pRootSignatureDesc, ppBlob, ppErrorBlob);
+/* GODOT end */
}
return E_INVALIDARG;

Some files were not shown because too many files have changed in this diff Show more